Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/socks.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. | |
| 9 * | |
| 10 * This software is licensed as described in the file COPYING, which | |
| 11 * you should have received as part of this distribution. The terms | |
| 12 * are also available at https://curl.haxx.se/docs/copyright.html. | |
| 13 * | |
| 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 | |
| 23 #include "curl_setup.h" | |
| 24 | |
| 25 #if !defined(CURL_DISABLE_PROXY) | |
| 26 | |
| 27 #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" | |
| 36 #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 */ | |
| 51 int 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 */ | |
| 55 ssize_t *n) /* amount bytes read */ | |
| 56 { | |
| 57 ssize_t nread = 0; | |
| 58 ssize_t allread = 0; | |
| 59 int result; | |
| 60 *n = 0; | |
| 61 for(;;) { | |
| 62 timediff_t timeleft = Curl_timeleft(conn->data, NULL, TRUE); | |
| 63 if(timeleft < 0) { | |
| 64 /* we already got the timeout */ | |
| 65 result = CURLE_OPERATION_TIMEDOUT; | |
| 66 break; | |
| 67 } | |
| 68 if(SOCKET_READABLE(sockfd, timeleft) <= 0) { | |
| 69 result = ~CURLE_OK; | |
| 70 break; | |
| 71 } | |
| 72 result = Curl_read_plain(sockfd, buf, buffersize, &nread); | |
| 73 if(CURLE_AGAIN == result) | |
| 74 continue; | |
| 75 if(result) | |
| 76 break; | |
| 77 | |
| 78 if(buffersize == nread) { | |
| 79 allread += nread; | |
| 80 *n = allread; | |
| 81 result = CURLE_OK; | |
| 82 break; | |
| 83 } | |
| 84 if(!nread) { | |
| 85 result = ~CURLE_OK; | |
| 86 break; | |
| 87 } | |
| 88 | |
| 89 buffersize -= nread; | |
| 90 buf += nread; | |
| 91 allread += nread; | |
| 92 } | |
| 93 return result; | |
| 94 } | |
| 95 | |
| 96 /* | |
| 97 * This function logs in to a SOCKS4 proxy and sends the specifics to the final | |
| 98 * destination server. | |
| 99 * | |
| 100 * Reference : | |
| 101 * https://www.openssh.com/txt/socks4.protocol | |
| 102 * | |
| 103 * Note : | |
| 104 * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" | |
| 105 * Nonsupport "Identification Protocol (RFC1413)" | |
| 106 */ | |
| 107 CURLcode Curl_SOCKS4(const char *proxy_user, | |
| 108 const char *hostname, | |
| 109 int remote_port, | |
| 110 int sockindex, | |
| 111 struct connectdata *conn) | |
| 112 { | |
| 113 const bool protocol4a = | |
| 114 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; | |
| 115 #define SOCKS4REQLEN 262 | |
| 116 unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user | |
| 117 id */ | |
| 118 CURLcode code; | |
| 119 curl_socket_t sock = conn->sock[sockindex]; | |
| 120 struct Curl_easy *data = conn->data; | |
| 121 | |
| 122 if(Curl_timeleft(data, NULL, TRUE) < 0) { | |
| 123 /* time-out, bail out, go home */ | |
| 124 failf(data, "Connection time-out"); | |
| 125 return CURLE_OPERATION_TIMEDOUT; | |
| 126 } | |
| 127 | |
| 128 if(conn->bits.httpproxy) | |
| 129 infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n", | |
| 130 protocol4a ? "a" : "", hostname, remote_port); | |
| 131 | |
| 132 (void)curlx_nonblock(sock, FALSE); | |
| 133 | |
| 134 infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port); | |
| 135 | |
| 136 /* | |
| 137 * Compose socks4 request | |
| 138 * | |
| 139 * Request format | |
| 140 * | |
| 141 * +----+----+----+----+----+----+----+----+----+----+....+----+ | |
| 142 * | VN | CD | DSTPORT | DSTIP | USERID |NULL| | |
| 143 * +----+----+----+----+----+----+----+----+----+----+....+----+ | |
| 144 * # of bytes: 1 1 2 4 variable 1 | |
| 145 */ | |
| 146 | |
| 147 socksreq[0] = 4; /* version (SOCKS4) */ | |
| 148 socksreq[1] = 1; /* connect */ | |
| 149 socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ | |
| 150 socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ | |
| 151 | |
| 152 /* DNS resolve only for SOCKS4, not SOCKS4a */ | |
| 153 if(!protocol4a) { | |
| 154 struct Curl_dns_entry *dns; | |
| 155 Curl_addrinfo *hp = NULL; | |
| 156 int rc; | |
| 157 | |
| 158 rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns); | |
| 159 | |
| 160 if(rc == CURLRESOLV_ERROR) | |
| 161 return CURLE_COULDNT_RESOLVE_PROXY; | |
| 162 | |
| 163 if(rc == CURLRESOLV_PENDING) | |
| 164 /* ignores the return code, but 'dns' remains NULL on failure */ | |
| 165 (void)Curl_resolver_wait_resolv(conn, &dns); | |
| 166 | |
| 167 /* | |
| 168 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It | |
| 169 * returns a Curl_addrinfo pointer that may not always look the same. | |
| 170 */ | |
| 171 if(dns) | |
| 172 hp = dns->addr; | |
| 173 if(hp) { | |
| 174 char buf[64]; | |
| 175 Curl_printable_address(hp, buf, sizeof(buf)); | |
| 176 | |
| 177 if(hp->ai_family == AF_INET) { | |
| 178 struct sockaddr_in *saddr_in; | |
| 179 | |
| 180 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; | |
| 181 socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0]; | |
| 182 socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1]; | |
| 183 socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2]; | |
| 184 socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3]; | |
| 185 | |
| 186 infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)\n", buf); | |
| 187 } | |
| 188 else { | |
| 189 hp = NULL; /* fail! */ | |
| 190 | |
| 191 failf(data, "SOCKS4 connection to %s not supported\n", buf); | |
| 192 } | |
| 193 | |
| 194 Curl_resolv_unlock(data, dns); /* not used anymore from now on */ | |
| 195 } | |
| 196 if(!hp) { | |
| 197 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", | |
| 198 hostname); | |
| 199 return CURLE_COULDNT_RESOLVE_HOST; | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 /* | |
| 204 * This is currently not supporting "Identification Protocol (RFC1413)". | |
| 205 */ | |
| 206 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ | |
| 207 if(proxy_user) { | |
| 208 size_t plen = strlen(proxy_user); | |
| 209 if(plen >= sizeof(socksreq) - 8) { | |
| 210 failf(data, "Too long SOCKS proxy name, can't use!\n"); | |
| 211 return CURLE_COULDNT_CONNECT; | |
| 212 } | |
| 213 /* copy the proxy name WITH trailing zero */ | |
| 214 memcpy(socksreq + 8, proxy_user, plen + 1); | |
| 215 } | |
| 216 | |
| 217 /* | |
| 218 * Make connection | |
| 219 */ | |
| 220 { | |
| 221 int result; | |
| 222 ssize_t actualread; | |
| 223 ssize_t written; | |
| 224 ssize_t hostnamelen = 0; | |
| 225 ssize_t packetsize = 9 + | |
| 226 strlen((char *)socksreq + 8); /* size including NUL */ | |
| 227 | |
| 228 /* If SOCKS4a, set special invalid IP address 0.0.0.x */ | |
| 229 if(protocol4a) { | |
| 230 socksreq[4] = 0; | |
| 231 socksreq[5] = 0; | |
| 232 socksreq[6] = 0; | |
| 233 socksreq[7] = 1; | |
| 234 /* If still enough room in buffer, also append hostname */ | |
| 235 hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */ | |
| 236 if(packetsize + hostnamelen <= SOCKS4REQLEN) | |
| 237 strcpy((char *)socksreq + packetsize, hostname); | |
| 238 else | |
| 239 hostnamelen = 0; /* Flag: hostname did not fit in buffer */ | |
| 240 } | |
| 241 | |
| 242 /* Send request */ | |
| 243 code = Curl_write_plain(conn, sock, (char *)socksreq, | |
| 244 packetsize + hostnamelen, | |
| 245 &written); | |
| 246 if(code || (written != packetsize + hostnamelen)) { | |
| 247 failf(data, "Failed to send SOCKS4 connect request."); | |
| 248 return CURLE_COULDNT_CONNECT; | |
| 249 } | |
| 250 if(protocol4a && hostnamelen == 0) { | |
| 251 /* SOCKS4a with very long hostname - send that name separately */ | |
| 252 hostnamelen = (ssize_t)strlen(hostname) + 1; | |
| 253 code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen, | |
| 254 &written); | |
| 255 if(code || (written != hostnamelen)) { | |
| 256 failf(data, "Failed to send SOCKS4 connect request."); | |
| 257 return CURLE_COULDNT_CONNECT; | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 packetsize = 8; /* receive data size */ | |
| 262 | |
| 263 /* Receive response */ | |
| 264 result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize, | |
| 265 &actualread); | |
| 266 if(result || (actualread != packetsize)) { | |
| 267 failf(data, "Failed to receive SOCKS4 connect request ack."); | |
| 268 return CURLE_COULDNT_CONNECT; | |
| 269 } | |
| 270 | |
| 271 /* | |
| 272 * Response format | |
| 273 * | |
| 274 * +----+----+----+----+----+----+----+----+ | |
| 275 * | VN | CD | DSTPORT | DSTIP | | |
| 276 * +----+----+----+----+----+----+----+----+ | |
| 277 * # of bytes: 1 1 2 4 | |
| 278 * | |
| 279 * VN is the version of the reply code and should be 0. CD is the result | |
| 280 * code with one of the following values: | |
| 281 * | |
| 282 * 90: request granted | |
| 283 * 91: request rejected or failed | |
| 284 * 92: request rejected because SOCKS server cannot connect to | |
| 285 * identd on the client | |
| 286 * 93: request rejected because the client program and identd | |
| 287 * report different user-ids | |
| 288 */ | |
| 289 | |
| 290 /* wrong version ? */ | |
| 291 if(socksreq[0] != 0) { | |
| 292 failf(data, | |
| 293 "SOCKS4 reply has wrong version, version should be 0."); | |
| 294 return CURLE_COULDNT_CONNECT; | |
| 295 } | |
| 296 | |
| 297 /* Result */ | |
| 298 switch(socksreq[1]) { | |
| 299 case 90: | |
| 300 infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":""); | |
| 301 break; | |
| 302 case 91: | |
| 303 failf(data, | |
| 304 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" | |
| 305 ", request rejected or failed.", | |
| 306 (unsigned char)socksreq[4], (unsigned char)socksreq[5], | |
| 307 (unsigned char)socksreq[6], (unsigned char)socksreq[7], | |
| 308 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), | |
| 309 (unsigned char)socksreq[1]); | |
| 310 return CURLE_COULDNT_CONNECT; | |
| 311 case 92: | |
| 312 failf(data, | |
| 313 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" | |
| 314 ", request rejected because SOCKS server cannot connect to " | |
| 315 "identd on the client.", | |
| 316 (unsigned char)socksreq[4], (unsigned char)socksreq[5], | |
| 317 (unsigned char)socksreq[6], (unsigned char)socksreq[7], | |
| 318 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), | |
| 319 (unsigned char)socksreq[1]); | |
| 320 return CURLE_COULDNT_CONNECT; | |
| 321 case 93: | |
| 322 failf(data, | |
| 323 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" | |
| 324 ", request rejected because the client program and identd " | |
| 325 "report different user-ids.", | |
| 326 (unsigned char)socksreq[4], (unsigned char)socksreq[5], | |
| 327 (unsigned char)socksreq[6], (unsigned char)socksreq[7], | |
| 328 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), | |
| 329 (unsigned char)socksreq[1]); | |
| 330 return CURLE_COULDNT_CONNECT; | |
| 331 default: | |
| 332 failf(data, | |
| 333 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" | |
| 334 ", Unknown.", | |
| 335 (unsigned char)socksreq[4], (unsigned char)socksreq[5], | |
| 336 (unsigned char)socksreq[6], (unsigned char)socksreq[7], | |
| 337 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), | |
| 338 (unsigned char)socksreq[1]); | |
| 339 return CURLE_COULDNT_CONNECT; | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 (void)curlx_nonblock(sock, TRUE); | |
| 344 | |
| 345 return CURLE_OK; /* Proxy was successful! */ | |
| 346 } | |
| 347 | |
| 348 /* | |
| 349 * This function logs in to a SOCKS5 proxy and sends the specifics to the final | |
| 350 * destination server. | |
| 351 */ | |
| 352 CURLcode Curl_SOCKS5(const char *proxy_user, | |
| 353 const char *proxy_password, | |
| 354 const char *hostname, | |
| 355 int remote_port, | |
| 356 int sockindex, | |
| 357 struct connectdata *conn) | |
| 358 { | |
| 359 /* | |
| 360 According to the RFC1928, section "6. Replies". This is what a SOCK5 | |
| 361 replies: | |
| 362 | |
| 363 +----+-----+-------+------+----------+----------+ | |
| 364 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | | |
| 365 +----+-----+-------+------+----------+----------+ | |
| 366 | 1 | 1 | X'00' | 1 | Variable | 2 | | |
| 367 +----+-----+-------+------+----------+----------+ | |
| 368 | |
| 369 Where: | |
| 370 | |
| 371 o VER protocol version: X'05' | |
| 372 o REP Reply field: | |
| 373 o X'00' succeeded | |
| 374 */ | |
| 375 | |
| 376 unsigned char socksreq[600]; /* room for large user/pw (255 max each) */ | |
| 377 int idx; | |
| 378 ssize_t actualread; | |
| 379 ssize_t written; | |
| 380 int result; | |
| 381 CURLcode code; | |
| 382 curl_socket_t sock = conn->sock[sockindex]; | |
| 383 struct Curl_easy *data = conn->data; | |
| 384 timediff_t timeout; | |
| 385 bool socks5_resolve_local = | |
| 386 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; | |
| 387 const size_t hostname_len = strlen(hostname); | |
| 388 ssize_t len = 0; | |
| 389 const unsigned long auth = data->set.socks5auth; | |
| 390 bool allow_gssapi = FALSE; | |
| 391 | |
| 392 if(conn->bits.httpproxy) | |
| 393 infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n", | |
| 394 hostname, remote_port); | |
| 395 | |
| 396 /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ | |
| 397 if(!socks5_resolve_local && hostname_len > 255) { | |
| 398 infof(conn->data, "SOCKS5: server resolving disabled for hostnames of " | |
| 399 "length > 255 [actual len=%zu]\n", hostname_len); | |
| 400 socks5_resolve_local = TRUE; | |
| 401 } | |
| 402 | |
| 403 /* get timeout */ | |
| 404 timeout = Curl_timeleft(data, NULL, TRUE); | |
| 405 | |
| 406 if(timeout < 0) { | |
| 407 /* time-out, bail out, go home */ | |
| 408 failf(data, "Connection time-out"); | |
| 409 return CURLE_OPERATION_TIMEDOUT; | |
| 410 } | |
| 411 | |
| 412 (void)curlx_nonblock(sock, TRUE); | |
| 413 | |
| 414 /* wait until socket gets connected */ | |
| 415 result = SOCKET_WRITABLE(sock, timeout); | |
| 416 | |
| 417 if(-1 == result) { | |
| 418 failf(conn->data, "SOCKS5: no connection here"); | |
| 419 return CURLE_COULDNT_CONNECT; | |
| 420 } | |
| 421 if(0 == result) { | |
| 422 failf(conn->data, "SOCKS5: connection timeout"); | |
| 423 return CURLE_OPERATION_TIMEDOUT; | |
| 424 } | |
| 425 | |
| 426 if(result & CURL_CSELECT_ERR) { | |
| 427 failf(conn->data, "SOCKS5: error occurred during connection"); | |
| 428 return CURLE_COULDNT_CONNECT; | |
| 429 } | |
| 430 | |
| 431 if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) | |
| 432 infof(conn->data, | |
| 433 "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n", | |
| 434 auth); | |
| 435 if(!(auth & CURLAUTH_BASIC)) | |
| 436 /* disable username/password auth */ | |
| 437 proxy_user = NULL; | |
| 438 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| 439 if(auth & CURLAUTH_GSSAPI) | |
| 440 allow_gssapi = TRUE; | |
| 441 #endif | |
| 442 | |
| 443 idx = 0; | |
| 444 socksreq[idx++] = 5; /* version */ | |
| 445 idx++; /* reserve for the number of authentication methods */ | |
| 446 socksreq[idx++] = 0; /* no authentication */ | |
| 447 if(allow_gssapi) | |
| 448 socksreq[idx++] = 1; /* GSS-API */ | |
| 449 if(proxy_user) | |
| 450 socksreq[idx++] = 2; /* username/password */ | |
| 451 /* write the number of authentication methods */ | |
| 452 socksreq[1] = (unsigned char) (idx - 2); | |
| 453 | |
| 454 (void)curlx_nonblock(sock, FALSE); | |
| 455 | |
| 456 infof(data, "SOCKS5 communication to %s:%d\n", hostname, remote_port); | |
| 457 | |
| 458 code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), | |
| 459 &written); | |
| 460 if(code || (written != (2 + (int)socksreq[1]))) { | |
| 461 failf(data, "Unable to send initial SOCKS5 request."); | |
| 462 return CURLE_COULDNT_CONNECT; | |
| 463 } | |
| 464 | |
| 465 (void)curlx_nonblock(sock, TRUE); | |
| 466 | |
| 467 result = SOCKET_READABLE(sock, timeout); | |
| 468 | |
| 469 if(-1 == result) { | |
| 470 failf(conn->data, "SOCKS5 nothing to read"); | |
| 471 return CURLE_COULDNT_CONNECT; | |
| 472 } | |
| 473 if(0 == result) { | |
| 474 failf(conn->data, "SOCKS5 read timeout"); | |
| 475 return CURLE_OPERATION_TIMEDOUT; | |
| 476 } | |
| 477 | |
| 478 if(result & CURL_CSELECT_ERR) { | |
| 479 failf(conn->data, "SOCKS5 read error occurred"); | |
| 480 return CURLE_RECV_ERROR; | |
| 481 } | |
| 482 | |
| 483 (void)curlx_nonblock(sock, FALSE); | |
| 484 | |
| 485 result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread); | |
| 486 if(result || (actualread != 2)) { | |
| 487 failf(data, "Unable to receive initial SOCKS5 response."); | |
| 488 return CURLE_COULDNT_CONNECT; | |
| 489 } | |
| 490 | |
| 491 if(socksreq[0] != 5) { | |
| 492 failf(data, "Received invalid version in initial SOCKS5 response."); | |
| 493 return CURLE_COULDNT_CONNECT; | |
| 494 } | |
| 495 if(socksreq[1] == 0) { | |
| 496 /* Nothing to do, no authentication needed */ | |
| 497 ; | |
| 498 } | |
| 499 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| 500 else if(allow_gssapi && (socksreq[1] == 1)) { | |
| 501 code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn); | |
| 502 if(code) { | |
| 503 failf(data, "Unable to negotiate SOCKS5 GSS-API context."); | |
| 504 return CURLE_COULDNT_CONNECT; | |
| 505 } | |
| 506 } | |
| 507 #endif | |
| 508 else if(socksreq[1] == 2) { | |
| 509 /* Needs user name and password */ | |
| 510 size_t proxy_user_len, proxy_password_len; | |
| 511 if(proxy_user && proxy_password) { | |
| 512 proxy_user_len = strlen(proxy_user); | |
| 513 proxy_password_len = strlen(proxy_password); | |
| 514 } | |
| 515 else { | |
| 516 proxy_user_len = 0; | |
| 517 proxy_password_len = 0; | |
| 518 } | |
| 519 | |
| 520 /* username/password request looks like | |
| 521 * +----+------+----------+------+----------+ | |
| 522 * |VER | ULEN | UNAME | PLEN | PASSWD | | |
| 523 * +----+------+----------+------+----------+ | |
| 524 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | | |
| 525 * +----+------+----------+------+----------+ | |
| 526 */ | |
| 527 len = 0; | |
| 528 socksreq[len++] = 1; /* username/pw subnegotiation version */ | |
| 529 socksreq[len++] = (unsigned char) proxy_user_len; | |
| 530 if(proxy_user && proxy_user_len) { | |
| 531 /* the length must fit in a single byte */ | |
| 532 if(proxy_user_len >= 255) { | |
| 533 failf(data, "Excessive user name length for proxy auth"); | |
| 534 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 535 } | |
| 536 memcpy(socksreq + len, proxy_user, proxy_user_len); | |
| 537 } | |
| 538 len += proxy_user_len; | |
| 539 socksreq[len++] = (unsigned char) proxy_password_len; | |
| 540 if(proxy_password && proxy_password_len) { | |
| 541 /* the length must fit in a single byte */ | |
| 542 if(proxy_password_len > 255) { | |
| 543 failf(data, "Excessive password length for proxy auth"); | |
| 544 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 545 } | |
| 546 memcpy(socksreq + len, proxy_password, proxy_password_len); | |
| 547 } | |
| 548 len += proxy_password_len; | |
| 549 | |
| 550 code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written); | |
| 551 if(code || (len != written)) { | |
| 552 failf(data, "Failed to send SOCKS5 sub-negotiation request."); | |
| 553 return CURLE_COULDNT_CONNECT; | |
| 554 } | |
| 555 | |
| 556 result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread); | |
| 557 if(result || (actualread != 2)) { | |
| 558 failf(data, "Unable to receive SOCKS5 sub-negotiation response."); | |
| 559 return CURLE_COULDNT_CONNECT; | |
| 560 } | |
| 561 | |
| 562 /* ignore the first (VER) byte */ | |
| 563 if(socksreq[1] != 0) { /* status */ | |
| 564 failf(data, "User was rejected by the SOCKS5 server (%d %d).", | |
| 565 socksreq[0], socksreq[1]); | |
| 566 return CURLE_COULDNT_CONNECT; | |
| 567 } | |
| 568 | |
| 569 /* Everything is good so far, user was authenticated! */ | |
| 570 } | |
| 571 else { | |
| 572 /* error */ | |
| 573 if(!allow_gssapi && (socksreq[1] == 1)) { | |
| 574 failf(data, | |
| 575 "SOCKS5 GSSAPI per-message authentication is not supported."); | |
| 576 return CURLE_COULDNT_CONNECT; | |
| 577 } | |
| 578 if(socksreq[1] == 255) { | |
| 579 if(!proxy_user || !*proxy_user) { | |
| 580 failf(data, | |
| 581 "No authentication method was acceptable. (It is quite likely" | |
| 582 " that the SOCKS5 server wanted a username/password, since none" | |
| 583 " was supplied to the server on this connection.)"); | |
| 584 } | |
| 585 else { | |
| 586 failf(data, "No authentication method was acceptable."); | |
| 587 } | |
| 588 return CURLE_COULDNT_CONNECT; | |
| 589 } | |
| 590 else { | |
| 591 failf(data, | |
| 592 "Undocumented SOCKS5 mode attempted to be used by server."); | |
| 593 return CURLE_COULDNT_CONNECT; | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 /* Authentication is complete, now specify destination to the proxy */ | |
| 598 len = 0; | |
| 599 socksreq[len++] = 5; /* version (SOCKS5) */ | |
| 600 socksreq[len++] = 1; /* connect */ | |
| 601 socksreq[len++] = 0; /* must be zero */ | |
| 602 | |
| 603 if(!socks5_resolve_local) { | |
| 604 socksreq[len++] = 3; /* ATYP: domain name = 3 */ | |
| 605 socksreq[len++] = (char) hostname_len; /* address length */ | |
| 606 memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */ | |
| 607 len += hostname_len; | |
| 608 } | |
| 609 else { | |
| 610 struct Curl_dns_entry *dns; | |
| 611 Curl_addrinfo *hp = NULL; | |
| 612 int rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns); | |
| 613 | |
| 614 if(rc == CURLRESOLV_ERROR) | |
| 615 return CURLE_COULDNT_RESOLVE_HOST; | |
| 616 | |
| 617 if(rc == CURLRESOLV_PENDING) { | |
| 618 /* this requires that we're in "wait for resolve" state */ | |
| 619 code = Curl_resolver_wait_resolv(conn, &dns); | |
| 620 if(code) | |
| 621 return code; | |
| 622 } | |
| 623 | |
| 624 /* | |
| 625 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It | |
| 626 * returns a Curl_addrinfo pointer that may not always look the same. | |
| 627 */ | |
| 628 if(dns) | |
| 629 hp = dns->addr; | |
| 630 if(hp) { | |
| 631 char buf[64]; | |
| 632 Curl_printable_address(hp, buf, sizeof(buf)); | |
| 633 | |
| 634 if(hp->ai_family == AF_INET) { | |
| 635 int i; | |
| 636 struct sockaddr_in *saddr_in; | |
| 637 socksreq[len++] = 1; /* ATYP: IPv4 = 1 */ | |
| 638 | |
| 639 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; | |
| 640 for(i = 0; i < 4; i++) { | |
| 641 socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; | |
| 642 } | |
| 643 | |
| 644 infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", buf); | |
| 645 } | |
| 646 #ifdef ENABLE_IPV6 | |
| 647 else if(hp->ai_family == AF_INET6) { | |
| 648 int i; | |
| 649 struct sockaddr_in6 *saddr_in6; | |
| 650 socksreq[len++] = 4; /* ATYP: IPv6 = 4 */ | |
| 651 | |
| 652 saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; | |
| 653 for(i = 0; i < 16; i++) { | |
| 654 socksreq[len++] = | |
| 655 ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; | |
| 656 } | |
| 657 | |
| 658 infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", buf); | |
| 659 } | |
| 660 #endif | |
| 661 else { | |
| 662 hp = NULL; /* fail! */ | |
| 663 | |
| 664 failf(data, "SOCKS5 connection to %s not supported\n", buf); | |
| 665 } | |
| 666 | |
| 667 Curl_resolv_unlock(data, dns); /* not used anymore from now on */ | |
| 668 } | |
| 669 if(!hp) { | |
| 670 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", | |
| 671 hostname); | |
| 672 return CURLE_COULDNT_RESOLVE_HOST; | |
| 673 } | |
| 674 } | |
| 675 | |
| 676 socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ | |
| 677 socksreq[len++] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ | |
| 678 | |
| 679 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| 680 if(conn->socks5_gssapi_enctype) { | |
| 681 failf(data, "SOCKS5 GSS-API protection not yet implemented."); | |
| 682 } | |
| 683 else | |
| 684 #endif | |
| 685 code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written); | |
| 686 | |
| 687 if(code || (len != written)) { | |
| 688 failf(data, "Failed to send SOCKS5 connect request."); | |
| 689 return CURLE_COULDNT_CONNECT; | |
| 690 } | |
| 691 | |
| 692 len = 10; /* minimum packet size is 10 */ | |
| 693 | |
| 694 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| 695 if(conn->socks5_gssapi_enctype) { | |
| 696 failf(data, "SOCKS5 GSS-API protection not yet implemented."); | |
| 697 } | |
| 698 else | |
| 699 #endif | |
| 700 result = Curl_blockread_all(conn, sock, (char *)socksreq, | |
| 701 len, &actualread); | |
| 702 | |
| 703 if(result || (len != actualread)) { | |
| 704 failf(data, "Failed to receive SOCKS5 connect request ack."); | |
| 705 return CURLE_COULDNT_CONNECT; | |
| 706 } | |
| 707 | |
| 708 if(socksreq[0] != 5) { /* version */ | |
| 709 failf(data, | |
| 710 "SOCKS5 reply has wrong version, version should be 5."); | |
| 711 return CURLE_COULDNT_CONNECT; | |
| 712 } | |
| 713 | |
| 714 /* Fix: in general, returned BND.ADDR is variable length parameter by RFC | |
| 715 1928, so the reply packet should be read until the end to avoid errors at | |
| 716 subsequent protocol level. | |
| 717 | |
| 718 +----+-----+-------+------+----------+----------+ | |
| 719 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | | |
| 720 +----+-----+-------+------+----------+----------+ | |
| 721 | 1 | 1 | X'00' | 1 | Variable | 2 | | |
| 722 +----+-----+-------+------+----------+----------+ | |
| 723 | |
| 724 ATYP: | |
| 725 o IP v4 address: X'01', BND.ADDR = 4 byte | |
| 726 o domain name: X'03', BND.ADDR = [ 1 byte length, string ] | |
| 727 o IP v6 address: X'04', BND.ADDR = 16 byte | |
| 728 */ | |
| 729 | |
| 730 /* Calculate real packet size */ | |
| 731 if(socksreq[3] == 3) { | |
| 732 /* domain name */ | |
| 733 int addrlen = (int) socksreq[4]; | |
| 734 len = 5 + addrlen + 2; | |
| 735 } | |
| 736 else if(socksreq[3] == 4) { | |
| 737 /* IPv6 */ | |
| 738 len = 4 + 16 + 2; | |
| 739 } | |
| 740 | |
| 741 /* At this point we already read first 10 bytes */ | |
| 742 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| 743 if(!conn->socks5_gssapi_enctype) { | |
| 744 /* decrypt_gssapi_blockread already read the whole packet */ | |
| 745 #endif | |
| 746 if(len > 10) { | |
| 747 result = Curl_blockread_all(conn, sock, (char *)&socksreq[10], | |
| 748 len - 10, &actualread); | |
| 749 if(result || ((len - 10) != actualread)) { | |
| 750 failf(data, "Failed to receive SOCKS5 connect request ack."); | |
| 751 return CURLE_COULDNT_CONNECT; | |
| 752 } | |
| 753 } | |
| 754 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| 755 } | |
| 756 #endif | |
| 757 | |
| 758 if(socksreq[1] != 0) { /* Anything besides 0 is an error */ | |
| 759 if(socksreq[3] == 1) { | |
| 760 failf(data, | |
| 761 "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)", | |
| 762 (unsigned char)socksreq[4], (unsigned char)socksreq[5], | |
| 763 (unsigned char)socksreq[6], (unsigned char)socksreq[7], | |
| 764 (((unsigned char)socksreq[8] << 8) | | |
| 765 (unsigned char)socksreq[9]), | |
| 766 (unsigned char)socksreq[1]); | |
| 767 } | |
| 768 else if(socksreq[3] == 3) { | |
| 769 unsigned char port_upper = (unsigned char)socksreq[len - 2]; | |
| 770 socksreq[len - 2] = 0; | |
| 771 failf(data, | |
| 772 "Can't complete SOCKS5 connection to %s:%d. (%d)", | |
| 773 (char *)&socksreq[5], | |
| 774 ((port_upper << 8) | | |
| 775 (unsigned char)socksreq[len - 1]), | |
| 776 (unsigned char)socksreq[1]); | |
| 777 socksreq[len - 2] = port_upper; | |
| 778 } | |
| 779 else if(socksreq[3] == 4) { | |
| 780 failf(data, | |
| 781 "Can't complete SOCKS5 connection to %02x%02x:%02x%02x:" | |
| 782 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d. (%d)", | |
| 783 (unsigned char)socksreq[4], (unsigned char)socksreq[5], | |
| 784 (unsigned char)socksreq[6], (unsigned char)socksreq[7], | |
| 785 (unsigned char)socksreq[8], (unsigned char)socksreq[9], | |
| 786 (unsigned char)socksreq[10], (unsigned char)socksreq[11], | |
| 787 (unsigned char)socksreq[12], (unsigned char)socksreq[13], | |
| 788 (unsigned char)socksreq[14], (unsigned char)socksreq[15], | |
| 789 (unsigned char)socksreq[16], (unsigned char)socksreq[17], | |
| 790 (unsigned char)socksreq[18], (unsigned char)socksreq[19], | |
| 791 (((unsigned char)socksreq[20] << 8) | | |
| 792 (unsigned char)socksreq[21]), | |
| 793 (unsigned char)socksreq[1]); | |
| 794 } | |
| 795 return CURLE_COULDNT_CONNECT; | |
| 796 } | |
| 797 infof(data, "SOCKS5 request granted.\n"); | |
| 798 | |
| 799 (void)curlx_nonblock(sock, TRUE); | |
| 800 return CURLE_OK; /* Proxy was successful! */ | |
| 801 } | |
| 802 | |
| 803 #endif /* CURL_DISABLE_PROXY */ |
