7a99357279bbf60aa04b726e39d63a57168b6398
[platform/upstream/curl.git] / lib / pingpong.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, 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 */
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_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  */
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_tvnow());
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 */
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_tvnow(); /* start response time-out now! */
147 }
148
149
150
151 /***********************************************************************
152  *
153  * Curl_pp_vsendf()
154  *
155  * Send the formated 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  */
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 = conn->data;
172
173 #ifdef HAVE_GSSAPI
174   enum protection_level data_sec = conn->data_prot;
175 #endif
176
177   DEBUGASSERT(pp->sendleft == 0);
178   DEBUGASSERT(pp->sendsize == 0);
179   DEBUGASSERT(pp->sendthis == NULL);
180
181   fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
182   if(!fmt_crlf)
183     return CURLE_OUT_OF_MEMORY;
184
185   s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
186   free(fmt_crlf);
187   if(!s)
188     return CURLE_OUT_OF_MEMORY;
189
190   bytes_written = 0;
191   write_len = strlen(s);
192
193   Curl_pp_init(pp);
194
195   result = Curl_convert_to_network(data, s, write_len);
196   /* Curl_convert_to_network calls failf if unsuccessful */
197   if(result) {
198     free(s);
199     return result;
200   }
201
202 #ifdef HAVE_GSSAPI
203   conn->data_prot = PROT_CMD;
204 #endif
205   result = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
206                      &bytes_written);
207 #ifdef HAVE_GSSAPI
208   DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
209   conn->data_prot = data_sec;
210 #endif
211
212   if(result) {
213     free(s);
214     return result;
215   }
216
217   if(conn->data->set.verbose)
218     Curl_debug(conn->data, CURLINFO_HEADER_OUT,
219                s, (size_t)bytes_written, conn);
220
221   if(bytes_written != (ssize_t)write_len) {
222     /* the whole chunk was not sent, keep it around and adjust sizes */
223     pp->sendthis = s;
224     pp->sendsize = write_len;
225     pp->sendleft = write_len - bytes_written;
226   }
227   else {
228     free(s);
229     pp->sendthis = NULL;
230     pp->sendleft = pp->sendsize = 0;
231     pp->response = Curl_tvnow();
232   }
233
234   return CURLE_OK;
235 }
236
237
238 /***********************************************************************
239  *
240  * Curl_pp_sendf()
241  *
242  * Send the formated string as a command to a pingpong server. Note that
243  * the string should not have any CRLF appended, as this function will
244  * append the necessary things itself.
245  *
246  * made to never block
247  */
248 CURLcode Curl_pp_sendf(struct pingpong *pp,
249                        const char *fmt, ...)
250 {
251   CURLcode result;
252   va_list ap;
253   va_start(ap, fmt);
254
255   result = Curl_pp_vsendf(pp, fmt, ap);
256
257   va_end(ap);
258
259   return result;
260 }
261
262 /*
263  * Curl_pp_readresp()
264  *
265  * Reads a piece of a server response.
266  */
267 CURLcode Curl_pp_readresp(curl_socket_t sockfd,
268                           struct pingpong *pp,
269                           int *code, /* return the server code if done */
270                           size_t *size) /* size of the response */
271 {
272   ssize_t perline; /* count bytes per line */
273   bool keepon=TRUE;
274   ssize_t gotbytes;
275   char *ptr;
276   struct connectdata *conn = pp->conn;
277   struct Curl_easy *data = conn->data;
278   char * const buf = data->state.buffer;
279   CURLcode result = CURLE_OK;
280
281   *code = 0; /* 0 for errors or not done */
282   *size = 0;
283
284   ptr=buf + pp->nread_resp;
285
286   /* number of bytes in the current line, so far */
287   perline = (ssize_t)(ptr-pp->linestart_resp);
288
289   while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
290
291     if(pp->cache) {
292       /* we had data in the "cache", copy that instead of doing an actual
293        * read
294        *
295        * pp->cache_size is cast to ssize_t here.  This should be safe, because
296        * it would have been populated with something of size int to begin
297        * with, even though its datatype may be larger than an int.
298        */
299       DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
300       memcpy(ptr, pp->cache, pp->cache_size);
301       gotbytes = (ssize_t)pp->cache_size;
302       free(pp->cache);    /* free the cache */
303       pp->cache = NULL;   /* clear the pointer */
304       pp->cache_size = 0; /* zero the size just in case */
305     }
306     else {
307 #ifdef HAVE_GSSAPI
308       enum protection_level prot = conn->data_prot;
309       conn->data_prot = PROT_CLEAR;
310 #endif
311       DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
312       result = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
313                          &gotbytes);
314 #ifdef HAVE_GSSAPI
315       DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
316       conn->data_prot = prot;
317 #endif
318       if(result == CURLE_AGAIN)
319         return CURLE_OK; /* return */
320
321       if(!result && (gotbytes > 0))
322         /* convert from the network encoding */
323         result = Curl_convert_from_network(data, ptr, gotbytes);
324       /* Curl_convert_from_network calls failf if unsuccessful */
325
326       if(result)
327         /* Set outer result variable to this error. */
328         keepon = FALSE;
329     }
330
331     if(!keepon)
332       ;
333     else if(gotbytes <= 0) {
334       keepon = FALSE;
335       result = CURLE_RECV_ERROR;
336       failf(data, "response reading failed");
337     }
338     else {
339       /* we got a whole chunk of data, which can be anything from one
340        * byte to a set of lines and possible just a piece of the last
341        * line */
342       ssize_t i;
343       ssize_t clipamount = 0;
344       bool restart = FALSE;
345
346       data->req.headerbytecount += (long)gotbytes;
347
348       pp->nread_resp += gotbytes;
349       for(i = 0; i < gotbytes; ptr++, i++) {
350         perline++;
351         if(*ptr=='\n') {
352           /* a newline is CRLF in pp-talk, so the CR is ignored as
353              the line isn't really terminated until the LF comes */
354
355           /* output debug output if that is requested */
356 #ifdef HAVE_GSSAPI
357           if(!conn->sec_complete)
358 #endif
359             if(data->set.verbose)
360               Curl_debug(data, CURLINFO_HEADER_IN,
361                          pp->linestart_resp, (size_t)perline, conn);
362
363           /*
364            * We pass all response-lines to the callback function registered
365            * for "headers". The response lines can be seen as a kind of
366            * headers.
367            */
368           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
369                                      pp->linestart_resp, perline);
370           if(result)
371             return result;
372
373           if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
374             /* This is the end of the last line, copy the last line to the
375                start of the buffer and zero terminate, for old times sake */
376             size_t n = ptr - pp->linestart_resp;
377             memmove(buf, pp->linestart_resp, n);
378             buf[n]=0; /* zero terminate */
379             keepon=FALSE;
380             pp->linestart_resp = ptr+1; /* advance pointer */
381             i++; /* skip this before getting out */
382
383             *size = pp->nread_resp; /* size of the response */
384             pp->nread_resp = 0; /* restart */
385             break;
386           }
387           perline=0; /* line starts over here */
388           pp->linestart_resp = ptr+1;
389         }
390       }
391
392       if(!keepon && (i != gotbytes)) {
393         /* We found the end of the response lines, but we didn't parse the
394            full chunk of data we have read from the server. We therefore need
395            to store the rest of the data to be checked on the next invoke as
396            it may actually contain another end of response already! */
397         clipamount = gotbytes - i;
398         restart = TRUE;
399         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
400                      "server response left\n",
401                      (int)clipamount));
402       }
403       else if(keepon) {
404
405         if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
406           /* We got an excessive line without newlines and we need to deal
407              with it. We keep the first bytes of the line then we throw
408              away the rest. */
409           infof(data, "Excessive server response line length received, "
410                 "%zd bytes. Stripping\n", gotbytes);
411           restart = TRUE;
412
413           /* we keep 40 bytes since all our pingpong protocols are only
414              interested in the first piece */
415           clipamount = 40;
416         }
417         else if(pp->nread_resp > BUFSIZE/2) {
418           /* We got a large chunk of data and there's potentially still
419              trailing data to take care of, so we put any such part in the
420              "cache", clear the buffer to make space and restart. */
421           clipamount = perline;
422           restart = TRUE;
423         }
424       }
425       else if(i == gotbytes)
426         restart = TRUE;
427
428       if(clipamount) {
429         pp->cache_size = clipamount;
430         pp->cache = malloc(pp->cache_size);
431         if(pp->cache)
432           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
433         else
434           return CURLE_OUT_OF_MEMORY;
435       }
436       if(restart) {
437         /* now reset a few variables to start over nicely from the start of
438            the big buffer */
439         pp->nread_resp = 0; /* start over from scratch in the buffer */
440         ptr = pp->linestart_resp = buf;
441         perline = 0;
442       }
443
444     } /* there was data */
445
446   } /* while there's buffer left and loop is requested */
447
448   pp->pending_resp = FALSE;
449
450   return result;
451 }
452
453 int Curl_pp_getsock(struct pingpong *pp,
454                     curl_socket_t *socks,
455                     int numsocks)
456 {
457   struct connectdata *conn = pp->conn;
458
459   if(!numsocks)
460     return GETSOCK_BLANK;
461
462   socks[0] = conn->sock[FIRSTSOCKET];
463
464   if(pp->sendleft) {
465     /* write mode */
466     return GETSOCK_WRITESOCK(0);
467   }
468
469   /* read mode */
470   return GETSOCK_READSOCK(0);
471 }
472
473 CURLcode Curl_pp_flushsend(struct pingpong *pp)
474 {
475   /* we have a piece of a command still left to send */
476   struct connectdata *conn = pp->conn;
477   ssize_t written;
478   curl_socket_t sock = conn->sock[FIRSTSOCKET];
479   CURLcode result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
480                                pp->sendleft, pp->sendleft, &written);
481   if(result)
482     return result;
483
484   if(written != (ssize_t)pp->sendleft) {
485     /* only a fraction was sent */
486     pp->sendleft -= written;
487   }
488   else {
489     free(pp->sendthis);
490     pp->sendthis=NULL;
491     pp->sendleft = pp->sendsize = 0;
492     pp->response = Curl_tvnow();
493   }
494   return CURLE_OK;
495 }
496
497 CURLcode Curl_pp_disconnect(struct pingpong *pp)
498 {
499   free(pp->cache);
500   pp->cache = NULL;
501   return CURLE_OK;
502 }
503
504 bool Curl_pp_moredata(struct pingpong *pp)
505 {
506   return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
507          TRUE : FALSE;
508 }
509
510 #endif