From 228d7ff964638f353b0ddcf10c252b5c609601e1 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 14 May 2026 17:31:37 +0100 Subject: [PATCH] Remove arbitrary limit on config file line lengths. Stop using the namebuff buffer to read config file lines. This reduced in size in 014e909f787e808bb35daa546d3f8f3663918de2 and caused regressions. We now resize the buffer as needed, so there is no practical limit. Use the same code to remove the same limit when reading /etc/ethers and /etc/resolv.conf. Also when copying output from the lease-change script into the log. Thanks to Kevin Darbyshire-Bryant for finding the regresssion. --- src/cache.c | 13 ++++++----- src/dhcp.c | 15 +++++++------ src/dnsmasq.h | 1 + src/helper.c | 18 +++++---------- src/network.c | 10 ++++----- src/option.c | 61 +++++++++++++++++++++++++++------------------------ src/util.c | 50 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 109 insertions(+), 59 deletions(-) diff --git a/src/cache.c b/src/cache.c index 32192c1..aec9a0b 100644 --- a/src/cache.c +++ b/src/cache.c @@ -1407,9 +1407,10 @@ static int eatspace(FILE *f) } } -static int gettok(FILE *f, char *token) +static int gettok(FILE *f, char *token, size_t buffsz) { - int c, count = 0; + int c; + unsigned int count = 0; while (1) { @@ -1422,7 +1423,7 @@ static int gettok(FILE *f, char *token) return eatspace(f); } - if (count < (MAXDNAMESTR - 1)) + if (count < (buffsz - 1)) { token[count++] = c; token[count] = 0; @@ -1447,7 +1448,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr lineno += eatspace(f); - while ((atnl = gettok(f, token)) != -1) + while ((atnl = gettok(f, token, MAXDNAMESTR)) != -1) { if (inet_pton(AF_INET, token, &addr) > 0) { @@ -1465,7 +1466,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr { my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno); while (atnl == 0) - atnl = gettok(f, token); + atnl = gettok(f, token, MAXDNAMESTR); lineno += atnl; continue; } @@ -1483,7 +1484,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr int fqdn, nomem; char *canon; - if ((atnl = gettok(f, token)) == -1) + if ((atnl = gettok(f, token, MAXDNAMESTR)) == -1) break; fqdn = !!strchr(token, '.'); diff --git a/src/dhcp.c b/src/dhcp.c index 3f8dc29..6546741 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -925,7 +925,8 @@ void dhcp_read_ethers(void) { FILE *f = fopen(ETHERSFILE, "r"); unsigned int flags; - char *buff = daemon->namebuff; + size_t linesz = 0; + char *line = NULL; char *ip, *cp; struct in_addr addr; unsigned char hwaddr[ETHER_ADDR_LEN]; @@ -958,22 +959,22 @@ void dhcp_read_ethers(void) up = &config->next; } - while (fgets(buff, MAXDNAMESTR, f)) + while (get_line_alloc(f, &line, &linesz)) { char *host = NULL; lineno++; - while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1])) - buff[strlen(buff)-1] = 0; + while (strlen(line) > 0 && isspace((unsigned char)line[strlen(line)-1])) + line[strlen(line)-1] = 0; - if ((*buff == '#') || (*buff == '+') || (*buff == 0)) + if ((*line == '#') || (*line == '+') || (*line == 0)) continue; - for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++); + for (ip = line; *ip && !isspace((unsigned char)*ip); ip++); for(; *ip && isspace((unsigned char)*ip); ip++) *ip = 0; - if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN) + if (!*ip || parse_hex(line, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN) { my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno); continue; diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 9bec05b..447b940 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1516,6 +1516,7 @@ void *whine_malloc_real(const char *func, unsigned int line, size_t size); void *whine_realloc_real(const char *wrapper, const char *func, unsigned int line, void *ptr, size_t size); int expand_buf_real(const char *func, unsigned int line, struct iovec *iov, size_t size); int expand_workspace_real(const char *func, unsigned int line, unsigned char ***wkspc, int *szp, int new); +int get_line_alloc(FILE *f, char **buffp, size_t *sizep); int sa_len(union mysockaddr *addr); int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2); int sockaddr_isnull(const union mysockaddr *s); diff --git a/src/helper.c b/src/helper.c index a594c26..30a7197 100644 --- a/src/helper.c +++ b/src/helper.c @@ -533,18 +533,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) close(pipeout[0]); else { - while (fgets(daemon->packet, daemon->packet_buff_sz, fp)) - { - /* do not include new lines, log will append them */ - size_t len = strlen(daemon->packet); - if (len > 0) - { - --len; - if (daemon->packet[len] == '\n') - daemon->packet[len] = 0; - } - send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet); - } + char *line = NULL; + size_t linesz = 0; + + while (get_line_alloc(fp, &line, &linesz)) + send_event(event_fd, EVENT_SCRIPT_LOG, 0, line); + fclose(fp); } } diff --git a/src/network.c b/src/network.c index 8985e84..9015b34 100644 --- a/src/network.c +++ b/src/network.c @@ -1699,19 +1699,19 @@ void check_servers(int no_loop_check) int reload_servers(char *fname) { FILE *f; - char *line; + char *line = NULL; + size_t linesz = 0; int gotone = 0; - /* buff happens to be MAXDNAMESTR long... */ if (!(f = fopen(fname, "r"))) { my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno)); return 0; } - + mark_servers(SERV_FROM_RESOLV); - - while ((line = fgets(daemon->namebuff, MAXDNAMESTR, f))) + + while (get_line_alloc(f, &line, &linesz)) { union mysockaddr addr, source_addr; char *token = strtok(line, " \t\n\r"); diff --git a/src/option.c b/src/option.c index 2bb9e91..1ba8335 100644 --- a/src/option.c +++ b/src/option.c @@ -5471,9 +5471,20 @@ err: static void read_file(char *file, FILE *f, int hard_opt, int from_script) { volatile int lineno = 0; - char *buff = daemon->namebuff; + char *buff; + size_t buffsz; + + /* Memory allocation failure longjmps here if mem_recover == 1 */ + if (hard_opt != 0) + { + setjmp(mem_jmp); + mem_recover = 1; + } - while (fgets(buff, MAXDNAMESTR, f)) + buff = NULL; + buffsz = 0; + + while (get_line_alloc(f, &buff, &buffsz)) { int white, i; volatile int option; @@ -5481,15 +5492,7 @@ static void read_file(char *file, FILE *f, int hard_opt, int from_script) size_t len; option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt; - - /* Memory allocation failure longjmps here if mem_recover == 1 */ - if (option != 0 || hard_opt == LOPT_REV_SERV) - { - if (setjmp(mem_jmp)) - continue; - mem_recover = 1; - } - + arg = NULL; lineno++; errmess = NULL; @@ -5949,16 +5952,15 @@ void reread_dhcp(void) void read_opts(int argc, char **argv, char *compile_opts) { size_t argbuf_size = 300; - char *argbuf = opt_malloc(argbuf_size); - char *buff = opt_malloc(MAXDNAMESTR+1); + char *argbuf = safe_malloc(argbuf_size); int option, testmode = 0; char *arg, *conffile = NULL; opterr = 0; - + daemon = opt_malloc(sizeof(struct daemon)); memset(daemon, 0, sizeof(struct daemon)); - daemon->namebuff = buff; + daemon->namebuff = safe_malloc(MAXDNAMESTR+1); daemon->workspacename = safe_malloc(MAXDNAMESTR+1); daemon->addrbuff = safe_malloc(ADDRSTRLEN); @@ -6197,9 +6199,9 @@ void read_opts(int argc, char **argv, char *compile_opts) /* create default, if not specified */ if (daemon->authserver && !daemon->hostmaster) { - strcpy(buff, "hostmaster."); - strcat(buff, daemon->authserver); - daemon->hostmaster = opt_string_alloc(buff); + strcpy(daemon->namebuff, "hostmaster."); + strncat(daemon->namebuff, daemon->authserver, MAXDNAMESTR - strlen(daemon->namebuff)); + daemon->hostmaster = opt_string_alloc(daemon->namebuff); } if (!daemon->dhcp_pxe_vendors) @@ -6214,11 +6216,11 @@ void read_opts(int argc, char **argv, char *compile_opts) { struct mx_srv_record *mx; - if (gethostname(buff, MAXDNAMESTR) == -1) + if (gethostname(daemon->namebuff, MAXDNAMESTR) == -1) die(_("cannot get host-name: %s"), NULL, EC_MISC); for (mx = daemon->mxnames; mx; mx = mx->next) - if (!mx->issrv && hostname_isequal(mx->name, buff)) + if (!mx->issrv && hostname_isequal(mx->name, daemon->namebuff)) break; if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx) @@ -6227,12 +6229,12 @@ void read_opts(int argc, char **argv, char *compile_opts) mx->next = daemon->mxnames; mx->issrv = 0; mx->target = NULL; - mx->name = opt_string_alloc(buff); + mx->name = opt_string_alloc(daemon->namebuff); daemon->mxnames = mx; } if (!daemon->mxtarget) - daemon->mxtarget = opt_string_alloc(buff); + daemon->mxtarget = opt_string_alloc(daemon->namebuff); for (mx = daemon->mxnames; mx; mx = mx->next) if (!mx->issrv && !mx->target) @@ -6247,7 +6249,8 @@ void read_opts(int argc, char **argv, char *compile_opts) if (option_bool(OPT_RESOLV_DOMAIN)) { - char *line; + char *line = NULL; + size_t linesz = 0; FILE *f; if (option_bool(OPT_NO_RESOLV) || @@ -6258,7 +6261,7 @@ void read_opts(int argc, char **argv, char *compile_opts) if (!(f = fopen((daemon->resolv_files)->name, "r"))) die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE); - while ((line = fgets(buff, MAXDNAMESTR, f))) + while (get_line_alloc(f, &line, &linesz)) { char *token = strtok(line, " \t\n\r"); @@ -6278,7 +6281,7 @@ void read_opts(int argc, char **argv, char *compile_opts) if (daemon->domain_suffix) { - /* add domain for any srv record without one. */ + /* add domain for any srv record without one. */ struct mx_srv_record *srv; for (srv = daemon->mxnames; srv; srv = srv->next) @@ -6288,11 +6291,11 @@ void read_opts(int argc, char **argv, char *compile_opts) { if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAMESTR) die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC); - strcpy(buff, srv->name); - strcat(buff, "."); - strcat(buff, daemon->domain_suffix); + strcpy(daemon->namebuff, srv->name); + strcat(daemon->namebuff, "."); + strcat(daemon->namebuff, daemon->domain_suffix); free(srv->name); - if (!(srv->name = canonicalise_opt(buff))) + if (!(srv->name = canonicalise_opt(daemon->namebuff))) die(_("bad srv-host name %s after domain appended"), srv->name, EC_MISC); } } diff --git a/src/util.c b/src/util.c index fe6624c..b41b7c1 100644 --- a/src/util.c +++ b/src/util.c @@ -1027,3 +1027,53 @@ void free_real(const char *func, unsigned int line, void *ptr) free(ptr); } } + +/* get lines from f, expand buffer as required. Buffer is freed when + EOF reached and we return zero. */ +int get_line_alloc(FILE *f, char **buffp, size_t *sizep) +{ + char c; + size_t cnt = 0; + char *buff = *buffp; + + while (1) { + c = getc(f); + + if (cnt == *sizep) + { + void *new; + + if ((new = whine_realloc(*buffp, cnt + 1024))) + { + buff = *buffp = new; + *sizep = cnt + 1024; + } + else + { + /* allocation failed, ignore line. Whine_realloc will have complained. */ + while (c != '\n' && c != EOF) + c = getc(f); + + cnt = 0; + continue; + } + } + + buff[cnt] = 0; + + if (c == '\n') + return 1; + + if (c == EOF) /* handle last line with no '\n' */ + { + /* handle last line without '\n' */ + if (cnt != 0) + return 1; + + free(buff); + return 0; + } + + buff[cnt++] = c; + } +}