• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, 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)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  */
Curl_pp_statemach(struct pingpong * pp,bool block)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 */
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,
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  */
Curl_pp_sendf(struct pingpong * pp,const char * fmt,...)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  */
Curl_pp_readresp(curl_socket_t sockfd,struct pingpong * pp,int * code,size_t * size)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 
Curl_pp_getsock(struct pingpong * pp,curl_socket_t * socks,int numsocks)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 
Curl_pp_flushsend(struct pingpong * pp)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 
Curl_pp_disconnect(struct pingpong * pp)507 CURLcode Curl_pp_disconnect(struct pingpong *pp)
508 {
509   free(pp->cache);
510   pp->cache = NULL;
511   return CURLE_OK;
512 }
513 
Curl_pp_moredata(struct pingpong * pp)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