From 320a9eedb762a2cd7b029eb0cc5393d3d4cae0e4 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Mon, 1 Dec 2025 17:16:30 +0100 Subject: [PATCH] mv to codeberg --- .woodpecker/build.yaml | 32 -- .woodpecker/release.sh | 54 -- .woodpecker/release.yaml | 31 -- Makefile | 54 -- README.md | 2 + TODO | 7 - client.c | 82 --- client.h | 87 --- host.c | 173 ------ host.h | 56 -- log.c | 48 -- log.h | 40 -- meson.build | 89 ---- meson_options.txt | 1 - net.c | 447 ---------------- net.h | 74 --- platform.h.in | 101 ---- udpxd.1 | 301 ----------- udpxd.c | 250 --------- udpxd.h | 44 -- udpxd.pod | 153 ------ uthash.h | 1096 -------------------------------------- vg.sh | 20 - 23 files changed, 2 insertions(+), 3240 deletions(-) delete mode 100644 .woodpecker/build.yaml delete mode 100755 .woodpecker/release.sh delete mode 100644 .woodpecker/release.yaml delete mode 100644 Makefile delete mode 100644 TODO delete mode 100644 client.c delete mode 100644 client.h delete mode 100644 host.c delete mode 100644 host.h delete mode 100644 log.c delete mode 100644 log.h delete mode 100644 meson.build delete mode 100644 meson_options.txt delete mode 100644 net.c delete mode 100644 net.h delete mode 100644 platform.h.in delete mode 100644 udpxd.1 delete mode 100644 udpxd.c delete mode 100644 udpxd.h delete mode 100644 udpxd.pod delete mode 100755 uthash.h delete mode 100755 vg.sh diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml deleted file mode 100644 index eb8a3e3..0000000 --- a/.woodpecker/build.yaml +++ /dev/null @@ -1,32 +0,0 @@ -matrix: - platform: - - linux/amd64 - -labels: - platform: ${platform} - -steps: - build: - when: - event: [push] - image: alpine:latest - commands: - - apk update - - apk add --no-cache bash build-base gdb pkgconfig meson ninja perl bind-tools - - meson setup --reconfigure build - - ninja -C build - - test: - when: - event: [push] - image: alpine:latest - commands: - - apk update - - apk add --no-cache bash bind-tools - - build/udpxd -l 127.0.0.1:53 -t 8.8.8.8:53 & - - dig +nocmd +noall +answer google.de a @127.0.0.1 - - killall udpxd - #- build/udpxd -l [::1]:53 -t [2001:4860:4860::8888]:53 & - #- dig +nocmd +noall +answer google.de a @::1 - #- killall udpxd - diff --git a/.woodpecker/release.sh b/.woodpecker/release.sh deleted file mode 100755 index a44513a..0000000 --- a/.woodpecker/release.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# This is my own simple codeberg generic releaser. It takes to -# binaries to be uploaded as arguments and takes every other args from -# env. Works on tags or normal commits (push), tags must start with v. - - -set -e - -die() { - echo $* - exit 1 -} - -if test -z "$DEPLOY_TOKEN"; then - die "token DEPLOY_TOKEN not set" -fi - -git fetch --all - -# determine current tag or commit hash -version="$CI_COMMIT_TAG" -previous="" -log="" -if test -z "$version"; then - version="${CI_COMMIT_SHA:0:6}" - log=$(git log -1 --oneline) -else - previous=$(git tag -l | grep -E "^v" | tac | grep -A1 "$version" | tail -1) - log=$(git log -1 --oneline "${previous}..${version}" | sed 's|^|- |g') -fi - -# release body -printf "# Changes\n\n %s\n" "$log" > body.txt - -# create the release -https --ignore-stdin --check-status -b -A bearer -a "$DEPLOY_TOKEN" POST \ - "https://codeberg.org/api/v1/repos/${CI_REPO_OWNER}/${CI_REPO_NAME}/releases" \ - tag_name="$version" name="Release $version" body=@body.txt > release.json - -# we need the id to upload files -ID=$(jq -r .id < release.json) - -if test -z "$ID"; then - cat release.json - die "failed to create release" -fi - -# actually upload -for file in "$@"; do - https --ignore-stdin --check-status -A bearer -a "$DEPLOY_TOKEN" -f POST \ - "https://codeberg.org/api/v1/repos/${CI_REPO_OWNER}/${CI_REPO_NAME}/releases/$ID/assets" \ - "name=${file}" "attachment@${file}" -done diff --git a/.woodpecker/release.yaml b/.woodpecker/release.yaml deleted file mode 100644 index fca2a46..0000000 --- a/.woodpecker/release.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# build release - -labels: - platform: linux/amd64 - -steps: - compile: - when: - event: [tag,manual] - image: alpine:latest - commands: - - apk update - - apk add --no-cache bash build-base gdb pkgconfig meson ninja perl git - - meson setup --reconfigure --prefer-static -Dc_link_args="-static -ldl" --buildtype=release build - - ninja -C build - - meson dist -C build --formats xztar,gztar,zip --allow-dirty - - file build/udpxd - - mv build/udpxd udpxd-linux-amd64-$CI_COMMIT_TAG - - mv build/meson-dist/* . - - release: - image: alpine:latest - when: - event: [tag,manual] - environment: - DEPLOY_TOKEN: - from_secret: DEPLOY_TOKEN - commands: - - apk update - - apk add --no-cache bash httpie jq git - - .woodpecker/release.sh udpxd-* diff --git a/Makefile b/Makefile deleted file mode 100644 index dd45d7c..0000000 --- a/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# -# This file is part of udpxd. -# -# Copyright (C) 2015-2016 T.v.Dein. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# You can contact me by mail: . - -.PHONY: all clean man install deprecation - -# warning: do not set -O to 2, see TODO -CFLAGS = -Wall -Wextra -Werror -O1 -g -LDFLAGS= -OBJS = host.o client.o net.o udpxd.o log.o -DST = udpxd -PREFIX = /usr/local -UID = root -GID = 0 -MAN = udpxd.1 - -all: deprecation $(DST) - -$(DST): deprecation $(OBJS) - $(CC) $(OBJS) -o $(DST) - -%.o: deprecation %.c - $(CC) -c $(CFLAGS) $*.c -o $*.o - -clean: deprecation - rm -rf *.o $(DST) build .cache - -man: deprecation - pod2man udpxd.pod > udpxd.1 - -install: deprecation $(DST) - install -d -o $(UID) -g $(GID) $(PREFIX)/sbin - install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1 - install -o $(UID) -g $(GID) -m 555 $(DST) $(PREFIX)/sbin/ - install -o $(UID) -g $(GID) -m 444 $(MAN) $(PREFIX)/man/man1/ - -deprecation: - @echo "+++ DEPRECATION NOTE: Use meson & ninja to build please! +++" diff --git a/README.md b/README.md index 87af482..3a872a1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ [![status-badge](https://ci.codeberg.org/api/badges/15646/status.svg)](https://ci.codeberg.org/repos/15646) [![License](https://img.shields.io/badge/license-GPL-blue.svg)](https://codeberg.org/scip/udpxd/raw/branch/main/LICENSE) +> [!CAUTION] +> This software is now being maintained on [Codeberg](https://codeberg.org/scip/udpxd/). ## UDPXD - A general purpose UDP relay/port forwarder/proxy diff --git a/TODO b/TODO deleted file mode 100644 index 3c65742..0000000 --- a/TODO +++ /dev/null @@ -1,7 +0,0 @@ -MUST: -- if compiled with -O2, gcc mangles the dst sockaddr_in pointers in some weird ways - -MAYBE: -- ctrl client to view current "sessions", refresh etc -- config file -- support multiple different setups with one process diff --git a/client.c b/client.c deleted file mode 100644 index b5983d7..0000000 --- a/client.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#include "client.h" -#include "log.h" - -void client_del(client_t *client) { - HASH_DEL(clients, client); -} - -void client_add(client_t *client) { - HASH_ADD_INT(clients, socket, client); -} - -client_t *client_find_fd(int fd) { - client_t *client = NULL; - HASH_FIND_INT(clients, &fd, client); - return client; /* maybe NULL! */ -} - -client_t *client_find_src(host_t *src) { - client_t *current = NULL; - client_iter(clients, current) { - if(strcmp(current->src->ip, src->ip) == 0 && current->src->port == src->port) - return current; - } - return NULL; -} - -void client_seen(client_t *client) { - client->lastseen = (long)time(0); -} - -client_t *client_new(int fd, host_t *src, host_t *dst) { - client_t *client = malloc(sizeof(client_t)); - client->socket = fd; - client->src = src; - client->dst = dst; - client_seen(client); - return client; -} - -void client_close(client_t *client) { - client_del(client); - close(client->socket); - host_clean(client->src); - host_clean(client->dst); - free(client); -} - -void client_clean(int asap) { - uint32_t now = (long)time(0); - uint32_t diff; - client_t *current; - client_iter(clients, current) { - diff = now - current->lastseen; - if(diff >= MAXAGE || asap) { - verbose("closing socket %s:%d for client %s:%d (aged out after %d seconds)\n", - current->src->ip, current->src->port, current->dst->ip, current->dst->port, MAXAGE); - client_close(current); - } - } -} - diff --git a/client.h b/client.h deleted file mode 100644 index eb2f7b6..0000000 --- a/client.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#ifndef _HAVE_CLIENT_H -#define _HAVE_CLIENT_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifndef byte -typedef uint8_t byte; -#endif - -#include "uthash.h" -#include "host.h" - -#define MAXAGE 30 /* seconds after which to close outgoing sockets and forget client src */ - -struct _client_t { - int socket; /* bind socket for outgoing traffic */ - host_t *src; /* client src (ip+port) from incoming socket */ - host_t *dst; /* client dst (ip+port) to outgoing socket */ - uint64_t lastseen; /* when did we recv last time from it */ - UT_hash_handle hh; -}; -typedef struct _client_t client_t; - -extern client_t *clients; -extern int VERBOSE; -extern int FORKED; - -/* wrapper for HASH_ITER */ -/** Iterate over the list of clients. - - Sample use: - - @code - client_t *current = NULL; - client_iter(clientlist, current) { - dosomething(current) - } - @endcode -*/ -#define client_iter(clients, client) \ - client_t *__c = NULL; \ - HASH_ITER(hh, clients, client, __c) - - -void client_del(client_t *client); -void client_add(client_t *client); -void client_seen(client_t *client); -void client_close(client_t *client); -void client_clean(int asap); - -client_t *client_find_fd(int fd); -client_t *client_find_src(host_t *src); -client_t *client_new(int fd, host_t *src, host_t *dst); - - -#endif diff --git a/host.c b/host.c deleted file mode 100644 index 4424a23..0000000 --- a/host.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#include "host.h" - -/* fill a generic struct depending of what we already know, - which is easier to pass between functions, - maybe v4 or v6, filled from existing structs or from strings, - which create the sockaddr* structs */ -host_t *get_host(char *ip, int port, struct sockaddr_in *v4, struct sockaddr_in6 *v6) { - host_t *host = malloc(sizeof(host_t)); - host->sock = NULL; - host->is_v6 = 0; - host->port = port; - - if(ip != NULL) { - if(is_v6(ip)) { - struct sockaddr_in6 *tmp = malloc(sizeof(struct sockaddr_in6)); - memset(tmp, 0, sizeof(struct sockaddr_in6)); - - inet_pton(AF_INET6, ip, (struct in6_addr*)&tmp->sin6_addr); - - tmp->sin6_family = AF_INET6; - tmp->sin6_port = htons( port ); - - unsigned int scope = get_v6_scope(ip); - if (is_linklocal((struct in6_addr*)&tmp->sin6_addr)) - tmp->sin6_scope_id = scope; - else - tmp->sin6_scope_id = 0; - - host->is_v6 = 1; - host->sock = (struct sockaddr*)tmp; - host->size = sizeof(struct sockaddr_in6); - if(tmp->sin6_scope_id != 0) { - host->ip = malloc(INET6_ADDRSTRLEN + 9); /* plus [ % ] \0 , scope*/ - sprintf(host->ip, "[%s%%%d]", ip, scope); - } - else { - host->ip = malloc(INET6_ADDRSTRLEN + 3); /* plus [ ] \0 */ - sprintf(host->ip, "[%s]", ip); - } - } - else { - struct sockaddr_in *tmp = malloc(sizeof(struct sockaddr_in)); - memset(tmp, 0, sizeof(struct sockaddr_in)); - tmp->sin_family = AF_INET; - tmp->sin_addr.s_addr = inet_addr( ip ); - tmp->sin_port = htons( port ); - - host->sock = (struct sockaddr*)tmp; - host->size = sizeof(struct sockaddr_in); - host->ip = malloc(INET_ADDRSTRLEN+1); - memcpy(host->ip, ip, INET_ADDRSTRLEN); - } - } - else if(v4 != NULL) { - struct sockaddr_in *tmp = malloc(sizeof(struct sockaddr_in)); - memcpy(tmp, v4, sizeof(struct sockaddr_in)); - host->ip = malloc(INET_ADDRSTRLEN); - inet_ntop(AF_INET, (struct in_addr *)&tmp->sin_addr, host->ip, INET_ADDRSTRLEN); - - host->port = ntohs(tmp->sin_port); - host->sock = (struct sockaddr*)tmp; - host->size = sizeof(struct sockaddr_in); - } - else if(v6 != NULL) { - struct sockaddr_in6 *tmp = malloc(sizeof(struct sockaddr_in6)); - memcpy(tmp, v6, sizeof(struct sockaddr_in6)); - char *myip = malloc(INET6_ADDRSTRLEN); - inet_ntop(AF_INET6, (struct in6_addr *)&tmp->sin6_addr, myip, INET6_ADDRSTRLEN); - - host->port = ntohs(tmp->sin6_port); - host->sock = (struct sockaddr*)tmp; - host->is_v6 = 1; - host->size = sizeof(struct sockaddr_in6); - - if(tmp->sin6_scope_id != 0) { - host->ip = malloc(INET6_ADDRSTRLEN + 9); /* plus [ % ] \0 , scope*/ - sprintf(host->ip, "[%s%%%d]", myip, tmp->sin6_scope_id); - } - else { - host->ip = malloc(INET6_ADDRSTRLEN + 3); /* plus [ ] \0 */ - sprintf(host->ip, "[%s]", myip); - } - free(myip); - } - else { - fprintf(stderr, "call invalid!\n"); - exit(1); /* shall not happen */ - } - - return host; -} - -int is_v6(char *ip) { - char *IS = strchr(ip, ':'); - if(IS == NULL) - return 0; - else - return 1; -} - -/* via http://stackoverflow.com/questions/13504934/binding-sockets-to-ipv6-addresses - return the interface index (aka scope) of an ipv6 address, which is required - in order to bind to it. -*/ -unsigned get_v6_scope(const char *ip){ - struct ifaddrs *addrs, *addr; - char ipAddress[NI_MAXHOST]; - uint32_t scope=0; - int i; - - // walk over the list of all interface addresses - getifaddrs(&addrs); - for(addr=addrs;addr;addr=addr->ifa_next){ - if (addr->ifa_addr && addr->ifa_addr->sa_family==AF_INET6){ // only interested in ipv6 ones - getnameinfo(addr->ifa_addr,sizeof(struct sockaddr_in6),ipAddress,sizeof(ipAddress),NULL,0,NI_NUMERICHOST); - // result actually contains the interface name, so strip it - for(i=0;ipAddress[i];i++){ - if(ipAddress[i]=='%'){ - ipAddress[i]='\0'; - break; - } - } - // if the ip matches, convert the interface name to a scope index - if(strcmp(ipAddress,ip)==0){ - scope=if_nametoindex(addr->ifa_name); - break; - } - } - } - freeifaddrs(addrs); - return scope; -} - -/* this is the contents of the makro IN6_IS_ADDR_LINKLOCAL, - which doesn't compile, when used directly, for whatever reasons */ -int is_linklocal(struct in6_addr *a) { - return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80)); -} - -void host_dump(host_t *host) { - fprintf(stderr, "host - ip: %s\n", host->ip); - fprintf(stderr, " port: %d\n", host->port); - fprintf(stderr, " isv6: %d\n", host->is_v6); - fprintf(stderr, " size: %ld\n", (long int)host->size); - fprintf(stderr, " src: %p\n", host->sock); -} - -void host_clean(host_t *host) { - free(host->sock); - free(host->ip); - free(host); -} diff --git a/host.h b/host.h deleted file mode 100644 index 6a63a92..0000000 --- a/host.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#ifndef _HAVE_HOST_H -#define _HAVE_HOST_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include // if_nametoindex() -#include - -struct _host_t { - int is_v6; - struct sockaddr *sock; - size_t size; - char *ip; - int port; -}; -typedef struct _host_t host_t; - -unsigned get_v6_scope(const char *ip); -int is_linklocal(struct in6_addr *a); -host_t *get_host(char *ip, int port, struct sockaddr_in *v4, struct sockaddr_in6 *v6); -int is_v6(char *ip); -void host_dump(host_t *host); -void host_clean(host_t *host); - -#endif diff --git a/log.c b/log.c deleted file mode 100644 index 2a278b6..0000000 --- a/log.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#include "log.h" - - - -void verbose(const char * fmt, ...) { - if(VERBOSE) { - char *msg = NULL; - va_list ap; - va_start(ap, fmt); - - if(vasprintf(&msg, fmt, ap) >= 0) { - - if(FORKED) { - syslog(LOG_INFO, "%s", msg); - } - else { - fprintf(stderr, "%s", msg); - } - free(msg); - va_end(ap); - } - else { - fprintf(stderr, "Fatal: could not store log message!\n"); - exit(1); - } - } -} diff --git a/log.h b/log.h deleted file mode 100644 index 134f0a9..0000000 --- a/log.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#ifndef _HAVE_LOG_H -#define _HAVE_LOG_H - -#define _WITH_DPRINTF -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include - -extern int VERBOSE; -extern int FORKED; - -void verbose(const char * fmt, ...); - -#endif diff --git a/meson.build b/meson.build deleted file mode 100644 index 660f3fc..0000000 --- a/meson.build +++ /dev/null @@ -1,89 +0,0 @@ -# -*-python-*- - -project( - 'udpxd', - 'c', - license: 'GPL', - version: '0.0.4', - meson_version: '>=1.3', - default_options: [ - 'warning_level=2', - 'werror=true', - ], -) - -add_project_arguments( - [ - '-Wno-unused-parameter', - '-Wno-unused-result', - '-Wno-missing-braces', - '-Wno-format-zero-length', - '-Wvla', - '-Wno-sign-compare', - '-Wno-narrowing', - '-Wno-stringop-truncation' - ], - language: 'c', -) - - -c = meson.get_compiler('c') -conf = configuration_data() - - -# check for funcs. -foreach func : ['getopt', 'malloc', 'fprintf', 'strncpy', 'strlen', 'strtok', 'strchr', 'signal', - 'select', 'free', 'perror', 'getsockname', 'setegid', 'seteuid', 'syslog', - 'va_start', 'va_end', 'inet_ntop', 'getifaddrs', 'getnameinfo', 'ntohs', 'memcpy', 'memset', 'sprintf' ] - conf.set('HAVE_'+func.to_upper(), c.has_function(func, prefix : '#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n')) -endforeach - - -# check commandline options -prefix = get_option('prefix') - -if get_option('buildtype') == 'debug' - conf.set('DEBUG', '1') -endif - - - -# setup conf map -version = '@0@'.format(meson.project_version()) -conf.set('prefix', prefix) -conf.set('VERSION', version) - - -# write out the config header -m = configure_file( - input : 'platform.h.in', - output : 'platform.h', - configuration : conf, -) - - -# code -udpxd_sources = files( - 'client.c', - 'host.c', - 'log.c', - 'net.c', - 'udpxd.c' -) - - - -executable( - 'udpxd', - [udpxd_sources], - install: true -) - -# build manual page -pod2man = find_program('pod2man', native: true) -if pod2man.found() - res = run_command(pod2man.full_path(), 'udpxd.pod', 'udpxd.1', check:true) - if res.returncode() == 0 - install_man('udpxd.1') - endif -endif diff --git a/meson_options.txt b/meson_options.txt deleted file mode 100644 index e62869c..0000000 --- a/meson_options.txt +++ /dev/null @@ -1 +0,0 @@ -# custom build options diff --git a/net.c b/net.c deleted file mode 100644 index bd39283..0000000 --- a/net.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#include "net.h" -#include "client.h" -#include "host.h" -#include "log.h" - - - -/* called each time when the loop restarts to feed select() correctly */ -int fill_set(fd_set *fds) { - int max = 0; - - client_t *current = NULL; - client_iter(clients, current) { - if (current->socket < (int)FD_SETSIZE) { - if (current->socket > max) - max = current->socket; - FD_SET(current->socket, fds); - } - else { - fprintf(stderr, "skipped client, socket too large!\n"); - } - } - - return max; -} - -/* return file handle ready to read */ -int get_sender(fd_set *fds) { - int i = 0; - - while(!FD_ISSET(i, fds)) - i++; - - return i; -} - - -/* bind to a socket, either for listen() or for outgoing src ip binding */ -int bindsocket( host_t *sock_h) { - int fd; - int err = 0; - - if(sock_h->is_v6) { - fd = socket( PF_INET6, SOCK_DGRAM, IPPROTO_UDP ); - } - else { - fd = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); - } - - if( ! ( fd >= 0 && -1 != bind( fd, (struct sockaddr*)sock_h->sock, sock_h->size ) ) ) { - err = 1; - } - - if(err) { - fprintf( stderr, "Cannot bind address ([%s]:%d)\n", sock_h->ip, sock_h->port ); - perror(NULL); - return -1; - } - - return fd; -} - -/* - returns: - -1: error in any case - 0: parent not forked (fork disabled) - 1: parent after successful fork - 2: child after successful fork - */ -int daemonize(char *pidfile) { - if(FORKED) { - // fork - pid_t pid, sid; - FILE *fd; - - pid = fork(); - - if (pid < 0) { - perror("fork error"); - return 1; - } - - if (pid > 0) { - /* leave parent */ - if((fd = fopen(pidfile, "w")) == NULL) { - perror("failed to write pidfile"); - return -1; - } - else { - fprintf(fd, "%d\n", pid); - fclose(fd); - } - return 1; - } - - /* child */ - - sid = setsid(); - if (sid < 0) { - perror("set sid error"); - return 1; - } - - umask(0); - - openlog("udpxd", LOG_NOWAIT|LOG_PID, LOG_USER); - - return 2; - } - - return 0; -} - -int drop_privileges(char *user, char *chrootdir) { - struct passwd *pw = getpwnam(user); - uid_t me = getuid(); - - if(!FORKED) - return 0; - - if ((chdir("/")) < 0) { - perror("failed to chdir to /"); - return 1; - } - - if(me == 0) { - /* drop privileges */ - if(chroot(chrootdir) != 0) { - perror("failed to chroot"); - return 1; - } - - if(pw == NULL) { - perror("user not found"); - return 1; - } - - if(setegid(pw->pw_gid) != 0) { - perror("could not set egid"); - return 1; - } - - if(setgid(pw->pw_gid) != 0) { - perror("could not set gid"); - return 1; - } - - if(setuid(pw->pw_uid) != 0) { - perror("could not set uid"); - return 1; - } - - if(seteuid(pw->pw_uid) != 0) { - perror("could not set euid"); - return 1; - } - - if (setuid(0) != -1) { - fprintf(stderr, "error, managed to regain root privileges after dropping them!\n"); - return 1; - } - } - - return 0; -} - -int start_listener (char *inip, char *inpt, char *srcip, char *srcpt, char *dstip, - char *dstpt, char *pidfile, char *chrootdir, char *user) { - host_t *listen_h, *dst_h, *bind_h; - - int dm = daemonize(pidfile); - switch(dm) { - case -1: - return 1; /* parent, fork error */ - break; - case 0: - break; /* parent, not forking */ - case 1: - return 0; /* parent, fork ok, leave */ - break; - case 2: - break; /* child, fork ok, continue */ - } - - listen_h = get_host(inip, atoi(inpt), NULL, NULL); - dst_h = get_host(dstip, atoi(dstpt), NULL, NULL); - bind_h = NULL; - - if(srcip != NULL) { - bind_h = get_host(srcip, atoi(srcpt), NULL, NULL); - } - else { - if(dst_h->is_v6) - bind_h = get_host("::0", 0, NULL, NULL); - else - bind_h = get_host("0.0.0.0", 0, NULL, NULL); - } - - int listen = bindsocket(listen_h); - - if(listen == -1) - return 1; - - if(VERBOSE) { - verbose("Listening on %s:%s, forwarding to %s:%s", - listen_h->ip, inpt, dst_h->ip, dstpt); - if(srcip != NULL) - verbose(", binding to %s\n", bind_h->ip); - else - verbose("\n"); - } - - if(drop_privileges(user, chrootdir) != 0) { - host_clean(bind_h); - host_clean(listen_h); - host_clean(dst_h); - return 1; - } - - if (dm) { - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - } - - main_loop(listen, listen_h, bind_h, dst_h); - - host_clean(bind_h); - host_clean(listen_h); - host_clean(dst_h); - - closelog(); - - return 0; -} - -/* handle new or known incoming requests */ -void handle_inside(int inside, host_t *listen_h, host_t *bind_h, host_t *dst_h) { - int len; - unsigned char buffer[MAX_BUFFER_SIZE]; - void *src; - client_t *client; - host_t *src_h; - int output; - size_t size = listen_h->size; - - src = malloc(size); - - len = recvfrom( inside, buffer, sizeof( buffer ), 0, - (struct sockaddr*)src, (socklen_t *)&size ); - - if(len > 0) { - if(listen_h->is_v6) - src_h = get_host(NULL, 0, NULL, (struct sockaddr_in6 *)src); - else - src_h = get_host(NULL, 0, (struct sockaddr_in *)src, NULL); - /* do we know it ? */ - client = client_find_src(src_h); - if(client != NULL) { - /* yes, we know it, send req out via existing bind socket */ - verbose("Client %s:%d is known, forwarding %d bytes to %s:%d ", - src_h->ip, src_h->port, len, dst_h->ip, dst_h->port); - verb_prbind(bind_h); - - if(sendto(client->socket, buffer, len, 0, (struct sockaddr*)dst_h->sock, dst_h->size) < 0) { - fprintf(stderr, "unable to forward to %s:%d\n", dst_h->ip, dst_h->port); - perror(NULL); - } - else { - client_seen(client); - } - host_clean(src_h); - } - else { - /* unknown client, open new out socket */ - verbose("Client %s:%d is unknown, forwarding %d bytes to %s:%d ", - src_h->ip, src_h->port, len, dst_h->ip, dst_h->port); - verb_prbind(bind_h); - - if (bind_h->port) - client_clean(1); - output = bindsocket(bind_h); - if (output >= 0) { - /* send req out */ - if(sendto(output, buffer, len, 0, (struct sockaddr*)dst_h->sock, dst_h->size) < 0) { - fprintf(stderr, "unable to forward to %s:%d\n", dst_h->ip, dst_h->port); - perror(NULL); - } - else { - size = listen_h->size; - host_t *ret_h; - if(listen_h->is_v6) { - struct sockaddr_in6 *ret = malloc(size); - getsockname(output, (struct sockaddr*)ret, (socklen_t *)&size); - ret_h = get_host(NULL, 0, NULL, ret); - free(ret); - client = client_new(output, src_h, ret_h); - } - else { - struct sockaddr_in *ret = malloc(size); - getsockname(output, (struct sockaddr*)ret, (socklen_t *)&size); - ret_h = get_host(NULL, 0, ret, NULL); - free(ret); - client = client_new(output, src_h, ret_h); - } - - client_add(client); - } - } - else { - host_clean(src_h); - } - } - } - free(src); -} - -/* handle answer from the outside */ -void handle_outside(int inside, int outside, host_t *outside_h) { - int len; - unsigned char buffer[MAX_BUFFER_SIZE]; - void *src; - client_t *client; - - size_t size = outside_h->size; - src = malloc(size); - - len = recvfrom( outside, buffer, sizeof( buffer ), 0, (struct sockaddr*)src, (socklen_t *)&size ); - free(src); - - if(len > 0) { - /* do we know it? */ - client = client_find_fd(outside); - if(client != NULL) { - /* yes, we know it */ - /* FIXME: check src vs. client->src ? */ - if(sendto(inside, buffer, len, 0, - (struct sockaddr*)client->src->sock, client->src->size) < 0) { - perror("unable to send back to client"); /* FIXME: add src+port */ - client_close(client); - } - } - else { - fprintf(stderr, "weird, no matching client found!\n"); - } - } - else { - fprintf(stderr, "weird, recvfrom returned 0 bytes!\n"); - } -} - -/* stores system specific information, used by longjmp(), see below */ -jmp_buf JumpBuffer; - -/* runs forever, handles incoming requests on the inside and answers on the outside */ -int main_loop(int listensocket, host_t *listen_h, host_t *bind_h, host_t *dst_h) { - int max, sender; - fd_set fds; - - /* we want to properly tear down running sessions when interrupted, - int_handler() will be called on INT or TERM signals */ - signal(SIGINT, int_handler); - signal(SIGTERM, int_handler); - - for(;;) { - /* - Normally returns 0, that is, if it's the first instruction after - entering the loop. However, it will return 1, when called from - longjmp(), which will be called by int_handler() if a SIGINT- or - TERM arrives. In that case we leave the loop, tear down - everything and exit. - */ - if (setjmp(JumpBuffer) == 1) { - break; - } - - FD_ZERO(&fds); - max = fill_set(&fds); - - FD_SET(listensocket, &fds); - if (listensocket > max) - max = listensocket; - - select(max + 1, &fds, NULL, NULL, NULL); - - if (FD_ISSET(listensocket, &fds)) { - /* incoming client on the inside, get src, bind output fd, add - to list if known, otherwise just handle it */ - handle_inside(listensocket, listen_h, bind_h, dst_h); - } - else { - /* remote answer came in on an output fd, proxy back to the inside */ - sender = get_sender(&fds); - handle_outside(listensocket, sender, dst_h); - } - - /* close old outputs, if any */ - client_clean(0); - } - - /* we came here via signal handler, clean up */ - close(listensocket); - client_clean(1); - - return 0; -} - -/* - Handle SIGINT- and TERM, call longjmp(), which jumps right into the - main loop, where it causes the loop to be left. - */ -void int_handler(int sig) { - signal(sig, SIG_IGN); - longjmp(JumpBuffer, 1); -} - -void verb_prbind (host_t *bind_h) { - if(VERBOSE) { - if(strcmp(bind_h->ip, "0.0.0.0") != 0 || strcmp(bind_h->ip, "[::0]") != 0) { - verbose("from %s:%d\n", bind_h->ip, bind_h->port); - } - else { - verbose("\n"); - } - } -} diff --git a/net.h b/net.h deleted file mode 100644 index 978e4ad..0000000 --- a/net.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#ifndef _HAVE_NET_H -#define _HAVE_NET_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "client.h" - -#define MAX_BUFFER_SIZE 65535 - -extern client_t *clients; -extern int VERBOSE; -extern int FORKED; - -void handle_inside(int inside, host_t *listen_h, host_t *bind_h, host_t *dst_h); -void handle_outside(int inside, int outside, host_t *outside_h); - -int main_loop(int listensocket, host_t *listen_h, host_t *bind_h, - host_t *dst_h); -int start_listener(char *inip, char *inpt, char *srcip, char *srcpt, - char *dstip, char *dstpt, char *pidfile, char *chrootdir, - char *user); -int daemonize(char *pidfile); -int drop_privileges(char *user, char *chrootdir); - -int fill_set(fd_set *fds); -int get_sender(fd_set *fds); -int bindsocket(host_t *sock_h); -void int_handler(int sig); -void verb_prbind(host_t *bind_h); - -#define _IS_LINK_LOCAL(a) \ - do { \ - IN6_IS_ADDR_LINKLOCAL(a); \ - } while (0) - -#endif diff --git a/platform.h.in b/platform.h.in deleted file mode 100644 index a2b742f..0000000 --- a/platform.h.in +++ /dev/null @@ -1,101 +0,0 @@ -/* platform.h.in. Generated from configure.ac by autoheader. */ - -#define PACKAGE "udpxd" -#define UDPXD_VERSION "@VERSION@" - -#mesondefine HAVE_GETOPT -#ifndef HAVE_GETOPT -error "HAVE_GETOPT not defined" -#endif -#mesondefine HAVE_MALLOC -#ifndef HAVE_MALLOC - error "HAVE_MALLOC not defined" -#endif -#mesondefine HAVE_FPRINTF -#ifndef HAVE_FPRINTF - error "HAVE_FPRINTF not defined" -#endif -#mesondefine HAVE_STRNCPY -#ifndef HAVE_STRNCPY - error "HAVE_STRNCPY not defined" -#endif -#mesondefine HAVE_STRLEN -#ifndef HAVE_STRLEN - error "HAVE_STRLEN not defined" -#endif -#mesondefine HAVE_STRTOK -#ifndef HAVE_STRTOK - error "HAVE_STRTOK not defined" -#endif -#mesondefine HAVE_STRCHR -#ifndef HAVE_STRCHR - error "HAVE_STRCHR not defined" -#endif -#mesondefine HAVE_SIGNAL -#ifndef HAVE_SIGNAL - error "HAVE_SIGNAL not defined" -#endif -#mesondefine HAVE_SELECT -#ifndef HAVE_SELECT - error "HAVE_SELECT not defined" -#endif -#mesondefine HAVE_FREE -#ifndef HAVE_FREE - error "HAVE_FREE not defined" -#endif -#mesondefine HAVE_PERROR -#ifndef HAVE_PERROR - error "HAVE_PERROR not defined" -#endif -#mesondefine HAVE_GETSOCKNAME -#ifndef HAVE_GETSOCKNAME - error "HAVE_GETSOCKNAME not defined" -#endif -#mesondefine HAVE_SETEGID -#ifndef HAVE_SETEGID - error "HAVE_SETEGID not defined" -#endif -#mesondefine HAVE_SETEUID -#ifndef HAVE_SETEUID - error "HAVE_SETEUID not defined" -#endif -#mesondefine HAVE_SYSLOG -#ifndef HAVE_SYSLOG - error "HAVE_SYSLOG not defined" -#endif -#mesondefine HAVE_VA_START -#ifndef HAVE_VA_START - error "HAVE_VA_START not defined" -#endif -#mesondefine HAVE_VA_END -#ifndef HAVE_VA_END - error "HAVE_VA_END not defined" -#endif -#mesondefine HAVE_INET_NTOP -#ifndef HAVE_INET_NTOP - error "HAVE_INET_NTOP not defined" -#endif -#mesondefine HAVE_GETIFADDRS -#ifndef HAVE_GETIFADDRS - error "HAVE_GETIFADDRS not defined" -#endif -#mesondefine HAVE_GETNAMEINFO -#ifndef HAVE_GETNAMEINFO - error "HAVE_GETNAMEINFO not defined" -#endif -#mesondefine HAVE_NTOHS -#ifndef HAVE_NTOHS - error "HAVE_NTOHS not defined" -#endif -#mesondefine HAVE_MEMCPY -#ifndef HAVE_MEMCPY - error "HAVE_MEMCPY not defined" -#endif -#mesondefine HAVE_MEMSET -#ifndef HAVE_MEMSET - error "HAVE_MEMSET not defined" -#endif -#mesondefine HAVE_SPRINTF -#ifndef HAVE_SPRINTF - error "HAVE_SPRINTF not defined" -#endif diff --git a/udpxd.1 b/udpxd.1 deleted file mode 100644 index 563fad8..0000000 --- a/udpxd.1 +++ /dev/null @@ -1,301 +0,0 @@ -.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) -.\" -.\" Standard preamble: -.\" ======================================================================== -.de Sp \" Vertical space (when we can't use .PP) -.if t .sp .5v -.if n .sp -.. -.de Vb \" Begin verbatim text -.ft CW -.nf -.ne \\$1 -.. -.de Ve \" End verbatim text -.ft R -.fi -.. -.\" Set up some character translations and predefined strings. \*(-- will -.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left -.\" double quote, and \*(R" will give a right double quote. \*(C+ will -.\" give a nicer C++. Capital omega is used to do unbreakable dashes and -.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, -.\" nothing in troff, for use with C<>. -.tr \(*W- -.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' -.ie n \{\ -. ds -- \(*W- -. ds PI pi -. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch -. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch -. ds L" "" -. ds R" "" -. ds C` "" -. ds C' "" -'br\} -.el\{\ -. ds -- \|\(em\| -. ds PI \(*p -. ds L" `` -. ds R" '' -. ds C` -. ds C' -'br\} -.\" -.\" Escape single quotes in literal strings from groff's Unicode transform. -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.\" -.\" If the F register is >0, we'll generate index entries on stderr for -.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index -.\" entries marked with X<> in POD. Of course, you'll have to process the -.\" output yourself in some meaningful fashion. -.\" -.\" Avoid warning from groff about undefined register 'F'. -.de IX -.. -.nr rF 0 -.if \n(.g .if rF .nr rF 1 -.if (\n(rF:(\n(.g==0)) \{\ -. if \nF \{\ -. de IX -. tm Index:\\$1\t\\n%\t"\\$2" -.. -. if !\nF==2 \{\ -. nr % 0 -. nr F 2 -. \} -. \} -.\} -.rr rF -.\" -.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). -.\" Fear. Run. Save yourself. No user-serviceable parts. -. \" fudge factors for nroff and troff -.if n \{\ -. ds #H 0 -. ds #V .8m -. ds #F .3m -. ds #[ \f1 -. ds #] \fP -.\} -.if t \{\ -. ds #H ((1u-(\\\\n(.fu%2u))*.13m) -. ds #V .6m -. ds #F 0 -. ds #[ \& -. ds #] \& -.\} -. \" simple accents for nroff and troff -.if n \{\ -. ds ' \& -. ds ` \& -. ds ^ \& -. ds , \& -. ds ~ ~ -. ds / -.\} -.if t \{\ -. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" -. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' -. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' -. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' -. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' -. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' -.\} -. \" troff and (daisy-wheel) nroff accents -.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' -.ds 8 \h'\*(#H'\(*b\h'-\*(#H' -.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] -.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' -.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' -.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] -.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] -.ds ae a\h'-(\w'a'u*4/10)'e -.ds Ae A\h'-(\w'A'u*4/10)'E -. \" corrections for vroff -.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' -.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' -. \" for low resolution devices (crt and lpr) -.if \n(.H>23 .if \n(.V>19 \ -\{\ -. ds : e -. ds 8 ss -. ds o a -. ds d- d\h'-1'\(ga -. ds D- D\h'-1'\(hy -. ds th \o'bp' -. ds Th \o'LP' -. ds ae ae -. ds Ae AE -.\} -.rm #[ #] #H #V #F C -.\" ======================================================================== -.\" -.IX Title "UDPXD 1" -.TH UDPXD 1 "2025-12-01" "perl v5.34.0" "User Contributed Perl Documentation" -.\" For nroff, turn off justification. Always turn off hyphenation; it makes -.\" way too many mistakes in technical documents. -.if n .ad l -.nh -.SH "NAME" -udpxd \- A general purpose UDP relay/port forwarder/proxy -.SH "SYNOPSIS" -.IX Header "SYNOPSIS" -.Vb 1 -\& Usage: udpxd [\-lbdfpvhV] -\& -\& Options: -\& \-\-listen \-l listen for incoming requests -\& \-\-bind \-b bind ip used for outgoing requests -\& specify port for promiscuous mode -\& \-\-to \-t destination to forward requests to -\& \-\-daemon \-d daemon mode, fork into background -\& \-\-pidfile \-p pidfile, default: /var/run/udpxd.pid -\& \-\-user \-u run as user (only in daemon mode) -\& \-\-chroot \-c chroot to (only in daemon mode) -\& \-\-help \-h \-? print help message -\& \-\-version \-V print program version -\& \-\-verbose \-v enable verbose logging -.Ve -.SH "DESCRIPTION" -.IX Header "DESCRIPTION" -udpxd can be used to forward or proxy \s-1UDP\s0 client traffic -to another port on another system. It also supports binding -to a specific ip address which will be used as the source -for outgoing packets. -.PP -It listens on the ip address and port specified with \fB\-l\fR -and waits for incoming udp packets. If one arrives, it sends -it to the destination specified with \fB\-t\fR. Responses will -be sent back accordingly. -.PP -If \fB\-b\fR has not been specified, udpxd uses the operating -systems default (e.g. routing) as the source where it sends -requests packets out. If \fB\-b\fR has been specified, then it -binds to the given ip address and uses this as the source -address. -.PP -In any case, udpxd behaves like a proxy. The receiving end -(\fB\-t\fR) only sees the source ip address of the outgoing -interface of the system running udpxd or the address specified -with \fB\-b\fR. -.PP -The options \fB\-l\fR and \fB\-t\fR are mandatory. -.PP -If the option \fB\-d\fR has been specified, udpxd forks into -the background and becomes a daemon. It writes it pidfile to -\&\f(CW\*(C`/var/run/udpxd.pid\*(C'\fR, which can be changed with the \fB\-p\fR -option. If started as root, it also drops privileges to the -user \f(CW\*(C`nobody\*(C'\fR or the user specified with \fB\-u\fR and chroots -to \f(CW\*(C`/var/empty\*(C'\fR or the directory specified with \fB\-c\fR. udpxd -will log to syslog facility user.info if \fB\-v\fR is specified and -if running in daemon mode. -.PP -\&\fBCaution: if not running in daemon mode, udpxd does not drop -its privileges and will continue to run as root (if started as -root).\fR -.PP -Udpxd supports ip version 4 and 6, it doesn't support hostnames, -\&\fB\-l\fR, \fB\-t\fR and \fB\-b\fR must be ip addresses. In order to specify an ipv6 -address and a port, use: -.PP -.Vb 1 -\& \-l [::1]:53 -.Ve -.PP -that is, surround the ipv6 address with brackets. -.PP -Port forwardings can be mixed: -.PP -.Vb 6 -\& listen | forward to -\& \-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\- -\& ipv4 | ipv4 -\& ipv6 | ipv4 -\& ipv4 | ipv6 -\& ipv6 | ipv6 -.Ve -.SH "EXAMPLES" -.IX Header "EXAMPLES" -Let's say you operate a multihomed unix system named 'foo' -with two interfaces: eth0 on the inside, eth1 on the outside: -.PP -.Vb 3 -\& foo: -\& eth0: 192.168.1.1 -\& eth1: 10.0.0.1 -.Ve -.PP -And let's say, you have a client in network 10.0.0.0/24 who whiches to reach -an ntp server in network 192.168.1.0/24; and you dont operate a -firewall, nat or routing on 'foo'. Run udpxd like this: -.PP -.Vb 1 -\& udpxd \-l 10.0.0.1:123 \-t 192.168.1.199:123 -.Ve -.PP -Now, if a client with the source ip address 10.0.0.110 sends -a ntp request to 10.0.0.1:123, udpxd will forward that -packet to 192.168.1.199:123 with the source ip address -192.168.1.1 (because this is where the route points to: eth0). -Responses from the ntp server will reach udpxd, which in turn -sends them back to the client, where they arrive with the source -address (and port) where udpxd is listening. -.PP -As you can see, udpxd can be used to implement hiding nat for -udp services in user space. -.PP -Another example would be, if 'foo' has multiple ip addresses -on eth0 (aliases) and you don't want to use the primary address -of the interface for outgoing packets. -.PP -.Vb 3 -\& foo, again: -\& eth0: 192.168.1.1,192.168.1.45 -\& eth0: 10.0.0.1 -.Ve -.PP -In order to use 192.168.1.45 as the source ip address, use the -\&\fB\-b\fR parameter: -.PP -.Vb 1 -\& udpxd \-l 10.0.0.1:123 \-t 192.168.1.199:123 \-b 192.168.1.45 -.Ve -.PP -In this case for the client everything looks as before, but the -ntp server on the other end will see ntp requests coming from -192.168.1.45 instead. -.PP -Here we listen on the ip v6 loopback address and forward traffic -to another ip v6 destination address: -.PP -.Vb 1 -\& udpxd \-l [::1]:53 \-t [2001:4860:4860::8888]:53 -.Ve -.PP -Or, we could listen on an ip v4 address and forward to an ip v6 -address: -.PP -.Vb 1 -\& udpxd \-l 192.168.1.1:53 \-t [2001:4860:4860::8888]:53 -.Ve -.SH "FILES" -.IX Header "FILES" -\&\fB/var/run/udpxd.pid\fR: created if running in daemon mode (\fB\-d\fR). -.SH "BUGS" -.IX Header "BUGS" -In order to report a bug, unexpected behavior, feature requests -or to submit a patch, please open an issue on github: -. -.SH "LICENSE" -.IX Header "LICENSE" -This software is licensed under the \s-1GNU GENERAL PUBLIC LICENSE\s0 version 3. -.PP -Copyright (c) 2015\-2017 by T. v. Dein. -.PP -This software uses \fButhash\fR (bundled), which is -Copyright (c) 2003\-2013 by Troy D. Hanson. -.SH "AUTHORS" -.IX Header "AUTHORS" -T.v.Dein \fBtom \s-1AT\s0 vondein \s-1DOT\s0 org\fR diff --git a/udpxd.c b/udpxd.c deleted file mode 100644 index 00ad44b..0000000 --- a/udpxd.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#include "udpxd.h" -#include "net.h" -#include "client.h" - -/* global client list */ -client_t *clients = NULL; -int VERBOSE = 0; -int FORKED = 0; - -/* parse ip:port */ -int parse_ip(char *src, char *ip, char *pt) { - char *ptr = NULL; - - if (strchr(optarg, '[')) { - /* v6 */ - ptr = strtok(&src[1], "]"); - - if(strlen(ptr) > INET6_ADDRSTRLEN) { - fprintf(stderr, "ip v6 address is too long!\n"); - return 1; - } - - strncpy(ip, ptr, strlen(ptr)+1); - ptr = strtok(NULL, "]"); - if(ptr) - ptr = &ptr[1]; /* remove : */ - } - else if(strchr(optarg, ':')) { - /* v4 */ - ptr = strtok(src, ":"); - - if(strlen(ptr) > INET_ADDRSTRLEN) { - fprintf(stderr, "ip v4 address is too long!\n"); - return 1; - } - - strncpy(ip, ptr, strlen(ptr)+1); - ptr = strtok(NULL, ":"); - } - else { - fprintf(stderr, "Invalid ip/port specification!\n"); - return 1; - } - - if(ptr != NULL) { - /* got a port */ - if(strlen(ptr) > 5) { - fprintf(stderr, "port is too long!\n"); - return 1; - } - else { - if(atoi(ptr) > 65535) { - fprintf(stderr, "maximum port number possible: 65535!\n"); - return 1; - } - strncpy(pt, ptr, strlen(ptr)+1); - } - } - else { - fprintf(stderr, "Port is missing!\n"); - return 1; - } - - return 0; -} - - - -void usage() { - fprintf(stderr, - "Usage: udpxd [-lbdfpvhV]\n\n" - "Options:\n" - "--listen -l listen for incoming requests\n" - "--bind -b bind ip used for outgoing requests\n" - " specify port for promiscuous mode\n" - "--to -t destination to forward requests to\n" - "--daemon -d daemon mode, fork into background\n" - "--pidfile -p pidfile, default: /var/run/udpxd.pid\n" - "--user -u run as user (only in daemon mode)\n" - "--chroot -c chroot to (only in daemon mode)\n" - "--help -h -? print help message\n" - "--version -V print program version\n" - "--verbose -v enable verbose logging\n\n" - "Options -l and -t are mandatory.\n\n" - "This is udpxd version %s.\n", UDPXD_VERSION - ); -} - - - -int main ( int argc, char* argv[] ) { - int opt, err; - char *inip, *inpt, *srcip, *srcpt, *dstip, *dstpt; - char pidfile[MAX_BUFFER_SIZE]; - char user[128]; - char chroot[MAX_BUFFER_SIZE]; - - err = 0; - - static struct option longopts[] = { - { "listen", required_argument, NULL, 'l' }, - { "bind", required_argument, NULL, 'b' }, - { "to", required_argument, NULL, 't' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - { "verbose", no_argument, NULL, 'v' }, - { "daemon", no_argument, NULL, 'd' }, - { "pidfile", required_argument, NULL, 'p' }, - { "user", required_argument, NULL, 'u' }, - { "chroot", required_argument, NULL, 'c' }, - }; - - if( argc < 2 ) { - usage(); - return 1; - } - - srcip = srcpt = dstip = inip = dstpt = inpt = NULL; - - /* set defaults */ - strncpy(pidfile, "/var/run/udpxd.pid", 19); - strncpy(user, "nobody", 7); - strncpy(chroot, "/var/empty", 11); - - while ((opt = getopt_long(argc, argv, "l:b:t:u:c:vdVh?", longopts, NULL)) != -1) { - switch (opt) { - case 'V': - fprintf(stderr, "This is %s version %s\n", argv[0], UDPXD_VERSION); - return 1; - break; - case 'd': - FORKED = 1; - break; - case 'h': - case '?': - usage(); - return 1; - break; - case 'v': - VERBOSE = 1; - break; - case 'l': - inip = malloc(INET6_ADDRSTRLEN+1); - inpt = malloc(6); - if (parse_ip(optarg, inip, inpt) != 0) { - fprintf(stderr, "Parameter -l has the format !\n"); - err = 1; - } - break; - case 't': - dstip = malloc(INET6_ADDRSTRLEN+1); - dstpt = malloc(6); - if (parse_ip(optarg, dstip, dstpt) != 0) { - fprintf(stderr, "Parameter -t has the format !\n"); - err = 1; - } - break; - case 'b': - srcip = malloc(INET6_ADDRSTRLEN+1+5); // +5 is for port - srcpt = malloc(6); - if(strlen(optarg) > INET6_ADDRSTRLEN+5) { - fprintf(stderr, "Bind ip address is too long!\n"); - err = 1; - } - else { - if (strchr(optarg, ':') == NULL || parse_ip(optarg, srcip, srcpt) != 0) { - strncpy(srcip, optarg, INET6_ADDRSTRLEN+5); - srcip[INET6_ADDRSTRLEN+5-1] = '\0'; - strncpy(srcpt, "0", 2); - } - } - break; - case 'p': - strncpy(pidfile, optarg, MAX_BUFFER_SIZE); - pidfile[MAX_BUFFER_SIZE-1] = '\0'; - break; - case 'u': - strncpy(user, optarg, 128); - user[128-1] = '\0'; - break; - case 'c': - strncpy(chroot, optarg, MAX_BUFFER_SIZE); - chroot[MAX_BUFFER_SIZE-1] = '\0'; - break; - default: - usage(); - return 1; - break; - } - } - - if(inip == NULL) { - fprintf(stderr, "-l parameter is required!\n"); - usage(); - err = 1; - } - - if(dstip == NULL) { - fprintf(stderr, "-t parameter is required!\n"); - usage(); - err = 1; - } - - if(srcip != NULL && dstip != NULL) { - if(is_v6(srcip) != is_v6(dstip)) { - fprintf(stderr, "Bind ip and destination ip must be both v4 or v6 and can't be mixed!\n"); - err = 1; - } - } - - if(! err) { - err = start_listener (inip, inpt, srcip, srcpt, dstip, dstpt, pidfile, chroot, user); - } - - if(srcip != NULL) - free(srcip); - if(srcpt != NULL) - free(srcpt); - if(dstip != NULL) - free(dstip); - if(inip != NULL) - free(inip); - if(inpt != NULL) - free(inpt); - if(dstpt != NULL) - free(dstpt); - - return err; -} diff --git a/udpxd.h b/udpxd.h deleted file mode 100644 index 6a14360..0000000 --- a/udpxd.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of udpxd. - - Copyright (C) 2015-2016 T.v.Dein. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - You can contact me by mail: . -*/ - -#ifndef _HAVE_UDPXD_H -#define _HAVE_UDPXD_H - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "platform.h" - -void usage(); -int parse_ip(char *src, char *ip, char *pt); - -#endif diff --git a/udpxd.pod b/udpxd.pod deleted file mode 100644 index 25c7f82..0000000 --- a/udpxd.pod +++ /dev/null @@ -1,153 +0,0 @@ -=head1 NAME - -udpxd - A general purpose UDP relay/port forwarder/proxy - -=head1 SYNOPSIS - - Usage: udpxd [-lbdfpvhV] - - Options: - --listen -l listen for incoming requests - --bind -b bind ip used for outgoing requests - specify port for promiscuous mode - --to -t destination to forward requests to - --daemon -d daemon mode, fork into background - --pidfile -p pidfile, default: /var/run/udpxd.pid - --user -u run as user (only in daemon mode) - --chroot -c chroot to (only in daemon mode) - --help -h -? print help message - --version -V print program version - --verbose -v enable verbose logging - -=head1 DESCRIPTION - -udpxd can be used to forward or proxy UDP client traffic -to another port on another system. It also supports binding -to a specific ip address which will be used as the source -for outgoing packets. - -It listens on the ip address and port specified with B<-l> -and waits for incoming udp packets. If one arrives, it sends -it to the destination specified with B<-t>. Responses will -be sent back accordingly. - -If B<-b> has not been specified, udpxd uses the operating -systems default (e.g. routing) as the source where it sends -requests packets out. If B<-b> has been specified, then it -binds to the given ip address and uses this as the source -address. - -In any case, udpxd behaves like a proxy. The receiving end -(B<-t>) only sees the source ip address of the outgoing -interface of the system running udpxd or the address specified -with B<-b>. - -The options B<-l> and B<-t> are mandatory. - -If the option B<-d> has been specified, udpxd forks into -the background and becomes a daemon. It writes it pidfile to -C, which can be changed with the B<-p> -option. If started as root, it also drops privileges to the -user C or the user specified with B<-u> and chroots -to C or the directory specified with B<-c>. udpxd -will log to syslog facility user.info if B<-v> is specified and -if running in daemon mode. - -B - -Udpxd supports ip version 4 and 6, it doesn't support hostnames, -B<-l>, B<-t> and B<-b> must be ip addresses. In order to specify an ipv6 -address and a port, use: - - -l [::1]:53 - -that is, surround the ipv6 address with brackets. - -Port forwardings can be mixed: - - listen | forward to - -------+----------- - ipv4 | ipv4 - ipv6 | ipv4 - ipv4 | ipv6 - ipv6 | ipv6 - -=head1 EXAMPLES - -Let's say you operate a multihomed unix system named 'foo' -with two interfaces: eth0 on the inside, eth1 on the outside: - - foo: - eth0: 192.168.1.1 - eth1: 10.0.0.1 - -And let's say, you have a client in network 10.0.0.0/24 who whiches to reach -an ntp server in network 192.168.1.0/24; and you dont operate a -firewall, nat or routing on 'foo'. Run udpxd like this: - - udpxd -l 10.0.0.1:123 -t 192.168.1.199:123 - -Now, if a client with the source ip address 10.0.0.110 sends -a ntp request to 10.0.0.1:123, udpxd will forward that -packet to 192.168.1.199:123 with the source ip address -192.168.1.1 (because this is where the route points to: eth0). -Responses from the ntp server will reach udpxd, which in turn -sends them back to the client, where they arrive with the source -address (and port) where udpxd is listening. - -As you can see, udpxd can be used to implement hiding nat for -udp services in user space. - -Another example would be, if 'foo' has multiple ip addresses -on eth0 (aliases) and you don't want to use the primary address -of the interface for outgoing packets. - - foo, again: - eth0: 192.168.1.1,192.168.1.45 - eth0: 10.0.0.1 - -In order to use 192.168.1.45 as the source ip address, use the -B<-b> parameter: - - udpxd -l 10.0.0.1:123 -t 192.168.1.199:123 -b 192.168.1.45 - -In this case for the client everything looks as before, but the -ntp server on the other end will see ntp requests coming from -192.168.1.45 instead. - -Here we listen on the ip v6 loopback address and forward traffic -to another ip v6 destination address: - - udpxd -l [::1]:53 -t [2001:4860:4860::8888]:53 - -Or, we could listen on an ip v4 address and forward to an ip v6 -address: - - udpxd -l 192.168.1.1:53 -t [2001:4860:4860::8888]:53 - -=head1 FILES - -B: created if running in daemon mode (B<-d>). - -=head1 BUGS - -In order to report a bug, unexpected behavior, feature requests -or to submit a patch, please open an issue on github: -L. - -=head1 LICENSE - -This software is licensed under the GNU GENERAL PUBLIC LICENSE version 3. - -Copyright (c) 2015-2017 by T. v. Dein. - -This software uses B (bundled), which is -Copyright (c) 2003-2013 by Troy D. Hanson. - -=head1 AUTHORS - -T.v.Dein B - -=cut diff --git a/uthash.h b/uthash.h deleted file mode 100755 index f78a73b..0000000 --- a/uthash.h +++ /dev/null @@ -1,1096 +0,0 @@ -/* -Copyright (c) 2003-2017, Troy D. Hanson http://troydhanson.github.com/uthash/ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef UTHASH_H -#define UTHASH_H - -#define UTHASH_VERSION 2.0.2 - -#include /* memcmp,strlen */ -#include /* ptrdiff_t */ -#include /* exit() */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ source) this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) -#if defined(_MSC_VER) /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define DECLTYPE(x) (decltype(x)) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#endif -#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) -#define NO_DECLTYPE -#else /* GNU, Sun and other compilers */ -#define DECLTYPE(x) (__typeof(x)) -#endif -#endif - -#ifdef NO_DECLTYPE -#define DECLTYPE(x) -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - char **_da_dst = (char**)(&(dst)); \ - *_da_dst = (char*)(src); \ -} while (0) -#else -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - (dst) = DECLTYPE(dst)(src); \ -} while (0) -#endif - -/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ -#if defined(_WIN32) -#if defined(_MSC_VER) && _MSC_VER >= 1600 -#include -#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) -#include -#else -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#endif -#elif defined(__GNUC__) && !defined(__VXWORKS__) -#include -#else -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#endif - -#ifndef uthash_fatal -#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ -#endif -#ifndef uthash_malloc -#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -#endif -#ifndef uthash_free -#define uthash_free(ptr,sz) free(ptr) /* free fcn */ -#endif -#ifndef uthash_strlen -#define uthash_strlen(s) strlen(s) -#endif -#ifndef uthash_memcmp -#define uthash_memcmp(a,b,n) memcmp(a,b,n) -#endif - -#ifndef uthash_noexpand_fyi -#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -#endif -#ifndef uthash_expand_fyi -#define uthash_expand_fyi(tbl) /* can be defined to log expands */ -#endif - -/* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ -#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ - -/* calculate the element whose hash handle address is hhp */ -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) -/* calculate the hash handle from element address elp */ -#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) - -#define HASH_VALUE(keyptr,keylen,hashv) \ -do { \ - HASH_FCN(keyptr, keylen, hashv); \ -} while (0) - -#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ -do { \ - (out) = NULL; \ - if (head) { \ - unsigned _hf_bkt; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ - } \ - } \ -} while (0) - -#define HASH_FIND(hh,head,keyptr,keylen,out) \ -do { \ - unsigned _hf_hashv; \ - HASH_VALUE(keyptr, keylen, _hf_hashv); \ - HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ -} while (0) - -#ifdef HASH_BLOOM -#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) -#define HASH_BLOOM_MAKE(tbl) \ -do { \ - (tbl)->bloom_nbits = HASH_BLOOM; \ - (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ - memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ -} while (0) - -#define HASH_BLOOM_FREE(tbl) \ -do { \ - uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0) - -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) -#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) - -#define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) - -#define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) - -#else -#define HASH_BLOOM_MAKE(tbl) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) -#define HASH_BLOOM_TEST(tbl,hashv) (1) -#define HASH_BLOOM_BYTELEN 0U -#endif - -#define HASH_MAKE_TABLE(hh,head) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ - sizeof(UT_hash_table)); \ - if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl->buckets, 0, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ -} while (0) - -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ -} while (0) - -#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ -} while (0) - -#define HASH_APPEND_LIST(hh, head, add) \ -do { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail->next = (add); \ - (head)->hh.tbl->tail = &((add)->hh); \ -} while (0) - -#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ -do { \ - do { \ - if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) \ - break; \ - } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ -} while (0) - -#ifdef NO_DECLTYPE -#undef HASH_AKBI_INNER_LOOP -#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ -do { \ - char *_hs_saved_head = (char*)(head); \ - do { \ - DECLTYPE_ASSIGN(head, _hs_iter); \ - if (cmpfcn(head, add) > 0) { \ - DECLTYPE_ASSIGN(head, _hs_saved_head); \ - break; \ - } \ - DECLTYPE_ASSIGN(head, _hs_saved_head); \ - } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ -} while (0) -#endif - -#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ -do { \ - unsigned _ha_bkt; \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (char*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - (head) = (add); \ - HASH_MAKE_TABLE(hh, head); \ - } else { \ - void *_hs_iter = (head); \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ - if (_hs_iter) { \ - (add)->hh.next = _hs_iter; \ - if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ - HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ - } else { \ - (head) = (add); \ - } \ - HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ - } else { \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - } \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ - HASH_FSCK(hh, head); \ -} while (0) - -#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ -do { \ - unsigned _hs_hashv; \ - HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) - -#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ - HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) - -#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ -do { \ - unsigned _ha_bkt; \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (char*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - (head) = (add); \ - HASH_MAKE_TABLE(hh, head); \ - } else { \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ - HASH_FSCK(hh, head); \ -} while (0) - -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -do { \ - unsigned _ha_hashv; \ - HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) - -#define HASH_TO_BKT(hashv,num_bkts,bkt) \ -do { \ - bkt = ((hashv) & ((num_bkts) - 1U)); \ -} while (0) - -/* delete "delptr" from the hash table. - * "the usual" patch-up process for the app-order doubly-linked-list. - * The use of _hd_hh_del below deserves special explanation. - * These used to be expressed using (delptr) but that led to a bug - * if someone used the same symbol for the head and deletee, like - * HASH_DELETE(hh,users,users); - * We want that to work, but by changing the head (users) below - * we were forfeiting our ability to further refer to the deletee (users) - * in the patch-up process. Solution: use scratch space to - * copy the deletee pointer, then the latter references are via that - * scratch pointer rather than through the repointed (users) symbol. - */ -#define HASH_DELETE(hh,head,delptr) \ -do { \ - struct UT_hash_handle *_hd_hh_del; \ - if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - head = NULL; \ - } else { \ - unsigned _hd_bkt; \ - _hd_hh_del = &((delptr)->hh); \ - if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ - (head)->hh.tbl->tail = \ - (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho); \ - } \ - if ((delptr)->hh.prev != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ - } else { \ - DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ - } \ - if (_hd_hh_del->next != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ - (head)->hh.tbl->hho))->prev = \ - _hd_hh_del->prev; \ - } \ - HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ - } \ - HASH_FSCK(hh,head); \ -} while (0) - - -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -#define HASH_FIND_STR(head,findstr,out) \ - HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out) -#define HASH_ADD_STR(head,strfield,add) \ - HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add) -#define HASH_REPLACE_STR(head,strfield,add,replaced) \ - HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced) -#define HASH_FIND_INT(head,findint,out) \ - HASH_FIND(hh,head,findint,sizeof(int),out) -#define HASH_ADD_INT(head,intfield,add) \ - HASH_ADD(hh,head,intfield,sizeof(int),add) -#define HASH_REPLACE_INT(head,intfield,add,replaced) \ - HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) -#define HASH_FIND_PTR(head,findptr,out) \ - HASH_FIND(hh,head,findptr,sizeof(void *),out) -#define HASH_ADD_PTR(head,ptrfield,add) \ - HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ - HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) -#define HASH_DEL(head,delptr) \ - HASH_DELETE(hh,head,delptr) - -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. - */ -#ifdef HASH_DEBUG -#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head) \ -do { \ - struct UT_hash_handle *_thh; \ - if (head) { \ - unsigned _bkt_i; \ - unsigned _count; \ - char *_prev; \ - _count = 0; \ - for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ - unsigned _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("invalid hh_prev %p, actual %p\n", \ - _thh->hh_prev, _prev ); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("invalid bucket count %u, actual %u\n", \ - (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid hh item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - /* traverse hh in app order; check next/prev integrity, count */ \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev !=(char*)(_thh->prev)) { \ - HASH_OOPS("invalid prev %p, actual %p\n", \ - _thh->prev, _prev ); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ - (head)->hh.tbl->hho) : NULL ); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid app item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - } \ -} while (0) -#else -#define HASH_FSCK(hh,head) -#endif - -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to - * the descriptor to which this macro is defined for tuning the hash function. - * The app can #include to get the prototype for write(2). */ -#ifdef HASH_EMIT_KEYS -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ -} while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -#endif - -/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION -#define HASH_FCN HASH_FUNCTION -#else -#define HASH_FCN HASH_JEN -#endif - -/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ -#define HASH_BER(key,keylen,hashv) \ -do { \ - unsigned _hb_keylen=(unsigned)keylen; \ - const unsigned char *_hb_key=(const unsigned char*)(key); \ - (hashv) = 0; \ - while (_hb_keylen-- != 0U) { \ - (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ - } \ -} while (0) - - -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -#define HASH_SAX(key,keylen,hashv) \ -do { \ - unsigned _sx_i; \ - const unsigned char *_hs_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ - } \ -} while (0) -/* FNV-1a variation */ -#define HASH_FNV(key,keylen,hashv) \ -do { \ - unsigned _fn_i; \ - const unsigned char *_hf_key=(const unsigned char*)(key); \ - hashv = 2166136261U; \ - for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ - hashv = hashv ^ _hf_key[_fn_i]; \ - hashv = hashv * 16777619U; \ - } \ -} while (0) - -#define HASH_OAT(key,keylen,hashv) \ -do { \ - unsigned _ho_i; \ - const unsigned char *_ho_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ - hashv += _ho_key[_ho_i]; \ - hashv += (hashv << 10); \ - hashv ^= (hashv >> 6); \ - } \ - hashv += (hashv << 3); \ - hashv ^= (hashv >> 11); \ - hashv += (hashv << 15); \ -} while (0) - -#define HASH_JEN_MIX(a,b,c) \ -do { \ - a -= b; a -= c; a ^= ( c >> 13 ); \ - b -= c; b -= a; b ^= ( a << 8 ); \ - c -= a; c -= b; c ^= ( b >> 13 ); \ - a -= b; a -= c; a ^= ( c >> 12 ); \ - b -= c; b -= a; b ^= ( a << 16 ); \ - c -= a; c -= b; c ^= ( b >> 5 ); \ - a -= b; a -= c; a ^= ( c >> 3 ); \ - b -= c; b -= a; b ^= ( a << 10 ); \ - c -= a; c -= b; c ^= ( b >> 15 ); \ -} while (0) - -#define HASH_JEN(key,keylen,hashv) \ -do { \ - unsigned _hj_i,_hj_j,_hj_k; \ - unsigned const char *_hj_key=(unsigned const char*)(key); \ - hashv = 0xfeedbeefu; \ - _hj_i = _hj_j = 0x9e3779b9u; \ - _hj_k = (unsigned)(keylen); \ - while (_hj_k >= 12U) { \ - _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ - + ( (unsigned)_hj_key[2] << 16 ) \ - + ( (unsigned)_hj_key[3] << 24 ) ); \ - _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ - + ( (unsigned)_hj_key[6] << 16 ) \ - + ( (unsigned)_hj_key[7] << 24 ) ); \ - hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ - + ( (unsigned)_hj_key[10] << 16 ) \ - + ( (unsigned)_hj_key[11] << 24 ) ); \ - \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - \ - _hj_key += 12; \ - _hj_k -= 12U; \ - } \ - hashv += (unsigned)(keylen); \ - switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ - case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ - case 1: _hj_i += _hj_key[0]; \ - } \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ -} while (0) - -/* The Paul Hsieh hash function */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif -#define HASH_SFH(key,keylen,hashv) \ -do { \ - unsigned const char *_sfh_key=(unsigned const char*)(key); \ - uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ - \ - unsigned _sfh_rem = _sfh_len & 3U; \ - _sfh_len >>= 2; \ - hashv = 0xcafebabeu; \ - \ - /* Main loop */ \ - for (;_sfh_len > 0U; _sfh_len--) { \ - hashv += get16bits (_sfh_key); \ - _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ - hashv = (hashv << 16) ^ _sfh_tmp; \ - _sfh_key += 2U*sizeof (uint16_t); \ - hashv += hashv >> 11; \ - } \ - \ - /* Handle end cases */ \ - switch (_sfh_rem) { \ - case 3: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 16; \ - hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ - hashv += hashv >> 11; \ - break; \ - case 2: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 11; \ - hashv += hashv >> 17; \ - break; \ - case 1: hashv += *_sfh_key; \ - hashv ^= hashv << 10; \ - hashv += hashv >> 1; \ - } \ - \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ -} while (0) - -#ifdef HASH_USING_NO_STRICT_ALIASING -/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. - * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. - * MurmurHash uses the faster approach only on CPU's where we know it's safe. - * - * Note the preprocessor built-in defines can be emitted using: - * - * gcc -m64 -dM -E - < /dev/null (on gcc) - * cc -## a.c (where a.c is a simple test file) (Sun Studio) - */ -#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) -#define MUR_GETBLOCK(p,i) p[i] -#else /* non intel */ -#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) -#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) -#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) -#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) -#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) -#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) -#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) -#else /* assume little endian non-intel */ -#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) -#endif -#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ - (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ - (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ - MUR_ONE_THREE(p)))) -#endif -#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) -#define MUR_FMIX(_h) \ -do { \ - _h ^= _h >> 16; \ - _h *= 0x85ebca6bu; \ - _h ^= _h >> 13; \ - _h *= 0xc2b2ae35u; \ - _h ^= _h >> 16; \ -} while (0) - -#define HASH_MUR(key,keylen,hashv) \ -do { \ - const uint8_t *_mur_data = (const uint8_t*)(key); \ - const int _mur_nblocks = (int)(keylen) / 4; \ - uint32_t _mur_h1 = 0xf88D5353u; \ - uint32_t _mur_c1 = 0xcc9e2d51u; \ - uint32_t _mur_c2 = 0x1b873593u; \ - uint32_t _mur_k1 = 0; \ - const uint8_t *_mur_tail; \ - const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ - int _mur_i; \ - for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ - _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - \ - _mur_h1 ^= _mur_k1; \ - _mur_h1 = MUR_ROTL32(_mur_h1,13); \ - _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ - } \ - _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ - _mur_k1=0; \ - switch((keylen) & 3U) { \ - case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ - case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ - case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - _mur_h1 ^= _mur_k1; \ - } \ - _mur_h1 ^= (uint32_t)(keylen); \ - MUR_FMIX(_mur_h1); \ - hashv = _mur_h1; \ -} while (0) -#endif /* HASH_USING_NO_STRICT_ALIASING */ - -/* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ -do { \ - if ((head).hh_head != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ - } else { \ - (out) = NULL; \ - } \ - while ((out) != NULL) { \ - if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ - if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \ - break; \ - } \ - } \ - if ((out)->hh.hh_next != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ - } else { \ - (out) = NULL; \ - } \ - } \ -} while (0) - -/* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,addhh) \ -do { \ - head.count++; \ - (addhh)->hh_next = head.hh_head; \ - (addhh)->hh_prev = NULL; \ - if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ - (head).hh_head=addhh; \ - if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ - && ((addhh)->tbl->noexpand != 1U)) { \ - HASH_EXPAND_BUCKETS((addhh)->tbl); \ - } \ -} while (0) - -/* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(hh,head,hh_del) \ - (head).count--; \ - if ((head).hh_head == hh_del) { \ - (head).hh_head = hh_del->hh_next; \ - } \ - if (hh_del->hh_prev) { \ - hh_del->hh_prev->hh_next = hh_del->hh_next; \ - } \ - if (hh_del->hh_next) { \ - hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } - -/* Bucket expansion has the effect of doubling the number of buckets - * and redistributing the items into the new buckets. Ideally the - * items will distribute more or less evenly into the new buckets - * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * - * With the items distributed into more buckets, the chain length - * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain - * length is the essence of how a hash provides constant time lookup. - * - * The calculation of tbl->ideal_chain_maxlen below deserves some - * explanation. First, keep in mind that we're calculating the ideal - * maximum chain length based on the *new* (doubled) bucket count. - * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate - * ceil(n/b). We don't depend on floating point arithmetic in this - * hash, so to calculate ceil(n/b) with integers we could write - * - * ceil(n/b) = (n/b) + ((n%b)?1:0) - * - * and in fact a previous version of this hash did just that. - * But now we have improved things a bit by recognizing that b is - * always a power of two. We keep its base 2 log handy (call it lb), - * so now we can write this with a bit shift and logical AND: - * - * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * - */ -#define HASH_EXPAND_BUCKETS(tbl) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ - memset(_he_new_buckets, 0, \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - tbl->ideal_chain_maxlen = \ - (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ - (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ - tbl->nonideal_items = 0; \ - for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ - { \ - _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh != NULL) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ - if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ - tbl->nonideal_items++; \ - _he_newbkt->expand_mult = _he_newbkt->count / \ - tbl->ideal_chain_maxlen; \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ - _he_thh; } \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ - } \ - } \ - uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - tbl->num_buckets *= 2U; \ - tbl->log2_num_buckets++; \ - tbl->buckets = _he_new_buckets; \ - tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ - (tbl->ineff_expands+1U) : 0U; \ - if (tbl->ineff_expands > 1U) { \ - tbl->noexpand=1; \ - uthash_noexpand_fyi(tbl); \ - } \ - uthash_expand_fyi(tbl); \ -} while (0) - - -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. - * HASH_SRT was added to allow the hash handle name to be passed in. */ -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -#define HASH_SRT(hh,head,cmpfcn) \ -do { \ - unsigned _hs_i; \ - unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ - struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ - if (head != NULL) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping != 0U) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p != NULL) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ - _hs_psize++; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - if (! (_hs_q) ) { break; } \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ - if (_hs_psize == 0U) { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else if (( \ - cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ - ) <= 0) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail != NULL ) { \ - _hs_tail->next = ((_hs_e != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - if (_hs_e != NULL) { \ - _hs_e->prev = ((_hs_tail != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ - } \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ - } \ - if (_hs_tail != NULL){ \ - _hs_tail->next = NULL; \ - } \ - if ( _hs_nmerges <= 1U ) { \ - _hs_looping=0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ - } \ - _hs_insize *= 2U; \ - } \ - HASH_FSCK(hh,head); \ - } \ -} while (0) - -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash - * hash handle that must be present in the structure. */ -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -do { \ - unsigned _src_bkt, _dst_bkt; \ - void *_last_elt=NULL, *_elt; \ - UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ - ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if (src != NULL) { \ - for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh != NULL; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ - if (dst == NULL) { \ - DECLTYPE_ASSIGN(dst,_elt); \ - HASH_MAKE_TABLE(hh_dst,dst); \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ - (dst)->hh_dst.tbl->num_items++; \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ - } \ - } \ - } \ - } \ - HASH_FSCK(hh_dst,dst); \ -} while (0) - -#define HASH_CLEAR(hh,head) \ -do { \ - if (head != NULL) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)=NULL; \ - } \ -} while (0) - -#define HASH_OVERHEAD(hh,head) \ - ((head != NULL) ? ( \ - (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ - ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ - sizeof(UT_hash_table) + \ - (HASH_BLOOM_BYTELEN))) : 0U) - -#ifdef NO_DECLTYPE -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#else -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#endif - -/* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) -#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) - -typedef struct UT_hash_bucket { - struct UT_hash_handle *hh_head; - unsigned count; - - /* expand_mult is normally set to 0. In this situation, the max chain length - * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). - * However, setting expand_mult to a non-zero value delays bucket expansion - * (that would be triggered by additions to this particular bucket) - * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. - * (The multiplier is simply expand_mult+1). The whole idea of this - * multiplier is to reduce bucket expansions, since they are expensive, in - * situations where we know that a particular bucket tends to be overused. - * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. - */ - unsigned expand_mult; - -} UT_hash_bucket; - -/* random signature used only to find hash tables in external analysis */ -#define HASH_SIGNATURE 0xa0111fe1u -#define HASH_BLOOM_SIGNATURE 0xb12220f2u - -typedef struct UT_hash_table { - UT_hash_bucket *buckets; - unsigned num_buckets, log2_num_buckets; - unsigned num_items; - struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ - ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ - - /* in an ideal situation (all buckets used equally), no bucket would have - * more than ceil(#items/#buckets) items. that's the ideal chain length. */ - unsigned ideal_chain_maxlen; - - /* nonideal_items is the number of items in the hash whose chain position - * exceeds the ideal chain maxlen. these items pay the penalty for an uneven - * hash distribution; reaching them in a chain traversal takes >ideal steps */ - unsigned nonideal_items; - - /* ineffective expands occur when a bucket doubling was performed, but - * afterward, more than half the items in the hash had nonideal chain - * positions. If this happens on two consecutive expansions we inhibit any - * further expansion, as it's not helping; this happens when the hash - * function isn't a good fit for the key domain. When expansion is inhibited - * the hash will still work, albeit no longer in constant time. */ - unsigned ineff_expands, noexpand; - - uint32_t signature; /* used only to find hash tables in external analysis */ -#ifdef HASH_BLOOM - uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ - uint8_t *bloom_bv; - uint8_t bloom_nbits; -#endif - -} UT_hash_table; - -typedef struct UT_hash_handle { - struct UT_hash_table *tbl; - void *prev; /* prev element in app order */ - void *next; /* next element in app order */ - struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ - struct UT_hash_handle *hh_next; /* next hh in bucket order */ - void *key; /* ptr to enclosing struct's key */ - unsigned keylen; /* enclosing struct's key len */ - unsigned hashv; /* result of hash-fcn(key) */ -} UT_hash_handle; - -#endif /* UTHASH_H */ diff --git a/vg.sh b/vg.sh deleted file mode 100755 index dd6a8dc..0000000 --- a/vg.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# small valgrind helper to execute unit tests with valgrind - -vg="valgrind --leak-check=full --show-reachable=yes" -cmd=$1 -shift - -if test -z "$cmd"; then - echo "Usage: $0 " - exit 1 -else - if echo "$cmd" | egrep "^\.\." > /dev/null 2>&1; then - cd tests - fi -fi - -$vg $cmd $* > log 2>&1 -less log -rm -f log -