Coverage Report

Created: 2024-02-25 06:14

/src/PROJ/curl/lib/pingpong.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) 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.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
 * SPDX-License-Identifier: curl
22
 *
23
 *   'pingpong' is for generic back-and-forth support functions used by FTP,
24
 *   IMAP, POP3, SMTP and whatever more that likes them.
25
 *
26
 ***************************************************************************/
27
28
#include "curl_setup.h"
29
30
#include "urldata.h"
31
#include "cfilters.h"
32
#include "sendf.h"
33
#include "select.h"
34
#include "progress.h"
35
#include "speedcheck.h"
36
#include "pingpong.h"
37
#include "multiif.h"
38
#include "vtls/vtls.h"
39
#include "strdup.h"
40
41
/* The last 3 #include files should be in this order */
42
#include "curl_printf.h"
43
#include "curl_memory.h"
44
#include "memdebug.h"
45
46
#ifdef USE_PINGPONG
47
48
/* Returns timeout in ms. 0 or negative number means the timeout has already
49
   triggered */
50
timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
51
                                 struct pingpong *pp, bool disconnecting)
52
0
{
53
0
  struct connectdata *conn = data->conn;
54
0
  timediff_t timeout_ms; /* in milliseconds */
55
0
  timediff_t response_time = (data->set.server_response_timeout)?
56
0
    data->set.server_response_timeout: pp->response_time;
57
58
  /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
59
     remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
60
     supposed to govern the response for any given server response, not for
61
     the time from connect to the given server response. */
62
63
  /* Without a requested timeout, we only wait 'response_time' seconds for the
64
     full response to arrive before we bail out */
65
0
  timeout_ms = response_time -
66
0
    Curl_timediff(Curl_now(), pp->response); /* spent time */
67
68
0
  if(data->set.timeout && !disconnecting) {
69
    /* if timeout is requested, find out how much remaining time we have */
70
0
    timediff_t timeout2_ms = data->set.timeout - /* timeout time */
71
0
      Curl_timediff(Curl_now(), conn->now); /* spent time */
72
73
    /* pick the lowest number */
74
0
    timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
75
0
  }
76
77
0
  return timeout_ms;
78
0
}
79
80
/*
81
 * Curl_pp_statemach()
82
 */
83
CURLcode Curl_pp_statemach(struct Curl_easy *data,
84
                           struct pingpong *pp, bool block,
85
                           bool disconnecting)
86
0
{
87
0
  struct connectdata *conn = data->conn;
88
0
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
89
0
  int rc;
90
0
  timediff_t interval_ms;
91
0
  timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting);
92
0
  CURLcode result = CURLE_OK;
93
94
0
  if(timeout_ms <= 0) {
95
0
    failf(data, "server response timeout");
96
0
    return CURLE_OPERATION_TIMEDOUT; /* already too little time */
97
0
  }
98
99
0
  if(block) {
100
0
    interval_ms = 1000;  /* use 1 second timeout intervals */
101
0
    if(timeout_ms < interval_ms)
102
0
      interval_ms = timeout_ms;
103
0
  }
104
0
  else
105
0
    interval_ms = 0; /* immediate */
106
107
0
  if(Curl_conn_data_pending(data, FIRSTSOCKET))
108
0
    rc = 1;
109
0
  else if(pp->overflow)
110
    /* We are receiving and there is data in the cache so just read it */
111
0
    rc = 1;
112
0
  else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET))
113
    /* We are receiving and there is data ready in the SSL library */
114
0
    rc = 1;
115
0
  else
116
0
    rc = Curl_socket_check(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
117
0
                           CURL_SOCKET_BAD,
118
0
                           pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
119
0
                           interval_ms);
120
121
0
  if(block) {
122
    /* if we didn't wait, we don't have to spend time on this now */
123
0
    if(Curl_pgrsUpdate(data))
124
0
      result = CURLE_ABORTED_BY_CALLBACK;
125
0
    else
126
0
      result = Curl_speedcheck(data, Curl_now());
127
128
0
    if(result)
129
0
      return result;
130
0
  }
131
132
0
  if(rc == -1) {
133
0
    failf(data, "select/poll error");
134
0
    result = CURLE_OUT_OF_MEMORY;
135
0
  }
136
0
  else if(rc)
137
0
    result = pp->statemachine(data, data->conn);
138
139
0
  return result;
140
0
}
141
142
/* initialize stuff to prepare for reading a fresh new response */
143
void Curl_pp_init(struct pingpong *pp)
144
0
{
145
0
  pp->nread_resp = 0;
146
0
  pp->response = Curl_now(); /* start response time-out now! */
147
0
  pp->pending_resp = TRUE;
148
0
  Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
149
0
  Curl_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
150
0
}
151
152
/***********************************************************************
153
 *
154
 * Curl_pp_vsendf()
155
 *
156
 * Send the formatted string as a command to a pingpong server. Note that
157
 * the string should not have any CRLF appended, as this function will
158
 * append the necessary things itself.
159
 *
160
 * made to never block
161
 */
