From 006dd63ff8db254b115463150da95de33fd352fa Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 2 May 2016 23:05:07 +0100 Subject: [PATCH] noip.c: Factor out address-range handling functions. * Introduce `parse_addrrange' and `foreach_addrrange' for parsing. The former captures a representation of the range syntax, which can contain things like `local' that actually cover multiple ranges, and the latter iterates over the implied address ranges. * Introduce `dump_addrrange' to produce a readable description of a range in the debugging output. No functional changes. --- noip.c | 211 ++++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 136 insertions(+), 75 deletions(-) diff --git a/noip.c b/noip.c index 3756ab9..423822d 100644 --- a/noip.c +++ b/noip.c @@ -89,6 +89,15 @@ typedef struct aclnode { unsigned short minport, maxport; } aclnode; +/* A type for an address range */ +typedef struct addrrange { + int type; + union { + struct { int af; ipaddr min, max; } range; + } u; +} addrrange; +enum { EMPTY, ANY, LOCAL, RANGE }; + /* Local address records */ typedef struct full_ipaddr { int af; @@ -507,22 +516,27 @@ static int parse_sockaddr(struct sockaddr *sa, const char *p) #ifdef DEBUG -/* Write to standard error a description of the ACL node A. */ -static void dump_aclnode(const aclnode *a) +static void dump_addrrange(int af, const ipaddr *min, const ipaddr *max) { char buf[ADDRBUFSZ]; const char *p; int plen; - fprintf(stderr, "noip(%d): %c ", getpid(), a->act ? '+' : '-'); - plen = common_prefix_length(a->af, &a->minaddr, &a->maxaddr); - p = inet_ntop(a->af, &a->minaddr, buf, sizeof(buf)); + plen = common_prefix_length(af, min, max); + p = inet_ntop(af, min, buf, sizeof(buf)); fprintf(stderr, strchr(p, ':') ? "[%s]" : "%s", p); if (plen < 0) { - p = inet_ntop(a->af, &a->maxaddr, buf, sizeof(buf)); + p = inet_ntop(af, &max, buf, sizeof(buf)); fprintf(stderr, strchr(p, ':') ? "-[%s]" : "-%s", p); - } else if (plen < address_width(a->af)) + } else if (plen < address_width(af)) fprintf(stderr, "/%d", plen); +} + +/* Write to standard error a description of the ACL node A. */ +static void dump_aclnode(const aclnode *a) +{ + fprintf(stderr, "noip(%d): %c ", getpid(), a->act ? '+' : '-'); + dump_addrrange(a->af, &a->minaddr, &a->maxaddr); if (a->minport != 0 || a->maxport != 0xffff) { fprintf(stderr, ":%u", (unsigned)a->minport); if (a->minport != a->maxport) @@ -985,92 +999,139 @@ static void parse_ports(char **pp, unsigned short *min, unsigned short *max) *pp = p; } -/* Make a new ACL node. ACT is the verdict; AF is the address family; - * MINADDR and MAXADDR are the ranges on IP addresses; MINPORT and MAXPORT - * are the ranges on port numbers; TAIL is the list tail to attach the new - * node to. - */ -#define ACLNODE(tail_, act_, \ - af_, minaddr_, maxaddr_, minport_, maxport_) do { \ - aclnode *a_; \ - NEW(a_); \ - a_->act = (act_); \ - a_->af = (af_); \ - a_->minaddr = (minaddr_); a_->maxaddr = (maxaddr_); \ - a_->minport = (minport_); a_->maxport = (maxport_); \ - *tail_ = a_; tail_ = &a_->next; \ -} while (0) - -/* Parse an ACL line. *PP points to the end of the line; *TAIL points to - * the list tail (i.e., the final link in the list). An ACL entry has the - * form +|- [any | local | ADDR | ADDR - ADDR | ADDR/ADDR | ADDR/INT] PORTS - * where PORTS is parsed by parse_ports above; an ACL line consists of a - * comma-separated sequence of entries.. +/* Parse an address range designator starting at PP and store a + * representation of it in R. An address range designator has the form: + * + * any | local | ADDR | ADDR - ADDR | ADDR/ADDR | ADDR/INT */ -static void parse_acl_line(char **pp, aclnode ***tail) +static int parse_addrrange(char **pp, addrrange *r) { - ipaddr minaddr, maxaddr; - unsigned short minport, maxport; - int i, af, n; - int act; + char *p = *pp, *q; + int n; int del; - char *p = *pp; - char *q; + int af; - for (;;) { + SKIPSPC; + if (KWMATCHP("any")) r->type = ANY; + else if (KWMATCHP("local")) r->type = LOCAL; + else { + parse_nextaddr(&p, &q, &del); + af = guess_address_family(q); + if (inet_pton(af, q, &r->u.range.min) <= 0) goto bad; + RESCAN(del); SKIPSPC; - if (*p == '+') act = ALLOW; - else if (*p == '-') act = DENY; - else goto bad; + if (*p == '-') { + p++; + parse_nextaddr(&p, &q, &del); + if (inet_pton(af, q, &r->u.range.max) <= 0) goto bad; + RESCAN(del); + } else if (*p == '/') { + p++; + NEXTNUMBER(q, del); + n = strtoul(q, 0, 0); + r->u.range.max = r->u.range.min; + mask_address(af, &r->u.range.min, n, 0); + mask_address(af, &r->u.range.max, n, 1); + RESCAN(del); + } else + r->u.range.max = r->u.range.min; + r->type = RANGE; + r->u.range.af = af; + } + *pp = p; + return (0); - p++; - SKIPSPC; - if (KWMATCHP("any")) { - parse_ports(&p, &minport, &maxport); +bad: + return (-1); +} + +/* Call FUNC on each individual address range in R. */ +static void foreach_addrrange(const addrrange *r, + void (*func)(int af, + const ipaddr *min, + const ipaddr *max, + void *p), + void *p) +{ + ipaddr minaddr, maxaddr; + int i, af; + + switch (r->type) { + case EMPTY: + break; + case ANY: for (i = 0; address_families[i] >= 0; i++) { af = address_families[i]; memset(&minaddr, 0, sizeof(minaddr)); maxaddr = minaddr; mask_address(af, &maxaddr, 0, 1); - ACLNODE(*tail, act, af, minaddr, maxaddr, minport, maxport); + func(af, &minaddr, &maxaddr, p); } - } else if (KWMATCHP("local")) { - parse_ports(&p, &minport, &maxport); + break; + case LOCAL: for (i = 0; address_families[i] >= 0; i++) { af = address_families[i]; memset(&minaddr, 0, sizeof(minaddr)); maxaddr = minaddr; mask_address(af, &maxaddr, 0, 1); - ACLNODE(*tail, act, af, minaddr, minaddr, minport, maxport); - ACLNODE(*tail, act, af, maxaddr, maxaddr, minport, maxport); + func(af, &minaddr, &minaddr, p); + func(af, &maxaddr, &maxaddr, p); } for (i = 0; i < n_local_ipaddrs; i++) { - ACLNODE(*tail, act, local_ipaddrs[i].af, - local_ipaddrs[i].addr, local_ipaddrs[i].addr, - minport, maxport); + func(local_ipaddrs[i].af, + &local_ipaddrs[i].addr, &local_ipaddrs[i].addr, + p); } - } else { - parse_nextaddr(&p, &q, &del); - af = guess_address_family(q); - if (inet_pton(af, q, &minaddr) <= 0) goto bad; - RESCAN(del); - SKIPSPC; - if (*p == '-') { - p++; - parse_nextaddr(&p, &q, &del); - if (inet_pton(af, q, &maxaddr) <= 0) goto bad; - RESCAN(del); - } else if (*p == '/') { - p++; - NEXTNUMBER(q, del); - n = strtoul(q, 0, 0); - maxaddr = minaddr; - mask_address(af, &minaddr, n, 0); - mask_address(af, &maxaddr, n, 1); - RESCAN(del); - } else - maxaddr = minaddr; - parse_ports(&p, &minport, &maxport); - ACLNODE(*tail, act, af, minaddr, maxaddr, minport, maxport); - } + break; + case RANGE: + func(r->u.range.af, &r->u.range.min, &r->u.range.max, p); + break; + default: + abort(); + } +} + +struct add_aclnode_ctx { + int act; + unsigned short minport, maxport; + aclnode ***tail; +}; + +static void add_aclnode(int af, const ipaddr *min, const ipaddr *max, + void *p) +{ + struct add_aclnode_ctx *ctx = p; + aclnode *a; + + NEW(a); + a->act = ctx->act; + a->af = af; + a->minaddr = *min; a->maxaddr = *max; + a->minport = ctx->minport; a->maxport = ctx->maxport; + **ctx->tail = a; *ctx->tail = &a->next; +} + +/* Parse an ACL line. *PP points to the end of the line; *TAIL points to + * the list tail (i.e., the final link in the list). An ACL entry has the + * form +|- ADDR-RANGE PORTS + * where PORTS is parsed by parse_ports above; an ACL line consists of a + * comma-separated sequence of entries.. + */ +static void parse_acl_line(char **pp, aclnode ***tail) +{ + struct add_aclnode_ctx ctx; + addrrange r; + char *p = *pp; + + ctx.tail = tail; + for (;;) { + SKIPSPC; + if (*p == '+') ctx.act = ALLOW; + else if (*p == '-') ctx.act = DENY; + else goto bad; + + p++; + if (parse_addrrange(&p, &r)) goto bad; + parse_ports(&p, &ctx.minport, &ctx.maxport); + foreach_addrrange(&r, add_aclnode, &ctx); SKIPSPC; if (*p != ',') break; if (*p) p++; -- 2.11.0