/*
* Copyright (c) 2002-2006 Hajimu UMEMOTO <ume@mahoroba.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Mahoroba: src/dtcpclient/dtcpclient.c,v 1.25 2006/05/25 16:36:21 ume Exp $
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <errno.h>
#include <libgen.h>
#include <netdb.h>
#include <paths.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sysexits.h>
#include <unistd.h>
#ifdef HAVE_MD5
#include <md5.h>
#else
#include <openssl/ssl.h>
#include <openssl/md5.h>
#define MD5Init MD5_Init
#define MD5Update MD5_Update
#define MD5Final MD5_Final
#endif
#define SERVICE_DTCP "20200"
#define KEEPALIVE_INTERVAL 60
#define TIMEOUT 60
#define RECONNECT_WAIT 10
#define MAXLINELEN 1024
#define DELIM " \t"
#define UDP_TUNNEL_PORT 4028
#ifndef _PATH_VARRUN
#define _PATH_VARRUN "/var/run"
#endif
#ifndef PREFIX
#define PREFIX "/usr/local"
#endif
#define PIDFILE _PATH_VARRUN "/dtcpclient.pid"
#define PASSWDFILE PREFIX "/etc/dtcpclient.auth"
#define SCRIPTFLIE PREFIX "/etc/dtcpclient.script"
typedef enum {
FALSE = 0,
TRUE = 1
} boolean;
typedef enum {
TUNNEL_ONLY,
TUNNEL_HOST,
TUNNEL_NETWORK
} tuntype;
static char *tuntype_table[] = {
"tunnelonly",
"host",
"network"
};
struct tunnel_info {
char *nickname;
char *server;
char *myaddr;
tuntype tuntype;
char *tuninfo[6];
};
static struct tunnel_info *tunnel = NULL;
static int dtcp_sock = -1;
static boolean dtcp_error = FALSE;
static boolean dtcp_connected = FALSE;
static char *pidfile = PIDFILE;
static char *script = SCRIPTFLIE;
static boolean behind_nat = FALSE;
static boolean debug = FALSE;
static boolean daemonize = FALSE;
static int mtu = 0;
static boolean verbose = FALSE;
static boolean syslog_opened = FALSE;
static boolean reconnect_request = FALSE;
static int signalpipe[2];
static void bye(int);
static void
usage(char *progname)
{
fprintf(stderr,
"usage: %s [-dDhlnU] [-b udpport] [-B naptport] [-e nickname] [-f pidfile] [-m mtu] [-p port] [-s script] [-t tuntype] [-u username] server\n",
progname);
exit(EX_USAGE);
}
static void
logmsg(int priority, const char *fmt, ...)
{
static char buf[MAXLINELEN];
va_list ap;
buf[0] = '\0';
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 3, fmt, ap);
va_end(ap);
if (syslog_opened)
syslog(priority, "%s", buf);
else
fprintf(stderr, "%s\n", buf);
}
static void
debugmsg(const char *fmt, ...)
{
static char buf[MAXLINELEN];
va_list ap;
if (!debug)
return;
buf[0] = '\0';
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 3, fmt, ap);
va_end(ap);
if (syslog_opened)
syslog(LOG_DEBUG, "%s", buf);
else
fprintf(stderr, "%s\n", buf);
}
static char *
authenticate(char *user, char *challenge, char *pass)
{
static const char hex[] = "0123456789ABCDEF";
static char ascii_digest[33];
MD5_CTX ctx;
unsigned char digest[16];
int i;
MD5Init(&ctx);
MD5Update(&ctx, user, strlen(user));
MD5Update(&ctx, challenge, strlen(challenge));
MD5Update(&ctx, pass, strlen(pass));
MD5Final(digest, &ctx);
for (i = 0; i < 16; i++) {
ascii_digest[i + i] = hex[digest[i] >> 4];
ascii_digest[i + i + 1] = hex[digest[i] & 0x0f];
}
ascii_digest[i + i] = '\0';
return ascii_digest;
}
static boolean
pidfile_create()
{
FILE *fp;
if ((fp = fopen(pidfile, "w")) == NULL)
return FALSE;
fprintf(fp, "%d\n", getpid());
fclose(fp);
return TRUE;
}
static void
pidfile_delete()
{
FILE *fp;
char buf[16];
if ((fp = fopen(pidfile, "r")) == NULL)
return;
if (fgets(buf, sizeof(buf), fp) == NULL)
return;
fclose(fp);
if (atoi(buf) == getpid())
unlink(pidfile);
}
static char *
getpassword_file(char *dst, char *user)
{
FILE *fp;
char buf[MAXLINELEN];
char *sep = ":";
char *p, *pw_dst, *pw_user, *pw_passwd;
if ((fp = fopen(PASSWDFILE, "r")) == NULL) {
debugmsg("no authinfo file found");
return NULL;
}
while (fgets(buf, sizeof(buf), fp) != NULL) {
if ((p = strrchr(buf, '\n')) != NULL)
*p = '\0';
if ((pw_dst = strtok(buf, sep)) == NULL)
continue;
if ((pw_user = strtok(NULL, sep)) == NULL)
continue;
if (strcmp(pw_dst, dst) != 0 || strcmp(pw_user, user) != 0)
continue;
if ((pw_passwd = strtok(NULL, sep)) == NULL)
continue;
fclose(fp);
return strdup(pw_passwd);
}
fclose(fp);
debugmsg("no relevant authinfo item found");
return NULL;
}
static char *
getpassword_tty(char *user)
{
char prompt[MAXLINELEN];
char *password;
snprintf(prompt, sizeof(prompt), "password for %s: ", user);
if ((password = getpass(prompt)) == NULL)
return NULL;
password = strdup(password);
return password;
}
char *
getpassword(char *dst, char *user)
{
char *password;
if ((password = getpassword_file(dst, user)) == NULL)
password = getpassword_tty(user);
return password;
}
static int
connect_to(char *host, char *port, sa_family_t family)
{
struct addrinfo hints, *res, *res0;
int err, sock;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if ((err = getaddrinfo(host, port, &hints, &res0)) != 0) {
logmsg(LOG_ERR, "getaddrinfo failed (dst=%s port=%s): %s",
host, port, gai_strerror(err));
return -1;
}
for (sock = -1, res = res0; res; res = res->ai_next) {
if ((sock = socket(res->ai_family, res->ai_socktype,
res->ai_protocol)) == -1)
continue;
if (connect(sock, res->ai_addr, res->ai_addrlen) != -1)
break;
close(sock);
sock = -1;
}
freeaddrinfo(res0);
if (sock == -1) {
logmsg(LOG_ERR, "could not connect to %s port %s", host, port);
return -1;
}
return sock;
}
static ssize_t
putbuf(char *str, size_t nbytes, int fd)
{
char *cp;
ssize_t nleft, nwritten;
cp = str;
nleft = nbytes;
while (nleft > 0) {
if ((nwritten = write(fd, cp, nleft)) < 0) {
if (errno != EINTR)
dtcp_error = TRUE;
if (verbose)
logmsg(LOG_ERR, "write failed: %s",
strerror(errno));
return -1;
}
nleft -= nwritten;
cp += nwritten;
}
return nbytes - nleft;
}
static int
dtcp_send(int fd, const char *fmt, ...)
{
char buf[MAXLINELEN];
va_list ap;
buf[0] = '\0';
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 3, fmt, ap);
va_end(ap);
debugmsg("<<< %s", buf);
strlcat(buf, "\r\n", sizeof(buf));
if (putbuf(buf, strlen(buf), fd) < 0)
return -1;
return 0;
}
static ssize_t
dtcp_recv(int sock, char *buf, size_t len)
{
fd_set readfds;
int maxsock, nsig, err;
struct timeval tv;
ssize_t n, nread;
maxsock = (sock >= signalpipe[0]) ? sock : signalpipe[0];
nread = 0;
while (len > nread) {
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
FD_SET(signalpipe[0], &readfds);
tv.tv_sec = TIMEOUT;
tv.tv_usec = 0;
err = select(maxsock + 1, &readfds, NULL, NULL, &tv);
if (err <= 0) {
if (err == 0) {
if (verbose)
logmsg(LOG_ERR, "select timeout");
} else if (errno == EINTR)
continue;
if (verbose)
logmsg(LOG_ERR, "select failed: %s",
strerror(errno));
dtcp_error = TRUE;
return -1;
}
if (FD_ISSET(signalpipe[0], &readfds)) {
if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) {
logmsg(LOG_ERR, "ioctl failed: %s",
strerror(errno));
bye(EX_OSERR);
/* NOTREACHED */
}
while (--nsig >= 0) {
char c;
if (read(signalpipe[0], &c, 1) != 1) {
if (verbose)
logmsg(LOG_ERR,
"read failed: %s",
strerror(errno));
bye(EX_OSERR);
/* NOTREACHED */
}
debugmsg("handling signal flag %c", c);
switch(c) {
case 'H': /* sighup */
reconnect_request = TRUE;
return -1;
}
}
}
if (FD_ISSET(sock, &readfds)) {
if ((n = read(sock, buf + nread, len - nread)) < 0) {
if (errno != EINTR)
dtcp_error = TRUE;
if (verbose)
logmsg(LOG_ERR, "read failed: %s",
strerror(errno));
return -1;
}
if (n == 0)
continue;
nread += n;
if (nread < 2 || buf[nread - 1] != '\n' ||
buf[nread - 2] != '\r')
continue;
nread -= 2;
buf[nread] = '\0';
debugmsg(">>> %s", buf);
return nread;
}
}
if (verbose)
logmsg(LOG_ERR, "response too long");
return -1;
}
static void
dtcp_close()
{
if (dtcp_sock < 0)
return;
if (!dtcp_error)
dtcp_send(dtcp_sock, "quit");
shutdown(dtcp_sock, 1);
close(dtcp_sock);
dtcp_sock = -1;
}
static boolean
tunnel_delete(struct tunnel_info *tunnel)
{
int i;
if (tunnel != NULL)
return FALSE;
if (tunnel->nickname) {
free(tunnel->nickname);
tunnel->nickname = NULL;
}
if (tunnel->server) {
free(tunnel->server);
tunnel->server = NULL;
}
if (tunnel->myaddr) {
free(tunnel->myaddr);
tunnel->myaddr = NULL;
}
for (i = 0; tunnel->tuninfo[i] != NULL; ++i) {
free(tunnel->tuninfo[i]);
tunnel->tuninfo[i] = NULL;
}
free(tunnel);
return TRUE;
}
static struct tunnel_info *
tunnel_new(const char *nickname, const char *server, const char *myaddr,
const tuntype tuntype, const char *tuninfo[])
{
struct tunnel_info *tunnel;
int i;
tunnel = (struct tunnel_info *)malloc(sizeof(struct tunnel_info));
if (tunnel == NULL)
return NULL;
memset(tunnel, 0, sizeof(*tunnel));
if (nickname && (tunnel->nickname = strdup(nickname)) == NULL) {
tunnel_delete(tunnel);
return NULL;
}
if ((tunnel->server = strdup(server)) == NULL) {
tunnel_delete(tunnel);
return NULL;
}
if ((tunnel->myaddr = strdup(myaddr)) == NULL) {
tunnel_delete(tunnel);
return NULL;
}
tunnel->tuntype = tuntype;
for (i = 0; i < 5 && tuninfo[i] != NULL; ++i)
if ((tunnel->tuninfo[i] = strdup(tuninfo[i])) == NULL) {
tunnel_delete(tunnel);
return NULL;
}
tunnel->tuninfo[i] = NULL;
return tunnel;
}
static void
invoke_script(const char *state, const char *server, const char*myaddr,
const tuntype tuntype, const char *tuninfo[])
{
pid_t pid;
char buf[MAXLINELEN];
char *argv[11];
int argc = 0, i;
int status;
argv[argc++] = script;
argv[argc++] = (char *)state;
argv[argc++] = (char *)server;
argv[argc++] = (char *)myaddr;
argv[argc++] = tuntype_table[tuntype];
for (i = 0; i < 5; ++i)
argv[argc++] = (char *)tuninfo[i];
argv[argc] = NULL;
if (verbose) {
strncpy(buf, "calling:", sizeof(buf));
for (argc = 0; argv[argc] != NULL; ++argc) {
strncat(buf, " ", sizeof(buf));
strncat(buf, argv[argc], sizeof(buf));
}
logmsg(LOG_NOTICE, "%s", buf);
}
switch (pid = vfork()) {
case -1:
logmsg(LOG_ERR, "cannot fork: %s", strerror(errno));
break;
case 0:
(void)execv(script, argv);
_exit(EX_UNAVAILABLE);
default:
(void)waitpid(pid, &status, 0);
}
}
static boolean
node_up(const char *nickname, const char *server, const char *myaddr,
const tuntype tuntype, const char *tuninfo[])
{
const char *name;
tunnel = tunnel_new(nickname, server, myaddr, tuntype, tuninfo);
if (tunnel == NULL)
return FALSE;
name = nickname ? nickname : server;
invoke_script("up", name, myaddr, tuntype, tuninfo);
return TRUE;
}
static boolean
node_down()
{
const char *name;
if (tunnel == NULL)
return FALSE;
name = tunnel->nickname ? tunnel->nickname : tunnel->server;
invoke_script("down", name, tunnel->myaddr, tunnel->tuntype,
(const char **)tunnel->tuninfo);
tunnel_delete(tunnel);
tunnel = NULL;
return TRUE;
}
static void
bye(int status)
{
dtcp_close();
node_down();
if (daemonize)
pidfile_delete();
exit(status);
}
static boolean
getmyaddr(int sock, char *hbuf, int len)
{
struct sockaddr_storage ss;
socklen_t salen;
int niflags, err;
salen = sizeof(ss);
if (getsockname(sock, (struct sockaddr *)&ss, &salen) != 0) {
logmsg(LOG_ERR, "getsockname failed: %s", strerror(errno));
return FALSE;
}
niflags = NI_NUMERICHOST;
#ifdef NI_WITHSCOPEID
if (((struct sockaddr *)&ss)->sa_family == AF_INET6)
niflags |= NI_WITHSCOPEID;
#endif
err = getnameinfo((struct sockaddr *)&ss, salen, hbuf, len, NULL, 0,
niflags);
if (err) {
logmsg(LOG_ERR, "getnameinfo failed: %s", gai_strerror(err));
return FALSE;
}
return TRUE;
}
static void
keep_alive(int sock)
{
char buf[MAXLINELEN];
char *p;
while (1) {
debugmsg("sleep(60)");
sleep(KEEPALIVE_INTERVAL);
if (dtcp_send(sock, "ping") < 0)
break;
if (dtcp_recv(sock, buf, sizeof(buf)) < 0)
break;
if ((p = strtok(buf, DELIM)) == NULL ||
strcmp(p, "+OK") != 0) {
if (verbose)
logmsg(LOG_ERR, "invalid response");
break;
}
}
}
static char *
getchallenge(char *buf)
{
char *challenge, *p;
for (challenge = buf; *challenge == ' '; challenge++)
;
if (*challenge == '\0')
return NULL;
for (p = challenge; *p != '\0' && *p != ' '; p++)
;
*p = '\0';
return challenge;
}
static char *
getresponse(char *buf)
{
char *response, *p;
for (response = buf; *response == ' '; response++)
;
if (*response == '\0')
return NULL;
for (p = response; *p != '\0' && *p != '\r' && *p != '\n'; p++)
;
*p = '\0';
return response;
}
static boolean
check_tuntype(tuntype tuntype, int nargs)
{
boolean valid = FALSE;
switch (nargs) {
case 2:
if (tuntype == TUNNEL_ONLY)
valid = TRUE;
break;
case 4:
if (tuntype == TUNNEL_HOST)
valid = TRUE;
break;
case 3:
case 5:
if (tuntype == TUNNEL_NETWORK)
valid = TRUE;
break;
}
return valid;
}
static boolean
dtcpc(char *nickname, char *dst, char *port, char *username, char *password,
tuntype tuntype, int udp_tunnel, int udp_tunnel_port, int udp_napt_port)
{
char myaddr[NI_MAXHOST], buf[MAXLINELEN], buf2[MAXLINELEN];
char *challenge, *response, *p;
char *tuninfo[6];
int i, r;
dtcp_error = FALSE;
dtcp_connected = FALSE;
if ((dtcp_sock = connect_to(dst, port, AF_INET)) < 0)
return FALSE;
if (!getmyaddr(dtcp_sock, myaddr, sizeof(myaddr))) {
dtcp_close();
return FALSE;
}
dtcp_connected = TRUE;
logmsg(LOG_NOTICE, "logging in to %s port %s", dst, port);
/* get greeting */
if (dtcp_recv(dtcp_sock, buf, sizeof(buf)) < 0) {
dtcp_close();
return FALSE;
}
if (strncmp(buf, "+OK ", 4) != 0 ||
(challenge = getchallenge(buf + 4)) == NULL) {
if (verbose)
logmsg(LOG_ERR, "invalid response");
dtcp_close();
return FALSE;
}
response = authenticate(username, challenge, password);
snprintf(buf, sizeof(buf), "tunnel %s %s %s",
username, response, tuntype_table[tuntype]);
if (mtu > 0) {
snprintf(buf2, sizeof(buf2), " %d", mtu);
strlcat(buf, buf2, sizeof(buf));
}
if (udp_tunnel) {
strlcat(buf, " proto=udp", sizeof(buf));
if (!behind_nat || udp_napt_port > 0) {
snprintf(buf2, sizeof(buf2), " port=%d",
(udp_napt_port > 0) ?
udp_napt_port : udp_tunnel_port);
strlcat(buf, buf2, sizeof(buf));
}
}
r = dtcp_send(dtcp_sock, buf);
if (r < 0) {
dtcp_close();
return FALSE;
}
if (dtcp_recv(dtcp_sock, buf, sizeof(buf)) < 0) {
dtcp_close();
return FALSE;
}
if (strncmp(buf, "+OK ", 4) != 0) {
if (strncmp(buf, "-ERR ", 5) != 0) {
if (verbose)
logmsg(LOG_ERR, "invalid response");
dtcp_close();
return FALSE;
}
if ((response = getresponse(buf + 4)) != NULL)
logmsg(LOG_ERR, "failed, reason: %s", response);
dtcp_close();
#if 0
if (response != NULL &&
strncmp(response, "authentication", 14) == 0) {
bye(EX_DATAERR);
/* NOTREACHED */
}
#endif
return FALSE;
}
if ((p = strtok(buf, DELIM)) == NULL || strcmp(p, "+OK") != 0) {
if (verbose)
logmsg(LOG_ERR, "invalid response");
dtcp_close();
return FALSE;
}
/* tunnelonly 1: me, 2: her */
/* host 1: me, 2: her 3: me6 4: her6 */
/* network 1: me, 2: her 3: prefix (4: me6, 5: her6) */
i = 0;
while ((p = strtok(NULL, DELIM)) != NULL) {
if (i > 4)
break;
tuninfo[i++] = p;
}
tuninfo[i] = NULL;
if (!check_tuntype(tuntype, i)) {
if (verbose)
logmsg(LOG_ERR, "invalid response");
dtcp_close();
return FALSE;
}
if (strcmp(myaddr, tuninfo[0]) != 0 && !behind_nat) {
logmsg(LOG_ERR, "failed, you are behind a NAT box (%s != %s)",
myaddr, tuninfo[0]);
dtcp_close();
bye(EX_DATAERR);
/* NOTREACHED */
}
if (udp_tunnel) {
snprintf(buf2, sizeof(buf2), ";%d", udp_tunnel_port);
strlcat(myaddr, buf2, sizeof(myaddr));
}
node_up(nickname, dst, myaddr, tuntype, (const char **)tuninfo);
keep_alive(dtcp_sock);
dtcp_close();
node_down();
return TRUE;
}
static void
flag_signal(int c)
{
char ch = c;
if (write(signalpipe[1], &ch, 1) != 1) {
logmsg(LOG_ERR, "write failed: %s", strerror(errno));
bye(EX_OSERR);
/* NOTREACHED */
}
}
static void
reconnect(int signo __attribute__((__unused__)))
{
logmsg(LOG_NOTICE, "reconnect by SIGHUP");
if (dtcp_sock >= 0)
flag_signal('H');
}
static void
terminate(int signo)
{
if (daemonize) {
switch (signo) {
case SIGTERM:
logmsg(LOG_NOTICE, "exit with SIGTERM");
break;
case SIGINT:
logmsg(LOG_NOTICE, "exit with SIGINT");
break;
default:
logmsg(LOG_NOTICE, "exit with signal %d", signo);
break;
}
}
bye(EX_OK);
/* NOTREACHED */
}
int
main(int argc, char *argv[])
{
int c;
boolean loop = FALSE;
char *progname;
char *nickname = NULL;
char *dst;
char *port = SERVICE_DTCP;
tuntype tuntype = TUNNEL_ONLY;
char *username = NULL;
char *password = NULL;
struct passwd *pwd;
char dtcp_mtu[10];
int udp_tunnel = FALSE;
int udp_tunnel_port = UDP_TUNNEL_PORT, udp_napt_port = 0;
progname = basename(argv[0]);
if ((pwd = getpwuid(getuid())) != NULL)
username = pwd->pw_name;
while ((c = getopt(argc, argv, "b:B:dDe:f:hlm:np:s:t:u:U")) != -1)
switch (c) {
case 'b':
udp_tunnel_port = atoi(optarg);
break;
case 'B':
udp_napt_port = atoi(optarg);
break;
case 'd':
debug = TRUE;
break;
case 'D':
daemonize = TRUE;
break;
case 'e':
nickname = optarg;
break;
case 'f':
pidfile = optarg;
break;
case 'h':
usage(progname);
/* NOTREACHED */
case 'l':
loop = TRUE;
break;
case 'm':
mtu = atoi(optarg);
break;
case 'n':
behind_nat = TRUE;
break;
case 'p':
port = optarg;
break;
case 's':
script = optarg;
break;
case 't':
if (strcmp(optarg, "tunnelonly") == 0)
tuntype = TUNNEL_ONLY;
else if (strcmp(optarg, "host") == 0)
tuntype = TUNNEL_HOST;
else if (strcmp(optarg, "network") == 0)
tuntype = TUNNEL_NETWORK;
else {
usage(progname);
/* NOTREACHED */
}
break;
case 'u':
username = optarg;
break;
case 'U':
udp_tunnel = TRUE;
break;
default:
usage(progname);
/* NOTREACHED */
}
argv += optind;
argc -= optind;
if (argc != 1) {
usage(progname);
/* NOTREACHED */
}
dst = argv[0];
if (daemonize) {
daemon(0, 0);
openlog((const char *)progname, LOG_PID, LOG_DAEMON);
syslog_opened = TRUE;
if (verbose)
logmsg(LOG_NOTICE, "start");
}
password = getpassword(dst, username);
if (pipe(signalpipe) != 0) {
logmsg(LOG_ERR, "pipe failed: %s", strerror(errno));
exit(EX_OSERR);
}
if (daemonize) {
if (!pidfile_create())
exit(EX_DATAERR);
}
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, terminate);
signal(SIGINT, terminate);
signal(SIGHUP, reconnect);
if (mtu > 0) {
snprintf(dtcp_mtu, sizeof(dtcp_mtu), "%d", mtu);
setenv("DTCP_MTU", dtcp_mtu, 1);
}
while (1) {
dtcpc(nickname, dst, port, username, password, tuntype,
udp_tunnel, udp_tunnel_port, udp_napt_port);
if (reconnect_request) {
reconnect_request = FALSE;
continue;
}
if (dtcp_connected)
logmsg(LOG_NOTICE, "connection was lost.");
if (!loop)
break;
sleep(RECONNECT_WAIT);
}
free(password);
if (daemonize)
pidfile_delete();
exit(EX_OK);
}
syntax highlighted by Code2HTML, v. 0.9.1