162
CURLcode Curl_pp_vsendf(struct Curl_easy *data,
163
                        struct pingpong *pp,
164
                        const char *fmt,
165
                        va_list args)
166
0
{
167
0
  ssize_t bytes_written = 0;
168
0
  size_t write_len;
169
0
  char *s;
170
0
  CURLcode result;
171
0
  struct connectdata *conn = data->conn;
172
173
#ifdef HAVE_GSSAPI
174
  enum protection_level data_sec;
175
#endif
176
177
0
  DEBUGASSERT(pp->sendleft == 0);
178
0
  DEBUGASSERT(pp->sendsize == 0);
179
0
  DEBUGASSERT(pp->sendthis == NULL);
180
181
0
  if(!conn)
182
    /* can't send without a connection! */
183
0
    return CURLE_SEND_ERROR;
184
185
0
  Curl_dyn_reset(&pp->sendbuf);
186
0
  result = Curl_dyn_vaddf(&pp->sendbuf, fmt, args);
187
0
  if(result)
188
0
    return result;
189
190
  /* append CRLF */
191
0
  result = Curl_dyn_addn(&pp->sendbuf, "\r\n", 2);
192
0
  if(result)
193
0
    return result;
194
195
0
  pp->pending_resp = TRUE;
196
0
  write_len = Curl_dyn_len(&pp->sendbuf);
197
0
  s = Curl_dyn_ptr(&pp->sendbuf);
198
199
#ifdef HAVE_GSSAPI
200
  conn->data_prot = PROT_CMD;
201
#endif
202
0
  result = Curl_nwrite(data, FIRSTSOCKET, s, write_len, &bytes_written);
203
0
  if(result)
204
0
    return result;
205
#ifdef HAVE_GSSAPI
206
  data_sec = conn->data_prot;
207
  DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
208
  conn->data_prot = (unsigned char)data_sec;
209
#endif
210
211
0
  Curl_debug(data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written);
212
213
0
  if(bytes_written != (ssize_t)write_len) {
214
    /* the whole chunk was not sent, keep it around and adjust sizes */
215
0
    pp->sendthis = s;
216
0
    pp->sendsize = write_len;
217
0
    pp->sendleft = write_len - bytes_written;
218
0
  }
219
0
  else {
220
0
    pp->sendthis = NULL;
221
0
    pp->sendleft = pp->sendsize = 0;
222
0
    pp->response = Curl_now();
223
0
  }
224
225
0
  return CURLE_OK;
226
0
}
227
228
229
/***********************************************************************
230
 *
231
 * Curl_pp_sendf()
232
 *
233
 * Send the formatted string as a command to a pingpong server. Note that
234
 * the string should not have any CRLF appended, as this function will
235
 * append the necessary things itself.
236
 *
237
 * made to never block
238
 */
239
CURLcode Curl_pp_sendf(struct Curl_easy *data, struct pingpong *pp,
240
                       const char *fmt, ...)
241
0
{
242
0
  CURLcode result;
243
0
  va_list ap;
244
0
  va_start(ap, fmt);
245
246
0
  result = Curl_pp_vsendf(data, pp, fmt, ap);
247
248
0
  va_end(ap);
249
250
0
  return result;
251
0
}
252
253
static CURLcode pingpong_read(struct Curl_easy *data,
254
                              curl_socket_t sockfd,
255
                              char *buffer,
256
                              size_t buflen,
257
                              ssize_t *nread)
258
0
{
259
0
  CURLcode result;
260
#ifdef HAVE_GSSAPI
261
  enum protection_level prot = data->conn->data_prot;
262
  data->conn->data_prot = PROT_CLEAR;
263
#endif
264
0
  result = Curl_read(data, sockfd, buffer, buflen, nread);
265
#ifdef HAVE_GSSAPI
266
  DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
267
  data->conn->data_prot = (unsigned char)prot;
268
#endif
269
0
  return result;
270
0
}
271
272
/*
273
 * Curl_pp_readresp()
274
 *
275
 * Reads a piece of a server response.
276
 */
277
CURLcode Curl_pp_readresp(struct Curl_easy *data,
278
                          curl_socket_t sockfd,
279
                          struct pingpong *pp,
280
                          int *code, /* return the server code if done */
281
                          size_t *size) /* size of the response */
