Annotation of embedaddon/curl/lib/pingpong.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
! 9: *
! 10: * This software is licensed as described in the file COPYING, which
! 11: * you should have received as part of this distribution. The terms
! 12: * are also available at https://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: * 'pingpong' is for generic back-and-forth support functions used by FTP,
! 22: * IMAP, POP3, SMTP and whatever more that likes them.
! 23: *
! 24: ***************************************************************************/
! 25:
! 26: #include "curl_setup.h"
! 27:
! 28: #include "urldata.h"
! 29: #include "sendf.h"
! 30: #include "select.h"
! 31: #include "progress.h"
! 32: #include "speedcheck.h"
! 33: #include "pingpong.h"
! 34: #include "multiif.h"
! 35: #include "non-ascii.h"
! 36: #include "vtls/vtls.h"
! 37:
! 38: /* The last 3 #include files should be in this order */
! 39: #include "curl_printf.h"
! 40: #include "curl_memory.h"
! 41: #include "memdebug.h"
! 42:
! 43: #ifdef USE_PINGPONG
! 44:
! 45: /* Returns timeout in ms. 0 or negative number means the timeout has already
! 46: triggered */
! 47: time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting)
! 48: {
! 49: struct connectdata *conn = pp->conn;
! 50: struct Curl_easy *data = conn->data;
! 51: time_t timeout_ms; /* in milliseconds */
! 52: long response_time = (data->set.server_response_timeout)?
! 53: data->set.server_response_timeout: pp->response_time;
! 54:
! 55: /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
! 56: remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
! 57: supposed to govern the response for any given server response, not for
! 58: the time from connect to the given server response. */
! 59:
! 60: /* Without a requested timeout, we only wait 'response_time' seconds for the
! 61: full response to arrive before we bail out */
! 62: timeout_ms = response_time -
! 63: (time_t)Curl_timediff(Curl_now(), pp->response); /* spent time */
! 64:
! 65: if(data->set.timeout && !disconnecting) {
! 66: /* if timeout is requested, find out how much remaining time we have */
! 67: time_t timeout2_ms = data->set.timeout - /* timeout time */
! 68: (time_t)Curl_timediff(Curl_now(), conn->now); /* spent time */
! 69:
! 70: /* pick the lowest number */
! 71: timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
! 72: }
! 73:
! 74: return timeout_ms;
! 75: }
! 76:
! 77: /*
! 78: * Curl_pp_statemach()
! 79: */
! 80: CURLcode Curl_pp_statemach(struct pingpong *pp, bool block,
! 81: bool disconnecting)
! 82: {
! 83: struct connectdata *conn = pp->conn;
! 84: curl_socket_t sock = conn->sock[FIRSTSOCKET];
! 85: int rc;
! 86: time_t interval_ms;
! 87: time_t timeout_ms = Curl_pp_state_timeout(pp, disconnecting);
! 88: struct Curl_easy *data = conn->data;
! 89: CURLcode result = CURLE_OK;
! 90:
! 91: if(timeout_ms <= 0) {
! 92: failf(data, "server response timeout");
! 93: return CURLE_OPERATION_TIMEDOUT; /* already too little time */
! 94: }
! 95:
! 96: if(block) {
! 97: interval_ms = 1000; /* use 1 second timeout intervals */
! 98: if(timeout_ms < interval_ms)
! 99: interval_ms = timeout_ms;
! 100: }
! 101: else
! 102: interval_ms = 0; /* immediate */
! 103:
! 104: if(Curl_ssl_data_pending(conn, FIRSTSOCKET))
! 105: rc = 1;
! 106: else if(Curl_pp_moredata(pp))
! 107: /* We are receiving and there is data in the cache so just read it */
! 108: rc = 1;
! 109: else if(!pp->sendleft && Curl_ssl_data_pending(conn, FIRSTSOCKET))
! 110: /* We are receiving and there is data ready in the SSL library */
! 111: rc = 1;
! 112: else
! 113: rc = Curl_socket_check(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
! 114: CURL_SOCKET_BAD,
! 115: pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
! 116: interval_ms);
! 117:
! 118: if(block) {
! 119: /* if we didn't wait, we don't have to spend time on this now */
! 120: if(Curl_pgrsUpdate(conn))
! 121: result = CURLE_ABORTED_BY_CALLBACK;
! 122: else
! 123: result = Curl_speedcheck(data, Curl_now());
! 124:
! 125: if(result)
! 126: return result;
! 127: }
! 128:
! 129: if(rc == -1) {
! 130: failf(data, "select/poll error");
! 131: result = CURLE_OUT_OF_MEMORY;
! 132: }
! 133: else if(rc)
! 134: result = pp->statemach_act(conn);
! 135:
! 136: return result;
! 137: }
! 138:
! 139: /* initialize stuff to prepare for reading a fresh new response */
! 140: void Curl_pp_init(struct pingpong *pp)
! 141: {
! 142: struct connectdata *conn = pp->conn;
! 143: pp->nread_resp = 0;
! 144: pp->linestart_resp = conn->data->state.buffer;
! 145: pp->pending_resp = TRUE;
! 146: pp->response = Curl_now(); /* start response time-out now! */
! 147: }
! 148:
! 149:
! 150:
! 151: /***********************************************************************
! 152: *
! 153: * Curl_pp_vsendf()
! 154: *
! 155: * Send the formatted string as a command to a pingpong server. Note that
! 156: * the string should not have any CRLF appended, as this function will
! 157: * append the necessary things itself.
! 158: *
! 159: * made to never block
! 160: */
! 161: CURLcode Curl_pp_vsendf(struct pingpong *pp,
! 162: const char *fmt,
! 163: va_list args)
! 164: {
! 165: ssize_t bytes_written;
! 166: size_t write_len;
! 167: char *fmt_crlf;
! 168: char *s;
! 169: CURLcode result;
! 170: struct connectdata *conn = pp->conn;
! 171: struct Curl_easy *data;
! 172:
! 173: #ifdef HAVE_GSSAPI
! 174: enum protection_level data_sec;
! 175: #endif
! 176:
! 177: DEBUGASSERT(pp->sendleft == 0);
! 178: DEBUGASSERT(pp->sendsize == 0);
! 179: DEBUGASSERT(pp->sendthis == NULL);
! 180:
! 181: if(!conn)
! 182: /* can't send without a connection! */
! 183: return CURLE_SEND_ERROR;
! 184:
! 185: data = conn->data;
! 186:
! 187: fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
! 188: if(!fmt_crlf)
! 189: return CURLE_OUT_OF_MEMORY;
! 190:
! 191: s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
! 192: free(fmt_crlf);
! 193: if(!s)
! 194: return CURLE_OUT_OF_MEMORY;
! 195:
! 196: bytes_written = 0;
! 197: write_len = strlen(s);
! 198:
! 199: Curl_pp_init(pp);
! 200:
! 201: result = Curl_convert_to_network(data, s, write_len);
! 202: /* Curl_convert_to_network calls failf if unsuccessful */
! 203: if(result) {
! 204: free(s);
! 205: return result;
! 206: }
! 207:
! 208: #ifdef HAVE_GSSAPI
! 209: conn->data_prot = PROT_CMD;
! 210: #endif
! 211: result = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
! 212: &bytes_written);
! 213: #ifdef HAVE_GSSAPI
! 214: data_sec = conn->data_prot;
! 215: DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
! 216: conn->data_prot = data_sec;
! 217: #endif
! 218:
! 219: if(result) {
! 220: free(s);
! 221: return result;
! 222: }
! 223:
! 224: if(conn->data->set.verbose)
! 225: Curl_debug(conn->data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written);
! 226:
! 227: if(bytes_written != (ssize_t)write_len) {
! 228: /* the whole chunk was not sent, keep it around and adjust sizes */
! 229: pp->sendthis = s;
! 230: pp->sendsize = write_len;
! 231: pp->sendleft = write_len - bytes_written;
! 232: }
! 233: else {
! 234: free(s);
! 235: pp->sendthis = NULL;
! 236: pp->sendleft = pp->sendsize = 0;
! 237: pp->response = Curl_now();
! 238: }
! 239:
! 240: return CURLE_OK;
! 241: }
! 242:
! 243:
! 244: /***********************************************************************
! 245: *
! 246: * Curl_pp_sendf()
! 247: *
! 248: * Send the formatted string as a command to a pingpong server. Note that
! 249: * the string should not have any CRLF appended, as this function will
! 250: * append the necessary things itself.
! 251: *
! 252: * made to never block
! 253: */
! 254: CURLcode Curl_pp_sendf(struct pingpong *pp,
! 255: const char *fmt, ...)
! 256: {
! 257: CURLcode result;
! 258: va_list ap;
! 259: va_start(ap, fmt);
! 260:
! 261: result = Curl_pp_vsendf(pp, fmt, ap);
! 262:
! 263: va_end(ap);
! 264:
! 265: return result;
! 266: }
! 267:
! 268: /*
! 269: * Curl_pp_readresp()
! 270: *
! 271: * Reads a piece of a server response.
! 272: */
! 273: CURLcode Curl_pp_readresp(curl_socket_t sockfd,
! 274: struct pingpong *pp,
! 275: int *code, /* return the server code if done */
! 276: size_t *size) /* size of the response */
! 277: {
! 278: ssize_t perline; /* count bytes per line */
! 279: bool keepon = TRUE;
! 280: ssize_t gotbytes;
! 281: char *ptr;
! 282: struct connectdata *conn = pp->conn;
! 283: struct Curl_easy *data = conn->data;
! 284: char * const buf = data->state.buffer;
! 285: CURLcode result = CURLE_OK;
! 286:
! 287: *code = 0; /* 0 for errors or not done */
! 288: *size = 0;
! 289:
! 290: ptr = buf + pp->nread_resp;
! 291:
! 292: /* number of bytes in the current line, so far */
! 293: perline = (ssize_t)(ptr-pp->linestart_resp);
! 294:
! 295: while((pp->nread_resp < (size_t)data->set.buffer_size) &&
! 296: (keepon && !result)) {
! 297:
! 298: if(pp->cache) {
! 299: /* we had data in the "cache", copy that instead of doing an actual
! 300: * read
! 301: *
! 302: * pp->cache_size is cast to ssize_t here. This should be safe, because
! 303: * it would have been populated with something of size int to begin
! 304: * with, even though its datatype may be larger than an int.
! 305: */
! 306: if((ptr + pp->cache_size) > (buf + data->set.buffer_size + 1)) {
! 307: failf(data, "cached response data too big to handle");
! 308: return CURLE_RECV_ERROR;
! 309: }
! 310: memcpy(ptr, pp->cache, pp->cache_size);
! 311: gotbytes = (ssize_t)pp->cache_size;
! 312: free(pp->cache); /* free the cache */
! 313: pp->cache = NULL; /* clear the pointer */
! 314: pp->cache_size = 0; /* zero the size just in case */
! 315: }
! 316: else {
! 317: #ifdef HAVE_GSSAPI
! 318: enum protection_level prot = conn->data_prot;
! 319: conn->data_prot = PROT_CLEAR;
! 320: #endif
! 321: DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <=
! 322: (buf + data->set.buffer_size + 1));
! 323: result = Curl_read(conn, sockfd, ptr,
! 324: data->set.buffer_size - pp->nread_resp,
! 325: &gotbytes);
! 326: #ifdef HAVE_GSSAPI
! 327: DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST);
! 328: conn->data_prot = prot;
! 329: #endif
! 330: if(result == CURLE_AGAIN)
! 331: return CURLE_OK; /* return */
! 332:
! 333: if(!result && (gotbytes > 0))
! 334: /* convert from the network encoding */
! 335: result = Curl_convert_from_network(data, ptr, gotbytes);
! 336: /* Curl_convert_from_network calls failf if unsuccessful */
! 337:
! 338: if(result)
! 339: /* Set outer result variable to this error. */
! 340: keepon = FALSE;
! 341: }
! 342:
! 343: if(!keepon)
! 344: ;
! 345: else if(gotbytes <= 0) {
! 346: keepon = FALSE;
! 347: result = CURLE_RECV_ERROR;
! 348: failf(data, "response reading failed");
! 349: }
! 350: else {
! 351: /* we got a whole chunk of data, which can be anything from one
! 352: * byte to a set of lines and possible just a piece of the last
! 353: * line */
! 354: ssize_t i;
! 355: ssize_t clipamount = 0;
! 356: bool restart = FALSE;
! 357:
! 358: data->req.headerbytecount += (long)gotbytes;
! 359:
! 360: pp->nread_resp += gotbytes;
! 361: for(i = 0; i < gotbytes; ptr++, i++) {
! 362: perline++;
! 363: if(*ptr == '\n') {
! 364: /* a newline is CRLF in pp-talk, so the CR is ignored as
! 365: the line isn't really terminated until the LF comes */
! 366:
! 367: /* output debug output if that is requested */
! 368: #ifdef HAVE_GSSAPI
! 369: if(!conn->sec_complete)
! 370: #endif
! 371: if(data->set.verbose)
! 372: Curl_debug(data, CURLINFO_HEADER_IN,
! 373: pp->linestart_resp, (size_t)perline);
! 374:
! 375: /*
! 376: * We pass all response-lines to the callback function registered
! 377: * for "headers". The response lines can be seen as a kind of
! 378: * headers.
! 379: */
! 380: result = Curl_client_write(conn, CLIENTWRITE_HEADER,
! 381: pp->linestart_resp, perline);
! 382: if(result)
! 383: return result;
! 384:
! 385: if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
! 386: /* This is the end of the last line, copy the last line to the
! 387: start of the buffer and zero terminate, for old times sake */
! 388: size_t n = ptr - pp->linestart_resp;
! 389: memmove(buf, pp->linestart_resp, n);
! 390: buf[n] = 0; /* zero terminate */
! 391: keepon = FALSE;
! 392: pp->linestart_resp = ptr + 1; /* advance pointer */
! 393: i++; /* skip this before getting out */
! 394:
! 395: *size = pp->nread_resp; /* size of the response */
! 396: pp->nread_resp = 0; /* restart */
! 397: break;
! 398: }
! 399: perline = 0; /* line starts over here */
! 400: pp->linestart_resp = ptr + 1;
! 401: }
! 402: }
! 403:
! 404: if(!keepon && (i != gotbytes)) {
! 405: /* We found the end of the response lines, but we didn't parse the
! 406: full chunk of data we have read from the server. We therefore need
! 407: to store the rest of the data to be checked on the next invoke as
! 408: it may actually contain another end of response already! */
! 409: clipamount = gotbytes - i;
! 410: restart = TRUE;
! 411: DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
! 412: "server response left\n",
! 413: (int)clipamount));
! 414: }
! 415: else if(keepon) {
! 416:
! 417: if((perline == gotbytes) && (gotbytes > data->set.buffer_size/2)) {
! 418: /* We got an excessive line without newlines and we need to deal
! 419: with it. We keep the first bytes of the line then we throw
! 420: away the rest. */
! 421: infof(data, "Excessive server response line length received, "
! 422: "%zd bytes. Stripping\n", gotbytes);
! 423: restart = TRUE;
! 424:
! 425: /* we keep 40 bytes since all our pingpong protocols are only
! 426: interested in the first piece */
! 427: clipamount = 40;
! 428: }
! 429: else if(pp->nread_resp > (size_t)data->set.buffer_size/2) {
! 430: /* We got a large chunk of data and there's potentially still
! 431: trailing data to take care of, so we put any such part in the
! 432: "cache", clear the buffer to make space and restart. */
! 433: clipamount = perline;
! 434: restart = TRUE;
! 435: }
! 436: }
! 437: else if(i == gotbytes)
! 438: restart = TRUE;
! 439:
! 440: if(clipamount) {
! 441: pp->cache_size = clipamount;
! 442: pp->cache = malloc(pp->cache_size);
! 443: if(pp->cache)
! 444: memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
! 445: else
! 446: return CURLE_OUT_OF_MEMORY;
! 447: }
! 448: if(restart) {
! 449: /* now reset a few variables to start over nicely from the start of
! 450: the big buffer */
! 451: pp->nread_resp = 0; /* start over from scratch in the buffer */
! 452: ptr = pp->linestart_resp = buf;
! 453: perline = 0;
! 454: }
! 455:
! 456: } /* there was data */
! 457:
! 458: } /* while there's buffer left and loop is requested */
! 459:
! 460: pp->pending_resp = FALSE;
! 461:
! 462: return result;
! 463: }
! 464:
! 465: int Curl_pp_getsock(struct pingpong *pp,
! 466: curl_socket_t *socks)
! 467: {
! 468: struct connectdata *conn = pp->conn;
! 469: socks[0] = conn->sock[FIRSTSOCKET];
! 470:
! 471: if(pp->sendleft) {
! 472: /* write mode */
! 473: return GETSOCK_WRITESOCK(0);
! 474: }
! 475:
! 476: /* read mode */
! 477: return GETSOCK_READSOCK(0);
! 478: }
! 479:
! 480: CURLcode Curl_pp_flushsend(struct pingpong *pp)
! 481: {
! 482: /* we have a piece of a command still left to send */
! 483: struct connectdata *conn = pp->conn;
! 484: ssize_t written;
! 485: curl_socket_t sock = conn->sock[FIRSTSOCKET];
! 486: CURLcode result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
! 487: pp->sendleft, pp->sendleft, &written);
! 488: if(result)
! 489: return result;
! 490:
! 491: if(written != (ssize_t)pp->sendleft) {
! 492: /* only a fraction was sent */
! 493: pp->sendleft -= written;
! 494: }
! 495: else {
! 496: free(pp->sendthis);
! 497: pp->sendthis = NULL;
! 498: pp->sendleft = pp->sendsize = 0;
! 499: pp->response = Curl_now();
! 500: }
! 501: return CURLE_OK;
! 502: }
! 503:
! 504: CURLcode Curl_pp_disconnect(struct pingpong *pp)
! 505: {
! 506: free(pp->cache);
! 507: pp->cache = NULL;
! 508: return CURLE_OK;
! 509: }
! 510:
! 511: bool Curl_pp_moredata(struct pingpong *pp)
! 512: {
! 513: return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
! 514: TRUE : FALSE;
! 515: }
! 516:
! 517: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>