aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2008-09-18 11:17:17 +0200
committerMax Kellermann <max@duempel.org>2008-09-18 11:17:17 +0200
commitdcb67250b34dc92aced026516e162a42000f1857 (patch)
tree6314bf5b1353347ce2b3973274513e2b78491aba
parent919cebb0bcd44170a7dd773717eefa1e515d628c (diff)
downloadmpd-dcb67250b34dc92aced026516e162a42000f1857.tar.gz
mpd-dcb67250b34dc92aced026516e162a42000f1857.tar.xz
mpd-dcb67250b34dc92aced026516e162a42000f1857.zip
libmpdclient: added resolver library
The resolver library provides unified access to all resolvers (getaddrinfo(), gethostbyname(), Unix domain sockets). Like getaddrinfo(), it can return more than one address for a host name. This fixes bug 1517 (http://www.musicpd.org/mantis/view.php?id=1517).
-rw-r--r--src/Makefile.am2
-rw-r--r--src/libmpdclient.c273
-rw-r--r--src/resolver.c195
-rw-r--r--src/resolver.h56
4 files changed, 327 insertions, 199 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 31cabfda0..b652ede98 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,6 +12,7 @@ ncmpc_LDADD = \
ncmpc_headers = \
libmpdclient.h\
+ resolver.h \
song.h \
mpdclient.h\
playlist.h \
@@ -40,6 +41,7 @@ lyr_src=${lyr_src_fixed}
ncmpc_SOURCES = \
libmpdclient.c\
+ resolver.c \
song.c \
main.c\
mpdclient.c\
diff --git a/src/libmpdclient.c b/src/libmpdclient.c
index b2c907b49..0f2b1b503 100644
--- a/src/libmpdclient.c
+++ b/src/libmpdclient.c
@@ -31,6 +31,7 @@
*/
#include "libmpdclient.h"
+#include "resolver.h"
#include "str_pool.h"
#include <assert.h>
@@ -61,12 +62,6 @@
# define MSG_DONTWAIT 0
#endif
-#ifndef MPD_NO_GAI
-# ifdef AI_ADDRCONFIG
-# define MPD_HAVE_GAI
-# endif
-#endif
-
#define COMMAND_LIST 1
#define COMMAND_LIST_OK 2
@@ -115,130 +110,6 @@ static int do_connect_fail(mpd_Connection *connection,
}
#endif /* !WIN32 */
-#ifdef MPD_HAVE_GAI
-static int mpd_connect(mpd_Connection * connection, const char * host, int port,
- float timeout)
-{
- int error;
- char service[20];
- struct addrinfo hints;
- struct addrinfo *res = NULL;
- struct addrinfo *addrinfo = NULL;
-
- /**
- * Setup hints
- */
- hints.ai_flags = AI_ADDRCONFIG;
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
- hints.ai_addrlen = 0;
- hints.ai_addr = NULL;
- hints.ai_canonname = NULL;
- hints.ai_next = NULL;
-
- snprintf(service, sizeof(service), "%d", port);
-
- error = getaddrinfo(host, service, &hints, &addrinfo);
-
- if (error) {
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "host \"%s\" not found: %s",host, gai_strerror(error));
- connection->error = MPD_ERROR_UNKHOST;
- return -1;
- }
-
- for (res = addrinfo; res; res = res->ai_next) {
- /* create socket */
- connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
- if (connection->sock < 0) {
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "problems creating socket: %s",
- strerror(errno));
- connection->error = MPD_ERROR_SYSTEM;
- freeaddrinfo(addrinfo);
- return -1;
- }
-
- mpd_setConnectionTimeout(connection,timeout);
-
- /* connect stuff */
- if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
- /* try the next address family */
- closesocket(connection->sock);
- connection->sock = -1;
- continue;
- }
- }
- freeaddrinfo(addrinfo);
-
- if (connection->sock < 0) {
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "problems connecting to \"%s\" on port"
- " %i: %s",host,port, strerror(errno));
- connection->error = MPD_ERROR_CONNPORT;
-
- return -1;
- }
-
- return 0;
-}
-#else /* !MPD_HAVE_GAI */
-static int mpd_connect(mpd_Connection * connection, const char * host, int port,
- float timeout)
-{
- struct hostent * he;
- struct sockaddr * dest;
- int destlen;
- struct sockaddr_in sin;
-
- if(!(he=gethostbyname(host))) {
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "host \"%s\" not found",host);
- connection->error = MPD_ERROR_UNKHOST;
- return -1;
- }
-
- memset(&sin,0,sizeof(struct sockaddr_in));
- /*dest.sin_family = he->h_addrtype;*/
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
-
- switch(he->h_addrtype) {
- case AF_INET:
- memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
- he->h_length);
- dest = (struct sockaddr *)&sin;
- destlen = sizeof(struct sockaddr_in);
- break;
- default:
- strcpy(connection->errorStr,"address type is not IPv4\n");
- connection->error = MPD_ERROR_SYSTEM;
- return -1;
- break;
- }
-
- if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
- strcpy(connection->errorStr,"problems creating socket");
- connection->error = MPD_ERROR_SYSTEM;
- return -1;
- }
-
- mpd_setConnectionTimeout(connection,timeout);
-
- /* connect stuff */
- if (do_connect_fail(connection, dest, destlen)) {
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "problems connecting to \"%s\" on port"
- " %i",host,port);
- connection->error = MPD_ERROR_CONNPORT;
- return -1;
- }
-
- return 0;
-}
-#endif /* !MPD_HAVE_GAI */
-
const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
{
"Artist",
@@ -333,53 +204,6 @@ static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int
return 0;
}
-#ifndef WIN32
-static int mpd_connect_un(mpd_Connection * connection,
- const char * host, float timeout)
-{
- int error, flags;
- size_t path_length;
- struct sockaddr_un sun;
-
- path_length = strlen(host);
- if (path_length >= sizeof(sun.sun_path)) {
- strcpy(connection->errorStr, "unix socket path is too long");
- connection->error = MPD_ERROR_UNKHOST;
- return -1;
- }
-
- sun.sun_family = AF_UNIX;
- memcpy(sun.sun_path, host, path_length + 1);
-
- connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (connection->sock < 0) {
- strcpy(connection->errorStr, "problems creating socket");
- connection->error = MPD_ERROR_SYSTEM;
- return -1;
- }
-
- mpd_setConnectionTimeout(connection, timeout);
-
- flags = fcntl(connection->sock, F_GETFL, 0);
- fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
-
- error = connect(connection->sock, (struct sockaddr*)&sun, sizeof(sun));
- if (error < 0) {
- /* try the next address family */
- close(connection->sock);
- connection->sock = 0;
-
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "problems connecting to \"%s\": %s",
- host, strerror(errno));
- connection->error = MPD_ERROR_CONNPORT;
- return -1;
- }
-
- return 0;
-}
-#endif /* WIN32 */
-
/**
* Wait for the socket to become readable.
*/
@@ -486,6 +310,76 @@ static int mpd_recv(mpd_Connection *connection)
}
}
+static int
+mpd_connect(mpd_Connection *connection, const char * host, int port)
+{
+ struct resolver *resolver;
+ const struct resolver_address *address;
+ int ret;
+
+ resolver = resolver_new(host, port);
+ if (resolver == NULL) {
+ snprintf(connection->errorStr, sizeof(connection->errorStr),
+ "host \"%s\" not found", host);
+ connection->error = MPD_ERROR_UNKHOST;
+ return -1;
+ }
+
+ while ((address = resolver_next(resolver)) != NULL) {
+ connection->sock = socket(address->family, SOCK_STREAM,
+ address->protocol);
+ if (connection->sock < 0) {
+ snprintf(connection->errorStr,
+ sizeof(connection->errorStr),
+ "problems creating socket: %s",
+ strerror(errno));
+ connection->error = MPD_ERROR_SYSTEM;
+ continue;
+ }
+
+ ret = do_connect_fail(connection,
+ address->addr, address->addrlen);
+ if (ret != 0) {
+ snprintf(connection->errorStr,
+ sizeof(connection->errorStr),
+ "problems connecting to \"%s\" on port"
+ " %i: %s", host, port, strerror(errno));
+ connection->error = MPD_ERROR_CONNPORT;
+
+ closesocket(connection->sock);
+ connection->sock = -1;
+ continue;
+ }
+
+ ret = mpd_wait_connected(connection);
+ if (ret > 0) {
+ resolver_free(resolver);
+ mpd_clearError(connection);
+ return 0;
+ }
+
+ if (ret == 0) {
+ snprintf(connection->errorStr,
+ sizeof(connection->errorStr),
+ "timeout in attempting to get a response from"
+ " \"%s\" on port %i", host, port);
+ connection->error = MPD_ERROR_NORESPONSE;
+ } else if (ret < 0) {
+ snprintf(connection->errorStr,
+ sizeof(connection->errorStr),
+ "problems connecting to \"%s\" on port %i: %s",
+ host, port, strerror(-ret));
+ connection->error = MPD_ERROR_CONNPORT;
+ }
+
+ closesocket(connection->sock);
+ connection->sock = -1;
+ }
+
+ resolver_free(resolver);
+ return -1;
+}
+
mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
int err;
char * rt;
@@ -504,30 +398,11 @@ mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
if (winsock_dll_error(connection))
return connection;
-#ifndef WIN32
- if (host[0] == '/')
- err = mpd_connect_un(connection, host, timeout);
- else
-#endif
- err = mpd_connect(connection, host, port, timeout);
- if (err < 0)
- return connection;
+ mpd_setConnectionTimeout(connection,timeout);
- err = mpd_wait_connected(connection);
- if (err == 0) {
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "timeout in attempting to get a response from"
- " \"%s\" on port %i",host,port);
- connection->error = MPD_ERROR_NORESPONSE;
- return connection;
- } else if (err < 0) {
- snprintf(connection->errorStr,
- sizeof(connection->errorStr),
- "problems connecting to \"%s\" on port %i: %s",
- host, port, strerror(-err));
- connection->error = MPD_ERROR_CONNPORT;
+ err = mpd_connect(connection, host, port);
+ if (err < 0)
return connection;
- }
while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
err = mpd_recv(connection);
diff --git a/src/resolver.c b/src/resolver.c
new file mode 100644
index 000000000..e5df05ce5
--- /dev/null
+++ b/src/resolver.c
@@ -0,0 +1,195 @@
+/* libmpdclient
+ (c) 2008 Max Kellermann <max@duempel.org>
+ This project's homepage is: http://www.musicpd.org
+
+ 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.
+
+ - Neither the name of the Music Player Daemon nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ 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 "resolver.h"
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef WIN32
+# include <ws2tcpip.h>
+# include <winsock.h>
+#else
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netdb.h>
+#endif
+
+#ifndef MPD_NO_GAI
+# ifdef AI_ADDRCONFIG
+# define MPD_HAVE_GAI
+# endif
+#endif
+
+struct resolver {
+ enum {
+ TYPE_ZERO, TYPE_ONE, TYPE_ANY
+ } type;
+
+#ifdef MPD_HAVE_GAI
+ struct addrinfo *ai;
+ const struct addrinfo *next;
+#else
+ struct sockaddr_in sin;
+#endif
+
+ struct resolver_address current;
+
+#ifndef WIN32
+ struct sockaddr_un sun;
+#endif
+};
+
+struct resolver *
+resolver_new(const char *host, int port)
+{
+ struct resolver *resolver;
+
+ resolver = malloc(sizeof(*resolver));
+ if (resolver == NULL)
+ return NULL;
+
+#ifndef WIN32
+ if (host[0] == '/') {
+ size_t path_length = strlen(host);
+ if (path_length >= sizeof(resolver->sun.sun_path)) {
+ free(resolver);
+ return NULL;
+ }
+
+ resolver->sun.sun_family = AF_UNIX;
+ memcpy(resolver->sun.sun_path, host, path_length + 1);
+
+ resolver->current.family = PF_UNIX;
+ resolver->current.protocol = 0;
+ resolver->current.addrlen = sizeof(resolver->sun);
+ resolver->current.addr = (const struct sockaddr *)&resolver->sun;
+ resolver->type = TYPE_ONE;
+ } else {
+#endif
+#ifdef MPD_HAVE_GAI
+ struct addrinfo hints;
+ char service[20];
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ snprintf(service, sizeof(service), "%d", port);
+
+ ret = getaddrinfo(host, service, &hints, &resolver->ai);
+ if (ret != 0) {
+ free(resolver);
+ return NULL;
+ }
+
+ resolver->next = resolver->ai;
+ resolver->type = TYPE_ANY;
+#else
+ const struct hostent *he;
+
+ he = gethostbyname(host);
+ if (he == NULL) {
+ free(resolver);
+ return NULL;
+ }
+
+ if (he->h_addrtype != AF_INET) {
+ free(resolver);
+ return NULL;
+ }
+
+
+ memset(&resolver->sin, 0, sizeof(resolver->sin));
+ resolver->sin.sin_family = AF_INET;
+ resolver->sin.sin_port = htons(port);
+ memcpy((char *)&resolver->sin.sin_addr.s_addr,
+ (char *)he->h_addr, he->h_length);
+
+ resolver->current.family = PF_INET;
+ resolver->current.protocol = 0;
+ resolver->current.addrlen = sizeof(resolver->sin);
+ resolver->current.addr = (const struct sockaddr *)&resolver->sin;
+
+ resolver->type = TYPE_ONE;
+#endif
+#ifndef WIN32
+ }
+#endif
+
+ return resolver;
+}
+
+void
+resolver_free(struct resolver *resolver)
+{
+#ifdef MPD_HAVE_GAI
+ if (resolver->type == TYPE_ANY)
+ freeaddrinfo(resolver->ai);
+#endif
+ free(resolver);
+}
+
+const struct resolver_address *
+resolver_next(struct resolver *resolver)
+{
+ if (resolver->type == TYPE_ZERO)
+ return NULL;
+
+ if (resolver->type == TYPE_ONE) {
+ resolver->type = TYPE_ZERO;
+ return &resolver->current;
+ }
+
+#ifdef MPD_HAVE_GAI
+ if (resolver->next == NULL)
+ return NULL;
+
+ resolver->current.family = resolver->next->ai_family;
+ resolver->current.protocol = resolver->next->ai_protocol;
+ resolver->current.addrlen = resolver->next->ai_addrlen;
+ resolver->current.addr = resolver->next->ai_addr;
+
+ resolver->next = resolver->next->ai_next;
+
+ return &resolver->current;
+#else
+ return NULL;
+#endif
+}
diff --git a/src/resolver.h b/src/resolver.h
new file mode 100644
index 000000000..6a79dc4cb
--- /dev/null
+++ b/src/resolver.h
@@ -0,0 +1,56 @@
+/* libmpdclient
+ (c) 2008 Max Kellermann <max@duempel.org>
+ This project's homepage is: http://www.musicpd.org
+
+ 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.
+
+ - Neither the name of the Music Player Daemon nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ 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 LIBMPDCLIENT_RESOLVER_H
+#define LIBMPDCLIENT_RESOLVER_H
+
+#include <stddef.h>
+
+struct resolver;
+
+struct resolver_address {
+ int family;
+ int protocol;
+ size_t addrlen;
+ const struct sockaddr *addr;
+};
+
+struct resolver *
+resolver_new(const char *host, int port);
+
+void
+resolver_free(struct resolver *resolver);
+
+const struct resolver_address *
+resolver_next(struct resolver *resolver);
+
+#endif