• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2018, 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 */
Curl_pp_state_timeout(struct pingpong * pp,bool disconnecting)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     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       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  */
Curl_pp_statemach(struct pingpong * pp,bool block,bool disconnecting)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 */
Curl_pp_init(struct pingpong * pp)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  */
Curl_pp_vsendf(struct pingpong * pp,const char * fmt,va_list args)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  */
Curl_pp_sendf(struct pingpong * pp,const char * fmt,...)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  */
Curl_pp_readresp(curl_socket_t sockfd,struct pingpong * pp,int * code,size_t * size)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 
Curl_pp_getsock(struct pingpong * pp,curl_socket_t * socks,int numsocks)465 int Curl_pp_getsock(struct pingpong *pp,
466                     curl_socket_t *socks,
467                     int numsocks)
468 {
469   struct connectdata *conn = pp->conn;
470 
471   if(!numsocks)
472     return GETSOCK_BLANK;
473 
474   socks[0] = conn->sock[FIRSTSOCKET];
475 
476   if(pp->sendleft) {
477     /* write mode */
478     return GETSOCK_WRITESOCK(0);
479   }
480 
481   /* read mode */
482   return GETSOCK_READSOCK(0);
483 }
484 
Curl_pp_flushsend(struct pingpong * pp)485 CURLcode Curl_pp_flushsend(struct pingpong *pp)
486 {
487   /* we have a piece of a command still left to send */
488   struct connectdata *conn = pp->conn;
489   ssize_t written;
490   curl_socket_t sock = conn->sock[FIRSTSOCKET];
491   CURLcode result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
492                                pp->sendleft, pp->sendleft, &written);
493   if(result)
494     return result;
495 
496   if(written != (ssize_t)pp->sendleft) {
497     /* only a fraction was sent */
498     pp->sendleft -= written;
499   }
500   else {
501     free(pp->sendthis);
502     pp->sendthis = NULL;
503     pp->sendleft = pp->sendsize = 0;
504     pp->response = Curl_now();
505   }
506   return CURLE_OK;
507 }
508 
Curl_pp_disconnect(struct pingpong * pp)509 CURLcode Curl_pp_disconnect(struct pingpong *pp)
510 {
511   free(pp->cache);
512   pp->cache = NULL;
513   return CURLE_OK;
514 }
515 
Curl_pp_moredata(struct pingpong * pp)516 bool Curl_pp_moredata(struct pingpong *pp)
517 {
518   return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
519          TRUE : FALSE;
520 }
521 
522 #endif
523