1 /* 2 * dhcpcd - DHCP client daemon 3 * Copyright (c) 2006-2018 Roy Marples <roy@marples.name> 4 * All rights reserved 5 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/time.h> 30 #ifdef __sun 31 #include <sys/sysmacros.h> 32 #endif 33 34 #include <assert.h> 35 #include <ctype.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <limits.h> 40 #ifdef BSD 41 # include <paths.h> 42 #endif 43 #include <stdarg.h> 44 #include <stdint.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <time.h> 49 #include <unistd.h> 50 51 #include "common.h" 52 #include "dhcpcd.h" 53 #include "if-options.h" 54 #include "logerr.h" 55 56 /* Most route(4) messages are less than 256 bytes. */ 57 #define IOVEC_BUFSIZ 256 58 59 ssize_t 60 setvar(char **e, const char *prefix, const char *var, const char *value) 61 { 62 size_t len = strlen(var) + strlen(value) + 3; 63 64 if (prefix) 65 len += strlen(prefix) + 1; 66 if ((*e = malloc(len)) == NULL) { 67 logerr(__func__); 68 return -1; 69 } 70 if (prefix) 71 snprintf(*e, len, "%s_%s=%s", prefix, var, value); 72 else 73 snprintf(*e, len, "%s=%s", var, value); 74 return (ssize_t)len; 75 } 76 77 ssize_t 78 setvard(char **e, const char *prefix, const char *var, size_t value) 79 { 80 81 char buffer[32]; 82 83 snprintf(buffer, sizeof(buffer), "%zu", value); 84 return setvar(e, prefix, var, buffer); 85 } 86 87 ssize_t 88 addvar(char ***e, const char *prefix, const char *var, const char *value) 89 { 90 ssize_t len; 91 92 len = setvar(*e, prefix, var, value); 93 if (len != -1) 94 (*e)++; 95 return (ssize_t)len; 96 } 97 98 ssize_t 99 addvard(char ***e, const char *prefix, const char *var, size_t value) 100 { 101 char buffer[32]; 102 103 snprintf(buffer, sizeof(buffer), "%zu", value); 104 return addvar(e, prefix, var, buffer); 105 } 106 107 const char * 108 hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen) 109 { 110 const unsigned char *hp, *ep; 111 char *p; 112 113 if (buf == NULL) 114 return NULL; 115 116 if (hwlen * 3 > buflen) { 117 errno = ENOBUFS; 118 return NULL; 119 } 120 121 hp = hwaddr; 122 ep = hp + hwlen; 123 p = buf; 124 125 while (hp < ep) { 126 if (hp != hwaddr) 127 *p ++= ':'; 128 p += snprintf(p, 3, "%.2x", *hp++); 129 } 130 *p ++= '\0'; 131 return buf; 132 } 133 134 size_t 135 hwaddr_aton(uint8_t *buffer, const char *addr) 136 { 137 char c[3]; 138 const char *p = addr; 139 uint8_t *bp = buffer; 140 size_t len = 0; 141 142 c[2] = '\0'; 143 while (*p) { 144 c[0] = *p++; 145 if (c[0] == '\n') 146 continue; 147 c[1] = *p++; 148 /* Ensure that digits are hex */ 149 if (isxdigit((unsigned char)c[0]) == 0 || 150 isxdigit((unsigned char)c[1]) == 0) 151 { 152 errno = EINVAL; 153 return 0; 154 } 155 /* We should have at least two entries 00:01 */ 156 if (len == 0 && *p == '\0') { 157 errno = EINVAL; 158 return 0; 159 } 160 /* Ensure that next data is EOL or a seperator with data */ 161 if (!(*p == '\0' || *p == '\n' || 162 (*p == ':' && *(p + 1) != '\0'))) 163 { 164 errno = EINVAL; 165 return 0; 166 } 167 if (*p) 168 p++; 169 if (bp) 170 *bp++ = (uint8_t)strtol(c, NULL, 16); 171 len++; 172 } 173 return len; 174 } 175 176 size_t 177 read_hwaddr_aton(uint8_t **data, const char *path) 178 { 179 FILE *fp; 180 char *buf; 181 size_t buf_len, len; 182 183 if ((fp = fopen(path, "r")) == NULL) 184 return 0; 185 186 buf = NULL; 187 buf_len = len = 0; 188 *data = NULL; 189 while (getline(&buf, &buf_len, fp) != -1) { 190 if ((len = hwaddr_aton(NULL, buf)) != 0) { 191 if (buf_len >= len) 192 *data = (uint8_t *)buf; 193 else { 194 if ((*data = malloc(len)) == NULL) 195 len = 0; 196 } 197 if (len != 0) 198 (void)hwaddr_aton(*data, buf); 199 if (buf_len < len) 200 free(buf); 201 break; 202 } 203 } 204 fclose(fp); 205 return len; 206 } 207 208 ssize_t 209 recvmsg_realloc(int fd, struct msghdr *msg, int flags) 210 { 211 struct iovec *iov; 212 ssize_t slen; 213 size_t len; 214 void *n; 215 216 assert(msg != NULL); 217 assert(msg->msg_iov != NULL && msg->msg_iovlen > 0); 218 assert((flags & (MSG_PEEK | MSG_TRUNC)) == 0); 219 220 /* Assume we are reallocing the last iovec. */ 221 iov = &msg->msg_iov[msg->msg_iovlen - 1]; 222 223 for (;;) { 224 /* Passing MSG_TRUNC should return the actual size needed. */ 225 slen = recvmsg(fd, msg, flags | MSG_PEEK | MSG_TRUNC); 226 if (slen == -1) 227 return -1; 228 if (!(msg->msg_flags & MSG_TRUNC)) 229 break; 230 231 len = (size_t)slen; 232 233 /* Some kernels return the size of the receive buffer 234 * on truncation, not the actual size needed. 235 * So grow the buffer and try again. */ 236 if (iov->iov_len == len) 237 len++; 238 else if (iov->iov_len > len) 239 break; 240 len = roundup(len, IOVEC_BUFSIZ); 241 if ((n = realloc(iov->iov_base, len)) == NULL) 242 return -1; 243 iov->iov_base = n; 244 iov->iov_len = len; 245 } 246 247 slen = recvmsg(fd, msg, flags); 248 if (slen != -1 && msg->msg_flags & MSG_TRUNC) { 249 /* This should not be possible ... */ 250 errno = ENOBUFS; 251 return -1; 252 } 253 return slen; 254 } 255