7 Commits
v0.72 ... main

Author SHA1 Message Date
Sam Hocevar
d4e0a60119 Fix a parsing bug in the rules that would ignore the very first rule. See #23. 2021-03-02 15:25:39 +01:00
Sam Hocevar
af88e6fd4f Release version 0.73. 2021-02-19 09:56:52 +01:00
Sam Hocevar
ff41723590 Improve error reporting. Fixes #15.
- Add an error message when the configuration file could not be found.
 - Log to stderr when running in the foreground.
2021-02-19 09:12:29 +01:00
Sam Hocevar
d3dd35327f Do not log to syslog when running in the foreground. Addresses #15. 2021-02-19 08:55:45 +01:00
Sam Hocevar
49bf544bad Fix another IPv6 parsing issue. Addresses #20. 2021-02-17 14:15:54 +01:00
Sam Hocevar
1060048fe8 Refactor getaddrinfo error reporting. 2021-02-17 08:21:58 +01:00
Sam Hocevar
e3b47d086a Fix some hostnames (e.g. 123.example.com) being wrongfully parsed as IPv6. 2021-02-17 08:17:03 +01:00
10 changed files with 407 additions and 360 deletions

View File

@@ -1,3 +1,8 @@
## Version 0.73 (2021/02/19)
* improve error reporting
* fixed another configuration parsing bug
## Version 0.72 (2021/02/16) ## Version 0.72 (2021/02/16)
* fixed a configuration parsing bug making 0.71 almost unusable * fixed a configuration parsing bug making 0.71 almost unusable

View File

@@ -1,6 +1,6 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.52) AC_PREREQ(2.52)
AC_INIT(rinetd, 0.72, sam@hocevar.net) AC_INIT(rinetd, 0.73, sam@hocevar.net)
AC_CONFIG_AUX_DIR(.auto) AC_CONFIG_AUX_DIR(.auto)
AC_CONFIG_SRCDIR([src/rinetd.c]) AC_CONFIG_SRCDIR([src/rinetd.c])
AC_CONFIG_HEADER([src/config.h]) AC_CONFIG_HEADER([src/config.h])

View File

@@ -1,4 +1,4 @@
.TH rinetd 8 "2021-02-12" "rinetd 0.72" .TH rinetd 8 "2021-02-19" "rinetd 0.73"
.SH NAME .SH NAME
rinetd \- internet redirection server rinetd \- internet redirection server

View File

@@ -78,7 +78,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<CompileAs>CompileAsC</CompileAs> <CompileAs>CompileAsC</CompileAs>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<PreprocessorDefinitions>_CONSOLE;PACKAGE_VERSION="0.72";_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_CONSOLE;PACKAGE_VERSION="0.73";_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>

View File

