]> ruderich.org/simon Gitweb - tlsproxy/tlsproxy.git/blob - connection.c
0d287491e861b9ad87f2d3844d592fc066768324
[tlsproxy/tlsproxy.git] / connection.c
1 /*
2  * Handle connections.
3  *
4  * Copyright (C) 2011-2013  Simon Ruderich
5  *
6  * This program 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  * This program 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 "tlsproxy.h"
21 #include "connection.h"
22 #include "verify.h"
23
24 #include <assert.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <netdb.h>
28 #include <poll.h>
29 #include <unistd.h>
30
31 #include <gnutls/x509.h>
32
33
34 /* Maximum length of a HTTP request line. Longer request lines are aborted
35  * with an error. The standard doesn't specify a maximum line length but this
36  * should be a good limit to make processing simpler. As HTTPS is used this
37  * doesn't limit long GET requests. */
38 #define MAX_REQUEST_LINE 4096
39
40 /* Format string used to send HTTP/1.0 error responses to the client.
41  *
42  * %s is used 5 times, first is the error code, then additional headers, next
43  * two are the error code (no %n$s which is not in C98!), the last is the
44  * message. */
45 #define HTTP_RESPONSE_FORMAT "HTTP/1.0 %s\r\n\
46 Content-Type: text/html; charset=US-ASCII\r\n\
47 %s\r\n\
48 <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n\
49 <html>\n\
50 <head><title>%s</title></head>\n\
51 <body>\n\
52 <h1>%s</h1>\n\
53 <p>%s</p>\n\
54 </body>\n\
55 </html>\n"
56
57
58 static int initialize_tls_session_client(int peer_socket,
59         const char *hostname,
60         gnutls_session_t *session,
61         gnutls_certificate_credentials_t *x509_cred);
62 static int initialize_tls_session_server(int peer_socket,
63         gnutls_session_t *session,
64         gnutls_certificate_credentials_t *x509_cred);
65 static int initialize_tls_session_both(unsigned int flags,
66         int peer_socket,
67         gnutls_session_t *session,
68         gnutls_certificate_credentials_t *x509_cred);
69
70 static int fdopen_read_write(int socket, FILE **read_fd, FILE **write_fd);
71 static int read_http_request(FILE *client_fd, char *request, size_t length);
72 static void send_bad_request(FILE *client_fd);
73 static void send_authentication_required(FILE *client_fd);
74 static void send_forwarding_failure(FILE *client_fd);
75 static void tls_send_invalid_cert_message(gnutls_session_t session);
76
77 static void transfer_data(int client, int server);
78 static int read_from_write_to(int from, int to);
79 static void transfer_data_tls(int client, int server,
80                               gnutls_session_t client_session,
81                               gnutls_session_t server_session);
82 static int read_from_write_to_tls(gnutls_session_t from, gnutls_session_t to,
83                                   size_t buffer_size);
84
85 static int connect_to_host(const char *hostname, const char *port);
86
87 static int parse_request(const char *buffer, char *host, char *port,
88                                              int *version_minor);
89
90 static void log_session_information(gnutls_session_t session);
91
92
93 void handle_connection(int client_socket) {
94     int server_socket;
95     FILE *client_fd_read, *client_fd_write, *server_fd_read, *server_fd_write;
96
97     char buffer[MAX_REQUEST_LINE];
98     char host[MAX_REQUEST_LINE];
99     char port[5 + 1];
100
101     int version_minor; /* x in HTTP/1.x */
102     int result;
103
104     /* client_x509_cred is used when talking to the client (acting as a TSL
105      * server), server_x509_cred is used when talking to the server (acting as
106      * a TSL client). */
107     gnutls_certificate_credentials_t client_x509_cred, server_x509_cred;
108
109     gnutls_session_t client_session, server_session;
110     /* initialize_tls_session_*() called? - used for goto out */
111     int client_session_init, server_session_init;
112     /* gnutls_handshake() called? - used for goto out */
113     int client_session_started, server_session_started;
114     /* Validation failed? If yes we need to send the special "invalid"
115      * certificate. */
116     int validation_failed;
117
118     LOG(DEBUG1, "new connection");
119
120     server_socket = -1;
121     client_fd_read = NULL;
122     client_fd_write = NULL;
123     server_fd_read = NULL;
124     server_fd_write = NULL;
125     client_session_init = 0;
126     server_session_init = 0;
127     client_session_started = 0;
128     server_session_started = 0;
129     validation_failed = 0;
130
131     if (fdopen_read_write(client_socket, &client_fd_read,
132                                          &client_fd_write) != 0) {
133         goto out;
134     }
135
136     /* Read request line (CONNECT ..) and headers (they are discarded). */
137     result = read_http_request(client_fd_read, buffer, sizeof(buffer));
138     if (result == -1) {
139         LOG(WARNING, "read_http_request(): client read error");
140         goto out;
141     } else if (result == -2) {
142         LOG(WARNING, "read_http_request(): client EOF");
143         send_bad_request(client_fd_write);
144         goto out;
145     } else if (result == -3) {
146         LOG(WARNING, "read_http_request(): proxy authentication failed");
147         send_authentication_required(client_fd_write);
148         goto out;
149     }
150
151     if (parse_request(buffer, host, port, &version_minor) != 0) {
152         LOG(WARNING, "bad request: >%s<", buffer);
153         send_bad_request(client_fd_write);
154         goto out;
155     }
156
157     LOG(DEBUG2, "target: %s:%s (HTTP 1.%d)", host, port, version_minor);
158
159     /* Connect to proxy server or directly to server. */
160     if (global_proxy_host != NULL && global_proxy_port != NULL) {
161         LOG(DEBUG1, "connecting to %s:%s", global_proxy_host,
162                                            global_proxy_port);
163         server_socket = connect_to_host(global_proxy_host, global_proxy_port);
164     } else {
165         LOG(DEBUG1, "connecting to %s:%s", host, port);
166         server_socket = connect_to_host(host, port);
167     }
168
169     if (server_socket < 0) {
170         LOG(WARNING, "failed to connect to server");
171         send_forwarding_failure(client_fd_write);
172         goto out;
173     }
174     if (fdopen_read_write(server_socket, &server_fd_read,
175                                          &server_fd_write) != 0) {
176         send_forwarding_failure(client_fd_write);
177         goto out;
178     }
179
180     /* Connect to proxy if requested (command line option). */
181     if (global_proxy_host != NULL && global_proxy_port != NULL) {
182         fprintf(server_fd_write, "CONNECT %s:%s HTTP/1.0\r\n", host, port);
183         fprintf(server_fd_write, "\r\n");
184         fflush(server_fd_write);
185
186         /* Read response line from proxy server. */
187         result = read_http_request(server_fd_read, buffer, sizeof(buffer));
188         if (result == -1) {
189             LOG(WARNING, "read_http_request(): proxy read error");
190             send_forwarding_failure(client_fd_write);
191             goto out;
192         } else if (result == -2) {
193             LOG(WARNING, "read_http_request(): proxy EOF");
194             send_forwarding_failure(client_fd_write);
195             goto out;
196         }
197
198         /* Check response of proxy server. */
199         if (strncmp(buffer, "HTTP/1.0 200", 12)) {
200             LOG(WARNING, "bad proxy response: >%s<", buffer);
201             send_forwarding_failure(client_fd_write);
202             goto out;
203         }
204     }
205
206     LOG(DEBUG1, "connection to server established");
207
208     /* If the -u option is used and we don't know this hostname's server
209      * certificate then just pass through the connection and let the client
210      * verify the server certificate. */
211     if (global_passthrough_unknown) {
212         char path[TLSPROXY_MAX_PATH_LENGTH];
213         FILE *file = NULL;
214
215         if (server_certificate_file(&file, host, path, sizeof(path)) == -2) {
216             /* We've established a connection, tell the client. */
217             fprintf(client_fd_write, "HTTP/1.0 200 Connection established\r\n");
218             fprintf(client_fd_write, "\r\n");
219             fflush(client_fd_write);
220
221             LOG(DEBUG1, "transferring data");
222
223             /* Proxy data between client and server until one side is done
224              * (EOF or error). */
225             transfer_data(client_socket, server_socket);
226
227             LOG(DEBUG1, "finished transferring data");
228
229             goto out;
230         }
231         /* server_certificate_file() may have opened the file, close it. */
232         if (file != NULL) {
233             fclose(file);
234         }
235     }
236
237     /* Initialize TLS client credentials to talk to the server. */
238     result = initialize_tls_session_server(server_socket, &server_session,
239                                                           &server_x509_cred);
240     if (result != 0) {
241         LOG(WARNING, "initialize_tls_session_server() failed");
242         send_forwarding_failure(client_fd_write);
243         goto out;
244     }
245     server_session_init = 1;
246
247     LOG(DEBUG1, "starting server TLS handshake");
248
249     /* Try to establish TLS handshake between us and server. */
250     result = gnutls_handshake(server_session);
251     if (result != GNUTLS_E_SUCCESS) {
252         LOG(WARNING, "server TLS handshake failed: %s",
253                      gnutls_strerror(result));
254         send_forwarding_failure(client_fd_write);
255         goto out;
256     }
257     server_session_started = 1;
258
259     LOG(DEBUG1, "server TLS handshake finished");
260
261     if (global_log_level >= LOG_DEBUG2_LEVEL) {
262         log_session_information(server_session);
263     }
264
265     /* Make sure the server certificate is valid and known. */
266     if (verify_tls_connection(server_session, host) != 0) {
267         LOG(ERROR, "server certificate validation failed!");
268         /* We'll send the error message over our TLS connection to the client,
269          * but with an invalid certificate. No data is transfered from/to the
270          * target server. */
271         validation_failed = 1;
272     }
273
274     /* Initialize TLS server credentials to talk to the client. */
275     result = initialize_tls_session_client(client_socket,
276                                            /* use a special host if the server
277                                             * certificate was invalid */
278                                            (validation_failed) ? "invalid"
279                                                                : host,
280                                            &client_session,
281                                            &client_x509_cred);
282     if (result != 0) {
283         LOG(WARNING, "initialize_tls_session_client() failed");
284         send_forwarding_failure(client_fd_write);
285         goto out;
286     }
287     client_session_init = 1;
288
289     /* We've established a connection, tell the client. */
290     fprintf(client_fd_write, "HTTP/1.0 200 Connection established\r\n");
291     fprintf(client_fd_write, "\r\n");
292     fflush(client_fd_write);
293
294     LOG(DEBUG1, "starting client TLS handshake");
295
296     /* Try to establish TLS handshake between client and us. */
297     result = gnutls_handshake(client_session);
298     if (result != GNUTLS_E_SUCCESS) {
299         LOG(WARNING, "client TLS handshake failed: %s",
300                      gnutls_strerror(result));
301         send_forwarding_failure(client_fd_write);
302         goto out;
303     }
304     client_session_started = 1;
305
306     LOG(DEBUG1, "client TLS handshake finished");
307
308     if (global_log_level >= LOG_DEBUG2_LEVEL) {
309         log_session_information(client_session);
310     }
311
312     /* Tell the client that the verification failed. Shouldn't be necessary as
313      * the client should terminate the connection because he received the
314      * invalid certificate but better be sure. */
315     if (validation_failed) {
316         tls_send_invalid_cert_message(client_session);
317         goto out;
318     }
319
320     LOG(DEBUG1, "transferring TLS data");
321
322     /* Proxy data between client and server until one side is done (EOF or
323      * error). */
324     transfer_data_tls(client_socket, server_socket,
325                       client_session, server_session);
326
327     LOG(DEBUG1, "finished transferring TLS data");
328
329 out:
330     /* Close TLS sessions if necessary. Use GNUTLS_SHUT_RDWR so the data is
331      * reliable transmitted. */
332     if (server_session_started) {
333         /* Recent gnutls-serv (used in the test-suite) won't terminate the
334          * connection when gnutls_bye(session, GNUTLS_SHUT_RDWR) is used
335          * before any other data was received. If the validation failed just
336          * close the connection without waiting for data, we won't read it
337          * anyway.
338          *
339          * For verified connections GNUTLS_SHUT_RDWR is important or we might
340          * lose data. */
341         gnutls_bye(server_session, validation_failed ? GNUTLS_SHUT_WR
342                                                      : GNUTLS_SHUT_RDWR);
343     }
344     if (client_session_started) {
345         gnutls_bye(client_session, GNUTLS_SHUT_RDWR);
346     }
347     if (server_session_init) {
348         gnutls_deinit(server_session);
349         gnutls_certificate_free_credentials(server_x509_cred);
350     }
351     if (client_session_init) {
352         gnutls_deinit(client_session);
353         gnutls_certificate_free_credentials(client_x509_cred);
354     }
355
356     /* Close connection to server/proxy. */
357     if (server_fd_read != NULL) {
358         if (server_fd_write != NULL) {
359             fclose(server_fd_write);
360         }
361         fclose(server_fd_read);
362     } else if (server_socket != -1) {
363         close(server_socket);
364     }
365     LOG(DEBUG1, "connection to server closed");
366     /* Close connection to client. */
367     if (client_fd_read != NULL) {
368         if (client_fd_write != NULL) {
369             fclose(client_fd_write);
370         }
371         fclose(client_fd_read);
372     } else {
373         close(client_socket);
374     }
375     LOG(DEBUG1, "connection to client closed");
376
377     LOG(DEBUG1, "connection finished");
378 }
379
380
381 static int initialize_tls_session_client(int peer_socket,
382         const char *hostname,
383         gnutls_session_t *session,
384         gnutls_certificate_credentials_t *x509_cred) {
385     int result;
386     int use_invalid_cert;
387     char path[TLSPROXY_MAX_PATH_LENGTH];
388
389     /* The "invalid" hostname is special. If it's used we send an invalid
390      * certificate to let the client know something is wrong. */
391     use_invalid_cert = (!strcmp(hostname, "invalid"));
392
393     if (proxy_certificate_path(hostname, path, sizeof(path)) != 0) {
394         LOG(ERROR,
395             "initialize_tls_session_client(): "
396             "failed to get proxy certificate path");
397         return -1;
398     }
399
400     result = gnutls_certificate_allocate_credentials(x509_cred);
401     if (result != GNUTLS_E_SUCCESS) {
402         LOG(ERROR,
403             "initialize_tls_session_client(): "
404             "gnutls_certificate_allocate_credentials(): %s",
405             gnutls_strerror(result));
406         return -1;
407     }
408
409     /* Load proxy CA file, this CA "list" is send to the client. */
410     if (!use_invalid_cert) {
411         result = gnutls_certificate_set_x509_trust_file(*x509_cred,
412                                                         PROXY_CA_PATH,
413                                                         GNUTLS_X509_FMT_PEM);
414         if (result <= 0) {
415             LOG(ERROR,
416                 "initialize_tls_session_client(): can't read CA file: '%s'",
417                 PROXY_CA_PATH);
418             gnutls_certificate_free_credentials(*x509_cred);
419             return -1;
420         } else if (result != 1) {
421             /* Must contain only one CA, our proxy CA. */
422             LOG(ERROR, "initialize_tls_session_client(): multiple CAs found");
423             gnutls_certificate_free_credentials(*x509_cred);
424             return -1;
425         }
426     }
427     /* If the invalid hostname was specified do nothing, we use a self-signed
428      * certificate in this case. */
429
430     /* And certificate for this website and proxy's private key. */
431     if (!use_invalid_cert) {
432         result = gnutls_certificate_set_x509_key_file(*x509_cred,
433                                                       path,
434                                                       PROXY_KEY_PATH,
435                                                       GNUTLS_X509_FMT_PEM);
436     /* If the invalid hostname was specified load our special "invalid"
437      * certificate. */
438     } else {
439         result = gnutls_certificate_set_x509_key_file(*x509_cred,
440                                                       PROXY_INVALID_CERT_PATH,
441                                                       PROXY_KEY_PATH,
442                                                       GNUTLS_X509_FMT_PEM);
443     }
444     if (result != GNUTLS_E_SUCCESS) {
445         LOG(ERROR,
446             "initialize_tls_session_client(): "
447             "can't read server certificate ('%s') or key file ('%s'): %s",
448             path, PROXY_KEY_PATH, gnutls_strerror(result));
449         gnutls_certificate_free_credentials(*x509_cred);
450         /* Could be a missing certificate. */
451         return -2;
452     }
453
454     gnutls_certificate_set_dh_params(*x509_cred, global_tls_dh_params);
455
456     return initialize_tls_session_both(GNUTLS_SERVER,
457                                        peer_socket, session, x509_cred);
458 }
459 static int initialize_tls_session_server(int peer_socket,
460         gnutls_session_t *session,
461         gnutls_certificate_credentials_t *x509_cred) {
462     int result;
463
464     result = gnutls_certificate_allocate_credentials(x509_cred);
465     if (result != GNUTLS_E_SUCCESS) {
466         LOG(ERROR,
467             "initialize_tls_session_server(): "
468             "gnutls_certificate_allocate_credentials(): %s",
469             gnutls_strerror(result));
470         return -1;
471     }
472
473     return initialize_tls_session_both(GNUTLS_CLIENT,
474                                        peer_socket, session, x509_cred);
475 }
476 static int initialize_tls_session_both(unsigned int flags,
477         int peer_socket,
478         gnutls_session_t *session,
479         gnutls_certificate_credentials_t *x509_cred) {
480     int result;
481
482     *session = NULL;
483
484     result = gnutls_init(session, flags);
485     if (result != GNUTLS_E_SUCCESS) {
486         LOG(ERROR,
487             "initialize_tls_session_both(): gnutls_init(): %s",
488             gnutls_strerror(result));
489         goto err;
490     }
491     result = gnutls_priority_set(*session, global_tls_priority_cache);
492     if (result != GNUTLS_E_SUCCESS) {
493         LOG(ERROR,
494             "initialize_tls_session_both(): gnutls_priority_set(): %s",
495             gnutls_strerror(result));
496         goto err;
497     }
498     result = gnutls_credentials_set(*session,
499                                     GNUTLS_CRD_CERTIFICATE, *x509_cred);
500     if (result != GNUTLS_E_SUCCESS) {
501         LOG(ERROR,
502             "initialize_tls_session_both(): gnutls_credentials_set(): %s",
503             gnutls_strerror(result));
504         goto err;
505     }
506
507 #ifdef HAVE_GNUTLS_TRANSPORT_SET_INT2
508     /* gnutls_transport_set_int() is a macro. */
509     gnutls_transport_set_int(*session, peer_socket);
510 #else
511     gnutls_transport_set_ptr(*session, (gnutls_transport_ptr_t)peer_socket);
512 #endif
513
514     return 0;
515
516 err:
517     if (*session) {
518         gnutls_deinit(*session);
519     }
520     gnutls_certificate_free_credentials(*x509_cred);
521     return -1;
522 }
523
524
525 static int fdopen_read_write(int socket, FILE **read_fd, FILE **write_fd) {
526     *read_fd = fdopen(socket, "r");
527     if (*read_fd == NULL) {
528         LOG_PERROR(WARNING, "fdopen_read_write(): fdopen(\"r\") failed");
529         return -1;
530     }
531
532     *write_fd = fdopen(dup(socket), "w");
533     if (*write_fd == NULL) {
534         LOG_PERROR(WARNING, "fdopen_read_write(): fdopen(\"w\") failed");
535         fclose(*read_fd);
536         *read_fd = NULL; /* "tell" caller read_fd is already closed */
537         return -1;
538     }
539
540     return 0;
541 }
542
543 /* Read HTTP request line and headers (ignored).
544  *
545  * On success 0 is returned, -1 on client error, -2 on unexpected EOF.
546  */
547 static int read_http_request(FILE *client_fd, char *request, size_t length) {
548     char buffer[MAX_REQUEST_LINE];
549     int found_proxy_authorization;
550
551     assert(length <= INT_MAX);
552     if (fgets(request, (int)length, client_fd) == NULL) {
553         if (ferror(client_fd)) {
554             LOG_PERROR(WARNING, "read_http_request(): fgets()");
555             return -1;
556         }
557         /* EOF */
558         return -2;
559     }
560
561     found_proxy_authorization = 0;
562     while (fgets(buffer, sizeof(buffer), client_fd) != NULL) {
563         const char *authentication = "Proxy-Authorization: Basic ";
564
565         if (global_http_digest_authorization != NULL
566                 && !strncmp(buffer, authentication, strlen(authentication))) {
567             found_proxy_authorization = 1;
568
569             /* Check if the passphrase matches. */
570             strtok(buffer, "\r\n");
571             if (strcmp(buffer + strlen(authentication),
572                        global_http_digest_authorization)) {
573                 return -3;
574             }
575         }
576
577         /* End of header. */
578         if (!strcmp(buffer, "\n") || !strcmp(buffer, "\r\n")) {
579             break;
580         }
581     }
582     if (ferror(client_fd)) {
583         LOG_PERROR(WARNING, "read_http_request(): fgets()");
584         return -1;
585     } else if (feof(client_fd)) {
586         return -2;
587     }
588
589     if (global_http_digest_authorization != NULL && !found_proxy_authorization) {
590         return -3;
591     }
592
593     return 0;
594 }
595
596 static void send_bad_request(FILE *client_fd) {
597     const char error[] = "400 Bad Request";
598     const char msg[]   = "Your browser sent an invalid request.";
599     fprintf(client_fd, HTTP_RESPONSE_FORMAT, error, "", error, error, msg);
600     fflush(client_fd);
601 }
602 static void send_authentication_required(FILE *client_fd) {
603     const char error[] = "407 Proxy Authentication Required";
604     const char auth[]  = "Proxy-Authenticate: Basic realm=\"tlsproxy\"\r\n";
605     const char msg[]   = "TODO";
606     fprintf(client_fd, HTTP_RESPONSE_FORMAT, error, auth, error, error, msg);
607     fflush(client_fd);
608 }
609 static void send_forwarding_failure(FILE *client_fd) {
610     const char error[] = "503 Forwarding failure";
611     const char msg[]   = "Failed to connect to server, check logs.";
612     fprintf(client_fd, HTTP_RESPONSE_FORMAT, error, "", error, error, msg);
613     fflush(client_fd);
614 }
615 static void tls_send_invalid_cert_message(gnutls_session_t session) {
616     const char error[] = "500 Internal Server Error";
617     const char msg[]   = "Server certificate validation failed, check logs.";
618
619     int result;
620     ssize_t size_written;
621     char buffer[sizeof(HTTP_RESPONSE_FORMAT)
622                 + 3 * sizeof(error) + sizeof(msg)];
623
624     result = snprintf(buffer, sizeof(buffer), HTTP_RESPONSE_FORMAT,
625                                               error, "", error, error, msg);
626     assert(result > 0 && (size_t)result < sizeof(buffer));
627
628     size_written = gnutls_record_send(session, buffer, strlen(buffer));
629     if (size_written < 0) {
630         LOG(WARNING, "tls_send_invalid_cert_message(): "
631                      "gnutls_record_send(): %s",
632                      gnutls_strerror((int)size_written));
633     }
634     /* Just an error message, no need to check if everything was written. */
635 }
636
637
638 /* Transfer data between client and server sockets until one closes the
639  * connection. */
640 static void transfer_data(int client, int server) {
641     struct pollfd fds[2];
642     fds[0].fd      = client;
643     fds[0].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
644     fds[0].revents = 0;
645     fds[1].fd      = server;
646     fds[1].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
647     fds[1].revents = 0;
648
649     LOG(DEBUG2, "transfer_data(): %d -> %d", client, server);
650
651     for (;;) {
652         int result = poll(fds, 2 /* fd count */, -1 /* no timeout */);
653         if (result < 0) {
654             LOG_PERROR(ERROR, "transfer_data(): poll()");
655             return;
656         }
657
658         /* Data available from client. */
659         if (fds[0].revents & POLLIN || fds[0].revents & POLLPRI) {
660             if (read_from_write_to(client, server) != 0) {
661                 /* EOF (or other error) */
662                 break;
663             }
664         }
665         /* Data available from server. */
666         if (fds[1].revents & POLLIN || fds[1].revents & POLLPRI) {
667             if (read_from_write_to(server, client) != 0) {
668                 /* EOF (or other error) */
669                 break;
670             }
671         }
672
673         /* Client closed connection. */
674         if (fds[0].revents & POLLERR || fds[0].revents & POLLHUP) {
675             break;
676         }
677         /* Server closed connection. */
678         if (fds[1].revents & POLLERR || fds[1].revents & POLLHUP) {
679             break;
680         }
681     }
682 }
683
684 /* Read available data from socket from and write it to socket to. At maximum
685  * 4096 bytes are read/written. */
686 static int read_from_write_to(int from, int to) {
687     ssize_t size_read;
688     ssize_t size_written;
689     char buffer[4096];
690
691     size_read = read(from, buffer, sizeof(buffer));
692     if (size_read < 0) {
693         LOG_PERROR(WARNING, "read_from_write_to(): read()");
694         return -1;
695     /* EOF */
696     } else if (size_read == 0) {
697         return -1;
698     }
699
700     size_written = write(to, buffer, (size_t)size_read);
701     if (size_written < 0) {
702         LOG_PERROR(WARNING, "read_from_write_to(): write()");
703         return -1;
704     }
705     if (size_read != size_written) {
706         LOG(ERROR, "read_from_write_to(): only written %zu of %zu bytes!",
707                    size_written, size_read);
708         return -1;
709     }
710
711     return 0;
712 }
713
714 /* Transfer data between client and server TLS connection until one closes the
715  * connection. */
716 static void transfer_data_tls(int client, int server,
717                               gnutls_session_t client_session,
718                               gnutls_session_t server_session) {
719     size_t buffer_size;
720
721     struct pollfd fds[2];
722     fds[0].fd      = client;
723     fds[0].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
724     fds[0].revents = 0;
725     fds[1].fd      = server;
726     fds[1].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
727     fds[1].revents = 0;
728
729     /* Get maximum possible buffer size. */
730     buffer_size = gnutls_record_get_max_size(client_session);
731     if (gnutls_record_get_max_size(server_session) < buffer_size) {
732         buffer_size = gnutls_record_get_max_size(server_session);
733     }
734     LOG(DEBUG2, "transfer_data_tls(): suggested buffer size: %zu",
735                 buffer_size);
736
737     for (;;) {
738         int result = poll(fds, 2 /* fd count */, -1 /* no timeout */);
739         if (result < 0) {
740             LOG_PERROR(ERROR, "transfer_data(): poll()");
741             return;
742         }
743
744         /* Data available from client. */
745         if (fds[0].revents & POLLIN || fds[0].revents & POLLPRI) {
746             if (read_from_write_to_tls(client_session, server_session,
747                                        buffer_size) != 0) {
748                 /* EOF (or other error) */
749                 break;
750             }
751         }
752         /* Data available from server. */
753         if (fds[1].revents & POLLIN || fds[1].revents & POLLPRI) {
754             if (read_from_write_to_tls(server_session, client_session,
755                                        buffer_size) != 0) {
756                 /* EOF (or other error) */
757                 break;
758             }
759         }
760
761         /* Client closed connection. */
762         if (fds[0].revents & POLLERR || fds[0].revents & POLLHUP) {
763             break;
764         }
765         /* Server closed connection. */
766         if (fds[1].revents & POLLERR || fds[1].revents & POLLHUP) {
767             break;
768         }
769     }
770 }
771
772 /* Read available data from session from and write to session to. */
773 static int read_from_write_to_tls(gnutls_session_t from,
774                                   gnutls_session_t to,
775                                   size_t buffer_size) {
776     ssize_t size_read;
777     ssize_t size_written;
778     char buffer[16384]; /* GnuTLS default maximum */
779
780     if (buffer_size > sizeof(buffer)) {
781         LOG(WARNING, "read_from_write_to_tls(): reduced buffer size to %zu",
782                      sizeof(buffer));
783         buffer_size = sizeof(buffer);
784     }
785
786     size_read = gnutls_record_recv(from, buffer, buffer_size);
787     if (size_read < 0) {
788         /* Allow rehandshakes. As handshakes might be insecure make sure that
789          * %SAFE_RENEGOTIATION is used in GnuTLS's priority string. */
790         if (size_read == GNUTLS_E_REHANDSHAKE) {
791             int result;
792
793             LOG(DEBUG1, "server requested TLS rehandshake");
794
795             result = gnutls_handshake(from);
796             if (result != GNUTLS_E_SUCCESS) {
797                 LOG(WARNING, "server TLS rehandshake failed: %s",
798                              gnutls_strerror(result));
799                 return -1;
800             }
801             return 0;
802         }
803
804         LOG(WARNING, "read_from_write_to_tls(): gnutls_record_recv(): %s",
805                      gnutls_strerror((int)size_read));
806         return -1;
807     /* EOF */
808     } else if (size_read == 0) {
809         return -1;
810     }
811
812     size_written = gnutls_record_send(to, buffer, (size_t)size_read);
813     if (size_written < 0) {
814         LOG(WARNING, "read_from_write_to_tls(): gnutls_record_send(): %s",
815                      gnutls_strerror((int)size_written));
816         return -1;
817     }
818     if (size_read != size_written) {
819         LOG(ERROR, "read_from_write_to_tls(): only written %zu of %zu bytes!",
820                    size_written, size_read);
821         return -1;
822     }
823
824     return 0;
825 }
826
827
828 static int connect_to_host(const char *hostname, const char *port) {
829     struct addrinfo gai_hints;
830     struct addrinfo *gai_result;
831     int gai_return;
832
833     int server_socket;
834     struct addrinfo *server;
835
836     if (hostname == NULL || port == NULL) {
837         return -1;
838     }
839
840     /* Get IP of hostname server. */
841     memset(&gai_hints, 0, sizeof(gai_hints));
842     gai_hints.ai_family   = AF_UNSPEC;
843     gai_hints.ai_socktype = SOCK_STREAM;
844     gai_hints.ai_protocol = 0;
845     gai_hints.ai_flags    = AI_NUMERICSERV /* given port is numeric */
846                           | AI_ADDRCONFIG  /* supported by this computer */
847                           | AI_V4MAPPED;   /* support IPv4 through IPv6 */
848     gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result);
849     if (gai_return != 0) {
850         if (gai_return == EAI_SYSTEM) {
851             LOG_PERROR(WARNING, "connect_to_host(): getaddrinfo()");
852         } else {
853             LOG(WARNING, "connect_to_host(): getaddrinfo(): %s",
854                          gai_strerror(gai_return));
855         }
856         return -1;
857     }
858
859     /* Now try to connect to each server returned by getaddrinfo(), use the
860      * first successful connect. */
861     for (server = gai_result; server != NULL; server = server->ai_next) {
862         server_socket = socket(server->ai_family,
863                                server->ai_socktype,
864                                server->ai_protocol);
865         if (server_socket < 0) {
866             LOG_PERROR(DEBUG1, "connect_to_host(): socket(), trying next");
867             continue;
868         }
869
870         if (connect(server_socket, server->ai_addr, server->ai_addrlen) == 0) {
871             break;
872         }
873         LOG_PERROR(DEBUG1, "connect_to_host(): connect(), trying next");
874
875         close(server_socket);
876     }
877     /* Make sure we free the result from getaddrinfo(). */
878     freeaddrinfo(gai_result);
879
880     if (server == NULL) {
881         LOG_PERROR(WARNING, "connect_to_host(): no server found, abort");
882         return -1;
883     }
884
885     return server_socket;
886 }
887
888
889 /* Parse HTTP CONNECT request string and save its parameters.
890  *
891  * The following format is expected: "CONNECT host:port HTTP/1.x".
892  *
893  * request and host must have the same size! port must be at least 6 bytes
894  * long (5 + '\0').
895  */
896 static int parse_request(const char *request, char *host, char *port,
897                                               int *version_minor) {
898     int port_unused; /* just used to verify the port is numeric */
899     char *position;
900
901     /* scanf() doesn't check spaces. */
902     if (strncmp(request, "CONNECT ", 8)) {
903         return -1;
904     }
905     /* Check request and extract data, "host:port" is not yet separated. */
906     if (sscanf(request, "CONNECT %s HTTP/1.%d", host, version_minor) != 2) {
907         return -1;
908     }
909     /* Make sure ":port" is there. */
910     if ((position = strchr(host, ':')) == NULL) {
911         return -1;
912     }
913     /* Make sure port is numeric. */
914     if (sscanf(position + 1, "%d", &port_unused) != 1) {
915         return -1;
916     }
917     /* Store it in *port. */
918     strncpy(port, position + 1, 5);
919     port[5] = '\0';
920     /* And remove port from host. */
921     *position = '\0';
922
923     return 0;
924 }
925
926
927 static void log_session_information(gnutls_session_t session) {
928     /* From doc/examples/ex-session-info.c of GnuTLS 3.2.3's tarball and
929      * modified, thanks. */
930
931     const char *tmp;
932     gnutls_credentials_type_t cred;
933     gnutls_kx_algorithm_t kx;
934     int dhe, ecdh;
935
936     dhe = 0;
937     ecdh = 0;
938
939     /* Key exchange algorithm. */
940     kx = gnutls_kx_get(session);
941     LOG(DEBUG2, "- key exchange: %s", gnutls_kx_get_name(kx));
942
943     /* Authentication type. */
944     cred = gnutls_auth_get_type(session);
945     switch (cred) {
946         case GNUTLS_CRD_CERTIFICATE:
947             if (kx == GNUTLS_KX_DHE_RSA
948                     || kx == GNUTLS_KX_DHE_DSS) {
949                 dhe = 1;
950 #ifdef GNUTLS_KX_ECDHE_RSA
951             } else if (kx == GNUTLS_KX_ECDHE_RSA
952                     || kx == GNUTLS_KX_ECDHE_ECDSA) {
953                 ecdh = 1;
954 #endif
955             }
956             break;
957
958         case GNUTLS_CRD_IA:
959         case GNUTLS_CRD_SRP:
960         case GNUTLS_CRD_PSK:
961         case GNUTLS_CRD_ANON:
962         default:
963             /* This shouldn't occur. */
964             LOG(WARNING, "unexpected authentication method: %d", cred);
965             break;
966     }
967
968     /* Information about key exchange. */
969     if (dhe) {
970         LOG(DEBUG2, "- ephemeral DH using prime of %d bits",
971                     gnutls_dh_get_prime_bits(session));
972     } else if (ecdh) {
973 #ifdef GNUTLS_KX_ECDHE_RSA
974         LOG(DEBUG2, "- ephemeral ECDH using curve %s",
975                     gnutls_ecc_curve_get_name(gnutls_ecc_curve_get(session)));
976 #endif
977     }
978
979     tmp = gnutls_protocol_get_name(gnutls_protocol_get_version(session));
980     LOG(DEBUG2, "- protocol: %s", tmp); /* e.g. TLS 1.0 */
981
982     tmp = gnutls_certificate_type_get_name(gnutls_certificate_type_get(session));
983     LOG(DEBUG2, "- certificate type: %s", tmp);
984
985     tmp = gnutls_compression_get_name(gnutls_compression_get(session));
986     LOG(DEBUG2, "- compression: %s", tmp);
987
988     tmp = gnutls_cipher_get_name(gnutls_cipher_get(session));
989     LOG(DEBUG2, "- cipher: %s", tmp);
990
991     tmp = gnutls_mac_get_name(gnutls_mac_get(session));
992     LOG(DEBUG2, "- MAC: %s", tmp);
993 }