From: Max Kellermann Date: Thu, 18 Sep 2008 09:17:17 +0000 (+0200) Subject: libmpdclient: added resolver library X-Git-Tag: v0.12_alpha1~244 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=dcb67250b34dc92aced026516e162a42000f1857;p=ncmpc.git 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). --- diff --git a/src/Makefile.am b/src/Makefile.am index 31cabfd..b652ede 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 b2c907b..0f2b1b5 100644 --- a/src/libmpdclient.c +++ b/src/libmpdclient.c @@ -31,6 +31,7 @@ */ #include "libmpdclient.h" +#include "resolver.h" #include "str_pool.h" #include @@ -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 0000000..e5df05c --- /dev/null +++ b/src/resolver.c @@ -0,0 +1,195 @@ +/* libmpdclient + (c) 2008 Max Kellermann + 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 +#include +#include +#include + +#ifdef WIN32 +# include +# include +#else +# include +# include +# include +# include +# include +#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 0000000..6a79dc4 --- /dev/null +++ b/src/resolver.h @@ -0,0 +1,56 @@ +/* libmpdclient + (c) 2008 Max Kellermann + 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 + +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