compiler warning fix
[platform/upstream/curl.git] / lib / socks.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2007, 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 http://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  * $Id$
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #include <string.h>
27
28 #ifdef NEED_MALLOC_H
29 #include <malloc.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34
35 #include "urldata.h"
36 #include "sendf.h"
37 #include "strequal.h"
38 #include "select.h"
39 #include "connect.h"
40 #include "timeval.h"
41 #include "socks.h"
42
43 /* The last #include file should be: */
44 #include "memdebug.h"
45
46 /*
47  * Helper read-from-socket functions. Does the same as Curl_read() but it
48  * blocks until all bytes amount of buffersize will be read. No more, no less.
49  *
50  * This is STUPID BLOCKING behaviour which we frown upon, but right now this
51  * is what we have...
52  */
53 static int blockread_all(struct connectdata *conn, /* connection data */
54                          curl_socket_t sockfd,     /* read from this socket */
55                          char *buf,                /* store read data here */
56                          ssize_t buffersize,       /* max amount to read */
57                          ssize_t *n,               /* amount bytes read */
58                          long conn_timeout)        /* timeout for data wait
59                                                       relative to
60                                                       conn->created */
61 {
62   ssize_t nread;
63   ssize_t allread = 0;
64   int result;
65   struct timeval tvnow;
66   long conntime;
67   *n = 0;
68   do {
69     tvnow = Curl_tvnow();
70     /* calculating how long connection is establishing */
71     conntime = Curl_tvdiff(tvnow, conn->created);
72     if(conntime > conn_timeout) {
73       /* we already got the timeout */
74       result = ~CURLE_OK;
75       break;
76     }
77     if(Curl_select(sockfd, CURL_SOCKET_BAD,
78                    (int)(conn_timeout - conntime)) <= 0) {
79       result = ~CURLE_OK;
80       break;
81     }
82     result = Curl_read(conn, sockfd, buf, buffersize, &nread);
83     if(result)
84       break;
85
86     if(buffersize == nread) {
87       allread += nread;
88       *n = allread;
89       result = CURLE_OK;
90       break;
91     }
92     buffersize -= nread;
93     buf += nread;
94     allread += nread;
95   } while(1);
96   return result;
97 }
98
99 /*
100 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
101 * destination server.
102 *
103 * Reference :
104 *   http://socks.permeo.com/protocol/socks4.protocol
105 *
106 * Note :
107 *   Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
108 *   Nonsupport "Identification Protocol (RFC1413)"
109 */
110 CURLcode Curl_SOCKS4(const char *proxy_name,
111                      char *hostname,
112                      int remote_port,
113                      int sockindex,
114                      struct connectdata *conn)
115 {
116   unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
117   int result;
118   CURLcode code;
119   curl_socket_t sock = conn->sock[sockindex];
120   long timeout;
121   struct SessionHandle *data = conn->data;
122
123   /* get timeout */
124   if(data->set.timeout && data->set.connecttimeout) {
125     if (data->set.timeout < data->set.connecttimeout)
126       timeout = data->set.timeout;
127     else
128       timeout = data->set.connecttimeout;
129   }
130   else if(data->set.timeout)
131     timeout = data->set.timeout;
132   else if(data->set.connecttimeout)
133     timeout = data->set.connecttimeout;
134   else
135     timeout = DEFAULT_CONNECT_TIMEOUT;
136
137   Curl_nonblock(sock, FALSE);
138
139   /*
140    * Compose socks4 request
141    *
142    * Request format
143    *
144    *     +----+----+----+----+----+----+----+----+----+----+....+----+
145    *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
146    *     +----+----+----+----+----+----+----+----+----+----+....+----+
147    * # of bytes:  1    1      2              4           variable       1
148    */
149
150   socksreq[0] = 4; /* version (SOCKS4) */
151   socksreq[1] = 1; /* connect */
152   *((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port);
153
154   /* DNS resolve */
155   {
156     struct Curl_dns_entry *dns;
157     Curl_addrinfo *hp=NULL;
158     int rc;
159
160     rc = Curl_resolv(conn, hostname, remote_port, &dns);
161
162     if(rc == CURLRESOLV_ERROR)
163       return CURLE_COULDNT_RESOLVE_PROXY;
164
165     if(rc == CURLRESOLV_PENDING)
166       /* this requires that we're in "wait for resolve" state */
167       rc = Curl_wait_for_resolv(conn, &dns);
168
169     /*
170      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
171      * returns a Curl_addrinfo pointer that may not always look the same.
172      */
173     if(dns)
174       hp=dns->addr;
175     if (hp) {
176       char buf[64];
177       unsigned short ip[4];
178       Curl_printable_address(hp, buf, sizeof(buf));
179
180       if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
181                       &ip[0], &ip[1], &ip[2], &ip[3])) {
182         /* Set DSTIP */
183         socksreq[4] = (unsigned char)ip[0];
184         socksreq[5] = (unsigned char)ip[1];
185         socksreq[6] = (unsigned char)ip[2];
186         socksreq[7] = (unsigned char)ip[3];
187       }
188       else
189         hp = NULL; /* fail! */
190
191       Curl_resolv_unlock(data, dns); /* not used anymore from now on */
192
193     }
194     if(!hp) {
195       failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
196             hostname);
197       return CURLE_COULDNT_RESOLVE_HOST;
198     }
199   }
200
201   /*
202    * This is currently not supporting "Identification Protocol (RFC1413)".
203    */
204   socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
205   if (proxy_name)
206     strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
207
208   /*
209    * Make connection
210    */
211   {
212     ssize_t actualread;
213     ssize_t written;
214     int packetsize = 9 +
215       (int)strlen((char*)socksreq + 8); /* size including NUL */
216
217     /* Send request */
218     code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
219     if ((code != CURLE_OK) || (written != packetsize)) {
220       failf(data, "Failed to send SOCKS4 connect request.");
221       return CURLE_COULDNT_CONNECT;
222     }
223
224     packetsize = 8; /* receive data size */
225
226     /* Receive response */
227     result = blockread_all(conn, sock, (char *)socksreq, packetsize,
228                            &actualread, timeout);
229     if ((result != CURLE_OK) || (actualread != packetsize)) {
230       failf(data, "Failed to receive SOCKS4 connect request ack.");
231       return CURLE_COULDNT_CONNECT;
232     }
233
234     /*
235      * Response format
236      *
237      *     +----+----+----+----+----+----+----+----+
238      *     | VN | CD | DSTPORT |      DSTIP        |
239      *     +----+----+----+----+----+----+----+----+
240      * # of bytes:  1    1      2              4
241      *
242      * VN is the version of the reply code and should be 0. CD is the result
243      * code with one of the following values:
244      *
245      * 90: request granted
246      * 91: request rejected or failed
247      * 92: request rejected because SOCKS server cannot connect to
248      *     identd on the client
249      * 93: request rejected because the client program and identd
250      *     report different user-ids
251      */
252
253     /* wrong version ? */
254     if (socksreq[0] != 0) {
255       failf(data,
256             "SOCKS4 reply has wrong version, version should be 4.");
257       return CURLE_COULDNT_CONNECT;
258     }
259
260     /* Result */
261     switch(socksreq[1])
262     {
263     case 90:
264       infof(data, "SOCKS4 request granted.\n");
265       break;
266     case 91:
267       failf(data,
268             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
269             ", request rejected or failed.",
270             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
271             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
272             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
273             socksreq[1]);
274       return CURLE_COULDNT_CONNECT;
275     case 92:
276       failf(data,
277             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
278             ", request rejected because SOCKS server cannot connect to "
279             "identd on the client.",
280             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
281             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
282             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
283             socksreq[1]);
284       return CURLE_COULDNT_CONNECT;
285     case 93:
286       failf(data,
287             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
288             ", request rejected because the client program and identd "
289             "report different user-ids.",
290             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
291             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
292             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
293             socksreq[1]);
294       return CURLE_COULDNT_CONNECT;
295     default:
296       failf(data,
297             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
298             ", Unknown.",
299             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
300             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
301             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
302             socksreq[1]);
303       return CURLE_COULDNT_CONNECT;
304     }
305   }
306
307   Curl_nonblock(sock, TRUE);
308
309   return CURLE_OK; /* Proxy was successful! */
310 }
311
312 /*
313  * This function logs in to a SOCKS5 proxy and sends the specifics to the final
314  * destination server.
315  */
316 CURLcode Curl_SOCKS5(const char *proxy_name,
317                      const char *proxy_password,
318                      char *hostname,
319                      int remote_port,
320                      int sockindex,
321                      struct connectdata *conn)
322 {
323   /*
324     According to the RFC1928, section "6.  Replies". This is what a SOCK5
325     replies:
326
327         +----+-----+-------+------+----------+----------+
328         |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
329         +----+-----+-------+------+----------+----------+
330         | 1  |  1  | X'00' |  1   | Variable |    2     |
331         +----+-----+-------+------+----------+----------+
332
333     Where:
334
335     o  VER    protocol version: X'05'
336     o  REP    Reply field:
337     o  X'00' succeeded
338   */
339
340   unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
341   ssize_t actualread;
342   ssize_t written;
343   int result;
344   CURLcode code;
345   curl_socket_t sock = conn->sock[sockindex];
346   struct SessionHandle *data = conn->data;
347   long timeout;
348
349   /* get timeout */
350   if(data->set.timeout && data->set.connecttimeout) {
351     if (data->set.timeout < data->set.connecttimeout)
352       timeout = data->set.timeout;
353     else
354       timeout = data->set.connecttimeout;
355   }
356   else if(data->set.timeout)
357     timeout = data->set.timeout;
358   else if(data->set.connecttimeout)
359     timeout = data->set.connecttimeout;
360   else
361     timeout = DEFAULT_CONNECT_TIMEOUT;
362
363   Curl_nonblock(sock, TRUE);
364
365   /* wait until socket gets connected */
366   result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout);
367
368   if(-1 == result) {
369     failf(conn->data, "SOCKS5: no connection here");
370     return CURLE_COULDNT_CONNECT;
371   }
372   else if(0 == result) {
373     failf(conn->data, "SOCKS5: connection timeout");
374     return CURLE_OPERATION_TIMEDOUT;
375   }
376
377   if(result & CSELECT_ERR) {
378     failf(conn->data, "SOCKS5: error occured during connection");
379     return CURLE_COULDNT_CONNECT;
380   }
381
382   socksreq[0] = 5; /* version */
383   socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
384   socksreq[2] = 0; /* no authentication */
385   socksreq[3] = 2; /* username/password */
386
387   Curl_nonblock(sock, FALSE);
388
389   code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
390                       &written);
391   if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
392     failf(data, "Unable to send initial SOCKS5 request.");
393     return CURLE_COULDNT_CONNECT;
394   }
395
396   Curl_nonblock(sock, TRUE);
397
398   result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout);
399
400   if(-1 == result) {
401     failf(conn->data, "SOCKS5 nothing to read");
402     return CURLE_COULDNT_CONNECT;
403   }
404   else if(0 == result) {
405     failf(conn->data, "SOCKS5 read timeout");
406     return CURLE_OPERATION_TIMEDOUT;
407   }
408
409   if(result & CSELECT_ERR) {
410     failf(conn->data, "SOCKS5 read error occured");
411     return CURLE_RECV_ERROR;
412   }
413
414   Curl_nonblock(sock, FALSE);
415
416   result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
417   if ((result != CURLE_OK) || (actualread != 2)) {
418     failf(data, "Unable to receive initial SOCKS5 response.");
419     return CURLE_COULDNT_CONNECT;
420   }
421
422   if (socksreq[0] != 5) {
423     failf(data, "Received invalid version in initial SOCKS5 response.");
424     return CURLE_COULDNT_CONNECT;
425   }
426   if (socksreq[1] == 0) {
427     /* Nothing to do, no authentication needed */
428     ;
429   }
430   else if (socksreq[1] == 2) {
431     /* Needs user name and password */
432     size_t userlen, pwlen;
433     int len;
434     if(proxy_name && proxy_password) {
435       userlen = strlen(proxy_name);
436       pwlen = proxy_password?strlen(proxy_password):0;
437     }
438     else {
439       userlen = 0;
440       pwlen = 0;
441     }
442
443     /*   username/password request looks like
444      * +----+------+----------+------+----------+
445      * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
446      * +----+------+----------+------+----------+
447      * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
448      * +----+------+----------+------+----------+
449      */
450     len = 0;
451     socksreq[len++] = 1;    /* username/pw subnegotiation version */
452     socksreq[len++] = (char) userlen;
453     memcpy(socksreq + len, proxy_name, (int) userlen);
454     len += userlen;
455     socksreq[len++] = (char) pwlen;
456     memcpy(socksreq + len, proxy_password, (int) pwlen);
457     len += pwlen;
458
459     code = Curl_write(conn, sock, (char *)socksreq, len, &written);
460     if ((code != CURLE_OK) || (len != written)) {
461       failf(data, "Failed to send SOCKS5 sub-negotiation request.");
462       return CURLE_COULDNT_CONNECT;
463     }
464
465     result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
466                          timeout);
467     if ((result != CURLE_OK) || (actualread != 2)) {
468       failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
469       return CURLE_COULDNT_CONNECT;
470     }
471
472     /* ignore the first (VER) byte */
473     if (socksreq[1] != 0) { /* status */
474       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
475             socksreq[0], socksreq[1]);
476       return CURLE_COULDNT_CONNECT;
477     }
478
479     /* Everything is good so far, user was authenticated! */
480   }
481   else {
482     /* error */
483     if (socksreq[1] == 1) {
484       failf(data,
485             "SOCKS5 GSSAPI per-message authentication is not supported.");
486       return CURLE_COULDNT_CONNECT;
487     }
488     else if (socksreq[1] == 255) {
489       if (!proxy_name || !*proxy_name) {
490         failf(data,
491               "No authentication method was acceptable. (It is quite likely"
492               " that the SOCKS5 server wanted a username/password, since none"
493               " was supplied to the server on this connection.)");
494       }
495       else {
496         failf(data, "No authentication method was acceptable.");
497       }
498       return CURLE_COULDNT_CONNECT;
499     }
500     else {
501       failf(data,
502             "Undocumented SOCKS5 mode attempted to be used by server.");
503       return CURLE_COULDNT_CONNECT;
504     }
505   }
506
507   /* Authentication is complete, now specify destination to the proxy */
508   socksreq[0] = 5; /* version (SOCKS5) */
509   socksreq[1] = 1; /* connect */
510   socksreq[2] = 0; /* must be zero */
511   socksreq[3] = 1; /* IPv4 = 1 */
512
513   {
514     struct Curl_dns_entry *dns;
515     Curl_addrinfo *hp=NULL;
516     int rc = Curl_resolv(conn, hostname, remote_port, &dns);
517
518     if(rc == CURLRESOLV_ERROR)
519       return CURLE_COULDNT_RESOLVE_HOST;
520
521     if(rc == CURLRESOLV_PENDING)
522       /* this requires that we're in "wait for resolve" state */
523       rc = Curl_wait_for_resolv(conn, &dns);
524
525     /*
526      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
527      * returns a Curl_addrinfo pointer that may not always look the same.
528      */
529     if(dns)
530       hp=dns->addr;
531     if (hp) {
532       char buf[64];
533       unsigned short ip[4];
534       Curl_printable_address(hp, buf, sizeof(buf));
535
536       if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
537                       &ip[0], &ip[1], &ip[2], &ip[3])) {
538         socksreq[4] = (unsigned char)ip[0];
539         socksreq[5] = (unsigned char)ip[1];
540         socksreq[6] = (unsigned char)ip[2];
541         socksreq[7] = (unsigned char)ip[3];
542       }
543       else
544         hp = NULL; /* fail! */
545
546       Curl_resolv_unlock(data, dns); /* not used anymore from now on */
547     }
548     if(!hp) {
549       failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
550             hostname);
551       return CURLE_COULDNT_RESOLVE_HOST;
552     }
553   }
554
555   *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
556
557   {
558     const int packetsize = 10;
559
560     code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
561     if ((code != CURLE_OK) || (written != packetsize)) {
562       failf(data, "Failed to send SOCKS5 connect request.");
563       return CURLE_COULDNT_CONNECT;
564     }
565
566     result = blockread_all(conn, sock, (char *)socksreq, packetsize,
567                            &actualread, timeout);
568     if ((result != CURLE_OK) || (actualread != packetsize)) {
569       failf(data, "Failed to receive SOCKS5 connect request ack.");
570       return CURLE_COULDNT_CONNECT;
571     }
572
573     if (socksreq[0] != 5) { /* version */
574       failf(data,
575             "SOCKS5 reply has wrong version, version should be 5.");
576       return CURLE_COULDNT_CONNECT;
577     }
578     if (socksreq[1] != 0) { /* Anything besides 0 is an error */
579         failf(data,
580               "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
581               (unsigned char)socksreq[4], (unsigned char)socksreq[5],
582               (unsigned char)socksreq[6], (unsigned char)socksreq[7],
583               (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
584               socksreq[1]);
585         return CURLE_COULDNT_CONNECT;
586     }
587   }
588
589   Curl_nonblock(sock, TRUE);
590   return CURLE_OK; /* Proxy was successful! */
591 }