1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2020, 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.haxx.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 /* ------------------------------------------------------------------ */
63 /* Return the scope of the given address. */
64 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;
93 return IPV6_SCOPE_GLOBAL;
97 #if defined(HAVE_GETIFADDRS)
99 if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
100 unsigned int local_scope_id, const char *interf,
101 char *buf, int buf_size)
103 struct ifaddrs *iface, *head;
104 if2ip_result_t res = IF2IP_NOT_FOUND;
110 #if !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) || \
111 !defined(ENABLE_IPV6)
112 (void) local_scope_id;
115 if(getifaddrs(&head) >= 0) {
116 for(iface = head; iface != NULL; iface = iface->ifa_next) {
117 if(iface->ifa_addr != NULL) {
118 if(iface->ifa_addr->sa_family == af) {
119 if(strcasecompare(iface->ifa_name, interf)) {
126 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
127 unsigned int scopeid = 0;
129 unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr);
131 if(ifscope != remote_scope) {
132 /* We are interested only in interface addresses whose scope
133 matches the remote address we want to connect to: global
134 for global, link-local for link-local, etc... */
135 if(res == IF2IP_NOT_FOUND)
136 res = IF2IP_AF_NOT_SUPPORTED;
141 &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr;
142 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
143 /* Include the scope of this interface as part of the address */
144 scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr)
147 /* If given, scope id should match. */
148 if(local_scope_id && scopeid != local_scope_id) {
149 if(res == IF2IP_NOT_FOUND)
150 res = IF2IP_AF_NOT_SUPPORTED;
156 msnprintf(scope, sizeof(scope), "%%%u", scopeid);
162 &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
164 ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
165 msnprintf(buf, buf_size, "%s%s", ip, scope);
169 else if((res == IF2IP_NOT_FOUND) &&
170 strcasecompare(iface->ifa_name, interf)) {
171 res = IF2IP_AF_NOT_SUPPORTED;
182 #elif defined(HAVE_IOCTL_SIOCGIFADDR)
184 if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
185 unsigned int local_scope_id, const char *interf,
186 char *buf, int buf_size)
190 struct sockaddr_in *s;
196 (void)local_scope_id;
198 if(!interf || (af != AF_INET))
199 return IF2IP_NOT_FOUND;
201 len = strlen(interf);
202 if(len >= sizeof(req.ifr_name))
203 return IF2IP_NOT_FOUND;
205 dummy = socket(AF_INET, SOCK_STREAM, 0);
206 if(CURL_SOCKET_BAD == dummy)
207 return IF2IP_NOT_FOUND;
209 memset(&req, 0, sizeof(req));
210 memcpy(req.ifr_name, interf, len + 1);
211 req.ifr_addr.sa_family = AF_INET;
213 if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
215 /* With SIOCGIFADDR, we cannot tell the difference between an interface
216 that does not exist and an interface that has no address of the
217 correct family. Assume the interface does not exist */
218 return IF2IP_NOT_FOUND;
221 s = (struct sockaddr_in *)(void *)&req.ifr_addr;
222 memcpy(&in, &s->sin_addr, sizeof(in));
223 r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
227 return IF2IP_NOT_FOUND;
233 if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
234 unsigned int local_scope_id, const char *interf,
235 char *buf, int buf_size)
239 (void) local_scope_id;
243 return IF2IP_NOT_FOUND;