diff options
-rwxr-xr-x | configure | 20 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | debian/changelog | 9 | ||||
-rw-r--r-- | debian/control | 4 | ||||
-rw-r--r-- | debian/files | 1 | ||||
-rw-r--r-- | debian/libhx-dev.symbols | 1 | ||||
-rw-r--r-- | debian/libhx32.symbols | 5 | ||||
-rw-r--r-- | doc/api.rst | 4 | ||||
-rw-r--r-- | doc/changelog.rst | 92 | ||||
-rw-r--r-- | doc/socket_functions.rst | 48 | ||||
-rw-r--r-- | include/libHX/libxml_helper.h | 15 | ||||
-rw-r--r-- | include/libHX/socket.h | 5 | ||||
-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 |
24 files changed, 504 insertions, 91 deletions
@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for libHX 4.10. +# Generated by GNU Autoconf 2.71 for libHX 4.14. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, @@ -618,8 +618,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libHX' PACKAGE_TARNAME='libhx' -PACKAGE_VERSION='4.10' -PACKAGE_STRING='libHX 4.10' +PACKAGE_VERSION='4.14' +PACKAGE_STRING='libHX 4.14' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1375,7 +1375,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libHX 4.10 to adapt to many kinds of systems. +\`configure' configures libHX 4.14 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1446,7 +1446,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libHX 4.10:";; + short | recursive ) echo "Configuration of libHX 4.14:";; esac cat <<\_ACEOF @@ -1561,7 +1561,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libHX configure 4.10 +libHX configure 4.14 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2195,7 +2195,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libHX $as_me 4.10, which was +It was created by libHX $as_me 4.14, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3684,7 +3684,7 @@ fi # Define the identity of the package. PACKAGE='libhx' - VERSION='4.10' + VERSION='4.14' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -18771,7 +18771,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libHX $as_me 4.10, which was +This file was extended by libHX $as_me 4.14, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18839,7 +18839,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -libHX config.status 4.10 +libHX config.status 4.14 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 10c01b7..92c72c7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([libHX], [4.10]) +AC_INIT([libHX], [4.14]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/debian/changelog b/debian/changelog index 16df304..429ee49 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +libhx (4.14-1) unstable; urgency=medium + + * New upstream release. + - Refresh symbols files. + * debian/control: + - Change Vcs-* to new URL. + + -- Jörg Frings-Fürst <debian@jff.email> Sat, 15 Jul 2023 10:34:12 +0200 + libhx (4.10-1) unstable; urgency=medium * New upstream release. diff --git a/debian/control b/debian/control index 98ef1be..470c2bb 100644 --- a/debian/control +++ b/debian/control @@ -7,8 +7,8 @@ Standards-Version: 4.6.2.0 Section: libs Rules-Requires-Root: no Homepage: https://inai.de/projects/libhx/ -Vcs-Git: git://jff.email/opt/git/libhx.git -Vcs-Browser: https://jff.email/cgit/libhx.git +Vcs-Git: git://git.jff.email/libhx.git +Vcs-Browser: https://git.jff.email/cgit/libhx.git Package: libhx32 Architecture: any diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..bd8c150 --- /dev/null +++ b/debian/files @@ -0,0 +1 @@ +libhx_4.14-1_source.buildinfo libs optional diff --git a/debian/libhx-dev.symbols b/debian/libhx-dev.symbols index 82ae111..a054c0f 100644 --- a/debian/libhx-dev.symbols +++ b/debian/libhx-dev.symbols @@ -2,6 +2,7 @@ libHX_rtcheck.so libhx-dev #MINVER# * Build-Depends-Package: libhx-dev LIBHX_3.25@LIBHX_3.25 3.25 LIBHX_3.27@LIBHX_3.27 4.7 + LIBHX_4.11@LIBHX_4.11 4.12 LIBHX_4.2@LIBHX_4.2 4.7 LIBHX_4.3@LIBHX_4.3 4.7 LIBHX_4.9@LIBHX_4.9 4.10 diff --git a/debian/libhx32.symbols b/debian/libhx32.symbols index 4921f2a..539b90e 100644 --- a/debian/libhx32.symbols +++ b/debian/libhx32.symbols @@ -1,5 +1,6 @@ libHX.so.32 libhx32 #MINVER# * Build-Depends-Package: libhx-dev + HX_addrport_split@LIBHX_4.11 4.12 HX_basename@LIBHX_3.25 3.25 HX_basename_exact@LIBHX_3.25 3.25 HX_chomp@LIBHX_3.25 3.25 @@ -21,9 +22,12 @@ libHX.so.32 libhx32 #MINVER# HX_getopt_usage@LIBHX_3.25 3.25 HX_getopt_usage_cb@LIBHX_3.25 3.25 HX_hexdump@LIBHX_3.25 3.25 + HX_inet_connect@LIBHX_4.11 4.12 + HX_inet_listen@LIBHX_4.11 4.12 HX_init@LIBHX_3.25 3.25 HX_ipaddr_is_local@LIBHX_4.9 4.10 HX_irand@LIBHX_3.25 3.25 + HX_local_listen@LIBHX_4.11 4.12 HX_memmem@LIBHX_3.25 3.25 HX_mkdir@LIBHX_3.25 3.25 HX_rand@LIBHX_3.25 3.25 @@ -142,6 +146,7 @@ libHX.so.32 libhx32 #MINVER# HXproc_wait@LIBHX_3.25 3.25 LIBHX_3.25@LIBHX_3.25 3.25 LIBHX_3.27@LIBHX_3.27 4.7 + LIBHX_4.11@LIBHX_4.11 4.12 LIBHX_4.2@LIBHX_4.2 4.7 LIBHX_4.3@LIBHX_4.3 4.7 LIBHX_4.9@LIBHX_4.9 4.10 diff --git a/doc/api.rst b/doc/api.rst index 5f009cc..5a3efe5 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -9,6 +9,10 @@ Function reference ====== ====== ====== ======================================== RMV MinVer FirstA Name ====== ====== ====== ======================================== +4.11 4.11 4.11 HX_addrport_split +4.11 4.11 4.11 HX_inet_connect +4.11 4.11 4.11 HX_inet_listen +4.11 4.11 4.11 HX_local_listen 4.9 4.9 4.9 HX_sockaddr_is_local 4.9 4.9 4.9 HX_ipaddr_is_local 4.7 4.7 4.7 HXQUOTE_BASE64IMAP diff --git a/doc/changelog.rst b/doc/changelog.rst index 9a1590c..26c4d06 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -1,3 +1,37 @@ +v4.14 (2023-07-14) +================== + +Fixes: + +* socket: make HX_addrport_split work on portless bracketed hostspec + + +v4.13 (2023-06-21) +================== + +Fixes: + +* io: do not fail HX_mkdir when a component is a symlink to a directory +* xml_helper: fix infinite recursion in xml_getnsprop + + +v4.12 (2023-02-27) +================== + +Fixes: + +* Plug a memory leak in HX_inet_listen + + +v4.11 (2023-02-26) +================== + +Enhancements: + +* socket: add HX_addrport_split, HX_inet_connect, HX_inet_listen, + HX_local_listen + + v4.10 (2023-01-29) ================== @@ -116,61 +150,3 @@ Enhancements: Fixes: * proc: re-close pipes when ``HXproc_build_pipes`` failed - - -v3.26 (2021-08-03) -================== - -Fixes: - -* io: cure a potential infinite loop on EOF with HXio_fullread() -* io: HXio_fullread() now returns actual bytes read rather than bytes requested -* time: rectified HX_timeval_sub producing wrong results - -Changes: - -* nullptr checks were added to HXshconfig_free, HXformat_free, HXdeque_free and - HXmap_free to make their behavior be in line with free(3). -* Documentation has been switched to reStructured Text. - - -v3.25 (2020-05-14) -================== - -Fixes: - -* string: fix out-of-bounds access when calling ``HX_strlcpy(x,y,0)`` - -Changes: - -* string: ``HX_split4`` renamed to ``HX_split_inplace`` -* string: ``HX_split5`` renamed to ``HX_split_fixed`` -* defs.h: removed partially implementation of ``FIELD_SIZEOF`` -* defs.h: removed custom ``offsetof`` definition; you will need to include - ``<stddef.h>`` or ``<cstddef>`` now. - - -v3.24 (2018-10-17) -================== - -Fixes: - -* defs: avoid compiler warning when using ``HX_list_for_each`` in C++ -* opt: synchronize ``HXOPT_AUTOHELP`` C behavior to C++ mode - - -v3.23 (2018-08-28) -================== - -Enhancements: - -* opt: the option parser now recognizes long option abbreviations -* io: use modern ``readdir`` rather than ``readdir_r`` - - -v3.22 (2014-08-25) -================== - -Enhancements: - -* string: add the ``HXQUOTE_SQLBQUOTE`` quoting variant diff --git a/doc/socket_functions.rst b/doc/socket_functions.rst index ead0f08..5d55cf8 100644 --- a/doc/socket_functions.rst +++ b/doc/socket_functions.rst @@ -6,10 +6,46 @@ Socket functions #include <libHX/socket.h> + int HX_addrport_split(const char *spec, char *host, size_t hsize, uint16_t *port); + int HX_inet_connect(const char *host, uint16_t port, unsigned int oflags); + int HX_inet_listen(const char *host, uint16_t port); + int HX_local_listen(const char *path); int HX_socket_from_env(const struct addrinfo *ai, const char *intf); int HX_sockaddr_is_local(const struct sockaddr *, socklen_t, unsigned int flags); int HX_ipaddr_is_local(const char *, unsigned int flags); +``HX_addrport_split`` + Splits a host specification like ``[fe80::1]:80`` or ``127.0.0.1:80`` + into a host and port part. The ``host`` parameter should point to a + buffer of size ``hsize``. ``port`` may be NULL. If ``spec`` did not + contain a port part, ``*port`` will *not* be updated, so it is wise to + set a default port first like in the example below. Upon success, the + value 2 is returned if both a host and a port were parsed (irrespective + of ``port`` being NULL or not). The value 1 is returned if only a host + portion was parsed. Upon error, a negative errno value is returned. + +``HX_inet_connect`` + The function first resolves the specified host or IPv6/IPv4 address + (must not be enclosed in square brackets), and then attempts to connect + to one of the addresses. The order of evaluation is dependent upon the + system defaults. (It may choose whatever protocol is offered by the + system.) ``oflags`` is a bitset which may contain ``O_NONBLOCK``, else + must be 0. Upon success, a socket file descriptor is returned. Upon + failure, a negative errno code is returned. + +``HX_inet_listen`` + The function first resolves ``host`` using ``getaddrinfo()` with + ``AI_PASSIVE``, then using ``HX_socket_from_env`` looks in the + environment for a matching socket to pick up, and otherwise uses the + first result from getaddrinfo to create a new socket. Upon error, a + negative errno value is returned. + +``HX_local_listen`` + The function creates a local system-specific socket. Using + ``HX_socket_from_env``, it will attempt to pick up a matching socket + from the environment, and otherwise create a new socket. Upon error, a + negative errno value is returned. + ``HX_socket_from_env`` The function looks up the current process's file descriptors for a socket that is listening and which matches the given addrinfo and @@ -32,3 +68,15 @@ Socket functions Takes a text representation of an IPv6/IPv4 address and, after transformation, calls ``HX_sockaddr_is_local``. ``flags`` and return value behave the same as that. + +Examples +-------- + +.. code-block:: c + + char host[256]; + uint16_t port = 443; + /* port won't be updated */ + HX_addrport_split("example.de", host, sizeof(host), &port); + /* port will be updated */ + HX_addrport_split("example.de:80", host, sizeof(host), &port); diff --git a/include/libHX/libxml_helper.h b/include/libHX/libxml_helper.h index ebe5613..5a02bf0 100644 --- a/include/libHX/libxml_helper.h +++ b/include/libHX/libxml_helper.h @@ -7,6 +7,7 @@ # include <string.h> #endif #include <libxml/parser.h> +#include <libHX/defs.h> #ifdef __cplusplus extern "C" { @@ -30,6 +31,10 @@ static __inline__ int xml_strcasecmp(const xmlChar *a, const char *b) #endif } +#ifdef __cplusplus +} /* extern "C" */ +#endif + static __inline__ char *xml_getprop(xmlNode *node, const char *attr) { #ifdef __cplusplus @@ -106,7 +111,15 @@ xml_setprop(xmlNode *node, const char *name, const char *value) } #ifdef __cplusplus -} /* extern "C" */ +static __inline__ const char *xml_getprop(const xmlNode *node, const char *attr) +{ + return xml_getprop(const_cast<xmlNode *>(node), attr); +} +static __inline__ char *xml_getnsprop(const xmlNode *node, const char *nsprefix, + const char *attr) +{ + return xml_getnsprop(const_cast<xmlNode *>(node), nsprefix, attr); +} #endif #endif /* _LIBHX_LIBXML_HELPER_H */ diff --git a/include/libHX/socket.h b/include/libHX/socket.h index e9db77f..e34e1c8 100644 --- a/include/libHX/socket.h +++ b/include/libHX/socket.h @@ -1,6 +1,7 @@ #ifndef _LIBHX_SOCKET_H #define _LIBHX_SOCKET_H 1 +#include <stdint.h> #ifdef _WIN32 # include <ws2tcpip.h> #else @@ -12,6 +13,10 @@ extern "C" { #endif +extern int HX_addrport_split(const char *spec, char *host, size_t hsize, uint16_t *port); +extern int HX_inet_connect(const char *host, uint16_t port, unsigned int oflags); +extern int HX_inet_listen(const char *host, uint16_t port); +extern int HX_local_listen(const char *path); extern int HX_socket_from_env(const struct addrinfo *, const char *intf); extern int HX_sockaddr_is_local(const struct sockaddr *, socklen_t, unsigned int flags); extern int HX_ipaddr_is_local(const char *, unsigned int flags); 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); |