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>