mirror of
https://github.com/samhocevar/rinetd.git
synced 2025-03-14 20:00:11 +08:00
Merge branch 'feature-ipv6-support' into main
This commit is contained in:
commit
3b33f45925
@ -33,6 +33,9 @@
|
||||
<ClInclude Include="src\rinetd.h" />
|
||||
<ClInclude Include="src\types.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="src\parse.peg" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{3F1D9D58-3205-48E9-9C93-557291A45D74}</ProjectGuid>
|
||||
|
40
src/net.c
40
src/net.c
@ -1,6 +1,6 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 Sam Hocevar <sam@hocevar.net>
|
||||
© 2003—2021 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
This software is released for free use under the terms of
|
||||
the GNU Public License, version 2 or higher. NO WARRANTY
|
||||
@ -12,8 +12,7 @@
|
||||
|
||||
#include "net.h"
|
||||
|
||||
void setSocketDefaults(SOCKET fd)
|
||||
{
|
||||
void setSocketDefaults(SOCKET fd) {
|
||||
/* Make socket non-blocking (FIXME: this uses legacy API) */
|
||||
FIONBIO_ARG_T ioctltmp = 1;
|
||||
#if _WIN32
|
||||
@ -33,3 +32,38 @@ void setSocketDefaults(SOCKET fd)
|
||||
#endif
|
||||
}
|
||||
|
||||
int getSocketType(int protocol) {
|
||||
return protocol == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM;
|
||||
}
|
||||
|
||||
int sameSocketAddress(struct sockaddr_storage *a, struct sockaddr_storage *b) {
|
||||
if (a->ss_family != b->ss_family)
|
||||
return 0;
|
||||
|
||||
switch (a->ss_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in *a4 = (struct sockaddr_in *)a;
|
||||
struct sockaddr_in *b4 = (struct sockaddr_in *)b;
|
||||
return a4->sin_port == b4->sin_port
|
||||
&& a4->sin_addr.s_addr == b4->sin_addr.s_addr;
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)a;
|
||||
struct sockaddr_in6 *b6 = (struct sockaddr_in6 *)b;
|
||||
return a6->sin6_port == b6->sin6_port
|
||||
&& a6->sin6_addr.s6_addr == b6->sin6_addr.s6_addr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t getPort(struct addrinfo* ai) {
|
||||
switch (ai->ai_family) {
|
||||
case AF_INET:
|
||||
return ntohs(((struct sockaddr_in*)ai->ai_addr)->sin_port);
|
||||
case AF_INET6:
|
||||
return ntohs(((struct sockaddr_in6*)ai->ai_addr)->sin6_port);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
12
src/net.h
12
src/net.h
@ -12,6 +12,8 @@
|
||||
/* Define this to a reasonably large value */
|
||||
# define FD_SETSIZE 4096
|
||||
# include <winsock2.h>
|
||||
# include <ws2ipdef.h>
|
||||
# include <ws2tcpip.h>
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
@ -22,6 +24,8 @@
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined HAVE_ERRNO_H
|
||||
# include <errno.h>
|
||||
#endif
|
||||
@ -59,6 +63,10 @@
|
||||
# ifndef WSAEAGAIN
|
||||
# define WSAEAGAIN WSAEWOULDBLOCK
|
||||
# endif
|
||||
# ifdef gai_strerror
|
||||
# undef gai_strerror
|
||||
# define gai_strerror gai_strerrorA
|
||||
# endif
|
||||
#else
|
||||
/* Windows sockets compatibility defines */
|
||||
# define INVALID_SOCKET (-1)
|
||||
@ -77,4 +85,6 @@ static inline int GetLastError(void) {
|
||||
#endif /* _WIN32 */
|
||||
|
||||
void setSocketDefaults(SOCKET fd);
|
||||
|
||||
int getSocketType(int protocol);
|
||||
int sameSocketAddress(struct sockaddr_storage *a, struct sockaddr_storage *b);
|
||||
uint16_t getPort(struct addrinfo* ai);
|
||||
|
624
src/parse.c
624
src/parse.c
File diff suppressed because it is too large
Load Diff
@ -21,10 +21,8 @@
|
||||
FILE *fp; \
|
||||
int currentLine; \
|
||||
int isAuthAllow; \
|
||||
char *tmpPort; \
|
||||
uint16_t tmpPortNum, tmpProto; \
|
||||
uint16_t bindPortNum, connectPortNum; \
|
||||
int bindProto, connectProto; \
|
||||
char *tmpPort, *bindPort, *connectPort; \
|
||||
int tmpProto, bindProto, connectProto; \
|
||||
int serverTimeout; \
|
||||
char *bindAddress, *connectAddress, *sourceAddress;
|
||||
#define YY_INPUT(yyctx, buf, result, max_size) \
|
||||
@ -51,8 +49,8 @@ comment = "#" (!eol .)*
|
||||
|
||||
server-rule = bind-address - bind-port - connect-address - connect-port (-? server-options)?
|
||||
{
|
||||
addServer(yy->bindAddress, yy->bindPortNum, yy->bindProto,
|
||||
yy->connectAddress, yy->connectPortNum, yy->connectProto,
|
||||
addServer(yy->bindAddress, yy->bindPort, yy->bindProto,
|
||||
yy->connectAddress, yy->connectPort, yy->connectProto,
|
||||
yy->serverTimeout > 0 ? yy->serverTimeout : RINETD_DEFAULT_UDP_TIMEOUT,
|
||||
yy->sourceAddress);
|
||||
yy->bindAddress = yy->connectAddress = yy->sourceAddress = NULL;
|
||||
@ -61,8 +59,8 @@ server-rule = bind-address - bind-port - connect-address - connect-port (-? se
|
||||
|
||||
bind-address = < address > { yy->bindAddress = strdup(yytext); }
|
||||
connect-address = < address > { yy->connectAddress = strdup(yytext); }
|
||||
bind-port = full-port { yy->bindPortNum = yy->tmpPortNum; yy->bindProto = yy->tmpProto; }
|
||||
connect-port = full-port { yy->connectPortNum = yy->tmpPortNum; yy->connectProto = yy->tmpProto; }
|
||||
bind-port = full-port { yy->bindPort = yy->tmpPort; yy->bindProto = yy->tmpProto; }
|
||||
connect-port = full-port { yy->connectPort = yy->tmpPort; yy->connectProto = yy->tmpProto; }
|
||||
server-options = "[" -? option-list -? "]"
|
||||
|
||||
option-list = option (-? ',' -? option-list)?
|
||||
@ -72,22 +70,12 @@ option-source = "src" -? "=" -? < address > { free(yy->sourceAddress);
|
||||
yy->sourceAddress = strdup(yytext); }
|
||||
|
||||
full-port = port proto
|
||||
{
|
||||
char const *proto = yy->tmpProto == protoTcp ? "tcp" : "udp";
|
||||
struct servent *service = getservbyname(yy->tmpPort, proto);
|
||||
int port = service ? ntohs(service->s_port) : atoi(yy->tmpPort);
|
||||
if (port <= 0 || port >= 65536) {
|
||||
syslog(LOG_ERR, "port %s/%s missing or out of range\n", yy->tmpPort, proto);
|
||||
PARSE_ERROR;
|
||||
}
|
||||
yy->tmpPortNum = (uint16_t)port;
|
||||
}
|
||||
|
||||
port = < (number | service) > { free(yy->tmpPort);
|
||||
yy->tmpPort = strdup(yytext); }
|
||||
proto = '/tcp' { yy->tmpProto = protoTcp; }
|
||||
| '/udp' { yy->tmpProto = protoUdp; }
|
||||
| '' { yy->tmpProto = protoTcp; }
|
||||
proto = '/tcp' { yy->tmpProto = IPPROTO_TCP; }
|
||||
| '/udp' { yy->tmpProto = IPPROTO_UDP; }
|
||||
| '' { yy->tmpProto = IPPROTO_TCP; }
|
||||
|
||||
auth-rule = auth-key - < pattern >
|
||||
{
|
||||
@ -143,21 +131,24 @@ invalid_syntax = < (!eol .)+ > eol
|
||||
}
|
||||
|
||||
service = name
|
||||
address = ipv4 | hostname # TODO: IPV6
|
||||
pattern = [0-9*?]+ ('.' [0-9*?]+ ('.' [0-9*?]+ ('.' [0-9*?]+)?)?)?
|
||||
address = ipv4 | ipv6 | hostname
|
||||
pattern = (hexdigit | '[' | ']' | ':' | '.' | [*?] )+
|
||||
number = digit+
|
||||
|
||||
ipv4 = number '.' number '.' number '.' number | '0'
|
||||
ipv6 = (hexdigit | ':')+ | '[' (hexdigit | ':')+ ']'
|
||||
hostname = (label '.')* name '.'?
|
||||
|
||||
|
||||
name = [a-zA-Z_] [-a-zA-Z0-9_]*
|
||||
label = [0-9]* name
|
||||
name = id ('-' | id | digit)*
|
||||
label = (id | digit) ('-' | id | digit)* # DNS labels may have digits
|
||||
filename = '"' [^"]+ '"'
|
||||
| [^ \t\r\n]+
|
||||
|
||||
- = [ \t]+
|
||||
digit = [0-9]
|
||||
hexdigit = [0-9a-fA-F]
|
||||
id = [a-zA-Z_]
|
||||
sol = { ++yy->currentLine; }
|
||||
eol = '\r'? '\n' | eof
|
||||
eof = '\0'
|
||||
|
329
src/rinetd.c
329
src/rinetd.c
@ -80,7 +80,7 @@ int logFormatCommon = 0;
|
||||
FILE *logFile = NULL;
|
||||
|
||||
char const *logMessages[] = {
|
||||
"unknown-error",
|
||||
"unknown-error",
|
||||
"done-local-closed",
|
||||
"done-remote-closed",
|
||||
"accept-failed -",
|
||||
@ -119,7 +119,6 @@ static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
static void handleAccept(ServerInfo const *srv);
|
||||
static ConnectionInfo *findAvailableConnection(void);
|
||||
static void setConnectionCount(int newCount);
|
||||
static int getAddress(char const *host, struct in_addr *iaddr);
|
||||
static int checkConnectionAllowed(ConnectionInfo const *cnx);
|
||||
|
||||
static int readArgs (int argc, char **argv, RinetdOptions *options);
|
||||
@ -211,6 +210,11 @@ static void clearConfiguration(void) {
|
||||
}
|
||||
free(srv->fromHost);
|
||||
free(srv->toHost);
|
||||
freeaddrinfo(srv->fromAddrInfo);
|
||||
freeaddrinfo(srv->toAddrInfo);
|
||||
if (srv->sourceAddrInfo) {
|
||||
freeaddrinfo(srv->sourceAddrInfo);
|
||||
}
|
||||
}
|
||||
/* Free memory associated with previous set. */
|
||||
free(seInfo);
|
||||
@ -252,101 +256,112 @@ static void readConfiguration(char const *file) {
|
||||
}
|
||||
}
|
||||
|
||||
void addServer(char *bindAddress, uint16_t bindPort, protocolType bindProto,
|
||||
char *connectAddress, uint16_t connectPort, protocolType connectProto,
|
||||
void addServer(char *bindAddress, char *bindPort, int bindProtocol,
|
||||
char *connectAddress, char *connectPort, int connectProtocol,
|
||||
int serverTimeout, char *sourceAddress)
|
||||
{
|
||||
/* Turn all of this stuff into reasonable addresses */
|
||||
struct in_addr iaddr;
|
||||
if (getAddress(bindAddress, &iaddr) < 0) {
|
||||
fprintf(stderr, "rinetd: host %s could not be resolved.\n",
|
||||
bindAddress);
|
||||
exit(1);
|
||||
}
|
||||
struct in_addr isourceaddr;
|
||||
isourceaddr.s_addr = INADDR_ANY;
|
||||
if (sourceAddress && getAddress(sourceAddress, &isourceaddr) < 0) {
|
||||
fprintf(stderr, "rinetd: host %s could not be resolved.\n",
|
||||
sourceAddress);
|
||||
exit(1);
|
||||
}
|
||||
ServerInfo si = {
|
||||
.fromHost = bindAddress,
|
||||
.toHost = connectAddress,
|
||||
.serverTimeout = serverTimeout,
|
||||
};
|
||||
|
||||
/* Make a server socket */
|
||||
SOCKET fd = socket(PF_INET,
|
||||
bindProto == protoTcp ? SOCK_STREAM : SOCK_DGRAM,
|
||||
bindProto == protoTcp ? IPPROTO_TCP : IPPROTO_UDP);
|
||||
if (fd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "couldn't create "
|
||||
"server socket! (%m)\n");
|
||||
exit(1);
|
||||
}
|
||||
struct sockaddr_in saddr;
|
||||
saddr.sin_family = AF_INET;
|
||||
memcpy(&saddr.sin_addr, &iaddr, sizeof(iaddr));
|
||||
saddr.sin_port = htons(bindPort);
|
||||
int tmp = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(const char *) &tmp, sizeof(tmp));
|
||||
if (bind(fd, (struct sockaddr *)
|
||||
&saddr, sizeof(saddr)) == SOCKET_ERROR) {
|
||||
/* Warn -- don't exit. */
|
||||
syslog(LOG_ERR, "couldn't bind to "
|
||||
"address %s port %d (%m)\n",
|
||||
bindAddress, (int)bindPort);
|
||||
closesocket(fd);
|
||||
struct addrinfo hints =
|
||||
{
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_protocol = bindProtocol,
|
||||
.ai_socktype = getSocketType(bindProtocol),
|
||||
.ai_flags = AI_PASSIVE,
|
||||
};
|
||||
struct addrinfo *servinfo;
|
||||
int ret = getaddrinfo(bindAddress, bindPort, &hints, &servinfo);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "rinetd: getaddrinfo error: %s\n", gai_strerror(ret));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (bindProto == protoTcp) {
|
||||
if (listen(fd, RINETD_LISTEN_BACKLOG) == SOCKET_ERROR) {
|
||||
/* Warn -- don't exit. */
|
||||
syslog(LOG_ERR, "couldn't listen to "
|
||||
"address %s port %d (%m)\n",
|
||||
bindAddress, (int)bindPort);
|
||||
closesocket(fd);
|
||||
for (struct addrinfo *it = servinfo; it; it = it->ai_next) {
|
||||
si.fd = socket(it->ai_family, it->ai_socktype, it->ai_protocol);
|
||||
if (si.fd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "couldn't create server socket! (%m)\n");
|
||||
freeaddrinfo(servinfo);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Make socket nonblocking in TCP mode only, otherwise
|
||||
we may miss some data. */
|
||||
setSocketDefaults(fd);
|
||||
int tmp = 1;
|
||||
setsockopt(si.fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&tmp, sizeof(tmp));
|
||||
|
||||
if (bind(si.fd, it->ai_addr, it->ai_addrlen) == SOCKET_ERROR) {
|
||||
syslog(LOG_ERR, "couldn't bind to address %s port %s (%m)\n",
|
||||
bindAddress, bindPort);
|
||||
closesocket(si.fd);
|
||||
freeaddrinfo(servinfo);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (bindProtocol == IPPROTO_TCP) {
|
||||
if (listen(si.fd, RINETD_LISTEN_BACKLOG) == SOCKET_ERROR) {
|
||||
/* Warn -- don't exit. */
|
||||
syslog(LOG_ERR, "couldn't listen to address %s port %s (%m)\n",
|
||||
bindAddress, bindPort);
|
||||
/* XXX: check whether this is correct */
|
||||
closesocket(si.fd);
|
||||
}
|
||||
|
||||
/* Make socket nonblocking in TCP mode only, otherwise
|
||||
we may miss some data. */
|
||||
setSocketDefaults(si.fd);
|
||||
}
|
||||
|
||||
si.fromAddrInfo = it;
|
||||
break;
|
||||
}
|
||||
|
||||
if (getAddress(connectAddress, &iaddr) < 0) {
|
||||
/* Warn -- don't exit. */
|
||||
syslog(LOG_ERR, "host %s could not be resolved.\n",
|
||||
bindAddress);
|
||||
closesocket(fd);
|
||||
/* Resolve destination address. */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_protocol = connectProtocol;
|
||||
hints.ai_socktype = getSocketType(connectProtocol);
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
ret = getaddrinfo(connectAddress, connectPort, &hints, &servinfo);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "rinetd: getaddrinfo error: %s\n", gai_strerror(ret));
|
||||
freeaddrinfo(si.fromAddrInfo);
|
||||
closesocket(si.fd);
|
||||
exit(1);
|
||||
}
|
||||
si.toAddrInfo = servinfo;
|
||||
|
||||
/* Resolve source address if applicable. */
|
||||
if (sourceAddress) {
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC,
|
||||
hints.ai_protocol = connectProtocol,
|
||||
hints.ai_socktype = getSocketType(connectProtocol),
|
||||
|
||||
ret = getaddrinfo(sourceAddress, NULL, &hints, &servinfo);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "rinetd: getaddrinfo error: %s\n", gai_strerror(ret));
|
||||
freeaddrinfo(si.fromAddrInfo);
|
||||
freeaddrinfo(si.toAddrInfo);
|
||||
exit(1);
|
||||
}
|
||||
si.sourceAddrInfo = servinfo;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (si.fd > maxfd) {
|
||||
maxfd = si.fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allocate server info */
|
||||
seInfo = (ServerInfo *)
|
||||
realloc(seInfo, sizeof(ServerInfo) * (seTotal + 1));
|
||||
seInfo = (ServerInfo *)realloc(seInfo, sizeof(ServerInfo) * (seTotal + 1));
|
||||
if (!seInfo) {
|
||||
exit(1);
|
||||
}
|
||||
ServerInfo *srv = &seInfo[seTotal];
|
||||
memset(srv, 0, sizeof(*srv));
|
||||
srv->fd = fd;
|
||||
srv->localAddr = iaddr;
|
||||
srv->localPort = htons(connectPort);
|
||||
srv->fromHost = bindAddress;
|
||||
if (!srv->fromHost) {
|
||||
exit(1);
|
||||
}
|
||||
srv->fromPort = bindPort;
|
||||
srv->fromProto = bindProto;
|
||||
srv->sourceAddr = isourceaddr;
|
||||
srv->toHost = connectAddress;
|
||||
if (!srv->toHost) {
|
||||
exit(1);
|
||||
}
|
||||
srv->toPort = connectPort;
|
||||
srv->toProto = connectProto;
|
||||
srv->serverTimeout = serverTimeout;
|
||||
#ifndef _WIN32
|
||||
if (fd > maxfd) {
|
||||
maxfd = fd;
|
||||
}
|
||||
#endif
|
||||
seInfo[seTotal] = si;
|
||||
++seTotal;
|
||||
}
|
||||
|
||||
@ -361,7 +376,7 @@ static void setConnectionCount(int newCount)
|
||||
closesocket(coInfo[i].local.fd);
|
||||
}
|
||||
if (coInfo[i].remote.fd != INVALID_SOCKET) {
|
||||
if (coInfo[i].remote.proto == protoTcp)
|
||||
if (coInfo[i].remote.protocol == IPPROTO_TCP)
|
||||
closesocket(coInfo[i].remote.fd);
|
||||
}
|
||||
free(coInfo[i].local.buffer);
|
||||
@ -477,7 +492,7 @@ static void selectPass(void)
|
||||
if (cnx->remote.recvPos < RINETD_BUFFER_SIZE) {
|
||||
FD_SET_EXT(cnx->remote.fd, readfds);
|
||||
/* For UDP connections, we need to handle timeouts */
|
||||
if (cnx->remote.proto == protoUdp) {
|
||||
if (cnx->remote.protocol == IPPROTO_UDP) {
|
||||
long delay = (long)(cnx->remoteTimeout - now);
|
||||
timeout.tv_sec = delay <= 1 ? 1
|
||||
: (delay < timeout.tv_sec || timeout.tv_sec == 0) ? delay
|
||||
@ -497,7 +512,7 @@ static void selectPass(void)
|
||||
if (cnx->remote.fd != INVALID_SOCKET) {
|
||||
/* Do not read on remote UDP sockets, the server does it,
|
||||
but handle timeouts instead. */
|
||||
if (cnx->remote.proto == protoTcp) {
|
||||
if (cnx->remote.protocol == IPPROTO_TCP) {
|
||||
if (FD_ISSET_EXT(cnx->remote.fd, readfds)) {
|
||||
handleRead(cnx, &cnx->remote, &cnx->local);
|
||||
}
|
||||
@ -575,7 +590,7 @@ static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
if (cnx->coClosing && (socket->sentPos == other_socket->recvPos)) {
|
||||
PERROR("rinetd: local closed and no more output");
|
||||
logEvent(cnx, cnx->server, cnx->coLog);
|
||||
if (socket->proto == protoTcp)
|
||||
if (socket->protocol == IPPROTO_TCP)
|
||||
closesocket(socket->fd);
|
||||
socket->fd = INVALID_SOCKET;
|
||||
return;
|
||||
@ -583,7 +598,7 @@ static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
|
||||
struct sockaddr const *addr = NULL;
|
||||
SOCKLEN_T addrlen = 0;
|
||||
if (socket->proto == protoUdp && socket == &cnx->remote) {
|
||||
if (socket->protocol == IPPROTO_UDP && socket == &cnx->remote) {
|
||||
addr = (struct sockaddr const*)&cnx->remoteAddress;
|
||||
addrlen = (SOCKLEN_T)sizeof(cnx->remoteAddress);
|
||||
}
|
||||
@ -611,16 +626,16 @@ static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socket)
|
||||
{
|
||||
cnx->coClosing = 1;
|
||||
if (socket->proto == protoTcp) {
|
||||
if (socket->protocol == IPPROTO_TCP) {
|
||||
/* One end fizzled out, so make sure we're all done with that */
|
||||
closesocket(socket->fd);
|
||||
} else /* if (socket->proto == protoUdp) */ {
|
||||
} else /* if (socket->protocol == IPPROTO_UDP) */ {
|
||||
/* Nothing to do in UDP mode */
|
||||
}
|
||||
socket->fd = INVALID_SOCKET;
|
||||
|
||||
if (other_socket->fd != INVALID_SOCKET) {
|
||||
if (other_socket->proto == protoTcp) {
|
||||
if (other_socket->protocol == IPPROTO_TCP) {
|
||||
#if !defined __linux__ && !defined _WIN32
|
||||
/* Now set up the other end for a polite closing */
|
||||
|
||||
@ -631,7 +646,7 @@ static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
setsockopt(other_socket->fd, SOL_SOCKET, SO_SNDLOWAT,
|
||||
&arg, sizeof(arg));
|
||||
#endif
|
||||
} else /* if (other_socket->proto == protoUdp) */ {
|
||||
} else /* if (other_socket->protocol == IPPROTO_UDP) */ {
|
||||
if (other_socket == &cnx->local)
|
||||
closesocket(other_socket->fd);
|
||||
other_socket->fd = INVALID_SOCKET;
|
||||
@ -646,13 +661,13 @@ static void handleAccept(ServerInfo const *srv)
|
||||
{
|
||||
int udpBytes = 0;
|
||||
|
||||
struct sockaddr addr;
|
||||
struct sockaddr_storage addr;
|
||||
SOCKLEN_T addrlen = sizeof(addr);
|
||||
|
||||
SOCKET nfd;
|
||||
if (srv->fromProto == protoTcp) {
|
||||
if (srv->fromAddrInfo->ai_protocol == IPPROTO_TCP) {
|
||||
/* In TCP mode, get remote address using accept(). */
|
||||
nfd = accept(srv->fd, &addr, &addrlen);
|
||||
nfd = accept(srv->fd, (struct sockaddr *)&addr, &addrlen);
|
||||
if (nfd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "accept(%llu): %m\n", (long long unsigned int)srv->fd);
|
||||
logEvent(NULL, srv, logAcceptFailed);
|
||||
@ -660,14 +675,14 @@ static void handleAccept(ServerInfo const *srv)
|
||||
}
|
||||
|
||||
setSocketDefaults(nfd);
|
||||
} else /* if (srv->fromProto == protoUdp) */ {
|
||||
} else /* if (srv->fromAddrInfo->ai_protocol == IPPROTO_UDP) */ {
|
||||
/* In UDP mode, get remote address using recvfrom() and check
|
||||
for an existing connection from this client. We need
|
||||
to read a lot of data otherwise the datagram contents
|
||||
may be lost later. */
|
||||
nfd = srv->fd;
|
||||
SSIZE_T ret = recvfrom(nfd, globalUdpBuffer,
|
||||
sizeof(globalUdpBuffer), 0, &addr, &addrlen);
|
||||
sizeof(globalUdpBuffer), 0, (struct sockaddr *)&addr, &addrlen);
|
||||
if (ret < 0) {
|
||||
if (GetLastError() == WSAEWOULDBLOCK) {
|
||||
return;
|
||||
@ -682,13 +697,10 @@ static void handleAccept(ServerInfo const *srv)
|
||||
|
||||
udpBytes = (int)ret;
|
||||
|
||||
/* Look for an existing connection with the same remote host and port. */
|
||||
for (int i = 0; i < coTotal; ++i) {
|
||||
ConnectionInfo *cnx = &coInfo[i];
|
||||
struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr;
|
||||
if (cnx->remote.fd == nfd
|
||||
&& cnx->remoteAddress.sin_family == addr_in->sin_family
|
||||
&& cnx->remoteAddress.sin_port == addr_in->sin_port
|
||||
&& cnx->remoteAddress.sin_addr.s_addr == addr_in->sin_addr.s_addr) {
|
||||
if (cnx->remote.fd == nfd && sameSocketAddress(&cnx->remoteAddress, &addr)) {
|
||||
cnx->remoteTimeout = time(NULL) + srv->serverTimeout;
|
||||
handleUdpRead(cnx, globalUdpBuffer, udpBytes);
|
||||
return;
|
||||
@ -702,16 +714,18 @@ static void handleAccept(ServerInfo const *srv)
|
||||
}
|
||||
|
||||
cnx->local.fd = INVALID_SOCKET;
|
||||
cnx->local.proto = srv->toProto;
|
||||
cnx->local.family = srv->toAddrInfo->ai_family;
|
||||
cnx->local.protocol = srv->toAddrInfo->ai_protocol;
|
||||
cnx->local.recvPos = cnx->local.sentPos = 0;
|
||||
cnx->local.totalBytesIn = cnx->local.totalBytesOut = 0;
|
||||
|
||||
cnx->remote.fd = nfd;
|
||||
cnx->remote.proto = srv->fromProto;
|
||||
cnx->remote.family = srv->fromAddrInfo->ai_family;
|
||||
cnx->remote.protocol = srv->fromAddrInfo->ai_protocol;
|
||||
cnx->remote.recvPos = cnx->remote.sentPos = 0;
|
||||
cnx->remote.totalBytesIn = cnx->remote.totalBytesOut = 0;
|
||||
cnx->remoteAddress = *(struct sockaddr_in *)&addr;
|
||||
if (srv->fromProto == protoUdp)
|
||||
cnx->remoteAddress = addr;
|
||||
if (srv->fromAddrInfo->ai_protocol == IPPROTO_UDP)
|
||||
cnx->remoteTimeout = time(NULL) + srv->serverTimeout;
|
||||
|
||||
cnx->coClosing = 0;
|
||||
@ -722,7 +736,7 @@ static void handleAccept(ServerInfo const *srv)
|
||||
if (logCode != logAllowed) {
|
||||
/* Local fd is not open yet, so only
|
||||
close the remote socket. */
|
||||
if (cnx->remote.proto == protoTcp)
|
||||
if (cnx->remote.protocol == IPPROTO_TCP)
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
logEvent(cnx, cnx->server, logCode);
|
||||
@ -732,46 +746,38 @@ static void handleAccept(ServerInfo const *srv)
|
||||
/* Now open a connection to the local server.
|
||||
This, too, is nonblocking. Why wait
|
||||
for anything when you don't have to? */
|
||||
struct sockaddr_in saddr;
|
||||
cnx->local.fd = srv->toProto == protoTcp
|
||||
? socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||
: socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
struct addrinfo* to = srv->toAddrInfo;
|
||||
cnx->local.fd = socket(to->ai_family, to->ai_socktype, to->ai_protocol);
|
||||
if (cnx->local.fd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "socket(): %m\n");
|
||||
if (cnx->remote.proto == protoTcp)
|
||||
if (cnx->remote.protocol == IPPROTO_TCP)
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
logEvent(cnx, srv, logLocalSocketFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (srv->toProto == protoTcp)
|
||||
if (srv->toAddrInfo->ai_protocol == IPPROTO_TCP)
|
||||
setSocketDefaults(cnx->local.fd);
|
||||
|
||||
/* Bind the local socket even if we use connect() later, so that
|
||||
/* Bind the local socket even though we use connect() later, so that
|
||||
we can specify a source address. */
|
||||
memset(&saddr, 0, sizeof(struct sockaddr_in));
|
||||
saddr.sin_family = AF_INET;
|
||||
memcpy(&saddr.sin_addr, &srv->sourceAddr, sizeof(struct in_addr));
|
||||
saddr.sin_port = 0;
|
||||
if (bind(cnx->local.fd, (struct sockaddr *)&saddr,
|
||||
sizeof(saddr)) == SOCKET_ERROR) {
|
||||
syslog(LOG_ERR, "bind(): %m\n");
|
||||
if (srv->sourceAddrInfo) {
|
||||
if (bind(cnx->local.fd, srv->sourceAddrInfo->ai_addr,
|
||||
srv->sourceAddrInfo->ai_addrlen) == SOCKET_ERROR) {
|
||||
syslog(LOG_ERR, "bind(): %m\n");
|
||||
}
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof(struct sockaddr_in));
|
||||
saddr.sin_family = AF_INET;
|
||||
memcpy(&saddr.sin_addr, &srv->localAddr, sizeof(struct in_addr));
|
||||
saddr.sin_port = srv->localPort;
|
||||
if (connect(cnx->local.fd, (struct sockaddr *)&saddr,
|
||||
sizeof(struct sockaddr_in)) == SOCKET_ERROR)
|
||||
if (connect(cnx->local.fd, srv->toAddrInfo->ai_addr,
|
||||
srv->toAddrInfo->ai_addrlen) == SOCKET_ERROR)
|
||||
{
|
||||
if ((GetLastError() != WSAEINPROGRESS) &&
|
||||
(GetLastError() != WSAEWOULDBLOCK))
|
||||
{
|
||||
PERROR("rinetd: connect");
|
||||
closesocket(cnx->local.fd);
|
||||
if (cnx->remote.proto == protoTcp)
|
||||
if (cnx->remote.protocol == IPPROTO_TCP)
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
cnx->local.fd = INVALID_SOCKET;
|
||||
@ -781,16 +787,15 @@ static void handleAccept(ServerInfo const *srv)
|
||||
}
|
||||
|
||||
/* Send a zero-size UDP packet to simulate a connection */
|
||||
if (srv->toProto == protoUdp) {
|
||||
if (srv->toAddrInfo->ai_protocol == IPPROTO_UDP) {
|
||||
int got = sendto(cnx->local.fd, NULL, 0, 0,
|
||||
(struct sockaddr const *)&saddr,
|
||||
(SOCKLEN_T)sizeof(saddr));
|
||||
srv->toAddrInfo->ai_addr, srv->toAddrInfo->ai_addrlen);
|
||||
/* FIXME: we ignore errors here... is it safe? */
|
||||
(void)got;
|
||||
}
|
||||
|
||||
/* Send UDP data to the other socket */
|
||||
if (srv->fromProto == protoUdp) {
|
||||
if (srv->fromAddrInfo->ai_protocol == IPPROTO_UDP) {
|
||||
handleUdpRead(cnx, globalUdpBuffer, udpBytes);
|
||||
}
|
||||
|
||||
@ -809,7 +814,10 @@ static void handleAccept(ServerInfo const *srv)
|
||||
static int checkConnectionAllowed(ConnectionInfo const *cnx)
|
||||
{
|
||||
ServerInfo const *srv = cnx->server;
|
||||
char const *addressText = inet_ntoa(cnx->remoteAddress.sin_addr);
|
||||
|
||||
char addressText[NI_MAXHOST];
|
||||
getnameinfo((struct sockaddr *)&cnx->remoteAddress, sizeof(cnx->remoteAddress),
|
||||
addressText, sizeof(addressText), NULL, 0, NI_NUMERICHOST);
|
||||
|
||||
/* 1. Check global allow rules. If there are no
|
||||
global allow rules, it's presumed OK at
|
||||
@ -865,56 +873,6 @@ static int checkConnectionAllowed(ConnectionInfo const *cnx)
|
||||
return logAllowed;
|
||||
}
|
||||
|
||||
static int getAddress(char const *host, struct in_addr *iaddr)
|
||||
{
|
||||
/* If this is an IP address, use inet_addr() */
|
||||
int is_ipaddr = 1;
|
||||
for (char const *p = host; *p; ++p) {
|
||||
if (!isdigit(*p) && *p != '.') {
|
||||
is_ipaddr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_ipaddr) {
|
||||
iaddr->s_addr = inet_addr(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, use gethostbyname() */
|
||||
struct hostent *h = gethostbyname(host);
|
||||
if (h) {
|
||||
#ifdef h_addr
|
||||
memcpy(&iaddr->s_addr, h->h_addr, 4);
|
||||
#else
|
||||
memcpy(&iaddr->s_addr, h->h_addr_list[0], 4);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
char const *msg = "(unknown DNS error)";
|
||||
switch (h_errno)
|
||||
{
|
||||
case HOST_NOT_FOUND:
|
||||
msg = "The specified host is unknown.";
|
||||
break;
|
||||
#ifdef NO_DATA
|
||||
case NO_DATA:
|
||||
#else
|
||||
case NO_ADDRESS:
|
||||
#endif
|
||||
msg = "The requested name is valid but does not have an IP address.";
|
||||
break;
|
||||
case NO_RECOVERY:
|
||||
msg = "A non-recoverable name server error occurred.";
|
||||
break;
|
||||
case TRY_AGAIN:
|
||||
msg = "A temporary error occurred on an authoritative name server. Try again later.";
|
||||
break;
|
||||
}
|
||||
syslog(LOG_ERR, "While resolving `%s' got: %s\n", host, msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if !HAVE_SIGACTION && !_WIN32
|
||||
RETSIGTYPE plumber(int s)
|
||||
{
|
||||
@ -982,6 +940,7 @@ static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int resul
|
||||
thanks folks */
|
||||
int timz;
|
||||
char tstr[1024];
|
||||
char addressText[NI_MAXHOST] = { '?' };
|
||||
struct tm *t = get_gmtoff(&timz);
|
||||
char sign = (timz < 0 ? '-' : '+');
|
||||
if (timz < 0) {
|
||||
@ -989,10 +948,10 @@ static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int resul
|
||||
}
|
||||
strftime(tstr, sizeof(tstr), "%d/%b/%Y:%H:%M:%S ", t);
|
||||
|
||||
char const *addressText = "?";
|
||||
int64_t bytesOut = 0, bytesIn = 0;
|
||||
if (cnx != NULL) {
|
||||
addressText = inet_ntoa(cnx->remoteAddress.sin_addr);
|
||||
getnameinfo((struct sockaddr *)&cnx->remoteAddress, sizeof(cnx->remoteAddress),
|
||||
addressText, sizeof(addressText), NULL, 0, NI_NUMERICHOST);
|
||||
bytesOut = cnx->remote.totalBytesOut;
|
||||
bytesIn = cnx->remote.totalBytesIn;
|
||||
}
|
||||
@ -1001,9 +960,9 @@ static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int resul
|
||||
uint16_t fromPort = 0, toPort = 0;
|
||||
if (srv != NULL) {
|
||||
fromHost = srv->fromHost;
|
||||
fromPort = srv->fromPort;
|
||||
fromPort = getPort(srv->fromAddrInfo);
|
||||
toHost = srv->toHost;
|
||||
toPort = srv->toPort;
|
||||
toPort = getPort(srv->toAddrInfo);
|
||||
}
|
||||
|
||||
if (result==logNotAllowed || result==logDenied)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2019 Sam Hocevar <sam@hocevar.net>
|
||||
© 2003—2021 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
This software is released for free use under the terms of
|
||||
the GNU Public License, version 2 or higher. NO WARRANTY
|
||||
@ -46,7 +46,6 @@ extern FILE *logFile;
|
||||
|
||||
/* Functions */
|
||||
|
||||
void addServer(char *bindAddress, uint16_t bindPort, protocolType bindProto,
|
||||
char *connectAddress, uint16_t connectPort, protocolType connectProto,
|
||||
void addServer(char *bindAddress, char *bindPort, int bindProtocol,
|
||||
char *connectAddress, char *connectPort, int connectProtocol,
|
||||
int serverTimeout, char *sourceAddress);
|
||||
|
||||
|
20
src/types.h
20
src/types.h
@ -1,6 +1,6 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2019 Sam Hocevar <sam@hocevar.net>
|
||||
© 2003—2021 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
This software is released for free use under the terms of
|
||||
the GNU Public License, version 2 or higher. NO WARRANTY
|
||||
@ -17,12 +17,6 @@ enum _rule_type {
|
||||
denyRule,
|
||||
};
|
||||
|
||||
typedef enum _protocol_type protocolType;
|
||||
enum _protocol_type {
|
||||
protoTcp = 1,
|
||||
protoUdp = 2,
|
||||
};
|
||||
|
||||
typedef struct _rule Rule;
|
||||
struct _rule
|
||||
{
|
||||
@ -35,14 +29,10 @@ struct _server_info {
|
||||
SOCKET fd;
|
||||
|
||||
/* In network order, for network purposes */
|
||||
struct in_addr localAddr;
|
||||
uint16_t localPort;
|
||||
struct in_addr sourceAddr;
|
||||
struct addrinfo *fromAddrInfo, *toAddrInfo, *sourceAddrInfo;
|
||||
|
||||
/* In ASCII and local byte order, for logging purposes */
|
||||
/* In ASCII, for logging purposes */
|
||||
char *fromHost, *toHost;
|
||||
int16_t fromPort, toPort;
|
||||
protocolType fromProto, toProto;
|
||||
|
||||
/* Offset and count into list of allow and deny rules. Any rules
|
||||
prior to globalAllowRules and globalDenyRules are global rules. */
|
||||
@ -56,7 +46,7 @@ typedef struct _socket Socket;
|
||||
struct _socket
|
||||
{
|
||||
SOCKET fd;
|
||||
protocolType proto;
|
||||
int family, protocol;
|
||||
/* recv: received on this socket
|
||||
sent: sent through this socket from the other buffer */
|
||||
int recvPos, sentPos;
|
||||
@ -68,7 +58,7 @@ typedef struct _connection_info ConnectionInfo;
|
||||
struct _connection_info
|
||||
{
|
||||
Socket remote, local;
|
||||
struct sockaddr_in remoteAddress;
|
||||
struct sockaddr_storage remoteAddress;
|
||||
time_t remoteTimeout;
|
||||
int coClosing;
|
||||
int coLog;
|
||||
|
Loading…
Reference in New Issue
Block a user