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