xref: /netbsd-src/external/bsd/dhcpcd/dist/src/common.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
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