2 * Simple LD_PRELOAD wrapper for connect() which uses tlsproxy as proxy for
3 * programs which don't support setting a TLS proxy.
5 * The following environment variables can be used:
7 * - TLSPROXYHELPER_PORT: port where tlsproxy is running (default 9000)
9 * If an error occurs -1 is returned and errno is set (either EAFNOSUPPORT if
10 * another protocol besides IPv4/IPv6 was used, or EINVAL if another error
11 * occurred). The error is written to stderr with "tlsproxyhelper:" as prefix.
15 * Copyright (C) 2011-2013 Simon Ruderich
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /* Necessary for RTLD_NEXT. */
37 /* Hack to prevent problems with different declarations of connect(). */
38 #define connect connect_real
40 #include <arpa/inet.h>
47 #include <sys/socket.h>
48 #include <sys/types.h>
54 #define POLL_TIMEOUT 30000 /* 30 seconds */
55 #define LOG_PREFIX "tlsproxyhelper: "
57 /* Load the function name using dlsym() if necessary and store it in pointer.
58 * Terminate program on failure. */
59 #define TLSPROXY_LOAD_FUNCTION(pointer, name) \
60 if ((pointer) == NULL) { \
62 dlerror(); /* Clear possibly existing error. */ \
64 *(void **) (&(pointer)) = dlsym(RTLD_NEXT, (name)); \
66 if ((error = dlerror()) != NULL) { \
67 fprintf(stderr, LOG_PREFIX "%s\n", error); \
73 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
75 static int get_tlsproxy_port(void);
76 static int poll_for(int fd, int read);
79 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
80 static int (*real_connect)(int, const struct sockaddr *, socklen_t);
82 char ip[INET6_ADDRSTRLEN];
85 int tlsproxy_port = 0;
88 struct sockaddr *my_addr;
89 socklen_t my_addr_len;
90 struct sockaddr_in my_addr_ipv4;
91 struct sockaddr_in6 my_addr_ipv6;
93 char buffer[128]; /* response from tlsproxy */
96 TLSPROXY_LOAD_FUNCTION(real_connect, "connect");
98 tlsproxy_port = get_tlsproxy_port();
100 /* Is the socket in non-blocking mode? */
104 if (addr->sa_family == AF_INET) {
105 struct sockaddr_in *ipv4_addr = (struct sockaddr_in *)addr;
107 /* Extract IP and port. */
108 snprintf(port, sizeof(port), "%u", ntohs(ipv4_addr->sin_port));
109 if (inet_ntop(AF_INET, &ipv4_addr->sin_addr, ip, sizeof(ip)) == NULL) {
110 perror(LOG_PREFIX "inet_ntop()");
115 /* Setup connection parameters to connect to tlsproxy. */
116 memset(&my_addr_ipv4, 0, sizeof(my_addr_ipv4));
117 my_addr_ipv4.sin_family = AF_INET;
118 my_addr_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
119 my_addr_ipv4.sin_port = htons((uint16_t)tlsproxy_port);
121 my_addr = (struct sockaddr *)&my_addr_ipv4;
122 my_addr_len = sizeof(my_addr_ipv4);
125 } else if (addr->sa_family == AF_INET6) {
126 struct sockaddr_in6 *ipv6_addr = (struct sockaddr_in6 *)addr;
128 /* Extract IP and port. */
129 snprintf(port, sizeof(port), "%u", ntohs(ipv6_addr->sin6_port));
130 if (inet_ntop(AF_INET6, &ipv6_addr->sin6_addr, ip, sizeof(ip)) == NULL) {
131 perror(LOG_PREFIX "inet_ntop()");
136 /* Setup connection parameters to connect to tlsproxy. */
137 memset(&my_addr_ipv6, 0, sizeof(my_addr_ipv6));
138 my_addr_ipv6.sin6_family = AF_INET6;
139 my_addr_ipv6.sin6_addr = in6addr_loopback;
140 my_addr_ipv6.sin6_port = htons((uint16_t)tlsproxy_port);
142 my_addr = (struct sockaddr *)&my_addr_ipv6;
143 my_addr_len = sizeof(my_addr_ipv6);
146 fprintf(stderr, LOG_PREFIX "unknown protocol family: %d\n",
148 errno = EAFNOSUPPORT;
152 /* Simple way to pass through DNS requests. */
153 if (!strcmp(port, "53")) {
154 return real_connect(sockfd, addr, addrlen);
157 /* Perform the real connect. */
158 if (real_connect(sockfd, my_addr, my_addr_len) != 0) {
159 /* Handle non-blocking sockets. */
160 if (errno == EINPROGRESS) {
163 perror(LOG_PREFIX "connect()");
164 /* errno is correctly set by real connect(). */
169 /* Make sure we can write. */
174 if (poll_for(sockfd, 0 /* write */) != 0) {
175 /* poll_for() writes the error message */
180 /* Check if connect() was successful. */
181 optlen = sizeof(optval);
182 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) != 0) {
183 perror(LOG_PREFIX "getsockopt()");
188 fprintf(stderr, LOG_PREFIX "connect() failed (SO_ERROR)\n");
194 /* Send "CONNECT ip:port HTTP/1.0" to the proxy. */
195 snprintf(buffer, sizeof(buffer),
196 "CONNECT %s:%s HTTP/1.0\r\n\r\n", ip, port);
197 size = write(sockfd, buffer, strlen(buffer));
198 if (size != (int)strlen(buffer)) {
199 perror(LOG_PREFIX "write()");
205 /* Make sure we can read. */
207 if (poll_for(sockfd, 1 /* read */) != 0) {
208 /* poll_for() writes the error message */
214 /* Read the response from the proxy and check if it's fine. */
215 size = read(sockfd, buffer, sizeof(buffer));
217 perror(LOG_PREFIX "read()");
221 if (strncmp(buffer, "HTTP/1.0 200 Connection established\r\n", 37)) {
222 fprintf(stderr, LOG_PREFIX "invalid proxy response: %s", buffer);
227 /* connect() returns 0 on success. */
231 static int get_tlsproxy_port(void) {
235 /* Extract port from the environment variable. */
236 string = getenv("TLSPROXYHELPER_PORT");
237 if (string != NULL) {
240 /* No valid port specified, use the default one. */
241 if (port <= 0 || port > 0xffff) {
248 /* Poll for read (mode = 1) or write (mode = 0) possibility on file descriptor
249 * sockfd. Returns 0 on success, -1 on failure. */
250 static int poll_for(int sockfd, int mode) {
252 struct pollfd fds[1];
255 fds[0].events = POLLERR | POLLHUP;
256 /* Either poll for read or write possibility. */
258 fds[0].events |= POLLIN | POLLPRI;
259 } else if (0 == mode) {
260 fds[0].events |= POLLOUT;
266 result = poll(fds, 1 /* fd count */, POLL_TIMEOUT);
268 perror(LOG_PREFIX "poll()");
270 } else if (result == 0) {
271 fprintf(stderr, LOG_PREFIX "poll() timeout\n");
275 if (fds[0].revents & POLLERR) {
276 fprintf(stderr, LOG_PREFIX "poll(): POLLERR\n");
279 if (fds[0].revents & POLLHUP) {
280 fprintf(stderr, LOG_PREFIX "poll(): POLLHUP\n");