52a232927bf426bec2da9a265b43c26a5063d34c
[elisp/starttls.git] / starttls.c
1 /* simple wrapper program for STARTTLS
2
3    Copyright (C) 1999, 2000 Free Software Foundation, Inc.
4
5    Author: Daiki Ueno <ueno@unixuser.org>
6    Created: 1999-11-19
7    Keywords: TLS, OpenSSL
8
9    This file is not part of any package.
10
11    This program is free software; you can redistribute it and/or modify 
12    it under the terms of the GNU General Public License as published by 
13    the Free Software Foundation; either version 2, or (at your option)  
14    any later version.                                                   
15
16    This program is distributed in the hope that it will be useful,      
17    but WITHOUT ANY WARRANTY; without even the implied warranty of       
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        
19    GNU General Public License for more details.                         
20
21    You should have received a copy of the GNU General Public License    
22    along with GNU Emacs; see the file COPYING.  If not, write to the    
23    Free Software Foundation, Inc., 59 Temple Place - Suite 330,         
24    Boston, MA 02111-1307, USA.                                          
25
26 */
27
28 #include <sys/types.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdbool.h>
33
34 #include <unistd.h>
35
36 #include <errno.h>
37 #include <sys/time.h>
38 #include <sys/socket.h>
39 #include <sys/file.h>
40 #include <sys/ioctl.h>
41 #include <sys/stat.h>
42 #include <netdb.h>
43 #include <stdio.h>
44 #include <signal.h>
45 #include <fcntl.h>
46 #include <netinet/in.h>
47 #include <poll.h>
48 #include <getopt.h>
49 #include "getaddrinfo.h"
50 #include "gettext.h"
51 #include "inet_ntop.h"
52 #include "strdup.h"
53
54 extern void tls_negotiate (int, const char *, const char *);
55 extern int tls_write(int, const char *, int);
56 extern int tls_read(int, char *, int);
57 extern int tls_pending();
58
59 static char *opt_cert_file = NULL, *opt_key_file = NULL;
60 static bool opt_assuan = false;
61 static int tls_fd = -1;
62
63 static void
64 usage (progname)
65      const char *progname;
66 {
67   printf ("%s (%s) %s\n"
68           "Copyright (C) 1999 Free Software Foundation, Inc.\n"
69           "This program comes with ABSOLUTELY NO WARRANTY.\n"
70           "This is free software, and you are welcome to redistribute it\n"
71           "under certain conditions. See the file COPYING for details.\n\n"
72           "Usage: %s [options] host port\n\n"
73           "Options:\n\n"
74           " --cert-file [file]      specify certificate file\n"
75           " --key-file [file]       specify private key file\n"
76           " --help                  show this help\n",
77           progname, PACKAGE, VERSION, progname);
78 }
79
80 static void
81 do_tls_negotiate(sig)
82      int sig;
83 {
84   tls_negotiate (tls_fd, opt_cert_file, opt_key_file);
85 }
86
87 int
88 tcp_connect (hostname, service)
89      const char *hostname, *service;
90 {
91   int server, _false = 0;
92   struct addrinfo *in, *in0, hints;
93
94   memset (&hints, 0, sizeof (hints));
95   hints.ai_family = AF_UNSPEC;
96   hints.ai_socktype = SOCK_STREAM;
97   if (getaddrinfo (hostname, service, &hints, &in0))
98     return -1;
99
100   for (in = in0; in; in = in->ai_next)
101     {
102       server = socket (in->ai_family, in->ai_socktype, in->ai_protocol);
103       if (server < 0)
104         continue;
105       if (connect (server, in->ai_addr, in->ai_addrlen) < 0)
106         {
107           server = -1;
108           continue;
109         }
110       break;
111   }
112
113   if (server < 0)
114     return -1;
115
116   setsockopt (server, SOL_SOCKET, SO_KEEPALIVE, (const char *) &_false,
117               sizeof (_false));
118
119   return server;
120 }
121
122 int
123 redirect (fd, buffer, nbuffer, write_function)
124      int fd;
125      char *buffer;
126      int nbuffer;
127      int (*write_function) (int, const char *, int);
128 {
129   sigset_t mask, orig_mask;
130   struct pollfd writefds[1];
131   int wrote;
132   char *retry;
133
134   sigemptyset (&mask);
135   sigaddset (&mask, SIGALRM);
136
137   writefds[0].fd = fd;
138   writefds[0].events = POLLOUT;
139
140   for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
141     {
142       int ready;
143
144       sigprocmask (SIG_SETMASK, &mask, &orig_mask);
145       ready = poll (writefds, 1, -1);
146       sigprocmask (SIG_SETMASK, &orig_mask, NULL);
147       if (ready == -1 && errno != EINTR)
148         {
149           if (opt_assuan)
150             printf ("ERR 1 poll: %s\r\n", strerror (errno));
151           return 1;
152         }
153       wrote = (*write_function)(fd, retry, nbuffer);
154       if (wrote == -1)
155         {
156           if (opt_assuan)
157             printf ("ERR 1 write: %s\r\n", strerror (errno));
158           return 1;
159         }
160     }
161 }
162
163 int
164 main (argc, argv) 
165      int argc;
166      char **argv;
167 {
168   int in = fileno (stdin), out = fileno (stdout),
169     nbuffer, wrote;
170   struct pollfd readfds[2], writefds[1];
171   char buffer[BUFSIZ], *retry;
172   struct sigaction act;
173   sigset_t orig_mask;
174
175   int this_option_optind = optind ? optind : 1;
176   int option_index = 0, c;
177   static struct option long_options[] =
178     {
179       {"cert-file", 1, 0, 'c'},
180       {"key-file", 1, 0, 'k'},
181       {"help", 0, 0, 'h'},
182       {"assuan", 0, 0, 'A'},
183       {0, 0, 0, 0}
184     };
185
186   while (1)
187     {
188       c = getopt_long (argc, argv, "c:k:hA", long_options, &option_index);
189       if (c == -1)
190         break;
191     
192       switch (c)
193         {
194         case 'c':
195           opt_cert_file = optarg;
196           break;
197         case 'k':
198           opt_key_file = optarg;
199           break;
200         case 'h':
201           usage (argv[0]);
202           return 0;
203         case 'A':
204           opt_assuan = true;
205           break;
206         default:
207           usage (argv[0]);
208           return 1;
209         }
210     }
211
212   if (optind+2 != argc)
213     {
214       usage (argv[0]);
215       return 1;
216     }
217
218   tls_fd = tcp_connect (argv[optind], argv[optind+1]);
219   if (tls_fd < 0)
220     {
221       perror ("tcp_connect");
222       return 1;
223     }
224
225   memset (&act, 0, sizeof (act));
226   act.sa_handler = do_tls_negotiate;
227   sigemptyset (&act.sa_mask);
228   sigaddset (&act.sa_mask, SIGALRM);
229   act.sa_flags = SA_RESTART|SA_RESETHAND;
230   sigaction (SIGALRM, &act, NULL);
231
232   readfds[0].fd = in;
233   readfds[1].fd = tls_fd;
234   readfds[0].events = POLLIN;
235   readfds[1].events = POLLIN;
236   writefds[0].events = POLLOUT;
237
238   while (1)
239     {
240       int ready;
241
242       sigprocmask (SIG_SETMASK, &act.sa_mask, &orig_mask);
243       ready = poll (readfds, 2, -1);
244       sigprocmask (SIG_SETMASK, &orig_mask, NULL);
245       if (ready == -1 && errno != EINTR)
246         {
247           if (opt_assuan)
248             printf ("ERR 1 poll: %s\r\n", strerror (errno));
249           return 1;
250         }
251       if (readfds[0].revents & POLLIN)
252         {
253           nbuffer = read (in, buffer, sizeof buffer -1);
254
255           if (nbuffer == 0)
256             goto finish;
257           redirect (tls_fd, buffer, nbuffer, tls_write);
258         }
259       if (readfds[1].revents & POLLIN)
260         {
261 readtop:
262           nbuffer = tls_read(tls_fd, buffer, sizeof buffer -1);
263           if (nbuffer == 0)
264             goto finish;
265           redirect (out, buffer, nbuffer, write);
266           if (tls_pending())
267             goto readtop;
268         }
269     }
270
271  finish:
272   close (in);
273   close (out);
274   
275   return 0;
276 }