✓ProveIt
Examples / Report #4099618 Saved analysis

CVE-2026-107929: local interface connection reuse mixup

curl · 2026-10-0917.1s analysis · $0.029 in model calls

What the report claims

CURLOPT_INTERFACE with documented prefixes is not part of the connection reuse key, causing later transfers to reuse connections bound to a different source address.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-107929 local interface connection reuse mixup

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Code supports the claim

The code supports the reported flaw. A maintainer should review its impact and decide what to do next.

Why this finding?
  • The code confirms that CURLOPT_INTERFACE with prefixes (if!, host!, ifhost!) is parsed into three separate strings (STRING_DEVICE, STRING_INTERFACE, STRING_BINDHOST) but only STRING_DEVICE is stored in the connection struct (conn->localdev) and compared during connection reuse.
  • bindlocal() reads all three strings when initially binding a socket, but connection reuse skips rebinding, so the second request's different source binding is silently ignored.
  • The unprefixed form works correctly because it lands entirely in STRING_DEVICE, which is already in the reuse key.
  • This allows a multi-tenant application sharing a libcurl connection cache to have one tenant's traffic egress from another tenant's source IP, bypassing source-IP ACLs.
  • The flaw exists in both the tested release (8.22.0) and current master (efd42dfb).
How this analysis ran 6 claims · 17 citations verified

Checked against curl tested commit efd42dfbbf65 (efd42dfbbf65). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
partially grounded
no model · 24 references checked out, 1 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
6 claims · 2922 tokens · $0.0003
3 · Evidence
8 code snippets
real curl source at efd42dfbbf65 · 3 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
23866 tokens · 13.4s · $0.0292
5 · Receipts
17 of 17 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the detailed report and reproduction. The code confirms the flaw: CURLOPT_INTERFACE with prefixes (if!, host!, ifhost!) is parsed into three strings but only STRING_DEVICE enters the connection reuse key (lib/url.c:1139-1142, lib/url.c:624-628). On reuse, bindlocal() is not called, so the second request's different binding is ignored. This is a valid security issue for applications using per-tenant source bindings with a shared connection cache. We will prepare a fix that includes STRING_INTERFACE and STRING_BINDHOST in the reuse key. Your patch approach of rebuilding the full spelling into localdev is sound; please open a PR when ready.

Inspect all claims and source code 6 claims · 8 code excerpts

Report claims

C1flaw supported

CURLOPT_INTERFACE with documented prefixes (`if!`, `host!`, `ifhost!`) is not included in the connection reuse key

Read the reasoning

The code confirms that CURLOPT_INTERFACE with prefixes (if!, host!, ifhost!) is parsed into three strings (STRING_DEVICE, STRING_INTERFACE, STRING_BINDHOST) but only STRING_DEVICE is included in the connection reuse key via conn->localdev. The match function compares only localdev, localport, and localportrange.

C2location supported

The reuse key is built in `allocate_conn()` and `url_match_connect_config()`

Read the reasoning

The reuse key is indeed built in allocate_conn (storing only STRING_DEVICE) and checked in url_match_connect_config (comparing only localdev).

C3trigger supported

Setting CURLOPT_INTERFACE with a prefix such as `host!127.0.0.2` and then making a second request that asks for a different source address

Read the reasoning

Setting CURLOPT_INTERFACE with a prefix triggers Curl_parse_interface which separates the input into device, interface, and host components, stored in three different string slots.

C4impact supported

The second transfer reuses the earlier connection and sends traffic from the earlier source address, ignoring the new source binding

Read the reasoning

Because the reuse key omits STRING_INTERFACE and STRING_BINDHOST, a second request with a different prefixed interface matches the existing connection. The socket is not rebound on reuse, so the original source address is used.

C5reproduction supported

Use curl with `--interface "host!127.0.0.2"` followed by `--interface "host!127.0.0.3"` against a keep-alive HTTP server and observe the local IP reported

Read the reasoning

The reproduction steps directly exercise the code path: two requests with different host! prefixes against a keep-alive server will match in url_match_connect_config because only STRING_DEVICE (empty for prefixed forms) is compared.

C6impact supported

A request configured for identity B can be sent over a socket bound to identity A, bypassing source‑IP ACLs

Read the reasoning

