Annotation of embedaddon/curl/lib/pingpong.c, revision 1.1.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>