1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifdef HAVE_NETINET_IN_H
26 # include <netinet/in.h>
28 #ifdef HAVE_ARPA_INET_H
29 # include <arpa/inet.h>
34 #ifdef HAVE_SYS_IOCTL_H
35 # include <sys/ioctl.h>
40 #ifdef HAVE_SYS_SOCKIO_H
41 # include <sys/sockio.h>
53 #include "inet_ntop.h"
56 /* The last 3 #include files should be in this order */
57 #include "curl_printf.h"
58 #include "curl_memory.h"
61 /* ------------------------------------------------------------------ */
64 /* Return the scope of the given address. */
65 unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
67 if(sa->sa_family == AF_INET6) {
68 const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa;
69 const unsigned char *b = sa6->sin6_addr.s6_addr;
70 unsigned short w = (unsigned short) ((b[0] << 8) | b[1]);
72 if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */
73 return IPV6_SCOPE_UNIQUELOCAL;
76 return IPV6_SCOPE_LINKLOCAL;
78 return IPV6_SCOPE_SITELOCAL;
80 w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] |
81 b[10] | b[11] | b[12] | b[13] | b[14];
82 if(w || b[15] != 0x01)
84 return IPV6_SCOPE_NODELOCAL;
89 return IPV6_SCOPE_GLOBAL;
93 #if defined(HAVE_GETIFADDRS)
95 if2ip_result_t Curl_if2ip(int af,
97 unsigned int remote_scope,
98 unsigned int local_scope_id,
101 char *buf, int buf_size)
103 struct ifaddrs *iface, *head;
104 if2ip_result_t res = IF2IP_NOT_FOUND;
106 #if defined(ENABLE_IPV6) && \
107 !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
108 (void) local_scope_id;
111 if(getifaddrs(&head) >= 0) {
112 for(iface = head; iface != NULL; iface = iface->ifa_next) {
113 if(iface->ifa_addr) {
114 if(iface->ifa_addr->sa_family == af) {
115 if(strcasecompare(iface->ifa_name, interf)) {
122 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
123 unsigned int scopeid = 0;
125 unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr);
127 if(ifscope != remote_scope) {
128 /* We are interested only in interface addresses whose scope
129 matches the remote address we want to connect to: global
130 for global, link-local for link-local, etc... */
131 if(res == IF2IP_NOT_FOUND)
132 res = IF2IP_AF_NOT_SUPPORTED;
137 &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr;
138 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
139 /* Include the scope of this interface as part of the address */
140 scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr)
143 /* If given, scope id should match. */
144 if(local_scope_id && scopeid != local_scope_id) {
145 if(res == IF2IP_NOT_FOUND)
146 res = IF2IP_AF_NOT_SUPPORTED;
152 msnprintf(scope, sizeof(scope), "%%%u", scopeid);
158 &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
160 ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
161 msnprintf(buf, buf_size, "%s%s", ip, scope);
165 else if((res == IF2IP_NOT_FOUND) &&
166 strcasecompare(iface->ifa_name, interf)) {
167 res = IF2IP_AF_NOT_SUPPORTED;
178 #elif defined(HAVE_IOCTL_SIOCGIFADDR)
180 if2ip_result_t Curl_if2ip(int af,
182 unsigned int remote_scope,
183 unsigned int local_scope_id,
186 char *buf, int buf_size)
190 struct sockaddr_in *s;
197 (void)local_scope_id;
200 if(!interf || (af != AF_INET))
201 return IF2IP_NOT_FOUND;
203 len = strlen(interf);
204 if(len >= sizeof(req.ifr_name))
205 return IF2IP_NOT_FOUND;
207 dummy = socket(AF_INET, SOCK_STREAM, 0);
208 if(CURL_SOCKET_BAD == dummy)
209 return IF2IP_NOT_FOUND;
211 memset(&req, 0, sizeof(req));
212 memcpy(req.ifr_name, interf, len + 1);
213 req.ifr_addr.sa_family = AF_INET;
215 if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
217 /* With SIOCGIFADDR, we cannot tell the difference between an interface
218 that does not exist and an interface that has no address of the
219 correct family. Assume the interface does not exist */
220 return IF2IP_NOT_FOUND;
223 s = (struct sockaddr_in *)(void *)&req.ifr_addr;
224 memcpy(&in, &s->sin_addr, sizeof(in));
225 r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
229 return IF2IP_NOT_FOUND;
235 if2ip_result_t Curl_if2ip(int af,
237 unsigned int remote_scope,
238 unsigned int local_scope_id,
241 char *buf, int buf_size)
246 (void) local_scope_id;
251 return IF2IP_NOT_FOUND;