Fix ntbtls-cli.c for Windows.
[ntbtls.git] / src / ntbtls-cli.c
1 /* ntbtls-cli.h - NTBTLS client test program
2  * Copyright (C) 2014 g10 Code GmbH
3  *
4  * This file is part of NTBTLS
5  *
6  * NTBTLS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * NTBTLS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25
26 #include <unistd.h>
27 #include <sys/types.h>
28 #ifdef HAVE_W32_SYSTEM
29 # define WIN32_LEAN_AND_MEAN
30 # ifdef HAVE_WINSOCK2_H
31 #  include <winsock2.h>
32 # endif
33 # include <windows.h>
34 #else
35 # include <sys/socket.h>
36 # include <netinet/in.h>
37 # include <arpa/inet.h>
38 # include <netdb.h>
39 #endif
40
41 #include "ntbtls.h"
42
43 #define PGMNAME "ntbtls-cli"
44
45 static int verbose;
46 static int errorcount;
47 static char *opt_hostname;
48
49 \f
50 /*
51  * Reporting functions.
52  */
53 static void
54 die (const char *format, ...)
55 {
56   va_list arg_ptr ;
57
58   fflush (stdout);
59 #ifdef HAVE_FLOCKFILE
60   flockfile (stderr);
61 #endif
62   fprintf (stderr, "%s: ", PGMNAME);
63   va_start (arg_ptr, format) ;
64   vfprintf (stderr, format, arg_ptr);
65   va_end (arg_ptr);
66   if (*format && format[strlen(format)-1] != '\n')
67     putc ('\n', stderr);
68 #ifdef HAVE_FLOCKFILE
69   funlockfile (stderr);
70 #endif
71   exit (1);
72 }
73
74
75 static void
76 fail (const char *format, ...)
77 {
78   va_list arg_ptr;
79
80   fflush (stdout);
81 #ifdef HAVE_FLOCKFILE
82   flockfile (stderr);
83 #endif
84   fprintf (stderr, "%s: ", PGMNAME);
85   va_start (arg_ptr, format);
86   vfprintf (stderr, format, arg_ptr);
87   va_end (arg_ptr);
88   if (*format && format[strlen(format)-1] != '\n')
89     putc ('\n', stderr);
90 #ifdef HAVE_FLOCKFILE
91   funlockfile (stderr);
92 #endif
93   errorcount++;
94   if (errorcount >= 50)
95     die ("stopped after 50 errors.");
96 }
97
98
99 static void
100 info (const char *format, ...)
101 {
102   va_list arg_ptr;
103
104   if (!verbose)
105     return;
106 #ifdef HAVE_FLOCKFILE
107   flockfile (stderr);
108 #endif
109   fprintf (stderr, "%s: ", PGMNAME);
110   va_start (arg_ptr, format);
111   vfprintf (stderr, format, arg_ptr);
112   if (*format && format[strlen(format)-1] != '\n')
113     putc ('\n', stderr);
114   va_end (arg_ptr);
115 #ifdef HAVE_FLOCKFILE
116   funlockfile (stderr);
117 #endif
118 }
119
120
121 \f
122 static int
123 connect_server (const char *server, unsigned short port)
124 {
125   gpg_error_t err;
126   int sock = -1;
127   struct sockaddr_in addr;
128   struct hostent *host;
129
130   addr.sin_family = AF_INET;
131   addr.sin_port = htons (port);
132   host = gethostbyname ((char*)server);
133   if (!host)
134     {
135       err = gpg_error_from_syserror ();
136       fail ("host '%s' not found: %s\n", server, gpg_strerror (err));
137       return -1;
138     }
139
140   addr.sin_addr = *(struct in_addr*)host->h_addr;
141
142   sock = socket (AF_INET, SOCK_STREAM, 0);
143   if (sock == -1)
144     {
145       err = gpg_error_from_syserror ();
146       die ("error creating socket: %s\n", gpg_strerror (err));
147       return -1;
148     }
149
150   if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1)
151     {
152       err = gpg_error_from_syserror ();
153       fail ("error connecting '%s': %s\n", server, gpg_strerror (err));
154       close (sock);
155       return -1;
156     }
157
158   info ("connected to '%s' port %hu\n", server, port);
159
160   return sock;
161 }
162
163
164 static int
165 connect_estreams (const char *server, int port,
166                   estream_t *r_in, estream_t *r_out)
167 {
168   gpg_error_t err;
169   int sock;
170
171   *r_in = *r_out = NULL;
172
173   sock = connect_server (server, port);
174   if (sock == -1)
175     return gpg_error (GPG_ERR_GENERAL);
176   *r_in = es_fdopen_nc (sock, "rb");
177   if (!*r_in)
178     {
179       err = gpg_error_from_syserror ();
180       close (sock);
181       return err;
182     }
183   *r_out = es_fdopen (sock, "wb");
184   if (!*r_out)
185     {
186       err = gpg_error_from_syserror ();
187       es_fclose (*r_in);
188       *r_in = NULL;
189       close (sock);
190       return err;
191     }
192
193   return 0;
194 }
195
196
197 \f
198 static void
199 simple_client (const char *server, int port)
200 {
201   gpg_error_t err;
202   ntbtls_t tls;
203   estream_t inbound, outbound;
204   estream_t readfp, writefp;
205   int c;
206
207   err = ntbtls_new (&tls, NTBTLS_CLIENT);
208   if (err)
209     die ("ntbtls_init failed: %s <%s>\n",
210          gpg_strerror (err), gpg_strsource (err));
211
212   err = connect_estreams (server, port, &inbound, &outbound);
213   if (err)
214     die ("error connecting server: %s <%s>\n",
215          gpg_strerror (err), gpg_strsource (err));
216
217   err = ntbtls_set_transport (tls, inbound, outbound);
218   if (err)
219     die ("ntbtls_set_transport failed: %s <%s>\n",
220          gpg_strerror (err), gpg_strsource (err));
221
222   err = ntbtls_get_stream (tls, &readfp, &writefp);
223   if (err)
224     die ("ntbtls_get_stream failed: %s <%s>\n",
225          gpg_strerror (err), gpg_strsource (err));
226
227   if (opt_hostname)
228     {
229       err = ntbtls_set_hostname (tls, opt_hostname);
230       if (err)
231         die ("ntbtls_set_hostname failed: %s <%s>\n",
232              gpg_strerror (err), gpg_strsource (err));
233     }
234
235   info ("starting handshake");
236   while ((err = ntbtls_handshake (tls)))
237     {
238       info ("handshake error: %s <%s>", gpg_strerror (err),gpg_strsource (err));
239       switch (gpg_err_code (err))
240         {
241         default:
242           break;
243         }
244       die ("handshake failed");
245     }
246   info ("handshake done");
247
248   do
249     {
250       es_fputs ("GET / HTTP/1.0\r\n", writefp);
251       if (opt_hostname)
252         es_fprintf (writefp, "Host: %s\r\n", opt_hostname);
253       es_fprintf (writefp, "X-ntbtls: %s\r\n",
254                   ntbtls_check_version (PACKAGE_VERSION));
255       es_fputs ("\r\n", writefp);
256       es_fflush (writefp);
257       while (/*es_pending (readfp) &&*/ (c = es_fgetc (readfp)) != EOF)
258         putchar (c);
259     }
260   while (c != EOF);
261
262   ntbtls_release (tls);
263   es_fclose (inbound);
264   es_fclose (outbound);
265 }
266
267
268
269 int
270 main (int argc, char **argv)
271 {
272   int last_argc = -1;
273   int debug_level = 0;
274   int port = 443;
275
276   if (argc)
277     { argc--; argv++; }
278   while (argc && last_argc != argc )
279     {
280       last_argc = argc;
281       if (!strcmp (*argv, "--"))
282         {
283           argc--; argv++;
284           break;
285         }
286       else if (!strcmp (*argv, "--help"))
287         {
288           fputs ("Usage: " PGMNAME " [OPTIONS] HOST\n"
289                  "Connect via TLS to HOST\n"
290                  "Options:\n"
291                  "  --version       print the library version\n"
292                  "  --verbose       show more diagnostics\n"
293                  "  --debug LEVEL   enable debugging at LEVEL\n"
294                  "  --port N        connect to port N (default is 443)\n"
295                  "  --hostname NAME use NAME for SNI\n"
296                  "\n", stdout);
297           return 0;
298         }
299       else if (!strcmp (*argv, "--version"))
300         {
301           printf ("%s\n", ntbtls_check_version (NULL));
302           if (verbose)
303             printf ("%s", ntbtls_check_version ("\001\001"));
304           return 0;
305         }
306       else if (!strcmp (*argv, "--verbose"))
307         {
308           verbose = 1;
309           argc--; argv++;
310         }
311       else if (!strcmp (*argv, "--debug"))
312         {
313           verbose = 1;
314           argc--; argv++;
315           if (argc)
316             {
317               debug_level = atoi (*argv);
318               argc--; argv++;
319             }
320           else
321             debug_level = 1;
322         }
323       else if (!strcmp (*argv, "--port"))
324         {
325           argc--; argv++;
326           if (argc)
327             {
328               port = atoi (*argv);
329               argc--; argv++;
330             }
331           else
332             port = 8443;
333         }
334       else if (!strcmp (*argv, "--hostname"))
335         {
336           if (argc < 2)
337             die ("argument missing for option '%s'\n", *argv);
338           argc--; argv++;
339           opt_hostname = *argv;
340           argc--; argv++;
341         }
342       else if (!strncmp (*argv, "--", 2) && (*argv)[2])
343         die ("Invalid option '%s'\n", *argv);
344     }
345
346   if (!ntbtls_check_version (PACKAGE_VERSION))
347     die ("NTBTLS library too old (need %s, have %s)\n",
348          PACKAGE_VERSION, ntbtls_check_version (NULL));
349
350   if (debug_level)
351     ntbtls_set_debug (debug_level, NULL, NULL);
352
353   simple_client (argc? *argv : "localhost", port);
354   return 0;
355 }