An application using per-tenant source bindings with a shared connection cache will have requests for tenant B reuse tenant A's connection, sending traffic from A's source IP and bypassing source-IP ACLs.

Deterministic checks that failed

  • line none of Curl_parse_interface, setstropt_interface, bindlocal, allocate_conn appear near line 1208 at efd42dfbbf65; it reads: 'Curl_hsts_applies(data->hsts, data->state.origin)) {'
Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: When CURLOPT_INTERFACE is set with one of the documented prefixes (`if!`, `host!`, `ifhost!`), that binding is not part of the connection reuse key, so a later transfer asking for a different source can silently reuse an existing connection and go out from the earlier transfer's source address. `Curl_parse_interface()` parses the option into three separate values and `setstropt_interface()` stores them as STRING_DEVICE, STRING_INTERFACE and STRING_BINDHOST (lib/cf-socket.c:588, lib/setopt.c:155-183), and `bindlocal()` uses all three (lib/cf-socket.c:661-663), but `allocate_conn()` copies only STRING_DEVICE into `conn->localdev` (lib/url.c:1208-1209) and the local-binding test in `url_match_connect_config()` compares only that field and the local ports (lib/url.c:625-628). The unprefixed form is unaffected because it lands in STRING_DEVICE and is already in the key, which is why the control below behaves correctly. Reuse never rebinds a socket, so the second transfer keeps the first connection's source address, and for the same reason a non-matching `if!` skips the CURLE_INTERFACE_FAILED that CURLOPT_INTERFACE(3) documents. I know the comment at lib/url.c:619-624 calls this matching deliberately rough, but it assumes reuse will "reuse the exact same binding parameters", and here they are explicitly different and the miss goes in the unsafe direction. ## Affected version 8.9.0 through 8.22.0, and current master. I reproduced it on released 8.22.0 and on master efd42dfbbf656870f8885815d07b0d16a5771f69. On master 424aa994ac223c0f90839aadee0fb59595344fce I only read the source: `allocate_conn()` has moved to url.c:1147 but still copies STRING_DEVICE alone, and STRING_INTERFACE and STRING_BINDHOST are still read nowhere but the bind path. The 8.9.0 lower bound comes from reading commit 3060557af702dd59155ea703a0790455c2f49a59, which split the option into three strings without adding the two new ones to the reuse key; I did not run 8.9.0. ``` curl 8.22.0 (x86_64-pc-linux-gnu) libcurl/8.22.0 OpenSSL/3.0.13 zlib/1.3 zstd/1.5.5 Release-Date: 2026-09-02 ``` Linux 6.8.0 x86_64, clang 18.1.3, built from the signed 8.22.0 release tarball. That build was configured with --enable-debug; the code above is not debug-conditional. ## Steps To Reproduce: The server has to speak HTTP/1.1 keep-alive, otherwise nothing is reusable and the bug stays hidden. Minima …

Source code

