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