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 * SPDX-License-Identifier: curl
23 ***************************************************************************/
25 #include "curl_setup.h"
27 #ifdef HAVE_NETINET_IN_H
28 # include <netinet/in.h>
30 #ifdef HAVE_ARPA_INET_H
31 # include <arpa/inet.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 # include <sys/ioctl.h>
42 #ifdef HAVE_SYS_SOCKIO_H
43 # include <sys/sockio.h>
55 #include "inet_ntop.h"
58 /* The last 3 #include files should be in this order */
59 #include "curl_printf.h"
60 #include "curl_memory.h"
63 /* ------------------------------------------------------------------ */
66 /* Return the scope of the given address. */
67 unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
69 if(sa->sa_family == AF_INET6) {
70 const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa;
71 const unsigned char *b = sa6->sin6_addr.s6_addr;
72 unsigned short w = (unsigned short) ((b[0] << 8) | b[1]);
74 if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */
75 return IPV6_SCOPE_UNIQUELOCAL;
78 return IPV6_SCOPE_LINKLOCAL;
80 return IPV6_SCOPE_SITELOCAL;
82 w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] |
83 b[10] | b[11] | b[12] | b[13] | b[14];
84 if(w || b[15] != 0x01)
86 return IPV6_SCOPE_NODELOCAL;
91 return IPV6_SCOPE_GLOBAL;
95 #if defined(HAVE_GETIFADDRS)
97 if2ip_result_t Curl_if2ip(int af,
99 unsigned int remote_scope,
100 unsigned int local_scope_id,
103 char *buf, int buf_size)
105 struct ifaddrs *iface, *head;
106 if2ip_result_t res = IF2IP_NOT_FOUND;
108 #if defined(ENABLE_IPV6) && \
109 !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
110 (void) local_scope_id;
113 if(getifaddrs(&head) >= 0) {
114 for(iface = head; iface != NULL; iface = iface->ifa_next) {
115 if(iface->ifa_addr) {
116 if(iface->ifa_addr->sa_family == af) {
117 if(strcasecompare(iface->ifa_name, interf)) {
124 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
125 unsigned int scopeid = 0;
127 unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr);
129 if(ifscope != remote_scope) {
130 /* We are interested only in interface addresses whose scope
131 matches the remote address we want to connect to: global
132 for global, link-local for link-local, etc... */
133 if(res == IF2IP_NOT_FOUND)
134 res = IF2IP_AF_NOT_SUPPORTED;
139 &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr;
140 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
141 /* Include the scope of this interface as part of the address */
142 scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr)
145 /* If given, scope id should match. */
146 if(local_scope_id && scopeid != local_scope_id) {
147 if(res == IF2IP_NOT_FOUND)
148 res = IF2IP_AF_NOT_SUPPORTED;
154 msnprintf(scope, sizeof(scope), "%%%u", scopeid);
160 &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
162 ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
163 msnprintf(buf, buf_size, "%s%s", ip, scope);
167 else if((res == IF2IP_NOT_FOUND) &&
168 strcasecompare(iface->ifa_name, interf)) {
169 res = IF2IP_AF_NOT_SUPPORTED;
180 #elif defined(HAVE_IOCTL_SIOCGIFADDR)
182 if2ip_result_t Curl_if2ip(int af,
184 unsigned int remote_scope,
185 unsigned int local_scope_id,
188 char *buf, int buf_size)
192 struct sockaddr_in *s;
199 (void)local_scope_id;
202 if(!interf || (af != AF_INET))
203 return IF2IP_NOT_FOUND;
205 len = strlen(interf);
206 if(len >= sizeof(req.ifr_name))
207 return IF2IP_NOT_FOUND;
209 dummy = socket(AF_INET, SOCK_STREAM, 0);
210 if(CURL_SOCKET_BAD == dummy)
211 return IF2IP_NOT_FOUND;
213 memset(&req, 0, sizeof(req));
214 memcpy(req.ifr_name, interf, len + 1);
215 req.ifr_addr.sa_family = AF_INET;
217 if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
219 /* With SIOCGIFADDR, we cannot tell the difference between an interface
220 that does not exist and an interface that has no address of the
221 correct family. Assume the interface does not exist */
222 return IF2IP_NOT_FOUND;
225 s = (struct sockaddr_in *)(void *)&req.ifr_addr;
226 memcpy(&in, &s->sin_addr, sizeof(in));
227 r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
231 return IF2IP_NOT_FOUND;
237 if2ip_result_t Curl_if2ip(int af,
239 unsigned int remote_scope,
240 unsigned int local_scope_id,
243 char *buf, int buf_size)
248 (void) local_scope_id;
253 return IF2IP_NOT_FOUND;