connect.c 19 KB

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