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