From 62abf7a6f9ad1e26a6abb82d8caa6a171a879ca3 Mon Sep 17 00:00:00 2001
From: Robert David Graham <robert_david_graham@yahoo.com>
Date: Mon, 18 Jun 2018 17:51:51 -0400
Subject: [PATCH] refactored TCP/IP stack

---
 src/main.c              |   2 +-
 src/proto-banner1.h     |   3 +-
 src/proto-ftp.c         |  15 +-
 src/proto-http.c        |  30 +-
 src/proto-imap4.c       |  61 ++-
 src/proto-interactive.c |   5 +
 src/proto-interactive.h |   7 +
 src/proto-ntlmssp.c     |   2 +-
 src/proto-pop3.c        |  28 +-
 src/proto-smb.c         |  11 +-
 src/proto-smtp.c        |  18 +-
 src/proto-ssh.c         |   4 +-
 src/proto-ssl.c         |   8 +-
 src/proto-tcp-telnet.c  |   4 +-
 src/proto-tcp.c         | 826 ++++++++++++++++++++++++----------------
 src/proto-tcp.h         |   1 -
 src/proto-vnc.c         |  22 +-
 17 files changed, 652 insertions(+), 395 deletions(-)

diff --git a/src/main.c b/src/main.c
index 0b6201a..27dc4cf 100644
--- a/src/main.c
+++ b/src/main.c
@@ -890,7 +890,7 @@ receive_thread(void *v)
                         0, seqno_me, secs, usecs, seqno_them);
                 }
 
-                /* If this contains payload, handle that */
+                /* If this contains payload, handle that second */
                 if (parsed.app_length) {
                     tcpcon_handle(tcpcon, tcb, TCP_WHAT_DATA,
                         px + parsed.app_offset, parsed.app_length,
diff --git a/src/proto-banner1.h b/src/proto-banner1.h
index 73c9710..47cfd32 100644
--- a/src/proto-banner1.h
+++ b/src/proto-banner1.h
@@ -1,7 +1,7 @@
 #ifndef PROTO_BANNER1_H
 #define PROTO_BANNER1_H
 #include <stdint.h>
-#define STATE_DONE 0xFFFFFFFF
+
 #include <stdio.h>
 #include "masscan-app.h"
 #include "proto-banout.h"
@@ -212,7 +212,6 @@ struct ProtocolState {
     unsigned short port;
     unsigned short app_proto;
     unsigned is_sent_sslhello:1;
-    unsigned is_done:1;
     struct BannerBase64 base64;
 
     union {
diff --git a/src/proto-ftp.c b/src/proto-ftp.c
index 65e5b08..94cbb9c 100644
--- a/src/proto-ftp.c
+++ b/src/proto-ftp.c
@@ -41,7 +41,8 @@ ftp_parse(  const struct Banner1 *banner1,
             case 102:
             case 103:
                 if (!isdigit(px[i]&0xFF)) {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                 } else {
                     ftp->code *= 10;
                     ftp->code += (px[i] - '0');
@@ -60,7 +61,8 @@ ftp_parse(  const struct Banner1 *banner1,
                     state++;
                     banout_append_char(banout, PROTO_FTP, px[i]);
                 } else {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                 }
                 break;
             case 5:
@@ -76,7 +78,8 @@ ftp_parse(  const struct Banner1 *banner1,
                         state = 0;
                     }
                 } else if (px[i] == '\0' || !isprint(px[i])) {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                     continue;
                 } else {
                     banout_append_char(banout, PROTO_FTP, px[i]);
@@ -100,10 +103,12 @@ ftp_parse(  const struct Banner1 *banner1,
                         tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0);
                         
                     } else {
-                        state = STATE_DONE;
+                        state = 0xffffffff;
+                        tcp_close(more);
                     }
                 } else if (px[i] == '\0' || !isprint(px[i])) {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                     continue;
                 } else {
                     banout_append_char(banout, PROTO_FTP, px[i]);
diff --git a/src/proto-http.c b/src/proto-http.c
index a058ef4..68395eb 100644
--- a/src/proto-http.c
+++ b/src/proto-http.c
@@ -1,5 +1,6 @@
 #include "proto-http.h"
 #include "proto-banner1.h"
+#include "proto-interactive.h"
 #include "smack.h"
 #include "unusedparm.h"
 #include "string_s.h"
@@ -235,7 +236,9 @@ http_parse(
         FIELD_VALUE,
         CONTENT,
         CONTENT_TAG,
-        CONTENT_FIELD
+        CONTENT_FIELD,
+        
+        DONE_PARSING
     };
 
     UNUSEDPARM(banner1_private);
@@ -248,22 +251,27 @@ http_parse(
     for (i=0; i<length; i++)
     switch (state) {
     case 0: case 1: case 2: case 3: case 4:
-        if (toupper(px[i]) != "HTTP/"[state])
-            state = STATE_DONE;
-        else
+        if (toupper(px[i]) != "HTTP/"[state]) {
+            state = DONE_PARSING;
+            tcp_close(more);
+        } else
             state++;
         break;
     case 5:
         if (px[i] == '.')
             state++;
-        else if (!isdigit(px[i]))
-            state = STATE_DONE;
+        else if (!isdigit(px[i])) {
+            state = DONE_PARSING;
+            tcp_close(more);
+        }
         break;
     case 6:
         if (isspace(px[i]))
             state++;
-        else if (!isdigit(px[i]))
-            state = STATE_DONE;
+        else if (!isdigit(px[i])) {
+            state = DONE_PARSING;
+            tcp_close(more);
+        }
         break;
     case 7:
         /* TODO: look for 1xx response code */
@@ -389,7 +397,7 @@ http_parse(
             banout_append_char(banout, PROTO_HTML_TITLE, px[i]);
         }
         break;
-    case STATE_DONE:
+    case DONE_PARSING:
     default:
         i = (unsigned)length;
         break;
@@ -402,11 +410,11 @@ http_parse(
 
 
 
-    if (state == STATE_DONE)
+    if (state == DONE_PARSING)
         pstate->state = state;
     else
         pstate->state = (state2 & 0xFFFF) << 16
-                | (id & 0xFF) << 8
+                | ((unsigned)id & 0xFF) << 8
                 | (state & 0xFF);
 }
 
diff --git a/src/proto-imap4.c b/src/proto-imap4.c
index 9bce94e..fc0c2e3 100644
--- a/src/proto-imap4.c
+++ b/src/proto-imap4.c
@@ -41,29 +41,37 @@ imap4_parse(  const struct Banner1 *banner1,
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == '*')
                     state++;
-                else
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 1:
                 if (px[i] == ' ') {
                     banout_append_char(banout, PROTO_IMAP4, px[i]);
                     continue;
-                } else
-                    state++;
+                } else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 /* fall through */
             case 2:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == 'O')
                     state++;
-                else
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 3:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == 'K')
                     state++;
-                else
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 4:
                 if (px[i] == ' ') {
@@ -92,46 +100,58 @@ imap4_parse(  const struct Banner1 *banner1,
                     state += 100;
                 else if (px[i] == 'a')
                     state++;
-                else
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 101:
             case 301:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == '0')
                     state++;
-                else 
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 102:
             case 302:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == '0')
                     state++;
-                else 
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 103:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == '1')
                     state++;
-                else 
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 303:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == '2')
                     state++;
-                else 
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 104:
             case 304:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == ' ')
                     state++;
-                else 
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 105:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
@@ -163,6 +183,7 @@ imap4_parse(  const struct Banner1 *banner1,
                 }
                 break;
                 
+            case 0xffffffff:
             default:
                 i = (unsigned)length;
                 break;
diff --git a/src/proto-interactive.c b/src/proto-interactive.c
index ad69683..8a5bb3e 100644
--- a/src/proto-interactive.c
+++ b/src/proto-interactive.c
@@ -12,6 +12,11 @@ tcp_transmit_alloc(struct InteractiveData *more, size_t length)
     return malloc(length);
 }
 
