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