Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) 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)
     48 {
     49   struct connectdata *conn = pp->conn;
     50   struct Curl_easy *data = conn->data;
     51   time_t timeout_ms; /* in milliseconds */
     52   time_t timeout2_ms; /* in milliseconds */
     53   long response_time = (data->set.server_response_timeout)?
     54     data->set.server_response_timeout: pp->response_time;
     55 
     56   /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
     57      remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
     58      supposed to govern the response for any given server response, not for
     59      the time from connect to the given server response. */
     60 
     61   /* Without a requested timeout, we only wait 'response_time' seconds for the
     62      full response to arrive before we bail out */
     63   timeout_ms = response_time -
     64     Curl_timediff(Curl_now(), pp->response); /* spent time */
     65 
     66   if(data->set.timeout) {
     67     /* if timeout is requested, find out how much remaining time we have */
     68     timeout2_ms = data->set.timeout - /* timeout time */
     69       Curl_timediff(Curl_now(), conn->now); /* spent time */
     70 
     71     /* pick the lowest number */
     72     timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
     73   }
     74 
     75   return timeout_ms;
     76 }
     77 
     78 /*
     79  * Curl_pp_statemach()
     80  */
     81 CURLcode Curl_pp_statemach(struct pingpong *pp, bool block)
     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);
     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,
    226                s, (size_t)bytes_written, conn);
    227 
    228   if(bytes_written != (ssize_t)write_len) {
    229     /* the whole chunk was not sent, keep it around and adjust sizes */
    230     pp->sendthis = s;
    231     pp->sendsize = write_len;
    232     pp->sendleft = write_len - bytes_written;
    233   }
    234   else {
    235     free(s);
    236     pp->sendthis = NULL;
    237     pp->sendleft = pp->sendsize = 0;
    238     pp->response = Curl_now();
    239   }
    240 
    241   return CURLE_OK;
    242 }
    243 
    244 
    245 /***********************************************************************
    246  *
    247  * Curl_pp_sendf()
    248  *
    249  * Send the formatted string as a command to a pingpong server. Note that
    250  * the string should not have any CRLF appended, as this function will
    251  * append the necessary things itself.
    252  *
    253  * made to never block
    254  */
    255 CURLcode Curl_pp_sendf(struct pingpong *pp,
    256                        const char *fmt, ...)
    257 {
    258   CURLcode result;
    259   va_list ap;
    260   va_start(ap, fmt);
    261 
    262   result = Curl_pp_vsendf(pp, fmt, ap);
    263 
    264   va_end(ap);
    265 
    266   return result;
    267 }
    268 
    269 /*
    270  * Curl_pp_readresp()
    271  *
    272  * Reads a piece of a server response.
    273  */
    274 CURLcode Curl_pp_readresp(curl_socket_t sockfd,
    275                           struct pingpong *pp,
    276                           int *code, /* return the server code if done */
    277                           size_t *size) /* size of the response */
    278 {
    279   ssize_t perline; /* count bytes per line */
    280   bool keepon = TRUE;
    281   ssize_t gotbytes;
    282   char *ptr;
    283   struct connectdata *conn = pp->conn;
    284   struct Curl_easy *data = conn->data;
    285   char * const buf = data->state.buffer;
    286   CURLcode result = CURLE_OK;
    287 
    288   *code = 0; /* 0 for errors or not done */
    289   *size = 0;
    290 
    291   ptr = buf + pp->nread_resp;
    292 
    293   /* number of bytes in the current line, so far */
    294   perline = (ssize_t)(ptr-pp->linestart_resp);
    295 
    296   while((pp->nread_resp < (size_t)data->set.buffer_size) &&
    297         (keepon && !result)) {
    298 
    299     if(pp->cache) {
    300       /* we had data in the "cache", copy that instead of doing an actual
    301        * read
    302        *
    303        * pp->cache_size is cast to ssize_t here.  This should be safe, because
    304        * it would have been populated with something of size int to begin
    305        * with, even though its datatype may be larger than an int.
    306        */
    307       DEBUGASSERT((ptr + pp->cache_size) <= (buf + data->set.buffer_size + 1));
    308       memcpy(ptr, pp->cache, pp->cache_size);
    309       gotbytes = (ssize_t)pp->cache_size;
    310       free(pp->cache);    /* free the cache */
    311       pp->cache = NULL;   /* clear the pointer */
    312       pp->cache_size = 0; /* zero the size just in case */
    313     }
    314     else {
    315 #ifdef HAVE_GSSAPI
    316       enum protection_level prot = conn->data_prot;
    317       conn->data_prot = PROT_CLEAR;
    318 #endif
    319       DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <=
    320                   (buf + data->set.buffer_size + 1));
    321       result = Curl_read(conn, sockfd, ptr,
    322                          data->set.buffer_size - pp->nread_resp,
    323                          &gotbytes);
    324 #ifdef HAVE_GSSAPI
    325       DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
    326       conn->data_prot = prot;
    327 #endif
    328       if(result == CURLE_AGAIN)
    329         return CURLE_OK; /* return */
    330 
    331       if(!result && (gotbytes > 0))
    332         /* convert from the network encoding */
    333         result = Curl_convert_from_network(data, ptr, gotbytes);
    334       /* Curl_convert_from_network calls failf if unsuccessful */
    335 
    336       if(result)
    337         /* Set outer result variable to this error. */
    338         keepon = FALSE;
    339     }
    340 
    341     if(!keepon)
    342       ;
    343     else if(gotbytes <= 0) {
    344       keepon = FALSE;
    345       result = CURLE_RECV_ERROR;
    346       failf(data, "response reading failed");
    347     }
    348     else {
    349       /* we got a whole chunk of data, which can be anything from one
    350        * byte to a set of lines and possible just a piece of the last
    351        * line */
    352       ssize_t i;
    353       ssize_t clipamount = 0;
    354       bool restart = FALSE;
    355 
    356       data->req.headerbytecount += (long)gotbytes;
    357 
    358       pp->nread_resp += gotbytes;
    359       for(i = 0; i < gotbytes; ptr++, i++) {
    360         perline++;
    361         if(*ptr == '\n') {
    362           /* a newline is CRLF in pp-talk, so the CR is ignored as
    363              the line isn't really terminated until the LF comes */
    364 
    365           /* output debug output if that is requested */
    366 #ifdef HAVE_GSSAPI
    367           if(!conn->sec_complete)
    368 #endif
    369             if(data->set.verbose)
    370               Curl_debug(data, CURLINFO_HEADER_IN,
    371                          pp->linestart_resp, (size_t)perline, conn);
    372 
    373           /*
    374            * We pass all response-lines to the callback function registered
    375            * for "headers". The response lines can be seen as a kind of
    376            * headers.
    377            */
    378           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
    379                                      pp->linestart_resp, perline);
    380           if(result)
    381             return result;
    382 
    383           if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
    384             /* This is the end of the last line, copy the last line to the
    385                start of the buffer and zero terminate, for old times sake */
    386             size_t n = ptr - pp->linestart_resp;
    387             memmove(buf, pp->linestart_resp, n);
    388             buf[n] = 0; /* zero terminate */
    389             keepon = FALSE;
    390             pp->linestart_resp = ptr + 1; /* advance pointer */
    391             i++; /* skip this before getting out */
    392 
    393             *size = pp->nread_resp; /* size of the response */
    394             pp->nread_resp = 0; /* restart */
    395             break;
    396           }
    397           perline = 0; /* line starts over here */
    398           pp->linestart_resp = ptr + 1;
    399         }
    400       }
    401 
    402       if(!keepon && (i != gotbytes)) {
    403         /* We found the end of the response lines, but we didn't parse the
    404            full chunk of data we have read from the server. We therefore need
    405            to store the rest of the data to be checked on the next invoke as
    406            it may actually contain another end of response already! */
    407         clipamount = gotbytes - i;
    408         restart = TRUE;
    409         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
    410                      "server response left\n",
    411                      (int)clipamount));
    412       }
    413       else if(keepon) {
    414 
    415         if((perline == gotbytes) && (gotbytes > data->set.buffer_size/2)) {
    416           /* We got an excessive line without newlines and we need to deal
    417              with it. We keep the first bytes of the line then we throw
    418              away the rest. */
    419           infof(data, "Excessive server response line length received, "
    420                 "%zd bytes. Stripping\n", gotbytes);
    421           restart = TRUE;
    422 
    423           /* we keep 40 bytes since all our pingpong protocols are only
    424              interested in the first piece */
    425           clipamount = 40;
    426         }
    427         else if(pp->nread_resp > (size_t)data->set.buffer_size/2) {
    428           /* We got a large chunk of data and there's potentially still
    429              trailing data to take care of, so we put any such part in the
    430              "cache", clear the buffer to make space and restart. */
    431           clipamount = perline;
    432           restart = TRUE;
    433         }
    434       }
    435       else if(i == gotbytes)
    436         restart = TRUE;
    437 
    438       if(clipamount) {
    439         pp->cache_size = clipamount;
    440         pp->cache = malloc(pp->cache_size);
    441         if(pp->cache)
    442           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
    443         else
    444           return CURLE_OUT_OF_MEMORY;
    445       }
    446       if(restart) {
    447         /* now reset a few variables to start over nicely from the start of
    448            the big buffer */
    449         pp->nread_resp = 0; /* start over from scratch in the buffer */
    450         ptr = pp->linestart_resp = buf;
    451         perline = 0;
    452       }
    453 
    454     } /* there was data */
    455 
    456   } /* while there's buffer left and loop is requested */
    457 
    458   pp->pending_resp = FALSE;
    459 
    460   return result;
    461 }
    462 
    463 int Curl_pp_getsock(struct pingpong *pp,
    464                     curl_socket_t *socks,
    465                     int numsocks)
    466 {
    467   struct connectdata *conn = pp->conn;
    468 
    469   if(!numsocks)
    470     return GETSOCK_BLANK;
    471 
    472   socks[0] = conn->sock[FIRSTSOCKET];
    473 
    474   if(pp->sendleft) {
    475     /* write mode */
    476     return GETSOCK_WRITESOCK(0);
    477   }
    478 
    479   /* read mode */
    480   return GETSOCK_READSOCK(0);
    481 }
    482 
    483 CURLcode Curl_pp_flushsend(struct pingpong *pp)
    484 {
    485   /* we have a piece of a command still left to send */
    486   struct connectdata *conn = pp->conn;
    487   ssize_t written;
    488   curl_socket_t sock = conn->sock[FIRSTSOCKET];
    489   CURLcode result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
    490                                pp->sendleft, pp->sendleft, &written);
    491   if(result)
    492     return result;
    493 
    494   if(written != (ssize_t)pp->sendleft) {
    495     /* only a fraction was sent */
    496     pp->sendleft -= written;
    497   }
    498   else {
    499     free(pp->sendthis);
    500     pp->sendthis = NULL;
    501     pp->sendleft = pp->sendsize = 0;
    502     pp->response = Curl_now();
    503   }
    504   return CURLE_OK;
    505 }
    506 
    507 CURLcode Curl_pp_disconnect(struct pingpong *pp)
    508 {
    509   free(pp->cache);
    510   pp->cache = NULL;
    511   return CURLE_OK;
    512 }
    513 
    514 bool Curl_pp_moredata(struct pingpong *pp)
    515 {
    516   return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
    517          TRUE : FALSE;
    518 }
    519 
    520 #endif
    521