pingpong.c
Go to the documentation of this file.
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)?
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;
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;
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 
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 */
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 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  */
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;
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)
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 formatted 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  */
249  const char *fmt, ...)
250 {
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  */
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;
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 < (size_t)data->set.buffer_size) &&
290  (keepon && !result)) {
291 
292  if(pp->cache) {
293  /* we had data in the "cache", copy that instead of doing an actual
294  * read
295  *
296  * pp->cache_size is cast to ssize_t here. This should be safe, because
297  * it would have been populated with something of size int to begin
298  * with, even though its datatype may be larger than an int.
299  */
300  DEBUGASSERT((ptr + pp->cache_size) <= (buf + data->set.buffer_size + 1));
301  memcpy(ptr, pp->cache, pp->cache_size);
302  gotbytes = (ssize_t)pp->cache_size;
303  free(pp->cache); /* free the cache */
304  pp->cache = NULL; /* clear the pointer */
305  pp->cache_size = 0; /* zero the size just in case */
306  }
307  else {
308 #ifdef HAVE_GSSAPI
309  enum protection_level prot = conn->data_prot;
310  conn->data_prot = PROT_CLEAR;
311 #endif
312  DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <=
313  (buf + data->set.buffer_size + 1));
314  result = Curl_read(conn, sockfd, ptr,
315  data->set.buffer_size - pp->nread_resp,
316  &gotbytes);
317 #ifdef HAVE_GSSAPI
318  DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST);
319  conn->data_prot = prot;
320 #endif
321  if(result == CURLE_AGAIN)
322  return CURLE_OK; /* return */
323 
324  if(!result && (gotbytes > 0))
325  /* convert from the network encoding */
326  result = Curl_convert_from_network(data, ptr, gotbytes);
327  /* Curl_convert_from_network calls failf if unsuccessful */
328 
329  if(result)
330  /* Set outer result variable to this error. */
331  keepon = FALSE;
332  }
333 
334  if(!keepon)
335  ;
336  else if(gotbytes <= 0) {
337  keepon = FALSE;
338  result = CURLE_RECV_ERROR;
339  failf(data, "response reading failed");
340  }
341  else {
342  /* we got a whole chunk of data, which can be anything from one
343  * byte to a set of lines and possible just a piece of the last
344  * line */
345  ssize_t i;
346  ssize_t clipamount = 0;
347  bool restart = FALSE;
348 
349  data->req.headerbytecount += (long)gotbytes;
350 
351  pp->nread_resp += gotbytes;
352  for(i = 0; i < gotbytes; ptr++, i++) {
353  perline++;
354  if(*ptr == '\n') {
355  /* a newline is CRLF in pp-talk, so the CR is ignored as
356  the line isn't really terminated until the LF comes */
357 
358  /* output debug output if that is requested */
359 #ifdef HAVE_GSSAPI
360  if(!conn->sec_complete)
361 #endif
362  if(data->set.verbose)
364  pp->linestart_resp, (size_t)perline, conn);
365 
366  /*
367  * We pass all response-lines to the callback function registered
368  * for "headers". The response lines can be seen as a kind of
369  * headers.
370  */
371  result = Curl_client_write(conn, CLIENTWRITE_HEADER,
372  pp->linestart_resp, perline);
373  if(result)
374  return result;
375 
376  if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
377  /* This is the end of the last line, copy the last line to the
378  start of the buffer and zero terminate, for old times sake */
379  size_t n = ptr - pp->linestart_resp;
380  memmove(buf, pp->linestart_resp, n);
381  buf[n] = 0; /* zero terminate */
382  keepon = FALSE;
383  pp->linestart_resp = ptr + 1; /* advance pointer */
384  i++; /* skip this before getting out */
385 
386  *size = pp->nread_resp; /* size of the response */
387  pp->nread_resp = 0; /* restart */
388  break;
389  }
390  perline = 0; /* line starts over here */
391  pp->linestart_resp = ptr + 1;
392  }
393  }
394 
395  if(!keepon && (i != gotbytes)) {
396  /* We found the end of the response lines, but we didn't parse the
397  full chunk of data we have read from the server. We therefore need
398  to store the rest of the data to be checked on the next invoke as
399  it may actually contain another end of response already! */
400  clipamount = gotbytes - i;
401  restart = TRUE;
402  DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
403  "server response left\n",
404  (int)clipamount));
405  }
406  else if(keepon) {
407 
408  if((perline == gotbytes) && (gotbytes > data->set.buffer_size/2)) {
409  /* We got an excessive line without newlines and we need to deal
410  with it. We keep the first bytes of the line then we throw
411  away the rest. */
412  infof(data, "Excessive server response line length received, "
413  "%zd bytes. Stripping\n", gotbytes);
414  restart = TRUE;
415 
416  /* we keep 40 bytes since all our pingpong protocols are only
417  interested in the first piece */
418  clipamount = 40;
419  }
420  else if(pp->nread_resp > (size_t)data->set.buffer_size/2) {
421  /* We got a large chunk of data and there's potentially still
422  trailing data to take care of, so we put any such part in the
423  "cache", clear the buffer to make space and restart. */
424  clipamount = perline;
425  restart = TRUE;
426  }
427  }
428  else if(i == gotbytes)
429  restart = TRUE;
430 
431  if(clipamount) {
432  pp->cache_size = clipamount;
433  pp->cache = malloc(pp->cache_size);
434  if(pp->cache)
435  memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
436  else
437  return CURLE_OUT_OF_MEMORY;
438  }
439  if(restart) {
440  /* now reset a few variables to start over nicely from the start of
441  the big buffer */
442  pp->nread_resp = 0; /* start over from scratch in the buffer */
443  ptr = pp->linestart_resp = buf;
444  perline = 0;
445  }
446 
447  } /* there was data */
448 
449  } /* while there's buffer left and loop is requested */
450 
451  pp->pending_resp = FALSE;
452 
453  return result;
454 }
455 
456 int Curl_pp_getsock(struct pingpong *pp,
457  curl_socket_t *socks,
458  int numsocks)
459 {
460  struct connectdata *conn = pp->conn;
461 
462  if(!numsocks)
463  return GETSOCK_BLANK;
464 
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 
477 {
478  /* we have a piece of a command still left to send */
479  struct connectdata *conn = pp->conn;
480  ssize_t written;
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  free(pp->sendthis);
493  pp->sendthis = NULL;
494  pp->sendleft = pp->sendsize = 0;
495  pp->response = Curl_tvnow();
496  }
497  return CURLE_OK;
498 }
499 
501 {
502  free(pp->cache);
503  pp->cache = NULL;
504  return CURLE_OK;
505 }
506 
507 bool Curl_pp_moredata(struct pingpong *pp)
508 {
509  return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
510  TRUE : FALSE;
511 }
512 
513 #endif
#define free(ptr)
Definition: curl_memory.h:130
long timeout
Definition: urldata.h:1547
int Curl_debug(struct Curl_easy *data, curl_infotype type, char *ptr, size_t size, struct connectdata *conn)
Definition: sendf.c:819
bool pending_resp
Definition: pingpong.h:54
struct curltime now
Definition: urldata.h:874
size_t nread_resp
Definition: pingpong.h:51
int numsocks
Definition: urldata.h:1751
size_t sendleft
Definition: pingpong.h:59
struct UserDefined set
Definition: urldata.h:1762
long response_time
Definition: pingpong.h:63
#define FIRSTSOCKET
Definition: urldata.h:487
CURLcode Curl_pp_disconnect(struct pingpong *pp)
Definition: pingpong.c:500
long server_response_timeout
Definition: urldata.h:1550
#define CURL_SOCKET_BAD
Definition: curl.h:131
#define failf
Definition: sendf.h:48
XmlRpcServer s
#define DEBUGASSERT(x)
CURLcode Curl_pp_vsendf(struct pingpong *pp, const char *fmt, va_list args)
Definition: pingpong.c:161
UNITTEST_START char * ptr
Definition: unit1330.c:38
CURLcode
Definition: curl.h:454
#define GETSOCK_READSOCK(x)
Definition: multiif.h:48
CURLcode Curl_pp_flushsend(struct pingpong *pp)
Definition: pingpong.c:476
#define malloc(size)
Definition: curl_memory.h:124
char * sendthis
Definition: pingpong.h:57
UNITTEST_START int result
Definition: unit1304.c:49
unsigned int i
Definition: unit1303.c:79
#define CURLMIN(x, y)
Definition: urldata.h:153
long headerbytecount
Definition: urldata.h:529
CURLcode(* statemach_act)(struct connectdata *conn)
Definition: pingpong.h:72
int Curl_pgrsUpdate(struct connectdata *conn)
Definition: progress.c:350
memcpy(filename, filename1, strlen(filename1))
#define Curl_ssl_data_pending(x, y)
Definition: vtls.h:264
#define FALSE
char * buffer
Definition: urldata.h:1253
struct SingleRequest req
Definition: urldata.h:1761
bool(* endofresp)(struct connectdata *conn, char *ptr, size_t len, int *code)
Definition: pingpong.h:74
CURLcode Curl_pp_statemach(struct pingpong *pp, bool block)
Definition: pingpong.c:81
UNITTEST_START int rc
Definition: unit1301.c:31
size_t sendsize
Definition: pingpong.h:60
#define Curl_convert_from_network(a, b, c)
Definition: non-ascii.h:57
#define Curl_convert_to_network(a, b, c)
Definition: non-ascii.h:56
#define GETSOCK_BLANK
Definition: multiif.h:42
#define Curl_tvnow()
Definition: timeval.h:57
#define CLIENTWRITE_HEADER
Definition: sendf.h:51
CURLcode Curl_write(struct connectdata *conn, curl_socket_t sockfd, const void *mem, size_t len, ssize_t *written)
Definition: sendf.c:318
#define GETSOCK_WRITESOCK(x)
Definition: multiif.h:45
Definition: curl.h:455
CURLcode Curl_speedcheck(struct Curl_easy *data, struct curltime now)
Definition: speedcheck.c:39
CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, size_t len)
Definition: sendf.c:624
size_t cache_size
Definition: pingpong.h:50
int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks, int numsocks)
Definition: pingpong.c:456
bool Curl_pp_moredata(struct pingpong *pp)
Definition: pingpong.c:507
struct UrlState state
Definition: urldata.h:1769
#define aprintf
Definition: curl_printf.h:46
int Curl_socket_check(curl_socket_t readfd0, curl_socket_t readfd1, curl_socket_t writefd, time_t timeout_ms)
Definition: select.c:145
#define ssize_t
Definition: config-win32.h:382
char * cache
Definition: pingpong.h:49
curl_socket_t sock[2]
Definition: urldata.h:876
char * linestart_resp
Definition: pingpong.h:52
void Curl_pp_init(struct pingpong *pp)
Definition: pingpong.c:140
char buf[3]
Definition: unit1398.c:32
struct connectdata * conn
Definition: pingpong.h:66
bool verbose
Definition: urldata.h:1635
#define infof
Definition: sendf.h:44
CURLcode Curl_pp_sendf(struct pingpong *pp, const char *fmt,...)
Definition: pingpong.c:248
#define Curl_tvdiff(x, y)
Definition: timeval.h:58
CURLcode Curl_pp_readresp(curl_socket_t sockfd, struct pingpong *pp, int *code, size_t *size)
Definition: pingpong.c:267
size_t size
Definition: unit1302.c:52
#define TRUE
#define vaprintf
Definition: curl_printf.h:47
int curl_socket_t
Definition: curl.h:130
long buffer_size
Definition: urldata.h:1591
struct curltime response
Definition: pingpong.h:61
Definition: debug.c:29
time_t Curl_pp_state_timeout(struct pingpong *pp)
Definition: pingpong.c:47
CURLcode Curl_read(struct connectdata *conn, curl_socket_t sockfd, char *buf, size_t sizerequested, ssize_t *n)
Definition: sendf.c:686
#define DEBUGF(x)
struct Curl_easy * data
Definition: urldata.h:791


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:16