From: phk Date: Fri, 11 Jul 2008 10:12:52 +0000 (+0000) Subject: Reject backend hostnames that resolve to multiple IPv4 or multiple X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d26516f8e6284905166677c33cdf04a091384c7;p=varnish Reject backend hostnames that resolve to multiple IPv4 or multiple IPv6 addresses, but accept one of each. Emit a bytestring representation of the sockaddr we found for each of these, into the shared object, so we avoid doing a DNS lookup again in the cacher. git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@2934 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- diff --git a/varnish-cache/include/vrt.h b/varnish-cache/include/vrt.h index 8bf11690..e7290dcb 100644 --- a/varnish-cache/include/vrt.h +++ b/varnish-cache/include/vrt.h @@ -57,6 +57,10 @@ struct vrt_backend_probe { struct vrt_backend { char *portname; char *hostname; + + const unsigned char *ipv4_sockaddr; + const unsigned char *ipv6_sockaddr; + char *vcl_name; char *ident; double connect_timeout; diff --git a/varnish-cache/lib/libvcl/vcc_backend.c b/varnish-cache/lib/libvcl/vcc_backend.c index 240f76d7..7c410bfa 100644 --- a/varnish-cache/lib/libvcl/vcc_backend.c +++ b/varnish-cache/lib/libvcl/vcc_backend.c @@ -88,6 +88,94 @@ CheckHostPort(const char *host, const char *port) return (NULL); } +/*-------------------------------------------------------------------- + * Struct sockaddr is not really designed to be a compile time + * initialized data structure, so we encode it as a byte-string + * and put it in an official sockaddr when we load the VCL. + */ + +static void +Emit_Sockaddr(struct tokenlist *tl, const struct token *t_host, const char *port) +{ + struct addrinfo *res, *res0, hint; + int n4, n6, len, error, retval; + const char *emit, *multiple; + unsigned char *u; + char hbuf[NI_MAXHOST]; + + AN(t_host->dec); + retval = 0; + memset(&hint, 0, sizeof hint); + hint.ai_family = PF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + error = getaddrinfo(t_host->dec, port, &hint, &res0); + AZ(error); + n4 = n6 = 0; + multiple = NULL; + for (res = res0; res; res = res->ai_next) { + emit = NULL; + if (res->ai_family == PF_INET) { + if (n4++ == 0) + emit = "ipv4_sockaddr"; + else + multiple = "IPv4"; + } else if (res->ai_family == PF_INET6) { + if (n6++ == 0) + emit = "ipv6_sockaddr"; + else + multiple = "IPv6"; + } else + continue; + + if (multiple != NULL) { + vsb_printf(tl->sb, + "Backend host %.*s: resolves to " + "multiple %s addresses.\n" + "Only one address is allowed.\n" + "Please specify which exact address " + "you want to use, we found these:\n", + PF(t_host), multiple); + for (res = res0; res; res = res->ai_next) { + error = getnameinfo(res->ai_addr, + res->ai_addrlen, hbuf, sizeof hbuf, + NULL, 0, NI_NUMERICHOST); + AZ(error); + vsb_printf(tl->sb, "\t%s\n", hbuf); + } + vcc_ErrWhere(tl, t_host); + return; + } + AN(emit); + AN(res->ai_addr); + AN(res->ai_addrlen); + assert(res->ai_addrlen < 256); + Fh(tl, 0, "\nstatic const unsigned char sockaddr%u[%d] = {\n", + tl->nsockaddr, res->ai_addrlen + 1); + Fh(tl, 0, " %3u, /* Length */\n", res->ai_addrlen); + u = (void*)res->ai_addr; + for (len = 0; len < res->ai_addrlen; len++) { + if ((len % 8) == 0) + Fh(tl, 0, " "); + Fh(tl, 0, " %3u", u[len]); + if (len + 1 < res->ai_addrlen) + Fh(tl, 0, ","); + if ((len % 8) == 7) + Fh(tl, 0, "\n"); + } + Fh(tl, 0, "\n};\n"); + Fb(tl, 0, "\t.%s = sockaddr%u,\n", emit, tl->nsockaddr++); + retval++; + } + freeaddrinfo(res0); + if (retval == 0) { + vsb_printf(tl->sb, + "Backend host '%.*s': resolves to " + "neither IPv4 nor IPv6 addresses.\n", + PF(t_host) ); + vcc_ErrWhere(tl, t_host); + } +} + /*-------------------------------------------------------------------- * When a new VCL is loaded, it is likely to contain backend declarations * identical to other loaded VCL programs, and we want to reuse the state @@ -266,7 +354,7 @@ vcc_ParseProbe(struct tokenlist *tl) ExpectErr(tl, CSTR); Fh(tl, 0, "\t\t.request =\n"); Fh(tl, 0, "\t\t\t\"GET \" "); - EncToken(tl->fh, tl->t); + EncToken(tl->fb, tl->t); Fh(tl, 0, " \" /HTTP/1.1\\r\\n\"\n"); Fh(tl, 0, "\t\t\t\"Connection: close\\r\\n\"\n"); Fh(tl, 0, "\t\t\t\"\\r\\n\",\n"); @@ -278,23 +366,19 @@ vcc_ParseProbe(struct tokenlist *tl) Fh(tl, 0, "\t\t.request =\n"); while (tl->t->tok == CSTR) { Fh(tl, 0, "\t\t\t"); - EncToken(tl->fh, tl->t); + EncToken(tl->fb, tl->t); Fh(tl, 0, " \"\\r\\n\"\n"); vcc_NextToken(tl); } Fh(tl, 0, "\t\t\t\"\\r\\n\",\n"); } else if (vcc_IdIs(t_field, "timeout")) { Fh(tl, 0, "\t\t.timeout = "); - tl->fb = tl->fh; vcc_TimeVal(tl); - tl->fb = NULL; ERRCHK(tl); Fh(tl, 0, ",\n"); } else if (vcc_IdIs(t_field, "rate")) { Fh(tl, 0, "\t\t.rate = "); - tl->fb = tl->fh; vcc_TimeVal(tl); - tl->fb = NULL; ERRCHK(tl); Fh(tl, 0, ",\n"); } else { @@ -339,6 +423,7 @@ vcc_ParseHostDef(struct tokenlist *tl, int *nbh, const struct token *name, const struct token *t_port = NULL; const char *ep; struct fld_spec *fs; + struct vsb *vsb; fs = vcc_FldSpec(tl, "!host", "?port", "?connect_timeout", "?probe", NULL); @@ -347,8 +432,12 @@ vcc_ParseHostDef(struct tokenlist *tl, int *nbh, const struct token *name, const ExpectErr(tl, '{'); vcc_NextToken(tl); + vsb = vsb_newauto(); + AN(vsb); + tl->fb = vsb; + *nbh = tl->nbackend_host++; - Fh(tl, 0, "\nstatic const struct vrt_backend bh_%d = {\n", *nbh); + Fb(tl, 0, "\nstatic const struct vrt_backend bh_%d = {\n", *nbh); /* Check for old syntax */ if (tl->t->tok == ID && vcc_IdIs(tl->t, "set")) { @@ -383,12 +472,10 @@ vcc_ParseHostDef(struct tokenlist *tl, int *nbh, const struct token *name, const ExpectErr(tl, ';'); vcc_NextToken(tl); } else if (vcc_IdIs(t_field, "connect_timeout")) { - Fh(tl, 0, "\t.connect_timeout = "); - tl->fb = tl->fh; + Fb(tl, 0, "\t.connect_timeout = "); vcc_TimeVal(tl); - tl->fb = NULL; ERRCHK(tl); - Fh(tl, 0, ",\n"); + Fb(tl, 0, ",\n"); ExpectErr(tl, ';'); vcc_NextToken(tl); } else if (vcc_IdIs(t_field, "probe")) { @@ -412,33 +499,42 @@ vcc_ParseHostDef(struct tokenlist *tl, int *nbh, const struct token *name, const vcc_ErrWhere(tl, t_host); return; } - Fh(tl, 0, "\t.hostname = "); - EncToken(tl->fh, t_host); - Fh(tl, 0, ",\n"); + Fb(tl, 0, "\t.hostname = "); + EncToken(tl->fb, t_host); + Fb(tl, 0, ",\n"); /* Check that the portname makes sense */ if (t_port != NULL) { - ep = CheckHostPort(t_host->dec, t_port->dec); + ep = CheckHostPort("localhost", t_port->dec); if (ep != NULL) { vsb_printf(tl->sb, "Backend port '%.*s': %s\n", PF(t_port), ep); vcc_ErrWhere(tl, t_port); return; } - Fh(tl, 0, "\t.portname = "); - EncToken(tl->fh, t_port); - Fh(tl, 0, ",\n"); + Fb(tl, 0, "\t.portname = "); + EncToken(tl->fb, t_port); + Fb(tl, 0, ",\n"); + Emit_Sockaddr(tl, t_host, t_port->dec); } else { - Fh(tl, 0, "\t.portname = \"80\",\n"); + Fb(tl, 0, "\t.portname = \"80\",\n"); + Emit_Sockaddr(tl, t_host, "80"); } + ERRCHK(tl); ExpectErr(tl, '}'); - vcc_EmitBeIdent(tl->fh, name, qual, serial, t_first, tl->t); - Fh(tl, 0, "\t.vcl_name = \"%.*s", PF(name)); + vcc_EmitBeIdent(tl->fb, name, qual, serial, t_first, tl->t); + Fb(tl, 0, "\t.vcl_name = \"%.*s", PF(name)); if (serial) - Fh(tl, 0, "[%d]", serial); - Fh(tl, 0, "\"\n};\n"); + Fb(tl, 0, "[%d]", serial); + Fb(tl, 0, "\"\n};\n"); vcc_NextToken(tl); + + tl->fb = NULL; + vsb_finish(vsb); + AZ(vsb_overflowed(vsb)); + Fh(tl, 0, "%s", vsb_data(vsb)); + vsb_delete(vsb); } /*-------------------------------------------------------------------- diff --git a/varnish-cache/lib/libvcl/vcc_compile.h b/varnish-cache/lib/libvcl/vcc_compile.h index 4fff65bc..911e511f 100644 --- a/varnish-cache/lib/libvcl/vcc_compile.h +++ b/varnish-cache/lib/libvcl/vcc_compile.h @@ -87,6 +87,7 @@ struct tokenlist { unsigned recnt; unsigned nhashcount; + unsigned nsockaddr; }; enum var_type { diff --git a/varnish-cache/lib/libvcl/vcc_fixed_token.c b/varnish-cache/lib/libvcl/vcc_fixed_token.c index accc74f2..905124d1 100644 --- a/varnish-cache/lib/libvcl/vcc_fixed_token.c +++ b/varnish-cache/lib/libvcl/vcc_fixed_token.c @@ -395,6 +395,10 @@ vcl_output_lang_h(struct vsb *sb) vsb_cat(sb, "struct vrt_backend {\n"); vsb_cat(sb, " char *portname;\n"); vsb_cat(sb, " char *hostname;\n"); + vsb_cat(sb, "\n"); + vsb_cat(sb, " const unsigned char *ipv4_sockaddr;\n"); + vsb_cat(sb, " const unsigned char *ipv6_sockaddr;\n"); + vsb_cat(sb, "\n"); vsb_cat(sb, " char *vcl_name;\n"); vsb_cat(sb, " char *ident;\n"); vsb_cat(sb, " double connect_timeout;\n");