Synchronise HEAD with CABAL branch.

This commit is contained in:
Guus Sliepen 2003-08-24 20:38:31 +00:00
parent efa5148bc7
commit 013a2e159e
173 changed files with 12252 additions and 28046 deletions

View file

@ -1,33 +1,31 @@
## Produce this file with automake to get Makefile.in
# $Id: Makefile.am,v 1.12 2002/05/02 13:11:55 zarq Exp $
SUBDIRS = pokey
# $Id: Makefile.am,v 1.13 2003/08/24 20:38:23 guus Exp $
sbin_PROGRAMS = tincd
EXTRA_DIST = linux/device.c freebsd/device.c openbsd/device.c solaris/device.c
EXTRA_DIST = linux/device.c freebsd/device.c openbsd/device.c solaris/device.c netbsd/device.c darwin/device.c cygwin/device.c mingw/device.c raw_socket/device.c
tincd_SOURCES = read_conf.c device.c meta.c net_packet.c net_setup.c \
net_socket.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c route.c tincd.c net.c callbacks.c
tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \
net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c route.c subnet.c tincd.c
INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib -I$(top_srcdir)/intl
nodist_tincd_SOURCES = device.c
noinst_HEADERS = read_conf.h device.h meta.h process.h \
protocol.h route.h callbacks.h
DEFAULT_INCLUDES =
LIBS = @LIBS@ @INTLLIBS@
INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib
noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h
LIBS = @LIBS@ @LIBINTL@
tincd_LDADD = \
$(top_builddir)/lib/libtinc.a -lgcrypt
$(top_builddir)/lib/libvpn.a
localedir = $(datadir)/locale
CFLAGS = @CFLAGS@ -DPKGLIBDIR=$(pkglibdir) -DCONFDIR=\"$(sysconfdir)\" \
-DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
dist-hook:
rm -f `find . -type l`
lint: $(tincd_SOURCES)
lclint -nullassign -nullret +trytorecover +posixlib -skipansiheaders -skipposixheaders +gnuextensions -I/usr/include -I/usr/lib/gcc-lib/i386-linux/2.95.2/include -I. -I/home/zarq/p/tinc/cvs/cabal/src -I.. -I.. -I/home/zarq/p/tinc/cvs/cabal/lib -I/home/zarq/p/tinc/cvs/cabal/intl -D_POSIX_SOURCE -D__ELF__ -Dunix -D__i386__ -Dlinux -DHAVE_CONFIG_H -DPKGLIBDIR=/usr/local/lib/tinc -DCONFDIR=\"/usr/local/etc\" -DLOCALEDIR=\"/usr/local/share/locale\" $^

View file

@ -1,39 +0,0 @@
#include "config.h"
#include <stdarg.h>
#include <hooks.h>
#include <node.h>
#include "callbacks.h"
#include "process.h"
#include "system.h"
void hook_node_visible(const char *hooktype, va_list ap)
{
char *name;
node_t *n;
n = va_arg(ap, node_t*);
asprintf(&name, "hosts/%s-down", n->name);
execute_script(name);
free(name);
}
void hook_node_invisible(const char *hooktype, va_list ap)
{
char *name;
node_t *n;
n = va_arg(ap, node_t*);
asprintf(&name, "hosts/%s-up", n->name);
execute_script(name);
free(name);
}
void init_callbacks(void)
{
add_hook("node-visible", hook_node_visible);
add_hook("node-invisible", hook_node_invisible);
}

View file

@ -1,7 +0,0 @@
#ifndef __TINC_CALLBACKS_H__
#define __TINC_CALLBACKS_H__
extern void init_callbacks(void);
#endif /* __TINC_CALLBACKS_H__ */

489
src/conf.c Normal file
View file

@ -0,0 +1,489 @@
/*
conf.c -- configuration code
Copyright (C) 1998 Robert van der Meulen
1998-2003 Ivo Timmermans <ivo@o2w.nl>
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
2000 Cris van Pelt <tribbel@arise.dhs.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: conf.c,v 1.14 2003/08/24 20:38:24 guus Exp $
*/
#include "system.h"
#include "avl_tree.h"
#include "conf.h"
#include "logger.h"
#include "netutl.h" /* for str2address */
#include "utils.h" /* for cp */
#include "xalloc.h"
avl_tree_t *config_tree;
int pingtimeout = 0; /* seconds before timeout */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */
static int config_compare(const config_t *a, const config_t *b)
{
int result;
result = strcasecmp(a->variable, b->variable);
if(result)
return result;
result = a->line - b->line;
if(result)
return result;
else
return strcmp(a->file, b->file);
}
void init_configuration(avl_tree_t ** config_tree)
{
cp();
*config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
}
void exit_configuration(avl_tree_t ** config_tree)
{
cp();
avl_delete_tree(*config_tree);
*config_tree = NULL;
}
config_t *new_config(void)
{
cp();
return (config_t *) xmalloc_and_zero(sizeof(config_t));
}
void free_config(config_t *cfg)
{
cp();
if(cfg->variable)
free(cfg->variable);
if(cfg->value)
free(cfg->value);
if(cfg->file)
free(cfg->file);
free(cfg);
}
void config_add(avl_tree_t *config_tree, config_t *cfg)
{
cp();
avl_insert(config_tree, cfg);
}
config_t *lookup_config(const avl_tree_t *config_tree, char *variable)
{
config_t cfg, *found;
cp();
cfg.variable = variable;
cfg.file = "";
cfg.line = 0;
found = avl_search_closest_greater(config_tree, &cfg);
if(!found)
return NULL;
if(strcasecmp(found->variable, variable))
return NULL;
return found;
}
config_t *lookup_config_next(const avl_tree_t *config_tree, const config_t *cfg)
{
avl_node_t *node;
config_t *found;
cp();
node = avl_search_node(config_tree, cfg);
if(node) {
if(node->next) {
found = (config_t *) node->next->data;
if(!strcasecmp(found->variable, cfg->variable))
return found;
}
}
return NULL;
}
bool get_config_bool(const config_t *cfg, bool *result)
{
cp();
if(!cfg)
return false;
if(!strcasecmp(cfg->value, "yes")) {
*result = true;
return true;
} else if(!strcasecmp(cfg->value, "no")) {
*result = false;
return true;
}
logger(LOG_ERR, _("\"yes\" or \"no\" expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return false;
}
bool get_config_int(const config_t *cfg, int *result)
{
cp();
if(!cfg)
return false;
if(sscanf(cfg->value, "%d", result) == 1)
return true;
logger(LOG_ERR, _("Integer expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return false;
}
bool get_config_string(const config_t *cfg, char **result)
{
cp();
if(!cfg)
return false;
*result = xstrdup(cfg->value);
return true;
}
bool get_config_address(const config_t *cfg, struct addrinfo **result)
{
struct addrinfo *ai;
cp();
if(!cfg)
return false;
ai = str2addrinfo(cfg->value, NULL, 0);
if(ai) {
*result = ai;
return true;
}
logger(LOG_ERR, _("Hostname or IP address expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return false;
}
bool get_config_subnet(const config_t *cfg, subnet_t ** result)
{
subnet_t *subnet;
cp();
if(!cfg)
return false;
subnet = str2net(cfg->value);
if(!subnet) {
logger(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return false;
}
/* Teach newbies what subnets are... */
if(((subnet->type == SUBNET_IPV4)
&& !maskcheck(&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t)))
|| ((subnet->type == SUBNET_IPV6)
&& !maskcheck(&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t)))) {
logger(LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
free(subnet);
return false;
}
*result = subnet;
return true;
}
/*
Read exactly one line and strip the trailing newline if any. If the
file was on EOF, return NULL. Otherwise, return all the data in a
dynamically allocated buffer.
If line is non-NULL, it will be used as an initial buffer, to avoid
unnecessary mallocing each time this function is called. If buf is
given, and buf needs to be expanded, the var pointed to by buflen
will be increased.
*/
static char *readline(FILE * fp, char **buf, size_t *buflen)
{
char *newline = NULL;
char *p;
char *line; /* The array that contains everything that has been read so far */
char *idx; /* Read into this pointer, which points to an offset within line */
size_t size, newsize; /* The size of the current array pointed to by line */
size_t maxlen; /* Maximum number of characters that may be read with fgets. This is newsize - oldsize. */
if(feof(fp))
return NULL;
if(buf && buflen) {
size = *buflen;
line = *buf;
} else {
size = 100;
line = xmalloc(size);
}
maxlen = size;
idx = line;
*idx = 0;
for(;;) {
errno = 0;
p = fgets(idx, maxlen, fp);
if(!p) { /* EOF or error */
if(feof(fp))
break;
/* otherwise: error; let the calling function print an error message if applicable */
free(line);
return NULL;
}
newline = strchr(p, '\n');
if(!newline) { /* We haven't yet read everything to the end of the line */
newsize = size << 1;
line = xrealloc(line, newsize);
idx = &line[size - 1];
maxlen = newsize - size + 1;
size = newsize;
} else {
*newline = '\0'; /* kill newline */
break; /* yay */
}
}
if(buf && buflen) {
*buflen = size;
*buf = line;
}
return line;
}
/*
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
int read_config_file(avl_tree_t *config_tree, const char *fname)
{
int err = -2; /* Parse error */
FILE *fp;
char *buffer, *line;
char *variable, *value;
int lineno = 0;
int len;
bool ignore = false;
config_t *cfg;
size_t bufsize;
cp();
fp = fopen(fname, "r");
if(!fp) {
logger(LOG_ERR, _("Cannot open config file %s: %s"), fname,
strerror(errno));
return -3;
}
bufsize = 100;
buffer = xmalloc(bufsize);
for(;;) {
line = readline(fp, &buffer, &bufsize);
if(!line) {
err = -1;
break;
}
if(feof(fp)) {
err = 0;
break;
}
lineno++;
if(!*line || *line == '#')
continue;
if(ignore) {
if(!strncmp(line, "-----END", 8))
ignore = false;
continue;
}
if(!strncmp(line, "-----BEGIN", 10)) {
ignore = true;
continue;
}
variable = value = line;
len = strcspn(value, "\t =");
value += len;
value += strspn(value, "\t ");
if(*value == '=') {
value++;
value += strspn(value, "\t ");
}
variable[len] = '\0';
if(!*value) {
logger(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
variable, lineno, fname);
break;
}
cfg = new_config();
cfg->variable = xstrdup(variable);
cfg->value = xstrdup(value);
cfg->file = xstrdup(fname);
cfg->line = lineno;
config_add(config_tree, cfg);
}
free(buffer);
fclose(fp);
return err;
}
bool read_server_config()
{
char *fname;
int x;
cp();
asprintf(&fname, "%s/tinc.conf", confbase);
x = read_config_file(config_tree, fname);
if(x == -1) { /* System error: complain */
logger(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
}
free(fname);
return x == 0;
}
FILE *ask_and_open(const char *filename, const char *what, const char *mode)
{
FILE *r;
char *directory;
char *fn;
/* Check stdin and stdout */
if(!isatty(0) || !isatty(1)) {
/* Argh, they are running us from a script or something. Write
the files to the current directory and let them burn in hell
for ever. */
fn = xstrdup(filename);
} else {
/* Ask for a file and/or directory name. */
fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
what, filename);
fflush(stdout);
fn = readline(stdin, NULL, NULL);
if(!fn) {
fprintf(stderr, _("Error while reading stdin: %s\n"),
strerror(errno));
return NULL;
}
if(!strlen(fn))
/* User just pressed enter. */
fn = xstrdup(filename);
}
#ifdef HAVE_MINGW
if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
#else
if(fn[0] != '/') {
#endif
/* The directory is a relative path or a filename. */
char *p;
directory = get_current_dir_name();
asprintf(&p, "%s/%s", directory, fn);
free(fn);
free(directory);
fn = p;
}
umask(0077); /* Disallow everything for group and other */
/* Open it first to keep the inode busy */
r = fopen(fn, mode);
if(!r) {
fprintf(stderr, _("Error opening file `%s': %s\n"),
fn, strerror(errno));
free(fn);
return NULL;
}
free(fn);
return r;
}

View file

@ -1,7 +1,7 @@
/*
conf.h -- header for conf.c
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1998-2003 Ivo Timmermans <ivo@o2w.nl>
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,54 +17,47 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: conf.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
$Id: conf.h,v 1.11 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_CONF_H__
#define __TINC_CONF_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <avl_tree.h>
#include "net.h"
#include "subnet.h"
#include "avl_tree.h"
typedef struct config_t {
char *variable;
char *value;
char *file;
int line;
char *variable;
char *value;
char *file;
int line;
} config_t;
#include "subnet.h"
extern avl_tree_t *config_tree;
extern int debug_lvl;
extern int pingtimeout;
extern int maxtimeout;
extern int bypass_security;
extern bool bypass_security;
extern char *confbase;
extern char *netname;
extern void init_configuration(avl_tree_t **);
extern void exit_configuration(avl_tree_t **);
extern config_t *new_config(void);
extern config_t *new_config(void) __attribute__ ((__malloc__));
extern void free_config(config_t *);
extern void config_add(avl_tree_t *, config_t *);
extern config_t *lookup_config(avl_tree_t *, char *);
extern config_t *lookup_config_next(avl_tree_t *, config_t *);
extern int get_config_bool(config_t *, int *);
extern int get_config_int(config_t *, int *);
extern int get_config_port(config_t *, port_t *);
extern int get_config_string(config_t *, char **);
extern int get_config_address(config_t *, struct addrinfo **);
struct subnet_t; /* Needed for next line. */
extern int get_config_subnet(config_t *, struct subnet_t **);
extern config_t *lookup_config(const avl_tree_t *, char *);
extern config_t *lookup_config_next(const avl_tree_t *, const config_t *);
extern bool get_config_bool(const config_t *, bool *);
extern bool get_config_int(const config_t *, int *);
extern bool get_config_string(const config_t *, char **);
extern bool get_config_address(const config_t *, struct addrinfo **);
extern bool get_config_subnet(const config_t *, struct subnet_t **);
extern int read_config_file(avl_tree_t *, const char *);
extern int read_server_config(void);
extern FILE *ask_and_safe_open(const char*, const char*, const char *);
extern int is_safe_path(const char *);
extern bool read_server_config(void);
extern FILE *ask_and_open(const char *, const char *, const char *);
extern bool is_safe_path(const char *);
#endif /* __TINC_CONF_H__ */
#endif /* __TINC_CONF_H__ */

143
src/connection.c Normal file
View file

@ -0,0 +1,143 @@
/*
connection.c -- connection list management
Copyright (C) 2000-2003 Guus Sliepen <guus@sliepen.eu.org>,
2000-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: connection.c,v 1.4 2003/08/24 20:38:24 guus Exp $
*/
#include "system.h"
#include "avl_tree.h"
#include "conf.h"
#include "list.h"
#include "logger.h"
#include "net.h" /* Don't ask. */
#include "netutl.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
avl_tree_t *connection_tree; /* Meta connections */
connection_t *broadcast;
static int connection_compare(const connection_t *a, const connection_t *b)
{
return (void *)a - (void *)b;
}
void init_connections(void)
{
cp();
connection_tree = avl_alloc_tree((avl_compare_t) connection_compare, NULL);
broadcast = new_connection();
broadcast->name = xstrdup(_("everyone"));
broadcast->hostname = xstrdup(_("BROADCAST"));
}
void exit_connections(void)
{
cp();
avl_delete_tree(connection_tree);
free_connection(broadcast);
}
connection_t *new_connection(void)
{
connection_t *c;
cp();
c = (connection_t *) xmalloc_and_zero(sizeof(connection_t));
if(!c)
return NULL;
gettimeofday(&c->start, NULL);
return c;
}
void free_connection(connection_t *c)
{
cp();
if(c->hostname)
free(c->hostname);
if(c->inkey)
free(c->inkey);
if(c->outkey)
free(c->outkey);
if(c->mychallenge)
free(c->mychallenge);
if(c->hischallenge)
free(c->hischallenge);
free(c);
}
void connection_add(connection_t *c)
{
cp();
avl_insert(connection_tree, c);
}
void connection_del(connection_t *c)
{
cp();
avl_delete(connection_tree, c);
}
void dump_connections(void)
{
avl_node_t *node;
connection_t *c;
cp();
logger(LOG_DEBUG, _("Connections:"));
for(node = connection_tree->head; node; node = node->next) {
c = (connection_t *) node->data;
logger(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x"),
c->name, c->hostname, c->options, c->socket, *(uint32_t *)&c->status);
}
logger(LOG_DEBUG, _("End of connections."));
}
bool read_connection_config(connection_t *c)
{
char *fname;
int x;
cp();
asprintf(&fname, "%s/hosts/%s", confbase, c->name);
x = read_config_file(c->config_tree, fname);
free(fname);
return x == 0;
}

110
src/connection.h Normal file
View file

@ -0,0 +1,110 @@
/*
connection.h -- header for connection.c
Copyright (C) 2000-2003 Guus Sliepen <guus@sliepen.eu.org>,
2000-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: connection.h,v 1.4 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_CONNECTION_H__
#define __TINC_CONNECTION_H__
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include "avl_tree.h"
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
typedef struct connection_status_t {
int pinged:1; /* sent ping */
int active:1; /* 1 if active.. */
int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
int termreq:1; /* the termination of this connection was requested */
int remove:1; /* Set to 1 if you want this connection removed */
int timeout:1; /* 1 if gotten timeout */
int encryptout:1; /* 1 if we can encrypt outgoing traffic */
int decryptin:1; /* 1 if we have to decrypt incoming traffic */
int mst:1; /* 1 if this connection is part of a minimum spanning tree */
int unused:18;
} connection_status_t;
#include "edge.h"
#include "list.h"
#include "net.h"
#include "node.h"
typedef struct connection_t {
char *name; /* name he claims to have */
union sockaddr_t address; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
int protocol_version; /* used protocol */
int socket; /* socket used for this connection */
long int options; /* options for this connection */
struct connection_status_t status; /* status info */
int estimated_weight; /* estimation for the weight of the edge for this connection */
struct timeval start; /* time this connection was started, used for above estimation */
struct outgoing_t *outgoing; /* used to keep track of outgoing connections */
struct node_t *node; /* node associated with the other end */
struct edge_t *edge; /* edge associated with this connection */
RSA *rsa_key; /* his public/private key */
const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */
const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */
EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */
EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */
char *inkey; /* His symmetric meta key + iv */
char *outkey; /* Our symmetric meta key + iv */
int inkeylength; /* Length of his key + iv */
int outkeylength; /* Length of our key + iv */
const EVP_MD *indigest;
const EVP_MD *outdigest;
int inmaclength;
int outmaclength;
int incompression;
int outcompression;
char *mychallenge; /* challenge we received from him */
char *hischallenge; /* challenge we sent to him */
char buffer[MAXBUFSIZE]; /* metadata input buffer */
int buflen; /* bytes read into buffer */
int reqlen; /* length of incoming request */
int tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */
time_t last_ping_time; /* last time we saw some activity from the other end */
avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
} connection_t;
extern avl_tree_t *connection_tree;
extern connection_t *broadcast;
extern void init_connections(void);
extern void exit_connections(void);
extern connection_t *new_connection(void) __attribute__ ((__malloc__));
extern void free_connection(connection_t *);
extern void connection_add(connection_t *);
extern void connection_del(connection_t *);
extern void dump_connections(void);
extern bool read_connection_config(connection_t *);
#endif /* __TINC_CONNECTION_H__ */

290
src/cygwin/device.c Normal file
View file

@ -0,0 +1,290 @@
/*
device.c -- Interaction with Windows tap driver in a Cygwin environment
Copyright (C) 2002-2003 Ivo Timmermans <ivo@o2w.nl>,
2002-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.c,v 1.2 2003/08/24 20:38:29 guus Exp $
*/
#include "system.h"
#include <w32api/windows.h>
#include <w32api/winioctl.h>
#include "conf.h"
#include "logger.h"
#include "net.h"
#include "route.h"
#include "utils.h"
#include "xalloc.h"
#define REG_CONTROL_NET "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define USERMODEDEVICEDIR "\\\\.\\"
#define USERDEVICEDIR "\\??\\"
#define TAPSUFFIX ".tap"
#define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_PHYSICAL_NETCARD | 8000, request, method, FILE_ANY_ACCESS)
#define TAP_IOCTL_GET_LASTMAC TAP_CONTROL_CODE(0, METHOD_BUFFERED)
#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED)
#define TAP_IOCTL_SET_STATISTICS TAP_CONTROL_CODE(2, METHOD_BUFFERED)
int device_fd = -1;
static HANDLE device_handle = INVALID_HANDLE_VALUE;
char *device = NULL;
char *iface = NULL;
char *device_info = NULL;
int device_total_in = 0;
int device_total_out = 0;
pid_t reader_pid;
int sp[2];
bool setup_device(void)
{
HKEY key, key2;
int i;
char regpath[1024];
char adapterid[1024];
char adaptername[1024];
char tapname[1024];
char gelukt = 0;
long len;
bool found = false;
cp();
get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
/* Open registry and look for network adapters */
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_NET, 0, KEY_READ, &key)) {
logger(LOG_ERR, _("Unable to read registry: %s"), winerror(GetLastError()));
return false;
}
for (i = 0; ; i++) {
len = sizeof(adapterid);
if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
break;
/* Find out more about this adapter */
snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", REG_CONTROL_NET, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
continue;
len = sizeof(adaptername);
err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
RegCloseKey(key2);
if(err)
continue;
if(device) {
if(!strcmp(device, adapterid)) {
found = true;
break;
} else
continue;
}
if(iface) {
if(!strcmp(iface, adaptername)) {
found = true;
break;
} else
continue;
}
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if(device_handle != INVALID_HANDLE_VALUE) {
CloseHandle(device_handle);
found = true;
break;
}
}
RegCloseKey(key);
if(!found) {
logger(LOG_ERR, _("No Windows tap device found!"));
return false;
}
if(!device)
device = xstrdup(adapterid);
if(!iface)
iface = xstrdup(adaptername);
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
/* Now we are going to open this device twice: once for reading and once for writing.
We do this because apparently it isn't possible to check for activity in the select() loop.
Furthermore I don't really know how to do it the "Windows" way. */
if(socketpair(AF_UNIX, SOCK_DGRAM, PF_UNIX, sp)) {
logger(LOG_DEBUG, _("System call `%s' failed: %s"), "socketpair", strerror(errno));
return false;
}
/* The parent opens the tap device for writing. */
device_handle = CreateFile(tapname, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM , 0);
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, _("Could not open Windows tap device %s (%s) for writing: %s"), device, iface, winerror(GetLastError()));
return false;
}
device_fd = sp[0];
/* Get MAC address from tap device */
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
logger(LOG_ERR, _("Could not get MAC address from Windows tap device %s (%s): %s"), device, iface, winerror(GetLastError()));
return false;
}
if(routing_mode == RMODE_ROUTER) {
overwrite_mac = 1;
}
/* Now we start the child */
reader_pid = fork();
if(reader_pid == -1) {
logger(LOG_DEBUG, _("System call `%s' failed: %s"), "fork", strerror(errno));
return false;
}
if(!reader_pid) {
/* The child opens the tap device for reading, blocking.
It passes everything it reads to the socket. */
char buf[MTU];
long lenin;
CloseHandle(device_handle);
device_handle = CreateFile(tapname, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, _("Could not open Windows tap device %s (%s) for reading: %s"), device, iface, winerror(GetLastError()));
buf[0] = 0;
write(sp[1], buf, 1);
exit(1);
}
logger(LOG_DEBUG, _("Tap reader forked and running."));
/* Notify success */
buf[0] = 1;
write(sp[1], buf, 1);
/* Pass packets */
for(;;) {
ReadFile(device_handle, buf, MTU, &lenin, NULL);
write(sp[1], buf, lenin);
}
}
read(device_fd, &gelukt, 1);
if(gelukt != 1) {
logger(LOG_DEBUG, _("Tap reader failed!"));
return false;
}
device_info = _("Windows tap device");
logger(LOG_INFO, _("%s (%s) is a %s"), device, iface, device_info);
return true;
}
void close_device(void)
{
cp();
close(sp[0]);
close(sp[1]);
CloseHandle(device_handle);
kill(reader_pid, SIGKILL);
}
bool read_packet(vpn_packet_t *packet)
{
int lenin;
cp();
if((lenin = read(sp[0], packet->data, MTU)) <= 0) {
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info,
device, strerror(errno));
return false;
}
packet->len = lenin;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len,
device_info);
return true;
}
bool write_packet(vpn_packet_t *packet)
{
long lenout;
cp();
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(!WriteFile (device_handle, packet->data, packet->len, &lenout, NULL)) {
logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, device, winerror(GetLastError()));
return false;
}
device_total_out += packet->len;
return true;
}
void dump_device_stats(void)
{
cp();
logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
logger(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
logger(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
}

118
src/darwin/device.c Normal file
View file

@ -0,0 +1,118 @@
/*
device.c -- Interaction with MacOS/X tun device
Copyright (C) 2001-2003 Ivo Timmermans <ivo@o2w.nl>,
2001-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.c,v 1.2 2003/08/24 20:38:29 guus Exp $
*/
#include "system.h"
#include "conf.h"
#include "logger.h"
#include "net.h"
#include "utils.h"
#define DEFAULT_DEVICE "/dev/tun0"
int device_fd = -1;
char *device;
char *iface;
char *device_info;
int device_total_in = 0;
int device_total_out = 0;
bool setup_device(void)
{
cp();
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = DEFAULT_DEVICE;
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
iface = rindex(device, '/') ? rindex(device, '/') + 1 : device;
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) {
logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
return false;
}
device_info = _("MacOS/X tun device");
logger(LOG_INFO, _("%s is a %s"), device, device_info);
return true;
}
void close_device(void)
{
cp();
close(device_fd);
}
bool read_packet(vpn_packet_t *packet)
{
int lenin;
cp();
if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info,
device, strerror(errno));
return false;
}
packet->data[12] = 0x08;
packet->data[13] = 0x00;
packet->len = lenin + 14;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"),
packet->len, device_info);
return true;
}
bool write_packet(vpn_packet_t *packet)
{
cp();
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info,
device, strerror(errno));
return false;
}
device_total_out += packet->len;
return true;
}
void dump_device_stats(void)
{
cp();
logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
logger(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
logger(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
}

View file

@ -1,7 +1,7 @@
/*
net.h -- generic header for device.c
Copyright (C) 2001-2002 Ivo Timmermans <zarq@iname.com>
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 2001-2003 Ivo Timmermans <zarq@iname.com>
2001-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,20 +17,23 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
$Id: device.h,v 1.3 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_DEVICE_H__
#define __TINC_DEVICE_H__
#include "net.h"
extern int device_fd;
extern char *device;
extern char *interface;
extern int setup_device(void);
extern char *iface;
extern bool setup_device(void);
extern void close_device(void);
extern int read_packet(vpn_packet_t *);
extern int write_packet(vpn_packet_t *);
extern bool read_packet(struct vpn_packet_t *);
extern bool write_packet(struct vpn_packet_t *);
extern void dump_device_stats(void);
#endif /* __TINC_DEVICE_H__ */
#endif /* __TINC_DEVICE_H__ */

162
src/edge.c Normal file
View file

@ -0,0 +1,162 @@
/*
edge.c -- edge tree management
Copyright (C) 2000-2003 Guus Sliepen <guus@sliepen.eu.org>,
2000-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: edge.c,v 1.4 2003/08/24 20:38:24 guus Exp $
*/
#include "system.h"
#include "avl_tree.h"
#include "edge.h"
#include "logger.h"
#include "netutl.h"
#include "node.h"
#include "utils.h"
#include "xalloc.h"
avl_tree_t *edge_weight_tree; /* Tree with all edges, sorted on weight */
static int edge_compare(const edge_t *a, const edge_t *b)
{
return strcmp(a->to->name, b->to->name);
}
static int edge_weight_compare(const edge_t *a, const edge_t *b)
{
int result;
result = a->weight - b->weight;
if(result)
return result;
result = strcmp(a->from->name, b->from->name);
if(result)
return result;
return strcmp(a->to->name, b->to->name);
}
void init_edges(void)
{
cp();
edge_weight_tree = avl_alloc_tree((avl_compare_t) edge_weight_compare, NULL);
}
avl_tree_t *new_edge_tree(void)
{
cp();
return avl_alloc_tree((avl_compare_t) edge_compare, (avl_action_t) free_edge);
}
void free_edge_tree(avl_tree_t *edge_tree)
{
cp();
avl_delete_tree(edge_tree);
}
void exit_edges(void)
{
cp();
avl_delete_tree(edge_weight_tree);
}
/* Creation and deletion of connection elements */
edge_t *new_edge(void)
{
cp();
return (edge_t *) xmalloc_and_zero(sizeof(edge_t));
}
void free_edge(edge_t *e)
{
cp();
sockaddrfree(&e->address);
free(e);
}
void edge_add(edge_t *e)
{
cp();
avl_insert(edge_weight_tree, e);
avl_insert(e->from->edge_tree, e);
e->reverse = lookup_edge(e->to, e->from);
if(e->reverse)
e->reverse->reverse = e;
}
void edge_del(edge_t *e)
{
cp();
if(e->reverse)
e->reverse->reverse = NULL;
avl_delete(edge_weight_tree, e);
avl_delete(e->from->edge_tree, e);
}
edge_t *lookup_edge(node_t *from, node_t *to)
{
edge_t v;
cp();
v.from = from;
v.to = to;
return avl_search(from->edge_tree, &v);
}
void dump_edges(void)
{
avl_node_t *node, *node2;
node_t *n;
edge_t *e;
char *address;
cp();
logger(LOG_DEBUG, _("Edges:"));
for(node = node_tree->head; node; node = node->next) {
n = (node_t *) node->data;
for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
e = (edge_t *) node2->data;
address = sockaddr2hostname(&e->address);
logger(LOG_DEBUG, _(" %s to %s at %s options %lx weight %d"),
e->from->name, e->to->name, address, e->options, e->weight);
free(address);
}
}
logger(LOG_DEBUG, _("End of edges."));
}

56
src/edge.h Normal file
View file

@ -0,0 +1,56 @@
/*
edge.h -- header for edge.c
Copyright (C) 2001-2003 Guus Sliepen <guus@sliepen.eu.org>,
2001-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: edge.h,v 1.4 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_EDGE_H__
#define __TINC_EDGE_H__
#include "avl_tree.h"
#include "connection.h"
#include "net.h"
#include "node.h"
typedef struct edge_t {
struct node_t *from;
struct node_t *to;
sockaddr_t address;
long int options; /* options turned on for this edge */
int weight; /* weight of this edge */
struct connection_t *connection; /* connection associated with this edge, if available */
struct edge_t *reverse; /* edge in the opposite direction, if available */
} edge_t;
extern avl_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
extern void init_edges(void);
extern void exit_edges(void);
extern edge_t *new_edge(void) __attribute__ ((__malloc__));
extern void free_edge(edge_t *);
extern avl_tree_t *new_edge_tree(void) __attribute__ ((__malloc__));
extern void free_edge_tree(avl_tree_t *);
extern void edge_add(edge_t *);
extern void edge_del(edge_t *);
extern edge_t *lookup_edge(struct node_t *, struct node_t *);
extern void dump_edges(void);
#endif /* __TINC_EDGE_H__ */

View file

@ -1,358 +0,0 @@
/*
encr.c -- everything that deals with encryption
Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
2000 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: encr.c,v 1.13 2000/10/18 20:12:08 zarq Exp $
*/
#include "config.h"
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
#include <sys/time.h>
#ifdef HAVE_GMP_H
# include <gmp.h>
#else
# ifdef HAVE_GMP2_GMP_H
# include <gmp2/gmp.h>
# endif
#endif
#include <utils.h>
#include <xalloc.h>
#include <cipher.h>
#include "conf.h"
#include "encr.h"
#include "net.h"
#include "protocol.h"
#include "system.h"
#define ENCR_GENERATOR "0xd"
#define ENCR_PRIME "0x7fffffffffffffffffffffffffffffff" /* Mersenne :) */
char text_key[1000];
char *my_public_key_base36;
int key_inited = 0, encryption_keylen;
mpz_t my_private_key, my_public_key, generator, shared_prime;
int my_key_expiry = (time_t)(-1);
char* mypassphrase;
int mypassphraselen;
int char_hex_to_bin(int c)
{
if(isdigit(c))
return c - '0';
else
return tolower(c) - 'a' + 10;
}
int str_hex_to_bin(unsigned char *bin, unsigned char *hex)
{
int i = 0, j = 0, l = strlen(hex);
cp
if(l&1)
{
i = j = 1;
bin[0] = char_hex_to_bin(hex[0]);
}
for(; i < l; i+=2, j++)
bin[j] = (char_hex_to_bin(hex[i]) << 4) + char_hex_to_bin(hex[i+1]);
cp
return j&1?j+1:j;
}
int read_passphrase(char *which, char **out)
{
FILE *f;
config_t const *cfg;
char *filename;
int size;
extern char *confbase;
char *pp;
cp
if((cfg = get_config_val(passphrasesdir)) == NULL)
{
asprintf(&filename, "%spassphrases/%s", confbase, which);
}
else
{
asprintf(&filename, "%s/%s", (char*)cfg->data.ptr, which);
}
if((f = fopen(filename, "rb")) == NULL)
{
if(debug_lvl > 1)
syslog(LOG_ERR, _("Could not open %s: %m"), filename);
return -1;
}
fscanf(f, "%d ", &size);
if(size < 1 || size > (1<<15))
{
syslog(LOG_ERR, _("Illegal passphrase in %s; size would be %d"), filename, size);
return -1;
}
/* Hmz... hackish... strange +1 and +2 stuff... I really like more comments on those alignment thingies! */
pp = xmalloc(size/4 + 1); /* Allocate enough for fgets */
fgets(pp, size/4 + 1, f); /* Read passhrase and reserve one byte for end-of-string */
fclose(f);
*out = xmalloc(size/8 + 2); /* Allocate enough bytes, +1 for rounding if bits%8 != 0, +1 for 2-byte alignment */
cp
return str_hex_to_bin(*out, pp);
}
int read_my_passphrase(void)
{
cp
if((mypassphraselen = read_passphrase("local", &mypassphrase)) < 0)
return -1;
cp
return 0;
}
int generate_private_key(void)
{
FILE *f;
int i;
char *s;
config_t const *cfg;
cp
if((cfg = get_config_val(keyexpire)) == NULL)
my_key_expiry = (time_t)(time(NULL) + 3600);
else
my_key_expiry = (time_t)(time(NULL) + cfg->data.val);
if(debug_lvl > 1)
syslog(LOG_NOTICE, _("Generating %d bits keys"), PRIVATE_KEY_BITS);
if((f = fopen("/dev/urandom", "r")) == NULL)
{
syslog(LOG_ERR, _("Opening /dev/urandom failed: %m"));
return -1;
}
s = xmalloc((2 * PRIVATE_KEY_LENGTH) + 1);
for(i = 0; i < PRIVATE_KEY_LENGTH; i++)
sprintf(&s[i << 1], "%02x", fgetc(f));
s[2 * PRIVATE_KEY_LENGTH] = '\0';
mpz_set_str(my_private_key, s, 16);
cp
return 0;
}
void calculate_public_key(void)
{
cp
mpz_powm(my_public_key, generator, my_private_key, shared_prime);
my_public_key_base36 = mpz_get_str(NULL, 36, my_public_key);
cp
}
unsigned char static_key[] = { 0x9c, 0xbf, 0x36, 0xa9, 0xce, 0x20, 0x1b, 0x8b, 0x67, 0x56, 0x21, 0x5d, 0x27, 0x1b, 0xd8, 0x7a };
int security_init(void)
{
cp
mpz_init(my_private_key);
mpz_init(my_public_key);
mpz_init_set_str(shared_prime, ENCR_PRIME, 0);
mpz_init_set_str(generator, ENCR_GENERATOR, 0);
if(read_my_passphrase() < 0)
return -1;
if(generate_private_key() < 0)
return -1;
if(cipher_init(CIPHER_BLOWFISH) < 0)
return -1;
calculate_public_key();
cp
return 0;
}
void set_shared_key(char *almost_key)
{
char *tmp;
int len;
mpz_t ak, our_shared_key;
cp
mpz_init_set_str(ak, almost_key, 36);
mpz_init(our_shared_key);
mpz_powm(our_shared_key, ak, my_private_key, shared_prime);
tmp = mpz_get_str(NULL, 16, our_shared_key);
len = str_hex_to_bin(text_key, tmp);
cipher_set_key(&encryption_key, len, text_key);
key_inited = 1;
encryption_keylen = len;
if(debug_lvl > 2)
syslog(LOG_INFO, _("Encryption key set to %s"), tmp);
free(tmp);
mpz_clear(ak);
mpz_clear(our_shared_key);
cp
}
void encrypt_passphrase(passphrase_t *pp)
{
char key[1000];
char tmp[1000];
unsigned char phrase[1000];
int keylen;
int i;
BF_KEY bf_key;
cp
mpz_get_str(tmp, 16, my_public_key);
keylen = str_hex_to_bin(key, tmp);
cipher_set_key(&bf_key, keylen, key);
low_crypt_key(mypassphrase, phrase, &bf_key, mypassphraselen, BF_ENCRYPT);
pp->len = ((mypassphraselen - 1) | 7) + 1;
pp->phrase = xmalloc((pp->len << 1) + 1);
for(i = 0; i < pp->len; i++)
snprintf(&(pp->phrase)[i << 1], 3, "%02x", (int)phrase[i]);
pp->phrase[(pp->len << 1) + 1] = '\0';
if(key_inited)
cipher_set_key(&encryption_key, encryption_keylen, text_key);
cp
}
int verify_passphrase(conn_list_t *cl, unsigned char *his_pubkey)
{
char key[1000];
char *tmp;
unsigned char phrase[1000];
int keylen, pplen;
mpz_t pk;
unsigned char *out;
BF_KEY bf_key;
char *which;
char *meuk;
cp
mpz_init_set_str(pk, his_pubkey, 36);
tmp = mpz_get_str(NULL, 16, pk);
keylen = str_hex_to_bin(key, tmp);
out = xmalloc((cl->pp->len >> 1) + 3);
pplen = str_hex_to_bin(phrase, cl->pp->phrase);
cipher_set_key(&bf_key, keylen, key);
low_crypt_key(phrase, out, &bf_key, pplen, BF_DECRYPT);
if(key_inited)
cipher_set_key(&encryption_key, encryption_keylen, text_key);
asprintf(&which, IP_ADDR_S, IP_ADDR_V(cl->vpn_ip));
if((pplen = read_passphrase(which, &meuk)) < 0)
return -1;
if(memcmp(meuk, out, pplen))
return -1;
cp
return 0;
}
char *make_shared_key(char *pk)
{
mpz_t tmp, res;
char *r;
cp
mpz_init_set_str(tmp, pk, 36);
mpz_init(res);
mpz_powm(res, tmp, my_private_key, shared_prime);
r = mpz_get_str(NULL, 36, res);
mpz_clear(res);
mpz_clear(tmp);
cp
return r;
}
/*
free a key after overwriting it
*/
void free_key(enc_key_t *k)
{
cp
if(!k)
return;
if(k->key)
{
memset(k->key, (char)(-1), k->length);
free(k->key);
}
free(k);
cp
}
void recalculate_encryption_keys(void)
{
conn_list_t *p;
char *ek;
cp
for(p = conn_list; p != NULL; p = p->next)
{
if(!p->public_key || !p->public_key->key)
/* We haven't received a key from this host (yet). */
continue;
ek = make_shared_key(p->public_key->key);
free_key(p->datakey);
p->datakey = xmalloc(sizeof(*p->datakey));
p->datakey->length = strlen(ek);
p->datakey->expiry = p->public_key->expiry;
p->datakey->key = xmalloc(strlen(ek) + 1);
strcpy(p->datakey->key, ek);
}
cp
}
void regenerate_keys(void)
{
cp
generate_private_key();
calculate_public_key();
send_key_changed_all();
recalculate_encryption_keys();
cp
}

View file

@ -1,46 +0,0 @@
/*
encr.h -- header for encr.c
Copyright (C) 1998,1999,2000 Ivo Timmermans <zarq@iname.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: encr.h,v 1.3 2000/10/18 20:12:08 zarq Exp $
*/
#ifndef __TINC_ENCR_H__
#define __TINC_ENCR_H__
#include "net.h"
#define PRIVATE_KEY_BITS 128
#define PRIVATE_KEY_LENGTH (PRIVATE_KEY_BITS >> 3)
extern char *my_public_key_base36;
extern int my_key_expiry;
extern int security_init(void);
extern int send_portnumbers(int);
extern void set_shared_key(char *);
extern int send_passphrase(conn_list_t *);
extern int send_public_key(conn_list_t *);
extern int verify_passphrase(conn_list_t *, unsigned char *);
extern char *make_shared_key(char*);
extern void encrypt_passphrase(passphrase_t *pp);
extern void free_key(enc_key_t*);
extern void regenerate_keys(void);
#endif /* __TINC_ENCR_H__ */

105
src/event.c Normal file
View file

@ -0,0 +1,105 @@
/*
event.c -- event queue
Copyright (C) 2002-2003 Guus Sliepen <guus@sliepen.eu.org>,
2002-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: event.c,v 1.4 2003/08/24 20:38:24 guus Exp $
*/
#include "system.h"
#include "avl_tree.h"
#include "event.h"
#include "utils.h"
#include "xalloc.h"
avl_tree_t *event_tree;
extern time_t now;
int id;
static int event_compare(const event_t *a, const event_t *b)
{
if(a->time > b->time)
return 1;
if(a->time < b->time)
return -1;
return a->id - b->id;
}
void init_events(void)
{
cp();
event_tree = avl_alloc_tree((avl_compare_t) event_compare, NULL);
}
void exit_events(void)
{
cp();
avl_delete_tree(event_tree);
}
event_t *new_event(void)
{
cp();
return (event_t *) xmalloc_and_zero(sizeof(event_t));
}
void free_event(event_t *event)
{
cp();
free(event);
}
void event_add(event_t *event)
{
cp();
event->id = ++id;
avl_insert(event_tree, event);
}
void event_del(event_t *event)
{
cp();
avl_delete(event_tree, event);
}
event_t *get_expired_event(void)
{
event_t *event;
cp();
if(event_tree->head) {
event = (event_t *) event_tree->head->data;
if(event->time < now) {
avl_delete(event_tree, event);
return event;
}
}
return NULL;
}

47
src/event.h Normal file
View file

@ -0,0 +1,47 @@
/*
event.h -- header for event.c
Copyright (C) 2002-2003 Guus Sliepen <guus@sliepen.eu.org>,
2002-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: event.h,v 1.4 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_EVENT_H__
#define __TINC_EVENT_H__
#include "avl_tree.h"
extern avl_tree_t *event_tree;
typedef void (*event_handler_t)(void *);
typedef struct {
time_t time;
int id;
event_handler_t handler;
void *data;
} event_t;
extern void init_events(void);
extern void exit_events(void);
extern event_t *new_event(void) __attribute__ ((__malloc__));
extern void free_event(event_t *);
extern void event_add(event_t *);
extern void event_del(event_t *);
extern event_t *get_expired_event(void);
#endif /* __TINC_EVENT_H__ */

292
src/graph.c Normal file
View file

@ -0,0 +1,292 @@
/*
graph.c -- graph algorithms
Copyright (C) 2001-2003 Guus Sliepen <guus@sliepen.eu.org>,
2001-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: graph.c,v 1.6 2003/08/24 20:38:24 guus Exp $
*/
/* We need to generate two trees from the graph:
1. A minimum spanning tree for broadcasts,
2. A single-source shortest path tree for unicasts.
Actually, the first one alone would suffice but would make unicast packets
take longer routes than necessary.
For the MST algorithm we can choose from Prim's or Kruskal's. I personally
favour Kruskal's, because we make an extra AVL tree of edges sorted on
weights (metric). That tree only has to be updated when an edge is added or
removed, and during the MST algorithm we just have go linearly through that
tree, adding safe edges until #edges = #nodes - 1. The implementation here
however is not so fast, because I tried to avoid having to make a forest and
merge trees.
For the SSSP algorithm Dijkstra's seems to be a nice choice. Currently a
simple breadth-first search is presented here.
The SSSP algorithm will also be used to determine whether nodes are directly,
indirectly or not reachable from the source. It will also set the correct
destination address and port of a node if possible.
*/
#include "system.h"
#include "avl_tree.h"
#include "connection.h"
#include "device.h"
#include "edge.h"
#include "logger.h"
#include "netutl.h"
#include "node.h"
#include "process.h"
#include "utils.h"
/* Implementation of Kruskal's algorithm.
Running time: O(EN)
Please note that sorting on weight is already done by add_edge().
*/
void mst_kruskal(void)
{
avl_node_t *node, *next;
edge_t *e;
node_t *n;
connection_t *c;
int nodes = 0;
int safe_edges = 0;
bool skipped;
cp();
/* Clear MST status on connections */
for(node = connection_tree->head; node; node = node->next) {
c = (connection_t *) node->data;
c->status.mst = false;
}
/* Do we have something to do at all? */
if(!edge_weight_tree->head)
return;
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Kruskal's algorithm:");
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next) {
n = (node_t *) node->data;
n->status.visited = false;
nodes++;
}
/* Starting point */
((edge_t *) edge_weight_tree->head->data)->from->status.visited = true;
/* Add safe edges */
for(skipped = false, node = edge_weight_tree->head; node; node = next) {
next = node->next;
e = (edge_t *) node->data;
if(!e->reverse || e->from->status.visited == e->to->status.visited) {
skipped = true;
continue;
}
e->from->status.visited = true;
e->to->status.visited = true;
if(e->connection)
e->connection->status.mst = true;
if(e->reverse->connection)
e->reverse->connection->status.mst = true;
safe_edges++;
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name,
e->to->name, e->weight);
if(skipped) {
skipped = false;
next = edge_weight_tree->head;
continue;
}
}
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes,
safe_edges);
}
/* Implementation of a simple breadth-first search algorithm.
Running time: O(E)
*/
void sssp_bfs(void)
{
avl_node_t *node, *from, *next, *to;
edge_t *e;
node_t *n;
avl_tree_t *todo_tree;
bool indirect;
char *name;
char *address, *port;
char *envp[7];
int i;
cp();
todo_tree = avl_alloc_tree(NULL, NULL);
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next) {
n = (node_t *) node->data;
n->status.visited = false;
n->status.indirect = true;
}
/* Begin with myself */
myself->status.visited = true;
myself->status.indirect = false;
myself->nexthop = myself;
myself->via = myself;
node = avl_alloc_node();
node->data = myself;
avl_insert_top(todo_tree, node);
/* Loop while todo_tree is filled */
while(todo_tree->head) {
for(from = todo_tree->head; from; from = next) { /* "from" is the node from which we start */
next = from->next;
n = (node_t *) from->data;
for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */
e = (edge_t *) to->data;
if(!e->reverse)
continue;
/* Situation:
/
/
------(n)-----(e->to)
\
\
n->address is set to the e->address of the edge left of n to n.
We are currently examining the edge e right of n from n:
- If e->reverse->address != n->address, then e->to is probably
not reachable for the nodes left of n. We do as if the indirectdata
flag is set on edge e.
- If edge e provides for better reachability of e->to, update
e->to and (re)add it to the todo_tree to (re)examine the reachability
of nodes behind it.
*/
indirect = n->status.indirect || e->options & OPTION_INDIRECT
|| ((n != myself) && sockaddrcmp(&n->address, &e->reverse->address));
if(e->to->status.visited
&& (!e->to->status.indirect || indirect))
continue;
e->to->status.visited = true;
e->to->status.indirect = indirect;
e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
e->to->via = indirect ? n->via : e->to;
e->to->options = e->options;
if(sockaddrcmp(&e->to->address, &e->address)) {
node = avl_unlink(node_udp_tree, e->to);
sockaddrfree(&e->to->address);
sockaddrcpy(&e->to->address, &e->address);
if(e->to->hostname)
free(e->to->hostname);
e->to->hostname = sockaddr2hostname(&e->to->address);
avl_insert_node(node_udp_tree, node);
}
node = avl_alloc_node();
node->data = e->to;
avl_insert_before(todo_tree, from, node);
}
avl_delete_node(todo_tree, from);
}
}
avl_free_tree(todo_tree);
/* Check reachability status. */
for(node = node_tree->head; node; node = next) {
next = node->next;
n = (node_t *) node->data;
if(n->status.visited != n->status.reachable) {
n->status.reachable = !n->status.reachable;
if(n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"),
n->name, n->hostname);
} else {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"),
n->name, n->hostname);
}
n->status.validkey = false;
n->status.waitingforkey = false;
asprintf(&envp[0], "NETNAME=%s", netname ? : "");
asprintf(&envp[1], "DEVICE=%s", device ? : "");
asprintf(&envp[2], "INTERFACE=%s", iface ? : "");
asprintf(&envp[3], "NODE=%s", n->name);
sockaddr2str(&n->address, &address, &port);
asprintf(&envp[4], "REMOTEADDRESS=%s", address);
asprintf(&envp[5], "REMOTEPORT=%s", port);
envp[6] = NULL;
asprintf(&name,
n->status.reachable ? "hosts/%s-up" : "hosts/%s-down",
n->name);
execute_script(name, envp);
free(name);
free(address);
free(port);
for(i = 0; i < 7; i++)
free(envp[i]);
}
}
}
void graph(void)
{
mst_kruskal();
sssp_bfs();
}

View file

@ -1,7 +1,7 @@
/*
graph.h -- header for graph.c
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
Copyright (C) 2001-2003 Guus Sliepen <guus@sliepen.eu.org>,
2001-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,14 +17,9 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: graph.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
$Id: graph.h,v 1.4 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_GRAPH_H__
#define __TINC_GRAPH_H__
extern void graph(void);
extern void mst_kruskal(void);
extern void sssp_bfs(void);
#endif /* __TINC_GRAPH_H__ */

135
src/logger.c Normal file
View file

@ -0,0 +1,135 @@
/*
logger.c -- logging code
Copyright (C) 2003 Guus Sliepen <guus@sliepen.eu.org>
2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: logger.c,v 1.2 2003/08/24 20:38:24 guus Exp $
*/
#include "system.h"
#include "conf.h"
#include "logger.h"
debug_t debug_level = DEBUG_NOTHING;
static logmode_t logmode = LOGMODE_STDERR;
static pid_t logpid;
extern char *logfilename;
static FILE *logfile = NULL;
#ifdef HAVE_MINGW
static HANDLE loghandle = NULL;
#endif
static const char *logident = NULL;
void openlogger(const char *ident, logmode_t mode) {
logident = ident;
logmode = mode;
switch(mode) {
case LOGMODE_STDERR:
logpid = getpid();
break;
case LOGMODE_FILE:
logpid = getpid();
logfile = fopen(logfilename, "a");
if(!logfile)
logmode = LOGMODE_NULL;
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
loghandle = RegisterEventSource(NULL, logident);
if(!loghandle)
logmode = LOGMODE_NULL;
break;
#else
#ifdef HAVE_SYSLOG_H
openlog(logident, LOG_CONS | LOG_PID, LOG_DAEMON);
break;
#endif
#endif
case LOGMODE_NULL:
break;
}
}
void logger(int priority, const char *format, ...) {
va_list ap;
va_start(ap, format);
switch(logmode) {
case LOGMODE_STDERR:
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
break;
case LOGMODE_FILE:
fprintf(logfile, "%ld %s[%d]: ", time(NULL), logident, logpid);
vfprintf(logfile, format, ap);
fprintf(logfile, "\n");
fflush(logfile);
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
{
char message[4096];
char *messages[] = {message};
vsnprintf(message, sizeof(message), format, ap);
ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
}
#else
#ifdef HAVE_SYSLOG_H
#ifdef HAVE_VSYSLOG
vsyslog(priority, format, ap);
#else
{
char message[4096];
vsnprintf(message, sizeof(message), format, ap);
syslog(priority, "%s", message);
}
#endif
break;
#endif
#endif
case LOGMODE_NULL:
break;
}
va_end(ap);
}
void closelogger(void) {
switch(logmode) {
case LOGMODE_FILE:
fclose(logfile);
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
DeregisterEventSource(loghandle);
break;
#else
#ifdef HAVE_SYSLOG_H
closelog();
break;
#endif
#endif
case LOGMODE_NULL:
case LOGMODE_STDERR:
break;
break;
}
}

55
src/logger.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef __TINC_LOGGER_H__
#define __TINC_LOGGER_H__
typedef enum debug_t {
DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */
DEBUG_ALWAYS = 0,
DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */
DEBUG_ERROR = 2, /* Show error messages received from other hosts */
DEBUG_STATUS = 2, /* Show status messages received from other hosts */
DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */
DEBUG_META = 4, /* Show contents of every request that is sent/received */
DEBUG_TRAFFIC = 5, /* Show network traffic information */
DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */
DEBUG_SCARY_THINGS = 10 /* You have been warned */
} debug_t;
typedef enum logmode_t {
LOGMODE_NULL,
LOGMODE_STDERR,
LOGMODE_FILE,
LOGMODE_SYSLOG
} logmode_t;
#ifdef HAVE_MINGW
#define LOG_EMERG EVENTLOG_ERROR_TYPE
#define LOG_ALERT EVENTLOG_ERROR_TYPE
#define LOG_CRIT EVENTLOG_ERROR_TYPE
#define LOG_ERR EVENTLOG_ERROR_TYPE
#define LOG_WARNING EVENTLOG_WARNING_TYPE
#define LOG_NOTICE EVENTLOG_INFORMATION_TYPE
#define LOG_INFO EVENTLOG_INFORMATION_TYPE
#define LOG_DEBUG EVENTLOG_INFORMATION_TYPE
#else
#ifndef HAVE_SYSLOG_H
enum {
LOG_EMERG,
LOG_ALERT,
LOG_CRIT,
LOG_ERR,
LOG_WARNING,
LOG_NOTICE,
LOG_INFO,
LOG_DEBUG,
};
#endif
#endif
extern debug_t debug_level;
extern void openlogger(const char *, logmode_t);
extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
extern void closelogger(void);
#define ifdebug(l) if(debug_level >= DEBUG_##l)
#endif /* __TINC_LOGGER_H__ */

View file

@ -1,7 +1,7 @@
/*
meta.c -- handle the meta communication
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
Copyright (C) 2000-2003 Guus Sliepen <guus@sliepen.eu.org>,
2000-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,204 +17,172 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: meta.c,v 1.4 2002/04/28 12:46:26 zarq Exp $
$Id: meta.c,v 1.5 2003/08/24 20:38:24 guus Exp $
*/
#include "config.h"
#include <utils.h>
#include <avl_tree.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
/* This line must be below the rest for FreeBSD */
#include <sys/types.h>
#include <sys/socket.h>
#include "system.h"
#include <openssl/evp.h>
#include "net.h"
#include "avl_tree.h"
#include "connection.h"
#include "system.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "protocol.h"
#include "logging.h"
#include "system.h"
#include "utils.h"
int send_meta(connection_t *c, char *buffer, int length)
bool send_meta(connection_t *c, const char *buffer, int length)
{
char *bufp;
int outlen;
char outbuf[MAXBUFSIZE];
cp
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length,
c->name, c->hostname);
const char *bufp;
int outlen;
char outbuf[MAXBUFSIZE];
int result;
if(c->status.encryptout)
{
#ifdef USE_OPENSSL
EVP_EncryptUpdate(c->outctx, outbuf, &outlen, buffer, length);
#endif
#ifdef USE_GCRYPT
outlen = gcry_cipher_encrypt(c->outctx, outbuf, sizeof(outbuf), buffer, length);
#endif
bufp = outbuf;
length = outlen;
}
else
bufp = buffer;
cp();
if(write(c->socket, bufp, length) < 0)
{
syslog(LOG_ERR, _("Sending meta data to %s (%s) failed: %s"), c->name, c->hostname, strerror(errno));
return -1;
}
cp
return 0;
ifdebug(META) logger(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length,
c->name, c->hostname);
if(c->status.encryptout) {
EVP_EncryptUpdate(c->outctx, outbuf, &outlen, buffer, length);
bufp = outbuf;
length = outlen;
} else
bufp = buffer;
while(length) {
result = send(c->socket, bufp, length, 0);
if(result <= 0) {
if(!errno || errno == EPIPE) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection closed by %s (%s)"),
c->name, c->hostname);
} else if(errno == EINTR)
continue;
else
logger(LOG_ERR, _("Sending meta data to %s (%s) failed: %s"), c->name,
c->hostname, strerror(errno));
return false;
}
bufp += result;
length -= result;
}
return true;
}
void broadcast_meta(connection_t *from, char *buffer, int length)
void broadcast_meta(connection_t *from, const char *buffer, int length)
{
avl_node_t *node;
connection_t *c;
cp
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c != from && c->status.active)
send_meta(c, buffer, length);
}
cp
avl_node_t *node;
connection_t *c;
cp();
for(node = connection_tree->head; node; node = node->next) {
c = (connection_t *) node->data;
if(c != from && c->status.active)
send_meta(c, buffer, length);
}
}
int receive_meta(connection_t *c)
bool receive_meta(connection_t *c)
{
int x, l = sizeof(x);
int oldlen, i;
int lenin, reqlen;
int decrypted = 0;
char inbuf[MAXBUFSIZE];
cp
if(getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
{
syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s %s (%s)"), __FILE__, __LINE__, c->socket, strerror(errno),
c->name, c->hostname);
return -1;
}
if(x)
{
syslog(LOG_ERR, _("Metadata socket error for %s (%s): %s"),
c->name, c->hostname, strerror(x));
return -1;
}
int oldlen, i;
int lenin, reqlen;
bool decrypted = false;
char inbuf[MAXBUFSIZE];
/* Strategy:
- Read as much as possible from the TCP socket in one go.
- Decrypt it.
- Check if a full request is in the input buffer.
- If yes, process request and remove it from the buffer,
then check again.
- If not, keep stuff in buffer and exit.
*/
cp();
lenin = read(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen);
/* Strategy:
- Read as much as possible from the TCP socket in one go.
- Decrypt it.
- Check if a full request is in the input buffer.
- If yes, process request and remove it from the buffer,
then check again.
- If not, keep stuff in buffer and exit.
*/
if(lenin<=0)
{
if(lenin==0)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Connection closed by %s (%s)"),
c->name, c->hostname);
}
else
if(errno==EINTR)
return 0;
else
syslog(LOG_ERR, _("Metadata socket read error for %s (%s): %s"),
c->name, c->hostname, strerror(errno));
lenin = recv(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen, 0);
return -1;
}
if(lenin <= 0) {
if(!lenin || !errno) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection closed by %s (%s)"),
c->name, c->hostname);
} else if(errno == EINTR)
return true;
else
logger(LOG_ERR, _("Metadata socket read error for %s (%s): %s"),
c->name, c->hostname, strerror(errno));
oldlen = c->buflen;
c->buflen += lenin;
return false;
}
while(lenin)
{
/* Decrypt */
oldlen = c->buflen;
c->buflen += lenin;
if(c->status.decryptin && !decrypted)
{
#ifdef USE_OPENSSL
EVP_DecryptUpdate(c->inctx, inbuf, &lenin, c->buffer + oldlen, lenin);
#endif
#ifdef USE_GCRYPT
lenin = gcry_cipher_decrypt(c->inctx, inbuf, sizeof(inbuf), c->buffer + oldlen, lenin);
#endif
memcpy(c->buffer + oldlen, inbuf, lenin);
decrypted = 1;
}
while(lenin) {
/* Decrypt */
/* Are we receiving a TCPpacket? */
if(c->status.decryptin && !decrypted) {
EVP_DecryptUpdate(c->inctx, inbuf, &lenin, c->buffer + oldlen, lenin);
memcpy(c->buffer + oldlen, inbuf, lenin);
decrypted = true;
}
if(c->tcplen)
{
if(c->tcplen <= c->buflen)
{
receive_tcppacket(c, c->buffer, c->tcplen);
/* Are we receiving a TCPpacket? */
c->buflen -= c->tcplen;
lenin -= c->tcplen;
memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
oldlen = 0;
c->tcplen = 0;
continue;
}
else
{
break;
}
}
if(c->tcplen) {
if(c->tcplen <= c->buflen) {
receive_tcppacket(c, c->buffer, c->tcplen);
/* Otherwise we are waiting for a request */
c->buflen -= c->tcplen;
lenin -= c->tcplen;
memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
oldlen = 0;
c->tcplen = 0;
continue;
} else {
break;
}
}
reqlen = 0;
/* Otherwise we are waiting for a request */
for(i = oldlen; i < c->buflen; i++)
{
if(c->buffer[i] == '\n')
{
c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
reqlen = i + 1;
break;
}
}
reqlen = 0;
if(reqlen)
{
if(receive_request(c))
return -1;
for(i = oldlen; i < c->buflen; i++) {
if(c->buffer[i] == '\n') {
c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
reqlen = i + 1;
break;
}
}
c->buflen -= reqlen;
lenin -= reqlen;
memmove(c->buffer, c->buffer + reqlen, c->buflen);
oldlen = 0;
continue;
}
else
{
break;
}
}
if(reqlen) {
c->reqlen = reqlen;
if(!receive_request(c))
return false;
if(c->buflen >= MAXBUFSIZE)
{
syslog(LOG_ERR, _("Metadata read buffer overflow for %s (%s)"),
c->name, c->hostname);
return -1;
}
c->buflen -= reqlen;
lenin -= reqlen;
memmove(c->buffer, c->buffer + reqlen, c->buflen);
oldlen = 0;
continue;
} else {
break;
}
}
c->last_ping_time = now;
cp
return 0;
if(c->buflen >= MAXBUFSIZE) {
logger(LOG_ERR, _("Metadata read buffer overflow for %s (%s)"),
c->name, c->hostname);
return false;
}
c->last_ping_time = now;
return true;
}

View file

@ -1,7 +1,7 @@
/*
meta.h -- header for meta.c
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
Copyright (C) 2000-2003 Guus Sliepen <guus@sliepen.eu.org>,
2000-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,7 +17,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: meta.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
$Id: meta.h,v 1.3 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_META_H__
@ -25,8 +25,8 @@
#include "connection.h"
extern int send_meta(connection_t *, const char *, int);
extern int broadcast_meta(connection_t *, const char *, int);
extern int receive_meta(connection_t *);
extern bool send_meta(struct connection_t *, const char *, int);
extern void broadcast_meta(struct connection_t *, const char *, int);
extern bool receive_meta(struct connection_t *);
#endif /* __TINC_META_H__ */
#endif /* __TINC_META_H__ */

349
src/mingw/device.c Normal file
View file

@ -0,0 +1,349 @@
/*
device.c -- Interaction with Windows tap driver in a MinGW environment
Copyright (C) 2002-2003 Ivo Timmermans <ivo@o2w.nl>,
2002-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.c,v 1.2 2003/08/24 20:38:29 guus Exp $
*/
#include "system.h"
#include <windows.h>
#include <winioctl.h>
#include "conf.h"
#include "logger.h"
#include "net.h"
#include "route.h"
#include "utils.h"
#include "xalloc.h"
#define REG_CONTROL_NET "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define USERMODEDEVICEDIR "\\\\.\\"
#define USERDEVICEDIR "\\??\\"
#define TAPSUFFIX ".tap"
#define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_PHYSICAL_NETCARD | 8000, request, method, FILE_ANY_ACCESS)
#define TAP_IOCTL_GET_LASTMAC TAP_CONTROL_CODE(0, METHOD_BUFFERED)
#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED)
#define TAP_IOCTL_SET_STATISTICS TAP_CONTROL_CODE(2, METHOD_BUFFERED)
int device_fd = 0;
HANDLE device_handle = INVALID_HANDLE_VALUE;
char *device = NULL;
char *iface = NULL;
char *device_info = NULL;
int device_total_in = 0;
int device_total_out = 0;
extern char *myport;
DWORD WINAPI tapreader(void *bla) {
int sock, err, status;
struct addrinfo *ai;
struct addrinfo hint = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP,
.ai_flags = 0,
};
char buf[MTU];
long len;
OVERLAPPED overlapped;
/* Open a socket to the parent process */
err = getaddrinfo(NULL, myport, &hint, &ai);
if(err || !ai) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "getaddrinfo", gai_strerror(errno));
return -1;
}
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
freeaddrinfo(ai);
if(sock < 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "socket", strerror(errno));
return -1;
}
if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "connect", strerror(errno));
return -1;
}
logger(LOG_DEBUG, _("Tap reader running"));
/* Read from tap device and send to parent */
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
overlapped.Offset = 0;
overlapped.OffsetHigh = 0;
ResetEvent(overlapped.hEvent);
status = ReadFile(device_handle, buf, sizeof(buf), &len, &overlapped);
if(!status) {
if(GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(overlapped.hEvent, INFINITE);
if(!GetOverlappedResult(device_handle, &overlapped, &len, FALSE))
continue;
} else {
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info,
device, strerror(errno));
return -1;
}
}
if(send(sock, buf, len, 0) <= 0)
return -1;
}
}
bool setup_device(void)
{
HKEY key, key2;
int i;
char regpath[1024];
char adapterid[1024];
char adaptername[1024];
char tapname[1024];
long len;
bool found = false;
int sock, err;
HANDLE thread;
struct addrinfo *ai;
struct addrinfo hint = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP,
.ai_flags = 0,
};
cp();
get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
/* Open registry and look for network adapters */
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_NET, 0, KEY_READ, &key)) {
logger(LOG_ERR, _("Unable to read registry: %s"), winerror(GetLastError()));
return false;
}
for (i = 0; ; i++) {
len = sizeof(adapterid);
if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
break;
/* Find out more about this adapter */
snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", REG_CONTROL_NET, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
continue;
len = sizeof(adaptername);
err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
RegCloseKey(key2);
if(err)
continue;
if(device) {
if(!strcmp(device, adapterid)) {
found = true;
break;
} else
continue;
}
if(iface) {
if(!strcmp(iface, adaptername)) {
found = true;
break;
} else
continue;
}
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
if(device_handle != INVALID_HANDLE_VALUE) {
found = true;
break;
}
}
RegCloseKey(key);
if(!found) {
logger(LOG_ERR, _("No Windows tap device found!"));
return false;
}
if(!device)
device = xstrdup(adapterid);
if(!iface)
iface = xstrdup(adaptername);
/* Try to open the corresponding tap device */
if(device_handle == INVALID_HANDLE_VALUE) {
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
}
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, _("%s (%s) is not a usable Windows tap device: %s"), device, iface, winerror(GetLastError()));
return false;
}
/* Get MAC address from tap device */
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
logger(LOG_ERR, _("Could not get MAC address from Windows tap device %s (%s): %s"), device, iface, winerror(GetLastError()));
return false;
}
if(routing_mode == RMODE_ROUTER) {
overwrite_mac = 1;
}
/* Create a listening socket */
err = getaddrinfo(NULL, myport, &hint, &ai);
if(err || !ai) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "getaddrinfo", gai_strerror(errno));
return false;
}
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(sock < 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "socket", strerror(errno));
return false;
}
if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "bind", strerror(errno));
return false;
}
freeaddrinfo(ai);
if(listen(sock, 1)) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "listen", strerror(errno));
return false;
}
/* Start the tap reader */
thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL);
if(!thread) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "CreateThread", winerror(GetLastError()));
return false;
}
/* Wait for the tap reader to connect back to us */
if((device_fd = accept(sock, NULL, 0)) == -1) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "accept", strerror(errno));
return false;
}
closesocket(sock);
device_info = _("Windows tap device");
logger(LOG_INFO, _("%s (%s) is a %s"), device, iface, device_info);
return true;
}
void close_device(void)
{
cp();
CloseHandle(device_handle);
}
bool read_packet(vpn_packet_t *packet)
{
int lenin;
cp();
if((lenin = recv(device_fd, packet->data, MTU, 0)) <= 0) {
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info,
device, strerror(errno));
return false;
}
packet->len = lenin;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len,
device_info);
return true;
}
bool write_packet(vpn_packet_t *packet)
{
long lenout;
OVERLAPPED overlapped = {0};
cp();
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(!WriteFile(device_handle, packet->data, packet->len, &lenout, &overlapped)) {
logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, device, winerror(GetLastError()));
return false;
}
device_total_out += packet->len;
return true;
}
void dump_device_stats(void)
{
cp();
logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
logger(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
logger(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
}

624
src/net.c
View file

@ -1,7 +1,7 @@
/*
net.c -- most of the network code
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1998-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,213 +17,183 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net.c,v 1.39 2002/04/28 12:46:26 zarq Exp $
$Id: net.c,v 1.40 2003/08/24 20:38:24 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include "system.h"
#include <openssl/rand.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "utils.h"
#include "avl_tree.h"
#include "conf.h"
#include "connection.h"
#include "device.h"
#include "event.h"
#include "graph.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "logging.h"
#include "subnet.h"
#include "xalloc.h"
#include "system.h"
int do_purge = 0;
int sighup = 0;
int sigalrm = 0;
bool do_purge = false;
volatile bool running = false;
time_t now = 0;
/* Purge edges and subnets of unreachable nodes. Use carefully. */
void purge(void)
static void purge(void)
{
avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext, *cnode;
node_t *n;
edge_t *e;
subnet_t *s;
connection_t *c;
cp
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_DEBUG, _("Purging unreachable nodes"));
avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
node_t *n;
edge_t *e;
subnet_t *s;
for(nnode = node_tree->head; nnode; nnode = nnext)
{
nnext = nnode->next;
n = (node_t *)nnode->data;
cp();
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, _("Purging node %s (%s)"), n->name, n->hostname);
ifdebug(PROTOCOL) logger(LOG_DEBUG, _("Purging unreachable nodes"));
for(snode = n->subnet_tree->head; snode; snode = snext)
{
snext = snode->next;
s = (subnet_t *)snode->data;
/* Remove all edges and subnets owned by unreachable nodes. */
for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
c = (connection_t *)cnode->data;
if(c->status.active)
send_del_subnet(c, s);
}
for(nnode = node_tree->head; nnode; nnode = nnext) {
nnext = nnode->next;
n = (node_t *) nnode->data;
subnet_del(n, s);
}
if(!n->status.reachable) {
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Purging node %s (%s)"), n->name,
n->hostname);
for(enode = n->edge_tree->head; enode; enode = enext)
{
enext = enode->next;
e = (edge_t *)enode->data;
for(snode = n->subnet_tree->head; snode; snode = snext) {
snext = snode->next;
s = (subnet_t *) snode->data;
send_del_subnet(broadcast, s);
subnet_del(n, s);
}
for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
c = (connection_t *)cnode->data;
if(c->status.active)
send_del_edge(c, e);
}
for(enode = n->edge_tree->head; enode; enode = enext) {
enext = enode->next;
e = (edge_t *) enode->data;
send_del_edge(broadcast, e);
edge_del(e);
}
}
}
edge_del(e);
}
/* Check if anyone else claims to have an edge to an unreachable node. If not, delete node. */
node_del(n);
}
}
cp
for(nnode = node_tree->head; nnode; nnode = nnext) {
nnext = nnode->next;
n = (node_t *) nnode->data;
if(!n->status.reachable) {
for(enode = edge_weight_tree->head; enode; enode = enext) {
enext = enode->next;
e = (edge_t *) enode->data;
if(e->to == n)
break;
}
if(!enode)
node_del(n);
}
}
}
/*
put all file descriptors in an fd_set array
While we're at it, purge stuff that needs to be removed.
*/
void build_fdset(fd_set *fs)
static int build_fdset(fd_set * fs)
{
avl_node_t *node, *next;
connection_t *c;
int i;
cp
FD_ZERO(fs);
avl_node_t *node, *next;
connection_t *c;
int i, max = 0;
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
cp();
if(c->status.remove)
connection_del(c);
else
FD_SET(c->socket, fs);
}
FD_ZERO(fs);
if(!connection_tree->head)
purge();
for(node = connection_tree->head; node; node = next) {
next = node->next;
c = (connection_t *) node->data;
for(i = 0; i < listen_sockets; i++)
{
FD_SET(listen_socket[i].tcp, fs);
FD_SET(listen_socket[i].udp, fs);
}
if(c->status.remove) {
connection_del(c);
if(!connection_tree->head)
purge();
} else {
FD_SET(c->socket, fs);
if(c->socket > max)
max = c->socket;
}
}
FD_SET(device_fd, fs);
cp
for(i = 0; i < listen_sockets; i++) {
FD_SET(listen_socket[i].tcp, fs);
if(listen_socket[i].tcp > max)
max = listen_socket[i].tcp;
FD_SET(listen_socket[i].udp, fs);
if(listen_socket[i].udp > max)
max = listen_socket[i].udp;
}
FD_SET(device_fd, fs);
if(device_fd > max)
max = device_fd;
return max;
}
/*
Terminate a connection:
- Close the socket
- Remove associated edge and tell other connections about it if report = 1
- Remove associated edge and tell other connections about it if report = true
- Check if we need to retry making an outgoing connection
- Deactivate the host
*/
void terminate_connection(connection_t *c, int report)
void terminate_connection(connection_t *c, bool report)
{
avl_node_t *node;
connection_t *other;
cp
if(c->status.remove)
return;
cp();
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Closing connection with %s (%s)"),
c->name, c->hostname);
if(c->status.remove)
return;
c->status.remove = 1;
c->status.active = 0;
ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Closing connection with %s (%s)"),
c->name, c->hostname);
if(c->node)
c->node->connection = NULL;
c->status.remove = true;
c->status.active = false;
if(c->socket)
close(c->socket);
if(c->node)
c->node->connection = NULL;
if(c->edge)
{
if(report)
{
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_del_edge(other, c->edge);
}
}
if(c->socket)
closesocket(c->socket);
edge_del(c->edge);
if(c->edge) {
if(report)
send_del_edge(broadcast, c->edge);
/* Run MST and SSSP algorithms */
edge_del(c->edge);
graph();
}
/* Run MST and SSSP algorithms */
/* Check if this was our outgoing connection */
graph();
}
if(c->outgoing)
{
retry_outgoing(c->outgoing);
c->outgoing = NULL;
}
cp
/* Check if this was our outgoing connection */
if(c->outgoing) {
retry_outgoing(c->outgoing);
c->outgoing = NULL;
}
}
/*
@ -234,218 +204,232 @@ cp
end does not reply in time, we consider them dead
and close the connection.
*/
void check_dead_connections(void)
static void check_dead_connections(void)
{
avl_node_t *node, *next;
connection_t *c;
cp
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
if(c->last_ping_time + pingtimeout < now)
{
if(c->status.active)
{
if(c->status.pinged)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_INFO, _("%s (%s) didn't respond to PING"),
c->name, c->hostname);
c->status.timeout = 1;
terminate_connection(c, 1);
}
else
{
send_ping(c);
}
}
else
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_WARNING, _("Timeout from %s (%s) during authentication"),
c->name, c->hostname);
terminate_connection(c, 0);
}
}
}
cp
avl_node_t *node, *next;
connection_t *c;
cp();
for(node = connection_tree->head; node; node = next) {
next = node->next;
c = (connection_t *) node->data;
if(c->last_ping_time + pingtimeout < now) {
if(c->status.active) {
if(c->status.pinged) {
ifdebug(CONNECTIONS) logger(LOG_INFO, _("%s (%s) didn't respond to PING"),
c->name, c->hostname);
c->status.timeout = true;
terminate_connection(c, true);
} else {
send_ping(c);
}
} else {
if(c->status.remove) {
logger(LOG_WARNING, _("Old connection_t for %s (%s) status %04x still lingering, deleting..."),
c->name, c->hostname, *(uint32_t *)&c->status);
connection_del(c);
continue;
}
ifdebug(CONNECTIONS) logger(LOG_WARNING, _("Timeout from %s (%s) during authentication"),
c->name, c->hostname);
terminate_connection(c, false);
}
}
}
}
/*
check all connections to see if anything
happened on their sockets
*/
void check_network_activity(fd_set *f)
static void check_network_activity(fd_set * f)
{
connection_t *c;
avl_node_t *node;
int result, i;
int len = sizeof(result);
vpn_packet_t packet;
cp
if(FD_ISSET(device_fd, f))
{
if(!read_packet(&packet))
route_outgoing(&packet);
}
connection_t *c;
avl_node_t *node;
int result, i;
int len = sizeof(result);
vpn_packet_t packet;
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
cp();
if(c->status.remove)
continue;
if(FD_ISSET(device_fd, f)) {
if(read_packet(&packet))
route_outgoing(&packet);
}
if(FD_ISSET(c->socket, f))
{
if(c->status.connecting)
{
c->status.connecting = 0;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
if(!result)
finish_connecting(c);
else
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_DEBUG, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(result));
close(c->socket);
do_outgoing_connection(c);
continue;
}
}
if(receive_meta(c) < 0)
{
terminate_connection(c, c->status.active);
continue;
}
}
}
for(node = connection_tree->head; node; node = node->next) {
c = (connection_t *) node->data;
for(i = 0; i < listen_sockets; i++)
{
if(FD_ISSET(listen_socket[i].udp, f))
handle_incoming_vpn_data(listen_socket[i].udp);
if(FD_ISSET(listen_socket[i].tcp, f))
handle_new_meta_connection(listen_socket[i].tcp);
}
cp
if(c->status.remove)
continue;
if(FD_ISSET(c->socket, f)) {
if(c->status.connecting) {
c->status.connecting = false;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
if(!result)
finish_connecting(c);
else {
ifdebug(CONNECTIONS) logger(LOG_DEBUG,
_("Error while connecting to %s (%s): %s"),
c->name, c->hostname, strerror(result));
closesocket(c->socket);
do_outgoing_connection(c);
continue;
}
}
if(!receive_meta(c)) {
terminate_connection(c, c->status.active);
continue;
}
}
}
for(i = 0; i < listen_sockets; i++) {
if(FD_ISSET(listen_socket[i].udp, f))
handle_incoming_vpn_data(listen_socket[i].udp);
if(FD_ISSET(listen_socket[i].tcp, f))
handle_new_meta_connection(listen_socket[i].tcp);
}
}
/*
this is where it all happens...
*/
void main_loop(void)
int main_loop(void)
{
fd_set fset;
struct timeval tv;
int r;
time_t last_ping_check;
event_t *event;
cp
last_ping_check = now;
fd_set fset;
struct timeval tv;
int r, maxfd;
time_t last_ping_check, last_config_check;
event_t *event;
srand(now);
cp();
for(;;)
{
now = time(NULL);
last_ping_check = now;
last_config_check = now;
srand(now);
tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
tv.tv_usec = 0;
running = true;
build_fdset(&fset);
while(running) {
now = time(NULL);
if((r = select(FD_SETSIZE, &fset, NULL, NULL, &tv)) < 0)
{
if(errno != EINTR && errno != EAGAIN)
{
syslog(LOG_ERR, _("Error while waiting for input: %s"), strerror(errno));
cp_trace();
dump_connections();
return;
}
tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
tv.tv_usec = 0;
continue;
}
maxfd = build_fdset(&fset);
check_network_activity(&fset);
r = select(maxfd + 1, &fset, NULL, NULL, &tv);
if(do_purge)
{
purge();
do_purge = 0;
}
if(r < 0) {
if(errno != EINTR && errno != EAGAIN) {
logger(LOG_ERR, _("Error while waiting for input: %s"),
strerror(errno));
cp_trace();
dump_connections();
return 1;
}
/* Let's check if everybody is still alive */
continue;
}
if(last_ping_check + pingtimeout < now)
{
check_dead_connections();
last_ping_check = now;
check_network_activity(&fset);
if(routing_mode== RMODE_SWITCH)
age_mac();
if(do_purge) {
purge();
do_purge = false;
}
age_past_requests();
/* Let's check if everybody is still alive */
/* Should we regenerate our key? */
if(last_ping_check + pingtimeout < now) {
check_dead_connections();
last_ping_check = now;
if(keyexpires < now)
{
if(debug_lvl >= DEBUG_STATUS)
syslog(LOG_INFO, _("Regenerating symmetric key"));
if(routing_mode == RMODE_SWITCH)
age_mac();
#ifdef USE_OPENSSL
RAND_pseudo_bytes(myself->key, myself->keylength);
#endif
send_key_changed(myself->connection, myself);
keyexpires = now + keylifetime;
}
}
age_past_requests();
/* Should we regenerate our key? */
if(keyexpires < now) {
ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key"));
RAND_pseudo_bytes(myself->key, myself->keylength);
if(myself->cipher)
EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, myself->key, myself->key + myself->cipher->key_len);
send_key_changed(broadcast, myself);
keyexpires = now + keylifetime;
}
}
while((event = get_expired_event()))
{
event->handler(event->data);
free(event);
}
while((event = get_expired_event())) {
event->handler(event->data);
free(event);
}
if(sigalrm)
{
syslog(LOG_INFO, _("Flushing event queue"));
if(sigalrm) {
logger(LOG_INFO, _("Flushing event queue"));
while(event_tree->head)
{
event = (event_t *)event_tree->head->data;
event->handler(event->data);
event_del(event);
}
sigalrm = 0;
}
while(event_tree->head) {
event = (event_t *) event_tree->head->data;
event->handler(event->data);
event_del(event);
}
sigalrm = false;
}
if(sighup)
{
sighup = 0;
close_network_connections();
exit_configuration(&config_tree);
if(sighup) {
connection_t *c;
avl_node_t *node;
char *fname;
struct stat s;
sighup = false;
/* Reread our own configuration file */
syslog(LOG_INFO, _("Rereading configuration file and restarting in 5 seconds..."));
sleep(5);
exit_configuration(&config_tree);
init_configuration(&config_tree);
init_configuration(&config_tree);
if(!read_server_config()) {
logger(LOG_ERR, _("Unable to reread configuration file, exitting."));
return 1;
}
if(read_server_config())
{
syslog(LOG_ERR, _("Unable to reread configuration file, exitting."));
exit(1);
}
/* Close connections to hosts that have a changed or deleted host config file */
for(node = connection_tree->head; node; node = node->next) {
c = (connection_t *) node->data;
if(c->outgoing) {
free(c->outgoing->name);
freeaddrinfo(c->outgoing->ai);
free(c->outgoing);
c->outgoing = NULL;
}
asprintf(&fname, "%s/hosts/%s", confbase, c->name);
if(stat(fname, &s) || s.st_mtime > last_config_check)
terminate_connection(c, c->status.active);
free(fname);
}
if(setup_network_connections())
return;
last_config_check = now;
continue;
}
}
cp
/* Try to make outgoing connections */
try_outgoing_connections();
}
}
return 0;
}

158
src/net.h Normal file
View file

@ -0,0 +1,158 @@
/*
net.h -- header for net.c
Copyright (C) 1998-2003 Ivo Timmermans <zarq@iname.com>
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net.h,v 1.13 2003/08/24 20:38:24 guus Exp $
*/
#ifndef __TINC_NET_H__
#define __TINC_NET_H__
#include <openssl/evp.h>
#include "ipv6.h"
#ifdef ENABLE_JUMBOGRAMS
#define MTU 9014 /* 9000 bytes payload + 14 bytes ethernet header */
#else
#define MTU 1514 /* 1500 bytes payload + 14 bytes ethernet header */
#endif
#define MAXSIZE (MTU + 4 + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */
#define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */
#define MAXSOCKETS 128 /* Overkill... */
#define MAXQUEUELENGTH 8 /* Maximum number of packats in a single queue */
typedef struct mac_t {
uint8_t x[6];
} mac_t;
typedef struct ipv4_t {
uint8_t x[4];
} ipv4_t;
typedef struct ipv6_t {
uint16_t x[8];
} ipv6_t;
typedef short length_t;
#define AF_UNKNOWN 0xFFFF
struct sockaddr_unknown {
uint16_t family;
uint16_t pad1;
uint32_t pad2;
char *address;
char *port;
};
typedef union sockaddr_t {
struct sockaddr sa;
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_unknown unknown;
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
struct sockaddr_storage storage;
#endif
} sockaddr_t;
#ifdef SA_LEN
#define SALEN(s) SA_LEN(&s)
#else
#define SALEN(s) (s.sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))
#endif
typedef struct vpn_packet_t {
length_t len; /* the actual number of bytes in the `data' field */
int priority; /* priority or TOS */
uint32_t seqno; /* 32 bits sequence number (network byte order of course) */
uint8_t data[MAXSIZE];
} vpn_packet_t;
typedef struct queue_element_t {
void *packet;
struct queue_element_t *prev;
struct queue_element_t *next;
} queue_element_t;
typedef struct packet_queue_t {
queue_element_t *head;
queue_element_t *tail;
} packet_queue_t;
typedef struct listen_socket_t {
int tcp;
int udp;
sockaddr_t sa;
} listen_socket_t;
#include "conf.h"
typedef struct outgoing_t {
char *name;
int timeout;
struct config_t *cfg;
struct addrinfo *ai;
struct addrinfo *aip;
} outgoing_t;
extern int maxtimeout;
extern int seconds_till_retry;
extern int addressfamily;
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
extern int keyexpires;
extern int keylifetime;
extern bool do_prune;
extern bool do_purge;
extern char *myport;
extern time_t now;
extern EVP_CIPHER_CTX packet_ctx;
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
#include "connection.h"
#include "node.h"
extern void retry_outgoing(outgoing_t *);
extern void handle_incoming_vpn_data(int);
extern void finish_connecting(struct connection_t *);
extern void do_outgoing_connection(struct connection_t *);
extern bool handle_new_meta_connection(int);
extern int setup_listen_socket(const sockaddr_t *);
extern int setup_vpn_in_socket(const sockaddr_t *);
extern void send_packet(const struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, char *, int);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
extern bool setup_network_connections(void);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
extern void close_network_connections(void);
extern int main_loop(void);
extern void terminate_connection(struct connection_t *, bool);
extern void flush_queue(struct node_t *);
extern bool read_rsa_public_key(struct connection_t *);
#ifndef HAVE_MINGW
#define closesocket(s) close(s)
#endif
#endif /* __TINC_NET_H__ */

View file

@ -1,7 +1,7 @@
/*
net_packet.c -- Handles in- and outgoing VPN packets
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1998-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,458 +17,409 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net_packet.c,v 1.4 2002/04/28 12:46:26 zarq Exp $
$Id: net_packet.c,v 1.5 2003/08/24 20:38:24 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include "system.h"
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/hmac.h>
#ifndef HAVE_RAND_PSEUDO_BYTES
#define RAND_pseudo_bytes RAND_bytes
#endif
#include <zlib.h>
#include <lzo1x.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "avl_tree.h"
#include "conf.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "logging.h"
#include "system.h"
#include "graph.h"
#include "list.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "process.h"
#include "route.h"
#include "utils.h"
#include "xalloc.h"
int keylifetime = 0;
int keyexpires = 0;
EVP_CIPHER_CTX packet_ctx;
static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
#define MAX_SEQNO 1073741824
static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level)
{
if(level == 10) {
lzo_uint lzolen = MAXSIZE;
lzo1x_1_compress(source, len, dest, &lzolen, lzo_wrkmem);
return lzolen;
} else if(level < 10) {
unsigned long destlen = MAXSIZE;
if(compress2(dest, &destlen, source, len, level) == Z_OK)
return destlen;
else
return -1;
} else {
lzo_uint lzolen = MAXSIZE;
lzo1x_999_compress(source, len, dest, &lzolen, lzo_wrkmem);
return lzolen;
}
return -1;
}
static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level)
{
if(level > 9) {
lzo_uint lzolen = MAXSIZE;
if(lzo1x_decompress_safe(source, len, dest, &lzolen, NULL) == LZO_E_OK)
return lzolen;
else
return -1;
} else {
unsigned long destlen = MAXSIZE;
if(uncompress(dest, &destlen, source, len) == Z_OK)
return destlen;
else
return -1;
}
return -1;
}
/* VPN packet I/O */
void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
static void receive_packet(node_t *n, vpn_packet_t *packet)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
int nextpkt = 0;
vpn_packet_t *outpkt = pkt[0];
int outlen, outpad;
long int complen = MTU + 12;
#ifdef USE_OPENSSL
EVP_CIPHER_CTX ctx;
char hmac[EVP_MAX_MD_SIZE];
#endif
#ifdef USE_GCRYPT
char *hmac;
#endif
cp
/* Check the message authentication code */
cp();
if(myself->digest && myself->maclength)
{
inpkt->len -= myself->maclength;
#ifdef USE_OPENSSL
HMAC(myself->digest, myself->key, myself->keylength, (char *)&inpkt->seqno, inpkt->len, hmac, NULL);
#endif
#ifdef USE_GCRYPT
hmac = xmalloc(gcry_md_get_algo_dlen(0)); /* myself->digest type */
gcry_md_hash_buffer(0, hmac, (char *)&inpkt->seqno, inpkt->len);
/* FIXME */
#endif
if(memcmp(hmac, (char *)&inpkt->seqno + inpkt->len, myself->maclength))
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname);
return;
}
}
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"),
packet->len, n->name, n->hostname);
/* Decrypt the packet */
route_incoming(n, packet);
}
if(myself->cipher)
{
outpkt = pkt[nextpkt++];
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
vpn_packet_t *outpkt = pkt[0];
int outlen, outpad;
char hmac[EVP_MAX_MD_SIZE];
int i;
#ifdef USE_OPENSSL
EVP_DecryptInit(&ctx, myself->cipher, myself->key, myself->key + myself->cipher->key_len);
EVP_DecryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
EVP_DecryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
#endif
#ifdef USE_GCRYPT
/* FIXME */
#endif
cp();
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
/* Check the message authentication code */
/* Check the sequence number */
if(myself->digest && myself->maclength) {
inpkt->len -= myself->maclength;
HMAC(myself->digest, myself->key, myself->keylength,
(char *) &inpkt->seqno, inpkt->len, hmac, NULL);
inpkt->len -= sizeof(inpkt->seqno);
inpkt->seqno = ntohl(inpkt->seqno);
if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, myself->maclength)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"),
n->name, n->hostname);
return;
}
}
if(inpkt->seqno <= n->received_seqno)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Got late or replayed packet from %s (%s), seqno %d"), n->name, n->hostname, inpkt->seqno);
return;
}
n->received_seqno = inpkt->seqno;
/* Decrypt the packet */
if(n->received_seqno > MAX_SEQNO)
keyexpires = 0;
if(myself->cipher) {
outpkt = pkt[nextpkt++];
/* Decompress the packet */
if(myself->compression)
{
outpkt = pkt[nextpkt++];
// EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, myself->key,
// myself->key + myself->cipher->key_len);
EVP_DecryptInit_ex(&packet_ctx, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(&packet_ctx, (char *) &outpkt->seqno, &outlen,
(char *) &inpkt->seqno, inpkt->len);
EVP_DecryptFinal_ex(&packet_ctx, (char *) &outpkt->seqno + outlen, &outpad);
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
if(uncompress(outpkt->data, &complen, inpkt->data, inpkt->len) != Z_OK)
{
syslog(LOG_ERR, _("Error while uncompressing packet from %s (%s)"), n->name, n->hostname);
return;
}
outpkt->len = complen;
inpkt = outpkt;
}
/* Check the sequence number */
receive_packet(n, inpkt);
cp
inpkt->len -= sizeof(inpkt->seqno);
inpkt->seqno = ntohl(inpkt->seqno);
if(inpkt->seqno != n->received_seqno + 1) {
if(inpkt->seqno >= n->received_seqno + sizeof(n->late) * 8) {
logger(LOG_WARNING, _("Lost %d packets from %s (%s)"),
inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
memset(n->late, 0, sizeof(n->late));
} else if (inpkt->seqno <= n->received_seqno) {
if(inpkt->seqno <= n->received_seqno - sizeof(n->late) * 8 || !(n->late[(inpkt->seqno / 8) % sizeof(n->late)] & (1 << inpkt->seqno % 8))) {
logger(LOG_WARNING, _("Got late or replayed packet from %s (%s), seqno %d, last received %d"),
n->name, n->hostname, inpkt->seqno, n->received_seqno);
} else
for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
n->late[(inpkt->seqno / 8) % sizeof(n->late)] |= 1 << i % 8;
}
}
n->received_seqno = inpkt->seqno;
n->late[(n->received_seqno / 8) % sizeof(n->late)] &= ~(1 << n->received_seqno % 8);
if(n->received_seqno > MAX_SEQNO)
keyexpires = 0;
/* Decompress the packet */
if(myself->compression) {
outpkt = pkt[nextpkt++];
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, myself->compression)) < 0) {
logger(LOG_ERR, _("Error while uncompressing packet from %s (%s)"),
n->name, n->hostname);
return;
}
inpkt = outpkt;
}
receive_packet(n, inpkt);
}
void receive_tcppacket(connection_t *c, char *buffer, int len)
{
vpn_packet_t outpkt;
cp
outpkt.len = len;
memcpy(outpkt.data, buffer, len);
vpn_packet_t outpkt;
receive_packet(c->node, &outpkt);
cp
cp();
outpkt.len = len;
memcpy(outpkt.data, buffer, len);
receive_packet(c->node, &outpkt);
}
void receive_packet(node_t *n, vpn_packet_t *packet)
static void send_udppacket(node_t *n, vpn_packet_t *inpkt)
{
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), packet->len, n->name, n->hostname);
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
vpn_packet_t *outpkt;
int origlen;
int outlen, outpad;
vpn_packet_t *copy;
static int priority = 0;
int origpriority;
int sock;
route_incoming(n, packet);
cp
}
cp();
void send_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
int nextpkt = 0;
vpn_packet_t *outpkt;
int origlen;
int outlen, outpad;
long int complen = MTU + 12;
vpn_packet_t *copy;
static int priority = 0;
int origpriority;
int sock;
/* Make sure we have a valid key */
#ifdef USE_OPENSSL
EVP_CIPHER_CTX ctx;
#endif
cp
/* Make sure we have a valid key */
if(!n->status.validkey) {
ifdebug(TRAFFIC) logger(LOG_INFO,
_("No valid key known yet for %s (%s), queueing packet"),
n->name, n->hostname);
if(!n->status.validkey)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("No valid key known yet for %s (%s), queueing packet"),
n->name, n->hostname);
/* Since packet is on the stack of handle_tap_input(), we have to make a copy of it first. */
/* Since packet is on the stack of handle_tap_input(),
we have to make a copy of it first. */
copy = xmalloc(sizeof(vpn_packet_t));
memcpy(copy, inpkt, sizeof(vpn_packet_t));
copy = xmalloc(sizeof(vpn_packet_t));
memcpy(copy, inpkt, sizeof(vpn_packet_t));
list_insert_tail(n->queue, copy);
list_insert_tail(n->queue, copy);
if(n->queue->count > MAXQUEUELENGTH)
list_delete_head(n->queue);
if(n->queue->count > MAXQUEUELENGTH)
list_delete_head(n->queue);
if(!n->status.waitingforkey)
send_req_key(n->nexthop->connection, myself, n);
if(!n->status.waitingforkey)
send_req_key(n->nexthop->connection, myself, n);
n->status.waitingforkey = true;
n->status.waitingforkey = 1;
return;
}
return;
}
origlen = inpkt->len;
origpriority = inpkt->priority;
origlen = inpkt->len;
origpriority = inpkt->priority;
/* Compress the packet */
/* Compress the packet */
if(n->compression) {
outpkt = pkt[nextpkt++];
if(n->compression)
{
outpkt = pkt[nextpkt++];
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->compression)) < 0) {
logger(LOG_ERR, _("Error while compressing packet to %s (%s)"),
n->name, n->hostname);
return;
}
if(compress2(outpkt->data, &complen, inpkt->data, inpkt->len, n->compression) != Z_OK)
{
syslog(LOG_ERR, _("Error while compressing packet to %s (%s)"), n->name, n->hostname);
return;
}
outpkt->len = complen;
inpkt = outpkt;
}
inpkt = outpkt;
}
/* Add sequence number */
/* Add sequence number */
inpkt->seqno = htonl(++(n->sent_seqno));
inpkt->len += sizeof(inpkt->seqno);
inpkt->seqno = htonl(++(n->sent_seqno));
inpkt->len += sizeof(inpkt->seqno);
/* Encrypt the packet */
/* Encrypt the packet */
if(n->cipher)
{
outpkt = pkt[nextpkt++];
if(n->cipher) {
outpkt = pkt[nextpkt++];
#ifdef USE_OPENSSL
EVP_EncryptInit(&ctx, n->cipher, n->key, n->key + n->cipher->key_len);
EVP_EncryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
EVP_EncryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
#endif
#ifdef USE_GCRYPT
gcry_cipher_ctl(n->cipher, GCRYCTL_SET_IV, n->key + n->keylength, n->keylength);
gcry_cipher_ctl(n->cipher, GCRYCTL_SET_KEY, n->key, n->keylength);
outlen = inpkt->len;
gcry_cipher_encrypt(n->cipher, (char *)&outpkt->seqno, outlen, (char *)&inpkt->seqno, inpkt->len);
/* FIXME */
#endif
// EVP_EncryptInit_ex(&packet_ctx, n->cipher, NULL, n->key, n->key + n->cipher->key_len);
EVP_EncryptInit_ex(&n->packet_ctx, NULL, NULL, NULL, NULL);
EVP_EncryptUpdate(&n->packet_ctx, (char *) &outpkt->seqno, &outlen,
(char *) &inpkt->seqno, inpkt->len);
EVP_EncryptFinal_ex(&n->packet_ctx, (char *) &outpkt->seqno + outlen, &outpad);
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
/* Add the message authentication code */
/* Add the message authentication code */
if(n->digest && n->maclength)
{
#ifdef USE_OPENSSL
HMAC(n->digest, n->key, n->keylength, (char *)&inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len, &outlen);
#endif
#ifdef USE_GCRYPT
char *hmac;
outlen = gcry_md_get_algo_dlen(0);
hmac = xmalloc(outlen); /* myself->digest type */
gcry_md_ctl(n->digest, GCRYCTL_SET_KEY, n->key, n->keylength);
gcry_md_hash_buffer(0, hmac, (char *)&inpkt->seqno, inpkt->len);
memcpy((char *)&inpkt->seqno, hmac, outlen);
/* FIXME */
#endif
inpkt->len += n->maclength;
}
if(n->digest && n->maclength) {
HMAC(n->digest, n->key, n->keylength, (char *) &inpkt->seqno,
inpkt->len, (char *) &inpkt->seqno + inpkt->len, &outlen);
inpkt->len += n->maclength;
}
/* Determine which socket we have to use */
/* Determine which socket we have to use */
for(sock = 0; sock < listen_sockets; sock++)
if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family)
break;
for(sock = 0; sock < listen_sockets; sock++)
if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family)
break;
if(sock >= listen_sockets)
sock = 0; /* If none is available, just use the first and hope for the best. */
/* Send the packet */
if(sock >= listen_sockets)
sock = 0; /* If none is available, just use the first and hope for the best. */
/* Send the packet */
#if defined(SOL_IP) && defined(IP_TOS)
if(priorityinheritance && origpriority != priority && listen_socket[sock].sa.sa.sa_family == AF_INET)
{
priority = origpriority;
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Setting outgoing packet priority to %d"), priority);
if(setsockopt(sock, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
syslog(LOG_ERR, _("System call `%s' failed: %s"), "setsockopt", strerror(errno));
}
if(priorityinheritance && origpriority != priority
&& listen_socket[sock].sa.sa.sa_family == AF_INET) {
priority = origpriority;
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Setting outgoing packet priority to %d"), priority);
if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
logger(LOG_ERR, _("System call `%s' failed: %s"), "setsockopt", strerror(errno));
}
#endif
if((sendto(listen_socket[sock].udp, (char *)&inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa))) < 0)
{
syslog(LOG_ERR, _("Error sending packet to %s (%s): %s"),
n->name, n->hostname, strerror(errno));
return;
}
inpkt->len = origlen;
cp
if((sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa))) < 0) {
logger(LOG_ERR, _("Error sending packet to %s (%s): %s"), n->name, n->hostname, strerror(errno));
return;
}
inpkt->len = origlen;
}
/*
send a packet to the given vpn ip.
*/
void send_packet(node_t *n, vpn_packet_t *packet)
void send_packet(const node_t *n, vpn_packet_t *packet)
{
node_t *via;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"),
packet->len, n->name, n->hostname);
node_t *via;
if(n == myself)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_NOTICE, _("Packet is looping back to us!"));
}
cp();
return;
}
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Node %s (%s) is not reachable"),
n->name, n->hostname);
return;
}
ifdebug(TRAFFIC) logger(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"),
packet->len, n->name, n->hostname);
via = (n->via == myself)?n->nexthop:n->via;
if(n == myself) {
ifdebug(TRAFFIC) logger(LOG_NOTICE, _("Packet is looping back to us!"));
return;
}
if(via != n && debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Sending packet to %s via %s (%s)"),
n->name, via->name, n->via->hostname);
if(!n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_INFO, _("Node %s (%s) is not reachable"),
n->name, n->hostname);
return;
}
if((myself->options | via->options) & OPTION_TCPONLY)
{
if(send_tcppacket(via->connection, packet))
terminate_connection(via->connection, 1);
}
else
send_udppacket(via, packet);
via = (n->via == myself) ? n->nexthop : n->via;
if(via != n)
ifdebug(TRAFFIC) logger(LOG_ERR, _("Sending packet to %s via %s (%s)"),
n->name, via->name, n->via->hostname);
if((myself->options | via->options) & OPTION_TCPONLY) {
if(!send_tcppacket(via->connection, packet))
terminate_connection(via->connection, true);
} else
send_udppacket(via, packet);
}
/* Broadcast a packet using the minimum spanning tree */
void broadcast_packet(node_t *from, vpn_packet_t *packet)
void broadcast_packet(const node_t *from, vpn_packet_t *packet)
{
avl_node_t *node;
connection_t *c;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
packet->len, from->name, from->hostname);
avl_node_t *node;
connection_t *c;
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c->status.active && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
}
cp
cp();
ifdebug(TRAFFIC) logger(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
packet->len, from->name, from->hostname);
for(node = connection_tree->head; node; node = node->next) {
c = (connection_t *) node->data;
if(c->status.active && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
}
}
void flush_queue(node_t *n)
{
list_node_t *node, *next;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Flushing queue for %s (%s)"), n->name, n->hostname);
list_node_t *node, *next;
for(node = n->queue->head; node; node = next)
{
next = node->next;
send_udppacket(n, (vpn_packet_t *)node->data);
list_delete_node(n->queue, node);
}
cp
cp();
ifdebug(TRAFFIC) logger(LOG_INFO, _("Flushing queue for %s (%s)"), n->name, n->hostname);
for(node = n->queue->head; node; node = next) {
next = node->next;
send_udppacket(n, (vpn_packet_t *) node->data);
list_delete_node(n->queue, node);
}
}
void handle_incoming_vpn_data(int sock)
{
vpn_packet_t pkt;
int x, l = sizeof(x);
char *hostname;
sockaddr_t from;
socklen_t fromlen = sizeof(from);
node_t *n;
cp
if(getsockopt(sock, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
{
syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s"),
__FILE__, __LINE__, sock, strerror(errno));
cp_trace();
exit(1);
}
if(x)
{
syslog(LOG_ERR, _("Incoming data socket error: %s"), strerror(x));
return;
}
vpn_packet_t pkt;
char *hostname;
sockaddr_t from;
socklen_t fromlen = sizeof(from);
node_t *n;
if((pkt.len = recvfrom(sock, (char *)&pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen)) <= 0)
{
syslog(LOG_ERR, _("Receiving packet failed: %s"), strerror(errno));
return;
}
cp();
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
pkt.len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
n = lookup_node_udp(&from);
if(pkt.len <= 0) {
logger(LOG_ERR, _("Receiving packet failed: %s"), strerror(errno));
return;
}
if(!n)
{
hostname = sockaddr2hostname(&from);
syslog(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
free(hostname);
return;
}
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
if(n->connection)
n->connection->last_ping_time = now;
n = lookup_node_udp(&from);
receive_udppacket(n, &pkt);
cp
if(!n) {
hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, _("Received UDP packet from unknown source %s"),
hostname);
free(hostname);
return;
}
if(n->connection)
n->connection->last_ping_time = now;
receive_udppacket(n, &pkt);
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
net_socket.c -- Handle various kinds of sockets.
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1998-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,468 +17,419 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net_socket.c,v 1.3 2002/04/13 11:07:12 zarq Exp $
$Id: net_socket.c,v 1.4 2003/08/24 20:38:25 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "logging.h"
#include "system.h"
int addressfamily = AF_INET;
#include "avl_tree.h"
#include "conf.h"
#include "connection.h"
#include "event.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "utils.h"
#include "xalloc.h"
#ifdef WSAEINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
int addressfamily = AF_UNSPEC;
int maxtimeout = 900;
int seconds_till_retry = 5;
listen_socket_t listen_socket[MAXSOCKETS];
int listen_sockets = 0;
int listen_sockets;
/* Setup sockets */
int setup_listen_socket(sockaddr_t *sa)
int setup_listen_socket(const sockaddr_t *sa)
{
int nfd, flags;
char *addrstr;
int option;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
char *interface;
struct ifreq ifr;
int nfd, flags;
char *addrstr;
int option;
char *iface;
#ifdef SO_BINDTODEVICE
struct ifreq ifr;
#endif
cp
if((nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
syslog(LOG_ERR, _("Creating metasocket failed: %s"), strerror(errno));
return -1;
}
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
return -1;
}
cp();
/* Optimize TCP settings */
nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
if(nfd < 0) {
logger(LOG_ERR, _("Creating metasocket failed: %s"), strerror(errno));
return -1;
}
#ifdef O_NONBLOCK
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
closesocket(nfd);
logger(LOG_ERR, _("System call `%s' failed: %s"), "fcntl",
strerror(errno));
return -1;
}
#endif
/* Optimize TCP settings */
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
#if defined(SOL_TCP) && defined(TCP_NODELAY)
setsockopt(nfd, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
setsockopt(nfd, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
#endif
#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
setsockopt(nfd, SOL_IP, IP_TOS, &option, sizeof(option));
option = IPTOS_LOWDELAY;
setsockopt(nfd, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
{
if(get_config_string
(lookup_config(config_tree, "BindToInterface"), &iface)) {
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
{
close(nfd);
syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
closesocket(nfd);
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
strerror(errno));
return -1;
}
#else
syslog(LOG_WARNING, _("BindToDevice not supported on this platform"));
logger(LOG_WARNING, _("BindToInterface not supported on this platform"));
#endif
}
}
if(bind(nfd, &sa->sa, SALEN(sa->sa)))
{
close(nfd);
addrstr = sockaddr2hostname(sa);
syslog(LOG_ERR, _("Can't bind to %s/tcp: %s"), addrstr, strerror(errno));
free(addrstr);
return -1;
}
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
addrstr = sockaddr2hostname(sa);
logger(LOG_ERR, _("Can't bind to %s/tcp: %s"), addrstr,
strerror(errno));
free(addrstr);
return -1;
}
if(listen(nfd, 3))
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "listen", strerror(errno));
return -1;
}
cp
return nfd;
if(listen(nfd, 3)) {
closesocket(nfd);
logger(LOG_ERR, _("System call `%s' failed: %s"), "listen",
strerror(errno));
return -1;
}
return nfd;
}
int setup_vpn_in_socket(sockaddr_t *sa)
int setup_vpn_in_socket(const sockaddr_t *sa)
{
int nfd, flags;
char *addrstr;
int option;
int nfd, flags;
char *addrstr;
int option;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
char *interface;
struct ifreq ifr;
char *iface;
struct ifreq ifr;
#endif
cp
if((nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
syslog(LOG_ERR, _("Creating UDP socket failed: %s"), strerror(errno));
return -1;
}
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
return -1;
}
cp();
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
{
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
{
close(nfd);
syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
return -1;
if(nfd < 0) {
logger(LOG_ERR, _("Creating UDP socket failed: %s"), strerror(errno));
return -1;
}
#ifdef O_NONBLOCK
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
closesocket(nfd);
logger(LOG_ERR, _("System call `%s' failed: %s"), "fcntl",
strerror(errno));
return -1;
}
}
#endif
if(bind(nfd, &sa->sa, SALEN(sa->sa)))
{
close(nfd);
addrstr = sockaddr2hostname(sa);
syslog(LOG_ERR, _("Can't bind to %s/udp: %s"), addrstr, strerror(errno));
free(addrstr);
return -1;
}
cp
return nfd;
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
if(get_config_string
(lookup_config(config_tree, "BindToInterface"), &iface)) {
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
closesocket(nfd);
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
strerror(errno));
return -1;
}
}
#endif
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
addrstr = sockaddr2hostname(sa);
logger(LOG_ERR, _("Can't bind to %s/udp: %s"), addrstr,
strerror(errno));
free(addrstr);
return -1;
}
return nfd;
}
void retry_outgoing(outgoing_t *outgoing)
{
event_t *event;
cp
outgoing->timeout += 5;
if(outgoing->timeout > maxtimeout)
outgoing->timeout = maxtimeout;
event_t *event;
event = new_event();
event->handler = (event_handler_t)setup_outgoing_connection;
event->time = now + outgoing->timeout;
event->data = outgoing;
event_add(event);
cp();
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Trying to re-establish outgoing connection in %d seconds"), outgoing->timeout);
cp
outgoing->timeout += 5;
if(outgoing->timeout > maxtimeout)
outgoing->timeout = maxtimeout;
event = new_event();
event->handler = (event_handler_t) setup_outgoing_connection;
event->time = now + outgoing->timeout;
event->data = outgoing;
event_add(event);
ifdebug(CONNECTIONS) logger(LOG_NOTICE,
_("Trying to re-establish outgoing connection in %d seconds"),
outgoing->timeout);
}
int setup_outgoing_socket(connection_t *c)
{
int option;
cp
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1)
{
syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
return -1;
}
/* Optimize TCP settings */
#ifdef HAVE_LINUX
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
option = IPTOS_LOWDELAY;
setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
/* Connect */
if(connect(c->socket, &c->address.sa, SALEN(c->address.sa)) == -1)
{
close(c->socket);
syslog(LOG_ERR, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(errno));
return -1;
}
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
cp
return 0;
}
void finish_connecting(connection_t *c)
{
cp
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
cp();
c->last_ping_time = now;
ifdebug(CONNECTIONS) logger(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
send_id(c);
cp
c->last_ping_time = now;
send_id(c);
}
void do_outgoing_connection(connection_t *c)
{
char *address, *port;
int option, result, flags;
cp
char *address, *port;
int option, result, flags;
cp();
begin:
if(!c->outgoing->ai)
{
if(!c->outgoing->cfg)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("Could not set up a meta connection to %s"), c->name);
c->status.remove = 1;
retry_outgoing(c->outgoing);
return;
}
if(!c->outgoing->ai) {
if(!c->outgoing->cfg) {
ifdebug(CONNECTIONS) logger(LOG_ERR, _("Could not set up a meta connection to %s"),
c->name);
c->status.remove = true;
retry_outgoing(c->outgoing);
return;
}
get_config_string(c->outgoing->cfg, &address);
get_config_string(c->outgoing->cfg, &address);
if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
asprintf(&port, "655");
if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
asprintf(&port, "655");
c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
free(address);
free(port);
c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
free(address);
free(port);
c->outgoing->aip = c->outgoing->ai;
c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
}
if(!c->outgoing->aip)
{
freeaddrinfo(c->outgoing->ai);
c->outgoing->ai = NULL;
goto begin;
}
memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
c->outgoing->aip = c->outgoing->aip->ai_next;
if(c->hostname)
free(c->hostname);
c->hostname = sockaddr2hostname(&c->address);
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
goto begin;
}
/* Optimize TCP settings */
#ifdef HAVE_LINUX
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
option = IPTOS_LOWDELAY;
setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
/* Non-blocking */
flags = fcntl(c->socket, F_GETFL);
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0)
{
syslog(LOG_ERR, _("fcntl for %s: %s"), c->hostname, strerror(errno));
}
/* Connect */
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
if(result == -1)
{
if(errno == EINPROGRESS)
{
c->status.connecting = 1;
return;
c->outgoing->aip = c->outgoing->ai;
c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
}
close(c->socket);
if(!c->outgoing->aip) {
freeaddrinfo(c->outgoing->ai);
c->outgoing->ai = NULL;
goto begin;
}
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("%s: %s"), c->hostname, strerror(errno));
memcpy(&c->address, c->outgoing->aip->ai_addr,
c->outgoing->aip->ai_addrlen);
c->outgoing->aip = c->outgoing->aip->ai_next;
goto begin;
}
if(c->hostname)
free(c->hostname);
finish_connecting(c);
return;
cp
c->hostname = sockaddr2hostname(&c->address);
ifdebug(CONNECTIONS) logger(LOG_INFO, _("Trying to connect to %s (%s)"), c->name,
c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1) {
ifdebug(CONNECTIONS) logger(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname,
strerror(errno));
goto begin;
}
/* Optimize TCP settings */
#if defined(SOL_TCP) && defined(TCP_NODELAY)
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
#endif
#if defined(SOL_IP) && defined(IP_TOS)
option = IPTOS_LOWDELAY;
setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
/* Non-blocking */
#ifdef O_NONBLOCK
flags = fcntl(c->socket, F_GETFL);
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0) {
logger(LOG_ERR, _("fcntl for %s: %s"), c->hostname, strerror(errno));
}
#endif
/* Connect */
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
if(result == -1) {
if(errno == EINPROGRESS) {
c->status.connecting = true;
return;
}
closesocket(c->socket);
ifdebug(CONNECTIONS) logger(LOG_ERR, _("%s: %s"), c->hostname, strerror(errno));
goto begin;
}
finish_connecting(c);
return;
}
void setup_outgoing_connection(outgoing_t *outgoing)
{
connection_t *c;
node_t *n;
cp
n = lookup_node(outgoing->name);
if(n)
if(n->connection)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Already connected to %s"), outgoing->name);
n->connection->outgoing = outgoing;
return;
}
connection_t *c;
node_t *n;
c = new_connection();
c->name = xstrdup(outgoing->name);
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
cp();
init_configuration(&c->config_tree);
read_connection_config(c);
outgoing->cfg = lookup_config(c->config_tree, "Address");
if(!outgoing->cfg)
{
syslog(LOG_ERR, _("No address specified for %s"), c->name);
free_connection(c);
free(outgoing->name);
free(outgoing);
return;
}
c->outgoing = outgoing;
c->last_ping_time = now;
n = lookup_node(outgoing->name);
connection_add(c);
if(n)
if(n->connection) {
ifdebug(CONNECTIONS) logger(LOG_INFO, _("Already connected to %s"), outgoing->name);
do_outgoing_connection(c);
n->connection->outgoing = outgoing;
return;
}
c = new_connection();
c->name = xstrdup(outgoing->name);
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
init_configuration(&c->config_tree);
read_connection_config(c);
outgoing->cfg = lookup_config(c->config_tree, "Address");
if(!outgoing->cfg) {
logger(LOG_ERR, _("No address specified for %s"), c->name);
free_connection(c);
free(outgoing->name);
free(outgoing);
return;
}
c->outgoing = outgoing;
c->last_ping_time = now;
connection_add(c);
do_outgoing_connection(c);
}
/*
accept a new tcp connect and create a
new connection
*/
int handle_new_meta_connection(int sock)
bool handle_new_meta_connection(int sock)
{
connection_t *c;
sockaddr_t sa;
int fd, len = sizeof(sa);
cp
if((fd = accept(sock, &sa.sa, &len)) < 0)
{
syslog(LOG_ERR, _("Accepting a new connection failed: %s"), strerror(errno));
return -1;
}
connection_t *c;
sockaddr_t sa;
int fd, len = sizeof(sa);
sockaddrunmap(&sa);
cp();
c = new_connection();
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
fd = accept(sock, &sa.sa, &len);
c->address = sa;
c->hostname = sockaddr2hostname(&sa);
c->socket = fd;
c->last_ping_time = now;
if(fd < 0) {
logger(LOG_ERR, _("Accepting a new connection failed: %s"),
strerror(errno));
return false;
}
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Connection from %s"), c->hostname);
sockaddrunmap(&sa);
connection_add(c);
c = new_connection();
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
c->allow_request = ID;
send_id(c);
cp
return 0;
c->address = sa;
c->hostname = sockaddr2hostname(&sa);
c->socket = fd;
c->last_ping_time = now;
ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection from %s"), c->hostname);
connection_add(c);
c->allow_request = ID;
send_id(c);
return true;
}
void try_outgoing_connections(void)
{
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
cp
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg))
{
get_config_string(cfg, &name);
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
if(check_id(name))
{
syslog(LOG_ERR, _("Invalid name for outgoing connection in %s line %d"), cfg->file, cfg->line);
free(name);
continue;
}
cp();
outgoing = xmalloc_and_zero(sizeof(*outgoing));
outgoing->name = name;
setup_outgoing_connection(outgoing);
}
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg;
cfg = lookup_config_next(config_tree, cfg)) {
get_config_string(cfg, &name);
if(!check_id(name)) {
logger(LOG_ERR,
_("Invalid name for outgoing connection in %s line %d"),
cfg->file, cfg->line);
free(name);
continue;
}
outgoing = xmalloc_and_zero(sizeof(*outgoing));
outgoing->name = name;
setup_outgoing_connection(outgoing);
}
}

304
src/netutl.c Normal file
View file

@ -0,0 +1,304 @@
/*
netutl.c -- some supporting network utility code
Copyright (C) 1998-2003 Ivo Timmermans <ivo@o2w.nl>
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: netutl.c,v 1.17 2003/08/24 20:38:25 guus Exp $
*/
#include "system.h"
#include "net.h"
#include "netutl.h"
#include "logger.h"
#include "utils.h"
#include "xalloc.h"
bool hostnames = false;
/*
Turn a string into a struct addrinfo.
Return NULL on failure.
*/
struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype)
{
struct addrinfo *ai, hint = {0};
int err;
cp();
hint.ai_family = addressfamily;
hint.ai_socktype = socktype;
err = getaddrinfo(address, service, &hint, &ai);
if(err) {
logger(LOG_WARNING, _("Error looking up %s port %s: %s"), address,
service, gai_strerror(err));
return NULL;
}
return ai;
}
sockaddr_t str2sockaddr(const char *address, const char *port)
{
struct addrinfo *ai, hint = {0};
sockaddr_t result;
int err;
cp();
hint.ai_family = AF_UNSPEC;
hint.ai_flags = AI_NUMERICHOST;
hint.ai_socktype = SOCK_STREAM;
err = getaddrinfo(address, port, &hint, &ai);
if(err || !ai) {
ifdebug(SCARY_THINGS)
logger(LOG_DEBUG, "Unknown type address %s port %s", address, port);
result.sa.sa_family = AF_UNKNOWN;
result.unknown.address = xstrdup(address);
result.unknown.port = xstrdup(port);
return result;
}
result = *(sockaddr_t *) ai->ai_addr;
freeaddrinfo(ai);
return result;
}
void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr)
{
char address[NI_MAXHOST];
char port[NI_MAXSERV];
char *scopeid;
int err;
cp();
if(sa->sa.sa_family == AF_UNKNOWN) {
*addrstr = xstrdup(sa->unknown.address);
*portstr = xstrdup(sa->unknown.port);
return;
}
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
if(err) {
logger(LOG_ERR, _("Error while translating addresses: %s"),
gai_strerror(err));
cp_trace();
raise(SIGFPE);
exit(0);
}
scopeid = strchr(address, '%');
if(scopeid)
*scopeid = '\0'; /* Descope. */
*addrstr = xstrdup(address);
*portstr = xstrdup(port);
}
char *sockaddr2hostname(const sockaddr_t *sa)
{
char *str;
char address[NI_MAXHOST] = "unknown";
char port[NI_MAXSERV] = "unknown";
int err;
cp();
if(sa->sa.sa_family == AF_UNKNOWN) {
asprintf(&str, _("%s port %s"), sa->unknown.address, sa->unknown.port);
return str;
}
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port),
hostnames ? 0 : (NI_NUMERICHOST | NI_NUMERICSERV));
if(err) {
logger(LOG_ERR, _("Error while looking up hostname: %s"),
gai_strerror(err));
}
asprintf(&str, _("%s port %s"), address, port);
return str;
}
int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b)
{
int result;
cp();
result = a->sa.sa_family - b->sa.sa_family;
if(result)
return result;
switch (a->sa.sa_family) {
case AF_UNSPEC:
return 0;
case AF_UNKNOWN:
result = strcmp(a->unknown.address, b->unknown.address);
if(result)
return result;
return strcmp(a->unknown.port, b->unknown.port);
case AF_INET:
result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
if(result)
return result;
return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof(a->in.sin_port));
case AF_INET6:
result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
if(result)
return result;
return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof(a->in6.sin6_port));
default:
logger(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"),
a->sa.sa_family);
cp_trace();
raise(SIGFPE);
exit(0);
}
}
void sockaddrcpy(sockaddr_t *a, const sockaddr_t *b) {
cp();
if(b->sa.sa_family != AF_UNKNOWN) {
*a = *b;
} else {
a->unknown.family = AF_UNKNOWN;
a->unknown.address = xstrdup(b->unknown.address);
a->unknown.port = xstrdup(b->unknown.port);
}
}
void sockaddrfree(sockaddr_t *a) {
cp();
if(a->sa.sa_family == AF_UNKNOWN) {
free(a->unknown.address);
free(a->unknown.port);
}
}
void sockaddrunmap(sockaddr_t *sa)
{
cp();
if(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->in6.sin6_addr)) {
sa->in.sin_addr.s_addr = ((uint32_t *) & sa->in6.sin6_addr)[3];
sa->in.sin_family = AF_INET;
}
}
/* Subnet mask handling */
int maskcmp(const void *va, const void *vb, int masklen, int len)
{
int i, m, result;
const char *a = va;
const char *b = vb;
cp();
for(m = masklen, i = 0; m >= 8; m -= 8, i++) {
result = a[i] - b[i];
if(result)
return result;
}
if(m)
return (a[i] & (0x100 - (1 << (8 - m)))) -
(b[i] & (0x100 - (1 << (8 - m))));
return 0;
}
void mask(void *va, int masklen, int len)
{
int i;
char *a = va;
cp();
i = masklen / 8;
masklen %= 8;
if(masklen)
a[i++] &= (0x100 - (1 << masklen));
for(; i < len; i++)
a[i] = 0;
}
void maskcpy(void *va, const void *vb, int masklen, int len)
{
int i, m;
char *a = va;
const char *b = vb;
cp();
for(m = masklen, i = 0; m >= 8; m -= 8, i++)
a[i] = b[i];
if(m) {
a[i] = b[i] & (0x100 - (1 << m));
i++;
}
for(; i < len; i++)
a[i] = 0;
}
bool maskcheck(const void *va, int masklen, int len)
{
int i;
const char *a = va;
cp();
i = masklen / 8;
masklen %= 8;
if(masklen && a[i++] & (0xff >> masklen))
return false;
for(; i < len; i++)
if(a[i] != 0)
return false;
return true;
}

View file

@ -1,7 +1,7 @@
/*
netutl.h -- header file for netutl.c
Copyright (C) 1998-2002 Ivo Timmermans <zarq@iname.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1998-2003 Ivo Timmermans <zarq@iname.com>
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,30 +17,27 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: netutl.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
$Id: netutl.h,v 1.6 2003/08/24 20:38:25 guus Exp $
*/
#ifndef __TINC_NETUTL_H__
#define __TINC_NETUTL_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "net.h"
extern int hostnames;
extern bool hostnames;
extern char *hostlookup(unsigned long);
extern struct addrinfo *str2addrinfo(char *, char *, int);
extern sockaddr_t str2sockaddr(char *, char *);
extern void sockaddr2str(sockaddr_t *, char **, char **);
extern char *sockaddr2hostname(sockaddr_t *);
extern int sockaddrcmp(sockaddr_t *, sockaddr_t *);
extern struct addrinfo *str2addrinfo(const char *, const char *, int);
extern sockaddr_t str2sockaddr(const char *, const char *);
extern void sockaddr2str(const sockaddr_t *, char **, char **);
extern char *sockaddr2hostname(const sockaddr_t *);
extern int sockaddrcmp(const sockaddr_t *, const sockaddr_t *);
extern void sockaddrunmap(sockaddr_t *);
extern int maskcmp(char *, char *, int, int);
extern void maskcpy(char *, char *, int, int);
extern void mask(char *, int, int);
extern int maskcheck(char *, int, int);
extern void sockaddrfree(sockaddr_t *);
extern void sockaddrcpy(sockaddr_t *, const sockaddr_t *);
extern int maskcmp(const void *, const void *, int, int);
extern void maskcpy(void *, const void *, int, int);
extern void mask(void *, int, int);
extern bool maskcheck(const void *, int, int);
#endif /* __TINC_NETUTL_H__ */
#endif /* __TINC_NETUTL_H__ */

190
src/node.c Normal file
View file

@ -0,0 +1,190 @@
/*
node.c -- node tree management
Copyright (C) 2001-2003 Guus Sliepen <guus@sliepen.eu.org>,
2001-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: node.c,v 1.4 2003/08/24 20:38:25 guus Exp $
*/
#include "system.h"
#include "avl_tree.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "utils.h"
#include "xalloc.h"
avl_tree_t *node_tree; /* Known nodes, sorted by name */
avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
node_t *myself;
static int node_compare(const node_t *a, const node_t *b)
{
return strcmp(a->name, b->name);
}
static int node_udp_compare(const node_t *a, const node_t *b)
{
int result;
cp();
result = sockaddrcmp(&a->address, &b->address);
if(result)
return result;
return (a->name && b->name) ? strcmp(a->name, b->name) : 0;
}
void init_nodes(void)
{
cp();
node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node);
node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL);
}
void exit_nodes(void)
{
cp();
avl_delete_tree(node_udp_tree);
avl_delete_tree(node_tree);
}
node_t *new_node(void)
{
node_t *n = (node_t *) xmalloc_and_zero(sizeof(*n));
cp();
n->subnet_tree = new_subnet_tree();
n->edge_tree = new_edge_tree();
n->queue = list_alloc((list_action_t) free);
EVP_CIPHER_CTX_init(&n->packet_ctx);
return n;
}
void free_node(node_t *n)
{
cp();
if(n->queue)
list_delete_list(n->queue);
if(n->name)
free(n->name);
if(n->hostname)
free(n->hostname);
if(n->key)
free(n->key);
if(n->subnet_tree)
free_subnet_tree(n->subnet_tree);
if(n->edge_tree)
free_edge_tree(n->edge_tree);
sockaddrfree(&n->address);
EVP_CIPHER_CTX_cleanup(&n->packet_ctx);
free(n);
}
void node_add(node_t *n)
{
cp();
avl_insert(node_tree, n);
avl_insert(node_udp_tree, n);
}
void node_del(node_t *n)
{
avl_node_t *node, *next;
edge_t *e;
subnet_t *s;
cp();
for(node = n->subnet_tree->head; node; node = next) {
next = node->next;
s = (subnet_t *) node->data;
subnet_del(n, s);
}
for(node = n->edge_tree->head; node; node = next) {
next = node->next;
e = (edge_t *) node->data;
edge_del(e);
}
avl_delete(node_tree, n);
avl_delete(node_udp_tree, n);
}
node_t *lookup_node(char *name)
{
node_t n = {0};
cp();
n.name = name;
return avl_search(node_tree, &n);
}
node_t *lookup_node_udp(const sockaddr_t *sa)
{
node_t n = {0};
cp();
n.address = *sa;
n.name = NULL;
return avl_search(node_udp_tree, &n);
}
void dump_nodes(void)
{
avl_node_t *node;
node_t *n;
cp();
logger(LOG_DEBUG, _("Nodes:"));
for(node = node_tree->head; node; node = node->next) {
n = (node_t *) node->data;
logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s"),
n->name, n->hostname, n->cipher ? n->cipher->nid : 0,
n->digest ? n->digest->type : 0, n->maclength, n->compression,
n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-",
n->via ? n->via->name : "-");
}
logger(LOG_DEBUG, _("End of nodes."));
}

90
src/node.h Normal file
View file

@ -0,0 +1,90 @@
/*
node.h -- header for node.c
Copyright (C) 2001-2003 Guus Sliepen <guus@sliepen.eu.org>,
2001-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: node.h,v 1.4 2003/08/24 20:38:25 guus Exp $
*/
#ifndef __TINC_NODE_H__
#define __TINC_NODE_H__
#include "avl_tree.h"
#include "connection.h"
#include "list.h"
#include "subnet.h"
typedef struct node_status_t {
int active:1; /* 1 if active.. */
int validkey:1; /* 1 if we currently have a valid key for him */
int waitingforkey:1; /* 1 if we already sent out a request */
int visited:1; /* 1 if this node has been visited by one of the graph algorithms */
int reachable:1; /* 1 if this node is reachable in the graph */
int indirect:1; /* 1 if this node is not directly reachable by us */
int unused:26;
} node_status_t;
typedef struct node_t {
char *name; /* name of this node */
long int options; /* options turned on for this node */
sockaddr_t address; /* his real (internet) ip to send UDP packets to */
char *hostname; /* the hostname of its real ip */
node_status_t status;
const EVP_CIPHER *cipher; /* Cipher type for UDP packets */
char *key; /* Cipher key and iv */
int keylength; /* Cipher key and iv length */
EVP_CIPHER_CTX packet_ctx; /* Cipher context */
const EVP_MD *digest; /* Digest type for MAC */
int maclength; /* Length of MAC */
int compression; /* Compressionlevel, 0 = no compression */
list_t *queue; /* Queue for packets awaiting to be encrypted */
struct node_t *nexthop; /* nearest node from us to him */
struct node_t *via; /* next hop for UDP packets */
avl_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
avl_tree_t *edge_tree; /* Edges with this node as one of the endpoints */
struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */
uint32_t sent_seqno; /* Sequence number last sent to this node */
uint32_t received_seqno; /* Sequence number last received from this node */
unsigned char late[16]; /* Bitfield marking late packets */
} node_t;
extern struct node_t *myself;
extern avl_tree_t *node_tree;
extern avl_tree_t *node_udp_tree;
extern void init_nodes(void);
extern void exit_nodes(void);
extern node_t *new_node(void) __attribute__ ((__malloc__));
extern void free_node(node_t *);
extern void node_add(node_t *);
extern void node_del(node_t *);
extern node_t *lookup_node(char *);
extern node_t *lookup_node_udp(const sockaddr_t *);
extern void dump_nodes(void);
#endif /* __TINC_NODE_H__ */

View file

@ -1 +0,0 @@
*.o .libs .deps pokey Makefile.in Makefile

View file

@ -1,30 +0,0 @@
## Produce this file with automake to get Makefile.in
# $Id: Makefile.am,v 1.4 2002/05/02 13:11:55 zarq Exp $
sbin_PROGRAMS = pokey
pokey_SOURCES = graph.c \
interface.c meta.c net.c net_packet.c net_setup.c net_socket.c netutl.c \
process.c protocol.c protocol_auth.c protocol_edge.c \
protocol_misc.c protocol_key.c protocol_subnet.c route.c \
pokey.c read_conf.c
INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib -I$(top_srcdir)/intl -I$(top_srcdir)/src -I/usr/include/gtk-1.2 -I/usr/include/glib-1.2 -I/usr/lib/glib/include -I/usr/include/libglade-1.0 -I/usr/include/gnome-1.0 -I/usr/include/gnome-xml -I/usr/include/libglade-1.0 -I/usr/include/gnome-1.0 -DNEED_GNOMESUPPORT_H -I/usr/lib/gnome-libs/include -I/usr/include/glib-1.2 -I/usr/lib/glib/include -I/usr/include/orbit-1.0 -I/usr/include/gtk-1.2 -I/usr/X11R6/include
noinst_HEADERS = device.h graph.h meta.h net.h netutl.h process.h \
protocol.h route.h read_conf.h
LIBS = @LIBS@ @INTLLIBS@
pokey_LDADD = \
$(top_builddir)/lib/libtinc.a
pokey_LDFLAGS = -L/usr/X11R6/lib -lglade-gnome -lglade -lgnome -lgnomeui -L/usr/lib -lglade-gnome -lglade -L/usr/lib -lxml -lz -rdynamic -L/usr/lib -L/usr/X11R6/lib -rdynamic -lgnomeui -lart_lgpl -lgdk_imlib -lSM -lICE -lgtk -lgdk -lgmodule -ldl -lXi -lXext -lX11 -lgnome -lgnomesupport -lesd -laudiofile -lm -ldb-3 -lglib -lxml -lz -lgtk -lgdk -lXi -lXext -lX11 -lm -lglib
localedir = $(datadir)/locale
CFLAGS = @CFLAGS@ -DPKGLIBDIR=$(pkglibdir) -DCONFDIR=\"$(sysconfdir)\" \
-DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
lint: $(tincd_SOURCES)
lclint -nullassign -nullret +trytorecover +posixlib -skipansiheaders -skipposixheaders +gnuextensions -I/usr/include -I/usr/lib/gcc-lib/i386-linux/2.95.2/include -I. -I/home/zarq/p/tinc/cvs/cabal/src -I.. -I.. -I/home/zarq/p/tinc/cvs/cabal/lib -I/home/zarq/p/tinc/cvs/cabal/intl -D_POSIX_SOURCE -D__ELF__ -Dunix -D__i386__ -Dlinux -DHAVE_CONFIG_H -DPKGLIBDIR=/usr/local/lib/tinc -DCONFDIR=\"/usr/local/etc\" -DLOCALEDIR=\"/usr/local/share/locale\" $^

View file

@ -1,45 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "myalloc.h"
#include "array.h"
void *array_add(array_t *array, void *element)
{
if(!array)
return NULL;
if(array->allocated == 0)
{
array->allocated = 4;
array->data = xcalloc(array->allocated, sizeof(void*));
array->elements = 0;
}
if(array->elements >= array->allocated - 1)
{
int newalloc;
newalloc = array->allocated << 1;
array->data = xrealloc(array->data, newalloc * sizeof(void*));
array->allocated = newalloc;
}
array->data[array->elements] = element;
array->elements++;
return element;
}
array_t *array_create(void)
{
array_t *r;
r = xcalloc(1, sizeof(*r));
return r;
}
void array_free(array_t *array)
{
free(array->data);
free(array);
}

View file

@ -1,18 +0,0 @@
#ifndef __ARRAY_H__
#define __ARRAY_H__
typedef struct array_t {
void **data;
int allocated;
int elements;
} array_t;
#define array_get_ptr(array) ((array)->data)
#define array_get_nelts(array) ((array)->elements)
#define array_get_element(array, index) ((array)->data[(index)])
void *array_add(array_t *array, void *element);
array_t *array_create(void);
void array_free(array_t *array);
#endif /* __ARRAY_H__ */

View file

@ -1,120 +0,0 @@
/*
connection.h -- header for connection.c
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: connection.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#ifndef __TINC_CONNECTION_H__
#define __TINC_CONNECTION_H__
#include <sys/time.h>
#include <avl_tree.h>
#include <list.h>
#ifdef HAVE_OPENSSL_EVP_H
# include <openssl/evp.h>
#else
# include <evp.h>
#endif
#ifdef HAVE_OPENSSL_RSA_H
# include <openssl/rsa.h>
#else
# include <rsa.h>
#endif
#include "net.h"
#include "conf.h"
#include "node.h"
#include "edge.h"
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
typedef struct connection_status_t {
int pinged:1; /* sent ping */
int active:1; /* 1 if active.. */
int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
int termreq:1; /* the termination of this connection was requested */
int remove:1; /* Set to 1 if you want this connection removed */
int timeout:1; /* 1 if gotten timeout */
int encryptout:1; /* 1 if we can encrypt outgoing traffic */
int decryptin:1; /* 1 if we have to decrypt incoming traffic */
int mst:1; /* 1 if this connection is part of a minimum spanning tree */
int unused:18;
} connection_status_t;
typedef struct connection_t {
char *name; /* name he claims to have */
sockaddr_t address; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
int protocol_version; /* used protocol */
int socket; /* socket used for this connection */
long int options; /* options for this connection */
struct connection_status_t status; /* status info */
int estimated_weight; /* estimation for the weight of the edge for this connection */
struct timeval start; /* time this connection was started, used for above estimation */
struct outgoing_t *outgoing; /* used to keep track of outgoing connections */
struct node_t *node; /* node associated with the other end */
struct edge_t *edge; /* edge associated with this connection */
RSA *rsa_key; /* his public/private key */
const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */
const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */
EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */
EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */
char *inkey; /* His symmetric meta key + iv */
char *outkey; /* Our symmetric meta key + iv */
int inkeylength; /* Length of his key + iv */
int outkeylength; /* Length of our key + iv */
const EVP_MD *indigest;
const EVP_MD *outdigest;
int inmaclength;
int outmaclength;
int incompression;
int outcompression;
char *mychallenge; /* challenge we received from him */
char *hischallenge; /* challenge we sent to him */
char buffer[MAXBUFSIZE]; /* metadata input buffer */
int buflen; /* bytes read into buffer */
int allow_request; /* defined if there's only one request possible */
time_t last_ping_time; /* last time we saw some activity from the other end */
avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
} connection_t;
extern avl_tree_t *connection_tree;
extern void init_connections(void);
extern void exit_connections(void);
extern connection_t *new_connection(void);
extern void free_connection(connection_t *);
extern void connection_add(connection_t *);
extern void connection_del(connection_t *);
extern void dump_connections(void);
extern int read_connection_config(connection_t *);
#endif /* __TINC_CONNECTION_H__ */

View file

@ -1,36 +0,0 @@
/*
net.h -- generic header for device.c
Copyright (C) 2001-2002 Ivo Timmermans <zarq@iname.com>
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#ifndef __TINC_DEVICE_H__
#define __TINC_DEVICE_H__
extern int device_fd;
extern char *device;
extern char *interface;
extern int setup_device(void);
extern void close_device(void);
extern int read_packet(vpn_packet_t *);
extern int write_packet(vpn_packet_t *);
extern void dump_device_stats(void);
#endif /* __TINC_DEVICE_H__ */

View file

@ -1,283 +0,0 @@
/*
graph.c -- graph algorithms
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: graph.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
/* We need to generate two trees from the graph:
1. A minimum spanning tree for broadcasts,
2. A single-source shortest path tree for unicasts.
Actually, the first one alone would suffice but would make unicast packets
take longer routes than necessary.
For the MST algorithm we can choose from Prim's or Kruskal's. I personally
favour Kruskal's, because we make an extra AVL tree of edges sorted on
weights (metric). That tree only has to be updated when an edge is added or
removed, and during the MST algorithm we just have go linearly through that
tree, adding safe edges until #edges = #nodes - 1. The implementation here
however is not so fast, because I tried to avoid having to make a forest and
merge trees.
For the SSSP algorithm Dijkstra's seems to be a nice choice. Currently a
simple breadth-first search is presented here.
The SSSP algorithm will also be used to determine whether nodes are directly,
indirectly or not reachable from the source. It will also set the correct
destination address and port of a node if possible.
*/
#include "config.h"
#include <string.h>
#if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
#include <sys/param.h>
#endif
#include <netinet/in.h>
#include <avl_tree.h>
#include <hooks.h>
#include <utils.h>
#include "interface.h"
#include "netutl.h"
#include "node.h"
#include "edge.h"
#include "connection.h"
#include "logging.h"
#include "system.h"
/* Implementation of Kruskal's algorithm.
Running time: O(EN)
Please note that sorting on weight is already done by add_edge().
*/
void mst_kruskal(void)
{
avl_node_t *node, *next;
edge_t *e;
node_t *n;
connection_t *c;
int nodes = 0;
int safe_edges = 0;
int skipped;
/* Clear MST status on connections */
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
c->status.mst = 0;
}
/* Do we have something to do at all? */
if(!edge_weight_tree->head)
return;
log(DEBUG_SCARY_THINGS, TLOG_DEBUG,
_("Running Kruskal's algorithm:"));
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next)
{
n = (node_t *)node->data;
n->status.visited = 0;
nodes++;
}
/* Starting point */
((edge_t *)edge_weight_tree->head->data)->from.node->status.visited = 1;
/* Add safe edges */
for(skipped = 0, node = edge_weight_tree->head; node; node = next)
{
next = node->next;
e = (edge_t *)node->data;
if(e->from.node->status.visited == e->to.node->status.visited)
{
skipped = 1;
continue;
}
e->from.node->status.visited = 1;
e->to.node->status.visited = 1;
if(e->connection)
e->connection->status.mst = 1;
safe_edges++;
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from.node->name, e->to.node->name, e->weight);
if(skipped)
{
next = edge_weight_tree->head;
continue;
}
}
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes, safe_edges);
}
/* Implementation of a simple breadth-first search algorithm.
Running time: O(E)
*/
void sssp_bfs(void)
{
avl_node_t *node, *from, *next, *to;
edge_t *e;
node_t *n;
halfconnection_t to_hc, from_hc;
avl_tree_t *todo_tree;
int indirect;
todo_tree = avl_alloc_tree(NULL, NULL);
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next)
{
n = (node_t *)node->data;
n->status.visited = 0;
n->status.indirect = 1;
}
/* Begin with myself */
myself->status.visited = 1;
myself->status.indirect = 0;
myself->nexthop = myself;
myself->via = myself;
node = avl_alloc_node();
node->data = myself;
avl_insert_top(todo_tree, node);
/* Loop while todo_tree is filled */
while(todo_tree->head)
{
for(from = todo_tree->head; from; from = next) /* "from" is the node from which we start */
{
next = from->next;
n = (node_t *)from->data;
for(to = n->edge_tree->head; to; to = to->next) /* "to" is the edge connected to "from" */
{
e = (edge_t *)to->data;
if(e->from.node == n) /* "from_hc" is the halfconnection with .node == from */
to_hc = e->to, from_hc = e->from;
else
to_hc = e->from, from_hc = e->to;
/* Situation:
/
/
------(n)from_hc-----to_hc
\
\
n->address is set to the to_hc.udpaddress of the edge left of n.
We are currently examining the edge right of n:
- If from_hc.udpaddress != n->address, then to_hc.node is probably
not reachable for the nodes left of n. We do as if the indirectdata
flag is set on edge e.
- If edge e provides for better reachability of to_hc.node, update
to_hc.node and (re)add it to the todo_tree to (re)examine the reachability
of nodes behind it.
*/
indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &from_hc.udpaddress));
if(to_hc.node->status.visited && (!to_hc.node->status.indirect || indirect))
continue;
to_hc.node->status.visited = 1;
to_hc.node->status.indirect = indirect;
to_hc.node->nexthop = (n->nexthop == myself) ? to_hc.node : n->nexthop;
to_hc.node->via = indirect ? n->via : to_hc.node;
to_hc.node->options = e->options;
if(sockaddrcmp(&to_hc.node->address, &to_hc.udpaddress))
{
node = avl_unlink(node_udp_tree, to_hc.node);
to_hc.node->address = to_hc.udpaddress;
if(to_hc.node->hostname)
free(to_hc.node->hostname);
to_hc.node->hostname = sockaddr2hostname(&to_hc.udpaddress);
avl_insert_node(node_udp_tree, node);
}
node = avl_alloc_node();
node->data = to_hc.node;
avl_insert_before(todo_tree, from, node);
}
avl_delete_node(todo_tree, from);
}
}
avl_free_tree(todo_tree);
/* Check reachability status. */
for(node = node_tree->head; node; node = next)
{
next = node->next;
n = (node_t *)node->data;
if(n->status.visited)
{
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Node %s (%s) became reachable"), n->name, n->hostname);
n->status.reachable = 1;
run_hooks("node-visible", n);
}
}
else
{
if(n->status.reachable)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Node %s (%s) became unreachable"), n->name, n->hostname);
n->status.reachable = 0;
n->status.validkey = 0;
n->status.waitingforkey = 0;
n->sent_seqno = 0;
run_hooks("node-invisible", n);
}
}
}
}
void graph(void)
{
mst_kruskal();
sssp_bfs();
}

File diff suppressed because it is too large Load diff

View file

@ -1,62 +0,0 @@
/*
interface.h -- header for interface.c
Copyright (C) 2002 Guus Sliepen <guus@sliepen.warande.net>,
2002 Ivo Timmermans <itimmermans@bigfoot.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: interface.h,v 1.2 2002/04/28 12:46:26 zarq Exp $
*/
#ifndef __TINC_INTERFACE_H__
#define __TINC_INTERFACE_H__
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <libgnomeui/gnome-canvas.h>
#include "node.h"
#include "edge.h"
#define INTERFACE_FILE "pokey.glade"
typedef struct graph_t {
struct graph_t *attractors[20];
struct graph_t *repellors[20];
int nat;
int nrp;
node_t *node;
} graph_t;
struct if_subnet_data {
GnomeCanvasItem *item; /* The gnome canvas item associated with the line */
GtkCTreeNode *ctn;
};
struct if_node_data {
double x, y;
int visible;
int id;
GnomeCanvasItem *item;
GtkCTreeNode *ctn;
GladeXML *hi_xml;
};
extern int build_graph;
void if_build_graph(void);
int init_interface(void);
#endif /* __TINC_INTERFACE_H__ */

View file

@ -1,103 +0,0 @@
/*
logging.c -- log messages to e.g. syslog
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: logging.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <syslog.h>
#include <avl_tree.h>
#include "logging.h"
avl_tree_t *log_hooks_tree = NULL;
int debug_lvl = 0;
int log_compare(const void *a, const void *b)
{
if(a < b)
return -1;
if(a > b)
return 1;
return 0;
}
void log(int level, int priority, char *fmt, ...)
{
avl_node_t *avlnode;
va_list args;
va_start(args, fmt);
for(avlnode = log_hooks_tree->head; avlnode; avlnode = avlnode->next)
{
assert(avlnode->data);
((log_function_t*)(avlnode->data))(level, priority, fmt, args);
}
va_end(args);
}
void log_add_hook(log_function_t *fn)
{
if(!log_hooks_tree)
log_hooks_tree = avl_alloc_tree(log_compare, NULL);
avl_insert(log_hooks_tree, (void*)fn);
}
void log_del_hook(log_function_t *fn)
{
avl_delete(log_hooks_tree, (void*)fn);
}
void log_default(int level, int priority, char *fmt, va_list ap)
{
if(debug_lvl >= level)
vfprintf(stderr, fmt, ap);
}
void log_syslog(int level, int priority, char *fmt, va_list ap)
{
const int priorities[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_ERR, LOG_CRIT };
if(debug_lvl >= level)
vsyslog(priorities[priority], fmt, ap);
}
void tinc_syslog(int priority, char *fmt, ...)
{
/* Mapping syslog prio -> tinc prio */
const int priorities[] = { TLOG_CRITICAL, TLOG_CRITICAL, TLOG_CRITICAL, TLOG_ERROR,
TLOG_NOTICE, TLOG_NOTICE, TLOG_INFO, TLOG_DEBUG };
avl_node_t *avlnode;
va_list args;
va_start(args, fmt);
for(avlnode = log_hooks_tree->head; avlnode; avlnode = avlnode->next)
{
assert(avlnode->data);
((log_function_t*)(avlnode->data))(0, priorities[priority], fmt, args);
}
va_end(args);
}

View file

@ -1,190 +0,0 @@
/*
meta.c -- handle the meta communication
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: meta.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <utils.h>
#include <avl_tree.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
/* This line must be below the rest for FreeBSD */
#include <sys/types.h>
#include <sys/socket.h>
#include <openssl/evp.h>
#include "net.h"
#include "connection.h"
#include "interface.h"
#include "system.h"
#include "protocol.h"
#include "logging.h"
int send_meta(connection_t *c, char *buffer, int length)
{
char *bufp;
int outlen;
char outbuf[MAXBUFSIZE];
cp
log(DEBUG_META, TLOG_DEBUG,
_("Sending %d bytes of metadata to %s (%s)"),
length, c->name, c->hostname);
if(c->status.encryptout)
{
EVP_EncryptUpdate(c->outctx, outbuf, &outlen, buffer, length);
bufp = outbuf;
length = outlen;
}
else
bufp = buffer;
if(write(c->socket, bufp, length) < 0)
{
syslog(LOG_ERR, _("Sending meta data to %s (%s) failed: %s"), c->name, c->hostname, strerror(errno));
return -1;
}
cp
return 0;
}
void broadcast_meta(connection_t *from, char *buffer, int length)
{
avl_node_t *node;
connection_t *c;
cp
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c != from && c->status.active)
send_meta(c, buffer, length);
}
cp
}
int receive_meta(connection_t *c)
{
int x, l = sizeof(x);
int oldlen, i;
int lenin, reqlen;
int decrypted = 0;
char inbuf[MAXBUFSIZE];
cp
if(getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
{
syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s %s (%s)"), __FILE__, __LINE__, c->socket, strerror(errno),
c->name, c->hostname);
return -1;
}
if(x)
{
syslog(LOG_ERR, _("Metadata socket error for %s (%s): %s"),
c->name, c->hostname, strerror(x));
return -1;
}
/* Strategy:
- Read as much as possible from the TCP socket in one go.
- Decrypt it.
- Check if a full request is in the input buffer.
- If yes, process request and remove it from the buffer,
then check again.
- If not, keep stuff in buffer and exit.
*/
lenin = read(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen);
if(lenin<=0)
{
if(lenin==0)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Connection closed by %s (%s)"),
c->name, c->hostname);
}
else
if(errno==EINTR)
return 0;
else
syslog(LOG_ERR, _("Metadata socket read error for %s (%s): %s"),
c->name, c->hostname, strerror(errno));
return -1;
}
oldlen = c->buflen;
c->buflen += lenin;
while(lenin)
{
/* Decrypt */
if(c->status.decryptin && !decrypted)
{
EVP_DecryptUpdate(c->inctx, inbuf, &lenin, c->buffer + oldlen, lenin);
memcpy(c->buffer + oldlen, inbuf, lenin);
decrypted = 1;
}
/* Otherwise we are waiting for a request */
reqlen = 0;
for(i = oldlen; i < c->buflen; i++)
{
if(c->buffer[i] == '\n')
{
c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
reqlen = i + 1;
break;
}
}
if(reqlen)
{
if(receive_request(c))
return -1;
c->buflen -= reqlen;
lenin -= reqlen;
memmove(c->buffer, c->buffer + reqlen, c->buflen);
oldlen = 0;
continue;
}
else
{
break;
}
}
if(c->buflen >= MAXBUFSIZE)
{
syslog(LOG_ERR, _("Metadata read buffer overflow for %s (%s)"),
c->name, c->hostname);
return -1;
}
c->last_ping_time = now;
cp
return 0;
}

View file

@ -1,32 +0,0 @@
/*
meta.h -- header for meta.c
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: meta.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#ifndef __TINC_META_H__
#define __TINC_META_H__
#include "connection.h"
extern int send_meta(connection_t *, const char *, int);
extern int broadcast_meta(connection_t *, const char *, int);
extern int receive_meta(connection_t *);
#endif /* __TINC_META_H__ */

View file

@ -1,467 +0,0 @@
/*
net.c -- most of the network code
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <openssl/rand.h>
#include <gtk/gtk.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "interface.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "logging.h"
#include "system.h"
int do_prune = 0;
int do_purge = 0;
int sighup = 0;
int sigalrm = 0;
time_t now = 0;
/*
put all file descriptors in an fd_set array
*/
void build_fdset(fd_set *fs)
{
avl_node_t *node;
connection_t *c;
int i;
cp
FD_ZERO(fs);
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
FD_SET(c->socket, fs);
}
for(i = 0; i < listen_sockets; i++)
{
FD_SET(listen_socket[i].tcp, fs);
FD_SET(listen_socket[i].udp, fs);
}
cp
}
/* Purge edges and subnets of unreachable nodes. Use carefully. */
void purge(void)
{
avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext, *cnode;
node_t *n;
edge_t *e;
subnet_t *s;
connection_t *c;
cp
log(DEBUG_PROTOCOL, TLOG_DEBUG,
_("Purging unreachable nodes"));
for(nnode = node_tree->head; nnode; nnode = nnext)
{
nnext = nnode->next;
n = (node_t *)nnode->data;
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, _("Purging node %s (%s)"), n->name, n->hostname);
for(snode = n->subnet_tree->head; snode; snode = snext)
{
snext = snode->next;
s = (subnet_t *)snode->data;
for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
c = (connection_t *)cnode->data;
if(c->status.active)
send_del_subnet(c, s);
}
subnet_del(n, s);
}
for(enode = n->edge_tree->head; enode; enode = enext)
{
enext = enode->next;
e = (edge_t *)enode->data;
for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
c = (connection_t *)cnode->data;
if(c->status.active)
send_del_edge(c, e);
}
edge_del(e);
}
node_del(n);
}
}
cp
}
/*
Terminate a connection:
- Close the socket
- Remove associated edge and tell other connections about it if report = 1
- Check if we need to retry making an outgoing connection
- Deactivate the host
*/
void terminate_connection(connection_t *c, int report)
{
avl_node_t *node;
connection_t *other;
cp
if(c->status.remove)
return;
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Closing connection with %s (%s)"),
c->name, c->hostname);
c->status.remove = 1;
if(c->socket)
close(c->socket);
if(c->edge)
{
if(report)
{
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_del_edge(other, c->edge);
}
}
edge_del(c->edge);
/* Run MST and SSSP algorithms */
graph();
}
/* Check if this was our outgoing connection */
if(c->outgoing)
{
retry_outgoing(c->outgoing);
c->outgoing = NULL;
}
/* Deactivate */
c->status.active = 0;
if(c->node)
c->node->connection = NULL;
do_prune = 1;
cp
}
/*
Check if the other end is active.
If we have sent packets, but didn't receive any,
then possibly the other end is dead. We send a
PING request over the meta connection. If the other
end does not reply in time, we consider them dead
and close the connection.
*/
void check_dead_connections(void)
{
avl_node_t *node, *next;
connection_t *c;
cp
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
if(c->last_ping_time + pingtimeout < now)
{
if(c->status.active)
{
if(c->status.pinged)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_INFO, _("%s (%s) didn't respond to PING"),
c->name, c->hostname);
c->status.timeout = 1;
terminate_connection(c, 1);
}
else
{
send_ping(c);
}
}
else
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_WARNING, _("Timeout from %s (%s) during authentication"),
c->name, c->hostname);
terminate_connection(c, 0);
}
}
}
cp
}
/*
check all connections to see if anything
happened on their sockets
*/
void check_network_activity(fd_set *f)
{
connection_t *c;
avl_node_t *node;
int result, i;
int len = sizeof(result);
cp
for(i = 0; i < listen_sockets; i++)
{
if(FD_ISSET(listen_socket[i].tcp, f))
handle_new_meta_connection(listen_socket[i].tcp);
}
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c->status.remove)
return;
if(FD_ISSET(c->socket, f))
{
if(c->status.connecting)
{
c->status.connecting = 0;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
if(!result)
finish_connecting(c);
else
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_DEBUG, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(result));
close(c->socket);
do_outgoing_connection(c);
continue;
}
}
if(receive_meta(c) < 0)
{
terminate_connection(c, c->status.active);
return;
}
}
}
cp
}
void prune_connections(void)
{
connection_t *c;
avl_node_t *node, *next;
cp
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
if(c->status.remove)
connection_del(c);
}
if(!connection_tree->head)
purge();
cp
}
/*
this is where it all happens...
*/
void main_loop(void)
{
fd_set fset;
struct timeval tv;
int r;
time_t last_ping_check;
event_t *event;
cp
last_ping_check = now;
srand(now);
for(;;)
{
now = time(NULL);
/* tv.tv_sec = 1 + (rand() & 7); /\* Approx. 5 seconds, randomized to prevent global synchronisation effects *\/ */
/* tv.tv_usec = 0; */
tv.tv_sec = 0;
tv.tv_usec = 50000;
if(do_prune)
{
prune_connections();
do_prune = 0;
}
build_fdset(&fset);
while(gtk_events_pending())
if(gtk_main_iteration() == FALSE)
return;
if((r = select(FD_SETSIZE, &fset, NULL, NULL, &tv)) < 0)
{
if(errno != EINTR) /* because of a signal */
{
syslog(LOG_ERR, _("Error while waiting for input: %s"), strerror(errno));
return;
}
}
if(r > 0)
check_network_activity(&fset);
if(do_purge)
{
purge();
do_purge = 0;
}
/* Let's check if everybody is still alive */
if(last_ping_check + pingtimeout < now)
{
check_dead_connections();
last_ping_check = now;
if(routing_mode== RMODE_SWITCH)
age_mac();
age_past_requests();
/* Should we regenerate our key? */
if(keyexpires < now)
{
if(debug_lvl >= DEBUG_STATUS)
syslog(LOG_INFO, _("Regenerating symmetric key"));
RAND_pseudo_bytes(myself->key, myself->keylength);
send_key_changed(myself->connection, myself);
keyexpires = now + keylifetime;
}
}
while((event = get_expired_event()))
{
event->handler(event->data);
free(event);
}
if(sigalrm)
{
syslog(LOG_INFO, _("Flushing event queue"));
while(event_tree->head)
{
event = (event_t *)event_tree->head->data;
event->handler(event->data);
event_del(event);
}
sigalrm = 0;
}
if(sighup)
{
sighup = 0;
close_network_connections();
exit_configuration(&config_tree);
syslog(LOG_INFO, _("Rereading configuration file and restarting in 5 seconds..."));
sleep(5);
init_configuration(&config_tree);
if(read_server_config())
{
syslog(LOG_ERR, _("Unable to reread configuration file, exitting."));
exit(1);
}
if(setup_network_connections())
return;
continue;
}
if(build_graph)
if_build_graph();
}
cp
}

View file

@ -1,153 +0,0 @@
/*
net.h -- header for net.c
Copyright (C) 1998-2002 Ivo Timmermans <zarq@iname.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#ifndef __TINC_NET_H__
#define __TINC_NET_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include "config.h"
#ifdef ENABLE_JUMBOGRAMS
#define MTU 9014 /* 9000 bytes payload + 14 bytes ethernet header */
#define MAXSIZE 9100 /* MTU + header (seqno) and trailer (CBC padding and HMAC) */
#define MAXBUFSIZE 9100 /* Must support TCP packets of length 9000. */
#else
#define MTU 1514 /* 1500 bytes payload + 14 bytes ethernet header */
#define MAXSIZE 1600 /* MTU + header (seqno) and trailer (CBC padding and HMAC) */
#define MAXBUFSIZE 2100 /* Quite large but needed for support of keys up to 8192 bits. */
#endif
#define MAXSOCKETS 128 /* Overkill... */
#define MAXQUEUELENGTH 8
typedef struct mac_t
{
unsigned char x[6];
} mac_t;
typedef struct ipv4_t
{
unsigned char x[4];
} ipv4_t;
typedef struct ip_mask_t {
ipv4_t address;
ipv4_t mask;
} ip_mask_t;
typedef struct ipv6_t
{
unsigned short x[8];
} ipv6_t;
typedef unsigned short port_t;
typedef short length_t;
typedef union {
struct sockaddr sa;
struct sockaddr_in in;
struct sockaddr_in6 in6;
} sockaddr_t;
#ifdef SA_LEN
#define SALEN(s) SA_LEN(&s)
#else
#define SALEN(s) (s.sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))
#endif
typedef struct vpn_packet_t {
length_t len; /* the actual number of bytes in the `data' field */
int priority; /* priority or TOS */
unsigned int seqno; /* 32 bits sequence number (network byte order of course) */
unsigned char data[MAXSIZE];
} vpn_packet_t;
typedef struct queue_element_t {
void *packet;
struct queue_element_t *prev;
struct queue_element_t *next;
} queue_element_t;
typedef struct packet_queue_t {
queue_element_t *head;
queue_element_t *tail;
} packet_queue_t;
typedef struct outgoing_t {
char *name;
int timeout;
struct config_t *cfg;
struct addrinfo *ai;
struct addrinfo *aip;
} outgoing_t;
typedef struct listen_socket_t {
int tcp;
int udp;
sockaddr_t sa;
} listen_socket_t;
extern int maxtimeout;
extern int seconds_till_retry;
extern int addressfamily;
extern char *request_name[];
extern char *status_text[];
#include "connection.h" /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
extern int keyexpires;
extern int keylifetime;
extern int do_prune;
extern int do_purge;
extern char *myport;
extern time_t now;
extern void retry_outgoing(outgoing_t *);
extern void handle_incoming_vpn_data(int);
extern void finish_connecting(connection_t *);
extern void do_outgoing_connection(connection_t *);
extern int handle_new_meta_connection(int);
extern int setup_listen_socket(sockaddr_t *);
extern int setup_vpn_in_socket(sockaddr_t *);
extern void send_packet(struct node_t *, vpn_packet_t *);
extern void receive_packet(struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, char *, int);
extern void broadcast_packet(struct node_t *, vpn_packet_t *);
extern int setup_network_connections(void);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
extern void close_network_connections(void);
extern void main_loop(void);
extern void terminate_connection(connection_t *, int);
extern void flush_queue(struct node_t *);
extern int read_rsa_public_key(struct connection_t *);
#endif /* __TINC_NET_H__ */

View file

@ -1,429 +0,0 @@
/*
net_packet.c -- Handles in- and outgoing VPN packets
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net_packet.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/hmac.h>
#ifndef HAVE_RAND_PSEUDO_BYTES
#define RAND_pseudo_bytes RAND_bytes
#endif
#include <zlib.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "logging.h"
#include "system.h"
int keylifetime = 0;
int keyexpires = 0;
#define MAX_SEQNO 1073741824
/* VPN packet I/O */
void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
int nextpkt = 0;
vpn_packet_t *outpkt = pkt[0];
int outlen, outpad;
long int complen = MTU + 12;
EVP_CIPHER_CTX ctx;
char hmac[EVP_MAX_MD_SIZE];
cp
/* Check the message authentication code */
if(myself->digest && myself->maclength)
{
inpkt->len -= myself->maclength;
HMAC(myself->digest, myself->key, myself->keylength, (char *)&inpkt->seqno, inpkt->len, hmac, NULL);
if(memcmp(hmac, (char *)&inpkt->seqno + inpkt->len, myself->maclength))
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname);
return;
}
}
/* Decrypt the packet */
if(myself->cipher)
{
outpkt = pkt[nextpkt++];
EVP_DecryptInit(&ctx, myself->cipher, myself->key, myself->key + myself->cipher->key_len);
EVP_DecryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
EVP_DecryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
/* Check the sequence number */
inpkt->len -= sizeof(inpkt->seqno);
inpkt->seqno = ntohl(inpkt->seqno);
if(inpkt->seqno <= n->received_seqno)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Got late or replayed packet from %s (%s), seqno %d"), n->name, n->hostname, inpkt->seqno);
return;
}
n->received_seqno = inpkt->seqno;
if(n->received_seqno > MAX_SEQNO)
keyexpires = 0;
/* Decompress the packet */
if(myself->compression)
{
outpkt = pkt[nextpkt++];
if(uncompress(outpkt->data, &complen, inpkt->data, inpkt->len) != Z_OK)
{
syslog(LOG_ERR, _("Error while uncompressing packet from %s (%s)"), n->name, n->hostname);
return;
}
outpkt->len = complen;
inpkt = outpkt;
}
receive_packet(n, inpkt);
cp
}
void receive_tcppacket(connection_t *c, char *buffer, int len)
{
vpn_packet_t outpkt;
cp
outpkt.len = len;
memcpy(outpkt.data, buffer, len);
receive_packet(c->node, &outpkt);
cp
}
void receive_packet(node_t *n, vpn_packet_t *packet)
{
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), packet->len, n->name, n->hostname);
cp
}
void send_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
int nextpkt = 0;
vpn_packet_t *outpkt;
int origlen;
int outlen, outpad;
long int complen = MTU + 12;
EVP_CIPHER_CTX ctx;
vpn_packet_t *copy;
static int priority = 0;
int origpriority;
int sock;
cp
/* Make sure we have a valid key */
if(!n->status.validkey)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("No valid key known yet for %s (%s), queueing packet"),
n->name, n->hostname);
/* Since packet is on the stack of handle_tap_input(),
we have to make a copy of it first. */
copy = xmalloc(sizeof(vpn_packet_t));
memcpy(copy, inpkt, sizeof(vpn_packet_t));
list_insert_tail(n->queue, copy);
if(n->queue->count > MAXQUEUELENGTH)
list_delete_head(n->queue);
if(!n->status.waitingforkey)
send_req_key(n->nexthop->connection, myself, n);
n->status.waitingforkey = 1;
return;
}
origlen = inpkt->len;
origpriority = inpkt->priority;
/* Compress the packet */
if(n->compression)
{
outpkt = pkt[nextpkt++];
if(compress2(outpkt->data, &complen, inpkt->data, inpkt->len, n->compression) != Z_OK)
{
syslog(LOG_ERR, _("Error while compressing packet to %s (%s)"), n->name, n->hostname);
return;
}
outpkt->len = complen;
inpkt = outpkt;
}
/* Add sequence number */
inpkt->seqno = htonl(++(n->sent_seqno));
inpkt->len += sizeof(inpkt->seqno);
/* Encrypt the packet */
if(n->cipher)
{
outpkt = pkt[nextpkt++];
EVP_EncryptInit(&ctx, n->cipher, n->key, n->key + n->cipher->key_len);
EVP_EncryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
EVP_EncryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
/* Add the message authentication code */
if(n->digest && n->maclength)
{
HMAC(n->digest, n->key, n->keylength, (char *)&inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len, &outlen);
inpkt->len += n->maclength;
}
/* Determine which socket we have to use */
for(sock = 0; sock < listen_sockets; sock++)
if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family)
break;
if(sock >= listen_sockets)
sock = 0; /* If none is available, just use the first and hope for the best. */
/* Send the packet */
#if defined(SOL_IP) && defined(IP_TOS)
if(priorityinheritance && origpriority != priority && listen_socket[sock].sa.sa.sa_family == AF_INET)
{
priority = origpriority;
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Setting outgoing packet priority to %d"), priority);
if(setsockopt(sock, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
syslog(LOG_ERR, _("System call `%s' failed: %s"), "setsockopt", strerror(errno));
}
#endif
if((sendto(listen_socket[sock].udp, (char *)&inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa))) < 0)
{
syslog(LOG_ERR, _("Error sending packet to %s (%s): %s"),
n->name, n->hostname, strerror(errno));
return;
}
inpkt->len = origlen;
cp
}
/*
send a packet to the given vpn ip.
*/
void send_packet(node_t *n, vpn_packet_t *packet)
{
node_t *via;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"),
packet->len, n->name, n->hostname);
if(n == myself)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_NOTICE, _("Packet is looping back to us!"));
}
return;
}
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Node %s (%s) is not reachable"),
n->name, n->hostname);
return;
}
via = (n->via == myself)?n->nexthop:n->via;
if(via != n && debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Sending packet to %s via %s (%s)"),
n->name, via->name, n->via->hostname);
if((myself->options | via->options) & OPTION_TCPONLY)
{
if(send_tcppacket(via->connection, packet))
terminate_connection(via->connection, 1);
}
else
send_udppacket(via, packet);
}
/* Broadcast a packet using the minimum spanning tree */
void broadcast_packet(node_t *from, vpn_packet_t *packet)
{
avl_node_t *node;
connection_t *c;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
packet->len, from->name, from->hostname);
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c->status.active && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
}
cp
}
void flush_queue(node_t *n)
{
list_node_t *node, *next;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Flushing queue for %s (%s)"), n->name, n->hostname);
for(node = n->queue->head; node; node = next)
{
next = node->next;
send_udppacket(n, (vpn_packet_t *)node->data);
list_delete_node(n->queue, node);
}
cp
}
void handle_incoming_vpn_data(int sock)
{
vpn_packet_t pkt;
int x, l = sizeof(x);
char *hostname;
sockaddr_t from;
socklen_t fromlen = sizeof(from);
node_t *n;
cp
if(getsockopt(sock, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
{
syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s"),
__FILE__, __LINE__, sock, strerror(errno));
cp_trace();
exit(1);
}
if(x)
{
syslog(LOG_ERR, _("Incoming data socket error: %s"), strerror(x));
return;
}
if((pkt.len = recvfrom(sock, (char *)&pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen)) <= 0)
{
syslog(LOG_ERR, _("Receiving packet failed: %s"), strerror(errno));
return;
}
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
n = lookup_node_udp(&from);
if(!n)
{
hostname = sockaddr2hostname(&from);
syslog(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
free(hostname);
return;
}
if(n->connection)
n->connection->last_ping_time = now;
receive_udppacket(n, &pkt);
cp
}

View file

@ -1,546 +0,0 @@
/*
net_setup.c -- Setup.
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net_setup.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "interface.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "logging.h"
#include "system.h"
char *myport;
int read_rsa_public_key(connection_t *c)
{
FILE *fp;
char *fname;
char *key;
cp
if(!c->rsa_key)
c->rsa_key = RSA_new();
/* First, check for simple PublicKey statement */
if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key))
{
BN_hex2bn(&c->rsa_key->n, key);
BN_hex2bn(&c->rsa_key->e, "FFFF");
free(key);
return 0;
}
/* Else, check for PublicKeyFile statement and read it */
if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
{
if(is_safe_path(fname))
{
if((fp = fopen(fname, "r")) == NULL)
{
log(0, TLOG_ERROR,
_("Error reading RSA public key file `%s': %s"),
fname, strerror(errno));
free(fname);
return -1;
}
free(fname);
c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
fclose(fp);
if(!c->rsa_key)
{
syslog(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"),
fname, strerror(errno));
return -1;
}
return 0;
}
else
{
free(fname);
return -1;
}
}
/* Else, check if a harnessed public key is in the config file */
asprintf(&fname, "%s/hosts/%s", confbase, c->name);
if((fp = fopen(fname, "r")))
{
c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
fclose(fp);
}
free(fname);
if(c->rsa_key)
return 0;
else
{
syslog(LOG_ERR, _("No public key for %s specified!"), c->name);
return -1;
}
}
int read_rsa_private_key(void)
{
FILE *fp;
char *fname, *key;
cp
if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key))
{
myself->connection->rsa_key = RSA_new();
BN_hex2bn(&myself->connection->rsa_key->d, key);
BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
free(key);
return 0;
}
if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
asprintf(&fname, "%s/rsa_key.priv", confbase);
if(is_safe_path(fname))
{
if((fp = fopen(fname, "r")) == NULL)
{
syslog(LOG_ERR, _("Error reading RSA private key file `%s': %s"),
fname, strerror(errno));
free(fname);
return -1;
}
free(fname);
myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
fclose(fp);
if(!myself->connection->rsa_key)
{
syslog(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"),
fname, strerror(errno));
return -1;
}
return 0;
}
free(fname);
return -1;
}
int check_rsa_key(RSA *rsa_key)
{
char *test1, *test2, *test3;
cp
if(rsa_key->p && rsa_key->q)
{
if(RSA_check_key(rsa_key) != 1)
return -1;
}
else
{
test1 = xmalloc(RSA_size(rsa_key));
test2 = xmalloc(RSA_size(rsa_key));
test3 = xmalloc(RSA_size(rsa_key));
if(RSA_public_encrypt(RSA_size(rsa_key), test1, test2, rsa_key, RSA_NO_PADDING) != RSA_size(rsa_key))
return -1;
if(RSA_private_decrypt(RSA_size(rsa_key), test2, test3, rsa_key, RSA_NO_PADDING) != RSA_size(rsa_key))
return -1;
if(memcmp(test1, test3, RSA_size(rsa_key)))
return -1;
}
cp
return 0;
}
/*
Configure node_t myself and set up the local sockets (listen only)
*/
int setup_myself(void)
{
config_t *cfg;
subnet_t *subnet;
char *name, *mode, *afname, *cipher, *digest;
int choice;
cp
myself = new_node();
myself->connection = new_connection();
init_configuration(&myself->connection->config_tree);
asprintf(&myself->hostname, _("MYSELF"));
asprintf(&myself->connection->hostname, _("MYSELF"));
myself->connection->options = 0;
myself->connection->protocol_version = PROT_CURRENT;
if(!get_config_string(lookup_config(config_tree, "Name"), &name)) /* Not acceptable */
{
syslog(LOG_ERR, _("Name for tinc daemon required!"));
return -1;
}
if(check_id(name))
{
syslog(LOG_ERR, _("Invalid name for myself!"));
free(name);
return -1;
}
myself->name = name;
myself->connection->name = xstrdup(name);
cp
if(read_rsa_private_key())
return -1;
if(read_connection_config(myself->connection))
{
syslog(LOG_ERR, _("Cannot open host configuration file for myself!"));
return -1;
}
if(read_rsa_public_key(myself->connection))
return -1;
cp
if(check_rsa_key(myself->connection->rsa_key))
{
syslog(LOG_ERR, _("Invalid public/private keypair!"));
return -1;
}
if(!get_config_string(lookup_config(myself->connection->config_tree, "Port"), &myport))
asprintf(&myport, "655");
/* Read in all the subnets specified in the host configuration file */
cfg = lookup_config(myself->connection->config_tree, "Subnet");
while(cfg)
{
if(!get_config_subnet(cfg, &subnet))
return -1;
subnet_add(myself, subnet);
cfg = lookup_config_next(myself->connection->config_tree, cfg);
}
cp
/* Check some options */
if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice))
if(choice)
myself->options |= OPTION_INDIRECT;
if(get_config_bool(lookup_config(config_tree, "TCPOnly"), &choice))
if(choice)
myself->options |= OPTION_TCPONLY;
if(get_config_bool(lookup_config(myself->connection->config_tree, "IndirectData"), &choice))
if(choice)
myself->options |= OPTION_INDIRECT;
if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice))
if(choice)
myself->options |= OPTION_TCPONLY;
if(myself->options & OPTION_TCPONLY)
myself->options |= OPTION_INDIRECT;
if(get_config_string(lookup_config(config_tree, "Mode"), &mode))
{
if(!strcasecmp(mode, "router"))
routing_mode = RMODE_ROUTER;
else if (!strcasecmp(mode, "switch"))
routing_mode = RMODE_SWITCH;
else if (!strcasecmp(mode, "hub"))
routing_mode = RMODE_HUB;
else
{
syslog(LOG_ERR, _("Invalid routing mode!"));
return -1;
}
free(mode);
}
else
routing_mode = RMODE_ROUTER;
get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
syslog(LOG_WARNING, _("PriorityInheritance not supported on this platform"));
#endif
if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
macexpire= 600;
if(get_config_int(lookup_config(myself->connection->config_tree, "MaxTimeout"), &maxtimeout))
{
if(maxtimeout <= 0)
{
syslog(LOG_ERR, _("Bogus maximum timeout!"));
return -1;
}
}
else
maxtimeout = 900;
if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname))
{
if(!strcasecmp(afname, "IPv4"))
addressfamily = AF_INET;
else if (!strcasecmp(afname, "IPv6"))
addressfamily = AF_INET6;
else if (!strcasecmp(afname, "any"))
addressfamily = AF_UNSPEC;
else
{
syslog(LOG_ERR, _("Invalid address family!"));
return -1;
}
free(afname);
}
else
addressfamily = AF_INET;
get_config_bool(lookup_config(config_tree, "Hostnames"), &hostnames);
cp
/* Generate packet encryption key */
if(get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
{
if(!strcasecmp(cipher, "none"))
{
myself->cipher = NULL;
}
else
{
if(!(myself->cipher = EVP_get_cipherbyname(cipher)))
{
syslog(LOG_ERR, _("Unrecognized cipher type!"));
return -1;
}
}
}
else
myself->cipher = EVP_bf_cbc();
if(myself->cipher)
myself->keylength = myself->cipher->key_len + myself->cipher->iv_len;
else
myself->keylength = 1;
myself->connection->outcipher = EVP_bf_ofb();
myself->key = (char *)xmalloc(myself->keylength);
RAND_pseudo_bytes(myself->key, myself->keylength);
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
keyexpires = now + keylifetime;
/* Check if we want to use message authentication codes... */
if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
{
if(!strcasecmp(digest, "none"))
{
myself->digest = NULL;
}
else
{
if(!(myself->digest = EVP_get_digestbyname(digest)))
{
syslog(LOG_ERR, _("Unrecognized digest type!"));
return -1;
}
}
}
else
myself->digest = EVP_sha1();
myself->connection->outdigest = EVP_sha1();
if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength))
{
if(myself->digest)
{
if(myself->maclength > myself->digest->md_size)
{
syslog(LOG_ERR, _("MAC length exceeds size of digest!"));
return -1;
}
else if (myself->maclength < 0)
{
syslog(LOG_ERR, _("Bogus MAC length!"));
return -1;
}
}
}
else
myself->maclength = 4;
myself->connection->outmaclength = 0;
/* Compression */
if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression))
{
if(myself->compression < 0 || myself->compression > 9)
{
syslog(LOG_ERR, _("Bogus compression level!"));
return -1;
}
}
else
myself->compression = 0;
myself->connection->outcompression = 0;
cp
/* Done */
myself->nexthop = myself;
myself->via = myself;
myself->status.active = 1;
node_add(myself);
graph();
syslog(LOG_NOTICE, _("Ready"));
cp
return 0;
}
/*
setup all initial network connections
*/
int setup_network_connections(void)
{
cp
now = time(NULL);
init_connections();
init_subnets();
init_nodes();
init_edges();
init_events();
init_requests();
if(get_config_int(lookup_config(config_tree, "PingTimeout"), &pingtimeout))
{
if(pingtimeout < 1)
{
pingtimeout = 86400;
}
}
else
pingtimeout = 60;
if(setup_myself() < 0)
return -1;
try_outgoing_connections();
cp
return 0;
}
/*
close all open network connections
*/
void close_network_connections(void)
{
avl_node_t *node, *next;
connection_t *c;
int i;
cp
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
if(c->outgoing)
free(c->outgoing->name), free(c->outgoing), c->outgoing = NULL;
terminate_connection(c, 0);
}
if(myself && myself->connection)
terminate_connection(myself->connection, 0);
for(i = 0; i < listen_sockets; i++)
{
close(listen_socket[i].tcp);
close(listen_socket[i].udp);
}
exit_requests();
exit_events();
exit_edges();
exit_subnets();
exit_nodes();
exit_connections();
cp
return;
}

View file

@ -1,491 +0,0 @@
/*
net_socket.c -- Handle various kinds of sockets.
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net_socket.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "interface.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "logging.h"
#include "system.h"
int addressfamily = AF_INET;
int maxtimeout = 900;
int seconds_till_retry = 5;
listen_socket_t listen_socket[MAXSOCKETS];
int listen_sockets = 0;
/* Setup sockets */
int setup_listen_socket(sockaddr_t *sa)
{
int nfd, flags;
char *addrstr;
int option;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
char *interface;
struct ifreq ifr;
#endif
cp
if((nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
log(0, TLOG_ERROR,
_("Creating metasocket failed: %s"),
strerror(errno));
return -1;
}
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
return -1;
}
/* Optimize TCP settings */
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
#if defined(SOL_TCP) && defined(TCP_NODELAY)
setsockopt(nfd, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
#endif
#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
setsockopt(nfd, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
{
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
{
close(nfd);
syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
return -1;
}
#else
syslog(LOG_WARNING, _("BindToDevice not supported on this platform"));
#endif
}
if(bind(nfd, &sa->sa, SALEN(sa->sa)))
{
close(nfd);
addrstr = sockaddr2hostname(sa);
syslog(LOG_ERR, _("Can't bind to %s/tcp: %s"), addrstr, strerror(errno));
free(addrstr);
return -1;
}
if(listen(nfd, 3))
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "listen", strerror(errno));
return -1;
}
cp
return nfd;
}
int setup_vpn_in_socket(sockaddr_t *sa)
{
int nfd, flags;
char *addrstr;
int option;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
char *interface;
struct ifreq ifr;
#endif
cp
if((nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
syslog(LOG_ERR, _("Creating UDP socket failed: %s"), strerror(errno));
return -1;
}
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
return -1;
}
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
{
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
{
close(nfd);
syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
return -1;
}
}
#endif
if(bind(nfd, &sa->sa, SALEN(sa->sa)))
{
close(nfd);
addrstr = sockaddr2hostname(sa);
syslog(LOG_ERR, _("Can't bind to %s/udp: %s"), addrstr, strerror(errno));
free(addrstr);
return -1;
}
cp
return nfd;
}
void retry_outgoing(outgoing_t *outgoing)
{
event_t *event;
cp
outgoing->timeout += 5;
if(outgoing->timeout > maxtimeout)
outgoing->timeout = maxtimeout;
event = new_event();
event->handler = (event_handler_t)setup_outgoing_connection;
event->time = now + outgoing->timeout;
event->data = outgoing;
event_add(event);
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Trying to re-establish outgoing connection in %d seconds"), outgoing->timeout);
cp
}
int setup_outgoing_socket(connection_t *c)
{
int option;
cp
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1)
{
syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
return -1;
}
/* Optimize TCP settings */
#ifdef HAVE_LINUX
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
option = IPTOS_LOWDELAY;
setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
/* Connect */
if(connect(c->socket, &c->address.sa, SALEN(c->address.sa)) == -1)
{
close(c->socket);
syslog(LOG_ERR, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(errno));
return -1;
}
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
cp
return 0;
}
void finish_connecting(connection_t *c)
{
cp
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
c->last_ping_time = now;
send_id(c);
cp
}
void do_outgoing_connection(connection_t *c)
{
char *address, *port;
int option, result, flags;
cp
begin:
if(!c->outgoing->ai)
{
if(!c->outgoing->cfg)
{
log(DEBUG_CONNECTIONS, TLOG_ERROR,
_("Could not set up a meta connection to %s"),
c->name);
c->status.remove = 1;
do_prune = 1;
retry_outgoing(c->outgoing);
return;
}
get_config_string(c->outgoing->cfg, &address);
if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
asprintf(&port, "655");
c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
free(address);
free(port);
c->outgoing->aip = c->outgoing->ai;
c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
}
if(!c->outgoing->aip)
{
freeaddrinfo(c->outgoing->ai);
c->outgoing->ai = NULL;
goto begin;
}
memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
c->outgoing->aip = c->outgoing->aip->ai_next;
if(c->hostname)
free(c->hostname);
c->hostname = sockaddr2hostname(&c->address);
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
goto begin;
}
/* Optimize TCP settings */
#ifdef HAVE_LINUX
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
option = IPTOS_LOWDELAY;
setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
/* Non-blocking */
flags = fcntl(c->socket, F_GETFL);
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0)
{
syslog(LOG_ERR, _("fcntl for %s: %s"), c->hostname, strerror(errno));
}
/* Connect */
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
if(result == -1)
{
if(errno == EINPROGRESS)
{
c->status.connecting = 1;
return;
}
close(c->socket);
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("%s: %s"), c->hostname, strerror(errno));
goto begin;
}
finish_connecting(c);
return;
cp
}
void setup_outgoing_connection(outgoing_t *outgoing)
{
connection_t *c;
node_t *n;
cp
n = lookup_node(outgoing->name);
if(n)
if(n->connection)
{
log(DEBUG_CONNECTIONS, TLOG_INFO,
_("Already connected to %s"),
outgoing->name);
n->connection->outgoing = outgoing;
return;
}
c = new_connection();
c->name = xstrdup(outgoing->name);
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
init_configuration(&c->config_tree);
read_connection_config(c);
outgoing->cfg = lookup_config(c->config_tree, "Address");
if(!outgoing->cfg)
{
syslog(LOG_ERR, _("No address specified for %s"), c->name);
free_connection(c);
free(outgoing->name);
free(outgoing);
return;
}
c->outgoing = outgoing;
c->last_ping_time = now;
connection_add(c);
do_outgoing_connection(c);
}
/*
accept a new tcp connect and create a
new connection
*/
int handle_new_meta_connection(int sock)
{
connection_t *c;
sockaddr_t sa;
int fd, len = sizeof(sa);
cp
if((fd = accept(sock, &sa.sa, &len)) < 0)
{
syslog(LOG_ERR, _("Accepting a new connection failed: %s"), strerror(errno));
return -1;
}
sockaddrunmap(&sa);
c = new_connection();
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
c->address = sa;
c->hostname = sockaddr2hostname(&sa);
c->socket = fd;
c->last_ping_time = now;
log(DEBUG_CONNECTIONS, TLOG_NOTICE,
_("Connection from %s"),
c->hostname);
connection_add(c);
c->allow_request = ID;
send_id(c);
cp
return 0;
}
void try_outgoing_connections(void)
{
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
cp
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg))
{
get_config_string(cfg, &name);
if(check_id(name))
{
syslog(LOG_ERR, _("Invalid name for outgoing connection in %s line %d"), cfg->file, cfg->line);
free(name);
continue;
}
outgoing = xmalloc_and_zero(sizeof(*outgoing));
outgoing->name = name;
setup_outgoing_connection(outgoing);
}
}

View file

@ -1,250 +0,0 @@
/*
netutl.c -- some supporting network utility code
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: netutl.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <utils.h>
#include <xalloc.h>
#include "errno.h"
#include "interface.h"
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "logging.h"
#include "system.h"
int hostnames = 0;
/*
Turn a string into a struct addrinfo.
Return NULL on failure.
*/
struct addrinfo *str2addrinfo(char *address, char *service, int socktype)
{
struct addrinfo hint, *ai;
int err;
cp
memset(&hint, 0, sizeof(hint));
hint.ai_family = addressfamily;
hint.ai_socktype = socktype;
if((err = getaddrinfo(address, service, &hint, &ai)))
{
log(DEBUG_ERROR, LOG_WARNING,
_("Error looking up %s port %s: %s\n"),
address, service, gai_strerror(err));
cp_trace();
return NULL;
}
cp
return ai;
}
sockaddr_t str2sockaddr(char *address, char *port)
{
struct addrinfo hint, *ai;
sockaddr_t result;
int err;
cp
memset(&hint, 0, sizeof(hint));
hint.ai_family = AF_UNSPEC;
hint.ai_flags = AI_NUMERICHOST;
hint.ai_socktype = SOCK_STREAM;
if((err = getaddrinfo(address, port, &hint, &ai) || !ai))
{
log(0, TLOG_ERROR,
_("Error looking up %s port %s: %s\n"),
address, port, gai_strerror(err));
cp_trace();
raise(SIGFPE);
exit(0);
}
result = *(sockaddr_t *)ai->ai_addr;
freeaddrinfo(ai);
cp
return result;
}
void sockaddr2str(sockaddr_t *sa, char **addrstr, char **portstr)
{
char address[NI_MAXHOST];
char port[NI_MAXSERV];
char *scopeid;
int err;
cp
if((err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)))
{
syslog(LOG_ERR, _("Error while translating addresses: %s"), gai_strerror(err));
cp_trace();
raise(SIGFPE);
exit(0);
}
#ifdef HAVE_LINUX
if((scopeid = strchr(address, '%')))
*scopeid = '\0'; /* Descope. */
#endif
*addrstr = xstrdup(address);
*portstr = xstrdup(port);
cp
}
char *sockaddr2hostname(sockaddr_t *sa)
{
char *str;
char address[NI_MAXHOST] = "unknown";
char port[NI_MAXSERV] = "unknown";
int err;
cp
if((err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), hostnames?0:(NI_NUMERICHOST|NI_NUMERICSERV))))
{
syslog(LOG_ERR, _("Error while looking up hostname: %s"), gai_strerror(err));
}
asprintf(&str, _("%s port %s"), address, port);
cp
return str;
}
int sockaddrcmp(sockaddr_t *a, sockaddr_t *b)
{
int result;
cp
result = a->sa.sa_family - b->sa.sa_family;
if(result)
return result;
switch(a->sa.sa_family)
{
case AF_UNSPEC:
return 0;
case AF_INET:
result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
if(result)
return result;
return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof(a->in.sin_port));
case AF_INET6:
result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
if(result)
return result;
return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof(a->in6.sin6_port));
default:
syslog(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"), a->sa.sa_family);
cp_trace();
raise(SIGFPE);
exit(0);
}
cp
}
void sockaddrunmap(sockaddr_t *sa)
{
if(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->in6.sin6_addr))
{
sa->in.sin_addr.s_addr = ((uint32_t *)&sa->in6.sin6_addr)[3];
sa->in.sin_family = AF_INET;
}
}
/* Subnet mask handling */
int maskcmp(char *a, char *b, int masklen, int len)
{
int i, m, result;
cp
for(m = masklen, i = 0; m >= 8; m -= 8, i++)
if((result = a[i] - b[i]))
return result;
if(m)
return (a[i] & (0x100 - (m << 1))) - (b[i] & (0x100 - (m << 1)));
return 0;
}
void mask(char *a, int masklen, int len)
{
int i;
cp
i = masklen / 8;
masklen %= 8;
if(masklen)
a[i++] &= (0x100 - (masklen << 1));
for(; i < len; i++)
a[i] = 0;
}
void maskcpy(char *a, char *b, int masklen, int len)
{
int i, m;
cp
for(m = masklen, i = 0; m >= 8; m -= 8, i++)
a[i] = b[i];
if(m)
{
a[i] = b[i] & (0x100 - (m << 1));
i++;
}
for(; i < len; i++)
a[i] = 0;
}
int maskcheck(char *a, int masklen, int len)
{
int i;
cp
i = masklen / 8;
masklen %= 8;
if(masklen)
if(a[i++] & ~(0x100 - (masklen << 1)))
return -1;
for(; i < len; i++)
if(a[i] != 0)
return -1;
return 0;
}

View file

@ -1,367 +0,0 @@
/*
tincd.c -- the main file for tincd
Copyright (C) 1998-2002 Ivo Timmermans <ivo@o2w.nl>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: pokey.c,v 1.3 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "interface.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "logging.h"
#include "system.h"
/* The name this program was run with. */
char *program_name;
/* If nonzero, display usage information and exit. */
int show_help;
/* If nonzero, print the version on standard output and exit. */
int show_version;
/* If nonzero, it will attempt to kill a running tincd and exit. */
int kill_tincd = 0;
/* If nonzero, generate public/private keypair for this host/net. */
int generate_keys = 0;
/* If nonzero, use null ciphers and skip all key exchanges. */
int bypass_security = 0;
char *identname; /* program name for syslog */
char *pidfilename; /* pid file location */
char **g_argv; /* a copy of the cmdline arguments */
char **environment; /* A pointer to the environment on startup */
/* GTK Interface */
GladeXML *xml = NULL;
static struct option const long_options[] =
{
{ "config", required_argument, NULL, 'c' },
{ "kill", optional_argument, NULL, 'k' },
{ "net", required_argument, NULL, 'n' },
{ "help", no_argument, &show_help, 1 },
{ "version", no_argument, &show_version, 1 },
{ "no-detach", no_argument, &do_detach, 0 },
{ "generate-keys", optional_argument, NULL, 'K'},
{ "debug", optional_argument, NULL, 'd'},
{ "bypass-security", no_argument, &bypass_security, 1 },
{ NULL, 0, NULL, 0 }
};
static void
usage(int status)
{
if(status != 0)
fprintf(stderr, _("Try `%s --help\' for more information.\n"), program_name);
else
{
printf(_("Usage: %s [option]...\n\n"), program_name);
printf(_(" -c, --config=DIR Read configuration options from DIR.\n"
" -D, --no-detach Don't fork and detach.\n"
" -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
" -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n"
" -n, --net=NETNAME Connect to net NETNAME.\n"));
printf(_(" -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
" --help Display this help and exit.\n"
" --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@nl.linux.org.\n"));
}
exit(status);
}
void
parse_options(int argc, char **argv, char **envp)
{
int r;
int option_index = 0;
while((r = getopt_long(argc, argv, "c:Dd::k::n:K::", long_options, &option_index)) != EOF)
{
switch(r)
{
case 0: /* long option */
break;
case 'c': /* config file */
confbase = xmalloc(strlen(optarg)+1);
strcpy(confbase, optarg);
break;
case 'D': /* no detach */
do_detach = 0;
break;
case 'd': /* inc debug level */
if(optarg)
debug_lvl = atoi(optarg);
else
debug_lvl++;
break;
case 'k': /* kill old tincds */
kill_tincd = optarg?atoi(optarg):SIGTERM;
break;
case 'n': /* net name given */
netname = xmalloc(strlen(optarg)+1);
strcpy(netname, optarg);
break;
case 'K': /* generate public/private keypair */
if(optarg)
{
generate_keys = atoi(optarg);
if(generate_keys < 512)
{
fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"),
optarg);
usage(1);
}
generate_keys &= ~7; /* Round it to bytes */
}
else
generate_keys = 1024;
break;
case '?':
usage(1);
default:
break;
}
}
}
/* This function prettyprints the key generation process */
void indicator(int a, int b, void *p)
{
switch(a)
{
case 0:
fprintf(stderr, ".");
break;
case 1:
fprintf(stderr, "+");
break;
case 2:
fprintf(stderr, "-");
break;
case 3:
switch(b)
{
case 0:
fprintf(stderr, " p\n");
break;
case 1:
fprintf(stderr, " q\n");
break;
default:
fprintf(stderr, "?");
}
break;
default:
fprintf(stderr, "?");
}
}
/*
Generate a public/private RSA keypair, and ask for a file to store
them in.
*/
int keygen(int bits)
{
RSA *rsa_key;
FILE *f;
char *name = NULL;
char *filename;
fprintf(stderr, _("Generating %d bits keys:\n"), bits);
rsa_key = RSA_generate_key(bits, 0xFFFF, indicator, NULL);
if(!rsa_key)
{
fprintf(stderr, _("Error during key generation!\n"));
return -1;
}
else
fprintf(stderr, _("Done.\n"));
get_config_string(lookup_config(config_tree, "Name"), &name);
if(name)
asprintf(&filename, "%s/hosts/%s", confbase, name);
else
asprintf(&filename, "%s/rsa_key.pub", confbase);
if((f = ask_and_safe_open(filename, _("public RSA key"), "a")) == NULL)
return -1;
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPublicKey(f, rsa_key);
fclose(f);
free(filename);
asprintf(&filename, "%s/rsa_key.priv", confbase);
if((f = ask_and_safe_open(filename, _("private RSA key"), "a")) == NULL)
return -1;
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
fclose(f);
free(filename);
return 0;
}
/*
Set all files and paths according to netname
*/
void make_names(void)
{
if(netname)
{
if(!pidfilename)
asprintf(&pidfilename, LOCALSTATEDIR "/run/tinc.%s.pid", netname);
if(!confbase)
asprintf(&confbase, "%s/tinc/%s", CONFDIR, netname);
else
log(0, LOG_INFO,
_("Both netname and configuration directory given, using the latter..."));
if(!identname)
asprintf(&identname, "tinc.%s", netname);
}
else
{
if(!pidfilename)
pidfilename = LOCALSTATEDIR "/run/tinc.pid";
if(!confbase)
asprintf(&confbase, "%s/tinc", CONFDIR);
if(!identname)
identname = "tinc";
}
}
int
main(int argc, char **argv, char **envp)
{
char *fake_argv[] = { argv[0], NULL };
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
environment = envp;
parse_options(argc, argv, envp);
if(show_version)
{
printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE, VERSION, __DATE__, __TIME__, PROT_CURRENT);
printf(_("Copyright (C) 1998-2002 Ivo Timmermans, Guus Sliepen and others.\n"
"See the AUTHORS file for a complete list.\n\n"
"tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
"and you are welcome to redistribute it under certain conditions;\n"
"see the file COPYING for details.\n"));
return 0;
}
if(show_help)
usage(0);
log_add_hook(log_default);
gnome_init("Pokey", "0.0", 1, fake_argv);
g_argv = argv;
make_names();
init_configuration(&config_tree);
/* Slllluuuuuuurrrrp! */
cp
RAND_load_file("/dev/urandom", 1024);
#ifdef HAVE_SSLEAY_ADD_ALL_ALGORITHMS
SSLeay_add_all_algorithms();
#else
OpenSSL_add_all_algorithms();
#endif
cp
if(read_server_config())
exit(1);
cp
setup_signals();
if(init_interface())
{
log(0, TLOG_ERROR,
_("Could not setup all necessary interface elements.\n"));
exit(1);
}
if(!setup_network_connections())
{
main_loop();
cleanup_and_exit(1);
}
log_add_hook(log_default);
log(0, TLOG_ERROR,
_("Could not set up network connections"));
cp_trace();
return 1;
}

File diff suppressed because it is too large Load diff

View file

@ -1,56 +0,0 @@
/* XPM */
static char * pokey2_xpm[] = {
"36 49 4 1",
" c None",
". c #000000",
"+ c #FFFFFF",
"@ c #E9A500",
" .......... ",
" ............. ",
" ................ ",
" ................. ",
" .................. ",
" ................... ",
" ..................... ",
" ........++............. ",
" .........++.............. ",
" ..+....+..++................ ",
" .++++++...+................. ",
" ....+.+...................... ",
" ..@@@@@@.++..................... ",
" .@@@@@@@@.+++..................... ",
"...........++++.................... ",
" .@@@@@@@@.++++.....................",
" .....@@@.++++.....................",
" .....+++++....................",
" .++++++++++...................",
" .++++++++++++..................",
" .++++++++++++..................",
" .+++++++++++++..................",
" .+++++++++++++..................",
" .+++++++++++++................. ",
" .+++++++++++++................. ",
" .+++++++++++++.....+........... ",
" .+++++++++++++.....+........... ",
" .++++++++++++++....+........... ",
" .++++++++++++++................ ",
" .++++++++++++++...+............ ",
" .++++++++++++++...+........... ",
" .++++++++++++++...+........... ",
" .++++++++++++++..++........... ",
" .++++++++++++++++++........... ",
" .++++++++++++++++++........... ",
" .++++++++++++++++++.......... ",
" .+++++++++++++++++.......... ",
" .++++++++++++++++........... ",
" .++++++++++++++++........... ",
" .++++++++++++++++........... ",
" .+++++++++++++++........... ",
" ..+++++++++++............. ",
" . .++++++++............... ",
" ..+++++.+............. ",
" ....+.++............ ",
" ................... ",
" .................... ",
" .............. .. ",
" . .. "};

View file

@ -1,469 +0,0 @@
/*
process.c -- process management functions
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: process.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <pidfile.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "interface.h"
#include "process.h"
#include "subnet.h"
#include "connection.h"
#include "logging.h"
#include "system.h"
/* If zero, don't detach from the terminal. */
int do_detach = 1;
extern char *identname;
extern char *pidfilename;
extern char **g_argv;
sigset_t emptysigset;
static int saved_debug_lvl = 0;
extern int sighup;
extern int sigalrm;
extern int do_purge;
void memory_full(int size)
{
log(0, TLOG_ERROR,
_("Memory exhausted (couldn't allocate %d bytes), exitting."),
size);
cp_trace();
exit(1);
}
/* Some functions the less gifted operating systems might lack... */
#ifndef HAVE_FCLOSEALL
int fcloseall(void)
{
fflush(stdin);
fflush(stdout);
fflush(stderr);
fclose(stdin);
fclose(stdout);
fclose(stderr);
return 0;
}
#endif
/*
Close network connections, and terminate neatly
*/
void cleanup_and_exit(int c)
{
cp
close_network_connections();
syslog(LOG_NOTICE, _("Terminating"));
closelog();
exit(c);
}
/*
check for an existing tinc for this net, and write pid to pidfile
*/
int write_pidfile(void)
{
int pid;
cp
if((pid = check_pid(pidfilename)))
{
if(netname)
fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
netname, pid);
else
fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
return 1;
}
/* if it's locked, write-protected, or whatever */
if(!write_pid(pidfilename))
return 1;
cp
return 0;
}
/*
kill older tincd for this net
*/
int kill_other(int signal)
{
int pid;
cp
if(!(pid = read_pid(pidfilename)))
{
if(netname)
fprintf(stderr, _("No other tincd is running for net `%s'.\n"), netname);
else
fprintf(stderr, _("No other tincd is running.\n"));
return 1;
}
errno = 0; /* No error, sometimes errno is only changed on error */
/* ESRCH is returned when no process with that pid is found */
if(kill(pid, signal) && errno == ESRCH)
{
if(netname)
fprintf(stderr, _("The tincd for net `%s' is no longer running. "), netname);
else
fprintf(stderr, _("The tincd is no longer running. "));
fprintf(stderr, _("Removing stale lock file.\n"));
remove_pid(pidfilename);
}
cp
return 0;
}
/*
Detach from current terminal, write pidfile, kill parent
*/
int detach(void)
{
cp
setup_signals();
/* First check if we can open a fresh new pidfile */
if(write_pidfile())
return -1;
/* If we succeeded in doing that, detach */
closelog();
if(do_detach)
{
if(daemon(0, 0) < 0)
{
fprintf(stderr, _("Couldn't detach from terminal: %s"), strerror(errno));
return -1;
}
/* Now UPDATE the pid in the pidfile, because we changed it... */
if(!write_pid(pidfilename))
return -1;
}
openlog(identname, LOG_CONS | LOG_PID, LOG_DAEMON);
if(debug_lvl > DEBUG_NOTHING)
log(0, TLOG_NOTICE,
_("tincd %s (%s %s) starting, debug level %d"),
VERSION, __DATE__, __TIME__, debug_lvl);
else
log(DEBUG_NOTHING, TLOG_NOTICE,
_("tincd %s starting"),
VERSION);
xalloc_fail_func = memory_full;
cp
return 0;
}
/*
Execute the program name, with sane environment. All output will be
redirected to syslog.
*/
void _execute_script(const char *name) __attribute__ ((noreturn));
void _execute_script(const char *name)
{
char *scriptname;
char *s;
cp
#ifdef HAVE_UNSETENV
unsetenv("NETNAME");
unsetenv("INTERFACE");
#endif
if(netname)
{
asprintf(&s, "NETNAME=%s", netname);
putenv(s); /* Don't free s! see man 3 putenv */
}
chdir("/");
asprintf(&scriptname, "%s/%s", confbase, name);
/* Close all file descriptors */
closelog(); /* <- this means we cannot use syslog() here anymore! */
fcloseall();
execl(scriptname, NULL);
/* No return on success */
if(errno != ENOENT) /* Ignore if the file does not exist */
exit(1); /* Some error while trying execl(). */
else
exit(0);
}
/*
Fork and execute the program pointed to by name.
*/
int execute_script(const char *name)
{
pid_t pid;
int status;
cp
if((pid = fork()) < 0)
{
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fork", strerror(errno));
return -1;
}
if(pid)
{
log(DEBUG_STATUS, TLOG_INFO,
_("Executing script %s"),
name);
if(waitpid(pid, &status, 0) == pid)
{
if(WIFEXITED(status)) /* Child exited by itself */
{
if(WEXITSTATUS(status))
{
syslog(LOG_ERR, _("Process %d (%s) exited with non-zero status %d"), pid, name, WEXITSTATUS(status));
return -1;
}
else
return 0;
}
else if(WIFSIGNALED(status)) /* Child was killed by a signal */
{
syslog(LOG_ERR, _("Process %d (%s) was killed by signal %d (%s)"),
pid, name, WTERMSIG(status), strsignal(WTERMSIG(status)));
return -1;
}
else /* Something strange happened */
{
syslog(LOG_ERR, _("Process %d (%s) terminated abnormally"), pid, name);
return -1;
}
}
else
{
syslog(LOG_ERR, _("System call `%s' failed: %s"), "waitpid", strerror(errno));
return -1;
}
}
cp
/* Child here */
_execute_script(name);
}
/*
Signal handlers.
*/
RETSIGTYPE
sigterm_handler(int a)
{
log(DEBUG_NOTHING, TLOG_NOTICE,
_("Got TERM signal"));
cleanup_and_exit(0);
}
RETSIGTYPE
sigquit_handler(int a)
{
log(DEBUG_NOTHING, TLOG_NOTICE,
_("Got QUIT signal"));
cleanup_and_exit(0);
}
RETSIGTYPE
fatal_signal_square(int a)
{
syslog(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a, strsignal(a));
cp_trace();
exit(1);
}
RETSIGTYPE
fatal_signal_handler(int a)
{
syslog(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
cp_trace();
syslog(LOG_NOTICE, _("Not restarting."));
exit(1);
}
RETSIGTYPE
sighup_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got HUP signal"));
sighup = 1;
}
RETSIGTYPE
sigint_handler(int a)
{
if(saved_debug_lvl)
{
syslog(LOG_NOTICE, _("Reverting to old debug level (%d)"),
saved_debug_lvl);
debug_lvl = saved_debug_lvl;
saved_debug_lvl = 0;
}
else
{
syslog(LOG_NOTICE, _("Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d."),
debug_lvl);
saved_debug_lvl = debug_lvl;
debug_lvl = 5;
}
}
RETSIGTYPE
sigalrm_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got ALRM signal"));
sigalrm = 1;
}
RETSIGTYPE
sigusr1_handler(int a)
{
dump_connections();
}
RETSIGTYPE
sigusr2_handler(int a)
{
dump_nodes();
dump_edges();
dump_subnets();
}
RETSIGTYPE
sigwinch_handler(int a)
{
extern int do_purge;
do_purge = 1;
}
RETSIGTYPE
unexpected_signal_handler(int a)
{
syslog(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
cp_trace();
}
RETSIGTYPE
ignore_signal_handler(int a)
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
syslog(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
cp_trace();
}
}
struct {
int signal;
void (*handler)(int);
} sighandlers[] = {
{ SIGHUP, sighup_handler },
{ SIGTERM, sigterm_handler },
{ SIGQUIT, sigquit_handler },
{ SIGSEGV, fatal_signal_handler },
{ SIGBUS, fatal_signal_handler },
{ SIGILL, fatal_signal_handler },
{ SIGPIPE, ignore_signal_handler },
{ SIGINT, sigint_handler },
{ SIGUSR1, sigusr1_handler },
{ SIGUSR2, sigusr2_handler },
{ SIGCHLD, ignore_signal_handler },
{ SIGALRM, sigalrm_handler },
{ SIGWINCH, sigwinch_handler },
{ 0, NULL }
};
void
setup_signals(void)
{
int i;
struct sigaction act;
sigemptyset(&emptysigset);
act.sa_handler = NULL;
act.sa_mask = emptysigset;
act.sa_flags = 0;
/* Set a default signal handler for every signal, errors will be
ignored. */
for(i = 0; i < NSIG; i++)
{
if(!do_detach)
act.sa_handler = SIG_DFL;
else
act.sa_handler = unexpected_signal_handler;
sigaction(i, &act, NULL);
}
/* If we didn't detach, allow coredumps */
if(!do_detach)
sighandlers[3].handler = SIG_DFL;
/* Then, for each known signal that we want to catch, assign a
handler to the signal, with error checking this time. */
for(i = 0; sighandlers[i].signal; i++)
{
act.sa_handler = sighandlers[i].handler;
if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
sighandlers[i].signal, strsignal(sighandlers[i].signal), strerror(errno));
}
}

View file

@ -1,36 +0,0 @@
/*
process.h -- header file for process.c
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: process.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#ifndef __TINC_PROCESS_H__
#define __TINC_PROCESS_H__
#include "config.h"
extern int do_detach;
extern void setup_signals(void);
extern int execute_script(const char *);
extern int detach(void);
extern int kill_other(int);
extern void cleanup_and_exit(int);
#endif /* __TINC_PROCESS_H__ */

View file

@ -1,245 +0,0 @@
/*
protocol.c -- handle the meta-protocol, basic functions
Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol.c,v 1.2 2002/05/02 11:50:07 zarq Exp $
*/
#include "config.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "interface.h"
#include "logging.h"
#include "system.h"
avl_tree_t *past_request_tree;
int check_id(char *id)
{
int i;
for (i = 0; i < strlen(id); i++)
if(!isalnum(id[i]) && id[i] != '_')
return -1;
return 0;
}
/* Generic request routines - takes care of logging and error
detection as well */
int send_request(connection_t *c, const char *format, ...)
{
va_list args;
char buffer[MAXBUFSIZE];
int len, request;
cp
/* Use vsnprintf instead of vasprintf: faster, no memory
fragmentation, cleanup is automatic, and there is a limit on the
input buffer anyway */
va_start(args, format);
len = vsnprintf(buffer, MAXBUFSIZE, format, args);
va_end(args);
if(len < 0 || len > MAXBUFSIZE-1)
{
syslog(LOG_ERR, _("Output buffer overflow while sending request to %s (%s)"), c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_PROTOCOL)
{
sscanf(buffer, "%d", &request);
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Sending %s to %s (%s): %s"), request_name[request], c->name, c->hostname, buffer);
else
syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], c->name, c->hostname);
}
buffer[len++] = '\n';
cp
return send_meta(c, buffer, len);
}
int receive_request(connection_t *c)
{
int request;
cp
if(sscanf(c->buffer, "%d", &request) == 1)
{
if((request < 0) || (request >= LAST) || (request_handlers[request] == NULL))
{
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Unknown request from %s (%s): %s"),
c->name, c->hostname, c->buffer);
else
syslog(LOG_ERR, _("Unknown request from %s (%s)"),
c->name, c->hostname);
return -1;
}
else
{
if(debug_lvl >= DEBUG_PROTOCOL)
{
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Got %s from %s (%s): %s"),
request_name[request], c->name, c->hostname, c->buffer);
else
syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
request_name[request], c->name, c->hostname);
}
}
if((c->allow_request != ALL) && (c->allow_request != request))
{
syslog(LOG_ERR, _("Unauthorized request from %s (%s)"), c->name, c->hostname);
return -1;
}
if(request_handlers[request](c))
/* Something went wrong. Probably scriptkiddies. Terminate. */
{
syslog(LOG_ERR, _("Error while processing %s from %s (%s)"),
request_name[request], c->name, c->hostname);
return -1;
}
}
else
{
syslog(LOG_ERR, _("Bogus data received from %s (%s)"),
c->name, c->hostname);
return -1;
}
cp
return 0;
}
int past_request_compare(past_request_t *a, past_request_t *b)
{
cp
return strcmp(a->request, b->request);
}
void free_past_request(past_request_t *r)
{
cp
if(r->request)
free(r->request);
free(r);
cp
}
void init_requests(void)
{
cp
past_request_tree = avl_alloc_tree((avl_compare_t)past_request_compare, (avl_action_t)free_past_request);
cp
}
void exit_requests(void)
{
cp
avl_delete_tree(past_request_tree);
cp
}
int seen_request(char *request)
{
past_request_t p, *new;
cp
p.request = request;
if(avl_search(past_request_tree, &p))
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, _("Already seen request"));
return 1;
}
else
{
new = (past_request_t *)xmalloc(sizeof(*new));
new->request = xstrdup(request);
new->firstseen = now;
avl_insert(past_request_tree, new);
return 0;
}
cp
}
void age_past_requests(void)
{
avl_node_t *node, *next;
past_request_t *p;
int left = 0, deleted = 0;
cp
for(node = past_request_tree->head; node; node = next)
{
next = node->next;
p = (past_request_t *)node->data;
if(p->firstseen + pingtimeout < now)
avl_delete_node(past_request_tree, node), deleted++;
else
left++;
}
if(debug_lvl >= DEBUG_SCARY_THINGS && left + deleted)
syslog(LOG_DEBUG, _("Aging past requests: deleted %d, left %d\n"), deleted, left);
cp
}
/* Jumptable for the request handlers */
int (*request_handlers[])(connection_t*) = {
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
/* add_node_h, del_node_h,*/
add_subnet_h, del_subnet_h,
add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h,
};
/* Request names */
char (*request_name[]) = {
"ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK",
"STATUS", "ERROR", "TERMREQ",
"PING", "PONG",
/* "ADD_NODE", "DEL_NODE",*/
"ADD_SUBNET", "DEL_SUBNET",
"ADD_EDGE", "DEL_EDGE",
"KEY_CHANGED", "REQ_KEY", "ANS_KEY",
};

View file

@ -1,120 +0,0 @@
/*
protocol.h -- header for protocol.c
Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol.h,v 1.2 2002/05/02 11:50:07 zarq Exp $
*/
#ifndef __TINC_PROTOCOL_H__
#define __TINC_PROTOCOL_H__
#include "net.h"
#include "node.h"
#include "subnet.h"
/* Protocol version. Different versions are incompatible,
incompatible version have different protocols.
*/
#define PROT_CURRENT 14
/* Request numbers */
enum {
ALL = -1, /* Guardian for allow_request */
ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
STATUS, ERROR, TERMREQ,
PING, PONG,
/* ADD_NODE, DEL_NODE,*/
ADD_SUBNET, DEL_SUBNET,
ADD_EDGE, DEL_EDGE,
KEY_CHANGED, REQ_KEY, ANS_KEY,
PACKET,
LAST /* Guardian for the highest request number */
};
typedef struct past_request_t {
char *request;
time_t firstseen;
} past_request_t;
/* Maximum size of strings in a request */
#define MAX_STRING_SIZE 2048
#define MAX_STRING "%2048s"
/* Basic functions */
extern int send_request(connection_t*, const char*, ...);
extern int receive_request(connection_t *);
extern int check_id(char *);
extern void init_requests(void);
extern void exit_requests(void);
extern int seen_request(char *);
extern void age_past_requests(void);
/* Requests */
extern int send_id(connection_t *);
extern int send_metakey(connection_t *);
extern int send_challenge(connection_t *);
extern int send_chal_reply(connection_t *);
extern int send_ack(connection_t *);
extern int send_status(connection_t *, int, char *);
extern int send_error(connection_t *, int, char *);
extern int send_termreq(connection_t *);
extern int send_ping(connection_t *);
extern int send_pong(connection_t *);
/* extern int send_add_node(connection_t *, node_t *); */
/* extern int send_del_node(connection_t *, node_t *); */
extern int send_add_subnet(connection_t *, subnet_t *);
extern int send_del_subnet(connection_t *, subnet_t *);
extern int send_add_edge(connection_t *, edge_t *);
extern int send_del_edge(connection_t *, edge_t *);
extern int send_key_changed(connection_t *, node_t *);
extern int send_req_key(connection_t *, node_t *, node_t *);
extern int send_ans_key(connection_t *, node_t *, node_t *);
extern int send_tcppacket(connection_t *, vpn_packet_t *);
/* Request handlers */
extern int (*request_handlers[])(connection_t *);
extern int id_h(connection_t *);
extern int metakey_h(connection_t *);
extern int challenge_h(connection_t *);
extern int chal_reply_h(connection_t *);
extern int ack_h(connection_t *);
extern int status_h(connection_t *);
extern int error_h(connection_t *);
extern int termreq_h(connection_t *);
extern int ping_h(connection_t *);
extern int pong_h(connection_t *);
/* extern int add_node_h(connection_t *); */
/* extern int del_node_h(connection_t *); */
extern int add_subnet_h(connection_t *);
extern int del_subnet_h(connection_t *);
extern int add_edge_h(connection_t *);
extern int del_edge_h(connection_t *);
extern int key_changed_h(connection_t *);
extern int req_key_h(connection_t *);
extern int ans_key_h(connection_t *);
extern int tcppacket_h(connection_t *);
#endif /* __TINC_PROTOCOL_H__ */

View file

@ -1,609 +0,0 @@
/*
protocol_auth.c -- handle the meta-protocol, authentication
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_auth.c,v 1.2 2002/05/02 11:50:07 zarq Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#ifndef HAVE_RAND_PSEUDO_BYTES
#define RAND_pseudo_bytes RAND_bytes
#endif
#include "conf.h"
#include "interface.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "logging.h"
#include "system.h"
int send_id(connection_t *c)
{
cp
return send_request(c, "%d %s %d", ID, myself->connection->name, myself->connection->protocol_version);
}
int id_h(connection_t *c)
{
char name[MAX_STRING_SIZE];
int bla;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" %d", name, &c->protocol_version) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ID", c->name, c->hostname);
return -1;
}
/* Check if identity is a valid name */
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ID", c->name, c->hostname, "invalid name");
return -1;
}
/* If we set c->name in advance, make sure we are connected to the right host */
if(c->name)
{
if(strcmp(c->name, name))
{
syslog(LOG_ERR, _("Peer %s is %s instead of %s"), c->hostname, name, c->name);
return -1;
}
}
else
c->name = xstrdup(name);
/* Check if version matches */
if(c->protocol_version != myself->connection->protocol_version)
{
syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
c->name, c->hostname, c->protocol_version);
return -1;
}
if(bypass_security)
{
if(!c->config_tree)
init_configuration(&c->config_tree);
c->allow_request = ACK;
return send_ack(c);
}
if(!c->config_tree)
{
init_configuration(&c->config_tree);
if((bla = read_connection_config(c)))
{
syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), c->hostname, c->name);
return -1;
}
}
if(read_rsa_public_key(c))
{
return -1;
}
/* Check some options */
if((get_config_bool(lookup_config(c->config_tree, "IndirectData"), &bla) && bla) || myself->options & OPTION_INDIRECT)
c->options |= OPTION_INDIRECT;
if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &bla) && bla) || myself->options & OPTION_TCPONLY)
c->options |= OPTION_TCPONLY | OPTION_INDIRECT;
c->allow_request = METAKEY;
cp
return send_metakey(c);
}
int send_metakey(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int len, x;
cp
len = RSA_size(c->rsa_key);
/* Allocate buffers for the meta key */
if(!c->outkey)
c->outkey = xmalloc(len);
if(!c->outctx)
c->outctx = xmalloc(sizeof(*c->outctx));
cp
/* Copy random data to the buffer */
RAND_bytes(c->outkey, len);
/* The message we send must be smaller than the modulus of the RSA key.
By definition, for a key of k bits, the following formula holds:
2^(k-1) <= modulus < 2^(k)
Where ^ means "to the power of", not "xor".
This means that to be sure, we must choose our message < 2^(k-1).
This can be done by setting the most significant bit to zero.
*/
c->outkey[0] &= 0x7F;
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
bin2hex(c->outkey, buffer, len);
buffer[len*2] = '\0';
syslog(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), buffer);
}
/* Encrypt the random data
We do not use one of the PKCS padding schemes here.
This is allowed, because we encrypt a totally random string
with a length equal to that of the modulus of the RSA key.
*/
if(RSA_public_encrypt(len, c->outkey, buffer, c->rsa_key, RSA_NO_PADDING) != len)
{
syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname);
return -1;
}
cp
/* Convert the encrypted random data to a hexadecimal formatted string */
bin2hex(buffer, buffer, len);
buffer[len*2] = '\0';
/* Send the meta key */
x = send_request(c, "%d %d %d %d %d %s", METAKEY,
c->outcipher?c->outcipher->nid:0, c->outdigest?c->outdigest->type:0,
c->outmaclength, c->outcompression, buffer);
/* Further outgoing requests are encrypted with the key we just generated */
if(c->outcipher)
{
EVP_EncryptInit(c->outctx, c->outcipher,
c->outkey + len - c->outcipher->key_len,
c->outkey + len - c->outcipher->key_len - c->outcipher->iv_len);
c->status.encryptout = 1;
}
cp
return x;
}
int metakey_h(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
int len;
cp
if(sscanf(c->buffer, "%*d %d %d %d %d "MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, c->hostname);
return -1;
}
cp
len = RSA_size(myself->connection->rsa_key);
/* Check if the length of the meta key is all right */
if(strlen(buffer) != len*2)
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong keylength");
return -1;
}
/* Allocate buffers for the meta key */
cp
if(!c->inkey)
c->inkey = xmalloc(len);
if(!c->inctx)
c->inctx = xmalloc(sizeof(*c->inctx));
/* Convert the challenge from hexadecimal back to binary */
cp
hex2bin(buffer,buffer,len);
/* Decrypt the meta key */
cp
if(RSA_private_decrypt(len, buffer, c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) /* See challenge() */
{
syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
bin2hex(c->inkey, buffer, len);
buffer[len*2] = '\0';
syslog(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer);
}
/* All incoming requests will now be encrypted. */
cp
/* Check and lookup cipher and digest algorithms */
if(cipher)
{
c->incipher = EVP_get_cipherbynid(cipher);
if(!c->incipher)
{
syslog(LOG_ERR, _("%s (%s) uses unknown cipher!"), c->name, c->hostname);
return -1;
}
EVP_DecryptInit(c->inctx, c->incipher,
c->inkey + len - c->incipher->key_len,
c->inkey + len - c->incipher->key_len - c->incipher->iv_len);
c->status.decryptin = 1;
}
else
{
c->incipher = NULL;
}
c->inmaclength = maclength;
if(digest)
{
c->indigest = EVP_get_digestbynid(digest);
if(!c->indigest)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), c->name, c->hostname);
return -1;
}
if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0)
{
syslog(LOG_ERR, _("%s (%s) uses bogus MAC length!"), c->name, c->hostname);
return -1;
}
}
else
{
c->indigest = NULL;
}
c->incompression = compression;
c->allow_request = CHALLENGE;
cp
return send_challenge(c);
}
int send_challenge(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int len, x;
cp
/* CHECKME: what is most reasonable value for len? */
len = RSA_size(c->rsa_key);
/* Allocate buffers for the challenge */
if(!c->hischallenge)
c->hischallenge = xmalloc(len);
cp
/* Copy random data to the buffer */
RAND_bytes(c->hischallenge, len);
cp
/* Convert to hex */
bin2hex(c->hischallenge, buffer, len);
buffer[len*2] = '\0';
cp
/* Send the challenge */
x = send_request(c, "%d %s", CHALLENGE, buffer);
cp
return x;
}
int challenge_h(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int len;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING, buffer) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, c->hostname);
return -1;
}
len = RSA_size(myself->connection->rsa_key);
/* Check if the length of the challenge is all right */
if(strlen(buffer) != len*2)
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong challenge length");
return -1;
}
/* Allocate buffers for the challenge */
if(!c->mychallenge)
c->mychallenge = xmalloc(len);
/* Convert the challenge from hexadecimal back to binary */
hex2bin(buffer,c->mychallenge,len);
c->allow_request = CHAL_REPLY;
/* Rest is done by send_chal_reply() */
cp
return send_chal_reply(c);
}
int send_chal_reply(connection_t *c)
{
char hash[EVP_MAX_MD_SIZE*2+1];
EVP_MD_CTX ctx;
cp
/* Calculate the hash from the challenge we received */
EVP_DigestInit(&ctx, c->indigest);
EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key));
EVP_DigestFinal(&ctx, hash, NULL);
/* Convert the hash to a hexadecimal formatted string */
bin2hex(hash,hash,c->indigest->md_size);
hash[c->indigest->md_size*2] = '\0';
/* Send the reply */
cp
return send_request(c, "%d %s", CHAL_REPLY, hash);
}
int chal_reply_h(connection_t *c)
{
char hishash[MAX_STRING_SIZE];
char myhash[EVP_MAX_MD_SIZE];
EVP_MD_CTX ctx;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING, hishash) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "CHAL_REPLY", c->name, c->hostname);
return -1;
}
/* Check if the length of the hash is all right */
if(strlen(hishash) != c->outdigest->md_size*2)
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply length"));
return -1;
}
/* Convert the hash to binary format */
hex2bin(hishash, hishash, c->outdigest->md_size);
/* Calculate the hash from the challenge we sent */
EVP_DigestInit(&ctx, c->outdigest);
EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key));
EVP_DigestFinal(&ctx, myhash, NULL);
/* Verify the incoming hash with the calculated hash */
if(memcmp(hishash, myhash, c->outdigest->md_size))
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply"));
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
hishash[SHA_DIGEST_LENGTH*2] = '\0';
syslog(LOG_DEBUG, _("Expected challenge reply: %s"), hishash);
}
return -1;
}
/* Identity has now been positively verified.
Send an acknowledgement with the rest of the information needed.
*/
c->allow_request = ACK;
cp
return send_ack(c);
}
int send_ack(connection_t *c)
{
/* ACK message contains rest of the information the other end needs
to create node_t and edge_t structures. */
int x;
char *address, *port;
struct timeval now;
cp
/* Estimate weight */
gettimeofday(&now, NULL);
c->estimated_weight = (now.tv_sec - c->start.tv_sec) * 1000 + (now.tv_usec - c->start.tv_usec) / 1000;
sockaddr2str(&c->address, &address, &port);
x = send_request(c, "%d %s %s %d %lx", ACK, myport, address, c->estimated_weight, c->options);
free(address);
free(port);
cp
return x;
}
void send_everything(connection_t *c)
{
avl_node_t *node, *node2;
node_t *n;
subnet_t *s;
edge_t *e;
/* Send all known subnets */
for(node = node_tree->head; node; node = node->next)
{
n = (node_t *)node->data;
for(node2 = n->subnet_tree->head; node2; node2 = node2->next)
{
s = (subnet_t *)node2->data;
send_add_subnet(c, s);
}
}
/* Send all known edges */
for(node = edge_tree->head; node; node = node->next)
{
e = (edge_t *)node->data;
if(e == c->edge)
continue;
send_add_edge(c, e);
}
}
int ack_h(connection_t *c)
{
char myaddress[MAX_STRING_SIZE];
char hisport[MAX_STRING_SIZE];
char *hisaddress, *dummy;
int weight;
long int options;
node_t *n;
connection_t *other;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" %d %lx", hisport, myaddress, &weight, &options) != 4)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ACK", c->name, c->hostname);
return -1;
}
/* Check if we already have a node_t for him */
n = lookup_node(c->name);
if(!n)
{
n = new_node();
n->name = xstrdup(c->name);
node_add(n);
}
else
{
if(n->connection)
{
/* Oh dear, we already have a connection to this node. */
log(DEBUG_CONNECTIONS, TLOG_DEBUG,
_("Established a second connection with %s (%s), closing old connection"),
n->name, n->hostname);
terminate_connection(n->connection, 0);
}
/* FIXME: check if information in existing node matches that of the other end of this connection */
}
n->connection = c;
c->node = n;
c->options |= options;
/* Create an edge_t for this connection */
c->edge = new_edge();
cp
c->edge->from.node = myself;
/* c->edge->from.tcpaddress = str2sockaddr(address, port);*/
c->edge->from.udpaddress = str2sockaddr(myaddress, myport);
c->edge->to.node = n;
/* c->edge->to.tcpaddress = c->address; */
sockaddr2str(&c->address, &hisaddress, &dummy);
c->edge->to.udpaddress = str2sockaddr(hisaddress, hisport);
free(hisaddress);
free(dummy);
c->edge->weight = (weight + c->estimated_weight) / 2;
c->edge->connection = c;
c->edge->options = c->options;
cp
edge_add(c->edge);
/* Activate this connection */
c->allow_request = ALL;
c->status.active = 1;
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), c->name, c->hostname);
cp
/* Send him everything we know */
send_everything(c);
/* Notify others of this connection */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_add_edge(other, c->edge);
}
/* Run MST and SSSP algorithms */
graph();
cp
return 0;
}

View file

@ -1,311 +0,0 @@
/*
protocol_edge.c -- handle the meta-protocol, edges
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_edge.c,v 1.2 2002/05/02 11:50:07 zarq Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "interface.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "logging.h"
#include "system.h"
int send_add_edge(connection_t *c, edge_t *e)
{
int x;
char *from_udpaddress, *from_udpport;
char *to_udpaddress, *to_udpport;
cp
/* sockaddr2str(&e->from.tcpaddress, &from_tcpaddress, &from_tcpport); */
sockaddr2str(&e->from.udpaddress, &from_udpaddress, &from_udpport);
/* sockaddr2str(&e->to.tcpaddress, &to_tcpaddress, &to_tcpport); */
sockaddr2str(&e->to.udpaddress, &to_udpaddress, &to_udpport);
x = send_request(c, "%d %lx %s %s %s %s %s %s %lx %d", ADD_EDGE, random(),
e->from.node->name, from_udpaddress, from_udpport,
e->to.node->name, to_udpaddress, to_udpport,
e->options, e->weight);
/* free(from_tcpaddress); */
/* free(from_tcpport); */
free(from_udpaddress);
free(from_udpport);
/* free(to_tcpaddress); */
/* free(to_tcpport); */
free(to_udpaddress);
free(to_udpport);
cp
return x;
}
int add_edge_h(connection_t *c)
{
connection_t *other;
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char from_address[MAX_STRING_SIZE];
/* char from_tcpport[MAX_STRING_SIZE]; */
char from_udpport[MAX_STRING_SIZE];
char to_address[MAX_STRING_SIZE];
/* char to_tcpport[MAX_STRING_SIZE]; */
char to_udpport[MAX_STRING_SIZE];
sockaddr_t from_udpaddress;
sockaddr_t to_udpaddress;
long int options;
int weight;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d",
from_name, from_address, from_udpport,
to_name, to_address, to_udpport,
&options, &weight) != 8)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_EDGE", c->name, c->hostname);
return -1;
}
/* Check if names are valid */
if(check_id(from_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(check_id(to_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* Lookup nodes */
from = lookup_node(from_name);
if(!from)
{
from = new_node();
from->name = xstrdup(from_name);
node_add(from);
}
to = lookup_node(to_name);
if(!to)
{
to = new_node();
to->name = xstrdup(to_name);
node_add(to);
}
/* Convert addresses */
/* from_tcpaddress = str2sockaddr(from_address, from_tcpport); */
from_udpaddress = str2sockaddr(from_address, from_udpport);
/* to_tcpaddress = str2sockaddr(to_address, to_tcpport); */
to_udpaddress = str2sockaddr(to_address, to_udpport);
/* Check if edge already exists */
e = lookup_edge(from, to);
if(e)
{
if(e->weight != weight || e->options != options
|| ((e->from.node == from) && (sockaddrcmp(&e->from.udpaddress, &from_udpaddress)|| sockaddrcmp(&e->to.udpaddress, &to_udpaddress)))
|| ((e->from.node == to) && (sockaddrcmp(&e->from.udpaddress, &to_udpaddress) || sockaddrcmp(&e->to.udpaddress, &from_udpaddress)))
)
{
if(from == myself || to == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
send_add_edge(c, e);
return 0;
}
else
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
edge_del(e);
}
}
else
return 0;
}
else if(from == myself || to == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not exist"), "ADD_EDGE", c->name, c->hostname);
e = new_edge();
e->from.node = from;
e->to.node = to;
send_del_edge(c, e);
free_edge(e);
return 0;
}
e = new_edge();
e->from.node = from;
/* e->from.tcpaddress = from_tcpaddress; */
e->from.udpaddress = from_udpaddress;
e->to.node = to;
/* e->to.tcpaddress = to_tcpaddress; */
e->to.udpaddress = to_udpaddress;
e->options = options;
e->weight = weight;
edge_add(e);
/* Tell the rest about the new edge */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* Run MST before or after we tell the rest? */
graph();
cp
return 0;
}
int send_del_edge(connection_t *c, edge_t *e)
{
cp
return send_request(c, "%d %lx %s %s", DEL_EDGE, random(),
e->from.node->name, e->to.node->name);
}
int del_edge_h(connection_t *c)
{
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
connection_t *other;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING"", from_name, to_name) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_EDGE",
c->name, c->hostname);
return -1;
}
/* Check if names are valid */
if(check_id(from_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(check_id(to_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* Lookup nodes */
from = lookup_node(from_name);
if(!from)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
to = lookup_node(to_name);
if(!to)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
/* Check if edge exists */
e = lookup_edge(from, to);
if(!e)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
if(e->from.node == myself || e->to.node == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_EDGE", c->name, c->hostname);
send_add_edge(c, e); /* Send back a correction */
return 0;
}
/* Tell the rest about the deleted edge */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* Delete the edge */
edge_del(e);
/* Run MST before or after we tell the rest? */
graph();
cp
return 0;
}

View file

@ -1,285 +0,0 @@
/*
protocol_key.c -- handle the meta-protocol, key exchange
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_key.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "interface.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "logging.h"
#include "system.h"
int mykeyused = 0;
int send_key_changed(connection_t *c, node_t *n)
{
connection_t *other;
avl_node_t *node;
cp
/* Only send this message if some other daemon requested our key previously.
This reduces unnecessary key_changed broadcasts.
*/
if(n == myself && !mykeyused)
return 0;
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%d %lx %s", KEY_CHANGED, random(), n->name);
}
cp
return 0;
}
int key_changed_h(connection_t *c)
{
char name[MAX_STRING_SIZE];
avl_node_t *node;
connection_t *other;
node_t *n;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING, name) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
c->name, c->hostname);
return -1;
}
if(seen_request(c->buffer))
return 0;
n = lookup_node(name);
if(!n)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"), "KEY_CHANGED",
c->name, c->hostname, name);
return -1;
}
n->status.validkey = 0;
n->status.waitingforkey = 0;
n->sent_seqno = 0;
/* Tell the others */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
cp
return 0;
}
int send_req_key(connection_t *c, node_t *from, node_t *to)
{
cp
return send_request(c, "%d %s %s", REQ_KEY,
from->name, to->name);
}
int req_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING, from_name, to_name) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY",
c->name, c->hostname);
return -1;
}
from = lookup_node(from_name);
if(!from)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "REQ_KEY",
c->name, c->hostname, from_name);
return -1;
}
to = lookup_node(to_name);
if(!to)
{
syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "REQ_KEY",
c->name, c->hostname, to_name);
return -1;
}
/* Check if this key request is for us */
if(to == myself) /* Yes, send our own key back */
{
mykeyused = 1;
from->received_seqno = 0;
send_ans_key(c, myself, from);
}
else
{
/* Proxy keys
if(to->status.validkey)
{
send_ans_key(c, to, from);
}
else
*/
send_req_key(to->nexthop->connection, from, to);
}
cp
return 0;
}
int send_ans_key(connection_t *c, node_t *from, node_t *to)
{
char key[MAX_STRING_SIZE];
cp
bin2hex(from->key, key, from->keylength);
key[from->keylength * 2] = '\0';
cp
return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
from->name, to->name, key, from->cipher?from->cipher->nid:0, from->digest?from->digest->type:0, from->maclength, from->compression);
}
int ans_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
node_t *from, *to;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", from_name, to_name, key, &cipher, &digest, &maclength, &compression) != 7)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY",
c->name, c->hostname);
return -1;
}
from = lookup_node(from_name);
if(!from)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "ANS_KEY",
c->name, c->hostname, from_name);
return -1;
}
to = lookup_node(to_name);
if(!to)
{
syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "ANS_KEY",
c->name, c->hostname, to_name);
return -1;
}
/* Forward it if necessary */
if(to != myself)
{
return send_request(to->nexthop->connection, "%s", c->buffer);
}
/* Update our copy of the origin's packet key */
if(from->key)
free(from->key);
from->key = xstrdup(key);
from->keylength = strlen(key) / 2;
hex2bin(from->key, from->key, from->keylength);
from->key[from->keylength] = '\0';
from->status.validkey = 1;
from->status.waitingforkey = 0;
/* Check and lookup cipher and digest algorithms */
if(cipher)
{
from->cipher = EVP_get_cipherbynid(cipher);
if(!from->cipher)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
return -1;
}
if(from->keylength != from->cipher->key_len + from->cipher->iv_len)
{
syslog(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
return -1;
}
}
else
{
from->cipher = NULL;
}
from->maclength = maclength;
if(digest)
{
from->digest = EVP_get_digestbynid(digest);
if(!from->digest)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
return -1;
}
if(from->maclength > from->digest->md_size || from->maclength < 0)
{
syslog(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
return -1;
}
}
else
{
from->digest = NULL;
}
from->compression = compression;
cp
return 0;
}

View file

@ -1,182 +0,0 @@
/*
protocol_misc.c -- handle the meta-protocol, miscellaneous functions
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_misc.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include "conf.h"
#include "interface.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "logging.h"
#include "system.h"
/* Status and error notification routines */
int send_status(connection_t *c, int statusno, char *statusstring)
{
cp
if(!statusstring)
statusstring = status_text[statusno];
cp
return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
}
int status_h(connection_t *c)
{
int statusno;
char statusstring[MAX_STRING_SIZE];
cp
if(sscanf(c->buffer, "%*d %d "MAX_STRING, &statusno, statusstring) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "STATUS",
c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_STATUS)
{
syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
c->name, c->hostname, status_text[statusno], statusstring);
}
cp
return 0;
}
int send_error(connection_t *c, int err, char *errstring)
{
cp
if(!errstring)
errstring = strerror(err);
return send_request(c, "%d %d %s", ERROR, err, errstring);
}
int error_h(connection_t *c)
{
int err;
char errorstring[MAX_STRING_SIZE];
cp
if(sscanf(c->buffer, "%*d %d "MAX_STRING, &err, errorstring) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ERROR",
c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_ERROR)
{
syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
c->name, c->hostname, strerror(err), errorstring);
}
terminate_connection(c, c->status.active);
cp
return 0;
}
int send_termreq(connection_t *c)
{
cp
return send_request(c, "%d", TERMREQ);
}
int termreq_h(connection_t *c)
{
cp
terminate_connection(c, c->status.active);
cp
return 0;
}
int send_ping(connection_t *c)
{
cp
c->status.pinged = 1;
c->last_ping_time = now;
cp
return send_request(c, "%d", PING);
}
int ping_h(connection_t *c)
{
cp
return send_pong(c);
}
int send_pong(connection_t *c)
{
cp
return send_request(c, "%d", PONG);
}
int pong_h(connection_t *c)
{
cp
c->status.pinged = 0;
/* Succesful connection, reset timeout if this is an outgoing connection. */
if(c->outgoing)
c->outgoing->timeout = 0;
cp
return 0;
}
/* Sending and receiving packets via TCP */
int send_tcppacket(connection_t *c, vpn_packet_t *packet)
{
int x;
cp
/* Evil hack. */
x = send_request(c, "%d %hd", PACKET, packet->len);
if(x)
return x;
cp
return send_meta(c, packet->data, packet->len);
}
/* Status strings */
char (*status_text[]) = {
"Warning",
};
/* Error strings */
char (*error_text[]) = {
"Error",
};

View file

@ -1,238 +0,0 @@
/*
protocol_subnet.c -- handle the meta-protocol, subnets
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_subnet.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "interface.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "logging.h"
#include "system.h"
int send_add_subnet(connection_t *c, subnet_t *subnet)
{
int x;
char *netstr;
cp
x = send_request(c, "%d %lx %s %s", ADD_SUBNET, random(),
subnet->owner->name, netstr = net2str(subnet));
free(netstr);
cp
return x;
}
int add_subnet_h(connection_t *c)
{
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
connection_t *other;
subnet_t *s;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name, c->hostname);
return -1;
}
/* Check if owner name is a valid */
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid name"));
return -1;
}
/* Check if subnet string is valid */
if(!(s = str2net(subnetstr)))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid subnet string"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* Check if the owner of the new subnet is in the connection list */
owner = lookup_node(name);
if(!owner)
{
owner = new_node();
owner->name = xstrdup(name);
node_add(owner);
}
/* Check if we already know this subnet */
if(lookup_subnet(owner, s))
{
free_subnet(s);
return 0;
}
/* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
if(owner == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "ADD_SUBNET", c->name, c->hostname);
s->owner = myself;
send_del_subnet(c, s);
return 0;
}
/* If everything is correct, add the subnet to the list of the owner */
subnet_add(owner, s);
/* Tell the rest */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
cp
return 0;
}
int send_del_subnet(connection_t *c, subnet_t *s)
{
int x;
char *netstr;
cp
netstr = net2str(s);
x = send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr);
free(netstr);
cp
return x;
}
int del_subnet_h(connection_t *c)
{
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
connection_t *other;
subnet_t *s, *find;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name, c->hostname);
return -1;
}
/* Check if owner name is a valid */
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid name"));
return -1;
}
/* Check if the owner of the new subnet is in the connection list */
if(!(owner = lookup_node(name)))
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return 0;
}
/* Check if subnet string is valid */
if(!(s = str2net(subnetstr)))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid subnet string"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* If everything is correct, delete the subnet from the list of the owner */
s->owner = owner;
find = lookup_subnet(owner, s);
free_subnet(s);
if(!find)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which does not appear in his subnet tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return 0;
}
/* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
if(owner == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_SUBNET", c->name, c->hostname);
send_add_subnet(c, find);
return 0;
}
/* Tell the rest */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* Finally, delete it. */
subnet_del(owner, find);
cp
return 0;
}

View file

@ -1,377 +0,0 @@
/*
route.c -- routing
Copyright (C) 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: route.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
#include <sys/param.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#if defined(HAVE_SOLARIS) || defined(HAVE_OPENBSD)
#include <net/if.h>
#define ETHER_ADDR_LEN 6
#else
#include <net/ethernet.h>
#endif
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/if_ether.h>
#include <utils.h>
#include <xalloc.h>
#include <string.h>
#include <avl_tree.h>
#include "net.h"
#include "interface.h"
#include "connection.h"
#include "subnet.h"
#include "route.h"
#include "protocol.h"
#include "device.h"
#include "logging.h"
#include "system.h"
int routing_mode = RMODE_ROUTER;
int priorityinheritance = 0;
int macexpire = 600;
subnet_t mymac;
void learn_mac(mac_t *address)
{
subnet_t *subnet;
avl_node_t *node;
connection_t *c;
cp
subnet = lookup_subnet_mac(address);
/* If we don't know this MAC address yet, store it */
if(!subnet || subnet->owner!=myself)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
address->x[0], address->x[1], address->x[2], address->x[3], address->x[4], address->x[5]);
subnet = new_subnet();
subnet->type = SUBNET_MAC;
memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
subnet_add(myself, subnet);
/* And tell all other tinc daemons it's our MAC */
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c->status.active)
send_add_subnet(c, subnet);
}
}
subnet->net.mac.lastseen = now;
}
void age_mac(void)
{
subnet_t *s;
connection_t *c;
avl_node_t *node, *next, *node2;
cp
for(node = myself->subnet_tree->head; node; node = next)
{
next = node->next;
s = (subnet_t *)node->data;
if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
s->net.mac.address.x[0], s->net.mac.address.x[1], s->net.mac.address.x[2], s->net.mac.address.x[3], s->net.mac.address.x[4], s->net.mac.address.x[5]);
for(node2 = connection_tree->head; node2; node2 = node2->next)
{
c = (connection_t *)node2->data;
if(c->status.active)
send_del_subnet(c, s);
}
subnet_del(myself, s);
}
}
cp
}
node_t *route_mac(vpn_packet_t *packet)
{
subnet_t *subnet;
cp
/* Learn source address */
learn_mac((mac_t *)(&packet->data[6]));
/* Lookup destination address */
subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
if(subnet)
return subnet->owner;
else
return NULL;
}
node_t *route_ipv4(vpn_packet_t *packet)
{
subnet_t *subnet;
cp
if(priorityinheritance)
packet->priority = packet->data[15];
subnet = lookup_subnet_ipv4((ipv4_t *)&packet->data[30]);
cp
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
packet->data[30], packet->data[31], packet->data[32], packet->data[33]);
}
return NULL;
}
cp
return subnet->owner;
}
node_t *route_ipv6(vpn_packet_t *packet)
{
subnet_t *subnet;
cp
subnet = lookup_subnet_ipv6((ipv6_t *)&packet->data[38]);
cp
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
ntohs(*(short unsigned int *)&packet->data[38]),
ntohs(*(short unsigned int *)&packet->data[40]),
ntohs(*(short unsigned int *)&packet->data[42]),
ntohs(*(short unsigned int *)&packet->data[44]),
ntohs(*(short unsigned int *)&packet->data[46]),
ntohs(*(short unsigned int *)&packet->data[48]),
ntohs(*(short unsigned int *)&packet->data[50]),
ntohs(*(short unsigned int *)&packet->data[52]));
}
return NULL;
}
cp
return subnet->owner;
}
unsigned short int inet_checksum(unsigned short int *data, int len, unsigned short int prevsum)
{
unsigned long int checksum = prevsum ^ 0xFFFF;
while(len--)
checksum += ntohs(*data++);
while(checksum >> 16)
checksum = (checksum & 0xFFFF) + (checksum >> 16);
return checksum ^ 0xFFFF;
}
void route_neighborsol(vpn_packet_t *packet)
{
struct ip6_hdr *hdr;
struct nd_neighbor_solicit *ns;
struct nd_opt_hdr *opt;
subnet_t *subnet;
short unsigned int checksum;
struct {
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
uint32_t length;
uint8_t junk[4];
} pseudo;
cp
hdr = (struct ip6_hdr *)(packet->data + 14);
ns = (struct nd_neighbor_solicit *)(packet->data + 14 + sizeof(*hdr));
opt = (struct nd_opt_hdr *)(packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
/* First, snatch the source address from the neighbor solicitation packet */
memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
/* Check if this is a valid neighbor solicitation request */
if(ns->nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
opt->nd_opt_type != ND_OPT_SOURCE_LINKADDR)
{
if(debug_lvl > DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
}
return;
}
/* Create pseudo header */
memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
pseudo.junk[3] = IPPROTO_ICMPV6;
/* Generate checksum */
checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
if(checksum)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
return;
}
/* Check if the IPv6 address exists on the VPN */
subnet = lookup_subnet_ipv6((ipv6_t *)&ns->nd_ns_target);
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
ntohs(((uint16_t *)&ns->nd_ns_target)[0]), ntohs(((uint16_t *)&ns->nd_ns_target)[1]), ntohs(((uint16_t *)&ns->nd_ns_target)[2]), ntohs(((uint16_t *)&ns->nd_ns_target)[3]),
ntohs(((uint16_t *)&ns->nd_ns_target)[4]), ntohs(((uint16_t *)&ns->nd_ns_target)[5]), ntohs(((uint16_t *)&ns->nd_ns_target)[6]), ntohs(((uint16_t *)&ns->nd_ns_target)[7]));
}
return;
}
/* Check if it is for our own subnet */
if(subnet->owner == myself)
return; /* silently ignore */
/* Create neighbor advertation reply */
memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(&hdr->ip6_dst, &hdr->ip6_src, 16); /* swap destination and source protocol address */
memcpy(&hdr->ip6_src, &ns->nd_ns_target, 16); /* ... */
memcpy((char *)opt + sizeof(*opt), packet->data + ETHER_ADDR_LEN, 6); /* add fake source hard addr */
ns->nd_ns_hdr.icmp6_cksum = 0;
ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_ADVERT;
ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[0] = 0x40; /* Set solicited flag */
ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[1] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[2] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[3] = 0;
opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
/* Create pseudo header */
memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
pseudo.junk[3] = IPPROTO_ICMPV6;
/* Generate checksum */
checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
ns->nd_ns_hdr.icmp6_cksum = htons(checksum);
cp
}
void route_arp(vpn_packet_t *packet)
{
struct ether_arp *arp;
subnet_t *subnet;
unsigned char ipbuf[4];
cp
/* First, snatch the source address from the ARP packet */
memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
/* This routine generates replies to ARP requests.
You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
*/
arp = (struct ether_arp *)(packet->data + 14);
/* Check if this is a valid ARP request */
if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
ntohs(arp->arp_pro) != ETHERTYPE_IP ||
(int) (arp->arp_hln) != ETHER_ADDR_LEN ||
(int) (arp->arp_pln) != 4 ||
ntohs(arp->arp_op) != ARPOP_REQUEST )
{
if(debug_lvl > DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
}
return;
}
/* Check if the IPv4 address exists on the VPN */
subnet = lookup_subnet_ipv4((ipv4_t *)arp->arp_tpa);
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
}
return;
}
/* Check if it is for our own subnet */
if(subnet->owner == myself)
return; /* silently ignore */
memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */
memcpy(arp->arp_tpa, arp->arp_spa, 4); /* swap destination and source protocol address */
memcpy(arp->arp_spa, ipbuf, 4); /* ... */
memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* add fake source hard addr */
arp->arp_op = htons(ARPOP_REPLY);
cp
}

View file

@ -1,41 +0,0 @@
/*
route.h -- header file for route.c
Copyright (C) 2000-2002 Ivo Timmermans <zarq@iname.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: route.h,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#ifndef __TINC_ROUTE_H__
#define __TINC_ROUTE_H__
enum
{
RMODE_HUB = 0,
RMODE_SWITCH,
RMODE_ROUTER,
};
extern int routing_mode;
extern int priorityinheritance;
extern int macexpire;
extern void age_mac(void);
extern void route_incoming(node_t *, vpn_packet_t *);
extern void route_outgoing(vpn_packet_t *);
#endif /* __TINC_ROUTE_H__ */

View file

@ -1,7 +1,7 @@
/*
process.c -- process management functions
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,309 +17,418 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: process.c,v 1.4 2002/04/13 11:07:12 zarq Exp $
$Id: process.c,v 1.5 2003/08/24 20:38:25 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <pidfile.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "process.h"
#include "subnet.h"
#include "device.h"
#include "connection.h"
#include "device.h"
#include "logging.h"
#include "system.h"
#include "conf.h"
#include "connection.h"
#include "device.h"
#include "edge.h"
#include "logger.h"
#include "node.h"
#include "pidfile.h"
#include "process.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
/* If zero, don't detach from the terminal. */
int do_detach = 1;
bool do_detach = true;
bool sighup = false;
bool sigalrm = false;
extern char *identname;
extern char *pidfilename;
extern char **g_argv;
extern bool use_logfile;
extern volatile bool running;
sigset_t emptysigset;
static int saved_debug_lvl = 0;
static int saved_debug_level = -1;
extern int sighup;
extern int sigalrm;
extern int do_purge;
void memory_full(int size)
static void memory_full(int size)
{
log(0, TLOG_ERROR,
_("Memory exhausted (couldn't allocate %d bytes), exitting."),
size);
cp_trace();
exit(1);
logger(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exitting."), size);
cp_trace();
exit(1);
}
/* Some functions the less gifted operating systems might lack... */
#ifndef HAVE_FCLOSEALL
int fcloseall(void)
static int fcloseall(void)
{
fflush(stdin);
fflush(stdout);
fflush(stderr);
fclose(stdin);
fclose(stdout);
fclose(stderr);
return 0;
fflush(stdin);
fflush(stdout);
fflush(stderr);
fclose(stdin);
fclose(stdout);
fclose(stderr);
return 0;
}
#endif
#ifdef HAVE_MINGW
extern char *identname;
extern char *program_name;
extern char **g_argv;
static SC_HANDLE manager = NULL;
static SC_HANDLE service = NULL;
static SERVICE_STATUS status = {0};
static SERVICE_STATUS_HANDLE statushandle = 0;
bool install_service(void) {
char command[4096] = "\"";
char **argp;
bool space;
SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!manager) {
logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
return false;
}
if(!strchr(program_name, '\\')) {
GetCurrentDirectory(sizeof(command) - 1, command + 1);
strncat(command, "\\", sizeof(command));
}
strncat(command, program_name, sizeof(command));
strncat(command, "\"", sizeof(command));
for(argp = g_argv + 1; *argp; argp++) {
space = strchr(*argp, ' ');
strncat(command, " ", sizeof(command));
if(space)
strncat(command, "\"", sizeof(command));
strncat(command, *argp, sizeof(command));
if(space)
strncat(command, "\"", sizeof(command));
}
service = CreateService(manager, identname, identname,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
command, "NDIS", NULL, NULL, NULL, NULL);
if(!service) {
logger(LOG_ERR, _("Could not create %s service: %s"), identname, winerror(GetLastError()));
return false;
}
ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
logger(LOG_INFO, _("%s service installed"), identname);
if(!StartService(service, 0, NULL))
logger(LOG_WARNING, _("Could not start %s service: %s"), identname, winerror(GetLastError()));
else
logger(LOG_INFO, _("%s service started"), identname);
return true;
}
bool remove_service(void) {
manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!manager) {
logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
return false;
}
service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
if(!service) {
logger(LOG_ERR, _("Could not open %s service: %s"), identname, winerror(GetLastError()));
return false;
}
if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
logger(LOG_ERR, _("Could not stop %s service: %s"), identname, winerror(GetLastError()));
else
logger(LOG_INFO, _("%s service stopped"), identname);
if(!DeleteService(service)) {
logger(LOG_ERR, _("Could not remove %s service: %s"), identname, winerror(GetLastError()));
return false;
}
logger(LOG_INFO, _("%s service removed"), identname);
return true;
}
DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
switch(request) {
case SERVICE_CONTROL_STOP:
logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_STOP");
break;
case SERVICE_CONTROL_SHUTDOWN:
logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_SHUTDOWN");
break;
default:
logger(LOG_WARNING, _("Got unexpected request %d"), request);
return ERROR_CALL_NOT_IMPLEMENTED;
}
if(running) {
running = false;
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(statushandle, &status);
return NO_ERROR;
} else {
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(statushandle, &status);
exit(1);
}
}
VOID WINAPI run_service(DWORD argc, LPTSTR* argv)
{
int err = 1;
extern int main2(int argc, char **argv);
status.dwServiceType = SERVICE_WIN32;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL);
if (!statushandle) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
err = 1;
} else {
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(statushandle, &status);
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(statushandle, &status);
err = main2(argc, argv);
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_STOPPED;
//status.dwWin32ExitCode = err;
SetServiceStatus(statushandle, &status);
}
return;
}
bool init_service(void) {
SERVICE_TABLE_ENTRY services[] = {
{identname, run_service},
{NULL, NULL}
};
if(!StartServiceCtrlDispatcher(services)) {
if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
return false;
}
else
logger(LOG_ERR, _("System call `%s' failed: %s"), "StartServiceCtrlDispatcher", winerror(GetLastError()));
}
return true;
}
#endif
#ifndef HAVE_MINGW
/*
check for an existing tinc for this net, and write pid to pidfile
*/
static bool write_pidfile(void)
{
int pid;
cp();
pid = check_pid(pidfilename);
if(pid) {
if(netname)
fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
netname, pid);
else
fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
return false;
}
/* if it's locked, write-protected, or whatever */
if(!write_pid(pidfilename))
return false;
return true;
}
#endif
/*
Close network connections, and terminate neatly
*/
void cleanup_and_exit(int c)
{
cp
close_network_connections();
if(debug_lvl > DEBUG_NOTHING)
dump_device_stats();
syslog(LOG_NOTICE, _("Terminating"));
closelog();
exit(c);
}
/*
check for an existing tinc for this net, and write pid to pidfile
*/
int write_pidfile(void)
{
int pid;
cp
if((pid = check_pid(pidfilename)))
{
if(netname)
fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
netname, pid);
else
fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
return 1;
}
/* if it's locked, write-protected, or whatever */
if(!write_pid(pidfilename))
return 1;
cp
return 0;
}
/*
kill older tincd for this net
*/
int kill_other(int signal)
bool kill_other(int signal)
{
int pid;
cp
if(!(pid = read_pid(pidfilename)))
{
if(netname)
fprintf(stderr, _("No other tincd is running for net `%s'.\n"), netname);
else
fprintf(stderr, _("No other tincd is running.\n"));
return 1;
}
#ifndef HAVE_MINGW
int pid;
errno = 0; /* No error, sometimes errno is only changed on error */
/* ESRCH is returned when no process with that pid is found */
if(kill(pid, signal) && errno == ESRCH)
{
if(netname)
fprintf(stderr, _("The tincd for net `%s' is no longer running. "), netname);
else
fprintf(stderr, _("The tincd is no longer running. "));
cp();
fprintf(stderr, _("Removing stale lock file.\n"));
remove_pid(pidfilename);
}
cp
return 0;
pid = read_pid(pidfilename);
if(!pid) {
if(netname)
fprintf(stderr, _("No other tincd is running for net `%s'.\n"),
netname);
else
fprintf(stderr, _("No other tincd is running.\n"));
return false;
}
errno = 0; /* No error, sometimes errno is only changed on error */
/* ESRCH is returned when no process with that pid is found */
if(kill(pid, signal) && errno == ESRCH) {
if(netname)
fprintf(stderr, _("The tincd for net `%s' is no longer running. "),
netname);
else
fprintf(stderr, _("The tincd is no longer running. "));
fprintf(stderr, _("Removing stale lock file.\n"));
remove_pid(pidfilename);
}
return true;
#else
return remove_service();
#endif
}
/*
Detach from current terminal, write pidfile, kill parent
*/
int detach(void)
bool detach(void)
{
cp
setup_signals();
cp();
/* First check if we can open a fresh new pidfile */
if(write_pidfile())
return -1;
setup_signals();
/* If we succeeded in doing that, detach */
/* First check if we can open a fresh new pidfile */
closelog();
#ifndef HAVE_MINGW
if(!write_pidfile())
return false;
if(do_detach)
{
if(daemon(0, 0) < 0)
{
fprintf(stderr, _("Couldn't detach from terminal: %s"), strerror(errno));
return -1;
}
/* If we succeeded in doing that, detach */
/* Now UPDATE the pid in the pidfile, because we changed it... */
if(!write_pid(pidfilename))
return -1;
}
openlog(identname, LOG_CONS | LOG_PID, LOG_DAEMON);
log_add_hook(log_syslog);
log_del_hook(log_default);
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
VERSION, __DATE__, __TIME__, debug_lvl);
else
syslog(LOG_NOTICE, _("tincd %s starting"), VERSION);
xalloc_fail_func = memory_full;
cp
return 0;
}
/*
Execute the program name, with sane environment. All output will be
redirected to syslog.
*/
void _execute_script(const char *scriptname) __attribute__ ((noreturn));
void _execute_script(const char *scriptname)
{
char *s;
cp
#ifdef HAVE_UNSETENV
unsetenv("NETNAME");
unsetenv("DEVICE");
unsetenv("INTERFACE");
closelogger();
#endif
if(netname)
{
asprintf(&s, "NETNAME=%s", netname);
putenv(s); /* Don't free s! see man 3 putenv */
}
if(do_detach) {
#ifndef HAVE_MINGW
if(daemon(0, 0)) {
fprintf(stderr, _("Couldn't detach from terminal: %s"),
strerror(errno));
return false;
}
if(device)
{
asprintf(&s, "DEVICE=%s", device);
putenv(s); /* Don't free s! see man 3 putenv */
}
/* Now UPDATE the pid in the pidfile, because we changed it... */
if(interface)
{
asprintf(&s, "INTERFACE=%s", interface);
putenv(s); /* Don't free s! see man 3 putenv */
}
if(!write_pid(pidfilename))
return false;
#else
if(!statushandle)
exit(install_service());
#endif
}
chdir("/");
/* Close all file descriptors */
closelog(); /* <- this means we cannot use syslog() here anymore! */
fcloseall();
openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
execl(scriptname, NULL);
/* No return on success */
if(errno != ENOENT) /* Ignore if the file does not exist */
exit(1); /* Some error while trying execl(). */
else
exit(0);
logger(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
VERSION, __DATE__, __TIME__, debug_level);
xalloc_fail_func = memory_full;
return true;
}
/*
Fork and execute the program pointed to by name.
*/
int execute_script(const char *name)
bool execute_script(const char *name, char **envp)
{
pid_t pid;
int status;
struct stat s;
char *scriptname;
cp
asprintf(&scriptname, "%s/%s", confbase, name);
#ifdef HAVE_SYSTEM
int status, len;
struct stat s;
char *scriptname;
/* First check if there is a script */
cp();
if(stat(scriptname, &s))
return 0;
#ifndef HAVE_MINGW
len = asprintf(&scriptname, "\"%s/%s\"", confbase, name);
#else
len = asprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
#endif
if(len < 0)
return false;
if((pid = fork()) < 0)
{
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fork", strerror(errno));
return -1;
}
scriptname[len - 1] = '\0';
if(pid)
{
if(debug_lvl >= DEBUG_STATUS)
syslog(LOG_INFO, _("Executing script %s"), name);
/* First check if there is a script */
free(scriptname);
if(stat(scriptname + 1, &s))
return true;
if(waitpid(pid, &status, 0) == pid)
{
if(WIFEXITED(status)) /* Child exited by itself */
{
if(WEXITSTATUS(status))
{
syslog(LOG_ERR, _("Process %d (%s) exited with non-zero status %d"), pid, name, WEXITSTATUS(status));
return -1;
}
else
return 0;
}
else if(WIFSIGNALED(status)) /* Child was killed by a signal */
{
syslog(LOG_ERR, _("Process %d (%s) was killed by signal %d (%s)"),
pid, name, WTERMSIG(status), strsignal(WTERMSIG(status)));
return -1;
}
else /* Something strange happened */
{
syslog(LOG_ERR, _("Process %d (%s) terminated abnormally"), pid, name);
return -1;
}
}
else
{
syslog(LOG_ERR, _("System call `%s' failed: %s"), "waitpid", strerror(errno));
return -1;
}
}
cp
/* Child here */
ifdebug(STATUS) logger(LOG_INFO, _("Executing script %s"), name);
_execute_script(scriptname);
#ifdef HAVE_PUTENV
/* Set environment */
while(*envp)
putenv(*envp++);
#endif
scriptname[len - 1] = '\"';
status = system(scriptname);
free(scriptname);
/* Unset environment? */
#ifdef WEXITSTATUS
if(status != -1) {
if(WIFEXITED(status)) { /* Child exited by itself */
if(WEXITSTATUS(status)) {
logger(LOG_ERR, _("Script %s exited with non-zero status %d"),
name, WEXITSTATUS(status));
return false;
}
} else if(WIFSIGNALED(status)) { /* Child was killed by a signal */
logger(LOG_ERR, _("Script %s was killed by signal %d (%s)"),
name, WTERMSIG(status), strsignal(WTERMSIG(status)));
return false;
} else { /* Something strange happened */
logger(LOG_ERR, _("Script %s terminated abnormally"), name);
return false;
}
} else {
logger(LOG_ERR, _("System call `%s' failed: %s"), "system", strerror(errno));
return false;
}
#endif
#endif
return true;
}
@ -327,186 +436,164 @@ cp
Signal handlers.
*/
RETSIGTYPE
sigterm_handler(int a)
#ifndef HAVE_MINGW
static RETSIGTYPE sigterm_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got TERM signal"));
cleanup_and_exit(0);
logger(LOG_NOTICE, _("Got %s signal"), "TERM");
running = false;
}
RETSIGTYPE
sigquit_handler(int a)
static RETSIGTYPE sigquit_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got QUIT signal"));
cleanup_and_exit(0);
logger(LOG_NOTICE, _("Got %s signal"), "QUIT");
running = false;
}
RETSIGTYPE
fatal_signal_square(int a)
static RETSIGTYPE fatal_signal_square(int a)
{
syslog(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a, strsignal(a));
cp_trace();
exit(1);
logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a,
strsignal(a));
cp_trace();
exit(1);
}
RETSIGTYPE
fatal_signal_handler(int a)
static RETSIGTYPE fatal_signal_handler(int a)
{
struct sigaction act;
syslog(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
cp_trace();
struct sigaction act;
logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
cp_trace();
if(do_detach)
{
syslog(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
if(do_detach) {
logger(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
act.sa_handler = fatal_signal_square;
act.sa_mask = emptysigset;
act.sa_flags = 0;
sigaction(SIGSEGV, &act, NULL);
act.sa_handler = fatal_signal_square;
act.sa_mask = emptysigset;
act.sa_flags = 0;
sigaction(SIGSEGV, &act, NULL);
close_network_connections();
sleep(5);
remove_pid(pidfilename);
execvp(g_argv[0], g_argv);
}
else
{
syslog(LOG_NOTICE, _("Not restarting."));
exit(1);
}
close_network_connections();
sleep(5);
remove_pid(pidfilename);
execvp(g_argv[0], g_argv);
} else {
logger(LOG_NOTICE, _("Not restarting."));
exit(1);
}
}
RETSIGTYPE
sighup_handler(int a)
static RETSIGTYPE sighup_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got HUP signal"));
sighup = 1;
logger(LOG_NOTICE, _("Got %s signal"), "HUP");
sighup = true;
}
RETSIGTYPE
sigint_handler(int a)
static RETSIGTYPE sigint_handler(int a)
{
if(saved_debug_lvl)
{
syslog(LOG_NOTICE, _("Reverting to old debug level (%d)"),
saved_debug_lvl);
debug_lvl = saved_debug_lvl;
saved_debug_lvl = 0;
}
else
{
syslog(LOG_NOTICE, _("Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d."),
debug_lvl);
saved_debug_lvl = debug_lvl;
debug_lvl = 5;
}
logger(LOG_NOTICE, _("Got %s signal"), "INT");
if(saved_debug_level != -1) {
logger(LOG_NOTICE, _("Reverting to old debug level (%d)"),
saved_debug_level);
debug_level = saved_debug_level;
saved_debug_level = -1;
} else {
logger(LOG_NOTICE,
_("Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d."),
debug_level);
saved_debug_level = debug_level;
debug_level = 5;
}
}
RETSIGTYPE
sigalrm_handler(int a)
static RETSIGTYPE sigalrm_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got ALRM signal"));
sigalrm = 1;
logger(LOG_NOTICE, _("Got %s signal"), "ALRM");
sigalrm = true;
}
RETSIGTYPE
sigusr1_handler(int a)
static RETSIGTYPE sigusr1_handler(int a)
{
dump_connections();
dump_connections();
}
RETSIGTYPE
sigusr2_handler(int a)
static RETSIGTYPE sigusr2_handler(int a)
{
dump_device_stats();
dump_nodes();
dump_edges();
dump_subnets();
dump_device_stats();
dump_nodes();
dump_edges();
dump_subnets();
}
RETSIGTYPE
sigwinch_handler(int a)
static RETSIGTYPE sigwinch_handler(int a)
{
extern int do_purge;
do_purge = 1;
do_purge = true;
}
RETSIGTYPE
unexpected_signal_handler(int a)
static RETSIGTYPE unexpected_signal_handler(int a)
{
syslog(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
cp_trace();
logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
cp_trace();
}
RETSIGTYPE
ignore_signal_handler(int a)
static RETSIGTYPE ignore_signal_handler(int a)
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
syslog(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
cp_trace();
}
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
}
struct {
int signal;
void (*handler)(int);
static struct {
int signal;
void (*handler)(int);
} sighandlers[] = {
{ SIGHUP, sighup_handler },
{ SIGTERM, sigterm_handler },
{ SIGQUIT, sigquit_handler },
{ SIGSEGV, fatal_signal_handler },
{ SIGBUS, fatal_signal_handler },
{ SIGILL, fatal_signal_handler },
{ SIGPIPE, ignore_signal_handler },
{ SIGINT, sigint_handler },
{ SIGUSR1, sigusr1_handler },
{ SIGUSR2, sigusr2_handler },
{ SIGCHLD, ignore_signal_handler },
{ SIGALRM, sigalrm_handler },
{ SIGWINCH, sigwinch_handler },
{ 0, NULL }
{SIGHUP, sighup_handler},
{SIGTERM, sigterm_handler},
{SIGQUIT, sigquit_handler},
{SIGSEGV, fatal_signal_handler},
{SIGBUS, fatal_signal_handler},
{SIGILL, fatal_signal_handler},
{SIGPIPE, ignore_signal_handler},
{SIGINT, sigint_handler},
{SIGUSR1, sigusr1_handler},
{SIGUSR2, sigusr2_handler},
{SIGCHLD, ignore_signal_handler},
{SIGALRM, sigalrm_handler},
{SIGWINCH, sigwinch_handler},
{0, NULL}
};
#endif
void
setup_signals(void)
void setup_signals(void)
{
int i;
struct sigaction act;
#ifndef HAVE_MINGW
int i;
struct sigaction act;
sigemptyset(&emptysigset);
act.sa_handler = NULL;
act.sa_mask = emptysigset;
act.sa_flags = 0;
sigemptyset(&emptysigset);
act.sa_handler = NULL;
act.sa_mask = emptysigset;
act.sa_flags = 0;
/* Set a default signal handler for every signal, errors will be
ignored. */
for(i = 0; i < NSIG; i++)
{
if(!do_detach)
act.sa_handler = SIG_DFL;
else
act.sa_handler = unexpected_signal_handler;
sigaction(i, &act, NULL);
}
/* Set a default signal handler for every signal, errors will be
ignored. */
for(i = 0; i < NSIG; i++) {
if(!do_detach)
act.sa_handler = SIG_DFL;
else
act.sa_handler = unexpected_signal_handler;
sigaction(i, &act, NULL);
}
/* If we didn't detach, allow coredumps */
if(!do_detach)
sighandlers[3].handler = SIG_DFL;
/* If we didn't detach, allow coredumps */
if(!do_detach)
sighandlers[3].handler = SIG_DFL;
/* Then, for each known signal that we want to catch, assign a
handler to the signal, with error checking this time. */
for(i = 0; sighandlers[i].signal; i++)
{
act.sa_handler = sighandlers[i].handler;
if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
sighandlers[i].signal, strsignal(sighandlers[i].signal), strerror(errno));
}
/* Then, for each known signal that we want to catch, assign a
handler to the signal, with error checking this time. */
for(i = 0; sighandlers[i].signal; i++) {
act.sa_handler = sighandlers[i].handler;
if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
sighandlers[i].signal, strsignal(sighandlers[i].signal),
strerror(errno));
}
#endif
}

View file

@ -1,7 +1,7 @@
/*
process.h -- header file for process.c
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,20 +17,19 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: process.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
$Id: process.h,v 1.3 2003/08/24 20:38:27 guus Exp $
*/
#ifndef __TINC_PROCESS_H__
#define __TINC_PROCESS_H__
#include "config.h"
extern int do_detach;
extern bool do_detach;
extern bool sighup;
extern bool sigalrm;
extern void setup_signals(void);
extern int execute_script(const char *);
extern int detach(void);
extern int kill_other(int);
extern void cleanup_and_exit(int);
extern bool execute_script(const char *, char **);
extern bool detach(void);
extern bool kill_other(int);
#endif /* __TINC_PROCESS_H__ */
#endif /* __TINC_PROCESS_H__ */

View file

@ -1,7 +1,7 @@
/*
protocol.c -- handle the meta-protocol, basic functions
Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
2000,2001 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2001 Ivo Timmermans <ivo@o2w.nl>,
2000,2001 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,228 +17,235 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol.c,v 1.31 2002/04/13 11:07:12 zarq Exp $
$Id: protocol.c,v 1.32 2003/08/24 20:38:27 guus Exp $
*/
#include "config.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "logging.h"
#include "system.h"
avl_tree_t *past_request_tree;
#include "conf.h"
#include "connection.h"
#include "logger.h"
#include "meta.h"
#include "protocol.h"
#include "utils.h"
#include "xalloc.h"
int check_id(char *id)
/* Jumptable for the request handlers */
static bool (*request_handlers[])(connection_t *) = {
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
add_subnet_h, del_subnet_h,
add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h, tcppacket_h,
};
/* Request names */
static char (*request_name[]) = {
"ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK",
"STATUS", "ERROR", "TERMREQ",
"PING", "PONG",
"ADD_SUBNET", "DEL_SUBNET",
"ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET",
};
static avl_tree_t *past_request_tree;
bool check_id(const char *id)
{
int i;
for(; *id; id++)
if(!isalnum(*id) && *id != '_')
return false;
for (i = 0; i < strlen(id); i++)
if(!isalnum(id[i]) && id[i] != '_')
return -1;
return 0;
return true;
}
/* Generic request routines - takes care of logging and error
detection as well */
int send_request(connection_t *c, const char *format, ...)
bool send_request(connection_t *c, const char *format, ...)
{
va_list args;
char buffer[MAXBUFSIZE];
int len, request;
va_list args;
char buffer[MAXBUFSIZE];
int len, request;
cp
/* Use vsnprintf instead of vasprintf: faster, no memory
fragmentation, cleanup is automatic, and there is a limit on the
input buffer anyway */
cp();
va_start(args, format);
len = vsnprintf(buffer, MAXBUFSIZE, format, args);
va_end(args);
/* Use vsnprintf instead of vasprintf: faster, no memory
fragmentation, cleanup is automatic, and there is a limit on the
input buffer anyway */
if(len < 0 || len > MAXBUFSIZE-1)
{
syslog(LOG_ERR, _("Output buffer overflow while sending request to %s (%s)"), c->name, c->hostname);
return -1;
}
va_start(args, format);
len = vsnprintf(buffer, MAXBUFSIZE, format, args);
va_end(args);
if(debug_lvl >= DEBUG_PROTOCOL)
{
sscanf(buffer, "%d", &request);
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Sending %s to %s (%s): %s"), request_name[request], c->name, c->hostname, buffer);
else
syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], c->name, c->hostname);
}
buffer[len++] = '\n';
cp
return send_meta(c, buffer, len);
}
int receive_request(connection_t *c)
{
int request;
cp
if(sscanf(c->buffer, "%d", &request) == 1)
{
if((request < 0) || (request >= LAST) || (request_handlers[request] == NULL))
{
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Unknown request from %s (%s): %s"),
c->name, c->hostname, c->buffer);
else
syslog(LOG_ERR, _("Unknown request from %s (%s)"),
c->name, c->hostname);
return -1;
}
else
{
if(debug_lvl >= DEBUG_PROTOCOL)
{
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Got %s from %s (%s): %s"),
request_name[request], c->name, c->hostname, c->buffer);
else
syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
request_name[request], c->name, c->hostname);
}
if(len < 0 || len > MAXBUFSIZE - 1) {
logger(LOG_ERR, _("Output buffer overflow while sending request to %s (%s)"),
c->name, c->hostname);
return false;
}
if((c->allow_request != ALL) && (c->allow_request != request))
{
syslog(LOG_ERR, _("Unauthorized request from %s (%s)"), c->name, c->hostname);
return -1;
}
ifdebug(PROTOCOL) {
sscanf(buffer, "%d", &request);
ifdebug(META)
logger(LOG_DEBUG, _("Sending %s to %s (%s): %s"),
request_name[request], c->name, c->hostname, buffer);
else
logger(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request],
c->name, c->hostname);
}
if(request_handlers[request](c))
/* Something went wrong. Probably scriptkiddies. Terminate. */
{
syslog(LOG_ERR, _("Error while processing %s from %s (%s)"),
request_name[request], c->name, c->hostname);
return -1;
}
}
else
{
syslog(LOG_ERR, _("Bogus data received from %s (%s)"),
c->name, c->hostname);
return -1;
}
cp
return 0;
buffer[len++] = '\n';
if(c == broadcast) {
broadcast_meta(NULL, buffer, len);
return true;
} else
return send_meta(c, buffer, len);
}
int past_request_compare(past_request_t *a, past_request_t *b)
void forward_request(connection_t *from)
{
cp
return strcmp(a->request, b->request);
int request;
cp();
ifdebug(PROTOCOL) {
sscanf(from->buffer, "%d", &request);
ifdebug(META)
logger(LOG_DEBUG, _("Forwarding %s from %s (%s): %s"),
request_name[request], from->name, from->hostname,
from->buffer);
else
logger(LOG_DEBUG, _("Forwarding %s from %s (%s)"),
request_name[request], from->name, from->hostname);
}
from->buffer[from->reqlen - 1] = '\n';
broadcast_meta(from, from->buffer, from->reqlen);
}
void free_past_request(past_request_t *r)
bool receive_request(connection_t *c)
{
cp
if(r->request)
free(r->request);
free(r);
cp
int request;
cp();
if(sscanf(c->buffer, "%d", &request) == 1) {
if((request < 0) || (request >= LAST) || !request_handlers[request]) {
ifdebug(META)
logger(LOG_DEBUG, _("Unknown request from %s (%s): %s"),
c->name, c->hostname, c->buffer);
else
logger(LOG_ERR, _("Unknown request from %s (%s)"),
c->name, c->hostname);
return false;
} else {
ifdebug(PROTOCOL) {
ifdebug(META)
logger(LOG_DEBUG, _("Got %s from %s (%s): %s"),
request_name[request], c->name, c->hostname,
c->buffer);
else
logger(LOG_DEBUG, _("Got %s from %s (%s)"),
request_name[request], c->name, c->hostname);
}
}
if((c->allow_request != ALL) && (c->allow_request != request)) {
logger(LOG_ERR, _("Unauthorized request from %s (%s)"), c->name,
c->hostname);
return false;
}
if(!request_handlers[request](c)) {
/* Something went wrong. Probably scriptkiddies. Terminate. */
logger(LOG_ERR, _("Error while processing %s from %s (%s)"),
request_name[request], c->name, c->hostname);
return false;
}
} else {
logger(LOG_ERR, _("Bogus data received from %s (%s)"),
c->name, c->hostname);
return false;
}
return true;
}
static int past_request_compare(const past_request_t *a, const past_request_t *b)
{
return strcmp(a->request, b->request);
}
static void free_past_request(past_request_t *r)
{
cp();
if(r->request)
free(r->request);
free(r);
}
void init_requests(void)
{
cp
past_request_tree = avl_alloc_tree((avl_compare_t)past_request_compare, (avl_action_t)free_past_request);
cp
cp();
past_request_tree = avl_alloc_tree((avl_compare_t) past_request_compare, (avl_action_t) free_past_request);
}
void exit_requests(void)
{
cp
avl_delete_tree(past_request_tree);
cp
cp();
avl_delete_tree(past_request_tree);
}
int seen_request(char *request)
bool seen_request(char *request)
{
past_request_t p, *new;
cp
p.request = request;
past_request_t *new, p = {0};
if(avl_search(past_request_tree, &p))
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, _("Already seen request"));
return 1;
}
else
{
new = (past_request_t *)xmalloc(sizeof(*new));
new->request = xstrdup(request);
new->firstseen = now;
avl_insert(past_request_tree, new);
return 0;
}
cp
cp();
p.request = request;
if(avl_search(past_request_tree, &p)) {
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Already seen request"));
return true;
} else {
new = (past_request_t *) xmalloc(sizeof(*new));
new->request = xstrdup(request);
new->firstseen = now;
avl_insert(past_request_tree, new);
return false;
}
}
void age_past_requests(void)
{
avl_node_t *node, *next;
past_request_t *p;
int left = 0, deleted = 0;
cp
for(node = past_request_tree->head; node; node = next)
{
next = node->next;
p = (past_request_t *)node->data;
if(p->firstseen + pingtimeout < now)
avl_delete_node(past_request_tree, node), deleted++;
else
left++;
}
avl_node_t *node, *next;
past_request_t *p;
int left = 0, deleted = 0;
if(debug_lvl >= DEBUG_SCARY_THINGS && left + deleted)
syslog(LOG_DEBUG, _("Aging past requests: deleted %d, left %d\n"), deleted, left);
cp
cp();
for(node = past_request_tree->head; node; node = next) {
next = node->next;
p = (past_request_t *) node->data;
if(p->firstseen + pingtimeout < now)
avl_delete_node(past_request_tree, node), deleted++;
else
left++;
}
if(left || deleted)
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Aging past requests: deleted %d, left %d"),
deleted, left);
}
/* Jumptable for the request handlers */
int (*request_handlers[])(connection_t*) = {
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
add_subnet_h, del_subnet_h,
add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h,
tcppacket_h,
};
/* Request names */
char (*request_name[]) = {
"ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK",
"STATUS", "ERROR", "TERMREQ",
"PING", "PONG",
"ADD_SUBNET", "DEL_SUBNET",
"ADD_EDGE", "DEL_EDGE",
"KEY_CHANGED", "REQ_KEY", "ANS_KEY",
"PACKET",
};

View file

@ -1,7 +1,7 @@
/*
protocol.h -- header for protocol.c
Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
2000,2001 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2001 Ivo Timmermans <ivo@o2w.nl>,
2000,2001 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,40 +17,41 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol.h,v 1.8 2002/04/28 12:46:26 zarq Exp $
$Id: protocol.h,v 1.9 2003/08/24 20:38:27 guus Exp $
*/
#ifndef __TINC_PROTOCOL_H__
#define __TINC_PROTOCOL_H__
#include "net.h"
#include "node.h"
#include "subnet.h"
/* Protocol version. Different versions are incompatible,
incompatible version have different protocols.
*/
#define PROT_CURRENT 14
#define PROT_CURRENT 17
/* Silly Windows */
#ifdef ERROR
#undef ERROR
#endif
/* Request numbers */
enum {
ALL = -1, /* Guardian for allow_request */
ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
STATUS, ERROR, TERMREQ,
PING, PONG,
/* ADD_NODE, DEL_NODE, */
ADD_SUBNET, DEL_SUBNET,
ADD_EDGE, DEL_EDGE,
KEY_CHANGED, REQ_KEY, ANS_KEY,
PACKET,
LAST /* Guardian for the highest request number */
};
typedef enum request_t {
ALL = -1, /* Guardian for allow_request */
ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
STATUS, ERROR, TERMREQ,
PING, PONG,
ADD_SUBNET, DEL_SUBNET,
ADD_EDGE, DEL_EDGE,
KEY_CHANGED, REQ_KEY, ANS_KEY,
PACKET,
LAST /* Guardian for the highest request number */
} request_t;
typedef struct past_request_t {
char *request;
time_t firstseen;
char *request;
time_t firstseen;
} past_request_t;
/* Maximum size of strings in a request */
@ -58,63 +59,63 @@ typedef struct past_request_t {
#define MAX_STRING_SIZE 2048
#define MAX_STRING "%2048s"
#include "edge.h"
#include "net.h"
#include "node.h"
#include "subnet.h"
/* Basic functions */
extern int send_request(connection_t*, const char*, ...);
extern int receive_request(connection_t *);
extern int check_id(char *);
extern bool send_request(struct connection_t *, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
extern void forward_request(struct connection_t *);
extern bool receive_request(struct connection_t *);
extern bool check_id(const char *);
extern void init_requests(void);
extern void exit_requests(void);
extern int seen_request(char *);
extern bool seen_request(char *);
extern void age_past_requests(void);
/* Requests */
extern int send_id(connection_t *);
extern int send_metakey(connection_t *);
extern int send_challenge(connection_t *);
extern int send_chal_reply(connection_t *);
extern int send_ack(connection_t *);
extern int send_status(connection_t *, int, char *);
extern int send_error(connection_t *, int, char *);
extern int send_termreq(connection_t *);
extern int send_ping(connection_t *);
extern int send_pong(connection_t *);
/* extern int send_add_node(connection_t *, node_t *); */
/* extern int send_del_node(connection_t *, node_t *); */
extern int send_add_subnet(connection_t *, subnet_t *);
extern int send_del_subnet(connection_t *, subnet_t *);
extern int send_add_edge(connection_t *, edge_t *);
extern int send_del_edge(connection_t *, edge_t *);
extern int send_key_changed(connection_t *, node_t *);
extern int send_req_key(connection_t *, node_t *, node_t *);
extern int send_ans_key(connection_t *, node_t *, node_t *);
extern int send_tcppacket(connection_t *, vpn_packet_t *);
extern bool send_id(struct connection_t *);
extern bool send_metakey(struct connection_t *);
extern bool send_challenge(struct connection_t *);
extern bool send_chal_reply(struct connection_t *);
extern bool send_ack(struct connection_t *);
extern bool send_status(struct connection_t *, int, const char *);
extern bool send_error(struct connection_t *, int,const char *);
extern bool send_termreq(struct connection_t *);
extern bool send_ping(struct connection_t *);
extern bool send_pong(struct connection_t *);
extern bool send_add_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_add_edge(struct connection_t *, const struct edge_t *);
extern bool send_del_edge(struct connection_t *, const struct edge_t *);
extern bool send_key_changed(struct connection_t *, const struct node_t *);
extern bool send_req_key(struct connection_t *, const struct node_t *, const struct node_t *);
extern bool send_ans_key(struct connection_t *, const struct node_t *, const struct node_t *);
extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *);
/* Request handlers */
extern int (*request_handlers[])(connection_t *);
extern bool id_h(struct connection_t *);
extern bool metakey_h(struct connection_t *);
extern bool challenge_h(struct connection_t *);
extern bool chal_reply_h(struct connection_t *);
extern bool ack_h(struct connection_t *);
extern bool status_h(struct connection_t *);
extern bool error_h(struct connection_t *);
extern bool termreq_h(struct connection_t *);
extern bool ping_h(struct connection_t *);
extern bool pong_h(struct connection_t *);
extern bool add_subnet_h(struct connection_t *);
extern bool del_subnet_h(struct connection_t *);
extern bool add_edge_h(struct connection_t *);
extern bool del_edge_h(struct connection_t *);
extern bool key_changed_h(struct connection_t *);
extern bool req_key_h(struct connection_t *);
extern bool ans_key_h(struct connection_t *);
extern bool tcppacket_h(struct connection_t *);
extern int id_h(connection_t *);
extern int metakey_h(connection_t *);
extern int challenge_h(connection_t *);
extern int chal_reply_h(connection_t *);
extern int ack_h(connection_t *);
extern int status_h(connection_t *);
extern int error_h(connection_t *);
extern int termreq_h(connection_t *);
extern int ping_h(connection_t *);
extern int pong_h(connection_t *);
/* extern int add_node_h(connection_t *); */
/* extern int del_node_h(connection_t *); */
extern int add_subnet_h(connection_t *);
extern int del_subnet_h(connection_t *);
extern int add_edge_h(connection_t *);
extern int del_edge_h(connection_t *);
extern int key_changed_h(connection_t *);
extern int req_key_h(connection_t *);
extern int ans_key_h(connection_t *);
extern int tcppacket_h(connection_t *);
#endif /* __TINC_PROTOCOL_H__ */
#endif /* __TINC_PROTOCOL_H__ */

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
protocol_edge.c -- handle the meta-protocol, edges
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,282 +17,238 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_edge.c,v 1.3 2002/04/13 11:07:12 zarq Exp $
$Id: protocol_edge.c,v 1.4 2003/08/24 20:38:27 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "logging.h"
#include "system.h"
int send_add_edge(connection_t *c, edge_t *e)
#include "avl_tree.h"
#include "conf.h"
#include "connection.h"
#include "edge.h"
#include "graph.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "protocol.h"
#include "utils.h"
#include "xalloc.h"
bool send_add_edge(connection_t *c, const edge_t *e)
{
int x;
char *from_udpaddress, *from_udpport;
char *to_udpaddress, *to_udpport;
cp
sockaddr2str(&e->from.udpaddress, &from_udpaddress, &from_udpport);
sockaddr2str(&e->to.udpaddress, &to_udpaddress, &to_udpport);
x = send_request(c, "%d %lx %s %s %s %s %s %s %lx %d", ADD_EDGE, random(),
e->from.node->name, from_udpaddress, from_udpport,
e->to.node->name, to_udpaddress, to_udpport,
e->options, e->weight);
free(from_udpaddress);
free(from_udpport);
free(to_udpaddress);
free(to_udpport);
cp
return x;
bool x;
char *address, *port;
cp();
sockaddr2str(&e->address, &address, &port);
x = send_request(c, "%d %lx %s %s %s %s %lx %d", ADD_EDGE, random(),
e->from->name, e->to->name, address, port,
e->options, e->weight);
free(address);
free(port);
return x;
}
int add_edge_h(connection_t *c)
bool add_edge_h(connection_t *c)
{
connection_t *other;
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char from_address[MAX_STRING_SIZE];
char from_udpport[MAX_STRING_SIZE];
char to_address[MAX_STRING_SIZE];
char to_udpport[MAX_STRING_SIZE];
sockaddr_t from_udpaddress;
sockaddr_t to_udpaddress;
long int options;
int weight;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d",
from_name, from_address, from_udpport,
to_name, to_address, to_udpport,
&options, &weight) != 8)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_EDGE", c->name, c->hostname);
return -1;
}
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char to_address[MAX_STRING_SIZE];
char to_port[MAX_STRING_SIZE];
sockaddr_t address;
long int options;
int weight;
/* Check if names are valid */
cp();
if(check_id(from_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d",
from_name, to_name, to_address, to_port, &options, &weight) != 6) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_EDGE", c->name,
c->hostname);
return false;
}
if(check_id(to_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
/* Check if names are valid */
if(seen_request(c->buffer))
return 0;
if(!check_id(from_name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name,
c->hostname, _("invalid name"));
return false;
}
/* Lookup nodes */
if(!check_id(to_name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name,
c->hostname, _("invalid name"));
return false;
}
from = lookup_node(from_name);
if(!from)
{
from = new_node();
from->name = xstrdup(from_name);
node_add(from);
}
if(seen_request(c->buffer))
return true;
to = lookup_node(to_name);
if(!to)
{
to = new_node();
to->name = xstrdup(to_name);
node_add(to);
}
/* Lookup nodes */
/* Convert addresses */
from_udpaddress = str2sockaddr(from_address, from_udpport);
to_udpaddress = str2sockaddr(to_address, to_udpport);
from = lookup_node(from_name);
/* Check if edge already exists */
e = lookup_edge(from, to);
if(e)
{
if(e->weight != weight || e->options != options
|| ((e->from.node == from) && (sockaddrcmp(&e->from.udpaddress, &from_udpaddress)|| sockaddrcmp(&e->to.udpaddress, &to_udpaddress)))
|| ((e->from.node == to) && (sockaddrcmp(&e->from.udpaddress, &to_udpaddress) || sockaddrcmp(&e->to.udpaddress, &from_udpaddress)))
)
{
if(from == myself || to == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
send_add_edge(c, e);
return 0;
}
else
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
edge_del(e);
}
}
else
return 0;
}
else if(from == myself || to == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not exist"), "ADD_EDGE", c->name, c->hostname);
e = new_edge();
e->from.node = from;
e->to.node = to;
send_del_edge(c, e);
free_edge(e);
return 0;
}
if(!from) {
from = new_node();
from->name = xstrdup(from_name);
node_add(from);
}
e = new_edge();
e->from.node = from;
e->from.udpaddress = from_udpaddress;
e->to.node = to;
e->to.udpaddress = to_udpaddress;
e->options = options;
e->weight = weight;
edge_add(e);
to = lookup_node(to_name);
/* Tell the rest about the new edge */
if(!to) {
to = new_node();
to->name = xstrdup(to_name);
node_add(to);
}
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* Convert addresses */
/* Run MST before or after we tell the rest? */
address = str2sockaddr(to_address, to_port);
graph();
cp
return 0;
/* Check if edge already exists */
e = lookup_edge(from, to);
if(e) {
if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address)) {
if(from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not match existing entry"),
"ADD_EDGE", c->name, c->hostname);
send_add_edge(c, e);
return true;
} else {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) which does not match existing entry"),
"ADD_EDGE", c->name, c->hostname);
edge_del(e);
graph();
}
} else
return true;
} else if(from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not exist"),
"ADD_EDGE", c->name, c->hostname);
e = new_edge();
e->from = from;
e->to = to;
send_del_edge(c, e);
free_edge(e);
return true;
}
e = new_edge();
e->from = from;
e->to = to;
e->address = address;
e->options = options;
e->weight = weight;
edge_add(e);
/* Tell the rest about the new edge */
forward_request(c);
/* Run MST before or after we tell the rest? */
graph();
return true;
}
int send_del_edge(connection_t *c, edge_t *e)
bool send_del_edge(connection_t *c, const edge_t *e)
{
cp
return send_request(c, "%d %lx %s %s", DEL_EDGE, random(),
e->from.node->name, e->to.node->name);
cp();
return send_request(c, "%d %lx %s %s", DEL_EDGE, random(),
e->from->name, e->to->name);
}
int del_edge_h(connection_t *c)
bool del_edge_h(connection_t *c)
{
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
connection_t *other;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING"", from_name, to_name) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_EDGE",
c->name, c->hostname);
return -1;
}
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
/* Check if names are valid */
cp();
if(check_id(from_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_EDGE", c->name,
c->hostname);
return false;
}
if(check_id(to_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
/* Check if names are valid */
if(seen_request(c->buffer))
return 0;
if(!check_id(from_name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name,
c->hostname, _("invalid name"));
return false;
}
/* Lookup nodes */
if(!check_id(to_name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name,
c->hostname, _("invalid name"));
return false;
}
from = lookup_node(from_name);
if(!from)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
if(seen_request(c->buffer))
return true;
to = lookup_node(to_name);
if(!to)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
/* Lookup nodes */
/* Check if edge exists */
e = lookup_edge(from, to);
if(!e)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
from = lookup_node(from_name);
if(e->from.node == myself || e->to.node == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_EDGE", c->name, c->hostname);
send_add_edge(c, e); /* Send back a correction */
return 0;
}
if(!from) {
ifdebug(PROTOCOL) logger(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"),
"DEL_EDGE", c->name, c->hostname);
return true;
}
/* Tell the rest about the deleted edge */
to = lookup_node(to_name);
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
if(!to) {
ifdebug(PROTOCOL) logger(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"),
"DEL_EDGE", c->name, c->hostname);
return true;
}
/* Delete the edge */
edge_del(e);
/* Check if edge exists */
/* Run MST before or after we tell the rest? */
e = lookup_edge(from, to);
graph();
cp
return 0;
if(!e) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) which does not appear in the edge tree"),
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(e->from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself"),
"DEL_EDGE", c->name, c->hostname);
send_add_edge(c, e); /* Send back a correction */
return true;
}
/* Tell the rest about the deleted edge */
forward_request(c);
/* Delete the edge */
edge_del(e);
/* Run MST before or after we tell the rest? */
graph();
return true;
}

View file

@ -1,7 +1,7 @@
/*
protocol_key.c -- handle the meta-protocol, key exchange
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,280 +17,243 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_key.c,v 1.3 2002/04/28 12:46:26 zarq Exp $
$Id: protocol_key.c,v 1.4 2003/08/24 20:38:27 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "system.h"
int mykeyused = 0;
#include "avl_tree.h"
#include "connection.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "protocol.h"
#include "utils.h"
#include "xalloc.h"
int send_key_changed(connection_t *c, node_t *n)
bool mykeyused = false;
bool send_key_changed(connection_t *c, const node_t *n)
{
connection_t *other;
avl_node_t *node;
cp
/* Only send this message if some other daemon requested our key previously.
This reduces unnecessary key_changed broadcasts.
*/
cp();
if(n == myself && !mykeyused)
return 0;
/* Only send this message if some other daemon requested our key previously.
This reduces unnecessary key_changed broadcasts.
*/
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%d %lx %s", KEY_CHANGED, random(), n->name);
}
cp
return 0;
if(n == myself && !mykeyused)
return true;
return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name);
}
int key_changed_h(connection_t *c)
bool key_changed_h(connection_t *c)
{
char name[MAX_STRING_SIZE];
avl_node_t *node;
connection_t *other;
node_t *n;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING, name) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
c->name, c->hostname);
return -1;
}
char name[MAX_STRING_SIZE];
node_t *n;
if(seen_request(c->buffer))
return 0;
cp();
n = lookup_node(name);
if(!n)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"), "KEY_CHANGED",
c->name, c->hostname, name);
return -1;
}
n->status.validkey = 0;
n->status.waitingforkey = 0;
n->sent_seqno = 0;
/* Tell the others */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
cp
return 0;
}
int send_req_key(connection_t *c, node_t *from, node_t *to)
{
cp
return send_request(c, "%d %s %s", REQ_KEY,
from->name, to->name);
}
int req_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING, from_name, to_name) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY",
c->name, c->hostname);
return -1;
}
from = lookup_node(from_name);
if(!from)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "REQ_KEY",
c->name, c->hostname, from_name);
return -1;
}
to = lookup_node(to_name);
if(!to)
{
syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "REQ_KEY",
c->name, c->hostname, to_name);
return -1;
}
/* Check if this key request is for us */
if(to == myself) /* Yes, send our own key back */
{
mykeyused = 1;
from->received_seqno = 0;
send_ans_key(c, myself, from);
}
else
{
/* Proxy keys
if(to->status.validkey)
{
send_ans_key(c, to, from);
}
else
*/
send_req_key(to->nexthop->connection, from, to);
}
cp
return 0;
}
int send_ans_key(connection_t *c, node_t *from, node_t *to)
{
char key[MAX_STRING_SIZE];
cp
bin2hex(from->key, key, from->keylength);
key[from->keylength * 2] = '\0';
cp
#ifdef USE_OPENSSL
return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
from->name, to->name, key, from->cipher?from->cipher->nid:0, from->digest?from->digest->type:0, from->maclength, from->compression);
#endif
#ifdef USE_GCRYPT
return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
from->name, to->name, key, from->cipher?-1:0, from->digest?-1:0, from->maclength, from->compression);
#endif
}
int ans_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
node_t *from, *to;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", from_name, to_name, key, &cipher, &digest, &maclength, &compression) != 7)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY",
c->name, c->hostname);
return -1;
}
from = lookup_node(from_name);
if(!from)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "ANS_KEY",
c->name, c->hostname, from_name);
return -1;
}
to = lookup_node(to_name);
if(!to)
{
syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "ANS_KEY",
c->name, c->hostname, to_name);
return -1;
}
/* Forward it if necessary */
if(to != myself)
{
return send_request(to->nexthop->connection, "%s", c->buffer);
}
/* Update our copy of the origin's packet key */
if(from->key)
free(from->key);
from->key = xstrdup(key);
from->keylength = strlen(key) / 2;
hex2bin(from->key, from->key, from->keylength);
from->key[from->keylength] = '\0';
from->status.validkey = 1;
from->status.waitingforkey = 0;
/* Check and lookup cipher and digest algorithms */
if(cipher)
{
#ifdef USE_OPENSSL
from->cipher = EVP_get_cipherbynid(cipher);
if(!from->cipher)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
return -1;
if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
c->name, c->hostname);
return false;
}
if(from->keylength != from->cipher->key_len + from->cipher->iv_len)
{
syslog(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
return -1;
}
#endif
}
else
{
from->cipher = NULL;
}
from->maclength = maclength;
if(seen_request(c->buffer))
return true;
if(digest)
{
#ifdef USE_OPENSSL
from->digest = EVP_get_digestbynid(digest);
if(!from->digest)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
return -1;
n = lookup_node(name);
if(!n) {
logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"),
"KEY_CHANGED", c->name, c->hostname, name);
return false;
}
if(from->maclength > from->digest->md_size || from->maclength < 0)
{
syslog(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
return -1;
}
#endif
}
else
{
from->digest = NULL;
}
from->compression = compression;
flush_queue(from);
cp
return 0;
n->status.validkey = false;
n->status.waitingforkey = false;
/* Tell the others */
forward_request(c);
return true;
}
bool send_req_key(connection_t *c, const node_t *from, const node_t *to)
{
cp();
return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name);
}
bool req_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
cp();
if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY", c->name,
c->hostname);
return false;
}
from = lookup_node(from_name);
if(!from) {
logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
"REQ_KEY", c->name, c->hostname, from_name);
return false;
}
to = lookup_node(to_name);
if(!to) {
logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
"REQ_KEY", c->name, c->hostname, to_name);
return false;
}
/* Check if this key request is for us */
if(to == myself) { /* Yes, send our own key back */
mykeyused = true;
from->received_seqno = 0;
memset(from->late, 0, sizeof(from->late));
send_ans_key(c, myself, from);
} else {
send_req_key(to->nexthop->connection, from, to);
}
return true;
}
bool send_ans_key(connection_t *c, const node_t *from, const node_t *to)
{
char key[MAX_STRING_SIZE];
cp();
bin2hex(from->key, key, from->keylength);
key[from->keylength * 2] = '\0';
return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
from->name, to->name, key,
from->cipher ? from->cipher->nid : 0,
from->digest ? from->digest->type : 0, from->maclength,
from->compression);
}
bool ans_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
node_t *from, *to;
cp();
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
from_name, to_name, key, &cipher, &digest, &maclength,
&compression) != 7) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name,
c->hostname);
return false;
}
from = lookup_node(from_name);
if(!from) {
logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
"ANS_KEY", c->name, c->hostname, from_name);
return false;
}
to = lookup_node(to_name);
if(!to) {
logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
"ANS_KEY", c->name, c->hostname, to_name);
return false;
}
/* Forward it if necessary */
if(to != myself) {
return send_request(to->nexthop->connection, "%s", c->buffer);
}
/* Update our copy of the origin's packet key */
if(from->key)
free(from->key);
from->key = xstrdup(key);
from->keylength = strlen(key) / 2;
hex2bin(from->key, from->key, from->keylength);
from->key[from->keylength] = '\0';
from->status.validkey = true;
from->status.waitingforkey = false;
from->sent_seqno = 0;
/* Check and lookup cipher and digest algorithms */
if(cipher) {
from->cipher = EVP_get_cipherbynid(cipher);
if(!from->cipher) {
logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
from->hostname);
return false;
}
if(from->keylength != from->cipher->key_len + from->cipher->iv_len) {
logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
from->hostname);
return false;
}
} else {
from->cipher = NULL;
}
from->maclength = maclength;
if(digest) {
from->digest = EVP_get_digestbynid(digest);
if(!from->digest) {
logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
from->hostname);
return false;
}
if(from->maclength > from->digest->md_size || from->maclength < 0) {
logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
from->name, from->hostname);
return false;
}
} else {
from->digest = NULL;
}
if(compression < 0 || compression > 11) {
logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
return false;
}
from->compression = compression;
if(from->cipher)
EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, from->key, from->key + from->cipher->key_len);
flush_queue(from);
return true;
}

View file

@ -1,7 +1,7 @@
/*
protocol_misc.c -- handle the meta-protocol, miscellaneous functions
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,182 +17,165 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_misc.c,v 1.3 2002/04/13 11:07:12 zarq Exp $
$Id: protocol_misc.c,v 1.4 2003/08/24 20:38:27 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "logging.h"
#include "system.h"
#include "conf.h"
#include "connection.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "utils.h"
/* Status and error notification routines */
int send_status(connection_t *c, int statusno, char *statusstring)
bool send_status(connection_t *c, int statusno, const char *statusstring)
{
cp
if(!statusstring)
statusstring = status_text[statusno];
cp
return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
cp();
if(!statusstring)
statusstring = "Status";
return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
}
int status_h(connection_t *c)
bool status_h(connection_t *c)
{
int statusno;
char statusstring[MAX_STRING_SIZE];
cp
if(sscanf(c->buffer, "%*d %d "MAX_STRING, &statusno, statusstring) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "STATUS",
c->name, c->hostname);
return -1;
}
int statusno;
char statusstring[MAX_STRING_SIZE];
if(debug_lvl >= DEBUG_STATUS)
{
syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
c->name, c->hostname, status_text[statusno], statusstring);
}
cp();
cp
return 0;
if(sscanf(c->buffer, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "STATUS",
c->name, c->hostname);
return false;
}
ifdebug(STATUS) logger(LOG_NOTICE, _("Status message from %s (%s): %d: %s"),
c->name, c->hostname, statusno, statusstring);
return true;
}
int send_error(connection_t *c, int err, char *errstring)
bool send_error(connection_t *c, int err, const char *errstring)
{
cp
if(!errstring)
errstring = strerror(err);
return send_request(c, "%d %d %s", ERROR, err, errstring);
cp();
if(!errstring)
errstring = "Error";
return send_request(c, "%d %d %s", ERROR, err, errstring);
}
int error_h(connection_t *c)
bool error_h(connection_t *c)
{
int err;
char errorstring[MAX_STRING_SIZE];
cp
if(sscanf(c->buffer, "%*d %d "MAX_STRING, &err, errorstring) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ERROR",
c->name, c->hostname);
return -1;
}
int err;
char errorstring[MAX_STRING_SIZE];
if(debug_lvl >= DEBUG_ERROR)
{
syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
c->name, c->hostname, strerror(err), errorstring);
}
cp();
terminate_connection(c, c->status.active);
cp
return 0;
if(sscanf(c->buffer, "%*d %d " MAX_STRING, &err, errorstring) != 2) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ERROR",
c->name, c->hostname);
return false;
}
ifdebug(ERROR) logger(LOG_NOTICE, _("Error message from %s (%s): %d: %s"),
c->name, c->hostname, err, errorstring);
terminate_connection(c, c->status.active);
return true;
}
int send_termreq(connection_t *c)
bool send_termreq(connection_t *c)
{
cp
return send_request(c, "%d", TERMREQ);
cp();
return send_request(c, "%d", TERMREQ);
}
int termreq_h(connection_t *c)
bool termreq_h(connection_t *c)
{
cp
terminate_connection(c, c->status.active);
cp
return 0;
cp();
terminate_connection(c, c->status.active);
return true;
}
int send_ping(connection_t *c)
bool send_ping(connection_t *c)
{
cp
c->status.pinged = 1;
c->last_ping_time = now;
cp
return send_request(c, "%d", PING);
cp();
c->status.pinged = true;
c->last_ping_time = now;
return send_request(c, "%d", PING);
}
int ping_h(connection_t *c)
bool ping_h(connection_t *c)
{
cp
return send_pong(c);
cp();
return send_pong(c);
}
int send_pong(connection_t *c)
bool send_pong(connection_t *c)
{
cp
return send_request(c, "%d", PONG);
cp();
return send_request(c, "%d", PONG);
}
int pong_h(connection_t *c)
bool pong_h(connection_t *c)
{
cp
c->status.pinged = 0;
cp();
/* Succesful connection, reset timeout if this is an outgoing connection. */
if(c->outgoing)
c->outgoing->timeout = 0;
cp
return 0;
c->status.pinged = false;
/* Succesful connection, reset timeout if this is an outgoing connection. */
if(c->outgoing)
c->outgoing->timeout = 0;
return true;
}
/* Sending and receiving packets via TCP */
int send_tcppacket(connection_t *c, vpn_packet_t *packet)
bool send_tcppacket(connection_t *c, vpn_packet_t *packet)
{
int x;
cp
/* Evil hack. */
cp();
x = send_request(c, "%d %hd", PACKET, packet->len);
/* Evil hack. */
if(x)
return x;
cp
return send_meta(c, packet->data, packet->len);
if(!send_request(c, "%d %hd", PACKET, packet->len))
return false;
return send_meta(c, packet->data, packet->len);
}
int tcppacket_h(connection_t *c)
bool tcppacket_h(connection_t *c)
{
short int len;
cp
if(sscanf(c->buffer, "%*d %hd", &len) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "PACKET", c->name, c->hostname);
return -1;
}
short int len;
/* Set reqlen to len, this will tell receive_meta() that a tcppacket is coming. */
cp();
c->tcplen = len;
cp
return 0;
if(sscanf(c->buffer, "%*d %hd", &len) != 1) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "PACKET", c->name,
c->hostname);
return false;
}
/* Set reqlen to len, this will tell receive_meta() that a tcppacket is coming. */
c->tcplen = len;
return true;
}
/* Status strings */
char (*status_text[]) = {
"Warning",
};
/* Error strings */
char (*error_text[]) = {
"Error",
};

View file

@ -1,7 +1,7 @@
/*
protocol_subnet.c -- handle the meta-protocol, subnets
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,221 +17,203 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: protocol_subnet.c,v 1.3 2002/04/13 11:07:12 zarq Exp $
$Id: protocol_subnet.c,v 1.4 2003/08/24 20:38:27 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "logging.h"
#include "system.h"
int send_add_subnet(connection_t *c, subnet_t *subnet)
#include "conf.h"
#include "connection.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "protocol.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
bool send_add_subnet(connection_t *c, const subnet_t *subnet)
{
int x;
char *netstr;
cp
x = send_request(c, "%d %lx %s %s", ADD_SUBNET, random(),
subnet->owner->name, netstr = net2str(subnet));
free(netstr);
cp
return x;
bool x;
char *netstr;
cp();
x = send_request(c, "%d %lx %s %s", ADD_SUBNET, random(),
subnet->owner->name, netstr = net2str(subnet));
free(netstr);
return x;
}
int add_subnet_h(connection_t *c)
bool add_subnet_h(connection_t *c)
{
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
connection_t *other;
subnet_t *s;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name, c->hostname);
return -1;
}
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
subnet_t *s;
/* Check if owner name is a valid */
cp();
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid name"));
return -1;
}
if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name,
c->hostname);
return false;
}
/* Check if subnet string is valid */
/* Check if owner name is a valid */
if(!(s = str2net(subnetstr)))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid subnet string"));
return -1;
}
if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name,
c->hostname, _("invalid name"));
return false;
}
if(seen_request(c->buffer))
return 0;
/* Check if the owner of the new subnet is in the connection list */
/* Check if subnet string is valid */
owner = lookup_node(name);
s = str2net(subnetstr);
if(!owner)
{
owner = new_node();
owner->name = xstrdup(name);
node_add(owner);
}
if(!s) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name,
c->hostname, _("invalid subnet string"));
return false;
}
/* Check if we already know this subnet */
if(lookup_subnet(owner, s))
{
free_subnet(s);
return 0;
}
if(seen_request(c->buffer))
return true;
/* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
/* Check if the owner of the new subnet is in the connection list */
if(owner == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "ADD_SUBNET", c->name, c->hostname);
s->owner = myself;
send_del_subnet(c, s);
return 0;
}
owner = lookup_node(name);
/* If everything is correct, add the subnet to the list of the owner */
if(!owner) {
owner = new_node();
owner->name = xstrdup(name);
node_add(owner);
}
subnet_add(owner, s);
/* Check if we already know this subnet */
/* Tell the rest */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
cp
return 0;
if(lookup_subnet(owner, s)) {
free_subnet(s);
return true;
}
/* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
if(owner == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself"),
"ADD_SUBNET", c->name, c->hostname);
s->owner = myself;
send_del_subnet(c, s);
return true;
}
/* If everything is correct, add the subnet to the list of the owner */
subnet_add(owner, s);
/* Tell the rest */
forward_request(c);
return true;
}
int send_del_subnet(connection_t *c, subnet_t *s)
bool send_del_subnet(connection_t *c, const subnet_t *s)
{
int x;
char *netstr;
cp
netstr = net2str(s);
x = send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr);
free(netstr);
cp
return x;
bool x;
char *netstr;
cp();
netstr = net2str(s);
x = send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr);
free(netstr);
return x;
}
int del_subnet_h(connection_t *c)
bool del_subnet_h(connection_t *c)
{
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
connection_t *other;
subnet_t *s, *find;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name, c->hostname);
return -1;
}
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
subnet_t *s, *find;
/* Check if owner name is a valid */
cp();
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid name"));
return -1;
}
if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name,
c->hostname);
return false;
}
/* Check if the owner of the new subnet is in the connection list */
/* Check if owner name is a valid */
if(!(owner = lookup_node(name)))
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return 0;
}
if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name,
c->hostname, _("invalid name"));
return false;
}
/* Check if subnet string is valid */
/* Check if the owner of the new subnet is in the connection list */
if(!(s = str2net(subnetstr)))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid subnet string"));
return -1;
}
owner = lookup_node(name);
if(seen_request(c->buffer))
return 0;
if(!owner) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
/* If everything is correct, delete the subnet from the list of the owner */
/* Check if subnet string is valid */
s->owner = owner;
s = str2net(subnetstr);
find = lookup_subnet(owner, s);
free_subnet(s);
if(!s) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name,
c->hostname, _("invalid subnet string"));
return false;
}
if(!find)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which does not appear in his subnet tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return 0;
}
/* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
if(owner == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_SUBNET", c->name, c->hostname);
send_add_subnet(c, find);
return 0;
}
if(seen_request(c->buffer))
return true;
/* Tell the rest */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* If everything is correct, delete the subnet from the list of the owner */
/* Finally, delete it. */
s->owner = owner;
subnet_del(owner, find);
find = lookup_subnet(owner, s);
cp
return 0;
free_subnet(s);
if(!find) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which does not appear in his subnet tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
/* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
if(owner == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself"),
"DEL_SUBNET", c->name, c->hostname);
send_add_subnet(c, find);
return true;
}
/* Tell the rest */
forward_request(c);
/* Finally, delete it. */
subnet_del(owner, find);
return true;
}

154
src/raw_socket/device.c Normal file
View file

@ -0,0 +1,154 @@
/*
device.c -- raw socket
Copyright (C) 2002-2003 Ivo Timmermans <ivo@o2w.nl>,
2002-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.c,v 1.2 2003/08/24 20:38:31 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "logger.h"
#include "system.h"
int device_fd = -1;
char *device;
char *interface;
char ifrname[IFNAMSIZ];
char *device_info;
int device_total_in = 0;
int device_total_out = 0;
bool setup_device(void)
{
struct ifreq ifr;
struct sockaddr_ll sa;
cp();
if(!get_config_string
(lookup_config(config_tree, "Interface"), &interface))
interface = "eth0";
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = interface;
device_info = _("raw socket");
if((device_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
logger(LOG_ERR, _("Could not open %s: %s"), device_info,
strerror(errno));
return false;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) {
close(device_fd);
logger(LOG_ERR, _("Can't find interface %s: %s"), interface,
strerror(errno));
return false;
}
memset(&sa, '0', sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(ETH_P_ALL);
sa.sll_ifindex = ifr.ifr_ifindex;
if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof(sa))) {
logger(LOG_ERR, _("Could not bind to %s: %s"), device, strerror(errno));
return false;
}
logger(LOG_INFO, _("%s is a %s"), device, device_info);
return true;
}
void close_device(void)
{
cp();
close(device_fd);
}
bool read_packet(vpn_packet_t *packet)
{
int lenin;
cp();
if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info,
device, strerror(errno));
return false;
}
packet->len = lenin;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len,
device_info);
return true;
}
bool write_packet(vpn_packet_t *packet)
{
cp();
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(write(device_fd, packet->data, packet->len) < 0) {
logger(LOG_ERR, _("Can't write to %s %s: %s"), device_info, device,
strerror(errno));
return false;
}
device_total_out += packet->len;
return true;
}
void dump_device_stats(void)
{
cp();
logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
logger(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
logger(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
}

View file

@ -1,411 +0,0 @@
/*
read_conf.c -- read the configuration files
Copyright (C) 1998 Robert van der Meulen
1998-2002 Ivo Timmermans <ivo@o2w.nl>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
2000 Cris van Pelt <tribbel@arise.dhs.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: read_conf.c,v 1.1 2002/04/28 12:46:26 zarq Exp $
*/
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <xalloc.h>
#include <utils.h> /* for cp */
#include <avl_tree.h>
#include "conf.h"
#include "netutl.h" /* for str2address */
#include "logging.h"
#include "system.h"
/*
Read exactly one line and strip the trailing newline if any. If the
file was on EOF, return NULL. Otherwise, return all the data in a
dynamically allocated buffer.
If line is non-NULL, it will be used as an initial buffer, to avoid
unnecessary mallocing each time this function is called. If buf is
given, and buf needs to be expanded, the var pointed to by buflen
will be increased.
*/
char *readline(FILE *fp, char **buf, size_t *buflen)
{
char *newline = NULL;
char *p;
char *line; /* The array that contains everything that has been read
so far */
char *idx; /* Read into this pointer, which points to an offset
within line */
size_t size, newsize; /* The size of the current array pointed to by
line */
size_t maxlen; /* Maximum number of characters that may be read with
fgets. This is newsize - oldsize. */
if(feof(fp))
return NULL;
if((buf != NULL) && (buflen != NULL))
{
size = *buflen;
line = *buf;
}
else
{
size = 100;
line = xmalloc(size);
}
maxlen = size;
idx = line;
*idx = 0;
for(;;)
{
errno = 0;
p = fgets(idx, maxlen, fp);
if(p == NULL) /* EOF or error */
{
if(feof(fp))
break;
/* otherwise: error; let the calling function print an error
message if applicable */
free(line);
return NULL;
}
newline = strchr(p, '\n');
if(newline == NULL)
/* We haven't yet read everything to the end of the line */
{
newsize = size << 1;
line = xrealloc(line, newsize);
idx = &line[size - 1];
maxlen = newsize - size + 1;
size = newsize;
}
else
{
*newline = '\0'; /* kill newline */
break; /* yay */
}
}
if((buf != NULL) && (buflen != NULL))
{
*buflen = size;
*buf = line;
}
return line;
}
/*
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
int read_config_file(avl_tree_t *config_tree, const char *fname)
{
int err = -2; /* Parse error */
FILE *fp;
char *buffer, *line;
char *variable, *value;
int lineno = 0, ignore = 0;
config_t *cfg;
size_t bufsize;
cp
if((fp = fopen (fname, "r")) == NULL)
{
syslog(LOG_ERR, _("Cannot open config file %s: %s"), fname, strerror(errno));
return -3;
}
bufsize = 100;
buffer = xmalloc(bufsize);
for(;;)
{
if((line = readline(fp, &buffer, &bufsize)) == NULL)
{
err = -1;
break;
}
if(feof(fp))
{
err = 0;
break;
}
lineno++;
if((variable = strtok(line, "\t =")) == NULL)
continue; /* no tokens on this line */
if(variable[0] == '#')
continue; /* comment: ignore */
if(!strcmp(variable, "-----BEGIN"))
ignore = 1;
if(!ignore)
{
if(((value = strtok(NULL, "\t\n\r =")) == NULL) || value[0] == '#')
{
syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
variable, lineno, fname);
break;
}
cfg = new_config();
cfg->variable = xstrdup(variable);
cfg->value = xstrdup(value);
cfg->file = xstrdup(fname);
cfg->line = lineno;
config_add(config_tree, cfg);
}
if(!strcmp(variable, "-----END"))
ignore = 0;
}
free(buffer);
fclose (fp);
cp
return err;
}
int read_server_config()
{
char *fname;
int x;
cp
asprintf(&fname, "%s/tinc.conf", confbase);
x = read_config_file(config_tree, fname);
if(x == -1) /* System error: complain */
{
syslog(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
}
free(fname);
cp
return x;
}
int isadir(const char* f)
{
struct stat s;
if(stat(f, &s) < 0)
return 0;
else
return S_ISDIR(s.st_mode);
}
int is_safe_path(const char *file)
{
char *p;
const char *f;
char x;
struct stat s;
char l[MAXBUFSIZE];
if(*file != '/')
{
syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
return 0;
}
p = strrchr(file, '/');
if(p == file) /* It's in the root */
p++;
x = *p;
*p = '\0';
f = file;
check1:
if(lstat(f, &s) < 0)
{
syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
return 0;
}
if(s.st_uid != geteuid())
{
syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
f, s.st_uid, geteuid());
return 0;
}
if(S_ISLNK(s.st_mode))
{
syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
f);
if(readlink(f, l, MAXBUFSIZE) < 0)
{
syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
return 0;
}
f = l;
goto check1;
}
*p = x;
f = file;
check2:
if(lstat(f, &s) < 0 && errno != ENOENT)
{
syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
return 0;
}
if(errno == ENOENT)
return 1;
if(s.st_uid != geteuid())
{
syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
f, s.st_uid, geteuid());
return 0;
}
if(S_ISLNK(s.st_mode))
{
syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
f);
if(readlink(f, l, MAXBUFSIZE) < 0)
{
syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
return 0;
}
f = l;
goto check2;
}
if(s.st_mode & 0007)
{
/* Accessible by others */
syslog(LOG_ERR, _("`%s' has unsecure permissions"),
f);
return 0;
}
return 1;
}
FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
{
FILE *r;
char *directory;
char *fn;
/* Check stdin and stdout */
if(!isatty(0) || !isatty(1))
{
/* Argh, they are running us from a script or something. Write
the files to the current directory and let them burn in hell
for ever. */
fn = xstrdup(filename);
}
else
{
/* Ask for a file and/or directory name. */
fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
what, filename);
fflush(stdout);
if((fn = readline(stdin, NULL, NULL)) == NULL)
{
fprintf(stderr, _("Error while reading stdin: %s\n"), strerror(errno));
return NULL;
}
if(strlen(fn) == 0)
/* User just pressed enter. */
fn = xstrdup(filename);
}
if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
{
/* The directory is a relative path or a filename. */
char *p;
directory = get_current_dir_name();
asprintf(&p, "%s/%s", directory, fn);
free(fn);
free(directory);
fn = p;
}
umask(0077); /* Disallow everything for group and other */
/* Open it first to keep the inode busy */
if((r = fopen(fn, mode)) == NULL)
{
fprintf(stderr, _("Error opening file `%s': %s\n"),
fn, strerror(errno));
free(fn);
return NULL;
}
/* Then check the file for nasty attacks */
if(!is_safe_path(fn)) /* Do not permit any directories that are
readable or writeable by other users. */
{
fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
"I will not create or overwrite this file.\n"),
fn);
fclose(r);
free(fn);
return NULL;
}
free(fn);
return r;
}
int read_connection_config(connection_t *c)
{
char *fname;
int x;
cp
asprintf(&fname, "%s/hosts/%s", confbase, c->name);
x = read_config_file(c->config_tree, fname);
free(fname);
cp
return x;
}

View file

@ -1,33 +0,0 @@
/*
conf.h -- header for conf.c
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: read_conf.h,v 1.2 2002/05/02 11:50:07 zarq Exp $
*/
#ifndef __TINC_READ_CONF_H__
#define __TINC_READ_CONF_H__
#include <avl_tree.h>
extern int read_config_file(avl_tree_t *, const char *);
extern int read_server_config(void);
extern FILE *ask_and_safe_open(const char*, const char*, const char *);
extern int is_safe_path(const char *);
#endif /* __TINC_READ_CONF_H__ */

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
route.h -- header file for route.c
Copyright (C) 2000-2002 Ivo Timmermans <zarq@iname.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 2000-2003 Ivo Timmermans <zarq@iname.com>
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,25 +17,30 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: route.h,v 1.2 2002/04/09 15:26:01 zarq Exp $
$Id: route.h,v 1.3 2003/08/24 20:38:28 guus Exp $
*/
#ifndef __TINC_ROUTE_H__
#define __TINC_ROUTE_H__
enum
{
RMODE_HUB = 0,
RMODE_SWITCH,
RMODE_ROUTER,
};
#include "net.h"
#include "node.h"
extern int routing_mode;
extern int priorityinheritance;
typedef enum rmode_t {
RMODE_HUB = 0,
RMODE_SWITCH,
RMODE_ROUTER,
} rmode_t;
extern rmode_t routing_mode;
extern bool overwrite_mac;
extern bool priorityinheritance;
extern int macexpire;
extern void age_mac(void);
extern void route_incoming(node_t *, vpn_packet_t *);
extern void route_outgoing(vpn_packet_t *);
extern mac_t mymac;
#endif /* __TINC_ROUTE_H__ */
extern void age_mac(void);
extern void route_incoming(struct node_t *, struct vpn_packet_t *);
extern void route_outgoing(struct vpn_packet_t *);
#endif /* __TINC_ROUTE_H__ */

413
src/subnet.c Normal file
View file

@ -0,0 +1,413 @@
/*
subnet.c -- handle subnet lookups and lists
Copyright (C) 2000-2003 Guus Sliepen <guus@sliepen.eu.org>,
2000-2003 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: subnet.c,v 1.4 2003/08/24 20:38:28 guus Exp $
*/
#include "system.h"
#include "avl_tree.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
/* lists type of subnet */
avl_tree_t *subnet_tree;
/* Subnet comparison */
static int subnet_compare_mac(const subnet_t *a, const subnet_t *b)
{
int result;
result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b)
{
int result;
result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
if(result)
return result;
result = a->net.ipv4.prefixlength - b->net.ipv4.prefixlength;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b)
{
int result;
result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
if(result)
return result;
result = a->net.ipv6.prefixlength - b->net.ipv6.prefixlength;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
static int subnet_compare(const subnet_t *a, const subnet_t *b)
{
int result;
result = a->type - b->type;
if(result)
return result;
switch (a->type) {
case SUBNET_MAC:
return subnet_compare_mac(a, b);
case SUBNET_IPV4:
return subnet_compare_ipv4(a, b);
case SUBNET_IPV6:
return subnet_compare_ipv6(a, b);
default:
logger(LOG_ERR, _("subnet_compare() was called with unknown subnet type %d, exitting!"),
a->type);
cp_trace();
exit(0);
}
return 0;
}
/* Initialising trees */
void init_subnets(void)
{
cp();
subnet_tree = avl_alloc_tree((avl_compare_t) subnet_compare, (avl_action_t) free_subnet);
}
void exit_subnets(void)
{
cp();
avl_delete_tree(subnet_tree);
}
avl_tree_t *new_subnet_tree(void)
{
cp();
return avl_alloc_tree((avl_compare_t) subnet_compare, NULL);
}
void free_subnet_tree(avl_tree_t *subnet_tree)
{
cp();
avl_delete_tree(subnet_tree);
}
/* Allocating and freeing space for subnets */
subnet_t *new_subnet(void)
{
cp();
return (subnet_t *) xmalloc_and_zero(sizeof(subnet_t));
}
void free_subnet(subnet_t *subnet)
{
cp();
free(subnet);
}
/* Adding and removing subnets */
void subnet_add(node_t *n, subnet_t *subnet)
{
cp();
subnet->owner = n;
avl_insert(subnet_tree, subnet);
avl_insert(n->subnet_tree, subnet);
}
void subnet_del(node_t *n, subnet_t *subnet)
{
cp();
avl_delete(n->subnet_tree, subnet);
avl_delete(subnet_tree, subnet);
}
/* Ascii representation of subnets */
subnet_t *str2net(const char *subnetstr)
{
int i, l;
subnet_t *subnet;
uint16_t x[8];
cp();
subnet = new_subnet();
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d",
&x[0], &x[1], &x[2], &x[3], &l) == 5) {
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = l;
for(i = 0; i < 4; i++)
subnet->net.ipv4.address.x[i] = x[i];
return subnet;
}
if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&l) == 9) {
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = l;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return subnet;
}
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu", &x[0], &x[1], &x[2], &x[3]) == 4) {
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = 32;
for(i = 0; i < 4; i++)
subnet->net.ipv4.address.x[i] = x[i];
return subnet;
}
if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8) {
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = 128;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return subnet;
}
if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6) {
subnet->type = SUBNET_MAC;
for(i = 0; i < 6; i++)
subnet->net.mac.address.x[i] = x[i];
return subnet;
}
free(subnet);
return NULL;
}
char *net2str(const subnet_t *subnet)
{
char *netstr;
cp();
switch (subnet->type) {
case SUBNET_MAC:
asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx",
subnet->net.mac.address.x[0],
subnet->net.mac.address.x[1],
subnet->net.mac.address.x[2],
subnet->net.mac.address.x[3],
subnet->net.mac.address.x[4], subnet->net.mac.address.x[5]);
break;
case SUBNET_IPV4:
asprintf(&netstr, "%hu.%hu.%hu.%hu/%d",
subnet->net.ipv4.address.x[0],
subnet->net.ipv4.address.x[1],
subnet->net.ipv4.address.x[2],
subnet->net.ipv4.address.x[3], subnet->net.ipv4.prefixlength);
break;
case SUBNET_IPV6:
asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
ntohs(subnet->net.ipv6.address.x[0]),
ntohs(subnet->net.ipv6.address.x[1]),
ntohs(subnet->net.ipv6.address.x[2]),
ntohs(subnet->net.ipv6.address.x[3]),
ntohs(subnet->net.ipv6.address.x[4]),
ntohs(subnet->net.ipv6.address.x[5]),
ntohs(subnet->net.ipv6.address.x[6]),
ntohs(subnet->net.ipv6.address.x[7]),
subnet->net.ipv6.prefixlength);
break;
default:
logger(LOG_ERR,
_("net2str() was called with unknown subnet type %d, exiting!"),
subnet->type);
cp_trace();
exit(0);
}
return netstr;
}
/* Subnet lookup routines */
subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet)
{
cp();
return avl_search(owner->subnet_tree, subnet);
}
subnet_t *lookup_subnet_mac(const mac_t *address)
{
subnet_t *p, subnet = {0};
cp();
subnet.type = SUBNET_MAC;
subnet.net.mac.address = *address;
subnet.owner = NULL;
p = (subnet_t *) avl_search(subnet_tree, &subnet);
return p;
}
subnet_t *lookup_subnet_ipv4(const ipv4_t *address)
{
subnet_t *p, subnet = {0};
cp();
subnet.type = SUBNET_IPV4;
subnet.net.ipv4.address = *address;
subnet.net.ipv4.prefixlength = 32;
subnet.owner = NULL;
do {
/* Go find subnet */
p = (subnet_t *) avl_search_closest_smaller(subnet_tree, &subnet);
/* Check if the found subnet REALLY matches */
if(p) {
if(p->type != SUBNET_IPV4) {
p = NULL;
break;
}
if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength, sizeof(ipv4_t)))
break;
else {
/* Otherwise, see if there is a bigger enclosing subnet */
subnet.net.ipv4.prefixlength = p->net.ipv4.prefixlength - 1;
maskcpy(&subnet.net.ipv4.address, &p->net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t));
}
}
} while(p);
return p;
}
subnet_t *lookup_subnet_ipv6(const ipv6_t *address)
{
subnet_t *p, subnet = {0};
cp();
subnet.type = SUBNET_IPV6;
subnet.net.ipv6.address = *address;
subnet.net.ipv6.prefixlength = 128;
subnet.owner = NULL;
do {
/* Go find subnet */
p = (subnet_t *) avl_search_closest_smaller(subnet_tree, &subnet);
/* Check if the found subnet REALLY matches */
if(p) {
if(p->type != SUBNET_IPV6)
return NULL;
if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength, sizeof(ipv6_t)))
break;
else {
/* Otherwise, see if there is a bigger enclosing subnet */
subnet.net.ipv6.prefixlength = p->net.ipv6.prefixlength - 1;
maskcpy(&subnet.net.ipv6.address, &p->net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t));
}
}
} while(p);
return p;
}
void dump_subnets(void)
{
char *netstr;
subnet_t *subnet;
avl_node_t *node;
cp();
logger(LOG_DEBUG, _("Subnet list:"));
for(node = subnet_tree->head; node; node = node->next) {
subnet = (subnet_t *) node->data;
netstr = net2str(subnet);
logger(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name);
free(netstr);
}
logger(LOG_DEBUG, _("End of subnet list."));
}

83
src/subnet.h Normal file
View file

@ -0,0 +1,83 @@
/*
subnet.h -- header for subnet.c
Copyright (C) 2000,2001 Guus Sliepen <guus@sliepen.eu.org>,
2000,2001 Ivo Timmermans <ivo@o2w.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: subnet.h,v 1.4 2003/08/24 20:38:28 guus Exp $
*/
#ifndef __TINC_SUBNET_H__
#define __TINC_SUBNET_H__
#include "net.h"
typedef enum subnet_type_t {
SUBNET_MAC = 0,
SUBNET_IPV4,
SUBNET_IPV6,
SUBNET_TYPES /* Guardian */
} subnet_type_t;
typedef struct subnet_mac_t {
mac_t address;
time_t lastseen;
} subnet_mac_t;
typedef struct subnet_ipv4_t {
ipv4_t address;
int prefixlength;
} subnet_ipv4_t;
typedef struct subnet_ipv6_t {
ipv6_t address;
int prefixlength;
} subnet_ipv6_t;
#include "node.h"
typedef struct subnet_t {
struct node_t *owner; /* the owner of this subnet */
struct node_t *uplink; /* the uplink which we should send packets to for this subnet */
subnet_type_t type; /* subnet type (IPv4? IPv6? MAC? something even weirder?) */
/* And now for the actual subnet: */
union net {
subnet_mac_t mac;
subnet_ipv4_t ipv4;
subnet_ipv6_t ipv6;
} net;
} subnet_t;
extern subnet_t *new_subnet(void) __attribute__ ((__malloc__));
extern void free_subnet(subnet_t *);
extern void init_subnets(void);
extern void exit_subnets(void);
extern avl_tree_t *new_subnet_tree(void) __attribute__ ((__malloc__));
extern void free_subnet_tree(avl_tree_t *);
extern void subnet_add(struct node_t *, subnet_t *);
extern void subnet_del(struct node_t *, subnet_t *);
extern char *net2str(const subnet_t *);
extern subnet_t *str2net(const char *);
extern subnet_t *lookup_subnet(const struct node_t *, const subnet_t *);
extern subnet_t *lookup_subnet_mac(const mac_t *);
extern subnet_t *lookup_subnet_ipv4(const ipv4_t *);
extern subnet_t *lookup_subnet_ipv6(const ipv6_t *);
extern void dump_subnets(void);
#endif /* __TINC_SUBNET_H__ */

View file

@ -1,7 +1,7 @@
/*
tincd.c -- the main file for tincd
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
Copyright (C) 1998-2003 Ivo Timmermans <ivo@o2w.nl>
2000-2003 Guus Sliepen <guus@sliepen.eu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,59 +17,47 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: tincd.c,v 1.16 2002/05/02 11:50:07 zarq Exp $
$Id: tincd.c,v 1.17 2003/08/24 20:38:29 guus Exp $
*/
#include "config.h"
#include "system.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
/* Darwin (MacOS/X) needs the following definition... */
#ifndef _P1003_1B_VISIBLE
#define _P1003_1B_VISIBLE
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef USE_OPENSSL
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#endif
#ifdef USE_GCRYPT
#include <gcrypt.h>
#endif
#include <lzo1x.h>
#include <utils.h>
#include <xalloc.h>
#include <getopt.h>
#include "callbacks.h"
#include "read_conf.h"
#include "conf.h"
#include "device.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "logging.h"
#include "system.h"
#include "utils.h"
#include "xalloc.h"
/* The name this program was run with. */
char *program_name;
char *program_name = NULL;
/* If nonzero, display usage information and exit. */
int show_help;
bool show_help = false;
/* If nonzero, print the version on standard output and exit. */
int show_version;
bool show_version = false;
/* If nonzero, it will attempt to kill a running tincd and exit. */
int kill_tincd = 0;
@ -78,332 +66,445 @@ int kill_tincd = 0;
int generate_keys = 0;
/* If nonzero, use null ciphers and skip all key exchanges. */
int bypass_security = 0;
bool bypass_security = false;
char *identname; /* program name for syslog */
char *pidfilename; /* pid file location */
char **g_argv; /* a copy of the cmdline arguments */
char **environment; /* A pointer to the environment on
startup */
/* If nonzero, disable swapping for this process. */
bool do_mlock = false;
static struct option const long_options[] =
{
{ "config", required_argument, NULL, 'c' },
{ "kill", optional_argument, NULL, 'k' },
{ "net", required_argument, NULL, 'n' },
{ "help", no_argument, &show_help, 1 },
{ "version", no_argument, &show_version, 1 },
{ "no-detach", no_argument, &do_detach, 0 },
{ "generate-keys", optional_argument, NULL, 'K'},
{ "debug", optional_argument, NULL, 'd'},
{ "bypass-security", no_argument, &bypass_security, 1 },
{ NULL, 0, NULL, 0 }
/* If nonzero, write log entries to a separate file. */
bool use_logfile = false;
char *identname = NULL; /* program name for syslog */
char *pidfilename = NULL; /* pid file location */
char *logfilename = NULL; /* log file location */
char **g_argv; /* a copy of the cmdline arguments */
static int status;
static struct option const long_options[] = {
{"config", required_argument, NULL, 'c'},
{"kill", optional_argument, NULL, 'k'},
{"net", required_argument, NULL, 'n'},
{"help", no_argument, NULL, 1},
{"version", no_argument, NULL, 2},
{"no-detach", no_argument, NULL, 'D'},
{"generate-keys", optional_argument, NULL, 'K'},
{"debug", optional_argument, NULL, 'd'},
{"bypass-security", no_argument, NULL, 3},
{"mlock", no_argument, NULL, 'L'},
{"logfile", optional_argument, NULL, 4},
{"pidfile", required_argument, NULL, 5},
{NULL, 0, NULL, 0}
};
static void
usage(int status)
#ifdef HAVE_MINGW
static struct WSAData wsa_state;
#endif
static void usage(bool status)
{
if(status != 0)
fprintf(stderr, _("Try `%s --help\' for more information.\n"), program_name);
else
{
printf(_("Usage: %s [option]...\n\n"), program_name);
printf(_(" -c, --config=DIR Read configuration options from DIR.\n"
" -D, --no-detach Don't fork and detach.\n"
" -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
" -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n"
" -n, --net=NETNAME Connect to net NETNAME.\n"));
printf(_(" -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
" --help Display this help and exit.\n"
" --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@nl.linux.org.\n"));
}
exit(status);
if(status)
fprintf(stderr, _("Try `%s --help\' for more information.\n"),
program_name);
else {
printf(_("Usage: %s [option]...\n\n"), program_name);
printf(_(" -c, --config=DIR Read configuration options from DIR.\n"
" -D, --no-detach Don't fork and detach.\n"
" -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
" -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n"
" -n, --net=NETNAME Connect to net NETNAME.\n"
" -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
" -L, --mlock Lock tinc into main memory.\n"
" --logfile[=FILENAME] Write log entries to a logfile.\n"
" --pidfile=FILENAME Write PID to FILENAME.\n"
" --help Display this help and exit.\n"
" --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@nl.linux.org.\n"));
}
}
void
parse_options(int argc, char **argv, char **envp)
static bool parse_options(int argc, char **argv)
{
int r;
int option_index = 0;
int r;
int option_index = 0;
while((r = getopt_long(argc, argv, "c:Dd::k::n:K::", long_options, &option_index)) != EOF)
{
switch(r)
{
case 0: /* long option */
break;
case 'c': /* config file */
confbase = xmalloc(strlen(optarg)+1);
strcpy(confbase, optarg);
break;
case 'D': /* no detach */
do_detach = 0;
break;
case 'd': /* inc debug level */
if(optarg)
debug_lvl = atoi(optarg);
else
debug_lvl++;
break;
case 'k': /* kill old tincds */
if(optarg)
{
if(!strcasecmp(optarg, "HUP"))
kill_tincd = SIGHUP;
else if(!strcasecmp(optarg, "TERM"))
kill_tincd = SIGTERM;
else if(!strcasecmp(optarg, "KILL"))
kill_tincd = SIGKILL;
else if(!strcasecmp(optarg, "USR1"))
kill_tincd = SIGUSR1;
else if(!strcasecmp(optarg, "USR2"))
kill_tincd = SIGUSR2;
else if(!strcasecmp(optarg, "WINCH"))
kill_tincd = SIGWINCH;
else if(!strcasecmp(optarg, "INT"))
kill_tincd = SIGINT;
else if(!strcasecmp(optarg, "ALRM"))
kill_tincd = SIGALRM;
else
{
kill_tincd = atoi(optarg);
if(!kill_tincd)
{
fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"), optarg);
usage(1);
}
}
}
else
kill_tincd = SIGTERM;
break;
case 'n': /* net name given */
netname = xmalloc(strlen(optarg)+1);
strcpy(netname, optarg);
break;
case 'K': /* generate public/private keypair */
if(optarg)
{
generate_keys = atoi(optarg);
if(generate_keys < 512)
{
fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"),
optarg);
usage(1);
}
generate_keys &= ~7; /* Round it to bytes */
}
else
generate_keys = 1024;
break;
case '?':
usage(1);
default:
break;
}
}
while((r = getopt_long(argc, argv, "c:DLd::k::n:K::", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
case 'c': /* config file */
confbase = xstrdup(optarg);
break;
case 'D': /* no detach */
do_detach = false;
break;
case 'L': /* no detach */
do_mlock = true;
break;
case 'd': /* inc debug level */
if(optarg)
debug_level = atoi(optarg);
else
debug_level++;
break;
case 'k': /* kill old tincds */
#ifndef HAVE_MINGW
if(optarg) {
if(!strcasecmp(optarg, "HUP"))
kill_tincd = SIGHUP;
else if(!strcasecmp(optarg, "TERM"))
kill_tincd = SIGTERM;
else if(!strcasecmp(optarg, "KILL"))
kill_tincd = SIGKILL;
else if(!strcasecmp(optarg, "USR1"))
kill_tincd = SIGUSR1;
else if(!strcasecmp(optarg, "USR2"))
kill_tincd = SIGUSR2;
else if(!strcasecmp(optarg, "WINCH"))
kill_tincd = SIGWINCH;
else if(!strcasecmp(optarg, "INT"))
kill_tincd = SIGINT;
else if(!strcasecmp(optarg, "ALRM"))
kill_tincd = SIGALRM;
else {
kill_tincd = atoi(optarg);
if(!kill_tincd) {
fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"),
optarg);
usage(true);
return false;
}
}
} else
kill_tincd = SIGTERM;
#else
kill_tincd = 1;
#endif
break;
case 'n': /* net name given */
netname = xstrdup(optarg);
break;
case 'K': /* generate public/private keypair */
if(optarg) {
generate_keys = atoi(optarg);
if(generate_keys < 512) {
fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"),
optarg);
usage(true);
return false;
}
generate_keys &= ~7; /* Round it to bytes */
} else
generate_keys = 1024;
break;
case 1: /* show help */
show_help = true;
break;
case 2: /* show version */
show_version = true;
break;
case 3: /* bypass security */
bypass_security = true;
break;
case 4: /* write log entries to a file */
use_logfile = true;
if(optarg)
logfilename = xstrdup(optarg);
break;
case 5: /* write PID to a file */
pidfilename = xstrdup(optarg);
break;
case '?':
usage(true);
return false;
default:
break;
}
}
return true;
}
/* This function prettyprints the key generation process */
void indicator(int a, int b, void *p)
static void indicator(int a, int b, void *p)
{
switch(a)
{
case 0:
fprintf(stderr, ".");
break;
case 1:
fprintf(stderr, "+");
break;
case 2:
fprintf(stderr, "-");
break;
case 3:
switch(b)
{
case 0:
fprintf(stderr, " p\n");
break;
case 1:
fprintf(stderr, " q\n");
break;
default:
fprintf(stderr, "?");
}
break;
default:
fprintf(stderr, "?");
}
switch (a) {
case 0:
fprintf(stderr, ".");
break;
case 1:
fprintf(stderr, "+");
break;
case 2:
fprintf(stderr, "-");
break;
case 3:
switch (b) {
case 0:
fprintf(stderr, " p\n");
break;
case 1:
fprintf(stderr, " q\n");
break;
default:
fprintf(stderr, "?");
}
break;
default:
fprintf(stderr, "?");
}
}
#ifdef USE_OPENSSL
/*
Generate a public/private RSA keypair, and ask for a file to store
them in.
*/
int keygen(int bits)
static bool keygen(int bits)
{
RSA *rsa_key;
FILE *f;
char *name = NULL;
char *filename;
RSA *rsa_key;
FILE *f;
char *name = NULL;
char *filename;
fprintf(stderr, _("Generating %d bits keys:\n"), bits);
rsa_key = RSA_generate_key(bits, 0xFFFF, indicator, NULL);
fprintf(stderr, _("Generating %d bits keys:\n"), bits);
rsa_key = RSA_generate_key(bits, 0xFFFF, indicator, NULL);
if(!rsa_key)
{
fprintf(stderr, _("Error during key generation!\n"));
return -1;
}
else
fprintf(stderr, _("Done.\n"));
if(!rsa_key) {
fprintf(stderr, _("Error during key generation!\n"));
return false;
} else
fprintf(stderr, _("Done.\n"));
get_config_string(lookup_config(config_tree, "Name"), &name);
asprintf(&filename, "%s/rsa_key.priv", confbase);
f = ask_and_open(filename, _("private RSA key"), "a");
if(name)
asprintf(&filename, "%s/hosts/%s", confbase, name);
else
asprintf(&filename, "%s/rsa_key.pub", confbase);
if((f = ask_and_safe_open(filename, _("public RSA key"), "a")) == NULL)
return -1;
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPublicKey(f, rsa_key);
fclose(f);
free(filename);
asprintf(&filename, "%s/rsa_key.priv", confbase);
if((f = ask_and_safe_open(filename, _("private RSA key"), "a")) == NULL)
return -1;
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
fclose(f);
free(filename);
return 0;
}
if(!f)
return false;
#ifdef HAVE_FCHMOD
/* Make it unreadable for others. */
fchmod(fileno(f), 0600);
#endif
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
fclose(f);
free(filename);
get_config_string(lookup_config(config_tree, "Name"), &name);
if(name)
asprintf(&filename, "%s/hosts/%s", confbase, name);
else
asprintf(&filename, "%s/rsa_key.pub", confbase);
f = ask_and_open(filename, _("public RSA key"), "a");
if(!f)
return false;
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPublicKey(f, rsa_key);
fclose(f);
free(filename);
return true;
}
/*
Set all files and paths according to netname
*/
void make_names(void)
static void make_names(void)
{
if(netname)
{
if(!pidfilename)
asprintf(&pidfilename, LOCALSTATEDIR "/run/tinc.%s.pid", netname);
if(!confbase)
asprintf(&confbase, "%s/tinc/%s", CONFDIR, netname);
else
log(0, TLOG_INFO, _("Both netname and configuration directory given, using the latter..."));
if(!identname)
asprintf(&identname, "tinc.%s", netname);
}
else
{
if(!pidfilename)
pidfilename = LOCALSTATEDIR "/run/tinc.pid";
if(!confbase)
asprintf(&confbase, "%s/tinc", CONFDIR);
if(!identname)
identname = "tinc";
}
#ifdef HAVE_MINGW
HKEY key;
char installdir[1024] = "";
long len = sizeof(installdir);
#endif
if(netname)
asprintf(&identname, "tinc.%s", netname);
else
identname = xstrdup("tinc");
#ifdef HAVE_MINGW
if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) {
if(!logfilename)
asprintf(&logfilename, "%s/log/%s.log", identname);
if(!confbase) {
if(netname)
asprintf(&confbase, "%s/%s", installdir, netname);
else
asprintf(&confbase, "%s", installdir);
}
}
RegCloseKey(key);
if(*installdir)
return;
}
#endif
if(!pidfilename)
asprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
if(!logfilename)
asprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
if(netname) {
if(!confbase)
asprintf(&confbase, CONFDIR "/tinc/%s", netname);
else
logger(LOG_INFO, _("Both netname and configuration directory given, using the latter..."));
} else {
if(!confbase)
asprintf(&confbase, CONFDIR "/tinc");
}
}
int
main(int argc, char **argv, char **envp)
int main(int argc, char **argv)
{
program_name = argv[0];
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
environment = envp;
parse_options(argc, argv, envp);
if(!parse_options(argc, argv))
return 1;
make_names();
if(show_version)
{
printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE, VERSION, __DATE__, __TIME__, PROT_CURRENT);
printf(_("Copyright (C) 1998-2002 Ivo Timmermans, Guus Sliepen and others.\n"
"See the AUTHORS file for a complete list.\n\n"
"tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
"and you are welcome to redistribute it under certain conditions;\n"
"see the file COPYING for details.\n"));
if(show_version) {
printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE,
VERSION, __DATE__, __TIME__, PROT_CURRENT);
printf(_("Copyright (C) 1998-2003 Ivo Timmermans, Guus Sliepen and others.\n"
"See the AUTHORS file for a complete list.\n\n"
"tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
"and you are welcome to redistribute it under certain conditions;\n"
"see the file COPYING for details.\n"));
return 0;
}
return 0;
}
if(show_help)
usage(0);
if(show_help) {
usage(false);
return 0;
}
log_add_hook(log_default);
if(kill_tincd)
return !kill_other(kill_tincd);
g_argv = argv;
openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
make_names();
init_configuration(&config_tree);
/* Lock all pages into memory if requested */
/* Slllluuuuuuurrrrp! */
cp
#ifdef USE_OPENSSL
RAND_load_file("/dev/urandom", 1024);
#ifdef HAVE_SSLEAY_ADD_ALL_ALGORITHMS
SSLeay_add_all_algorithms();
if(do_mlock)
#ifdef HAVE_MLOCKALL
if(mlockall(MCL_CURRENT | MCL_FUTURE)) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall",
strerror(errno));
#else
OpenSSL_add_all_algorithms();
{
logger(LOG_ERR, _("mlockall() not supported on this platform!"));
#endif
return -1;
}
cp
if(generate_keys)
{
read_server_config();
exit(keygen(generate_keys));
}
#endif
g_argv = argv;
if(kill_tincd)
exit(kill_other(kill_tincd));
init_configuration(&config_tree);
if(read_server_config())
exit(1);
cp
if(detach())
exit(0);
/* Slllluuuuuuurrrrp! */
init_callbacks();
cp
for(;;)
{
if(!setup_network_connections())
{
main_loop();
cleanup_and_exit(1);
}
RAND_load_file("/dev/urandom", 1024);
log(0, TLOG_ERROR, _("Unrecoverable error"));
cp_trace();
OpenSSL_add_all_algorithms();
if(do_detach)
{
log(0, TLOG_NOTICE, _("Restarting in %d seconds!"), maxtimeout);
sleep(maxtimeout);
}
else
{
log(0, TLOG_ERROR, _("Not restarting."));
exit(1);
}
}
if(generate_keys) {
read_server_config();
return !keygen(generate_keys);
}
if(!read_server_config())
return 1;
if(lzo_init() != LZO_E_OK) {
logger(LOG_ERR, _("Error initializing LZO compressor!"));
return 1;
}
#ifdef HAVE_MINGW
if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "WSAStartup", winerror(GetLastError()));
return 1;
}
if(!do_detach || !init_service())
return main2(argc, argv);
else
return 1;
}
int main2(int argc, char **argv)
{
#endif
if(!detach())
return 1;
/* Setup sockets and open device. If it doesn't work, don't give up but try again. */
while(!setup_network_connections()) {
if(do_detach) {
logger(LOG_NOTICE, _("Restarting in %d seconds!"), maxtimeout);
sleep(maxtimeout);
} else {
logger(LOG_ERR, _("Not restarting."));
return 1;
}
}
/* Start main loop. It only exits when tinc is killed. */
status = main_loop();
/* Shutdown properly. */
close_network_connections();
ifdebug(CONNECTIONS)
dump_device_stats();
logger(LOG_NOTICE, _("Terminating"));
return status;
}