diff --git a/src/shrpx.cc b/src/shrpx.cc index 018073bd..454206a4 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -146,52 +146,6 @@ struct SignalServer { pid_t worker_process_pid; }; -namespace { -int resolve_hostname(Address *addr, const char *hostname, uint16_t port, - int family) { - int rv; - - auto service = util::utos(port); - - addrinfo hints{}; - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif // AI_ADDRCONFIG - addrinfo *res; - - rv = getaddrinfo(hostname, service.c_str(), &hints, &res); - if (rv != 0) { - LOG(FATAL) << "Unable to resolve address for " << hostname << ": " - << gai_strerror(rv); - return -1; - } - - auto res_d = defer(freeaddrinfo, res); - - char host[NI_MAXHOST]; - rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr, - 0, NI_NUMERICHOST); - if (rv != 0) { - LOG(FATAL) << "Address resolution for " << hostname - << " failed: " << gai_strerror(rv); - - return -1; - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Address resolution for " << hostname - << " succeeded: " << host; - } - - memcpy(&addr->su, res->ai_addr, res->ai_addrlen); - addr->len = res->ai_addrlen; - - return 0; -} -} // namespace - namespace { int chown_to_running_user(const char *path) { return chown(path, get_config()->uid, get_config()->gid); @@ -1075,11 +1029,6 @@ constexpr auto DEFAULT_ACCESSLOG_FORMAT = StringRef::from_lit( R"("$http_referer" "$http_user_agent")"); } // namespace -namespace { -constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1"; -constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80; -} // namespace; - namespace { void fill_default_config() { *mod_config() = {}; @@ -2151,7 +2100,6 @@ void process_options(int argc, char **argv, auto &listenerconf = mod_config()->conn.listener; auto &upstreamconf = mod_config()->conn.upstream; - auto &downstreamconf = mod_config()->conn.downstream; if (listenerconf.addrs.empty()) { UpstreamAddr addr{}; @@ -2185,140 +2133,11 @@ void process_options(int argc, char **argv, } } - auto &addr_groups = downstreamconf.addr_groups; - - if (addr_groups.empty()) { - DownstreamAddrConfig addr{}; - addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); - addr.port = DEFAULT_DOWNSTREAM_PORT; - addr.proto = PROTO_HTTP1; - - DownstreamAddrGroupConfig g(StringRef::from_lit("/")); - g.addrs.push_back(std::move(addr)); - mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size()); - addr_groups.push_back(std::move(g)); - } else if (get_config()->http2_proxy) { - // We don't support host mapping in these cases. Move all - // non-catch-all patterns to catch-all pattern. - DownstreamAddrGroupConfig catch_all(StringRef::from_lit("/")); - for (auto &g : addr_groups) { - std::move(std::begin(g.addrs), std::end(g.addrs), - std::back_inserter(catch_all.addrs)); - } - std::vector().swap(addr_groups); - std::vector().swap(mod_config()->wildcard_patterns); - // maybe not necessary? - mod_config()->router = Router(); - mod_config()->router.add_route(StringRef{catch_all.pattern}, - addr_groups.size()); - addr_groups.push_back(std::move(catch_all)); - } else { - auto &wildcard_patterns = mod_config()->wildcard_patterns; - std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns), - [](const WildcardPattern &lhs, const WildcardPattern &rhs) { - return std::lexicographical_compare( - rhs.host.rbegin(), rhs.host.rend(), lhs.host.rbegin(), - lhs.host.rend()); - }); - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Reverse sorted wildcard hosts (compared from tail to head, " - "and sorted in reverse order):"; - for (auto &wp : mod_config()->wildcard_patterns) { - LOG(INFO) << wp.host; - } - } - } - - // backward compatibility: override all SNI fields with the option - // value --backend-tls-sni-field - if (!tlsconf.backend_sni_name.empty()) { - auto &sni = tlsconf.backend_sni_name; - for (auto &addr_group : addr_groups) { - for (auto &addr : addr_group.addrs) { - addr.sni = sni; - } - } - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Resolving backend address"; - } - - ssize_t catch_all_group = -1; - for (size_t i = 0; i < addr_groups.size(); ++i) { - auto &g = addr_groups[i]; - if (g.pattern == StringRef::from_lit("/")) { - catch_all_group = i; - } - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern - << "'"; - for (auto &addr : g.addrs) { - LOG(INFO) << "group " << i << " -> " << addr.host.c_str() - << (addr.host_unix ? "" : ":" + util::utos(addr.port)) - << ", proto=" << strproto(addr.proto) - << (addr.tls ? ", tls" : ""); - } - } - } - - if (catch_all_group == -1) { - LOG(FATAL) << "backend: No catch-all backend address is configured"; + if (configure_downstream_group(mod_config(), get_config()->http2_proxy, false, + tlsconf) != 0) { exit(EXIT_FAILURE); } - downstreamconf.addr_group_catch_all = catch_all_group; - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Catch-all pattern is group " << catch_all_group; - } - - for (auto &g : addr_groups) { - for (auto &addr : g.addrs) { - - if (addr.host_unix) { - // for AF_UNIX socket, we use "localhost" as host for backend - // hostport. This is used as Host header field to backend and - // not going to be passed to any syscalls. - addr.hostport = "localhost"; - - auto path = addr.host.c_str(); - auto pathlen = addr.host.size(); - - if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { - LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " - << sizeof(addr.addr.su.un.sun_path); - exit(EXIT_FAILURE); - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Use UNIX domain socket path " << path - << " for backend connection"; - } - - addr.addr.su.un.sun_family = AF_UNIX; - // copy path including terminal NULL - std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path); - addr.addr.len = sizeof(addr.addr.su.un); - - continue; - } - - addr.hostport = ImmutableString( - util::make_http_hostport(StringRef(addr.host), addr.port)); - - auto hostport = util::make_hostport(StringRef{addr.host}, addr.port); - - if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port, - downstreamconf.family) == -1) { - LOG(FATAL) << "Resolving backend address failed: " << hostport; - exit(EXIT_FAILURE); - } - LOG(NOTICE) << "Resolved backend address: " << hostport << " -> " - << util::to_numeric_addr(&addr.addr); - } - } - auto &proxy = mod_config()->downstream_http_proxy; if (!proxy.host.empty()) { auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port); diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 5dba902e..f7140ab1 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -2904,4 +2904,193 @@ StringRef strproto(shrpx_proto proto) { assert(0); } +// Configures the following member in |config|: router, +// conn.downstream.addr_groups, wildcard_patterns, +int configure_downstream_group(Config *config, bool http2_proxy, + bool numeric_addr_only, + const TLSConfig &tlsconf) { + auto &downstreamconf = config->conn.downstream; + auto &addr_groups = downstreamconf.addr_groups; + + if (addr_groups.empty()) { + DownstreamAddrConfig addr{}; + addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); + addr.port = DEFAULT_DOWNSTREAM_PORT; + addr.proto = PROTO_HTTP1; + + DownstreamAddrGroupConfig g(StringRef::from_lit("/")); + g.addrs.push_back(std::move(addr)); + config->router.add_route(StringRef{g.pattern}, addr_groups.size()); + addr_groups.push_back(std::move(g)); + } else if (http2_proxy) { + // We don't support host mapping in these cases. Move all + // non-catch-all patterns to catch-all pattern. + DownstreamAddrGroupConfig catch_all(StringRef::from_lit("/")); + for (auto &g : addr_groups) { + std::move(std::begin(g.addrs), std::end(g.addrs), + std::back_inserter(catch_all.addrs)); + } + std::vector().swap(addr_groups); + std::vector().swap(config->wildcard_patterns); + // maybe not necessary? + config->router = Router(); + config->router.add_route(StringRef{catch_all.pattern}, addr_groups.size()); + addr_groups.push_back(std::move(catch_all)); + } else { + auto &wildcard_patterns = config->wildcard_patterns; + std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns), + [](const WildcardPattern &lhs, const WildcardPattern &rhs) { + return std::lexicographical_compare( + rhs.host.rbegin(), rhs.host.rend(), lhs.host.rbegin(), + lhs.host.rend()); + }); + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Reverse sorted wildcard hosts (compared from tail to head, " + "and sorted in reverse order):"; + for (auto &wp : config->wildcard_patterns) { + LOG(INFO) << wp.host; + } + } + } + + // backward compatibility: override all SNI fields with the option + // value --backend-tls-sni-field + if (!tlsconf.backend_sni_name.empty()) { + auto &sni = tlsconf.backend_sni_name; + for (auto &addr_group : addr_groups) { + for (auto &addr : addr_group.addrs) { + addr.sni = sni; + } + } + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend address"; + } + + ssize_t catch_all_group = -1; + for (size_t i = 0; i < addr_groups.size(); ++i) { + auto &g = addr_groups[i]; + if (g.pattern == StringRef::from_lit("/")) { + catch_all_group = i; + } + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern + << "'"; + for (auto &addr : g.addrs) { + LOG(INFO) << "group " << i << " -> " << addr.host.c_str() + << (addr.host_unix ? "" : ":" + util::utos(addr.port)) + << ", proto=" << strproto(addr.proto) + << (addr.tls ? ", tls" : ""); + } + } + } + + if (catch_all_group == -1) { + LOG(FATAL) << "backend: No catch-all backend address is configured"; + return -1; + } + + downstreamconf.addr_group_catch_all = catch_all_group; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Catch-all pattern is group " << catch_all_group; + } + + auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST : 0; + + for (auto &g : addr_groups) { + for (auto &addr : g.addrs) { + + if (addr.host_unix) { + // for AF_UNIX socket, we use "localhost" as host for backend + // hostport. This is used as Host header field to backend and + // not going to be passed to any syscalls. + addr.hostport = "localhost"; + + auto path = addr.host.c_str(); + auto pathlen = addr.host.size(); + + if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { + LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " + << sizeof(addr.addr.su.un.sun_path); + return -1; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Use UNIX domain socket path " << path + << " for backend connection"; + } + + addr.addr.su.un.sun_family = AF_UNIX; + // copy path including terminal NULL + std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path); + addr.addr.len = sizeof(addr.addr.su.un); + + continue; + } + + addr.hostport = ImmutableString( + util::make_http_hostport(StringRef(addr.host), addr.port)); + + auto hostport = util::make_hostport(StringRef{addr.host}, addr.port); + + if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port, + downstreamconf.family, resolve_flags) == -1) { + LOG(FATAL) << "Resolving backend address failed: " << hostport; + return -1; + } + LOG(NOTICE) << "Resolved backend address: " << hostport << " -> " + << util::to_numeric_addr(&addr.addr); + } + } + + return 0; +} + +int resolve_hostname(Address *addr, const char *hostname, uint16_t port, + int family, int additional_flags) { + int rv; + + auto service = util::utos(port); + + addrinfo hints{}; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= additional_flags; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + addrinfo *res; + + rv = getaddrinfo(hostname, service.c_str(), &hints, &res); + if (rv != 0) { + LOG(FATAL) << "Unable to resolve address for " << hostname << ": " + << gai_strerror(rv); + return -1; + } + + auto res_d = defer(freeaddrinfo, res); + + char host[NI_MAXHOST]; + rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr, + 0, NI_NUMERICHOST); + if (rv != 0) { + LOG(FATAL) << "Address resolution for " << hostname + << " failed: " << gai_strerror(rv); + + return -1; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Address resolution for " << hostname + << " succeeded: " << host; + } + + memcpy(&addr->su, res->ai_addr, res->ai_addrlen); + addr->len = res->ai_addrlen; + + return 0; +} + } // namespace shrpx diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 477fa95e..605949a5 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -283,6 +283,9 @@ constexpr auto SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT = constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; +constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1"; +constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80; + enum shrpx_proto { PROTO_NONE, PROTO_HTTP1, PROTO_HTTP2, PROTO_MEMCACHED }; enum shrpx_forwarded_param { @@ -713,6 +716,13 @@ read_tls_ticket_key_file(const std::vector &files, // Returns string representation of |proto|. StringRef strproto(shrpx_proto proto); +int configure_downstream_group(Config *config, bool http2_proxy, + bool numeric_addr_only, + const TLSConfig &tlsconf); + +int resolve_hostname(Address *addr, const char *hostname, uint16_t port, + int family, int additional_flags = 0); + } // namespace shrpx #endif // SHRPX_CONFIG_H