diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/Makefile.in | 2 | ||||
-rw-r--r-- | src/format.c | 5 | ||||
-rw-r--r-- | src/io.c | 22 | ||||
-rw-r--r-- | src/libHX.map | 8 | ||||
-rw-r--r-- | src/map.c | 2 | ||||
-rw-r--r-- | src/proc.c | 2 | ||||
-rw-r--r-- | src/rand.c | 2 | ||||
-rw-r--r-- | src/socket.c | 257 | ||||
-rw-r--r-- | src/tc-list.c | 8 | ||||
-rw-r--r-- | src/tc-socket.c | 75 | ||||
-rw-r--r-- | src/tc-string.c | 4 |
12 files changed, 370 insertions, 19 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} diff --git a/src/format.c b/src/format.c index 25c09d6..e924c6a 100644 --- a/src/format.c +++ b/src/format.c @@ -7,6 +7,7 @@ * General Public License as published by the Free Software Foundation; * either version 2.1 or (at your option) any later version. */ +#include <assert.h> #include <errno.h> #include <stdbool.h> #include <stddef.h> @@ -681,6 +682,7 @@ EXPORT_SYMBOL ssize_t HXformat3_aprintf(const struct HXformat_map *blk, out: ret = -errno; HXmc_free(out); + *resultp = nullptr; return ret; } @@ -716,7 +718,7 @@ EXPORT_SYMBOL int HXformat_sprintf(const struct HXformat_map *ftable, EXPORT_SYMBOL ssize_t HXformat3_sprintf(const struct HXformat_map *ftable, char *dest, size_t size, const char *fmt) { - hxmc_t *str; + hxmc_t *str = nullptr; ssize_t ret; if ((ret = HXformat_aprintf(ftable, &str, fmt)) < 0) @@ -725,6 +727,7 @@ EXPORT_SYMBOL ssize_t HXformat3_sprintf(const struct HXformat_map *ftable, *dest = '\0'; return 0; } + assert(str != nullptr); strncpy(dest, str, size); size_t xl = strlen(dest); HXmc_free(str); @@ -11,6 +11,7 @@ # include "config.h" #endif #include <sys/stat.h> +#include <assert.h> #include <errno.h> #include <fcntl.h> #include <limits.h> @@ -65,7 +66,7 @@ static int mkdir_gen(const char *d, unsigned int mode) return 1; if (errno != EEXIST) return -errno; - if (lstat(d, &sb) == 0) { + if (stat(d, &sb) == 0) { #if defined(_WIN32) if (sb.st_mode & S_IFDIR) #else @@ -423,6 +424,7 @@ HX_realpath_symres(struct HX_realpath_state *state, const char *path) return -ELOOP; #endif + assert(state->link_target != nullptr); if (*state->link_target == '/') { *state->dest = '\0'; if (HXmc_setlen(&state->dest, 0) == NULL) @@ -594,39 +596,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; @@ -1145,7 +1145,7 @@ static const struct HXmap_node *HXumap_traverse(struct HXumap_trav *trav) while (trav->head == &hmap->bk_array[trav->bk_current]) { if (++trav->bk_current >= HXhash_primes[hmap->power]) - return false; + return NULL; trav->head = hmap->bk_array[trav->bk_current].next; } @@ -69,7 +69,7 @@ static int HXproc_switch_group(const struct passwd *pw, const char *group) return HXproc_switch_gid(pw, gr->gr_gid); } -EXPORT_SYMBOL int HXproc_switch_user(const char *user, const char *group) +EXPORT_SYMBOL enum HXproc_su_status HXproc_switch_user(const char *user, const char *group) { const struct passwd *pw = NULL; if (user != NULL && *user != '\0') { @@ -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..0b6f674 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,261 @@ # 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') { + memmove(host, spec, hlen); + host[hlen] = '\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; + if (ret != 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..3cdeb90 100644 --- a/src/tc-socket.c +++ b/src/tc-socket.c @@ -7,18 +7,46 @@ #include <libHX/socket.h> #ifndef _WIN32 # include <netdb.h> +# include <unistd.h> #endif #ifndef AI_V4MAPPED # define AI_V4MAPPED 0 #endif +#include "internal.h" -int main(void) +static int t_parse(void) +{ + char host[32] = "bogus"; + if (HX_addrport_split("[::1]", host, sizeof(host), nullptr) != 1 || + strcmp(host, "::1") != 0) + return 1; + if (HX_addrport_split("[]", host, sizeof(host), nullptr) != 1 || + strcmp(host, "") != 0) + return 1; + if (HX_addrport_split("", host, sizeof(host), nullptr) != 1 || + strcmp(host, "") != 0) + return 1; + return 0; +} + +static int t_local(void) { static const char *addrs[] = { "::1", "::2", "::ffff:127.0.0.1", "::", + "[::1]", "[::2]", "[::ffff:127.0.0.1]", "[::]", "127.0.0.1", "127.0.0.2", "1.1.1.1", "255.255.255.255", + "[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 +55,50 @@ 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; +} + +int main(void) +{ + int ret = t_parse(); + if (ret != 0) + return ret; + ret = t_local(); + if (ret != EXIT_SUCCESS) + return ret; return EXIT_SUCCESS; } diff --git a/src/tc-string.c b/src/tc-string.c index 77235c2..9bf668b 100644 --- a/src/tc-string.c +++ b/src/tc-string.c @@ -84,12 +84,16 @@ static void t_strncat(void) { char data[5] = "DATA"; +#ifndef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-truncation" +#endif if (snprintf(data, sizeof(data), "12345678") >= static_cast(ssize_t, sizeof(data))) printf("Not enough space\n"); +#ifndef __clang__ #pragma GCC diagnostic pop +#endif printf("String: >%s<\n", data); HX_strlcat(data, "pqrstuv__", 2); |