// CODYlib -*- mode:c++ -*- // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org // License: Apache v2.0 // Cody #include "internal.hh" #if CODY_NETWORKING // C #include #include // OS #include #include #include #include #include #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif // Server-side networking helpers namespace Cody { int ListenSocket (char const **e, sockaddr const *addr, socklen_t len, unsigned backlog) { char const *errstr = nullptr; int fd = socket (addr->sa_family, SOCK_STREAM, 0); if (fd < 0) { errstr = "creating socket"; fail:; int err = errno; if (e) *e = errstr; if (fd >= 0) close (fd); errno = err; return -1; } if (bind (fd, addr, len) < 0) { errstr = "binding socket"; goto fail; } if (listen (fd, backlog ? backlog : 17) < 0) { errstr = "listening socket"; goto fail; } return fd; } int ListenLocal (char const **e, char const *name, unsigned backlog) { sockaddr_un addr; size_t len = strlen (name); if (len >= sizeof (addr.sun_path)) { errno = ENAMETOOLONG; return -1; } memset (&addr, 0, offsetof (sockaddr_un, sun_path)); addr.sun_family = AF_UNIX; memcpy (addr.sun_path, name, len + 1); return ListenSocket (e, (sockaddr *)&addr, sizeof (addr), backlog); } int ListenInet6 (char const **e, char const *name, int port, unsigned backlog) { addrinfo *addrs = nullptr; int fd = -1; char const *errstr = nullptr; fd = socket (AF_INET6, SOCK_STREAM, 0); if (fd < 0) { errstr = "creating socket"; fail:; int err = errno; if (e) *e = errstr; if (fd >= 0) close (fd); if (addrs) freeaddrinfo (addrs); errno = err; return -1; } addrinfo hints; hints.ai_flags = AI_NUMERICSERV; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = nullptr; hints.ai_canonname = nullptr; hints.ai_next = nullptr; /* getaddrinfo requires a port number, but is quite happy to accept invalid ones. So don't rely on it. */ if (int err = getaddrinfo (name, "0", &hints, &addrs)) { errstr = gai_strerror (err); // What's the best errno to set? errno = 0; goto fail; } sockaddr_in6 addr; memset (&addr, 0, sizeof (addr)); addr.sin6_family = AF_INET6; for (struct addrinfo *next = addrs; next; next = next->ai_next) if (next->ai_family == AF_INET6 && next->ai_socktype == SOCK_STREAM) { sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr; in6->sin6_port = htons (port); if (ntohs (in6->sin6_port) != port) errno = EINVAL; else if (!bind (fd, next->ai_addr, next->ai_addrlen)) goto listen; } errstr = "binding socket"; goto fail; listen:; freeaddrinfo (addrs); addrs = nullptr; if (listen (fd, backlog ? backlog : 17) < 0) { errstr = "listening socket"; goto fail; } return fd; } } #endif