]> ruderich.org/simon Gitweb - tlsproxy/tlsproxy.git/blob - lib/tlsproxyhelper.c
Add libtlsproxyhelper.so.
[tlsproxy/tlsproxy.git] / lib / tlsproxyhelper.c
1 /*
2  * Copyright (C) 2011-2013  Simon Ruderich
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18
19 #include <config.h>
20
21 /* Necessary for RTLD_NEXT. */
22 #define _GNU_SOURCE
23
24 /* Hack to prevent problems with different declarations of connect(). */
25 #define connect connect_real
26
27 #include <arpa/inet.h>
28 #include <dlfcn.h>
29 #include <errno.h>
30 #include <poll.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37
38 #undef connect
39
40
41 #define POLL_TIMEOUT 30000 /* 30 seconds */
42 #define LOG_PREFIX "tlsproxyhelper: "
43
44 /* Load the function name using dlsym() if necessary and store it in pointer.
45  * Terminate program on failure. */
46 #define TLSPROXY_LOAD_FUNCTION(pointer, name) \
47     if ((pointer) == NULL) { \
48         char *error; \
49         dlerror(); /* Clear possibly existing error. */ \
50         \
51         *(void **) (&(pointer)) = dlsym(RTLD_NEXT, (name)); \
52         \
53         if ((error = dlerror()) != NULL) { \
54             fprintf(stderr, LOG_PREFIX "%s\n", error); \
55             exit(EXIT_FAILURE); \
56         } \
57     }
58
59
60 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
61
62 static int get_tlsproxy_port(void);
63 static int poll_for(int fd, int read);
64
65
66 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
67     static int (*real_connect)(int, const struct sockaddr *, socklen_t);
68
69     char ip[INET6_ADDRSTRLEN];
70     char port[5 + 1];
71
72     int tlsproxy_port = 0;
73
74     int non_blocking;
75     struct sockaddr *my_addr;
76     socklen_t        my_addr_len;
77     struct sockaddr_in  my_addr_ipv4;
78     struct sockaddr_in6 my_addr_ipv6;
79
80     char buffer[128]; /* response from tlsproxy */
81     ssize_t size;
82
83     TLSPROXY_LOAD_FUNCTION(real_connect, "connect");
84
85     tlsproxy_port = get_tlsproxy_port();
86
87     /* Is the socket in non-blocking mode? */
88     non_blocking = 0;
89
90     /* IPv4. */
91     if (addr->sa_family == AF_INET) {
92         struct sockaddr_in *ipv4_addr = (struct sockaddr_in *)addr;
93
94         /* Extract IP and port. */
95         snprintf(port, sizeof(port), "%u", ntohs(ipv4_addr->sin_port));
96         if (inet_ntop(AF_INET, &ipv4_addr->sin_addr, ip, sizeof(ip)) == NULL) {
97             perror(LOG_PREFIX "inet_ntop()");
98             errno = EINVAL;
99             return -1;
100         }
101
102         /* Setup connection parameters to connect to tlsproxy. */
103         memset(&my_addr_ipv4, 0, sizeof(my_addr_ipv4));
104         my_addr_ipv4.sin_family      = AF_INET;
105         my_addr_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
106         my_addr_ipv4.sin_port        = htons((uint16_t)tlsproxy_port);
107
108         my_addr     = (struct sockaddr *)&my_addr_ipv4;
109         my_addr_len = sizeof(my_addr_ipv4);
110
111     /* IPv6. */
112     } else if (addr->sa_family == AF_INET6) {
113         struct sockaddr_in6 *ipv6_addr = (struct sockaddr_in6 *)addr;
114
115         /* Extract IP and port. */
116         snprintf(port, sizeof(port), "%u", ntohs(ipv6_addr->sin6_port));
117         if (inet_ntop(AF_INET6, &ipv6_addr->sin6_addr, ip, sizeof(ip)) == NULL) {
118             perror(LOG_PREFIX "inet_ntop()");
119             errno = EINVAL;
120             return -1;
121         }
122
123         /* Setup connection parameters to connect to tlsproxy. */
124         memset(&my_addr_ipv6, 0, sizeof(my_addr_ipv6));
125         my_addr_ipv6.sin6_family = AF_INET6;
126         my_addr_ipv6.sin6_addr   = in6addr_loopback;
127         my_addr_ipv6.sin6_port   = htons((uint16_t)tlsproxy_port);
128
129         my_addr     = (struct sockaddr *)&my_addr_ipv6;
130         my_addr_len = sizeof(my_addr_ipv6);
131
132     } else {
133         fprintf(stderr, LOG_PREFIX "unknown protocol family: %d\n",
134                         addr->sa_family);
135         errno = EAFNOSUPPORT;
136         return -1;
137     }
138
139     /* Simple way to pass through DNS requests. */
140     if (!strcmp(port, "53")) {
141         return real_connect(sockfd, addr, addrlen);
142     }
143
144     /* Perform the real connect. */
145     if (real_connect(sockfd, my_addr, my_addr_len) != 0) {
146         /* Handle non-blocking sockets. */
147         if (errno == EINPROGRESS) {
148             non_blocking = 1;
149         } else {
150             perror(LOG_PREFIX "connect()");
151             /* errno is correctly set by real connect(). */
152             return -1;
153         }
154     }
155
156     /* Make sure we can write. */
157     if (non_blocking) {
158         int optval;
159         socklen_t optlen;
160
161         if (poll_for(sockfd, 0 /* write */) != 0) {
162             /* poll_for() writes the error message */
163             errno = EINVAL;
164             return -1;
165         }
166
167         /* Check if connect() was successful. */
168         optlen = sizeof(optval);
169         if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) != 0) {
170             perror(LOG_PREFIX "getsockopt()");
171             errno = EINVAL;
172             return -1;
173         }
174         if (optval != 0) {
175             fprintf(stderr, LOG_PREFIX "connect() failed (SO_ERROR)");
176             errno = EINVAL;
177             return -1;
178         }
179     }
180
181     /* Send "CONNECT ip:port HTTP/1.0" to the proxy. */
182     snprintf(buffer, sizeof(buffer),
183              "CONNECT %s:%s HTTP/1.0\r\n\r\n", ip, port);
184     size = write(sockfd, buffer, strlen(buffer));
185     if (size != (int)strlen(buffer)) {
186         perror(LOG_PREFIX "write()");
187         errno = EINVAL;
188         return -1;
189     }
190     fsync(sockfd);
191
192     /* Make sure we can read. */
193     if (non_blocking) {
194         if (poll_for(sockfd, 1 /* read */) != 0) {
195             /* poll_for() writes the error message */
196             errno = EINVAL;
197             return -1;
198         }
199     }
200
201     /* Read the response from the proxy and check if it's fine. */
202     size = read(sockfd, buffer, sizeof(buffer));
203     if (size < 0) {
204         perror(LOG_PREFIX "read()");
205         errno = EINVAL;
206         return -1;
207     }
208     if (strncmp(buffer, "HTTP/1.0 200 Connection established\r\n", 37)) {
209         fprintf(stderr, LOG_PREFIX "invalid proxy response: %s", buffer);
210         errno = EINVAL;
211         return -1;
212     }
213
214     /* connect() returns 0 on success. */
215     return 0;
216 }
217
218 static int get_tlsproxy_port(void) {
219     const char *string;
220     int port = 0;
221
222     /* Extract port from the environment variable. */
223     string = getenv("TLSPROXYHELPER_PORT");
224     if (string != NULL) {
225         port = atoi(string);
226     }
227     /* No valid port specified, use the default one. */
228     if (port <= 0 || port > 0xffff) {
229         port = 9000;
230     }
231
232     return port;
233 }
234
235 /* Poll for read (mode = 1) or write (mode = 0) possibility on file descriptor
236  * sockfd. Returns 0 on success, -1 on failure. */
237 static int poll_for(int sockfd, int mode) {
238     int result;
239     struct pollfd fds[1];
240
241     fds[0].fd      = sockfd;
242     fds[0].events  = POLLERR | POLLHUP;
243     /* Either poll for read or write possibility. */
244     if (1 == mode) {
245         fds[0].events |= POLLIN | POLLPRI;
246     } else if (0 == mode) {
247         fds[0].events |= POLLOUT;
248     } else {
249         abort();
250     }
251     fds[0].revents = 0;
252
253     result = poll(fds, 1 /* fd count */, POLL_TIMEOUT);
254     if (result < 0) {
255         perror(LOG_PREFIX "poll()");
256         return -1;
257     } else if (result == 0) {
258         fprintf(stderr, LOG_PREFIX "poll() timeout\n");
259         return -1;
260     }
261
262     if (fds[0].revents & POLLERR || fds[0].revents & POLLHUP) {
263         fprintf(stderr, LOG_PREFIX "poll(): POLLERR | POLLHUP\n");
264         return -1;
265     }
266
267     return 0;
268 }