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