/* SPDX-License-Identifier: GPL-2.0 */ /** \file net.c Networking-related helper functions. */ #include "para.h" #include #include #include #include #include #include #include "error.h" #include "net.h" #include "string.h" #include "list.h" #include "fd.h" /* Whether the given address conforms to the IPv4 address format. */ static inline bool is_valid_ipv4_address(const char *address) { struct in_addr test_it; return inet_pton(AF_INET, address, &test_it) != 0; } /** * Parse and validate IPv4 address/netmask string. * * \param cidr Address in CIDR notation * \param addr Copy of the IPv4 address part of \a cidr * \param addrlen Size of \a addr in bytes * \param netmask Value of the netmask part in \a cidr or the * default of 32 if not specified. * * \return Pointer to \a addr if successful, NULL on error. * \sa RFC 4632. */ char *parse_cidr(const char *cidr, char *addr, ssize_t addrlen, int32_t *netmask) { const char *o = cidr; char *c = addr, *end = c + (addrlen - 1); *netmask = 0x20; if (cidr == NULL || addrlen < 1) goto failed; for (o = cidr; (*c = *o == '/'? '\0' : *o); c++, o++) if (c == end) goto failed; if (*o == '/') if (para_atoi32(++o, netmask) < 0 || *netmask < 0 || *netmask > 0x20) goto failed; if (is_valid_ipv4_address(addr)) return addr; failed: *addr = '\0'; return NULL; } static bool is_v4_dot_quad(const char *address) { bool result; regex_t r; assert(para_regcomp(&r, "^([0-9]+\\.){3}[0-9]+$", REG_EXTENDED | REG_NOSUB) >= 0); result = regexec(&r, address, 0, NULL, 0) == 0; regfree(&r); return result; } /* Whether a string conforms to IPv6 address format (RFC 4291). */ static inline bool is_valid_ipv6_address(const char *address) { struct in6_addr test_it; return inet_pton(AF_INET6, address, &test_it) != 0; } /** * Perform basic syntax checking on the host-part of an URL: * * - Since ':' is invalid in IPv4 addresses and DNS names, the * presence of ':' causes interpretation as IPv6 address; * - next the first-match-wins algorithm from RFC 3986 is applied; * - else the string is considered as DNS name, to be resolved later. * * \param host The host string to check. * \return True if \a host passes the syntax checks. * * \sa RFC 3986, 3.2.2; RFC 1123, 2.1; RFC 1034, 3.5. */ static bool host_string_ok(const char *host) { if (host == NULL || *host == '\0') return false; if (strchr(host, ':') != NULL) return is_valid_ipv6_address(host); if (is_v4_dot_quad(host)) return is_valid_ipv4_address(host); return true; } /** * Parse and validate URL string. * * The URL syntax is loosely based on RFC 3986, supporting one of * - "["host"]"[:port] for native IPv6 addresses and * - host[:port] for IPv4 hostnames and DNS names. * * Native IPv6 addresses must be enclosed in square brackets, since * otherwise there is an ambiguity with the port separator `:'. * The 'port' part is always considered to be a number; if absent, * it is set to -1, to indicate that a default port is to be used. * * The following are valid examples: * - 10.10.1.1 * - 10.10.1.2:8000 * - localhost * - localhost:8001 * - [::1]:8000 * - [badc0de::1] * * \param url The URL string to take apart. * \param host To return the copied host part of \a url. * \param hostlen The maximum length of \a host. * \param port To return the port number (if any) of \a url. * * \return Pointer to \a host, or \p NULL if failed. If \p NULL is returned, * \a host and \a port are undefined. If no port number was present in \a url, * \a port is set to -1. * * \sa RFC 3986, 3.2.2/3.2.3. */ char *parse_url(const char *url, char *host, ssize_t hostlen, int32_t *port) { const char *o = url; char *c = host, *end = c + (hostlen - 1); *port = -1; if (o == NULL || hostlen < 1) goto failed; if (*o == '[') { for (++o; (*c = *o == ']' ? '\0' : *o); c++, o++) if (c == end) goto failed; if (*o++ != ']' || (*o != '\0' && *o != ':')) goto failed; } else { for (; (*c = *o == ':'? '\0' : *o); c++, o++) { if (c == end && o[1]) goto failed; } } if (*o == ':') if (para_atoi32(++o, port) < 0 || *port < 0 || *port > 0xffff) goto failed; if (host_string_ok(host)) return host; failed: *host = '\0'; return NULL; } /* * Stringify port number, resolve into service name where defined. * * \param port 2-byte port number, in host-byte-order. * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL. * \return Pointer to static result buffer. * * \sa getservbyport(3), services(5), nsswitch.conf(5). */ const char *stringify_port(int port, const char *transport) { static char service[NI_MAXSERV]; if (port < 0 || port > 0xFFFF) { snprintf(service, sizeof(service), "undefined (%d)", port); } else { struct servent *se = getservbyport(htons(port), transport); if (se == NULL) snprintf(service, sizeof(service), "%d", port); else snprintf(service, sizeof(service), "%s", se->s_name); } return service; } /* * Determine the socket type, given the symbolic name of the transport-layer * protocol. See ip(7), socket(2). */ static inline int sock_type(const unsigned l4type) { switch (l4type) { case IPPROTO_UDP: return SOCK_DGRAM; case IPPROTO_TCP: return SOCK_STREAM; } return -1; /* not supported here */ } /* Pretty-print transport-layer name. */ static const char *layer4_name(const unsigned l4type) { switch (l4type) { case IPPROTO_UDP: return "UDP"; case IPPROTO_TCP: return "TCP"; } return "UNKNOWN PROTOCOL"; } /** * Flowopts: Transport-layer independent encapsulation of socket options. * * These collect individual socket options into a queue, which is disposed of * directly after makesock(). The 'pre_conn_opt' structure is for internal use * only and should not be visible elsewhere. * * \sa setsockopt(2), \ref makesock(). */ struct pre_conn_opt { int sock_level; /**< Second argument to setsockopt() */ int sock_option; /**< Third argument to setsockopt() */ char *opt_name; /**< Stringified \a sock_option */ void *opt_val; /**< Fourth argument to setsockopt() */ socklen_t opt_len; /**< Fifth argument to setsockopt() */ struct list_head node; /**< FIFO, as sockopt order matters. */ }; /** * Resolve an IPv4/IPv6 address. * * \param l4type The layer-4 type (\p IPPROTO_xxx). * \param passive Whether \p AI_PASSIVE should be included as hint. * \param host Remote or local hostname or IPv/6 address string. * \param port_number Used to set the port in each returned address structure. * \param result addrinfo structures are returned here. * * The interpretation of \a host depends on the value of \a passive. On a * passive socket host is interpreted as an interface IPv4/6 address (can be * left NULL). On an active socket, \a host is the peer DNS name or IPv4/6 * address to connect to. * * \return Standard. * * \sa getaddrinfo(3). */ int lookup_address(unsigned l4type, bool passive, const char *host, int port_number, struct addrinfo **result) { int ret; char port[6]; /* port number has at most 5 digits */ struct addrinfo *addr = NULL, hints; *result = NULL; sprintf(port, "%d", port_number & 0xffff); /* Set up address hint structure */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = sock_type(l4type); /* only use addresses available on the host */ hints.ai_flags = AI_ADDRCONFIG; if (passive && host == NULL) hints.ai_flags |= AI_PASSIVE; /* Obtain local/remote address information */ ret = getaddrinfo(host, port, &hints, &addr); if (ret != 0) { PARA_ERROR_LOG("can not resolve %s address %s#%s: %s\n", layer4_name(l4type), host? host : (passive? "[loopback]" : "[localhost]"), port, gai_strerror(ret)); return -E_ADDRESS_LOOKUP; } *result = addr; return 1; } /** * Create an active or passive socket. * * \param l4type IPPROTO_TCP or IPPROTO_UDP. * \param passive Whether to call bind(2) or connect(2). * \param ai Address information as obtained from \ref lookup_address(). * * bind(2) is called on passive sockets, and connect(2) on active sockets. The * algorithm tries all possible address combinations until it succeeds. * * \return File descriptor on success, \p E_MAKESOCK on errors. * * \sa \ref lookup_address(), \ref makesock(), ip(7), ipv6(7), bind(2), * connect(2). */ int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai) { int ret = -E_MAKESOCK, on = 1; for (; ai; ai = ai->ai_next) { int fd; ret = socket(ai->ai_family, sock_type(l4type), l4type); if (ret < 0) { PARA_NOTICE_LOG("socket(): %s\n", strerror(errno)); continue; } fd = ret; if (!passive) { if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) { PARA_NOTICE_LOG("connect(): %s\n", strerror(errno)); close(fd); continue; } return fd; } /* * Reuse the address on passive sockets to avoid failure on * restart (protocols using listen()) and when creating * multiple listener instances (UDP multicast). */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { PARA_NOTICE_LOG("setsockopt(): %s\n", strerror(errno)); close(fd); continue; } if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) { PARA_NOTICE_LOG("bind(): %s\n", strerror(errno)); close(fd); continue; } return fd; } return -E_MAKESOCK; } /** * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket. * * \param l4type The layer-4 type (\p IPPROTO_xxx). * \param passive Whether this is a passive or active socket. * \param host Passed to \ref lookup_address(). * \param port_number Passed to \ref lookup_address(). * * This creates a ready-made IPv4/v6 socket structure after looking up the * necessary parameters. The function first calls \ref lookup_address() and * passes the address information to makesock_addrinfo() to create and * initialize the socket. * * \return The newly created file descriptor on success, a negative error code * on failure. * * \sa \ref lookup_address(), \ref makesock_addrinfo(). */ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_number) { struct addrinfo *ai; int ret = lookup_address(l4type, passive, host, port_number, &ai); if (ret >= 0) ret = makesock_addrinfo(l4type, passive, ai); if (ai) freeaddrinfo(ai); if (ret < 0) { PARA_NOTICE_LOG("can not create %s socket %s#%d.\n", layer4_name(l4type), host? host : (passive? "[loopback]" : "[localhost]"), port_number); } return ret; } /** * Create a passive / listening socket. * * \param l4type The transport-layer type (\p IPPROTO_xxx). * \param addr Passed to \ref parse_url() if not NULL. * \param port Ignored if addr contains a port number. * * \return Positive integer (socket descriptor) on success, negative value * otherwise. * * \sa \ref makesock(), ip(7), ipv6(7), bind(2), listen(2). */ int para_listen(unsigned l4type, const char *addr, uint16_t port) { char host[MAX_HOSTLEN]; int ret, fd, addr_port; if (addr) { if (!parse_url(addr, host, sizeof(host), &addr_port)) return -ERRNO_TO_PARA_ERROR(EINVAL); if (addr_port > 0) port = addr_port; addr = host; } fd = makesock(l4type, true /* passive */, addr, port); if (fd > 0) { ret = listen(fd, BACKLOG); if (ret < 0) { ret = errno; close(fd); return -ERRNO_TO_PARA_ERROR(ret); } PARA_INFO_LOG("listening on %s port %u, fd %d\n", layer4_name(l4type), port, fd); } return fd; } /** * Create a socket which listens on all network addresses. * * \param l4type See \ref para_listen(). * \param port See \ref para_listen(). * * This is a simple wrapper for \ref para_listen() which passes a NULL pointer * as the address information. * * \return See \ref para_listen(). */ int para_listen_simple(unsigned l4type, uint16_t port) { return para_listen(l4type, NULL, port); } /* Compute the address-family dependent address length of an IPv4/v6 socket. */ static socklen_t salen(const struct sockaddr *sa) { assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); return sa->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); } /* * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address. * ss: Container of IPv4/6 address. * Returns: Pointer to normalized address (may be static storage). * * \sa RFC 3493. */ static const struct sockaddr * normalize_ip_address(const struct sockaddr_storage *ss) { assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6); if (ss_is_addr_v4mapped(ss)) { const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss; static struct sockaddr_in ia; ia.sin_family = AF_INET; ia.sin_port = ia6->sin6_port; memcpy(&ia.sin_addr.s_addr, &(ia6->sin6_addr.s6_addr[12]), 4); return (const struct sockaddr *)&ia; } return (const struct sockaddr *)ss; } /** * Look up the remote side of a connected socket structure. * * \param fd The socket descriptor of the connected socket. * * \return A static character string identifying hostname and port of the * chosen side in numeric host:port format. * * \sa getsockname(2), getpeername(2), \ref parse_url(), getnameinfo(3), * services(5), nsswitch.conf(5). */ char *remote_name(int fd) { struct sockaddr_storage ss = {.ss_family = 0}; const struct sockaddr *sa; socklen_t sslen = sizeof(ss); char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; static char output[sizeof(hbuf) + sizeof(sbuf) + 4]; int ret; if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) { PARA_ERROR_LOG("can not determine address from fd %d: %s\n", fd, strerror(errno)); snprintf(output, sizeof(output), "(unknown)"); return output; } sa = normalize_ip_address(&ss); ret = getnameinfo(sa, salen(sa), hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (ret) { PARA_WARNING_LOG("hostname lookup error (%s).\n", gai_strerror(ret)); snprintf(output, sizeof(output), "(lookup error)"); } else if (sa->sa_family == AF_INET6) snprintf(output, sizeof(output), "[%s]:%s", hbuf, sbuf); else snprintf(output, sizeof(output), "%s:%s", hbuf, sbuf); return output; } /** * Extract IPv4 or IPv6-mapped-IPv4 address from sockaddr_storage. * * \param ss Container of IPv4/6 address. * \param ia Extracted IPv4 address (different from 0) or 0 if unsuccessful. * * \sa RFC 3493. */ void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia) { const struct sockaddr *sa = normalize_ip_address(ss); memset(ia, 0, sizeof(*ia)); if (sa->sa_family == AF_INET) *ia = ((struct sockaddr_in *)sa)->sin_addr; } /** * Compare the address part of IPv4/6 addresses. * * \param sa1 First address. * \param sa2 Second address. * * \return True iff the IP address of \a sa1 and \a sa2 match. */ bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2) { if (!sa1 || !sa2) return false; if (sa1->sa_family != sa2->sa_family) return false; if (sa1->sa_family == AF_INET) { struct sockaddr_in *a1 = (typeof(a1))sa1, *a2 = (typeof (a2))sa2; return a1->sin_addr.s_addr == a2->sin_addr.s_addr; } else if (sa1->sa_family == AF_INET6) { struct sockaddr_in6 *a1 = (typeof(a1))sa1, *a2 = (typeof (a2))sa2; return !memcmp(a1, a2, sizeof(*a1)); } else return false; } /** * Receive data from a file descriptor. * * \param fd The file descriptor. * \param buf The buffer to write the data to. * \param size The size of \a buf. * * Receive at most \a size bytes from file descriptor \a fd. * * \return The number of bytes received on success, negative on errors, zero if * the peer has performed an orderly shutdown. * * \sa recv(2). */ __must_check int recv_bin_buffer(int fd, char *buf, size_t size) { ssize_t n; n = recv(fd, buf, size, 0); if (n == -1) return -ERRNO_TO_PARA_ERROR(errno); return n; } /** * Receive and write terminating NULL byte. * * \param fd The file descriptor. * \param buf The buffer to write the data to. * \param size The size of \a buf. * * Read at most \a size - 1 bytes from file descriptor \a fd and * write a NULL byte at the end of the received data. * * \return The return value of the underlying call to \a recv_bin_buffer(). * * \sa \ref recv_bin_buffer() */ int recv_buffer(int fd, char *buf, size_t size) { int n; assert(size); n = recv_bin_buffer(fd, buf, size - 1); if (n >= 0) buf[n] = '\0'; else *buf = '\0'; return n; } /** * Wrapper around the accept system call. * * \param fd The listening socket. * \param addr Structure which is filled in with the address of the peer socket. * \param size Should contain the size of the structure pointed to by \a addr. * \param new_fd Result pointer. * * Accept incoming connections on addr, retry if interrupted. * * \return Negative on errors, zero if no connections are present to be accepted, * one otherwise. * * \sa accept(2). */ int para_accept(int fd, void *addr, socklen_t size, int *new_fd) { int ret; do ret = accept(fd, (struct sockaddr *) addr, &size); while (ret < 0 && errno == EINTR); if (ret >= 0) { *new_fd = ret; return 1; } if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; return -ERRNO_TO_PARA_ERROR(errno); } /** * The buffer size of the sun_path component of struct sockaddr_un. * * While glibc doesn't define UNIX_PATH_MAX, it documents it has being limited * to 108 bytes. On NetBSD it is only 104 bytes though. We trust UNIX_PATH_MAX * if it is defined and use the size of the ->sun_path member otherwise. This * should be safe everywhere. */ #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path)) #endif /* * Prepare a structure for AF_UNIX socket addresses. * * This just copies name to the sun_path component of u, prepending a zero byte * if abstract sockets are supported. * * The first call to this function tries to bind a socket to the abstract name * space. The result of this test is stored in a static variable. Subsequent * calls read this variable and create abstract sockets on systems that support * them. If a NULL pointer is passed as the name, the function only * initializes the static variable. */ static int init_unix_addr(struct sockaddr_un *u, const char *name) { static int use_abstract; memset(u->sun_path, 0, UNIX_PATH_MAX); u->sun_family = PF_UNIX; if (use_abstract == 0) { /* executed only once */ int fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -ERRNO_TO_PARA_ERROR(errno); memcpy(u->sun_path, "\0x\0", 3); if (bind(fd, (struct sockaddr *)u, sizeof(*u)) == 0) use_abstract = 1; /* yes */ else use_abstract = -1; /* no */ close(fd); PARA_NOTICE_LOG("%susing abstract socket namespace\n", use_abstract == 1? "" : "not "); } if (!name) return 0; if (strlen(name) + 1 >= UNIX_PATH_MAX) return -E_NAME_TOO_LONG; strcpy(u->sun_path + (use_abstract == 1? 1 : 0), name); return 1; } /** * Create a socket for local communication and listen on it. * * \param name The socket pathname. * * This function creates a passive local socket for sequenced, reliable, * two-way, connection-based byte streams. The socket file descriptor is set to * nonblocking mode and listen(2) is called to prepare the socket for * accepting incoming connection requests. * * \return The file descriptor on success, negative error code on failure. * * \sa socket(2), \sa bind(2), \sa chmod(2), listen(2), unix(7). */ int create_local_socket(const char *name) { struct sockaddr_un unix_addr; int fd, ret; ret = init_unix_addr(&unix_addr, name); if (ret <= 0) /* error, or name was NULL */ return ret; ret = socket(PF_UNIX, SOCK_STREAM, 0); if (ret < 0) return -ERRNO_TO_PARA_ERROR(errno); fd = ret; ret = mark_fd_nonblocking(fd); if (ret < 0) goto err; ret = bind(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)); if (ret < 0) { ret = -ERRNO_TO_PARA_ERROR(errno); goto err; } if (unix_addr.sun_path[0] != 0) { /* pathname socket */ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; ret = -E_CHMOD; if (chmod(name, mode) < 0) goto err; } if (listen(fd , 5) < 0) { ret = -ERRNO_TO_PARA_ERROR(errno); goto err; } return fd; err: close(fd); return ret; } /** * Prepare, create, and connect to a Unix domain socket for local communication. * * \param name The socket pathname. * * This function creates a local socket for sequenced, reliable, two-way, * connection-based byte streams. * * \return The file descriptor of the connected socket on success, negative on * errors. * * \sa \ref create_local_socket(), unix(7), connect(2). */ int connect_local_socket(const char *name) { struct sockaddr_un unix_addr; int fd, ret; PARA_DEBUG_LOG("connecting to %s\n", name); fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -ERRNO_TO_PARA_ERROR(errno); ret = init_unix_addr(&unix_addr, name); if (ret < 0) goto err; if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1) return fd; ret = -ERRNO_TO_PARA_ERROR(errno); err: close(fd); return ret; }