summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--src/event/ServerSocket.cxx10
-rw-r--r--src/net/AddressInfo.cxx81
-rw-r--r--src/net/AddressInfo.hxx142
-rw-r--r--src/net/HostParser.cxx140
-rw-r--r--src/net/HostParser.hxx76
-rw-r--r--src/net/Resolver.cxx181
-rw-r--r--src/net/Resolver.hxx69
-rw-r--r--test/run_resolver.cxx18
9 files changed, 613 insertions, 106 deletions
diff --git a/Makefile.am b/Makefile.am
index 30ba26786..db18baf69 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -594,7 +594,9 @@ libnet_a_SOURCES = \
src/net/Features.hxx \
src/net/Init.hxx \
src/net/ToString.cxx src/net/ToString.hxx \
+ src/net/HostParser.cxx src/net/HostParser.hxx \
src/net/Resolver.cxx src/net/Resolver.hxx \
+ src/net/AddressInfo.cxx src/net/AddressInfo.hxx \
src/net/StaticSocketAddress.cxx src/net/StaticSocketAddress.hxx \
src/net/AllocatedSocketAddress.cxx src/net/AllocatedSocketAddress.hxx \
src/net/IPv4Address.cxx src/net/IPv4Address.hxx \
diff --git a/src/event/ServerSocket.cxx b/src/event/ServerSocket.cxx
index 67919b913..e2a97f5cb 100644
--- a/src/event/ServerSocket.cxx
+++ b/src/event/ServerSocket.cxx
@@ -27,6 +27,7 @@
#include "net/SocketError.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/Resolver.hxx"
+#include "net/AddressInfo.hxx"
#include "net/ToString.hxx"
#include "event/SocketMonitor.hxx"
#include "fs/AllocatedPath.hxx"
@@ -369,12 +370,9 @@ void
ServerSocket::AddHost(const char *hostname, unsigned port)
{
#ifdef HAVE_TCP
- struct addrinfo *ai = resolve_host_port(hostname, port,
- AI_PASSIVE, SOCK_STREAM);
- AtScopeExit(ai) { freeaddrinfo(ai); };
-
- for (const struct addrinfo *i = ai; i != nullptr; i = i->ai_next)
- AddAddress(SocketAddress(i->ai_addr, i->ai_addrlen));
+ for (const auto &i : Resolve(hostname, port,
+ AI_PASSIVE, SOCK_STREAM))
+ AddAddress(i);
++next_serial;
#else /* HAVE_TCP */
diff --git a/src/net/AddressInfo.cxx b/src/net/AddressInfo.cxx
new file mode 100644
index 000000000..100214d8d
--- /dev/null
+++ b/src/net/AddressInfo.cxx
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016-2017 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AddressInfo.hxx"
+
+#include <assert.h>
+
+static constexpr int address_family_ranking[] = {
+#ifdef HAVE_UN
+ AF_LOCAL,
+#endif
+ AF_INET6,
+};
+
+static bool
+IsAddressFamilyBetter(int previous, int next)
+{
+ for (auto i : address_family_ranking) {
+ if (next == i)
+ return previous != i;
+ if (previous == i)
+ return false;
+ }
+
+ return false;
+}
+
+static bool
+IsBetter(const AddressInfo &previous, const AddressInfo &next)
+{
+ return IsAddressFamilyBetter(previous.GetFamily(),
+ next.GetFamily());
+}
+
+static bool
+IsBetter(const AddressInfo *previous, const AddressInfo &next)
+{
+ return previous == nullptr || IsBetter(*previous, next);
+}
+
+const AddressInfo &
+AddressInfoList::GetBest() const
+{
+ assert(!empty());
+
+ const AddressInfo *best = nullptr;
+
+ for (const auto &i : *this)
+ if (IsBetter(best, i))
+ best = &i;
+
+ assert(best != nullptr);
+ return *best;
+}
diff --git a/src/net/AddressInfo.hxx b/src/net/AddressInfo.hxx
new file mode 100644
index 000000000..63f8ebe64
--- /dev/null
+++ b/src/net/AddressInfo.hxx
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016-2017 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef NET_ADDRESS_INFO_HXX
+#define NET_ADDRESS_INFO_HXX
+
+#include "SocketAddress.hxx"
+
+#include <algorithm>
+#include <utility>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <netdb.h>
+#endif
+
+class AddressInfo : addrinfo {
+ /* this class cannot be instantiated, it can only be cast from
+ a struct addrinfo pointer */
+ AddressInfo() = delete;
+ ~AddressInfo() = delete;
+
+public:
+ constexpr int GetFamily() const {
+ return ai_family;
+ }
+
+ constexpr int GetType() const {
+ return ai_socktype;
+ }
+
+ constexpr int GetProtocol() const {
+ return ai_protocol;
+ }
+
+ constexpr operator SocketAddress() const {
+ return {ai_addr, (SocketAddress::size_type)ai_addrlen};
+ }
+};
+
+class AddressInfoList {
+ struct addrinfo *value = nullptr;
+
+public:
+ AddressInfoList() = default;
+ explicit AddressInfoList(struct addrinfo *_value):value(_value) {}
+
+ AddressInfoList(AddressInfoList &&src)
+ :value(std::exchange(src.value, nullptr)) {}
+
+ ~AddressInfoList() {
+ freeaddrinfo(value);
+ }
+
+ AddressInfoList &operator=(AddressInfoList &&src) {
+ std::swap(value, src.value);
+ return *this;
+ }
+
+ bool empty() const {
+ return value == nullptr;
+ }
+
+ const AddressInfo &front() const {
+ return *(const AddressInfo *)value;
+ }
+
+ /**
+ * Pick the best address from the list, e.g. prefer IPv6 over
+ * IPv4 (if both are available). We do this because binding
+ * to an IPv6 wildcard address also allows accepting IPv4
+ * connections.
+ */
+ gcc_pure
+ const AddressInfo &GetBest() const;
+
+ class const_iterator {
+ struct addrinfo *cursor;
+
+ public:
+ explicit constexpr const_iterator(struct addrinfo *_cursor)
+ :cursor(_cursor) {}
+
+ constexpr bool operator==(const_iterator other) const {
+ return cursor == other.cursor;
+ }
+
+ constexpr bool operator!=(const_iterator other) const {
+ return cursor != other.cursor;
+ }
+
+ const_iterator &operator++() {
+ cursor = cursor->ai_next;
+ return *this;
+ }
+
+ constexpr const AddressInfo &operator*() const {
+ return *(const AddressInfo *)cursor;
+ }
+
+ constexpr const AddressInfo *operator->() const {
+ return (const AddressInfo *)cursor;
+ }
+ };
+
+ const_iterator begin() const {
+ return const_iterator(value);
+ }
+
+ const_iterator end() const {
+ return const_iterator(nullptr);
+ }
+};
+
+#endif
diff --git a/src/net/HostParser.cxx b/src/net/HostParser.cxx
new file mode 100644
index 000000000..18bc986b3
--- /dev/null
+++ b/src/net/HostParser.cxx
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2007-2017 Content Management AG
+ * All rights reserved.
+ *
+ * author: Max Kellermann <mk@cm4all.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HostParser.hxx"
+#include "util/CharUtil.hxx"
+
+#include <string.h>
+
+static inline bool
+IsValidHostnameChar(char ch)
+{
+ return IsAlphaNumericASCII(ch) ||
+ ch == '-' || ch == '.' ||
+ ch == '*'; /* for wildcards */
+}
+
+static inline bool
+IsValidScopeChar(char ch)
+{
+ return IsAlphaNumericASCII(ch) ||
+ ch == '-' || ch == '_';
+}
+
+static const char *
+FindScopeEnd(const char *p)
+{
+ if (*p == '%' && IsValidScopeChar(p[1])) {
+ p += 2;
+ while (IsValidScopeChar(*p))
+ ++p;
+ }
+
+ return p;
+}
+
+static inline bool
+IsValidIPv6Char(char ch)
+{
+ return IsDigitASCII(ch) ||
+ (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F') ||
+ ch == ':';
+}
+
+static const char *
+FindIPv6End(const char *p)
+{
+ while (IsValidIPv6Char(*p))
+ ++p;
+
+ /* allow "%scope" after numeric IPv6 address */
+ p = FindScopeEnd(p);
+
+ return p;
+}
+
+ExtractHostResult
+ExtractHost(const char *src)
+{
+ ExtractHostResult result{nullptr, src};
+ const char *hostname;
+
+ if (IsValidHostnameChar(*src)) {
+ const char *colon = nullptr;
+
+ hostname = src++;
+
+ while (IsValidHostnameChar(*src) || *src == ':') {
+ if (*src == ':') {
+ if (colon != nullptr) {
+ /* found a second colon: assume it's an IPv6
+ address */
+ result.end = FindIPv6End(src + 1);
+ result.host = {hostname, result.end};
+ return result;
+ } else
+ /* remember the position of the first colon */
+ colon = src;
+ }
+
+ ++src;
+ }
+
+ if (colon != nullptr)
+ /* hostname ends at colon */
+ src = colon;
+
+ result.end = src;
+ result.host = {hostname, result.end};
+ } else if (src[0] == ':' && src[1] == ':') {
+ /* IPv6 address beginning with "::" */
+ result.end = FindIPv6End(src + 2);
+ result.host = {src, result.end};
+ } else if (src[0] == '[') {
+ /* "[hostname]:port" (IPv6?) */
+
+ hostname = ++src;
+ const char *end = strchr(hostname, ']');
+ if (end == nullptr || end == hostname)
+ /* failed, return nullptr */
+ return result;
+
+ result.host = {hostname, end};
+ result.end = end + 1;
+ } else {
+ /* failed, return nullptr */
+ }
+
+ return result;
+}
diff --git a/src/net/HostParser.hxx b/src/net/HostParser.hxx
new file mode 100644
index 000000000..0166feadf
--- /dev/null
+++ b/src/net/HostParser.hxx
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007-2017 Content Management AG
+ * All rights reserved.
+ *
+ * author: Max Kellermann <mk@cm4all.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef NET_HOST_PARSER_HXX
+#define NET_HOST_PARSER_HXX
+
+#include "util/StringView.hxx"
+#include "util/Compiler.h"
+
+/**
+ * Result type for ExtractHost().
+ */
+struct ExtractHostResult {
+ /**
+ * The host part of the address.
+ *
+ * If nothing was parsed, then this is nullptr.
+ */
+ StringView host;
+
+ /**
+ * Pointer to the first character that was not parsed. On
+ * success, this is usually a pointer to the zero terminator or to
+ * a colon followed by a port number.
+ *
+ * If nothing was parsed, then this is a pointer to the given
+ * source string.
+ */
+ const char *end;
+
+ constexpr bool HasFailed() const {
+ return host == nullptr;
+ }
+};
+
+/**
+ * Extract the host from a string in the form "IP:PORT" or
+ * "[IPv6]:PORT". Stops at the first invalid character (e.g. the
+ * colon).
+ *
+ * @param src the input string
+ */
+gcc_pure
+ExtractHostResult
+ExtractHost(const char *src);
+
+#endif
diff --git a/src/net/Resolver.cxx b/src/net/Resolver.cxx
index 160447e03..2eeddc7aa 100644
--- a/src/net/Resolver.cxx
+++ b/src/net/Resolver.cxx
@@ -1,91 +1,160 @@
/*
- * Copyright 2003-2017 The Music Player Daemon Project
- * http://www.musicpd.org
+ * Copyright 2007-2017 Content Management AG
+ * All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * author: Max Kellermann <mk@cm4all.com>
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Resolver.hxx"
+#include "AddressInfo.hxx"
+#include "HostParser.hxx"
#include "util/RuntimeError.hxx"
-
-#include <string>
+#include "util/CharUtil.hxx"
#ifdef _WIN32
#include <ws2tcpip.h>
#else
-#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
+#include <net/if.h>
#endif
#include <string.h>
#include <stdio.h>
+#include <errno.h>
-struct addrinfo *
-resolve_host_port(const char *host_port, unsigned default_port,
- int flags, int socktype)
+static inline bool
+ai_is_passive(const struct addrinfo *ai)
{
- std::string p(host_port);
- const char *host = p.c_str(), *port = nullptr;
-
- if (host_port[0] == '[') {
- /* IPv6 needs enclosing square braces, to
- differentiate between IP colons and the port
- separator */
-
- size_t q = p.find(']', 1);
- if (q != p.npos && p[q + 1] == ':' && p[q + 2] != 0) {
- p[q] = 0;
- port = host + q + 2;
- ++host;
- }
- }
+ return ai == nullptr || (ai->ai_flags & AI_PASSIVE) != 0;
+}
+
+#ifndef _WIN32
+
+/**
+ * Check if there is an interface name after '%', and if so, replace
+ * it with the interface index, because getaddrinfo() understands only
+ * the index, not the name (tested on Linux/glibc).
+ */
+static void
+FindAndResolveInterfaceName(char *host, size_t size)
+{
+ char *percent = strchr(host, '%');
+ if (percent == nullptr || percent + 64 > host + size)
+ return;
- if (port == nullptr) {
- /* port is after the colon, but only if it's the only
- colon (don't split IPv6 addresses) */
+ char *interface = percent + 1;
+ if (!IsAlphaASCII(*interface))
+ return;
- auto q = p.find(':');
- if (q != p.npos && p[q + 1] != 0 &&
- p.find(':', q + 1) == p.npos) {
- p[q] = 0;
- port = host + q + 1;
+ const unsigned i = if_nametoindex(interface);
+ if (i == 0)
+ throw FormatRuntimeError("No such interface: %s", interface);
+
+ sprintf(interface, "%u", i);
+}
+
+#endif
+
+static int
+Resolve(const char *host_and_port, int default_port,
+ const struct addrinfo *hints,
+ struct addrinfo **ai_r)
+{
+ const char *host, *port;
+ char buffer[256], port_string[16];
+
+ if (host_and_port != nullptr) {
+ const auto eh = ExtractHost(host_and_port);
+ if (eh.HasFailed())
+ return EAI_NONAME;
+
+ if (eh.host.size >= sizeof(buffer)) {
+#ifdef _WIN32
+ return EAI_MEMORY;
+#else
+ errno = ENAMETOOLONG;
+ return EAI_SYSTEM;
+#endif
}
- }
- char buffer[32];
- if (port == nullptr && default_port != 0) {
- snprintf(buffer, sizeof(buffer), "%u", default_port);
- port = buffer;
- }
+ memcpy(buffer, eh.host.data, eh.host.size);
+ buffer[eh.host.size] = 0;
+ host = buffer;
- if ((flags & AI_PASSIVE) != 0 && strcmp(host, "*") == 0)
+#ifndef _WIN32
+ FindAndResolveInterfaceName(buffer, sizeof(buffer));
+#endif
+
+ port = eh.end;
+ if (*port == ':') {
+ /* port specified */
+ ++port;
+ } else if (*port == 0) {
+ /* no port specified */
+ snprintf(port_string, sizeof(port_string), "%d", default_port);
+ port = port_string;
+ } else
+ throw std::runtime_error("Garbage after host name");
+
+ if (ai_is_passive(hints) && strcmp(host, "*") == 0)
+ host = nullptr;
+ } else {
host = nullptr;
+ snprintf(port_string, sizeof(port_string), "%d", default_port);
+ port = port_string;
+ }
+
+ return getaddrinfo(host, port, hints, ai_r);
+}
+AddressInfoList
+Resolve(const char *host_and_port, int default_port,
+ const struct addrinfo *hints)
+{
+ struct addrinfo *ai;
+ int result = Resolve(host_and_port, default_port, hints, &ai);
+ if (result != 0)
+ throw FormatRuntimeError("Failed to resolve '%s': %s",
+ host_and_port, gai_strerror(result));
+
+ return AddressInfoList(ai);
+}
+
+AddressInfoList
+Resolve(const char *host_port, unsigned default_port, int flags, int socktype)
+{
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = flags;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = socktype;
- struct addrinfo *ai;
- int ret = getaddrinfo(host, port, &hints, &ai);
- if (ret != 0)
- throw FormatRuntimeError("Failed to look up '%s': %s",
- host_port, gai_strerror(ret));
-
- return ai;
+ return Resolve(host_port, default_port, &hints);
}
diff --git a/src/net/Resolver.hxx b/src/net/Resolver.hxx
index 1f8b89659..f5630e02e 100644
--- a/src/net/Resolver.hxx
+++ b/src/net/Resolver.hxx
@@ -1,44 +1,55 @@
/*
- * Copyright 2003-2017 The Music Player Daemon Project
- * http://www.musicpd.org
+ * Copyright 2007-2017 Content Management AG
+ * All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * author: Max Kellermann <mk@cm4all.com>
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MPD_RESOLVER_HXX
-#define MPD_RESOLVER_HXX
-
-#include "check.h"
-#include "util/Compiler.h"
+#ifndef NET_RESOLVER_HXX
+#define NET_RESOLVER_HXX
struct addrinfo;
+class AddressInfoList;
/**
- * Resolve a specification in the form "host", "host:port",
- * "[host]:port". This is a convenience wrapper for getaddrinfo().
+ * Resolve the given host name (which may include a port), and fall
+ * back to the given default port.
*
- * Throws #std::runtime_error on error.
+ * This is a wrapper for getaddrinfo() and it does not support local
+ * sockets.
*
- * @param default_port a default port number that will be used if none
- * is given in the string (if applicable); pass 0 to go without a
- * default
- * @return an #addrinfo linked list that must be freed with
- * freeaddrinfo()
+ * Throws on error.
*/
-addrinfo *
-resolve_host_port(const char *host_port, unsigned default_port,
- int flags, int socktype);
+AddressInfoList
+Resolve(const char *host_and_port, int default_port,
+ const struct addrinfo *hints);
+
+AddressInfoList
+Resolve(const char *host_port, unsigned default_port, int flags, int socktype);
#endif
diff --git a/test/run_resolver.cxx b/test/run_resolver.cxx
index 681d64445..8e712301a 100644
--- a/test/run_resolver.cxx
+++ b/test/run_resolver.cxx
@@ -19,20 +19,13 @@
#include "config.h"
#include "net/Resolver.hxx"
+#include "net/AddressInfo.hxx"
#include "net/ToString.hxx"
#include "net/SocketAddress.hxx"
#include "Log.hxx"
#include <exception>
-#ifdef _WIN32
-#include <ws2tcpip.h>
-#include <winsock.h>
-#else
-#include <sys/socket.h>
-#include <netdb.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
@@ -43,15 +36,10 @@ try {
return EXIT_FAILURE;
}
- struct addrinfo *ai =
- resolve_host_port(argv[1], 80, AI_PASSIVE, SOCK_STREAM);
-
- for (const struct addrinfo *i = ai; i != NULL; i = i->ai_next) {
- const auto s = ToString({i->ai_addr, i->ai_addrlen});
- printf("%s\n", s.c_str());
+ for (const auto &i : Resolve(argv[1], 80, AI_PASSIVE, SOCK_STREAM)) {
+ printf("%s\n", ToString(i).c_str());
}
- freeaddrinfo(ai);
return EXIT_SUCCESS;
} catch (...) {
LogError(std::current_exception());