diff options
Diffstat (limited to 'src/socket.c')
-rw-r--r-- | src/socket.c | 316 |
1 files changed, 303 insertions, 13 deletions
diff --git a/src/socket.c b/src/socket.c index 99e4477..06abc93 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1,30 +1,320 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include "socket.h" +#include "utils.h" +static int socket_setopts(int sockfd); +static inline void socket_freeaddr(struct addrinfo **results); -int socket_init_in(psocket *psocket, const char *listen_addr, unsigned int listen_port) + +static int socket_setopts(int sockfd) { - struct in_addr addr = {0}; + int s, enable = 1; + + s = fcntl(sockfd, F_GETFL, 0); + if (s < 0) + return 1; + s |= O_CLOEXEC; + if (fcntl(sockfd, F_SETFL, s) == -1) + return 1; - assert(psocket); - if (!inet_aton(listen_addr, &addr)) + s = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + if (s) return 1; - psocket->sock.sin_family = AF_INET; - psocket->sock.sin_addr = addr; - psocket->sock.sin_port = htons(listen_port); - psocket->fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); - return psocket->fd < 0; + return 0; +} + +static inline void socket_freeaddr(struct addrinfo **results) +{ + if (*results) { + freeaddrinfo(*results); + *results = NULL; + } +} + +int socket_nonblock(const psocket *psock) +{ + return set_fd_nonblock(psock->fd); +} + +int socket_init_in(const char *addr, + const char *port, struct addrinfo **results) +{ + int s; + struct addrinfo hints; + + assert(addr || port); /* getaddrinfo wants either node or service */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* IPV4 && IPV6 */ + hints.ai_socktype = SOCK_STREAM; /* TCP */ + hints.ai_flags = AI_PASSIVE; /* all interfaces */ + + s = getaddrinfo(addr, port, &hints, results); + if (s) + socket_freeaddr(results); + + return s; } -int socket_bind_listen(psocket *psocket) +int socket_bind_in(psocket *psock, struct addrinfo **results) { - assert(psocket); - if (bind(psocket->fd, &psocket->sock, sizeof(psocket->sock)) < 0) + int s = 1, fd = -1, rv; + struct addrinfo *rp = NULL; + + assert(psock && results && *results); + psock->fd = -1; + + for (rp = *results; rp != NULL; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd < 0) + continue; + rv = bind(fd, rp->ai_addr, rp->ai_addrlen); + if (!rv) + break; + close(fd); + } + + if (!rp) + goto finalise; + + s = socket_setopts(fd); + if (s) + goto finalise; + + psock->fd = fd; + psock->addr_len = rp->ai_addrlen; + psock->addr = *rp->ai_addr; + psock->family = rp->ai_family; + psock->socktype = rp->ai_socktype; + psock->protocol = rp->ai_protocol; + s = socket_nonblock(psock); + +finalise: + socket_freeaddr(results); + + return s; +} + +int socket_listen_in(psocket *psock) +{ + assert(psock); + + return listen(psock->fd, POTD_BACKLOG) != 0; +} + +int socket_accept_in(const psocket *psock, psocket *client_psock) +{ + int fd; + + assert(psock && client_psock); + + *client_psock = *psock; + fd = accept(psock->fd, &client_psock->addr, + &client_psock->addr_len); + if (fd < 0) + return 1; + if (socket_setopts(fd)) + { + close(fd); + return 1; + } + + client_psock->fd = fd; + if (socket_nonblock(client_psock)) { + socket_close(client_psock); return 1; - return listen(psocket->fd, POTD_BACKLOG) < 0; + } + + return 0; +} + +int socket_connect_in(psocket *psock, struct addrinfo **results) +{ + int s = 1, fd = -1, rv; + struct addrinfo *rp = NULL; + + assert(psock && results && *results); + psock->fd = -1; + + for (rp = *results; rp != NULL; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd < 0) + continue; + rv = connect(fd, rp->ai_addr, rp->ai_addrlen); + if (!rv) + break; + close(fd); + } + + if (!rp) + goto finalise; + + s = socket_setopts(fd); + if (s) + goto finalise; + + psock->fd = fd; + psock->addr_len = rp->ai_addrlen; + psock->addr = *(rp->ai_addr); + psock->family = rp->ai_family; + psock->socktype = rp->ai_socktype; + psock->protocol = rp->ai_protocol; + s = socket_nonblock(psock); + +finalise: + socket_freeaddr(results); + + return s; +} + +int socket_connectaddr_in(psocket *psock, struct addrinfo **results, + char host_buf[NI_MAXHOST], + char service_buf[NI_MAXSERV]) +{ + if (socket_connect_in(psock, results)) + return -1; + return socket_addrtostr_in(psock, host_buf, service_buf); +} + +int socket_addrtostr_in(const psocket *psock, + char hbuf[NI_MAXHOST], char sbuf[NI_MAXSERV]) +{ + int s; + + assert(psock); + s = getnameinfo(&psock->addr, + psock->addr_len, + &hbuf[0], NI_MAXHOST, + &sbuf[0], NI_MAXSERV, + NI_NUMERICHOST | NI_NUMERICSERV); + + return s; +} + +int socket_reconnect_in(psocket *psock) +{ + int rv; + + assert(psock); + if (psock->fd >= 0) + return 1; + + psock->fd = socket(psock->family, psock->socktype, psock->protocol); + if (psock->fd < 0) + return 1; + rv = connect(psock->fd, &psock->addr, psock->addr_len); + if (rv) { + socket_close(psock); + return 1; + } + + if (socket_setopts(psock->fd)) { + socket_close(psock); + return 1; + } + + return socket_nonblock(psock); +} + +int socket_close(psocket *psock) +{ + int rv; + + assert(psock); + if (psock->fd < 0) + return 0; + rv = close(psock->fd); + psock->fd = -1; + + return rv; +} + +void socket_clone(const psocket *src, psocket *dst) +{ + assert(src && dst); + + memcpy(dst, src, sizeof(*dst)); + dst->fd = -1; +} + +ssize_t socket_get_ifnames(const psocket *test_sock, char name[][IFNAMSIZ], + size_t siz, int loopback_only) +{ + struct ifreq ifr; + struct ifreq *it, *end; + struct ifconf ifc; + char buf[1024]; + int sock; + size_t rc = 0; + + assert(test_sock); + sock = socket(test_sock->family, test_sock->socktype, + test_sock->protocol); + if (sock <= 0) + return -1; + + ifc.ifc_len = sizeof buf; + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) + return -1; + it = ifc.ifc_req; + end = it + (ifc.ifc_len / sizeof(struct ifreq)); + + for (; it != end; ++it) { + strncpy(ifr.ifr_name, it->ifr_name, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { + if (loopback_only && !(ifr.ifr_flags & IFF_LOOPBACK)) + continue; + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { + strncpy(name[rc++], it->ifr_name, IFNAMSIZ); + if (siz == rc) + break; + } + } + } + + close(sock); + + return rc; +} + +int socket_set_ifaddr(const psocket *test_sock, + const char *ifname, const char *addr, const char *mask) +{ + struct ifreq ifr; + int sock; + + assert(test_sock); + sock = socket(test_sock->family, test_sock->socktype, + test_sock->protocol); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + + ifr.ifr_addr.sa_family = AF_INET; + inet_pton(AF_INET, addr, ifr.ifr_addr.sa_data + 2); + ioctl(sock, SIOCSIFADDR, &ifr); + + inet_pton(AF_INET, mask, ifr.ifr_addr.sa_data + 2); + ioctl(sock, SIOCSIFNETMASK, &ifr); + + ioctl(sock, SIOCGIFFLAGS, &ifr); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); + + ioctl(sock, SIOCSIFFLAGS, &ifr); + close(sock); + + return 0; } |