1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/types.h>
33 #include <sys/socket.h>
34
35 #include <net/if.h>
36 #include <net/if_dl.h>
37
38 #include <assert.h>
39 #include <errno.h>
40 #include <stdint.h>
41 #include <string.h>
42
43 int
link_addr(const char * addr,struct sockaddr_dl * sdl)44 link_addr(const char *addr, struct sockaddr_dl *sdl)
45 {
46 char *cp = sdl->sdl_data;
47 char *cplim = sdl->sdl_len + (char *)sdl;
48 const char *nptr;
49 size_t newsize;
50 int error = 0;
51 char delim = 0;
52
53 /* Initialise the sdl to zero, except for sdl_len. */
54 bzero((char *)&sdl->sdl_family, sdl->sdl_len - 1);
55 sdl->sdl_family = AF_LINK;
56
57 /*
58 * Everything up to the first ':' is the interface name. Usually the
59 * ':' should always be present even if there's no interface name, but
60 * since this interface was poorly specified in the past, accept a
61 * missing colon as meaning no interface name.
62 */
63 if ((nptr = strchr(addr, ':')) != NULL) {
64 size_t namelen = nptr - addr;
65
66 /* Ensure the sdl is large enough to store the name. */
67 if (namelen > cplim - cp) {
68 errno = ENOSPC;
69 return (-1);
70 }
71
72 memcpy(cp, addr, namelen);
73 cp += namelen;
74 sdl->sdl_nlen = namelen;
75 /* Skip the interface name and the colon. */
76 addr += namelen + 1;
77 }
78
79 /*
80 * The remainder of the string should be hex digits representing the
81 * address, with optional delimiters. Each two hex digits form one
82 * octet, but octet output can be forced using a delimiter, so we accept
83 * a long string of hex digits, or a mix of delimited and undelimited
84 * digits like "1122.3344.5566", or delimited one- or two-digit octets
85 * like "1.22.3".
86 *
87 * If anything fails at this point, exit the loop so we set sdl_alen and
88 * sdl_len based on whatever we did manage to parse. This preserves
89 * compatibility with the 4.3BSD version of link_addr, which had no way
90 * to indicate an error and would just return.
91 */
92 #define DIGIT(c) \
93 (((c) >= '0' && (c) <= '9') ? ((c) - '0') \
94 : ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) \
95 : ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) \
96 : (-1))
97 #define ISDELIM(c) (((c) == '.' || (c) == ':' || (c) == '-') && \
98 (delim == 0 || delim == (c)))
99
100 for (;;) {
101 int digit, digit2;
102
103 /*
104 * Treat any leading delimiters as empty bytes. This supports
105 * the (somewhat obsolete) form of Ethernet addresses with empty
106 * octets, e.g. "1::3:4:5:6".
107 */
108 while (ISDELIM(*addr) && cp < cplim) {
109 delim = *addr++;
110 *cp++ = 0;
111 }
112
113 /* Did we reach the end of the string? */
114 if (*addr == '\0')
115 break;
116
117 /*
118 * If not, the next character must be a digit, so make sure we
119 * have room for at least one more octet.
120 */
121
122 if (cp >= cplim) {
123 error = ENOSPC;
124 break;
125 }
126
127 if ((digit = DIGIT(*addr)) == -1) {
128 error = EINVAL;
129 break;
130 }
131
132 ++addr;
133
134 /* If the next character is another digit, consume it. */
135 if ((digit2 = DIGIT(*addr)) != -1) {
136 digit = (digit << 4) | digit2;
137 ++addr;
138 }
139
140 if (ISDELIM(*addr)) {
141 /*
142 * If the digit is followed by a delimiter, write it
143 * and consume the delimiter.
144 */
145 delim = *addr++;
146 *cp++ = digit;
147 } else if (DIGIT(*addr) != -1) {
148 /*
149 * If two digits are followed by a third digit, treat
150 * the two digits we have as a single octet and
151 * continue.
152 */
153 *cp++ = digit;
154 } else if (*addr == '\0') {
155 /* If the digit is followed by EOS, we're done. */
156 *cp++ = digit;
157 break;
158 } else {
159 /* Otherwise, the input was invalid. */
160 error = EINVAL;
161 break;
162 }
163 }
164 #undef DIGIT
165 #undef ISDELIM
166
167 /* How many bytes did we write to the address? */
168 sdl->sdl_alen = cp - LLADDR(sdl);
169
170 /*
171 * The user might have given us an sdl which is larger than sizeof(sdl);
172 * in that case, record the actual size of the new sdl.
173 */
174 newsize = cp - (char *)sdl;
175 if (newsize > sizeof(*sdl))
176 sdl->sdl_len = (u_char)newsize;
177
178 if (error == 0)
179 return (0);
180
181 errno = error;
182 return (-1);
183 }
184
185
186 char *
link_ntoa(const struct sockaddr_dl * sdl)187 link_ntoa(const struct sockaddr_dl *sdl)
188 {
189 static char obuf[64];
190 size_t buflen;
191 _Static_assert(sizeof(obuf) >= IFNAMSIZ + 20, "obuf is too small");
192
193 /*
194 * Ignoring the return value of link_ntoa_r() is safe here because it
195 * always writes the terminating NUL. This preserves the traditional
196 * behaviour of link_ntoa().
197 */
198 buflen = sizeof(obuf);
199 (void)link_ntoa_r(sdl, obuf, &buflen);
200 return obuf;
201 }
202
203 int
link_ntoa_r(const struct sockaddr_dl * sdl,char * obuf,size_t * buflen)204 link_ntoa_r(const struct sockaddr_dl *sdl, char *obuf, size_t *buflen)
205 {
206 static const char hexlist[] = "0123456789abcdef";
207 char *out;
208 const u_char *in, *inlim;
209 int namelen, i, rem;
210 size_t needed;
211
212 assert(sdl);
213 assert(buflen);
214 /* obuf may be null */
215
216 needed = 1; /* 1 for the NUL */
217 out = obuf;
218 if (obuf)
219 rem = *buflen;
220 else
221 rem = 0;
222
223 /*
224 * Check if at least n bytes are available in the output buffer, plus 1 for the
225 * trailing NUL. If not, set rem = 0 so we stop writing.
226 * Either way, increment needed by the amount we would have written.
227 */
228 #define CHECK(n) do { \
229 if ((SIZE_MAX - (n)) >= needed) \
230 needed += (n); \
231 if (rem >= ((n) + 1)) \
232 rem -= (n); \
233 else \
234 rem = 0; \
235 } while (0)
236
237 /*
238 * Write the char c to the output buffer, unless the buffer is full.
239 * Note that if obuf is NULL, rem is always zero.
240 */
241 #define OUT(c) do { \
242 if (rem > 0) \
243 *out++ = (c); \
244 } while (0)
245
246 namelen = (sdl->sdl_nlen <= IFNAMSIZ) ? sdl->sdl_nlen : IFNAMSIZ;
247 if (namelen > 0) {
248 CHECK(namelen);
249 if (rem > 0) {
250 bcopy(sdl->sdl_data, out, namelen);
251 out += namelen;
252 }
253
254 if (sdl->sdl_alen > 0) {
255 CHECK(1);
256 OUT(':');
257 }
258 }
259
260 in = (const u_char *)LLADDR(sdl);
261 inlim = in + sdl->sdl_alen;
262
263 while (in < inlim) {
264 if (in != (const u_char *)LLADDR(sdl)) {
265 CHECK(1);
266 OUT('.');
267 }
268 i = *in++;
269 if (i > 0xf) {
270 CHECK(2);
271 OUT(hexlist[i >> 4]);
272 OUT(hexlist[i & 0xf]);
273 } else {
274 CHECK(1);
275 OUT(hexlist[i]);
276 }
277 }
278
279 #undef CHECK
280 #undef OUT
281
282 /*
283 * We always leave enough room for the NUL if possible, but the user
284 * might have passed a NULL or zero-length buffer.
285 */
286 if (out && *buflen)
287 *out = '\0';
288
289 *buflen = needed;
290 return ((rem > 0) ? 0 : -1);
291 }
292