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