282
0
{
283
0
  struct connectdata *conn = data->conn;
284
0
  CURLcode result = CURLE_OK;
285
286
0
  *code = 0; /* 0 for errors or not done */
287
0
  *size = 0;
288
289
0
  if(pp->nfinal) {
290
    /* a previous call left this many bytes in the beginning of the buffer as
291
       that was the final line; now ditch that */
292
0
    size_t full = Curl_dyn_len(&pp->recvbuf);
293
294
    /* trim off the "final" leading part */
295
0
    Curl_dyn_tail(&pp->recvbuf, full -  pp->nfinal);
296
297
0
    pp->nfinal = 0; /* now gone */
298
0
  }
299
0
  if(!pp->overflow) {
300
0
    ssize_t gotbytes = 0;
301
0
    char buffer[900];
302
303
0
    result = pingpong_read(data, sockfd, buffer, sizeof(buffer), &gotbytes);
304
0
    if(result == CURLE_AGAIN)
305
0
      return CURLE_OK;
306
307
0
    if(result)
308
0
      return result;
309
310
0
    if(gotbytes <= 0) {
311
0
      failf(data, "response reading failed (errno: %d)", SOCKERRNO);
312
0
      return CURLE_RECV_ERROR;
313
0
    }
314
315
0
    result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes);
316
0
    if(result)
317
0
      return result;
318
319
0
    data->req.headerbytecount += (unsigned int)gotbytes;
320
321
0
    pp->nread_resp += gotbytes;
322
0
  }
323
324
0
  do {
325
0
    char *line = Curl_dyn_ptr(&pp->recvbuf);
326
0
    char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf));
327
0
    if(nl) {
328
      /* a newline is CRLF in pp-talk, so the CR is ignored as
329
         the line isn't really terminated until the LF comes */
330
0
      size_t length = nl - line + 1;
331
332
      /* output debug output if that is requested */
333
#ifdef HAVE_GSSAPI
334
      if(!conn->sec_complete)
335
#endif
336
0
        Curl_debug(data, CURLINFO_HEADER_IN, line, length);
337
338
      /*
339
       * Pass all response-lines to the callback function registered for
340
       * "headers". The response lines can be seen as a kind of headers.
341
       */
342
0
      result = Curl_client_write(data, CLIENTWRITE_INFO, line, length);
343
0
      if(result)
344
0
        return result;
345
346
0
      if(pp->endofresp(data, conn, line, length, code)) {
347
        /* When at "end of response", keep the endofresp line first in the
348
           buffer since it will be accessed outside (by pingpong
349
           parsers). Store the overflow counter to inform about additional
350
           data in this buffer after the endofresp line. */
351
0
        pp->nfinal = length;
352
0
        if(Curl_dyn_len(&pp->recvbuf) > length)
353
0
          pp->overflow = Curl_dyn_len(&pp->recvbuf) - length;
354
0
        else
355
0
          pp->overflow = 0;
356
0
        *size = pp->nread_resp; /* size of the response */
357
0
        pp->nread_resp = 0; /* restart */
358
0
        break;
359
0
      }
360
0
      if(Curl_dyn_len(&pp->recvbuf) > length)
361
        /* keep the remaining piece */
362
0
        Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length);
363
0
      else
364
0
        Curl_dyn_reset(&pp->recvbuf);
365
0
    }
366
0
    else {
367
      /* without a newline, there is no overflow */
368
0
      pp->overflow = 0;
369
0
      break;
370
0
    }
371
372
0
  } while(1); /* while there's buffer left to scan */
373
374
0
  pp->pending_resp = FALSE;
375
376
0
  return result;
377
0
}
378
379
int Curl_pp_getsock(struct Curl_easy *data,
380
                    struct pingpong *pp, curl_socket_t *socks)
381
0
{
382
0
  struct connectdata *conn = data->conn;
383
0
  socks[0] = conn->sock[FIRSTSOCKET];
384
385
0
  if(pp->sendleft) {
386
    /* write mode */
387
0
    return GETSOCK_WRITESOCK(0);
388
0
  }
389
390
  /* read mode */
391
0
  return GETSOCK_READSOCK(0);
392
0
}
393
394
CURLcode Curl_pp_flushsend(struct Curl_easy *data,
395
                           struct pingpong *pp)
396
0
{
397
  /* we have a piece of a command still left to send */
398
0
  ssize_t written;
399
0
  CURLcode result = Curl_nwrite(data, FIRSTSOCKET,
400
0
                                pp->sendthis + pp->sendsize - pp->sendleft,
401
0
                                pp->sendleft, &written);
402
0
  if(result)
403
0
    return result;
404
405
0
  if(written != (ssize_t)pp->sendleft) {
406
    /* only a fraction was sent */
407
0
    pp->sendleft -= written;
408
0
  }
409
0
  else {
410
0
    pp->sendthis = NULL;
411
0
    pp->sendleft = pp->sendsize = 0;
412
0
    pp->response = Curl_now();
413
0
  }
414
0
  return CURLE_OK;
415
0
}
416
417
CURLcode Curl_pp_disconnect(struct pingpong *pp)
418
0
{
419
0
  Curl_dyn_free(&pp->sendbuf);
420
0
  Curl_dyn_free(&pp->recvbuf);
421
0
  return CURLE_OK;
422
0
}
423
424
bool Curl_pp_moredata(struct pingpong *pp)
425
0
{
426
0
  return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf));
427
0
}
428
429
#endif