pingpong.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  *   'pingpong' is for generic back-and-forth support functions used by FTP,
00022  *   IMAP, POP3, SMTP and whatever more that likes them.
00023  *
00024  ***************************************************************************/
00025 
00026 #include "curl_setup.h"
00027 
00028 #include "urldata.h"
00029 #include "sendf.h"
00030 #include "select.h"
00031 #include "progress.h"
00032 #include "speedcheck.h"
00033 #include "pingpong.h"
00034 #include "multiif.h"
00035 #include "non-ascii.h"
00036 #include "vtls/vtls.h"
00037 
00038 /* The last 3 #include files should be in this order */
00039 #include "curl_printf.h"
00040 #include "curl_memory.h"
00041 #include "memdebug.h"
00042 
00043 #ifdef USE_PINGPONG
00044 
00045 /* Returns timeout in ms. 0 or negative number means the timeout has already
00046    triggered */
00047 time_t Curl_pp_state_timeout(struct pingpong *pp)
00048 {
00049   struct connectdata *conn = pp->conn;
00050   struct Curl_easy *data=conn->data;
00051   time_t timeout_ms; /* in milliseconds */
00052   time_t timeout2_ms; /* in milliseconds */
00053   long response_time= (data->set.server_response_timeout)?
00054     data->set.server_response_timeout: pp->response_time;
00055 
00056   /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
00057      remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
00058      supposed to govern the response for any given server response, not for
00059      the time from connect to the given server response. */
00060 
00061   /* Without a requested timeout, we only wait 'response_time' seconds for the
00062      full response to arrive before we bail out */
00063   timeout_ms = response_time -
00064     Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
00065 
00066   if(data->set.timeout) {
00067     /* if timeout is requested, find out how much remaining time we have */
00068     timeout2_ms = data->set.timeout - /* timeout time */
00069       Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
00070 
00071     /* pick the lowest number */
00072     timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
00073   }
00074 
00075   return timeout_ms;
00076 }
00077 
00078 /*
00079  * Curl_pp_statemach()
00080  */
00081 CURLcode Curl_pp_statemach(struct pingpong *pp, bool block)
00082 {
00083   struct connectdata *conn = pp->conn;
00084   curl_socket_t sock = conn->sock[FIRSTSOCKET];
00085   int rc;
00086   time_t interval_ms;
00087   time_t timeout_ms = Curl_pp_state_timeout(pp);
00088   struct Curl_easy *data=conn->data;
00089   CURLcode result = CURLE_OK;
00090 
00091   if(timeout_ms <=0) {
00092     failf(data, "server response timeout");
00093     return CURLE_OPERATION_TIMEDOUT; /* already too little time */
00094   }
00095 
00096   if(block) {
00097     interval_ms = 1000;  /* use 1 second timeout intervals */
00098     if(timeout_ms < interval_ms)
00099       interval_ms = timeout_ms;
00100   }
00101   else
00102     interval_ms = 0; /* immediate */
00103 
00104   if(Curl_ssl_data_pending(conn, FIRSTSOCKET))
00105     rc = 1;
00106   else if(Curl_pp_moredata(pp))
00107     /* We are receiving and there is data in the cache so just read it */
00108     rc = 1;
00109   else if(!pp->sendleft && Curl_ssl_data_pending(conn, FIRSTSOCKET))
00110     /* We are receiving and there is data ready in the SSL library */
00111     rc = 1;
00112   else
00113     rc = Curl_socket_check(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
00114                            CURL_SOCKET_BAD,
00115                            pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
00116                            interval_ms);
00117 
00118   if(block) {
00119     /* if we didn't wait, we don't have to spend time on this now */
00120     if(Curl_pgrsUpdate(conn))
00121       result = CURLE_ABORTED_BY_CALLBACK;
00122     else
00123       result = Curl_speedcheck(data, Curl_tvnow());
00124 
00125     if(result)
00126       return result;
00127   }
00128 
00129   if(rc == -1) {
00130     failf(data, "select/poll error");
00131     result = CURLE_OUT_OF_MEMORY;
00132   }
00133   else if(rc)
00134     result = pp->statemach_act(conn);
00135 
00136   return result;
00137 }
00138 
00139 /* initialize stuff to prepare for reading a fresh new response */
00140 void Curl_pp_init(struct pingpong *pp)
00141 {
00142   struct connectdata *conn = pp->conn;
00143   pp->nread_resp = 0;
00144   pp->linestart_resp = conn->data->state.buffer;
00145   pp->pending_resp = TRUE;
00146   pp->response = Curl_tvnow(); /* start response time-out now! */
00147 }
00148 
00149 
00150 
00151 /***********************************************************************
00152  *
00153  * Curl_pp_vsendf()
00154  *
00155  * Send the formated string as a command to a pingpong server. Note that
00156  * the string should not have any CRLF appended, as this function will
00157  * append the necessary things itself.
00158  *
00159  * made to never block
00160  */
00161 CURLcode Curl_pp_vsendf(struct pingpong *pp,
00162                         const char *fmt,
00163                         va_list args)
00164 {
00165   ssize_t bytes_written;
00166   size_t write_len;
00167   char *fmt_crlf;
00168   char *s;
00169   CURLcode result;
00170   struct connectdata *conn = pp->conn;
00171   struct Curl_easy *data = conn->data;
00172 
00173 #ifdef HAVE_GSSAPI
00174   enum protection_level data_sec = conn->data_prot;
00175 #endif
00176 
00177   DEBUGASSERT(pp->sendleft == 0);
00178   DEBUGASSERT(pp->sendsize == 0);
00179   DEBUGASSERT(pp->sendthis == NULL);
00180 
00181   fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
00182   if(!fmt_crlf)
00183     return CURLE_OUT_OF_MEMORY;
00184 
00185   s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
00186   free(fmt_crlf);
00187   if(!s)
00188     return CURLE_OUT_OF_MEMORY;
00189 
00190   bytes_written = 0;
00191   write_len = strlen(s);
00192 
00193   Curl_pp_init(pp);
00194 
00195   result = Curl_convert_to_network(data, s, write_len);
00196   /* Curl_convert_to_network calls failf if unsuccessful */
00197   if(result) {
00198     free(s);
00199     return result;
00200   }
00201 
00202 #ifdef HAVE_GSSAPI
00203   conn->data_prot = PROT_CMD;
00204 #endif
00205   result = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
00206                      &bytes_written);
00207 #ifdef HAVE_GSSAPI
00208   DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
00209   conn->data_prot = data_sec;
00210 #endif
00211 
00212   if(result) {
00213     free(s);
00214     return result;
00215   }
00216 
00217   if(conn->data->set.verbose)
00218     Curl_debug(conn->data, CURLINFO_HEADER_OUT,
00219                s, (size_t)bytes_written, conn);
00220 
00221   if(bytes_written != (ssize_t)write_len) {
00222     /* the whole chunk was not sent, keep it around and adjust sizes */
00223     pp->sendthis = s;
00224     pp->sendsize = write_len;
00225     pp->sendleft = write_len - bytes_written;
00226   }
00227   else {
00228     free(s);
00229     pp->sendthis = NULL;
00230     pp->sendleft = pp->sendsize = 0;
00231     pp->response = Curl_tvnow();
00232   }
00233 
00234   return CURLE_OK;
00235 }
00236 
00237 
00238 /***********************************************************************
00239  *
00240  * Curl_pp_sendf()
00241  *
00242  * Send the formated string as a command to a pingpong server. Note that
00243  * the string should not have any CRLF appended, as this function will
00244  * append the necessary things itself.
00245  *
00246  * made to never block
00247  */
00248 CURLcode Curl_pp_sendf(struct pingpong *pp,
00249                        const char *fmt, ...)
00250 {
00251   CURLcode result;
00252   va_list ap;
00253   va_start(ap, fmt);
00254 
00255   result = Curl_pp_vsendf(pp, fmt, ap);
00256 
00257   va_end(ap);
00258 
00259   return result;
00260 }
00261 
00262 /*
00263  * Curl_pp_readresp()
00264  *
00265  * Reads a piece of a server response.
00266  */
00267 CURLcode Curl_pp_readresp(curl_socket_t sockfd,
00268                           struct pingpong *pp,
00269                           int *code, /* return the server code if done */
00270                           size_t *size) /* size of the response */
00271 {
00272   ssize_t perline; /* count bytes per line */
00273   bool keepon=TRUE;
00274   ssize_t gotbytes;
00275   char *ptr;
00276   struct connectdata *conn = pp->conn;
00277   struct Curl_easy *data = conn->data;
00278   char * const buf = data->state.buffer;
00279   CURLcode result = CURLE_OK;
00280 
00281   *code = 0; /* 0 for errors or not done */
00282   *size = 0;
00283 
00284   ptr=buf + pp->nread_resp;
00285 
00286   /* number of bytes in the current line, so far */
00287   perline = (ssize_t)(ptr-pp->linestart_resp);
00288 
00289   while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
00290 
00291     if(pp->cache) {
00292       /* we had data in the "cache", copy that instead of doing an actual
00293        * read
00294        *
00295        * pp->cache_size is cast to ssize_t here.  This should be safe, because
00296        * it would have been populated with something of size int to begin
00297        * with, even though its datatype may be larger than an int.
00298        */
00299       DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
00300       memcpy(ptr, pp->cache, pp->cache_size);
00301       gotbytes = (ssize_t)pp->cache_size;
00302       free(pp->cache);    /* free the cache */
00303       pp->cache = NULL;   /* clear the pointer */
00304       pp->cache_size = 0; /* zero the size just in case */
00305     }
00306     else {
00307 #ifdef HAVE_GSSAPI
00308       enum protection_level prot = conn->data_prot;
00309       conn->data_prot = PROT_CLEAR;
00310 #endif
00311       DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
00312       result = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
00313                          &gotbytes);
00314 #ifdef HAVE_GSSAPI
00315       DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
00316       conn->data_prot = prot;
00317 #endif
00318       if(result == CURLE_AGAIN)
00319         return CURLE_OK; /* return */
00320 
00321       if(!result && (gotbytes > 0))
00322         /* convert from the network encoding */
00323         result = Curl_convert_from_network(data, ptr, gotbytes);
00324       /* Curl_convert_from_network calls failf if unsuccessful */
00325 
00326       if(result)
00327         /* Set outer result variable to this error. */
00328         keepon = FALSE;
00329     }
00330 
00331     if(!keepon)
00332       ;
00333     else if(gotbytes <= 0) {
00334       keepon = FALSE;
00335       result = CURLE_RECV_ERROR;
00336       failf(data, "response reading failed");
00337     }
00338     else {
00339       /* we got a whole chunk of data, which can be anything from one
00340        * byte to a set of lines and possible just a piece of the last
00341        * line */
00342       ssize_t i;
00343       ssize_t clipamount = 0;
00344       bool restart = FALSE;
00345 
00346       data->req.headerbytecount += (long)gotbytes;
00347 
00348       pp->nread_resp += gotbytes;
00349       for(i = 0; i < gotbytes; ptr++, i++) {
00350         perline++;
00351         if(*ptr=='\n') {
00352           /* a newline is CRLF in pp-talk, so the CR is ignored as
00353              the line isn't really terminated until the LF comes */
00354 
00355           /* output debug output if that is requested */
00356 #ifdef HAVE_GSSAPI
00357           if(!conn->sec_complete)
00358 #endif
00359             if(data->set.verbose)
00360               Curl_debug(data, CURLINFO_HEADER_IN,
00361                          pp->linestart_resp, (size_t)perline, conn);
00362 
00363           /*
00364            * We pass all response-lines to the callback function registered
00365            * for "headers". The response lines can be seen as a kind of
00366            * headers.
00367            */
00368           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
00369                                      pp->linestart_resp, perline);
00370           if(result)
00371             return result;
00372 
00373           if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
00374             /* This is the end of the last line, copy the last line to the
00375                start of the buffer and zero terminate, for old times sake */
00376             size_t n = ptr - pp->linestart_resp;
00377             memmove(buf, pp->linestart_resp, n);
00378             buf[n]=0; /* zero terminate */
00379             keepon=FALSE;
00380             pp->linestart_resp = ptr+1; /* advance pointer */
00381             i++; /* skip this before getting out */
00382 
00383             *size = pp->nread_resp; /* size of the response */
00384             pp->nread_resp = 0; /* restart */
00385             break;
00386           }
00387           perline=0; /* line starts over here */
00388           pp->linestart_resp = ptr+1;
00389         }
00390       }
00391 
00392       if(!keepon && (i != gotbytes)) {
00393         /* We found the end of the response lines, but we didn't parse the
00394            full chunk of data we have read from the server. We therefore need
00395            to store the rest of the data to be checked on the next invoke as
00396            it may actually contain another end of response already! */
00397         clipamount = gotbytes - i;
00398         restart = TRUE;
00399         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
00400                      "server response left\n",
00401                      (int)clipamount));
00402       }
00403       else if(keepon) {
00404 
00405         if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
00406           /* We got an excessive line without newlines and we need to deal
00407              with it. We keep the first bytes of the line then we throw
00408              away the rest. */
00409           infof(data, "Excessive server response line length received, "
00410                 "%zd bytes. Stripping\n", gotbytes);
00411           restart = TRUE;
00412 
00413           /* we keep 40 bytes since all our pingpong protocols are only
00414              interested in the first piece */
00415           clipamount = 40;
00416         }
00417         else if(pp->nread_resp > BUFSIZE/2) {
00418           /* We got a large chunk of data and there's potentially still
00419              trailing data to take care of, so we put any such part in the
00420              "cache", clear the buffer to make space and restart. */
00421           clipamount = perline;
00422           restart = TRUE;
00423         }
00424       }
00425       else if(i == gotbytes)
00426         restart = TRUE;
00427 
00428       if(clipamount) {
00429         pp->cache_size = clipamount;
00430         pp->cache = malloc(pp->cache_size);
00431         if(pp->cache)
00432           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
00433         else
00434           return CURLE_OUT_OF_MEMORY;
00435       }
00436       if(restart) {
00437         /* now reset a few variables to start over nicely from the start of
00438            the big buffer */
00439         pp->nread_resp = 0; /* start over from scratch in the buffer */
00440         ptr = pp->linestart_resp = buf;
00441         perline = 0;
00442       }
00443 
00444     } /* there was data */
00445 
00446   } /* while there's buffer left and loop is requested */
00447 
00448   pp->pending_resp = FALSE;
00449 
00450   return result;
00451 }
00452 
00453 int Curl_pp_getsock(struct pingpong *pp,
00454                     curl_socket_t *socks,
00455                     int numsocks)
00456 {
00457   struct connectdata *conn = pp->conn;
00458 
00459   if(!numsocks)
00460     return GETSOCK_BLANK;
00461 
00462   socks[0] = conn->sock[FIRSTSOCKET];
00463 
00464   if(pp->sendleft) {
00465     /* write mode */
00466     return GETSOCK_WRITESOCK(0);
00467   }
00468 
00469   /* read mode */
00470   return GETSOCK_READSOCK(0);
00471 }
00472 
00473 CURLcode Curl_pp_flushsend(struct pingpong *pp)
00474 {
00475   /* we have a piece of a command still left to send */
00476   struct connectdata *conn = pp->conn;
00477   ssize_t written;
00478   curl_socket_t sock = conn->sock[FIRSTSOCKET];
00479   CURLcode result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
00480                                pp->sendleft, pp->sendleft, &written);
00481   if(result)
00482     return result;
00483 
00484   if(written != (ssize_t)pp->sendleft) {
00485     /* only a fraction was sent */
00486     pp->sendleft -= written;
00487   }
00488   else {
00489     free(pp->sendthis);
00490     pp->sendthis=NULL;
00491     pp->sendleft = pp->sendsize = 0;
00492     pp->response = Curl_tvnow();
00493   }
00494   return CURLE_OK;
00495 }
00496 
00497 CURLcode Curl_pp_disconnect(struct pingpong *pp)
00498 {
00499   free(pp->cache);
00500   pp->cache = NULL;
00501   return CURLE_OK;
00502 }
00503 
00504 bool Curl_pp_moredata(struct pingpong *pp)
00505 {
00506   return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
00507          TRUE : FALSE;
00508 }
00509 
00510 #endif


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06