+ 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++;
+ }
+ if (*p) goto bad;
+ *pp = p;
+ return;
+
+bad:
+ D( fprintf(stderr, "noip(%d): bad acl spec (ignored)\n", getpid()); )
+ return;
+}
+
+/* Parse an ACL from an environment variable VAR, attaching it to the list
+ * TAIL.
+ */
+static void parse_acl_env(const char *var, aclnode ***tail)
+{
+ char *p, *q;
+
+ if ((p = getenv(var)) != 0) {
+ p = q = xstrdup(p);
+ parse_acl_line(&q, tail);
+ free(p);
+ }
+}
+
+struct add_impbind_ctx {
+ int af, how;
+ ipaddr addr;
+};
+
+static void add_impbind(int af, const ipaddr *min, const ipaddr *max,
+ void *p)
+{
+ struct add_impbind_ctx *ctx = p;
+ impbind *i;
+
+ if (ctx->af && af != ctx->af) return;
+ NEW(i);
+ i->af = af;
+ i->how = ctx->how;
+ i->minaddr = *min; i->maxaddr = *max;
+ switch (ctx->how) {
+ case EXPLICIT: i->bindaddr = ctx->addr;
+ case SAME: break;
+ default: abort();
+ }
+ *impbind_tail = i; impbind_tail = &i->next;
+}
+
+/* Parse an implicit-bind line. An implicit-bind entry has the form
+ * ADDR-RANGE {ADDR | same}
+ */
+static void parse_impbind_line(char **pp)
+{
+ struct add_impbind_ctx ctx;
+ char *p = *pp, *q;
+ addrrange r;
+ int del;
+
+ for (;;) {
+ if (parse_addrrange(&p, &r)) goto bad;
+ SKIPSPC;
+ if (KWMATCHP("same")) {
+ ctx.how = SAME;
+ ctx.af = 0;