File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / pingpong.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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>