Imported Upstream version 7.48.0
[platform/upstream/curl.git] / lib / pingpong.c
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 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 #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 */
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  */
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 */
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  */
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  */
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  */
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
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
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
494 CURLcode Curl_pp_disconnect(struct pingpong *pp)
495 {
496   free(pp->cache);
497   pp->cache = NULL;
498   return CURLE_OK;
499 }
500
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