summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.in2
-rw-r--r--src/io.c19
-rw-r--r--src/libHX.map8
-rw-r--r--src/rand.c2
-rw-r--r--src/socket.c252
-rw-r--r--src/tc-list.c8
-rw-r--r--src/tc-socket.c44
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}
diff --git a/src/io.c b/src/io.c
index 720a9c5..a6c0a68 100644
--- a/src/io.c
+++ b/src/io.c
@@ -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;
diff --git a/src/rand.c b/src/rand.c
index 4a3f3ce..498d4a5 100644
--- a/src/rand.c
+++ b/src/rand.c
@@ -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;
}