diff --git a/src/server/net.c b/src/server/net.c
index c1b09ac9332a346c949e45b16abffb140f4ea8d2..9c0468ee6679d9674af255d9bf72d27a46a09576 100644
--- a/src/server/net.c
+++ b/src/server/net.c
@@ -109,6 +109,7 @@ static gboolean net_push_userlist_cb(gpointer data)
 	gchar *msg = NULL;
 
 
+	g_mutex_lock(&listlock);
 
 	for (elem = con_list; elem; elem = elem->next) {
 
@@ -146,6 +147,8 @@ static gboolean net_push_userlist_cb(gpointer data)
 		g_free(msg);
 	}
 
+	g_mutex_unlock(&listlock);
+
 	return G_SOURCE_REMOVE;
 }
 
@@ -223,6 +226,8 @@ static void drop_con_begin(struct con_data *c)
 
 /**
  * @brief finalize a connection drop
+ *
+ * @note the connection must already be removed from con_list at this point!
  */
 
 static void drop_con_finalize(struct con_data *c)
@@ -230,13 +235,20 @@ static void drop_con_finalize(struct con_data *c)
 	gchar *buf;
 
 
-	g_mutex_lock(&listlock);
+	if (!c) {
+		g_warning("c is NULL");
+		return;
+	}
 
-	if (!c->con)
-		goto exit;
+	if (!c->con) {
+		g_warning("c->con is NULL");
+		return;
+	}
 
-	if (G_IS_OBJECT(c->con))
-		goto exit;
+	if (G_IS_OBJECT(c->con)) {
+		g_warning("c->con still holds references");
+		return;
+	}
 
 	if (c->kick) {
 		buf = g_strdup_printf("I kicked <tt><span foreground='#F1C40F'>"
@@ -263,10 +275,6 @@ static void drop_con_finalize(struct con_data *c)
 
 	g_free(buf);
 	g_free(c);
-
-
-exit:
-	g_mutex_unlock(&listlock);
 }
 
 
@@ -517,6 +525,9 @@ drop_pkt:
 	g_free(pkt);
 
 	ret = g_buffered_input_stream_fill_finish(bistream, res, &error);
+
+	g_object_unref(c->con);
+
 	if (ret < 0)
 		goto error;
 
@@ -531,6 +542,8 @@ exit:
 	if (!G_IS_OBJECT(c->con))
 		return;
 
+	g_object_ref(c->con);
+
 	/* continue buffering */
 	g_buffered_input_stream_fill_async(bistream,
 					   g_buffered_input_stream_get_buffer_size(bistream),
@@ -551,7 +564,10 @@ error:
 	}
 
 	drop_con_begin(c);
-	drop_con_finalize(c);
+
+	/* if this was the last reference, call finalize */
+	if (!G_IS_OBJECT(c->con))
+		drop_con_finalize(c);
 
 	return;
 
@@ -571,6 +587,8 @@ static void assign_default_priv(struct con_data *c)
 	struct con_data *item;
 
 
+	g_mutex_lock(&listlock);
+
 	for (elem = con_list; elem; elem = elem->next) {
 
 		item = (struct con_data *) elem->data;
@@ -581,6 +599,8 @@ static void assign_default_priv(struct con_data *c)
 
 	if (!priv)
 		c->priv = TRUE;
+
+	g_mutex_unlock(&listlock);
 }
 
 
@@ -602,6 +622,8 @@ static void begin_reception(struct con_data *c)
 	bistream = G_BUFFERED_INPUT_STREAM(c->istream);
 	bufsize = g_buffered_input_stream_get_buffer_size(bistream);
 
+	g_object_ref(c->con);
+
 	g_buffered_input_stream_fill_async(bistream, bufsize,
 					   G_PRIORITY_DEFAULT, c->ca,
 					   net_buffer_ready, c);
@@ -718,6 +740,8 @@ gint net_send(const char *pkt, gsize nbytes)
 
 	g_mutex_lock(&netlock_big);
 
+	g_mutex_lock(&listlock);
+
 	for (elem = con_list; elem; elem = elem->next) {
 
 		c = (struct con_data *) elem->data;
@@ -726,7 +750,6 @@ gint net_send(const char *pkt, gsize nbytes)
 			continue;
 
 		if (c->kick) {
-
 			drop = c;
 			continue;
 		}
@@ -734,8 +757,12 @@ gint net_send(const char *pkt, gsize nbytes)
 		ret |= net_send_single(c, pkt, nbytes);
 	}
 
+	g_mutex_unlock(&listlock);
+
+	/* drop one per cycle */
 	if (drop)
 		drop_con_begin(drop);
+
 	g_mutex_unlock(&netlock_big);
 
 	return ret;
@@ -758,10 +785,12 @@ void net_server_reassign_control(gpointer ref)
 
 	c = (struct con_data *) ref;
 
+	g_mutex_lock(&listlock);
 	for (elem = con_list; elem; elem = elem->next) {
 		item = (struct con_data *) elem->data;
 		item->priv = FALSE;
 	}
+	g_mutex_unlock(&listlock);
 
 	c->priv = TRUE;