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