]> ruderich.org/simon Gitweb - tlsproxy/tlsproxy.git/blob - src/connection.c
Intercept TLS connections between client and server.
[tlsproxy/tlsproxy.git] / src / connection.c
1 /*
2  * Handle connections.
3  *
4  * Copyright (C) 2011  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
23 /* close() */
24 #include <unistd.h>
25 /* getaddrinfo() */
26 #include <netdb.h>
27 /* poll() */
28 #include <poll.h>
29 /* errno */
30 #include <errno.h>
31 /* va_*() */
32 #include <stdarg.h>
33 /* pthread_*() */
34 #include <pthread.h>
35
36
37 /* Maximum line of a HTTP request line. Longer request lines are aborted with
38  * an error. The standard doesn't specify a maximum line length but this
39  * should be a good limit to make processing simpler. */
40 #define MAX_REQUEST_LINE 4096
41
42 /* Paths to necessary TLS files: the CA and the server key. */
43 #define PROXY_CA_FILE  "proxy-ca.pem"
44 #define PROXY_KEY_FILE "proxy-key.pem"
45
46 /* Helper macro for LOG/LOG_PERROR. Print file/line number if compiled with
47  * debug output. */
48 #ifdef DEBUG
49 #define LOG_PRINT_LOCATION fprintf(stdout, "%s:%-3d ", __FILE__, __LINE__);
50 #else
51 #define LOG_PRINT_LOCATION
52 #endif
53 /* Call log_message() and print current file and line number. */
54 #define LOG \
55     LOG_PRINT_LOCATION \
56     log_message
57 /* perror() replacement with debug level support. */
58 #define LOG_PERROR(level, message) \
59     LOG_PRINT_LOCATION \
60     log_message(level, "%s: %s", message, strerror(errno))
61
62
63 static int initialize_tls_session_client(int peer_socket,
64         const char *hostname,
65         gnutls_session_t *session,
66         gnutls_certificate_credentials_t *x509_cred);
67 static int initialize_tls_session_server(int peer_socket,
68         gnutls_session_t *session,
69         gnutls_certificate_credentials_t *x509_cred);
70
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_forwarding_failure(FILE *client_fd);
74
75 #if 0
76 static void transfer_data(int client, int server);
77 static int read_from_write_to(int from, int to);
78 #endif
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
84 static int connect_to_host(const char *hostname, const char *port);
85
86 static int parse_request(const char *buffer, char *host, char *port,
87                                              int *version_minor);
88
89 static void log_message(int level, const char *format, ...);
90
91
92 void handle_connection(int client_socket) {
93     int server_socket;
94     FILE *client_fd, *server_fd;
95
96     char buffer[MAX_REQUEST_LINE];
97     char host[MAX_REQUEST_LINE];
98     char port[5 + 1];
99
100     int version_minor;
101     int result;
102
103     /* client_x509_cred is used when talking to the client (acting as a TSL
104      * server), server_x509_cred is used when talking to the server (acting as
105      * a TSL client). */
106     gnutls_certificate_credentials_t client_x509_cred, server_x509_cred;
107
108     gnutls_session_t client_session, server_session;
109     /* initialize_tls_session_*() called? - used for goto out */
110     int client_session_init, server_session_init;
111     /* gnutls_handshake() called? - used for goto out */
112     int client_session_started, server_session_started;
113
114     LOG(LOG_DEBUG, "new connection");
115
116     server_socket = -1;
117     client_fd = NULL;
118     server_fd = NULL;
119     client_session_init = 0;
120     server_session_init = 0;
121     client_session_started = 0;
122     server_session_started = 0;
123
124     client_fd = fdopen(client_socket, "a+");
125     if (NULL == client_fd) {
126         LOG_PERROR(LOG_WARNING, "fdopen(): client failed");
127         goto out;
128     }
129
130     /* Read request line (CONNECT ..) and headers (they are discarded). */
131     result = read_http_request(client_fd, buffer, sizeof(buffer));
132     if (-1 == result) {
133         /* Read error. */
134         LOG(LOG_WARNING, "read_http_request(): client read error");
135         goto out;
136     } else if (-2 == result) {
137         /* EOF */
138         LOG(LOG_WARNING, "read_http_request(): client EOF");
139         send_bad_request(client_fd);
140         goto out;
141     }
142
143     if (0 != parse_request(buffer, host, port, &version_minor)) {
144         LOG(LOG_WARNING, "bad request: %s", buffer);
145         send_bad_request(client_fd);
146         goto out;
147     }
148
149     LOG(LOG_DEBUG, "target: %s:%s (HTTP 1.%d)", host, port, version_minor);
150
151     /* Connect to proxy server or directly to server. */
152     if (NULL != global_proxy_host && NULL != global_proxy_port) {
153         LOG(LOG_DEBUG, "connecting to %s:%s", global_proxy_host,
154                                               global_proxy_port);
155         server_socket = connect_to_host(global_proxy_host, global_proxy_port);
156     } else {
157         LOG(LOG_DEBUG, "connecting to %s:%s", host, port);
158         server_socket = connect_to_host(host, port);
159     }
160
161     if (-1 == server_socket) {
162         LOG(LOG_WARNING, "failed to connect to server");
163         send_forwarding_failure(client_fd);
164         goto out;
165     }
166     server_fd = fdopen(server_socket, "a+");
167     if (NULL == server_fd) {
168         LOG_PERROR(LOG_WARNING, "fdopen(): server failed");
169         send_forwarding_failure(client_fd);
170         goto out;
171     }
172
173     /* Connect to proxy if requested (command line option). */
174     if (NULL != global_proxy_host && NULL != global_proxy_port) {
175         fprintf(server_fd, "CONNECT %s:%s HTTP/1.0\r\n", host, port);
176         fprintf(server_fd, "\r\n");
177
178         /* Read response line from proxy server. */
179         result = read_http_request(server_fd, buffer, sizeof(buffer));
180         if (-1 == result) {
181             /* Read error. */
182             LOG(LOG_WARNING, "read_http_request(): proxy read error");
183             send_forwarding_failure(client_fd);
184             goto out;
185         } else if (-2 == result) {
186             /* EOF */
187             LOG(LOG_WARNING, "read_http_request(): proxy EOF");
188             send_forwarding_failure(client_fd);
189             goto out;
190         }
191
192         /* Check response of proxy server. */
193         if (0 != strncmp(buffer, "HTTP/1.0 200", 12)) {
194             LOG(LOG_WARNING, "bad proxy response: %s", buffer);
195             send_forwarding_failure(client_fd);
196             goto out;
197         }
198     }
199
200     LOG(LOG_DEBUG, "connection to server established");
201
202     /* We've established a connection, tell the client. */
203     fprintf(client_fd, "HTTP/1.0 200 Connection established\r\n");
204     fprintf(client_fd, "\r\n");
205     fflush(client_fd);
206
207     /* Initialize TLS server credentials to talk to the client. */
208     result = initialize_tls_session_client(client_socket, host,
209                                            &client_session,
210                                            &client_x509_cred);
211     if (0 != result) {
212         LOG(LOG_WARNING, "initialize_tls_session_client() failed");
213         send_forwarding_failure(client_fd);
214         goto out;
215     }
216     client_session_init = 1;
217
218     LOG(LOG_DEBUG, "starting client TLS handshake");
219
220     /* Try to establish TLS handshake between client and us. */
221     result = gnutls_handshake(client_session);
222     if (GNUTLS_E_SUCCESS != result) {
223         LOG(LOG_WARNING, "client TLS handshake failed: %s",
224                          gnutls_strerror(result));
225         send_forwarding_failure(client_fd);
226         goto out;
227     }
228     client_session_started = 1;
229
230     LOG(LOG_DEBUG, "client TLS handshake finished");
231
232     result = initialize_tls_session_server(server_socket, &server_session,
233                                                           &server_x509_cred);
234     /* Initialize TLS client credentials to talk to the server. */
235     if (0 != result) {
236         LOG(LOG_WARNING, "initialize_tls_session_server() failed");
237         send_forwarding_failure(client_fd);
238         goto out;
239     }
240     server_session_init = 1;
241
242     LOG(LOG_DEBUG, "starting server TLS handshake");
243
244     /* Try to establish TLS handshake between us and server. */
245     result = gnutls_handshake(server_session);
246     if (GNUTLS_E_SUCCESS != result) {
247         LOG(LOG_WARNING, "server TLS handshake failed: %s",
248                          gnutls_strerror(result));
249         send_forwarding_failure(client_fd);
250         goto out;
251     }
252     server_session_started = 1;
253
254     LOG(LOG_DEBUG, "server TLS handshake finished, transferring data");
255
256     /* FIXME: verify server's fingerprint */
257
258     /* Proxy data between client and server until one suite is done (EOF or
259      * error). */
260     transfer_data_tls(client_socket, server_socket,
261                       client_session, server_session);
262
263     LOG(LOG_DEBUG, "finished transferring data");
264
265 out:
266     /* Close TLS sessions if necessary. */
267     if (0 != server_session_started) {
268         gnutls_bye(server_session, GNUTLS_SHUT_WR);
269     }
270     if (0 != client_session_started) {
271         gnutls_bye(client_session, GNUTLS_SHUT_WR);
272     }
273     if (0 != server_session_init) {
274         gnutls_deinit(server_session);
275         gnutls_certificate_free_credentials(server_x509_cred);
276     }
277     if (0 != client_session_init) {
278         gnutls_deinit(client_session);
279         gnutls_certificate_free_cas(client_x509_cred);
280         gnutls_certificate_free_keys(client_x509_cred);
281         gnutls_certificate_free_credentials(client_x509_cred);
282     }
283
284     /* Close connection to server/proxy. */
285     if (NULL != server_fd) {
286         fclose(server_fd);
287     } else if (-1 != server_socket) {
288         close(server_socket);
289     }
290     LOG(LOG_DEBUG, "connection to server closed");
291     /* Close connection to client. */
292     if (NULL != client_fd) {
293         fclose(client_fd);
294     } else {
295         close(client_socket);
296     }
297     LOG(LOG_DEBUG, "connection to client closed");
298
299     LOG(LOG_DEBUG, "connection finished");
300 }
301
302
303 static int initialize_tls_session_client(int peer_socket,
304         const char *hostname,
305         gnutls_session_t *session,
306         gnutls_certificate_credentials_t *x509_cred) {
307     int result;
308     char path[1024];
309     /* The server certificate for the given hostname is stored in
310      * "./certificate-hostname-proxy.pem". */
311 #define PATH_FORMAT "./certificate-%s-proxy.pem"
312
313     /* Hostname too long. */
314     if (sizeof(path) - strlen(PATH_FORMAT) <= strlen(hostname)) {
315         LOG(LOG_WARNING,
316             "initialize_tls_session_client(): hostname too long: '%s'",
317             hostname);
318         return -1;
319     }
320     /* Try to prevent path traversals in hostnames. */
321     if (NULL != strstr(hostname, "..")) {
322         LOG(LOG_WARNING,
323             "initialize_tls_session_client(): possible path traversal: '%s'",
324             hostname);
325         return -1;
326     }
327     snprintf(path, sizeof(path), PATH_FORMAT, hostname);
328 #undef PATH_FORMAT
329
330     result = gnutls_certificate_allocate_credentials(x509_cred);
331     if (GNUTLS_E_SUCCESS != result) {
332         LOG(LOG_ERROR,
333             "initialize_tls_session_client(): \
334 gnutls_certificate_allocate_credentials(): %s",
335             gnutls_strerror(result));
336         return -1;
337     }
338
339     /* Load proxy CA file, this CA "list" is send to the client. */
340     result = gnutls_certificate_set_x509_trust_file(*x509_cred,
341                                                     PROXY_CA_FILE,
342                                                     GNUTLS_X509_FMT_PEM);
343     if (0 >= result) {
344         LOG(LOG_ERROR,
345             "initialize_tls_session_client(): can't read CA file: '%s'",
346             PROXY_CA_FILE);
347         return -1;
348     }
349     /* And certificate for this website and proxy's private key. */
350     result = gnutls_certificate_set_x509_key_file(*x509_cred,
351                                                   path, PROXY_KEY_FILE,
352                                                   GNUTLS_X509_FMT_PEM);
353     if (GNUTLS_E_SUCCESS != result) {
354         LOG(LOG_ERROR,
355             "initialize_tls_session_client(): \
356 can't read server certificate ('%s') or key file ('%s'): %s",
357             path, PROXY_KEY_FILE, gnutls_strerror(result));
358         /* Could be a missing certificate. */
359         return -2;
360     }
361
362     gnutls_certificate_set_dh_params(*x509_cred, tls_dh_params);
363
364     result = gnutls_init(session, GNUTLS_SERVER);
365     if (GNUTLS_E_SUCCESS != result) {
366         LOG(LOG_ERROR,
367             "initialize_tls_session_client(): gnutls_init(): %s",
368             gnutls_strerror(result));
369         return -1;
370     }
371     result = gnutls_priority_set(*session, tls_priority_cache);
372     if (GNUTLS_E_SUCCESS != result) {
373         LOG(LOG_ERROR,
374             "initialize_tls_session_client(): gnutls_priority_set(): %s",
375             gnutls_strerror(result));
376         return -1;
377     }
378     result = gnutls_credentials_set(*session,
379                                     GNUTLS_CRD_CERTIFICATE, *x509_cred);
380     if (GNUTLS_E_SUCCESS != result) {
381         LOG(LOG_ERROR,
382             "initialize_tls_session_client(): gnutls_credentials_set(): %s",
383             gnutls_strerror(result));
384         return -1;
385     }
386
387     gnutls_transport_set_ptr(*session, (gnutls_transport_ptr_t)peer_socket);
388
389     return 0;
390 }
391 static int initialize_tls_session_server(int peer_socket,
392         gnutls_session_t *session,
393         gnutls_certificate_credentials_t *x509_cred) {
394     int result;
395
396     result = gnutls_certificate_allocate_credentials(x509_cred);
397     if (GNUTLS_E_SUCCESS != result) {
398         LOG(LOG_ERROR,
399             "initialize_tls_session_server(): \
400 gnutls_certificate_allocate_credentials(): %s",
401             gnutls_strerror(result));
402         return -1;
403     }
404
405     result = gnutls_init(session, GNUTLS_CLIENT);
406     if (GNUTLS_E_SUCCESS != result) {
407         LOG(LOG_ERROR,
408             "initialize_tls_session_server(): gnutls_init(): %s",
409             gnutls_strerror(result));
410         return -1;
411     }
412     gnutls_priority_set(*session, tls_priority_cache);
413     if (GNUTLS_E_SUCCESS != result) {
414         LOG(LOG_ERROR,
415             "initialize_tls_session_server(): gnutls_priority_set(): %s",
416             gnutls_strerror(result));
417         return -1;
418     }
419     result = gnutls_credentials_set(*session,
420                                     GNUTLS_CRD_CERTIFICATE, *x509_cred);
421     if (GNUTLS_E_SUCCESS != result) {
422         LOG(LOG_ERROR,
423             "initialize_tls_session_server(): gnutls_credentials_set(): %s",
424             gnutls_strerror(result));
425         return -1;
426     }
427
428     gnutls_transport_set_ptr(*session, (gnutls_transport_ptr_t)peer_socket);
429
430     return 0;
431 }
432
433
434 /* Read HTTP request line and headers (ignored).
435  *
436  * On success 0 is returned, -1 on client error, -2 on unexpected EOF.
437  */
438 static int read_http_request(FILE *client_fd, char *request, size_t length) {
439     char buffer[MAX_REQUEST_LINE];
440
441     if (NULL == fgets(request, (int)length, client_fd)) {
442         if (ferror(client_fd)) {
443             LOG_PERROR(LOG_WARNING, "read_http_request(): fgets()");
444             return -1;
445         }
446
447         return -2;
448     }
449
450     while (NULL != fgets(buffer, MAX_REQUEST_LINE, client_fd)) {
451         /* End of header. */
452         if (0 == strcmp(buffer, "\n") || 0 == strcmp(buffer, "\r\n")) {
453             break;
454         }
455     }
456     if (ferror(client_fd)) {
457         LOG_PERROR(LOG_WARNING, "read_http_request(): fgets()");
458         return -1;
459     }
460
461     return 0;
462 }
463
464 static void send_bad_request(FILE *client_fd) {
465     fprintf(client_fd, "HTTP/1.0 400 Bad Request\r\n");
466     fprintf(client_fd, "\r\n");
467 }
468 static void send_forwarding_failure(FILE *client_fd) {
469     fprintf(client_fd, "HTTP/1.0 503 Forwarding failure\r\n");
470     fprintf(client_fd, "\r\n");
471 }
472
473
474 #if 0
475 /* Transfer data between client and server sockets until one closes the
476  * connection. */
477 static void transfer_data(int client, int server) {
478     struct pollfd fds[2];
479     fds[0].fd      = client;
480     fds[0].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
481     fds[0].revents = 0;
482     fds[1].fd      = server;
483     fds[1].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
484     fds[1].revents = 0;
485
486     for (;;) {
487         int result = poll(fds, 2, -1 /* no timeout */);
488         if (result < 0) {
489             LOG_PERROR(LOG_ERROR, "transfer_data(): poll()");
490             return;
491         }
492
493         /* Data available from client. */
494         if (fds[0].revents & POLLIN || fds[0].revents & POLLPRI) {
495             if (0 != read_from_write_to(client, server)) {
496                 /* EOF (or other error) */
497                 break;
498             }
499         }
500         /* Data available from server. */
501         if (fds[1].revents & POLLIN || fds[1].revents & POLLPRI) {
502             if (0 != read_from_write_to(server, client)) {
503                 /* EOF (or other error) */
504                 break;
505             }
506         }
507
508         /* Client closed connection. */
509         if (fds[0].revents & POLLERR || fds[0].revents & POLLHUP) {
510             break;
511         }
512         /* Server closed connection. */
513         if (fds[1].revents & POLLERR || fds[1].revents & POLLHUP) {
514             break;
515         }
516     }
517 }
518
519 /* Read available data from socket from and write it to socket to. At maximum
520  * 4096 bytes are read/written. */
521 static int read_from_write_to(int from, int to) {
522     ssize_t size_read;
523     ssize_t size_written;
524     char buffer[4096];
525
526     size_read = read(from, buffer, sizeof(buffer));
527     if (0 > size_read) {
528         LOG_PERROR(LOG_WARNING, "read_from_write_to(): read()");
529         return -1;
530     }
531     /* EOF */
532     if (0 == size_read) {
533         return -1;
534     }
535
536     size_written = write(to, buffer, (size_t)size_read);
537     if (0 > size_written) {
538         LOG_PERROR(LOG_WARNING, "read_from_write_to(): write()");
539         return -1;
540     }
541     if (size_read != size_written) {
542         LOG(LOG_ERROR, "read_from_write_to(): only written %ld of %ld bytes!",
543                        (long int)size_written, (long int)size_read);
544         return -1;
545     }
546
547     return 0;
548 }
549 #endif
550
551 /* Transfer data between client and server TLS connection until one closes the
552  * connection. */
553 static void transfer_data_tls(int client, int server,
554                               gnutls_session_t client_session,
555                               gnutls_session_t server_session) {
556     struct pollfd fds[2];
557     fds[0].fd      = client;
558     fds[0].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
559     fds[0].revents = 0;
560     fds[1].fd      = server;
561     fds[1].events  = POLLIN | POLLPRI | POLLHUP | POLLERR;
562     fds[1].revents = 0;
563
564     for (;;) {
565         int result = poll(fds, 2, -1 /* no timeout */);
566         if (result < 0) {
567             LOG_PERROR(LOG_ERROR, "transfer_data(): poll()");
568             return;
569         }
570
571         /* Data available from client. */
572         if (fds[0].revents & POLLIN || fds[0].revents & POLLPRI) {
573             if (0 != read_from_write_to_tls(client_session, server_session)) {
574                 /* EOF (or other error) */
575                 break;
576             }
577         }
578         /* Data available from server. */
579         if (fds[1].revents & POLLIN || fds[1].revents & POLLPRI) {
580             if (0 != read_from_write_to_tls(server_session, client_session)) {
581                 /* EOF (or other error) */
582                 break;
583             }
584         }
585
586         /* Client closed connection. */
587         if (fds[0].revents & POLLERR || fds[0].revents & POLLHUP) {
588             break;
589         }
590         /* Server closed connection. */
591         if (fds[1].revents & POLLERR || fds[1].revents & POLLHUP) {
592             break;
593         }
594     }
595 }
596
597 /* Read available data from session from and write to session to. */
598 static int read_from_write_to_tls(gnutls_session_t from,
599                                   gnutls_session_t to) {
600     size_t size;
601     ssize_t size_read;
602     ssize_t size_written;
603     char buffer[16384];
604
605     /* Get maximum possible buffer size. */
606     size = gnutls_record_get_max_size(from);
607     LOG(LOG_DEBUG, "read_from_write_to_tls(): suggested buffer size: %ld",
608                    (long int)size);
609     if (size > gnutls_record_get_max_size(to)) {
610         size = gnutls_record_get_max_size(to);
611     }
612     if (size > sizeof(buffer)) {
613         size = sizeof(buffer);
614     }
615     LOG(LOG_DEBUG, "read_from_write_to_tls(): used buffer size: %ld",
616                    (long int)size);
617
618     size_read = gnutls_record_recv(from, buffer, size);
619     if (0 > size_read) {
620         LOG(LOG_WARNING, "read_from_write_to_tls(): gnutls_record_recv(): %s",
621                          gnutls_strerror((int)size_read));
622         return -1;
623     }
624     /* EOF */
625     if (0 == size_read) {
626         return -1;
627     }
628
629     size_written = gnutls_record_send(to, buffer, (size_t)size_read);
630     if (0 > size_written) {
631         LOG(LOG_WARNING, "read_from_write_to_tls(): gnutls_record_send(): %s",
632                          gnutls_strerror((int)size_written));
633         return -1;
634     }
635     if (size_read != size_written) {
636         LOG(LOG_ERROR, "read_from_write_to_tls(): only written %ld of %ld bytes!",
637                        (long int)size_written, (long int)size_read);
638         return -1;
639     }
640
641     return 0;
642 }
643
644
645 static int connect_to_host(const char *hostname, const char *port) {
646     struct addrinfo gai_hints;
647     struct addrinfo *gai_result;
648     int gai_return;
649
650     int server_socket;
651     struct addrinfo *server;
652
653     if (NULL == hostname || NULL == port) {
654         return -1;
655     }
656
657     /* Get IP of hostname server. */
658     memset(&gai_hints, 0, sizeof(gai_hints));
659     gai_hints.ai_family   = AF_UNSPEC;
660     gai_hints.ai_socktype = SOCK_STREAM;
661     gai_hints.ai_protocol = 0;
662     gai_hints.ai_flags    = AI_NUMERICSERV /* given port is numeric */
663                           | AI_ADDRCONFIG  /* supported by this computer */
664                           | AI_V4MAPPED;   /* support IPv4 through IPv6 */
665     gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result);
666     if (0 != gai_return) {
667         LOG_PERROR(LOG_WARNING, "connect_to_host(): getaddrinfo()");
668         return -1;
669     }
670
671     /* Now try to connect to each server returned by getaddrinfo(), use the
672      * first successful connect. */
673     for (server = gai_result; NULL != server; server = server->ai_next) {
674         server_socket = socket(server->ai_family,
675                                server->ai_socktype,
676                                server->ai_protocol);
677         if (-1 == server_socket) {
678             LOG_PERROR(LOG_DEBUG, "connect_to_host(): socket(), trying next");
679             continue;
680         }
681
682         if (-1 != connect(server_socket, server->ai_addr,
683                                          server->ai_addrlen)) {
684             break;
685         }
686         LOG_PERROR(LOG_DEBUG, "connect_to_host(): connect(), trying next");
687
688         close(server_socket);
689     }
690     /* Make sure we free the result from getaddrinfo(). */
691     freeaddrinfo(gai_result);
692
693     if (NULL == server) {
694         LOG_PERROR(LOG_WARNING, "connect_to_host(): no server found, abort");
695         return -1;
696     }
697
698     return server_socket;
699 }
700
701
702 /* Parse HTTP CONNECT request string and save its parameters.
703  *
704  * The following format is expected: "CONNECT host:port HTTP/1.x".
705  *
706  * request and host must have the same size! port must be at least 6 bytes
707  * long (5 + '\0').
708  */
709 static int parse_request(const char *request, char *host, char *port,
710                                               int *version_minor) {
711     int port_unused; /* just used to verify the port is numeric */
712     char *position;
713
714     /* scanf() doesn't check spaces. */
715     if (0 != strncmp(request, "CONNECT ", 8)) {
716         return -1;
717     }
718     /* Check request and extract data, "host:port" is not yet separated. */
719     if (2 != sscanf(request, "CONNECT %s HTTP/1.%d",
720                              host, version_minor)) {
721         return -1;
722     }
723     /* Make sure ":port" is there. */
724     if (NULL == (position = strchr(host, ':'))) {
725         return -1;
726     }
727     /* Make sure port is numeric. */
728     if (1 != sscanf(position + 1, "%d", &port_unused)) {
729         return -1;
730     }
731     /* Store it in *port. */
732     strncpy(port, position + 1, 5);
733     port[5] = '\0';
734     /* And remove port from host. */
735     *position = '\0';
736
737     return 0;
738 }
739
740
741 static void log_message(int level, const char *format, ...) {
742     va_list ap;
743     const char *level_string;
744
745     if (global_log_level < level) {
746         return;
747     }
748
749     switch (level) {
750         case LOG_ERROR:   level_string = "ERROR"; break;
751         case LOG_WARNING: level_string = "WARN "; break;
752         case LOG_DEBUG:   level_string = "DEBUG"; break;
753         default:          level_string = "UNKNOWN";
754     }
755
756     va_start(ap, format);
757     fprintf(stdout, "[%s] [%d] ", level_string, (int)pthread_self());
758     vfprintf(stdout, format, ap);
759     fprintf(stdout, "\n");
760     va_end(ap);
761 }