mirror of
https://github.com/samhocevar/rinetd.git
synced 2025-03-14 20:00:11 +08:00
Merge branch 'feature-udp-support'. Closes #8.
This commit is contained in:
commit
f64f42ba81
@ -8,12 +8,12 @@ man_MANS = rinetd.8
|
||||
sysconf_DATA = rinetd.conf
|
||||
|
||||
sbin_PROGRAMS = rinetd
|
||||
rinetd_SOURCES = rinetd.c rinetd.h parse.h match.c match.h
|
||||
rinetd_SOURCES = rinetd.c rinetd.h parse.c parse.h match.c match.h net.c net.h
|
||||
|
||||
BUILT_SOURCES = parse.h
|
||||
BUILT_SOURCES = parse.c
|
||||
|
||||
# If peg/leg is available, regenerate parse.h
|
||||
parse.h: parse.peg
|
||||
parse.c: parse.peg
|
||||
if which leg >/dev/null 2>&1; then leg -o $@ $^; else touch $@; fi
|
||||
|
||||
# _POSIX_C_SOURCE is for SA_RESTART and others
|
||||
|
10
match.c
10
match.c
@ -1,5 +1,13 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 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
|
||||
IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
13
match.h
13
match.h
@ -1,9 +1,14 @@
|
||||
#ifndef MATCH_H
|
||||
#define MATCH_H 1
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 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
|
||||
IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
#pragma once
|
||||
|
||||
extern int match(char const *s, char const *p);
|
||||
extern int matchNoCase(char const *s, char const *p);
|
||||
extern int matchBody(char const *s, char const *p, int nocase);
|
||||
|
||||
#endif /* MATCH_H */
|
||||
|
||||
|
35
net.c
Normal file
35
net.c
Normal file
@ -0,0 +1,35 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 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
|
||||
IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "net.h"
|
||||
|
||||
void setSocketDefaults(SOCKET fd)
|
||||
{
|
||||
/* Make socket non-blocking (FIXME: this uses legacy API) */
|
||||
FIONBIO_ARG_T ioctltmp = 1;
|
||||
#if _WIN32
|
||||
ioctlsocket(fd, FIONBIO, &ioctltmp);
|
||||
#else
|
||||
ioctl(fd, FIONBIO, &ioctltmp);
|
||||
#endif
|
||||
|
||||
#if defined __linux__
|
||||
int tmp = 0;
|
||||
setsockopt(fd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
|
||||
#endif
|
||||
|
||||
#if !defined __linux__ && !defined _WIN32
|
||||
int tmp = 1024;
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp));
|
||||
#endif
|
||||
}
|
||||
|
74
net.h
Normal file
74
net.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 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
|
||||
IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#if _WIN32
|
||||
/* Define this to a reasonably large value */
|
||||
# define FD_SETSIZE 4096
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <netdb.h>
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#if defined HAVE_ERRNO_H
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#if defined HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* We've got to get FIONBIO from somewhere. Try the Solaris location
|
||||
if it isn't defined yet by the above includes. */
|
||||
#ifndef FIONBIO
|
||||
# include <sys/filio.h>
|
||||
#endif /* FIONBIO */
|
||||
|
||||
#if HAVE_SOCKLEN_T
|
||||
# define SOCKLEN_T socklen_t
|
||||
#else
|
||||
# define SOCKLEN_T int
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
# define FIONBIO_ARG_T u_long
|
||||
#else
|
||||
# define FIONBIO_ARG_T int
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
/* _WIN32 doesn't really have WSAEAGAIN */
|
||||
# ifndef WSAEAGAIN
|
||||
# define WSAEAGAIN WSAEWOULDBLOCK
|
||||
# endif
|
||||
#else
|
||||
/* Windows sockets compatibility defines */
|
||||
# define INVALID_SOCKET (-1)
|
||||
# define SOCKET_ERROR (-1)
|
||||
static inline int closesocket(int s) {
|
||||
return close(s);
|
||||
}
|
||||
# define WSAEWOULDBLOCK EWOULDBLOCK
|
||||
# define WSAEAGAIN EAGAIN
|
||||
# define WSAEINPROGRESS EINPROGRESS
|
||||
# define WSAEINTR EINTR
|
||||
# define SOCKET int
|
||||
static inline int GetLastError(void) {
|
||||
return errno;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
void setSocketDefaults(SOCKET fd);
|
||||
|
203
parse.peg
203
parse.peg
@ -1,123 +1,86 @@
|
||||
# Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
# and Boutell.Com, Inc.
|
||||
# © 2003—2017 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
|
||||
# IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
%{
|
||||
#if HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "net.h"
|
||||
#include "types.h"
|
||||
#include "rinetd.h"
|
||||
#include "parse.h"
|
||||
|
||||
#define YY_CTX_LOCAL 1
|
||||
#define YY_CTX_MEMBERS \
|
||||
FILE *fp; \
|
||||
int currentLine; \
|
||||
int isAuthAllow; \
|
||||
unsigned int port, bindPort, connectPort; \
|
||||
char *bindAddress, *connectAddress;
|
||||
FILE *fp; \
|
||||
int currentLine; \
|
||||
int isAuthAllow; \
|
||||
char *tmpPort; \
|
||||
int tmpPortNum, tmpProto; \
|
||||
int bindPortNum, bindProto, connectPortNum, connectProto; \
|
||||
int serverTimeout; \
|
||||
char *bindAddress, *connectAddress;
|
||||
#define YY_INPUT(yyctx, buf, result, max_size) \
|
||||
{ \
|
||||
int yyc = fgetc(yyctx->fp); \
|
||||
result = (EOF == yyc) ? 0 : (*(buf) = yyc, 1); \
|
||||
int yyc = fgetc(yyctx->fp); \
|
||||
result = (EOF == yyc) ? 0 : (*(buf) = yyc, 1); \
|
||||
}
|
||||
#define PARSE_ERROR exit(1);
|
||||
|
||||
#if defined __clang__
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wunused-label"
|
||||
#elif defined __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wunused-label"
|
||||
#endif
|
||||
|
||||
%}
|
||||
|
||||
file = (sol (line eol | invalid_syntax))*
|
||||
line = -? (rule | auth | logfile | pidlogfile | logcommon )? -? comment?
|
||||
|
||||
file = (sol (line eol | invalid-syntax))*
|
||||
line = -? (command -?)? comment?
|
||||
command = (server-rule | auth-rule | logfile | pidlogfile | logcommon)
|
||||
comment = "#" (!eol .)*
|
||||
|
||||
rule = bind_address - bind_port - connect_address - connect_port {
|
||||
/* Turn all of this stuff into reasonable addresses */
|
||||
struct in_addr iaddr;
|
||||
if (getAddress(yy->bindAddress, &iaddr) < 0) {
|
||||
fprintf(stderr, "rinetd: host %s could not be resolved.\n",
|
||||
yy->bindAddress);
|
||||
PARSE_ERROR;
|
||||
}
|
||||
/* Make a server socket */
|
||||
SOCKET fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (fd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "couldn't create "
|
||||
"server socket! (%m)\n");
|
||||
PARSE_ERROR;
|
||||
}
|
||||
struct sockaddr_in saddr;
|
||||
saddr.sin_family = AF_INET;
|
||||
memcpy(&saddr.sin_addr, &iaddr, sizeof(iaddr));
|
||||
saddr.sin_port = htons(yy->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",
|
||||
yy->bindAddress, yy->bindPort);
|
||||
closesocket(fd);
|
||||
PARSE_ERROR;
|
||||
}
|
||||
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",
|
||||
yy->bindAddress, yy->bindPort);
|
||||
closesocket(fd);
|
||||
PARSE_ERROR;
|
||||
}
|
||||
#if _WIN32
|
||||
u_long ioctltmp;
|
||||
#else
|
||||
int ioctltmp;
|
||||
#endif
|
||||
ioctlsocket(fd, FIONBIO, &ioctltmp);
|
||||
if (getAddress(yy->connectAddress, &iaddr) < 0) {
|
||||
/* Warn -- don't exit. */
|
||||
syslog(LOG_ERR, "host %s could not be resolved.\n",
|
||||
yy->bindAddress);
|
||||
closesocket(fd);
|
||||
PARSE_ERROR;
|
||||
}
|
||||
/* Allocate server info */
|
||||
seInfo = (ServerInfo *)
|
||||
realloc(seInfo, sizeof(ServerInfo) * (seTotal + 1));
|
||||
if (!seInfo) {
|
||||
PARSE_ERROR;
|
||||
}
|
||||
ServerInfo *srv = &seInfo[seTotal];
|
||||
memset(srv, 0, sizeof(*srv));
|
||||
srv->fd = fd;
|
||||
srv->localAddr = iaddr;
|
||||
srv->localPort = htons(yy->connectPort);
|
||||
srv->fromHost = yy->bindAddress;
|
||||
if (!srv->fromHost) {
|
||||
PARSE_ERROR;
|
||||
}
|
||||
srv->fromPort = yy->bindPort;
|
||||
srv->toHost = yy->connectAddress;
|
||||
if (!srv->toHost) {
|
||||
PARSE_ERROR;
|
||||
}
|
||||
srv->toPort = yy->connectPort;
|
||||
#ifndef _WIN32
|
||||
if (fd > maxfd) {
|
||||
maxfd = fd;
|
||||
}
|
||||
#endif
|
||||
++seTotal;
|
||||
|
||||
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,
|
||||
yy->serverTimeout > 0 ? yy->serverTimeout : RINETD_DEFAULT_UDP_TIMEOUT);
|
||||
yy->bindAddress = yy->connectAddress = NULL;
|
||||
yy->serverTimeout = 0;
|
||||
}
|
||||
|
||||
bind_address = < ipv4 > { yy->bindAddress = strdup(yytext); }
|
||||
connect_address = < ipv4 > { yy->connectAddress = strdup(yytext); }
|
||||
bind_port = port { yy->bindPort = yy->port; }
|
||||
connect_port = port { yy->connectPort = yy->port; }
|
||||
bind-address = < ipv4 > { yy->bindAddress = strdup(yytext); }
|
||||
connect-address = < ipv4 > { 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; }
|
||||
server-options = ("timeout" -? "=" -? < number >) { yy->serverTimeout = atoi(yytext); }
|
||||
|
||||
port = < (number | service) > {
|
||||
struct servent *bindService = getservbyname(yytext, "tcp");
|
||||
yy->port = bindService ? ntohs(bindService->s_port) : atoi(yytext);
|
||||
if (yy->port == 0 || yy->port >= 65536) {
|
||||
syslog(LOG_ERR, "bind port missing or out of range\n");
|
||||
full-port = port proto
|
||||
{
|
||||
char const *proto = yy->tmpProto == protoTcp ? "tcp" : "udp";
|
||||
struct servent *service = getservbyname(yy->tmpPort, proto);
|
||||
yy->tmpPortNum = service ? ntohs(service->s_port) : atoi(yy->tmpPort);
|
||||
if (yy->tmpPortNum == 0 || yy->tmpPortNum >= 65536) {
|
||||
syslog(LOG_ERR, "port %s/%s missing or out of range\n", yy->tmpPort, proto);
|
||||
PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth_key - < pattern > {
|
||||
port = < (number | service) > { yy->tmpPort = strdup(yytext); }
|
||||
proto = '/tcp' { yy->tmpProto = protoTcp; }
|
||||
| '/udp' { yy->tmpProto = protoUdp; }
|
||||
| '' { yy->tmpProto = protoTcp; }
|
||||
|
||||
auth-rule = auth-key - < pattern >
|
||||
{
|
||||
allRules = (Rule *)
|
||||
realloc(allRules, sizeof(Rule) * (allRulesCount + 1));
|
||||
if (!allRules) {
|
||||
@ -139,27 +102,31 @@ auth = auth_key - < pattern > {
|
||||
++allRulesCount;
|
||||
}
|
||||
|
||||
auth_key = < ("allow" | "deny") > { yy->isAuthAllow = (yytext[0] == 'a'); }
|
||||
auth-key = < ("allow" | "deny") > { yy->isAuthAllow = (yytext[0] == 'a'); }
|
||||
|
||||
logfile = "logfile" - < filename > {
|
||||
logfile = "logfile" - < filename >
|
||||
{
|
||||
logFileName = strdup(yytext);
|
||||
if (!logFileName) {
|
||||
PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pidlogfile = "pidlogfile" - < filename > {
|
||||
pidlogfile = "pidlogfile" - < filename >
|
||||
{
|
||||
pidLogFileName = strdup(yytext);
|
||||
if (!pidLogFileName) {
|
||||
PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
logcommon = "logcommon" {
|
||||
logcommon = "logcommon"
|
||||
{
|
||||
logFormatCommon = 1;
|
||||
}
|
||||
|
||||
invalid_syntax = < (!eol .)+ > eol {
|
||||
invalid_syntax = < (!eol .)+ > eol
|
||||
{
|
||||
fprintf(stderr, "rinetd: invalid syntax at line %d: %s\n",
|
||||
yy->currentLine, yytext);
|
||||
PARSE_ERROR; /* FIXME */
|
||||
@ -180,3 +147,27 @@ sol = { ++yy->currentLine; }
|
||||
eol = '\r'? '\n' | eof
|
||||
eof = '\0'
|
||||
|
||||
%%
|
||||
|
||||
void parseConfiguration(char const *file)
|
||||
{
|
||||
FILE *in = fopen(file, "r");
|
||||
if (!in) {
|
||||
PARSE_ERROR;
|
||||
}
|
||||
|
||||
yycontext ctx;
|
||||
memset(&ctx, 0, sizeof(yycontext));
|
||||
ctx.fp = in;
|
||||
if (!yyparse(&ctx)) {
|
||||
syslog(LOG_ERR, "invalid syntax "
|
||||
"on file %s, line %d.\n", file, -1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
|
||||
/* Avopid warnings for these unused functions */
|
||||
(void)yySet; (void)yyPush; (void)yyPop; (void)yyAccept;
|
||||
}
|
||||
|
||||
|
501
rinetd.c
501
rinetd.c
@ -1,3 +1,11 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 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
|
||||
IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
@ -7,27 +15,9 @@
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
/* Define this to a reasonably large value */
|
||||
# define FD_SETSIZE 4096
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
# include "getopt.h"
|
||||
# define syslog fprintf
|
||||
# define LOG_ERR stderr
|
||||
# define LOG_INFO stdout
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <unistd.h>
|
||||
# include <netdb.h>
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <getopt.h>
|
||||
# include <errno.h>
|
||||
# include <syslog.h>
|
||||
# define INVALID_SOCKET (-1)
|
||||
# define SOCKET_ERROR (-1)
|
||||
# if TIME_WITH_SYS_TIME
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
@ -46,43 +36,17 @@
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
#if _WIN32
|
||||
/* _WIN32 doesn't really have WSAEAGAIN */
|
||||
# ifndef WSAEAGAIN
|
||||
# define WSAEAGAIN WSAEWOULDBLOCK
|
||||
# endif
|
||||
#else
|
||||
/* Windows sockets compatibility defines */
|
||||
# define INVALID_SOCKET (-1)
|
||||
# define SOCKET_ERROR (-1)
|
||||
static inline int closesocket(int s) {
|
||||
return close(s);
|
||||
}
|
||||
# define ioctlsocket ioctl
|
||||
# define WSAEWOULDBLOCK EWOULDBLOCK
|
||||
# define WSAEAGAIN EAGAIN
|
||||
# define WSAEINPROGRESS EINPROGRESS
|
||||
# define WSAEINTR EINTR
|
||||
# define SOCKET int
|
||||
static inline int GetLastError(void) {
|
||||
return errno;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#ifdef DEBUG
|
||||
# define PERROR perror
|
||||
#else
|
||||
# define PERROR(x)
|
||||
#endif /* DEBUG */
|
||||
|
||||
/* We've got to get FIONBIO from somewhere. Try the Solaris location
|
||||
if it isn't defined yet by the above includes. */
|
||||
#ifndef FIONBIO
|
||||
# include <sys/filio.h>
|
||||
#endif /* FIONBIO */
|
||||
|
||||
#include "match.h"
|
||||
#include "net.h"
|
||||
#include "types.h"
|
||||
#include "rinetd.h"
|
||||
#include "parse.h"
|
||||
|
||||
Rule *allRules = NULL;
|
||||
int allRulesCount = 0;
|
||||
@ -121,8 +85,7 @@ char const *logMessages[] = {
|
||||
"denied",
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
logUnknownError = 0,
|
||||
logLocalClosedFirst,
|
||||
logRemoteClosedFirst,
|
||||
@ -131,6 +94,7 @@ enum
|
||||
logLocalBindFailed,
|
||||
logLocalConnectFailed,
|
||||
logOpened,
|
||||
logAllowed,
|
||||
logNotAllowed,
|
||||
logDenied,
|
||||
};
|
||||
@ -148,10 +112,9 @@ 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 void refuse(ConnectionInfo *cnx, int logCode);
|
||||
static int checkConnectionAllowed(ConnectionInfo const *cnx);
|
||||
|
||||
static int readArgs (int argc, char **argv, RinetdOptions *options);
|
||||
static int getConfLine(FILE *in, char *line, int space, int *lnum);
|
||||
static void clearConfiguration(void);
|
||||
static void readConfiguration(char const *file);
|
||||
|
||||
@ -159,8 +122,6 @@ static void registerPID(char const *pid_file_name);
|
||||
static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int result);
|
||||
static struct tm *get_gmtoff(int *tz);
|
||||
|
||||
#include "parse.h"
|
||||
|
||||
/* Signal handlers */
|
||||
#if !HAVE_SIGACTION && !_WIN32
|
||||
static RETSIGTYPE plumber(int s);
|
||||
@ -220,7 +181,7 @@ int main(int argc, char *argv[])
|
||||
registerPID(pidLogFileName ? pidLogFileName : RINETD_PID_FILE);
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "Starting redirections...");
|
||||
syslog(LOG_INFO, "Starting redirections...\n");
|
||||
while (1) {
|
||||
selectPass();
|
||||
}
|
||||
@ -265,19 +226,7 @@ static void clearConfiguration(void) {
|
||||
static void readConfiguration(char const *file) {
|
||||
|
||||
/* Parse the configuration file. */
|
||||
FILE *in = fopen(file, "r");
|
||||
if (!in) {
|
||||
goto lowMemory;
|
||||
}
|
||||
yycontext ctx;
|
||||
memset(&ctx, 0, sizeof(yycontext));
|
||||
ctx.fp = in;
|
||||
if (!yyparse(&ctx)) {
|
||||
syslog(LOG_ERR, "invalid syntax "
|
||||
"on file %s, line %d.\n", file, -1);
|
||||
exit(1);
|
||||
}
|
||||
fclose(in);
|
||||
parseConfiguration(file);
|
||||
|
||||
/* Open the log file */
|
||||
if (logFile) {
|
||||
@ -293,33 +242,94 @@ static void readConfiguration(char const *file) {
|
||||
logFileName);
|
||||
}
|
||||
}
|
||||
return;
|
||||
lowMemory:
|
||||
syslog(LOG_ERR, "not enough memory to start rinetd.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int getConfLine(FILE *in, char *line, int space, int *lnum)
|
||||
{
|
||||
while (1) {
|
||||
(*lnum)++;
|
||||
if (!fgets(line, space, in)) {
|
||||
return 0;
|
||||
}
|
||||
char const *p = line;
|
||||
while (isspace(*p)) {
|
||||
p++;
|
||||
}
|
||||
if (!(*p)) {
|
||||
/* Blank lines are OK */
|
||||
continue;
|
||||
}
|
||||
if (*p == '#') {
|
||||
/* Comment lines are also OK */
|
||||
continue;
|
||||
}
|
||||
return 1;
|
||||
void addServer(char *bindAddress, int bindPort, int bindProto,
|
||||
char *connectAddress, int connectPort, int connectProto,
|
||||
int serverTimeout) {
|
||||
/* 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);
|
||||
}
|
||||
/* 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, bindPort);
|
||||
closesocket(fd);
|
||||
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, bindPort);
|
||||
closesocket(fd);
|
||||
}
|
||||
|
||||
setSocketDefaults(fd);
|
||||
}
|
||||
|
||||
if (getAddress(connectAddress, &iaddr) < 0) {
|
||||
/* Warn -- don't exit. */
|
||||
syslog(LOG_ERR, "host %s could not be resolved.\n",
|
||||
bindAddress);
|
||||
closesocket(fd);
|
||||
exit(1);
|
||||
}
|
||||
/* Allocate server info */
|
||||
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->toHost = connectAddress;
|
||||
if (!srv->toHost) {
|
||||
exit(1);
|
||||
}
|
||||
srv->toPort = connectPort;
|
||||
srv->toProto = connectProto;
|
||||
srv->serverTimeout = serverTimeout;
|
||||
#ifndef _WIN32
|
||||
if (fd > maxfd) {
|
||||
maxfd = fd;
|
||||
}
|
||||
#endif
|
||||
++seTotal;
|
||||
}
|
||||
|
||||
static void setConnectionCount(int newCount)
|
||||
@ -333,7 +343,8 @@ static void setConnectionCount(int newCount)
|
||||
closesocket(coInfo[i].local.fd);
|
||||
}
|
||||
if (coInfo[i].remote.fd != INVALID_SOCKET) {
|
||||
closesocket(coInfo[i].remote.fd);
|
||||
if (coInfo[i].remote.proto == protoTcp)
|
||||
closesocket(coInfo[i].remote.fd);
|
||||
}
|
||||
free(coInfo[i].local.buffer);
|
||||
}
|
||||
@ -396,8 +407,8 @@ static ConnectionInfo *findAvailableConnection(void)
|
||||
return &coInfo[oldTotal];
|
||||
}
|
||||
|
||||
static void selectPass(void) {
|
||||
|
||||
static void selectPass(void)
|
||||
{
|
||||
int const fdSetCount = maxfd / FD_SETSIZE + 1;
|
||||
# define FD_ZERO_EXT(ar) for (int i = 0; i < fdSetCount; ++i) { FD_ZERO(&(ar)[i]); }
|
||||
#ifdef _WIN32
|
||||
@ -409,6 +420,11 @@ static void selectPass(void) {
|
||||
# define FD_ISSET_EXT(fd, ar) FD_ISSET((fd) % FD_SETSIZE, &(ar)[(fd) / FD_SETSIZE])
|
||||
#endif
|
||||
|
||||
/* Timeout value -- infinite by default */
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = timeout.tv_usec = 0;
|
||||
time_t now = time(NULL);
|
||||
|
||||
fd_set readfds[fdSetCount], writefds[fdSetCount];
|
||||
FD_ZERO_EXT(readfds);
|
||||
FD_ZERO_EXT(writefds);
|
||||
@ -437,6 +453,13 @@ static void selectPass(void) {
|
||||
/* Get more input if we have room for it */
|
||||
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) {
|
||||
long delay = (long)(cnx->remoteTimeout - now);
|
||||
timeout.tv_sec = delay <= 1 ? 1
|
||||
: (delay < timeout.tv_sec || timeout.tv_sec == 0) ? delay
|
||||
: timeout.tv_sec;
|
||||
}
|
||||
}
|
||||
/* Send more output if we have any, or if we’re closing */
|
||||
if (cnx->remote.sentPos < cnx->local.recvPos || cnx->coClosing) {
|
||||
@ -444,12 +467,21 @@ static void selectPass(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
select(maxfd + 1, readfds, writefds, 0, 0);
|
||||
|
||||
select(maxfd + 1, readfds, writefds, 0, timeout.tv_sec ? &timeout : NULL);
|
||||
for (int i = 0; i < coTotal; ++i) {
|
||||
ConnectionInfo *cnx = &coInfo[i];
|
||||
if (cnx->remote.fd != INVALID_SOCKET) {
|
||||
if (FD_ISSET_EXT(cnx->remote.fd, readfds)) {
|
||||
handleRead(cnx, &cnx->remote, &cnx->local);
|
||||
/* Do not read on remote UDP sockets, the server does it,
|
||||
but handle timeouts instead. */
|
||||
if (cnx->remote.proto == protoTcp) {
|
||||
if (FD_ISSET_EXT(cnx->remote.fd, readfds)) {
|
||||
handleRead(cnx, &cnx->remote, &cnx->local);
|
||||
}
|
||||
} else {
|
||||
if (now > cnx->remoteTimeout) {
|
||||
handleClose(cnx, &cnx->remote, &cnx->local);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cnx->remote.fd != INVALID_SOCKET) {
|
||||
@ -508,12 +540,22 @@ 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);
|
||||
closesocket(socket->fd);
|
||||
if (socket->proto == protoTcp)
|
||||
closesocket(socket->fd);
|
||||
socket->fd = INVALID_SOCKET;
|
||||
return;
|
||||
}
|
||||
int got = send(socket->fd, other_socket->buffer + socket->sentPos,
|
||||
other_socket->recvPos - socket->sentPos, 0);
|
||||
|
||||
struct sockaddr const *addr = NULL;
|
||||
SOCKLEN_T addrlen = 0;
|
||||
if (socket->proto == protoUdp && socket == &cnx->remote) {
|
||||
addr = (struct sockaddr const*)&cnx->remoteAddress;
|
||||
addrlen = (SOCKLEN_T)sizeof(cnx->remoteAddress);
|
||||
}
|
||||
|
||||
int got = sendto(socket->fd, other_socket->buffer + socket->sentPos,
|
||||
other_socket->recvPos - socket->sentPos, 0,
|
||||
addr, addrlen);
|
||||
if (got < 0) {
|
||||
if (GetLastError() == WSAEWOULDBLOCK) {
|
||||
return;
|
||||
@ -534,12 +576,15 @@ static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socket)
|
||||
{
|
||||
cnx->coClosing = 1;
|
||||
/* One end fizzled out, so make sure we're all done with that */
|
||||
closesocket(socket->fd);
|
||||
if (socket->proto == protoTcp) {
|
||||
/* One end fizzled out, so make sure we're all done with that */
|
||||
closesocket(socket->fd);
|
||||
} else /* if (socket->proto == protoUdp) */ {
|
||||
/* Nothing to do in UDP mode */
|
||||
}
|
||||
socket->fd = INVALID_SOCKET;
|
||||
if (other_socket->fd != INVALID_SOCKET) {
|
||||
#ifndef __linux__
|
||||
#ifndef _WIN32
|
||||
#if !defined __linux__ && !defined _WIN32
|
||||
/* Now set up the other end for a polite closing */
|
||||
|
||||
/* Request a low-water mark equal to the entire
|
||||
@ -548,8 +593,7 @@ static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
int arg = 1024;
|
||||
setsockopt(other_socket->fd, SOL_SOCKET, SO_SNDLOWAT,
|
||||
&arg, sizeof(arg));
|
||||
#endif /* _WIN32 */
|
||||
#endif /* __linux__ */
|
||||
#endif
|
||||
cnx->coLog = socket == &cnx->local ?
|
||||
logLocalClosedFirst : logRemoteClosedFirst;
|
||||
}
|
||||
@ -557,112 +601,96 @@ static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socke
|
||||
|
||||
static void handleAccept(ServerInfo const *srv)
|
||||
{
|
||||
struct sockaddr addr;
|
||||
SOCKLEN_T addrlen = sizeof(addr);
|
||||
|
||||
SOCKET nfd;
|
||||
if (srv->fromProto == protoTcp) {
|
||||
/* In TCP mode, get remote address using accept(). */
|
||||
nfd = accept(srv->fd, &addr, &addrlen);
|
||||
if (nfd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "accept(%d): %m\n", srv->fd);
|
||||
logEvent(NULL, srv, logAcceptFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
setSocketDefaults(nfd);
|
||||
} else /* if (srv->fromProto == protoUdp) */ {
|
||||
/* In UDP mode, get remote address using recvfrom() and check
|
||||
for an existing connection from this client. */
|
||||
nfd = srv->fd;
|
||||
ssize_t ret = recvfrom(srv->fd, NULL, 0, MSG_PEEK,
|
||||
&addr, &addrlen);
|
||||
if (ret < 0) {
|
||||
if (GetLastError() == WSAEWOULDBLOCK) {
|
||||
return;
|
||||
}
|
||||
if (GetLastError() == WSAEINPROGRESS) {
|
||||
return;
|
||||
}
|
||||
syslog(LOG_ERR, "recvfrom(%d): %m\n", srv->fd);
|
||||
logEvent(NULL, srv, logAcceptFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
cnx->remoteTimeout = time(NULL) + srv->serverTimeout;
|
||||
handleRead(cnx, &cnx->remote, &cnx->local);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionInfo *cnx = findAvailableConnection();
|
||||
if (!cnx) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr addr;
|
||||
struct in_addr address;
|
||||
#if HAVE_SOCKLEN_T
|
||||
socklen_t addrlen;
|
||||
#else
|
||||
int addrlen;
|
||||
#endif
|
||||
addrlen = sizeof(addr);
|
||||
SOCKET nfd = accept(srv->fd, &addr, &addrlen);
|
||||
if (nfd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "accept(%d): %m", srv->fd);
|
||||
logEvent(NULL, srv, logAcceptFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
u_long ioctltmp;
|
||||
#else
|
||||
int ioctltmp;
|
||||
#endif
|
||||
ioctlsocket(nfd, FIONBIO, &ioctltmp);
|
||||
|
||||
#ifndef _WIN32
|
||||
int tmp = 0;
|
||||
setsockopt(nfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
|
||||
#endif
|
||||
|
||||
cnx->local.fd = INVALID_SOCKET;
|
||||
cnx->local.proto = srv->toProto;
|
||||
cnx->local.recvPos = cnx->local.sentPos = 0;
|
||||
cnx->local.recvBytes = cnx->local.sentBytes = 0;
|
||||
|
||||
cnx->remote.fd = nfd;
|
||||
cnx->remote.proto = srv->fromProto;
|
||||
cnx->remote.recvPos = cnx->remote.sentPos = 0;
|
||||
cnx->remote.recvBytes = cnx->remote.sentBytes = 0;
|
||||
cnx->remoteAddress = *(struct sockaddr_in *)&addr;
|
||||
if (srv->fromProto == protoUdp)
|
||||
cnx->remoteTimeout = time(NULL) + srv->serverTimeout;
|
||||
|
||||
cnx->coClosing = 0;
|
||||
cnx->coLog = logUnknownError;
|
||||
cnx->server = srv;
|
||||
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) &addr;
|
||||
cnx->reAddresses.s_addr = address.s_addr = sin->sin_addr.s_addr;
|
||||
char const *addressText = inet_ntoa(address);
|
||||
int logCode = checkConnectionAllowed(cnx);
|
||||
if (logCode != logAllowed) {
|
||||
/* Local fd is not open yet, so only
|
||||
close the remote socket. */
|
||||
if (cnx->remote.proto == protoTcp)
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
logEvent(cnx, cnx->server, logCode);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 1. Check global allow rules. If there are no
|
||||
global allow rules, it's presumed OK at
|
||||
this step. If there are any, and it doesn't
|
||||
match at least one, kick it out. */
|
||||
int good = 1;
|
||||
for (int j = 0; j < globalRulesCount; ++j) {
|
||||
if (allRules[j].type == allowRule) {
|
||||
good = 0;
|
||||
if (match(addressText, allRules[j].pattern)) {
|
||||
good = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!good) {
|
||||
refuse(cnx, logNotAllowed);
|
||||
return;
|
||||
}
|
||||
/* 2. Check global deny rules. If it matches
|
||||
any of the global deny rules, kick it out. */
|
||||
for (int j = 0; j < globalRulesCount; ++j) {
|
||||
if (allRules[j].type == denyRule
|
||||
&& match(addressText, allRules[j].pattern)) {
|
||||
refuse(cnx, logDenied);
|
||||
}
|
||||
}
|
||||
/* 3. Check allow rules specific to this forwarding rule.
|
||||
If there are none, it's OK. If there are any,
|
||||
it must match at least one. */
|
||||
good = 1;
|
||||
for (int j = 0; j < srv->rulesCount; ++j) {
|
||||
if (allRules[srv->rulesStart + j].type == allowRule) {
|
||||
good = 0;
|
||||
if (match(addressText,
|
||||
allRules[srv->rulesStart + j].pattern)) {
|
||||
good = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!good) {
|
||||
refuse(cnx, logNotAllowed);
|
||||
return;
|
||||
}
|
||||
/* 4. Check deny rules specific to this forwarding rule. If
|
||||
it matches any of the deny rules, kick it out. */
|
||||
for (int j = 0; j < srv->rulesCount; ++j) {
|
||||
if (allRules[srv->rulesStart + j].type == denyRule
|
||||
&& match(addressText, allRules[srv->rulesStart + j].pattern)) {
|
||||
refuse(cnx, logDenied);
|
||||
}
|
||||
}
|
||||
/* 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 = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
cnx->local.fd = srv->toProto == protoTcp
|
||||
? socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||
: socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (cnx->local.fd == INVALID_SOCKET) {
|
||||
syslog(LOG_ERR, "socket(): %m");
|
||||
closesocket(cnx->remote.fd);
|
||||
syslog(LOG_ERR, "socket(): %m\n");
|
||||
if (cnx->remote.proto == protoTcp)
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
logEvent(cnx, srv, logLocalSocketFailed);
|
||||
return;
|
||||
@ -675,7 +703,8 @@ static void handleAccept(ServerInfo const *srv)
|
||||
saddr.sin_addr.s_addr = 0;
|
||||
if (bind(cnx->local.fd, (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR) {
|
||||
closesocket(cnx->local.fd);
|
||||
closesocket(cnx->remote.fd);
|
||||
if (cnx->remote.proto == protoTcp)
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
cnx->local.fd = INVALID_SOCKET;
|
||||
logEvent(cnx, srv, logLocalBindFailed);
|
||||
@ -688,18 +717,8 @@ static void handleAccept(ServerInfo const *srv)
|
||||
memcpy(&saddr.sin_addr, &srv->localAddr, sizeof(struct in_addr));
|
||||
saddr.sin_port = srv->localPort;
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef __linux__
|
||||
tmp = 0;
|
||||
setsockopt(cnx->local.fd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
|
||||
#else
|
||||
tmp = 1024;
|
||||
setsockopt(cnx->local.fd, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp));
|
||||
#endif /* __linux__ */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
ioctltmp = 1;
|
||||
ioctlsocket(cnx->local.fd, FIONBIO, &ioctltmp);
|
||||
if (srv->toProto == protoTcp)
|
||||
setSocketDefaults(cnx->local.fd);
|
||||
|
||||
if (connect(cnx->local.fd, (struct sockaddr *)&saddr,
|
||||
sizeof(struct sockaddr_in)) == SOCKET_ERROR)
|
||||
@ -709,7 +728,8 @@ static void handleAccept(ServerInfo const *srv)
|
||||
{
|
||||
PERROR("rinetd: connect");
|
||||
closesocket(cnx->local.fd);
|
||||
closesocket(cnx->remote.fd);
|
||||
if (cnx->remote.proto == protoTcp)
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
cnx->local.fd = INVALID_SOCKET;
|
||||
logEvent(cnx, srv, logLocalConnectFailed);
|
||||
@ -729,13 +749,63 @@ static void handleAccept(ServerInfo const *srv)
|
||||
logEvent(cnx, srv, logOpened);
|
||||
}
|
||||
|
||||
static void refuse(ConnectionInfo *cnx, int logCode)
|
||||
static int checkConnectionAllowed(ConnectionInfo const *cnx)
|
||||
{
|
||||
/* Local fd is not open yet when we refuse(), so only
|
||||
close the remote socket. */
|
||||
closesocket(cnx->remote.fd);
|
||||
cnx->remote.fd = INVALID_SOCKET;
|
||||
logEvent(cnx, cnx->server, logCode);
|
||||
ServerInfo const *srv = cnx->server;
|
||||
char const *addressText = inet_ntoa(cnx->remoteAddress.sin_addr);
|
||||
|
||||
/* 1. Check global allow rules. If there are no
|
||||
global allow rules, it's presumed OK at
|
||||
this step. If there are any, and it doesn't
|
||||
match at least one, kick it out. */
|
||||
int good = 1;
|
||||
for (int j = 0; j < globalRulesCount; ++j) {
|
||||
if (allRules[j].type == allowRule) {
|
||||
good = 0;
|
||||
if (match(addressText, allRules[j].pattern)) {
|
||||
good = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!good) {
|
||||
return logNotAllowed;
|
||||
}
|
||||
/* 2. Check global deny rules. If it matches
|
||||
any of the global deny rules, kick it out. */
|
||||
for (int j = 0; j < globalRulesCount; ++j) {
|
||||
if (allRules[j].type == denyRule
|
||||
&& match(addressText, allRules[j].pattern)) {
|
||||
return logDenied;
|
||||
}
|
||||
}
|
||||
/* 3. Check allow rules specific to this forwarding rule.
|
||||
If there are none, it's OK. If there are any,
|
||||
it must match at least one. */
|
||||
good = 1;
|
||||
for (int j = 0; j < srv->rulesCount; ++j) {
|
||||
if (allRules[srv->rulesStart + j].type == allowRule) {
|
||||
good = 0;
|
||||
if (match(addressText,
|
||||
allRules[srv->rulesStart + j].pattern)) {
|
||||
good = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!good) {
|
||||
return logNotAllowed;
|
||||
}
|
||||
/* 4. Check deny rules specific to this forwarding rule. If
|
||||
it matches any of the deny rules, kick it out. */
|
||||
for (int j = 0; j < srv->rulesCount; ++j) {
|
||||
if (allRules[srv->rulesStart + j].type == denyRule
|
||||
&& match(addressText, allRules[srv->rulesStart + j].pattern)) {
|
||||
return logDenied;
|
||||
}
|
||||
}
|
||||
|
||||
return logAllowed;
|
||||
}
|
||||
|
||||
static int getAddress(char const *host, struct in_addr *iaddr)
|
||||
@ -784,7 +854,7 @@ static int getAddress(char const *host, struct in_addr *iaddr)
|
||||
msg = "A temporary error occurred on an authoritative name server. Try again later.";
|
||||
break;
|
||||
}
|
||||
syslog(LOG_ERR, "While resolving `%s' got: %s", host, msg);
|
||||
syslog(LOG_ERR, "While resolving `%s' got: %s\n", host, msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -800,7 +870,7 @@ RETSIGTYPE plumber(int s)
|
||||
RETSIGTYPE hup(int s)
|
||||
{
|
||||
(void)s;
|
||||
syslog(LOG_INFO, "Received SIGHUP, reloading configuration...");
|
||||
syslog(LOG_INFO, "Received SIGHUP, reloading configuration...\n");
|
||||
/* Learn the new rules */
|
||||
clearConfiguration();
|
||||
readConfiguration(options.conf_file);
|
||||
@ -866,8 +936,7 @@ static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int resul
|
||||
int bytesOutput = 0;
|
||||
int bytesInput = 0;
|
||||
if (cnx != NULL) {
|
||||
struct in_addr const *reAddress = &cnx->reAddresses;
|
||||
addressText = inet_ntoa(*reAddress);
|
||||
addressText = inet_ntoa(cnx->remoteAddress.sin_addr);
|
||||
bytesOutput = cnx->remote.sentBytes;
|
||||
bytesInput = cnx->remote.recvBytes;
|
||||
}
|
||||
@ -884,7 +953,7 @@ static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int resul
|
||||
}
|
||||
|
||||
if (result==logNotAllowed || result==logDenied)
|
||||
syslog(LOG_INFO, "%s %s"
|
||||
syslog(LOG_INFO, "%s %s\n"
|
||||
, addressText
|
||||
, logMessages[result]);
|
||||
if (logFile) {
|
||||
|
@ -16,8 +16,10 @@
|
||||
# to apply to only that forwarding rule
|
||||
#
|
||||
# bindadress bindport connectaddress connectport
|
||||
127.0.0.1 4000 127.0.0.1 22
|
||||
#127.0.0.1 4000/udp 127.0.0.1 22
|
||||
#127.0.0.1 4000 127.0.0.1 3000
|
||||
#127.0.0.1 4000/udp 127.0.0.1 22
|
||||
127.0.0.1 4000 127.0.0.1 3000/udp
|
||||
127.0.0.1 3000/udp 127.0.0.1 22
|
||||
|
||||
# logging information
|
||||
logfile /var/log/rinetd.log
|
||||
|
91
rinetd.h
91
rinetd.h
@ -1,69 +1,50 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 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
|
||||
IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Syslog feature */
|
||||
|
||||
#if _WIN32
|
||||
# include <stdio.h>
|
||||
# define syslog fprintf
|
||||
# define LOG_ERR stderr
|
||||
# define LOG_INFO stdout
|
||||
#else
|
||||
# include <syslog.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/* Constants */
|
||||
|
||||
static int const RINETD_BUFFER_SIZE = 16384;
|
||||
static int const RINETD_LISTEN_BACKLOG = 128;
|
||||
static int const RINETD_DEFAULT_UDP_TIMEOUT = 72;
|
||||
|
||||
#define RINETD_CONFIG_FILE "/etc/rinetd.conf"
|
||||
#define RINETD_PID_FILE "/var/run/rinetd.pid"
|
||||
|
||||
/* Program state */
|
||||
/* Global configuration */
|
||||
|
||||
enum ruleType {
|
||||
allowRule,
|
||||
denyRule,
|
||||
};
|
||||
extern Rule *allRules;
|
||||
extern int allRulesCount;
|
||||
extern int globalRulesCount;
|
||||
|
||||
typedef struct _rule Rule;
|
||||
struct _rule
|
||||
{
|
||||
char *pattern;
|
||||
int type;
|
||||
};
|
||||
extern ServerInfo *seInfo;
|
||||
extern int seTotal;
|
||||
|
||||
typedef struct _server_info ServerInfo;
|
||||
struct _server_info {
|
||||
SOCKET fd;
|
||||
extern char *logFileName;
|
||||
extern char *pidLogFileName;
|
||||
extern int logFormatCommon;
|
||||
extern FILE *logFile;
|
||||
|
||||
/* In network order, for network purposes */
|
||||
struct in_addr localAddr;
|
||||
unsigned short localPort;
|
||||
/* Functions */
|
||||
|
||||
/* In ASCII and local byte order, for logging purposes */
|
||||
char *fromHost, *toHost;
|
||||
int fromPort, toPort;
|
||||
|
||||
/* Offset and count into list of allow and deny rules. Any rules
|
||||
prior to globalAllowRules and globalDenyRules are global rules. */
|
||||
int rulesStart, rulesCount;
|
||||
};
|
||||
|
||||
typedef struct _socket Socket;
|
||||
struct _socket
|
||||
{
|
||||
SOCKET fd;
|
||||
/* recv: received on this socket
|
||||
sent: sent to this socket from the other buffer */
|
||||
int recvPos, sentPos;
|
||||
int recvBytes, sentBytes;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
typedef struct _connection_info ConnectionInfo;
|
||||
struct _connection_info
|
||||
{
|
||||
Socket remote, local;
|
||||
struct in_addr reAddresses;
|
||||
int coClosing;
|
||||
int coLog;
|
||||
ServerInfo const *server; // only useful for logEvent
|
||||
};
|
||||
|
||||
/* Option parsing */
|
||||
|
||||
typedef struct _rinetd_options RinetdOptions;
|
||||
struct _rinetd_options
|
||||
{
|
||||
char const *conf_file;
|
||||
int foreground;
|
||||
};
|
||||
void addServer(char *bindAddress, int bindPort, int bindProto,
|
||||
char *connectAddress, int connectPort, int connectProto,
|
||||
int serverTimeout);
|
||||
|
||||
|
81
types.h
Normal file
81
types.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* Copyright © 1997—1999 Thomas Boutell <boutell@boutell.com>
|
||||
and Boutell.Com, Inc.
|
||||
© 2003—2017 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
|
||||
IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
|
||||
enum ruleType {
|
||||
allowRule,
|
||||
denyRule,
|
||||
};
|
||||
|
||||
enum protocolType {
|
||||
protoTcp = 1,
|
||||
protoUdp = 2,
|
||||
};
|
||||
|
||||
typedef struct _rule Rule;
|
||||
struct _rule
|
||||
{
|
||||
char *pattern;
|
||||
int type;
|
||||
};
|
||||
|
||||
typedef struct _server_info ServerInfo;
|
||||
struct _server_info {
|
||||
SOCKET fd;
|
||||
|
||||
/* In network order, for network purposes */
|
||||
struct in_addr localAddr;
|
||||
unsigned short localPort;
|
||||
|
||||
/* In ASCII and local byte order, for logging purposes */
|
||||
char *fromHost, *toHost;
|
||||
int fromPort, fromProto, toPort, toProto;
|
||||
|
||||
/* Offset and count into list of allow and deny rules. Any rules
|
||||
prior to globalAllowRules and globalDenyRules are global rules. */
|
||||
int rulesStart, rulesCount;
|
||||
/* Timeout for UDP traffic before we consider the connection
|
||||
was dropped by the remote host. */
|
||||
int serverTimeout;
|
||||
};
|
||||
|
||||
typedef struct _socket Socket;
|
||||
struct _socket
|
||||
{
|
||||
SOCKET fd;
|
||||
int proto;
|
||||
/* recv: received on this socket
|
||||
sent: sent to this socket from the other buffer */
|
||||
int recvPos, sentPos;
|
||||
int recvBytes, sentBytes;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
typedef struct _connection_info ConnectionInfo;
|
||||
struct _connection_info
|
||||
{
|
||||
Socket remote, local;
|
||||
struct sockaddr_in remoteAddress;
|
||||
time_t remoteTimeout;
|
||||
int coClosing;
|
||||
int coLog;
|
||||
ServerInfo const *server; // only useful for logEvent
|
||||
};
|
||||
|
||||
/* Option parsing */
|
||||
|
||||
typedef struct _rinetd_options RinetdOptions;
|
||||
struct _rinetd_options
|
||||
{
|
||||
char const *conf_file;
|
||||
int foreground;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user