diff --git a/src/server/net.c b/src/server/net.c
index 1de3a9c10a0c5040ff96515c0cd2ed983146a0fc..3e5719e2e15dc1865ad6a705a1e99c32035e9fc0 100644
--- a/src/server/net.c
+++ b/src/server/net.c
@@ -39,6 +39,7 @@
 /* client connection data */
 struct con_data {
 	GSocketConnection *con;
+	GInputStream *istream;
 	gsize nbytes;
 	gboolean priv;
 	gchar *nick;
@@ -58,6 +59,9 @@ struct thread {
 /* tracks client connections */
 static GList *con_list;
 
+static GMutex netlock;
+static GMutex netlock_big;
+static GMutex listlock;
 
 
 /**
@@ -188,6 +192,7 @@ static void drop_con_begin(struct con_data *c)
 
 	gchar *str;
 
+	g_mutex_lock(&listlock);
 
 	str = net_get_host_string(c->con);
 	g_message("Initiating disconnect for %s (%s)", str, c->nick);
@@ -205,6 +210,8 @@ static void drop_con_begin(struct con_data *c)
 	g_object_unref(c->con);
 
 	try_disconnect_socket(c);
+
+	g_mutex_unlock(&listlock);
 }
 
 
@@ -217,8 +224,13 @@ static void drop_con_finalize(struct con_data *c)
 	gchar *buf;
 
 
+	g_mutex_lock(&listlock);
+
+	if (!c->con)
+		goto exit;
+
 	if (G_IS_OBJECT(c->con))
-	    return;
+		goto exit;
 
 	if (c->kick) {
 		buf = g_strdup_printf("I kicked <tt><span foreground='#F1C40F'>"
@@ -234,9 +246,15 @@ static void drop_con_finalize(struct con_data *c)
 	net_server_broadcast_message(buf, NULL);
 	net_push_userlist_cb(NULL);
 
+	g_object_unref(c->istream);
+	g_object_unref(c->ca);
+
 	g_free(c->nick);
 	g_free(buf);
 	g_free(c);
+
+exit:
+	g_mutex_unlock(&listlock);
 }
 
 
@@ -390,6 +408,9 @@ static void net_buffer_ready(GObject *source_object, GAsyncResult *res,
 
 
 pending:
+	if (!G_IS_OBJECT(c->con))
+		return;
+
 	buf = g_buffered_input_stream_peek_buffer(bistream, &nbytes);
 
 	if (nbytes == c->nbytes) {
@@ -415,8 +436,11 @@ pending:
 			  "%ld bytes.", pkt_size,
 			  g_buffered_input_stream_get_buffer_size(bistream));
 
-		if (net_send(buf, nbytes) < 0)
+#if 1 /* careful there, this has abuse potential :) */
+		/* here's your shit back */
+		if (net_send_single(c, buf, nbytes) < 0)
 			goto error;
+#endif
 
 
 		if (pkt_size < MAX_PAYLOAD_SIZE) {
@@ -490,6 +514,8 @@ drop_pkt:
 	cmd_invalid_pkt(PKT_TRANS_ID_UNDEF);
 
 exit:
+	if (!G_IS_OBJECT(c->con))
+		return;
 
 	/* continue buffering */
 	g_buffered_input_stream_fill_async(bistream,
@@ -557,9 +583,9 @@ static void begin_reception(struct con_data *c)
 
 	/* set up as buffered input stream */
 	istream = g_io_stream_get_input_stream(G_IO_STREAM(c->con));
-	istream = g_buffered_input_stream_new(istream);
+	c->istream = g_buffered_input_stream_new(istream);
 
-	bistream = G_BUFFERED_INPUT_STREAM(istream);
+	bistream = G_BUFFERED_INPUT_STREAM(c->istream);
 	bufsize = g_buffered_input_stream_get_buffer_size(bistream);
 
 	g_buffered_input_stream_fill_async(bistream, bufsize,
@@ -616,7 +642,9 @@ static gboolean net_incoming(GSocketService    *service,
 	begin_reception(c);
 
 	/* add to list of connections for outgoing data */
+	g_mutex_lock(&listlock);
 	con_list = g_list_append(con_list, c);
+	g_mutex_unlock(&listlock);
 
 	/* push new username after 1 seconds, so they have time to configure
 	 * theirs
@@ -641,12 +669,20 @@ static gboolean net_incoming(GSocketService    *service,
 
 gint net_send_single(gpointer ref, const char *pkt, gsize nbytes)
 {
+	gint ret;
+
 	struct con_data *c;
 
+
 	c = (struct con_data *) ref;
 
+	g_mutex_lock(&netlock);
+
+	ret = net_send_internal(c, pkt, nbytes);
+
+	g_mutex_unlock(&netlock);
 
-	return net_send_internal(c, pkt, nbytes);
+	return ret;
 }
 
 
@@ -663,28 +699,30 @@ gint net_send(const char *pkt, gsize nbytes)
 	GList *elem;
 
 	struct con_data *c;
+	struct con_data *drop = NULL;
 
 
+	g_mutex_lock(&netlock_big);
 
 	for (elem = con_list; elem; elem = elem->next) {
 
 		c = (struct con_data *) elem->data;
 
-		if (c->kick) {
-
-			/* rewind one element, the current one will be removed
-			 * in drop_con_begin()
-			 */
-			elem = elem->prev;
+		if (!G_IS_OBJECT(c->con))
+			continue;
 
-			drop_con_begin(c);
+		if (c->kick) {
 
+			drop = c;
 			continue;
 		}
 
 		ret |= net_send_single(c, pkt, nbytes);
 	}
 
+	if (drop)
+		drop_con_begin(drop);
+	g_mutex_unlock(&netlock_big);
 
 	return ret;
 }