1 // CODYlib -*- mode:c++ -*- 2 // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org 3 // License: Apache v2.0 4 5 // Cody 6 #include "internal.hh" 7 #if CODY_NETWORKING 8 // C 9 #include <cerrno> 10 #include <cstring> 11 // OS 12 #include <netdb.h> 13 #include <unistd.h> 14 #include <arpa/inet.h> 15 #include <netinet/in.h> 16 #include <sys/un.h> 17 18 #ifndef AI_NUMERICSERV 19 #define AI_NUMERICSERV 0 20 #endif 21 22 // Server-side networking helpers 23 24 namespace Cody { 25 26 int ListenSocket (char const **e, sockaddr const *addr, socklen_t len, 27 unsigned backlog) 28 { 29 char const *errstr = nullptr; 30 31 int fd = socket (addr->sa_family, SOCK_STREAM, 0); 32 if (fd < 0) 33 { 34 errstr = "creating socket"; 35 36 fail:; 37 int err = errno; 38 if (e) 39 *e = errstr; 40 if (fd >= 0) 41 close (fd); 42 errno = err; 43 return -1; 44 } 45 46 if (bind (fd, addr, len) < 0) 47 { 48 errstr = "binding socket"; 49 goto fail; 50 } 51 52 if (listen (fd, backlog ? backlog : 17) < 0) 53 { 54 errstr = "listening socket"; 55 goto fail; 56 } 57 58 return fd; 59 } 60 61 int ListenLocal (char const **e, char const *name, unsigned backlog) 62 { 63 sockaddr_un addr; 64 size_t len = strlen (name); 65 66 if (len >= sizeof (addr.sun_path)) 67 { 68 errno = ENAMETOOLONG; 69 return -1; 70 } 71 72 memset (&addr, 0, offsetof (sockaddr_un, sun_path)); 73 addr.sun_family = AF_UNIX; 74 memcpy (addr.sun_path, name, len + 1); 75 76 return ListenSocket (e, (sockaddr *)&addr, sizeof (addr), backlog); 77 } 78 79 int ListenInet6 (char const **e, char const *name, int port, unsigned backlog) 80 { 81 addrinfo *addrs = nullptr; 82 int fd = -1; 83 char const *errstr = nullptr; 84 85 fd = socket (AF_INET6, SOCK_STREAM, 0); 86 if (fd < 0) 87 { 88 errstr = "creating socket"; 89 90 fail:; 91 int err = errno; 92 if (e) 93 *e = errstr; 94 if (fd >= 0) 95 close (fd); 96 if (addrs) 97 freeaddrinfo (addrs); 98 errno = err; 99 return -1; 100 } 101 102 addrinfo hints; 103 hints.ai_flags = AI_NUMERICSERV; 104 hints.ai_family = AF_INET6; 105 hints.ai_socktype = SOCK_STREAM; 106 hints.ai_protocol = 0; 107 hints.ai_addrlen = 0; 108 hints.ai_addr = nullptr; 109 hints.ai_canonname = nullptr; 110 hints.ai_next = nullptr; 111 112 /* getaddrinfo requires a port number, but is quite happy to accept 113 invalid ones. So don't rely on it. */ 114 if (int err = getaddrinfo (name, "0", &hints, &addrs)) 115 { 116 errstr = gai_strerror (err); 117 // What's the best errno to set? 118 errno = 0; 119 goto fail; 120 } 121 122 sockaddr_in6 addr; 123 memset (&addr, 0, sizeof (addr)); 124 addr.sin6_family = AF_INET6; 125 126 for (struct addrinfo *next = addrs; next; next = next->ai_next) 127 if (next->ai_family == AF_INET6 128 && next->ai_socktype == SOCK_STREAM) 129 { 130 sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr; 131 in6->sin6_port = htons (port); 132 if (ntohs (in6->sin6_port) != port) 133 errno = EINVAL; 134 else if (!bind (fd, next->ai_addr, next->ai_addrlen)) 135 goto listen; 136 } 137 138 errstr = "binding socket"; 139 goto fail; 140 141 listen:; 142 freeaddrinfo (addrs); 143 addrs = nullptr; 144 145 if (listen (fd, backlog ? backlog : 17) < 0) 146 { 147 errstr = "listening socket"; 148 goto fail; 149 } 150 151 return fd; 152 } 153 154 } 155 #endif 156