lib/cf-socket.clines 563–641 · lines 588-588 cited by the report; definition of Curl_parse_interface
563/*
564 * Curl_parse_interface()
565 *
566 * This is used to parse interface argument in the following formats.
567 * In all the examples, `host` can be an IP address or a hostname.
568 *
569 * <iface_or_host> - can be either an interface name or a host.
570 * if!<iface> - interface name.
571 * host!<host> - hostname.
572 * ifhost!<iface>!<host> - interface name and hostname.
573 *
574 * Parameters:
575 *
576 * input [in] - input string.
577 * len [in] - length of the input string.
578 * dev [in/out] - address where a pointer to newly allocated memory
579 * holding the interface-or-host will be stored upon
580 * completion.
581 * iface [in/out] - address where a pointer to newly allocated memory
582 * holding the interface will be stored upon completion.
583 * host [in/out] - address where a pointer to newly allocated memory
584 * holding the host will be stored upon completion.
585 *
586 * Returns CURLE_OK on success.
587 */
588CURLcode Curl_parse_interface(const char *input,
C3: Curl_parse_interface definition shows prefix parsing logic
589 char **dev, char **iface, char **host)
590{
591 static const char if_prefix[] = "if!";
592 static const char host_prefix[] = "host!";
593 static const char if_host_prefix[] = "ifhost!";
594 size_t len;
595
596 DEBUGASSERT(dev);
597 DEBUGASSERT(iface);
598 DEBUGASSERT(host);
599
600 len = strlen(input);
601 if(len > 512)
602 return CURLE_BAD_FUNCTION_ARGUMENT;
603
604 if(!strncmp(if_prefix, input, strlen(if_prefix))) {
605 input += strlen(if_prefix);
606 if(!*input)
607 return CURLE_BAD_FUNCTION_ARGUMENT;
608 *iface = curlx_memdup0(input, len - strlen(if_prefix));
609 return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
610 }
611 else if(!strncmp(host_prefix, input, strlen(host_prefix))) {
612 input += strlen(host_prefix);
613 if(!*input)
614 return CURLE_BAD_FUNCTION_ARGUMENT;
615 *host = curlx_memdup0(input, len - strlen(host_prefix));
616 return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
617 }
618 else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) {
619 const char *host_part;
620 input += strlen(if_host_prefix);
621 len -= strlen(if_host_prefix);
622 host_part = memchr(input, '!', len);
623 if(!host_part || !*(host_part + 1))
624 return CURLE_BAD_FUNCTION_ARGUMENT;
625 *iface = curlx_memdup0(input, host_part - input);
626 if(!*iface)
627 return CURLE_OUT_OF_MEMORY;
628 ++host_part;
629 *host = curlx_memdup0(host_part, len - (host_part - input));
630 if(!*host) {
631 curlx_safefree(*iface);
632 return CURLE_OUT_OF_MEMORY;
633 }
634 return CURLE_OK;
635 }
636
637 if(!*input)
638 return CURLE_BAD_FUNCTION_ARGUMENT;
639 *dev = curlx_memdup0(input, len);
640 return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
641}
lib/setopt.clines 130–208 · lines 155-183 cited by the report
130 DEBUGASSERT(passwdp);
131
132 /* Parse the login details if specified. If not, then we treat NULL as a
133 hint to clear the existing data */
134 if(option) {
135 size_t len = strlen(option);
136 CURLcode result;
137 if(len > CURL_MAX_INPUT_LENGTH)
138 return CURLE_BAD_FUNCTION_ARGUMENT;
139
140 result = Curl_parse_login_details(option, len, &user, &passwd, NULL);
141 if(result)
142 return result;
143 }
144
145 curlx_free(*userp);
146 *userp = user;
147
148 curlx_strzero(*passwdp);
149 curlx_free(*passwdp);
150 *passwdp = passwd;
151
152 return CURLE_OK;
153}
154
155static CURLcode setstropt_interface(struct Curl_easy *data, char *option)
156{
157 char *dev = NULL;
158 char *iface = NULL;
159 char *host = NULL;
160 CURLcode result;
161
162 if(option) {
163 /* Parse the interface details if set, otherwise clear them all */
164 result = Curl_parse_interface(option, &dev, &iface, &host);
C3: Curl_parse_interface splits the option into dev, iface, host
165 if(result)
166 return result;
167 }
168
169 result = CURL_EASY_STR_SETN(data, STRING_DEVICE, dev);
C1: Only STRING_DEVICE is stored from the parsed interface option
170 dev = NULL;
171 if(!result) {
172 result = CURL_EASY_STR_SETN(data, STRING_INTERFACE, iface);
C1: STRING_INTERFACE is set but not used in connection reuse key
173 iface = NULL;
174 }
175 if(!result) {
176 result = CURL_EASY_STR_SETN(data, STRING_BINDHOST, host);
C1: STRING_BINDHOST is set but not used in connection reuse key
177 host = NULL;
178 }
179 curlx_free(dev);
180 curlx_free(iface);
181 curlx_free(host);
182 return result;
183}
184
185#ifdef USE_SSL
186#define C_SSLVERSION_VALUE(x) ((x) & 0xffff)
187#define C_SSLVERSION_MAX_VALUE(x) ((unsigned long)(x) & 0xffff0000)
188#endif
189
190static CURLcode protocol2num(const char *str, curl_prot_t *val)
191{
192 /*
193 * We are asked to cherry-pick protocols, so play it safe and disallow all
194 * protocols to start with, and re-add the wanted ones back in.
195 */
196 *val = 0;
197
198 if(!str)
199 return CURLE_BAD_FUNCTION_ARGUMENT;
200
201 if(curl_strequal(str, "all")) {
202 *val = ~(curl_prot_t)0;
203 return CURLE_OK;
204 }
205
206 do {
207 const char *token = str;
208 size_t tlen;
lib/cf-socket.clines 636–804 · lines 661-663 cited by the report; definition of bindlocal (truncated)
636
637 if(!*input)
638 return CURLE_BAD_FUNCTION_ARGUMENT;
639 *dev = curlx_memdup0(input, len);
640 return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
641}
642
643#ifndef CURL_DISABLE_BINDLOCAL
644static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
645 curl_socket_t sockfd, int af, unsigned int scope,
646 uint8_t transport)
647{
648 struct Curl_sockaddr_storage sa;
649 struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
650 curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
651 struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
652#ifdef USE_IPV6
653 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
654#endif
655
656 struct Curl_dns_entry *h = NULL;
657 unsigned short port = data->set.localport; /* use this port number, 0 for
658 "random" */
659 /* how many port numbers to try to bind to, increasing one at a time */
660 int portnum = data->set.localportrange;
661 const char *dev = CURL_EASY_STR(data, STRING_DEVICE);
C4: bindlocal reads all three strings but is not called on reuse
C6: bindlocal uses the request's binding parameters but is skipped on reuse
662 const char *iface_input = CURL_EASY_STR(data, STRING_INTERFACE);
663 const char *host_input = CURL_EASY_STR(data, STRING_BINDHOST);
C4: STRING_BINDHOST used only during initial bind
664 const char *iface = iface_input ? iface_input : dev;
665 const char *host = host_input ? host_input : dev;
666 int sockerr;
667#ifdef IP_BIND_ADDRESS_NO_PORT
668 int on = 1;
669#endif
670#ifndef USE_IPV6
671 (void)scope;
672#endif
673
674 /*************************************************************
675 * Select device to bind socket to
676 *************************************************************/
677 if(!iface && !host && !port)
678 /* no local kind of binding was requested */
679 return CURLE_OK;
680 else if(iface && (strlen(iface) >= 255))
681 return CURLE_BAD_FUNCTION_ARGUMENT;
682
683 memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
684
685 if(iface || host) {
686 char myhost[256] = "";
687 int done = 0; /* -1 for error, 1 for address found */
688 if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
689
690#ifdef SO_BINDTODEVICE
691 /*
692 * This binds the local socket to a particular interface. This will
693 * force even requests to other local interfaces to go out the external
694 * interface. Only bind to the interface when specified as interface,
695 * not as a hostname or ip address.
696 *
697 * The interface might be a VRF, eg: vrf-blue, which means it cannot be
698 * converted to an IP address and would fail Curl_if2ip. Try to
699 * use it straight away.
700 */
701 if(iface &&
702 setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
703 iface, (curl_socklen_t)strlen(iface) + 1) == 0 &&
704 /* This is often "errno 1, error: Operation not permitted" if you are
705 * not running as root or another suitable privileged user. If it
706 * succeeds it means the parameter was a valid interface and not an IP
707 * address. Return immediately.
708 */
709 !host_input) {
710 infof(data, "socket successfully bound to interface '%s'", iface);
711 return CURLE_OK;
712 }
713#endif
714 if(!host_input) {
715 /* Discover IP from input device, then bind to it */
716 if2ip_result = Curl_if2ip(af,
717#ifdef USE_IPV6
718 scope, conn->scope_id,
719#endif
720 iface, myhost, sizeof(myhost));
721 }
722 switch(if2ip_result) {
723 case IF2IP_NOT_FOUND:
724 if(iface_input && !host_input) {
725 /* Do not fall back to treating it as a hostname */
726 char buffer[STRERROR_LEN];
727 data->state.os_errno = sockerr = SOCKERRNO;
728 failf(data, "Could not bind to interface '%s' with errno %d: %s",
729 iface, sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
730 return CURLE_INTERFACE_FAILED;
731 }
732 break;
733 case IF2IP_AF_NOT_SUPPORTED:
734 /* Signal the caller to try another address family if available */
735 return CURLE_UNSUPPORTED_PROTOCOL;
736 case IF2IP_FOUND:
737 /*
738 * We now have the numerical IP address in the 'myhost' buffer
739 */
740 host = myhost;
741 infof(data, "Local Interface %s is ip %s using address family %d",
742 iface, host, af);
743 done = 1;
744 break;
745 }
746 if(!iface_input || host_input) {
747 /*
748 * This was not an interface, resolve the name as a hostname
749 * or IP number
750 *
751 * Temporarily force name resolution to use only the address type
752 * of the connection. The resolve functions should really be changed
753 * to take a type parameter instead.
754 */
755 uint8_t dns_queries = (af == AF_INET) ?
756 CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA);
757#ifdef USE_IPV6
758 if(af == AF_INET6)
759 dns_queries = CURL_DNSQ_AAAA;
760#endif
761
762 (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h);
763 if(h) {
764 int h_af = h->addr->ai_family;
765 /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
766 Curl_printable_address(h->addr, myhost, sizeof(myhost));
767 infof(data, "Name '%s' family %d resolved to '%s' family %d",
768 host, af, myhost, h_af);
769 Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
770 if(af != h_af) {
771 /* bad IP version combo, signal the caller to try another address
772 family if available */
773 return CURLE_UNSUPPORTED_PROTOCOL;
774 }
775 done = 1;
776 }
777 else {
778 /*
779 * provided dev was no interface (or interfaces are not supported
780 * e.g. Solaris) no ip address and no domain we fail here
781 */
782 done = -1;
783 }
784 }
785
786 if(done > 0) {
787#ifdef USE_IPV6
788 /* IPv6 address */
789 if(af == AF_INET6) {
790#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
791 char *scope_ptr = strchr(myhost, '%');
792 if(scope_ptr)
793 *(scope_ptr++) = '\0';
794#endif
795 if(curlx_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
796 si6->sin6_family = AF_INET6;
797 si6->sin6_port = htons(port);
798#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
799 if(scope_ptr) {
800 /* The "myhost" string either comes from Curl_if2ip or from
801 Curl_printable_address. The latter returns only numeric scope
802 IDs and the former returns none at all. Making the scope ID,
803 if present, known to be numeric */
804 curl_off_t scope_id;
lib/url.clines 594–653 · lines 625-628 cited by the report; lines 619-624 cited by the report
594 * reuse connections that have TLS. */
595 BIT(wait_pipe);
596 BIT(seen_pending_conn);
597 BIT(seen_single_use_conn);
598 BIT(seen_multiplex_conn);
599};
600
601static bool url_match_connect_config(struct connectdata *conn,
602 struct url_conn_match *m)
603{
604 /* connect-only or to-be-closed connections will not be reused */
605 if(conn->bits.connect_only || conn->bits.close || conn->bits.no_reuse)
606 return FALSE;
607
608 /* ip_version must match */
609 if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER &&
610 m->data->set.ipver != conn->ip_version)
611 return FALSE;
612
613 if((m->needle->localdev || m->needle->localport) &&
614 /* If we are bound to a specific local end (IP+port), we must not reuse a
615 random other one, although if we did not ask for a particular one we
616 can reuse one that was bound.
617
618 This comparison is a bit rough and too strict. Since the input
619 parameters can be specified in numerous ways and still end up the same
620 it would take a lot of processing to make it really accurate. Instead,
621 this matching will assume that reuses of bound connections will most
622 likely also reuse the exact same binding parameters and missing out a
623 few edge cases should not hurt anyone much. */
624 ((conn->localport != m->needle->localport) ||
C1: url_match_connect_config compares only localdev, localport, localportrange
C2: url_match_connect_config uses only localdev for local binding comparison
C4: Connection match ignores STRING_INTERFACE and STRING_BINDHOST
C5: The match logic that causes the misbehavior
C6: Connection reuse ignores the per-request binding parameters
625 (conn->localportrange != m->needle->localportrange) ||
626 (m->needle->localdev &&
627 (!conn->localdev || strcmp(conn->localdev, m->needle->localdev)))))
C1: No comparison of STRING_INTERFACE or STRING_BINDHOST values
628 return FALSE;
629
630 if(!m->needle->via_peer != !conn->via_peer)
631 /* do not mix connections that use the "connect to host" feature and
632 * connections that do not use this feature */
633 return FALSE;
634
635 return TRUE;
636}
637
638static bool url_match_fully_connected(struct connectdata *conn,
639 struct url_conn_match *m)
640{
641 if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
642 conn->bits.upgrade_in_progress) {
643 /* Not yet connected, or a protocol upgrade is in progress. The later
644 * happens for HTTP/2 Upgrade: requests that need a response. */
645 if(m->may_multiplex) {
646 m->seen_pending_conn = TRUE;
647 /* Do not pick a connection that has not connected yet */
648 infof(m->data, "Connection #%" FMT_OFF_T
649 " is not open enough, cannot reuse", conn->connection_id);
650 }
651 /* Do not pick a connection that has not connected yet */
652 return FALSE;
653 }
lib/url.clines 1114–1172 · lines 1147-1147 cited by the report; definition of allocate_conn
1114static struct connectdata *allocate_conn(struct Curl_easy *data)
1115{
1116 struct connectdata *conn = curlx_calloc(1, sizeof(struct connectdata));
1117 if(!conn)
1118 return NULL;
1119
1120 /* and we setup a few fields in case we end up actually using this struct */
1121 conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1122 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1123 conn->recv_idx = 0; /* default for receiving transfer data */
1124 conn->send_idx = 0; /* default for sending transfer data */
1125 conn->connection_id = -1; /* no ID */
1126 conn->attached_xfers = 0;
1127 conn->shutdown.start_ms[FIRSTSOCKET] =
1128 conn->shutdown.start_ms[SECONDARYSOCKET] = -1;
1129
1130#ifndef CURL_DISABLE_FTP
1131 conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
1132 conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
1133#endif
1134 conn->ip_version = data->set.ipver;
1135 conn->bits.connect_only = (bool)data->set.connect_only;
1136 conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
1137
1138 /* Store the local bind parameters that will be used for this connection */
1139 if(CURL_EASY_STR(data, STRING_DEVICE)) {
C1: allocate_conn copies only STRING_DEVICE to conn->localdev
C2: allocate_conn builds the connection struct with only STRING_DEVICE
1140 conn->localdev = curlx_strdup(CURL_EASY_STR(data, STRING_DEVICE));
1141 if(!conn->localdev)
1142 goto error;
C1: No copy of STRING_INTERFACE or STRING_BINDHOST to connection struct
1143 }
1144#ifndef CURL_DISABLE_BINDLOCAL
1145 conn->localportrange = data->set.localportrange;
1146 conn->localport = data->set.localport;
1147#endif
1148
1149 /* the close socket stuff needs to be copied to the connection struct as
1150 it may live on without (this specific) Curl_easy */
1151 conn->fclosesocket = data->set.fclosesocket;
1152 conn->closesocket_client = data->set.closesocket_client;
1153#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1154 conn->gssapi_delegation = data->set.gssapi_delegation;
1155#endif
1156 return conn;
1157error:
1158
1159 curlx_free(conn->localdev);
1160 curlx_free(conn);
1161 return NULL;
1162}
1163
1164static CURLcode url_set_conn_scheme(struct Curl_easy *data,
1165 struct connectdata *conn)
1166{
1167 /* URL scheme is usable for connection when it is
1168 * - allowed
1169 * - not from a redirect or an allowed redirect protocol */
1170 const struct Curl_scheme *scheme = conn->origin->scheme;
1171 if(scheme->run &&
1172 (data->set.allowed_protocols & scheme->protocol) &&
lib/url.c@curl-8_22_0lines 1181–1231 · definition of allocate_conn as in release curl-8_22_0; differs from master; commits that changed it since: 6ee61c3fe1 lib: connection shutdown, cleanup code | 9a012386b8 connect: fix for first ms connection shutdown | 52ec4a95bf connectdata: use less curltime
Show 51 lines (not cited by the judge)
1181static struct connectdata *allocate_conn(struct Curl_easy *data)
1182{
1183 struct connectdata *conn = curlx_calloc(1, sizeof(struct connectdata));
1184 if(!conn)
1185 return NULL;
1186
1187 /* and we setup a few fields in case we end up actually using this struct */
1188
1189 conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1190 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1191 conn->recv_idx = 0; /* default for receiving transfer data */
1192 conn->send_idx = 0; /* default for sending transfer data */
1193 conn->connection_id = -1; /* no ID */
1194 conn->attached_xfers = 0;
1195
1196 /* Remember time this connection started */
1197 conn->lastused = conn->lastupkeep = conn->created = *Curl_pgrs_now(data);
1198
1199#ifndef CURL_DISABLE_FTP
1200 conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
1201 conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
1202#endif
1203 conn->ip_version = data->set.ipver;
1204 conn->bits.connect_only = (bool)data->set.connect_only;
1205 conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
1206
1207 /* Store the local bind parameters that will be used for this connection */
1208 if(CURL_EASY_STR(data, STRING_DEVICE)) {
1209 conn->localdev = curlx_strdup(CURL_EASY_STR(data, STRING_DEVICE));
1210 if(!conn->localdev)
1211 goto error;
1212 }
1213#ifndef CURL_DISABLE_BINDLOCAL
1214 conn->localportrange = data->set.localportrange;
1215 conn->localport = data->set.localport;
1216#endif
1217
1218 /* the close socket stuff needs to be copied to the connection struct as
1219 it may live on without (this specific) Curl_easy */
1220 conn->fclosesocket = data->set.fclosesocket;
1221 conn->closesocket_client = data->set.closesocket_client;
1222#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1223 conn->gssapi_delegation = data->set.gssapi_delegation;
1224#endif
1225 return conn;
1226error:
1227
1228 curlx_free(conn->localdev);
1229 curlx_free(conn);
1230 return NULL;
1231}
lib/cf-socket.c@curl-8_22_0lines 644–804 · definition of bindlocal as in release curl-8_22_0; differs from master; commits that changed it since: ba666ecef6 clang-tidy: merge nested `if()`s
Show 161 lines (not cited by the judge)
644static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
645 curl_socket_t sockfd, int af, unsigned int scope,
646 uint8_t transport)
647{
648 struct Curl_sockaddr_storage sa;
649 struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
650 curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
651 struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
652#ifdef USE_IPV6
653 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
654#endif
655
656 struct Curl_dns_entry *h = NULL;
657 unsigned short port = data->set.localport; /* use this port number, 0 for
658 "random" */
659 /* how many port numbers to try to bind to, increasing one at a time */
660 int portnum = data->set.localportrange;
661 const char *dev = CURL_EASY_STR(data, STRING_DEVICE);
662 const char *iface_input = CURL_EASY_STR(data, STRING_INTERFACE);
663 const char *host_input = CURL_EASY_STR(data, STRING_BINDHOST);
664 const char *iface = iface_input ? iface_input : dev;
665 const char *host = host_input ? host_input : dev;
666 int sockerr;
667#ifdef IP_BIND_ADDRESS_NO_PORT
668 int on = 1;
669#endif
670#ifndef USE_IPV6
671 (void)scope;
672#endif
673
674 /*************************************************************
675 * Select device to bind socket to
676 *************************************************************/
677 if(!iface && !host && !port)
678 /* no local kind of binding was requested */
679 return CURLE_OK;
680 else if(iface && (strlen(iface) >= 255))
681 return CURLE_BAD_FUNCTION_ARGUMENT;
682
683 memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
684
685 if(iface || host) {
686 char myhost[256] = "";
687 int done = 0; /* -1 for error, 1 for address found */
688 if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
689
690#ifdef SO_BINDTODEVICE
691 if(iface) {
692 /*
693 * This binds the local socket to a particular interface. This will
694 * force even requests to other local interfaces to go out the external
695 * interface. Only bind to the interface when specified as interface,
696 * not as a hostname or ip address.
697 *
698 * The interface might be a VRF, eg: vrf-blue, which means it cannot be
699 * converted to an IP address and would fail Curl_if2ip. Try to
700 * use it straight away.
701 */
702 if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
703 iface, (curl_socklen_t)strlen(iface) + 1) == 0) {
704 /* This is often "errno 1, error: Operation not permitted" if you are
705 * not running as root or another suitable privileged user. If it
706 * succeeds it means the parameter was a valid interface and not an IP
707 * address. Return immediately.
708 */
709 if(!host_input) {
710 infof(data, "socket successfully bound to interface '%s'", iface);
711 return CURLE_OK;
712 }
713 }
714 }
715#endif
716 if(!host_input) {
717 /* Discover IP from input device, then bind to it */
718 if2ip_result = Curl_if2ip(af,
719#ifdef USE_IPV6
720 scope, conn->scope_id,
721#endif
722 iface, myhost, sizeof(myhost));
723 }
724 switch(if2ip_result) {
725 case IF2IP_NOT_FOUND:
726 if(iface_input && !host_input) {
727 /* Do not fall back to treating it as a hostname */
728 char buffer[STRERROR_LEN];
729 data->state.os_errno = sockerr = SOCKERRNO;
730 failf(data, "Could not bind to interface '%s' with errno %d: %s",
731 iface, sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
732 return CURLE_INTERFACE_FAILED;
733 }
734 break;
735 case IF2IP_AF_NOT_SUPPORTED:
736 /* Signal the caller to try another address family if available */
737 return CURLE_UNSUPPORTED_PROTOCOL;
738 case IF2IP_FOUND:
739 /*
740 * We now have the numerical IP address in the 'myhost' buffer
741 */
742 host = myhost;
743 infof(data, "Local Interface %s is ip %s using address family %d",
744 iface, host, af);
745 done = 1;
746 break;
747 }
748 if(!iface_input || host_input) {
749 /*
750 * This was not an interface, resolve the name as a hostname
751 * or IP number
752 *
753 * Temporarily force name resolution to use only the address type
754 * of the connection. The resolve functions should really be changed
755 * to take a type parameter instead.
756 */
757 uint8_t dns_queries = (af == AF_INET) ?
758 CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA);
759#ifdef USE_IPV6
760 if(af == AF_INET6)
761 dns_queries = CURL_DNSQ_AAAA;
762#endif
763
764 (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h);
765 if(h) {
766 int h_af = h->addr->ai_family;
767 /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
768 Curl_printable_address(h->addr, myhost, sizeof(myhost));
769 infof(data, "Name '%s' family %d resolved to '%s' family %d",
770 host, af, myhost, h_af);
771 Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
772 if(af != h_af) {
773 /* bad IP version combo, signal the caller to try another address
774 family if available */
775 return CURLE_UNSUPPORTED_PROTOCOL;
776 }
777 done = 1;
778 }
779 else {
780 /*
781 * provided dev was no interface (or interfaces are not supported
782 * e.g. Solaris) no ip address and no domain we fail here
783 */
784 done = -1;
785 }
786 }
787
788 if(done > 0) {
789#ifdef USE_IPV6
790 /* IPv6 address */
791 if(af == AF_INET6) {
792#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
793 char *scope_ptr = strchr(myhost, '%');
794 if(scope_ptr)
795 *(scope_ptr++) = '\0';
796#endif
797 if(curlx_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
798 si6->sin6_family = AF_INET6;
799 si6->sin6_port = htons(port);
800#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
801 if(scope_ptr) {
802 /* The "myhost" string either comes from Curl_if2ip or from
803 Curl_printable_address. The latter returns only numeric scope
804 IDs and the former returns none at all. Making the scope ID,
lib/url.c@curl-8_22_0lines 602–638 · definition of url_match_connect_config as in release curl-8_22_0; differs from master; commits that changed it since: ba666ecef6 clang-tidy: merge nested `if()`s
Show 37 lines (not cited by the judge)
602static bool url_match_connect_config(struct connectdata *conn,
603 struct url_conn_match *m)
604{
605 /* connect-only or to-be-closed connections will not be reused */
606 if(conn->bits.connect_only || conn->bits.close || conn->bits.no_reuse)
607 return FALSE;
608
609 /* ip_version must match */
610 if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER &&
611 m->data->set.ipver != conn->ip_version)
612 return FALSE;
613
614 if(m->needle->localdev || m->needle->localport) {
615 /* If we are bound to a specific local end (IP+port), we must not reuse a
616 random other one, although if we did not ask for a particular one we
617 can reuse one that was bound.
618
619 This comparison is a bit rough and too strict. Since the input
620 parameters can be specified in numerous ways and still end up the same
621 it would take a lot of processing to make it really accurate. Instead,
622 this matching will assume that reuses of bound connections will most
623 likely also reuse the exact same binding parameters and missing out a
624 few edge cases should not hurt anyone much. */
625 if((conn->localport != m->needle->localport) ||
626 (conn->localportrange != m->needle->localportrange) ||
627 (m->needle->localdev &&
628 (!conn->localdev || strcmp(conn->localdev, m->needle->localdev))))
629 return FALSE;
630 }
631
632 if(!m->needle->via_peer != !conn->via_peer)
633 /* do not mix connections that use the "connect to host" feature and
634 * connections that do not use this feature */
635 return FALSE;
636
637 return TRUE;
638}