1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2015, 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 http://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 #include "curl_printf.h"
38 
39 #include "curl_memory.h"
40 /* The last #include file should be: */
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 long Curl_pp_state_timeout(struct pingpong *pp)
48 {
49   struct connectdata *conn = pp->conn;
50   struct SessionHandle *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  */
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   long interval_ms;
87   long timeout_ms = Curl_pp_state_timeout(pp);
88   struct SessionHandle *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 */
Curl_pp_init(struct pingpong * pp)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  */
Curl_pp_vsendf(struct pingpong * pp,const char * fmt,va_list args)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 SessionHandle *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  */
Curl_pp_sendf(struct pingpong * pp,const char * fmt,...)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  */
Curl_pp_readresp(curl_socket_t sockfd,struct pingpong * pp,int * code,size_t * size)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 SessionHandle *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 
Curl_pp_getsock(struct pingpong * pp,curl_socket_t * socks,int numsocks)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 
Curl_pp_flushsend(struct pingpong * pp)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 
Curl_pp_disconnect(struct pingpong * pp)494 CURLcode Curl_pp_disconnect(struct pingpong *pp)
495 {
496   free(pp->cache);
497   pp->cache = NULL;
498   return CURLE_OK;
499 }
500 
Curl_pp_moredata(struct pingpong * pp)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