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