CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, bool *connected) { struct Curl_easy *data = conn->data; CURLcode result = CURLE_OK; time_t allow; int error = 0; struct curltime now; int rc; int i; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ if(conn->bits.tcpconnect[sockindex]) { /* we are connected already! */ *connected = TRUE; return CURLE_OK; } now = Curl_tvnow(); for(i = 0; i<2; i++) { const int other = i ^ 1; if(conn->tempsock[i] == CURL_SOCKET_BAD) continue; #ifdef mpeix /* Call this function once now, and ignore the results. We do this to "clear" the error state on the socket so that we can later read it reliably. This is reported necessary on the MPE/iX operating system. */ (void)verifyconnect(conn->tempsock[i], NULL); #endif /* check socket for connect */ rc = SOCKET_WRITABLE(conn->tempsock[i], 0); if(rc == 0) { /* no connection yet */ error = 0; if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) { infof(data, "After %ldms connect time, move on!\n", conn->timeoutms_per_addr); error = ETIMEDOUT; } /* should we try another protocol family? */ if(i == 0 && conn->tempaddr[1] == NULL && curlx_tvdiff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) { trynextip(conn, sockindex, 1); } } else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) { if(verifyconnect(conn->tempsock[i], &error)) { /* we are connected with TCP, awesome! */ /* use this socket from now on */ conn->sock[sockindex] = conn->tempsock[i]; conn->ip_addr = conn->tempaddr[i]; conn->tempsock[i] = CURL_SOCKET_BAD; /* close the other socket, if open */ if(conn->tempsock[other] != CURL_SOCKET_BAD) { Curl_closesocket(conn, conn->tempsock[other]); conn->tempsock[other] = CURL_SOCKET_BAD; } /* see if we need to do any proxy magic first once we connected */ result = Curl_connected_proxy(conn, sockindex); if(result) return result; conn->bits.tcpconnect[sockindex] = TRUE; *connected = TRUE; if(sockindex == FIRSTSOCKET) Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ Curl_updateconninfo(conn, conn->sock[sockindex]); Curl_verboseconnect(conn); return CURLE_OK; } infof(data, "Connection failed\n"); } else if(rc & CURL_CSELECT_ERR) (void)verifyconnect(conn->tempsock[i], &error); /* * The connection failed here, we should attempt to connect to the "next * address" for the given host. But first remember the latest error. */ if(error) { data->state.os_errno = error; SET_SOCKERRNO(error); if(conn->tempaddr[i]) { CURLcode status; char ipaddress[MAX_IPADR_LEN]; Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN); infof(data, "connect to %s port %ld failed: %s\n", ipaddress, conn->port, Curl_strerror(conn, error)); conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ? allow : allow / 2; status = trynextip(conn, sockindex, i); if(status != CURLE_COULDNT_CONNECT || conn->tempsock[other] == CURL_SOCKET_BAD) /* the last attempt failed and no other sockets remain open */ result = status; } } } /* * Now that we've checked whether we are connected, check whether we've * already timed out. * * First figure out how long time we have left to connect */ allow = Curl_timeleft(data, &now, TRUE); if(allow < 0) { /* time-out, bail out, go home */ failf(data, "Connection timeout after %ld ms", Curl_tvdiff(now, data->progress.t_startsingle)); return CURLE_OPERATION_TIMEDOUT; } if(result) { /* no more addresses to try */ const char *hostname; /* if the first address family runs out of addresses to try before the happy eyeball timeout, go ahead and try the next family now */ if(conn->tempaddr[1] == NULL) { result = trynextip(conn, sockindex, 1); if(!result) return result; } if(conn->bits.socksproxy) hostname = conn->socks_proxy.host.name; else if(conn->bits.httpproxy) hostname = conn->http_proxy.host.name; else if(conn->bits.conn_to_host) hostname = conn->conn_to_host.name; else hostname = conn->host.name; failf(data, "Failed to connect to %s port %ld after %ld ms: %s", hostname, conn->port, Curl_tvdiff(now, data->progress.t_startsingle), Curl_strerror(conn, error)); } return result; }
static CURLcode gopher_do(struct connectdata *conn, bool *done) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; curl_off_t *bytecount = &data->req.bytecount; char *path = data->state.up.path; char *sel = NULL; char *sel_org = NULL; ssize_t amount, k; size_t len; *done = TRUE; /* unconditionally */ /* Create selector. Degenerate cases: / and /1 => convert to "" */ if(strlen(path) <= 2) { sel = (char *)""; len = strlen(sel); } else { char *newp; /* Otherwise, drop / and the first character (i.e., item type) ... */ newp = path; newp += 2; /* ... and finally unescape */ result = Curl_urldecode(data, newp, 0, &sel, &len, FALSE); if(result) return result; sel_org = sel; } /* We use Curl_write instead of Curl_sendf to make sure the entire buffer is sent, which could be sizeable with long selectors. */ k = curlx_uztosz(len); for(;;) { result = Curl_write(conn, sockfd, sel, k, &amount); if(!result) { /* Which may not have written it all! */ result = Curl_client_write(conn, CLIENTWRITE_HEADER, sel, amount); if(result) break; k -= amount; sel += amount; if(k < 1) break; /* but it did write it all */ } else break; /* Don't busyloop. The entire loop thing is a work-around as it causes a BLOCKING behavior which is a NO-NO. This function should rather be split up in a do and a doing piece where the pieces that aren't possible to send now will be sent in the doing function repeatedly until the entire request is sent. Wait a while for the socket to be writable. Note that this doesn't acknowledge the timeout. */ if(SOCKET_WRITABLE(sockfd, 100) < 0) { result = CURLE_SEND_ERROR; break; } } free(sel_org); if(!result) /* We can use Curl_sendf to send the terminal \r\n relatively safely and save allocing another string/doing another _write loop. */ result = Curl_sendf(sockfd, conn, "\r\n"); if(result) { failf(data, "Failed sending Gopher request"); return result; } result = Curl_client_write(conn, CLIENTWRITE_HEADER, (char *)"\r\n", 2); if(result) return result; Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL); /* no upload */ return CURLE_OK; }