CURLcode Curl_pp_flushsend(struct pingpong *pp) { /* we have a piece of a command still left to send */ struct connectdata *conn = pp->conn; ssize_t written; CURLcode result = CURLE_OK; curl_socket_t sock = conn->sock[FIRSTSOCKET]; result = Curl_write(conn, sock, pp->sendthis + pp->sendsize - pp->sendleft, pp->sendleft, &written); if(result) return result; if(written != (ssize_t)pp->sendleft) { /* only a fraction was sent */ pp->sendleft -= written; } else { free(pp->sendthis); pp->sendthis=NULL; pp->sendleft = pp->sendsize = 0; pp->response = Curl_tvnow(); } return CURLE_OK; }
/* * Sends data over the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n) { curl_socket_t sfd; CURLcode ret; ssize_t n1; struct connectdata *c = NULL; struct SessionHandle *data = (struct SessionHandle *)curl; ret = easy_connection(data, &sfd, &c); if(ret) return ret; *n = 0; ret = Curl_write(c, sfd, buffer, buflen, &n1); if(n1 == -1) return CURLE_SEND_ERROR; /* detect EAGAIN */ if((CURLE_OK == ret) && (0 == n1)) return CURLE_AGAIN; *n = (size_t)n1; return ret; }
/* * Sends data over the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, size_t buflen, size_t *n) { curl_socket_t sfd; CURLcode result; ssize_t n1; struct connectdata *c = NULL; result = easy_connection(data, &sfd, &c); if(result) return result; *n = 0; result = Curl_write(c, sfd, buffer, buflen, &n1); if(n1 == -1) return CURLE_SEND_ERROR; /* detect EAGAIN */ if(!result && !n1) return CURLE_AGAIN; *n = (size_t)n1; return result; }
/* Escape and send a telnet data block */ static CURLcode send_telnet_data(struct connectdata *conn, char *buffer, ssize_t nread) { ssize_t escapes, i, j, outlen; unsigned char *outbuf = NULL; CURLcode result = CURLE_OK; ssize_t bytes_written, total_written; /* Determine size of new buffer after escaping */ escapes = 0; for(i = 0; i < nread; i++) if((unsigned char)buffer[i] == CURL_IAC) escapes++; outlen = nread + escapes; if(outlen == nread) outbuf = (unsigned char *)buffer; else { outbuf = malloc(nread + escapes + 1); if(!outbuf) return CURLE_OUT_OF_MEMORY; j = 0; for(i = 0; i < nread; i++) { outbuf[j++] = buffer[i]; if((unsigned char)buffer[i] == CURL_IAC) outbuf[j++] = CURL_IAC; } outbuf[j] = '\0'; } total_written = 0; while(!result && total_written < outlen) { /* Make sure socket is writable to avoid EWOULDBLOCK condition */ struct pollfd pfd[1]; pfd[0].fd = conn->sock[FIRSTSOCKET]; pfd[0].events = POLLOUT; switch(Curl_poll(pfd, 1, -1)) { case -1: /* error, abort writing */ case 0: /* timeout (will never happen) */ result = CURLE_SEND_ERROR; break; default: /* write! */ bytes_written = 0; result = Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf + total_written, outlen - total_written, &bytes_written); total_written += bytes_written; break; } } /* Free malloc copy if escaped */ if(outbuf != (unsigned char *)buffer) free(outbuf); return result; }
/*********************************************************************** * * smtp_done() * * The DONE function. This does what needs to be done after a single DO has * performed. * * Input argument is already checked for validity. */ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; struct FTP *smtp = data->state.proto.smtp; CURLcode result = CURLE_OK; ssize_t bytes_written; (void)premature; if(!smtp) /* When the easy handle is removed from the multi while libcurl is still * trying to resolve the host name, it seems that the smtp struct is not * yet initialized, but the removal action calls Curl_done() which calls * this function. So we simply return success if no smtp pointer is set. */ return CURLE_OK; if(status) { conn->bits.close = TRUE; /* marked for closure */ result = status; /* use the already set error code */ } else /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */ /* write to socket (send away data) */ result = Curl_write(conn, conn->writesockfd, /* socket to send to */ SMTP_EOB, /* buffer pointer */ SMTP_EOB_LEN, /* buffer size */ &bytes_written); /* actually sent away */ if(status == CURLE_OK) { struct smtp_conn *smtpc = &conn->proto.smtpc; struct pingpong *pp = &smtpc->pp; pp->response = Curl_tvnow(); /* timeout relative now */ state(conn, SMTP_POSTDATA); /* run the state-machine TODO: when the multi interface is used, this _really_ should be using the smtp_multi_statemach function but we have no general support for non-blocking DONE operations, not in the multi state machine and with Curl_done() invokes on several places in the code! */ result = smtp_easy_statemach(conn); } /* clear these for next connection */ smtp->transfer = FTPTRANSFER_BODY; return result; }
/* Curl_sendf() sends formated data to the server */ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, const char *fmt, ...) { struct SessionHandle *data = conn->data; ssize_t bytes_written; size_t write_len; CURLcode res; char *s; char *sptr; va_list ap; va_start(ap, fmt); s = vaprintf(fmt, ap); /* returns an allocated string */ va_end(ap); if(!s) return CURLE_OUT_OF_MEMORY; /* failure */ bytes_written=0; write_len = strlen(s); sptr = s; while (1) { /* Write the buffer to the socket */ res = Curl_write(conn, sockfd, sptr, write_len, &bytes_written); if(CURLE_OK != res) break; if(data->set.verbose) Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written); if((size_t)bytes_written != write_len) { /* if not all was written at once, we must advance the pointer, decrease the size left and try again! */ write_len -= bytes_written; sptr += bytes_written; } else break; } free(s); /* free the output string */ return res; }
/* * add_buffer_send() sends a buffer and frees all associated memory. */ static CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in, long *bytes_written) { ssize_t amount; CURLcode res; char *ptr; int size; if(conn->data->set.verbose) { fputs("> ", conn->data->set.err); /* this data _may_ contain binary stuff */ fwrite(in->buffer, in->size_used, 1, conn->data->set.err); } /* The looping below is required since we use non-blocking sockets, but due to the circumstances we will just loop and try again and again etc */ ptr = in->buffer; size = in->size_used; do { res = Curl_write(conn, sockfd, ptr, size, &amount); if(CURLE_OK != res) break; if(amount != size) { size -= amount; ptr += amount; } else break; } while(1); if(in->buffer) free(in->buffer); free(in); *bytes_written = amount; return res; }
/* * The implementation of nghttp2_send_callback type. Here we write |data| with * size |length| to the network and return the number of bytes actually * written. See the documentation of nghttp2_send_callback for the details. */ static ssize_t send_callback(nghttp2_session *h2, const uint8_t *data, size_t length, int flags, void *userp) { struct connectdata *conn = (struct connectdata *)userp; ssize_t written; CURLcode rc = Curl_write(conn, conn->sock[FIRSTSOCKET], data, length, &written); (void)h2; (void)flags; if(rc) { failf(conn->data, "Failed sending HTTP2 data"); return NGHTTP2_ERR_CALLBACK_FAILURE; } else if(!written) return NGHTTP2_ERR_WOULDBLOCK; return written; }
/* TODO: write large chunks of data instead of one byte at a time */ static CURLcode send_telnet_data(struct connectdata *conn, char *buffer, ssize_t nread) { unsigned char outbuf[2]; ssize_t bytes_written, total_written; int out_count; CURLcode result = CURLE_OK; while(!result && nread--) { outbuf[0] = *buffer++; out_count = 1; if(outbuf[0] == CURL_IAC) outbuf[out_count++] = CURL_IAC; total_written = 0; do { /* Make sure socket is writable to avoid EWOULDBLOCK condition */ struct pollfd pfd[1]; pfd[0].fd = conn->sock[FIRSTSOCKET]; pfd[0].events = POLLOUT; switch (Curl_poll(pfd, 1, -1)) { case -1: /* error, abort writing */ case 0: /* timeout (will never happen) */ result = CURLE_SEND_ERROR; break; default: /* write! */ bytes_written = 0; result = Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf+total_written, out_count-total_written, &bytes_written); total_written += bytes_written; break; } /* handle partial write */ } while(!result && total_written < out_count); } return result; }
/*********************************************************************** * * smtp_done() * * The DONE function. This does what needs to be done after a single DO has * performed. * * Input argument is already checked for validity. */ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; struct FTP *smtp = data->state.proto.smtp; CURLcode result=CURLE_OK; ssize_t bytes_written; (void)premature; if(!smtp) /* When the easy handle is removed from the multi while libcurl is still * trying to resolve the host name, it seems that the smtp struct is not * yet initialized, but the removal action calls Curl_done() which calls * this function. So we simply return success if no smtp pointer is set. */ return CURLE_OK; if(status) { conn->bits.close = TRUE; /* marked for closure */ result = status; /* use the already set error code */ } else /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */ /* write to socket (send away data) */ result = Curl_write(conn, conn->writesockfd, /* socket to send to */ SMTP_EOB, /* buffer pointer */ SMTP_EOB_LEN, /* buffer size */ &bytes_written); /* actually sent away */ /* clear these for next connection */ smtp->transfer = FTPTRANSFER_BODY; return result; }
/*********************************************************************** * * smtp_done() * * The DONE function. This does what needs to be done after a single DO has * performed. * * Input argument is already checked for validity. */ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, bool premature) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; struct SMTP *smtp = data->req.protop; struct pingpong *pp = &conn->proto.smtpc.pp; char *eob; ssize_t len; ssize_t bytes_written; (void)premature; if(!smtp || !pp->conn) return CURLE_OK; /* Cleanup our per-request based variables */ Curl_safefree(smtp->custom); if(status) { connclose(conn, "SMTP done with bad status"); /* marked for closure */ result = status; /* use the already set error code */ } else if(!data->set.connect_only && data->set.mail_rcpt && (data->set.upload || data->set.mimepost.kind)) { /* Calculate the EOB taking into account any terminating CRLF from the previous line of the email or the CRLF of the DATA command when there is "no mail data". RFC-5321, sect. 4.1.1.4. Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to fail when using a different pointer following a previous write, that returned CURLE_AGAIN, we duplicate the EOB now rather than when the bytes written doesn't equal len. */ if(smtp->trailing_crlf || !conn->data->state.infilesize) { eob = strdup(SMTP_EOB + 2); len = SMTP_EOB_LEN - 2; } else { eob = strdup(SMTP_EOB); len = SMTP_EOB_LEN; } if(!eob) return CURLE_OUT_OF_MEMORY; /* Send the end of block data */ result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); if(result) { free(eob); return result; } if(bytes_written != len) { /* The whole chunk was not sent so keep it around and adjust the pingpong structure accordingly */ pp->sendthis = eob; pp->sendsize = len; pp->sendleft = len - bytes_written; } else { /* Successfully sent so adjust the response timeout relative to now */ pp->response = Curl_now(); free(eob); } state(conn, SMTP_POSTDATA); /* Run the state-machine TODO: when the multi interface is used, this _really_ should be using the smtp_multi_statemach function but we have no general support for non-blocking DONE operations! */ result = smtp_block_statemach(conn); } /* Clear the transfer mode for the next request */ smtp->transfer = FTPTRANSFER_BODY; return result; }
/* * This function logs in to a SOCKS4 proxy and sends the specifics to the final * destination server. * * Reference : * http://socks.permeo.com/protocol/socks4.protocol * * Note : * Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ CURLcode Curl_SOCKS4(const char *proxy_name, struct connectdata *conn) { unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */ int result; CURLcode code; curl_socket_t sock = conn->sock[FIRSTSOCKET]; long timeout; struct SessionHandle *data = conn->data; /* get timeout */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) timeout = data->set.timeout*1000; else timeout = data->set.connecttimeout*1000; } else if(data->set.timeout) timeout = data->set.timeout*1000; else if(data->set.connecttimeout) timeout = data->set.connecttimeout*1000; else timeout = DEFAULT_CONNECT_TIMEOUT; Curl_nonblock(sock, FALSE); /* * Compose socks4 request * * Request format * * +----+----+----+----+----+----+----+----+----+----+....+----+ * | VN | CD | DSTPORT | DSTIP | USERID |NULL| * +----+----+----+----+----+----+----+----+----+----+....+----+ * # of bytes: 1 1 2 4 variable 1 */ socksreq[0] = 4; /* version (SOCKS4) */ socksreq[1] = 1; /* connect */ *((unsigned short*)&socksreq[2]) = htons(conn->remote_port); /* DNS resolve */ { struct Curl_dns_entry *dns; Curl_addrinfo *hp=NULL; int rc; rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); if(rc == CURLRESOLV_ERROR) return CURLE_COULDNT_RESOLVE_PROXY; if(rc == CURLRESOLV_PENDING) /* this requires that we're in "wait for resolve" state */ rc = Curl_wait_for_resolv(conn, &dns); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) hp=dns->addr; if (hp) { char buf[64]; unsigned short ip[4]; Curl_printable_address(hp, buf, sizeof(buf)); if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3])) { /* Set DSTIP */ socksreq[4] = (unsigned char)ip[0]; socksreq[5] = (unsigned char)ip[1]; socksreq[6] = (unsigned char)ip[2]; socksreq[7] = (unsigned char)ip[3]; } else hp = NULL; /* fail! */ Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", conn->host.name); return CURLE_COULDNT_RESOLVE_HOST; } } /* * This is currently not supporting "Identification Protocol (RFC1413)". */ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ if (proxy_name) strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8); /* * Make connection */ { ssize_t actualread; ssize_t written; int packetsize = 9 + (int)strlen((char*)socksreq + 8); /* size including NUL */ /* Send request */ code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); if ((code != CURLE_OK) || (written != packetsize)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } packetsize = 8; /* receive data size */ /* Receive response */ result = blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread, timeout); if ((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS4 connect request ack."); return CURLE_COULDNT_CONNECT; } /* * Response format * * +----+----+----+----+----+----+----+----+ * | VN | CD | DSTPORT | DSTIP | * +----+----+----+----+----+----+----+----+ * # of bytes: 1 1 2 4 * * VN is the version of the reply code and should be 0. CD is the result * code with one of the following values: * * 90: request granted * 91: request rejected or failed * 92: request rejected because SOCKS server cannot connect to * identd on the client * 93: request rejected because the client program and identd * report different user-ids */ /* wrong version ? */ if (socksreq[0] != 0) { failf(data, "SOCKS4 reply has wrong version, version should be 4."); return CURLE_COULDNT_CONNECT; } /* Result */ switch(socksreq[1]) { case 90: infof(data, "SOCKS4 request granted.\n"); break; case 91: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected or failed.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 92: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because SOCKS server cannot connect to " "identd on the client.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 93: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because the client program and identd " "report different user-ids.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; default: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", Unknown.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; } } Curl_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ }
CURLcode Curl_telnet(struct connectdata *conn) { CURLcode code; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; #if defined(WIN32) || defined(WIN64) HMODULE wsock2; WSOCK2_FUNC close_event_func; WSOCK2_FUNC create_event_func; WSOCK2_FUNC event_select_func; WSOCK2_FUNC enum_netevents_func; WSAEVENT event_handle; WSANETWORKEVENTS events; HANDLE stdin_handle; HANDLE objs[2]; DWORD waitret; DWORD readfile_read; #else fd_set readfd; fd_set keepfd; #endif ssize_t nread; bool keepon = TRUE; char *buf = data->state.buffer; struct TELNET *tn; code = init_telnet(conn); if(code) return code; tn = (struct TELNET *)conn->proto.telnet; code = check_telnet_options(conn); if(code) return code; #if defined(WIN32) || defined(WIN64) /* ** This functionality only works with WinSock >= 2.0. So, ** make sure have it. */ code = check_wsock2(data); if (code) return code; /* OK, so we have WinSock 2.0. We need to dynamically */ /* load ws2_32.dll and get the function pointers we need. */ wsock2 = LoadLibrary("WS2_32.DLL"); if (wsock2 == NULL) { failf(data,"failed to load WS2_32.DLL (%d)",GetLastError()); return CURLE_FAILED_INIT; } /* Grab a pointer to WSACreateEvent */ create_event_func = GetProcAddress(wsock2,"WSACreateEvent"); if (create_event_func == NULL) { failf(data,"failed to find WSACreateEvent function (%d)", GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSACloseEvent */ close_event_func = GetProcAddress(wsock2,"WSACloseEvent"); if (create_event_func == NULL) { failf(data,"failed to find WSACloseEvent function (%d)", GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEventSelect */ event_select_func = GetProcAddress(wsock2,"WSAEventSelect"); if (event_select_func == NULL) { failf(data,"failed to find WSAEventSelect function (%d)", GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEnumNetworkEvents */ enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents"); if (enum_netevents_func == NULL) { failf(data,"failed to find WSAEnumNetworkEvents function (%d)", GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* We want to wait for both stdin and the socket. Since ** the select() function in winsock only works on sockets ** we have to use the WaitForMultipleObjects() call. */ /* First, create a sockets event object */ event_handle = (WSAEVENT)create_event_func(); if (event_handle == WSA_INVALID_EVENT) { failf(data,"WSACreateEvent failed (%d)",WSAGetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* The get the Windows file handle for stdin */ stdin_handle = GetStdHandle(STD_INPUT_HANDLE); /* Create the list of objects to wait for */ objs[0] = stdin_handle; objs[1] = event_handle; /* Tell winsock what events we want to listen to */ if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) { close_event_func(event_handle); FreeLibrary(wsock2); return 0; } /* Keep on listening and act on events */ while(keepon) { waitret = WaitForMultipleObjects(2, objs, FALSE, INFINITE); switch(waitret - WAIT_OBJECT_0) { case 0: { unsigned char outbuf[2]; int out_count = 0; ssize_t bytes_written; char *buffer = buf; if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer), &readfile_read, NULL)) { keepon = FALSE; break; } nread = readfile_read; while(nread--) { outbuf[0] = *buffer++; out_count = 1; if(outbuf[0] == CURL_IAC) outbuf[out_count++] = CURL_IAC; Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf, out_count, &bytes_written); } } break; case 1: if(enum_netevents_func(sockfd, event_handle, &events) != SOCKET_ERROR) { if(events.lNetworkEvents & FD_READ) { /* This reallu OUGHT to check its return code. */ (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); telrcv(conn, (unsigned char *)buf, nread); fflush(stdout); /* Negotiate if the peer has started negotiating, otherwise don't. We don't want to speak telnet with non-telnet servers, like POP or SMTP. */ if(tn->please_negotiate && !tn->already_negotiated) { negotiate(conn); tn->already_negotiated = 1; } } if(events.lNetworkEvents & FD_CLOSE) { keepon = FALSE; } } break; } } /* We called WSACreateEvent, so call WSACloseEvent */ if (close_event_func(event_handle) == FALSE) { infof(data,"WSACloseEvent failed (%d)",WSAGetLastError()); } /* "Forget" pointers into the library we're about to free */ create_event_func = NULL; close_event_func = NULL; event_select_func = NULL; enum_netevents_func = NULL; /* We called LoadLibrary, so call FreeLibrary */ if (!FreeLibrary(wsock2)) infof(data,"FreeLibrary(wsock2) failed (%d)",GetLastError()); #else FD_ZERO (&readfd); /* clear it */ FD_SET (sockfd, &readfd); FD_SET (0, &readfd); keepfd = readfd; while (keepon) { struct timeval interval; readfd = keepfd; /* set this every lap in the loop */ interval.tv_sec = 1; interval.tv_usec = 0; switch (select (sockfd + 1, &readfd, NULL, NULL, &interval)) { case -1: /* error, stop reading */ keepon = FALSE; continue; case 0: /* timeout */ break; default: /* read! */ if(FD_ISSET(0, &readfd)) { /* read from stdin */ unsigned char outbuf[2]; int out_count = 0; ssize_t bytes_written; char *buffer = buf; nread = read(0, buf, 255); while(nread--) { outbuf[0] = *buffer++; out_count = 1; if(outbuf[0] == CURL_IAC) outbuf[out_count++] = CURL_IAC; Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf, out_count, &bytes_written); } } if(FD_ISSET(sockfd, &readfd)) { /* This OUGHT to check the return code... */ (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); /* if we receive 0 or less here, the server closed the connection and we bail out from this! */ if (nread <= 0) { keepon = FALSE; break; } telrcv(conn, (unsigned char *)buf, nread); /* Negotiate if the peer has started negotiating, otherwise don't. We don't want to speak telnet with non-telnet servers, like POP or SMTP. */ if(tn->please_negotiate && !tn->already_negotiated) { negotiate(conn); tn->already_negotiated = 1; } } } if(data->set.timeout) { struct timeval now; /* current time */ now = Curl_tvnow(); if(Curl_tvdiff(now, conn->created)/1000 >= data->set.timeout) { failf(data, "Time-out"); code = CURLE_OPERATION_TIMEOUTED; keepon = FALSE; } } } #endif /* mark this as "no further transfer wanted" */ Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); return code; }
/* * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. */ CURLcode Curl_SOCKS5(const char *proxy_name, const char *proxy_password, struct connectdata *conn) { /* According to the RFC1928, section "6. Replies". This is what a SOCK5 replies: +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ Where: o VER protocol version: X'05' o REP Reply field: o X'00' succeeded */ unsigned char socksreq[600]; /* room for large user/pw (255 max each) */ ssize_t actualread; ssize_t written; int result; CURLcode code; curl_socket_t sock = conn->sock[FIRSTSOCKET]; struct SessionHandle *data = conn->data; long timeout; /* get timeout */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) timeout = data->set.timeout*1000; else timeout = data->set.connecttimeout*1000; } else if(data->set.timeout) timeout = data->set.timeout*1000; else if(data->set.connecttimeout) timeout = data->set.connecttimeout*1000; else timeout = DEFAULT_CONNECT_TIMEOUT; Curl_nonblock(sock, TRUE); /* wait until socket gets connected */ result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout); if(-1 == result) { failf(conn->data, "SOCKS5: no connection here"); return CURLE_COULDNT_CONNECT; } else if(0 == result) { failf(conn->data, "SOCKS5: connection timeout"); return CURLE_OPERATION_TIMEDOUT; } if(result & CSELECT_ERR) { failf(conn->data, "SOCKS5: error occured during connection"); return CURLE_COULDNT_CONNECT; } socksreq[0] = 5; /* version */ socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */ socksreq[2] = 0; /* no authentication */ socksreq[3] = 2; /* username/password */ Curl_nonblock(sock, FALSE); code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), &written); if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { failf(data, "Unable to send initial SOCKS5 request."); return CURLE_COULDNT_CONNECT; } Curl_nonblock(sock, TRUE); result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout); if(-1 == result) { failf(conn->data, "SOCKS5 nothing to read"); return CURLE_COULDNT_CONNECT; } else if(0 == result) { failf(conn->data, "SOCKS5 read timeout"); return CURLE_OPERATION_TIMEDOUT; } if(result & CSELECT_ERR) { failf(conn->data, "SOCKS5 read error occured"); return CURLE_RECV_ERROR; } Curl_nonblock(sock, FALSE); result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout); if ((result != CURLE_OK) || (actualread != 2)) { failf(data, "Unable to receive initial SOCKS5 response."); return CURLE_COULDNT_CONNECT; } if (socksreq[0] != 5) { failf(data, "Received invalid version in initial SOCKS5 response."); return CURLE_COULDNT_CONNECT; } if (socksreq[1] == 0) { /* Nothing to do, no authentication needed */ ; } else if (socksreq[1] == 2) { /* Needs user name and password */ size_t userlen, pwlen; int len; if(proxy_name && proxy_password) { userlen = strlen(proxy_name); pwlen = proxy_password?strlen(proxy_password):0; } else { userlen = 0; pwlen = 0; } /* username/password request looks like * +----+------+----------+------+----------+ * |VER | ULEN | UNAME | PLEN | PASSWD | * +----+------+----------+------+----------+ * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | * +----+------+----------+------+----------+ */ len = 0; socksreq[len++] = 1; /* username/pw subnegotiation version */ socksreq[len++] = (char) userlen; memcpy(socksreq + len, proxy_name, (int) userlen); len += userlen; socksreq[len++] = (char) pwlen; memcpy(socksreq + len, proxy_password, (int) pwlen); len += pwlen; code = Curl_write(conn, sock, (char *)socksreq, len, &written); if ((code != CURLE_OK) || (len != written)) { failf(data, "Failed to send SOCKS5 sub-negotiation request."); return CURLE_COULDNT_CONNECT; } result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout); if ((result != CURLE_OK) || (actualread != 2)) { failf(data, "Unable to receive SOCKS5 sub-negotiation response."); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if (socksreq[1] != 0) { /* status */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); return CURLE_COULDNT_CONNECT; } /* Everything is good so far, user was authenticated! */ } else { /* error */ if (socksreq[1] == 1) { failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); return CURLE_COULDNT_CONNECT; } else if (socksreq[1] == 255) { if (!proxy_name || !*proxy_name) { failf(data, "No authentication method was acceptable. (It is quite likely" " that the SOCKS5 server wanted a username/password, since none" " was supplied to the server on this connection.)"); } else { failf(data, "No authentication method was acceptable."); } return CURLE_COULDNT_CONNECT; } else { failf(data, "Undocumented SOCKS5 mode attempted to be used by server."); return CURLE_COULDNT_CONNECT; } } /* Authentication is complete, now specify destination to the proxy */ socksreq[0] = 5; /* version (SOCKS5) */ socksreq[1] = 1; /* connect */ socksreq[2] = 0; /* must be zero */ socksreq[3] = 1; /* IPv4 = 1 */ { struct Curl_dns_entry *dns; Curl_addrinfo *hp=NULL; int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); if(rc == CURLRESOLV_ERROR) return CURLE_COULDNT_RESOLVE_HOST; if(rc == CURLRESOLV_PENDING) /* this requires that we're in "wait for resolve" state */ rc = Curl_wait_for_resolv(conn, &dns); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) hp=dns->addr; if (hp) { char buf[64]; unsigned short ip[4]; Curl_printable_address(hp, buf, sizeof(buf)); if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3])) { socksreq[4] = (unsigned char)ip[0]; socksreq[5] = (unsigned char)ip[1]; socksreq[6] = (unsigned char)ip[2]; socksreq[7] = (unsigned char)ip[3]; } else hp = NULL; /* fail! */ Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", conn->host.name); return CURLE_COULDNT_RESOLVE_HOST; } } *((unsigned short*)&socksreq[8]) = htons(conn->remote_port); { const int packetsize = 10; code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); if ((code != CURLE_OK) || (written != packetsize)) { failf(data, "Failed to send SOCKS5 connect request."); return CURLE_COULDNT_CONNECT; } result = blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread, timeout); if ((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS5 connect request ack."); return CURLE_COULDNT_CONNECT; } if (socksreq[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); return CURLE_COULDNT_CONNECT; } if (socksreq[1] != 0) { /* Anything besides 0 is an error */ failf(data, "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; } } Curl_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ }
/*********************************************************************** * * Curl_pp_sendfv() * * Send the formated string as a command to a pingpong server. Note that * the string should not have any CRLF appended, as this function will * append the necessary things itself. * * NOTE: we build the command in a fixed-length buffer, which sets length * restrictions on the command! * * made to never block */ CURLcode Curl_pp_vsendf(struct pingpong *pp, const char *fmt, va_list args) { ssize_t bytes_written; /* may still not be big enough for some krb5 tokens */ #define SBUF_SIZE 1024 char s[SBUF_SIZE]; size_t write_len; char *sptr=s; CURLcode res = CURLE_OK; struct connectdata *conn = pp->conn; struct SessionHandle *data = conn->data; #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) enum protection_level data_sec = conn->data_prot; #endif vsnprintf(s, SBUF_SIZE-3, fmt, args); strcat(s, "\r\n"); /* append a trailing CRLF */ bytes_written=0; write_len = strlen(s); Curl_pp_init(pp); #ifdef CURL_DOES_CONVERSIONS res = Curl_convert_to_network(data, s, write_len); /* Curl_convert_to_network calls failf if unsuccessful */ if(res != CURLE_OK) { return res; } #endif /* CURL_DOES_CONVERSIONS */ #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) conn->data_prot = prot_cmd; #endif res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len, &bytes_written); #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) conn->data_prot = data_sec; #endif if(CURLE_OK != res) return res; if(conn->data->set.verbose) Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, (size_t)bytes_written, conn); if(bytes_written != (ssize_t)write_len) { /* the whole chunk was not sent, store the rest of the data */ write_len -= bytes_written; sptr += bytes_written; pp->sendthis = malloc(write_len); if(pp->sendthis) { memcpy(pp->sendthis, sptr, write_len); pp->sendsize = pp->sendleft = write_len; } else { failf(data, "out of memory"); res = CURLE_OUT_OF_MEMORY; } } else pp->response = Curl_tvnow(); return res; }
/*********************************************************************** * * smtp_done() * * The DONE function. This does what needs to be done after a single DO has * performed. * * Input argument is already checked for validity. */ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; struct FTP *smtp = data->state.proto.smtp; CURLcode result = CURLE_OK; ssize_t bytes_written; (void)premature; if(!smtp) /* When the easy handle is removed from the multi while libcurl is still * trying to resolve the host name, it seems that the smtp struct is not * yet initialized, but the removal action calls Curl_done() which calls * this function. So we simply return success if no smtp pointer is set. */ return CURLE_OK; if(status) { conn->bits.close = TRUE; /* marked for closure */ result = status; /* use the already set error code */ } else if(!data->set.connect_only) { struct smtp_conn *smtpc = &conn->proto.smtpc; struct pingpong *pp = &smtpc->pp; /* Send the end of block data */ result = Curl_write(conn, conn->writesockfd, /* socket to send to */ SMTP_EOB, /* buffer pointer */ SMTP_EOB_LEN, /* buffer size */ &bytes_written); /* actually sent away */ if(result) return result; if(bytes_written != SMTP_EOB_LEN) { /* The whole chunk was not sent so keep it around and adjust the pingpong structure accordingly */ pp->sendthis = strdup(SMTP_EOB); pp->sendsize = SMTP_EOB_LEN; pp->sendleft = SMTP_EOB_LEN - bytes_written; } else /* Successfully sent so adjust the response timeout relative to now */ pp->response = Curl_tvnow(); state(conn, SMTP_POSTDATA); /* Run the state-machine TODO: when the multi interface is used, this _really_ should be using the smtp_multi_statemach function but we have no general support for non-blocking DONE operations, not in the multi state machine and with Curl_done() invokes on several places in the code! */ result = smtp_easy_statemach(conn); } /* Clear the transfer mode for the next connection */ smtp->transfer = FTPTRANSFER_BODY; return result; }
CURLcode Curl_telnet(struct connectdata *conn, bool *done) { CURLcode code; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; #ifdef USE_WINSOCK HMODULE wsock2; WSOCK2_FUNC close_event_func; WSOCK2_FUNC create_event_func; WSOCK2_FUNC event_select_func; WSOCK2_FUNC enum_netevents_func; WSAEVENT event_handle; WSANETWORKEVENTS events; HANDLE stdin_handle; HANDLE objs[2]; DWORD obj_count; DWORD wait_timeout; DWORD waitret; DWORD readfile_read; #else int interval_ms; struct pollfd pfd[2]; #endif ssize_t nread; bool keepon = TRUE; char *buf = data->state.buffer; struct TELNET *tn; *done = TRUE; /* uncontionally */ code = init_telnet(conn); if(code) return code; tn = (struct TELNET *)data->reqdata.proto.telnet; code = check_telnet_options(conn); if(code) return code; #ifdef USE_WINSOCK /* ** This functionality only works with WinSock >= 2.0. So, ** make sure have it. */ code = check_wsock2(data); if (code) return code; /* OK, so we have WinSock 2.0. We need to dynamically */ /* load ws2_32.dll and get the function pointers we need. */ wsock2 = LoadLibrary("WS2_32.DLL"); if (wsock2 == NULL) { failf(data,"failed to load WS2_32.DLL (%d)", ERRNO); return CURLE_FAILED_INIT; } /* Grab a pointer to WSACreateEvent */ create_event_func = GetProcAddress(wsock2,"WSACreateEvent"); if (create_event_func == NULL) { failf(data,"failed to find WSACreateEvent function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSACloseEvent */ close_event_func = GetProcAddress(wsock2,"WSACloseEvent"); if (close_event_func == NULL) { failf(data,"failed to find WSACloseEvent function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEventSelect */ event_select_func = GetProcAddress(wsock2,"WSAEventSelect"); if (event_select_func == NULL) { failf(data,"failed to find WSAEventSelect function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEnumNetworkEvents */ enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents"); if (enum_netevents_func == NULL) { failf(data,"failed to find WSAEnumNetworkEvents function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* We want to wait for both stdin and the socket. Since ** the select() function in winsock only works on sockets ** we have to use the WaitForMultipleObjects() call. */ /* First, create a sockets event object */ event_handle = (WSAEVENT)create_event_func(); if (event_handle == WSA_INVALID_EVENT) { failf(data,"WSACreateEvent failed (%d)", SOCKERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* The get the Windows file handle for stdin */ stdin_handle = GetStdHandle(STD_INPUT_HANDLE); /* Create the list of objects to wait for */ objs[0] = event_handle; objs[1] = stdin_handle; /* Tell winsock what events we want to listen to */ if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) { close_event_func(event_handle); FreeLibrary(wsock2); return 0; } /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it, else use the old WaitForMultipleObjects() way */ if(GetFileType(stdin_handle) == FILE_TYPE_PIPE) { /* Don't wait for stdin_handle, just wait for event_handle */ obj_count = 1; /* Check stdin_handle per 100 milliseconds */ wait_timeout = 100; } else { obj_count = 2; wait_timeout = INFINITE; } /* Keep on listening and act on events */ while(keepon) { waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout); switch(waitret) { case WAIT_TIMEOUT: { unsigned char outbuf[2]; int out_count = 0; ssize_t bytes_written; char *buffer = buf; while(1) { if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, &readfile_read, NULL)) { keepon = FALSE; break; } nread = readfile_read; if(!nread) break; if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer), &readfile_read, NULL)) { keepon = FALSE; break; } nread = readfile_read; while(nread--) { outbuf[0] = *buffer++; out_count = 1; if(outbuf[0] == CURL_IAC) outbuf[out_count++] = CURL_IAC; Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf, out_count, &bytes_written); } } } break; case WAIT_OBJECT_0 + 1: { unsigned char outbuf[2]; int out_count = 0; ssize_t bytes_written; char *buffer = buf; if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer), &readfile_read, NULL)) { keepon = FALSE; break; } nread = readfile_read; while(nread--) { outbuf[0] = *buffer++; out_count = 1; if(outbuf[0] == CURL_IAC) outbuf[out_count++] = CURL_IAC; Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf, out_count, &bytes_written); } } break; case WAIT_OBJECT_0: if(enum_netevents_func(sockfd, event_handle, &events) != SOCKET_ERROR) { if(events.lNetworkEvents & FD_READ) { /* This reallu OUGHT to check its return code. */ (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); telrcv(conn, (unsigned char *)buf, nread); fflush(stdout); /* Negotiate if the peer has started negotiating, otherwise don't. We don't want to speak telnet with non-telnet servers, like POP or SMTP. */ if(tn->please_negotiate && !tn->already_negotiated) { negotiate(conn); tn->already_negotiated = 1; } } if(events.lNetworkEvents & FD_CLOSE) { keepon = FALSE; } } break; } } /* We called WSACreateEvent, so call WSACloseEvent */ if (close_event_func(event_handle) == FALSE) { infof(data,"WSACloseEvent failed (%d)", SOCKERRNO); } /* "Forget" pointers into the library we're about to free */ create_event_func = NULL; close_event_func = NULL; event_select_func = NULL; enum_netevents_func = NULL; /* We called LoadLibrary, so call FreeLibrary */ if (!FreeLibrary(wsock2)) infof(data,"FreeLibrary(wsock2) failed (%d)", ERRNO); #else pfd[0].fd = sockfd; pfd[0].events = POLLIN; pfd[1].fd = 0; pfd[1].events = POLLIN; interval_ms = 1 * 1000; while (keepon) { switch (Curl_poll(pfd, 2, interval_ms)) { case -1: /* error, stop reading */ keepon = FALSE; continue; case 0: /* timeout */ break; default: /* read! */ if(pfd[1].revents & POLLIN) { /* read from stdin */ unsigned char outbuf[2]; int out_count = 0; ssize_t bytes_written; char *buffer = buf; nread = read(0, buf, 255); while(nread--) { outbuf[0] = *buffer++; out_count = 1; if(outbuf[0] == CURL_IAC) outbuf[out_count++] = CURL_IAC; Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf, out_count, &bytes_written); } } if(pfd[0].revents & POLLIN) { /* This OUGHT to check the return code... */ (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); /* if we receive 0 or less here, the server closed the connection and we bail out from this! */ if (nread <= 0) { keepon = FALSE; break; } telrcv(conn, (unsigned char *)buf, nread); /* Negotiate if the peer has started negotiating, otherwise don't. We don't want to speak telnet with non-telnet servers, like POP or SMTP. */ if(tn->please_negotiate && !tn->already_negotiated) { negotiate(conn); tn->already_negotiated = 1; } } } if(data->set.timeout) { struct timeval now; /* current time */ now = Curl_tvnow(); if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); code = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; } } } #endif /* mark this as "no further transfer wanted" */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); return code; }
/*********************************************************************** * * Curl_pp_vsendf() * * Send the formatted string as a command to a pingpong server. Note that * the string should not have any CRLF appended, as this function will * append the necessary things itself. * * made to never block */ CURLcode Curl_pp_vsendf(struct pingpong *pp, const char *fmt, va_list args) { ssize_t bytes_written; size_t write_len; char *fmt_crlf; char *s; CURLcode result; struct connectdata *conn = pp->conn; struct Curl_easy *data; #ifdef HAVE_GSSAPI enum protection_level data_sec; #endif DEBUGASSERT(pp->sendleft == 0); DEBUGASSERT(pp->sendsize == 0); DEBUGASSERT(pp->sendthis == NULL); if(!conn) /* can't send without a connection! */ return CURLE_SEND_ERROR; data = conn->data; fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */ if(!fmt_crlf) return CURLE_OUT_OF_MEMORY; s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */ free(fmt_crlf); if(!s) return CURLE_OUT_OF_MEMORY; bytes_written = 0; write_len = strlen(s); Curl_pp_init(pp); result = Curl_convert_to_network(data, s, write_len); /* Curl_convert_to_network calls failf if unsuccessful */ if(result) { free(s); return result; } #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif result = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len, &bytes_written); #ifdef HAVE_GSSAPI data_sec = conn->data_prot; DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; #endif if(result) { free(s); return result; } if(conn->data->set.verbose) Curl_debug(conn->data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written, conn); if(bytes_written != (ssize_t)write_len) { /* the whole chunk was not sent, keep it around and adjust sizes */ pp->sendthis = s; pp->sendsize = write_len; pp->sendleft = write_len - bytes_written; } else { free(s); pp->sendthis = NULL; pp->sendleft = pp->sendsize = 0; pp->response = Curl_now(); } return CURLE_OK; }
static CURLcode gopher_do(struct connectdata *conn, bool *done) { CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; curl_off_t *bytecount = &data->req.bytecount; char *path = data->state.path; char *sel; char *sel_org = NULL; ssize_t amount, k; int len; *done = TRUE; /* unconditionally */ /* Create selector. Degenerate cases: / and /1 => convert to "" */ if(strlen(path) <= 2) { sel = (char *)""; len = (int)strlen(sel); } else { char *newp; size_t j, i; /* Otherwise, drop / and the first character (i.e., item type) ... */ newp = path; newp+=2; /* ... then turn ? into TAB for search servers, Veronica, etc. ... */ j = strlen(newp); for(i=0; i<j; i++) if(newp[i] == '?') newp[i] = '\x09'; /* ... and finally unescape */ sel = curl_easy_unescape(data, newp, 0, &len); if(!sel) return CURLE_OUT_OF_MEMORY; 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) { free(sel_org); return result; } k -= amount; sel += amount; if(k < 1) break; /* but it did write it all */ } else { failf(data, "Failed sending Gopher request"); free(sel_org); return result; } /* 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. */ Curl_socket_ready(CURL_SOCKET_BAD, sockfd, 100); } free(sel_org); /* 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; }