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