+void
+tcp_close(struct InteractiveData *more)
+{
+    more->is_closing = 1;
+}
 
 /*
  * This doesn't actually transmit right now. Instead, marks the payload as ready
diff --git a/src/proto-interactive.h b/src/proto-interactive.h
index e5b4077..4813f3e 100644
--- a/src/proto-interactive.h
+++ b/src/proto-interactive.h
@@ -6,6 +6,7 @@ struct InteractiveData {
     const void *m_payload;
     unsigned m_length;
     unsigned is_payload_dynamic:1;
+    unsigned is_closing:1;
 };
 enum {
     TCPTRAN_DYNAMIC = 0x0001,
@@ -17,6 +18,12 @@ enum {
 void
 tcp_transmit(struct InteractiveData *more, const void *data, size_t length, unsigned flags);
 
+/**
+ * Called to close the connection
+ */
+void
+tcp_close(struct InteractiveData *more);
+
 /**
  * Called to allocate a TCP buffer.
  */
diff --git a/src/proto-ntlmssp.c b/src/proto-ntlmssp.c
index f1b7fa7..f00a09b 100644
--- a/src/proto-ntlmssp.c
+++ b/src/proto-ntlmssp.c
@@ -180,7 +180,7 @@ ntlmssp_decode(struct NtlmsspDecode *x,
     /* Parse all the fields */
     for (i=info_offset; i+4<info_offset+info_length && i+4<length; ) {
         unsigned type = px[i] | px[i+1]<<8;
-        unsigned len = px[i+2] | px[i+3]<<8;
+        size_t len = px[i+2] | px[i+3]<<8;
         i += 4;
         
         if (len > info_offset + info_length - i)
diff --git a/src/proto-pop3.c b/src/proto-pop3.c
index e6435b9..43fd7aa 100644
--- a/src/proto-pop3.c
+++ b/src/proto-pop3.c
@@ -40,9 +40,10 @@ pop3_parse(  const struct Banner1 *banner1,
         switch (state) {
             case 0: case 1: case 2:
                 banout_append_char(banout, PROTO_POP3, px[i]);
-                if ("+OK"[state] != px[i])
-                    state = STATE_DONE;
-                else
+                if ("+OK"[state] != px[i]) {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                } else
                     state++;
                 break;
             case 3:
@@ -60,7 +61,8 @@ pop3_parse(  const struct Banner1 *banner1,
                 else if (px[i] == '+')
                     state++;
                 else {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                 }
                 break;
             case 5:
@@ -68,16 +70,20 @@ pop3_parse(  const struct Banner1 *banner1,
                 banout_append_char(banout, PROTO_POP3, px[i]);
                 if (px[i] == 'O')
                     state++;
-                else
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 6:
             case 206:
                 banout_append_char(banout, PROTO_POP3, px[i]);
                 if (px[i] == 'K')
                     state += 2; /* oops, I had too many states here */
-                else
-                    state = STATE_DONE;
+                else {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             case 8:
                 if (px[i] == '\r')
@@ -132,8 +138,10 @@ pop3_parse(  const struct Banner1 *banner1,
                 if (px[i] == '\r')
                     continue;
                 banout_append_char(banout, PROTO_POP3, px[i]);
-                if (px[i] == '\n')
-                    state = STATE_DONE;
+                if (px[i] == '\n') {
+                    state = 0xffffffff;
+                    tcp_close(more);
+                }
                 break;
             default:
                 i = (unsigned)length;
diff --git a/src/proto-smb.c b/src/proto-smb.c
index 34bdc9a..7e3edd2 100644
--- a/src/proto-smb.c
+++ b/src/proto-smb.c
@@ -471,8 +471,7 @@ smb1_parse_setup1(struct SMBSTUFF *smb, const unsigned char *px, size_t offset,
         max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset);
     
     for (;offset<max; offset++) {
-        printf("%02x ", px[offset]);
-
+        
         switch (state) {
             case D_PADDING:
                 if (smb->hdr.smb1.flags2 & 0x8000) {
@@ -1492,6 +1491,10 @@ smb_parse_smb(struct SMBSTUFF *smb, const unsigned char *px, size_t max, struct
             if (smb->hdr.smb1.byte_offset >= smb->hdr.smb1.byte_count) {
                 state = SMB1_DATA_AFTER;
                 i--; /* unconsume byte because of auto-increment */
+                
+                /* close the connection, we've found all we can */
+                if (smb->hdr.smb1.command == 0x73)
+                    tcp_close(more);
             }
             break;
             
@@ -1615,6 +1618,10 @@ smb_parse_smb(struct SMBSTUFF *smb, const unsigned char *px, size_t max, struct
             if (smb->hdr.smb2.blob_length == 0) {
                 i--;
                 state = SMB2_PARSE_REMAINDER;
+                
+                /* Close the connection when we get a SessionSetup response */
+                if (smb->hdr.smb2.opcode == 1)
+                    tcp_close(more);
             }
         }
             break;
diff --git a/src/proto-smtp.c b/src/proto-smtp.c
index 6a78eef..132b298 100644
--- a/src/proto-smtp.c
+++ b/src/proto-smtp.c
@@ -66,7 +66,8 @@ smtp_parse(  const struct Banner1 *banner1,
             case 202:
             case 203:
                 if (!isdigit(px[i]&0xFF)) {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                 } else {
                     smtp->code *= 10;
                     smtp->code += (px[i] - '0');
@@ -86,7 +87,8 @@ smtp_parse(  const struct Banner1 *banner1,
                     state++;
                     banout_append_char(banout, PROTO_SMTP, px[i]);
                 } else {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                 }
                 break;
             case 5:
@@ -102,7 +104,8 @@ smtp_parse(  const struct Banner1 *banner1,
                         state = 0;
                     }
                 } else if (px[i] == '\0' || !isprint(px[i])) {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                     continue;
                 } else {
                     banout_append_char(banout, PROTO_SMTP, px[i]);
@@ -121,7 +124,8 @@ smtp_parse(  const struct Banner1 *banner1,
                         state = 100;
                     }
                 } else if (px[i] == '\0' || !isprint(px[i])) {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                     continue;
                 } else {
                     banout_append_char(banout, PROTO_SMTP, px[i]);
@@ -145,10 +149,12 @@ smtp_parse(  const struct Banner1 *banner1,
                         tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0);
                         
                     } else {
-                        state = STATE_DONE;
+                        state = 0xffffffff;
+                        tcp_close(more);
                     }
                 } else if (px[i] == '\0' || !isprint(px[i])) {
-                    state = STATE_DONE;
+                    state = 0xffffffff;
+                    tcp_close(more);
                     continue;
                 } else {
                     banout_append_char(banout, PROTO_SMTP, px[i]);
diff --git a/src/proto-ssh.c b/src/proto-ssh.c
index 79ab491..058367f 100644
--- a/src/proto-ssh.c
+++ b/src/proto-ssh.c
@@ -2,6 +2,7 @@
 #include "proto-banner1.h"
 #include "unusedparm.h"
 #include "masscan-app.h"
+#include "proto-interactive.h"
 #include <ctype.h>
 
 
@@ -28,7 +29,8 @@ ssh_parse(  const struct Banner1 *banner1,
         if (px[i] == '\r')
             continue;
         if (px[i] == '\n' || px[i] == '\0' || !isprint(px[i])) {
-            state = STATE_DONE;
+            state++;
+            tcp_close(more);
             continue;
         }
         banout_append_char(banout, PROTO_SSH2, px[i]);
diff --git a/src/proto-ssl.c b/src/proto-ssl.c
index 7942f2e..0dad60d 100644
--- a/src/proto-ssl.c
+++ b/src/proto-ssl.c
@@ -372,7 +372,8 @@ parse_server_cert(
         void *banner1_private,
         struct ProtocolState *pstate,
         const unsigned char *px, size_t length,
-        struct BannerOutput *banout)
+        struct BannerOutput *banout,
+        struct InteractiveData *more)
 {
     struct SSL_SERVER_CERT *data = &pstate->sub.ssl.x.server_cert;
     unsigned state = data->state;
@@ -464,7 +465,7 @@ parse_server_cert(
                 state = CLEN0;
                 if (remaining == 0) {
                     if (!banner1->is_heartbleed)
-                        pstate->is_done = 1;
+                        tcp_close(more);
                 }
             }
         }
@@ -616,7 +617,8 @@ parse_handshake(
                                       banner1_private,
                                       pstate,
                                       px+i, len,
-                                      banout);
+                                      banout,
+                                      more);
                     break;
             }
 
diff --git a/src/proto-tcp-telnet.c b/src/proto-tcp-telnet.c
index 17bf826..b2d5592 100644
--- a/src/proto-tcp-telnet.c
+++ b/src/proto-tcp-telnet.c
@@ -1,5 +1,6 @@
 #include "proto-tcp-telnet.h"
 #include "proto-banner1.h"
+#include "proto-interactive.h"
 #include "unusedparm.h"
 #include "masscan-app.h"
 #include <ctype.h>
@@ -28,7 +29,8 @@ telnet_parse(  const struct Banner1 *banner1,
         if (px[i] == '\r')
             continue;
         if (px[i] == '\n' || px[i] == '\0' || !isprint(px[i])) {
-            state = STATE_DONE;
+            state++;
+            tcp_close(more);
             continue;
         }
         banout_append_char(banout, PROTO_SSH2, px[i]);
diff --git a/src/proto-tcp.c b/src/proto-tcp.c
index dc911be..84fd412 100644
--- a/src/proto-tcp.c
+++ b/src/proto-tcp.c
@@ -60,6 +60,7 @@ struct TCP_Control_Block
      * it. (Most payloads are static memory) */
     unsigned is_payload_dynamic:1;
 
+    unsigned established;
 
     unsigned short payload_length;
     time_t when_created;
@@ -93,9 +94,15 @@ struct TCP_ConnectionTable {
 
 enum {
     STATE_SYN_SENT,
-    STATE_READY_TO_SEND,
-    STATE_PAYLOAD_SENT,
-    STATE_WAITING_FOR_RESPONSE,
+    //STATE_SYN_RECEIVED,
+    STATE_ESTABLISHED_SEND, /* our own special state, can only send */
+    STATE_ESTABLISHED_RECV, /* our own special state, can only receive */
+    //STATE_CLOSE_WATI,
+    STATE_LAST_ACK,
+    STATE_FIN_WAIT1,
+    STATE_FIN_WAIT2,
+    STATE_CLOSING,
+    STATE_TIME_WAIT,
 };
 
 
@@ -856,6 +863,69 @@ tcp_send_RST(
 }
 
 /***************************************************************************
+ * DEBUG: when printing debug messages (-d option), this prints a string
+ * for the given state.
+ ***************************************************************************/
+static const char *
+state_to_string(int state)
+{
+    static char buf[64];
+    switch (state) {
+            //STATE_SYN_RECEIVED,
+            //STATE_CLOSE_WATI,
+        case STATE_LAST_ACK:        return "LAST-ACK";
+        case STATE_FIN_WAIT1:       return "FIN-WAIT-1";
+        case STATE_FIN_WAIT2:       return "FIN-WAIT-2";
+        case STATE_CLOSING:         return "CLOSING";
+        case STATE_TIME_WAIT:       return "TIME-WAIT";
+        case STATE_SYN_SENT:        return "SYN_SENT";
+        case STATE_ESTABLISHED_SEND:return "ESTABLISHED_SEND";
+        case STATE_ESTABLISHED_RECV:return "ESTABLISHED_RECV";
+            
+        default:
+            sprintf_s(buf, sizeof(buf), "%d", state);
+            return buf;
+    }
+}
+
+/***************************************************************************
+ * DEBUG: when printing debug messages (-d option), this prints a string
+ * for the given state.
+ ***************************************************************************/
+static const char *
+what_to_string(enum TCP_What state)
+{
+    static char buf[64];
+    switch (state) {
+        case TCP_WHAT_TIMEOUT: return "TIMEOUT";
+        case TCP_WHAT_SYNACK: return "SYNACK";
+        case TCP_WHAT_RST: return "RST";
+        case TCP_WHAT_FIN: return "FIN";
+        case TCP_WHAT_ACK: return "ACK";
+        case TCP_WHAT_DATA: return "DATA";
+        default:
+            sprintf_s(buf, sizeof(buf), "%d", state);
+            return buf;
+    }
+}
+
+
+/***************************************************************************
+ ***************************************************************************/
+
+void
+LOGSEND(struct TCP_Control_Block *tcb, const char *what)
+{
+    LOGip(5, tcb->ip_them, tcb->port_them, "=%s : --->> %s                  \n",
+          state_to_string(tcb->tcpstate),
+          what);
+}
+
+
+/***************************************************************************
+ * Sends a fake FIN when we've already closed our connection, on the
+ * assumption this will help the other side close their side more
+ * gracefully. Maybe we shoulid do a RST instead.
  ***************************************************************************/
 void
 tcpcon_send_FIN(
@@ -877,6 +947,7 @@ tcpcon_send_FIN(
     tcb.seqno_them = seqno_them + 1;
     tcb.ackno_them = ackno_them;
 
+    LOGSEND(&tcb, "peer(FIN) fake");
     tcpcon_send_packet(tcpcon, &tcb, 0x11, 0, 0, 0);
 }
 
@@ -886,7 +957,7 @@ tcpcon_send_FIN(
  * examples are SSH banners, FTP banners, or the response from HTTP
  * requests
  ***************************************************************************/
-static unsigned
+static size_t
 parse_banner(
     struct TCP_ConnectionTable *tcpcon,
     struct TCP_Control_Block *tcb,
@@ -903,61 +974,13 @@ parse_banner(
                                     payload_length,
                                     &tcb->banout,
                                     more);
-
-    if (tcb->banner1_state.state == STATE_DONE)
-        return STATE_DONE;
-    else if (tcb->banner1_state.is_done)
-        return STATE_DONE;
-    else
-        return 0;
-}
-
-
-/***************************************************************************
- * DEBUG: when printing debug messages (-d option), this prints a string
- * for the given state.
- ***************************************************************************/
-static const char *
-state_to_string(int state)
-{
-    static char buf[64];
-    switch (state) {
-    case STATE_SYN_SENT: return "STATE_SYN_SENT";
-    case STATE_READY_TO_SEND: return "STATE_READY_TO_SEND";
-    case STATE_PAYLOAD_SENT: return "STATE_PAYLOAD_SENT";
-    case STATE_WAITING_FOR_RESPONSE: return "STATE_WAITING_FOR_RESPONSE";
-
-    default:
-        sprintf_s(buf, sizeof(buf), "%d", state);
-        return buf;
-    }
+    return payload_length;
 }
 
-/***************************************************************************
- * DEBUG: when printing debug messages (-d option), this prints a string
- * for the given state.
- ***************************************************************************/
-static const char *
-what_to_string(int state)
-{
-    static char buf[64];
-    switch (state) {
-    case TCP_WHAT_NOTHING: return "TCP_WHAT_NOTHING";
-    case TCP_WHAT_TIMEOUT: return "TCP_WHAT_TIMEOUT";
-    case TCP_WHAT_SYNACK: return "TCP_WHAT_SYNACK";
-    case TCP_WHAT_RST: return "TCP_WHAT_RST";
-    case TCP_WHAT_FIN: return "TCP_WHAT_FIN";
-    case TCP_WHAT_ACK: return "TCP_WHAT_ACK";
-    case TCP_WHAT_DATA: return "TCP_WHAT_DATA";
-    default:
-        sprintf_s(buf, sizeof(buf), "%d", state);
-        return buf;
-    }
-}
 
 /***************************************************************************
  ***************************************************************************/
-static void
+static int
 handle_ack(
     struct TCP_Control_Block *tcb,
     uint32_t ackno)
@@ -971,7 +994,7 @@ handle_ack(
 
     /* Normal: just discard repeats */
     if (ackno == tcb->ackno_them) {
-        return;
+        return 0;
     }
 
     /* Make sure this isn't a duplicate ACK from past
@@ -982,7 +1005,7 @@ handle_ack(
                 "old ackno = 0x%08x, this ackno = 0x%08x\n",
                 (tcb->ip_them>>24)&0xFF, (tcb->ip_them>>16)&0xFF, (tcb->ip_them>>8)&0xFF, (tcb->ip_them>>0)&0xFF,
                 tcb->ackno_me, ackno);
-        return;
+        return 0;
     }
 
     /* Make sure this isn't invalid ACK from the future
@@ -993,11 +1016,181 @@ handle_ack(
                 "my seqno = 0x%08x, their ackno = 0x%08x\n",
                 (tcb->ip_them>>24)&0xFF, (tcb->ip_them>>16)&0xFF, (tcb->ip_them>>8)&0xFF, (tcb->ip_them>>0)&0xFF,
                 tcb->seqno_me, ackno);
-        return;
+        return 0;
     }
 
     /* now that we've verified this is a good ACK, record this number */
     tcb->ackno_them = ackno;
+    
+    /* Mark that this was a good ack */
+    return 1;
+}
+
+enum AppAction {
+    APP_CONNECTED,
+    APP_RECV_TIMEOUT,
+    APP_RECV_PAYLOAD,
+    APP_SEND_SENT,
+};
+
+
+/***************************************************************************
+ ***************************************************************************/
+static void
+application(struct TCP_ConnectionTable *tcpcon,
+                 struct TCP_Control_Block *tcb,
+                 enum AppAction action, const void *payload, size_t payload_length,
+                 unsigned secs, unsigned usecs)
+{
+    struct Banner1 *banner1 = tcpcon->banner1;
+    
+    enum {
+        App_Connect,
+        App_ReceiveHello,
+        App_ReceiveNext,
+        App_SendNext,
+    };
+    
+    switch (tcb->established) {
+        case App_Connect:
+            /*
+             * Wait 1 second for "server hello" (like SSH), and if that's
+             * not found, then transmit a "client hello"
+             */
+            assert(action == APP_CONNECTED);
+            LOGSEND(tcb, "+timeout");
+            timeouts_add( tcpcon->timeouts,
+                         tcb->timeout,
+                         offsetof(struct TCP_Control_Block, timeout),
+                         TICKS_FROM_TV(secs+tcpcon->timeout_hello,usecs)
+                         );
+            /* Start of connection */
+            tcb->tcpstate = STATE_ESTABLISHED_RECV;
+            tcb->established = App_ReceiveHello;
+            break;
+        case App_ReceiveHello:
+            if (action == APP_RECV_TIMEOUT) {
+                /* if we have a "hello" message to send to the server,
+                 * then send it */
+                if (banner1->tcp_payloads[tcb->port_them]) {
+                    size_t x_len = 0;
+                    const unsigned char *x;
+                    unsigned ctrl = 0;
+                    
+                    x_len = banner1->tcp_payloads[tcb->port_them]->hello_length;
+                    x = banner1->tcp_payloads[tcb->port_them]->hello;
+                    if (banner1->tcp_payloads[tcb->port_them] == &banner_ssl)
+                        tcb->banner1_state.is_sent_sslhello = 1;
+                    
+                    /*
+                     * KLUDGE
+                     */
+                    if (tcpcon->banner1->is_heartbleed) {
+                        ctrl = CTRL_SMALL_WINDOW;
+                    }
+                    
+                    /* Send request. This actually doens't send the packet right
+                     * now, but instead queues up a packet that the transmit
+                     * thread will send soon. */
+                    LOGSEND(tcb, "peer(payload)");
+                    tcpcon_send_packet(tcpcon, tcb,
+                                       0x18,
+                                       x, x_len, ctrl);
+                    LOGip(4, tcb->ip_them, tcb->port_them,
+                          "sending payload %u bytes\n",
+                          x_len);
+                    
+                    /* Increment our sequence number */
+                    tcb->seqno_me += (uint32_t)x_len;
+                    
+                    /* change our state to reflect that we are now waiting for
+                     * acknowledgement of the data we've sent */
+                    tcb->tcpstate = STATE_ESTABLISHED_SEND;
+                }
+                
+                /* Add a timeout so that we can resend the data in case it
+                 * goes missing. Note that we put this back in the timeout
+                 * system regardless if we've sent data. */
+                LOGSEND(tcb, "+timeout");
+                timeouts_add(   tcpcon->timeouts,
+                             tcb->timeout,
+                             offsetof(struct TCP_Control_Block, timeout),
+                             TICKS_FROM_TV(secs+1,usecs)
+                             );
+                break;
+            } else if (action == APP_RECV_PAYLOAD) {
+                tcb->established = App_ReceiveNext;
+                /* fall through */
+            }
+            /* fall through */
+        case App_ReceiveNext:
+            if (action == APP_RECV_PAYLOAD) {
+                struct InteractiveData more = {0};
+                
+                /* [--banners]
+                 * This is an important part of the system, where the TCP
+                 * stack passes incoming packet payloads off to the application
+                 * layer protocol parsers. This is where, in Sockets API, you
+                 * might call the 'recv()' function.
+                 */
+                parse_banner(
+                                   tcpcon,
+                                   tcb,
+                                   payload,
+                                   payload_length,
+                                   &more);
+                
+                /* move their sequence number forward */
+                tcb->seqno_them += (unsigned)payload_length;
+                
+                /* acknowledge the bytes received */
+                if (more.m_length) {
+                    //printf("." "sending more data %u bytes\n", more.length);
+                    LOGSEND(tcb, "peer(ACK)");
+                    LOGSEND(tcb, "peer(payload)");
+                    tcpcon_send_packet(tcpcon, tcb, 0x18, more.m_payload, more.m_length, 0);
+                    tcb->seqno_me += (uint32_t)more.m_length;
+                    tcb->is_payload_dynamic = more.is_payload_dynamic;
+                    tcb->tcpstate = STATE_ESTABLISHED_SEND;
+                    tcb->established = App_SendNext;
+                } else {
+                    LOGSEND(tcb, "peer(ACK)");
+                    tcpcon_send_packet(tcpcon, tcb,
+                                       0x10,
+                                       0, 0, 0);
+                }
+                
+                if (more.is_closing) {
+                    /* Send FIN packet */
+                    LOGSEND(tcb, "peer(FIN)");
+                    tcpcon_send_packet(tcpcon, tcb,
+                                       0x11,
+                                       0, 0, 0);
+                    tcb->seqno_me++;
+                    
+                    tcb->tcpstate = STATE_FIN_WAIT1;
+                    LOGSEND(tcb, "+timeout");
+
+                    timeouts_add(   tcpcon->timeouts,
+                                 tcb->timeout,
+                                 offsetof(struct TCP_Control_Block, timeout),
+                                 TICKS_FROM_TV(secs+1,usecs)
+                                 );
+                    //tcpcon_destroy_tcb(tcpcon, tcb, Reason_StateDone);
+                }
+            }
+            break;
+        case App_SendNext:
+            if (action == APP_SEND_SENT) {
+                tcb->tcpstate = STATE_ESTABLISHED_RECV;
+                tcb->established = App_ReceiveNext;
+            }
+            break;
+        default:
+            LOG(0, "TCP state error\n");
+            exit(1);
+            break;
+    }
 }
 
 
@@ -1012,15 +1205,19 @@ handle_ack(
 void
 tcpcon_handle(struct TCP_ConnectionTable *tcpcon,
               struct TCP_Control_Block *tcb,
-              int what, const void *vpayload, size_t payload_length,
+              int in_what, const void *vpayload, size_t payload_length,
               unsigned secs, unsigned usecs,
               unsigned seqno_them)
 {
+    enum TCP_What what = in_what;
     const unsigned char *payload = (const unsigned char *)vpayload;
-    struct Banner1 *banner1 = tcpcon->banner1;
 
     if (tcb == NULL)
         return;
+    
+    LOGip(5, tcb->ip_them, tcb->port_them, "=%s : %s                  \n",
+          state_to_string(tcb->tcpstate),
+          what_to_string(what));
 
     /* Make sure no connection lasts more than ~30 seconds */
     if (what == TCP_WHAT_TIMEOUT) {
@@ -1028,6 +1225,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon,
             LOGip(8, tcb->ip_them, tcb->port_them,
                 "%s                \n",
                 "CONNECTION TIMEOUT---");
+            LOGSEND(tcb, "peer(RST)");
             tcpcon_send_packet(tcpcon, tcb,
                 0x04,
                 0, 0, 0);
@@ -1035,277 +1233,257 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon,
             return;
         }
     }
+    
+    if (what == TCP_WHAT_RST) {
+        LOGSEND(tcb, "tcb(destroy)");
+        tcpcon_destroy_tcb(tcpcon, tcb, Reason_RST);
+        return;
+    }
+    
+    
+    /*
+     *
+     *
+     *
+     *
+     *
+     *
+     */
+    switch (tcb->tcpstate) {
+            /* TODO: validate any SYNACK is real before sending it here
+             * to the state-machine, by validating that it's acking
+             * something */
+        case STATE_SYN_SENT:
+            switch (what) {
+                case TCP_WHAT_RST:
+                case TCP_WHAT_TIMEOUT:
+                //case TCP_WHAT_SYNACK:
+                case TCP_WHAT_FIN:
+                case TCP_WHAT_ACK:
+                case TCP_WHAT_DATA:
+                    break;
+                case TCP_WHAT_SYNACK:
+                    /* Send "ACK" to acknowlege their "SYN-ACK" */
+                    LOGSEND(tcb, "peer(ACK)");
+                    tcpcon_send_packet(tcpcon, tcb,
+                                       0x10,
+                                       0, 0, 0);
+                    LOGSEND(tcb, "app(connected)");
+                    application(tcpcon, tcb, APP_CONNECTED, 0, 0, secs, usecs);
+                    break;
+                }
+            break;
+        case STATE_ESTABLISHED_SEND:
+        case STATE_ESTABLISHED_RECV:
+            switch (what) {
+                case TCP_WHAT_RST:
+                    break;
+                case TCP_WHAT_SYNACK:
+                    /* Send "ACK" to acknowlege their "SYN-ACK" */
+                    LOGSEND(tcb, "peer(ACK)");
+                    tcpcon_send_packet(tcpcon, tcb,
+                                       0x10, /* ACK */
+                                       0, 0, 0);
+                    break;
+                case TCP_WHAT_FIN:
+                    if (tcb->tcpstate == STATE_ESTABLISHED_RECV) {
+                        tcb->seqno_them = seqno_them + 1;
+                        
+                        LOGSEND(tcb, "peer(FIN)");
+                        tcpcon_send_packet(tcpcon, tcb,
+                                           0x11, /* FIN-ACK */
+                                           0, 0, 0);
+                        tcb->seqno_me++;
+                        tcb->tcpstate = STATE_LAST_ACK;
+                    } else if (tcb->tcpstate == STATE_ESTABLISHED_RECV) {
+                        /* Do nothing, the same thing as if we received data
+                         * during the SENd state. The other side will send it
+                         * again after it has acknolwedged our data */
+                        ;
+                    }
+                    break;
+                case TCP_WHAT_ACK:
+                    /* There's actually nothing that goes on in this state. We are
+                     * just waiting for the timer to expire. In the meanwhile,
+                     * though, the other side is might acknowledge that we sent
+                     * a SYN-ACK */
+                     
+                    /* NOTE: the arg 'payload_length' was overloaded here to be the
+                     * 'ackno' instead */
+                    handle_ack( tcb, (uint32_t)payload_length);
+                    if (tcb->tcpstate == STATE_ESTABLISHED_SEND) {
+                        if (tcb->ackno_them - tcb->seqno_me == 0) {
+                            /* All the payload has been sent */
+                            if (tcb->is_payload_dynamic)
+                                free((void*)tcb->payload);
+                            tcb->payload = 0;
+                            tcb->payload_length = 0;
+                            tcb->is_payload_dynamic = 0;
+                            
+                            LOGSEND(tcb, "app(sent)");
+                            application(tcpcon, tcb, APP_SEND_SENT, 0, 0, secs, usecs);
+                            tcb->tcpstate = STATE_ESTABLISHED_RECV;
+                            LOGSEND(tcb, "+timeout");
+                            timeouts_add(   tcpcon->timeouts,
+                                         tcb->timeout,
+                                         offsetof(struct TCP_Control_Block, timeout),
+                                         TICKS_FROM_TV(secs+10,usecs)
+                                         );
+                        } else {
+                            /* Reset the timeout, waiting for more data to arrive */
+                            LOGSEND(tcb, "+timeout");
+                            timeouts_add(   tcpcon->timeouts,
+                                         tcb->timeout,
+                                         offsetof(struct TCP_Control_Block, timeout),
+                                         TICKS_FROM_TV(secs+1,usecs)
+                                         );
+                            
+                        }
+                    }
+                    break;
+                case TCP_WHAT_TIMEOUT:
+                    if (tcb->tcpstate == STATE_ESTABLISHED_RECV) {
+                        /* Didn't receive data in the expected timeframe. This is
+                         * often a normal condition, such as during the start
+                         * of a scanned connection, when we don't understand the
+                         * protocol and are simply waiting for anything the
+                         * server might send us.
+                         */
+                        LOGSEND(tcb, "app(timeout)");
+                        application(tcpcon, tcb, APP_RECV_TIMEOUT, 0, 0, secs, usecs);
+                    } else if (tcb->tcpstate == STATE_ESTABLISHED_SEND) {
+                        /*
+                         * We did not get a complete ACK of our sent data, so retransmit
+                         * it to the server
+                         */
+                        uint32_t len;
+                        
+                        
+                        len = tcb->seqno_me - tcb->ackno_them;
+                        
+                        /* Resend the payload */
+                        tcb->seqno_me -= len;
+                        LOGSEND(tcb, "peer(payload) retransmit");
+                        tcpcon_send_packet(tcpcon, tcb,
+                                           0x18,
+                                           tcb->payload + tcb->payload_length - len,
+                                           len, 0);
+                        tcb->seqno_me += len;
+                        
+                        
+                        /* Now that we've resent the packet, register another
+                         * timeout in order to resend it yet again if not
+                         * acknolwedgeld. */
+                        LOGSEND(tcb, "+timeout");
+                        timeouts_add(tcpcon->timeouts,
+                                     tcb->timeout,
+                                     offsetof(struct TCP_Control_Block, timeout),
+                                     TICKS_FROM_TV(secs+2,usecs)
+                                     );
+                    }
+        
+                    break;
+                case TCP_WHAT_DATA:
+                    
+                    if ((unsigned)(tcb->seqno_them - seqno_them) > payload_length)  {
+                        LOGSEND(tcb, "peer(ACK)");
+                        tcpcon_send_packet(tcpcon, tcb,
+                                           0x10,
+                                           0, 0, 0);
+                        return;
+                    }
+                    
+                    while (seqno_them != tcb->seqno_them && payload_length) {
+                        seqno_them++;
+                        payload_length--;
+                        payload++;
+                    }
+                    
+                    if (payload_length == 0) {
+                        LOGSEND(tcb, "peer(ACK)");
+                        tcpcon_send_packet(tcpcon, tcb,
+                                           0x10,
+                                           0, 0, 0);
+                        return;
+                    }
+                    
+                    LOGSEND(tcb, "app(payload)");
+                    application(tcpcon, tcb, APP_RECV_PAYLOAD, payload, payload_length, secs, usecs);
+                    break;
 
-    LOGip(8, tcb->ip_them, tcb->port_them, "=%s : %s                  \n",
-            state_to_string(tcb->tcpstate),
-            what_to_string(what));
-
-    switch (tcb->tcpstate<<8 | what) {
-    case STATE_SYN_SENT<<8 | TCP_WHAT_SYNACK:
-    case STATE_READY_TO_SEND<<8 | TCP_WHAT_SYNACK:
-        /* This is where we respond to a SYN-ACK. Note that this can happen
-         * in two places. We immediately transition to the "ready" state,
-         * but if the other side doesn't receive our acknowledgement,
-         * then it'll send a duplicate SYN-ACK which we'll have to process
-         * in the "ready" state. That's okay, we'll simply reset our
-         * timers and try again.
-         */
-
-        /* Send "ACK" to acknowlege their "SYN-ACK" */
-        tcpcon_send_packet(tcpcon, tcb,
-                    0x10,
-                    0, 0, 0);
-
-        /* Change ourselves to the "ready" state.*/
-        tcb->tcpstate = STATE_READY_TO_SEND;
-
-        /*
-         * Wait 1 second for "server hello" (like SSH), and if that's
-         * not found, then transmit a "client hello"
-         */
-        timeouts_add(   tcpcon->timeouts,
-                        tcb->timeout,
-                        offsetof(struct TCP_Control_Block, timeout),
-                        TICKS_FROM_TV(secs+tcpcon->timeout_hello,usecs)
-                        );
-        break;
-
-    case STATE_READY_TO_SEND<<8 | TCP_WHAT_ACK:
-        /* There's actually nothing that goes on in this state. We are
-         * just waiting for the timer to expire. In the meanwhile,
-         * though, the other side is might acknowledge that we sent
-         * a SYN-ACK */
-
-        /* NOTE: the arg 'payload_length' was overloaded here to be the
-         * 'ackno' instead */
-        handle_ack( tcb, (uint32_t)payload_length);
-        break;
-
-    case STATE_READY_TO_SEND<<8 | TCP_WHAT_TIMEOUT:
-        /* if we have a "hello" message to send to the server,
-         * then send it */
-        if (banner1->tcp_payloads[tcb->port_them]) {
-            size_t x_len = 0;
-            const unsigned char *x;
-            unsigned ctrl = 0;
-
-            x_len = banner1->tcp_payloads[tcb->port_them]->hello_length;
-            x = banner1->tcp_payloads[tcb->port_them]->hello;
-            if (banner1->tcp_payloads[tcb->port_them] == &banner_ssl)
-                tcb->banner1_state.is_sent_sslhello = 1;
-
-            /*
-             * KLUDGE
-             */
-            if (tcpcon->banner1->is_heartbleed) {
-                ctrl = CTRL_SMALL_WINDOW;
-            }
-
-            /* Send request. This actually doens't send the packet right
-             * now, but instead queues up a packet that the transmit
-             * thread will send soon. */
-            tcpcon_send_packet(tcpcon, tcb,
-                0x18,
-                x, x_len, ctrl);
-            LOGip(4, tcb->ip_them, tcb->port_them,
-                "sending payload %u bytes\n",
-                x_len);
-
-            /* Increment our sequence number */
-            tcb->seqno_me += (uint32_t)x_len;
-
-            /* change our state to reflect that we are now waiting for
-             * acknowledgement of the data we've sent */
-            tcb->tcpstate = STATE_PAYLOAD_SENT;
-        }
-
-        /* Add a timeout so that we can resend the data in case it
-            * goes missing. Note that we put this back in the timeout
-            * system regardless if we've sent data. */
-        timeouts_add(   tcpcon->timeouts,
-                        tcb->timeout,
-                        offsetof(struct TCP_Control_Block, timeout),
-                        TICKS_FROM_TV(secs+1,usecs)
-                        );
-        break;
-
-    case STATE_READY_TO_SEND<<8 | TCP_WHAT_DATA:
-    case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_DATA:
-        {
-            unsigned err;
-            struct InteractiveData more;
-            
-            memset(&more, 0, sizeof(more));
-
-            if ((unsigned)(tcb->seqno_them - seqno_them) > payload_length)  {
-                tcpcon_send_packet(tcpcon, tcb,
-                        0x10,
-                        0, 0, 0);
-                return;
-            }
-
-            while (seqno_them != tcb->seqno_them && payload_length) {
-                seqno_them++;
-                payload_length--;
-                payload++;
             }
-
-            if (payload_length == 0) {
-                tcpcon_send_packet(tcpcon, tcb,
-                        0x10,
-                        0, 0, 0);
-                return;
+            break;
+        case STATE_FIN_WAIT1:
+            switch (what) {
+                case TCP_WHAT_TIMEOUT:
+                    /* resend FIN packet */
+                    LOGSEND(tcb, "peer(FIN)");
+                    tcpcon_send_packet(tcpcon, tcb,
+                                       0x11,
+                                       0, 0, 0);
+                    
+                    /* reset timeout */
+                    LOGSEND(tcb, "+timeout");
+                    timeouts_add(   tcpcon->timeouts,
+                                 tcb->timeout,
+                                 offsetof(struct TCP_Control_Block, timeout),
+                                 TICKS_FROM_TV(secs+1,usecs)
+                                 );
+                    break;
+                case TCP_WHAT_ACK:
+                    if (handle_ack( tcb, (uint32_t)payload_length)) {
+                        tcb->tcpstate = STATE_FIN_WAIT2;
+                        LOGSEND(tcb, "+timeout");
+                        timeouts_add(   tcpcon->timeouts,
+                                     tcb->timeout,
+                                     offsetof(struct TCP_Control_Block, timeout),
+                                     TICKS_FROM_TV(secs+5,usecs)
+                                     );
+                    }
+                    break;
+                case TCP_WHAT_FIN:
+                    break;
+                case TCP_WHAT_SYNACK:
+                case TCP_WHAT_RST:
+                case TCP_WHAT_DATA:
+                    break;
             }
+            break;
             
-
-            /* [--banners]
-             * This is an important part of the system, where the TCP
-             * stack passes incoming packet payloads off to the application
-             * layer protocol parsers. This is where, in Sockets API, you
-             * might call the 'recv()' function.
-             */
-            err = parse_banner(
-                        tcpcon,
-                        tcb,
-                        payload,
-                        payload_length,
-                        &more);
-
-            /* move their sequence number forward */
-            tcb->seqno_them += (unsigned)payload_length;
-            
-            /* acknowledge the bytes sent */
-            if (more.m_length) {
-                //printf("." "sending more data %u bytes\n", more.length);
-                tcpcon_send_packet(tcpcon, tcb, 0x18, more.m_payload, more.m_length, 0);
-                tcb->seqno_me += (uint32_t)more.m_length;
-                tcb->is_payload_dynamic = more.is_payload_dynamic;
-            } else {
-                tcpcon_send_packet(tcpcon, tcb,
-                        0x10,
-                        0, 0, 0);
-            }
-
-            if (err == STATE_DONE) {
-                tcpcon_send_packet(tcpcon, tcb,
-                    0x11,
-                    0, 0, 0);
-                tcb->seqno_me++;
-                tcpcon_destroy_tcb(tcpcon, tcb, Reason_StateDone);
+        case STATE_FIN_WAIT2:
+        case STATE_TIME_WAIT:
+            switch (what) {
+                case TCP_WHAT_TIMEOUT:
+                    if (tcb->tcpstate == STATE_TIME_WAIT) {
+                        tcpcon_destroy_tcb(tcpcon, tcb, Reason_Timeout);
+                        return;
+                    }
+                    break;
+                case TCP_WHAT_ACK:
+                    break;
+                case TCP_WHAT_FIN:
+                    tcb->seqno_them = seqno_them + 1;
+                    LOGSEND(tcb, "peer(ACK)");
+                    tcpcon_send_packet(tcpcon, tcb,
+                                       0x10,
+                                       0, 0, 0);
+                    tcb->tcpstate = STATE_TIME_WAIT;
+                    LOGSEND(tcb, "+timeout");
+                    timeouts_add(   tcpcon->timeouts,
+                                 tcb->timeout,
+                                 offsetof(struct TCP_Control_Block, timeout),
+                                 TICKS_FROM_TV(secs+5,usecs)
+                                 );
+                    break;
+                case TCP_WHAT_SYNACK:
+                case TCP_WHAT_RST:
+                case TCP_WHAT_DATA:
+                    break;
             }
-        }
-        break;
-
-    case STATE_READY_TO_SEND<<8 | TCP_WHAT_FIN:
-        tcb->seqno_them = seqno_them + (unsigned)payload_length + 1;
-        tcpcon_send_packet(tcpcon, tcb,
-                    0x11, /*reset */
-                    0, 0, 0);
-        tcpcon_destroy_tcb(tcpcon, tcb, Reason_FIN);
-        break;
-
-   case STATE_PAYLOAD_SENT<<8 | TCP_WHAT_SYNACK:
-       /* ignore duplciate SYN-ack */
-       break;
-
-   case STATE_PAYLOAD_SENT<<8 | TCP_WHAT_ACK:
-        /* There's actually nothing that goes on in this state. We are just waiting
-         * for the timer to expire. In the meanwhile, though, the other side is
-         * might acknowledge that we sent a SYN-ACK */
-
-        /* NOTE: the arg 'payload_length' was overloaded here to be the
-         * 'ackno' instead */
-        handle_ack(tcb, (uint32_t)payload_length);
-
-        if (tcb->ackno_them - tcb->seqno_me == 0) {
-            /* Now wait for response */
-            if (tcb->is_payload_dynamic)
-                free((void*)tcb->payload);
-            tcb->payload = 0;
-            tcb->payload_length = 0;
-            tcb->is_payload_dynamic = 0;
-            tcb->tcpstate = STATE_WAITING_FOR_RESPONSE;
-            timeouts_add(   tcpcon->timeouts,
-                            tcb->timeout,
-                            offsetof(struct TCP_Control_Block, timeout),
-                            TICKS_FROM_TV(secs+10,usecs)
-                            );
-        } else {
-            /* Reset the timeout, waiting for more data to arrive */
-            timeouts_add(   tcpcon->timeouts,
-                            tcb->timeout,
-                            offsetof(struct TCP_Control_Block, timeout),
-                            TICKS_FROM_TV(secs+1,usecs)
-                            );
-
-        }
-        break;
-
-    case STATE_PAYLOAD_SENT<<8 | TCP_WHAT_TIMEOUT:
-        {
-            uint32_t len;
-
-
-            len = tcb->seqno_me - tcb->ackno_them;
-
-            /* Resend the payload */
-            tcb->seqno_me -= len;
-            tcpcon_send_packet(tcpcon, tcb,
-                0x18,
-                tcb->payload + tcb->payload_length - len,
-                len, 0);
-            LOGip(4, tcb->ip_them, tcb->port_them,
-                "- re-sending payload %u bytes\n",
-                len);
-            tcb->seqno_me += len;
-
-
-            /*  */
-            timeouts_add(   tcpcon->timeouts,
-                            tcb->timeout,
-                            offsetof(struct TCP_Control_Block, timeout),
-                            TICKS_FROM_TV(secs+2,usecs)
-                         );
-        }
-        break;
-    case STATE_PAYLOAD_SENT<<8 | TCP_WHAT_FIN:
-        /* ignore this, since they havne't acked our payload */
-        break;
-
-    case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_ACK:
-        handle_ack(tcb, (uint32_t)payload_length);
-        break;
-
-
-    case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_FIN:
-        tcb->seqno_them = seqno_them + (unsigned)payload_length + 1;
-        tcpcon_send_packet(tcpcon, tcb,
-            0x11,
-            0, 0, 0);
-        tcb->seqno_me++;
-        tcpcon_flush_banners(tcpcon, tcb);
-        break;
-    
-    case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_TIMEOUT:
-        if (tcb->when_created + tcpcon->timeout_connection < secs) {
-            tcpcon_send_packet(tcpcon, tcb,
-                0x04,
-                0, 0, 0);
-            tcpcon_destroy_tcb(tcpcon, tcb, Reason_Foo);
-        }
-        break;
-
-    case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_RST:
-    case STATE_READY_TO_SEND<<8 | TCP_WHAT_RST:
-    case STATE_PAYLOAD_SENT<<8 | TCP_WHAT_RST:
-        tcpcon_destroy_tcb(tcpcon, tcb, Reason_RST);
-        break;
+            break;
 
-    default:
-        LOGip(3, tcb->ip_them, tcb->port_them, "tcb: unknown event %s : %s\n",
-            state_to_string(tcb->tcpstate),
-            what_to_string(what));
+        default:
+            LOG(1, "TCP-state: unknown state\n");
     }
-
 }
 
diff --git a/src/proto-tcp.h b/src/proto-tcp.h
index 5f3d467..d8d14ee 100644
--- a/src/proto-tcp.h
+++ b/src/proto-tcp.h
@@ -77,7 +77,6 @@ void
 tcpcon_timeouts(struct TCP_ConnectionTable *tcpcon, unsigned secs, unsigned usecs);
 
 enum TCP_What {
-    TCP_WHAT_NOTHING,
     TCP_WHAT_TIMEOUT,
     TCP_WHAT_SYNACK,
     TCP_WHAT_RST,
diff --git a/src/proto-vnc.c b/src/proto-vnc.c
index 584d904..70ee1c3 100644
--- a/src/proto-vnc.c
+++ b/src/proto-vnc.c
@@ -108,6 +108,7 @@ vnc_parse(  const struct Banner1 *banner1,
         RFB3_7_SECURITYTYPES=100,
         RFB_SERVERINIT=200,
         RFB_SECURITYRESULT=300,
+        RFB_DONE=0xffffffff,
     };
     
     UNUSEDPARM(banner1_private);
@@ -157,7 +158,8 @@ vnc_parse(  const struct Banner1 *banner1,
                         state = RFB3_7_SECURITYTYPES;
                     }
                 } else {
-                    state = STATE_DONE;
+                    state = 0xFFFFFFFF;
+                    tcp_close(more);
                 }
                 break;
             case RFB3_3_SECURITYTYPES:
@@ -192,8 +194,10 @@ vnc_parse(  const struct Banner1 *banner1,
                      * We move immediately to ClientInit stage */
                     tcp_transmit(more, "\x01", 1, 0);
                     state = RFB_SERVERINIT;
-                } else
-                    state = STATE_DONE;
+                } else {
+                    state = RFB_DONE;
+                    tcp_close(more);
+                }
                 break;
             case RFB_SECURITYRESULT+3:
                 pstate->sub.vnc.sectype <<= 8;
@@ -215,7 +219,8 @@ vnc_parse(  const struct Banner1 *banner1,
                 break;
             case RFB_SECURITYERROR+4:
                 if (pstate->sub.vnc.sectype == 0) {
-                    state = STATE_DONE;
+                    state = RFB_DONE;
+                    tcp_close(more);
                 } else {
                     pstate->sub.vnc.sectype--;
                     banout_append_char(banout, PROTO_VNC_RFB, px[i]);
@@ -303,7 +308,8 @@ vnc_parse(  const struct Banner1 *banner1,
                 if (pstate->sub.vnc.sectype) {
                     banout_append(banout, PROTO_VNC_RFB, " name=[", AUTO_LEN);
                 } else {
-                    state = STATE_DONE;
+                    state = RFB_DONE;
+                    tcp_close(more);
                 }
                 break;
                 
@@ -312,13 +318,15 @@ vnc_parse(  const struct Banner1 *banner1,
                 banout_append_char(banout, PROTO_VNC_RFB, px[i]);
                 if (pstate->sub.vnc.sectype == 0) {
                     banout_append(banout, PROTO_VNC_RFB, "]", AUTO_LEN);
-                    state = STATE_DONE;
+                    state = RFB_DONE;
+                    tcp_close(more);
                 }
                 break;
 
 
                 
-            case STATE_DONE:
+            case RFB_DONE:
+                tcp_close(more);
                 i = (unsigned)length;
                 break;
             default:
-- 
GitLab