From c5325ffdd1c6749beaf842c272eb28ecd5a070b6 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Fri, 28 Sep 2012 17:05:01 +0200
Subject: [PATCH] Correctly add/remove outgoing connections when reloading
 configuration.

---
 src/net.c        | 46 ++++++++++++++++---------------------
 src/net.h        |  1 +
 src/net_setup.c  |  2 ++
 src/net_socket.c | 59 +++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 76 insertions(+), 32 deletions(-)

diff --git a/src/net.c b/src/net.c
index 11fbcb1..8cf4a76 100644
--- a/src/net.c
+++ b/src/net.c
@@ -40,6 +40,7 @@
 int contradicting_add_edge = 0;
 int contradicting_del_edge = 0;
 static int sleeptime = 10;
+time_t last_config_check = 0;
 
 /* Purge edges and subnets of unreachable nodes. Use carefully. */
 
@@ -263,7 +264,6 @@ int reload_configuration(void) {
 	splay_node_t *node, *next;
 	char *fname;
 	struct stat s;
-	static time_t last_config_check = 0;
 
 	/* Reread our own configuration file */
 
@@ -286,31 +286,6 @@ int reload_configuration(void) {
 
 	setup_myself_reloadable();
 
-	/* Close connections to hosts that have a changed or deleted host config file */
-	
-	for(node = connection_tree->head; node; node = next) {
-		c = node->data;
-		next = node->next;
-
-		if(c->status.control)
-			continue;
-		
-		if(c->outgoing) {
-			free(c->outgoing->name);
-			if(c->outgoing->ai)
-				freeaddrinfo(c->outgoing->ai);
-			free(c->outgoing);
-			c->outgoing = NULL;
-		}
-		
-		xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
-		if(stat(fname, &s) || s.st_mtime > last_config_check)
-			terminate_connection(c, c->status.active);
-		free(fname);
-	}
-
-	last_config_check = time(NULL);
-
 	/* If StrictSubnet is set, expire deleted Subnets and read new ones in */
 
 	if(strictsubnets) {
@@ -387,6 +362,25 @@ int reload_configuration(void) {
 	
 	try_outgoing_connections();
 
+	/* Close connections to hosts that have a changed or deleted host config file */
+
+	for(node = connection_tree->head; node; node = next) {
+		c = node->data;
+		next = node->next;
+
+		if(c->status.control)
+			continue;
+
+		xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
+		if(stat(fname, &s) || s.st_mtime > last_config_check) {
+			fprintf(stderr, "ZOMG %ld > %ld\n", s.st_mtime, last_config_check);
+			terminate_connection(c, c->status.active);
+		}
+		free(fname);
+	}
+
+	last_config_check = time(NULL);
+
 	return 0;
 }
 
diff --git a/src/net.h b/src/net.h
index 667409e..4344f21 100644
--- a/src/net.h
+++ b/src/net.h
@@ -132,6 +132,7 @@ extern bool do_prune;
 extern char *myport;
 extern int contradicting_add_edge;
 extern int contradicting_del_edge;
+extern time_t last_config_check;
 
 extern char *proxyhost;
 extern char *proxyport;
diff --git a/src/net_setup.c b/src/net_setup.c
index ecffed9..ba2ad5f 100644
--- a/src/net_setup.c
+++ b/src/net_setup.c
@@ -916,6 +916,8 @@ static bool setup_myself(void) {
 		return false;
 	}
 
+	last_config_check = time(NULL);
+
 	return true;
 }
 
diff --git a/src/net_socket.c b/src/net_socket.c
index c107351..9bac38a 100644
--- a/src/net_socket.c
+++ b/src/net_socket.c
@@ -576,6 +576,9 @@ void handle_new_meta_connection(int sock, short events, void *data) {
 }
 
 static void free_outgoing(outgoing_t *outgoing) {
+	if(event_initialized(&outgoing->ev))
+		event_del(&outgoing->ev);
+
 	if(outgoing->ai)
 		freeaddrinfo(outgoing->ai);
 
@@ -590,8 +593,19 @@ void try_outgoing_connections(void) {
 	char *name;
 	outgoing_t *outgoing;
 	
-	outgoing_list = list_alloc((list_action_t)free_outgoing);
-			
+	/* If there is no outgoing list yet, create one. Otherwise, mark all outgoings as deleted. */
+
+	if(!outgoing_list) {
+		outgoing_list = list_alloc((list_action_t)free_outgoing);
+	} else {
+		for(list_node_t *i = outgoing_list->head; i; i = i->next) {
+			outgoing = i->data;
+			outgoing->timeout = -1;
+		}
+	}
+
+	/* Make sure there is one outgoing_t in the list for each ConnectTo. */
+
 	for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
 		get_config_string(cfg, &name);
 
@@ -603,9 +617,42 @@ void try_outgoing_connections(void) {
 			continue;
 		}
 
-		outgoing = xmalloc_and_zero(sizeof *outgoing);
-		outgoing->name = name;
-		list_insert_tail(outgoing_list, outgoing);
-		setup_outgoing_connection(outgoing);
+		bool found = false;
+
+		for(list_node_t *i = outgoing_list->head; i; i = i->next) {
+			outgoing = i->data;
+			if(!strcmp(outgoing->name, name)) {
+				found = true;
+				outgoing->timeout = 0;
+				break;
+			}
+		}
+
+		if(!found) {
+			outgoing = xmalloc_and_zero(sizeof *outgoing);
+			outgoing->name = name;
+			list_insert_tail(outgoing_list, outgoing);
+			setup_outgoing_connection(outgoing);
+		}
+	}
+
+	/* Terminate any connections whose outgoing_t is to be deleted. */
+
+	for(splay_node_t *n = connection_tree->head, *next; n; n = next) {
+		next = n->next;
+		connection_t *c = n->data;
+		if(c->outgoing && c->outgoing->timeout == -1) {
+			c->outgoing = NULL;
+			terminate_connection(c, c->status.active);
+		}
+	}
+
+	/* Delete outgoing_ts for which there is no ConnectTo. */
+
+	for(list_node_t *i = outgoing_list->head, *next; i; i = next) {
+		next = i->next;
+		outgoing = i->data;
+		if(outgoing->timeout == -1)
+			list_delete_node(outgoing_list, i);
 	}
 }
-- 
2.25.1