diff --git a/src/libmpdclient.c b/src/libmpdclient.c
index 1b0851ff125c08b4d8d86dc9806b5af676a6345d..dfca0a347a0ae5b0de341f3ba214580427b056fe 100644 (file)
--- a/src/libmpdclient.c
+++ b/src/libmpdclient.c
*/
#include "libmpdclient.h"
*/
#include "libmpdclient.h"
+#include "resolver.h"
#include "str_pool.h"
#include "str_pool.h"
+#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <stdio.h>
# define MSG_DONTWAIT 0
#endif
# 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
#define COMMAND_LIST 1
#define COMMAND_LIST_OK 2
}
#endif /* !WIN32 */
}
#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",
const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
{
"Artist",
@@ -332,59 +204,196 @@ static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int
return 0;
}
return 0;
}
-#ifndef WIN32
-static int mpd_connect_un(mpd_Connection * connection,
- const char * host, float timeout)
+/**
+ * Wait for the socket to become readable.
+ */
+static int mpd_wait(mpd_Connection *connection)
{
{
- int error, flags;
- size_t path_length;
- struct sockaddr_un sun;
+ struct timeval tv;
+ fd_set fds;
+ int ret;
- 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;
+ assert(connection->sock >= 0);
+
+ while (1) {
+ tv = connection->timeout;
+ FD_ZERO(&fds);
+ FD_SET(connection->sock, &fds);
+
+ ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
+ if (ret > 0)
+ return 0;
+
+ if (ret == 0 || !SELECT_ERRNO_IGNORE)
+ return -1;
}
}
+}
+
+/**
+ * Wait until the socket is connected and check its result. Returns 1
+ * on success, 0 on timeout, -errno on error.
+ */
+static int mpd_wait_connected(mpd_Connection *connection)
+{
+ int ret;
+ int s_err = 0;
+ socklen_t s_err_size = sizeof(s_err);
+
+ ret = mpd_wait(connection);
+ if (ret < 0)
+ return 0;
+
+ ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
+ (char*)&s_err, &s_err_size);
+ if (ret < 0)
+ return -errno;
+
+ if (s_err != 0)
+ return -s_err;
+
+ return 1;
+}
- sun.sun_family = AF_UNIX;
- memcpy(sun.sun_path, host, path_length + 1);
+/**
+ * Attempt to read data from the socket into the input buffer.
+ * Returns 0 on success, -1 on error.
+ */
+static int mpd_recv(mpd_Connection *connection)
+{
+ int ret;
+ ssize_t nbytes;
+
+ assert(connection != NULL);
+ assert(connection->buflen <= sizeof(connection->buffer));
+ assert(connection->bufstart <= connection->buflen);
- connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (connection->sock < 0) {
if (connection->sock < 0) {
- strcpy(connection->errorStr, "problems creating socket");
- connection->error = MPD_ERROR_SYSTEM;
+ strcpy(connection->errorStr, "not connected");
+ connection->error = MPD_ERROR_CONNCLOSED;
+ connection->doneProcessing = 1;
+ connection->doneListOk = 0;
return -1;
}
return -1;
}
- mpd_setConnectionTimeout(connection, timeout);
+ if (connection->buflen >= sizeof(connection->buffer)) {
+ /* delete consumed data from beginning of buffer */
+ connection->buflen -= connection->bufstart;
+ memmove(connection->buffer,
+ connection->buffer + connection->bufstart,
+ connection->buflen);
+ connection->bufstart = 0;
+ }
- flags = fcntl(connection->sock, F_GETFL, 0);
- fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
+ if (connection->buflen >= sizeof(connection->buffer)) {
+ strcpy(connection->errorStr, "buffer overrun");
+ connection->error = MPD_ERROR_BUFFEROVERRUN;
+ connection->doneProcessing = 1;
+ connection->doneListOk = 0;
+ return -1;
+ }
+
+ while (1) {
+ ret = mpd_wait(connection);
+ if (ret < 0) {
+ strcpy(connection->errorStr, "connection timeout");
+ connection->error = MPD_ERROR_TIMEOUT;
+ connection->doneProcessing = 1;
+ connection->doneListOk = 0;
+ return -1;
+ }
+
+ nbytes = read(connection->sock,
+ connection->buffer + connection->buflen,
+ sizeof(connection->buffer) - connection->buflen);
+ if (nbytes > 0) {
+ connection->buflen += nbytes;
+ return 0;
+ }
- error = connect(connection->sock, (struct sockaddr*)&sun, sizeof(sun));
- if (error < 0) {
- /* try the next address family */
- close(connection->sock);
- connection->sock = 0;
+ if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
+ strcpy(connection->errorStr, "connection closed");
+ connection->error = MPD_ERROR_CONNCLOSED;
+ connection->doneProcessing = 1;
+ connection->doneListOk = 0;
+ return -1;
+ }
+ }
+}
+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),
snprintf(connection->errorStr, sizeof(connection->errorStr),
- "problems connecting to \"%s\": %s",
- host, strerror(errno));
- connection->error = MPD_ERROR_CONNPORT;
+ "host \"%s\" not found", host);
+ connection->error = MPD_ERROR_UNKHOST;
return -1;
}
return -1;
}
- return 0;
+ 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;
}
}
-#endif /* WIN32 */
mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
int err;
char * rt;
mpd_Connection * connection = malloc(sizeof(mpd_Connection));
mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
int err;
char * rt;
mpd_Connection * connection = malloc(sizeof(mpd_Connection));
- struct timeval tv;
- fd_set fds;
connection->buflen = 0;
connection->bufstart = 0;
connection->buflen = 0;
connection->bufstart = 0;
@@ -399,52 +408,16 @@ mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
if (winsock_dll_error(connection))
return connection;
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);
+ mpd_setConnectionTimeout(connection,timeout);
+
+ err = mpd_connect(connection, host, port);
if (err < 0)
return connection;
while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
if (err < 0)
return connection;
while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
- tv.tv_sec = connection->timeout.tv_sec;
- tv.tv_usec = connection->timeout.tv_usec;
- FD_ZERO(&fds);
- FD_SET(connection->sock,&fds);
- if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
- ssize_t readed;
- readed = recv(connection->sock,
- &(connection->buffer[connection->buflen]),
- sizeof(connection->buffer) - connection->buflen, 0);
- if(readed<=0) {
- snprintf(connection->errorStr, sizeof(connection->errorStr),
- "problems getting a response from"
- " \"%s\" on port %i : %s",host,
- port, strerror(errno));
- connection->error = MPD_ERROR_NORESPONSE;
- return connection;
- }
- connection->buflen+=readed;
- }
- else if(err<0) {
- if (SELECT_ERRNO_IGNORE)
- continue;
- snprintf(connection->errorStr,
- sizeof(connection->errorStr),
- "problems connecting to \"%s\" on port"
- " %i",host,port);
- connection->error = MPD_ERROR_CONNPORT;
- return connection;
- }
- else {
- 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;
+ err = mpd_recv(connection);
+ if (err < 0)
return connection;
return connection;
- }
}
*rt = '\0';
}
*rt = '\0';
const char *commandPtr = command;
int commandLen = strlen(command);
const char *commandPtr = command;
int commandLen = strlen(command);
+ if (connection->sock < 0) {
+ strcpy(connection->errorStr, "not connected");
+ connection->error = MPD_ERROR_CONNCLOSED;
+ return;
+ }
+
if (!connection->doneProcessing && !connection->commandList) {
strcpy(connection->errorStr,
"not done processing current command");
if (!connection->doneProcessing && !connection->commandList) {
strcpy(connection->errorStr,
"not done processing current command");
char * rt = NULL;
char * name = NULL;
char * value = NULL;
char * rt = NULL;
char * name = NULL;
char * value = NULL;
- fd_set fds;
- struct timeval tv;
char * tok = NULL;
char * tok = NULL;
- ssize_t readed;
- char * bufferCheck = NULL;
int err;
int pos;
int err;
int pos;
return;
}
return;
}
- bufferCheck = connection->buffer+connection->bufstart;
- while (connection->bufstart >= connection->buflen ||
- !(rt = memchr(bufferCheck, '\n',
- connection->buffer + connection->buflen -
- bufferCheck))) {
- if (connection->buflen >= sizeof(connection->buffer)) {
- memmove(connection->buffer,
- connection->buffer + connection->bufstart,
- connection->buflen - connection->bufstart);
- connection->buflen -= connection->bufstart;
- connection->bufstart = 0;
- }
- if (connection->buflen >= sizeof(connection->buffer)) {
- strcpy(connection->errorStr,"buffer overrun");
- connection->error = MPD_ERROR_BUFFEROVERRUN;
- connection->doneProcessing = 1;
- connection->doneListOk = 0;
+ while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
+ connection->buflen - connection->bufstart))) {
+ err = mpd_recv(connection);
+ if (err < 0)
return;
return;
- }
- bufferCheck = connection->buffer+connection->buflen;
- tv.tv_sec = connection->timeout.tv_sec;
- tv.tv_usec = connection->timeout.tv_usec;
- FD_ZERO(&fds);
- FD_SET(connection->sock,&fds);
- if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
- readed = recv(connection->sock,
- connection->buffer+connection->buflen,
- sizeof(connection->buffer) - connection->buflen,
- MSG_DONTWAIT);
- if(readed<0 && SENDRECV_ERRNO_IGNORE) {
- continue;
- }
- if(readed<=0) {
- strcpy(connection->errorStr,"connection"
- " closed");
- connection->error = MPD_ERROR_CONNCLOSED;
- connection->doneProcessing = 1;
- connection->doneListOk = 0;
- return;
- }
- connection->buflen+=readed;
- }
- else if(err<0 && SELECT_ERRNO_IGNORE) continue;
- else {
- strcpy(connection->errorStr,"connection timeout");
- connection->error = MPD_ERROR_TIMEOUT;
- connection->doneProcessing = 1;
- connection->doneListOk = 0;
- return;
- }
}
*rt = '\0';
}
*rt = '\0';