diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/Makefile.in | 2 | ||||
-rw-r--r-- | src/io.c | 19 | ||||
-rw-r--r-- | src/libHX.map | 8 | ||||
-rw-r--r-- | src/rand.c | 2 | ||||
-rw-r--r-- | src/socket.c | 252 | ||||
-rw-r--r-- | src/tc-list.c | 8 | ||||
-rw-r--r-- | src/tc-socket.c | 44 |
8 files changed, 323 insertions, 14 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 921e6b7..f7ff4f6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,7 @@ libHX_la_SOURCES = deque.c dl.c format.c io.c map.c \ mc.c misc.c opt.c proc.c \ rand.c socket.c string.c time.c libHX_la_LIBADD = ${libdl_LIBS} -lm ${libpthread_LIBS} ${librt_LIBS} -libHX_la_LDFLAGS = -no-undefined -version-info 36:0:4 +libHX_la_LDFLAGS = -no-undefined -version-info 37:0:5 if WITH_GNU_LD libHX_la_LDFLAGS += -Wl,--version-script=${srcdir}/libHX.map endif diff --git a/src/Makefile.in b/src/Makefile.in index 8257e20..43f7444 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -780,7 +780,7 @@ libHX_la_SOURCES = deque.c dl.c format.c io.c map.c mc.c misc.c opt.c \ proc.c rand.c socket.c string.c time.c $(am__append_3) libHX_la_LIBADD = ${libdl_LIBS} -lm ${libpthread_LIBS} ${librt_LIBS} \ $(am__append_4) -libHX_la_LDFLAGS = -no-undefined -version-info 36:0:4 $(am__append_2) +libHX_la_LDFLAGS = -no-undefined -version-info 37:0:5 $(am__append_2) EXTRA_libHX_la_DEPENDENCIES = libHX.map libHX_rtcheck_la_SOURCES = rtcheck.c libHX_rtcheck_la_LIBADD = ${libdl_LIBS} @@ -11,6 +11,7 @@ # include "config.h" #endif #include <sys/stat.h> +#include <assert.h> #include <errno.h> #include <fcntl.h> #include <limits.h> @@ -594,39 +595,37 @@ EXPORT_SYMBOL int HX_rrmdir(const char *dir) EXPORT_SYMBOL ssize_t HXio_fullread(int fd, void *vbuf, size_t size) { char *buf = vbuf; - size_t done = 0; if (size > SSIZE_MAX) size = SSIZE_MAX; - while (done < size) { - ssize_t ret = read(fd, buf, size - done); + while (size > 0) { + ssize_t ret = read(fd, buf, size); if (ret < 0) return ret; else if (ret == 0) break; - done += ret; buf += ret; + size -= ret; } - return done; + return buf - static_cast(char *, vbuf); } EXPORT_SYMBOL ssize_t HXio_fullwrite(int fd, const void *vbuf, size_t size) { const char *buf = vbuf; - size_t done = 0; if (size > SSIZE_MAX) size = SSIZE_MAX; - while (done < size) { - ssize_t ret = write(fd, buf, size - done); + while (size > 0) { + ssize_t ret = write(fd, buf, size); if (ret < 0) return ret; else if (ret == 0) break; - done += ret; buf += ret; + size -= ret; } - return done; + return buf - static_cast(const char *, vbuf); } #if __linux__ diff --git a/src/libHX.map b/src/libHX.map index c4cef55..33415fa 100644 --- a/src/libHX.map +++ b/src/libHX.map @@ -159,3 +159,11 @@ global: HX_ipaddr_is_local; HX_sockaddr_is_local; } LIBHX_4.3; + +LIBHX_4.11 { +global: + HX_addrport_split; + HX_inet_connect; + HX_inet_listen; + HX_local_listen; +} LIBHX_4.9; @@ -27,7 +27,7 @@ static unsigned int HXrand_obtain_seed(void) { - unsigned int s; + unsigned long s; #if defined(HAVE_CLOCK_GETTIME) struct timespec tv; diff --git a/src/socket.c b/src/socket.c index ced884a..9b5d78a 100644 --- a/src/socket.c +++ b/src/socket.c @@ -17,10 +17,12 @@ #ifdef _WIN32 # include <ws2tcpip.h> #else +# include <fcntl.h> # include <netdb.h> # include <unistd.h> # include <netinet/in.h> # include <sys/socket.h> +# include <sys/stat.h> #endif #ifdef HAVE_SYS_UN_H # include <sys/un.h> @@ -46,6 +48,256 @@ # define AI_V4MAPPED 0 #endif +/** + * Return the pointer to the singular colon character, any other input + * yields nullptr. + */ +static inline const char *has_exactly_one_colon(const char *s) +{ + s = strchr(s, ':'); + if (s == nullptr) + return nullptr; + return strchr(s + 1, ':') == nullptr ? s : nullptr; +} + +/** + * @spec: "[" HOST-ANY "]" [ ":" PORT ] + * HOST-NAME [ ":" PORT ] + * HOST-IPV4 [ ":" PORT ] + * HOST-IPV6 + * @host: buffer for placing the extracted hostname + * (can overlap @spec) + * @hsize: buffer size for @host + * @port: storage space for extracted port number + * (can be nullptr) + * + * Returns <0 (error code) if unparsable or if the output buffer is too small. + * Success if on >=0. + */ +int HX_addrport_split(const char *spec, char *host, + size_t hbufsz, uint16_t *pport) +{ + if (*spec == '[') { + /* We also happen to allow IPv4 addrs and hostnames in [] */ + ++spec; + const char *end = strchr(spec, ']'); + if (end == nullptr) + return -EINVAL; + unsigned long hlen = end - spec; + if (hlen >= hbufsz) + return -E2BIG; + if (*++end == '\0') + return 1; + if (*end++ != ':') + return -EINVAL; + char *nend = nullptr; + uint16_t port = strtoul(end, &nend, 10); + if (nend == nullptr || *nend != '\0') + return -EINVAL; + memmove(host, spec, hlen); + host[hlen] = '\0'; + if (pport == nullptr) + return 2; + *pport = port; + return 2; + } + const char *onecolon = has_exactly_one_colon(spec); + if (onecolon != nullptr) { + unsigned long hlen = onecolon - spec; + if (hlen >= hbufsz) + return -E2BIG; + char *nend = nullptr; + uint16_t port = strtoul(onecolon + 1, &nend, 10); + if (nend == nullptr || *nend != '\0') + return -EINVAL; + memmove(host, spec, hlen); + host[hlen] = '\0'; + if (pport == nullptr) + return 2; + *pport = port; + return 2; + } + size_t hlen = strlen(spec); + if (hlen >= SIZE_MAX || ++hlen >= hbufsz) + return -E2BIG; + memmove(host, spec, hlen); + return 1; +} + +static int HX_inet_lookup(const char *host, uint16_t port, unsigned int xflags, + struct addrinfo **res) +{ + struct addrinfo hints = {}; +#if defined(AI_V4MAPPED) + hints.ai_flags = AI_V4MAPPED | xflags; +#else + hints.ai_flags = xflags; +#endif + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + + char portbuf[HXSIZEOF_Z32]; + snprintf(portbuf, sizeof(portbuf), "%hu", port); + return getaddrinfo(host, port == 0 ? nullptr : portbuf, &hints, res); +} + +int HX_inet_connect(const char *host, uint16_t port, unsigned int oflags) +{ + struct addrinfo *aires = nullptr; + int ret = HX_inet_lookup(host, port, AI_ADDRCONFIG, &aires); + int saved_errno = 0; + for (const struct addrinfo *r = aires; r != nullptr; r = r->ai_next) { + int fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol); + if (fd < 0) { + if (saved_errno == 0) + saved_errno = errno; + continue; + } +#ifdef O_NONBLOCK + if (oflags & O_NONBLOCK) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + flags = 0; + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) != 0) { + saved_errno = errno; + close(fd); + continue; + } + } +#endif + ret = connect(fd, r->ai_addr, r->ai_addrlen); + if (ret == 0) { + freeaddrinfo(aires); + return fd; + } +#ifdef O_NONBLOCK + if ((errno == EWOULDBLOCK || errno == EINPROGRESS) && + (oflags & O_NONBLOCK)) { + freeaddrinfo(aires); + return fd; + } +#endif + saved_errno = errno; + close(fd); + } + if (aires == nullptr && saved_errno == 0) + saved_errno = EHOSTUNREACH; + if (aires != nullptr) + freeaddrinfo(aires); + return -(errno = saved_errno); +} + +static int HX_gai_listen(const struct addrinfo *r) +{ + int fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol); + if (fd < 0) + return -2; + static const int y = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, STUPIDWIN(&y), sizeof(y)) < 0) + /* warn setsockopt: %s, strerror(errno)) */ ; + int ret = bind(fd, r->ai_addr, r->ai_addrlen); + if (ret != 0) { + int se = errno; + close(fd); + errno = se; + return -1; + } + ret = listen(fd, SOMAXCONN); + if (ret != 0) { + int se = errno; + close(fd); + errno = se; + return -2; + } + return fd; +} + +int HX_inet_listen(const char *host, uint16_t port) +{ + struct addrinfo *aires = nullptr; + int ret = HX_inet_lookup(host, port, AI_PASSIVE, &aires); + if (ret != 0) + ; + int saved_errno = EHOSTUNREACH; + bool use_env = getenv("HX_LISTEN_TOP_FD") != nullptr || getenv("LISTEN_FDS") != nullptr; + for (const struct addrinfo *r = aires; r != nullptr; r = r->ai_next) { + if (use_env) { + int fd = HX_socket_from_env(r, nullptr); + if (fd >= 0) { + freeaddrinfo(aires); + return fd; + } + } + int fd = HX_gai_listen(r); + if (fd >= 0) { + freeaddrinfo(aires); + return fd; + } + saved_errno = errno; + if (fd == -2) + continue; + break; + } + if (aires != nullptr) + freeaddrinfo(aires); + return -(errno = saved_errno); +} + +int HX_local_listen(const char *path) +{ +#ifdef HAVE_SYS_UN_H + struct sockaddr_un u; + if (strlen(path) >= sizeof(u.sun_path)) + return -EINVAL; + u.sun_family = AF_LOCAL; + strcpy(u.sun_path, path); + struct addrinfo r = {}; + r.ai_flags = AI_PASSIVE; + r.ai_family = AF_LOCAL; + r.ai_socktype = SOCK_STREAM; + r.ai_addrlen = sizeof(u) - sizeof(u.sun_path) + strlen(u.sun_path) + 1; + r.ai_addr = reinterpret_cast(struct sockaddr *, &u); + bool use_env = getenv("HX_LISTEN_TOP_FD") != nullptr || getenv("LISTEN_FDS") != nullptr; + if (use_env) { + int fd = HX_socket_from_env(&r, nullptr); + if (fd >= 0) + return fd; + } + int ret = HX_gai_listen(&r); + if (ret >= 0) + return ret; /* fd */ + if (ret == -2 || errno != EADDRINUSE) + return -errno; + + struct stat sb; + ret = stat(path, &sb); + if (ret < 0) + return -errno; + if (!S_ISSOCK(sb.st_mode)) + return -ENOTSOCK; + + int testfd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (testfd < 0) + return -errno; + ret = connect(testfd, r.ai_addr, r.ai_addrlen); + close(testfd); + if (ret == 0) + return -EADDRINUSE; + + /* There will be a TOCTOU report, but what can you do... */ + ret = unlink(path); + if (ret < 0 && errno != ENOENT) + return -errno; + ret = HX_gai_listen(&r); + if (ret >= 0) + return ret; /* fd */ + return -errno; +#else + return -EPROTONOSUPPORT; +#endif +} + static int try_sk_from_env(int fd, const struct addrinfo *ai, const char *intf) { int value = 0; diff --git a/src/tc-list.c b/src/tc-list.c index e0087e3..2fd6380 100644 --- a/src/tc-list.c +++ b/src/tc-list.c @@ -69,8 +69,14 @@ static void l_dump(bool pop) while ((obj = (pop ? HXclist_pop(&strings_ct, struct text_object, list) : HXclist_shift(&strings_ct, struct text_object, list) - )) != NULL) + )) != NULL) { printf("%s item %u (\"%s\")\n", msg[pop], ++i, obj->id); +#ifdef __cplusplus + delete obj; +#else + free(obj); +#endif + } printf("Remaining elements: %u\n", strings_ct.items); } diff --git a/src/tc-socket.c b/src/tc-socket.c index 9c73d24..9904ba8 100644 --- a/src/tc-socket.c +++ b/src/tc-socket.c @@ -7,6 +7,7 @@ #include <libHX/socket.h> #ifndef _WIN32 # include <netdb.h> +# include <unistd.h> #endif #ifndef AI_V4MAPPED # define AI_V4MAPPED 0 @@ -19,6 +20,15 @@ int main(void) "127.0.0.1", "127.0.0.2", "1.1.1.1", "255.255.255.255", }; for (size_t i = 0; i < ARRAY_SIZE(addrs); ++i) { + char host[32] = {}; + uint16_t port = 0; + + int ret = HX_addrport_split(addrs[i], host, sizeof(host), &port); + if (ret >= 0) + printf("Parse \"%s\" -> [%s]:%hu\n", addrs[i], host, port); + else + return EXIT_FAILURE; + printf("%-16s\t", addrs[i]); int lcl = HX_ipaddr_is_local(addrs[i], AI_V4MAPPED); if (lcl < 0) { @@ -27,5 +37,39 @@ int main(void) } printf("%d\n", lcl); } + + char host[32] = {}; + uint16_t port = 0; + int ret = HX_addrport_split("[fe80::1]:80", host, sizeof(host), &port); + if (ret < 0 || port != 80) + return EXIT_FAILURE; + port = 443; + ret = HX_addrport_split("::80", host, sizeof(host), &port); + if (ret < 0 || port != 443) + return EXIT_FAILURE; + ret = HX_addrport_split(":::80", host, sizeof(host), &port); + if (ret < 0 || port != 443) + return EXIT_FAILURE; + ret = HX_addrport_split("0.0.0.0", host, sizeof(host), &port); + if (ret < 0 || port != 443) + return EXIT_FAILURE; + ret = HX_addrport_split("0.0.0.0:80", host, sizeof(host), &port); + if (ret < 0 || port != 80) + return EXIT_FAILURE; + + int fd = HX_inet_connect("::1", 80, 0); + if (fd >= 0) { + printf("Connected to [::1]:80\n"); + close(fd); + } else { + fprintf(stderr, "HX_inet_connect [::1]:80: %s\n", strerror(-fd)); + } + fd = HX_inet_connect("::", 80, 0); + if (fd >= 0) { + printf("Connected to [::]:80\n"); + close(fd); + } else { + fprintf(stderr, "HX_inet_connect [::]:80: %s\n", strerror(-fd)); + } return EXIT_SUCCESS; } |