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