Index: uspace/app/websrv/websrv.c
===================================================================
--- uspace/app/websrv/websrv.c	(revision 309469de3bd19fbad3fec33c420c02d88b8ba687)
+++ uspace/app/websrv/websrv.c	(revision b99f6e2bfcdcd33a1aa7616e8bf217218ee9059b)
@@ -142,5 +142,5 @@
 		rc = tcp_conn_recv_wait(conn, rbuf, BUFFER_SIZE, &nrecv);
 		if (rc != EOK) {
-			fprintf(stderr, "recv() failed (%d)\n", rc);
+			fprintf(stderr, "tcp_conn_recv() failed (%d)\n", rc);
 			return rc;
 		}
@@ -372,4 +372,11 @@
 		fprintf(stderr, "Error processing request (%s)\n",
 		    str_error(rc));
+		return;
+	}
+
+	rc = tcp_conn_send_fin(conn);
+	if (rc != EOK) {
+		fprintf(stderr, "Error sending FIN.\n");
+		return;
 	}
 }
Index: uspace/lib/c/generic/inet/tcp.c
===================================================================
--- uspace/lib/c/generic/inet/tcp.c	(revision 309469de3bd19fbad3fec33c420c02d88b8ba687)
+++ uspace/lib/c/generic/inet/tcp.c	(revision b99f6e2bfcdcd33a1aa7616e8bf217218ee9059b)
@@ -34,4 +34,5 @@
 
 #include <errno.h>
+#include <fibril.h>
 #include <inet/endpoint.h>
 #include <inet/tcp.h>
@@ -43,4 +44,11 @@
 
 static void tcp_cb_conn(ipc_callid_t, ipc_call_t *, void *);
+static int tcp_conn_fibril(void *);
+
+/** Incoming TCP connection info */
+typedef struct {
+	tcp_listener_t *lst;
+	tcp_conn_t *conn;
+} tcp_in_conn_t;
 
 static int tcp_callback_create(tcp_t *tcp)
@@ -116,12 +124,10 @@
 }
 
-int tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
+static int tcp_conn_new(tcp_t *tcp, sysarg_t id, tcp_cb_t *cb, void *arg,
     tcp_conn_t **rconn)
 {
-	async_exch_t *exch;
 	tcp_conn_t *conn;
-	ipc_call_t answer;
-
-	printf("tcp_conn_create()\n");
+
+	printf("tcp_conn_new()\n");
 
 	conn = calloc(1, sizeof(tcp_conn_t));
@@ -132,4 +138,24 @@
 	fibril_mutex_initialize(&conn->lock);
 	fibril_condvar_initialize(&conn->cv);
+
+	conn->tcp = tcp;
+	conn->id = id;
+	conn->cb = cb;
+	conn->cb_arg = arg;
+
+	list_append(&conn->ltcp, &tcp->conn);
+	*rconn = conn;
+
+	return EOK;
+}
+
+int tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
+    tcp_conn_t **rconn)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+	sysarg_t conn_id;
+
+	printf("tcp_conn_create()\n");
 
 	exch = async_exchange_begin(tcp->sess);
@@ -151,15 +177,12 @@
 		goto error;
 
-	conn->tcp = tcp;
-	conn->id = IPC_GET_ARG1(answer);
-	conn->cb = cb;
-	conn->cb_arg = arg;
-
-	list_append(&conn->ltcp, &tcp->conn);
-	*rconn = conn;
+	conn_id = IPC_GET_ARG1(answer);
+
+	rc = tcp_conn_new(tcp, conn_id, cb, arg, rconn);
+	if (rc != EOK)
+		return rc;
 
 	return EOK;
 error:
-	free(conn);
 	return (int) rc;
 }
@@ -265,4 +288,16 @@
 	free(lst);
 	(void) rc;
+}
+
+static int tcp_listener_get(tcp_t *tcp, sysarg_t id, tcp_listener_t **rlst)
+{
+	list_foreach(tcp->listener, ltcp, tcp_listener_t, lst) {
+		if (lst->id == id) {
+			*rlst = lst;
+			return EOK;
+		}
+	}
+
+	return EINVAL;
 }
 
