1diff --git a/configure.in b/configure.in
2index 86f32b8..910b1ec 100644
3--- a/configure.in
4+++ b/configure.in
5@@ -98,6 +98,13 @@ AC_ARG_WITH(libpcap,
6 AC_SUBST(PCAP_CFLAGS)
7 AC_SUBST(PCAPLIB)
8
9+AC_ARG_ENABLE(tcpreasm,
10+[  --enable-tcpreasm     enable tcp reassembly support],
11+,
12+[   enable_tcpreasm="no"]
13+)
14+test "${enable_tcpreasm}" = "yes" && CFLAGS="${CFLAGS} -DENABLE_TCPREASM"
15+
16 dnl Checks for libglib2.8
17 AC_ARG_ENABLE(libglib,
18 [  --disable-libglib	use glib2 for multiprocessing support],
19diff --git a/src/libnids.c b/src/libnids.c
20index 42b5f96..4d0273e 100644
21--- a/src/libnids.c
22+++ b/src/libnids.c
23@@ -47,6 +47,9 @@ static struct proc_node *ip_procs;
24 static struct proc_node *udp_procs;
25
26 struct proc_node *tcp_procs;
27+#ifdef ENABLE_TCPREASM
28+struct proc_node *tcp_resume_procs;
29+#endif
30 static int linktype;
31 static pcap_t *desc = NULL;
32
33@@ -109,6 +112,10 @@ struct nids_prm nids_params = {
34     20000,			/* queue_limit */
35     0,				/* tcp_workarounds */
36     NULL			/* pcap_desc */
37+#ifdef ENABLE_TCPREASM
38+    ,
39+    1				/* tcp_resume_wscale */
40+#endif
41 };
42
43 static int nids_ip_filter(struct ip *x, int len)
44@@ -470,6 +477,9 @@ static void init_procs()
45     ip_procs->item = gen_ip_proc;
46     ip_procs->next = 0;
47     tcp_procs = 0;
48+#ifdef ENABLE_TCPREASM
49+    tcp_resume_procs = 0;
50+#endif
51     udp_procs = 0;
52 }
53
54diff --git a/src/nids.h b/src/nids.h
55index c44f131..b703c9d 100644
56--- a/src/nids.h
57+++ b/src/nids.h
58@@ -48,10 +48,19 @@ enum
59 # define NIDS_RESET 4
60 # define NIDS_TIMED_OUT 5
61 # define NIDS_EXITING   6	/* nids is exiting; last chance to get data */
62+#ifdef ENABLE_TCPREASM
63+# define NIDS_RESUME 7
64+#endif
65
66 # define NIDS_DO_CHKSUM  0
67 # define NIDS_DONT_CHKSUM 1
68
69+#ifdef ENABLE_TCPREASM
70+# define NIDS_TCP_RESUME_NONE   0
71+# define NIDS_TCP_RESUME_CLIENT 1
72+# define NIDS_TCP_RESUME_SERVER 2
73+#endif
74+
75 struct tuple4
76 {
77   u_short source;
78@@ -63,6 +72,10 @@ struct tuple4
79 struct half_stream
80 {
81   char state;
82+#ifdef ENABLE_TCPREASM
83+  char resume_second_half;
84+#endif
85+
86   char collect;
87   char collect_urg;
88
89@@ -131,6 +144,9 @@ struct nids_prm
90   int queue_limit;
91   int tcp_workarounds;
92   pcap_t *pcap_desc;
93+#ifdef ENABLE_TCPREASM
94+  int tcp_resume_wscale;
95+#endif
96 };
97
98 struct tcp_timeout
99@@ -148,6 +164,10 @@ void nids_register_ip (void (*));
100 void nids_unregister_ip (void (*));
101 void nids_register_tcp (void (*));
102 void nids_unregister_tcp (void (*x));
103+#ifdef ENABLE_TCPREASM
104+void nids_register_tcp_resume (void (*));
105+void nids_unregister_tcp_resume (void (*x));
106+#endif
107 void nids_register_udp (void (*));
108 void nids_unregister_udp (void (*));
109 void nids_killtcp (struct tcp_stream *);
110diff --git a/src/tcp.c b/src/tcp.c
111index f0ee031..08360b6 100644
112--- a/src/tcp.c
113+++ b/src/tcp.c
114@@ -48,6 +48,9 @@ enum {
115 #define EXP_SEQ (snd->first_data_seq + rcv->count + rcv->urg_count)
116
117 extern struct proc_node *tcp_procs;
118+#ifdef ENABLE_TCPREASM
119+extern struct proc_node *tcp_resume_procs;
120+#endif
121
122 static struct tcp_stream **tcp_stream_table;
123 static struct tcp_stream *streams_pool;
124@@ -248,7 +251,103 @@ static int get_wscale(struct tcphdr * this_tcphdr, unsigned int * ws)
125   return ret;
126 }
127
128-
129+#ifdef ENABLE_TCPREASM
130+static struct tcp_stream *
131+initiate_tcp_resume(struct tcphdr * this_tcphdr, struct ip * this_iphdr, int direction)
132+{
133+	struct tcp_stream *tolink;
134+	struct tcp_stream *a_tcp;
135+	int hash_index;
136+	struct tuple4 addr;
137+	struct half_stream *half;
138+	struct half_stream *other_half;
139+
140+	switch (direction)
141+	{
142+		case NIDS_TCP_RESUME_CLIENT:
143+			addr.source = ntohs(this_tcphdr->th_sport);
144+			addr.dest = ntohs(this_tcphdr->th_dport);
145+			addr.saddr = this_iphdr->ip_src.s_addr;
146+			addr.daddr = this_iphdr->ip_dst.s_addr;
147+			break;
148+		case NIDS_TCP_RESUME_SERVER:
149+			addr.source = ntohs(this_tcphdr->th_dport);
150+			addr.dest = ntohs(this_tcphdr->th_sport);
151+			addr.saddr = this_iphdr->ip_dst.s_addr;
152+			addr.daddr = this_iphdr->ip_src.s_addr;
153+			break;
154+		default:
155+			return NULL;
156+	}
157+	hash_index = mk_hash_index(addr);
158+
159+	if (tcp_num > max_stream) {
160+		struct lurker_node *i;
161+
162+		tcp_oldest->nids_state = NIDS_TIMED_OUT;
163+		for (i = tcp_oldest->listeners; i; i = i->next)
164+			(i->item) (tcp_oldest, &i->data);
165+		nids_free_tcp_stream(tcp_oldest);
166+		nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_TOOMUCH, ugly_iphdr, this_tcphdr);
167+	}
168+	a_tcp = free_streams;
169+	if (!a_tcp) {
170+		fprintf(stderr, "gdb me ...\n");
171+		pause();
172+	}
173+	free_streams = a_tcp->next_free;
174+
175+	tcp_num++;
176+	tolink = tcp_stream_table[hash_index];
177+	memset(a_tcp, 0, sizeof(struct tcp_stream));
178+	a_tcp->hash_index = hash_index;
179+	a_tcp->addr = addr;
180+	if (direction == NIDS_TCP_RESUME_CLIENT)
181+	{
182+		half = &a_tcp->client;
183+		other_half = &a_tcp->server;
184+	}
185+	else
186+	{
187+		half = &a_tcp->server;
188+		other_half = &a_tcp->client;
189+	}
190+	half->state = TCP_ESTABLISHED;
191+	half->seq = ntohl(this_tcphdr->th_seq) + 1;
192+	half->first_data_seq = half->seq - 1;
193+	half->window = ntohs(this_tcphdr->th_win);
194+	half->ts_on = 0;
195+	half->wscale = nids_params.tcp_resume_wscale;
196+	if (this_tcphdr->th_flags & TH_ACK)
197+		half->ack_seq = ntohl(this_tcphdr->th_ack);
198+
199+#ifdef ENABLE_TCPREASM_DEBUG
200+	DEBUG_REASSEMBLY("new connection: seq = %u, ack_seq = %u\n",
201+					 half->seq, half->ack_seq);
202+#endif
203+
204+	other_half->ack_seq = half->seq;
205+	other_half->state = TCP_ESTABLISHED;
206+	other_half->resume_second_half = 1;
207+	other_half->ts_on = 0;
208+	other_half->window = half->window;
209+	other_half->wscale = nids_params.tcp_resume_wscale;
210+	a_tcp->next_node = tolink;
211+	a_tcp->prev_node = 0;
212+	if (tolink)
213+		tolink->prev_node = a_tcp;
214+	tcp_stream_table[hash_index] = a_tcp;
215+	a_tcp->next_time = tcp_latest;
216+	a_tcp->prev_time = 0;
217+	if (!tcp_oldest)
218+		tcp_oldest = a_tcp;
219+	if (tcp_latest)
220+		tcp_latest->prev_time = a_tcp;
221+	tcp_latest = a_tcp;
222+
223+	return a_tcp;
224+}
225+#endif
226
227
228 static void
229@@ -712,6 +811,9 @@ process_tcp(u_char * data, int skblen)
230   unsigned int tmp_ts;
231   struct tcp_stream *a_tcp;
232   struct half_stream *snd, *rcv;
233+#ifdef ENABLE_TCPREASM
234+  int resumed_tcp = 0;
235+#endif
236
237   ugly_iphdr = this_iphdr;
238   iplen = ntohs(this_iphdr->ip_len);
239@@ -743,6 +845,22 @@ process_tcp(u_char * data, int skblen)
240 		       this_tcphdr);
241     return;
242   }
243+
244+#ifdef ENABLE_TCPREASM_DEBUG
245+  DEBUG_REASSEMBLY("process_tcp starting: packet is %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
246+     (ntohl(this_iphdr->ip_src.s_addr) >> 24) & 0xff,
247+     (ntohl(this_iphdr->ip_src.s_addr) >> 16) & 0xff,
248+     (ntohl(this_iphdr->ip_src.s_addr) >> 8) & 0xff,
249+     (ntohl(this_iphdr->ip_src.s_addr)) & 0xff,
250+     ntohs(this_tcphdr->th_sport),
251+     (ntohl(this_iphdr->ip_dst.s_addr) >> 24) & 0xff,
252+     (ntohl(this_iphdr->ip_dst.s_addr) >> 16) & 0xff,
253+     (ntohl(this_iphdr->ip_dst.s_addr) >> 8) & 0xff,
254+     (ntohl(this_iphdr->ip_dst.s_addr)) & 0xff,
255+     ntohs(this_tcphdr->th_dport)
256+  );
257+#endif
258+
259 #if 0
260   check_flags(this_iphdr, this_tcphdr);
261 //ECN
262@@ -750,10 +868,64 @@ process_tcp(u_char * data, int skblen)
263   if (!(a_tcp = find_stream(this_tcphdr, this_iphdr, &from_client))) {
264     if ((this_tcphdr->th_flags & TH_SYN) &&
265 	!(this_tcphdr->th_flags & TH_ACK) &&
266-	!(this_tcphdr->th_flags & TH_RST))
267+	!(this_tcphdr->th_flags & TH_RST)) {
268       add_new_tcp(this_tcphdr, this_iphdr);
269+      return;
270+    }
271+
272+#ifdef ENABLE_TCPREASM
273+#ifdef ENABLE_TCPREASM_DEBUG
274+    DEBUG_REASSEMBLY("packet is not in stream context: SYN %u, RST %u, ACK %u\n",
275+      this_tcphdr->th_flags & TH_SYN,
276+      this_tcphdr->th_flags & TH_RST,
277+      this_tcphdr->th_flags & TH_ACK
278+    );
279+#endif
280+
281+    /* does this look like a stream we should try to resume?
282+     * the conditions for it are:
283+     * - No SYN (that's the whole point)
284+     * - No RST or FIN (no point in doing that)
285+     * - we have a resume callback to identify direction
286+     */
287+    if ((this_tcphdr->th_flags & TH_SYN) != 0 ||
288+	(this_tcphdr->th_flags & TH_RST) != 0 ||
289+	(this_tcphdr->th_flags & TH_FIN) != 0) {
290+	    return;
291+    }
292+    else {
293+      struct proc_node *i;
294+      for (i = tcp_resume_procs; i; i = i->next) {
295+        int resume;
296+
297+#ifdef ENABLE_TCPREASM_DEBUG
298+	DEBUG_REASSEMBLY("trying to resume stream\n");
299+#endif
300+
301+	i->item(this_tcphdr, this_iphdr, &resume);
302+	from_client = (resume == NIDS_TCP_RESUME_CLIENT);
303+	a_tcp = initiate_tcp_resume(this_tcphdr, this_iphdr, resume);
304+
305+#ifdef ENABLE_TCPREASM_DEBUG
306+        DEBUG_REASSEMBLY("a_tcp = %p, from_client = %u, resume = %u\n",
307+	   a_tcp, from_client, resume);
308+#endif
309+
310+	if (a_tcp) {
311+	  resumed_tcp = 1;
312+	  break;
313+	}
314+      }
315+    }
316+
317+    if (!resumed_tcp)
318+      return;
319+  }
320+#else
321     return;
322   }
323+#endif
324+
325   if (from_client) {
326     snd = &a_tcp->client;
327     rcv = &a_tcp->server;
328@@ -762,6 +934,38 @@ process_tcp(u_char * data, int skblen)
329     rcv = &a_tcp->client;
330     snd = &a_tcp->server;
331   }
332+
333+#ifdef ENABLE_TCPREASM
334+#ifdef ENABLE_TCPREASM_DEBUG
335+  DEBUG_REASSEMBLY("processing packet: seq = %u, ack = %u, snd->seq = %u, rcv->ack_seq = %u\n",
336+      ntohl(this_tcphdr->th_seq),
337+      ntohl(this_tcphdr->th_ack),
338+      snd->seq, rcv->ack_seq);
339+#endif
340+
341+  /* are we the 2nd half of the resume? */
342+  if (snd->resume_second_half) {
343+	snd->seq = ntohl(this_tcphdr->th_seq) + 1;
344+	snd->first_data_seq = snd->seq - 1;
345+	snd->window = ntohs(this_tcphdr->th_win);
346+	snd->resume_second_half = 0;
347+	snd->ack_seq = rcv->seq;
348+
349+#ifdef ENABLE_TCPREASM_DEBUG
350+	DEBUG_REASSEMBLY("second half resumed, seq = %u, first = %u, ack = %u\n",
351+	 snd->seq, snd->first_data_seq, snd->ack_seq);
352+#endif
353+
354+  }
355+
356+  if (resumed_tcp) {
357+	snd->state = TCP_ESTABLISHED;
358+	a_tcp->nids_state = NIDS_RESUME;
359+	goto do_lurkers;
360+  }
361+#endif
362+
363+  /* normal SYN+ACK processing */
364   if ((this_tcphdr->th_flags & TH_SYN)) {
365     if (from_client || a_tcp->client.state != TCP_SYN_SENT ||
366       a_tcp->server.state != TCP_CLOSE || !(this_tcphdr->th_flags & TH_ACK))
367@@ -797,8 +1001,15 @@ process_tcp(u_char * data, int skblen)
368   	( !before(ntohl(this_tcphdr->th_seq), rcv->ack_seq + rcv->window*rcv->wscale) ||
369           before(ntohl(this_tcphdr->th_seq) + datalen, rcv->ack_seq)
370         )
371-     )
372+     ) {
373+#ifdef ENABLE_TCPREASM_DEBUG
374+ 	DEBUG_REASSEMBLY("packet is ignored: "
375+ 	 "datalen=%u, seq=%u, rcv->ack_seq=%u, rcv->window=%u, rcv->wscale=%u\n",
376+ 	 datalen, ntohl(this_tcphdr->th_seq),
377+ 	 rcv->ack_seq, rcv->window, rcv->wscale);
378+#endif
379      return;
380+     }
381
382   if ((this_tcphdr->th_flags & TH_RST)) {
383     if (a_tcp->nids_state == NIDS_DATA) {
384@@ -830,13 +1041,24 @@ process_tcp(u_char * data, int skblen)
385
386 	  a_tcp->server.state = TCP_ESTABLISHED;
387 	  a_tcp->nids_state = NIDS_JUST_EST;
388+#ifdef ENABLE_TCPREASM
389+do_lurkers:
390+#ifdef ENABLE_TCPREASM_DEBUG
391+	  DEBUG_REASSEMBLY("notifying lurkers of new stream\n");
392+#endif
393+#endif
394+
395 	  for (i = tcp_procs; i; i = i->next) {
396 	    char whatto = 0;
397 	    char cc = a_tcp->client.collect;
398 	    char sc = a_tcp->server.collect;
399 	    char ccu = a_tcp->client.collect_urg;
400 	    char scu = a_tcp->server.collect_urg;
401-
402+
403+#ifdef ENABLE_TCPREASM_DEBUG
404+	    DEBUG_REASSEMBLY("  %p, nids_state = %u\n", i, a_tcp->nids_state);
405+#endif
406+
407 	    (i->item) (a_tcp, &data);
408 	    if (cc < a_tcp->client.collect)
409 	      whatto |= COLLECT_cc;
410@@ -866,6 +1088,10 @@ process_tcp(u_char * data, int skblen)
411 	    }
412 	  }
413 	  if (!a_tcp->listeners) {
414+#ifdef ENABLE_TCPREASM_DEBUG
415+	    DEBUG_REASSEMBLY("no listeners, killing stream\n");
416+#endif
417+
418 	    nids_free_tcp_stream(a_tcp);
419 	    return;
420 	  }
421@@ -890,9 +1116,16 @@ process_tcp(u_char * data, int skblen)
422     }
423   }
424   if (datalen + (this_tcphdr->th_flags & TH_FIN) > 0)
425+     {
426+#ifdef ENABLE_TCPREASM_DEBUG
427+	DEBUG_REASSEMBLY("calling tcp_queue, datalen = %u, data = %.*s...\n",
428+	 datalen, datalen > 10 ? 10 : datalen, (char *) (this_tcphdr) + 4 * this_tcphdr->th_off);
429+#endif
430+
431     tcp_queue(a_tcp, this_tcphdr, snd, rcv,
432 	      (char *) (this_tcphdr) + 4 * this_tcphdr->th_off,
433 	      datalen, skblen);
434+	}
435   snd->window = ntohs(this_tcphdr->th_win);
436   if (rcv->rmem_alloc > 65535)
437     prune_queue(rcv, this_tcphdr);
438@@ -919,6 +1152,20 @@ nids_unregister_tcp(void (*x))
439   unregister_callback(&tcp_procs, x);
440 }
441
442+#ifdef ENABLE_TCPREASM
443+void
444+nids_register_tcp_resume(void (*x))
445+{
446+  register_callback(&tcp_resume_procs, x);
447+}
448+
449+void
450+nids_unregister_tcp_resume(void (*x))
451+{
452+  unregister_callback(&tcp_resume_procs, x);
453+}
454+#endif
455+
456 int
457 tcp_init(int size)
458 {
459