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 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 * 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 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