summaryrefslogtreecommitdiff
blob: 256a16ffd5ba065bb200ccdccab68de54468f64e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
From fdd93b1e9545b66d1b3a2a1ec24d4c8613ee43fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@gentoo.org>
Date: Sat, 24 Jan 2015 23:59:21 +0100
Subject: [PATCH] Support SOCKSv5 proxy

Support using a SOCKSv5 proxy specified as DISTCC_SOCKS_PROXY.
The variable can either a hostname, a host:port pair or an absolute path
to a UNIX socket. When SOCKS is used, the hostname is passed to
the proxy and therefore the name resolution is done remotely.
---
 src/clinet.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 135 insertions(+), 2 deletions(-)

diff --git a/src/clinet.c b/src/clinet.c
index 010a884..4773d8b 100644
--- a/src/clinet.c
+++ b/src/clinet.c
@@ -31,6 +31,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stddef.h>
 #include <unistd.h>
 #include <string.h>
 #include <fcntl.h>
@@ -42,8 +43,10 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#include <arpa/inet.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <sys/un.h>
 
 #include <netdb.h>
 
@@ -161,7 +164,7 @@ out_failed:
 /**
  * Open a socket to a tcp remote host with the specified port.
  **/
-int dcc_connect_by_name(const char *host, int port, int *p_fd)
+static int dcc_connect_by_name_real(const char *host, int port, int *p_fd)
 {
     struct addrinfo hints;
     struct addrinfo *res;
@@ -201,7 +204,7 @@ int dcc_connect_by_name(const char *host, int port, int *p_fd)
  *
  * @todo Don't try for too long to connect.
  **/
-int dcc_connect_by_name(const char *host, int port, int *p_fd)
+static int dcc_connect_by_name_real(const char *host, int port, int *p_fd)
 {
     struct sockaddr_in sock_out;
     struct hostent *hp;
@@ -224,3 +227,133 @@ int dcc_connect_by_name(const char *host, int port, int *p_fd)
 }
 
 #endif /* not ENABLE_RFC2553 */
+
+static int dcc_connect_via_socks5(const char *host, int port, int *p_fd, const char *proxy)
+{
+	int ret;
+	char *proxy_host, *proxy_it;
+	int proxy_port;
+	char buf[262];
+	int host_length;
+	struct sockaddr_in addr_buf;
+	int skip_bytes;
+
+	host_length = strlen(host);
+	if (host_length > 255) {
+		rs_log_error("hostname \"%s\" too long for SOCKSv5 (over 255 chars)", host);
+		return EXIT_CONNECT_FAILED;
+	}
+
+	if (proxy[0] == '/') { /* UNIX socket */
+		struct sockaddr_un unix_addr;
+
+		if (strlen(proxy) >= sizeof(unix_addr.sun_path))
+		{
+			rs_log_error("UNIX socket path \"%s\" too long", proxy);
+			return EXIT_CONNECT_FAILED;
+		}
+
+		unix_addr.sun_family = AF_UNIX;
+		strcpy(unix_addr.sun_path, proxy);
+
+		ret = dcc_connect_by_addr((struct sockaddr *) &unix_addr,
+				offsetof(struct sockaddr_un, sun_path) + strlen(proxy) + 1,
+				p_fd);
+
+	} else { /* hostname? IP address? */
+		proxy_host = strdup(proxy);
+		if (proxy_host == NULL) return EXIT_OUT_OF_MEMORY;
+
+		proxy_it = strrchr(proxy_host, ':');
+		if (proxy_it) {
+			*(proxy_it++) = 0;
+			proxy_port = atoi(proxy_it);
+
+			if (proxy_port <= 0) {
+				rs_log_error("invalid proxy port \"%s\"", proxy_it);
+				free(proxy_host);
+				return EXIT_CONNECT_FAILED;
+			}
+		}
+		else
+			proxy_port = 1080;
+
+		ret = dcc_connect_by_name_real(proxy_host, proxy_port, p_fd);
+		free(proxy_host);
+	}
+
+	if (ret != 0)
+		return ret;
+
+	/* connected to proxy, now identifier/method selection */
+	buf[0] = 0x05; /* SOCKSv5 */
+	buf[1] = 0x01; /* one method */
+	buf[2] = 0x00; /* NO AUTHENTICATION REQUIRED */
+	ret = dcc_writex(*p_fd, buf, 3);
+	if (ret != 0)
+		return ret;
+
+	/* wait for method selection */
+	ret = dcc_readx(*p_fd, buf, 2);
+	if (ret != 0)
+		return ret;
+	if (buf[0] != 0x05 || buf[1] != 0x00) { /* version, method */
+		rs_log_error("invalid proxy reply (version 0x%02x, method 0x%02x)",
+				buf[0], buf[1]);
+		return EXIT_CONNECT_FAILED;
+	}
+
+	/* send connect request */
+	buf[0] = 0x05; /* SOCKSv5 */
+	buf[1] = 0x01; /* CONNECT command */
+	buf[2] = 0x00; /* reserved */
+	buf[3] = 0x03; /* DOMAINNAME address type */
+	buf[4] = host_length;
+	memcpy(&buf[5], host, host_length);
+	addr_buf.sin_port = htons(port);
+	memcpy(&buf[5 + host_length], &addr_buf.sin_port, 2);
+	ret = dcc_writex(*p_fd, buf, 7 + host_length);
+	if (ret != 0)
+		return ret;
+
+	/* wait for the connection */
+	/* read first 4 bytes of reply + 2 extra bytes we know will be there */
+	ret = dcc_readx(*p_fd, buf, 6);
+	if (ret != 0)
+		return ret;
+	if (buf[0] != 0x05 || buf[2] != 0x00) { /* version, reserved */
+		rs_log_error("invalid proxy reply (version 0x%02x, reserved 0x%02x)",
+				buf[0], buf[2]);
+		return EXIT_CONNECT_FAILED;
+	}
+	if (buf[1] != 0x00) { /* reply */
+		rs_log_error("proxy connection failed, reason=0x%02x", buf[1]);
+		return EXIT_CONNECT_FAILED;
+	}
+
+	/* now read the remaining (packet size - 6) bytes */
+	switch (buf[3]) { /* address type */
+		case 0x01: skip_bytes = 4; break; /* IPv4 */
+		case 0x03: skip_bytes = buf[4] + 1; break; /* hostname with length field */
+		case 0x04: skip_bytes = 16; break; /* IPv6 */
+		default:
+		   rs_log_error("invalid proxy reply (address type 0x%02x)", buf[3]);
+		   return EXIT_CONNECT_FAILED;
+	}
+	ret = dcc_readx(*p_fd, buf, skip_bytes);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
+
+int dcc_connect_by_name(const char *host, int port, int *p_fd)
+{
+	const char *proxy;
+
+	proxy = getenv("DISTCC_SOCKS_PROXY");
+	if (proxy)
+		return dcc_connect_via_socks5(host, port, p_fd, proxy);
+	else
+		return dcc_connect_by_name_real(host, port, p_fd);
+}
-- 
2.3.0