mirror of
https://codeberg.org/scip/udpxd.git
synced 2025-12-16 19:40:58 +01:00
mv to codeberg
This commit is contained in:
@@ -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
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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-*
|
|
||||||
54
Makefile
54
Makefile
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
|
|
||||||
.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! +++"
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
[](https://ci.codeberg.org/repos/15646)
|
[](https://ci.codeberg.org/repos/15646)
|
||||||
[](https://codeberg.org/scip/udpxd/raw/branch/main/LICENSE)
|
[](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
|
## UDPXD - A general purpose UDP relay/port forwarder/proxy
|
||||||
|
|
||||||
|
|||||||
7
TODO
7
TODO
@@ -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
|
|
||||||
82
client.c
82
client.c
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
87
client.h
87
client.h
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _HAVE_CLIENT_H
|
|
||||||
#define _HAVE_CLIENT_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
|
|
||||||
#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
|
|
||||||
173
host.c
173
host.c
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
56
host.h
56
host.h
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _HAVE_HOST_H
|
|
||||||
#define _HAVE_HOST_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <net/if.h> // if_nametoindex()
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
|
|
||||||
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
|
|
||||||
48
log.c
48
log.c
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
log.h
40
log.h
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _HAVE_LOG_H
|
|
||||||
#define _HAVE_LOG_H
|
|
||||||
|
|
||||||
#define _WITH_DPRINTF
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
extern int VERBOSE;
|
|
||||||
extern int FORKED;
|
|
||||||
|
|
||||||
void verbose(const char * fmt, ...);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
89
meson.build
89
meson.build
@@ -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 <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n#include <sys/socket.h>\n#include <syslog.h>\n#include <stdarg.h>\n#include <arpa/inet.h>\n#include <sys/types.h>\n#include <ifaddrs.h>\n#include <netdb.h>\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
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# custom build options
|
|
||||||
447
net.c
447
net.c
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
74
net.h
74
net.h
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _HAVE_NET_H
|
|
||||||
#define _HAVE_NET_H
|
|
||||||
|
|
||||||
#include <setjmp.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#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
|
|
||||||
101
platform.h.in
101
platform.h.in
@@ -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
|
|
||||||
301
udpxd.1
301
udpxd.1
@@ -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 <ip:port> listen for incoming requests
|
|
||||||
\& \-\-bind \-b <ip[:port]> bind ip used for outgoing requests
|
|
||||||
\& specify port for promiscuous mode
|
|
||||||
\& \-\-to \-t <ip:port> destination to forward requests to
|
|
||||||
\& \-\-daemon \-d daemon mode, fork into background
|
|
||||||
\& \-\-pidfile \-p <file> pidfile, default: /var/run/udpxd.pid
|
|
||||||
\& \-\-user \-u <user> run as user (only in daemon mode)
|
|
||||||
\& \-\-chroot \-c <path> chroot to <path> (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:
|
|
||||||
<https://codeberg.org/scip/udpxd/issues>.
|
|
||||||
.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
|
|
||||||
250
udpxd.c
250
udpxd.c
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 <ip:port> listen for incoming requests\n"
|
|
||||||
"--bind -b <ip[:port]> bind ip used for outgoing requests\n"
|
|
||||||
" specify port for promiscuous mode\n"
|
|
||||||
"--to -t <ip:port> destination to forward requests to\n"
|
|
||||||
"--daemon -d daemon mode, fork into background\n"
|
|
||||||
"--pidfile -p <file> pidfile, default: /var/run/udpxd.pid\n"
|
|
||||||
"--user -u <user> run as user (only in daemon mode)\n"
|
|
||||||
"--chroot -c <path> chroot to <path> (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 <ip-address:port>!\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 <ip-address:port>!\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;
|
|
||||||
}
|
|
||||||
44
udpxd.h
44
udpxd.h
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
You can contact me by mail: <tom AT vondein DOT org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _HAVE_UDPXD_H
|
|
||||||
#define _HAVE_UDPXD_H
|
|
||||||
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "platform.h"
|
|
||||||
|
|
||||||
void usage();
|
|
||||||
int parse_ip(char *src, char *ip, char *pt);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
153
udpxd.pod
153
udpxd.pod
@@ -1,153 +0,0 @@
|
|||||||
=head1 NAME
|
|
||||||
|
|
||||||
udpxd - A general purpose UDP relay/port forwarder/proxy
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
Usage: udpxd [-lbdfpvhV]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--listen -l <ip:port> listen for incoming requests
|
|
||||||
--bind -b <ip[:port]> bind ip used for outgoing requests
|
|
||||||
specify port for promiscuous mode
|
|
||||||
--to -t <ip:port> destination to forward requests to
|
|
||||||
--daemon -d daemon mode, fork into background
|
|
||||||
--pidfile -p <file> pidfile, default: /var/run/udpxd.pid
|
|
||||||
--user -u <user> run as user (only in daemon mode)
|
|
||||||
--chroot -c <path> chroot to <path> (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</var/run/udpxd.pid>, which can be changed with the B<-p>
|
|
||||||
option. If started as root, it also drops privileges to the
|
|
||||||
user C<nobody> or the user specified with B<-u> and chroots
|
|
||||||
to C</var/empty> 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<Caution: if not running in daemon mode, udpxd does not drop
|
|
||||||
its privileges and will continue to run as root (if started as
|
|
||||||
root).>
|
|
||||||
|
|
||||||
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</var/run/udpxd.pid>: 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<https://codeberg.org/scip/udpxd/issues>.
|
|
||||||
|
|
||||||
=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<uthash> (bundled), which is
|
|
||||||
Copyright (c) 2003-2013 by Troy D. Hanson.
|
|
||||||
|
|
||||||
=head1 AUTHORS
|
|
||||||
|
|
||||||
T.v.Dein B<tom AT vondein DOT org>
|
|
||||||
|
|
||||||
=cut
|
|
||||||
20
vg.sh
20
vg.sh
@@ -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 <commandline ..>"
|
|
||||||
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
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user