Revert "Update to 7.40.1"
[platform/upstream/curl.git] / lib / pingpong.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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 error;
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   error = Curl_convert_to_network(data, s, write_len);
195   /* Curl_convert_to_network calls failf if unsuccessful */
196   if(error) {
197     free(s);
198     return error;
199   }
200
201 #ifdef HAVE_GSSAPI
202   conn->data_prot = PROT_CMD;
203 #endif
204   error = 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(error) {
212     free(s);
213     return error;
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 res;
251   va_list ap;
252   va_start(ap, fmt);
253
254   res = Curl_pp_vsendf(pp, fmt, ap);
255
256   va_end(ap);
257
258   return res;
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   keepon=TRUE;
289
290   while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
291
292     if(pp->cache) {
293       /* we had data in the "cache", copy that instead of doing an actual
294        * read
295        *
296        * pp->cache_size is cast to ssize_t here.  This should be safe, because
297        * it would have been populated with something of size int to begin
298        * with, even though its datatype may be larger than an int.
299        */
300       DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
301       memcpy(ptr, pp->cache, pp->cache_size);
302       gotbytes = (ssize_t)pp->cache_size;
303       free(pp->cache);    /* free the cache */
304       pp->cache = NULL;   /* clear the pointer */
305       pp->cache_size = 0; /* zero the size just in case */
306     }
307     else {
308       int res;
309 #ifdef HAVE_GSSAPI
310       enum protection_level prot = conn->data_prot;
311       conn->data_prot = PROT_CLEAR;
312 #endif
313       DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
314       res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
315                       &gotbytes);
316 #ifdef HAVE_GSSAPI
317       DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
318       conn->data_prot = prot;
319 #endif
320       if(res == CURLE_AGAIN)
321         return CURLE_OK; /* return */
322
323       if((res == CURLE_OK) && (gotbytes > 0))
324         /* convert from the network encoding */
325         res = Curl_convert_from_network(data, ptr, gotbytes);
326       /* Curl_convert_from_network calls failf if unsuccessful */
327
328       if(CURLE_OK != res) {
329         result = (CURLcode)res; /* Set outer result variable to this error. */
330         keepon = FALSE;
331       }
332     }
333
334     if(!keepon)
335       ;
336     else if(gotbytes <= 0) {
337       keepon = FALSE;
338       result = CURLE_RECV_ERROR;
339       failf(data, "response reading failed");
340     }
341     else {
342       /* we got a whole chunk of data, which can be anything from one
343        * byte to a set of lines and possible just a piece of the last
344        * line */
345       ssize_t i;
346       ssize_t clipamount = 0;
347       bool restart = FALSE;
348
349       data->req.headerbytecount += (long)gotbytes;
350
351       pp->nread_resp += gotbytes;
352       for(i = 0; i < gotbytes; ptr++, i++) {
353         perline++;
354         if(*ptr=='\n') {
355           /* a newline is CRLF in pp-talk, so the CR is ignored as
356              the line isn't really terminated until the LF comes */
357
358           /* output debug output if that is requested */
359 #ifdef HAVE_GSSAPI
360           if(!conn->sec_complete)
361 #endif
362             if(data->set.verbose)
363               Curl_debug(data, CURLINFO_HEADER_IN,
364                          pp->linestart_resp, (size_t)perline, conn);
365
366           /*
367            * We pass all response-lines to the callback function registered
368            * for "headers". The response lines can be seen as a kind of
369            * headers.
370            */
371           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
372                                      pp->linestart_resp, perline);
373           if(result)
374             return result;
375
376           if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
377             /* This is the end of the last line, copy the last line to the
378                start of the buffer and zero terminate, for old times sake */
379             size_t n = ptr - pp->linestart_resp;
380             memmove(buf, pp->linestart_resp, n);
381             buf[n]=0; /* zero terminate */
382             keepon=FALSE;
383             pp->linestart_resp = ptr+1; /* advance pointer */
384             i++; /* skip this before getting out */
385
386             *size = pp->nread_resp; /* size of the response */
387             pp->nread_resp = 0; /* restart */
388             break;
389           }
390           perline=0; /* line starts over here */
391           pp->linestart_resp = ptr+1;
392         }
393       }
394
395       if(!keepon && (i != gotbytes)) {
396         /* We found the end of the response lines, but we didn't parse the
397            full chunk of data we have read from the server. We therefore need
398            to store the rest of the data to be checked on the next invoke as
399            it may actually contain another end of response already! */
400         clipamount = gotbytes - i;
401         restart = TRUE;
402         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
403                      "server response left\n",
404                      (int)clipamount));
405       }
406       else if(keepon) {
407
408         if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
409           /* We got an excessive line without newlines and we need to deal
410              with it. We keep the first bytes of the line then we throw
411              away the rest. */
412           infof(data, "Excessive server response line length received, "
413                 "%zd bytes. Stripping\n", gotbytes);
414           restart = TRUE;
415
416           /* we keep 40 bytes since all our pingpong protocols are only
417              interested in the first piece */
418           clipamount = 40;
419         }
420         else if(pp->nread_resp > BUFSIZE/2) {
421           /* We got a large chunk of data and there's potentially still
422              trailing data to take care of, so we put any such part in the
423              "cache", clear the buffer to make space and restart. */
424           clipamount = perline;
425           restart = TRUE;
426         }
427       }
428       else if(i == gotbytes)
429         restart = TRUE;
430
431       if(clipamount) {
432         pp->cache_size = clipamount;
433         pp->cache = malloc(pp->cache_size);
434         if(pp->cache)
435           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
436         else
437           return CURLE_OUT_OF_MEMORY;
438       }
439       if(restart) {
440         /* now reset a few variables to start over nicely from the start of
441            the big buffer */
442         pp->nread_resp = 0; /* start over from scratch in the buffer */
443         ptr = pp->linestart_resp = buf;
444         perline = 0;
445       }
446
447     } /* there was data */
448
449   } /* while there's buffer left and loop is requested */
450
451   pp->pending_resp = FALSE;
452
453   return result;
454 }
455
456 int Curl_pp_getsock(struct pingpong *pp,
457                     curl_socket_t *socks,
458                     int numsocks)
459 {
460   struct connectdata *conn = pp->conn;
461
462   if(!numsocks)
463     return GETSOCK_BLANK;
464
465   socks[0] = conn->sock[FIRSTSOCKET];
466
467   if(pp->sendleft) {
468     /* write mode */
469     return GETSOCK_WRITESOCK(0);
470   }
471
472   /* read mode */
473   return GETSOCK_READSOCK(0);
474 }
475
476 CURLcode Curl_pp_flushsend(struct pingpong *pp)
477 {
478   /* we have a piece of a command still left to send */
479   struct connectdata *conn = pp->conn;
480   ssize_t written;
481   CURLcode result = CURLE_OK;
482   curl_socket_t sock = conn->sock[FIRSTSOCKET];
483
484   result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
485                       pp->sendleft, pp->sendleft, &written);
486   if(result)
487     return result;
488
489   if(written != (ssize_t)pp->sendleft) {
490     /* only a fraction was sent */
491     pp->sendleft -= written;
492   }
493   else {
494     free(pp->sendthis);
495     pp->sendthis=NULL;
496     pp->sendleft = pp->sendsize = 0;
497     pp->response = Curl_tvnow();
498   }
499   return CURLE_OK;
500 }
501
502 CURLcode Curl_pp_disconnect(struct pingpong *pp)
503 {
504   if(pp->cache) {
505     free(pp->cache);
506     pp->cache = NULL;
507   }
508   return CURLE_OK;
509 }
510
511 bool Curl_pp_moredata(struct pingpong *pp)
512 {
513   return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
514          TRUE : FALSE;
515 }
516
517 #endif