Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * SPDX-License-Identifier: BSD-2-Clause
      4  * Copyright (c) 2006-2025 Roy Marples <roy (at) marples.name>
      5  * 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  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/stat.h>
     30 #include <sys/statvfs.h>
     31 
     32 #include <ctype.h>
     33 #include <errno.h>
     34 #include <fcntl.h>
     35 #include <stdbool.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <unistd.h>
     40 
     41 #include "common.h"
     42 #include "eloop.h"
     43 
     44 const char *
     45 hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
     46 {
     47 	const unsigned char *hp, *ep;
     48 	char *p;
     49 
     50 	/* Allow a hwlen of 0 to be an empty string. */
     51 	if (buf == NULL || buflen == 0) {
     52 		errno = ENOBUFS;
     53 		return NULL;
     54 	}
     55 
     56 	if (hwlen * 3 > buflen) {
     57 		/* We should still terminate the string just in case. */
     58 		buf[0] = '\0';
     59 		errno = ENOBUFS;
     60 		return NULL;
     61 	}
     62 
     63 	hp = hwaddr;
     64 	ep = hp + hwlen;
     65 	p = buf;
     66 	while (hp < ep) {
     67 		if (hp != hwaddr)
     68 			*p++ = ':';
     69 		p += snprintf(p, 3, "%.2x", *hp++);
     70 	}
     71 	*p++ = '\0';
     72 	return buf;
     73 }
     74 
     75 size_t
     76 hwaddr_aton(uint8_t *buffer, const char *addr)
     77 {
     78 	char c[3];
     79 	const char *p = addr;
     80 	uint8_t *bp = buffer;
     81 	size_t len = 0;
     82 
     83 	c[2] = '\0';
     84 	while (*p != '\0') {
     85 		/* Skip separators */
     86 		c[0] = *p++;
     87 		switch (c[0]) {
     88 		case '\n': /* long duid split on lines */
     89 		case ':':  /* typical mac address */
     90 		case '-':  /* uuid */
     91 			continue;
     92 		}
     93 		c[1] = *p++;
     94 		/* Ensure that digits are hex */
     95 		if (isxdigit((unsigned char)c[0]) == 0 ||
     96 		    isxdigit((unsigned char)c[1]) == 0) {
     97 			errno = EINVAL;
     98 			return 0;
     99 		}
    100 		/* We should have at least two entries 00:01 */
    101 		if (len == 0 && *p == '\0') {
    102 			errno = EINVAL;
    103 			return 0;
    104 		}
    105 		if (bp)
    106 			*bp++ = (uint8_t)strtol(c, NULL, 16);
    107 		len++;
    108 	}
    109 	return len;
    110 }
    111 
    112 ssize_t
    113 readfile(const char *file, void **data, size_t *len)
    114 {
    115 	int fd;
    116 	ssize_t bytes = -1;
    117 	struct stat st;
    118 	size_t nlen;
    119 	char *buf;
    120 
    121 	fd = open(file, O_RDONLY);
    122 	if (fd == -1)
    123 		return -1;
    124 	if (fstat(fd, &st) == -1)
    125 		goto out;
    126 
    127 	/* Ensure that what we read is NUL terminated
    128 	 * because it's mainly text files and this just
    129 	 * makes it easier. */
    130 	nlen = (size_t)st.st_size + 1;
    131 	if (nlen > *len) {
    132 		void *ndata = realloc(*data, nlen);
    133 		if (ndata == NULL)
    134 			goto out;
    135 		*data = ndata;
    136 		*len = nlen;
    137 	}
    138 	bytes = read(fd, *data, *len - 1);
    139 
    140 out:
    141 	close(fd);
    142 	if (bytes == -1)
    143 		return -1;
    144 	buf = *data;
    145 	buf[bytes] = '\0';
    146 	return bytes;
    147 }
    148 
    149 ssize_t
    150 writefile(const char *file, mode_t mode, const void *data, size_t len)
    151 {
    152 	int fd;
    153 	ssize_t bytes;
    154 
    155 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
    156 	if (fd == -1)
    157 		return -1;
    158 	bytes = write(fd, data, len);
    159 	close(fd);
    160 	return bytes;
    161 }
    162 
    163 int
    164 filemtime(const char *file, time_t *time)
    165 {
    166 	struct stat st;
    167 
    168 	if (stat(file, &st) == -1)
    169 		return -1;
    170 	*time = st.st_mtime;
    171 	return 0;
    172 }
    173 
    174 /* Handy routine to read very long lines in text files.
    175  * This means we read the whole line and avoid any nasty buffer overflows.
    176  * We strip leading space and avoid comment lines, making the code that calls
    177  * us smaller. */
    178 char *
    179 get_line(char **__restrict buf, size_t *__restrict buflen)
    180 {
    181 	char *p, *c;
    182 	bool quoted;
    183 
    184 	do {
    185 		p = *buf;
    186 		if (*buf == NULL)
    187 			return NULL;
    188 		c = memchr(*buf, '\n', *buflen);
    189 		if (c == NULL) {
    190 			c = memchr(*buf, '\0', *buflen);
    191 			if (c == NULL)
    192 				return NULL;
    193 			*buflen = (size_t)(c - *buf);
    194 			*buf = NULL;
    195 		} else {
    196 			*c++ = '\0';
    197 			*buflen -= (size_t)(c - *buf);
    198 			*buf = c;
    199 		}
    200 		for (; *p == ' ' || *p == '\t'; p++)
    201 			;
    202 	} while (*p == '\0' || *p == '\n' || *p == '#' || *p == ';');
    203 
    204 	/* Strip embedded comments unless in a quoted string or escaped */
    205 	quoted = false;
    206 	for (c = p; *c != '\0'; c++) {
    207 		if (*c == '\\') {
    208 			c++; /* escaped */
    209 			continue;
    210 		}
    211 		if (*c == '"')
    212 			quoted = !quoted;
    213 		else if (*c == '#' && !quoted) {
    214 			*c = '\0';
    215 			break;
    216 		}
    217 	}
    218 	return p;
    219 }
    220 
    221 int
    222 is_root_local(void)
    223 {
    224 #ifdef ST_LOCAL
    225 	struct statvfs vfs;
    226 
    227 	if (statvfs("/", &vfs) == -1)
    228 		return -1;
    229 	return vfs.f_flag & ST_LOCAL ? 1 : 0;
    230 #else
    231 	errno = ENOTSUP;
    232 	return -1;
    233 #endif
    234 }
    235 
    236 uint32_t
    237 lifetime_left(uint32_t lifetime, const struct timespec *acquired,
    238     struct timespec *now)
    239 {
    240 	uint32_t elapsed;
    241 	struct timespec n;
    242 
    243 	if (lifetime == INFINITE_LIFETIME)
    244 		return lifetime;
    245 
    246 	if (now == NULL) {
    247 		timespecclear(&n);
    248 		now = &n;
    249 	}
    250 	if (!timespecisset(now))
    251 		clock_gettime(CLOCK_MONOTONIC, now);
    252 
    253 	elapsed = (uint32_t)eloop_timespec_diff(now, acquired, NULL);
    254 	if (elapsed > lifetime)
    255 		return 0;
    256 
    257 	return lifetime - elapsed;
    258 }
    259