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 */
Curl_pp_state_timeout(struct pingpong * pp,bool disconnecting)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 */
Curl_pp_statemach(struct pingpong * pp,bool block,bool disconnecting)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 */
Curl_pp_init(struct pingpong * pp)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 */
Curl_pp_setup(struct pingpong * pp)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 */
Curl_pp_vsendf(struct pingpong * pp,const char * fmt,va_list args)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 */
Curl_pp_sendf(struct pingpong * pp,const char * fmt,...)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 */
Curl_pp_readresp(curl_socket_t sockfd,struct pingpong * pp,int * code,size_t * size)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
Curl_pp_getsock(struct pingpong * pp,curl_socket_t * socks)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
Curl_pp_flushsend(struct pingpong * pp)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
Curl_pp_disconnect(struct pingpong * pp)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
Curl_pp_moredata(struct pingpong * pp)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
513