@@ -10,6 +10,7 @@
# include <config.h> # include <config.h>
#endif #endif
#include <stdio.h>
#include "net.h" #include "net.h"
void setSocketDefaults(SOCKET fd) { void setSocketDefaults(SOCKET fd) {
@@ -32,13 +33,23 @@ void setSocketDefaults(SOCKET fd) {
#endif #endif
} }
struct addrinfo getAddrInfoHint(int protocol) { int getAddrInfoWithProto(char *address, char *port, int protocol, struct addrinfo **ai)
return (struct addrinfo) { {
struct addrinfo hints = {
.ai_family = AF_UNSPEC, .ai_family = AF_UNSPEC,
.ai_protocol = protocol, .ai_protocol = protocol,
.ai_socktype = protocol == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM, .ai_socktype = protocol == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM,
.ai_flags = AI_PASSIVE, .ai_flags = AI_PASSIVE,
}; };
int ret = getaddrinfo(address, port, &hints, ai);
if (ret != 0) {
fprintf(stderr, "rinetd: cannot resolve host \"%s\" port %s "
"(getaddrinfo() error: %s)\n",
address, port ? port : "<null>", gai_strerror(ret));
}
return ret;
} }
int sameSocketAddress(struct sockaddr_storage *a, struct sockaddr_storage *b) { int sameSocketAddress(struct sockaddr_storage *a, struct sockaddr_storage *b) {

View File

@@ -85,6 +85,6 @@ static inline int GetLastError(void) {
#endif /* _WIN32 */ #endif /* _WIN32 */
void setSocketDefaults(SOCKET fd); void setSocketDefaults(SOCKET fd);
struct addrinfo getAddrInfoHint(int protocol);
int sameSocketAddress(struct sockaddr_storage *a, struct sockaddr_storage *b); int sameSocketAddress(struct sockaddr_storage *a, struct sockaddr_storage *b);
int getAddrInfoWithProto(char *address, char *port, int protocol, struct addrinfo **ai);
uint16_t getPort(struct addrinfo* ai); uint16_t getPort(struct addrinfo* ai);

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,10 @@
result = (EOF == yyc) ? 0 : (*(buf) = yyc, 1); \ result = (EOF == yyc) ? 0 : (*(buf) = yyc, 1); \
} }
#define PARSE_ERROR exit(1); #define PARSE_ERROR exit(1);
#define MEMORY_ERROR { \
logError("could not allocate memory when parsing configuration.\n"); \
exit(1); \
}
#if defined __clang__ #if defined __clang__
#pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wunused-parameter"
@@ -86,15 +90,15 @@ auth-rule = auth-key - < pattern >
allRules = (Rule *) allRules = (Rule *)
realloc(allRules, sizeof(Rule) * (allRulesCount + 1)); realloc(allRules, sizeof(Rule) * (allRulesCount + 1));
if (!allRules) { if (!allRules) {
PARSE_ERROR; MEMORY_ERROR;
} }
allRules[allRulesCount].pattern = strdup(yytext); allRules[allRulesCount].pattern = strdup(yytext);
if (!allRules[allRulesCount].pattern) { if (!allRules[allRulesCount].pattern) {
PARSE_ERROR; MEMORY_ERROR;
} }
allRules[allRulesCount].type = yy->isAuthAllow ? allowRule : denyRule; allRules[allRulesCount].type = yy->isAuthAllow ? allowRule : denyRule;
if (seTotal > 0) { if (seTotal > 0) {
if (seInfo[seTotal - 1].rulesStart == 0) { if (seInfo[seTotal - 1].rulesStart == 0 && seInfo[seTotal - 1].rulesCount == 0) {
seInfo[seTotal - 1].rulesStart = allRulesCount; seInfo[seTotal - 1].rulesStart = allRulesCount;
} }
++seInfo[seTotal - 1].rulesCount; ++seInfo[seTotal - 1].rulesCount;
@@ -110,7 +114,7 @@ logfile = "logfile" - < filename >
{ {
logFileName = strdup(yytext); logFileName = strdup(yytext);
if (!logFileName) { if (!logFileName) {
PARSE_ERROR; MEMORY_ERROR;
} }
} }
@@ -118,7 +122,7 @@ pidlogfile = "pidlogfile" - < filename >
{ {
pidLogFileName = strdup(yytext); pidLogFileName = strdup(yytext);
if (!pidLogFileName) { if (!pidLogFileName) {
PARSE_ERROR; MEMORY_ERROR;
} }
} }
@@ -129,7 +133,7 @@ logcommon = "logcommon"
invalid_syntax = < (!eol .)+ > eol invalid_syntax = < (!eol .)+ > eol
{ {
fprintf(stderr, "rinetd: invalid syntax at line %d: %s\n", logError("invalid syntax at line %d: %s\n",
yy->currentLine, yytext); yy->currentLine, yytext);
PARSE_ERROR; /* FIXME */ PARSE_ERROR; /* FIXME */
} }
@@ -139,9 +143,10 @@ address = ipv4 | ipv6 | hostname
pattern = (hexdigit | '[' | ']' | ':' | '.' | [*?] )+ pattern = (hexdigit | '[' | ']' | ':' | '.' | [*?] )+
number = digit+ number = digit+
ipv4 = number '.' number '.' number '.' number | '0' ipv4 = number '.' number '.' number '.' number | '0'
ipv6 = (hexdigit | ':')+ | '[' (hexdigit | ':')+ ']' ipv6 = '[' bare_ipv6 ']' | bare_ipv6
hostname = (label '.')* name '.'? bare_ipv6 = hexdigit* ':' (hexdigit | ':')* # Must have at least one ':'
hostname = (label '.')* name '.'?
name = id ('-' | id | digit)* name = id ('-' | id | digit)*
@@ -163,15 +168,15 @@ void parseConfiguration(char const *file)
{ {
FILE *in = fopen(file, "r"); FILE *in = fopen(file, "r");
if (!in) { if (!in) {
PARSE_ERROR; logError("could not open configuration file %s.\n", file);
exit(1);
} }
yycontext ctx; yycontext ctx;
memset(&ctx, 0, sizeof(yycontext)); memset(&ctx, 0, sizeof(yycontext));
ctx.fp = in; ctx.fp = in;
if (!yyparse(&ctx)) { if (!yyparse(&ctx)) {
syslog(LOG_ERR, "invalid syntax " logError("invalid syntax in file %s, line %d.\n", file, -1);
"on file %s, line %d.\n", file, -1);
exit(1); exit(1);
} }
yyrelease(&ctx); yyrelease(&ctx);

View File

@@ -28,9 +28,11 @@
# elif HAVE_SYS_TIME_H # elif HAVE_SYS_TIME_H
# include <sys/time.h> # include <sys/time.h>
# endif # endif
# include <syslog.h>
#endif /* _WIN32 */ #endif /* _WIN32 */
#include <stdio.h> #include <stdio.h>
#include <stdarg.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <stdlib.h> #include <stdlib.h>
@@ -106,11 +108,13 @@ enum {
logDenied, logDenied,
}; };
RinetdOptions options = { static RinetdOptions options = {
RINETD_CONFIG_FILE, RINETD_CONFIG_FILE,
0, 0,
}; };
static int forked = 0;
static void selectPass(void); static void selectPass(void);
static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socket); static void handleWrite(ConnectionInfo *cnx, Socket *socket, Socket *other_socket);
static void handleRead(ConnectionInfo *cnx, Socket *socket, Socket *other_socket); static void handleRead(ConnectionInfo *cnx, Socket *socket, Socket *other_socket);
@@ -158,15 +162,19 @@ int main(int argc, char *argv[])
readArgs(argc, argv, &options); readArgs(argc, argv, &options);
if (!options.foreground) {
#if HAVE_DAEMON && !DEBUG #if HAVE_DAEMON && !DEBUG
if (!options.foreground && daemon(0, 0) != 0) { if (daemon(0, 0) != 0) {
exit(0); exit(0);
} }
forked = 1;
#elif HAVE_FORK && !DEBUG #elif HAVE_FORK && !DEBUG
if (!options.foreground && fork() != 0) { if (fork() != 0) {
exit(0); exit(0);
} }
forked = 1;
#endif #endif
}
#if HAVE_SIGACTION #if HAVE_SIGACTION
struct sigaction act; struct sigaction act;
@@ -188,7 +196,7 @@ int main(int argc, char *argv[])
registerPID(pidLogFileName ? pidLogFileName : RINETD_PID_FILE); registerPID(pidLogFileName ? pidLogFileName : RINETD_PID_FILE);
} }
syslog(LOG_INFO, "Starting redirections...\n"); logInfo("starting redirections...\n");
while (1) { while (1) {
selectPass(); selectPass();
} }
@@ -196,6 +204,38 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
void logError(char const *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
#if !_WIN32
if (forked)
vsyslog(LOG_ERR, fmt, ap);
else
#endif
{
fprintf(stderr, "rinetd error: ");
vfprintf(stderr, fmt, ap);
}
va_end(ap);
}
void logInfo(char const *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
#if !_WIN32
if (forked)
vsyslog(LOG_INFO, fmt, ap);
else
#endif
{
fprintf(stderr, "rinetd: ");
vfprintf(stderr, fmt, ap);
}
va_end(ap);
}
static void clearConfiguration(void) { static void clearConfiguration(void) {
/* Remove references to server information */ /* Remove references to server information */
for (int i = 0; i < coTotal; ++i) { for (int i = 0; i < coTotal; ++i) {
@@ -250,7 +290,7 @@ static void readConfiguration(char const *file) {
if (logFile) { if (logFile) {
setvbuf(logFile, NULL, _IONBF, 0); setvbuf(logFile, NULL, _IONBF, 0);
} else { } else {
syslog(LOG_ERR, "could not open %s to append (%m).\n", logError("could not open %s to append (%m).\n",
logFileName); logFileName);
} }
} }
@@ -267,16 +307,15 @@ void addServer(char *bindAddress, char *bindPort, int bindProtocol,
}; };
/* Make a server socket */ /* Make a server socket */
struct addrinfo hints = getAddrInfoHint(bindProtocol), *ai; struct addrinfo *ai;
int ret = getaddrinfo(bindAddress, bindPort, &hints, &ai); int ret = getAddrInfoWithProto(bindAddress, bindPort, bindProtocol, &ai);
if (ret != 0) { if (ret != 0) {
fprintf(stderr, "rinetd: getaddrinfo error: %s\n", gai_strerror(ret));
exit(1); exit(1);
} }
si.fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); si.fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (si.fd == INVALID_SOCKET) { if (si.fd == INVALID_SOCKET) {
syslog(LOG_ERR, "couldn't create server socket! (%m)\n"); logError("couldn't create server socket! (%m)\n");
freeaddrinfo(ai); freeaddrinfo(ai);
exit(1); exit(1);
} }
@@ -285,7 +324,7 @@ void addServer(char *bindAddress, char *bindPort, int bindProtocol,
setsockopt(si.fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&tmp, sizeof(tmp)); setsockopt(si.fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&tmp, sizeof(tmp));
if (bind(si.fd, ai->ai_addr, ai->ai_addrlen) == SOCKET_ERROR) { if (bind(si.fd, ai->ai_addr, ai->ai_addrlen) == SOCKET_ERROR) {
syslog(LOG_ERR, "couldn't bind to address %s port %s (%m)\n", logError("couldn't bind to address %s port %s (%m)\n",
bindAddress, bindPort); bindAddress, bindPort);
closesocket(si.fd); closesocket(si.fd);
freeaddrinfo(ai); freeaddrinfo(ai);
@@ -295,7 +334,7 @@ void addServer(char *bindAddress, char *bindPort, int bindProtocol,
if (bindProtocol == IPPROTO_TCP) { if (bindProtocol == IPPROTO_TCP) {
if (listen(si.fd, RINETD_LISTEN_BACKLOG) == SOCKET_ERROR) { if (listen(si.fd, RINETD_LISTEN_BACKLOG) == SOCKET_ERROR) {
/* Warn -- don't exit. */ /* Warn -- don't exit. */
syslog(LOG_ERR, "couldn't listen to address %s port %s (%m)\n", logError("couldn't listen to address %s port %s (%m)\n",
bindAddress, bindPort); bindAddress, bindPort);
/* XXX: check whether this is correct */ /* XXX: check whether this is correct */
closesocket(si.fd); closesocket(si.fd);
@@ -308,10 +347,8 @@ void addServer(char *bindAddress, char *bindPort, int bindProtocol,
si.fromAddrInfo = ai; si.fromAddrInfo = ai;
/* Resolve destination address. */ /* Resolve destination address. */
hints = getAddrInfoHint(connectProtocol); ret = getAddrInfoWithProto(connectAddress, connectPort, connectProtocol, &ai);
ret = getaddrinfo(connectAddress, connectPort, &hints, &ai);
if (ret != 0) { if (ret != 0) {
fprintf(stderr, "rinetd: getaddrinfo error: %s\n", gai_strerror(ret));
freeaddrinfo(si.fromAddrInfo); freeaddrinfo(si.fromAddrInfo);
closesocket(si.fd); closesocket(si.fd);
exit(1); exit(1);
@@ -320,10 +357,8 @@ void addServer(char *bindAddress, char *bindPort, int bindProtocol,
/* Resolve source address if applicable. */ /* Resolve source address if applicable. */
if (sourceAddress) { if (sourceAddress) {
hints = getAddrInfoHint(connectProtocol); ret = getAddrInfoWithProto(sourceAddress, NULL, connectProtocol, &ai);
ret = getaddrinfo(sourceAddress, NULL, &hints, &ai);
if (ret != 0) { if (ret != 0) {
fprintf(stderr, "rinetd: getaddrinfo error: %s\n", gai_strerror(ret));
freeaddrinfo(si.fromAddrInfo); freeaddrinfo(si.fromAddrInfo);
freeaddrinfo(si.toAddrInfo); freeaddrinfo(si.toAddrInfo);
exit(1); exit(1);
@@ -413,7 +448,7 @@ static ConnectionInfo *findAvailableConnection(void)
int oldTotal = coTotal; int oldTotal = coTotal;
setConnectionCount(coTotal * 4 / 3 + 8); setConnectionCount(coTotal * 4 / 3 + 8);
if (coTotal == oldTotal) { if (coTotal == oldTotal) {
syslog(LOG_ERR, "not enough memory to add slots. " logError("not enough memory to add slots. "
"Currently %d slots.\n", coTotal); "Currently %d slots.\n", coTotal);
/* Go back to the previous total number of slots */ /* Go back to the previous total number of slots */
return NULL; return NULL;
@@ -650,7 +685,7 @@ static void handleAccept(ServerInfo const *srv)
/* In TCP mode, get remote address using accept(). */ /* In TCP mode, get remote address using accept(). */
nfd = accept(srv->fd, (struct sockaddr *)&addr, &addrlen); nfd = accept(srv->fd, (struct sockaddr *)&addr, &addrlen);
if (nfd == INVALID_SOCKET) { if (nfd == INVALID_SOCKET) {
syslog(LOG_ERR, "accept(%llu): %m\n", (long long unsigned int)srv->fd); logError("accept(%llu): %m\n", (long long unsigned int)srv->fd);
logEvent(NULL, srv, logAcceptFailed); logEvent(NULL, srv, logAcceptFailed);
return; return;
} }
@@ -671,7 +706,7 @@ static void handleAccept(ServerInfo const *srv)
if (GetLastError() == WSAEINPROGRESS) { if (GetLastError() == WSAEINPROGRESS) {
return; return;
} }
syslog(LOG_ERR, "recvfrom(%llu): %m\n", (long long unsigned int)srv->fd); logError("recvfrom(%llu): %m\n", (long long unsigned int)srv->fd);
logEvent(NULL, srv, logAcceptFailed); logEvent(NULL, srv, logAcceptFailed);
return; return;
} }
@@ -730,7 +765,7 @@ static void handleAccept(ServerInfo const *srv)
struct addrinfo* to = srv->toAddrInfo; struct addrinfo* to = srv->toAddrInfo;
cnx->local.fd = socket(to->ai_family, to->ai_socktype, to->ai_protocol); cnx->local.fd = socket(to->ai_family, to->ai_socktype, to->ai_protocol);
if (cnx->local.fd == INVALID_SOCKET) { if (cnx->local.fd == INVALID_SOCKET) {
syslog(LOG_ERR, "socket(): %m\n"); logError("socket(): %m\n");
if (cnx->remote.protocol == IPPROTO_TCP) if (cnx->remote.protocol == IPPROTO_TCP)
closesocket(cnx->remote.fd); closesocket(cnx->remote.fd);
cnx->remote.fd = INVALID_SOCKET; cnx->remote.fd = INVALID_SOCKET;
@@ -746,7 +781,7 @@ static void handleAccept(ServerInfo const *srv)
if (srv->sourceAddrInfo) { if (srv->sourceAddrInfo) {
if (bind(cnx->local.fd, srv->sourceAddrInfo->ai_addr, if (bind(cnx->local.fd, srv->sourceAddrInfo->ai_addr,
srv->sourceAddrInfo->ai_addrlen) == SOCKET_ERROR) { srv->sourceAddrInfo->ai_addrlen) == SOCKET_ERROR) {
syslog(LOG_ERR, "bind(): %m\n"); logError("bind(): %m\n");
} }
} }
@@ -866,7 +901,7 @@ RETSIGTYPE plumber(int s)
RETSIGTYPE hup(int s) RETSIGTYPE hup(int s)
{ {
(void)s; (void)s;
syslog(LOG_INFO, "Received SIGHUP, reloading configuration...\n"); logInfo("received SIGHUP, reloading configuration...\n");
/* Learn the new rules */ /* Learn the new rules */
clearConfiguration(); clearConfiguration();
readConfiguration(options.conf_file); readConfiguration(options.conf_file);
@@ -896,8 +931,6 @@ void registerPID(char const *pid_file_name)
FILE *pid_file = fopen(pid_file_name, "w"); FILE *pid_file = fopen(pid_file_name, "w");
if (pid_file == NULL) { if (pid_file == NULL) {
/* non-fatal, non-Linux may lack /var/run... */ /* non-fatal, non-Linux may lack /var/run... */
fprintf(stderr, "rinetd: Couldn't write to "
"%s. PID was not logged.\n", pid_file_name);
goto error; goto error;
} else { } else {
fprintf(pid_file, "%d\n", getpid()); fprintf(pid_file, "%d\n", getpid());
@@ -907,8 +940,8 @@ void registerPID(char const *pid_file_name)
} }
return; return;
error: error:
syslog(LOG_ERR, "Couldn't write to " logError("couldn't write to %s. PID was not logged (%m).\n",
"%s. PID was not logged (%m).\n", pid_file_name); pid_file_name);
#else #else
/* add other systems with wherever they register processes */ /* add other systems with wherever they register processes */
(void)pid_file_name; (void)pid_file_name;
@@ -947,7 +980,7 @@ static void logEvent(ConnectionInfo const *cnx, ServerInfo const *srv, int resul
} }
if (result==logNotAllowed || result==logDenied) if (result==logNotAllowed || result==logDenied)
syslog(LOG_INFO, "%s %s\n" logInfo("%s %s\n"
, addressText , addressText
, logMessages[result]); , logMessages[result]);
if (logFile) { if (logFile) {
@@ -1014,7 +1047,7 @@ static int readArgs (int argc, char **argv, RinetdOptions *options)
case 'c': case 'c':
options->conf_file = optarg; options->conf_file = optarg;
if (!options->conf_file) { if (!options->conf_file) {
syslog(LOG_ERR, "Not enough memory to " fprintf(stderr, "Not enough memory to "
"launch rinetd.\n"); "launch rinetd.\n");
exit(1); exit(1);
} }

View File

@@ -8,17 +8,6 @@
#pragma once #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 */
#include <stdint.h> #include <stdint.h>
/* Constants */ /* Constants */
@@ -46,6 +35,8 @@ extern FILE *logFile;
/* Functions */ /* Functions */
void logError(char const *fmt, ...);
void logInfo(char const *fmt, ...);
void addServer(char *bindAddress, char *bindPort, int bindProtocol, void addServer(char *bindAddress, char *bindPort, int bindProtocol,
char *connectAddress, char *connectPort, int connectProtocol, char *connectAddress, char *connectPort, int connectProtocol,
int serverTimeout, char *sourceAddress); int serverTimeout, char *sourceAddress);