1 /*
2 * Copyright (c) 2010, 2011, 2012, 2013
3 * Inferno Nettverk A/S, Norway. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. The above copyright notice, this list of conditions and the following
9 * disclaimer must appear in all copies of the software, derivative works
10 * or modified versions, and any portions thereof, aswell as in all
11 * supporting documentation.
12 * 2. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by
15 * Inferno Nettverk A/S, Norway.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Inferno Nettverk A/S requests users of this software to return to
31 *
32 * Software Distribution Coordinator or sdc@inet.no
33 * Inferno Nettverk A/S
34 * Oslo Research Park
35 * Gaustadall�en 21
36 * NO-0349 Oslo
37 * Norway
38 *
39 * any improvements or extensions that they make and grant Inferno Nettverk A/S
40 * the rights to redistribute these changes.
41 *
42 */
43
44 static const char rcsid[] =
45 "$Id: iface.c,v 1.23.4.1 2014/08/24 15:16:48 michaels Exp $";
46
47 #include "common.h"
48
49 #if !SOCKS_CLIENT
50
51 #if !HAVE_SIOCGIFHWADDR
52
53 /*
54 * Retrieve ifconfig(8) output for the interface "ifname".
55 * The output is stored in "output", which is of size "outputlen".
56 * Returns 0 on success, -1 on error.
57 */
58 static int
59 get_ifconfig_output(const char *ifname, char *output, size_t outputlen);
60
61 /*
62 * Parses the ifconfig output present in the string "output" for
63 * the macaddress. If found, the macaddress is stored in "addr",
64 * which must be at least ETHER_ADDR_LEN.
65 * Returns 0 if the output was parsed ok, -1 on error.
66 */
67 static int
68 parse_ifconfig_output(const char *input, unsigned char *addr);
69
70 #endif /* !HAVE_SIOCGIFHWADDR */
71
72 #endif /* !SOCKS_CLIENT */
73
74 #undef getifaddrs
75
76 int
socks_getifaddrs(ifap)77 socks_getifaddrs(ifap)
78 struct ifaddrs **ifap;
79 {
80 const char *function = "sockd_getifaddrs()";
81 int rc;
82
83 rc = getifaddrs(ifap);
84
85 #if !SOCKS_CLIENT
86 if (rc != 0) {
87 if (ERRNOISNOFILE(errno) && sockscf.state.reservedfdv[0] != -1) {
88 close(sockscf.state.reservedfdv[0]);
89
90 rc = getifaddrs(ifap);
91
92 sockscf.state.reservedfdv[0] = makedummyfd(0, 0);
93 }
94 }
95 #endif /* !SOCKS_CLIENT */
96
97 return rc;
98 }
99
100 #if !SOCKS_CLIENT
101
102 ipv6_addrscope_t
ipv6_addrscope(a)103 ipv6_addrscope(a)
104 const struct in6_addr *a;
105 {
106 const char *function = "ipv6_addrscope()";
107 ipv6_addrscope_t scope;
108 char ntop[256];
109
110 if (IN6_IS_ADDR_UNSPECIFIED(a) || IN6_IS_ADDR_MC_GLOBAL(a))
111 scope = addrscope_global;
112 else if (IN6_IS_ADDR_LINKLOCAL(a) || IN6_IS_ADDR_MC_LINKLOCAL(a))
113 scope = addrscope_linklocal;
114 else if (IN6_IS_ADDR_LOOPBACK(a) || IN6_IS_ADDR_MC_NODELOCAL(a))
115 scope = addrscope_nodelocal;
116 else
117 /*
118 * If nothing else matched, assume global scope.
119 */
120 scope = addrscope_global;
121
122 if (inet_ntop(AF_INET6, a, ntop, sizeof(ntop)) == NULL)
123 snprintf(ntop, sizeof(ntop), "<%s>", strerror(errno));
124
125 slog(LOG_DEBUG, "%s: address %s, addrscope: %s",
126 function, ntop, addrscope2string(scope));
127
128 return scope;
129 }
130
131 unsigned char *
sockd_getmacaddr(ifname,addr)132 sockd_getmacaddr(ifname, addr)
133 const char *ifname;
134 unsigned char *addr;
135 {
136 const char *function = "sockd_getmacaddr()";
137
138 #if HAVE_SIOCGIFHWADDR
139 struct ifreq ifr;
140 int rc, s;
141
142 slog(LOG_DEBUG, "%s: HAVE_SIOCGIFHWADDR. Ifname %s", function, ifname);
143
144 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
145 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = NUL;
146
147 if ((s = sockscf.state.reservedfdv[0]) == -1)
148 s = sockscf.state.reservedfdv[0] = makedummyfd(0, 0);
149
150 if (s == -1) {
151 swarn("%s: could not create socket", function);
152 return NULL;
153 }
154
155 rc = ioctl(s, SIOCGIFHWADDR, &ifr);
156
157 if (rc != 0) {
158 swarn("%s: ioctl(SIOCGIFHWADDR)", function);
159 return NULL;
160 }
161
162 memcpy(addr, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
163
164 #else /* !HAVE_SIOCGIFHWADDR */
165 char output[1024];
166
167 slog(LOG_DEBUG, "%s: don't have HAVE_SIOCGIFHWADDR, so using ifconfig. "
168 "Ifname %s",
169 function, ifname);
170
171 if (get_ifconfig_output(ifname, output, sizeof(output)) != 0) {
172 slog(LOG_DEBUG,
173 "%s: could not retrieve ifconfig(8) output for interface %s",
174 function, ifname);
175
176 return NULL;
177 }
178
179 if (parse_ifconfig_output(output, addr) != 0) {
180 slog(LOG_DEBUG, "%s: parsing ifconfig output for interface %s failed",
181 function, ifname);
182
183 #if HAVE_SOLARIS_BUGS
184 if (!sockscf.state.haveprivs)
185 swarnx("%s: parsing ifconfig output for interface %s failed. "
186 "Retrieving the hardware address requires the elevated "
187 "privileges on Solaris. Please make sure %s is started "
188 "by root",
189 function, ifname, PRODUCT);
190 #endif /* HAVE_SOLARIS_BUGS */
191
192 return NULL;
193 }
194 #endif /* !SIOCGIFHWADDR */
195
196 slog(LOG_DEBUG,
197 "%s: mac address of interface %s is %02x:%02x:%02x:%02x:%02x:%02x",
198 function,
199 ifname,
200 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
201
202 return addr;
203 }
204
205 #if !HAVE_SIOCGIFHWADDR
206 static int
get_ifconfig_output(ifname,output,outputlen)207 get_ifconfig_output(ifname, output, outputlen)
208 const char *ifname;
209 char *output;
210 size_t outputlen;
211 {
212 const char *function = "get_ifconfig_output()";
213 FILE *fp;
214 size_t i, rc;
215 const char *ifconfig_paths[] = { "ifconfig",
216 "/sbin/ifconfig",
217 "/usr/sbin/ifconfig"
218 };
219
220 for (i = 0; i < ELEMENTS(ifconfig_paths); ++i) {
221 char cmd[256];
222
223 snprintf(cmd, sizeof(cmd), "%s %s 2>/dev/null",
224 ifconfig_paths[i], ifname);
225
226 slog(LOG_DEBUG, "%s: trying %s ... \n", function, cmd);
227
228 /* at least on Solaris we need to be privileged to read the hw addr. */
229 sockd_priv(SOCKD_PRIV_PRIVILEGED, PRIV_ON);
230 fp = popen(cmd, "r");
231 sockd_priv(SOCKD_PRIV_PRIVILEGED, PRIV_OFF);
232
233 if (fp == NULL) {
234 slog(LOG_DEBUG, "%s: popen(%s) failed: %s\n",
235 function, cmd, strerror(errno));
236
237 continue;
238 }
239
240 rc = fread(output, 1, outputlen - 1, fp);
241 output[rc] = NUL;
242 pclose(fp);
243
244 if (rc == 0) {
245 slog(LOG_DEBUG, "%s: popen(%s) failed: %s\n",
246 function, cmd, strerror(errno));
247
248 continue;
249 }
250 else
251 break;
252 }
253
254 if (fp == NULL)
255 return -1;
256
257 return 0;
258 }
259
260 static int
parse_ifconfig_output(input,addr)261 parse_ifconfig_output(input, addr)
262 const char *input;
263 unsigned char *addr;
264 {
265 const char *function = "parse_ifconfig_output()";
266 regex_t preg;
267 regmatch_t pmatch;
268 size_t i, nextbyte;
269
270 if (regcomp(&preg,
271 "[[:space:]|^]"
272 "[0-9a-fA-F]{1,2}:"
273 "[0-9a-fA-F]{1,2}:"
274 "[0-9a-fA-F]{1,2}:"
275 "[0-9a-fA-F]{1,2}:"
276 "[0-9a-fA-F]{1,2}:"
277 "[0-9a-fA-F]{1,2}"
278 "[[:space:]|$]",
279 REG_EXTENDED) != 0)
280 return -1;
281
282 if (regexec(&preg, input, 1, &pmatch, 0) != 0)
283 return -1;
284
285 nextbyte = pmatch.rm_so;
286 while (isspace(input[nextbyte]))
287 ++nextbyte;
288
289 for (i = 0; i < 6; ++i) {
290 char *endptr;
291
292 errno = 0;
293 addr[i] = (unsigned char)strtol(&input[nextbyte], &endptr, 16);
294 nextbyte += (endptr - &input[nextbyte]) + strlen(":");
295
296 /* last byte will not have a ':' after it. */
297 if (i != 5 && *endptr != ':') {
298 swarnx("%s: missing ':' separator in string: %s", function, input);
299 return -1;
300 }
301
302 if (errno == ERANGE) {
303 swarn("%s: out of range in string: %s", function, input);
304 return -1;
305 }
306 }
307
308 return 0;
309 }
310
311 #endif /* !HAVE_SIOCGIFHWADDR */
312
313 #endif /* !SOCKS_CLIENT */
314