| // CODYlib -*- mode:c++ -*- | |
| // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org | |
| // License: Apache v2.0 | |
| // Cody | |
| #include "internal.hh" | |
| #if CODY_NETWORKING | |
| // C | |
| #include <cerrno> | |
| #include <cstring> | |
| // OS | |
| #include <netdb.h> | |
| #include <unistd.h> | |
| #include <arpa/inet.h> | |
| #include <netinet/in.h> | |
| #include <sys/un.h> | |
| #ifndef AI_NUMERICSERV | |
| #define AI_NUMERICSERV 0 | |
| #endif | |
| // Client-side networking helpers | |
| namespace Cody { | |
| int OpenSocket (char const **e, sockaddr const *addr, socklen_t len) | |
| { | |
| 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 (connect (fd, addr, len) < 0) | |
| { | |
| errstr = "connecting socket"; | |
| goto fail; | |
| } | |
| return fd; | |
| } | |
| int OpenLocal (char const **e, char const *name) | |
| { | |
| 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 OpenSocket (e, (sockaddr *)&addr, sizeof (addr)); | |
| } | |
| int OpenInet6 (char const **e, char const *name, int port) | |
| { | |
| addrinfo *addrs = nullptr; | |
| int fd = -1; | |
| char const *errstr = nullptr; | |
| fd = socket (AF_INET6, SOCK_STREAM, 0); | |
| if (fd < 0) | |
| { | |
| errstr = "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 = 0; | |
| 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; | |
| if (int err = getaddrinfo (name, nullptr, &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 (!connect (fd, next->ai_addr, next->ai_addrlen)) | |
| goto done; | |
| } | |
| errstr = "connecting"; | |
| goto fail; | |
| done:; | |
| freeaddrinfo (addrs); | |
| return fd; | |
| } | |
| } | |
| #endif |