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