@@ -552,4 +587,59 @@
 	printf("tcp_ev_urg_data()\n");
 	async_answer_0(iid, ENOTSUP);
+}
+
+static void tcp_ev_new_conn(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
+{
+	tcp_listener_t *lst;
+	tcp_conn_t *conn;
+	sysarg_t lst_id;
+	sysarg_t conn_id;
+	fid_t fid;
+	tcp_in_conn_t *cinfo;
+	int rc;
+
+	printf("tcp_ev_new_conn()\n");
+	lst_id = IPC_GET_ARG1(*icall);
+	conn_id = IPC_GET_ARG2(*icall);
+
+	printf("new conn: lst_id=%zu conn_id=%zu\n", lst_id, conn_id);
+
+	rc = tcp_listener_get(tcp, lst_id, &lst);
+	if (rc != EOK) {
+		printf("listener ID %zu not found\n",
+		    lst_id);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
+	if (rc != EOK) {
+		printf("Failed creating new incoming connection.\n");
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
+		printf("Creating connection fibril\n");
+		cinfo = calloc(1, sizeof(tcp_in_conn_t));
+		if (cinfo == NULL) {
+			printf("Failed creating new incoming connection info.\n");
+			async_answer_0(iid, ENOMEM);
+			return;
+		}
+
+		cinfo->lst = lst;
+		cinfo->conn = conn;
+
+		fid = fibril_create(tcp_conn_fibril, cinfo);
+		if (fid == 0) {
+			printf("Error creating connection fibril.\n");
+			async_answer_0(iid, ENOMEM);
+		}
+
+		fibril_add_ready(fid);
+	}
+
+	async_answer_0(iid, EOK);
 }
 
@@ -589,4 +679,7 @@
 			tcp_ev_urg_data(tcp, callid, &call);
 			break;
+		case TCP_EV_NEW_CONN:
+			tcp_ev_new_conn(tcp, callid, &call);
+			break;
 		default:
 			async_answer_0(callid, ENOTSUP);
@@ -596,4 +689,16 @@
 }
 
+/** Fibril for handling incoming TCP connection in background */
+static int tcp_conn_fibril(void *arg)
+{
+	tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
+
+	printf("tcp_conn_fibril: begin\n");
+	cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
+	printf("tcp_conn_fibril: end\n");
+	tcp_conn_destroy(cinfo->conn);
+
+	return EOK;
+}
 
 /** @}
Index: uspace/lib/c/include/ipc/tcp.h
===================================================================
--- uspace/lib/c/include/ipc/tcp.h	(revision 309469de3bd19fbad3fec33c420c02d88b8ba687)
+++ uspace/lib/c/include/ipc/tcp.h	(revision b99f6e2bfcdcd33a1aa7616e8bf217218ee9059b)
@@ -57,10 +57,7 @@
 	TCP_EV_CONN_RESET,
 	TCP_EV_DATA,
-	TCP_EV_URG_DATA
+	TCP_EV_URG_DATA,
+	TCP_EV_NEW_CONN
 } tcp_event_t;
-
-typedef enum {
-	TCP_LEV_NEW_CONN = IPC_FIRST_USER_METHOD
-} tcp_listen_event_t;
 
 #endif
Index: uspace/srv/net/tcp/service.c
===================================================================
--- uspace/srv/net/tcp/service.c	(revision 309469de3bd19fbad3fec33c420c02d88b8ba687)
+++ uspace/srv/net/tcp/service.c	(revision b99f6e2bfcdcd33a1aa7616e8bf217218ee9059b)
@@ -59,7 +59,11 @@
 static void tcp_ev_conn_failed(tcp_cconn_t *);
 static void tcp_ev_conn_reset(tcp_cconn_t *);
+static void tcp_ev_new_conn(tcp_clst_t *, tcp_cconn_t *);
 
 static void tcp_service_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
 static void tcp_service_recv_data(tcp_conn_t *, void *);
+static void tcp_service_lst_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
+
+static int tcp_cconn_create(tcp_client_t *, tcp_conn_t *, tcp_cconn_t **);
 
 static tcp_cb_t tcp_service_cb = {
@@ -68,4 +72,9 @@
 };
 
+static tcp_cb_t tcp_service_lst_cb = {
+	.cstate_change = tcp_service_lst_cstate_change,
+	.recv_data = NULL
+};
+
 static void tcp_service_cstate_change(tcp_conn_t *conn, void *arg,
     tcp_cstate_t old_state)
@@ -93,4 +102,60 @@
 }
 
+static void tcp_service_lst_cstate_change(tcp_conn_t *conn, void *arg,
+    tcp_cstate_t old_state)
+{
+	tcp_cstate_t nstate;
+	tcp_clst_t *clst;
+	tcp_cconn_t *cconn;
+	int rc;
+	tcp_error_t trc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_service_lst_cstate_change()");
+	nstate = conn->cstate;
+	clst = tcp_uc_get_userptr(conn);
+
+	if ((old_state == st_syn_sent || old_state == st_syn_received) &&
+	    (nstate == st_established)) {
+		/* Connection established */
+		clst->conn = NULL;
+
+		rc = tcp_cconn_create(clst->client, conn, &cconn);
+		if (rc != EOK) {
+			/* XXX Could not create client connection */
+			return;
+		}
+
+		/* XXX Is there a race here (i.e. the connection is already active)? */
+		tcp_uc_set_cb(conn, &tcp_service_cb, cconn);
+
+		/* New incoming connection */
+		tcp_ev_new_conn(clst, cconn);
+	}
+
+	if (old_state != st_closed && nstate == st_closed && conn->reset) {
+		/* Connection reset */
+		/* XXX */
+	}
+
+	/* XXX Failed to establish connection */
+	if (0) {
+		/* XXX */
+	}
+
+	/* Replenish sentinel connection */
+
+	trc = tcp_uc_open(&clst->elocal, NULL, ap_passive, tcp_open_nonblock,
+	    &conn);
+	if (trc != TCP_EOK) {
+		/* XXX Could not replenish connection */
+		return;
+	}
+
+	clst->conn = conn;
+
+	/* XXX Is there a race here (i.e. the connection is already active)? */
+	tcp_uc_set_cb(conn, &tcp_service_lst_cb, clst);
+}
+
 static void tcp_service_recv_data(tcp_conn_t *conn, void *arg)
 {
@@ -150,4 +215,19 @@
 	exch = async_exchange_begin(cconn->client->sess);
 	aid_t req = async_send_1(exch, TCP_EV_CONN_RESET, cconn->id, NULL);
+	async_exchange_end(exch);
+
+	async_forget(req);
+}
+
+/** New incoming connection */
+static void tcp_ev_new_conn(tcp_clst_t *clst, tcp_cconn_t *cconn)
+{
+	async_exch_t *exch;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ev_new_conn()");
+
+	exch = async_exchange_begin(clst->client->sess);
+	aid_t req = async_send_2(exch, TCP_EV_NEW_CONN, clst->id, cconn->id,
+	    NULL);
 	async_exchange_end(exch);
 
@@ -343,5 +423,5 @@
 	local.port = ep->port;
 
-	trc = tcp_uc_open(&local, NULL, ap_passive, 0, &conn);
+	trc = tcp_uc_open(&local, NULL, ap_passive, tcp_open_nonblock, &conn);
 	if (trc != TCP_EOK)
 		return EIO;
@@ -354,6 +434,8 @@
 	}
 
-//	assoc->cb = &udp_cassoc_cb;
-//	assoc->cb_arg = cassoc;
+	clst->elocal = local;
+
+	/* XXX Is there a race here (i.e. the connection is already active)? */
+	tcp_uc_set_cb(conn, &tcp_service_lst_cb, clst);
 
 	*rlst_id = clst->id;
Index: uspace/srv/net/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tcp/tcp_type.h	(revision 309469de3bd19fbad3fec33c420c02d88b8ba687)
+++ uspace/srv/net/tcp/tcp_type.h	(revision b99f6e2bfcdcd33a1aa7616e8bf217218ee9059b)
@@ -338,4 +338,6 @@
 /** TCP client listener */
 typedef struct tcp_clst {
+	/** Local endpoint */
+	tcp_sock_t elocal;
 	/** Connection */
 	tcp_conn_t *conn;
