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 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#include "urldata.h"
28#include "strerror.h"
29#include "cfilters.h"
30#include "connect.h"
31#include "url.h" /* for Curl_safefree() */
32#include "sendf.h"
33#include "sockaddr.h" /* required for Curl_sockaddr_storage */
34#include "multiif.h"
35#include "progress.h"
36#include "warnless.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#ifndef ARRAYSIZE
44#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
45#endif
46
47#ifdef DEBUGBUILD
48/* used by unit2600.c */
49void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
50{
51 cf->connected = FALSE;
52 if(cf->next)
53 cf->next->cft->do_close(cf->next, data);
54}
55#endif
56
57static void conn_report_connect_stats(struct Curl_easy *data,
58 struct connectdata *conn);
59
60void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
61 const char **phost, const char **pdisplay_host,
62 int *pport)
63{
64 if(cf->next)
65 cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
66 else {
67 *phost = cf->conn->host.name;
68 *pdisplay_host = cf->conn->host.dispname;
69 *pport = cf->conn->port;
70 }
71}
72
73int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
74 struct Curl_easy *data,
75 curl_socket_t *socks)
76{
77 return cf->next?
78 cf->next->cft->get_select_socks(cf->next, data, socks) : 0;
79}
80
81bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
82 const struct Curl_easy *data)
83{
84 return cf->next?
85 cf->next->cft->has_data_pending(cf->next, data) : FALSE;
86}
87
88ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
89 const void *buf, size_t len, CURLcode *err)
90{
91 return cf->next?
92 cf->next->cft->do_send(cf->next, data, buf, len, err) :
93 CURLE_RECV_ERROR;
94}
95
96ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
97 char *buf, size_t len, CURLcode *err)
98{
99 return cf->next?
100 cf->next->cft->do_recv(cf->next, data, buf, len, err) :
101 CURLE_SEND_ERROR;
102}
103
104bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
105 struct Curl_easy *data,
106 bool *input_pending)
107{
108 return cf->next?
109 cf->next->cft->is_alive(cf->next, data, input_pending) :
110 FALSE; /* pessimistic in absence of data */
111}
112
113CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
114 struct Curl_easy *data)
115{
116 return cf->next?
117 cf->next->cft->keep_alive(cf->next, data) :
118 CURLE_OK;
119}
120
121CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
122 struct Curl_easy *data,
123 int query, int *pres1, void *pres2)
124{
125 return cf->next?
126 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
127 CURLE_UNKNOWN_OPTION;
128}
129
130void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
131 struct Curl_easy *data)
132{
133 struct Curl_cfilter *cfn, *cf = *pcf;
134
135 if(cf) {
136 *pcf = NULL;
137 while(cf) {
138 cfn = cf->next;
139 /* prevent destroying filter to mess with its sub-chain, since
140 * we have the reference now and will call destroy on it.
141 */
142 cf->next = NULL;
143 cf->cft->destroy(cf, data);
144 free(cf);
145 cf = cfn;
146 }
147 }
148}
149
150void Curl_conn_cf_discard_all(struct Curl_easy *data,
151 struct connectdata *conn, int index)
152{
153 Curl_conn_cf_discard_chain(pcf: &conn->cfilter[index], data);
154}
155
156void Curl_conn_close(struct Curl_easy *data, int index)
157{
158 struct Curl_cfilter *cf;
159
160 DEBUGASSERT(data->conn);
161 /* it is valid to call that without filters being present */
162 cf = data->conn->cfilter[index];
163 if(cf) {
164 cf->cft->do_close(cf, data);
165 }
166}
167
168ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
169 size_t len, CURLcode *code)
170{
171 struct Curl_cfilter *cf;
172
173 DEBUGASSERT(data);
174 DEBUGASSERT(data->conn);
175 cf = data->conn->cfilter[num];
176 while(cf && !cf->connected) {
177 cf = cf->next;
178 }
179 if(cf) {
180 return cf->cft->do_recv(cf, data, buf, len, code);
181 }
182 failf(data, fmt: "recv: no filter connected");
183 *code = CURLE_FAILED_INIT;
184 return -1;
185}
186
187ssize_t Curl_conn_send(struct Curl_easy *data, int num,
188 const void *mem, size_t len, CURLcode *code)
189{
190 struct Curl_cfilter *cf;
191
192 DEBUGASSERT(data);
193 DEBUGASSERT(data->conn);
194 cf = data->conn->cfilter[num];
195 while(cf && !cf->connected) {
196 cf = cf->next;
197 }
198 if(cf) {
199 return cf->cft->do_send(cf, data, mem, len, code);
200 }
201 failf(data, fmt: "send: no filter connected");
202 DEBUGASSERT(0);
203 *code = CURLE_FAILED_INIT;
204 return -1;
205}
206
207CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
208 const struct Curl_cftype *cft,
209 void *ctx)
210{
211 struct Curl_cfilter *cf;
212 CURLcode result = CURLE_OUT_OF_MEMORY;
213
214 DEBUGASSERT(cft);
215 cf = calloc(sizeof(*cf), 1);
216 if(!cf)
217 goto out;
218
219 cf->cft = cft;
220 cf->ctx = ctx;
221 result = CURLE_OK;
222out:
223 *pcf = cf;
224 return result;
225}
226
227void Curl_conn_cf_add(struct Curl_easy *data,
228 struct connectdata *conn,
229 int index,
230 struct Curl_cfilter *cf)
231{
232 (void)data;
233 DEBUGASSERT(conn);
234 DEBUGASSERT(!cf->conn);
235 DEBUGASSERT(!cf->next);
236
237 cf->next = conn->cfilter[index];
238 cf->conn = conn;
239 cf->sockindex = index;
240 conn->cfilter[index] = cf;
241 CURL_TRC_CF(data, cf, "added");
242}
243
244void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
245 struct Curl_cfilter *cf_new)
246{
247 struct Curl_cfilter *tail, **pnext;
248
249 DEBUGASSERT(cf_at);
250 DEBUGASSERT(cf_new);
251 DEBUGASSERT(!cf_new->conn);
252
253 tail = cf_at->next;
254 cf_at->next = cf_new;
255 do {
256 cf_new->conn = cf_at->conn;
257 cf_new->sockindex = cf_at->sockindex;
258 pnext = &cf_new->next;
259 cf_new = cf_new->next;
260 } while(cf_new);
261 *pnext = tail;
262}
263
264bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf,
265 struct Curl_cfilter *discard,
266 struct Curl_easy *data,
267 bool destroy_always)
268{
269 struct Curl_cfilter **pprev = &cf->next;
270 bool found = FALSE;
271
272 /* remove from sub-chain and destroy */
273 DEBUGASSERT(cf);
274 while(*pprev) {
275 if(*pprev == cf) {
276 *pprev = discard->next;
277 discard->next = NULL;
278 found = TRUE;
279 break;
280 }
281 pprev = &((*pprev)->next);
282 }
283 if(found || destroy_always) {
284 discard->next = NULL;
285 discard->cft->destroy(discard, data);
286 free(discard);
287 }
288 return found;
289}
290
291CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
292 struct Curl_easy *data,
293 bool blocking, bool *done)
294{
295 if(cf)
296 return cf->cft->do_connect(cf, data, blocking, done);
297 return CURLE_FAILED_INIT;
298}
299
300void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
301{
302 if(cf)
303 cf->cft->do_close(cf, data);
304}
305
306int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
307 struct Curl_easy *data,
308 curl_socket_t *socks)
309{
310 if(cf)
311 return cf->cft->get_select_socks(cf, data, socks);
312 return 0;
313}
314
315ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
316 const void *buf, size_t len, CURLcode *err)
317{
318 if(cf)
319 return cf->cft->do_send(cf, data, buf, len, err);
320 *err = CURLE_SEND_ERROR;
321 return -1;
322}
323
324ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
325 char *buf, size_t len, CURLcode *err)
326{
327 if(cf)
328 return cf->cft->do_recv(cf, data, buf, len, err);
329 *err = CURLE_RECV_ERROR;
330 return -1;
331}
332
333CURLcode Curl_conn_connect(struct Curl_easy *data,
334 int sockindex,
335 bool blocking,
336 bool *done)
337{
338 struct Curl_cfilter *cf;
339 CURLcode result = CURLE_OK;
340
341 DEBUGASSERT(data);
342 DEBUGASSERT(data->conn);
343
344 cf = data->conn->cfilter[sockindex];
345 DEBUGASSERT(cf);
346 if(!cf)
347 return CURLE_FAILED_INIT;
348
349 *done = cf->connected;
350 if(!*done) {
351 result = cf->cft->do_connect(cf, data, blocking, done);
352 if(!result && *done) {
353 Curl_conn_ev_update_info(data, conn: data->conn);
354 conn_report_connect_stats(data, conn: data->conn);
355 data->conn->keepalive = Curl_now();
356 }
357 else if(result) {
358 conn_report_connect_stats(data, conn: data->conn);
359 }
360 }
361
362 return result;
363}
364
365bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
366{
367 struct Curl_cfilter *cf;
368
369 cf = conn->cfilter[sockindex];
370 return cf && cf->connected;
371}
372
373bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
374{
375 struct Curl_cfilter *cf;
376
377 cf = data->conn->cfilter[sockindex];
378 while(cf) {
379 if(cf->connected)
380 return TRUE;
381 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
382 return FALSE;
383 cf = cf->next;
384 }
385 return FALSE;
386}
387
388bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
389{
390 for(; cf; cf = cf->next) {
391 if(cf->cft->flags & CF_TYPE_SSL)
392 return TRUE;
393 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
394 return FALSE;
395 }
396 return FALSE;
397}
398
399bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
400{
401 return conn? Curl_conn_cf_is_ssl(cf: conn->cfilter[sockindex]) : FALSE;
402}
403
404bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
405{
406 struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
407
408 for(; cf; cf = cf->next) {
409 if(cf->cft->flags & CF_TYPE_MULTIPLEX)
410 return TRUE;
411 if(cf->cft->flags & CF_TYPE_IP_CONNECT
412 || cf->cft->flags & CF_TYPE_SSL)
413 return FALSE;
414 }
415 return FALSE;
416}
417
418bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
419{
420 struct Curl_cfilter *cf;
421
422 (void)data;
423 DEBUGASSERT(data);
424 DEBUGASSERT(data->conn);
425
426 cf = data->conn->cfilter[sockindex];
427 while(cf && !cf->connected) {
428 cf = cf->next;
429 }
430 if(cf) {
431 return cf->cft->has_data_pending(cf, data);
432 }
433 return FALSE;
434}
435
436int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
437 curl_socket_t *socks)
438{
439 struct Curl_cfilter *cf;
440
441 DEBUGASSERT(data);
442 DEBUGASSERT(data->conn);
443 cf = data->conn->cfilter[sockindex];
444
445 /* if the next one is not yet connected, that's the one we want */
446 while(cf && cf->next && !cf->next->connected)
447 cf = cf->next;
448 if(cf) {
449 return cf->cft->get_select_socks(cf, data, socks);
450 }
451 return GETSOCK_BLANK;
452}
453
454void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
455 const char **phost, const char **pdisplay_host,
456 int *pport)
457{
458 struct Curl_cfilter *cf;
459
460 DEBUGASSERT(data->conn);
461 cf = data->conn->cfilter[sockindex];
462 if(cf) {
463 cf->cft->get_host(cf, data, phost, pdisplay_host, pport);
464 }
465 else {
466 /* Some filter ask during shutdown for this, mainly for debugging
467 * purposes. We hand out the defaults, however this is not always
468 * accurate, as the connection might be tunneled, etc. But all that
469 * state is already gone here. */
470 *phost = data->conn->host.name;
471 *pdisplay_host = data->conn->host.dispname;
472 *pport = data->conn->remote_port;
473 }
474}
475
476CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
477 struct Curl_easy *data,
478 int event, int arg1, void *arg2)
479{
480 (void)cf;
481 (void)data;
482 (void)event;
483 (void)arg1;
484 (void)arg2;
485 return CURLE_OK;
486}
487
488CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
489 struct Curl_easy *data,
490 bool ignore_result,
491 int event, int arg1, void *arg2)
492{
493 CURLcode result = CURLE_OK;
494
495 for(; cf; cf = cf->next) {
496 if(Curl_cf_def_cntrl == cf->cft->cntrl)
497 continue;
498 result = cf->cft->cntrl(cf, data, event, arg1, arg2);
499 if(!ignore_result && result)
500 break;
501 }
502 return result;
503}
504
505curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
506 struct Curl_easy *data)
507{
508 curl_socket_t sock;
509 if(cf && !cf->cft->query(cf, data, CF_QUERY_SOCKET, NULL, &sock))
510 return sock;
511 return CURL_SOCKET_BAD;
512}
513
514curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
515{
516 struct Curl_cfilter *cf;
517
518 cf = data->conn? data->conn->cfilter[sockindex] : NULL;
519 /* if the top filter has not connected, ask it (and its sub-filters)
520 * for the socket. Otherwise conn->sock[sockindex] should have it.
521 */
522 if(cf && !cf->connected)
523 return Curl_conn_cf_get_socket(cf, data);
524 return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
525}
526
527static CURLcode cf_cntrl_all(struct connectdata *conn,
528 struct Curl_easy *data,
529 bool ignore_result,
530 int event, int arg1, void *arg2)
531{
532 CURLcode result = CURLE_OK;
533 size_t i;
534
535 for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
536 result = Curl_conn_cf_cntrl(cf: conn->cfilter[i], data, ignore_result,
537 event, arg1, arg2);
538 if(!ignore_result && result)
539 break;
540 }
541 return result;
542}
543
544void Curl_conn_ev_data_attach(struct connectdata *conn,
545 struct Curl_easy *data)
546{
547 cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, arg1: 0, NULL);
548}
549
550void Curl_conn_ev_data_detach(struct connectdata *conn,
551 struct Curl_easy *data)
552{
553 cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, arg1: 0, NULL);
554}
555
556CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
557{
558 return cf_cntrl_all(conn: data->conn, data, FALSE,
559 CF_CTRL_DATA_SETUP, arg1: 0, NULL);
560}
561
562CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
563{
564 return cf_cntrl_all(conn: data->conn, data, FALSE,
565 CF_CTRL_DATA_IDLE, arg1: 0, NULL);
566}
567
568/**
569 * Notify connection filters that the transfer represented by `data`
570 * is donw with sending data (e.g. has uploaded everything).
571 */
572void Curl_conn_ev_data_done_send(struct Curl_easy *data)
573{
574 cf_cntrl_all(conn: data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, arg1: 0, NULL);
575}
576
577/**
578 * Notify connection filters that the transfer represented by `data`
579 * is finished - eventually premature, e.g. before being complete.
580 */
581void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature)
582{
583 cf_cntrl_all(conn: data->conn, data, TRUE, CF_CTRL_DATA_DONE, arg1: premature, NULL);
584}
585
586CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
587{
588 return cf_cntrl_all(conn: data->conn, data, FALSE,
589 CF_CTRL_DATA_PAUSE, arg1: do_pause, NULL);
590}
591
592void Curl_conn_ev_update_info(struct Curl_easy *data,
593 struct connectdata *conn)
594{
595 cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, arg1: 0, NULL);
596}
597
598/**
599 * Update connection statistics
600 */
601static void conn_report_connect_stats(struct Curl_easy *data,
602 struct connectdata *conn)
603{
604 struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
605 if(cf) {
606 struct curltime connected;
607 struct curltime appconnected;
608
609 memset(s: &connected, c: 0, n: sizeof(connected));
610 cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
611 if(connected.tv_sec || connected.tv_usec)
612 Curl_pgrsTimeWas(data, timer: TIMER_CONNECT, timestamp: connected);
613
614 memset(s: &appconnected, c: 0, n: sizeof(appconnected));
615 cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
616 if(appconnected.tv_sec || appconnected.tv_usec)
617 Curl_pgrsTimeWas(data, timer: TIMER_APPCONNECT, timestamp: appconnected);
618 }
619}
620
621bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
622 bool *input_pending)
623{
624 struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
625 return cf && !cf->conn->bits.close &&
626 cf->cft->is_alive(cf, data, input_pending);
627}
628
629CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
630 struct connectdata *conn,
631 int sockindex)
632{
633 struct Curl_cfilter *cf = conn->cfilter[sockindex];
634 return cf? cf->cft->keep_alive(cf, data) : CURLE_OK;
635}
636
637size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
638 struct connectdata *conn,
639 int sockindex)
640{
641 CURLcode result;
642 int n = 0;
643
644 struct Curl_cfilter *cf = conn->cfilter[sockindex];
645 result = cf? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT,
646 &n, NULL) : CURLE_UNKNOWN_OPTION;
647 return (result || n <= 0)? 1 : (size_t)n;
648}
649