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