From c8521994dab5002cbbd95df98d1b53a58421bdc7 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 18 Jun 2019 11:33:53 +0100 Subject: [PATCH] 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. --- configure | 53 +++-- src/arp.c | 1 + src/auth.c | 1 + src/common.c | 48 ---- src/common.h | 5 - src/dhcp-common.c | 351 +++++++++------------------- src/dhcp-common.h | 13 +- src/dhcp.c | 209 +++++++---------- src/dhcp.h | 8 +- src/dhcp6.c | 92 +++----- src/dhcp6.h | 2 +- src/dhcpcd.c | 6 + src/dhcpcd.h | 8 + src/ipv4.c | 1 + src/ipv4ll.c | 16 +- src/ipv4ll.h | 2 +- src/ipv6.c | 18 +- src/ipv6.h | 2 +- src/ipv6nd.c | 70 +++--- src/ipv6nd.h | 2 +- src/script.c | 570 +++++++++++++++++----------------------------- src/script.h | 1 + 22 files changed, 542 insertions(+), 937 deletions(-) diff --git a/configure b/configure index 4a4920e2..79201c04 100755 --- a/configure +++ b/configure @@ -15,6 +15,7 @@ ARC4RANDOM= CLOSEFROM= RBTREE= CONSTTIME_MEMEQUAL= +OPEN_MEMSTREAM= STRLCPY= UDEV= OS= @@ -741,29 +742,25 @@ if [ "$ARC4RANDOM_UNIFORM" = no ]; then echo "#include \"compat/arc4random_uniform.h\"" >>$CONFIG_H fi - -if [ -z "$STRLCPY" ]; then - printf "Testing for strlcpy ... " - cat <_strlcpy.c -#include +if [ -z "$OPEN_MEMSTREAM" ]; then + printf "Testing for open_memstream ... " + cat <_open_memstream.c +#include 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 @@ if [ "$SETPROCTITLE" = yes ]; then echo "#define HAVE_SETPROCTITLE" >>$CONFIG_H fi +if [ -z "$STRLCPY" ]; then + printf "Testing for strlcpy ... " + cat <_strlcpy.c +#include +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 <_strtoi.c diff --git a/src/arp.c b/src/arp.c index 59d82e9f..f48bc91c 100644 --- a/src/arp.c +++ b/src/arp.c @@ -36,6 +36,7 @@ #include #include +#include #include #include diff --git a/src/auth.c b/src/auth.c index cd774150..0e4c9f4d 100644 --- a/src/auth.c +++ b/src/auth.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common.c b/src/common.c index 0214af2c..d6d5b474 100644 --- a/src/common.c +++ b/src/common.c @@ -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) { diff --git a/src/common.h b/src/common.h index 1022e6ff..3e63ab77 100644 --- a/src/common.h +++ b/src/common.h @@ -193,11 +193,6 @@ void get_line_free(void); 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 *); diff --git a/src/dhcp-common.c b/src/dhcp-common.c index c562ada7..6d66ebef 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -36,8 +36,6 @@ #include #include -#include /* 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 @@ dhcp_optlen(const struct dhcp_opt *opt, size_t dl) 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 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt, 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 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt, 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 @@ dhcp_set_leasefile(char *leasefile, size_t len, int family, 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 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, /* 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 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, * 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 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, * 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 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, 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 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, } out: - if (env) - free(pfx); - - /* Return number of options found */ - return n; + free(pfx); } void @@ -1070,7 +937,7 @@ dhcp_read_lease_fd(int fd, void **lease) size_t sz; void *buf; ssize_t len; - + if (fstat(fd, &st) != 0) goto out; if (!S_ISREG(st.st_mode)) { diff --git a/src/dhcp-common.h b/src/dhcp-common.h index a4de7420..109f2017 100644 --- a/src/dhcp-common.h +++ b/src/dhcp-common.h @@ -33,9 +33,18 @@ #include +#include /* 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 decode_rfc1035(char *, size_t, const uint8_t *, size_t); 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 **), diff --git a/src/dhcp.c b/src/dhcp.c index 25a9e72c..fc0c5490 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -340,23 +341,25 @@ get_option_uint8(struct dhcpcd_ctx *ctx, } 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 @@ decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl) 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 (o != out) { - *o++ = ' '; - len--; - } /* If we have ocets then we have a destination and netmask */ + addr.s_addr = 0; 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 @@ decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp, 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 @@ decode_rfc3361(const uint8_t *data, size_t dl) 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 @@ decode_rfc3361(const uint8_t *data, size_t dl) 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 @@ dhcp_getoption(struct dhcpcd_ctx *ctx, } 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 @@ dhcp_env(char **env, const char *prefix, 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 @@ dhcp_env(char **env, const char *prefix, 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 @@ dhcp_env(char **env, const char *prefix, 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 @@ dhcp_env(char **env, const char *prefix, /* 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 @@ dhcp_env(char **env, const char *prefix, 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 diff --git a/src/dhcp.h b/src/dhcp.h index 245d20a4..e7bb1131 100644 --- a/src/dhcp.h +++ b/src/dhcp.h @@ -245,15 +245,15 @@ struct dhcp_state { #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 *); diff --git a/src/dhcp6.c b/src/dhcp6.c index aba6d42b..6ebb591a 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -3952,7 +3952,7 @@ dhcp6_handleifa(int cmd, struct ipv6_addr *ia, pid_t pid) } 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 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, #ifndef SMALL const struct dhcp6_state *state; const struct ipv6_addr *ap; - char *v, *val; #endif n = 0; @@ -3984,28 +3983,20 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, 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 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, 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 @@ delegated: #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; - } - 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++ = ' '; - } + 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 = '\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 diff --git a/src/dhcp6.h b/src/dhcp6.h index 8853d7f8..4e08eb68 100644 --- a/src/dhcp6.h +++ b/src/dhcp6.h @@ -228,7 +228,7 @@ size_t dhcp6_find_delegates(struct interface *); 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); diff --git a/src/dhcpcd.c b/src/dhcpcd.c index fc500353..7737c83e 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -2133,5 +2133,11 @@ exit1: 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; } diff --git a/src/dhcpcd.h b/src/dhcpcd.h index 7ce3cad3..4ce1c15e 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -156,6 +156,14 @@ struct dhcpcd_ctx { #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; diff --git a/src/ipv4.c b/src/ipv4.c index 5f138b5c..91188cc8 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/src/ipv4ll.c b/src/ipv4ll.c index 8a1cc914..2f7a3f8b 100644 --- a/src/ipv4ll.c +++ b/src/ipv4ll.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -137,7 +138,7 @@ ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp) } 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 @@ ipv4ll_env(char **env, const char *prefix, const struct interface *ifp) 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; diff --git a/src/ipv4ll.h b/src/ipv4ll.h index 5fefa429..d7fb0c32 100644 --- a/src/ipv4ll.h +++ b/src/ipv4ll.h @@ -59,7 +59,7 @@ struct ipv4ll_state { 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 *); diff --git a/src/ipv6.c b/src/ipv6.c index 8288cb40..01286173 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -1540,23 +1540,17 @@ ipv6_staticdadcallback(void *arg) } 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 diff --git a/src/ipv6.h b/src/ipv6.h index 38382993..152361b1 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -287,7 +287,7 @@ void ipv6_addtempaddrs(struct interface *, const struct timespec *); 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 */ diff --git a/src/ipv6nd.c b/src/ipv6nd.c index 5ece711e..b879c171 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -1384,11 +1384,11 @@ ipv6nd_getoption(struct dhcpcd_ctx *ctx, } 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 @@ ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) 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 @@ ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) 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 @@ ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) !(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 diff --git a/src/ipv6nd.h b/src/ipv6nd.h index 2727e514..555ef2fe 100644 --- a/src/ipv6nd.h +++ b/src/ipv6nd.h @@ -95,7 +95,7 @@ struct rs_state { 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 *, diff --git a/src/script.c b/src/script.c index 451b0308..1952a04f 100644 --- a/src/script.c +++ b/src/script.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -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 @@ exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env) } #ifdef INET -static char * -make_var(const char *prefix, const char *var) -{ - 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) +append_config(FILE *fp, 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 @@ static const char *protocols[] = { "static6" }; +int +efprintf(FILE *fp, const char *fmt, ...) +{ + 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, char ***argv) +make_env(const struct interface *ifp, const char *reason) { - int protocol, r; - char **env, **nenv, *p; - size_t e, elen, l; -#if defined(INET) || defined(INET6) - ssize_t n; -#endif + 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 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) 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 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) 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; + if (efprintf(fp, "reason=%s", reason) == -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 (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="); - TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { - e += strlen(ifp2->name) + 1; - } - EMALLOC(8, e); - p = env[8]; - strlcpy(p, "interface_order=", e); - e -= l; - p += l; + 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) { - l = strlcpy(p, ifp2->name, e); - p += l; - e -= l; - *p++ = ' '; - e--; + if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) { + if (fputc(' ', fp) == EOF) + return -1; + } + if (fprintf(fp, "%s", ifp2->name) == -1) + return -1; } - *--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 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) #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 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) } } 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 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) 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 @@ dumplease: #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) - goto eexit; - env = nenv; - e = 0; - while (ifo->environ[e]) { - env[elen + e] = strdup(ifo->environ[e]); - if (env[elen + e] == NULL) + for (i = 0; ifo->environ[i] != NULL; i++) + if (efprintf(fp, "%s", ifo->environ[i]) == -1) goto eexit; - e++; - } - elen += e; } - env[elen] = NULL; - *argv = env; - return (ssize_t)elen; + /* 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; + 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++; + } + 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; + + 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 @@ send_interface(struct fd_list *fd, const struct interface *ifp) 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 @@ script_runreason(const struct interface *ifp, const char *reason) 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 @@ script_runreason(const struct interface *ifp, const char *reason) 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 @@ script_runreason(const struct interface *ifp, const char *reason) 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); } diff --git a/src/script.h b/src/script.h index 6b7a593b..4408eeb4 100644 --- a/src/script.h +++ b/src/script.h @@ -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 *); -- 2.47.3