]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/connect.c
hello world
[icculus/iodoom3.git] / neo / curl / lib / connect.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, 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: connect.c,v 1.82 2004/03/17 12:46:45 bagder Exp $
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #ifndef WIN32
27 /* headers for non-win32 */
28 #include <sys/time.h>
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32 #ifdef HAVE_SYS_SOCKET_H
33 #include <sys/socket.h>
34 #endif
35 #include <sys/ioctl.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #ifdef HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 #ifdef HAVE_FCNTL_H
43 #include <fcntl.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
50 #endif
51 #ifdef HAVE_STDLIB_H
52 #include <stdlib.h> /* required for free() prototype, without it, this crashes
53                        on macos 68K */
54 #endif
55 #if (defined(HAVE_FIONBIO) && defined(__NOVELL_LIBC__))
56 #include <sys/filio.h>
57 #endif
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #undef in_addr_t
60 #define in_addr_t unsigned long
61 #endif
62 #ifdef  VMS
63 #include <in.h>
64 #include <inet.h>
65 #endif
66
67 #endif
68 #include <stdio.h>
69 #include <errno.h>
70 #include <string.h>
71
72 #ifndef TRUE
73 #define TRUE 1
74 #define FALSE 0
75 #endif
76
77 #ifdef WIN32
78 #include <windows.h>
79 #define EINPROGRESS WSAEINPROGRESS
80 #define EWOULDBLOCK WSAEWOULDBLOCK
81 #define EISCONN     WSAEISCONN
82 #define ENOTSOCK    WSAENOTSOCK
83 #define ECONNREFUSED WSAECONNREFUSED
84 #endif
85
86 #include "urldata.h"
87 #include "sendf.h"
88 #include "if2ip.h"
89 #include "connect.h"
90
91 /* The last #include file should be: */
92 #ifdef CURLDEBUG
93 #include "memdebug.h"
94 #endif
95
96 static bool verifyconnect(curl_socket_t sockfd);
97
98 int Curl_ourerrno(void)
99 {
100 #ifdef WIN32
101   return (int)GetLastError();
102 #else
103   return errno;
104 #endif
105 }
106
107 /*************************************************************************
108  * Curl_nonblock
109  *
110  * Description:
111  *  Set the socket to either blocking or non-blocking mode.
112  */
113
114 int Curl_nonblock(curl_socket_t sockfd,    /* operate on this */
115                   int nonblock   /* TRUE or FALSE */)
116 {
117 #undef SETBLOCK
118 #ifdef HAVE_O_NONBLOCK
119   /* most recent unix versions */
120   int flags;
121
122   flags = fcntl(sockfd, F_GETFL, 0);
123   if (TRUE == nonblock)
124     return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
125   else
126     return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
127 #define SETBLOCK 1
128 #endif
129
130 #ifdef HAVE_FIONBIO
131   /* older unix versions */
132   int flags;
133
134   flags = nonblock;
135   return ioctl(sockfd, FIONBIO, &flags);
136 #define SETBLOCK 2
137 #endif
138
139 #ifdef HAVE_IOCTLSOCKET
140   /* Windows? */
141   int flags;
142   flags = nonblock;
143   return ioctlsocket(sockfd, FIONBIO, &flags);
144 #define SETBLOCK 3
145 #endif
146
147 #ifdef HAVE_IOCTLSOCKET_CASE
148   /* presumably for Amiga */
149   return IoctlSocket(sockfd, FIONBIO, (long)nonblock);
150 #define SETBLOCK 4
151 #endif
152
153 #ifdef HAVE_SO_NONBLOCK
154   /* BeOS */
155   long b = nonblock ? 1 : 0;
156   return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
157 #define SETBLOCK 5
158 #endif
159
160 #ifdef HAVE_DISABLED_NONBLOCKING
161   return 0; /* returns success */
162 #define SETBLOCK 6
163 #endif
164
165 #ifndef SETBLOCK
166 #error "no non-blocking method was found/used/set"
167 #endif
168 }
169
170 /*
171  * waitconnect() returns:
172  * 0    fine connect
173  * -1   select() error
174  * 1    select() timeout
175  * 2    select() returned with an error condition
176  */
177 static
178 int waitconnect(curl_socket_t sockfd, /* socket */
179                 long timeout_msec)
180 {
181   fd_set fd;
182   fd_set errfd;
183   struct timeval interval;
184   int rc;
185 #ifdef mpeix
186   /* Call this function once now, and ignore the results. We do this to
187      "clear" the error state on the socket so that we can later read it
188      reliably. This is reported necessary on the MPE/iX operating system. */
189   verifyconnect(sockfd);
190 #endif
191
192   /* now select() until we get connect or timeout */
193   FD_ZERO(&fd);
194   FD_SET(sockfd, &fd);
195
196   FD_ZERO(&errfd);
197   FD_SET(sockfd, &errfd);
198
199   interval.tv_sec = timeout_msec/1000;
200   timeout_msec -= interval.tv_sec*1000;
201
202   interval.tv_usec = timeout_msec*1000;
203
204   rc = select(sockfd+1, NULL, &fd, &errfd, &interval);
205   if(-1 == rc)
206     /* error, no connect here, try next */
207     return -1;
208   
209   else if(0 == rc)
210     /* timeout, no connect today */
211     return 1;
212
213   if(FD_ISSET(sockfd, &errfd))
214     /* error condition caught */
215     return 2;
216
217   /* we have a connect! */
218   return 0;
219 }
220
221 static CURLcode bindlocal(struct connectdata *conn,
222                           curl_socket_t sockfd)
223 {
224 #ifdef HAVE_INET_NTOA
225   bool bindworked = FALSE;
226   struct SessionHandle *data = conn->data;
227
228   /*************************************************************
229    * Select device to bind socket to
230    *************************************************************/
231   if (strlen(data->set.device)<255) {
232     struct Curl_dns_entry *h=NULL;
233     size_t size;
234     char myhost[256] = "";
235     in_addr_t in;
236     int rc;
237     bool was_iface = FALSE;
238
239     /* First check if the given name is an IP address */
240     in=inet_addr(data->set.device);
241       
242     if((in == CURL_INADDR_NONE) &&
243        Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
244       /*
245        * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
246        */
247       rc = Curl_resolv(conn, myhost, 0, &h);
248       if(rc == 1)
249         (void)Curl_wait_for_resolv(conn, &h);
250
251       if(h)
252         was_iface = TRUE;
253     }
254
255     if(!was_iface) {
256       /*
257        * This was not an interface, resolve the name as a host name
258        * or IP number
259        */
260       rc = Curl_resolv(conn, data->set.device, 0, &h);
261       if(rc == 1)
262         (void)Curl_wait_for_resolv(conn, &h);
263
264       if(h)
265         /* we know data->set.device is shorter than the myhost array */
266         strcpy(myhost, data->set.device);
267     }
268
269     if(! *myhost) {
270       /* need to fix this
271          h=Curl_gethost(data,
272          getmyhost(*myhost,sizeof(myhost)),
273          hostent_buf,
274          sizeof(hostent_buf));
275       */
276       failf(data, "Couldn't bind to '%s'", data->set.device);
277       return CURLE_HTTP_PORT_FAILED;
278     }
279
280     infof(data, "We bind local end to %s\n", myhost);
281
282 #ifdef SO_BINDTODEVICE
283     /* I am not sure any other OSs than Linux that provide this feature, and
284      * at the least I cannot test. --Ben
285      *
286      * This feature allows one to tightly bind the local socket to a
287      * particular interface.  This will force even requests to other local
288      * interfaces to go out the external interface.
289      *
290      */
291     if (was_iface) {
292       /* Only bind to the interface when specified as interface, not just as a
293        * hostname or ip address.
294        */
295       if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
296                      data->set.device, strlen(data->set.device)+1) != 0) {
297         /* printf("Failed to BINDTODEVICE, socket: %d  device: %s error: %s\n",
298            sockfd, data->set.device, strerror(errno)); */
299         infof(data, "SO_BINDTODEVICE %s failed\n",
300               data->set.device);
301         /* This is typiclally "errno 1, error: Operation not permitted" if
302            you're not running as root or another suitable privileged user */
303       }
304     }
305 #endif
306
307     in=inet_addr(myhost);
308     if (CURL_INADDR_NONE != in) {
309
310       if ( h ) {
311         Curl_addrinfo *addr = h->addr;
312
313         Curl_resolv_unlock(data, h);
314         /* we don't need it anymore after this function has returned */
315
316 #ifdef ENABLE_IPV6
317         if( bind(sockfd, addr->ai_addr, addr->ai_addrlen) >= 0) {
318           /* we succeeded to bind */
319           struct sockaddr_in6 add;
320
321           bindworked = TRUE;
322         
323           size = sizeof(add);
324           if(getsockname(sockfd, (struct sockaddr *) &add,
325                          (socklen_t *)&size)<0) {
326             failf(data, "getsockname() failed");
327             return CURLE_HTTP_PORT_FAILED;
328           }
329         }
330 #else
331         {
332           struct sockaddr_in sa;
333
334           memset((char *)&sa, 0, sizeof(sa));
335           memcpy((char *)&sa.sin_addr, addr->h_addr, addr->h_length);
336           sa.sin_family = AF_INET;
337           sa.sin_addr.s_addr = in;
338           sa.sin_port = 0; /* get any port */
339         
340           if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) {
341             /* we succeeded to bind */
342             struct sockaddr_in add;
343         
344             bindworked = TRUE;
345             
346             size = sizeof(add);
347             if(getsockname(sockfd, (struct sockaddr *) &add,
348                            (socklen_t *)&size)<0) {
349               failf(data, "getsockname() failed");
350               return CURLE_HTTP_PORT_FAILED;
351             }
352           }
353         }
354 #endif
355         if(!bindworked) {
356           switch(errno) {
357           case EBADF:
358             failf(data, "Invalid descriptor: %d", errno);
359             break;
360           case EINVAL:
361             failf(data, "Invalid request: %d", errno);
362             break;
363           case EACCES:
364             failf(data, "Address is protected, user not superuser: %d", errno);
365             break;
366           case ENOTSOCK:
367             failf(data,
368                   "Argument is a descriptor for a file, not a socket: %d",
369                   errno);
370             break;
371           case EFAULT:
372             failf(data, "Inaccessable memory error: %d", errno);
373             break;
374           case ENAMETOOLONG:
375             failf(data, "Address too long: %d", errno);
376             break;
377           case ENOMEM:
378             failf(data, "Insufficient kernel memory was available: %d", errno);
379             break;
380           default:
381             failf(data, "errno %d", errno);
382             break;
383           } /* end of switch(errno) */
384         
385           return CURLE_HTTP_PORT_FAILED;
386         } /* end of else */
387         
388       } /* end of if  h */
389       else {
390         failf(data,"could't find my own IP address (%s)", myhost);
391         return CURLE_HTTP_PORT_FAILED;
392       }
393     } /* end of inet_addr */
394
395     else {
396       failf(data, "could't find my own IP address (%s)", myhost);
397       return CURLE_HTTP_PORT_FAILED;
398     }
399
400     return CURLE_OK;
401
402   } /* end of device selection support */
403 #endif /* end of HAVE_INET_NTOA */
404
405   return CURLE_HTTP_PORT_FAILED;
406 }
407
408 /*
409  * verifyconnect() returns TRUE if the connect really has happened.
410  */
411 static bool verifyconnect(curl_socket_t sockfd)
412 {
413 #if defined(SO_ERROR) && !defined(WIN32)
414   int err = 0;
415   socklen_t errSize = sizeof(err);
416   if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
417                        (void *)&err, &errSize))
418     err = Curl_ourerrno();
419
420   if ((0 == err) || (EISCONN == err))
421     /* we are connected, awesome! */
422     return TRUE;
423   
424   /* This wasn't a successful connect */
425   return FALSE;
426 #else
427   (void)sockfd;
428   return TRUE;
429 #endif
430 }
431
432 /*
433  * Curl_is_connected() is used from the multi interface to check if the
434  * firstsocket has connected.
435  */
436
437 CURLcode Curl_is_connected(struct connectdata *conn,
438                            curl_socket_t sockfd,
439                            bool *connected)
440 {
441   int rc;
442   struct SessionHandle *data = conn->data;
443
444   *connected = FALSE; /* a very negative world view is best */
445
446   if(data->set.timeout || data->set.connecttimeout) {
447     /* there is a timeout set */
448
449     /* Evaluate in milliseconds how much time that has passed */
450     long has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
451
452     /* subtract the most strict timeout of the ones */
453     if(data->set.timeout && data->set.connecttimeout) {
454       if (data->set.timeout < data->set.connecttimeout)
455         has_passed -= data->set.timeout*1000;
456       else 
457         has_passed -= data->set.connecttimeout*1000;
458     }
459     else if(data->set.timeout)
460       has_passed -= data->set.timeout*1000;
461     else
462       has_passed -= data->set.connecttimeout*1000;
463
464     if(has_passed > 0 ) {
465       /* time-out, bail out, go home */
466       failf(data, "Connection time-out");
467       return CURLE_OPERATION_TIMEOUTED;
468     }
469   }
470   if(conn->bits.tcpconnect) {
471     /* we are connected already! */
472     *connected = TRUE;
473     return CURLE_OK;
474   }
475
476   /* check for connect without timeout as we want to return immediately */
477   rc = waitconnect(sockfd, 0);
478
479   if(0 == rc) {
480     if (verifyconnect(sockfd)) {
481       /* we are connected, awesome! */
482       *connected = TRUE;
483       return CURLE_OK;
484     }
485     /* nope, not connected for real */
486     failf(data, "Connection failed");
487     return CURLE_COULDNT_CONNECT;
488   }
489   else if(1 != rc) {
490     int error = Curl_ourerrno();
491     failf(data, "Failed connect to %s:%d, errno: %d",
492           conn->hostname, conn->port, error);
493     return CURLE_COULDNT_CONNECT;
494   }
495   /*
496    * If the connection phase is "done" here, we should attempt to connect
497    * to the "next address" in the Curl_hostaddr structure that we resolved
498    * before. But we don't have that struct around anymore and we can't just
499    * keep a pointer since the cache might in fact have gotten pruned by the
500    * time we want to read this... Alas, we don't do this yet.
501    */
502
503   return CURLE_OK;
504 }
505
506
507 /*
508  * TCP connect to the given host with timeout, proxy or remote doesn't matter.
509  * There might be more than one IP address to try out. Fill in the passed
510  * pointer with the connected socket.
511  */
512
513 CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
514                           struct Curl_dns_entry *remotehost, /* use this one */
515                           int port,                  /* connect to this */
516                           curl_socket_t *sockconn,   /* the connected socket */
517                           Curl_ipconnect **addr,     /* the one we used */
518                           bool *connected)           /* really connected? */
519 {
520   struct SessionHandle *data = conn->data;
521   int rc;
522   curl_socket_t sockfd= CURL_SOCKET_BAD;
523   int aliasindex=0;
524   char *hostname;
525
526   struct timeval after;
527   struct timeval before = Curl_tvnow();
528
529 #ifdef ENABLE_IPV6
530   struct addrinfo *ai;
531 #endif  
532
533   /*************************************************************
534    * Figure out what maximum time we have left
535    *************************************************************/
536   long timeout_ms=300000; /* milliseconds, default to five minutes */
537
538   *connected = FALSE; /* default to not connected */
539
540   if(data->set.timeout || data->set.connecttimeout) {
541     double has_passed;
542
543     /* Evaluate in milliseconds how much time that has passed */
544     has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
545
546 #ifndef min
547 #define min(a, b)   ((a) < (b) ? (a) : (b))
548 #endif
549
550     /* get the most strict timeout of the ones converted to milliseconds */
551     if(data->set.timeout && data->set.connecttimeout) {
552       if (data->set.timeout < data->set.connecttimeout)
553         timeout_ms = data->set.timeout*1000;
554       else 
555         timeout_ms = data->set.connecttimeout*1000;
556     }
557     else if(data->set.timeout)
558       timeout_ms = data->set.timeout*1000;
559     else
560       timeout_ms = data->set.connecttimeout*1000;
561
562     /* subtract the passed time */
563     timeout_ms -= (long)has_passed;
564
565     if(timeout_ms < 0) {
566       /* a precaution, no need to continue if time already is up */
567       failf(data, "Connection time-out");
568       return CURLE_OPERATION_TIMEOUTED;
569     }
570   }
571
572   hostname = data->change.proxy?conn->proxyhost:conn->hostname;
573   infof(data, "About to connect() to %s port %d\n",
574         hostname, port);
575
576 #ifdef ENABLE_IPV6
577   /*
578    * Connecting with a getaddrinfo chain
579    */
580   for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) {
581     sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
582     if (sockfd == CURL_SOCKET_BAD)
583       continue;
584 #else
585   /*
586    * Connecting with old style IPv4-only support
587    */
588
589   /* This is the loop that attempts to connect to all IP-addresses we
590      know for the given host. One by one. */
591   for(rc=-1, aliasindex=0;
592       rc && (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
593       aliasindex++) {
594     struct sockaddr_in serv_addr;
595
596     /* create an IPv4 TCP socket */
597     sockfd = socket(AF_INET, SOCK_STREAM, 0);
598     if(CURL_SOCKET_BAD == sockfd) {
599       failf(data, "couldn't create socket");
600       return CURLE_COULDNT_CONNECT; /* big time error */
601     }
602
603     /* nasty address work before connect can be made */
604     memset((char *) &serv_addr, '\0', sizeof(serv_addr));
605     memcpy((char *)&(serv_addr.sin_addr),
606            (struct in_addr *)remotehost->addr->h_addr_list[aliasindex],
607            sizeof(struct in_addr));
608     serv_addr.sin_family = remotehost->addr->h_addrtype;
609     serv_addr.sin_port = htons((unsigned short)port);
610 #endif
611
612     if(conn->data->set.device) {
613       /* user selected to bind the outgoing socket to a specified "device"
614          before doing connect */
615       CURLcode res = bindlocal(conn, sockfd);
616       if(res)
617         return res;
618     }
619
620     /* set socket non-blocking */
621     Curl_nonblock(sockfd, TRUE);
622
623     /* do not use #ifdef within the function arguments below, as connect() is
624        a defined macro on some platforms and some compilers don't like to mix
625        #ifdefs with macro usage! (AmigaOS is one such platform) */
626 #ifdef ENABLE_IPV6
627     rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
628 #else
629     rc = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
630 #endif
631
632     if(-1 == rc) {
633       int error=Curl_ourerrno();
634
635       switch (error) {
636       case EINPROGRESS:
637       case EWOULDBLOCK:
638 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
639         /* On some platforms EAGAIN and EWOULDBLOCK are the
640          * same value, and on others they are different, hence
641          * the odd #if
642          */
643       case EAGAIN:
644 #endif
645         /* asynchronous connect, wait for connect or timeout */
646         if(data->state.used_interface == Curl_if_multi)
647           /* don't hang when doing multi */
648           timeout_ms = 0;
649         
650         rc = waitconnect(sockfd, timeout_ms);
651         break;
652       default:
653         /* unknown error, fallthrough and try another address! */
654         failf(data, "Failed to connect to %s IP number %d: %d",
655               hostname, aliasindex+1, error);
656         break;
657       }
658     }
659
660     /* The '1 == rc' comes from the waitconnect(), and not from connect().
661        We can be sure of this since connect() cannot return 1. */
662     if((1 == rc) && (data->state.used_interface == Curl_if_multi)) {
663       /* Timeout when running the multi interface, we return here with a
664          CURLE_OK return code. */
665       rc = 0;
666       break;
667     }
668       
669     if(0 == rc) {
670       if (verifyconnect(sockfd)) {
671         /* we are connected, awesome! */
672         *connected = TRUE; /* this is a true connect */
673         break;
674       }
675       /* nope, not connected for real */
676       rc = -1;
677     }
678
679     /* connect failed or timed out */
680     sclose(sockfd);
681     sockfd = -1;
682
683     /* get a new timeout for next attempt */
684     after = Curl_tvnow();
685     timeout_ms -= Curl_tvdiff(after, before);
686     if(timeout_ms < 0) {
687       failf(data, "connect() timed out!");
688       return CURLE_OPERATION_TIMEOUTED;
689     }
690     before = after;
691   }
692   if (sockfd == CURL_SOCKET_BAD) {
693     /* no good connect was made */
694     *sockconn = -1;
695     failf(data, "Connect failed");
696     return CURLE_COULDNT_CONNECT;
697   }
698
699   /* leave the socket in non-blocking mode */
700
701   /* store the address we use */
702   if(addr) {
703 #ifdef ENABLE_IPV6
704     *addr = ai;
705 #else
706     *addr = (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
707 #endif
708   }
709
710   /* allow NULL-pointers to get passed in */
711   if(sockconn)
712     *sockconn = sockfd;    /* the socket descriptor we've connected */
713
714   return CURLE_OK;
715 }