1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, 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.haxx.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 ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #ifdef HAVE_NETINET_IN_H
26 # include <netinet/in.h>
27 #endif
28 #ifdef HAVE_ARPA_INET_H
29 # include <arpa/inet.h>
30 #endif
31 #ifdef HAVE_NET_IF_H
32 # include <net/if.h>
33 #endif
34 #ifdef HAVE_SYS_IOCTL_H
35 # include <sys/ioctl.h>
36 #endif
37 #ifdef HAVE_NETDB_H
38 # include <netdb.h>
39 #endif
40 #ifdef HAVE_SYS_SOCKIO_H
41 # include <sys/sockio.h>
42 #endif
43 #ifdef HAVE_IFADDRS_H
44 # include <ifaddrs.h>
45 #endif
46 #ifdef HAVE_STROPTS_H
47 # include <stropts.h>
48 #endif
49 #ifdef __VMS
50 # include <inet.h>
51 #endif
52
53 #include "inet_ntop.h"
54 #include "strcase.h"
55 #include "if2ip.h"
56 /* The last 3 #include files should be in this order */
57 #include "curl_printf.h"
58 #include "curl_memory.h"
59 #include "memdebug.h"
60
61 /* ------------------------------------------------------------------ */
62
63 /* Return the scope of the given address. */
Curl_ipv6_scope(const struct sockaddr * sa)64 unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
65 {
66 #ifndef ENABLE_IPV6
67 (void) sa;
68 #else
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]);
73
74 switch(w & 0xFFC0) {
75 case 0xFE80:
76 return IPV6_SCOPE_LINKLOCAL;
77 case 0xFEC0:
78 return IPV6_SCOPE_SITELOCAL;
79 case 0x0000:
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)
83 break;
84 return IPV6_SCOPE_NODELOCAL;
85 default:
86 break;
87 }
88 }
89 #endif
90
91 return IPV6_SCOPE_GLOBAL;
92 }
93
94
95 #if defined(HAVE_GETIFADDRS)
96
Curl_if_is_interface_name(const char * interf)97 bool Curl_if_is_interface_name(const char *interf)
98 {
99 bool result = FALSE;
100
101 struct ifaddrs *iface, *head;
102
103 if(getifaddrs(&head) >= 0) {
104 for(iface=head; iface != NULL; iface=iface->ifa_next) {
105 if(strcasecompare(iface->ifa_name, interf)) {
106 result = TRUE;
107 break;
108 }
109 }
110 freeifaddrs(head);
111 }
112 return result;
113 }
114
Curl_if2ip(int af,unsigned int remote_scope,unsigned int remote_scope_id,const char * interf,char * buf,int buf_size)115 if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
116 unsigned int remote_scope_id, const char *interf,
117 char *buf, int buf_size)
118 {
119 struct ifaddrs *iface, *head;
120 if2ip_result_t res = IF2IP_NOT_FOUND;
121
122 #ifndef ENABLE_IPV6
123 (void) remote_scope;
124
125 #ifndef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
126 (void) remote_scope_id;
127 #endif
128
129 #endif
130
131 if(getifaddrs(&head) >= 0) {
132 for(iface = head; iface != NULL; iface=iface->ifa_next) {
133 if(iface->ifa_addr != NULL) {
134 if(iface->ifa_addr->sa_family == af) {
135 if(strcasecompare(iface->ifa_name, interf)) {
136 void *addr;
137 char *ip;
138 char scope[12] = "";
139 char ipstr[64];
140 #ifdef ENABLE_IPV6
141 if(af == AF_INET6) {
142 unsigned int scopeid = 0;
143 unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr);
144
145 if(ifscope != remote_scope) {
146 /* We are interested only in interface addresses whose
147 scope matches the remote address we want to
148 connect to: global for global, link-local for
149 link-local, etc... */
150 if(res == IF2IP_NOT_FOUND) res = IF2IP_AF_NOT_SUPPORTED;
151 continue;
152 }
153
154 addr =
155 &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr;
156 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
157 /* Include the scope of this interface as part of the address */
158 scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr)
159 ->sin6_scope_id;
160
161 /* If given, scope id should match. */
162 if(remote_scope_id && scopeid != remote_scope_id) {
163 if(res == IF2IP_NOT_FOUND)
164 res = IF2IP_AF_NOT_SUPPORTED;
165
166 continue;
167 }
168 #endif
169 if(scopeid)
170 snprintf(scope, sizeof(scope), "%%%u", scopeid);
171 }
172 else
173 #endif
174 addr =
175 &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
176 res = IF2IP_FOUND;
177 ip = (char *) Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
178 snprintf(buf, buf_size, "%s%s", ip, scope);
179 break;
180 }
181 }
182 else if((res == IF2IP_NOT_FOUND) &&
183 strcasecompare(iface->ifa_name, interf)) {
184 res = IF2IP_AF_NOT_SUPPORTED;
185 }
186 }
187 }
188
189 freeifaddrs(head);
190 }
191
192 return res;
193 }
194
195 #elif defined(HAVE_IOCTL_SIOCGIFADDR)
196
Curl_if_is_interface_name(const char * interf)197 bool Curl_if_is_interface_name(const char *interf)
198 {
199 /* This is here just to support the old interfaces */
200 char buf[256];
201
202 return (Curl_if2ip(AF_INET, 0 /* unused */, 0, interf, buf, sizeof(buf)) ==
203 IF2IP_NOT_FOUND) ? FALSE : TRUE;
204 }
205
Curl_if2ip(int af,unsigned int remote_scope,unsigned int remote_scope_id,const char * interf,char * buf,int buf_size)206 if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
207 unsigned int remote_scope_id, const char *interf,
208 char *buf, int buf_size)
209 {
210 struct ifreq req;
211 struct in_addr in;
212 struct sockaddr_in *s;
213 curl_socket_t dummy;
214 size_t len;
215
216 (void)remote_scope;
217 (void)remote_scope_id;
218
219 if(!interf || (af != AF_INET))
220 return IF2IP_NOT_FOUND;
221
222 len = strlen(interf);
223 if(len >= sizeof(req.ifr_name))
224 return IF2IP_NOT_FOUND;
225
226 dummy = socket(AF_INET, SOCK_STREAM, 0);
227 if(CURL_SOCKET_BAD == dummy)
228 return IF2IP_NOT_FOUND;
229
230 memset(&req, 0, sizeof(req));
231 memcpy(req.ifr_name, interf, len+1);
232 req.ifr_addr.sa_family = AF_INET;
233
234 if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
235 sclose(dummy);
236 /* With SIOCGIFADDR, we cannot tell the difference between an interface
237 that does not exist and an interface that has no address of the
238 correct family. Assume the interface does not exist */
239 return IF2IP_NOT_FOUND;
240 }
241
242 s = (struct sockaddr_in *)&req.ifr_addr;
243 memcpy(&in, &s->sin_addr, sizeof(in));
244 Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
245
246 sclose(dummy);
247 return IF2IP_FOUND;
248 }
249
250 #else
251
Curl_if_is_interface_name(const char * interf)252 bool Curl_if_is_interface_name(const char *interf)
253 {
254 (void) interf;
255
256 return FALSE;
257 }
258
Curl_if2ip(int af,unsigned int remote_scope,unsigned int remote_scope_id,const char * interf,char * buf,int buf_size)259 if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
260 unsigned int remote_scope_id, const char *interf,
261 char *buf, int buf_size)
262 {
263 (void) af;
264 (void) remote_scope;
265 (void) remote_scope_id;
266 (void) interf;
267 (void) buf;
268 (void) buf_size;
269 return IF2IP_NOT_FOUND;
270 }
271
272 #endif
273