Line | Count | Source |
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 | | * IDN conversions |
26 | | */ |
27 | | #include "curl_setup.h" |
28 | | |
29 | | #ifdef HAVE_NETINET_IN_H |
30 | | #include <netinet/in.h> |
31 | | #endif |
32 | | #ifdef HAVE_NETDB_H |
33 | | #include <netdb.h> |
34 | | #endif |
35 | | #ifdef HAVE_ARPA_INET_H |
36 | | #include <arpa/inet.h> |
37 | | #endif |
38 | | #ifdef HAVE_NET_IF_H |
39 | | #include <net/if.h> |
40 | | #endif |
41 | | #ifdef HAVE_IPHLPAPI_H |
42 | | #include <Iphlpapi.h> |
43 | | #endif |
44 | | #ifdef HAVE_SYS_IOCTL_H |
45 | | #include <sys/ioctl.h> |
46 | | #endif |
47 | | #ifdef HAVE_SYS_PARAM_H |
48 | | #include <sys/param.h> |
49 | | #endif |
50 | | |
51 | | #ifdef __VMS |
52 | | #include <in.h> |
53 | | #include <inet.h> |
54 | | #endif |
55 | | |
56 | | #ifdef HAVE_SYS_UN_H |
57 | | #include <sys/un.h> |
58 | | #endif |
59 | | |
60 | | #if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK) |
61 | | #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5) |
62 | | #include <wincrypt.h> /* workaround for old mingw-w64 missing to include it */ |
63 | | #endif |
64 | | #include <iphlpapi.h> |
65 | | #endif |
66 | | |
67 | | #include "curl_addrinfo.h" |
68 | | #include "curl_trc.h" |
69 | | #include "protocol.h" |
70 | | #include "http_proxy.h" |
71 | | #include "idn.h" |
72 | | #include "curlx/strdup.h" |
73 | | #include "curlx/strparse.h" |
74 | | #include "peer.h" |
75 | | #include "urldata.h" |
76 | | #include "url.h" |
77 | | #include "urlapi-int.h" |
78 | | #include "vtls/vtls.h" |
79 | | |
80 | | struct peer_parse { |
81 | | const struct Curl_scheme *scheme; |
82 | | struct Curl_str host_user; |
83 | | struct Curl_str host; |
84 | | struct Curl_str zoneid; |
85 | | char *tmp_host_user; |
86 | | char *tmp_host; |
87 | | char *tmp_zoneid; |
88 | | uint32_t scopeid; |
89 | | uint16_t port; |
90 | | bool ipv6; |
91 | | bool unix_socket; |
92 | | bool abstract_uds; |
93 | | }; |
94 | | |
95 | | static void peer_parse_clear(struct peer_parse *pp) |
96 | 18.1k | { |
97 | 18.1k | curlx_free(pp->tmp_host_user); |
98 | 18.1k | curlx_free(pp->tmp_host); |
99 | 18.1k | curlx_free(pp->tmp_zoneid); |
100 | 18.1k | memset(pp, 0, sizeof(*pp)); |
101 | 18.1k | } |
102 | | |
103 | | static CURLcode peer_create(struct peer_parse *pp, |
104 | | struct Curl_peer **ppeer) |
105 | 17.8k | { |
106 | 17.8k | struct Curl_peer *peer = NULL; |
107 | 17.8k | CURLcode result = CURLE_OK; |
108 | 17.8k | size_t zone_alen = 0, host_alen = 0; |
109 | | |
110 | 17.8k | if(!pp || !pp->scheme) |
111 | 0 | return CURLE_FAILED_INIT; |
112 | 17.8k | if(!pp->host.len && !(pp->scheme->flags & PROTOPT_NONETWORK)) |
113 | 0 | return CURLE_FAILED_INIT; |
114 | | |
115 | 17.8k | if((pp->host.str != pp->host_user.str) || |
116 | 17.4k | (pp->host.len != pp->host_user.len)) { |
117 | 317 | host_alen = pp->host.len + 1; |
118 | 317 | } |
119 | 17.8k | zone_alen = pp->zoneid.len ? (pp->zoneid.len + 1) : 0; |
120 | | |
121 | | /* null-terminator already part of struct */ |
122 | 17.8k | peer = curlx_calloc(1, sizeof(*peer) + |
123 | 17.8k | pp->host_user.len + host_alen + zone_alen); |
124 | 17.8k | if(!peer) { |
125 | 0 | result = CURLE_OUT_OF_MEMORY; |
126 | 0 | goto out; |
127 | 0 | } |
128 | | |
129 | 17.8k | peer->refcount = 1; |
130 | 17.8k | peer->scheme = pp->scheme; |
131 | 17.8k | peer->hostname = peer->user_hostname; |
132 | 17.8k | peer->port = pp->port; |
133 | 17.8k | peer->scopeid = pp->scopeid; |
134 | 17.8k | peer->ipv6 = pp->ipv6; |
135 | 17.8k | peer->unix_socket = pp->unix_socket; |
136 | 17.8k | peer->abstract_uds = pp->abstract_uds; |
137 | | |
138 | 17.8k | if(pp->host_user.len) |
139 | 17.7k | memcpy(peer->user_hostname, pp->host_user.str, pp->host_user.len); |
140 | | |
141 | 17.8k | if(host_alen) { |
142 | 317 | peer->hostname = peer->user_hostname + pp->host_user.len + 1; |
143 | 317 | memcpy(peer->hostname, pp->host.str, pp->host.len); |
144 | 317 | } |
145 | | |
146 | 17.8k | if(zone_alen) { |
147 | 22 | peer->zoneid = peer->user_hostname + pp->host_user.len + 1 + host_alen; |
148 | 22 | memcpy(peer->zoneid, pp->zoneid.str, pp->zoneid.len); |
149 | 22 | #ifdef USE_IPV6 |
150 | | /* Determine scope_id if not already provided */ |
151 | 22 | if(!peer->scopeid) { |
152 | 22 | const char *p = peer->zoneid; |
153 | 22 | curl_off_t scope; |
154 | 22 | if(!curlx_str_number(&p, &scope, UINT_MAX) && !*p) { |
155 | | /* A plain number, use it directly as a scope id. */ |
156 | 2 | peer->scopeid = (uint32_t)scope; |
157 | 2 | } |
158 | 20 | #ifdef HAVE_IF_NAMETOINDEX |
159 | 20 | else { |
160 | | /* Zone identifier is not fully numeric */ |
161 | 20 | unsigned int idx = 0; |
162 | 20 | idx = if_nametoindex(peer->zoneid); |
163 | 20 | if(idx) { |
164 | 1 | peer->scopeid = (uint32_t)idx; |
165 | 1 | } |
166 | 19 | else { |
167 | | /* Do we want to return an error here? */ |
168 | 19 | } |
169 | 20 | } |
170 | 22 | #endif /* HAVE_IF_NAMETOINDEX */ |
171 | 22 | } |
172 | 22 | #endif /* USE_IPV6 */ |
173 | 22 | } |
174 | | |
175 | 17.8k | out: |
176 | 17.8k | if(!result) |
177 | 17.8k | *ppeer = peer; |
178 | 0 | else |
179 | 0 | Curl_peer_unlink(&peer); |
180 | 17.8k | return result; |
181 | 17.8k | } |
182 | | |
183 | | static CURLcode peer_parse_host(struct Curl_easy *data, |
184 | | struct peer_parse *pp, |
185 | | bool scan_for_ipv6) |
186 | 17.8k | { |
187 | 17.8k | if(!pp || !pp->host_user.str || !pp->host_user.len) |
188 | 0 | return CURLE_FAILED_INIT; |
189 | | |
190 | 17.8k | if(pp->host_user.str[0] == '[') { |
191 | 317 | const char *s = pp->host_user.str + 1; |
192 | 317 | struct Curl_str tmp; |
193 | 317 | if(curlx_str_until(&s, &tmp, pp->host_user.len - 1, ']')) |
194 | 0 | return CURLE_URL_MALFORMAT; |
195 | | |
196 | 317 | if(!Curl_looks_like_ipv6(tmp.str, tmp.len, TRUE, |
197 | 317 | &pp->host, &pp->zoneid)) { |
198 | 0 | failf(data, "Invalid IPv6 address format in '%.*s'", |
199 | 0 | (int)pp->host_user.len, pp->host_user.str); |
200 | 0 | return CURLE_URL_MALFORMAT; |
201 | 0 | } |
202 | 317 | pp->ipv6 = TRUE; |
203 | 317 | } |
204 | 17.5k | else { |
205 | 17.5k | #ifdef USE_IDN |
206 | 17.5k | if(!Curl_is_ASCII_str(&pp->host_user)) { |
207 | 178 | CURLcode result; |
208 | 178 | if(!pp->tmp_host_user) { |
209 | | /* need a null-terminated string for IDN */ |
210 | 178 | pp->tmp_host_user = curlx_memdup0(pp->host_user.str, |
211 | 178 | pp->host_user.len); |
212 | 178 | if(!pp->tmp_host_user) |
213 | 0 | return CURLE_OUT_OF_MEMORY; |
214 | 178 | } |
215 | 178 | result = Curl_idn_decode(pp->tmp_host_user, &pp->tmp_host); |
216 | 178 | if(result) |
217 | 178 | return result; |
218 | 0 | pp->host.str = pp->tmp_host; |
219 | 0 | pp->host.len = strlen(pp->host.str); |
220 | 0 | } |
221 | 17.3k | else |
222 | 17.3k | #endif |
223 | 17.3k | if(scan_for_ipv6 && |
224 | 74 | Curl_looks_like_ipv6(pp->host_user.str, pp->host_user.len, TRUE, |
225 | 74 | &pp->host, &pp->zoneid)) { |
226 | 74 | if(pp->host_user.len < MAX_IPADR_LEN) { |
227 | 74 | char tmp[MAX_IPADR_LEN]; |
228 | 74 | memcpy(tmp, pp->host_user.str, pp->host_user.len); |
229 | 74 | tmp[pp->host_user.len] = 0; |
230 | 74 | pp->ipv6 = !Curl_is_ipv4addr(tmp); |
231 | 74 | } |
232 | 0 | else |
233 | 0 | pp->ipv6 = TRUE; |
234 | 74 | } |
235 | 17.2k | else |
236 | 17.2k | pp->host = pp->host_user; |
237 | 17.5k | } |
238 | 17.6k | return CURLE_OK; |
239 | 17.8k | } |
240 | | |
241 | | CURLcode Curl_peer_create(struct Curl_easy *data, |
242 | | const struct Curl_scheme *scheme, |
243 | | const char *hostname, |
244 | | uint16_t port, |
245 | | struct Curl_peer **ppeer) |
246 | 74 | { |
247 | 74 | struct peer_parse pp; |
248 | 74 | CURLcode result; |
249 | | |
250 | 74 | Curl_peer_unlink(ppeer); |
251 | 74 | memset(&pp, 0, sizeof(pp)); |
252 | 74 | pp.scheme = scheme; |
253 | 74 | pp.host_user.str = hostname; |
254 | 74 | pp.host_user.len = strlen(hostname); |
255 | 74 | pp.port = port; |
256 | | |
257 | 74 | result = peer_parse_host(data, &pp, TRUE); |
258 | 74 | if(!result) |
259 | 74 | result = peer_create(&pp, ppeer); |
260 | | |
261 | 74 | peer_parse_clear(&pp); |
262 | 74 | return result; |
263 | 74 | } |
264 | | |
265 | | #ifdef USE_UNIX_SOCKETS |
266 | | CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, |
267 | | const char *path, |
268 | | bool abstract_unix_socket, |
269 | | struct Curl_peer **ppeer) |
270 | 106 | { |
271 | 106 | struct peer_parse pp; |
272 | 106 | size_t pathlen = path ? strlen(path) : 0; |
273 | 106 | CURLcode result = CURLE_OK; |
274 | | |
275 | 106 | Curl_peer_unlink(ppeer); |
276 | 106 | memset(&pp, 0, sizeof(pp)); |
277 | 106 | if(!scheme) |
278 | 0 | return CURLE_FAILED_INIT; |
279 | 106 | if(!pathlen) |
280 | 2 | return CURLE_FAILED_INIT; |
281 | | |
282 | 104 | pp.scheme = scheme; |
283 | 104 | pp.host_user.str = pp.host.str = path; |
284 | 104 | pp.host_user.len = pp.host.len = pathlen; |
285 | 104 | pp.unix_socket = TRUE; |
286 | 104 | pp.abstract_uds = abstract_unix_socket; |
287 | | |
288 | 104 | result = peer_create(&pp, ppeer); |
289 | 104 | peer_parse_clear(&pp); |
290 | 104 | return result; |
291 | 106 | } |
292 | | #endif /* USE_UNIX_SOCKETS */ |
293 | | |
294 | | void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src) |
295 | 100k | { |
296 | 100k | if(*pdest != src) { |
297 | 90.4k | Curl_peer_unlink(pdest); |
298 | 90.4k | *pdest = src; |
299 | 90.4k | if(src) { |
300 | 90.4k | DEBUGASSERT(src->refcount < UINT32_MAX); |
301 | 90.4k | src->refcount++; |
302 | 90.4k | } |
303 | 90.4k | } |
304 | 100k | } |
305 | | |
306 | | void Curl_peer_unlink(struct Curl_peer **ppeer) |
307 | 468k | { |
308 | 468k | if(*ppeer) { |
309 | 108k | struct Curl_peer *peer = *ppeer; |
310 | | |
311 | 108k | DEBUGASSERT(peer->refcount); |
312 | 108k | *ppeer = NULL; |
313 | 108k | if(peer->refcount) |
314 | 108k | peer->refcount--; |
315 | 108k | if(!peer->refcount) { |
316 | 17.8k | curlx_free(peer); |
317 | 17.8k | } |
318 | 108k | } |
319 | 468k | } |
320 | | |
321 | | bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2) |
322 | 32.1k | { |
323 | 32.1k | return (p1 == p2) || |
324 | 8.19k | (p1 && p2 && |
325 | 7.90k | (p1->scheme == p2->scheme) && |
326 | 7.87k | Curl_peer_same_destination(p1, p2)); |
327 | 32.1k | } |
328 | | |
329 | | static bool peer_same_hostname(struct Curl_peer *p1, struct Curl_peer *p2) |
330 | 8.59k | { |
331 | | /* UNIX domain socket paths must be compared case-sensitive, |
332 | | * as many filesystem are like that. */ |
333 | 8.59k | return (p1->unix_socket == p2->unix_socket) && |
334 | 8.58k | (p1->abstract_uds == p2->abstract_uds) && |
335 | 8.58k | (p1->ipv6 == p2->ipv6) && |
336 | | /* unix_socket paths are case-sensitive, hostnames are not. |
337 | | * Do strcmp() first on both, since we do a lot of matches |
338 | | * on the same names and platform strcmp is way faster. */ |
339 | 8.35k | (!strcmp(p1->hostname, p2->hostname) || |
340 | 8.28k | (!p1->unix_socket && |
341 | 8.28k | curl_strequal(p1->hostname, p2->hostname))); |
342 | 8.59k | } |
343 | | |
344 | | static bool peer_same_zoneid(const char *z1, const char *z2) |
345 | 21 | { |
346 | 21 | return (z1 == z2) || |
347 | 0 | (z1 && z2 && !strcmp(z1, z2)); |
348 | 21 | } |
349 | | |
350 | | bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2) |
351 | 53.4k | { |
352 | 53.4k | return (p1 == p2) || |
353 | 16.1k | (p1 && p2 && |
354 | 16.1k | (p1->port == p2->port) && |
355 | 8.59k | peer_same_hostname(p1, p2) && |
356 | 73 | (p1->scopeid == p2->scopeid) && |
357 | 21 | (p1->scopeid || peer_same_zoneid(p1->zoneid, p2->zoneid))); |
358 | 53.4k | } |
359 | | |
360 | | CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, |
361 | | uint16_t port_override, |
362 | | uint32_t scopeid_override, |
363 | | struct Curl_peer **ppeer) |
364 | 8.78k | { |
365 | 8.78k | struct peer_parse pp; |
366 | 8.78k | char *zoneid = NULL, *scheme = NULL, *hostname = NULL; |
367 | 8.78k | CURLUcode uc; |
368 | 8.78k | CURLcode result; |
369 | | |
370 | 8.78k | Curl_peer_unlink(ppeer); |
371 | 8.78k | memset(&pp, 0, sizeof(pp)); |
372 | | |
373 | 8.78k | uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, 0); |
374 | 8.78k | if(uc) |
375 | 0 | return Curl_uc_to_curlcode(uc); |
376 | 8.78k | pp.scheme = Curl_get_scheme(scheme); |
377 | 8.78k | if(!pp.scheme) { |
378 | 178 | failf(data, "Protocol \"%s\" not supported%s", scheme, |
379 | 178 | data->state.this_is_a_follow ? " (in redirect)" : ""); |
380 | 178 | result = CURLE_UNSUPPORTED_PROTOCOL; |
381 | 178 | goto out; |
382 | 178 | } |
383 | | |
384 | 8.61k | uc = curl_url_get(uh, CURLUPART_HOST, &hostname, 0); |
385 | 8.61k | if(uc) { |
386 | 29 | if((uc == CURLUE_NO_HOST) && (pp.scheme->flags & PROTOPT_NONETWORK)) |
387 | 29 | ; /* acceptable */ |
388 | 0 | else { |
389 | 0 | result = CURLE_OUT_OF_MEMORY; |
390 | 0 | goto out; |
391 | 0 | } |
392 | 29 | } |
393 | 8.58k | else if(strlen(hostname) > MAX_URL_LEN) { |
394 | 1 | failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); |
395 | 1 | result = CURLE_URL_MALFORMAT; |
396 | 1 | goto out; |
397 | 1 | } |
398 | | |
399 | 8.61k | pp.host_user.str = hostname ? hostname : ""; |
400 | 8.61k | pp.host_user.len = strlen(pp.host_user.str); |
401 | 8.61k | if(pp.host_user.len) { |
402 | 8.58k | result = peer_parse_host(data, &pp, FALSE); |
403 | 8.58k | if(result) |
404 | 178 | goto out; |
405 | 8.58k | } |
406 | 29 | else |
407 | 29 | pp.host = pp.host_user; |
408 | | |
409 | 8.43k | if(port_override) { |
410 | | /* if set, we use this instead of the port possibly given in the URL */ |
411 | 57 | char portbuf[16]; |
412 | 57 | curl_msnprintf(portbuf, sizeof(portbuf), "%d", port_override); |
413 | 57 | uc = curl_url_set(uh, CURLUPART_PORT, portbuf, 0); |
414 | 57 | if(uc) { |
415 | 0 | result = CURLE_OUT_OF_MEMORY; |
416 | 0 | goto out; |
417 | 0 | } |
418 | 57 | else |
419 | 57 | pp.port = port_override; |
420 | 57 | } |
421 | 8.37k | else { |
422 | 8.37k | uc = Curl_url_get_port(uh, &pp.port); |
423 | 8.37k | if(uc) { |
424 | 28 | if(uc == CURLUE_OUT_OF_MEMORY) { |
425 | 0 | result = CURLE_OUT_OF_MEMORY; |
426 | 0 | goto out; |
427 | 0 | } |
428 | 28 | else if(!(pp.scheme->flags & PROTOPT_NONETWORK)) { |
429 | 0 | result = CURLE_URL_MALFORMAT; |
430 | 0 | goto out; |
431 | 0 | } |
432 | | /* no port ok when not a network scheme */ |
433 | 28 | } |
434 | 8.37k | } |
435 | | |
436 | 8.43k | if(scopeid_override) |
437 | | /* Override any scope id from an URL zone. */ |
438 | 127 | pp.scopeid = scopeid_override; |
439 | 8.30k | else { |
440 | 8.30k | if(curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0) == |
441 | 8.30k | CURLUE_OUT_OF_MEMORY) { |
442 | 0 | result = CURLE_OUT_OF_MEMORY; |
443 | 0 | goto out; |
444 | 0 | } |
445 | 8.30k | if(zoneid) { |
446 | 22 | pp.zoneid.str = zoneid; |
447 | 22 | pp.zoneid.len = strlen(zoneid); |
448 | 22 | } |
449 | 8.30k | } |
450 | | |
451 | 8.43k | result = peer_create(&pp, ppeer); |
452 | 8.43k | if(result) |
453 | 0 | failf(data, "Error %d creating peer for %s:%u", |
454 | 0 | (int)result, pp.host_user.str, pp.port); |
455 | | |
456 | 8.78k | out: |
457 | 8.78k | peer_parse_clear(&pp); |
458 | 8.78k | curlx_free(scheme); |
459 | 8.78k | curlx_free(hostname); |
460 | 8.78k | curlx_free(zoneid); |
461 | 8.78k | return result; |
462 | 8.43k | } |
463 | | |
464 | | /* Parse a "host:port" string to connect to into a peer. |
465 | | * IPv6 addresses might appear in brackets or without them. */ |
466 | | CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, |
467 | | const struct Curl_peer *dest, |
468 | | const char *connect_to, |
469 | | struct Curl_peer **ppeer) |
470 | 6.88k | { |
471 | 6.88k | struct peer_parse pp; |
472 | 6.88k | const char *portstr = NULL; |
473 | 6.88k | bool port_switch = FALSE; |
474 | 6.88k | CURLcode result; |
475 | | |
476 | 6.88k | Curl_peer_unlink(ppeer); |
477 | 6.88k | memset(&pp, 0, sizeof(pp)); |
478 | 6.88k | if(!connect_to || !*connect_to) |
479 | 0 | return CURLE_FAILED_INIT; |
480 | | |
481 | 6.88k | pp.scheme = dest->scheme; |
482 | | |
483 | | /* detect and extract RFC6874-style IPv6-addresses */ |
484 | 6.88k | if(connect_to[0] == '[') { |
485 | 0 | const char *s = strchr(connect_to + 1, ']'); |
486 | 0 | if(!s) { |
487 | 0 | failf(data, "Invalid IPv6 address format in '%s'", connect_to); |
488 | 0 | result = CURLE_SETOPT_OPTION_SYNTAX; |
489 | 0 | goto out; |
490 | 0 | } |
491 | 0 | portstr = strchr(s, ':'); |
492 | 0 | pp.host_user.str = connect_to; |
493 | 0 | pp.host_user.len = s - pp.host_user.str + 1; |
494 | 0 | pp.ipv6 = TRUE; |
495 | 0 | } |
496 | 6.88k | else { |
497 | 6.88k | portstr = strchr(connect_to, ':'); |
498 | 6.88k | pp.host_user.str = connect_to; |
499 | 6.88k | pp.host_user.len = portstr ? |
500 | 6.88k | (size_t)(portstr - connect_to) : strlen(connect_to); |
501 | 6.88k | } |
502 | | |
503 | 6.88k | if(!pp.host_user.len) { /* no hostname found, only port switch */ |
504 | 0 | port_switch = TRUE; |
505 | 0 | pp.host_user.str = dest->user_hostname; |
506 | 0 | pp.host_user.len = strlen(dest->user_hostname); |
507 | 0 | } |
508 | | |
509 | 6.88k | result = peer_parse_host(data, &pp, FALSE); |
510 | 6.88k | if(result) |
511 | 0 | goto out; |
512 | | /* On a port switch, ipv6 must stay the same */ |
513 | 6.88k | DEBUGASSERT(!port_switch || ((bool)pp.ipv6 == (bool)dest->ipv6)); |
514 | | |
515 | 6.88k | if(portstr && portstr[1]) { |
516 | 0 | const char *p = portstr + 1; |
517 | 0 | curl_off_t portparse; |
518 | 0 | if(curlx_str_number(&p, &portparse, 0xffff)) { |
519 | 0 | failf(data, "No valid port number in '%s'", connect_to); |
520 | 0 | result = CURLE_SETOPT_OPTION_SYNTAX; |
521 | 0 | goto out; |
522 | 0 | } |
523 | 0 | pp.port = (uint16_t)portparse; /* we know it will fit */ |
524 | 0 | } |
525 | 6.88k | else |
526 | 6.88k | pp.port = dest->port; |
527 | | |
528 | | #ifndef USE_IPV6 |
529 | | if(pp.ipv6) { |
530 | | failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); |
531 | | result = CURLE_NOT_BUILT_IN; |
532 | | goto out; |
533 | | } |
534 | | #endif |
535 | 6.88k | if(pp.ipv6 && port_switch) { |
536 | 0 | if(dest->zoneid) { |
537 | 0 | pp.zoneid.str = dest->zoneid; |
538 | 0 | pp.zoneid.len = strlen(dest->zoneid); |
539 | 0 | } |
540 | 0 | pp.scopeid = dest->scopeid; |
541 | 0 | } |
542 | | |
543 | 6.88k | result = peer_create(&pp, ppeer); |
544 | 6.88k | CURL_TRC_M(data, "connect-to peer_create2 -> %d", (int)result); |
545 | | |
546 | 6.88k | out: |
547 | 6.88k | CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, (int)result); |
548 | 6.88k | peer_parse_clear(&pp); |
549 | 6.88k | return result; |
550 | 6.88k | } |
551 | | |
552 | | #ifndef CURL_DISABLE_PROXY |
553 | | |
554 | | #ifdef USE_UNIX_SOCKETS |
555 | 167 | #define UNIX_SOCKET_PREFIX "localhost" |
556 | | #endif |
557 | | |
558 | | CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data, |
559 | | const char *scheme, |
560 | | uint8_t *proxytype, const char *url) |
561 | 4.62k | { |
562 | 4.62k | if(!scheme) |
563 | 0 | return CURLE_OK; |
564 | | |
565 | 4.62k | if(curl_strequal("https", scheme)) { |
566 | 0 | if(*proxytype != CURLPROXY_HTTPS2 && *proxytype != CURLPROXY_HTTPS3) |
567 | 0 | *proxytype = CURLPROXY_HTTPS; |
568 | 0 | } |
569 | 4.62k | else if(curl_strequal("socks5h", scheme)) |
570 | 0 | *proxytype = CURLPROXY_SOCKS5_HOSTNAME; |
571 | 4.62k | else if(curl_strequal("socks5", scheme)) |
572 | 334 | *proxytype = CURLPROXY_SOCKS5; |
573 | 4.28k | else if(curl_strequal("socks4a", scheme)) |
574 | 0 | *proxytype = CURLPROXY_SOCKS4A; |
575 | 4.28k | else if(curl_strequal("socks4", scheme) || curl_strequal("socks", scheme)) |
576 | 0 | *proxytype = CURLPROXY_SOCKS4; |
577 | 4.28k | else if(curl_strequal("http", scheme)) { |
578 | 4.28k | if(*proxytype != CURLPROXY_HTTP_1_0) |
579 | 4.28k | *proxytype = CURLPROXY_HTTP; |
580 | 4.28k | } |
581 | 0 | else { |
582 | | /* Any other xxx:// reject! */ |
583 | 0 | failf(data, "Unsupported proxy scheme for \'%s\'", url); |
584 | 0 | return CURLE_COULDNT_CONNECT; |
585 | 0 | } |
586 | 4.62k | return CURLE_OK; |
587 | 4.62k | } |
588 | | |
589 | | CURLcode Curl_peer_from_proxy_url(CURLU *uh, |
590 | | struct Curl_easy *data, |
591 | | const char *url, |
592 | | uint8_t proxytype, |
593 | | struct Curl_peer **ppeer, |
594 | | uint8_t *pproxytype) |
595 | 2.31k | { |
596 | 2.31k | struct peer_parse pp; |
597 | 2.31k | char *scheme = NULL; |
598 | 2.31k | char *portptr = NULL; |
599 | 2.31k | #ifdef USE_UNIX_SOCKETS |
600 | 2.31k | bool is_socks = FALSE; |
601 | 2.31k | #endif |
602 | 2.31k | CURLUcode uc; |
603 | 2.31k | CURLcode result = CURLE_OK; |
604 | | |
605 | 2.31k | Curl_peer_unlink(ppeer); |
606 | 2.31k | memset(&pp, 0, sizeof(pp)); |
607 | 2.31k | pp.port = CURL_DEFAULT_PROXY_PORT; |
608 | 2.31k | uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, |
609 | 2.31k | CURLU_NON_SUPPORT_SCHEME | CURLU_NO_GUESS_SCHEME); |
610 | 2.31k | if(uc) { |
611 | 0 | if(uc == CURLUE_OUT_OF_MEMORY) { |
612 | 0 | result = CURLE_OUT_OF_MEMORY; |
613 | 0 | goto out; |
614 | 0 | } |
615 | | /* URL came without scheme, the passed `proxytype` determines it */ |
616 | 0 | switch(proxytype) { |
617 | 0 | case CURLPROXY_HTTP: |
618 | 0 | case CURLPROXY_HTTP_1_0: |
619 | 0 | pp.scheme = &Curl_scheme_http; |
620 | 0 | break; |
621 | 0 | case CURLPROXY_HTTPS: |
622 | 0 | case CURLPROXY_HTTPS2: |
623 | 0 | case CURLPROXY_HTTPS3: |
624 | 0 | pp.scheme = &Curl_scheme_https; |
625 | 0 | break; |
626 | 0 | case CURLPROXY_SOCKS4: |
627 | 0 | pp.scheme = &Curl_scheme_socks4; |
628 | 0 | break; |
629 | 0 | case CURLPROXY_SOCKS4A: |
630 | 0 | pp.scheme = &Curl_scheme_socks4a; |
631 | 0 | break; |
632 | 0 | case CURLPROXY_SOCKS5: |
633 | 0 | pp.scheme = &Curl_scheme_socks5; |
634 | 0 | break; |
635 | 0 | case CURLPROXY_SOCKS5_HOSTNAME: |
636 | 0 | pp.scheme = &Curl_scheme_socks5h; |
637 | 0 | break; |
638 | 0 | default: |
639 | 0 | failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, url); |
640 | 0 | result = CURLE_COULDNT_RESOLVE_PROXY; |
641 | 0 | goto out; |
642 | 0 | } |
643 | 0 | } |
644 | 2.31k | else { |
645 | 2.31k | pp.scheme = Curl_get_scheme(scheme); |
646 | 2.31k | result = Curl_scheme_to_proxytype(data, scheme, &proxytype, url); |
647 | 2.31k | if(result) |
648 | 0 | goto out; |
649 | 2.31k | } |
650 | 2.31k | DEBUGASSERT(pp.scheme); |
651 | | |
652 | 2.31k | if(CURL_PROXY_IS_HTTPS(proxytype) && |
653 | 0 | !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { |
654 | 0 | failf(data, "Unsupported proxy \'%s\', libcurl is built without the " |
655 | 0 | "HTTPS-proxy support.", url); |
656 | 0 | result = CURLE_NOT_BUILT_IN; |
657 | 0 | goto out; |
658 | 0 | } |
659 | | |
660 | 2.31k | switch(pp.scheme->family) { |
661 | 167 | case CURLPROTO_SOCKS: |
662 | 167 | #ifdef USE_UNIX_SOCKETS |
663 | 167 | is_socks = TRUE; |
664 | 167 | #endif |
665 | 167 | break; |
666 | 2.14k | case CURLPROTO_HTTP: |
667 | 2.14k | break; |
668 | 0 | default: |
669 | 0 | failf(data, "Unsupported proxy protocol for \'%s\'", url); |
670 | 0 | result = CURLE_COULDNT_CONNECT; |
671 | 0 | goto out; |
672 | 2.31k | } |
673 | | |
674 | 2.31k | uc = curl_url_get(uh, CURLUPART_PORT, &portptr, CURLU_NO_DEFAULT_PORT); |
675 | 2.31k | if(uc == CURLUE_OUT_OF_MEMORY) { |
676 | 0 | result = CURLE_OUT_OF_MEMORY; |
677 | 0 | goto out; |
678 | 0 | } |
679 | 2.31k | if(portptr) { |
680 | 0 | curl_off_t num; |
681 | 0 | const char *p = portptr; |
682 | 0 | if(!curlx_str_number(&p, &num, UINT16_MAX)) |
683 | 0 | pp.port = (uint16_t)num; |
684 | | /* Should we not error out when the port number is invalid? */ |
685 | 0 | curlx_free(portptr); |
686 | 0 | } |
687 | 2.31k | else { |
688 | | /* No port in URL, take the set one or the scheme's default */ |
689 | 2.31k | if(data->set.proxyport) |
690 | 16 | pp.port = data->set.proxyport; |
691 | 2.29k | else |
692 | 2.29k | pp.port = pp.scheme->defport; |
693 | 2.31k | } |
694 | | |
695 | | /* now, clone the proxy hostname */ |
696 | 2.31k | uc = curl_url_get(uh, CURLUPART_HOST, &pp.tmp_host_user, CURLU_URLDECODE); |
697 | 2.31k | if(uc) { |
698 | 0 | result = CURLE_OUT_OF_MEMORY; |
699 | 0 | goto out; |
700 | 0 | } |
701 | 2.31k | pp.host_user.str = pp.tmp_host_user; |
702 | 2.31k | pp.host_user.len = strlen(pp.tmp_host_user); |
703 | | |
704 | 2.31k | #ifdef USE_UNIX_SOCKETS |
705 | 2.31k | if(is_socks && curl_strequal(UNIX_SOCKET_PREFIX, pp.tmp_host_user)) { |
706 | 0 | uc = curl_url_get(uh, CURLUPART_PATH, &pp.tmp_host, CURLU_URLDECODE); |
707 | 0 | if(uc) { |
708 | 0 | result = CURLE_OUT_OF_MEMORY; |
709 | 0 | goto out; |
710 | 0 | } |
711 | | /* path will be "/", if no path was found */ |
712 | 0 | if(strcmp("/", pp.tmp_host)) { |
713 | 0 | pp.host.str = pp.tmp_host; |
714 | 0 | pp.host.len = strlen(pp.tmp_host); |
715 | 0 | pp.unix_socket = TRUE; |
716 | 0 | } |
717 | 0 | else { |
718 | 0 | pp.host = pp.host_user; |
719 | 0 | } |
720 | 0 | } |
721 | 2.31k | #endif /* USE_UNIX_SOCKETS */ |
722 | | |
723 | 2.31k | if(!pp.host.len) { |
724 | 2.31k | result = peer_parse_host(data, &pp, FALSE); |
725 | 2.31k | if(result) |
726 | 0 | goto out; |
727 | 2.31k | } |
728 | | |
729 | 2.31k | uc = curl_url_get(uh, CURLUPART_ZONEID, &pp.tmp_zoneid, 0); |
730 | 2.31k | if(uc == CURLUE_OUT_OF_MEMORY) { |
731 | 0 | result = CURLE_OUT_OF_MEMORY; |
732 | 0 | goto out; |
733 | 0 | } |
734 | 2.31k | if(pp.tmp_zoneid) { |
735 | 0 | pp.zoneid.str = pp.tmp_zoneid; |
736 | 0 | pp.zoneid.len = strlen(pp.tmp_zoneid); |
737 | 0 | } |
738 | | |
739 | 2.31k | *pproxytype = proxytype; |
740 | 2.31k | result = peer_create(&pp, ppeer); |
741 | | |
742 | 2.31k | out: |
743 | 2.31k | peer_parse_clear(&pp); |
744 | 2.31k | curlx_free(scheme); |
745 | 2.31k | #ifdef DEBUGBUILD |
746 | 2.31k | if(!result) |
747 | 2.31k | DEBUGASSERT(*ppeer); |
748 | 2.31k | #endif |
749 | 2.31k | return result; |
750 | 2.31k | } |
751 | | |
752 | | #endif /* !CURL_DISABLE_PROXY */ |