ntbtls-cli: New option --head and use a default SNI.
[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 static int opt_head;
49
50
51 \f
52 /*
53  * Reporting functions.
54  */
55 static void
56 die (const char *format, ...)
57 {
58   va_list arg_ptr ;
59
60   fflush (stdout);
61 #ifdef HAVE_FLOCKFILE
62   flockfile (stderr);
63 #endif
64   fprintf (stderr, "%s: ", PGMNAME);
65   va_start (arg_ptr, format) ;
66   vfprintf (stderr, format, arg_ptr);
67   va_end (arg_ptr);
68   if (*format && format[strlen(format)-1] != '\n')
69     putc ('\n', stderr);
70 #ifdef HAVE_FLOCKFILE
71   funlockfile (stderr);
72 #endif
73   exit (1);
74 }
75
76
77 static void
78 fail (const char *format, ...)
79 {
80   va_list arg_ptr;
81
82   fflush (stdout);
83 #ifdef HAVE_FLOCKFILE
84   flockfile (stderr);
85 #endif
86   fprintf (stderr, "%s: ", PGMNAME);
87   va_start (arg_ptr, format);
88   vfprintf (stderr, format, arg_ptr);
89   va_end (arg_ptr);
90   if (*format && format[strlen(format)-1] != '\n')
91     putc ('\n', stderr);
92 #ifdef HAVE_FLOCKFILE
93   funlockfile (stderr);
94 #endif
95   errorcount++;
96   if (errorcount >= 50)
97     die ("stopped after 50 errors.");
98 }
99
100
101 static void
102 info (const char *format, ...)
103 {
104   va_list arg_ptr;
105
106   if (!verbose)
107     return;
108 #ifdef HAVE_FLOCKFILE
109   flockfile (stderr);
110 #endif
111   fprintf (stderr, "%s: ", PGMNAME);
112   va_start (arg_ptr, format);
113   vfprintf (stderr, format, arg_ptr);
114   if (*format && format[strlen(format)-1] != '\n')
115     putc ('\n', stderr);
116   va_end (arg_ptr);
117 #ifdef HAVE_FLOCKFILE
118   funlockfile (stderr);
119 #endif
120 }
121
122
123 \f
124 static int
125 connect_server (const char *server, unsigned short port)
126 {
127   gpg_error_t err;
128   int sock = -1;
129   struct sockaddr_in addr;
130   struct hostent *host;
131
132   addr.sin_family = AF_INET;
133   addr.sin_port = htons (port);
134   host = gethostbyname ((char*)server);
135   if (!host)
136     {
137       err = gpg_error_from_syserror ();
138       fail ("host '%s' not found: %s\n", server, gpg_strerror (err));
139       return -1;
140     }
141
142   addr.sin_addr = *(struct in_addr*)host->h_addr;
143
144   sock = socket (AF_INET, SOCK_STREAM, 0);
145   if (sock == -1)
146     {
147       err = gpg_error_from_syserror ();
148       die ("error creating socket: %s\n", gpg_strerror (err));
149       return -1;
150     }
151
152   if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1)
153     {
154       err = gpg_error_from_syserror ();
155       fail ("error connecting '%s': %s\n", server, gpg_strerror (err));
156       close (sock);
157       return -1;
158     }
159
160   info ("connected to '%s' port %hu\n", server, port);
161
162   return sock;
163 }
164
165
166 static int
167 connect_estreams (const char *server, int port,
168                   estream_t *r_in, estream_t *r_out)
169 {
170   gpg_error_t err;
171   int sock;
172
173   *r_in = *r_out = NULL;
174
175   sock = connect_server (server, port);
176   if (sock == -1)
177     return gpg_error (GPG_ERR_GENERAL);
178   *r_in = es_fdopen_nc (sock, "rb");
179   if (!*r_in)
180     {
181       err = gpg_error_from_syserror ();
182       close (sock);
183       return err;
184     }
185   *r_out = es_fdopen (sock, "wb");
186   if (!*r_out)
187     {
188       err = gpg_error_from_syserror ();
189       es_fclose (*r_in);
190       *r_in = NULL;
191       close (sock);
192       return err;
193     }
194
195   return 0;
196 }
197
198
199 \f
200 static void
201 simple_client (const char *server, int port)
202 {
203   gpg_error_t err;
204   ntbtls_t tls;
205   estream_t inbound, outbound;
206   estream_t readfp, writefp;
207   int c;
208
209   err = ntbtls_new (&tls, NTBTLS_CLIENT);
210   if (err)
211     die ("ntbtls_init failed: %s <%s>\n",
212          gpg_strerror (err), gpg_strsource (err));
213
214   err = connect_estreams (server, port, &inbound, &outbound);
215   if (err)
216     die ("error connecting server: %s <%s>\n",
217          gpg_strerror (err), gpg_strsource (err));
218
219   err = ntbtls_set_transport (tls, inbound, outbound);
220   if (err)
221     die ("ntbtls_set_transport failed: %s <%s>\n",
222          gpg_strerror (err), gpg_strsource (err));
223
224   err = ntbtls_get_stream (tls, &readfp, &writefp);
225   if (err)
226     die ("ntbtls_get_stream failed: %s <%s>\n",
227          gpg_strerror (err), gpg_strsource (err));
228
229   if (opt_hostname)
230     {
231       err = ntbtls_set_hostname (tls, opt_hostname);
232       if (err)
233         die ("ntbtls_set_hostname failed: %s <%s>\n",
234              gpg_strerror (err), gpg_strsource (err));
235     }
236
237   info ("starting handshake");
238   while ((err = ntbtls_handshake (tls)))
239     {
240       info ("handshake error: %s <%s>", gpg_strerror (err),gpg_strsource (err));
241       switch (gpg_err_code (err))
242         {
243         default:
244           break;
245         }
246       die ("handshake failed");
247     }
248   info ("handshake done");
249
250   do
251     {
252       es_fprintf (writefp, "%s / HTTP/1.0\r\n", opt_head? "HEAD":"GET");
253       if (opt_hostname)
254         es_fprintf (writefp, "Host: %s\r\n", opt_hostname);
255       es_fprintf (writefp, "X-ntbtls: %s\r\n",
256                   ntbtls_check_version (PACKAGE_VERSION));
257       es_fputs ("\r\n", writefp);
258       es_fflush (writefp);
259       while (/*es_pending (readfp) &&*/ (c = es_fgetc (readfp)) != EOF)
260         putchar (c);
261     }
262   while (c != EOF);
263
264   ntbtls_release (tls);
265   es_fclose (inbound);
266   es_fclose (outbound);
267 }
268
269
270
271 int
272 main (int argc, char **argv)
273 {
274   int last_argc = -1;
275   int debug_level = 0;
276   int port = 443;
277   char *host;
278
279   if (argc)
280     { argc--; argv++; }
281   while (argc && last_argc != argc )
282     {
283       last_argc = argc;
284       if (!strcmp (*argv, "--"))
285         {
286           argc--; argv++;
287           break;
288         }
289       else if (!strcmp (*argv, "--help"))
290         {
291           fputs ("Usage: " PGMNAME " [OPTIONS] HOST\n"
292                  "Connect via TLS to HOST\n"
293                  "Options:\n"
294                  "  --version       print the library version\n"
295                  "  --verbose       show more diagnostics\n"
296                  "  --debug LEVEL   enable debugging at LEVEL\n"
297                  "  --port N        connect to port N (default is 443)\n"
298                  "  --hostname NAME use NAME instead of HOST for SNI\n"
299                  "  --head          send a HEAD and not a GET request\n"
300                  "\n", stdout);
301           return 0;
302         }
303       else if (!strcmp (*argv, "--version"))
304         {
305           printf ("%s\n", ntbtls_check_version (NULL));
306           if (verbose)
307             printf ("%s", ntbtls_check_version ("\001\001"));
308           return 0;
309         }
310       else if (!strcmp (*argv, "--verbose"))
311         {
312           verbose = 1;
313           argc--; argv++;
314         }
315       else if (!strcmp (*argv, "--debug"))
316         {
317           verbose = 1;
318           argc--; argv++;
319           if (argc)
320             {
321               debug_level = atoi (*argv);
322               argc--; argv++;
323             }
324           else
325             debug_level = 1;
326         }
327       else if (!strcmp (*argv, "--port"))
328         {
329           argc--; argv++;
330           if (argc)
331             {
332               port = atoi (*argv);
333               argc--; argv++;
334             }
335           else
336             port = 8443;
337         }
338       else if (!strcmp (*argv, "--hostname"))
339         {
340           if (argc < 2)
341             die ("argument missing for option '%s'\n", *argv);
342           argc--; argv++;
343           opt_hostname = *argv;
344           argc--; argv++;
345         }
346       else if (!strcmp (*argv, "--head"))
347         {
348           opt_head = 1;
349           argc--; argv++;
350         }
351       else if (!strncmp (*argv, "--", 2) && (*argv)[2])
352         die ("Invalid option '%s'\n", *argv);
353     }
354
355   host = argc? *argv : "localhost";
356   if (!opt_hostname)
357     opt_hostname = host;
358   if (!*opt_hostname)
359     opt_hostname = NULL;
360
361   if (!ntbtls_check_version (PACKAGE_VERSION))
362     die ("NTBTLS library too old (need %s, have %s)\n",
363          PACKAGE_VERSION, ntbtls_check_version (NULL));
364
365   if (debug_level)
366     ntbtls_set_debug (debug_level, NULL, NULL);
367
368   simple_client (host, port);
369   return 0;
370 }