/* * verifyconnect() returns TRUE if the connect really has happened. */ static bool verifyconnect(curl_socket_t sockfd, int *error) { bool rc = TRUE; #ifdef SO_ERROR int err = 0; socklen_t errSize = sizeof(err); #ifdef WIN32 /* * In October 2003 we effectively nullified this function on Windows due to * problems with it using all CPU in multi-threaded cases. * * In May 2004, we bring it back to offer more info back on connect failures. * Gisle Vanem could reproduce the former problems with this function, but * could avoid them by adding this SleepEx() call below: * * "I don't have Rational Quantify, but the hint from his post was * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe * just Sleep(0) would be enough?) would release whatever * mutex/critical-section the ntdll call is waiting on. * * Someone got to verify this on Win-NT 4.0, 2000." */ #ifdef _WIN32_WCE Sleep(0); #else SleepEx(0, FALSE); #endif #endif if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) err = Curl_sockerrno(); #ifdef _WIN32_WCE /* Always returns this error, bug in CE? */ if(WSAENOPROTOOPT==err) err=0; #endif if ((0 == err) || (EISCONN == err)) /* we are connected, awesome! */ rc = TRUE; else /* This wasn't a successful connect */ rc = FALSE; if (error) *error = err; #else (void)sockfd; if (error) *error = Curl_sockerrno(); #endif return rc; }
static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) { #ifdef TCP_NODELAY struct SessionHandle *data= conn->data; socklen_t onoff = (socklen_t) data->set.tcp_nodelay; int proto = IPPROTO_TCP; #ifdef HAVE_GETPROTOBYNAME struct protoent *pe = getprotobyname("tcp"); if (pe) proto = pe->p_proto; #endif if(setsockopt(sockfd, proto, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) infof(data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(conn, Curl_sockerrno())); else infof(data,"TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; #endif }
static ssize_t Curl_plain_send(struct connectdata *conn, int num, void *mem, size_t len) { curl_socket_t sockfd = conn->sock[num]; ssize_t bytes_written = swrite(sockfd, mem, len); if(-1 == bytes_written) { int err = Curl_sockerrno(); if( #ifdef WSAEWOULDBLOCK /* This is how Windows does it */ (WSAEWOULDBLOCK == err) #else /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned due to its inability to send off data without blocking. We therefor treat both error codes the same here */ (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) #endif ) /* this is just a case of EWOULDBLOCK */ bytes_written=0; else failf(conn->data, "Send failure: %s", Curl_strerror(conn, err)); } return bytes_written; }
/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when sending data to a dead peer (instead of relying on the 4th argument to send being MSG_NOSIGNAL). Possibly also existing and in use on other BSD systems? */ static void nosigpipe(struct connectdata *conn, curl_socket_t sockfd) { struct SessionHandle *data= conn->data; int onoff = 1; if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, sizeof(onoff)) < 0) infof(data, "Could not set SO_NOSIGPIPE: %s\n", Curl_strerror(conn, Curl_sockerrno())); }
static void dump_addrinfo(struct connectdata *conn, const struct addrinfo *ai) { printf("dump_addrinfo:\n"); for ( ; ai; ai = ai->ai_next) { char buf[INET6_ADDRSTRLEN]; printf(" fam %2d, CNAME %s, ", ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>"); if (Curl_printable_address(ai, buf, sizeof(buf))) printf("%s\n", buf); else printf("failed; %s\n", Curl_strerror(conn, Curl_sockerrno())); } }
static void send_negotiation(struct connectdata *conn, int cmd, int option) { unsigned char buf[3]; ssize_t bytes_written; int err; struct SessionHandle *data = conn->data; buf[0] = CURL_IAC; buf[1] = (unsigned char)cmd; buf[2] = (unsigned char)option; bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3); if(bytes_written < 0) { err = Curl_sockerrno(); failf(data,"Sending data failed (%d)",err); } printoption(conn->data, "SENT", cmd, option); }
/* singleipconnect() connects to the given IP only, and it may return without having connected if used from the multi interface. */ static curl_socket_t singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, long timeout_ms, bool *connected) { char addr_buf[128]; int rc; int error; bool isconnected; struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode res; sockfd = socket(ai->ai_family, conn->socktype, ai->ai_protocol); if (sockfd == CURL_SOCKET_BAD) return CURL_SOCKET_BAD; *connected = FALSE; /* default is not connected */ Curl_printable_address(ai, addr_buf, sizeof(addr_buf)); infof(data, " Trying %s... ", addr_buf); if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if (error) { sclose(sockfd); /* close the socket and bail out */ return CURL_SOCKET_BAD; } } /* possibly bind the local end to an IP, interface or port */ res = bindlocal(conn, sockfd); if(res) { sclose(sockfd); /* close socket and bail out */ return CURL_SOCKET_BAD; } /* set socket non-blocking */ Curl_nonblock(sockfd, TRUE); /* Connect TCP sockets, bind UDP */ if(conn->socktype == SOCK_STREAM) rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); else rc = 0; if(-1 == rc) { error = Curl_sockerrno(); switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif rc = waitconnect(sockfd, timeout_ms); break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s: %s", addr_buf, Curl_strerror(conn,error)); data->state.os_errno = error; break; } } /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ return sockfd; } isconnected = verifyconnect(sockfd, &error); if(!rc && isconnected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); return sockfd; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); else { data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); } /* connect failed or timed out */ sclose(sockfd); return CURL_SOCKET_BAD; }
CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, bool *connected) { int rc; struct SessionHandle *data = conn->data; CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; long allow_total = 0; long has_passed; curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); /* subtract the most strict timeout of the ones */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) allow_total = allow = data->set.timeout*1000; else allow = data->set.connecttimeout*1000; } else if(data->set.timeout) { allow_total = allow = data->set.timeout*1000; } else if(data->set.connecttimeout) { allow = data->set.connecttimeout*1000; } if(has_passed > allow ) { /* time-out, bail out, go home */ failf(data, "Connection time-out after %ld ms", has_passed); return CURLE_OPERATION_TIMEOUTED; } if(conn->bits.tcpconnect) { /* we are connected already! */ Curl_expire(data, allow_total); *connected = TRUE; return CURLE_OK; } Curl_expire(data, allow); /* check for connect without timeout as we want to return immediately */ rc = waitconnect(sockfd, 0); if(WAITCONN_CONNECTED == rc) { int error; if (verifyconnect(sockfd, &error)) { /* we are connected, awesome! */ *connected = TRUE; return CURLE_OK; } /* nope, not connected for real */ data->state.os_errno = error; infof(data, "Connection failed\n"); if(trynextip(conn, sockindex, connected)) { code = CURLE_COULDNT_CONNECT; } } else if(WAITCONN_TIMEOUT != rc) { int error = 0; /* nope, not connected */ if (WAITCONN_FDSET_ERROR == rc) { (void)verifyconnect(sockfd, &error); data->state.os_errno = error; infof(data, "%s\n",Curl_strerror(conn,error)); } else infof(data, "Connection failed\n"); if(trynextip(conn, sockindex, connected)) { error = Curl_sockerrno(); data->state.os_errno = error; failf(data, "Failed connect to %s:%d; %s", conn->host.name, conn->port, Curl_strerror(conn,error)); code = CURLE_COULDNT_CONNECT; } } /* * If the connection failed here, we should attempt to connect to the "next * address" for the given host. */ return code; }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd) { struct SessionHandle *data = conn->data; struct sockaddr_in me; struct sockaddr *sock = NULL; /* bind to this address */ socklen_t socksize; /* size of the data sock points to */ unsigned short port = data->set.localport; /* use this port number, 0 for "random" */ /* how many port numbers to try to bind to, increasing one at a time */ int portnum = data->set.localportrange; /************************************************************* * Select device to bind socket to *************************************************************/ if (data->set.device && (strlen(data->set.device)<255) ) { struct Curl_dns_entry *h=NULL; char myhost[256] = ""; in_addr_t in; int rc; bool was_iface = FALSE; /* First check if the given name is an IP address */ in=inet_addr(data->set.device); if((in == CURL_INADDR_NONE) && Curl_if2ip(data->set.device, myhost, sizeof(myhost))) { /* * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer */ rc = Curl_resolv(conn, myhost, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); if(h) { was_iface = TRUE; Curl_resolv_unlock(data, h); } } if(!was_iface) { /* * This was not an interface, resolve the name as a host name * or IP number */ rc = Curl_resolv(conn, data->set.device, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); if(h) { if(in == CURL_INADDR_NONE) /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_inet_ntop(h->addr->ai_addr->sa_family, &((struct sockaddr_in*)h->addr->ai_addr)->sin_addr, myhost, sizeof myhost); else /* we know data->set.device is shorter than the myhost array */ strcpy(myhost, data->set.device); Curl_resolv_unlock(data, h); } } if(! *myhost) { /* need to fix this h=Curl_gethost(data, getmyhost(*myhost,sizeof(myhost)), hostent_buf, sizeof(hostent_buf)); */ failf(data, "Couldn't bind to '%s'", data->set.device); return CURLE_HTTP_PORT_FAILED; } infof(data, "Bind local address to %s\n", myhost); #ifdef SO_BINDTODEVICE /* I am not sure any other OSs than Linux that provide this feature, and * at the least I cannot test. --Ben * * This feature allows one to tightly bind the local socket to a * particular interface. This will force even requests to other local * interfaces to go out the external interface. * */ if (was_iface) { /* Only bind to the interface when specified as interface, not just as a * hostname or ip address. */ if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, data->set.device, strlen(data->set.device)+1) != 0) { /* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n", sockfd, data->set.device, Curl_strerror(Curl_sockerrno())); */ infof(data, "SO_BINDTODEVICE %s failed\n", data->set.device); /* This is typically "errno 1, error: Operation not permitted" if you're not running as root or another suitable privileged user */ } } #endif in=inet_addr(myhost); if (CURL_INADDR_NONE == in) { failf(data,"couldn't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } /* end of inet_addr */ if ( h ) { Curl_addrinfo *addr = h->addr; sock = addr->ai_addr; socksize = addr->ai_addrlen; } else return CURLE_HTTP_PORT_FAILED; } else if(port) { /* if a local port number is requested but no local IP, extract the address from the socket */ memset(&me, 0, sizeof(struct sockaddr)); me.sin_family = AF_INET; me.sin_addr.s_addr = INADDR_ANY; sock = (struct sockaddr *)&me; socksize = sizeof(struct sockaddr); } else /* no local kind of binding was requested */ return CURLE_OK; do { /* Set port number to bind to, 0 makes the system pick one */ if(sock->sa_family == AF_INET) ((struct sockaddr_in *)sock)->sin_port = htons(port); #ifdef ENABLE_IPV6 else ((struct sockaddr_in6 *)sock)->sin6_port = htons(port); #endif if( bind(sockfd, sock, socksize) >= 0) { /* we succeeded to bind */ struct Curl_sockaddr_storage add; size_t size; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } /* We re-use/clobber the port variable here below */ if(((struct sockaddr *)&add)->sa_family == AF_INET) port = ntohs(((struct sockaddr_in *)&add)->sin_port); #ifdef ENABLE_IPV6 else port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port); #endif infof(data, "Local port: %d\n", port); return CURLE_OK; } if(--portnum > 0) { infof(data, "Bind to local port %d failed, trying next\n", port); port++; /* try next port */ } else break; } while(1); data->state.os_errno = Curl_sockerrno(); failf(data, "bind failure: %s", Curl_strerror(conn, data->state.os_errno)); return CURLE_HTTP_PORT_FAILED; }
/* * This function is called to shut down the SSL layer but keep the * socket open (CCC - Clear Command Channel) */ int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) { int result; int retval = 0; struct SessionHandle *data = conn->data; int done = 0; ssize_t nread; char buf[120]; /* This has only been tested on the proftpd server, and the mod_tls code sends a close notify alert without waiting for a close notify alert in response. Thus we wait for a close notify alert from the server, but we do not send one. Let's hope other servers do the same... */ if(conn->ssl[sockindex].session) { while(!done) { int what = Curl_select(conn->sock[sockindex], CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); if(what > 0) { /* Something to read, let's do it and hope that it is the close notify alert from the server */ result = gnutls_record_recv(conn->ssl[sockindex].session, buf, sizeof(buf)); switch(result) { case 0: /* This is the expected response. There was no data but only the close notify alert */ done = 1; break; case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n"); break; default: retval = -1; done = 1; break; } } else if(0 == what) { /* timeout */ failf(data, "SSL shutdown timeout"); done = 1; break; } else { /* anything that gets here is fatally bad */ failf(data, "select on SSL socket, errno: %d", Curl_sockerrno()); retval = -1; done = 1; } } gnutls_deinit(conn->ssl[sockindex].session); } gnutls_certificate_free_credentials(conn->ssl[sockindex].cred); conn->ssl[sockindex].session = NULL; conn->ssl[sockindex].use = FALSE; return retval; }
/* this function does a BLOCKING SSL/TLS (re-)handshake */ static CURLcode handshake(struct connectdata *conn, gnutls_session session, int sockindex, bool duringconnect) { struct SessionHandle *data = conn->data; int rc; do { rc = gnutls_handshake(session); if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { long timeout_ms = DEFAULT_CONNECT_TIMEOUT; long has_passed; if(duringconnect && data->set.connecttimeout) timeout_ms = data->set.connecttimeout*1000; if(data->set.timeout) { /* get the strictest timeout of the ones converted to milliseconds */ if((data->set.timeout*1000) < timeout_ms) timeout_ms = data->set.timeout*1000; } /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); /* subtract the passed time */ timeout_ms -= has_passed; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEOUTED; } rc = Curl_select(conn->sock[sockindex], conn->sock[sockindex], (int)timeout_ms); if(rc > 0) /* reabable or writable, go loop*/ continue; else if(0 == rc) { /* timeout */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } else { /* anything that gets here is fatally bad */ failf(data, "select on SSL socket, errno: %d", Curl_sockerrno()); return CURLE_SSL_CONNECT_ERROR; } } else break; } while(1); if (rc < 0) { failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } return CURLE_OK; }
/* * Internal read-from-socket function. This is meant to deal with plain * sockets, SSL sockets and kerberos sockets. * * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return * a regular CURLcode value. */ int Curl_read(struct connectdata *conn, /* connection data */ curl_socket_t sockfd, /* read from this socket */ char *buf, /* store read data here */ size_t sizerequested, /* max amount to read */ ssize_t *n) /* amount bytes read */ { ssize_t nread; size_t bytesfromsocket = 0; char *buffertofill = NULL; bool pipelining = (bool)(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)); /* Set 'num' to 0 or 1, depending on which socket that has been sent here. If it is the second socket, we set num to 1. Otherwise to 0. This lets us use the correct ssl handle. */ int num = (sockfd == conn->sock[SECONDARYSOCKET]); *n=0; /* reset amount to zero */ /* If session can pipeline, check connection buffer */ if(pipelining) { size_t bytestocopy = MIN(conn->buf_len - conn->read_pos, sizerequested); /* Copy from our master buffer first if we have some unread data there*/ if (bytestocopy > 0) { memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy); conn->read_pos += bytestocopy; conn->bits.stream_was_rewound = FALSE; *n = (ssize_t)bytestocopy; return CURLE_OK; } /* If we come here, it means that there is no data to read from the buffer, * so we read from the socket */ bytesfromsocket = MIN(sizerequested, sizeof(conn->master_buffer)); buffertofill = conn->master_buffer; } else { bytesfromsocket = MIN((long)sizerequested, conn->data->set.buffer_size ? conn->data->set.buffer_size : BUFSIZE); buffertofill = buf; } if(conn->ssl[num].use) { nread = Curl_ssl_recv(conn, num, buffertofill, bytesfromsocket); if(nread == -1) { return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */ } } #ifdef USE_LIBSSH2 else if (conn->protocol & PROT_SCP) { nread = Curl_scp_recv(conn, num, buffertofill, bytesfromsocket); /* TODO: return CURLE_OK also for nread <= 0 read failures and timeouts ? */ } else if (conn->protocol & PROT_SFTP) { nread = Curl_sftp_recv(conn, num, buffertofill, bytesfromsocket); } #endif /* !USE_LIBSSH2 */ else { if(conn->sec_complete) nread = Curl_sec_read(conn, sockfd, buffertofill, bytesfromsocket); else nread = sread(sockfd, buffertofill, bytesfromsocket); if(-1 == nread) { int err = Curl_sockerrno(); #ifdef USE_WINSOCK if(WSAEWOULDBLOCK == err) #else if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)) #endif return -1; } } if (nread >= 0) { if(pipelining) { memcpy(buf, conn->master_buffer, nread); conn->buf_len = nread; conn->read_pos = nread; } *n += nread; } return CURLE_OK; }
/* * This is an internal function used for waiting for read or write * events on single file descriptors. It attempts to replace select() * in order to avoid limits with FD_SETSIZE. * * Return values: * -1 = system call error * 0 = timeout * CSELECT_IN | CSELECT_OUT | CSELECT_ERR */ int Curl_select(curl_socket_t readfd, curl_socket_t writefd, int timeout_ms) { #if defined(HAVE_POLL_FINE) || defined(CURL_HAVE_WSAPOLL) struct pollfd pfd[2]; int num; int r; int ret; num = 0; if (readfd != CURL_SOCKET_BAD) { pfd[num].fd = readfd; pfd[num].events = POLLIN; num++; } if (writefd != CURL_SOCKET_BAD) { pfd[num].fd = writefd; pfd[num].events = POLLOUT; num++; } #ifdef HAVE_POLL_FINE do { r = poll(pfd, num, timeout_ms); } while((r == -1) && (errno == EINTR)); #else r = WSAPoll(pfd, num, timeout_ms); #endif if (r < 0) return -1; if (r == 0) return 0; ret = 0; num = 0; if (readfd != CURL_SOCKET_BAD) { if (pfd[num].revents & (POLLIN|POLLHUP)) ret |= CSELECT_IN; if (pfd[num].revents & POLLERR) { #ifdef __CYGWIN__ /* Cygwin 1.5.21 needs this hack to pass test 160 */ if (errno == EINPROGRESS) ret |= CSELECT_IN; else #endif ret |= CSELECT_ERR; } num++; } if (writefd != CURL_SOCKET_BAD) { if (pfd[num].revents & POLLOUT) ret |= CSELECT_OUT; if (pfd[num].revents & (POLLERR|POLLHUP)) ret |= CSELECT_ERR; } return ret; #else struct timeval timeout; fd_set fds_read; fd_set fds_write; fd_set fds_err; curl_socket_t maxfd; int r; int ret; timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; if((readfd == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) { /* According to POSIX we should pass in NULL pointers if we don't want to wait for anything in particular but just use the timeout function. Windows however returns immediately if done so. I copied the MSDOS delay() use from src/main.c that already had this work-around. */ #ifdef WIN32 Sleep(timeout_ms); #elif defined(__MSDOS__) delay(timeout_ms); #else select(0, NULL, NULL, NULL, &timeout); #endif return 0; } FD_ZERO(&fds_err); maxfd = (curl_socket_t)-1; FD_ZERO(&fds_read); if (readfd != CURL_SOCKET_BAD) { VERIFY_SOCK(readfd); FD_SET(readfd, &fds_read); FD_SET(readfd, &fds_err); maxfd = readfd; } FD_ZERO(&fds_write); if (writefd != CURL_SOCKET_BAD) { VERIFY_SOCK(writefd); FD_SET(writefd, &fds_write); FD_SET(writefd, &fds_err); if (writefd > maxfd) maxfd = writefd; } do { r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); } while((r == -1) && (Curl_sockerrno() == EINTR)); if (r < 0) return -1; if (r == 0) return 0; ret = 0; if (readfd != CURL_SOCKET_BAD) { if (FD_ISSET(readfd, &fds_read)) ret |= CSELECT_IN; if (FD_ISSET(readfd, &fds_err)) ret |= CSELECT_ERR; } if (writefd != CURL_SOCKET_BAD) { if (FD_ISSET(writefd, &fds_write)) ret |= CSELECT_OUT; if (FD_ISSET(writefd, &fds_err)) ret |= CSELECT_ERR; } return ret; #endif }
/* * This is a wrapper around poll(). If poll() does not exist, then * select() is used instead. An error is returned if select() is * being used and a file descriptor too large for FD_SETSIZE. * * Return values: * -1 = system call error or fd >= FD_SETSIZE * 0 = timeout * 1 = number of structures with non zero revent fields */ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) { int r; #ifdef HAVE_POLL_FINE do { r = poll(ufds, nfds, timeout_ms); } while((r == -1) && (errno == EINTR)); #elif defined(CURL_HAVE_WSAPOLL) r = WSAPoll(ufds, nfds, timeout_ms); #else struct timeval timeout; struct timeval *ptimeout; fd_set fds_read; fd_set fds_write; fd_set fds_err; curl_socket_t maxfd; unsigned int i; FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_err); maxfd = (curl_socket_t)-1; for (i = 0; i < nfds; i++) { if (ufds[i].fd == CURL_SOCKET_BAD) continue; #ifndef USE_WINSOCK /* winsock sockets are not in range [0..FD_SETSIZE] */ if (ufds[i].fd >= FD_SETSIZE) { errno = EINVAL; return -1; } #endif if (ufds[i].fd > maxfd) maxfd = ufds[i].fd; if (ufds[i].events & POLLIN) FD_SET(ufds[i].fd, &fds_read); if (ufds[i].events & POLLOUT) FD_SET(ufds[i].fd, &fds_write); if (ufds[i].events & POLLERR) FD_SET(ufds[i].fd, &fds_err); } if (timeout_ms < 0) { ptimeout = NULL; /* wait forever */ } else { timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; ptimeout = &timeout; } do { r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout); } while((r == -1) && (Curl_sockerrno() == EINTR)); if (r < 0) return -1; if (r == 0) return 0; r = 0; for (i = 0; i < nfds; i++) { ufds[i].revents = 0; if (ufds[i].fd == CURL_SOCKET_BAD) continue; if (FD_ISSET(ufds[i].fd, &fds_read)) ufds[i].revents |= POLLIN; if (FD_ISSET(ufds[i].fd, &fds_write)) ufds[i].revents |= POLLOUT; if (FD_ISSET(ufds[i].fd, &fds_err)) ufds[i].revents |= POLLERR; if (ufds[i].revents != 0) r++; } #endif return r; }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and * CURLE_OPERATION_TIMEDOUT if a time-out occurred. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { CURLcode rc=CURLE_OK; struct SessionHandle *data = conn->data; long timeout = CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */ /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ if(conn->data->set.connecttimeout) timeout = conn->data->set.connecttimeout; else if(conn->data->set.timeout) timeout = conn->data->set.timeout; /* We convert the number of seconds into number of milliseconds here: */ if(timeout < 2147483) /* maximum amount of seconds that can be multiplied with 1000 and still fit within 31 bits */ timeout *= 1000; else timeout = 0x7fffffff; /* ridiculous amount of time anyway */ /* Wait for the name resolve query to complete. */ while (1) { int nfds=0; fd_set read_fds, write_fds; struct timeval *tvp, tv, store; int count; struct timeval now = Curl_tvnow(); long timediff; store.tv_sec = (int)timeout/1000; store.tv_usec = (timeout%1000)*1000; FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds); if (nfds == 0) /* no file descriptors means we're done waiting */ break; tvp = ares_timeout(data->state.areschannel, &store, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); if (count < 0 && Curl_sockerrno() != EINVAL) break; ares_process(data->state.areschannel, &read_fds, &write_fds); timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */ timeout -= timediff?timediff:1; /* always deduct at least 1 */ if (timeout < 0) { /* our timeout, so we cancel the ares operation */ ares_cancel(data->state.areschannel); break; } } /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) *entry = conn->async.dns; if(!conn->async.dns) { /* a name was not resolved */ if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) { failf(data, "Resolving host timed out: %s", conn->host.dispname); rc = CURLE_OPERATION_TIMEDOUT; } else if(conn->async.done) { failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, ares_strerror(conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; /* close the connection, since we can't return failure here without cleaning up this connection properly */ conn->bits.close = TRUE; } return rc; }
static void suboption(struct connectdata *conn) { struct curl_slist *v; unsigned char temp[2048]; ssize_t bytes_written; size_t len; size_t tmplen; int err; char varname[128]; char varval[128]; struct SessionHandle *data = conn->data; struct TELNET *tn = (struct TELNET *)data->reqdata.proto.telnet; printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2); switch (CURL_SB_GET(tn)) { case CURL_TELOPT_TTYPE: len = strlen(tn->subopt_ttype) + 4 + 2; snprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE, CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE); bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { err = Curl_sockerrno(); failf(data,"Sending data failed (%d)",err); } printsub(data, '>', &temp[2], len-2); break; case CURL_TELOPT_XDISPLOC: len = strlen(tn->subopt_xdisploc) + 4 + 2; snprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC, CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE); bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { err = Curl_sockerrno(); failf(data,"Sending data failed (%d)",err); } printsub(data, '>', &temp[2], len-2); break; case CURL_TELOPT_NEW_ENVIRON: snprintf((char *)temp, sizeof(temp), "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON, CURL_TELQUAL_IS); len = 4; for(v = tn->telnet_vars;v;v = v->next) { tmplen = (strlen(v->data) + 1); /* Add the variable only if it fits */ if(len + tmplen < (int)sizeof(temp)-6) { sscanf(v->data, "%127[^,],%127s", varname, varval); snprintf((char *)&temp[len], sizeof(temp) - len, "%c%s%c%s", CURL_NEW_ENV_VAR, varname, CURL_NEW_ENV_VALUE, varval); len += tmplen; } } snprintf((char *)&temp[len], sizeof(temp) - len, "%c%c", CURL_IAC, CURL_SE); len += 2; bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { err = Curl_sockerrno(); failf(data,"Sending data failed (%d)",err); } printsub(data, '>', &temp[2], len-2); break; } return; }