blob: 742d41156c65e70ec34a417cf9a8ed9df3c9c56f [file] [log] [blame]
9487f7f2011-08-03 07:05:30 -07001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Dirk Vogta7d7f962016-12-01 10:50:48 +01008 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
9487f7f2011-08-03 07:05:30 -07009 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
Dirk Vogta7d7f962016-12-01 10:50:48 +010012 * are also available at https://curl.haxx.se/docs/copyright.html.
9487f7f2011-08-03 07:05:30 -070013 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
Dirk Vogta7d7f962016-12-01 10:50:48 +010023#include "curl_setup.h"
9487f7f2011-08-03 07:05:30 -070024
Dirk Vogta7d7f962016-12-01 10:50:48 +010025#if !defined(CURL_DISABLE_PROXY)
9487f7f2011-08-03 07:05:30 -070026
9487f7f2011-08-03 07:05:30 -070027#ifdef HAVE_NETINET_IN_H
28#include <netinet/in.h>
29#endif
30#ifdef HAVE_ARPA_INET_H
31#include <arpa/inet.h>
32#endif
33
34#include "urldata.h"
35#include "sendf.h"
9487f7f2011-08-03 07:05:30 -070036#include "select.h"
37#include "connect.h"
38#include "timeval.h"
39#include "socks.h"
40
41/* The last #include file should be: */
42#include "memdebug.h"
43
44/*
45 * Helper read-from-socket functions. Does the same as Curl_read() but it
46 * blocks until all bytes amount of buffersize will be read. No more, no less.
47 *
48 * This is STUPID BLOCKING behaviour which we frown upon, but right now this
49 * is what we have...
50 */
51int Curl_blockread_all(struct connectdata *conn, /* connection data */
52 curl_socket_t sockfd, /* read from this socket */
53 char *buf, /* store read data here */
54 ssize_t buffersize, /* max amount to read */
Dirk Vogta7d7f962016-12-01 10:50:48 +010055 ssize_t *n) /* amount bytes read */
9487f7f2011-08-03 07:05:30 -070056{
57 ssize_t nread;
58 ssize_t allread = 0;
59 int result;
Dirk Vogta7d7f962016-12-01 10:50:48 +010060 long timeleft;
9487f7f2011-08-03 07:05:30 -070061 *n = 0;
62 for(;;) {
Dirk Vogta7d7f962016-12-01 10:50:48 +010063 timeleft = Curl_timeleft(conn->data, NULL, TRUE);
64 if(timeleft < 0) {
9487f7f2011-08-03 07:05:30 -070065 /* we already got the timeout */
66 result = CURLE_OPERATION_TIMEDOUT;
67 break;
68 }
Dirk Vogta7d7f962016-12-01 10:50:48 +010069 if(SOCKET_READABLE(sockfd, timeleft) <= 0) {
9487f7f2011-08-03 07:05:30 -070070 result = ~CURLE_OK;
71 break;
72 }
73 result = Curl_read_plain(sockfd, buf, buffersize, &nread);
74 if(CURLE_AGAIN == result)
75 continue;
76 else if(result)
77 break;
78
79 if(buffersize == nread) {
80 allread += nread;
81 *n = allread;
82 result = CURLE_OK;
83 break;
84 }
85 if(!nread) {
86 result = ~CURLE_OK;
87 break;
88 }
89
90 buffersize -= nread;
91 buf += nread;
92 allread += nread;
93 }
94 return result;
95}
96
97/*
98* This function logs in to a SOCKS4 proxy and sends the specifics to the final
99* destination server.
100*
101* Reference :
102* http://socks.permeo.com/protocol/socks4.protocol
103*
104* Note :
105* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
106* Nonsupport "Identification Protocol (RFC1413)"
107*/
108CURLcode Curl_SOCKS4(const char *proxy_name,
109 const char *hostname,
110 int remote_port,
111 int sockindex,
112 struct connectdata *conn,
113 bool protocol4a)
114{
115#define SOCKS4REQLEN 262
116 unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
117 id */
118 int result;
119 CURLcode code;
120 curl_socket_t sock = conn->sock[sockindex];
Dirk Vogta7d7f962016-12-01 10:50:48 +0100121 struct Curl_easy *data = conn->data;
9487f7f2011-08-03 07:05:30 -0700122
Dirk Vogta7d7f962016-12-01 10:50:48 +0100123 if(Curl_timeleft(data, NULL, TRUE) < 0) {
9487f7f2011-08-03 07:05:30 -0700124 /* time-out, bail out, go home */
125 failf(data, "Connection time-out");
126 return CURLE_OPERATION_TIMEDOUT;
127 }
128
Dirk Vogta7d7f962016-12-01 10:50:48 +0100129 (void)curlx_nonblock(sock, FALSE);
130
131 infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
9487f7f2011-08-03 07:05:30 -0700132
133 /*
134 * Compose socks4 request
135 *
136 * Request format
137 *
138 * +----+----+----+----+----+----+----+----+----+----+....+----+
139 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
140 * +----+----+----+----+----+----+----+----+----+----+....+----+
141 * # of bytes: 1 1 2 4 variable 1
142 */
143
144 socksreq[0] = 4; /* version (SOCKS4) */
145 socksreq[1] = 1; /* connect */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100146 socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
147 socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
9487f7f2011-08-03 07:05:30 -0700148
149 /* DNS resolve only for SOCKS4, not SOCKS4a */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100150 if(!protocol4a) {
9487f7f2011-08-03 07:05:30 -0700151 struct Curl_dns_entry *dns;
152 Curl_addrinfo *hp=NULL;
153 int rc;
154
155 rc = Curl_resolv(conn, hostname, remote_port, &dns);
156
157 if(rc == CURLRESOLV_ERROR)
158 return CURLE_COULDNT_RESOLVE_PROXY;
159
160 if(rc == CURLRESOLV_PENDING)
161 /* ignores the return code, but 'dns' remains NULL on failure */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100162 (void)Curl_resolver_wait_resolv(conn, &dns);
9487f7f2011-08-03 07:05:30 -0700163
164 /*
165 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
166 * returns a Curl_addrinfo pointer that may not always look the same.
167 */
168 if(dns)
169 hp=dns->addr;
170 if(hp) {
171 char buf[64];
9487f7f2011-08-03 07:05:30 -0700172 Curl_printable_address(hp, buf, sizeof(buf));
173
Dirk Vogta7d7f962016-12-01 10:50:48 +0100174 if(hp->ai_family == AF_INET) {
175 struct sockaddr_in *saddr_in;
176
177 saddr_in = (struct sockaddr_in*)(void*)hp->ai_addr;
178 socksreq[4] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[0];
179 socksreq[5] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[1];
180 socksreq[6] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[2];
181 socksreq[7] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[3];
182
183 infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)\n", buf);
9487f7f2011-08-03 07:05:30 -0700184 }
Dirk Vogta7d7f962016-12-01 10:50:48 +0100185 else {
9487f7f2011-08-03 07:05:30 -0700186 hp = NULL; /* fail! */
187
Dirk Vogta7d7f962016-12-01 10:50:48 +0100188 failf(data, "SOCKS4 connection to %s not supported\n", buf);
189 }
9487f7f2011-08-03 07:05:30 -0700190
Dirk Vogta7d7f962016-12-01 10:50:48 +0100191 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
9487f7f2011-08-03 07:05:30 -0700192 }
193 if(!hp) {
194 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
195 hostname);
196 return CURLE_COULDNT_RESOLVE_HOST;
197 }
198 }
199
200 /*
201 * This is currently not supporting "Identification Protocol (RFC1413)".
202 */
203 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100204 if(proxy_name) {
205 size_t plen = strlen(proxy_name);
206 if(plen >= sizeof(socksreq) - 8) {
207 failf(data, "Too long SOCKS proxy name, can't use!\n");
208 return CURLE_COULDNT_CONNECT;
209 }
210 /* copy the proxy name WITH trailing zero */
211 memcpy(socksreq + 8, proxy_name, plen+1);
212 }
9487f7f2011-08-03 07:05:30 -0700213
214 /*
215 * Make connection
216 */
217 {
218 ssize_t actualread;
219 ssize_t written;
220 ssize_t hostnamelen = 0;
221 int packetsize = 9 +
222 (int)strlen((char*)socksreq + 8); /* size including NUL */
223
224 /* If SOCKS4a, set special invalid IP address 0.0.0.x */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100225 if(protocol4a) {
9487f7f2011-08-03 07:05:30 -0700226 socksreq[4] = 0;
227 socksreq[5] = 0;
228 socksreq[6] = 0;
229 socksreq[7] = 1;
230 /* If still enough room in buffer, also append hostname */
231 hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100232 if(packetsize + hostnamelen <= SOCKS4REQLEN)
9487f7f2011-08-03 07:05:30 -0700233 strcpy((char*)socksreq + packetsize, hostname);
234 else
235 hostnamelen = 0; /* Flag: hostname did not fit in buffer */
236 }
237
238 /* Send request */
239 code = Curl_write_plain(conn, sock, (char *)socksreq,
240 packetsize + hostnamelen,
241 &written);
Dirk Vogta7d7f962016-12-01 10:50:48 +0100242 if(code || (written != packetsize + hostnamelen)) {
9487f7f2011-08-03 07:05:30 -0700243 failf(data, "Failed to send SOCKS4 connect request.");
244 return CURLE_COULDNT_CONNECT;
245 }
Dirk Vogta7d7f962016-12-01 10:50:48 +0100246 if(protocol4a && hostnamelen == 0) {
9487f7f2011-08-03 07:05:30 -0700247 /* SOCKS4a with very long hostname - send that name separately */
248 hostnamelen = (ssize_t)strlen(hostname) + 1;
249 code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
250 &written);
Dirk Vogta7d7f962016-12-01 10:50:48 +0100251 if(code || (written != hostnamelen)) {
9487f7f2011-08-03 07:05:30 -0700252 failf(data, "Failed to send SOCKS4 connect request.");
253 return CURLE_COULDNT_CONNECT;
254 }
255 }
256
257 packetsize = 8; /* receive data size */
258
259 /* Receive response */
260 result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
Dirk Vogta7d7f962016-12-01 10:50:48 +0100261 &actualread);
262 if(result || (actualread != packetsize)) {
9487f7f2011-08-03 07:05:30 -0700263 failf(data, "Failed to receive SOCKS4 connect request ack.");
264 return CURLE_COULDNT_CONNECT;
265 }
266
267 /*
268 * Response format
269 *
270 * +----+----+----+----+----+----+----+----+
271 * | VN | CD | DSTPORT | DSTIP |
272 * +----+----+----+----+----+----+----+----+
273 * # of bytes: 1 1 2 4
274 *
275 * VN is the version of the reply code and should be 0. CD is the result
276 * code with one of the following values:
277 *
278 * 90: request granted
279 * 91: request rejected or failed
280 * 92: request rejected because SOCKS server cannot connect to
281 * identd on the client
282 * 93: request rejected because the client program and identd
283 * report different user-ids
284 */
285
286 /* wrong version ? */
287 if(socksreq[0] != 0) {
288 failf(data,
289 "SOCKS4 reply has wrong version, version should be 4.");
290 return CURLE_COULDNT_CONNECT;
291 }
292
293 /* Result */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100294 switch(socksreq[1]) {
9487f7f2011-08-03 07:05:30 -0700295 case 90:
Dirk Vogta7d7f962016-12-01 10:50:48 +0100296 infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
9487f7f2011-08-03 07:05:30 -0700297 break;
298 case 91:
299 failf(data,
300 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
301 ", request rejected or failed.",
302 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
303 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
Dirk Vogta7d7f962016-12-01 10:50:48 +0100304 (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
305 (unsigned char)socksreq[1]);
9487f7f2011-08-03 07:05:30 -0700306 return CURLE_COULDNT_CONNECT;
307 case 92:
308 failf(data,
309 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
310 ", request rejected because SOCKS server cannot connect to "
311 "identd on the client.",
312 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
313 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
Dirk Vogta7d7f962016-12-01 10:50:48 +0100314 (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
315 (unsigned char)socksreq[1]);
9487f7f2011-08-03 07:05:30 -0700316 return CURLE_COULDNT_CONNECT;
317 case 93:
318 failf(data,
319 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
320 ", request rejected because the client program and identd "
321 "report different user-ids.",
322 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
323 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
Dirk Vogta7d7f962016-12-01 10:50:48 +0100324 (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
325 (unsigned char)socksreq[1]);
9487f7f2011-08-03 07:05:30 -0700326 return CURLE_COULDNT_CONNECT;
327 default:
328 failf(data,
329 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
330 ", Unknown.",
331 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
332 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
Dirk Vogta7d7f962016-12-01 10:50:48 +0100333 (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
334 (unsigned char)socksreq[1]);
9487f7f2011-08-03 07:05:30 -0700335 return CURLE_COULDNT_CONNECT;
336 }
337 }
338
Dirk Vogta7d7f962016-12-01 10:50:48 +0100339 (void)curlx_nonblock(sock, TRUE);
9487f7f2011-08-03 07:05:30 -0700340
341 return CURLE_OK; /* Proxy was successful! */
342}
343
344/*
345 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
346 * destination server.
347 */
348CURLcode Curl_SOCKS5(const char *proxy_name,
349 const char *proxy_password,
350 const char *hostname,
351 int remote_port,
352 int sockindex,
353 struct connectdata *conn)
354{
355 /*
356 According to the RFC1928, section "6. Replies". This is what a SOCK5
357 replies:
358
359 +----+-----+-------+------+----------+----------+
360 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
361 +----+-----+-------+------+----------+----------+
362 | 1 | 1 | X'00' | 1 | Variable | 2 |
363 +----+-----+-------+------+----------+----------+
364
365 Where:
366
367 o VER protocol version: X'05'
368 o REP Reply field:
369 o X'00' succeeded
370 */
371
372 unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
373 ssize_t actualread;
374 ssize_t written;
375 int result;
376 CURLcode code;
377 curl_socket_t sock = conn->sock[sockindex];
Dirk Vogta7d7f962016-12-01 10:50:48 +0100378 struct Curl_easy *data = conn->data;
9487f7f2011-08-03 07:05:30 -0700379 long timeout;
Dirk Vogta7d7f962016-12-01 10:50:48 +0100380 bool socks5_resolve_local = (conn->proxytype == CURLPROXY_SOCKS5)?TRUE:FALSE;
9487f7f2011-08-03 07:05:30 -0700381 const size_t hostname_len = strlen(hostname);
Dirk Vogta7d7f962016-12-01 10:50:48 +0100382 ssize_t len = 0;
9487f7f2011-08-03 07:05:30 -0700383
384 /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100385 if(!socks5_resolve_local && hostname_len > 255) {
386 infof(conn->data, "SOCKS5: server resolving disabled for hostnames of "
9487f7f2011-08-03 07:05:30 -0700387 "length > 255 [actual len=%zu]\n", hostname_len);
388 socks5_resolve_local = TRUE;
389 }
390
391 /* get timeout */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100392 timeout = Curl_timeleft(data, NULL, TRUE);
9487f7f2011-08-03 07:05:30 -0700393
394 if(timeout < 0) {
395 /* time-out, bail out, go home */
396 failf(data, "Connection time-out");
397 return CURLE_OPERATION_TIMEDOUT;
398 }
399
Dirk Vogta7d7f962016-12-01 10:50:48 +0100400 (void)curlx_nonblock(sock, TRUE);
9487f7f2011-08-03 07:05:30 -0700401
402 /* wait until socket gets connected */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100403 result = SOCKET_WRITABLE(sock, timeout);
9487f7f2011-08-03 07:05:30 -0700404
405 if(-1 == result) {
406 failf(conn->data, "SOCKS5: no connection here");
407 return CURLE_COULDNT_CONNECT;
408 }
409 else if(0 == result) {
410 failf(conn->data, "SOCKS5: connection timeout");
411 return CURLE_OPERATION_TIMEDOUT;
412 }
413
414 if(result & CURL_CSELECT_ERR) {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100415 failf(conn->data, "SOCKS5: error occurred during connection");
9487f7f2011-08-03 07:05:30 -0700416 return CURLE_COULDNT_CONNECT;
417 }
418
419 socksreq[0] = 5; /* version */
420#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
421 socksreq[1] = (char)(proxy_name ? 3 : 2); /* number of methods (below) */
422 socksreq[2] = 0; /* no authentication */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100423 socksreq[3] = 1; /* GSS-API */
9487f7f2011-08-03 07:05:30 -0700424 socksreq[4] = 2; /* username/password */
425#else
426 socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
427 socksreq[2] = 0; /* no authentication */
428 socksreq[3] = 2; /* username/password */
429#endif
430
Dirk Vogta7d7f962016-12-01 10:50:48 +0100431 (void)curlx_nonblock(sock, FALSE);
432
433 infof(data, "SOCKS5 communication to %s:%d\n", hostname, remote_port);
9487f7f2011-08-03 07:05:30 -0700434
435 code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
436 &written);
Dirk Vogta7d7f962016-12-01 10:50:48 +0100437 if(code || (written != (2 + (int)socksreq[1]))) {
9487f7f2011-08-03 07:05:30 -0700438 failf(data, "Unable to send initial SOCKS5 request.");
439 return CURLE_COULDNT_CONNECT;
440 }
441
Dirk Vogta7d7f962016-12-01 10:50:48 +0100442 (void)curlx_nonblock(sock, TRUE);
9487f7f2011-08-03 07:05:30 -0700443
Dirk Vogta7d7f962016-12-01 10:50:48 +0100444 result = SOCKET_READABLE(sock, timeout);
9487f7f2011-08-03 07:05:30 -0700445
446 if(-1 == result) {
447 failf(conn->data, "SOCKS5 nothing to read");
448 return CURLE_COULDNT_CONNECT;
449 }
450 else if(0 == result) {
451 failf(conn->data, "SOCKS5 read timeout");
452 return CURLE_OPERATION_TIMEDOUT;
453 }
454
455 if(result & CURL_CSELECT_ERR) {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100456 failf(conn->data, "SOCKS5 read error occurred");
9487f7f2011-08-03 07:05:30 -0700457 return CURLE_RECV_ERROR;
458 }
459
Dirk Vogta7d7f962016-12-01 10:50:48 +0100460 (void)curlx_nonblock(sock, FALSE);
9487f7f2011-08-03 07:05:30 -0700461
Dirk Vogta7d7f962016-12-01 10:50:48 +0100462 result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
463 if(result || (actualread != 2)) {
9487f7f2011-08-03 07:05:30 -0700464 failf(data, "Unable to receive initial SOCKS5 response.");
465 return CURLE_COULDNT_CONNECT;
466 }
467
468 if(socksreq[0] != 5) {
469 failf(data, "Received invalid version in initial SOCKS5 response.");
470 return CURLE_COULDNT_CONNECT;
471 }
472 if(socksreq[1] == 0) {
473 /* Nothing to do, no authentication needed */
474 ;
475 }
476#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
477 else if(socksreq[1] == 1) {
478 code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
Dirk Vogta7d7f962016-12-01 10:50:48 +0100479 if(code) {
480 failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
9487f7f2011-08-03 07:05:30 -0700481 return CURLE_COULDNT_CONNECT;
482 }
483 }
484#endif
485 else if(socksreq[1] == 2) {
486 /* Needs user name and password */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100487 size_t proxy_name_len, proxy_password_len;
9487f7f2011-08-03 07:05:30 -0700488 if(proxy_name && proxy_password) {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100489 proxy_name_len = strlen(proxy_name);
490 proxy_password_len = strlen(proxy_password);
9487f7f2011-08-03 07:05:30 -0700491 }
492 else {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100493 proxy_name_len = 0;
494 proxy_password_len = 0;
9487f7f2011-08-03 07:05:30 -0700495 }
496
497 /* username/password request looks like
498 * +----+------+----------+------+----------+
499 * |VER | ULEN | UNAME | PLEN | PASSWD |
500 * +----+------+----------+------+----------+
501 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
502 * +----+------+----------+------+----------+
503 */
504 len = 0;
505 socksreq[len++] = 1; /* username/pw subnegotiation version */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100506 socksreq[len++] = (unsigned char) proxy_name_len;
507 if(proxy_name && proxy_name_len)
508 memcpy(socksreq + len, proxy_name, proxy_name_len);
509 len += proxy_name_len;
510 socksreq[len++] = (unsigned char) proxy_password_len;
511 if(proxy_password && proxy_password_len)
512 memcpy(socksreq + len, proxy_password, proxy_password_len);
513 len += proxy_password_len;
9487f7f2011-08-03 07:05:30 -0700514
515 code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
Dirk Vogta7d7f962016-12-01 10:50:48 +0100516 if(code || (len != written)) {
9487f7f2011-08-03 07:05:30 -0700517 failf(data, "Failed to send SOCKS5 sub-negotiation request.");
518 return CURLE_COULDNT_CONNECT;
519 }
520
Dirk Vogta7d7f962016-12-01 10:50:48 +0100521 result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
522 if(result || (actualread != 2)) {
9487f7f2011-08-03 07:05:30 -0700523 failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
524 return CURLE_COULDNT_CONNECT;
525 }
526
527 /* ignore the first (VER) byte */
528 if(socksreq[1] != 0) { /* status */
529 failf(data, "User was rejected by the SOCKS5 server (%d %d).",
530 socksreq[0], socksreq[1]);
531 return CURLE_COULDNT_CONNECT;
532 }
533
534 /* Everything is good so far, user was authenticated! */
535 }
536 else {
537 /* error */
538#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
539 if(socksreq[1] == 255) {
540#else
541 if(socksreq[1] == 1) {
542 failf(data,
543 "SOCKS5 GSSAPI per-message authentication is not supported.");
544 return CURLE_COULDNT_CONNECT;
545 }
546 else if(socksreq[1] == 255) {
547#endif
548 if(!proxy_name || !*proxy_name) {
549 failf(data,
550 "No authentication method was acceptable. (It is quite likely"
551 " that the SOCKS5 server wanted a username/password, since none"
552 " was supplied to the server on this connection.)");
553 }
554 else {
555 failf(data, "No authentication method was acceptable.");
556 }
557 return CURLE_COULDNT_CONNECT;
558 }
559 else {
560 failf(data,
561 "Undocumented SOCKS5 mode attempted to be used by server.");
562 return CURLE_COULDNT_CONNECT;
563 }
564 }
565
566 /* Authentication is complete, now specify destination to the proxy */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100567 len = 0;
568 socksreq[len++] = 5; /* version (SOCKS5) */
569 socksreq[len++] = 1; /* connect */
570 socksreq[len++] = 0; /* must be zero */
9487f7f2011-08-03 07:05:30 -0700571
572 if(!socks5_resolve_local) {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100573 socksreq[len++] = 3; /* ATYP: domain name = 3 */
574 socksreq[len++] = (char) hostname_len; /* address length */
575 memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
576 len += hostname_len;
9487f7f2011-08-03 07:05:30 -0700577 }
578 else {
579 struct Curl_dns_entry *dns;
Dirk Vogta7d7f962016-12-01 10:50:48 +0100580 Curl_addrinfo *hp = NULL;
9487f7f2011-08-03 07:05:30 -0700581 int rc = Curl_resolv(conn, hostname, remote_port, &dns);
582
9487f7f2011-08-03 07:05:30 -0700583 if(rc == CURLRESOLV_ERROR)
584 return CURLE_COULDNT_RESOLVE_HOST;
585
586 if(rc == CURLRESOLV_PENDING) {
587 /* this requires that we're in "wait for resolve" state */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100588 code = Curl_resolver_wait_resolv(conn, &dns);
589 if(code)
9487f7f2011-08-03 07:05:30 -0700590 return code;
591 }
592
593 /*
594 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
595 * returns a Curl_addrinfo pointer that may not always look the same.
596 */
597 if(dns)
598 hp=dns->addr;
599 if(hp) {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100600 int i;
9487f7f2011-08-03 07:05:30 -0700601 char buf[64];
9487f7f2011-08-03 07:05:30 -0700602 Curl_printable_address(hp, buf, sizeof(buf));
603
Dirk Vogta7d7f962016-12-01 10:50:48 +0100604 if(hp->ai_family == AF_INET) {
605 struct sockaddr_in *saddr_in;
606 socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
607
608 saddr_in = (struct sockaddr_in*)(void*)hp->ai_addr;
609 for(i = 0; i < 4; i++) {
610 socksreq[len++] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[i];
611 }
612
613 infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", buf);
9487f7f2011-08-03 07:05:30 -0700614 }
Dirk Vogta7d7f962016-12-01 10:50:48 +0100615#ifdef ENABLE_IPV6
616 else if(hp->ai_family == AF_INET6) {
617 struct sockaddr_in6 *saddr_in6;
618 socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
619
620 saddr_in6 = (struct sockaddr_in6*)(void*)hp->ai_addr;
621 for(i = 0; i < 16; i++) {
622 socksreq[len++] = ((unsigned char*)&saddr_in6->sin6_addr.s6_addr)[i];
623 }
624
625 infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", buf);
626 }
627#endif
628 else {
9487f7f2011-08-03 07:05:30 -0700629 hp = NULL; /* fail! */
630
Dirk Vogta7d7f962016-12-01 10:50:48 +0100631 failf(data, "SOCKS5 connection to %s not supported\n", buf);
632 }
633
9487f7f2011-08-03 07:05:30 -0700634 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
635 }
636 if(!hp) {
637 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
638 hostname);
639 return CURLE_COULDNT_RESOLVE_HOST;
640 }
9487f7f2011-08-03 07:05:30 -0700641 }
642
Dirk Vogta7d7f962016-12-01 10:50:48 +0100643 socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
644 socksreq[len++] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
645
9487f7f2011-08-03 07:05:30 -0700646#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
647 if(conn->socks5_gssapi_enctype) {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100648 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
649 }
650 else
9487f7f2011-08-03 07:05:30 -0700651#endif
Dirk Vogta7d7f962016-12-01 10:50:48 +0100652 code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
653
654 if(code || (len != written)) {
9487f7f2011-08-03 07:05:30 -0700655 failf(data, "Failed to send SOCKS5 connect request.");
656 return CURLE_COULDNT_CONNECT;
657 }
658
Dirk Vogta7d7f962016-12-01 10:50:48 +0100659 len = 10; /* minimum packet size is 10 */
9487f7f2011-08-03 07:05:30 -0700660
661#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
662 if(conn->socks5_gssapi_enctype) {
Dirk Vogta7d7f962016-12-01 10:50:48 +0100663 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
664 }
665 else
9487f7f2011-08-03 07:05:30 -0700666#endif
Dirk Vogta7d7f962016-12-01 10:50:48 +0100667 result = Curl_blockread_all(conn, sock, (char *)socksreq,
668 len, &actualread);
669
670 if(result || (len != actualread)) {
9487f7f2011-08-03 07:05:30 -0700671 failf(data, "Failed to receive SOCKS5 connect request ack.");
672 return CURLE_COULDNT_CONNECT;
673 }
674
675 if(socksreq[0] != 5) { /* version */
676 failf(data,
677 "SOCKS5 reply has wrong version, version should be 5.");
678 return CURLE_COULDNT_CONNECT;
679 }
9487f7f2011-08-03 07:05:30 -0700680
681 /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
682 1928, so the reply packet should be read until the end to avoid errors at
683 subsequent protocol level.
684
685 +----+-----+-------+------+----------+----------+
686 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
687 +----+-----+-------+------+----------+----------+
688 | 1 | 1 | X'00' | 1 | Variable | 2 |
689 +----+-----+-------+------+----------+----------+
690
691 ATYP:
692 o IP v4 address: X'01', BND.ADDR = 4 byte
693 o domain name: X'03', BND.ADDR = [ 1 byte length, string ]
694 o IP v6 address: X'04', BND.ADDR = 16 byte
695 */
696
697 /* Calculate real packet size */
698 if(socksreq[3] == 3) {
699 /* domain name */
700 int addrlen = (int) socksreq[4];
Dirk Vogta7d7f962016-12-01 10:50:48 +0100701 len = 5 + addrlen + 2;
9487f7f2011-08-03 07:05:30 -0700702 }
703 else if(socksreq[3] == 4) {
704 /* IPv6 */
Dirk Vogta7d7f962016-12-01 10:50:48 +0100705 len = 4 + 16 + 2;
9487f7f2011-08-03 07:05:30 -0700706 }
707
708 /* At this point we already read first 10 bytes */
709#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
710 if(!conn->socks5_gssapi_enctype) {
711 /* decrypt_gssapi_blockread already read the whole packet */
712#endif
Dirk Vogta7d7f962016-12-01 10:50:48 +0100713 if(len > 10) {
9487f7f2011-08-03 07:05:30 -0700714 result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
Dirk Vogta7d7f962016-12-01 10:50:48 +0100715 len - 10, &actualread);
716 if(result || ((len - 10) != actualread)) {
9487f7f2011-08-03 07:05:30 -0700717 failf(data, "Failed to receive SOCKS5 connect request ack.");
718 return CURLE_COULDNT_CONNECT;
719 }
720 }
721#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
722 }
723#endif
724
Dirk Vogta7d7f962016-12-01 10:50:48 +0100725 if(socksreq[1] != 0) { /* Anything besides 0 is an error */
726 if(socksreq[3] == 1) {
727 failf(data,
728 "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
729 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
730 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
731 (((unsigned char)socksreq[8] << 8) |
732 (unsigned char)socksreq[9]),
733 (unsigned char)socksreq[1]);
734 }
735 else if(socksreq[3] == 3) {
736 unsigned char port_upper = (unsigned char)socksreq[len - 2];
737 socksreq[len - 2] = 0;
738 failf(data,
739 "Can't complete SOCKS5 connection to %s:%d. (%d)",
740 (char *)&socksreq[5],
741 ((port_upper << 8) |
742 (unsigned char)socksreq[len - 1]),
743 (unsigned char)socksreq[1]);
744 socksreq[len - 2] = port_upper;
745 }
746 else if(socksreq[3] == 4) {
747 failf(data,
748 "Can't complete SOCKS5 connection to %02x%02x:%02x%02x:"
749 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d. (%d)",
750 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
751 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
752 (unsigned char)socksreq[8], (unsigned char)socksreq[9],
753 (unsigned char)socksreq[10], (unsigned char)socksreq[11],
754 (unsigned char)socksreq[12], (unsigned char)socksreq[13],
755 (unsigned char)socksreq[14], (unsigned char)socksreq[15],
756 (unsigned char)socksreq[16], (unsigned char)socksreq[17],
757 (unsigned char)socksreq[18], (unsigned char)socksreq[19],
758 (((unsigned char)socksreq[20] << 8) |
759 (unsigned char)socksreq[21]),
760 (unsigned char)socksreq[1]);
761 }
762 return CURLE_COULDNT_CONNECT;
763 }
764 else {
765 infof(data, "SOCKS5 request granted.\n");
766 }
767
768 (void)curlx_nonblock(sock, TRUE);
9487f7f2011-08-03 07:05:30 -0700769 return CURLE_OK; /* Proxy was successful! */
770}
771
772#endif /* CURL_DISABLE_PROXY */
773