]> git.ozlabs.org Git - ccan/blobdiff - ccan/net/net.c
net: add server support.
[ccan] / ccan / net / net.c
index 82d6c4e54bd82e2c02415cb6cd7ebcf721f49659..eef28e2fd7100ed8f94179c8015c3cbc2dccd582 100644 (file)
@@ -1,5 +1,5 @@
+/* Licensed under BSD-MIT - see LICENSE file for details */
 #include <ccan/net/net.h>
-#include <ccan/noerr/noerr.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <poll.h>
@@ -10,6 +10,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <netinet/in.h>
 
 struct addrinfo *net_client_lookup(const char *hostname,
                                   const char *service,
@@ -64,7 +65,7 @@ static void remove_fd(struct pollfd pfd[],
 
 int net_connect(const struct addrinfo *addrinfo)
 {
-       int sockfd = -1;
+       int sockfd = -1, saved_errno;
        unsigned int i, num;
        const struct addrinfo *ipv4 = NULL, *ipv6 = NULL;
        const struct addrinfo *addr[MAX_PROTOS];
@@ -142,8 +143,98 @@ got_one:
                sockfd = pfd[i].fd;
 
 out:
+       saved_errno = errno;
        for (i = 0; i < num; i++)
                if (pfd[i].fd != sockfd)
-                       close_noerr(pfd[i].fd);
+                       close(pfd[i].fd);
+       errno = saved_errno;
        return sockfd;
 }
+
+struct addrinfo *net_server_lookup(const char *service,
+                                  int family,
+                                  int socktype)
+{
+       struct addrinfo *res, hints;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = family;
+       hints.ai_socktype = socktype;
+       hints.ai_flags = AI_PASSIVE;
+       hints.ai_protocol = 0;
+
+       if (getaddrinfo(NULL, service, &hints, &res) != 0)
+               return NULL;
+
+       return res;
+}
+
+static bool should_listen(const struct addrinfo *addrinfo)
+{
+#ifdef SOCK_SEQPACKET
+       if (addrinfo->ai_socktype == SOCK_SEQPACKET)
+               return true;
+#endif
+       return (addrinfo->ai_socktype == SOCK_STREAM);
+}
+
+static int make_listen_fd(const struct addrinfo *addrinfo)
+{
+       int saved_errno, fd, on = 1;
+
+       fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
+                   addrinfo->ai_protocol);
+       if (fd < 0)
+               return -1;
+
+       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+       if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
+               goto fail;
+
+       if (should_listen(addrinfo) && listen(fd, 5) != 0)
+               goto fail;
+       return fd;
+
+fail:
+       saved_errno = errno;
+       close(fd);
+       errno = saved_errno;
+       return -1;
+}
+
+int net_bind(const struct addrinfo *addrinfo, int fds[2])
+{
+       const struct addrinfo *ipv6, *ipv4;
+       unsigned int num;
+
+       if (addrinfo->ai_family == AF_INET)
+               ipv4 = addrinfo;
+       else if (addrinfo->ai_family == AF_INET6)
+               ipv6 = addrinfo;
+
+       if (addrinfo->ai_next) {
+               if (addrinfo->ai_next->ai_family == AF_INET)
+                       ipv4 = addrinfo->ai_next;
+               else if (addrinfo->ai_next->ai_family == AF_INET6)
+                       ipv6 = addrinfo->ai_next;
+       }
+
+       num = 0;
+       /* Take IPv6 first, since it might bind to IPv4 port too. */
+       if (ipv6) {
+               if ((fds[num] = make_listen_fd(ipv6)) >= 0)
+                       num++;
+               else
+                       ipv6 = NULL;
+       }
+       if (ipv4) {
+               if ((fds[num] = make_listen_fd(ipv4)) >= 0)
+                       num++;
+               else
+                       ipv4 = NULL;
+       }
+       if (num == 0)
+               return -1;
+
+       return num;
+}