aboutsummaryrefslogtreecommitdiff
path: root/src/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/socket.c')
-rw-r--r--src/socket.c316
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;
}