Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / socks.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2022, 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.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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #if !defined(CURL_DISABLE_PROXY)
28
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
34 #endif
35
36 #include "urldata.h"
37 #include "sendf.h"
38 #include "select.h"
39 #include "connect.h"
40 #include "timeval.h"
41 #include "socks.h"
42 #include "multiif.h" /* for getsock macros */
43 #include "inet_pton.h"
44
45 /* The last 3 #include files should be in this order */
46 #include "curl_printf.h"
47 #include "curl_memory.h"
48 #include "memdebug.h"
49
50 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
51 /*
52  * Helper read-from-socket functions. Does the same as Curl_read() but it
53  * blocks until all bytes amount of buffersize will be read. No more, no less.
54  *
55  * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
56  */
57 int Curl_blockread_all(struct Curl_easy *data,   /* transfer */
58                        curl_socket_t sockfd,     /* read from this socket */
59                        char *buf,                /* store read data here */
60                        ssize_t buffersize,       /* max amount to read */
61                        ssize_t *n)               /* amount bytes read */
62 {
63   ssize_t nread = 0;
64   ssize_t allread = 0;
65   int result;
66   *n = 0;
67   for(;;) {
68     timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
69     if(timeout_ms < 0) {
70       /* we already got the timeout */
71       result = CURLE_OPERATION_TIMEDOUT;
72       break;
73     }
74     if(!timeout_ms)
75       timeout_ms = TIMEDIFF_T_MAX;
76     if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
77       result = ~CURLE_OK;
78       break;
79     }
80     result = Curl_read_plain(sockfd, buf, buffersize, &nread);
81     if(CURLE_AGAIN == result)
82       continue;
83     if(result)
84       break;
85
86     if(buffersize == nread) {
87       allread += nread;
88       *n = allread;
89       result = CURLE_OK;
90       break;
91     }
92     if(!nread) {
93       result = ~CURLE_OK;
94       break;
95     }
96
97     buffersize -= nread;
98     buf += nread;
99     allread += nread;
100   }
101   return result;
102 }
103 #endif
104
105 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
106 #define DEBUG_AND_VERBOSE
107 #define sxstate(x,y) socksstate(x,y, __LINE__)
108 #else
109 #define sxstate(x,y) socksstate(x,y)
110 #endif
111
112 /* always use this function to change state, to make debugging easier */
113 static void socksstate(struct Curl_easy *data,
114                        enum connect_t state
115 #ifdef DEBUG_AND_VERBOSE
116                        , int lineno
117 #endif
118 )
119 {
120   struct connectdata *conn = data->conn;
121   enum connect_t oldstate = conn->cnnct.state;
122 #ifdef DEBUG_AND_VERBOSE
123   /* synced with the state list in urldata.h */
124   static const char * const statename[] = {
125     "INIT",
126     "SOCKS_INIT",
127     "SOCKS_SEND",
128     "SOCKS_READ_INIT",
129     "SOCKS_READ",
130     "GSSAPI_INIT",
131     "AUTH_INIT",
132     "AUTH_SEND",
133     "AUTH_READ",
134     "REQ_INIT",
135     "RESOLVING",
136     "RESOLVED",
137     "RESOLVE_REMOTE",
138     "REQ_SEND",
139     "REQ_SENDING",
140     "REQ_READ",
141     "REQ_READ_MORE",
142     "DONE"
143   };
144 #endif
145
146   if(oldstate == state)
147     /* don't bother when the new state is the same as the old state */
148     return;
149
150   conn->cnnct.state = state;
151
152 #ifdef DEBUG_AND_VERBOSE
153   infof(data,
154         "SXSTATE: %s => %s conn %p; line %d",
155         statename[oldstate], statename[conn->cnnct.state], conn,
156         lineno);
157 #endif
158 }
159
160 int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock,
161                        int sockindex)
162 {
163   int rc = 0;
164   sock[0] = conn->sock[sockindex];
165   switch(conn->cnnct.state) {
166   case CONNECT_RESOLVING:
167   case CONNECT_SOCKS_READ:
168   case CONNECT_AUTH_READ:
169   case CONNECT_REQ_READ:
170   case CONNECT_REQ_READ_MORE:
171     rc = GETSOCK_READSOCK(0);
172     break;
173   default:
174     rc = GETSOCK_WRITESOCK(0);
175     break;
176   }
177   return rc;
178 }
179
180 /*
181 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
182 * destination server.
183 *
184 * Reference :
185 *   https://www.openssh.com/txt/socks4.protocol
186 *
187 * Note :
188 *   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
189 *   Nonsupport "Identification Protocol (RFC1413)"
190 */
191 CURLproxycode Curl_SOCKS4(const char *proxy_user,
192                           const char *hostname,
193                           int remote_port,
194                           int sockindex,
195                           struct Curl_easy *data,
196                           bool *done)
197 {
198   struct connectdata *conn = data->conn;
199   const bool protocol4a =
200     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
201   unsigned char *socksreq = (unsigned char *)data->state.buffer;
202   CURLcode result;
203   curl_socket_t sockfd = conn->sock[sockindex];
204   struct connstate *sx = &conn->cnnct;
205   struct Curl_dns_entry *dns = NULL;
206   ssize_t actualread;
207   ssize_t written;
208
209   /* make sure that the buffer is at least 600 bytes */
210   DEBUGASSERT(READBUFFER_MIN >= 600);
211
212   if(!SOCKS_STATE(sx->state) && !*done)
213     sxstate(data, CONNECT_SOCKS_INIT);
214
215   switch(sx->state) {
216   case CONNECT_SOCKS_INIT:
217     /* SOCKS4 can only do IPv4, insist! */
218     conn->ip_version = CURL_IPRESOLVE_V4;
219     if(conn->bits.httpproxy)
220       infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
221             protocol4a ? "a" : "", hostname, remote_port);
222
223     infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port);
224
225     /*
226      * Compose socks4 request
227      *
228      * Request format
229      *
230      *     +----+----+----+----+----+----+----+----+----+----+....+----+
231      *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
232      *     +----+----+----+----+----+----+----+----+----+----+....+----+
233      * # of bytes:  1    1      2              4           variable       1
234      */
235
236     socksreq[0] = 4; /* version (SOCKS4) */
237     socksreq[1] = 1; /* connect */
238     socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
239     socksreq[3] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
240
241     /* DNS resolve only for SOCKS4, not SOCKS4a */
242     if(!protocol4a) {
243       enum resolve_t rc =
244         Curl_resolv(data, hostname, remote_port, FALSE, &dns);
245
246       if(rc == CURLRESOLV_ERROR)
247         return CURLPX_RESOLVE_HOST;
248       else if(rc == CURLRESOLV_PENDING) {
249         sxstate(data, CONNECT_RESOLVING);
250         infof(data, "SOCKS4 non-blocking resolve of %s", hostname);
251         return CURLPX_OK;
252       }
253       sxstate(data, CONNECT_RESOLVED);
254       goto CONNECT_RESOLVED;
255     }
256
257     /* socks4a doesn't resolve anything locally */
258     sxstate(data, CONNECT_REQ_INIT);
259     goto CONNECT_REQ_INIT;
260
261   case CONNECT_RESOLVING:
262     /* check if we have the name resolved by now */
263     dns = Curl_fetch_addr(data, hostname, (int)conn->port);
264
265     if(dns) {
266 #ifdef CURLRES_ASYNCH
267       data->state.async.dns = dns;
268       data->state.async.done = TRUE;
269 #endif
270       infof(data, "Hostname '%s' was found", hostname);
271       sxstate(data, CONNECT_RESOLVED);
272     }
273     else {
274       result = Curl_resolv_check(data, &dns);
275       if(!dns) {
276         if(result)
277           return CURLPX_RESOLVE_HOST;
278         return CURLPX_OK;
279       }
280     }
281     /* FALLTHROUGH */
282   CONNECT_RESOLVED:
283   case CONNECT_RESOLVED: {
284     struct Curl_addrinfo *hp = NULL;
285     /*
286      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
287      * returns a Curl_addrinfo pointer that may not always look the same.
288      */
289     if(dns) {
290       hp = dns->addr;
291
292       /* scan for the first IPv4 address */
293       while(hp && (hp->ai_family != AF_INET))
294         hp = hp->ai_next;
295
296       if(hp) {
297         struct sockaddr_in *saddr_in;
298         char buf[64];
299         Curl_printable_address(hp, buf, sizeof(buf));
300
301         saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
302         socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
303         socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
304         socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
305         socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
306
307         infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
308
309         Curl_resolv_unlock(data, dns); /* not used anymore from now on */
310       }
311       else
312         failf(data, "SOCKS4 connection to %s not supported", hostname);
313     }
314     else
315       failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
316             hostname);
317
318     if(!hp)
319       return CURLPX_RESOLVE_HOST;
320   }
321     /* FALLTHROUGH */
322   CONNECT_REQ_INIT:
323   case CONNECT_REQ_INIT:
324     /*
325      * This is currently not supporting "Identification Protocol (RFC1413)".
326      */
327     socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
328     if(proxy_user) {
329       size_t plen = strlen(proxy_user);
330       if(plen >= (size_t)data->set.buffer_size - 8) {
331         failf(data, "Too long SOCKS proxy user name, can't use");
332         return CURLPX_LONG_USER;
333       }
334       /* copy the proxy name WITH trailing zero */
335       memcpy(socksreq + 8, proxy_user, plen + 1);
336     }
337
338     /*
339      * Make connection
340      */
341     {
342       size_t packetsize = 9 +
343         strlen((char *)socksreq + 8); /* size including NUL */
344
345       /* If SOCKS4a, set special invalid IP address 0.0.0.x */
346       if(protocol4a) {
347         size_t hostnamelen = 0;
348         socksreq[4] = 0;
349         socksreq[5] = 0;
350         socksreq[6] = 0;
351         socksreq[7] = 1;
352         /* append hostname */
353         hostnamelen = strlen(hostname) + 1; /* length including NUL */
354         if(hostnamelen <= 255)
355           strcpy((char *)socksreq + packetsize, hostname);
356         else {
357           failf(data, "SOCKS4: too long host name");
358           return CURLPX_LONG_HOSTNAME;
359         }
360         packetsize += hostnamelen;
361       }
362       sx->outp = socksreq;
363       sx->outstanding = packetsize;
364       sxstate(data, CONNECT_REQ_SENDING);
365     }
366     /* FALLTHROUGH */
367   case CONNECT_REQ_SENDING:
368     /* Send request */
369     result = Curl_write_plain(data, sockfd, (char *)sx->outp,
370                               sx->outstanding, &written);
371     if(result && (CURLE_AGAIN != result)) {
372       failf(data, "Failed to send SOCKS4 connect request.");
373       return CURLPX_SEND_CONNECT;
374     }
375     if(written != sx->outstanding) {
376       /* not done, remain in state */
377       sx->outstanding -= written;
378       sx->outp += written;
379       return CURLPX_OK;
380     }
381
382     /* done sending! */
383     sx->outstanding = 8; /* receive data size */
384     sx->outp = socksreq;
385     sxstate(data, CONNECT_SOCKS_READ);
386
387     /* FALLTHROUGH */
388   case CONNECT_SOCKS_READ:
389     /* Receive response */
390     result = Curl_read_plain(sockfd, (char *)sx->outp,
391                              sx->outstanding, &actualread);
392     if(result && (CURLE_AGAIN != result)) {
393       failf(data, "SOCKS4: Failed receiving connect request ack: %s",
394             curl_easy_strerror(result));
395       return CURLPX_RECV_CONNECT;
396     }
397     else if(!result && !actualread) {
398       /* connection closed */
399       failf(data, "connection to proxy closed");
400       return CURLPX_CLOSED;
401     }
402     else if(actualread != sx->outstanding) {
403       /* remain in reading state */
404       sx->outstanding -= actualread;
405       sx->outp += actualread;
406       return CURLPX_OK;
407     }
408     sxstate(data, CONNECT_DONE);
409     break;
410   default: /* lots of unused states in SOCKS4 */
411     break;
412   }
413
414   /*
415    * Response format
416    *
417    *     +----+----+----+----+----+----+----+----+
418    *     | VN | CD | DSTPORT |      DSTIP        |
419    *     +----+----+----+----+----+----+----+----+
420    * # of bytes:  1    1      2              4
421    *
422    * VN is the version of the reply code and should be 0. CD is the result
423    * code with one of the following values:
424    *
425    * 90: request granted
426    * 91: request rejected or failed
427    * 92: request rejected because SOCKS server cannot connect to
428    *     identd on the client
429    * 93: request rejected because the client program and identd
430    *     report different user-ids
431    */
432
433   /* wrong version ? */
434   if(socksreq[0]) {
435     failf(data,
436           "SOCKS4 reply has wrong version, version should be 0.");
437     return CURLPX_BAD_VERSION;
438   }
439
440   /* Result */
441   switch(socksreq[1]) {
442   case 90:
443     infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
444     break;
445   case 91:
446     failf(data,
447           "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
448           ", request rejected or failed.",
449           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
450           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
451           (unsigned char)socksreq[1]);
452     return CURLPX_REQUEST_FAILED;
453   case 92:
454     failf(data,
455           "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
456           ", request rejected because SOCKS server cannot connect to "
457           "identd on the client.",
458           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
459           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
460           (unsigned char)socksreq[1]);
461     return CURLPX_IDENTD;
462   case 93:
463     failf(data,
464           "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
465           ", request rejected because the client program and identd "
466           "report different user-ids.",
467           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
468           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
469           (unsigned char)socksreq[1]);
470     return CURLPX_IDENTD_DIFFER;
471   default:
472     failf(data,
473           "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
474           ", Unknown.",
475           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
476           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
477           (unsigned char)socksreq[1]);
478     return CURLPX_UNKNOWN_FAIL;
479   }
480
481   *done = TRUE;
482   return CURLPX_OK; /* Proxy was successful! */
483 }
484
485 /*
486  * This function logs in to a SOCKS5 proxy and sends the specifics to the final
487  * destination server.
488  */
489 CURLproxycode Curl_SOCKS5(const char *proxy_user,
490                           const char *proxy_password,
491                           const char *hostname,
492                           int remote_port,
493                           int sockindex,
494                           struct Curl_easy *data,
495                           bool *done)
496 {
497   /*
498     According to the RFC1928, section "6.  Replies". This is what a SOCK5
499     replies:
500
501         +----+-----+-------+------+----------+----------+
502         |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
503         +----+-----+-------+------+----------+----------+
504         | 1  |  1  | X'00' |  1   | Variable |    2     |
505         +----+-----+-------+------+----------+----------+
506
507     Where:
508
509     o  VER    protocol version: X'05'
510     o  REP    Reply field:
511     o  X'00' succeeded
512   */
513   struct connectdata *conn = data->conn;
514   unsigned char *socksreq = (unsigned char *)data->state.buffer;
515   char dest[256] = "unknown";  /* printable hostname:port */
516   int idx;
517   ssize_t actualread;
518   ssize_t written;
519   CURLcode result;
520   curl_socket_t sockfd = conn->sock[sockindex];
521   bool socks5_resolve_local =
522     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
523   const size_t hostname_len = strlen(hostname);
524   ssize_t len = 0;
525   const unsigned long auth = data->set.socks5auth;
526   bool allow_gssapi = FALSE;
527   struct connstate *sx = &conn->cnnct;
528   struct Curl_dns_entry *dns = NULL;
529
530   if(!SOCKS_STATE(sx->state) && !*done)
531     sxstate(data, CONNECT_SOCKS_INIT);
532
533   switch(sx->state) {
534   case CONNECT_SOCKS_INIT:
535     if(conn->bits.httpproxy)
536       infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
537             hostname, remote_port);
538
539     /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
540     if(!socks5_resolve_local && hostname_len > 255) {
541       infof(data, "SOCKS5: server resolving disabled for hostnames of "
542             "length > 255 [actual len=%zu]", hostname_len);
543       socks5_resolve_local = TRUE;
544     }
545
546     if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
547       infof(data,
548             "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu",
549             auth);
550     if(!(auth & CURLAUTH_BASIC))
551       /* disable username/password auth */
552       proxy_user = NULL;
553 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
554     if(auth & CURLAUTH_GSSAPI)
555       allow_gssapi = TRUE;
556 #endif
557
558     idx = 0;
559     socksreq[idx++] = 5;   /* version */
560     idx++;                 /* number of authentication methods */
561     socksreq[idx++] = 0;   /* no authentication */
562     if(allow_gssapi)
563       socksreq[idx++] = 1; /* GSS-API */
564     if(proxy_user)
565       socksreq[idx++] = 2; /* username/password */
566     /* write the number of authentication methods */
567     socksreq[1] = (unsigned char) (idx - 2);
568
569     result = Curl_write_plain(data, sockfd, (char *)socksreq, idx, &written);
570     if(result && (CURLE_AGAIN != result)) {
571       failf(data, "Unable to send initial SOCKS5 request.");
572       return CURLPX_SEND_CONNECT;
573     }
574     if(written != idx) {
575       sxstate(data, CONNECT_SOCKS_SEND);
576       sx->outstanding = idx - written;
577       sx->outp = &socksreq[written];
578       return CURLPX_OK;
579     }
580     sxstate(data, CONNECT_SOCKS_READ);
581     goto CONNECT_SOCKS_READ_INIT;
582   case CONNECT_SOCKS_SEND:
583     result = Curl_write_plain(data, sockfd, (char *)sx->outp,
584                               sx->outstanding, &written);
585     if(result && (CURLE_AGAIN != result)) {
586       failf(data, "Unable to send initial SOCKS5 request.");
587       return CURLPX_SEND_CONNECT;
588     }
589     if(written != sx->outstanding) {
590       /* not done, remain in state */
591       sx->outstanding -= written;
592       sx->outp += written;
593       return CURLPX_OK;
594     }
595     /* FALLTHROUGH */
596   CONNECT_SOCKS_READ_INIT:
597   case CONNECT_SOCKS_READ_INIT:
598     sx->outstanding = 2; /* expect two bytes */
599     sx->outp = socksreq; /* store it here */
600     /* FALLTHROUGH */
601   case CONNECT_SOCKS_READ:
602     result = Curl_read_plain(sockfd, (char *)sx->outp,
603                              sx->outstanding, &actualread);
604     if(result && (CURLE_AGAIN != result)) {
605       failf(data, "Unable to receive initial SOCKS5 response.");
606       return CURLPX_RECV_CONNECT;
607     }
608     else if(!result && !actualread) {
609       /* connection closed */
610       failf(data, "Connection to proxy closed");
611       return CURLPX_CLOSED;
612     }
613     else if(actualread != sx->outstanding) {
614       /* remain in reading state */
615       sx->outstanding -= actualread;
616       sx->outp += actualread;
617       return CURLPX_OK;
618     }
619     else if(socksreq[0] != 5) {
620       failf(data, "Received invalid version in initial SOCKS5 response.");
621       return CURLPX_BAD_VERSION;
622     }
623     else if(socksreq[1] == 0) {
624       /* DONE! No authentication needed. Send request. */
625       sxstate(data, CONNECT_REQ_INIT);
626       goto CONNECT_REQ_INIT;
627     }
628     else if(socksreq[1] == 2) {
629       /* regular name + password authentication */
630       sxstate(data, CONNECT_AUTH_INIT);
631       goto CONNECT_AUTH_INIT;
632     }
633 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
634     else if(allow_gssapi && (socksreq[1] == 1)) {
635       sxstate(data, CONNECT_GSSAPI_INIT);
636       result = Curl_SOCKS5_gssapi_negotiate(sockindex, data);
637       if(result) {
638         failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
639         return CURLPX_GSSAPI;
640       }
641     }
642 #endif
643     else {
644       /* error */
645       if(!allow_gssapi && (socksreq[1] == 1)) {
646         failf(data,
647               "SOCKS5 GSSAPI per-message authentication is not supported.");
648         return CURLPX_GSSAPI_PERMSG;
649       }
650       else if(socksreq[1] == 255) {
651         failf(data, "No authentication method was acceptable.");
652         return CURLPX_NO_AUTH;
653       }
654     }
655     failf(data,
656           "Undocumented SOCKS5 mode attempted to be used by server.");
657     return CURLPX_UNKNOWN_MODE;
658 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
659   case CONNECT_GSSAPI_INIT:
660     /* GSSAPI stuff done non-blocking */
661     break;
662 #endif
663
664   default: /* do nothing! */
665     break;
666
667   CONNECT_AUTH_INIT:
668   case CONNECT_AUTH_INIT: {
669     /* Needs user name and password */
670     size_t proxy_user_len, proxy_password_len;
671     if(proxy_user && proxy_password) {
672       proxy_user_len = strlen(proxy_user);
673       proxy_password_len = strlen(proxy_password);
674     }
675     else {
676       proxy_user_len = 0;
677       proxy_password_len = 0;
678     }
679
680     /*   username/password request looks like
681      * +----+------+----------+------+----------+
682      * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
683      * +----+------+----------+------+----------+
684      * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
685      * +----+------+----------+------+----------+
686      */
687     len = 0;
688     socksreq[len++] = 1;    /* username/pw subnegotiation version */
689     socksreq[len++] = (unsigned char) proxy_user_len;
690     if(proxy_user && proxy_user_len) {
691       /* the length must fit in a single byte */
692       if(proxy_user_len >= 255) {
693         failf(data, "Excessive user name length for proxy auth");
694         return CURLPX_LONG_USER;
695       }
696       memcpy(socksreq + len, proxy_user, proxy_user_len);
697     }
698     len += proxy_user_len;
699     socksreq[len++] = (unsigned char) proxy_password_len;
700     if(proxy_password && proxy_password_len) {
701       /* the length must fit in a single byte */
702       if(proxy_password_len > 255) {
703         failf(data, "Excessive password length for proxy auth");
704         return CURLPX_LONG_PASSWD;
705       }
706       memcpy(socksreq + len, proxy_password, proxy_password_len);
707     }
708     len += proxy_password_len;
709     sxstate(data, CONNECT_AUTH_SEND);
710     sx->outstanding = len;
711     sx->outp = socksreq;
712   }
713     /* FALLTHROUGH */
714   case CONNECT_AUTH_SEND:
715     result = Curl_write_plain(data, sockfd, (char *)sx->outp,
716                               sx->outstanding, &written);
717     if(result && (CURLE_AGAIN != result)) {
718       failf(data, "Failed to send SOCKS5 sub-negotiation request.");
719       return CURLPX_SEND_AUTH;
720     }
721     if(sx->outstanding != written) {
722       /* remain in state */
723       sx->outstanding -= written;
724       sx->outp += written;
725       return CURLPX_OK;
726     }
727     sx->outp = socksreq;
728     sx->outstanding = 2;
729     sxstate(data, CONNECT_AUTH_READ);
730     /* FALLTHROUGH */
731   case CONNECT_AUTH_READ:
732     result = Curl_read_plain(sockfd, (char *)sx->outp,
733                              sx->outstanding, &actualread);
734     if(result && (CURLE_AGAIN != result)) {
735       failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
736       return CURLPX_RECV_AUTH;
737     }
738     else if(!result && !actualread) {
739       /* connection closed */
740       failf(data, "connection to proxy closed");
741       return CURLPX_CLOSED;
742     }
743     else if(actualread != sx->outstanding) {
744       /* remain in state */
745       sx->outstanding -= actualread;
746       sx->outp += actualread;
747       return CURLPX_OK;
748     }
749     /* ignore the first (VER) byte */
750     else if(socksreq[1]) { /* status */
751       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
752             socksreq[0], socksreq[1]);
753       return CURLPX_USER_REJECTED;
754     }
755
756     /* Everything is good so far, user was authenticated! */
757     sxstate(data, CONNECT_REQ_INIT);
758     /* FALLTHROUGH */
759   CONNECT_REQ_INIT:
760   case CONNECT_REQ_INIT:
761     if(socks5_resolve_local) {
762       enum resolve_t rc = Curl_resolv(data, hostname, remote_port,
763                                       FALSE, &dns);
764
765       if(rc == CURLRESOLV_ERROR)
766         return CURLPX_RESOLVE_HOST;
767
768       if(rc == CURLRESOLV_PENDING) {
769         sxstate(data, CONNECT_RESOLVING);
770         return CURLPX_OK;
771       }
772       sxstate(data, CONNECT_RESOLVED);
773       goto CONNECT_RESOLVED;
774     }
775     goto CONNECT_RESOLVE_REMOTE;
776
777   case CONNECT_RESOLVING:
778     /* check if we have the name resolved by now */
779     dns = Curl_fetch_addr(data, hostname, remote_port);
780
781     if(dns) {
782 #ifdef CURLRES_ASYNCH
783       data->state.async.dns = dns;
784       data->state.async.done = TRUE;
785 #endif
786       infof(data, "SOCKS5: hostname '%s' found", hostname);
787     }
788
789     if(!dns) {
790       result = Curl_resolv_check(data, &dns);
791       if(!dns) {
792         if(result)
793           return CURLPX_RESOLVE_HOST;
794         return CURLPX_OK;
795       }
796     }
797     /* FALLTHROUGH */
798   CONNECT_RESOLVED:
799   case CONNECT_RESOLVED: {
800     struct Curl_addrinfo *hp = NULL;
801     size_t destlen;
802     if(dns)
803       hp = dns->addr;
804     if(!hp) {
805       failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
806             hostname);
807       return CURLPX_RESOLVE_HOST;
808     }
809
810     Curl_printable_address(hp, dest, sizeof(dest));
811     destlen = strlen(dest);
812     msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
813
814     len = 0;
815     socksreq[len++] = 5; /* version (SOCKS5) */
816     socksreq[len++] = 1; /* connect */
817     socksreq[len++] = 0; /* must be zero */
818     if(hp->ai_family == AF_INET) {
819       int i;
820       struct sockaddr_in *saddr_in;
821       socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
822
823       saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
824       for(i = 0; i < 4; i++) {
825         socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
826       }
827
828       infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
829     }
830 #ifdef ENABLE_IPV6
831     else if(hp->ai_family == AF_INET6) {
832       int i;
833       struct sockaddr_in6 *saddr_in6;
834       socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
835
836       saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
837       for(i = 0; i < 16; i++) {
838         socksreq[len++] =
839           ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
840       }
841
842       infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
843     }
844 #endif
845     else {
846       hp = NULL; /* fail! */
847       failf(data, "SOCKS5 connection to %s not supported", dest);
848     }
849
850     Curl_resolv_unlock(data, dns); /* not used anymore from now on */
851     goto CONNECT_REQ_SEND;
852   }
853   CONNECT_RESOLVE_REMOTE:
854   case CONNECT_RESOLVE_REMOTE:
855     /* Authentication is complete, now specify destination to the proxy */
856     len = 0;
857     socksreq[len++] = 5; /* version (SOCKS5) */
858     socksreq[len++] = 1; /* connect */
859     socksreq[len++] = 0; /* must be zero */
860
861     if(!socks5_resolve_local) {
862       /* ATYP: domain name = 3,
863          IPv6 == 4,
864          IPv4 == 1 */
865       unsigned char ip4[4];
866 #ifdef ENABLE_IPV6
867       if(conn->bits.ipv6_ip) {
868         char ip6[16];
869         if(1 != Curl_inet_pton(AF_INET6, hostname, ip6))
870           return CURLPX_BAD_ADDRESS_TYPE;
871         socksreq[len++] = 4;
872         memcpy(&socksreq[len], ip6, sizeof(ip6));
873         len += sizeof(ip6);
874       }
875       else
876 #endif
877       if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) {
878         socksreq[len++] = 1;
879         memcpy(&socksreq[len], ip4, sizeof(ip4));
880         len += sizeof(ip4);
881       }
882       else {
883         socksreq[len++] = 3;
884         socksreq[len++] = (char) hostname_len; /* one byte address length */
885         memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
886         len += hostname_len;
887       }
888       infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
889             hostname, remote_port);
890     }
891     /* FALLTHROUGH */
892
893   CONNECT_REQ_SEND:
894   case CONNECT_REQ_SEND:
895     /* PORT MSB */
896     socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
897     /* PORT LSB */
898     socksreq[len++] = (unsigned char)(remote_port & 0xff);
899
900 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
901     if(conn->socks5_gssapi_enctype) {
902       failf(data, "SOCKS5 GSS-API protection not yet implemented.");
903       return CURLPX_GSSAPI_PROTECTION;
904     }
905 #endif
906     sx->outp = socksreq;
907     sx->outstanding = len;
908     sxstate(data, CONNECT_REQ_SENDING);
909     /* FALLTHROUGH */
910   case CONNECT_REQ_SENDING:
911     result = Curl_write_plain(data, sockfd, (char *)sx->outp,
912                               sx->outstanding, &written);
913     if(result && (CURLE_AGAIN != result)) {
914       failf(data, "Failed to send SOCKS5 connect request.");
915       return CURLPX_SEND_REQUEST;
916     }
917     if(sx->outstanding != written) {
918       /* remain in state */
919       sx->outstanding -= written;
920       sx->outp += written;
921       return CURLPX_OK;
922     }
923 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
924     if(conn->socks5_gssapi_enctype) {
925       failf(data, "SOCKS5 GSS-API protection not yet implemented.");
926       return CURLPX_GSSAPI_PROTECTION;
927     }
928 #endif
929     sx->outstanding = 10; /* minimum packet size is 10 */
930     sx->outp = socksreq;
931     sxstate(data, CONNECT_REQ_READ);
932     /* FALLTHROUGH */
933   case CONNECT_REQ_READ:
934     result = Curl_read_plain(sockfd, (char *)sx->outp,
935                              sx->outstanding, &actualread);
936     if(result && (CURLE_AGAIN != result)) {
937       failf(data, "Failed to receive SOCKS5 connect request ack.");
938       return CURLPX_RECV_REQACK;
939     }
940     else if(!result && !actualread) {
941       /* connection closed */
942       failf(data, "connection to proxy closed");
943       return CURLPX_CLOSED;
944     }
945     else if(actualread != sx->outstanding) {
946       /* remain in state */
947       sx->outstanding -= actualread;
948       sx->outp += actualread;
949       return CURLPX_OK;
950     }
951
952     if(socksreq[0] != 5) { /* version */
953       failf(data,
954             "SOCKS5 reply has wrong version, version should be 5.");
955       return CURLPX_BAD_VERSION;
956     }
957     else if(socksreq[1]) { /* Anything besides 0 is an error */
958       CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
959       int code = socksreq[1];
960       failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
961             hostname, (unsigned char)socksreq[1]);
962       if(code < 9) {
963         /* RFC 1928 section 6 lists: */
964         static const CURLproxycode lookup[] = {
965           CURLPX_OK,
966           CURLPX_REPLY_GENERAL_SERVER_FAILURE,
967           CURLPX_REPLY_NOT_ALLOWED,
968           CURLPX_REPLY_NETWORK_UNREACHABLE,
969           CURLPX_REPLY_HOST_UNREACHABLE,
970           CURLPX_REPLY_CONNECTION_REFUSED,
971           CURLPX_REPLY_TTL_EXPIRED,
972           CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
973           CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
974         };
975         rc = lookup[code];
976       }
977       return rc;
978     }
979
980     /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
981        1928, so the reply packet should be read until the end to avoid errors
982        at subsequent protocol level.
983
984        +----+-----+-------+------+----------+----------+
985        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
986        +----+-----+-------+------+----------+----------+
987        | 1  |  1  | X'00' |  1   | Variable |    2     |
988        +----+-----+-------+------+----------+----------+
989
990        ATYP:
991        o  IP v4 address: X'01', BND.ADDR = 4 byte
992        o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
993        o  IP v6 address: X'04', BND.ADDR = 16 byte
994     */
995
996     /* Calculate real packet size */
997     if(socksreq[3] == 3) {
998       /* domain name */
999       int addrlen = (int) socksreq[4];
1000       len = 5 + addrlen + 2;
1001     }
1002     else if(socksreq[3] == 4) {
1003       /* IPv6 */
1004       len = 4 + 16 + 2;
1005     }
1006     else if(socksreq[3] == 1) {
1007       len = 4 + 4 + 2;
1008     }
1009     else {
1010       failf(data, "SOCKS5 reply has wrong address type.");
1011       return CURLPX_BAD_ADDRESS_TYPE;
1012     }
1013
1014     /* At this point we already read first 10 bytes */
1015 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1016     if(!conn->socks5_gssapi_enctype) {
1017       /* decrypt_gssapi_blockread already read the whole packet */
1018 #endif
1019       if(len > 10) {
1020         sx->outstanding = len - 10; /* get the rest */
1021         sx->outp = &socksreq[10];
1022         sxstate(data, CONNECT_REQ_READ_MORE);
1023       }
1024       else {
1025         sxstate(data, CONNECT_DONE);
1026         break;
1027       }
1028 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1029     }
1030 #endif
1031     /* FALLTHROUGH */
1032   case CONNECT_REQ_READ_MORE:
1033     result = Curl_read_plain(sockfd, (char *)sx->outp,
1034                              sx->outstanding, &actualread);
1035     if(result && (CURLE_AGAIN != result)) {
1036       failf(data, "Failed to receive SOCKS5 connect request ack.");
1037       return CURLPX_RECV_ADDRESS;
1038     }
1039     else if(!result && !actualread) {
1040       /* connection closed */
1041       failf(data, "connection to proxy closed");
1042       return CURLPX_CLOSED;
1043     }
1044     else if(actualread != sx->outstanding) {
1045       /* remain in state */
1046       sx->outstanding -= actualread;
1047       sx->outp += actualread;
1048       return CURLPX_OK;
1049     }
1050     sxstate(data, CONNECT_DONE);
1051   }
1052   infof(data, "SOCKS5 request granted.");
1053
1054   *done = TRUE;
1055   return CURLPX_OK; /* Proxy was successful! */
1056 }
1057
1058 #endif /* CURL_DISABLE_PROXY */