2017-09-05 18:52:39 +08:00
|
|
|
|
/* 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. */
|
|
|
|
|
|
2016-01-11 04:39:30 +08:00
|
|
|
|
#if HAVE_CONFIG_H
|
2016-01-12 18:09:32 +08:00
|
|
|
|
# include <config.h>
|
2016-01-11 04:39:30 +08:00
|
|
|
|
#endif
|
2016-01-11 03:50:29 +08:00
|
|
|
|
|
2016-01-11 05:05:21 +08:00
|
|
|
|
#ifndef RETSIGTYPE
|
2016-01-12 18:09:32 +08:00
|
|
|
|
# define RETSIGTYPE void
|
2016-01-11 05:05:21 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#if _WIN32
|
2016-01-12 18:09:32 +08:00
|
|
|
|
# include "getopt.h"
|
2016-01-11 03:50:29 +08:00
|
|
|
|
#else
|
2016-01-12 18:09:32 +08:00
|
|
|
|
# include <getopt.h>
|
|
|
|
|
# if TIME_WITH_SYS_TIME
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# include <time.h>
|
|
|
|
|
# elif HAVE_SYS_TIME_H
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# endif
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#endif /* _WIN32 */
|
2016-01-11 03:50:29 +08:00
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <fcntl.h>
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#if _WIN32 || (!TIME_WITH_SYS_TIME && !HAVE_SYS_TIME_H)
|
2016-01-12 18:09:32 +08:00
|
|
|
|
# include <time.h>
|
2016-01-11 05:41:50 +08:00
|
|
|
|
#endif
|
2016-01-11 03:46:19 +08:00
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
2016-01-11 03:43:31 +08:00
|
|
|
|
#ifdef DEBUG
|
2016-01-12 18:09:32 +08:00
|
|
|
|
# define PERROR perror
|
2016-01-11 03:43:31 +08:00
|
|
|
|
#else
|
2016-01-12 18:09:32 +08:00
|
|
|
|
# define PERROR(x)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
2016-01-11 03:50:29 +08:00
|
|
|
|
#include "match.h"
|
2017-09-07 23:13:58 +08:00
|
|
|
|
#include "net.h"
|
2017-09-05 18:52:39 +08:00
|
|
|
|
#include "types.h"
|
2016-02-07 01:10:44 +08:00
|
|
|
|
#include "rinetd.h"
|
2017-09-05 18:52:39 +08:00
|
|
|
|
#include "parse.h"
|
2016-02-06 22:34:53 +08:00
|
|
|
|
|
|
|
|
|
Rule *allRules = NULL;
|
|
|
|
|
int allRulesCount = 0;
|
|
|
|
|
int globalRulesCount = 0;
|
|
|
|
|
|
2016-02-06 19:53:52 +08:00
|
|
|
|
ServerInfo *seInfo = NULL;
|
2016-01-13 13:36:14 +08:00
|
|
|
|
int seTotal = 0;
|
2016-01-11 08:39:59 +08:00
|
|
|
|
|
2016-01-13 13:36:14 +08:00
|
|
|
|
ConnectionInfo *coInfo = NULL;
|
|
|
|
|
int coTotal = 0;
|
2016-01-11 08:39:59 +08:00
|
|
|
|
|
2017-07-20 00:36:42 +08:00
|
|
|
|
/* On Windows, the maximum number of file descriptors in an fd_set
|
|
|
|
|
is simply FD_SETSIZE and the first argument to select() is
|
|
|
|
|
ignored, so maxfd will never change. */
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
int const maxfd = 0;
|
|
|
|
|
#else
|
2016-01-11 03:43:31 +08:00
|
|
|
|
int maxfd = 0;
|
2017-07-20 00:36:42 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
2017-09-08 19:28:09 +08:00
|
|
|
|
/* Global static buffer for UDP data. */
|
|
|
|
|
static char globalUdpBuffer[65536];
|
|
|
|
|
|
2016-02-06 19:53:52 +08:00
|
|
|
|
char *logFileName = NULL;
|
|
|
|
|
char *pidLogFileName = NULL;
|
2016-01-11 03:46:19 +08:00
|
|
|
|
int logFormatCommon = 0;
|
2016-02-06 19:53:52 +08:00
|
|
|
|
FILE *logFile = NULL;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
|
2016-01-11 04:22:36 +08:00
|
|
|
|
char const *logMessages[] = {
|
2016-02-07 01:14:14 +08:00
|
|
|
|
"unknown-error",
|
2016-01-11 03:46:19 +08:00
|
|
|
|
"done-local-closed",
|
|
|
|
|
"done-remote-closed",
|
|
|
|
|
"accept-failed -",
|
|
|
|
|
"local-socket-failed -",
|
|
|
|
|
"local-bind-failed -",
|
|
|
|
|
"local-connect-failed -",
|
2016-01-11 07:21:47 +08:00
|
|
|
|
"opened",
|
2016-01-11 03:46:19 +08:00
|
|
|
|
"not-allowed",
|
|
|
|
|
"denied",
|
|
|
|
|
};
|
2016-01-11 05:48:22 +08:00
|
|
|
|
|
2017-09-05 07:45:00 +08:00
|
|
|
|
enum {
|
2016-02-07 01:14:14 +08:00
|
|
|
|
logUnknownError = 0,
|
|
|
|
|
logLocalClosedFirst,
|
|
|
|
|
logRemoteClosedFirst,
|
|
|
|
|
logAcceptFailed,
|
|
|
|
|
logLocalSocketFailed,
|
|
|
|
|
logLocalBindFailed,
|
|
|
|
|
logLocalConnectFailed,
|
|
|
|
|
logOpened,
|
2017-09-06 20:54:52 +08:00
|
|
|
|
logAllowed,
|
2016-02-07 01:14:14 +08:00
|
|
|
|
logNotAllowed,
|
|
|
|
|
logDenied,
|
2016-01-11 07:21:47 +08:00
|
|
|
|
};
|
2016-01-11 03:46:19 +08:00
|
|
|
|
|
|
|
|
|
RinetdOptions options = {
|
2016-02-06 22:07:12 +08:00
|
|
|
|
RINETD_CONFIG_FILE,
|
2016-01-11 04:31:08 +08:00
|
|
|
|
0,
|
2016-01-11 03:46:19 +08:00
|
|
|
|
};
|
2016-01-11 03:43:31 +08:00
|
|
|
|
|
2016-02-07 01:10:44 +08:00
|
|
|
|
static void selectPass(void);
|
|
|
|
|
static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socket);
|
|
|
|
|
static void handleRead(ConnectionInfo *cnx, Socket *socket, Socket *other_socket);
|
2017-09-08 19:28:09 +08:00
|
|
|
|
static void handleUdpRead(ConnectionInfo *cnx, char const *buffer, int bytes);
|
2016-02-07 01:10:44 +08:00
|
|
|
|
static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socket);
|
2016-02-08 09:36:23 +08:00
|
|
|
|
static void handleAccept(ServerInfo const *srv);
|
2016-02-07 01:23:50 +08:00
|
|
|
|
static ConnectionInfo *findAvailableConnection(void);
|
2016-02-07 01:31:50 +08:00
|
|
|
|
static void setConnectionCount(int newCount);
|
2016-02-07 01:23:50 +08:00
|
|
|
|
static int getAddress(char const *host, struct in_addr *iaddr);
|
2017-09-06 20:54:52 +08:00
|
|
|
|
static int checkConnectionAllowed(ConnectionInfo const *cnx);
|
2016-02-07 01:10:44 +08:00
|
|
|
|
|
|
|
|
|
static int readArgs (int argc, char **argv, RinetdOptions *options);
|
2016-02-07 01:40:02 +08:00
|
|
|
|
static void clearConfiguration(void);
|
2017-09-05 02:14:11 +08:00
|
|
|
|
static void readConfiguration(char const *file);
|
2016-02-07 01:10:44 +08:00
|
|
|
|
|
2017-09-06 23:58:54 +08:00
|
|
|
|
static void registerPID(char const *pid_file_name);
|
2016-02-08 09:36:23 +08:00
|
|
|
|
static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int result);
|
2016-02-07 01:10:44 +08:00
|
|
|
|
static struct tm *get_gmtoff(int *tz);
|
|
|
|
|
|
|
|
|
|
/* Signal handlers */
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#if !HAVE_SIGACTION && !_WIN32
|
2016-02-07 01:10:44 +08:00
|
|
|
|
static RETSIGTYPE plumber(int s);
|
2016-02-07 07:02:29 +08:00
|
|
|
|
#endif
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#if !_WIN32
|
2016-02-07 01:10:44 +08:00
|
|
|
|
static RETSIGTYPE hup(int s);
|
2016-02-07 07:02:29 +08:00
|
|
|
|
#endif
|
2016-02-07 01:40:02 +08:00
|
|
|
|
static RETSIGTYPE quit(int s);
|
2016-02-07 01:10:44 +08:00
|
|
|
|
|
2016-01-11 03:43:31 +08:00
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#ifdef _WIN32
|
2016-01-11 03:50:29 +08:00
|
|
|
|
WSADATA wsaData;
|
2016-02-07 07:02:29 +08:00
|
|
|
|
int result = WSAStartup(MAKEWORD(1, 1), &wsaData);
|
2016-01-11 03:50:29 +08:00
|
|
|
|
if (result != 0) {
|
|
|
|
|
fprintf(stderr, "Your computer was not connected "
|
|
|
|
|
"to the Internet at the time that "
|
|
|
|
|
"this program was launched, or you "
|
|
|
|
|
"do not have a 32-bit "
|
|
|
|
|
"connection to the Internet.");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
2016-01-11 05:05:21 +08:00
|
|
|
|
#else
|
2016-02-07 07:02:29 +08:00
|
|
|
|
openlog("rinetd", LOG_PID, LOG_DAEMON);
|
2016-01-11 05:05:21 +08:00
|
|
|
|
#endif
|
2016-02-07 07:02:29 +08:00
|
|
|
|
|
|
|
|
|
readArgs(argc, argv, &options);
|
|
|
|
|
|
|
|
|
|
#if HAVE_DAEMON && !DEBUG
|
|
|
|
|
if (!options.foreground && daemon(0, 0) != 0) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
exit(0);
|
2016-02-07 07:02:29 +08:00
|
|
|
|
}
|
|
|
|
|
#elif HAVE_FORK && !DEBUG
|
|
|
|
|
if (!options.foreground && fork() != 0) {
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
2016-01-11 05:41:50 +08:00
|
|
|
|
#endif
|
2016-02-07 07:02:29 +08:00
|
|
|
|
|
|
|
|
|
#if HAVE_SIGACTION
|
|
|
|
|
struct sigaction act;
|
|
|
|
|
act.sa_handler = SIG_IGN;
|
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
|
|
|
act.sa_flags = SA_RESTART;
|
|
|
|
|
sigaction(SIGPIPE, &act, NULL);
|
|
|
|
|
act.sa_handler = &hup;
|
|
|
|
|
sigaction(SIGHUP, &act, NULL);
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#elif !_WIN32
|
2016-02-07 07:02:29 +08:00
|
|
|
|
signal(SIGPIPE, plumber);
|
|
|
|
|
signal(SIGHUP, hup);
|
|
|
|
|
#endif
|
|
|
|
|
signal(SIGINT, quit);
|
|
|
|
|
signal(SIGTERM, quit);
|
|
|
|
|
|
2017-09-05 02:14:11 +08:00
|
|
|
|
readConfiguration(options.conf_file);
|
2017-09-06 23:58:54 +08:00
|
|
|
|
if (pidLogFileName || !options.foreground) {
|
|
|
|
|
registerPID(pidLogFileName ? pidLogFileName : RINETD_PID_FILE);
|
|
|
|
|
}
|
2016-02-07 07:02:29 +08:00
|
|
|
|
|
2017-09-07 19:26:04 +08:00
|
|
|
|
syslog(LOG_INFO, "Starting redirections...\n");
|
2016-02-07 07:02:29 +08:00
|
|
|
|
while (1) {
|
|
|
|
|
selectPass();
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-02-07 07:02:29 +08:00
|
|
|
|
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 01:40:02 +08:00
|
|
|
|
static void clearConfiguration(void) {
|
2016-02-08 09:36:23 +08:00
|
|
|
|
/* Remove references to server information */
|
|
|
|
|
for (int i = 0; i < coTotal; ++i) {
|
|
|
|
|
ConnectionInfo *cnx = &coInfo[i];
|
|
|
|
|
cnx->server = NULL;
|
|
|
|
|
}
|
2016-02-06 19:53:52 +08:00
|
|
|
|
/* Close existing server sockets. */
|
|
|
|
|
for (int i = 0; i < seTotal; ++i) {
|
|
|
|
|
ServerInfo *srv = &seInfo[i];
|
|
|
|
|
if (srv->fd != INVALID_SOCKET) {
|
|
|
|
|
closesocket(srv->fd);
|
2016-01-11 05:48:22 +08:00
|
|
|
|
}
|
2016-02-06 19:58:46 +08:00
|
|
|
|
free(srv->fromHost);
|
|
|
|
|
free(srv->toHost);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-02-06 19:53:52 +08:00
|
|
|
|
/* Free memory associated with previous set. */
|
|
|
|
|
free(seInfo);
|
|
|
|
|
seInfo = NULL;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
seTotal = 0;
|
2016-02-06 22:34:53 +08:00
|
|
|
|
/* Forget existing rules. */
|
|
|
|
|
for (int i = 0; i < allRulesCount; ++i) {
|
|
|
|
|
free(allRules[i].pattern);
|
2016-01-11 03:46:19 +08:00
|
|
|
|
}
|
2016-02-06 19:53:52 +08:00
|
|
|
|
/* Free memory associated with previous set. */
|
2016-02-06 22:34:53 +08:00
|
|
|
|
free(allRules);
|
|
|
|
|
allRules = NULL;
|
|
|
|
|
allRulesCount = globalRulesCount = 0;
|
2016-02-06 19:53:52 +08:00
|
|
|
|
/* Free file names */
|
|
|
|
|
free(logFileName);
|
|
|
|
|
logFileName = NULL;
|
|
|
|
|
free(pidLogFileName);
|
|
|
|
|
pidLogFileName = NULL;
|
2016-02-07 01:40:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-05 02:14:11 +08:00
|
|
|
|
static void readConfiguration(char const *file) {
|
|
|
|
|
|
2016-02-06 19:23:37 +08:00
|
|
|
|
/* Parse the configuration file. */
|
2017-09-05 18:52:39 +08:00
|
|
|
|
parseConfiguration(file);
|
2017-09-05 02:14:11 +08:00
|
|
|
|
|
2016-01-11 03:46:19 +08:00
|
|
|
|
/* Open the log file */
|
|
|
|
|
if (logFile) {
|
|
|
|
|
fclose(logFile);
|
2016-02-06 19:53:52 +08:00
|
|
|
|
logFile = NULL;
|
2016-01-11 03:46:19 +08:00
|
|
|
|
}
|
|
|
|
|
if (logFileName) {
|
|
|
|
|
logFile = fopen(logFileName, "a");
|
2016-01-11 05:23:40 +08:00
|
|
|
|
if (logFile) {
|
|
|
|
|
setvbuf(logFile, NULL, _IONBF, 0);
|
|
|
|
|
} else {
|
|
|
|
|
syslog(LOG_ERR, "could not open %s to append (%m).\n",
|
2016-01-11 03:46:19 +08:00
|
|
|
|
logFileName);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-05 07:45:00 +08:00
|
|
|
|
void addServer(char *bindAddress, int bindPort, int bindProto,
|
2017-09-08 18:32:05 +08:00
|
|
|
|
char *connectAddress, int connectPort, int connectProto,
|
2017-09-08 19:28:09 +08:00
|
|
|
|
int serverTimeout)
|
|
|
|
|
{
|
2017-09-05 07:45:00 +08:00
|
|
|
|
/* 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);
|
2017-09-05 18:52:39 +08:00
|
|
|
|
exit(1);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
/* 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");
|
2017-09-05 18:52:39 +08:00
|
|
|
|
exit(1);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
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 *)
|
2017-09-08 19:28:09 +08:00
|
|
|
|
&saddr, sizeof(saddr)) == SOCKET_ERROR) {
|
2017-09-05 07:45:00 +08:00
|
|
|
|
/* Warn -- don't exit. */
|
|
|
|
|
syslog(LOG_ERR, "couldn't bind to "
|
|
|
|
|
"address %s port %d (%m)\n",
|
|
|
|
|
bindAddress, bindPort);
|
|
|
|
|
closesocket(fd);
|
2017-09-05 18:52:39 +08:00
|
|
|
|
exit(1);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
2017-09-07 23:04:05 +08:00
|
|
|
|
|
2017-09-05 23:26:30 +08:00
|
|
|
|
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);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2017-09-07 23:04:05 +08:00
|
|
|
|
|
2017-09-08 19:28:09 +08:00
|
|
|
|
/* Make socket nonblocking in TCP mode only, otherwise
|
|
|
|
|
we may miss some data. */
|
2017-09-07 23:13:58 +08:00
|
|
|
|
setSocketDefaults(fd);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
2017-09-06 19:55:49 +08:00
|
|
|
|
|
2017-09-05 07:45:00 +08:00
|
|
|
|
if (getAddress(connectAddress, &iaddr) < 0) {
|
|
|
|
|
/* Warn -- don't exit. */
|
|
|
|
|
syslog(LOG_ERR, "host %s could not be resolved.\n",
|
|
|
|
|
bindAddress);
|
|
|
|
|
closesocket(fd);
|
2017-09-05 18:52:39 +08:00
|
|
|
|
exit(1);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
/* Allocate server info */
|
|
|
|
|
seInfo = (ServerInfo *)
|
|
|
|
|
realloc(seInfo, sizeof(ServerInfo) * (seTotal + 1));
|
|
|
|
|
if (!seInfo) {
|
2017-09-05 18:52:39 +08:00
|
|
|
|
exit(1);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
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) {
|
2017-09-05 18:52:39 +08:00
|
|
|
|
exit(1);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
srv->fromPort = bindPort;
|
|
|
|
|
srv->fromProto = bindProto;
|
|
|
|
|
srv->toHost = connectAddress;
|
|
|
|
|
if (!srv->toHost) {
|
2017-09-05 18:52:39 +08:00
|
|
|
|
exit(1);
|
2017-09-05 07:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
srv->toPort = connectPort;
|
|
|
|
|
srv->toProto = connectProto;
|
2017-09-08 18:32:05 +08:00
|
|
|
|
srv->serverTimeout = serverTimeout;
|
2017-09-05 07:45:00 +08:00
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
if (fd > maxfd) {
|
|
|
|
|
maxfd = fd;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2017-09-05 07:45:00 +08:00
|
|
|
|
#endif
|
|
|
|
|
++seTotal;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 01:31:50 +08:00
|
|
|
|
static void setConnectionCount(int newCount)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2016-02-07 01:31:50 +08:00
|
|
|
|
if (newCount == coTotal) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = newCount; i < coTotal; ++i) {
|
2016-02-08 09:36:23 +08:00
|
|
|
|
if (coInfo[i].local.fd != INVALID_SOCKET) {
|
|
|
|
|
closesocket(coInfo[i].local.fd);
|
|
|
|
|
}
|
|
|
|
|
if (coInfo[i].remote.fd != INVALID_SOCKET) {
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (coInfo[i].remote.proto == protoTcp)
|
|
|
|
|
closesocket(coInfo[i].remote.fd);
|
2016-02-08 09:36:23 +08:00
|
|
|
|
}
|
2016-02-07 01:31:50 +08:00
|
|
|
|
free(coInfo[i].local.buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newCount == 0) {
|
|
|
|
|
free(coInfo);
|
|
|
|
|
coInfo = NULL;
|
|
|
|
|
coTotal = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-13 13:36:14 +08:00
|
|
|
|
ConnectionInfo * newCoInfo = (ConnectionInfo *)
|
2016-02-07 01:31:50 +08:00
|
|
|
|
malloc(sizeof(ConnectionInfo) * newCount);
|
2016-01-13 13:36:14 +08:00
|
|
|
|
if (!newCoInfo) {
|
|
|
|
|
return;
|
2016-01-11 05:48:22 +08:00
|
|
|
|
}
|
2016-01-13 13:36:14 +08:00
|
|
|
|
|
|
|
|
|
memcpy(newCoInfo, coInfo, sizeof(ConnectionInfo) * coTotal);
|
|
|
|
|
|
2016-02-07 01:31:50 +08:00
|
|
|
|
for (int i = coTotal; i < newCount; ++i) {
|
2016-01-13 13:36:14 +08:00
|
|
|
|
ConnectionInfo *cnx = &newCoInfo[i];
|
2016-02-07 01:31:50 +08:00
|
|
|
|
memset(cnx, 0, sizeof(*cnx));
|
2016-02-06 23:54:17 +08:00
|
|
|
|
cnx->local.fd = INVALID_SOCKET;
|
|
|
|
|
cnx->remote.fd = INVALID_SOCKET;
|
|
|
|
|
cnx->local.buffer = (char *) malloc(sizeof(char) * 2 * RINETD_BUFFER_SIZE);
|
|
|
|
|
if (!cnx->local.buffer) {
|
2016-01-13 13:36:14 +08:00
|
|
|
|
while (i-- >= coTotal) {
|
2016-02-06 23:54:17 +08:00
|
|
|
|
free(newCoInfo[i].local.buffer);
|
2016-01-13 13:36:14 +08:00
|
|
|
|
}
|
|
|
|
|
free(newCoInfo);
|
|
|
|
|
return;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-02-06 23:54:17 +08:00
|
|
|
|
cnx->remote.buffer = cnx->local.buffer + RINETD_BUFFER_SIZE;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-01-13 13:36:14 +08:00
|
|
|
|
|
|
|
|
|
free(coInfo);
|
|
|
|
|
coInfo = newCoInfo;
|
2016-02-07 01:31:50 +08:00
|
|
|
|
coTotal = newCount;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 01:23:50 +08:00
|
|
|
|
static ConnectionInfo *findAvailableConnection(void)
|
2016-02-07 00:28:47 +08:00
|
|
|
|
{
|
|
|
|
|
/* Find an existing closed connection to reuse */
|
|
|
|
|
for (int j = 0; j < coTotal; ++j) {
|
|
|
|
|
if (coInfo[j].local.fd == INVALID_SOCKET
|
|
|
|
|
&& coInfo[j].remote.fd == INVALID_SOCKET) {
|
|
|
|
|
return &coInfo[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate new connections and pick the first one */
|
|
|
|
|
int oldTotal = coTotal;
|
2016-02-07 01:31:50 +08:00
|
|
|
|
setConnectionCount(coTotal * 4 / 3 + 8);
|
2016-02-07 00:28:47 +08:00
|
|
|
|
if (coTotal == oldTotal) {
|
|
|
|
|
syslog(LOG_ERR, "not enough memory to add slots. "
|
|
|
|
|
"Currently %d slots.\n", coTotal);
|
|
|
|
|
/* Go back to the previous total number of slots */
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return &coInfo[oldTotal];
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-07 19:26:04 +08:00
|
|
|
|
static void selectPass(void)
|
|
|
|
|
{
|
2016-02-06 21:23:02 +08:00
|
|
|
|
int const fdSetCount = maxfd / FD_SETSIZE + 1;
|
2016-01-13 14:47:12 +08:00
|
|
|
|
# define FD_ZERO_EXT(ar) for (int i = 0; i < fdSetCount; ++i) { FD_ZERO(&(ar)[i]); }
|
2017-07-20 00:36:42 +08:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
/* On Windows, only one fd_set is usable because of its structure. */
|
|
|
|
|
# define FD_SET_EXT(fd, ar) FD_SET(fd, &(ar)[0])
|
|
|
|
|
# define FD_ISSET_EXT(fd, ar) FD_ISSET(fd, &(ar)[0])
|
|
|
|
|
#else
|
2016-02-06 21:23:02 +08:00
|
|
|
|
# define FD_SET_EXT(fd, ar) FD_SET((fd) % FD_SETSIZE, &(ar)[(fd) / FD_SETSIZE])
|
|
|
|
|
# define FD_ISSET_EXT(fd, ar) FD_ISSET((fd) % FD_SETSIZE, &(ar)[(fd) / FD_SETSIZE])
|
2017-07-20 00:36:42 +08:00
|
|
|
|
#endif
|
2016-01-13 14:47:12 +08:00
|
|
|
|
|
2017-09-08 18:32:05 +08:00
|
|
|
|
/* Timeout value -- infinite by default */
|
|
|
|
|
struct timeval timeout;
|
|
|
|
|
timeout.tv_sec = timeout.tv_usec = 0;
|
|
|
|
|
time_t now = time(NULL);
|
|
|
|
|
|
2016-01-13 14:47:12 +08:00
|
|
|
|
fd_set readfds[fdSetCount], writefds[fdSetCount];
|
|
|
|
|
FD_ZERO_EXT(readfds);
|
|
|
|
|
FD_ZERO_EXT(writefds);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
/* Server sockets */
|
2016-01-12 18:36:42 +08:00
|
|
|
|
for (int i = 0; i < seTotal; ++i) {
|
2016-01-11 08:39:59 +08:00
|
|
|
|
if (seInfo[i].fd != INVALID_SOCKET) {
|
2016-01-13 14:47:12 +08:00
|
|
|
|
FD_SET_EXT(seInfo[i].fd, readfds);
|
2016-01-11 03:46:19 +08:00
|
|
|
|
}
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
/* Connection sockets */
|
2016-01-12 18:36:42 +08:00
|
|
|
|
for (int i = 0; i < coTotal; ++i) {
|
2016-01-12 09:54:28 +08:00
|
|
|
|
ConnectionInfo *cnx = &coInfo[i];
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (cnx->local.fd != INVALID_SOCKET) {
|
|
|
|
|
/* Accept more output from the local
|
|
|
|
|
server if there's room */
|
|
|
|
|
if (cnx->local.recvPos < RINETD_BUFFER_SIZE) {
|
|
|
|
|
FD_SET_EXT(cnx->local.fd, readfds);
|
2016-01-11 05:48:22 +08:00
|
|
|
|
}
|
2016-02-06 23:54:17 +08:00
|
|
|
|
/* Send more input to the local server
|
|
|
|
|
if we have any, or if we’re closing */
|
|
|
|
|
if (cnx->local.sentPos < cnx->remote.recvPos || cnx->coClosing) {
|
|
|
|
|
FD_SET_EXT(cnx->local.fd, writefds);
|
2016-01-11 05:48:22 +08:00
|
|
|
|
}
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (cnx->remote.fd != INVALID_SOCKET) {
|
|
|
|
|
/* Get more input if we have room for it */
|
|
|
|
|
if (cnx->remote.recvPos < RINETD_BUFFER_SIZE) {
|
|
|
|
|
FD_SET_EXT(cnx->remote.fd, readfds);
|
2017-09-08 18:32:05 +08:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2016-02-06 23:54:17 +08:00
|
|
|
|
}
|
|
|
|
|
/* Send more output if we have any, or if we’re closing */
|
|
|
|
|
if (cnx->remote.sentPos < cnx->local.recvPos || cnx->coClosing) {
|
|
|
|
|
FD_SET_EXT(cnx->remote.fd, writefds);
|
|
|
|
|
}
|
2016-01-11 05:48:22 +08:00
|
|
|
|
}
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2017-09-08 18:32:05 +08:00
|
|
|
|
|
|
|
|
|
select(maxfd + 1, readfds, writefds, 0, timeout.tv_sec ? &timeout : NULL);
|
2016-01-12 18:36:42 +08:00
|
|
|
|
for (int i = 0; i < coTotal; ++i) {
|
2016-01-12 09:54:28 +08:00
|
|
|
|
ConnectionInfo *cnx = &coInfo[i];
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (cnx->remote.fd != INVALID_SOCKET) {
|
2017-09-08 18:32:05 +08:00
|
|
|
|
/* Do not read on remote UDP sockets, the server does it,
|
|
|
|
|
but handle timeouts instead. */
|
|
|
|
|
if (cnx->remote.proto == protoTcp) {
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (FD_ISSET_EXT(cnx->remote.fd, readfds)) {
|
|
|
|
|
handleRead(cnx, &cnx->remote, &cnx->local);
|
|
|
|
|
}
|
2017-09-08 18:32:05 +08:00
|
|
|
|
} else {
|
|
|
|
|
if (now > cnx->remoteTimeout) {
|
|
|
|
|
handleClose(cnx, &cnx->remote, &cnx->local);
|
|
|
|
|
}
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (cnx->remote.fd != INVALID_SOCKET) {
|
|
|
|
|
if (FD_ISSET_EXT(cnx->remote.fd, writefds)) {
|
2016-02-07 00:05:49 +08:00
|
|
|
|
handleWrite(cnx, &cnx->remote, &cnx->local);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (cnx->local.fd != INVALID_SOCKET) {
|
|
|
|
|
if (FD_ISSET_EXT(cnx->local.fd, readfds)) {
|
2016-02-07 00:05:49 +08:00
|
|
|
|
handleRead(cnx, &cnx->local, &cnx->remote);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (cnx->local.fd != INVALID_SOCKET) {
|
|
|
|
|
if (FD_ISSET_EXT(cnx->local.fd, writefds)) {
|
2016-02-07 00:05:49 +08:00
|
|
|
|
handleWrite(cnx, &cnx->local, &cnx->remote);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-08 09:49:24 +08:00
|
|
|
|
/* Handle servers last because handleAccept() may modify coTotal */
|
|
|
|
|
for (int i = 0; i < seTotal; ++i) {
|
|
|
|
|
ServerInfo *srv = &seInfo[i];
|
|
|
|
|
if (srv->fd != INVALID_SOCKET) {
|
|
|
|
|
if (FD_ISSET_EXT(srv->fd, readfds)) {
|
|
|
|
|
handleAccept(srv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 00:05:49 +08:00
|
|
|
|
static void handleRead(ConnectionInfo *cnx, Socket *socket, Socket *other_socket)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2016-02-07 00:05:49 +08:00
|
|
|
|
if (RINETD_BUFFER_SIZE == socket->recvPos) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-02-07 00:05:49 +08:00
|
|
|
|
int got = recv(socket->fd, socket->buffer + socket->recvPos,
|
|
|
|
|
RINETD_BUFFER_SIZE - socket->recvPos, 0);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
if (got < 0) {
|
2016-01-11 03:50:29 +08:00
|
|
|
|
if (GetLastError() == WSAEWOULDBLOCK) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-01-11 03:50:29 +08:00
|
|
|
|
if (GetLastError() == WSAEINPROGRESS) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-02-06 19:53:52 +08:00
|
|
|
|
}
|
|
|
|
|
if (got <= 0) {
|
|
|
|
|
/* Prepare for closing */
|
2016-02-07 00:05:49 +08:00
|
|
|
|
handleClose(cnx, socket, other_socket);
|
2016-02-06 23:54:17 +08:00
|
|
|
|
return;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-02-07 00:05:49 +08:00
|
|
|
|
socket->recvBytes += got;
|
|
|
|
|
socket->recvPos += got;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-08 19:28:09 +08:00
|
|
|
|
static void handleUdpRead(ConnectionInfo *cnx, char const *buffer, int bytes)
|
|
|
|
|
{
|
|
|
|
|
Socket *socket = &cnx->remote;
|
|
|
|
|
int got = bytes < RINETD_BUFFER_SIZE - socket->recvPos
|
|
|
|
|
? bytes : RINETD_BUFFER_SIZE - socket->recvPos;
|
|
|
|
|
if (got > 0) {
|
|
|
|
|
memcpy(socket->buffer + socket->recvPos, buffer, got);
|
|
|
|
|
socket->recvBytes += got;
|
|
|
|
|
socket->recvPos += got;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 00:05:49 +08:00
|
|
|
|
static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socket)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2016-02-07 00:05:49 +08:00
|
|
|
|
if (cnx->coClosing && (socket->sentPos == other_socket->recvPos)) {
|
2016-02-06 23:54:17 +08:00
|
|
|
|
PERROR("rinetd: local closed and no more output");
|
2016-02-07 01:14:14 +08:00
|
|
|
|
logEvent(cnx, cnx->server, cnx->coLog);
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (socket->proto == protoTcp)
|
|
|
|
|
closesocket(socket->fd);
|
2016-02-07 00:05:49 +08:00
|
|
|
|
socket->fd = INVALID_SOCKET;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-09-08 18:56:29 +08:00
|
|
|
|
|
|
|
|
|
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);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
if (got < 0) {
|
2016-01-11 03:50:29 +08:00
|
|
|
|
if (GetLastError() == WSAEWOULDBLOCK) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-01-11 03:50:29 +08:00
|
|
|
|
if (GetLastError() == WSAEINPROGRESS) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-02-07 00:05:49 +08:00
|
|
|
|
handleClose(cnx, socket, other_socket);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-02-07 00:05:49 +08:00
|
|
|
|
socket->sentPos += got;
|
|
|
|
|
socket->sentBytes += got;
|
|
|
|
|
if (socket->sentPos == other_socket->recvPos) {
|
|
|
|
|
socket->sentPos = other_socket->recvPos = 0;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 00:05:49 +08:00
|
|
|
|
static void handleClose(ConnectionInfo *cnx, Socket *socket, Socket *other_socket)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2016-01-12 09:54:28 +08:00
|
|
|
|
cnx->coClosing = 1;
|
2017-09-06 19:55:49 +08:00
|
|
|
|
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 */
|
|
|
|
|
}
|
2016-02-07 00:05:49 +08:00
|
|
|
|
socket->fd = INVALID_SOCKET;
|
2017-09-08 19:28:09 +08:00
|
|
|
|
|
2016-02-07 00:05:49 +08:00
|
|
|
|
if (other_socket->fd != INVALID_SOCKET) {
|
2017-09-08 19:28:09 +08:00
|
|
|
|
if (other_socket->proto == protoTcp) {
|
2017-09-07 19:26:04 +08:00
|
|
|
|
#if !defined __linux__ && !defined _WIN32
|
2017-09-08 19:28:09 +08:00
|
|
|
|
/* Now set up the other end for a polite closing */
|
|
|
|
|
|
|
|
|
|
/* Request a low-water mark equal to the entire
|
|
|
|
|
output buffer, so the next write notification
|
|
|
|
|
tells us for sure that we can close the socket. */
|
|
|
|
|
int arg = 1024;
|
|
|
|
|
setsockopt(other_socket->fd, SOL_SOCKET, SO_SNDLOWAT,
|
|
|
|
|
&arg, sizeof(arg));
|
2017-09-07 19:26:04 +08:00
|
|
|
|
#endif
|
2017-09-08 19:28:09 +08:00
|
|
|
|
} else /* if (other_socket->proto == protoUdp) */ {
|
|
|
|
|
if (other_socket == &cnx->local)
|
|
|
|
|
closesocket(other_socket->fd);
|
|
|
|
|
other_socket->fd = INVALID_SOCKET;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 00:05:49 +08:00
|
|
|
|
cnx->coLog = socket == &cnx->local ?
|
|
|
|
|
logLocalClosedFirst : logRemoteClosedFirst;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 09:36:23 +08:00
|
|
|
|
static void handleAccept(ServerInfo const *srv)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2017-09-08 19:28:09 +08:00
|
|
|
|
int udpBytes = 0;
|
|
|
|
|
|
2016-01-11 03:43:31 +08:00
|
|
|
|
struct sockaddr addr;
|
2017-09-06 19:55:49 +08:00
|
|
|
|
SOCKLEN_T addrlen = sizeof(addr);
|
|
|
|
|
|
|
|
|
|
SOCKET nfd;
|
|
|
|
|
if (srv->fromProto == protoTcp) {
|
2017-09-07 22:29:23 +08:00
|
|
|
|
/* In TCP mode, get remote address using accept(). */
|
2017-09-06 19:55:49 +08:00
|
|
|
|
nfd = accept(srv->fd, &addr, &addrlen);
|
|
|
|
|
if (nfd == INVALID_SOCKET) {
|
2017-09-07 19:26:04 +08:00
|
|
|
|
syslog(LOG_ERR, "accept(%d): %m\n", srv->fd);
|
2017-09-06 19:55:49 +08:00
|
|
|
|
logEvent(NULL, srv, logAcceptFailed);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-02-09 09:28:19 +08:00
|
|
|
|
|
2017-09-07 23:13:58 +08:00
|
|
|
|
setSocketDefaults(nfd);
|
2017-09-06 19:55:49 +08:00
|
|
|
|
} else /* if (srv->fromProto == protoUdp) */ {
|
2017-09-07 22:29:23 +08:00
|
|
|
|
/* In UDP mode, get remote address using recvfrom() and check
|
2017-09-08 19:28:09 +08:00
|
|
|
|
for an existing connection from this client. We need
|
|
|
|
|
to read a lot of data otherwise the datagram contents
|
|
|
|
|
may be lost later. */
|
2017-09-06 19:55:49 +08:00
|
|
|
|
nfd = srv->fd;
|
2017-09-08 19:28:09 +08:00
|
|
|
|
ssize_t ret = recvfrom(nfd, globalUdpBuffer,
|
|
|
|
|
sizeof(globalUdpBuffer), 0, &addr, &addrlen);
|
2017-09-06 19:55:49 +08:00
|
|
|
|
if (ret < 0) {
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (GetLastError() == WSAEWOULDBLOCK) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (GetLastError() == WSAEINPROGRESS) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-09-07 19:26:04 +08:00
|
|
|
|
syslog(LOG_ERR, "recvfrom(%d): %m\n", srv->fd);
|
2017-09-06 19:55:49 +08:00
|
|
|
|
logEvent(NULL, srv, logAcceptFailed);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-09-07 22:29:23 +08:00
|
|
|
|
|
2017-09-08 19:28:09 +08:00
|
|
|
|
udpBytes = (int)ret;
|
|
|
|
|
|
2017-09-07 22:29:23 +08:00
|
|
|
|
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) {
|
2017-09-08 18:32:05 +08:00
|
|
|
|
cnx->remoteTimeout = time(NULL) + srv->serverTimeout;
|
2017-09-08 19:28:09 +08:00
|
|
|
|
handleUdpRead(cnx, globalUdpBuffer, udpBytes);
|
2017-09-07 22:29:23 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-07 00:28:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-07 22:29:23 +08:00
|
|
|
|
ConnectionInfo *cnx = findAvailableConnection();
|
|
|
|
|
if (!cnx) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-01-12 18:36:42 +08:00
|
|
|
|
|
2016-02-06 23:54:17 +08:00
|
|
|
|
cnx->local.fd = INVALID_SOCKET;
|
2017-09-05 07:45:00 +08:00
|
|
|
|
cnx->local.proto = srv->toProto;
|
2016-02-07 00:28:47 +08:00
|
|
|
|
cnx->local.recvPos = cnx->local.sentPos = 0;
|
|
|
|
|
cnx->local.recvBytes = cnx->local.sentBytes = 0;
|
2017-09-08 18:32:05 +08:00
|
|
|
|
|
2016-02-06 23:54:17 +08:00
|
|
|
|
cnx->remote.fd = nfd;
|
2017-09-05 07:45:00 +08:00
|
|
|
|
cnx->remote.proto = srv->fromProto;
|
2016-02-07 00:28:47 +08:00
|
|
|
|
cnx->remote.recvPos = cnx->remote.sentPos = 0;
|
|
|
|
|
cnx->remote.recvBytes = cnx->remote.sentBytes = 0;
|
2017-09-07 22:29:23 +08:00
|
|
|
|
cnx->remoteAddress = *(struct sockaddr_in *)&addr;
|
2017-09-08 18:32:05 +08:00
|
|
|
|
if (srv->fromProto == protoUdp)
|
|
|
|
|
cnx->remoteTimeout = time(NULL) + srv->serverTimeout;
|
|
|
|
|
|
2016-02-07 00:28:47 +08:00
|
|
|
|
cnx->coClosing = 0;
|
2016-02-07 01:14:14 +08:00
|
|
|
|
cnx->coLog = logUnknownError;
|
2016-02-08 09:36:23 +08:00
|
|
|
|
cnx->server = srv;
|
2016-02-07 00:28:47 +08:00
|
|
|
|
|
2017-09-06 20:54:52 +08:00
|
|
|
|
int logCode = checkConnectionAllowed(cnx);
|
|
|
|
|
if (logCode != logAllowed) {
|
2017-09-07 00:05:25 +08:00
|
|
|
|
/* Local fd is not open yet, so only
|
|
|
|
|
close the remote socket. */
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (cnx->remote.proto == protoTcp)
|
|
|
|
|
closesocket(cnx->remote.fd);
|
2017-09-07 00:05:25 +08:00
|
|
|
|
cnx->remote.fd = INVALID_SOCKET;
|
|
|
|
|
logEvent(cnx, cnx->server, logCode);
|
2016-02-06 19:53:52 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-09-06 20:54:52 +08:00
|
|
|
|
|
2016-01-11 03:43:31 +08:00
|
|
|
|
/* 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;
|
2017-09-05 07:45:00 +08:00
|
|
|
|
cnx->local.fd = srv->toProto == protoTcp
|
|
|
|
|
? socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
|
|
|
|
|
: socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (cnx->local.fd == INVALID_SOCKET) {
|
2017-09-07 19:26:04 +08:00
|
|
|
|
syslog(LOG_ERR, "socket(): %m\n");
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (cnx->remote.proto == protoTcp)
|
|
|
|
|
closesocket(cnx->remote.fd);
|
2016-02-06 23:54:17 +08:00
|
|
|
|
cnx->remote.fd = INVALID_SOCKET;
|
2016-02-08 09:36:23 +08:00
|
|
|
|
logEvent(cnx, srv, logLocalSocketFailed);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-01-11 05:41:50 +08:00
|
|
|
|
|
2017-09-08 19:28:09 +08:00
|
|
|
|
if (srv->toProto == protoTcp)
|
|
|
|
|
setSocketDefaults(cnx->local.fd);
|
|
|
|
|
|
2016-01-11 05:41:50 +08:00
|
|
|
|
#if 0 // You don't need bind(2) on a socket you'll use for connect(2).
|
2016-01-11 03:43:31 +08:00
|
|
|
|
/* Bind the local socket */
|
|
|
|
|
saddr.sin_family = AF_INET;
|
|
|
|
|
saddr.sin_port = INADDR_ANY;
|
|
|
|
|
saddr.sin_addr.s_addr = 0;
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (bind(cnx->local.fd, (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR) {
|
|
|
|
|
closesocket(cnx->local.fd);
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (cnx->remote.proto == protoTcp)
|
|
|
|
|
closesocket(cnx->remote.fd);
|
2016-02-06 23:54:17 +08:00
|
|
|
|
cnx->remote.fd = INVALID_SOCKET;
|
|
|
|
|
cnx->local.fd = INVALID_SOCKET;
|
2016-02-08 09:36:23 +08:00
|
|
|
|
logEvent(cnx, srv, logLocalBindFailed);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-01-11 05:41:50 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
2016-01-11 03:43:31 +08:00
|
|
|
|
memset(&saddr, 0, sizeof(struct sockaddr_in));
|
|
|
|
|
saddr.sin_family = AF_INET;
|
2016-02-07 00:28:47 +08:00
|
|
|
|
memcpy(&saddr.sin_addr, &srv->localAddr, sizeof(struct in_addr));
|
|
|
|
|
saddr.sin_port = srv->localPort;
|
2016-01-12 18:36:42 +08:00
|
|
|
|
|
2016-02-06 23:54:17 +08:00
|
|
|
|
if (connect(cnx->local.fd, (struct sockaddr *)&saddr,
|
2016-02-09 09:28:19 +08:00
|
|
|
|
sizeof(struct sockaddr_in)) == SOCKET_ERROR)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2016-01-11 03:50:29 +08:00
|
|
|
|
if ((GetLastError() != WSAEINPROGRESS) &&
|
|
|
|
|
(GetLastError() != WSAEWOULDBLOCK))
|
|
|
|
|
{
|
2016-01-11 03:46:19 +08:00
|
|
|
|
PERROR("rinetd: connect");
|
2016-02-06 23:54:17 +08:00
|
|
|
|
closesocket(cnx->local.fd);
|
2017-09-07 22:29:23 +08:00
|
|
|
|
if (cnx->remote.proto == protoTcp)
|
|
|
|
|
closesocket(cnx->remote.fd);
|
2016-02-06 23:54:17 +08:00
|
|
|
|
cnx->remote.fd = INVALID_SOCKET;
|
|
|
|
|
cnx->local.fd = INVALID_SOCKET;
|
2016-02-08 09:36:23 +08:00
|
|
|
|
logEvent(cnx, srv, logLocalConnectFailed);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-07 00:28:47 +08:00
|
|
|
|
|
2017-09-08 19:28:09 +08:00
|
|
|
|
/* Send a zero-size UDP packet to simulate a connection */
|
|
|
|
|
if (srv->toProto == protoUdp) {
|
|
|
|
|
int got = sendto(cnx->local.fd, NULL, 0, 0,
|
|
|
|
|
&saddr, (SOCKLEN_T)sizeof(saddr));
|
|
|
|
|
/* FIXME: we ignore errors here... is it safe? */
|
|
|
|
|
(void)got;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Send UDP data to the other socket */
|
|
|
|
|
if (srv->fromProto == protoUdp) {
|
|
|
|
|
handleUdpRead(cnx, globalUdpBuffer, udpBytes);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#ifndef _WIN32
|
2016-02-07 00:28:47 +08:00
|
|
|
|
if (cnx->local.fd > maxfd) {
|
|
|
|
|
maxfd = cnx->local.fd;
|
|
|
|
|
}
|
|
|
|
|
if (cnx->remote.fd > maxfd) {
|
|
|
|
|
maxfd = cnx->remote.fd;
|
|
|
|
|
}
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#endif /* _WIN32 */
|
2016-02-07 00:28:47 +08:00
|
|
|
|
|
2016-02-08 09:36:23 +08:00
|
|
|
|
logEvent(cnx, srv, logOpened);
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-06 20:54:52 +08:00
|
|
|
|
static int checkConnectionAllowed(ConnectionInfo const *cnx)
|
2016-02-07 01:10:44 +08:00
|
|
|
|
{
|
2017-09-06 20:54:52 +08:00
|
|
|
|
ServerInfo const *srv = cnx->server;
|
2017-09-07 22:29:23 +08:00
|
|
|
|
char const *addressText = inet_ntoa(cnx->remoteAddress.sin_addr);
|
2017-09-06 20:54:52 +08:00
|
|
|
|
|
|
|
|
|
/* 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;
|
2016-02-07 01:10:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-06 20:26:33 +08:00
|
|
|
|
static int getAddress(char const *host, struct in_addr *iaddr)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2016-02-06 20:26:33 +08:00
|
|
|
|
/* 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;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-06 20:26:33 +08:00
|
|
|
|
if (is_ipaddr) {
|
2016-01-11 03:43:31 +08:00
|
|
|
|
iaddr->s_addr = inet_addr(host);
|
2016-02-06 21:28:54 +08:00
|
|
|
|
return 0;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-02-06 20:26:33 +08:00
|
|
|
|
|
|
|
|
|
/* Otherwise, use gethostbyname() */
|
|
|
|
|
struct hostent *h = gethostbyname(host);
|
|
|
|
|
if (h) {
|
2016-02-06 21:28:54 +08:00
|
|
|
|
#ifdef h_addr
|
2016-02-06 20:26:33 +08:00
|
|
|
|
memcpy(&iaddr->s_addr, h->h_addr, 4);
|
2016-02-06 21:28:54 +08:00
|
|
|
|
#else
|
|
|
|
|
memcpy(&iaddr->s_addr, h->h_addr_list[0], 4);
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
2016-02-06 20:26:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char const *msg = "(unknown DNS error)";
|
|
|
|
|
switch (h_errno)
|
|
|
|
|
{
|
|
|
|
|
case HOST_NOT_FOUND:
|
|
|
|
|
msg = "The specified host is unknown.";
|
|
|
|
|
break;
|
2016-02-06 21:28:54 +08:00
|
|
|
|
#ifdef NO_DATA
|
|
|
|
|
case NO_DATA:
|
|
|
|
|
#else
|
2016-02-06 20:26:33 +08:00
|
|
|
|
case NO_ADDRESS:
|
2016-02-06 21:28:54 +08:00
|
|
|
|
#endif
|
2016-02-06 20:26:33 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
2017-09-07 19:26:04 +08:00
|
|
|
|
syslog(LOG_ERR, "While resolving `%s' got: %s\n", host, msg);
|
2016-02-06 21:28:54 +08:00
|
|
|
|
return -1;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#if !HAVE_SIGACTION && !_WIN32
|
2016-01-11 05:05:21 +08:00
|
|
|
|
RETSIGTYPE plumber(int s)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
|
|
|
|
/* Just reinstall */
|
|
|
|
|
signal(SIGPIPE, plumber);
|
|
|
|
|
}
|
2016-01-11 05:05:21 +08:00
|
|
|
|
#endif
|
2016-01-11 03:43:31 +08:00
|
|
|
|
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#if !_WIN32
|
2016-01-11 05:05:21 +08:00
|
|
|
|
RETSIGTYPE hup(int s)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2016-01-12 18:46:31 +08:00
|
|
|
|
(void)s;
|
2017-09-07 19:26:04 +08:00
|
|
|
|
syslog(LOG_INFO, "Received SIGHUP, reloading configuration...\n");
|
2016-01-11 03:46:19 +08:00
|
|
|
|
/* Learn the new rules */
|
2016-02-07 01:40:02 +08:00
|
|
|
|
clearConfiguration();
|
2017-09-05 02:14:11 +08:00
|
|
|
|
readConfiguration(options.conf_file);
|
2016-02-07 07:02:29 +08:00
|
|
|
|
#if !HAVE_SIGACTION
|
2016-01-11 03:46:19 +08:00
|
|
|
|
/* And reinstall the signal handler */
|
2016-01-11 03:43:31 +08:00
|
|
|
|
signal(SIGHUP, hup);
|
2016-01-11 05:05:21 +08:00
|
|
|
|
#endif
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-02-08 16:20:55 +08:00
|
|
|
|
#endif /* _WIN32 */
|
2016-01-11 03:43:31 +08:00
|
|
|
|
|
2016-02-07 01:40:02 +08:00
|
|
|
|
RETSIGTYPE quit(int s)
|
2016-01-12 18:46:31 +08:00
|
|
|
|
{
|
|
|
|
|
(void)s;
|
|
|
|
|
/* Obey the request, but first flush the log */
|
|
|
|
|
if (logFile) {
|
|
|
|
|
fclose(logFile);
|
|
|
|
|
}
|
2016-02-07 01:40:02 +08:00
|
|
|
|
/* ...and get rid of memory allocations */
|
|
|
|
|
setConnectionCount(0);
|
|
|
|
|
clearConfiguration();
|
2016-01-12 18:46:31 +08:00
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-06 23:58:54 +08:00
|
|
|
|
void registerPID(char const *pid_file_name)
|
2016-01-11 03:43:31 +08:00
|
|
|
|
{
|
2017-09-06 23:58:54 +08:00
|
|
|
|
#if defined(__linux__)
|
2016-02-06 22:07:12 +08:00
|
|
|
|
FILE *pid_file = fopen(pid_file_name, "w");
|
2016-01-11 03:43:31 +08:00
|
|
|
|
if (pid_file == NULL) {
|
|
|
|
|
/* non-fatal, non-Linux may lack /var/run... */
|
2016-01-11 03:46:19 +08:00
|
|
|
|
fprintf(stderr, "rinetd: Couldn't write to "
|
|
|
|
|
"%s. PID was not logged.\n", pid_file_name);
|
2016-01-11 05:41:50 +08:00
|
|
|
|
goto error;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
} else {
|
|
|
|
|
fprintf(pid_file, "%d\n", getpid());
|
2016-01-11 05:41:50 +08:00
|
|
|
|
/* errors aren't fatal */
|
2016-01-12 18:36:42 +08:00
|
|
|
|
if (fclose(pid_file))
|
2016-01-11 05:41:50 +08:00
|
|
|
|
goto error;
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-01-11 05:41:50 +08:00
|
|
|
|
return;
|
|
|
|
|
error:
|
|
|
|
|
syslog(LOG_ERR, "Couldn't write to "
|
|
|
|
|
"%s. PID was not logged (%m).\n", pid_file_name);
|
2016-02-09 09:28:19 +08:00
|
|
|
|
#else
|
2017-09-06 23:58:54 +08:00
|
|
|
|
/* add other systems with wherever they register processes */
|
2016-02-09 09:28:19 +08:00
|
|
|
|
(void)pid_file_name;
|
2017-09-06 23:58:54 +08:00
|
|
|
|
#endif
|
2016-01-11 03:43:31 +08:00
|
|
|
|
}
|
2016-01-11 03:46:19 +08:00
|
|
|
|
|
2016-02-08 09:36:23 +08:00
|
|
|
|
static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int result)
|
2016-01-11 03:46:19 +08:00
|
|
|
|
{
|
|
|
|
|
/* Bit of borrowing from Apache logging module here,
|
|
|
|
|
thanks folks */
|
|
|
|
|
int timz;
|
|
|
|
|
char tstr[1024];
|
2016-01-12 18:36:42 +08:00
|
|
|
|
struct tm *t = get_gmtoff(&timz);
|
|
|
|
|
char sign = (timz < 0 ? '-' : '+');
|
2016-01-11 03:46:19 +08:00
|
|
|
|
if (timz < 0) {
|
|
|
|
|
timz = -timz;
|
|
|
|
|
}
|
|
|
|
|
strftime(tstr, sizeof(tstr), "%d/%b/%Y:%H:%M:%S ", t);
|
2016-01-11 05:48:22 +08:00
|
|
|
|
|
2016-02-08 09:36:23 +08:00
|
|
|
|
char const *addressText = "?";
|
2016-01-12 18:36:42 +08:00
|
|
|
|
int bytesOutput = 0;
|
|
|
|
|
int bytesInput = 0;
|
2016-01-12 09:54:28 +08:00
|
|
|
|
if (cnx != NULL) {
|
2017-09-07 22:29:23 +08:00
|
|
|
|
addressText = inet_ntoa(cnx->remoteAddress.sin_addr);
|
2016-02-06 23:54:17 +08:00
|
|
|
|
bytesOutput = cnx->remote.sentBytes;
|
|
|
|
|
bytesInput = cnx->remote.recvBytes;
|
2016-01-11 03:46:19 +08:00
|
|
|
|
}
|
2016-02-08 09:36:23 +08:00
|
|
|
|
|
|
|
|
|
char const *fromHost = "?";
|
|
|
|
|
int fromPort = 0;
|
|
|
|
|
char const *toHost = "?";
|
|
|
|
|
int toPort = 0;
|
|
|
|
|
if (srv != NULL) {
|
|
|
|
|
fromHost = srv->fromHost;
|
|
|
|
|
fromPort = srv->fromPort;
|
|
|
|
|
toHost = srv->toHost;
|
|
|
|
|
toPort = srv->toPort;
|
|
|
|
|
}
|
2016-01-12 18:36:42 +08:00
|
|
|
|
|
|
|
|
|
if (result==logNotAllowed || result==logDenied)
|
2017-09-07 19:26:04 +08:00
|
|
|
|
syslog(LOG_INFO, "%s %s\n"
|
2016-01-12 18:36:42 +08:00
|
|
|
|
, addressText
|
|
|
|
|
, logMessages[result]);
|
2016-01-11 03:46:19 +08:00
|
|
|
|
if (logFile) {
|
|
|
|
|
if (logFormatCommon) {
|
|
|
|
|
/* Fake a common log format log file in a way that
|
|
|
|
|
most web analyzers can do something interesting with.
|
|
|
|
|
We lie and say the protocol is HTTP because we don't
|
|
|
|
|
want the web analyzer to reject the line. We also
|
|
|
|
|
lie and claim success (code 200) because we don't
|
|
|
|
|
want the web analyzer to ignore the line as an
|
|
|
|
|
error and not analyze the "URL." We put a result
|
|
|
|
|
message into our "URL" instead. The last field
|
|
|
|
|
is an extra, giving the number of input bytes,
|
2016-01-11 05:48:22 +08:00
|
|
|
|
after several placeholders meant to fill the
|
|
|
|
|
positions frequently occupied by user agent,
|
2016-01-11 03:46:19 +08:00
|
|
|
|
referrer, and server name information. */
|
2016-01-11 05:41:50 +08:00
|
|
|
|
fprintf(logFile, "%s - - "
|
2016-01-11 03:46:19 +08:00
|
|
|
|
"[%s %c%.2d%.2d] "
|
|
|
|
|
"\"GET /rinetd-services/%s/%d/%s/%d/%s HTTP/1.0\" "
|
|
|
|
|
"200 %d - - - %d\n",
|
2016-01-11 05:41:50 +08:00
|
|
|
|
addressText,
|
2016-01-11 03:46:19 +08:00
|
|
|
|
tstr,
|
|
|
|
|
sign,
|
|
|
|
|
timz / 60,
|
|
|
|
|
timz % 60,
|
2016-02-08 09:36:23 +08:00
|
|
|
|
fromHost, fromPort,
|
|
|
|
|
toHost, toPort,
|
2016-01-11 03:46:19 +08:00
|
|
|
|
logMessages[result],
|
|
|
|
|
bytesOutput,
|
|
|
|
|
bytesInput);
|
|
|
|
|
} else {
|
|
|
|
|
/* Write an rinetd-specific log entry with a
|
|
|
|
|
less goofy format. */
|
2016-01-11 05:41:50 +08:00
|
|
|
|
fprintf(logFile, "%s\t%s\t%s\t%d\t%s\t%d\t%d"
|
2016-01-11 03:46:19 +08:00
|
|
|
|
"\t%d\t%s\n",
|
|
|
|
|
tstr,
|
2016-01-11 05:41:50 +08:00
|
|
|
|
addressText,
|
2016-02-08 09:36:23 +08:00
|
|
|
|
fromHost, fromPort,
|
|
|
|
|
toHost, toPort,
|
2016-01-11 05:48:22 +08:00
|
|
|
|
bytesInput,
|
|
|
|
|
bytesOutput,
|
2016-01-11 03:46:19 +08:00
|
|
|
|
logMessages[result]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 01:10:44 +08:00
|
|
|
|
static int readArgs (int argc, char **argv, RinetdOptions *options)
|
2016-01-11 03:46:19 +08:00
|
|
|
|
{
|
2016-01-12 18:36:42 +08:00
|
|
|
|
for (;;) {
|
2016-01-11 03:46:19 +08:00
|
|
|
|
int option_index = 0;
|
|
|
|
|
static struct option long_options[] = {
|
|
|
|
|
{"conf-file", 1, 0, 'c'},
|
2016-01-11 04:31:08 +08:00
|
|
|
|
{"foreground", 0, 0, 'f'},
|
2016-01-11 03:46:19 +08:00
|
|
|
|
{"help", 0, 0, 'h'},
|
|
|
|
|
{"version", 0, 0, 'v'},
|
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
|
};
|
2016-01-12 18:36:42 +08:00
|
|
|
|
int c = getopt_long (argc, argv, "c:fshv",
|
2016-01-11 03:46:19 +08:00
|
|
|
|
long_options, &option_index);
|
|
|
|
|
if (c == -1) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch (c) {
|
|
|
|
|
case 'c':
|
2016-02-07 01:40:02 +08:00
|
|
|
|
options->conf_file = optarg;
|
2016-02-06 19:53:52 +08:00
|
|
|
|
if (!options->conf_file) {
|
|
|
|
|
syslog(LOG_ERR, "Not enough memory to "
|
|
|
|
|
"launch rinetd.\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-01-11 04:31:08 +08:00
|
|
|
|
case 'f':
|
2016-02-07 01:40:02 +08:00
|
|
|
|
options->foreground = 1;
|
2016-02-06 19:53:52 +08:00
|
|
|
|
break;
|
2016-01-11 03:46:19 +08:00
|
|
|
|
case 'h':
|
2016-02-06 19:53:52 +08:00
|
|
|
|
printf("Usage: rinetd [OPTION]\n"
|
|
|
|
|
" -c, --conf-file FILE read configuration "
|
|
|
|
|
"from FILE\n"
|
|
|
|
|
" -f, --foreground do not run in the "
|
|
|
|
|
"background\n"
|
|
|
|
|
" -h, --help display this help\n"
|
|
|
|
|
" -v, --version display version "
|
|
|
|
|
"number\n\n");
|
|
|
|
|
printf("Most options are controlled through the\n"
|
|
|
|
|
"configuration file. See the rinetd(8)\n"
|
|
|
|
|
"manpage for more information.\n");
|
|
|
|
|
exit (0);
|
2016-01-11 03:46:19 +08:00
|
|
|
|
case 'v':
|
2016-02-06 19:53:52 +08:00
|
|
|
|
printf ("rinetd %s\n", PACKAGE_VERSION);
|
|
|
|
|
exit (0);
|
2016-01-11 03:46:19 +08:00
|
|
|
|
case '?':
|
|
|
|
|
default:
|
2016-02-06 19:53:52 +08:00
|
|
|
|
exit (1);
|
2016-01-11 03:46:19 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-11 03:50:29 +08:00
|
|
|
|
return 0;
|
2016-01-11 03:46:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get_gmtoff was borrowed from Apache. Thanks folks. */
|
|
|
|
|
|
2017-09-07 00:19:57 +08:00
|
|
|
|
static struct tm *get_gmtoff(int *tz)
|
|
|
|
|
{
|
2016-01-11 03:46:19 +08:00
|
|
|
|
time_t tt = time(NULL);
|
|
|
|
|
|
|
|
|
|
/* Assume we are never more than 24 hours away. */
|
2017-09-07 00:19:57 +08:00
|
|
|
|
struct tm gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */
|
|
|
|
|
struct tm *t = localtime(&tt); /* buffer... so be careful */
|
|
|
|
|
int days = t->tm_yday - gmt.tm_yday;
|
|
|
|
|
int hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24)
|
2016-01-11 03:46:19 +08:00
|
|
|
|
+ t->tm_hour - gmt.tm_hour);
|
2017-09-07 00:19:57 +08:00
|
|
|
|
int minutes = hours * 60 + t->tm_min - gmt.tm_min;
|
2016-01-11 03:46:19 +08:00
|
|
|
|
*tz = minutes;
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|