* Use gnulib.
[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 #ifdef HAVE_POLL_H
48 #include <sys/poll.h>
49 #endif
50 #include "getopt.h"
51 #include "getaddrinfo.h"
52 #include "gettext.h"
53 #include "inet_ntop.h"
54 #include "strdup.h"
55
56 extern void tls_negotiate (int, const char *, const char *);
57 extern int tls_write(int, const char *, int);
58 extern int tls_read(int, char *, int);
59 extern int tls_pending();
60
61 static char *opt_cert_file = NULL, *opt_key_file = NULL;
62 static int tls_fd;
63
64 static void
65 usage (progname)
66      const char *progname;
67 {
68   printf ("%s (%s) %s\n"
69           "Copyright (C) 1999 Free Software Foundation, Inc.\n"
70           "This program comes with ABSOLUTELY NO WARRANTY.\n"
71           "This is free software, and you are welcome to redistribute it\n"
72           "under certain conditions. See the file COPYING for details.\n\n"
73           "Usage: %s [options] host port\n\n"
74           "Options:\n\n"
75           " --cert-file [file]      specify certificate file\n"
76           " --key-file [file]       specify private key file\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 main (argc, argv) 
124   int argc;
125   char **argv;
126 {
127   int in = fileno (stdin), out = fileno (stdout), 
128     nbuffer, wrote;
129 #ifdef HAVE_POLL
130   struct pollfd readfds[2], writefds[1];
131 #else
132   fd_set readfds, writefds;
133 #endif
134   char buffer[BUFSIZ], *retry;
135   struct sigaction act;
136
137   int this_option_optind = optind ? optind : 1;
138   int option_index = 0, c;
139   static struct option long_options[] =
140     {
141       {"cert-file", 1, 0, 'c'},
142       {"key-file", 1, 0, 'k'},
143       {0, 0, 0, 0}
144     };
145
146   while (1)
147     {
148       c = getopt_long (argc, argv, "c:k:", long_options, &option_index);
149       if (c == -1)
150         break;
151     
152       switch (c)
153         {
154         case 'c':
155           opt_cert_file = optarg;
156           break;
157         case 'k':
158           opt_key_file = optarg;
159           break;
160         default:
161           usage (argv[0]);
162           return 1;
163         }
164     }
165
166   if (optind+2 != argc)
167     {
168       usage (argv[0]);
169       return 1;
170     }
171
172   tls_fd = tcp_connect (argv[optind], argv[optind+1]);
173   if (tls_fd < 0)
174     {
175       perror ("tcp_connect");
176       return 1;
177     }
178
179   memset (&act, 0, sizeof (act));
180   act.sa_handler = do_tls_negotiate;
181   sigemptyset (&act.sa_mask);
182   act.sa_flags = SA_RESTART|SA_RESETHAND;
183   sigaction (SIGALRM, &act, NULL);
184
185 #ifdef HAVE_POLL
186   readfds[0].fd = in;
187   readfds[1].fd = tls_fd;
188   readfds[0].events = POLLIN;
189   readfds[1].events = POLLIN;
190   writefds[0].events = POLLOUT;
191 #endif
192
193   while (1)
194     {
195 #ifdef HAVE_POLL
196       if (poll (readfds, 2, -1) == -1 && errno != EINTR)
197 #else
198       FD_ZERO (&readfds);
199       FD_SET (tls_fd, &readfds);
200       FD_SET (in, &readfds);
201       if (select (tls_fd+1, &readfds, NULL, NULL, NULL) == -1
202           && errno != EINTR )
203 #endif
204         {
205           perror ("poll");
206           return 1;
207         }
208 #ifdef HAVE_POLL
209       if (readfds[0].revents & POLLIN)
210 #else
211       if (FD_ISSET (in, &readfds))
212 #endif
213         {
214           nbuffer = read (in, buffer, sizeof buffer -1);
215
216           if (nbuffer == 0)
217             goto finish;
218           for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
219             {
220 #ifdef HAVE_POLL
221               writefds[0].fd = tls_fd;
222               if (poll (writefds, 1, -1) == -1)
223 #else
224               FD_ZERO (&writefds);
225               FD_SET (tls_fd, &writefds);
226               if (select (tls_fd+1, NULL, &writefds, NULL, NULL) == -1)
227 #endif
228                 {
229                   perror ("poll");
230                   return 1;
231                 }
232               wrote = tls_write(tls_fd, retry, nbuffer);
233               if (wrote < 0) goto finish;
234             }
235         }
236 #ifdef HAVE_POLL
237       if (readfds[1].revents & POLLIN)
238 #else
239       if (FD_ISSET (tls_fd, &readfds))
240 #endif
241         {
242 readtop:
243           nbuffer = tls_read(tls_fd, buffer, sizeof buffer -1);
244           if (nbuffer == 0)
245             goto finish;
246           for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
247             {
248 #ifdef HAVE_POLL
249               writefds[0].fd = out;
250               if (poll (writefds, 1, -1) == -1)
251 #else
252               FD_ZERO (&writefds);
253               FD_SET (out, &writefds);
254               if (select (out+1, NULL, &writefds, NULL, NULL) == -1)
255 #endif
256                 {
257                   perror ("poll");
258                   return 1;
259                 }
260               wrote = write (out, retry, nbuffer);
261               if (wrote < 0) goto finish;
262             }
263           if (tls_pending())
264             goto readtop;
265         }
266     }
267
268  finish:
269   close (in);
270   close (out);
271   
272   return 0;
273 }