Mercurial > hg > dhcpcd
changeset 4535:f2ddefe6c69e draft
script: Write variables to a FILE
Each variable is NULL terminated inside the file just like the
control stream which saves us from having to flatten it when
writing to the control stream.
Once written, create env pointers to the start of each string
just after the NULL terminator.
This also means that we just need to free two buffers when
dhcpcd exits (FILE buffer and env buffer) rather than each variable
individually.
If open_memstream(3) is not supported by libc then dhcpcd will
write to a file in /tmp instead.
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Tue, 18 Jun 2019 11:33:53 +0100 |
| parents | 003dc9907bba |
| children | 37f793c7948e |
| files | configure src/arp.c src/auth.c src/common.c src/common.h src/dhcp-common.c src/dhcp-common.h src/dhcp.c src/dhcp.h src/dhcp6.c src/dhcp6.h src/dhcpcd.c src/dhcpcd.h src/ipv4.c src/ipv4ll.c src/ipv4ll.h src/ipv6.c src/ipv6.h src/ipv6nd.c src/ipv6nd.h src/script.c src/script.h |
| diffstat | 22 files changed, 547 insertions(+), 942 deletions(-) [+] |
line wrap: on
line diff
--- a/configure Fri Jun 14 17:05:24 2019 +0100 +++ b/configure Tue Jun 18 11:33:53 2019 +0100 @@ -15,6 +15,7 @@ CLOSEFROM= RBTREE= CONSTTIME_MEMEQUAL= +OPEN_MEMSTREAM= STRLCPY= UDEV= OS= @@ -741,29 +742,25 @@ echo "#include \"compat/arc4random_uniform.h\"" >>$CONFIG_H fi - -if [ -z "$STRLCPY" ]; then - printf "Testing for strlcpy ... " - cat <<EOF >_strlcpy.c -#include <string.h> +if [ -z "$OPEN_MEMSTREAM" ]; then + printf "Testing for open_memstream ... " + cat <<EOF >_open_memstream.c +#include <stdio.h> int main(void) { - const char s1[] = "foo"; - char s2[10]; - strlcpy(s2, s1, sizeof(s2)); + open_memstream(NULL, NULL); return 0; } EOF - if $XCC _strlcpy.c -o _strlcpy 2>&3; then - STRLCPY=yes + if $XCC _open_memstream.c -o _open_memstream 2>&3; then + OPEN_MEMSTREAM=yes else - STRLCPY=no + OPEN_MEMSTREAM=no fi - echo "$STRLCPY" - rm -f _strlcpy.c _strlcpy + echo "$OPEN_MEMSTREAM" + rm -f _open_memstream.c _open_memstream fi -if [ "$STRLCPY" = no ]; then - echo "COMPAT_SRCS+= compat/strlcpy.c" >>$CONFIG_MK - echo "#include \"compat/strlcpy.h\"" >>$CONFIG_H +if [ "$OPEN_MEMSTREAM" = yes ]; then + echo "#define HAVE_OPEN_MEMSTREAM" >>$CONFIG_H fi if [ -z "$PIDFILE_LOCK" ]; then @@ -824,6 +821,30 @@ echo "#define HAVE_SETPROCTITLE" >>$CONFIG_H fi +if [ -z "$STRLCPY" ]; then + printf "Testing for strlcpy ... " + cat <<EOF >_strlcpy.c +#include <string.h> +int main(void) { + const char s1[] = "foo"; + char s2[10]; + strlcpy(s2, s1, sizeof(s2)); + return 0; +} +EOF + if $XCC _strlcpy.c -o _strlcpy 2>&3; then + STRLCPY=yes + else + STRLCPY=no + fi + echo "$STRLCPY" + rm -f _strlcpy.c _strlcpy +fi +if [ "$STRLCPY" = no ]; then + echo "COMPAT_SRCS+= compat/strlcpy.c" >>$CONFIG_MK + echo "#include \"compat/strlcpy.h\"" >>$CONFIG_H +fi + if [ -z "$STRTOI" ]; then printf "Testing for strtoi ... " cat <<EOF >_strtoi.c
--- a/src/arp.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/arp.c Tue Jun 18 11:33:53 2019 +0100 @@ -36,6 +36,7 @@ #include <errno.h> #include <stdlib.h> +#include <stdio.h> #include <string.h> #include <unistd.h>
--- a/src/auth.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/auth.c Tue Jun 18 11:33:53 2019 +0100 @@ -30,6 +30,7 @@ #include <fcntl.h> #include <inttypes.h> #include <stddef.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h>
--- a/src/common.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/common.c Tue Jun 18 11:33:53 2019 +0100 @@ -56,54 +56,6 @@ /* Most route(4) messages are less than 256 bytes. */ #define IOVEC_BUFSIZ 256 -ssize_t -setvar(char **e, const char *prefix, const char *var, const char *value) -{ - size_t len = strlen(var) + strlen(value) + 3; - - if (prefix) - len += strlen(prefix) + 1; - if ((*e = malloc(len)) == NULL) { - logerr(__func__); - return -1; - } - if (prefix) - snprintf(*e, len, "%s_%s=%s", prefix, var, value); - else - snprintf(*e, len, "%s=%s", var, value); - return (ssize_t)len; -} - -ssize_t -setvard(char **e, const char *prefix, const char *var, size_t value) -{ - - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%zu", value); - return setvar(e, prefix, var, buffer); -} - -ssize_t -addvar(char ***e, const char *prefix, const char *var, const char *value) -{ - ssize_t len; - - len = setvar(*e, prefix, var, value); - if (len != -1) - (*e)++; - return (ssize_t)len; -} - -ssize_t -addvard(char ***e, const char *prefix, const char *var, size_t value) -{ - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%zu", value); - return addvar(e, prefix, var, buffer); -} - const char * hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen) {
--- a/src/common.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/common.h Tue Jun 18 11:33:53 2019 +0100 @@ -193,11 +193,6 @@ extern int clock_monotonic; int get_monotonic(struct timespec *); -ssize_t setvar(char **, const char *, const char *, const char *); -ssize_t setvard(char **, const char *, const char *, size_t); -ssize_t addvar(char ***, const char *, const char *, const char *); -ssize_t addvard(char ***, const char *, const char *, size_t); - const char *hwaddr_ntoa(const void *, size_t, char *, size_t); size_t hwaddr_aton(uint8_t *, const char *); size_t read_hwaddr_aton(uint8_t **, const char *);
--- a/src/dhcp-common.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcp-common.c Tue Jun 18 11:33:53 2019 +0100 @@ -36,8 +36,6 @@ #include <string.h> #include <unistd.h> -#include <arpa/nameser.h> /* after normal includes for sunos */ - #include "config.h" #include "common.h" @@ -46,13 +44,7 @@ #include "if.h" #include "ipv6.h" #include "logerr.h" - -/* Support very old arpa/nameser.h as found in OpenBSD */ -#ifndef NS_MAXDNAME -#define NS_MAXCDNAME MAXCDNAME -#define NS_MAXDNAME MAXDNAME -#define NS_MAXLABEL MAXLABEL -#endif +#include "script.h" const char * dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo) @@ -624,41 +616,9 @@ return (ssize_t)sz; } -/* It's possible for DHCPv4 to contain an IPv6 address */ static ssize_t -ipv6_printaddr(char *s, size_t sl, const uint8_t *d, const char *ifname) -{ - char buf[INET6_ADDRSTRLEN]; - const char *p; - size_t l; - - p = inet_ntop(AF_INET6, d, buf, sizeof(buf)); - if (p == NULL) - return -1; - - l = strlen(p); - if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) - l += 1 + strlen(ifname); - - if (s == NULL) - return (ssize_t)l; - - if (sl < l) { - errno = ENOMEM; - return -1; - } - - s += strlcpy(s, p, sl); - if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) { - *s++ = '%'; - s += strlcpy(s, ifname, sl); - } - *s = '\0'; - return (ssize_t)l; -} - -static ssize_t -print_option(char *s, size_t len, const struct dhcp_opt *opt, +print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt, + int vname, const uint8_t *data, size_t dl, const char *ifname) { const uint8_t *e, *t; @@ -669,46 +629,53 @@ struct in_addr addr; ssize_t bytes = 0, sl; size_t l; -#ifdef INET - char *tmp; -#endif + + /* Ensure a valid length */ + dl = (size_t)dhcp_optlen(opt, dl); + if ((ssize_t)dl == -1) + return 0; + + if (fprintf(fp, "%s", prefix) == -1) + return -1; + if (vname) { + if (fprintf(fp, "_%s", opt->var) == -1) + return -1; + } + if (fputc('=', fp) == EOF) + return -1; + if (dl == 0) + return 1; if (opt->type & OT_RFC1035) { - sl = decode_rfc1035(s, len, data, dl); + char domain[NS_MAXDNAME]; + + sl = decode_rfc1035(domain, sizeof(domain), data, dl); if (sl == 0 || sl == -1) return sl; - if (s != NULL) { - if (valid_domainname(s, opt->type) == -1) - return -1; - } - return sl; + if (valid_domainname(domain, opt->type) == -1) + return -1; + return efprintf(fp, "%s", domain); } #ifdef INET - if (opt->type & OT_RFC3361) { - if ((tmp = decode_rfc3361(data, dl)) == NULL) - return -1; - l = strlen(tmp); - sl = print_string(s, len, opt->type, (uint8_t *)tmp, l); - free(tmp); - return sl; - } + if (opt->type & OT_RFC3361) + return print_rfc3361(fp, data, dl); if (opt->type & OT_RFC3442) - return decode_rfc3442(s, len, data, dl); + return print_rfc3442(fp, data, dl); #endif - if (opt->type & OT_STRING) - return print_string(s, len, opt->type, data, dl); + if (opt->type & OT_STRING) { + char buf[1024]; - if (opt->type & OT_FLAG) { - if (s) { - *s++ = '1'; - *s = '\0'; - } - return 1; + if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1) + return -1; + return efprintf(fp, "%s", buf); } + if (opt->type & OT_FLAG) + return efprintf(fp, "1"); + if (opt->type & OT_BITFLAG) { /* bitflags are a string, MSB first, such as ABCDEFGH * where A is 10000000, B is 01000000, etc. */ @@ -722,109 +689,80 @@ opt->bitflags[l] != '0' && *data & (1 << sl)) { - if (s) - *s++ = opt->bitflags[l]; - bytes++; + if (fputc(opt->bitflags[l], fp) == EOF) + return -1; } } - if (s) - *s = '\0'; - return bytes; - } - - if (!s) { - if (opt->type & OT_UINT8) - l = 3; - else if (opt->type & OT_INT8) - l = 4; - else if (opt->type & OT_UINT16) { - l = 5; - dl /= 2; - } else if (opt->type & OT_INT16) { - l = 6; - dl /= 2; - } else if (opt->type & OT_UINT32) { - l = 10; - dl /= 4; - } else if (opt->type & OT_INT32) { - l = 11; - dl /= 4; - } else if (opt->type & OT_ADDRIPV4) { - l = 16; - dl /= 4; - } else if (opt->type & OT_ADDRIPV6) { - e = data + dl; - l = 0; - while (data < e) { - if (l) - l++; /* space */ - sl = ipv6_printaddr(NULL, 0, data, ifname); - if (sl == -1) - return l == 0 ? -1 : (ssize_t)l; - l += (size_t)sl; - data += 16; - } - return (ssize_t)l; - } else { - errno = EINVAL; + if (fputc('\0', fp) == EOF) return -1; - } - return (ssize_t)(l * dl); + return 1; } t = data; e = data + dl; while (data < e) { if (data != t) { - *s++ = ' '; - bytes++; - len--; + if (fputc(' ', fp) == EOF) + return -1; } if (opt->type & OT_UINT8) { - sl = snprintf(s, len, "%u", *data); + if (fprintf(fp, "%u", *data) == -1) + return -1; data++; } else if (opt->type & OT_INT8) { - sl = snprintf(s, len, "%d", *data); + if (fprintf(fp, "%d", *data) == -1) + return -1; data++; } else if (opt->type & OT_UINT16) { memcpy(&u16, data, sizeof(u16)); u16 = ntohs(u16); - sl = snprintf(s, len, "%u", u16); + if (fprintf(fp, "%u", u16) == -1) + return -1; data += sizeof(u16); } else if (opt->type & OT_INT16) { memcpy(&u16, data, sizeof(u16)); s16 = (int16_t)ntohs(u16); - sl = snprintf(s, len, "%d", s16); + if (fprintf(fp, "%d", s16) == -1) + return -1; data += sizeof(u16); } else if (opt->type & OT_UINT32) { memcpy(&u32, data, sizeof(u32)); u32 = ntohl(u32); - sl = snprintf(s, len, "%u", u32); + if (fprintf(fp, "%u", u32) == -1) + return -1; data += sizeof(u32); } else if (opt->type & OT_INT32) { memcpy(&u32, data, sizeof(u32)); s32 = (int32_t)ntohl(u32); - sl = snprintf(s, len, "%d", s32); + if (fprintf(fp, "%d", s32) == -1) + return -1; data += sizeof(u32); } else if (opt->type & OT_ADDRIPV4) { memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); - sl = snprintf(s, len, "%s", inet_ntoa(addr)); + if (fprintf(fp, "%s", inet_ntoa(addr)) == -1) + return -1; data += sizeof(addr.s_addr); } else if (opt->type & OT_ADDRIPV6) { - sl = ipv6_printaddr(s, len, data, ifname); + char buf[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL) + return -1; + if (fprintf(fp, "%s", buf) == -1) + return -1; + if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) { + if (fprintf(fp,"%%%s", ifname) == -1) + return -1; + } data += 16; } else { errno = EINVAL; return -1; } - if (sl == -1) - return bytes == 0 ? -1 : bytes; - len -= (size_t)sl; - bytes += sl; - s += sl; } - return bytes; + if (fputc('\0', fp) == EOF) + return -1; + return 1; } int @@ -859,61 +797,15 @@ ifp->name, ssid); } -static size_t -dhcp_envoption1(char **env, const char *prefix, - const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol, - const char *ifname) -{ - ssize_t len; - size_t e; - char *v, *val; - int r; - - /* Ensure a valid length */ - ol = (size_t)dhcp_optlen(opt, ol); - if ((ssize_t)ol == -1) - return 0; - - len = print_option(NULL, 0, opt, od, ol, ifname); - if (len < 0) - return 0; - if (vname) - e = strlen(opt->var) + 1; - else - e = 0; - if (prefix) - e += strlen(prefix); - e += (size_t)len + 2; - if (env == NULL) - return e; - v = val = *env = malloc(e); - if (v == NULL) - return 0; - if (vname) - r = snprintf(val, e, "%s_%s=", prefix, opt->var); - else - r = snprintf(val, e, "%s=", prefix); - if (r != -1 && len != 0) { - v += r; - if (print_option(v, (size_t)len + 1, opt, od, ol, ifname) == -1) - r = -1; - } - if (r == -1) { - free(val); - return 0; - } - return e; -} - -size_t -dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, +void +dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix, const char *ifname, struct dhcp_opt *opt, const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, size_t *, unsigned int *, size_t *, const uint8_t *, size_t, struct dhcp_opt **), const uint8_t *od, size_t ol) { - size_t e, i, n, eos, eol; + size_t i, eos, eol; ssize_t eo; unsigned int eoc; const uint8_t *eod; @@ -923,52 +815,36 @@ /* If no embedded or encapsulated options, it's easy */ if (opt->embopts_len == 0 && opt->encopts_len == 0) { - if (!(opt->type & OT_RESERVED)) { - if (dhcp_envoption1(env == NULL ? NULL : &env[0], - prefix, opt, 1, od, ol, ifname)) - return 1; - else - logerr("%s: %s %d", - ifname, __func__, opt->option); - } - return 0; + if (opt->type & OT_RESERVED) + return; + if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1) + logerr("%s: %s %d", ifname, __func__, opt->option); + return; } /* Create a new prefix based on the option */ - if (env) { - if (opt->type & OT_INDEX) { - if (opt->index > 999) { - errno = ENOBUFS; - logerr(__func__); - return 0; - } - } - e = strlen(prefix) + strlen(opt->var) + 2 + - (opt->type & OT_INDEX ? 3 : 0); - pfx = malloc(e); - if (pfx == NULL) { - logerr(__func__); - return 0; - } - if (opt->type & OT_INDEX) - snprintf(pfx, e, "%s_%s%d", prefix, - opt->var, ++opt->index); - else - snprintf(pfx, e, "%s_%s", prefix, opt->var); - } else - pfx = NULL; + if (opt->type & OT_INDEX) { + if (asprintf(&pfx, "%s_%s%d", + prefix, opt->var, ++opt->index) == -1) + pfx = NULL; + } else { + if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1) + pfx = NULL; + } + if (pfx == NULL) { + logerr(__func__); + return; + } /* Embedded options are always processed first as that * is a fixed layout */ - n = 0; for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) { eo = dhcp_optlen(eopt, ol); if (eo == -1) { - if (env == NULL) - logerrx("%s: %s %d.%d/%zu: " - "malformed embedded option", - ifname, __func__, opt->option, - eopt->option, i); + logerrx("%s: %s %d.%d/%zu: " + "malformed embedded option", + ifname, __func__, opt->option, + eopt->option, i); goto out; } if (eo == 0) { @@ -977,9 +853,9 @@ * This may not be an error as some options like * DHCP FQDN in RFC4702 have a string as the last * option which is optional. */ - if (env == NULL && - (ol != 0 || !(eopt->type & OT_OPTIONAL))) - logerrx("%s: %s %d.%d/%zu: missing embedded option", + if (ol != 0 || !(eopt->type & OT_OPTIONAL)) + logerrx("%s: %s %d.%d/%zu: " + "missing embedded option", ifname, __func__, opt->option, eopt->option, i); goto out; @@ -989,10 +865,8 @@ * This avoids new_fqdn_fqdn which would be silly. */ if (!(eopt->type & OT_RESERVED)) { ov = strcmp(opt->var, eopt->var); - if (dhcp_envoption1(env == NULL ? NULL : &env[n], - pfx, eopt, ov, od, (size_t)eo, ifname)) - n++; - else if (env == NULL) + if (print_option(fp, pfx, eopt, ov, od, (size_t)eo, + ifname) == -1) logerr("%s: %s %d.%d/%zu", ifname, __func__, opt->option, eopt->option, i); @@ -1023,19 +897,16 @@ i < opt->encopts_len; i++, eopt++) { - if (eopt->option == eoc) { - if (eopt->type & OT_OPTION) { - if (oopt == NULL) - /* Report error? */ - continue; - } - n += dhcp_envoption(ctx, - env == NULL ? NULL : &env[n], pfx, - ifname, - eopt->type & OT_OPTION ? oopt:eopt, - dgetopt, eod, eol); - break; + if (eopt->option != eoc) + continue; + if (eopt->type & OT_OPTION) { + if (oopt == NULL) + /* Report error? */ + continue; } + dhcp_envoption(ctx, fp, pfx, ifname, + eopt->type & OT_OPTION ? oopt:eopt, + dgetopt, eod, eol); } od += eos + eol; ol -= eos + eol; @@ -1043,11 +914,7 @@ } out: - if (env) - free(pfx); - - /* Return number of options found */ - return n; + free(pfx); } void @@ -1070,7 +937,7 @@ size_t sz; void *buf; ssize_t len; - + if (fstat(fd, &st) != 0) goto out; if (!S_ISREG(st.st_mode)) {
--- a/src/dhcp-common.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcp-common.h Tue Jun 18 11:33:53 2019 +0100 @@ -33,9 +33,18 @@ #include <stdint.h> +#include <arpa/nameser.h> /* after normal includes for sunos */ + #include "common.h" #include "dhcpcd.h" +/* Support very old arpa/nameser.h as found in OpenBSD */ +#ifndef NS_MAXDNAME +#define NS_MAXCDNAME MAXCDNAME +#define NS_MAXDNAME MAXDNAME +#define NS_MAXLABEL MAXLABEL +#endif + /* Max MTU - defines dhcp option length */ #define IP_UDP_SIZE 28 #define MTU_MAX 1500 - IP_UDP_SIZE @@ -111,8 +120,8 @@ ssize_t print_string(char *, size_t, int, const uint8_t *, size_t); int dhcp_set_leasefile(char *, size_t, int, const struct interface *); -size_t dhcp_envoption(struct dhcpcd_ctx *, - char **, const char *, const char *, struct dhcp_opt *, +void dhcp_envoption(struct dhcpcd_ctx *, + FILE *, const char *, const char *, struct dhcp_opt *, const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, size_t *, unsigned int *, size_t *, const uint8_t *, size_t, struct dhcp_opt **),
--- a/src/dhcp.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcp.c Tue Jun 18 11:33:53 2019 +0100 @@ -47,6 +47,7 @@ #include <inttypes.h> #include <stdbool.h> #include <stddef.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -340,23 +341,25 @@ } ssize_t -decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl) +print_rfc3442(FILE *fp, const uint8_t *data, size_t data_len) { - const uint8_t *e; - size_t bytes = 0, ocets; - int b; + const uint8_t *p = data, *e; + size_t ocets; uint8_t cidr; struct in_addr addr; - char *o = out; /* Minimum is 5 -first is CIDR and a router length of 4 */ - if (pl < 5) { + if (data_len < 5) { errno = EINVAL; return -1; } - e = p + pl; + e = p + data_len; while (p < e) { + if (p != data) { + if (fputc(' ', fp) == EOF) + return -1; + } cidr = *p++; if (cidr > 32) { errno = EINVAL; @@ -367,41 +370,25 @@ errno = ERANGE; return -1; } - if (!out) { - p += 4 + ocets; - bytes += ((4 * 4) * 2) + 4; - continue; - } - if ((((4 * 4) * 2) + 4) > len) { - errno = ENOBUFS; - return -1; + /* If we have ocets then we have a destination and netmask */ + addr.s_addr = 0; + if (ocets > 0) { + memcpy(&addr.s_addr, p, ocets); + p += ocets; } - if (o != out) { - *o++ = ' '; - len--; - } - /* If we have ocets then we have a destination and netmask */ - if (ocets > 0) { - addr.s_addr = 0; - memcpy(&addr.s_addr, p, ocets); - b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr); - p += ocets; - } else - b = snprintf(o, len, "0.0.0.0/0"); - o += b; - len -= (size_t)b; + if (fprintf(fp, "%s/%d", inet_ntoa(addr), cidr) == -1) + return -1; /* Finally, snag the router */ memcpy(&addr.s_addr, p, 4); p += 4; - b = snprintf(o, len, " %s", inet_ntoa(addr)); - o += b; - len -= (size_t)b; + if (fprintf(fp, " %s", inet_ntoa(addr)) == -1) + return -1; } - if (out) - return o - out; - return (ssize_t)bytes; + if (fputc('\0', fp) == EOF) + return -1; + return 1; } static int @@ -474,15 +461,12 @@ return n; } -char * -decode_rfc3361(const uint8_t *data, size_t dl) +ssize_t +print_rfc3361(FILE *fp, const uint8_t *data, size_t dl) { uint8_t enc; - size_t l; - ssize_t r; - char *sip = NULL; + char sip[NS_MAXDNAME]; struct in_addr addr; - char *p; if (dl < 2) { errno = EINVAL; @@ -493,13 +477,10 @@ dl--; switch (enc) { case 0: - if ((r = decode_rfc1035(NULL, 0, data, dl)) > 0) { - l = (size_t)r + 1; - sip = malloc(l); - if (sip == NULL) - return 0; - decode_rfc1035(sip, l, data, dl); - } + if (decode_rfc1035(sip, sizeof(sip), data, dl) == -1) + return -1; + if (efprintf(fp, "%s", sip) == -1) + return -1; break; case 1: if (dl == 0 || dl % 4 != 0) { @@ -507,25 +488,27 @@ break; } addr.s_addr = INADDR_BROADCAST; - l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1; - sip = p = malloc(l); - if (sip == NULL) - return 0; - while (dl != 0) { + for (; + dl != 0; + data += sizeof(addr.s_addr), dl -= sizeof(addr.s_addr)) + { memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); - data += sizeof(addr.s_addr); - p += snprintf(p, l - (size_t)(p - sip), - "%s ", inet_ntoa(addr)); - dl -= sizeof(addr.s_addr); + if (fprintf(fp, "%s", inet_ntoa(addr)) == -1) + return -1; + if (dl != 0) { + if (fputc(' ', fp) == EOF) + return -1; + } } - *--p = '\0'; + if (fputc('\0', fp) == EOF) + return -1; break; default: errno = EINVAL; return 0; } - return sip; + return 1; } static char * @@ -1302,9 +1285,8 @@ } ssize_t -dhcp_env(char **env, const char *prefix, - const struct bootp *bootp, size_t bootp_len, - const struct interface *ifp) +dhcp_env(FILE *fenv, const char *prefix, const struct interface *ifp, + const struct bootp *bootp, size_t bootp_len) { const struct if_options *ifo; const uint8_t *p; @@ -1313,8 +1295,7 @@ struct in_addr brd; struct dhcp_opt *opt, *vo; size_t e, i, pl; - char **ep; - char cidr[4], safe[(BOOTP_FILE_LEN * 4) + 1]; + char safe[(BOOTP_FILE_LEN * 4) + 1]; uint8_t overl = 0; uint32_t en; @@ -1324,97 +1305,63 @@ DHO_OPTSOVERLOADED) == -1) overl = 0; - if (env == NULL) { - if (bootp->yiaddr || bootp->ciaddr) - e += 5; - if (*bootp->file && !(overl & 1)) - e++; - if (*bootp->sname && !(overl & 2)) - e++; - for (i = 0, opt = ifp->ctx->dhcp_opts; - i < ifp->ctx->dhcp_opts_len; - i++, opt++) - { - if (has_option_mask(ifo->nomask, opt->option)) - continue; - if (dhcp_getoverride(ifo, opt->option)) - continue; - p = get_option(ifp->ctx, bootp, bootp_len, - opt->option, &pl); - if (!p) - continue; - e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name, - opt, dhcp_getoption, p, pl); - } - for (i = 0, opt = ifo->dhcp_override; - i < ifo->dhcp_override_len; - i++, opt++) - { - if (has_option_mask(ifo->nomask, opt->option)) - continue; - p = get_option(ifp->ctx, bootp, bootp_len, - opt->option, &pl); - if (!p) - continue; - e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name, - opt, dhcp_getoption, p, pl); - } - return (ssize_t)e; - } - - ep = env; if (bootp->yiaddr || bootp->ciaddr) { /* Set some useful variables that we derive from the DHCP * message but are not necessarily in the options */ addr.s_addr = bootp->yiaddr ? bootp->yiaddr : bootp->ciaddr; - addvar(&ep, prefix, "ip_address", inet_ntoa(addr)); + if (efprintf(fenv, "%s_ip_address=%s", + prefix, inet_ntoa(addr)) == -1) + return -1; if (get_option_addr(ifp->ctx, &net, - bootp, bootp_len, DHO_SUBNETMASK) == -1) - { + bootp, bootp_len, DHO_SUBNETMASK) == -1) { net.s_addr = ipv4_getnetmask(addr.s_addr); - addvar(&ep, prefix, - "subnet_mask", inet_ntoa(net)); + if (efprintf(fenv, "%s_subnet_mask=%s", + prefix, inet_ntoa(net)) == -1) + return -1; } - snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net)); - addvar(&ep, prefix, "subnet_cidr", cidr); + if (efprintf(fenv, "%s_subnet_cidr=%d", + prefix, inet_ntocidr(net))== -1) + return -1; if (get_option_addr(ifp->ctx, &brd, bootp, bootp_len, DHO_BROADCAST) == -1) { brd.s_addr = addr.s_addr | ~net.s_addr; - addvar(&ep, prefix, - "broadcast_address", inet_ntoa(brd)); + if (efprintf(fenv, "%s_broadcast_address=%s", + prefix, inet_ntoa(brd)) == -1) + return -1; } addr.s_addr = bootp->yiaddr & net.s_addr; - addvar(&ep, prefix, - "network_number", inet_ntoa(addr)); + if (efprintf(fenv, "%s_network_number=%s", + prefix, inet_ntoa(addr)) == -1) + return -1; } if (*bootp->file && !(overl & 1)) { print_string(safe, sizeof(safe), OT_STRING, bootp->file, sizeof(bootp->file)); - addvar(&ep, prefix, "filename", safe); + if (efprintf(fenv, "%s_filename=%s", prefix, safe) == -1) + return -1; } if (*bootp->sname && !(overl & 2)) { print_string(safe, sizeof(safe), OT_STRING | OT_DOMAIN, bootp->sname, sizeof(bootp->sname)); - addvar(&ep, prefix, "server_name", safe); + if (efprintf(fenv, "%s_server_name=%s", prefix, safe) == -1) + return -1; } /* Zero our indexes */ - if (env) { - for (i = 0, opt = ifp->ctx->dhcp_opts; - i < ifp->ctx->dhcp_opts_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ifp->options->dhcp_override; - i < ifp->options->dhcp_override_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ifp->ctx->vivso; - i < ifp->ctx->vivso_len; - i++, opt++) - dhcp_zero_index(opt); - } + for (i = 0, opt = ifp->ctx->dhcp_opts; + i < ifp->ctx->dhcp_opts_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->options->dhcp_override; + i < ifp->options->dhcp_override_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->ctx->vivso; + i < ifp->ctx->vivso_len; + i++, opt++) + dhcp_zero_index(opt); for (i = 0, opt = ifp->ctx->dhcp_opts; i < ifp->ctx->dhcp_opts_len; @@ -1427,7 +1374,7 @@ p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl); if (p == NULL) continue; - ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name, + dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, opt, dhcp_getoption, p, pl); if (opt->option != DHO_VIVSO || pl <= (int)sizeof(uint32_t)) @@ -1440,7 +1387,7 @@ /* Skip over en + total size */ p += sizeof(en) + 1; pl -= sizeof(en) + 1; - ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name, + dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, vo, dhcp_getoption, p, pl); } @@ -1453,11 +1400,11 @@ p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl); if (p == NULL) continue; - ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name, + dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, opt, dhcp_getoption, p, pl); } - return ep - env; + return 1; } static void
--- a/src/dhcp.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcp.h Tue Jun 18 11:33:53 2019 +0100 @@ -245,15 +245,15 @@ #include "dhcpcd.h" #include "if-options.h" -char *decode_rfc3361(const uint8_t *, size_t); -ssize_t decode_rfc3442(char *, size_t, const uint8_t *p, size_t); +ssize_t print_rfc3361(FILE *, const uint8_t *, size_t); +ssize_t print_rfc3442(FILE *, const uint8_t *, size_t); void dhcp_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); uint16_t dhcp_get_mtu(const struct interface *); int dhcp_get_routes(rb_tree_t *, struct interface *); -ssize_t dhcp_env(char **, const char *, const struct bootp *, size_t, - const struct interface *); +ssize_t dhcp_env(FILE *, const char *, const struct interface *, + const struct bootp *, size_t); void dhcp_handleifa(int, struct ipv4_addr *, pid_t pid); void dhcp_drop(struct interface *, const char *);
--- a/src/dhcp6.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcp6.c Tue Jun 18 11:33:53 2019 +0100 @@ -3952,7 +3952,7 @@ } ssize_t -dhcp6_env(char **env, const char *prefix, const struct interface *ifp, +dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp, const struct dhcp6_message *m, size_t len) { const struct if_options *ifo; @@ -3966,7 +3966,6 @@ #ifndef SMALL const struct dhcp6_state *state; const struct ipv6_addr *ap; - char *v, *val; #endif n = 0; @@ -3984,28 +3983,20 @@ ctx = ifp->ctx; /* Zero our indexes */ - if (env) { - for (i = 0, opt = ctx->dhcp6_opts; - i < ctx->dhcp6_opts_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ifp->options->dhcp6_override; - i < ifp->options->dhcp6_override_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ctx->vivso; - i < ctx->vivso_len; - i++, opt++) - dhcp_zero_index(opt); - i = strlen(prefix) + strlen("_dhcp6") + 1; - pfx = malloc(i); - if (pfx == NULL) { - logerr(__func__); - return -1; - } - snprintf(pfx, i, "%s_dhcp6", prefix); - } else - pfx = NULL; + for (i = 0, opt = ctx->dhcp6_opts; + i < ctx->dhcp6_opts_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->options->dhcp6_override; + i < ifp->options->dhcp6_override_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ctx->vivso; + i < ctx->vivso_len; + i++, opt++) + dhcp_zero_index(opt); + if (asprintf(&pfx, "%s_dhcp6", prefix) == -1) + return -1; /* Unlike DHCP, DHCPv6 options *may* occur more than once. * There is also no provision for option concatenation unlike DHCP. */ @@ -4051,15 +4042,13 @@ opt = NULL; } if (opt) { - n += dhcp_envoption(ifp->ctx, - env == NULL ? NULL : &env[n], - pfx, ifp->name, + dhcp_envoption(ifp->ctx, + fp, pfx, ifp->name, opt, dhcp6_getoption, p, o.len); } if (vo) { - n += dhcp_envoption(ifp->ctx, - env == NULL ? NULL : &env[n], - pfx, ifp->name, + dhcp_envoption(ifp->ctx, + fp, pfx, ifp->name, vo, dhcp6_getoption, p + sizeof(en), o.len - sizeof(en)); @@ -4071,38 +4060,29 @@ #ifndef SMALL /* Needed for Delegated Prefixes */ state = D6_CSTATE(ifp); - i = 0; TAILQ_FOREACH(ap, &state->addrs, next) { - if (ap->delegating_prefix) { - i += strlen(ap->saddr) + 1; - } + if (ap->delegating_prefix) + break; } - if (env && i) { - i += strlen(prefix) + strlen("_delegated_dhcp6_prefix="); - v = val = env[n] = malloc(i); - if (v == NULL) { - logerr(__func__); - return -1; + if (ap == NULL) + return 1; + if (fprintf(fp, "%s_delegated_dhcp6_prefix=", prefix) == -1) + return -1; + TAILQ_FOREACH(ap, &state->addrs, next) { + if (ap->delegating_prefix == NULL) + continue; + if (ap != TAILQ_FIRST(&state->addrs)) { + if (fputc(' ', fp) == EOF) + return -1; } - v += snprintf(val, i, "%s_delegated_dhcp6_prefix=", prefix); - TAILQ_FOREACH(ap, &state->addrs, next) { - if (ap->delegating_prefix) { - /* Can't use stpcpy(3) due to "security" */ - const char *sap = ap->saddr; - - do - *v++ = *sap; - while (*++sap != '\0'); - *v++ = ' '; - } - } - *--v = '\0'; + if (fprintf(fp, "%s", ap->saddr) == -1) + return -1; } - if (i) - n++; + if (fputc('\0', fp) == EOF) + return -1; #endif - return (ssize_t)n; + return 1; } int
--- a/src/dhcp6.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcp6.h Tue Jun 18 11:33:53 2019 +0100 @@ -228,7 +228,7 @@ int dhcp6_start(struct interface *, enum DH6S); void dhcp6_reboot(struct interface *); void dhcp6_renew(struct interface *); -ssize_t dhcp6_env(char **, const char *, const struct interface *, +ssize_t dhcp6_env(FILE *, const char *, const struct interface *, const struct dhcp6_message *, size_t); void dhcp6_free(struct interface *); void dhcp6_handleifa(int, struct ipv6_addr *, pid_t);
--- a/src/dhcpcd.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcpcd.c Tue Jun 18 11:33:53 2019 +0100 @@ -2133,5 +2133,11 @@ if (ctx.options & DHCPCD_FORKED) _exit(i); /* so atexit won't remove our pidfile */ #endif +#ifdef HAVE_OPEN_MEMSTREAM + if (ctx.script_fp) + fclose(ctx.script_fp); +#endif + free(ctx.script_buf); + free(ctx.script_env); return i; }
--- a/src/dhcpcd.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/dhcpcd.h Tue Jun 18 11:33:53 2019 +0100 @@ -156,6 +156,14 @@ #endif struct eloop *eloop; +#ifdef HAVE_OPEN_MEMSTREAM + FILE *script_fp; +#endif + char *script_buf; + size_t script_buflen; + char **script_env; + size_t script_envlen; + int control_fd; int control_unpriv_fd; struct fd_list_head control_fds;
--- a/src/ipv4.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/ipv4.c Tue Jun 18 11:33:53 2019 +0100 @@ -38,6 +38,7 @@ #include <ctype.h> #include <errno.h> #include <stdbool.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>
--- a/src/ipv4ll.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/ipv4ll.c Tue Jun 18 11:33:53 2019 +0100 @@ -30,6 +30,7 @@ #include <assert.h> #include <errno.h> #include <stdbool.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -137,7 +138,7 @@ } ssize_t -ipv4ll_env(char **env, const char *prefix, const struct interface *ifp) +ipv4ll_env(FILE *fp, const char *prefix, const struct interface *ifp) { const struct ipv4ll_state *state; const char *pf = prefix == NULL ? "" : "_"; @@ -147,24 +148,21 @@ if ((state = IPV4LL_CSTATE(ifp)) == NULL || state->addr == NULL) return 0; - if (env == NULL) - return 5; - /* Emulate a DHCP environment */ - if (asprintf(&env[0], "%s%sip_address=%s", + if (efprintf(fp, "%s%sip_address=%s", prefix, pf, inet_ntoa(state->addr->addr)) == -1) return -1; - if (asprintf(&env[1], "%s%ssubnet_mask=%s", + if (efprintf(fp, "%s%ssubnet_mask=%s", prefix, pf, inet_ntoa(state->addr->mask)) == -1) return -1; - if (asprintf(&env[2], "%s%ssubnet_cidr=%d", + if (efprintf(fp, "%s%ssubnet_cidr=%d", prefix, pf, inet_ntocidr(state->addr->mask)) == -1) return -1; - if (asprintf(&env[3], "%s%sbroadcast_address=%s", + if (efprintf(fp, "%s%sbroadcast_address=%s", prefix, pf, inet_ntoa(state->addr->brd)) == -1) return -1; netnum.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr; - if (asprintf(&env[4], "%s%snetwork_number=%s", + if (efprintf(fp, "%s%snetwork_number=%s", prefix, pf, inet_ntoa(netnum)) == -1) return -1; return 5;
--- a/src/ipv4ll.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/ipv4ll.h Tue Jun 18 11:33:53 2019 +0100 @@ -59,7 +59,7 @@ int ipv4ll_subnetroute(rb_tree_t *, struct interface *); int ipv4ll_defaultroute(rb_tree_t *,struct interface *); -ssize_t ipv4ll_env(char **, const char *, const struct interface *); +ssize_t ipv4ll_env(FILE *, const char *, const struct interface *); void ipv4ll_start(void *); void ipv4ll_claimed(void *); void ipv4ll_handle_failure(void *);
--- a/src/ipv6.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/ipv6.c Tue Jun 18 11:33:53 2019 +0100 @@ -1540,23 +1540,17 @@ } ssize_t -ipv6_env(char **env, const char *prefix, const struct interface *ifp) +ipv6_env(FILE *fp, const char *prefix, const struct interface *ifp) { - char **ep; - ssize_t n; struct ipv6_addr *ia; - ep = env; - n = 0; ia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6, IN6_IFF_NOTUSEABLE); - if (ia) { - if (env) - addvar(&ep, prefix, "ip6_address", ia->saddr); - n++; - } - - return n; + if (ia == NULL) + return 0; + if (efprintf(fp, "%s_ip6_address=%s", prefix, ia->saddr) == -1) + return -1; + return 1; } int
--- a/src/ipv6.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/ipv6.h Tue Jun 18 11:33:53 2019 +0100 @@ -287,7 +287,7 @@ int ipv6_start(struct interface *); int ipv6_staticdadcompleted(const struct interface *); int ipv6_startstatic(struct interface *); -ssize_t ipv6_env(char **, const char *, const struct interface *); +ssize_t ipv6_env(FILE *, const char *, const struct interface *); void ipv6_ctxfree(struct dhcpcd_ctx *); bool inet6_getroutes(struct dhcpcd_ctx *, rb_tree_t *); #endif /* INET6 */
--- a/src/ipv6nd.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/ipv6nd.c Tue Jun 18 11:33:53 2019 +0100 @@ -1384,11 +1384,11 @@ } ssize_t -ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) +ipv6nd_env(FILE *fp, const struct interface *ifp) { size_t i, j, n, len, olen; struct ra *rap; - char ndprefix[32], abuf[24]; + char ndprefix[32]; struct dhcp_opt *opt; uint8_t *p; struct nd_opt_hdr ndo; @@ -1401,34 +1401,24 @@ if (rap->iface != ifp) continue; i++; - if (prefix != NULL) - snprintf(ndprefix, sizeof(ndprefix), - "%s_nd%zu", prefix, i); - else - snprintf(ndprefix, sizeof(ndprefix), - "nd%zu", i); - if (env) - setvar(&env[n], ndprefix, "from", rap->sfrom); - n++; - if (env) - setvard(&env[n], ndprefix, "acquired", - (size_t)rap->acquired.tv_sec); - n++; - if (env) - setvard(&env[n], ndprefix, "now", (size_t)now.tv_sec); - n++; + snprintf(ndprefix, sizeof(ndprefix), "nd%zu", i); + if (efprintf(fp, "%s_from=%s", ndprefix, rap->sfrom) == -1) + return -1; + if (efprintf(fp, "%s_acquired=%ld", ndprefix, + rap->acquired.tv_sec) == -1) + return -1; + if (efprintf(fp, "%s_now=%ld", ndprefix, now.tv_sec) == -1) + return -1; /* Zero our indexes */ - if (env) { - for (j = 0, opt = rap->iface->ctx->nd_opts; - j < rap->iface->ctx->nd_opts_len; - j++, opt++) - dhcp_zero_index(opt); - for (j = 0, opt = rap->iface->options->nd_override; - j < rap->iface->options->nd_override_len; - j++, opt++) - dhcp_zero_index(opt); - } + for (j = 0, opt = rap->iface->ctx->nd_opts; + j < rap->iface->ctx->nd_opts_len; + j++, opt++) + dhcp_zero_index(opt); + for (j = 0, opt = rap->iface->options->nd_override; + j < rap->iface->options->nd_override_len; + j++, opt++) + dhcp_zero_index(opt); /* Unlike DHCP, ND6 options *may* occur more than once. * There is also no provision for option concatenation @@ -1461,13 +1451,12 @@ if (j == rap->iface->ctx->nd_opts_len) opt = NULL; } - if (opt) { - n += dhcp_envoption(rap->iface->ctx, - env == NULL ? NULL : &env[n], - ndprefix, rap->iface->name, - opt, ipv6nd_getoption, - p + sizeof(ndo), olen - sizeof(ndo)); - } + if (opt == NULL) + continue; + dhcp_envoption(rap->iface->ctx, fp, + ndprefix, rap->iface->name, + opt, ipv6nd_getoption, + p + sizeof(ndo), olen - sizeof(ndo)); } /* We need to output the addresses we actually made @@ -1481,15 +1470,12 @@ !(ia->flags & IPV6_AF_ADDED) || ia->prefix_vltime == 0) continue; - j++; - if (env) { - snprintf(abuf, sizeof(abuf), "addr%zu", j); - setvar(&env[n], ndprefix, abuf, ia->saddr); - } - n++; + if (efprintf(fp, "%s_addr%zu=%s", + ndprefix, j++, ia->saddr) == -1) + return -1; } } - return (ssize_t)n; + return 1; } void
--- a/src/ipv6nd.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/ipv6nd.h Tue Jun 18 11:33:53 2019 +0100 @@ -95,7 +95,7 @@ void ipv6nd_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); void ipv6nd_startrs(struct interface *); -ssize_t ipv6nd_env(char **, const char *, const struct interface *); +ssize_t ipv6nd_env(FILE *, const struct interface *); const struct ipv6_addr *ipv6nd_iffindaddr(const struct interface *ifp, const struct in6_addr *addr, unsigned int flags); struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *,
--- a/src/script.c Fri Jun 14 17:05:24 2019 +0100 +++ b/src/script.c Tue Jun 18 11:33:53 2019 +0100 @@ -36,6 +36,7 @@ #include <errno.h> #include <signal.h> #include <spawn.h> +#include <stdarg.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -56,7 +57,7 @@ #define RC_SVCNAME "RC_SVCNAME" #endif -#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" +#define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin" static const char * const if_params[] = { "interface", @@ -120,103 +121,23 @@ } #ifdef INET -static char * -make_var(const char *prefix, const char *var) +static int +append_config(FILE *fp, const char *prefix, const char *const *config) { - size_t len; - char *v; - - len = strlen(prefix) + strlen(var) + 2; - if ((v = malloc(len)) == NULL) { - logerr(__func__); - return NULL; - } - snprintf(v, len, "%s_%s", prefix, var); - return v; -} - - -static int -append_config(char ***env, size_t *len, - const char *prefix, const char *const *config) -{ - size_t i, j, e1; - char **ne, *eq, **nep, *p; - int ret; + size_t i; if (config == NULL) return 0; - ne = *env; - ret = 0; + /* Do we need to replace existing config rather than append? */ for (i = 0; config[i] != NULL; i++) { - eq = strchr(config[i], '='); - e1 = (size_t)(eq - config[i] + 1); - for (j = 0; j < *len; j++) { - if (strncmp(ne[j], prefix, strlen(prefix)) == 0 && - ne[j][strlen(prefix)] == '_' && - strncmp(ne[j] + strlen(prefix) + 1, - config[i], e1) == 0) - { - p = make_var(prefix, config[i]); - if (p == NULL) { - ret = -1; - break; - } - free(ne[j]); - ne[j] = p; - break; - } - } - if (j == *len) { - j++; - p = make_var(prefix, config[i]); - if (p == NULL) { - ret = -1; - break; - } - nep = realloc(ne, sizeof(char *) * (j + 1)); - if (nep == NULL) { - logerr(__func__); - free(p); - ret = -1; - break; - } - ne = nep; - ne[j - 1] = p; - *len = j; - } + if (efprintf(fp, "%s_%s", prefix, config[i]) == -1) + return -1; } - *env = ne; - return ret; + return 1; } -#endif - -static ssize_t -arraytostr(const char *const *argv, char **s) -{ - const char *const *ap; - char *p; - size_t len, l; - if (*argv == NULL) - return 0; - len = 0; - ap = argv; - while (*ap) - len += strlen(*ap++) + 1; - *s = p = malloc(len); - if (p == NULL) - return -1; - ap = argv; - while (*ap) { - l = strlen(*ap) + 1; - memcpy(p, *ap, l); - p += l; - ap++; - } - return (ssize_t)len; -} +#endif #define PROTO_LINK 0 #define PROTO_DHCP 1 @@ -233,15 +154,32 @@ "static6" }; -static ssize_t -make_env(const struct interface *ifp, const char *reason, char ***argv) +int +efprintf(FILE *fp, const char *fmt, ...) { - int protocol, r; - char **env, **nenv, *p; - size_t e, elen, l; -#if defined(INET) || defined(INET6) - ssize_t n; -#endif + va_list args; + int r; + + va_start(args, fmt); + r = vfprintf(fp, fmt, args); + va_end(args); + if (r == -1) + return -1; + /* Write a trailing NULL so we can easily create env strings. */ + if (fputc('\0', fp) == EOF) + return -1; + return r; +} + +static ssize_t +make_env(const struct interface *ifp, const char *reason) +{ + struct dhcpcd_ctx *ctx = ifp->ctx; + FILE *fp; + char **env, **envp, *buf, *bufp, *endp, *path; + size_t nenv; + long buf_pos, i; + int protocol; const struct if_options *ifo = ifp->options; const struct interface *ifp2; int af; @@ -255,6 +193,31 @@ const struct dhcp6_state *d6_state; #endif +#ifdef HAVE_OPEN_MEMSTREAM + if (ctx->script_fp == NULL) { + fp = open_memstream(&ctx->script_buf, &ctx->script_buflen); + if (fp == NULL) + goto eexit; + ctx->script_fp = fp; + } else { + fp = ctx->script_fp; + rewind(fp); + } +#else + char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX"; + int tmpfd; + + fp = NULL; + tmpfd = mkstemp(tmpfile); + if (tmpfd == -1) + goto eexit; + unlink(tmpfile); + fp = fopen(tmpfile, "w+"); + close(tmpfd); + if (fp == NULL) + goto eexit; +#endif + #ifdef INET state = D_STATE(ifp); #ifdef IPV4LL @@ -309,71 +272,60 @@ protocol = PROTO_DHCP; #endif - /* When dumping the lease, we only want to report interface and - reason - the other interface variables are meaningless */ - if (ifp->ctx->options & DHCPCD_DUMPLEASE) - elen = 2; - else - elen = 11; + /* Needed for scripts */ + path = getenv("PATH"); + if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1) + goto eexit; -#define EMALLOC(i, l) if ((env[(i)] = malloc((l))) == NULL) goto eexit; - /* Make our env + space for profile, wireless and debug */ - env = calloc(1, sizeof(char *) * (elen + 5 + 1)); - if (env == NULL) + if (efprintf(fp, "interface=%s", ifp->name) == -1) goto eexit; - e = strlen("interface") + strlen(ifp->name) + 2; - EMALLOC(0, e); - snprintf(env[0], e, "interface=%s", ifp->name); - e = strlen("reason") + strlen(reason) + 2; - EMALLOC(1, e); - snprintf(env[1], e, "reason=%s", reason); + if (efprintf(fp, "reason=%s", reason) == -1) + goto eexit; if (ifp->ctx->options & DHCPCD_DUMPLEASE) goto dumplease; - e = 20; - EMALLOC(2, e); - snprintf(env[2], e, "pid=%d", getpid()); - EMALLOC(3, e); - snprintf(env[3], e, "ifcarrier=%s", + if (efprintf(fp, "pid=%d", getpid()) == -1) + goto eexit; + if (efprintf(fp, "ifcarrier=%s", ifp->carrier == LINK_UNKNOWN ? "unknown" : - ifp->carrier == LINK_UP ? "up" : "down"); - EMALLOC(4, e); - snprintf(env[4], e, "ifmetric=%d", ifp->metric); - EMALLOC(5, e); - snprintf(env[5], e, "ifwireless=%d", ifp->wireless); - EMALLOC(6, e); - snprintf(env[6], e, "ifflags=%u", ifp->flags); - EMALLOC(7, e); - snprintf(env[7], e, "ifmtu=%d", if_getmtu(ifp)); - l = e = strlen("interface_order="); + ifp->carrier == LINK_UP ? "up" : "down") == -1) + goto eexit; + if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1) + goto eexit; + if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1) + goto eexit; + if (efprintf(fp, "ifflags=%u", ifp->flags) == -1) + goto eexit; + if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1) + goto eexit; + + if (fprintf(fp, "interface_order=") == -1) + goto eexit; TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { - e += strlen(ifp2->name) + 1; + if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) { + if (fputc(' ', fp) == EOF) + return -1; + } + if (fprintf(fp, "%s", ifp2->name) == -1) + return -1; } - EMALLOC(8, e); - p = env[8]; - strlcpy(p, "interface_order=", e); - e -= l; - p += l; - TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { - l = strlcpy(p, ifp2->name, e); - p += l; - e -= l; - *p++ = ' '; - e--; - } - *--p = '\0'; + if (fputc('\0', fp) == EOF) + return -1; + if (strcmp(reason, "STOPPED") == 0) { - env[9] = strdup("if_up=false"); - if (ifo->options & DHCPCD_RELEASE) - env[10] = strdup("if_down=true"); - else - env[10] = strdup("if_down=false"); + if (efprintf(fp, "if_up=false") == -1) + goto eexit; + if (efprintf(fp, "if_down=%s", + ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1) + goto eexit; } else if (strcmp(reason, "TEST") == 0 || strcmp(reason, "PREINIT") == 0 || strcmp(reason, "CARRIER") == 0 || strcmp(reason, "UNKNOWN") == 0) { - env[9] = strdup("if_up=false"); - env[10] = strdup("if_down=false"); + if (efprintf(fp, "if_up=false") == -1) + goto eexit; + if (efprintf(fp, "if_down=false") == -1) + goto eexit; } else if (1 == 2 /* appease ifdefs */ #ifdef INET || (protocol == PROTO_DHCP && state && state->new) @@ -390,24 +342,23 @@ #endif ) { - env[9] = strdup("if_up=true"); - env[10] = strdup("if_down=false"); + if (efprintf(fp, "if_up=true") == -1) + goto eexit; + if (efprintf(fp, "if_down=false") == -1) + goto eexit; } else { - env[9] = strdup("if_up=false"); - env[10] = strdup("if_down=true"); + if (efprintf(fp, "if_up=false") == -1) + goto eexit; + if (efprintf(fp, "if_down=true") == -1) + goto eexit; } - if (env[9] == NULL || env[10] == NULL) - goto eexit; if (protocols[protocol] != NULL) { - r = asprintf(&env[elen], "protocol=%s", protocols[protocol]); - if (r == -1) + if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1) goto eexit; - elen++; } if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) { - e = 20; - EMALLOC(elen, e); - snprintf(env[elen++], e, "if_afwaiting=%d", af); + if (efprintf(fp, "if_afwaiting=%d", af) == -1) + goto eexit; } if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) { TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { @@ -416,20 +367,18 @@ } } if (af != AF_MAX) { - e = 20; - EMALLOC(elen, e); - snprintf(env[elen++], e, "af_waiting=%d", af); + if (efprintf(fp, "af_waiting=%d", af) == -1) + goto eexit; } if (ifo->options & DHCPCD_DEBUG) { - e = strlen("syslog_debug=true") + 1; - EMALLOC(elen, e); - snprintf(env[elen++], e, "syslog_debug=true"); + if (efprintf(fp, "syslog_debug=true") == -1) + goto eexit; } if (*ifp->profile) { - e = strlen("profile=") + strlen(ifp->profile) + 1; - EMALLOC(elen, e); - snprintf(env[elen++], e, "profile=%s", ifp->profile); + if (efprintf(fp, "profile=%s", ifp->profile) == -1) + goto eexit; } +#if 0 if (ifp->wireless) { static const char *pfx = "ifssid="; size_t pfx_len; @@ -447,44 +396,22 @@ elen++; } } +#endif #ifdef INET if (protocol == PROTO_DHCP && state && state->old) { - n = dhcp_env(NULL, NULL, state->old, state->old_len, ifp); - if (n == -1) + if (dhcp_env(fp, "old", ifp, + state->old, state->old_len) == -1) goto eexit; - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp_env(env + elen, "old", - state->old, state->old_len, ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } - if (append_config(&env, &elen, "old", + if (append_config(fp, "old", (const char *const *)ifo->config) == -1) goto eexit; } #endif #ifdef DHCP6 if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) { - n = dhcp6_env(NULL, NULL, ifp, - d6_state->old, d6_state->old_len); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp6_env(env + elen, "old", ifp, - d6_state->old, d6_state->old_len); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (dhcp6_env(fp, "old", ifp, + d6_state->old, d6_state->old_len) == -1) + goto eexit; } #endif @@ -492,144 +419,114 @@ #ifdef INET #ifdef IPV4LL if (protocol == PROTO_IPV4LL) { - n = ipv4ll_env(NULL, NULL, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - if ((n = ipv4ll_env(env + elen, - istate->down ? "old" : "new", ifp)) == -1) - goto eexit; - elen += (size_t)n; - } + if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1) + goto eexit; } #endif if (protocol == PROTO_DHCP && state && state->new) { - n = dhcp_env(NULL, NULL, state->new, state->new_len, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp_env(env + elen, "new", - state->new, state->new_len, ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } - if (append_config(&env, &elen, "new", + if (dhcp_env(fp, "new", ifp, + state->new, state->new_len) == -1) + goto eexit; + if (append_config(fp, "new", (const char *const *)ifo->config) == -1) goto eexit; } #endif #ifdef INET6 if (protocol == PROTO_STATIC6) { - n = ipv6_env(NULL, NULL, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = ipv6_env(env + elen, "new", ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (ipv6_env(fp, "new", ifp) == -1) + goto eexit; } #ifdef DHCP6 if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) { - n = dhcp6_env(NULL, NULL, ifp, - d6_state->new, d6_state->new_len); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp6_env(env + elen, "new", ifp, - d6_state->new, d6_state->new_len); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (dhcp6_env(fp, "new", ifp, + d6_state->new, d6_state->new_len) == -1) + goto eexit; } #endif if (protocol == PROTO_RA) { - n = ipv6nd_env(NULL, NULL, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = ipv6nd_env(env + elen, NULL, ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (ipv6nd_env(fp, ifp) == -1) + goto eexit; } #endif /* Add our base environment */ if (ifo->environ) { - e = 0; - while (ifo->environ[e++]) - ; - nenv = realloc(env, sizeof(char *) * (elen + e + 1)); - if (nenv == NULL) + for (i = 0; ifo->environ[i] != NULL; i++) + if (efprintf(fp, "%s", ifo->environ[i]) == -1) + goto eexit; + } + + /* Convert buffer to argv */ + fflush(fp); + + buf_pos = ftell(fp); + if (buf_pos == -1) { + logerr(__func__); + goto eexit; + } +#ifdef HAVE_OPEN_MEMSTREAM + buf = ctx->script_buf; +#else + size_t buf_len = (size_t)buf_pos; + if (ctx->script_buflen < buf_len) { + buf = realloc(ctx->script_buf, buf_len); + if (buf == NULL) goto eexit; - env = nenv; - e = 0; - while (ifo->environ[e]) { - env[elen + e] = strdup(ifo->environ[e]); - if (env[elen + e] == NULL) - goto eexit; - e++; - } - elen += e; + ctx->script_buf = buf; + ctx->script_buflen = buf_len; + } + buf = ctx->script_buf; + rewind(fp); + if (fread(buf, sizeof(char), buf_len, fp) != buf_len) + goto eexit; + fclose(fp); + fp = NULL; +#endif + + nenv = 0; + endp = buf + buf_pos; + for (bufp = buf; bufp < endp; bufp++) { + if (*bufp == '\0') + nenv++; } - env[elen] = NULL; + if (ctx->script_envlen < nenv) { + env = reallocarray(ctx->script_env, nenv, sizeof(*env)); + if (env == NULL) + goto eexit; + ctx->script_env = env; + ctx->script_envlen = nenv; + } + bufp = buf; + envp = ctx->script_env; + *envp++ = bufp++; + envp--; /* Avoid setting the last \0 to an invalid pointer */ + for (; bufp < endp; bufp++) { + if (*bufp == '\0') + *envp++ = bufp + 1; + } + *envp = NULL; - *argv = env; - return (ssize_t)elen; + return (ssize_t)nenv; eexit: logerr(__func__); - if (env) { - nenv = env; - while (*nenv) - free(*nenv++); - free(env); - } +#ifndef HAVE_OPEN_MEMSTREAM + if (fp != NULL) + fclose(fp); +#endif return -1; } static int -send_interface1(struct fd_list *fd, const struct interface *iface, +send_interface1(struct fd_list *fd, const struct interface *ifp, const char *reason) { - char **env, **ep, *s; - size_t elen; - int retval; + struct dhcpcd_ctx *ctx = ifp->ctx; - if (make_env(iface, reason, &env) == -1) + if (make_env(ifp, reason) == -1) return -1; - s = NULL; - elen = (size_t)arraytostr((const char *const *)env, &s); - if ((ssize_t)elen == -1) { - free(s); - retval = -1; - } else - retval = control_queue(fd, s, elen, 1); - ep = env; - while (*ep) - free(*ep++); - free(env); - return retval; + return control_queue(fd, ctx->script_buf, ctx->script_buflen, 1); } int @@ -696,10 +593,8 @@ int script_runreason(const struct interface *ifp, const char *reason) { + struct dhcpcd_ctx *ctx = ifp->ctx; char *argv[2]; - char **env = NULL, **ep; - char *svcname, *path, *bigenv; - size_t e, elen = 0; pid_t pid; int status = 0; struct fd_list *fd; @@ -709,8 +604,7 @@ return 0; /* Make our env */ - elen = (size_t)make_env(ifp, reason, &env); - if (elen == (size_t)-1) { + if (make_env(ifp, reason) == -1) { logerr(__func__); return -1; } @@ -722,43 +616,7 @@ argv[1] = NULL; logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason); - /* Resize for PATH and RC_SVCNAME */ - svcname = getenv(RC_SVCNAME); - ep = reallocarray(env, elen + 2 + (svcname ? 1 : 0), sizeof(char *)); - if (ep == NULL) { - elen = 0; - goto out; - } - env = ep; - /* Add path to it */ - path = getenv("PATH"); - if (path) { - e = strlen("PATH") + strlen(path) + 2; - env[elen] = malloc(e); - if (env[elen] == NULL) { - elen = 0; - goto out; - } - snprintf(env[elen], e, "PATH=%s", path); - } else { - env[elen] = strdup(DEFAULT_PATH); - if (env[elen] == NULL) { - elen = 0; - goto out; - } - } - if (svcname) { - e = strlen(RC_SVCNAME) + strlen(svcname) + 2; - env[++elen] = malloc(e); - if (env[elen] == NULL) { - elen = 0; - goto out; - } - snprintf(env[elen], e, "%s=%s", RC_SVCNAME, svcname); - } - env[++elen] = NULL; - - pid = exec_script(ifp->ctx, argv, env); + pid = exec_script(ctx, argv, ctx->script_env); if (pid == -1) logerr("%s: %s", __func__, argv[0]); else if (pid != 0) { @@ -781,36 +639,16 @@ send_listeners: /* Send to our listeners */ - bigenv = NULL; status = 0; - TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) { + TAILQ_FOREACH(fd, &ctx->control_fds, next) { if (!(fd->flags & FD_LISTEN)) continue; - if (bigenv == NULL) { - elen = (size_t)arraytostr((const char *const *)env, - &bigenv); - if ((ssize_t)elen == -1) { - logerr("%s: arraytostr", ifp->name); - break; - } - } - if (control_queue(fd, bigenv, elen, 1) == -1) + if (control_queue(fd, ctx->script_buf, ctx->script_buflen, 1) + == -1) logerr("%s: control_queue", __func__); else status = 1; } - if (!status) - free(bigenv); -out: - /* Cleanup */ - ep = env; - while (*ep) - free(*ep++); - free(env); - if (elen == 0) { - logerr(__func__); - return -1; - } return WEXITSTATUS(status); }
--- a/src/script.h Fri Jun 14 17:05:24 2019 +0100 +++ b/src/script.h Tue Jun 18 11:33:53 2019 +0100 @@ -30,6 +30,7 @@ #include "control.h" +__printflike(2, 3) int efprintf(FILE *, const char *, ...); void if_printoptions(void); int send_interface(struct fd_list *, const struct interface *); int script_runreason(const struct interface *, const char *);
