xref: /dragonfly/contrib/dhcpcd/src/common.c (revision 16dd80e4)
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 != '\0') {
144 		/* Skip separators */
145 		c[0] = *p++;
146 		switch (c[0]) {
147 		case '\n':	/* long duid split on lines */
148 		case ':':	/* typical mac address */
149 		case '-':	/* uuid */
150 			continue;
151 		}
152 		c[1] = *p++;
153 		/* Ensure that digits are hex */
154 		if (isxdigit((unsigned char)c[0]) == 0 ||
155 		    isxdigit((unsigned char)c[1]) == 0)
156 		{
157 			errno = EINVAL;
158 			return 0;
159 		}
160 		/* We should have at least two entries 00:01 */
161 		if (len == 0 && *p == '\0') {
162 			errno = EINVAL;
163 			return 0;
164 		}
165 		if (bp)
166 			*bp++ = (uint8_t)strtol(c, NULL, 16);
167 		len++;
168 	}
169 	return len;
170 }
171 
172 size_t
173 read_hwaddr_aton(uint8_t **data, const char *path)
174 {
175 	FILE *fp;
176 	char *buf;
177 	size_t buf_len, len;
178 
179 	if ((fp = fopen(path, "r")) == NULL)
180 		return 0;
181 
182 	buf = NULL;
183 	buf_len = len = 0;
184 	*data = NULL;
185 	while (getline(&buf, &buf_len, fp) != -1) {
186 		if ((len = hwaddr_aton(NULL, buf)) != 0) {
187 			if (buf_len >= len)
188 				*data = (uint8_t *)buf;
189 			else {
190 				if ((*data = malloc(len)) == NULL)
191 					len = 0;
192 			}
193 			if (len != 0)
194 				(void)hwaddr_aton(*data, buf);
195 			if (buf_len < len)
196 				free(buf);
197 			break;
198 		}
199 	}
200 	fclose(fp);
201 	return len;
202 }
203 
204 ssize_t
205 recvmsg_realloc(int fd, struct msghdr *msg, int flags)
206 {
207 	struct iovec *iov;
208 	ssize_t slen;
209 	size_t len;
210 	void *n;
211 
212 	assert(msg != NULL);
213 	assert(msg->msg_iov != NULL && msg->msg_iovlen > 0);
214 	assert((flags & (MSG_PEEK | MSG_TRUNC)) == 0);
215 
216 	/* Assume we are reallocing the last iovec. */
217 	iov = &msg->msg_iov[msg->msg_iovlen - 1];
218 
219 	for (;;) {
220 		/* Passing MSG_TRUNC should return the actual size needed. */
221 		slen = recvmsg(fd, msg, flags | MSG_PEEK | MSG_TRUNC);
222 		if (slen == -1)
223 			return -1;
224 		if (!(msg->msg_flags & MSG_TRUNC))
225 			break;
226 
227 		len = (size_t)slen;
228 
229 		/* Some kernels return the size of the receive buffer
230 		 * on truncation, not the actual size needed.
231 		 * So grow the buffer and try again. */
232 		if (iov->iov_len == len)
233 			len++;
234 		else if (iov->iov_len > len)
235 			break;
236 		len = roundup(len, IOVEC_BUFSIZ);
237 		if ((n = realloc(iov->iov_base, len)) == NULL)
238 			return -1;
239 		iov->iov_base = n;
240 		iov->iov_len = len;
241 	}
242 
243 	slen = recvmsg(fd, msg, flags);
244 	if (slen != -1 && msg->msg_flags & MSG_TRUNC) {
245 		/* This should not be possible ... */
246 		errno = ENOBUFS;
247 		return -1;
248 	}
249 	return slen;
250 }
251