diff options
Diffstat (limited to 'src/socket.c')
-rw-r--r-- | src/socket.c | 391 |
1 files changed, 281 insertions, 110 deletions
diff --git a/src/socket.c b/src/socket.c index 1029a71..d4dedd1 100644 --- a/src/socket.c +++ b/src/socket.c @@ -55,7 +55,7 @@ static int wsa_init = 0; #ifdef AF_INET6 #include <net/if.h> #include <ifaddrs.h> -#if defined (__APPLE__) || defined (__FreeBSD__) +#if defined (__APPLE__) || defined (__FreeBSD__) || defined (__HAIKU__) #include <net/if_dl.h> #endif #ifdef __linux__ @@ -65,6 +65,9 @@ static int wsa_init = 0; #endif #include "common.h" #include "libimobiledevice-glue/socket.h" +#ifdef HAVE_POLL +#include <sys/poll.h> +#endif #define RECV_TIMEOUT 20000 #define SEND_TIMEOUT 10000 @@ -82,12 +85,12 @@ static int wsa_init = 0; static int verbose = 0; -LIBIMOBILEDEVICE_GLUE_API void socket_set_verbose(int level) +void socket_set_verbose(int level) { verbose = level; } -LIBIMOBILEDEVICE_GLUE_API const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size) +const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size) { #ifdef WIN32 WSADATA wsa_data; @@ -140,8 +143,186 @@ LIBIMOBILEDEVICE_GLUE_API const char *socket_addr_to_string(struct sockaddr *add return NULL; } +enum poll_status +{ + poll_status_success, + poll_status_timeout, + poll_status_error +}; + +#ifdef WIN32 +static inline __attribute__((always_inline)) int WSAError_to_errno(int wsaerr) +{ + switch (wsaerr) { + case WSAEINVAL: + return EINVAL; + case WSAENOTSOCK: + return ENOTSOCK; + case WSAENOTCONN: + return ENOTCONN; + case WSAESHUTDOWN: + return ENOTCONN; + case WSAECONNRESET: + return ECONNRESET; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAENETDOWN: + return ENETDOWN; + case WSAENETRESET: + return ENETRESET; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAEWOULDBLOCK: + return EWOULDBLOCK; + case WSAEINPROGRESS: + return EINPROGRESS; + case WSAENOBUFS: + return ENOBUFS; + case WSAEINTR: + return EINTR; + case WSAEACCES: + return EACCES; + case WSAEFAULT: + return EFAULT; + default: + break; + } + return wsaerr; +} +#endif + +// timeout of -1 means infinity +static inline __attribute__((always_inline)) enum poll_status poll_wrapper(int fd, fd_mode mode, int timeout) +{ +#ifdef HAVE_POLL + // https://man7.org/linux/man-pages/man2/select.2.html + // Correspondence between select() and poll() notifications + // #define POLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN | + // EPOLLHUP | EPOLLERR) + // /* Ready for reading */ + // #define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT | + // EPOLLERR) + // /* Ready for writing */ + // #define POLLEX_SET (EPOLLPRI) + // /* Exceptional condition */ + + short events; + switch (mode) { + case FDM_READ: + events = POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR; + break; + case FDM_WRITE: + events = POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR; + break; + case FDM_EXCEPT: + events = POLLPRI; + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: fd_mode %d unsupported\n", __func__, mode); + return poll_status_error; + } + while (1) { + struct pollfd pfd = { + .fd = fd, + .events = events, + }; + switch (poll(&pfd, 1, timeout)) { + case 1: + if((pfd.revents & (POLLNVAL | POLLERR)) != 0) + { + if (verbose >= 2) + fprintf(stderr, "%s: poll unexpected events: %d\n", __func__, (int)pfd.revents); + return poll_status_error; + } + return poll_status_success; + case 0: + return poll_status_timeout; + case -1: + if(errno == EINTR) + { + if (verbose >= 2) + fprintf(stderr, "%s: EINTR\n", __func__); + continue; + } + // fallthrough + default: + if (verbose >= 2) + fprintf(stderr, "%s: poll failed: %s\n", __func__, strerror(errno)); + return poll_status_error; + } + } +#else + fd_set fds; + int sret; + int eagain; + struct timeval to; + struct timeval *pto; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + sret = poll_status_error; + + 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 (mode) { + case FDM_READ: + sret = select(fd + 1, &fds, NULL, NULL, pto); + break; + case FDM_WRITE: + sret = select(fd + 1, NULL, &fds, NULL, pto); + break; + case FDM_EXCEPT: + sret = select(fd + 1, NULL, NULL, &fds, pto); + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: fd_mode %d unsupported\n", __func__, mode); + return poll_status_error; + } + + if (sret == 1) { + return poll_status_success; + } else if (sret == 0) { + return poll_status_timeout; + } else { + switch (errno) { + case EINTR: + // interrupt signal in select + if (verbose >= 2) + fprintf(stderr, "%s: EINTR\n", __func__); + eagain = 1; + break; + case EAGAIN: + if (verbose >= 2) + fprintf(stderr, "%s: EAGAIN\n", __func__); + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: select failed: %s\n", __func__, strerror(errno)); + return poll_status_error; + } + } + } while (eagain); + + return sret; +#endif +} + #ifndef WIN32 -LIBIMOBILEDEVICE_GLUE_API int socket_create_unix(const char *filename) +int socket_create_unix(const char *filename) { struct sockaddr_un name; int sock; @@ -187,7 +368,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_create_unix(const char *filename) return sock; } -LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) +int socket_connect_unix(const char *filename) { struct sockaddr_un name; int sfd = -1; @@ -246,20 +427,22 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) break; } if (errno == EINPROGRESS) { - 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) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); if (so_error == 0) { + errno = 0; break; } + errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { + errno = so_error; + } } } socket_close(sfd); @@ -276,7 +459,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) } #endif -LIBIMOBILEDEVICE_GLUE_API int socket_create(const char* addr, uint16_t port) +int socket_create(const char* addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -303,9 +486,6 @@ LIBIMOBILEDEVICE_GLUE_API int socket_create(const char* addr, uint16_t port) sprintf(portstr, "%d", port); - if (!addr) { - addr = "localhost"; - } res = getaddrinfo(addr, portstr, &hints, &result); if (res != 0) { fprintf(stderr, "%s: getaddrinfo: %s\n", __func__, gai_strerror(res)); @@ -699,7 +879,7 @@ static int getifaddrs(struct ifaddrs** ifap) #endif #endif -LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf[6]) +int get_primary_mac_address(unsigned char mac_addr_buf[6]) { int result = -1; struct ifaddrs *ifaddr = NULL, *ifa = NULL; @@ -714,13 +894,13 @@ LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf if (ifa->ifa_flags & IFF_LOOPBACK) { continue; } -#if defined(__APPLE__) || defined (__FreeBSD__) +#if defined(__APPLE__) || defined (__FreeBSD__) || defined (__HAIKU__) if (ifa->ifa_addr->sa_family != AF_LINK) { continue; } #if defined (__APPLE__) if (!strcmp(ifa->ifa_name, "en0")) { -#elif defined (__FreeBSD__) +#elif defined (__FreeBSD__) || defined (__HAIKU__) { #endif memcpy(mac_addr_buf, (unsigned char *)LLADDR((struct sockaddr_dl *)(ifa)->ifa_addr), 6); @@ -742,6 +922,12 @@ LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf result = 0; break; } +#elif defined(__CYGWIN__) + if (ifa->ifa_data) { + memcpy(mac_addr_buf, ((struct ifaddrs_hwdata *)ifa->ifa_data)->ifa_hwaddr.sa_data, 6); + result = 0; + break; + } #else #error get_primary_mac_address is not supported on this platform. #endif @@ -787,10 +973,12 @@ static int32_t _sockaddr_in6_scope_id(struct sockaddr_in6* addr) continue; } +#ifndef __HAIKU__ /* skip if not running */ if ((ifa->ifa_flags & IFF_RUNNING) == 0) { continue; } +#endif struct sockaddr_in6* addr_in = (struct sockaddr_in6*)ifa->ifa_addr; @@ -840,7 +1028,7 @@ static int32_t _sockaddr_in6_scope_id(struct sockaddr_in6* addr) } #endif -LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_t port) +int socket_connect_addr(struct sockaddr* addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -924,14 +1112,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ 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) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); @@ -939,7 +1120,20 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ errno = 0; break; } +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } } } socket_close(sfd); @@ -970,7 +1164,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ return sfd; } -LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) +int socket_connect(const char *addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -993,11 +1187,6 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) int flags = 0; #endif - if (!addr) { - errno = EINVAL; - return -1; - } - memset(&hints, '\0', sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -1048,20 +1237,28 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) 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) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); if (so_error == 0) { + errno = 0; break; } +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } } } socket_close(sfd); @@ -1090,75 +1287,39 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) return sfd; } -LIBIMOBILEDEVICE_GLUE_API int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout) +int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout) { - fd_set fds; - int sret; - int eagain; - struct timeval to; - struct timeval *pto; - if (fd < 0) { if (verbose >= 2) fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd); - return -1; + return -EINVAL; } - FD_ZERO(&fds); - FD_SET(fd, &fds); - - 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: - sret = select(fd + 1, &fds, NULL, NULL, pto); - break; - case FDM_WRITE: - sret = select(fd + 1, NULL, &fds, NULL, pto); - break; - case FDM_EXCEPT: - sret = select(fd + 1, NULL, NULL, &fds, pto); - break; - default: - return -1; - } + int timeout_ms; + if (timeout > 0) { + timeout_ms = (int)timeout; + if (timeout_ms <= 0) + timeout_ms = -1; + } else { + timeout_ms = -1; + } - if (sret < 0) { - switch (errno) { - case EINTR: - // interrupt signal in select - if (verbose >= 2) - fprintf(stderr, "%s: EINTR\n", __func__); - eagain = 1; - break; - case EAGAIN: - if (verbose >= 2) - fprintf(stderr, "%s: EAGAIN\n", __func__); - break; - default: - if (verbose >= 2) - fprintf(stderr, "%s: select failed: %s\n", __func__, - strerror(errno)); - return -1; - } - } else if (sret == 0) { + switch (poll_wrapper(fd, fdm, timeout_ms)) { + case poll_status_success: + return 1; + case poll_status_timeout: return -ETIMEDOUT; - } - } while (eagain); + case poll_status_error: + default: + if (verbose >= 2) + fprintf(stderr, "%s: poll_wrapper failed\n", __func__); + return -ECONNRESET; + } - return sret; + return -ECONNRESET; } -LIBIMOBILEDEVICE_GLUE_API int socket_accept(int fd, uint16_t port) +int socket_accept(int fd, uint16_t port) { #ifdef WIN32 int addr_len; @@ -1174,12 +1335,12 @@ LIBIMOBILEDEVICE_GLUE_API int socket_accept(int fd, uint16_t port) return result; } -LIBIMOBILEDEVICE_GLUE_API int socket_shutdown(int fd, int how) +int socket_shutdown(int fd, int how) { return shutdown(fd, how); } -LIBIMOBILEDEVICE_GLUE_API int socket_close(int fd) { +int socket_close(int fd) { #ifdef WIN32 return closesocket(fd); #else @@ -1187,17 +1348,17 @@ LIBIMOBILEDEVICE_GLUE_API int socket_close(int fd) { #endif } -LIBIMOBILEDEVICE_GLUE_API int socket_receive(int fd, void *data, size_t length) +int socket_receive(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, 0, RECV_TIMEOUT); } -LIBIMOBILEDEVICE_GLUE_API int socket_peek(int fd, void *data, size_t length) +int socket_peek(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); } -LIBIMOBILEDEVICE_GLUE_API int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout) +int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout) { int res; int result; @@ -1209,19 +1370,22 @@ LIBIMOBILEDEVICE_GLUE_API int socket_receive_timeout(int fd, void *data, size_t } // if we get here, there _is_ data available result = recv(fd, data, length, flags); - if (res > 0 && result == 0) { + if (result == 0) { // but this is an error condition if (verbose >= 3) fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd); return -ECONNRESET; } if (result < 0) { +#ifdef WIN32 + errno = WSAError_to_errno(WSAGetLastError()); +#endif return -errno; } return result; } -LIBIMOBILEDEVICE_GLUE_API int socket_send(int fd, void *data, size_t length) +int socket_send(int fd, void *data, size_t length) { int flags = 0; int res = socket_check_fd(fd, FDM_WRITE, SEND_TIMEOUT); @@ -1231,10 +1395,17 @@ LIBIMOBILEDEVICE_GLUE_API int socket_send(int fd, void *data, size_t length) #ifdef MSG_NOSIGNAL flags |= MSG_NOSIGNAL; #endif - return send(fd, data, length, flags); + int s = (int)send(fd, data, length, flags); + if (s < 0) { +#ifdef WIN32 + errno = WSAError_to_errno(WSAGetLastError()); +#endif + return -errno; + } + return s; } -LIBIMOBILEDEVICE_GLUE_API int socket_get_socket_port(int fd, uint16_t *port) +int socket_get_socket_port(int fd, uint16_t *port) { #ifdef WIN32 int addr_len; |