summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/socket.c215
-rw-r--r--common/socket.h10
2 files changed, 165 insertions, 60 deletions
diff --git a/common/socket.c b/common/socket.c
index e2968a6..aa97848 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -1,8 +1,8 @@
/*
* socket.c
*
- * Copyright (c) 2012 Martin Szulecki All Rights Reserved.
- * Copyright (c) 2012 Nikias Bassen All Rights Reserved.
+ * Copyright (C) 2012-2018 Nikias Bassen <nikias@gmx.li>
+ * Copyright (C) 2012 Martin Szulecki <m.szulecki@libimobiledevice.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,6 +19,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
@@ -29,18 +32,26 @@
#include <sys/stat.h>
#ifdef WIN32
#include <winsock2.h>
+#include <ws2tcpip.h>
#include <windows.h>
static int wsa_init = 0;
#else
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <netdb.h>
#include <arpa/inet.h>
+#include <fcntl.h>
#endif
#include "socket.h"
#define RECV_TIMEOUT 20000
+#define CONNECT_TIMEOUT 5000
+
+#ifndef ECONNRESET
+#define ECONNRESET 108
+#endif
static int verbose = 0;
@@ -54,34 +65,34 @@ int socket_create_unix(const char *filename)
{
struct sockaddr_un name;
int sock;
- size_t size;
+#ifdef SO_NOSIGPIPE
+ int yes = 1;
+#endif
// remove if still present
unlink(filename);
/* Create the socket. */
- sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
return -1;
}
+#ifdef SO_NOSIGPIPE
+ if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ socket_close(sock);
+ return -1;
+ }
+#endif
+
/* Bind a name to the socket. */
- name.sun_family = AF_LOCAL;
+ name.sun_family = AF_UNIX;
strncpy(name.sun_path, filename, sizeof(name.sun_path));
name.sun_path[sizeof(name.sun_path) - 1] = '\0';
- /* The size of the address is
- the offset of the start of the filename,
- plus its length,
- plus one for the terminating null byte.
- Alternatively you can just do:
- size = SUN_LEN (&name);
- */
- size = (offsetof(struct sockaddr_un, sun_path)
- + strlen(name.sun_path) + 1);
-
- if (bind(sock, (struct sockaddr *) &name, size) < 0) {
+ if (bind(sock, (struct sockaddr*)&name, sizeof(name)) < 0) {
perror("bind");
socket_close(sock);
return -1;
@@ -100,8 +111,11 @@ int socket_connect_unix(const char *filename)
{
struct sockaddr_un name;
int sfd = -1;
- size_t size;
struct stat fst;
+#ifdef SO_NOSIGPIPE
+ int yes = 1;
+#endif
+ int bufsize = 0x20000;
// check if socket file exists...
if (stat(filename, &fst) != 0) {
@@ -118,20 +132,33 @@ int socket_connect_unix(const char *filename)
return -1;
}
// make a new socket
- if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
if (verbose >= 2)
fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno));
return -1;
}
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (void*)&bufsize, sizeof(int)) == -1) {
+ perror("Could not set send buffer for socket");
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, (void*)&bufsize, sizeof(int)) == -1) {
+ perror("Could not set receive buffer for socket");
+ }
+
+#ifdef SO_NOSIGPIPE
+ if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ socket_close(sfd);
+ return -1;
+ }
+#endif
// and connect to 'filename'
- name.sun_family = AF_LOCAL;
+ name.sun_family = AF_UNIX;
strncpy(name.sun_path, filename, sizeof(name.sun_path));
name.sun_path[sizeof(name.sun_path) - 1] = 0;
- size = (offsetof(struct sockaddr_un, sun_path)
- + strlen(name.sun_path) + 1);
-
- if (connect(sfd, (struct sockaddr *) &name, size) < 0) {
+ if (connect(sfd, (struct sockaddr*)&name, sizeof(name)) < 0) {
socket_close(sfd);
if (verbose >= 2)
fprintf(stderr, "%s: connect: %s\n", __func__,
@@ -170,6 +197,14 @@ int socket_create(uint16_t port)
return -1;
}
+#ifdef SO_NOSIGPIPE
+ if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ socket_close(sfd);
+ return -1;
+ }
+#endif
+
memset((void *) &saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
@@ -194,9 +229,14 @@ int socket_connect(const char *addr, uint16_t port)
{
int sfd = -1;
int yes = 1;
- struct hostent *hp;
- struct sockaddr_in saddr;
+ int bufsize = 0x20000;
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ char portstr[8];
+ int res;
#ifdef WIN32
+ u_long l_yes = 1;
+ u_long l_no = 0;
WSADATA wsa_data;
if (!wsa_init) {
if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
@@ -205,6 +245,8 @@ int socket_connect(const char *addr, uint16_t port)
}
wsa_init = 1;
}
+#else
+ int flags = 0;
#endif
if (!addr) {
@@ -212,39 +254,99 @@ int socket_connect(const char *addr, uint16_t port)
return -1;
}
- if ((hp = gethostbyname(addr)) == NULL) {
- if (verbose >= 2)
- fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr);
+ memset(&hints, '\0', sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ sprintf(portstr, "%d", port);
+
+ res = getaddrinfo(addr, portstr, &hints, &result);
+ if (res != 0) {
+ fprintf(stderr, "%s: getaddrinfo: %s\n", __func__, gai_strerror(res));
return -1;
}
- if (!hp->h_addr) {
- if (verbose >= 2)
- fprintf(stderr, "%s: gethostbyname returned NULL address!\n",
- __func__);
- return -1;
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (sfd == -1) {
+ continue;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ socket_close(sfd);
+ continue;
+ }
+
+#ifdef WIN32
+ ioctlsocket(sfd, FIONBIO, &l_yes);
+#else
+ fcntl(sfd, F_SETFL, O_NONBLOCK);
+#endif
+
+ if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) {
+ break;
+ }
+#ifdef WIN32
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+ if (errno == EINPROGRESS)
+#endif
+ {
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(sfd, &fds);
+
+ struct timeval timeout;
+ timeout.tv_sec = CONNECT_TIMEOUT / 1000;
+ timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000;
+ if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) {
+ int so_error;
+ socklen_t len = sizeof(so_error);
+ getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len);
+ if (so_error == 0) {
+ break;
+ }
+ }
+ }
+ socket_close(sfd);
}
- if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
- perror("socket()");
+ freeaddrinfo(result);
+
+ if (rp == NULL) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: Could not connect to %s:%d\n", __func__, addr, port);
return -1;
}
- if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
+#ifdef WIN32
+ ioctlsocket(sfd, FIONBIO, &l_no);
+#else
+ flags = fcntl(sfd, F_GETFL, 0);
+ fcntl(sfd, F_SETFL, flags & (~O_NONBLOCK));
+#endif
+
+#ifdef SO_NOSIGPIPE
+ if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
perror("setsockopt()");
socket_close(sfd);
return -1;
}
+#endif
- memset((void *) &saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr;
- saddr.sin_port = htons(port);
+ if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(int)) == -1) {
+ perror("Could not set TCP_NODELAY on socket");
+ }
- if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
- perror("connect");
- socket_close(sfd);
- return -2;
+ if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (void*)&bufsize, sizeof(int)) == -1) {
+ perror("Could not set send buffer for socket");
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, (void*)&bufsize, sizeof(int)) == -1) {
+ perror("Could not set receive buffer for socket");
}
return sfd;
@@ -258,7 +360,7 @@ int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout)
struct timeval to;
struct timeval *pto;
- if (fd <= 0) {
+ if (fd < 0) {
if (verbose >= 2)
fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd);
return -1;
@@ -267,17 +369,16 @@ int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout)
FD_ZERO(&fds);
FD_SET(fd, &fds);
- if (timeout > 0) {
- to.tv_sec = (time_t) (timeout / 1000);
- to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000);
- pto = &to;
- } else {
- pto = NULL;
- }
-
sret = -1;
do {
+ if (timeout > 0) {
+ to.tv_sec = (time_t) (timeout / 1000);
+ to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000);
+ pto = &to;
+ } else {
+ pto = NULL;
+ }
eagain = 0;
switch (fdm) {
case FDM_READ:
@@ -378,7 +479,7 @@ int socket_receive_timeout(int fd, void *data, size_t length, int flags,
// but this is an error condition
if (verbose >= 3)
fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd);
- return -EAGAIN;
+ return -ECONNRESET;
}
if (result < 0) {
return -errno;
@@ -388,5 +489,9 @@ int socket_receive_timeout(int fd, void *data, size_t length, int flags,
int socket_send(int fd, void *data, size_t length)
{
- return send(fd, data, length, 0);
+ int flags = 0;
+#ifdef MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+#endif
+ return send(fd, data, length, flags);
}
diff --git a/common/socket.h b/common/socket.h
index 81ee083..e31de6b 100644
--- a/common/socket.h
+++ b/common/socket.h
@@ -1,8 +1,8 @@
/*
* socket.h
*
- * Copyright (c) 2012 Martin Szulecki All Rights Reserved.
- * Copyright (c) 2012 Nikias Bassen All Rights Reserved.
+ * Copyright (C) 2012 Martin Szulecki <m.szulecki@libimobiledevice.org>
+ * Copyright (C) 2012 Nikias Bassen <nikias@gmx.li>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,8 +19,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef __SOCKET_SOCKET_H
-#define __SOCKET_SOCKET_H
+#ifndef SOCKET_SOCKET_H
+#define SOCKET_SOCKET_H
#include <stdlib.h>
#include <stdint.h>
@@ -62,4 +62,4 @@ int socket_send(int fd, void *data, size_t size);
void socket_set_verbose(int level);
-#endif /* __SOCKET_SOCKET_H */
+#endif /* SOCKET_SOCKET_H */