From b72e8ab66fb3a3689697d62947b6c81c1083dc08 Mon Sep 17 00:00:00 2001
From: Robert David Graham <robert_david_graham@yahoo.com>
Date: Sun, 10 Jun 2018 22:31:03 -0400
Subject: [PATCH] SMBv2

---
 src/proto-banner1.h                      |   73 +-
 src/proto-banout.c                       |   63 +-
 src/proto-banout.h                       |   10 +
 src/proto-ftp.c                          |    6 +-
 src/proto-imap4.c                        |    7 +-
 src/proto-interactive.c                  |    9 +-
 src/proto-interactive.h                  |   11 +-
 src/proto-ntlmssp.c                      |  268 +++
 src/proto-ntlmssp.h                      |   25 +
 src/proto-pop3.c                         |    8 +-
 src/proto-sctp.c                         |    2 +-
 src/proto-smb.c                          | 1883 ++++++++++++++++++----
 src/proto-smtp.c                         |    9 +-
 src/proto-snmp.c                         |    4 +-
 src/proto-spnego.h                       |   28 +
 src/proto-ssl.c                          |    3 +-
 src/proto-tcp.c                          |   33 +-
 src/proto-vnc.c                          |   19 +-
 src/proto-x509.c                         |  325 +++-
 src/proto-x509.h                         |    3 +
 xcode4/masscan.xcodeproj/project.pbxproj |    8 +
 21 files changed, 2426 insertions(+), 371 deletions(-)
 create mode 100644 src/proto-ntlmssp.c
 create mode 100644 src/proto-ntlmssp.h
 create mode 100644 src/proto-spnego.h

diff --git a/src/proto-banner1.h b/src/proto-banner1.h
index a8b3dbe..73c9710 100644
--- a/src/proto-banner1.h
+++ b/src/proto-banner1.h
@@ -6,6 +6,7 @@
 #include "masscan-app.h"
 #include "proto-banout.h"
 #include "proto-x509.h"
+#include "proto-spnego.h"
 
 struct InteractiveData;
 struct Banner1;
@@ -139,39 +140,70 @@ struct Smb72_Negotiate {
     uint16_t DialectIndex;
     uint16_t SecurityMode;
     uint64_t SystemTime;
+    uint32_t SessionKey;
     uint32_t Capabilities;
     uint16_t ServerTimeZone;
     uint8_t  ChallengeLength;
     uint8_t  ChallengeOffset;
 };
 
+struct Smb73_Setup {
+    uint16_t BlobLength;
+    uint16_t BlobOffset;
+};
+
 struct SMBSTUFF {
+    unsigned nbt_state;
     unsigned char nbt_type;
     unsigned char nbt_flags;
-    unsigned length;
+    unsigned is_printed_ver:1;
+    unsigned is_printed_guid:1;
+    unsigned is_printed_time:1;
+    unsigned nbt_length;
     unsigned nbt_err;
     
-    struct {
-        unsigned char   command;
-        unsigned        status;
-        unsigned char   flags1;
-        unsigned short  flags2;
-        unsigned        pid;
-        unsigned char   signature[8];
-        unsigned short  tid;
-        unsigned short  uid;
-        unsigned short  mid;
-        unsigned short  param_length;
-        unsigned short  param_offset;
-        unsigned short  byte_count;
-        unsigned short  byte_offset;
-        unsigned short  byte_state;
-        unsigned short  unicode_char;
-    } smb1;
+    union {
+        struct {
+            unsigned char   command;
+            unsigned        status;
+            unsigned char   flags1;
+            unsigned short  flags2;
+            unsigned        pid;
+            unsigned char   signature[8];
+            unsigned short  tid;
+            unsigned short  uid;
+            unsigned short  mid;
+            unsigned short  param_length;
+            unsigned short  param_offset;
+            unsigned short  byte_count;
+            unsigned short  byte_offset;
+            unsigned short  byte_state;
+            unsigned short  unicode_char;
+        } smb1;
+        struct {
+            unsigned seqno;
+            unsigned short header_length;
+            unsigned short offset;
+            unsigned short state;
+            unsigned short opcode;
+            unsigned short struct_length;
+            unsigned is_dynamic:1;
+            unsigned char flags;
+            unsigned ntstatus;
+            unsigned number;
+            unsigned short blob_offset;
+            unsigned short blob_length;
+        } smb2;
+    } hdr;
     union {
         struct Smb72_Negotiate negotiate;
-    } parms1;
-    
+        struct Smb73_Setup setup;
+        struct {
+            uint64_t current_time;
+            uint64_t boot_time;
+        } negotiate2;
+    } parms;
+    struct SpnegoDecode spnego;
 };
 
 struct ProtocolState {
@@ -217,6 +249,7 @@ struct ProtocolParserStream {
         const unsigned char *px, size_t length,
         struct BannerOutput *banout,
         struct InteractiveData *more);
+    void (*cleanup)(struct ProtocolState *stream_state);
 };
 
 
diff --git a/src/proto-banout.c b/src/proto-banout.c
index e24553e..60555c7 100644
--- a/src/proto-banout.c
+++ b/src/proto-banout.c
@@ -177,6 +177,68 @@ banout_append_char(struct BannerOutput *banout, unsigned proto, int c)
     banout_append(banout, proto, &cc, 1);
 }
 
+/***************************************************************************
+ ***************************************************************************/
+void
+banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits)
+{
+    if (digits == 0) {
+        for (digits=16; digits>0; digits--)
+            if (number>>((digits-1)*4) & 0xF)
+                break;
+    }
+    
+    for (;digits>0; digits--) {
+        char c = "0123456789abcdef"[(number>>(unsigned long long)((digits-1)*4)) & 0xF];
+        banout_append_char(banout, proto, c);
+    }
+}
+
+/***************************************************************************
+ * Output either a normal character, or the hex form of a UTF-8 string
+ ***************************************************************************/
+void
+banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c)
+{
+    if (c & ~0xFFFF) {
+        unsigned c2;
+        c2 = 0xF0 | ((c>>18)&0x03);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+        c2 = 0x80 | ((c>>12)&0x3F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+        c2 = 0x80 | ((c>> 6)&0x3F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+        c2 = 0x80 | ((c>> 0)&0x3F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+    } else if (c & ~0x7FF) {
+        unsigned c2;
+        c2 = 0xE0 | ((c>>12)&0x0F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+        c2 = 0x80 | ((c>> 6)&0x3F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+        c2 = 0x80 | ((c>> 0)&0x3F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+    } else if (c & ~0x7f) {
+        unsigned c2;
+        c2 = 0xc0 | ((c>> 6)&0x1F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+        c2 = 0x80 | ((c>> 0)&0x3F);
+        banout_append(banout, proto, "\\x", 2);
+        banout_append_hexint(banout, proto, c2, 2);
+    } else
+        banout_append_char(banout, proto, c);
+}
+
+
+
 /***************************************************************************
  ***************************************************************************/
 static struct BannerOutput *
@@ -270,7 +332,6 @@ banout_append(struct BannerOutput *banout, unsigned proto,
     memcpy(p->banner + p->length, px, length);
     p->length = (unsigned)(p->length + length);
 
-    
 }
 
 /*****************************************************************************
diff --git a/src/proto-banout.h b/src/proto-banout.h
index 55b639c..ef76c60 100644
--- a/src/proto-banout.h
+++ b/src/proto-banout.h
@@ -65,6 +65,16 @@ banout_append(struct BannerOutput *banout, unsigned proto, const void *px, size_
 void
 banout_append_char(struct BannerOutput *banout, unsigned proto, int c);
 
+/**
+ * Append an integer, with hex digits, with the specified number of
+ * digits
+ */
+void
+banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits);
+
+void
+banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c);
+
 /**
  * Select a specific string (of the specified protocol).
  */
diff --git a/src/proto-ftp.c b/src/proto-ftp.c
index 137ea66..65e5b08 100644
--- a/src/proto-ftp.c
+++ b/src/proto-ftp.c
@@ -68,8 +68,7 @@ ftp_parse(  const struct Banner1 *banner1,
                     continue;
                 else if (px[i] == '\n') {
                     if (ftp->is_last) {
-                        more->payload = "AUTH TLS\r\n";
-                        more->length = 10;
+                        tcp_transmit(more, "AUTH TLS\r\n", 10, 0);
                         state = 100;
                         banout_append_char(banout, PROTO_FTP, px[i]);
                     } else {
@@ -98,8 +97,7 @@ ftp_parse(  const struct Banner1 *banner1,
                         pstate->port = (unsigned short)port;
                         state = 0;
                         
-                        more->payload = banner_ssl.hello;
-                        more->length = (unsigned)banner_ssl.hello_length;
+                        tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0);
                         
                     } else {
                         state = STATE_DONE;
diff --git a/src/proto-imap4.c b/src/proto-imap4.c
index 3d1fb9b..9bce94e 100644
--- a/src/proto-imap4.c
+++ b/src/proto-imap4.c
@@ -81,7 +81,7 @@ imap4_parse(  const struct Banner1 *banner1,
             case 5:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == '\n') {
-                    tcp_transmit(more, "a001 CAPABILITY\r\n", 17); 
+                    tcp_transmit(more, "a001 CAPABILITY\r\n", 17, 0);
                     state = 100;
                 }
                 break;
@@ -136,7 +136,7 @@ imap4_parse(  const struct Banner1 *banner1,
             case 105:
                 banout_append_char(banout, PROTO_IMAP4, px[i]);
                 if (px[i] == '\n') {
-                    tcp_transmit(more, "a002 STARTTLS\r\n", 15);
+                    tcp_transmit(more, "a002 STARTTLS\r\n", 15, 0);
                     state = 300;
                 }
                 break;
@@ -158,8 +158,7 @@ imap4_parse(  const struct Banner1 *banner1,
                     pstate->port = (unsigned short)port;
                     state = 0;
                     
-                    more->payload = banner_ssl.hello;
-                    more->length = (unsigned)banner_ssl.hello_length;
+                    tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0);
                     break;
                 }
                 break;
diff --git a/src/proto-interactive.c b/src/proto-interactive.c
index 2c57fa4..f075103 100644
--- a/src/proto-interactive.c
+++ b/src/proto-interactive.c
@@ -1,8 +1,11 @@
 #include "proto-interactive.h"
 
 void
-tcp_transmit(struct InteractiveData *more, const void *payload, size_t length)
+tcp_transmit(struct InteractiveData *more, const void *payload, size_t length, unsigned flags)
 {
-    more->payload = payload;
-    more->length = (unsigned)length;
+    more->m_payload = payload;
+    more->m_length = (unsigned)length;
+    
+    if (flags & TCPTRAN_DYNAMIC)
+        more->is_payload_dynamic = 1;
 }
diff --git a/src/proto-interactive.h b/src/proto-interactive.h
index 04852ab..4bb8a5f 100644
--- a/src/proto-interactive.h
+++ b/src/proto-interactive.h
@@ -3,11 +3,14 @@
 #include <stdio.h>
 
 struct InteractiveData {
-    const void *payload;
-    unsigned length;
+    const void *m_payload;
+    unsigned m_length;
+    unsigned is_payload_dynamic:1;
+};
+enum {
+    TCPTRAN_DYNAMIC = 0x0001,
 };
-
 void
-tcp_transmit(struct InteractiveData *more, const void *data, size_t length);
+tcp_transmit(struct InteractiveData *more, const void *data, size_t length, unsigned flags);
 
 #endif
diff --git a/src/proto-ntlmssp.c b/src/proto-ntlmssp.c
new file mode 100644
index 0000000..250b16d
--- /dev/null
+++ b/src/proto-ntlmssp.c
@@ -0,0 +1,268 @@
+#include "proto-ntlmssp.h"
+#include "masscan-app.h"
+#include "proto-banout.h"
+#include "string_s.h"
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ +--------+--------+--------+--------+
+ |  'N'   |  'T'   |  'L'   |  'M'   |
+ +-      -+-      -+-      -+-      -+
+ |  'S'   |  'S'   |  'P'   | '\0'   |
+ +--------+--------+--------+--------+
+ |           MessageType             |
+ +--------+--------+--------+--------+
+ |  TargetNameLen  | TargetNameMaxLen| TagetName fields set to zero if
+ +--------+--------+--------+--------+ NTLMSSP_REQUEST_TARGET  flag not set
+ |           TargetNameOffset        |
+ +--------+--------+--------+--------+
+ |            NegotiateFlags         |
+ +--------+--------+--------+--------+
+ |                                   |
+ +-          ServerChallenge        -+
+ |                                   |
+ +--------+--------+--------+--------+
+ |                                   |
+ +-             Reserved            -+
+ |                                   |
+ +--------+--------+--------+--------+
+ |  TargetInfoLen  | TargetInfoMaxLen| TagetInfo fields set to zero if
+ +--------+--------+--------+--------+ NTLMSSP_NEGOTIATE_TARGET_INFO  flag not set
+ |           TargetInfoOffset        |
+ +--------+--------+--------+--------+
+ |MajorVer|MinorVer|   ProductBuild  |
+ +--------+--------+--------+--------+
+ |          Reserved        |NTLMver |
+ +--------+--------+--------+--------+
+ |                                   |
+ +-      -+-      -+-      -+-      -+
+ . . . . . . . . . . . . . . . . . . .
+ +-      -+-      -+-      -+-      -+
+ |        |        |        |        |
+ +--------+--------+--------+--------+
+
+ 
+ Signature (8 bytes):
+    "An 8-byte character array that MUST contain the ASCII string
+    ('N', 'T', 'L', 'M', 'S', 'S', 'P', '\0')."
+ MessageType (4 bytes):
+    "A 32-bit unsigned integer that indicates the message type. This field MUST
+    be set to 0x00000002."
+ 
+ 
+ TargetNameLen (2 bytes):
+    "A 16-bit unsigned integer that defines the size, in bytes, of
+    TargetName in Payload."
+    Zero if NTLMSSP_REQUEST_TARGET not set.
+ TargetNameMaxLen (2 bytes):
+    "A 16-bit unsigned integer that SHOULD be set to the value
+    of TargetNameLen and MUST be ignored on receipt."
+    Zero if NTLMSSP_REQUEST_TARGET not set.
+ TargetNameBufferOffset (4 bytes):
+    "A 32-bit unsigned integer that defines the offset, in
+    bytes, from the beginning of the CHALLENGE_MESSAGE to TargetName in Payload. If
+    TargetName is a Unicode string, the values of TargetNameBufferOffset and
+    TargetNameLen MUST be multiples of 2."
+ 
+ 
+ 
+ VERSION FIELDS:
+    These fields are valid only if "NTLMSSP_NEGOTIATE_VERSION" flag is set.
+ 
+  MajorVer [ProductMajorVersion] (1 byte):
+    "An 8-bit unsigned integer that SHOULD contain the major
+    version number of the operating system in use."
+  MinorVer [ProductMinorVersion] (1 byte):
+    "An 8-bit unsigned integer that SHOULD<34> contain the minor
+    version number of the operating system in use."
+  ProductBuild (2 bytes):
+    "A 16-bit unsigned integer that contains the build number of the operating
+    system in use. This field SHOULD be set to a 16-bit quantity that identifies the operating system
+    build number."
+  NTLMRevisionCurrent (1 byte):
+    "An 8-bit unsigned integer that contains a value indicating the
+    current revision of the NTLMSSP in use. This field SHOULD contain the following value:"
+        "NTLMSSP_REVISION_W2K3 (0x0F): Version 15 of the NTLMSSP is in use."
+ 
+ 
+ */
+
+static void
+append_unicode_string(struct BannerOutput *banout, unsigned proto, const char *name, const unsigned char *value, size_t value_length)
+{
+    unsigned j;
+    banout_append_char(banout, proto, ' ');
+    banout_append(banout, PROTO_SMB, name, AUTO_LEN);
+    banout_append_char(banout, proto, '=');
+    for (j=0; j<value_length; j += 2) {
+        unsigned c = value[j] | value[j+1]<<8;
+        banout_append_unicode(banout, PROTO_SMB, c);
+    }
+}
+
+void
+ntlmssp_decode(struct NtlmsspDecode *x,
+              const unsigned char *px, size_t length,
+              struct BannerOutput *banout)
+{
+    unsigned message_type;
+    unsigned name_offset;
+    unsigned name_length;
+    unsigned info_offset;
+    unsigned info_length;
+    unsigned flags;
+    unsigned i;
+    
+    if (length > x->length - x->offset)
+        length = x->length - x->offset;
+    
+    /* See if we have a fragment, in which case we need to allocate a buffer
+     * to contain it */
+    if (x->offset == 0 && x->length > length) {
+        x->buf = malloc(x->length);
+        memcpy(x->buf, px, length);
+        x->offset = (unsigned)length;
+        return;
+    } else if (x->offset) {
+        memcpy(x->buf + x->offset, px, length);
+        x->offset += length;
+        if (x->offset < x->length)
+            return;
+        
+        /* now reset the input to point to our buffer instead */
+        px = x->buf;
+        length = x->length;
+    }
+    
+    if (length < 56)
+        goto end;
+    
+    /* Verify the signature. There are other protocols that we could possibly
+     * detect at this point and do something else useful with, but for right now,
+     * we are just doing NTLM */
+    if (memcmp("NTLMSSP", px, 8) != 0)
+        goto end;
+    
+    /* Verify this is a "challenge" packet, which has all the interesting
+     * fields. */
+    message_type = px[8] | px[9]<<8 | px[10]<<16 | px[11]<<24;
+    if (message_type != 2)
+        goto end;
+    
+    /* Grab the Domain field. This is a pointer in these 8 bytes here
+     * that points into the payload section of the chunk */
+    name_length = px[12] | px[13]<<8;
+    name_offset = px[16] | px[17]<<8 | px[18]<<16 | px[19]<<24;
+    if (name_length && name_length + name_offset < length) {
+        append_unicode_string(banout, PROTO_SMB, "domain", px+name_offset, name_length);
+    }
+    
+    /* Grab flags */
+    flags = px[20] | px[21]<<8 | px[22]<<16 | px[23]<<24;
+    
+    /* Info field */
+    info_length = px[40] | px[41]<<8;
+    info_offset = px[44] | px[45]<<8 | px[46]<<16 | px[47]<<24;
+
+    /* Version field */
+    {
+        char buf[64];
+        sprintf_s(buf, sizeof(buf), " version=%u.%u.%u ntlm-ver=%u",
+                  px[48],
+                  px[49],
+                  px[50] | px[51]<<8,
+                  px[55]
+                  );
+    }
+
+    /* 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;
+        i += 4;
+        
+        if (len > info_offset + info_length - i)
+            len = info_offset + info_length - i;
+        if (len > length - i)
+            len = length - i;
+        
+        switch (type) {
+            case 0x00: /* MsvAvEOL */
+                i = info_offset + info_length;
+                continue;
+            case 1: /* MsvAvNbComputerName */
+                append_unicode_string(banout, PROTO_SMB, "name", px+i, len);
+                break;
+            case 2: /* MsvAvNbDomainName */
+                append_unicode_string(banout, PROTO_SMB, "domain", px+i, len);
+                break;
+            case 3: /* MsvAvDnsComputerName */
+                append_unicode_string(banout, PROTO_SMB, "name-dns", px+i, len);
+                break;
+            case 4: /* MsvAvDnsDomainName */
+                append_unicode_string(banout, PROTO_SMB, "domain-dns", px+i, len);
+                break;
+            case 5: /* MsvAvDnsTreeName */
+                append_unicode_string(banout, PROTO_SMB, "forest", px+i, len);
+                break;
+            case 6: /* MsvAvFlags */
+                break;
+            case 7: /* MsvAvTimestamp */
+                break;
+            case 8: /* MsvAvSingleHost */
+                break;
+            case 9: /* MsvAvTargetName */
+                append_unicode_string(banout, PROTO_SMB, "target", px+i, len);
+                break;
+            case 10: /* MsvChannelBindings */
+                break;
+            default:
+                break;
+        }
+        i += len;
+    }
+
+    
+    
+    /* Grab the other fields. This*/
+    
+end:
+    /*
+     * Free the buffer if needed
+     */
+    if (x->buf) {
+        free(x->buf);
+        x->buf = 0;
+    }
+    
+}
+
+void
+ntlmssp_cleanup(struct NtlmsspDecode *x)
+{
+    if (x->buf) {
+        free(x->buf);
+        x->buf = 0;
+    }
+}
+
+void
+ntlmssp_decode_init(struct NtlmsspDecode *x, size_t length)
+{
+    memset(x, 0, sizeof(*x));
+    
+    /* [security] Double-check this input, since it's ultimately driven by user-input.
+     * The code that leads to here should already have double-checked this, but I'm
+     * doing it again just in case. This is larger than any input that should be
+     * seen in the real world that a hacker isn't messing with.
+     */
+    if (length > 65536)
+        length = 65536;
+    
+    x->length = (unsigned)length;
+    x->offset = 0;
+    x->buf = NULL;
+    
+}
+
diff --git a/src/proto-ntlmssp.h b/src/proto-ntlmssp.h
new file mode 100644
index 0000000..d0f155a
--- /dev/null
+++ b/src/proto-ntlmssp.h
@@ -0,0 +1,25 @@
+#ifndef PROTO_NTLMSSP_H
+#define PROTO_NTLMSSP_H
+#include <stdio.h>
+struct BannerOutput;
+
+struct NtlmsspDecode
+{
+    unsigned length;
+    unsigned offset;
+    unsigned char *buf;
+};
+
+void
+ntlmssp_decode_init(struct NtlmsspDecode *x, size_t length);
+
+void
+ntlmssp_cleanup(struct NtlmsspDecode *x);
+
+void
+ntlmssp_decode(struct NtlmsspDecode *x,
+              const unsigned char *px, size_t length,
+              struct BannerOutput *banout);
+
+#endif
+
diff --git a/src/proto-pop3.c b/src/proto-pop3.c
index af62140..e6435b9 100644
--- a/src/proto-pop3.c
+++ b/src/proto-pop3.c
@@ -48,7 +48,7 @@ pop3_parse(  const struct Banner1 *banner1,
             case 3:
                 banout_append_char(banout, PROTO_POP3, px[i]);
                 if (px[i] == '\n') {
-                    tcp_transmit(more, "CAPA\r\n", 6);
+                    tcp_transmit(more, "CAPA\r\n", 6, 0);
                     state++;
                 }
                 break;
@@ -102,7 +102,7 @@ pop3_parse(  const struct Banner1 *banner1,
                     continue;
                 banout_append_char(banout, PROTO_POP3, px[i]);
                 if (px[i] == '\n') {
-                    tcp_transmit(more, "STLS\r\n", 6);
+                    tcp_transmit(more, "STLS\r\n", 6, 0);
                     state = 204;
                 } else {
                     state = 8;
@@ -122,8 +122,8 @@ pop3_parse(  const struct Banner1 *banner1,
                     pstate->port = (unsigned short)port;
                     state = 0;
                     
-                    more->payload = banner_ssl.hello;
-                    more->length = (unsigned)banner_ssl.hello_length;
+                    tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0);
+                    
                     break;
                 }
                 break;
diff --git a/src/proto-sctp.c b/src/proto-sctp.c
index 750c33f..2c51a81 100644
--- a/src/proto-sctp.c
+++ b/src/proto-sctp.c
@@ -9,7 +9,7 @@
 #define CRC32C_POLY 0x1EDC6F41
 #define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF])
 
-static unsigned long  crc_c[256] =
+static unsigned   crc_c[256] =
 {
 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
diff --git a/src/proto-smb.c b/src/proto-smb.c
index 28663a1..5b030e5 100644
--- a/src/proto-smb.c
+++ b/src/proto-smb.c
@@ -77,46 +77,108 @@ struct SmbParams params[] = {
     //{0x72,  5,   2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxNumberVcs)},
     //{0x72,  7,   4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxBufferSize)},
     //{0x72, 11,   4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxRawSize)},
-    //{0x72, 15,   4, IT_uint32, offsetof(struct Smb72_Negotiate, SessionKey)},
+    {0x72, 15,   4, IT_uint32, offsetof(struct Smb72_Negotiate, SessionKey)},
     {0x72, 19,   4, IT_uint32, offsetof(struct Smb72_Negotiate, Capabilities)},
     {0x72, 23,   8, IT_uint64, offsetof(struct Smb72_Negotiate, SystemTime)},
     {0x72, 31,   2, IT_uint16, offsetof(struct Smb72_Negotiate, ServerTimeZone)},
     {0x72, 33,   1, IT_uint8,  offsetof(struct Smb72_Negotiate, ChallengeLength)},
     
+    {0x73,  6,   2, IT_uint16, offsetof(struct Smb73_Setup, BlobLength)},
+    
     {0xFF, 0,   65536, IT_uint0,  0},
     
 };
 
 #define memberat(t, s, offset) (t*)((char*)(s)+(offset))
 
+
+
 static char smb1_null_session_setup[] = {
-    0x00, 0x00, 0x00, 0xbe, 0xff, 0x53, 0x4d, 0x42,
-    0x73, 0x00, 0x00, 0x00, 0x00, 0x18, 0x03, 0x80,
-    0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06,
-    0xe8, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xca,
-    0x00, 0x00, 0x00, 0x00, 0x0d, 0x75, 0x00, 0x84,
-    0x00, 0x04, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x47,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00,
-    0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
-    0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x4e, 0x00,
-    0x54, 0x00, 0x20, 0x00, 0x31, 0x00, 0x33, 0x00,
-    0x38, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00,
-    0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00,
-    0x4e, 0x00, 0x54, 0x00, 0x20, 0x00, 0x34, 0x00,
-    0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-    0x00, 0x2f, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00,
-    0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x00,
-    0x30, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x30, 0x00,
-    0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00,
-    0x5c, 0x00, 0x49, 0x00, 0x50, 0x00, 0x43, 0x00,
-    0x24, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f,
-    0x3f, 0x00
+    0x00, 0x00, 0x00, 0x7e, 0xff, 0x53, 0x4d, 0x42,
+    0x73, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc0,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+    0xff, 0xff, 0x01, 0x00, 0x0d, 0xff, 0x00, 0x00,
+    0x00, 0x04, 0x41, 0x32, 0x00, 0xef, 0x00, 0x53,
+    0x45, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x5c, 0xc0, 0x80, 0x00, 0x41,
+    0x00, 0x00, 0x47, 0x00, 0x55, 0x00, 0x45, 0x00,
+    0x53, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x4d, 0x00, 0x61, 0x00, 0x63, 0x00, 0x20, 0x00,
+    0x4f, 0x00, 0x53, 0x00, 0x20, 0x00, 0x58, 0x00,
+    0x20, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00,
+    0x31, 0x00, 0x33, 0x00, 0x00, 0x00, 0x53, 0x00,
+    0x4d, 0x00, 0x42, 0x00, 0x46, 0x00, 0x53, 0x00,
+    0x20, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x32, 0x00,
+    0x00, 0x00
 };
 
+static char smb1_null_session_setup_ex[] = {
+    0x00, 0x00, 0x00, 0xb8, 0xff, 0x53, 0x4d, 0x42,
+    0x73, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+    0x00, 0x00, 0x01, 0x00, 0x0c, 0xff, 0x00, 0x00,
+    0x00, 0x04, 0x41, 0x32, 0x00, 0xf1, 0x00, 0xa5,
+    0x12, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x5c, 0xc0, 0x80, 0x80, 0x7d, 0x00, 0x60,
+    0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05,
+    0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, 0x30,
+    0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01,
+    0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x2a, 0x04,
+    0x28, 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x02, 0x88,
+    0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x06, 0x01, 0xb0, 0x1d, 0x0f, 0x00, 0x00,
+    0x00, 0x00, 0x4d, 0x00, 0x61, 0x00, 0x63, 0x00,
+    0x20, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x20, 0x00,
+    0x58, 0x00, 0x20, 0x00, 0x31, 0x00, 0x30, 0x00,
+    0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x00, 0x00,
+    0x53, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x46, 0x00,
+    0x53, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2e, 0x00,
+    0x32, 0x00, 0x00, 0x00
+};
+
+char smb2_negotiate_request[] = {
+    0x00, 0x00, 0x00, 0x6c, 0xfe, 0x53, 0x4d, 0x42,
+    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x04, 0x00,
+    0x02, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
+    0x17, 0x97, 0x90, 0x40, 0xcd, 0xf0, 0x5e, 0x31,
+    0x8d, 0xea, 0xef, 0x98, 0xcd, 0xa5, 0x08, 0xda,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x02, 0x02, 0x10, 0x02, 0x00, 0x03, 0x02, 0x03
+};
+char smb2_null_session_setup[] = {
+    0x00, 0x00, 0x00, 0xa2, 0xfe, 0x53, 0x4d, 0x42,
+    0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x02,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x58, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x48, 0x06, 0x06,
+    0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e,
+    0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a,
+    0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02,
+    0x02, 0x0a, 0xa2, 0x2a, 0x04, 0x28, 0x4e, 0x54,
+    0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x15, 0x82, 0x88, 0x62, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01,
+    0xb0, 0x1d, 0x0f, 0x00, 0x00, 0x00
+};
 
 /*****************************************************************************
  *
@@ -135,34 +197,34 @@ smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, s
     size_t original_offset = offset;
     size_t c;
     
-    if (max > offset + (smb->smb1.param_length - smb->smb1.param_offset))
-        max = offset + (smb->smb1.param_length - smb->smb1.param_offset);
+    if (max > offset + (smb->hdr.smb1.param_length - smb->hdr.smb1.param_offset))
+        max = offset + (smb->hdr.smb1.param_length - smb->hdr.smb1.param_offset);
     
     //printf("\n max=%04x  \n", *(unsigned short*)(px+max));
     
     /* Find the correct header */
-    for (c=0; params[c].command != smb->smb1.command && params[c].command != 0xFF; c++)
+    for (c=0; params[c].command != smb->hdr.smb1.command && params[c].command != 0xFF; c++)
         ;
     
-    for (; offset < max; offset++, smb->smb1.param_offset++) {
+    for (; offset < max; offset++, smb->hdr.smb1.param_offset++) {
         again:
         
-        //printf("\n%u/%u %u\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c);
+        //printf("\n%u/%u %u\n", (unsigned)smb->hdr.smb1.param_offset, (unsigned)smb->hdr.smb1.param_length, (unsigned)c);
         
         /* If we've gone past our header, just continue consuming bytes */
-        if (params[c].command != smb->smb1.command)
+        if (params[c].command != smb->hdr.smb1.command)
             continue;
         
         /* If we've gone past the end of this field, goto next field */
-        if (params[c].external_offset + params[c].external_length <= smb->smb1.param_offset) {
+        if (params[c].external_offset + params[c].external_length <= smb->hdr.smb1.param_offset) {
             c++;
             goto again;
         }
         /* Haven't reached the next field yet */
-        if (params[c].external_offset > smb->smb1.param_offset)
+        if (params[c].external_offset > smb->hdr.smb1.param_offset)
             continue;
         
-        //printf("\n%u/%u %u [%02x]\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c, px[offset]);
+        //printf("\n%u/%u %u [%02x]\n", (unsigned)smb->hdr.smb1.param_offset, (unsigned)smb->hdr.smb1.param_length, (unsigned)c, px[offset]);
         
         /* Shift the type, because all fields little-endian */
         switch (params[c].internal_type) {
@@ -171,29 +233,29 @@ smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, s
                 break;
             case IT_uint8:
             {
-                uint8_t *x = memberat(uint8_t, &smb->parms1, params[c].internal_offset);
+                uint8_t *x = memberat(uint8_t, &smb->parms, params[c].internal_offset);
                 *x = px[offset];
             }
                 break;
             case IT_uint16:
             {
-                uint16_t *x = memberat(uint16_t, &smb->parms1, params[c].internal_offset);
+                uint16_t *x = memberat(uint16_t, &smb->parms, params[c].internal_offset);
                 //*x <<= 8;
-                *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8);
+                *x |= px[offset] << ((smb->hdr.smb1.param_offset - params[c].external_offset)*8);
             }
                 break;
             case IT_uint32:
             {
-                uint32_t *x = memberat(uint32_t, &smb->parms1, params[c].internal_offset);
+                uint32_t *x = memberat(uint32_t, &smb->parms, params[c].internal_offset);
                 //*x <<= 8;
-                *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8);
+                *x |= px[offset] << ((smb->hdr.smb1.param_offset - params[c].external_offset)*8);
             }
                 break;
             case IT_uint64:
             {
-                uint64_t *x = memberat(uint64_t, &smb->parms1, params[c].internal_offset);
+                uint64_t *x = memberat(uint64_t, &smb->parms, params[c].internal_offset);
                 //*x <<= 8;
-                *x |= (uint64_t)px[offset] << (uint64_t)((smb->smb1.param_offset - params[c].external_offset)*8);
+                *x |= (uint64_t)px[offset] << (uint64_t)((smb->hdr.smb1.param_offset - params[c].external_offset)*8);
             }
                 break;
         }
@@ -218,123 +280,898 @@ convert_windows_time(long long int filetime)
 
 /*****************************************************************************
  *****************************************************************************/
-enum {
-    D_START,
-    D_NEGOT_CHALLENGE,
-    D_NEGOT_DOMAINA,
-    D_NEGOT_NAMEA,
-    D_NEGOT_DOMAIN1,
-    D_NEGOT_DOMAIN2,
-    D_NEGOT_NAME1,
-    D_NEGOT_NAME2,
-    D_NEGOT_END,
-    
-    D_UNKNOWN,
-};
 
-/*****************************************************************************
- *****************************************************************************/
-static void
-name_append_char(struct BannerOutput *banout, unsigned c)
-{
-    if (c & 0xFF80)
-        banout_append_char(banout, PROTO_SMB, '.');
-    else if (isalnum(c))
-        banout_append_char(banout, PROTO_SMB, c);
-    else switch (c) {
-        case '-':
-        case '_':
-        case '$':
-        case '*':
-            banout_append_char(banout, PROTO_SMB, c);
-            break;
-        default:
-            banout_append_char(banout, PROTO_SMB, '.');
-            break;
-            
-    }
-}
+
+
 /*****************************************************************************
  *****************************************************************************/
 static size_t
-smb1_parse_data(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+smb1_parse_negotiate1(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
 {
     size_t original_offset = offset;
-    unsigned state = smb->smb1.byte_state;
-    
+    unsigned state = smb->hdr.smb1.byte_state;
+    enum {
+        D_NEGOT_CHALLENGE,
+        D_NEGOT_DOMAINA,
+        D_NEGOT_NAMEA,
+        D_NEGOT_DOMAINU1,
+        D_NEGOT_DOMAINU2,
+        D_NEGOT_DOMAIN1,
+        D_NEGOT_DOMAIN2,
+        D_NEGOT_NAMEU1,
+        D_NEGOT_NAMEU2,
+        D_NEGOT_NAME1,
+        D_NEGOT_NAME2,
+        D_NEGOT_END,
+        
+        D_UNKNOWN,
+    };
     
-    if (max > offset + (smb->smb1.byte_count - smb->smb1.byte_offset))
-        max = offset + (smb->smb1.byte_count - smb->smb1.byte_offset);
+    if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset))
+        max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset);
     
     for (;offset<max; offset++)
     switch (state) {
-        case D_START:
-            state = D_UNKNOWN;
-            break;
         case D_NEGOT_CHALLENGE:
-            if (smb->parms1.negotiate.ChallengeLength == 0) {
-                if (smb->smb1.flags2 & 0x0080) {
-                    state = D_NEGOT_DOMAIN1;
+            if (smb->parms.negotiate.ChallengeLength == 0) {
+                if (smb->hdr.smb1.flags2 & 0x8000) {
+                    state = D_NEGOT_DOMAINU1;
                 } else {
                     state = D_NEGOT_DOMAINA;
                 }
                 offset--;
             } else
-                smb->parms1.negotiate.ChallengeLength--;
+                smb->parms.negotiate.ChallengeLength--;
+            break;
+        case D_NEGOT_DOMAINU1:
+        case D_NEGOT_NAMEU1:
+            smb->hdr.smb1.unicode_char = px[offset];
+            state++;
+            break;
+        case D_NEGOT_DOMAINU2:
+            smb->hdr.smb1.unicode_char |= px[offset]<<8;
+            if (smb->hdr.smb1.unicode_char == 0) {
+                state = D_NEGOT_NAMEU1;
+            } else {
+                banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN);
+                banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                state++;
+            }
+            break;
+        case D_NEGOT_NAMEU2:
+            smb->hdr.smb1.unicode_char |= px[offset]<<8;
+            if (smb->hdr.smb1.unicode_char == 0) {
+                state = D_NEGOT_END;
+            } else {
+                banout_append(banout, PROTO_SMB, " name=", AUTO_LEN);
+                banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                state++;
+            }
             break;
         case D_NEGOT_DOMAIN1:
         case D_NEGOT_NAME1:
-            smb->smb1.unicode_char = px[offset];
+            smb->hdr.smb1.unicode_char = px[offset];
             state++;
             break;
         case D_NEGOT_DOMAIN2:
-        case D_NEGOT_DOMAINA:
         case D_NEGOT_NAME2:
-        case D_NEGOT_NAMEA:
-            smb->smb1.unicode_char |= px[offset] << 8;
-            if (state == D_NEGOT_DOMAINA || state == D_NEGOT_NAMEA)
-                smb->smb1.unicode_char >>= 8;
-            if (smb->smb1.unicode_char == 0) {
-                banout_append_char(banout, PROTO_SMB, ' ');
+            smb->hdr.smb1.unicode_char |= px[offset]<<8;
+            if (smb->hdr.smb1.unicode_char == 0) {
                 state++;
             } else {
-                name_append_char(banout, smb->smb1.unicode_char);
+                banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
                 state--;
             }
             break;
+            
+        case D_NEGOT_DOMAINA:
+        case D_NEGOT_NAMEA:
+            if (px[offset] == 0) {
+                state++;
+            } else {
+                banout_append_char(banout, PROTO_SMB, px[offset]);
+                state++;
+            }
+            break;
         default:
             break;
     }
     
-    smb->smb1.byte_state = state;
-    smb->smb1.byte_offset += (offset - original_offset);
+    smb->hdr.smb1.byte_state = state;
+    smb->hdr.smb1.byte_offset += (offset - original_offset);
     return offset - original_offset;
 }
 
 /*****************************************************************************
  *****************************************************************************/
-static void
-smb_parse_record(
-                 const struct Banner1 *banner1,
-                 void *banner1_private,
-                 struct ProtocolState *pstate,
-                 const unsigned char *px, size_t length,
-                 struct BannerOutput *banout,
+static size_t
+smb1_parse_setup1(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->hdr.smb1.byte_state;
+    enum {
+        D_PADDING,
+        D_OSA1,
+        D_OSA2,
+        D_VERSIONA1,
+        D_VERSIONA2,
+        D_DOMAINA1,
+        D_DOMAINA2,
+        D_ENDA,
+        
+        D_OSU1,
+        D_OSU2,
+        D_OSU3,
+        D_OSU4,
+        D_VERSION1,
+        D_VERSION2,
+        D_VERSION3,
+        D_VERSION4,
+        D_DOMAIN1,
+        D_DOMAIN2,
+        D_DOMAIN3,
+        D_DOMAIN4,
+        
+        D_UNKNOWN,
+    };
+    
+    if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset))
+        max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset);
+    
+    for (;offset<max; offset++) {
+        //printf("\\x%02x", px[offset]);
+        switch (state) {
+            case D_PADDING:
+                if (smb->hdr.smb1.flags2 & 0x8000) {
+                    state = D_OSU1;
+                } else {
+                    state = D_OSA1;
+                }
+                break;
+            case D_OSA1:
+                if (px[offset] == 0)
+                    state = D_VERSIONA1;
+                else {
+                    banout_append(banout, PROTO_SMB, " os=", AUTO_LEN);
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                    state = D_OSA2;
+                }
+                break;
+            case D_OSA2:
+                if (px[offset] == 0)
+                    state = D_VERSIONA1;
+                else
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                break;
+
+            case D_VERSIONA1:
+                if (px[offset] == 0)
+                    state = D_DOMAINA1;
+                else {
+                    banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN);
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                    state = D_VERSIONA2;
+                }
+                break;
+            case D_VERSIONA2:
+                if (px[offset] == 0)
+                    state = D_DOMAINA1;
+                else
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                break;
+            case D_DOMAINA1:
+                if (px[offset] == 0)
+                    state = D_UNKNOWN;
+                else {
+                    banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN);
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                    state = D_DOMAINA2;
+                }
+                break;
+            case D_DOMAINA2:
+                if (px[offset] == 0)
+                    state = D_UNKNOWN;
+                else
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                break;
+
+            case D_OSU1:
+            case D_OSU3:
+            case D_VERSION1:
+            case D_VERSION3:
+            case D_DOMAIN1:
+            case D_DOMAIN3:
+                smb->hdr.smb1.unicode_char = px[offset];
+                state++;
+                break;
+                
+            case D_OSU2:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_VERSION1;
+                else {
+                    banout_append(banout, PROTO_SMB, " os=", AUTO_LEN);
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state = D_OSU3;
+                }
+                break;
+                
+            case D_OSU4:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_VERSION1;
+                else {
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state--;
+                }
+                break;
+                
+
+            case D_VERSION2:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_DOMAIN1;
+                else {
+                    banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN);
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state = D_VERSION3;
+                }
+                break;
+
+            case D_VERSION4:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_DOMAIN1;
+                else {
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state--;
+                }
+                break;
+
+            case D_DOMAIN2:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_UNKNOWN;
+                else {
+                    banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN);
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state = D_DOMAIN3;
+                }
+                break;
+
+            case D_DOMAIN4:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_UNKNOWN;
+                else {
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state--;
+                }
+                break;
+          default:
+                break;
+        }
+    }
+    
+    smb->hdr.smb1.byte_state = state;
+    smb->hdr.smb1.byte_offset += (offset - original_offset);
+    return offset - original_offset;
+}
+/*****************************************************************************
+ *****************************************************************************/
+static size_t
+smb1_parse_setup2(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->hdr.smb1.byte_state;
+    enum {
+        D_BLOB,
+        D_PADDING,
+        D_PADDING2,
+        D_OSA1,
+        D_OSA2,
+        D_VERSIONA1,
+        D_VERSIONA2,
+        D_DOMAINA1,
+        D_DOMAINA2,
+        D_ENDA,
+        
+        D_OSU1,
+        D_OSU2,
+        D_OSU3,
+        D_OSU4,
+        D_VERSION1,
+        D_VERSION2,
+        D_VERSION3,
+        D_VERSION4,
+        D_DOMAIN1,
+        D_DOMAIN2,
+        D_DOMAIN3,
+        D_DOMAIN4,
+        
+        D_UNKNOWN,
+    };
+    
+    if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset))
+        max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset);
+    
+    for (;offset<max; offset++) {
+        //printf("\\x%02x", px[offset]);
+        switch (state) {
+            case D_BLOB:
+                if (smb->parms.setup.BlobOffset == 0) {
+                    spnego_decode_init(&smb->spnego, smb->hdr.smb2.blob_length);
+                }
+            {
+                size_t new_max = max;
+                if (new_max > offset + smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset)
+                    new_max = offset + smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset;
+                spnego_decode(&smb->spnego, px+offset, new_max-offset, banout);
+                
+                smb->parms.setup.BlobOffset += (new_max-offset);
+                offset = new_max;
+                if (smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset == 0) {
+                    offset--;
+                    state = D_PADDING;
+                }
+            }
+                break;
+            case D_PADDING:
+                /* If the blog length is odd, then there is no padding. Otherwise,
+                 * there is one byte of padding */
+                if (smb->parms.setup.BlobLength & 1)
+                    offset--;
+                state = D_PADDING2;
+                break;
+            case D_PADDING2:
+                if (smb->hdr.smb1.flags2 & 0x8000) {
+                    state = D_OSU1;
+                } else {
+                    state = D_OSA1;
+                }
+                break;
+            case D_OSA1:
+                if (px[offset] == 0)
+                    state = D_VERSIONA1;
+                else {
+                    banout_append(banout, PROTO_SMB, " os=", AUTO_LEN);
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                    state = D_OSA2;
+                }
+                break;
+            case D_OSA2:
+                if (px[offset] == 0)
+                    state = D_VERSIONA1;
+                else
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                break;
+                
+            case D_VERSIONA1:
+                if (px[offset] == 0)
+                    state = D_DOMAINA1;
+                else {
+                    banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN);
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                    state = D_VERSIONA2;
+                }
+                break;
+            case D_VERSIONA2:
+                if (px[offset] == 0)
+                    state = D_DOMAINA1;
+                else
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                break;
+            case D_DOMAINA1:
+                if (px[offset] == 0)
+                    state = D_UNKNOWN;
+                else {
+                    banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN);
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                    state = D_DOMAINA2;
+                }
+                break;
+            case D_DOMAINA2:
+                if (px[offset] == 0)
+                    state = D_UNKNOWN;
+                else
+                    banout_append_char(banout, PROTO_SMB, px[offset]);
+                break;
+                
+            case D_OSU1:
+            case D_OSU3:
+            case D_VERSION1:
+            case D_VERSION3:
+            case D_DOMAIN1:
+            case D_DOMAIN3:
+                smb->hdr.smb1.unicode_char = px[offset];
+                state++;
+                break;
+                
+            case D_OSU2:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_VERSION1;
+                else {
+                    banout_append(banout, PROTO_SMB, " os=", AUTO_LEN);
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state = D_OSU3;
+                }
+                break;
+                
+            case D_OSU4:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_VERSION1;
+                else {
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state--;
+                }
+                break;
+                
+                
+            case D_VERSION2:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_DOMAIN1;
+                else {
+                    banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN);
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state = D_VERSION3;
+                }
+                break;
+                
+            case D_VERSION4:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_DOMAIN1;
+                else {
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state--;
+                }
+                break;
+                
+            case D_DOMAIN2:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_UNKNOWN;
+                else {
+                    banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN);
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state = D_DOMAIN3;
+                }
+                break;
+                
+            case D_DOMAIN4:
+                smb->hdr.smb1.unicode_char |= px[offset]<<8;
+                if (smb->hdr.smb1.unicode_char == 0)
+                    state = D_UNKNOWN;
+                else {
+                    banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char);
+                    state--;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+    
+    smb->hdr.smb1.byte_state = state;
+    smb->hdr.smb1.byte_offset += (offset - original_offset);
+    return offset - original_offset;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static size_t
+smb1_parse_negotiate2(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->hdr.smb1.byte_state;
+    
+    
+    if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset))
+        max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset);
+    
+    for (;offset<max; offset++)
+        switch (state) {
+            case 0:
+                state = 1;
+                break;
+            default:
+                break;
+        }
+    
+    smb->hdr.smb1.byte_state = state;
+    smb->hdr.smb1.byte_offset += (offset - original_offset);
+    return offset - original_offset;
+}
+
+
+/*****************************************************************************
+ * A default parser for SMBv2 structs. The simplest implementation would be
+ * to sipmly skip the "struct_length" bytes. However, we have all this
+ * extra code to serve as a template for creating additional functions.
+ *****************************************************************************/
+static size_t
+smb2_parse_response(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->hdr.smb2.state;
+    
+    if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset))
+        max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset);
+    
+    for (;offset<max; offset++)
+        switch (state) {
+            default:
+                break;
+        }
+    
+    smb->hdr.smb2.state = state;
+    smb->hdr.smb2.offset += (offset - original_offset);
+    return offset - original_offset;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static size_t
+smb2_parse_negotiate(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->hdr.smb2.state;
+    
+    enum {
+        N_SECMOD1, N_SECMOD2,
+        N_DIALECT1, N_DIALECT2,
+        N_CONTEXTS1, N_CONTEXTS2,
+        N_GUID01, N_GUID02, N_GUID03, N_GUID04,
+        N_GUID05, N_GUID06, N_GUID07, N_GUID08,
+        N_GUID09, N_GUID10, N_GUID11, N_GUID12,
+        N_GUID13, N_GUID14, N_GUID15, N_GUID16,
+        N_CAP1, N_CAP2, N_CAP3, N_CAP4,
+        N_TRANSACTSIZE1, N_TRANSACTSIZE2, N_TRANSACTSIZE3, N_TRANSACTSIZE4,
+        N_READSIZE1, N_READSIZE2, N_READSIZE3, N_READSIZE4,
+        N_WRITESIZE1, N_WRITESIZE2, N_WRITESIZE3, N_WRITESIZE4,
+        N_TIME1, N_TIME2, N_TIME3, N_TIME4,
+        N_TIME5, N_TIME6, N_TIME7, N_TIME8,
+        N_BOOT1, N_BOOT2, N_BOOT3, N_BOOT4,
+        N_BOOT5, N_BOOT6, N_BOOT7, N_BOOT8,
+        N_BLOB_OFFSET1, N_BLOB_OFFSET2,
+        N_BLOB_LENGTH1, N_BLOB_LENGTH2,
+    };
+    /*
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |          Buffer Code          |                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |               |               |               |               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    +-+-+-+-+                                               +-+-+-+-+
+    |                             Server                            |
+    +-+-+-+-+                      GUID                     +-+-+-+-+
+    |                                                               |
+    +-+-+-+-+                                               +-+-+-+-+
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |               |               |               |               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |               |               |               |               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |               |               |               |               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |               |               |               |               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    +-+-+-+-+                  Current Time                 +-+-+-+-+
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    +-+-+-+-+                   Boot Time                   +-+-+-+-+
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |        Sec Blob Offset        |        Sec Blob Length        |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |               |               |               |               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    | Sec Blob  ANS.1/DER encoded blob containing supported authentication mechanisms
+    +-+-+-+-+...
+    */
+    
+    if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset))
+        max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset);
+    
+    for (;offset<max; offset++)
+        switch (state) {
+            case N_SECMOD1: case N_SECMOD2:
+            case N_DIALECT1: case N_DIALECT2:
+            case N_CONTEXTS1:
+                state++;
+                break;
+            case N_CONTEXTS2:
+                if (!smb->is_printed_guid)
+                    banout_append(banout, PROTO_SMB, " guid=", AUTO_LEN);
+                state++;
+                break;
+            case N_GUID01:
+            case N_GUID05:
+            case N_GUID07:
+                smb->hdr.smb2.number = px[offset];
+                state++;
+                break;
+            case N_GUID02: case N_GUID03: case N_GUID04:
+                smb->hdr.smb2.number |= px[offset] << (8*(state-N_GUID01));
+                if (state == N_GUID04 && !smb->is_printed_guid) {
+                    banout_append_hexint(banout, PROTO_SMB, smb->hdr.smb2.number, 8);
+                    banout_append_char(banout, PROTO_SMB, '-');
+                }
+                state++;
+                break;
+            case N_GUID06:
+            case N_GUID08:
+                smb->hdr.smb2.number |= px[offset] << 8;
+                if (!smb->is_printed_guid) {
+                    banout_append_hexint(banout, PROTO_SMB, smb->hdr.smb2.number, 4);
+                    banout_append_char(banout, PROTO_SMB, '-');
+                }
+                state++;
+                break;
+            case N_GUID10:
+                if (!smb->is_printed_guid) {
+                    banout_append_hexint(banout, PROTO_SMB, px[offset], 2);
+                    banout_append_char(banout, PROTO_SMB, '-');
+                }
+                state++;
+                break;
+            case N_GUID09: case N_GUID11: case N_GUID12:
+            case N_GUID13: case N_GUID14: case N_GUID15: case N_GUID16:
+                if (!smb->is_printed_guid)
+                    banout_append_hexint(banout, PROTO_SMB, px[offset], 2);
+                if (state == N_GUID16)
+                    smb->is_printed_guid = 1;
+                state++;
+                break;
+            case N_CAP1: case N_CAP2: case N_CAP3: case N_CAP4:
+            case N_TRANSACTSIZE1: case N_TRANSACTSIZE2: case N_TRANSACTSIZE3: case N_TRANSACTSIZE4:
+            case N_READSIZE1: case N_READSIZE2: case N_READSIZE3: case N_READSIZE4:
+            case N_WRITESIZE1: case N_WRITESIZE2: case N_WRITESIZE3: case N_WRITESIZE4:
+                state++;
+                break;
+            case N_TIME1: case N_TIME2: case N_TIME3: case N_TIME4:
+            case N_TIME5: case N_TIME6: case N_TIME7: case N_TIME8:
+                smb->parms.negotiate2.current_time |= ((uint64_t)px[offset]<<(uint64_t)((state-N_TIME1)*8));
+                if (state == N_TIME8 && !smb->is_printed_time) {
+                    char str[64] = "(err)";
+                    time_t timestamp = convert_windows_time(smb->parms.negotiate2.current_time);
+                    struct tm tm = {0};
+                    size_t len;
+                    
+                    gmtime_s(&tm, &timestamp);
+                    len = strftime(str, sizeof(str), " time=%Y-%m-%d %H:%M:%S ", &tm);
+                    banout_append(banout, PROTO_SMB, str, len);
+                    smb->is_printed_time = 1;
+                }
+                state++;
+                break;
+            case N_BOOT1: case N_BOOT2: case N_BOOT3: case N_BOOT4:
+            case N_BOOT5: case N_BOOT6: case N_BOOT7: case N_BOOT8:
+                smb->parms.negotiate2.boot_time |= (px[offset]<<((state-N_BOOT1)*8));
+                state++;
+                break;
+            case N_BLOB_OFFSET1:
+                smb->hdr.smb2.blob_offset = px[offset];
+                state++;
+                break;
+            case N_BLOB_OFFSET2:
+                smb->hdr.smb2.blob_offset |= (px[offset]<<8);
+                state++;
+                break;
+            case N_BLOB_LENGTH1:
+                smb->hdr.smb2.blob_length = px[offset];
+                state++;
+                break;
+            case N_BLOB_LENGTH2:
+                smb->hdr.smb2.blob_length |= (px[offset]<<8);
+                state++;
+                break;
+            default:
+                break;
+        }
+    
+    smb->hdr.smb2.state = state;
+    smb->hdr.smb2.offset += (offset - original_offset);
+    return offset - original_offset;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static size_t
+smb2_parse_setup(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->hdr.smb2.state;
+    
+    /*
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |          Buffer Code          |           Flags               |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |        Sec Blob Offset        |        Sec Blob Length        |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     | Sec Blob
+     +-+-+-+-+...
+     */
+    enum {
+        N_FLAGS1, N_FLAGS2,
+        N_BLOB_OFFSET1, N_BLOB_OFFSET2,
+        N_BLOB_LENGTH1, N_BLOB_LENGTH2,
+
+    };
+    
+    if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset))
+        max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset);
+    
+    for (;offset<max; offset++)
+        switch (state) {
+            case N_FLAGS1: case N_FLAGS2:
+                state++;
+                break;
+            case N_BLOB_OFFSET1:
+                smb->hdr.smb2.blob_offset = px[offset];
+                state++;
+                break;
+            case N_BLOB_OFFSET2:
+                smb->hdr.smb2.blob_offset |= (px[offset]<<8);
+                state++;
+                break;
+            case N_BLOB_LENGTH1:
+                smb->hdr.smb2.blob_length = px[offset];
+                state++;
+                break;
+            case N_BLOB_LENGTH2:
+                smb->hdr.smb2.blob_length |= (px[offset]<<8);
+                state++;
+                break;
+            default:
+                break;
+        }
+    
+    smb->hdr.smb2.state = state;
+    smb->hdr.smb2.offset += (offset - original_offset);
+    return offset - original_offset;
+}
+
+
+
+/*****************************************************************************
+ *****************************************************************************/
+static size_t
+smb2_parse_header(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->hdr.smb2.state;
+    enum {
+        SMB2_CRED_CHARGE1, SMB2_CRED_CHARG2,
+        SMB2_STATUS1, SMB2_STATUS2, SMB2_STATUS3, SMB2_STATUS4,
+        SMB2_OPCODE1, SMB2_OPCODE2,
+        SMB2_CRED_GRANT1, SMB2_CRED_GRANT2,
+        SMB2_FLAGS1, SMB2_FLAGS2, SMB2_FLAGS3, SMB2_FLAGS4,
+        SMB2_CHAIN_OFFSET1, SMB2_CHAIN_OFFSET2,
+        SMB2_CHAIN_OFFSET3, SMB2_CHAIN_OFFSET4,
+        SMB2_MSGID1, SMB2_MSGID2, SMB2_MSGID3, SMB2_MSGID4,
+        SMB2_MSGID5, SMB2_MSGID6, SMB2_MSGID7, SMB2_MSGID8,
+        SMB2_PID1, SMB2_PID2, SMB2_PID3, SMB2_PID4,
+        SMB2_TID1, SMB2_TID2, SMB2_TID3, SMB2_TID4,
+        SMB2_SESSID1, SMB2_SESSID2, SMB2_SESSID3, SMB2_SESSID4,
+        SMB2_SIG01, SMB2_SIG02, SMB2_SIG03, SMB2_SIG04,
+        SMB2_SIG05, SMB2_SIG06, SMB2_SIG07, SMB2_SIG08,
+        SMB2_SIG09, SMB2_SIG10, SMB2_SIG11, SMB2_SIG12,
+        SMB2_SIG13, SMB2_SIG14, SMB2_SIG15, SMB2_SIG16,
+        SMB2_ERROR
+    };
+    /*
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |     0xFE      |      'S'      |      'M'      |      'B'      |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |          Header Length        |           (padding)           |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                          NT_Status                            |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |            Opcode             |            (padding)          |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |       :S:C:P:R|               |               |               |    Flags
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                          Chain Offset                         |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                        Command Sequence-                      |
+     +-+-+-+-+-+-+                                     +-+-+-+-+-+-+-+
+     |                             Number                            |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                           Process ID                          |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                            Tree ID                            |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                                                               |
+     +-+-+-+-+                    User ID                    +-+-+-+-+
+     |                                                               |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                                                               |
+     +-+-+-+-+                                               +-+-+-+-+
+     |                                                               |
+     +-+-+-+-+                   Signature                   +-+-+-+-+
+     |                                                               |
+     +-+-+-+-+                                               +-+-+-+-+
+     |                                                               |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     */
+    
+    if (max > offset + (smb->hdr.smb2.header_length - smb->hdr.smb2.offset))
+        max = offset + (smb->hdr.smb2.header_length - smb->hdr.smb2.offset);
+    
+    for (;offset<max; offset++)
+        switch (state) {
+            case SMB2_CRED_CHARGE1: case SMB2_CRED_CHARG2:
+                state++;
+                break;
+            case SMB2_STATUS1: case SMB2_STATUS2:
+            case SMB2_STATUS3: case SMB2_STATUS4:
+                smb->hdr.smb2.ntstatus |= (px[offset] << ((state - SMB2_STATUS1)*8));
+                state++;
+                break;
+            case SMB2_OPCODE1: case SMB2_OPCODE2:
+                smb->hdr.smb2.opcode |= (px[offset] << ((state - SMB2_OPCODE1)*8));
+                state++;
+                break;
+            case SMB2_CRED_GRANT1: case SMB2_CRED_GRANT2:
+                state++;
+                break;
+            case SMB2_FLAGS1:
+                smb->hdr.smb2.flags = px[offset];
+                if ((smb->hdr.smb2.flags & 1) == 0) {
+                    banout_append(banout, PROTO_SMB, " PARSERROR[flags] ", AUTO_LEN);
+                    state = SMB2_ERROR;
+                } else
+                    state++;
+                break;
+            case SMB2_FLAGS2: case SMB2_FLAGS3: case SMB2_FLAGS4:
+            case SMB2_CHAIN_OFFSET1: case SMB2_CHAIN_OFFSET2:
+            case SMB2_CHAIN_OFFSET3: case SMB2_CHAIN_OFFSET4:
+                state++;
+                break;
+            case SMB2_MSGID1:
+            case SMB2_MSGID2: case SMB2_MSGID3: case SMB2_MSGID4:
+            case SMB2_MSGID5: case SMB2_MSGID6: case SMB2_MSGID7: case SMB2_MSGID8:
+                smb->hdr.smb2.seqno |= (px[offset] << ((state - SMB2_MSGID1)*8));
+                state++;
+                break;
+            case SMB2_PID1: case SMB2_PID2: case SMB2_PID3: case SMB2_PID4:
+            case SMB2_TID1: case SMB2_TID2: case SMB2_TID3: case SMB2_TID4:
+            case SMB2_SESSID1: case SMB2_SESSID2: case SMB2_SESSID3: case SMB2_SESSID4:
+            case SMB2_SIG01: case SMB2_SIG02: case SMB2_SIG03: case SMB2_SIG04:
+            case SMB2_SIG05: case SMB2_SIG06: case SMB2_SIG07: case SMB2_SIG08:
+            case SMB2_SIG09: case SMB2_SIG10: case SMB2_SIG11: case SMB2_SIG12:
+            case SMB2_SIG13: case SMB2_SIG14: case SMB2_SIG15: case SMB2_SIG16:
+                state++;
+                break;
+                
+            default:
+                break;
+        }
+    
+    smb->hdr.smb2.state = state;
+    smb->hdr.smb2.offset += (offset - original_offset);
+    return offset - original_offset;
+}
+
+
+/*****************************************************************************
+ *****************************************************************************/
+static size_t
+smb_parse_smb(struct SMBSTUFF *smb, const unsigned char *px, size_t max, struct BannerOutput *banout,
                  struct InteractiveData *more)
 {
     size_t len; /*scratch variables used in a couple places */
-    size_t max;
-    unsigned state = pstate->state;
-    struct SMBSTUFF *smb = &pstate->sub.smb;
-    size_t i;
+    unsigned state = smb->nbt_state;
+    size_t i = 0;
     enum {
-        NBT_TYPE,
-        NBT_FLAGS,
-        NBT_LEN1,
-        NBT_LEN2,
-        NBT_ERR,
-        NBT_DRAIN,
-        
         SMB_VER,
         SMB1_VER_S, SMB1_VER_M, SMB1_VER_B,
         
@@ -374,25 +1211,20 @@ smb_parse_record(
         
         
         SMB2_VER_S, SMB2_VER_M, SMB2_VER_B,
-        NBT_UNKNOWN,
-    };
-    
-    /*
-     * On first run thourhg, the offset will be zero
-     */
-    i = 0;
-    
-again:
-    
-    /*
-     * Make sure we don't go past the end of the NetBIOS header portion
-     */
-    max = length;
-    if (state > NBT_LEN2) {
-        if (max > i + smb->length)
-            max = i + smb->length;
-    }
+        SMB2_HDR_LEN1, SMB2_HDR_LEN2,
+        SMB2_PARSE_HEADER,
+        SMB2_STRUCT_LEN1, SMB2_STRUCT_LEN2,
+        SMB2_PARSE_STRUCT,
+        SMB2_UNTIL_BLOB,
+        SMB2_PARSE_BLOB,
+        SMB2_PARSE_REMAINDER,
+        
+        SMB_ERROR,
+     };
     
+    if (max > i + smb->nbt_length)
+        max = i + smb->nbt_length;
+
     
     /*
      * `for all bytes in the segment`
@@ -400,169 +1232,74 @@ again:
      */
     for (; i<max; i++)
     switch (state) {
-            /*
-             All session packets are of the following general structure:
-             
-             1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
-             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-             |      TYPE     |     FLAGS     |            LENGTH             |
-             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-             |                                                               |
-             /               TRAILER (Packet Type Dependent)                 /
-             |                                                               |
-             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-             */
-        case NBT_TYPE:
-            smb->nbt_type = px[i];
-            state++;
-            break;
-        case NBT_FLAGS:
-            smb->nbt_flags = px[i];
-            smb->length = 0;
-            state++;
-            break;
-        case NBT_LEN1:
-            smb->length <<= 8;
-            smb->length |= px[i];
-            state++;
-            break;
-        case NBT_LEN2:
-            smb->length <<= 8;
-            smb->length |= px[i];
-            if (max > i + smb->length)
-                max = i + smb->length;
-            
-            /*
-             00 -  SESSION MESSAGE
-             81 -  SESSION REQUEST
-             82 -  POSITIVE SESSION RESPONSE
-             83 -  NEGATIVE SESSION RESPONSE
-             84 -  RETARGET SESSION RESPONSE
-             85 -  SESSION KEEP ALIVE
-             */
-            switch (smb->nbt_type) {
-                case 0x00:
-                    state = SMB_VER;
-                    break;
-                case 0x81:
-                    banout_append(banout, PROTO_SMB, "ERR session request", AUTO_LEN);
-                    state = NBT_UNKNOWN;
-                    break;
-                case 0x82:
-                case 0x85:
-                    state = NBT_DRAIN;
-                    break;
-                case 0x83:
-                    state = NBT_ERR;
-                    break;
-                case 0x84:
-                    banout_append(banout, PROTO_SMB, "ERR retarget", AUTO_LEN);
-                    state = NBT_UNKNOWN;
-                    break;
-                default:
-                    banout_append(banout, PROTO_SMB, "ERR unknown response", AUTO_LEN);
-                    break;
-            }
-            break;
-        case NBT_ERR:
-            smb->nbt_err = px[i];
-            /*
-             80 -  Not listening on called name
-             81 -  Not listening for calling name
-             82 -  Called name not present
-             83 -  Called name present, but insufficient resources
-             8F -  Unspecified error
-             */
-            switch (smb->nbt_err) {
-                case 0x80:
-                    banout_append(banout, PROTO_SMB, "ERROR(Not listening on called name)", AUTO_LEN);
-                    break;
-                case 0x81:
-                    banout_append(banout, PROTO_SMB, "ERROR(Not listening for calling name)", AUTO_LEN);
-                    break;
-                case 0x82:
-                    banout_append(banout, PROTO_SMB, "ERROR(Called name not present)", AUTO_LEN);
-                    break;
-                case 0x83:
-                    banout_append(banout, PROTO_SMB, "ERROR(Called name present, but insufficient resources)", AUTO_LEN);
-                    break;
-                case 0x8F:
-                    banout_append(banout, PROTO_SMB, "ERROR(Unspecified error)", AUTO_LEN);
-                    break;
-                default:
-                    banout_append(banout, PROTO_SMB, "ERROR(UNKNOWN)", AUTO_LEN);
-                    break;
-
-            }
-            state = NBT_DRAIN;
-            break;
-            
-        case NBT_DRAIN:
-            state = NBT_DRAIN;
-            break;
-            
         case SMB_VER:
             switch (px[i]) {
                 case 0xFF:
-                    banout_append(banout, PROTO_SMB, "SMBv1 ", AUTO_LEN);
+                    if (!smb->is_printed_ver)
+                        banout_append(banout, PROTO_SMB, "SMBv1 ", AUTO_LEN);
+                    smb->is_printed_ver = 1;
                     state = SMB1_VER_S;
                     break;
                 case 0xFE:
-                    banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN);
+                    if (!smb->is_printed_ver)
+                        banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN);
+                    smb->is_printed_ver = 1;
                     state = SMB2_VER_S;
                     break;
                 default:
-                    banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN);
-                    state = NBT_UNKNOWN;
+                    if (!smb->is_printed_ver)
+                        banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN);
+                    smb->is_printed_ver = 1;
+                    state = SMB_ERROR;
             }
             break;
         case SMB1_VER_S:
         case SMB2_VER_S:
-            
             if (px[i] != 'S')
-                state = NBT_UNKNOWN;
+                state = SMB_ERROR;
             else
                 state++;
             break;
         case SMB1_VER_M:
         case SMB2_VER_M:
             if (px[i] != 'M')
-                state = NBT_UNKNOWN;
+                state = SMB_ERROR;
             else
                 state++;
             break;
         case SMB1_VER_B:
         case SMB2_VER_B:
             if (px[i] != 'B')
-                state = NBT_UNKNOWN;
+                state = SMB_ERROR;
             else
                 state++;
             break;
             
         case SMB1_CMD:
-            memset(&smb->smb1, 0, sizeof(smb->smb1));
-            smb->smb1.command = px[i];
+            memset(&smb->hdr, 0, sizeof(smb->hdr));
+            smb->hdr.smb1.command = px[i];
             state++;
             break;
         case SMB1_STATUS1: case SMB1_STATUS2: case SMB1_STATUS3: case SMB1_STATUS4:
-            smb->smb1.status <<= 8;
-            smb->smb1.status |= px[i];
+            smb->hdr.smb1.status <<= 8;
+            smb->hdr.smb1.status |= px[i];
             state++;
             break;
         case SMB1_FLAGS1:
-            smb->smb1.flags1 = px[i];
+            smb->hdr.smb1.flags1 = px[i];
             state++;
             break;
         case SMB1_FLAGS2:
+            smb->hdr.smb1.flags2 = px[i];
+            state++;
+            break;
         case SMB1_FLAGS3:
-            smb->smb1.flags2 <<= 8;
-            smb->smb1.flags2 |= px[i];
+            smb->hdr.smb1.flags2 |= px[i]<<8;
             state++;
             break;
         case SMB1_PID1: case SMB1_PID2:
-            smb->smb1.pid <<= 8;
-            smb->smb1.pid |= px[i];
+            smb->hdr.smb1.pid <<= 8;
+            smb->hdr.smb1.pid |= px[i];
             state++;
             break;
         case SMB1_SIG1: case SMB1_SIG2: case SMB1_SIG3: case SMB1_SIG4:
@@ -573,27 +1310,28 @@ again:
             state++;
             break;
         case SMB1_TID1: case SMB1_TID2:
-            smb->smb1.tid <<= 8;
-            smb->smb1.tid |= px[i];
+            smb->hdr.smb1.tid <<= 8;
+            smb->hdr.smb1.tid |= px[i];
             state++;
             break;
         case SMB1_PID3: case SMB1_PID4:
-            smb->smb1.pid <<= 8;
-            smb->smb1.pid |= px[i];
+            smb->hdr.smb1.pid <<= 8;
+            smb->hdr.smb1.pid |= px[i];
             state++;
             break;
         case SMB1_UID1: case SMB1_UID2:
-            smb->smb1.uid <<= 8;
-            smb->smb1.uid |= px[i];
+            smb->hdr.smb1.uid <<= 8;
+            smb->hdr.smb1.uid |= px[i];
             state++;
             break;
         case SMB1_MID1: case SMB1_MID2:
-            smb->smb1.mid <<= 8;
-            smb->smb1.mid |= px[i];
+            smb->hdr.smb1.mid <<= 8;
+            smb->hdr.smb1.mid |= px[i];
             state++;
             break;
         case SMB1_WORD_COUNT:
-            smb->smb1.param_length = px[i]*2;
+            smb->hdr.smb1.param_length = px[i]*2;
+            memset(&smb->parms, 0, sizeof(smb->parms));
             state++;
             break;
         case SMB1_PARAMETERS:
@@ -602,7 +1340,7 @@ again:
              * (meaning, up to word_count*2 bytes) */
             len = smb_params_parse(smb, px, i, max);
             i += len;
-            if (smb->smb1.param_offset < smb->smb1.param_length)
+            if (smb->hdr.smb1.param_offset < smb->hdr.smb1.param_length)
                 break;
             
             /* We've reached the end of the parameters field, so go onto 
@@ -620,44 +1358,77 @@ again:
              * actually contained
              * TODO: I should make this a function, but I'm lazy 
              */
-            switch (smb->smb1.command) {
+            switch (smb->hdr.smb1.command) {
                 case 0x72:
-                {
-                    char str[64] = "(err)";
-                    time_t timestamp = convert_windows_time(smb->parms1.negotiate.SystemTime);
-                    struct tm tm = {0};
+                    if (!smb->is_printed_time) {
+                        char str[64] = "(err)";
+                        time_t timestamp = convert_windows_time(smb->parms.negotiate.SystemTime);
+                        struct tm tm = {0};
+                        
+                        gmtime_s(&tm, &timestamp);
+                        
+                        len = strftime(str, sizeof(str), " time=%Y-%m-%d %H:%M:%S", &tm);
+                        banout_append(banout, PROTO_SMB, str, len);
+                        sprintf_s(str, sizeof(str), " TZ=%+d ", (short)smb->parms.negotiate.ServerTimeZone);
+                        banout_append(banout, PROTO_SMB, str, AUTO_LEN);
+                        
+                        smb->is_printed_time = 1;
+                    }
+                    smb->hdr.smb1.byte_state = 0;
                     
-                    gmtime_s(&tm, &timestamp);
-                    
-                    len = strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S ", &tm);
-                    banout_append(banout, PROTO_SMB, str, len);
-                    sprintf_s(str, sizeof(str), "TZ%+d ", (short)smb->parms1.negotiate.ServerTimeZone);
-                    banout_append(banout, PROTO_SMB, str, AUTO_LEN);
-                    smb->smb1.byte_state = D_NEGOT_CHALLENGE;
-                    
-                    //more->payload = smb1_null_session_setup;
-                    //more->length = sizeof(smb1_null_session_setup);
-                    
-                }
+                    if (smb->hdr.smb1.flags2 & 0x0800) {
+                        tcp_transmit(more, smb1_null_session_setup_ex, sizeof(smb1_null_session_setup_ex), 0);
+                    } else {
+                        if (smb->parms.negotiate.SessionKey) {
+                            unsigned char *buf;
+                            buf = malloc(sizeof(smb1_null_session_setup));
+                            memcpy(buf, smb1_null_session_setup, sizeof(smb1_null_session_setup));
+                            buf[0x2f] = (unsigned char)(smb->parms.negotiate.SessionKey>> 0) & 0xFF;
+                            buf[0x30] = (unsigned char)(smb->parms.negotiate.SessionKey>> 8) & 0xFF;
+                            buf[0x31] = (unsigned char)(smb->parms.negotiate.SessionKey>>16) & 0xFF;
+                            buf[0x32] = (unsigned char)(smb->parms.negotiate.SessionKey>>24) & 0xFF;
+                            tcp_transmit(more, buf, sizeof(smb1_null_session_setup), TCPTRAN_DYNAMIC);
+                        } else {
+                            tcp_transmit(more, smb1_null_session_setup, sizeof(smb1_null_session_setup), 0);
+                        }
+                    }
+                
+                    break;
+                case 0x73: /* session setup and x */
                     break;
                 default:
-                    banout_append(banout, PROTO_SMB, "-- ", AUTO_LEN);
-                    smb->smb1.byte_state = D_UNKNOWN;
+                    banout_append(banout, PROTO_SMB, " PARSERR(unknown-resonse) ", AUTO_LEN);
+                    smb->hdr.smb1.byte_state = 0;
             }
             
             break;
             
         case SMB1_BYTE_COUNT1:
-            smb->smb1.byte_count = px[i];
+            smb->hdr.smb1.byte_count = px[i];
             state++;
             break;
         case SMB1_BYTE_COUNT2:
-            smb->smb1.byte_count |= px[i]<<8;
+            smb->hdr.smb1.byte_count |= px[i]<<8;
             state++;
             break;
         case SMB1_DATA:
-            i += smb1_parse_data(smb, px, i, max, banout);
-            if (smb->smb1.byte_offset >= smb->smb1.byte_count) {
+            switch (smb->hdr.smb1.command) {
+                case 0x72:
+                    if (smb->hdr.smb1.flags2 & 0x0800)
+                        i += smb1_parse_negotiate2(smb, px, i, max, banout);
+                    else
+                        i += smb1_parse_negotiate1(smb, px, i, max, banout);
+                    break;
+                case 0x73: /* session setup and x */
+                    if (smb->hdr.smb1.flags2 & 0x0800)
+                        i += smb1_parse_setup2(smb, px, i, max, banout);
+                    else
+                        i += smb1_parse_setup1(smb, px, i, max, banout);
+                    break;
+                default:
+                    ;
+            }
+            if (smb->hdr.smb1.byte_offset >= smb->hdr.smb1.byte_count) {
                 state = SMB1_DATA_AFTER;
                 i--; /* unconsume byte because of auto-increment */
             }
@@ -672,23 +1443,309 @@ again:
             }
             break;
     
+        case SMB2_HDR_LEN1:
+            memset(&smb->hdr, 0, sizeof(smb->hdr));
+            smb->hdr.smb2.header_length = px[i];
+            state++;
+            break;
+            
+        case SMB2_HDR_LEN2:
+            smb->hdr.smb2.header_length |= (px[i]<<8);
+            if (smb->hdr.smb2.header_length < 12) {
+                banout_append(banout, PROTO_SMB, " PARSERROR[hdrlen] ", AUTO_LEN);
+                state = SMB_ERROR;
+            } else {
+                smb->hdr.smb2.offset = 6;
+                state++;
+            }
+            break;
+            
+        case SMB2_PARSE_HEADER:
+            i += smb2_parse_header(smb, px, i, max, banout);
+            if (smb->hdr.smb2.offset >= smb->hdr.smb2.header_length) {
+                state++;
+                i--;
+            }
+            break;
+        case SMB2_STRUCT_LEN1:
+            smb->hdr.smb2.struct_length = px[i];
+            state++;
+            break;
+            
+        case SMB2_STRUCT_LEN2:
+            smb->hdr.smb2.struct_length |= (px[i]<<8);
+            smb->hdr.smb2.is_dynamic = (smb->hdr.smb2.struct_length&1);
+            smb->hdr.smb2.struct_length &= 0xFFFe;
+            smb->hdr.smb2.state = 0;
+            smb->hdr.smb2.offset = 2;
+            memset(&smb->parms, 0, sizeof(smb->parms));
+            if (smb->hdr.smb2.struct_length < 2) {
+                banout_append(banout, PROTO_SMB, " PARSERROR[structlen] ", AUTO_LEN);
+                state = SMB_ERROR;
+            } else
+                state++;
+            break;
+        case SMB2_PARSE_STRUCT:
+            /*
+             * Parse the data portion
+             */
+            switch (smb->hdr.smb2.opcode) {
+                case 0x00: /* Negotiate Response */
+                    i += smb2_parse_negotiate(smb, px, i, max, banout);
+                    break;
+                case 0x01: /* Session Setup */
+                    i += smb2_parse_setup(smb, px, i, max, banout);
+                    break;
+                default:
+                    i += smb2_parse_response(smb, px, i, max, banout);
+                    break;
+            }
+            
+            /*
+             * Respond if necessary
+             */
+            if (smb->hdr.smb2.offset >= smb->hdr.smb2.struct_length) {
+                switch (smb->hdr.smb2.opcode) {
+                    case 0x00: /* negoiate response */
+                        if (smb->hdr.smb2.seqno == 0) {
+                            tcp_transmit(more, smb2_negotiate_request, sizeof(smb2_negotiate_request), 0);
+                        } else if (smb->hdr.smb2.seqno == 1) {
+                            tcp_transmit(more, smb2_null_session_setup, sizeof(smb2_null_session_setup), 0);
+                        }
+                        break;
+                    default:
+                        ;
+                }
+                i--;
+                
+                /*
+                 * Process security blob
+                 */
+                if (smb->hdr.smb2.blob_length == 0)
+                    state = SMB2_PARSE_REMAINDER;
+                else if (smb->hdr.smb2.blob_offset < smb->hdr.smb2.header_length + smb->hdr.smb2.struct_length) {
+                    printf("\n***** parse error *****\n");
+                    state = SMB2_PARSE_REMAINDER;
+                } else {
+                    smb->hdr.smb2.blob_offset -= smb->hdr.smb2.header_length;
+                    smb->hdr.smb2.blob_offset -= smb->hdr.smb2.struct_length;
+                    state = SMB2_UNTIL_BLOB;
+                }
+                
+            }
+            break;
+        case SMB2_UNTIL_BLOB:
+            if (smb->hdr.smb2.blob_offset == 0) {
+                spnego_decode_init(&smb->spnego, smb->hdr.smb2.blob_length);
+                i--;
+                state = SMB2_PARSE_BLOB;
+            } else
+                smb->hdr.smb2.blob_offset--;
+            break;
+        case SMB2_PARSE_BLOB:
+        {
+            size_t new_max = max;
+            if (new_max > i + smb->hdr.smb2.blob_length)
+                new_max = i + smb->hdr.smb2.blob_length;
+            spnego_decode(&smb->spnego, px+i, new_max-i, banout);
+            
+            smb->hdr.smb2.blob_length -= (new_max-i);
+            i = new_max;
+            if (smb->hdr.smb2.blob_length == 0) {
+                i--;
+                state = SMB2_PARSE_REMAINDER;
+            }
+        }
+            break;
+        case SMB2_PARSE_REMAINDER:
+        case SMB_ERROR:
         default:
-            i = length;
             break;
     }
-    
-    /*
-     * If there are multiple response packets, then
-     * loop around and process the next one
-     */
-    if (i < length) {
-        state = 0;
-        goto again;
-    }
 
+    smb->nbt_length -= i;
+    smb->nbt_state = state;
+    return i;
+}
+
+
+/*****************************************************************************
+
+ 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |      TYPE     |     FLAGS     |            LENGTH             |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |                                                               |
+ /               TRAILER (Packet Type Dependent)                 /
+ |                                                               |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *****************************************************************************/
+static void
+smb_parse_record(
+                 const struct Banner1 *banner1,
+                 void *banner1_private,
+                 struct ProtocolState *pstate,
+                 const unsigned char *px, size_t max,
+                 struct BannerOutput *banout,
+                 struct InteractiveData *more)
+{
+    size_t i;
+    unsigned state = pstate->state;
+    struct SMBSTUFF *smb = &pstate->sub.smb;
+
+    enum {
+        NBT_TYPE,
+        NBT_FLAGS,
+        NBT_LEN1,
+        NBT_LEN2,
+        NBT_ERR,
+        NBT_SMB,
+        NBT_DRAIN,
+        NBT_UNKNOWN,
+    };
+    
+    for (i=0; i<max; i++)
+        switch (state) {
+            case NBT_TYPE:
+                if (smb->spnego.ntlmssp.buf)
+                    ntlmssp_cleanup(&smb->spnego.ntlmssp);
+                smb->nbt_type = px[i];
+                state++;
+                break;
+            case NBT_FLAGS:
+                smb->nbt_flags = px[i] & 0xFE;
+                smb->nbt_length = px[i] & 0x01;
+                state++;
+                break;
+            case NBT_LEN1:
+                smb->nbt_length <<= 8;
+                smb->nbt_length |= px[i];
+                state++;
+                break;
+            case NBT_LEN2:
+                smb->nbt_length <<= 8;
+                smb->nbt_length |= px[i];
+                state++;
+                
+                
+                
+                /*
+                 00 -  SESSION MESSAGE
+                 81 -  SESSION REQUEST
+                 82 -  POSITIVE SESSION RESPONSE
+                 83 -  NEGATIVE SESSION RESPONSE
+                 84 -  RETARGET SESSION RESPONSE
+                 85 -  SESSION KEEP ALIVE
+                 */
+                switch (smb->nbt_type) {
+                    case 0x00:
+                        state = NBT_SMB;
+                        smb->nbt_state = 0;
+                        break;
+                    case 0x81:
+                        banout_append(banout, PROTO_SMB, " PARSERR(nbt-sess) ", AUTO_LEN);
+                        state = NBT_UNKNOWN;
+                        break;
+                    case 0x82:
+                    case 0x85:
+                        state = NBT_DRAIN;
+                        break;
+                    case 0x83:
+                        state = NBT_ERR;
+                        break;
+                    case 0x84:
+                        banout_append(banout, PROTO_SMB, " PARSERR(nbt-retarget) ", AUTO_LEN);
+                        state = NBT_UNKNOWN;
+                        break;
+                    default:
+                        banout_append(banout, PROTO_SMB, "ERR unknown response", AUTO_LEN);
+                        break;
+                }
+                break;
+            case NBT_ERR:
+                smb->nbt_err = px[i];
+                /*
+                 80 -  Not listening on called name
+                 81 -  Not listening for calling name
+                 82 -  Called name not present
+                 83 -  Called name present, but insufficient resources
+                 8F -  Unspecified error
+                 */
+                switch (smb->nbt_err) {
+                    case 0x80:
+                        banout_append(banout, PROTO_SMB, "ERROR(Not listening on called name)", AUTO_LEN);
+                        break;
+                    case 0x81:
+                        banout_append(banout, PROTO_SMB, "ERROR(Not listening for calling name)", AUTO_LEN);
+                        break;
+                    case 0x82:
+                        banout_append(banout, PROTO_SMB, "ERROR(Called name not present)", AUTO_LEN);
+                        break;
+                    case 0x83:
+                        banout_append(banout, PROTO_SMB, "ERROR(Called name present, but insufficient resources)", AUTO_LEN);
+                        break;
+                    case 0x8F:
+                        banout_append(banout, PROTO_SMB, "ERROR(Unspecified error)", AUTO_LEN);
+                        break;
+                    default:
+                        banout_append(banout, PROTO_SMB, "ERROR(UNKNOWN)", AUTO_LEN);
+                        break;
+                        
+                }
+                state = NBT_DRAIN;
+                break;
+                
+            case NBT_SMB:
+                i += smb_parse_smb(smb, px+i, max-i, banout, more);
+                if (smb->nbt_length == 0) {
+                    state = 0;
+                    i--;
+                }
+                break;
+            
+            case NBT_DRAIN:
+            case NBT_UNKNOWN:
+            default:
+                break;
+        }
+    
     pstate->state = state;
 }
 
+/*****************************************************************************
+ *****************************************************************************/
+static int negot_add_dialect(unsigned char *buf, size_t sizeof_buf, const char *dialect)
+{
+    size_t nbt_length;
+    size_t dialect_length = strlen(dialect) + 1;
+    size_t word_count;
+    size_t byte_count;
+    
+    /* Parse NetBIOS header */
+    if (sizeof_buf < 4 || sizeof_buf + 4 < dialect_length)
+        return -1;
+    if (buf[0] != 0)
+        return -1;
+    nbt_length = buf[2]<<8 | buf[3];
+    if (nbt_length <= 4 || nbt_length >= sizeof_buf - dialect_length)
+        return -1;
+    
+    /* Parse SMB header */
+    if (memcmp(buf+4, "\xFF" "SMB" "\x72", 5) != 0)
+        return -1;
+    if (nbt_length < 39)
+        return -1;
+    word_count = buf[36];
+    if (word_count != 0)
+        return -1;
+    byte_count = buf[37] | buf[38]<<8;
+    
+    
+    
+    return 0;
+}
+
 /*****************************************************************************
  *****************************************************************************/
 static void *
@@ -725,7 +1782,7 @@ smb0_hello_template[] = {
     0x00
 };
 static const char
-smb1_hello_template[] = {
+smb1x_hello_template[] = {
     0x00, 0x00, 0x00, 0x2f, 0xff, 0x53, 0x4d, 0x42,
     0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc0, /* */
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -735,6 +1792,194 @@ smb1_hello_template[] = {
     0x31, 0x32, 0x00
 };
 
+static const char
+xsmb1_hello_template[] = {
+    0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42,
+    0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+    0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02,
+    0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e,
+    0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20,
+    0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53,
+    0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f,
+    0x00
+};
+
+static const char
+smb1_hello_template[] = {
+    0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42,
+    0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+    0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02,
+    0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e,
+    0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20,
+    0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53,
+    0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f,
+    0x00
+
+};
+
+static const unsigned char
+smb2_negot_response[] = {
+    
+    0x00, 0x00, 0x01, 0xc0, 0xfe, 0x53, 0x4d, 0x42,
+    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x01, 0x00,
+    0xff, 0x02, 0x00, 0x00, 0x39, 0xf6, 0x39, 0xe2,
+    0x4b, 0xac, 0x2e, 0x4d, 0xaf, 0x5e, 0xbf, 0x1a,
+    0xfa, 0xe3, 0x2f, 0xa1, 0x07, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+    0x00, 0x00, 0x80, 0x00, 0x6e, 0xa8, 0x4f, 0x0d,
+    0xf6, 0xfe, 0xd3, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x82, 0x01, 0x3c,
+    0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02,
+    0xa0, 0x82, 0x01, 0x30, 0x30, 0x82, 0x01, 0x2c,
+    0xa0, 0x1a, 0x30, 0x18, 0x06, 0x0a, 0x2b, 0x06,
+    0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x1e,
+    0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
+    0x37, 0x02, 0x02, 0x0a, 0xa2, 0x82, 0x01, 0x0c,
+    0x04, 0x82, 0x01, 0x08, 0x4e, 0x45, 0x47, 0x4f,
+    0x45, 0x58, 0x54, 0x53, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+    0x70, 0x00, 0x00, 0x00, 0x59, 0x3d, 0x22, 0xb1,
+    0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b,
+    0xbd, 0x31, 0x9c, 0xfd, 0x0d, 0x6f, 0x2c, 0xaa,
+    0x13, 0xd3, 0xe9, 0x37, 0xdc, 0x82, 0xe6, 0x8c,
+    0xb6, 0xcd, 0xa5, 0xaf, 0xe0, 0x80, 0xae, 0xa1,
+    0xd4, 0x88, 0x5e, 0xb9, 0x55, 0x6b, 0xbc, 0x78,
+    0xd9, 0xd3, 0x3f, 0xe8, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x5c, 0x33, 0x53, 0x0d,
+    0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3,
+    0x78, 0x6e, 0xc3, 0x08, 0x4e, 0x45, 0x47, 0x4f,
+    0x45, 0x58, 0x54, 0x53, 0x03, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+    0x98, 0x00, 0x00, 0x00, 0x59, 0x3d, 0x22, 0xb1,
+    0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b,
+    0xbd, 0x31, 0x9c, 0xfd, 0x5c, 0x33, 0x53, 0x0d,
+    0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3,
+    0x78, 0x6e, 0xc3, 0x08, 0x40, 0x00, 0x00, 0x00,
+    0x58, 0x00, 0x00, 0x00, 0x30, 0x56, 0xa0, 0x54,
+    0x30, 0x52, 0x30, 0x27, 0x80, 0x25, 0x30, 0x23,
+    0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+    0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
+    0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67,
+    0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+    0x4b, 0x65, 0x79, 0x30, 0x27, 0x80, 0x25, 0x30,
+    0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+    0x04, 0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65,
+    0x6e, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e,
+    0x67, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
+    0x20, 0x4b, 0x65, 0x79,
+    
+    0x00, 0x00, 0x01, 0xc0, 0xfe, 0x53, 0x4d, 0x42,
+    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x01, 0x00,
+    0x02, 0x03, 0x00, 0x00, 0x39, 0xf6, 0x39, 0xe2,
+    0x4b, 0xac, 0x2e, 0x4d, 0xaf, 0x5e, 0xbf, 0x1a,
+    0xfa, 0xe3, 0x2f, 0xa1, 0x67, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+    0x00, 0x00, 0x80, 0x00, 0x58, 0x06, 0xe8, 0x0d,
+    0xf6, 0xfe, 0xd3, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x82, 0x01, 0x3c,
+    0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02,
+    0xa0, 0x82, 0x01, 0x30, 0x30, 0x82, 0x01, 0x2c,
+    0xa0, 0x1a, 0x30, 0x18, 0x06, 0x0a, 0x2b, 0x06,
+    0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x1e,
+    0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
+    0x37, 0x02, 0x02, 0x0a, 0xa2, 0x82, 0x01, 0x0c,
+    0x04, 0x82, 0x01, 0x08, 0x4e, 0x45, 0x47, 0x4f,
+    0x45, 0x58, 0x54, 0x53, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+    0x70, 0x00, 0x00, 0x00, 0x5a, 0x3d, 0x22, 0xb1,
+    0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b,
+    0xbd, 0x31, 0x9c, 0xfd, 0x28, 0x43, 0x2b, 0x64,
+    0xbd, 0x80, 0x88, 0x56, 0x11, 0xb5, 0xad, 0x5b,
+    0x41, 0x4c, 0x68, 0x0b, 0xf7, 0xdf, 0xac, 0x96,
+    0xfe, 0xb3, 0xed, 0x9d, 0xe2, 0x5a, 0xb5, 0x8c,
+    0x1f, 0x0e, 0xeb, 0xff, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x5c, 0x33, 0x53, 0x0d,
+    0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3,
+    0x78, 0x6e, 0xc3, 0x08, 0x4e, 0x45, 0x47, 0x4f,
+    0x45, 0x58, 0x54, 0x53, 0x03, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+    0x98, 0x00, 0x00, 0x00, 0x5a, 0x3d, 0x22, 0xb1,
+    0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b,
+    0xbd, 0x31, 0x9c, 0xfd, 0x5c, 0x33, 0x53, 0x0d,
+    0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3,
+    0x78, 0x6e, 0xc3, 0x08, 0x40, 0x00, 0x00, 0x00,
+    0x58, 0x00, 0x00, 0x00, 0x30, 0x56, 0xa0, 0x54,
+    0x30, 0x52, 0x30, 0x27, 0x80, 0x25, 0x30, 0x23,
+    0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+    0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
+    0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67,
+    0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+    0x4b, 0x65, 0x79, 0x30, 0x27, 0x80, 0x25, 0x30,
+    0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+    0x04, 0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65,
+    0x6e, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e,
+    0x67, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
+    0x20, 0x4b, 0x65, 0x79,
+    
+    0x00, 0x00, 0x01, 0x0f, 0xfe, 0x53, 0x4d, 0x42,
+    0x40, 0x00, 0x01, 0x00, 0x16, 0x00, 0x00, 0xc0,
+    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x0c,
+    0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+    0x48, 0x00, 0xc7, 0x00, 0xa1, 0x81, 0xc4, 0x30,
+    0x81, 0xc1, 0xa0, 0x03, 0x0a, 0x01, 0x01, 0xa1,
+    0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01,
+    0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x81, 0xab,
+    0x04, 0x81, 0xa8, 0x4e, 0x54, 0x4c, 0x4d, 0x53,
+    0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10,
+    0x00, 0x10, 0x00, 0x38, 0x00, 0x00, 0x00, 0x15,
+    0x82, 0x8a, 0x62, 0x38, 0xe7, 0x63, 0xbc, 0xc7,
+    0xfa, 0xc9, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x48,
+    0x00, 0x00, 0x00, 0x0a, 0x00, 0xee, 0x42, 0x00,
+    0x00, 0x00, 0x0f, 0x42, 0x00, 0x52, 0x00, 0x4f,
+    0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x53,
+    0x00, 0x4b, 0x00, 0x02, 0x00, 0x10, 0x00, 0x42,
+    0x00, 0x52, 0x00, 0x4f, 0x00, 0x41, 0x00, 0x44,
+    0x00, 0x45, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x01,
+    0x00, 0x10, 0x00, 0x42, 0x00, 0x52, 0x00, 0x4f,
+    0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x53,
+    0x00, 0x4b, 0x00, 0x04, 0x00, 0x10, 0x00, 0x62,
+    0x00, 0x72, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x64,
+    0x00, 0x65, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x03,
+    0x00, 0x10, 0x00, 0x62, 0x00, 0x72, 0x00, 0x6f,
+    0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73,
+    0x00, 0x6b, 0x00, 0x07, 0x00, 0x08, 0x00, 0x9c,
+    0x8b, 0x80, 0x0e, 0xf6, 0xfe, 0xd3, 0x01, 0x00,
+    0x00, 0x00, 0x00,
+
+    
+    
+};
+
 /*****************************************************************************
  *****************************************************************************/
 static int
@@ -785,6 +2030,32 @@ smb_selftest(void)
 
     };
     
+    /*
+     * SMBv2 negotiate response
+     */
+    banner1 = banner1_create();
+    banout_init(banout1);
+    memset(&state[0], 0, sizeof(state[0]));
+    
+    smb_parse_record(banner1,
+                     0,
+                     state,
+                     smb2_negot_response,
+                     sizeof(smb2_negot_response),
+                     banout1,
+                     &more);
+    x = banout_is_contains(banout1, PROTO_SMB,
+                           "SHIPBAR");
+    if (!x) {
+        printf("smb parser failure: google.com\n");
+        return 1;
+    }
+    banner1_destroy(banner1);
+    banout_release(banout1);
+    
+    /*
+     * SMBv1 negotiate response
+     */
     banner1 = banner1_create();
     banout_init(banout1);
     memset(&state[0], 0, sizeof(state[0]));
@@ -805,6 +2076,8 @@ smb_selftest(void)
     banner1_destroy(banner1);
     banout_release(banout1);
     
+    
+    
     /*
      *  LET'S FUZZ THIS CRAP!!!
      *
@@ -841,6 +2114,16 @@ smb_selftest(void)
     return 0;
 }
 
+/*****************************************************************************
+ *****************************************************************************/
+static void
+smb_cleanup(struct ProtocolState *pstate)
+{
+    struct SMBSTUFF *smb = &pstate->sub.smb;
+    if (smb->spnego.ntlmssp.buf)
+        ntlmssp_cleanup(&smb->spnego.ntlmssp);
+}
+
 /*****************************************************************************
  * This is the 'plugin' structure that registers callbacks for this parser in
  * the main system.
@@ -850,11 +2133,13 @@ struct ProtocolParserStream banner_smb0 = {
     smb_selftest,
     smb_init,
     smb_parse_record,
+    smb_cleanup
 };
 struct ProtocolParserStream banner_smb1 = {
     "smb", 445, smb1_hello_template, sizeof(smb1_hello_template), 0,
     smb_selftest,
     smb_init,
     smb_parse_record,
+    smb_cleanup
 };
 
diff --git a/src/proto-smtp.c b/src/proto-smtp.c
index d47986c..6a78eef 100644
--- a/src/proto-smtp.c
+++ b/src/proto-smtp.c
@@ -94,8 +94,7 @@ smtp_parse(  const struct Banner1 *banner1,
                     continue;
                 else if (px[i] == '\n') {
                     if (smtp->is_last) {
-                        more->payload = "EHLO masscan\r\n";
-                        more->length = 14;
+                        tcp_transmit(more, "EHLO masscan\r\n", 14, 0);
                         state = 100;
                         banout_append_char(banout, PROTO_SMTP, px[i]);
                     } else {
@@ -114,8 +113,7 @@ smtp_parse(  const struct Banner1 *banner1,
                     continue;
                 else if (px[i] == '\n') {
                     if (smtp->is_last) {
-                        more->payload = "STARTTLS\r\n";
-                        more->length = 10;
+                        tcp_transmit(more, "STARTTLS\r\n", 10, 0);
                         state = 200;
                         banout_append_char(banout, PROTO_SMTP, px[i]);
                     } else {
@@ -144,8 +142,7 @@ smtp_parse(  const struct Banner1 *banner1,
                         pstate->port = (unsigned short)port;
                         state = 0;
                         
-                        more->payload = banner_ssl.hello;
-                        more->length = (unsigned)banner_ssl.hello_length;
+                        tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0);
                         
                     } else {
                         state = STATE_DONE;
diff --git a/src/proto-snmp.c b/src/proto-snmp.c
index a34135f..fd4ae9e 100644
--- a/src/proto-snmp.c
+++ b/src/proto-snmp.c
@@ -503,14 +503,14 @@ convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src)
 
     while (*src) {
         const char *next_src;
-        unsigned long id;
+        unsigned id;
         unsigned count;
         unsigned i;
 
         while (*src == '.')
             src++;
 
-        id = strtoul(src, (char**)&next_src, 0);
+        id = (unsigned)strtoul(src, (char**)&next_src, 0);
         if (src == next_src)
             break;
         else
diff --git a/src/proto-spnego.h b/src/proto-spnego.h
new file mode 100644
index 0000000..0c25665
--- /dev/null
+++ b/src/proto-spnego.h
@@ -0,0 +1,28 @@
+#ifndef PROTO_SPNEGO_H
+#define PROTO_SPNEGO_H
+
+#include "proto-x509.h"
+#include "proto-ntlmssp.h"
+
+struct SpnegoDecode
+{
+    /*
+     * ====== KLUDGE ALERT: there's no generic ASN.1 encoding, it's specific to
+     * ====== x.509 parsing, so therefore we are just going to overload that
+     * ====== a bit until we move the code out into it's own ASN.1 module
+     */
+    struct CertDecode x509[1];
+    
+    struct NtlmsspDecode ntlmssp;
+};
+
+void
+spnego_decode_init(struct SpnegoDecode *x, size_t length);
+
+void
+spnego_decode(struct SpnegoDecode *x,
+            const unsigned char *px, size_t length,
+            struct BannerOutput *banout);
+
+#endif
+
diff --git a/src/proto-ssl.c b/src/proto-ssl.c
index 9982ec9..7942f2e 100644
--- a/src/proto-ssl.c
+++ b/src/proto-ssl.c
@@ -571,8 +571,7 @@ parse_handshake(
             static const char heartbleed_request[] = 
                 "\x15\x03\x02\x00\x02\x01\x80"
                 "\x18\x03\x02\x00\x03\x01" "\x40\x00";
-            more->payload = heartbleed_request;
-            more->length = sizeof(heartbleed_request)-1;
+            tcp_transmit(more, heartbleed_request, sizeof(heartbleed_request)-1, 0);
         }
         DROPDOWN(i,length,state);
 
diff --git a/src/proto-tcp.c b/src/proto-tcp.c
index f04f1df..339e228 100644
--- a/src/proto-tcp.c
+++ b/src/proto-tcp.c
@@ -20,6 +20,7 @@
 #include "proto-banner1.h"
 #include "proto-ssl.h"
 #include "proto-http.h"
+#include "proto-smb.h"
 #include "output.h"
 #include "string_s.h"
 #include "main-globals.h"
@@ -53,6 +54,11 @@ struct TCP_Control_Block
 
     unsigned char ttl;
     unsigned tcpstate:4;
+    
+    /* If the payload we've sent was dynamically allocated with
+     * malloc() from the heap, in which case we'll have to free()
+     * it. (Most payloads are static memory) */
+    unsigned is_payload_dynamic:1;
 
 
     unsigned short payload_length;
@@ -512,8 +518,6 @@ tcpcon_destroy_tcb(
     
     UNUSEDPARM(reason);
 
-//printf("." "tcb age = %u-sec, reason=%u                                   \n", time(0) - tcb->when_created, reason);
-
     /*
      * The TCB doesn't point to it's location in the table. Therefore, we
      * have to do a lookup to find the head pointer in the table.
@@ -551,6 +555,15 @@ tcpcon_destroy_tcb(
      * banners.
      */
     tcpcon_flush_banners(tcpcon, tcb);
+    if (tcb->is_payload_dynamic && tcb->payload_length && tcb->payload)
+        free((void*)tcb->payload);
+    
+    /* KLUDGE: this needs to be made more elegant */
+    switch (tcb->banner1_state.app_proto) {
+        case PROTO_SMB:
+            banner_smb1.cleanup(&tcb->banner1_state);
+            break;
+    }
 
     /*
      * Unlink this from the timeout system.
@@ -1113,8 +1126,8 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon,
         {
             unsigned err;
             struct InteractiveData more;
-            more.payload = 0;
-            more.length =0;
+            
+            memset(&more, 0, sizeof(more));
 
             if ((unsigned)(tcb->seqno_them - seqno_them) > payload_length)  {
                 tcpcon_send_packet(tcpcon, tcb,
@@ -1154,10 +1167,11 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon,
             tcb->seqno_them += (unsigned)payload_length;
             
             /* acknowledge the bytes sent */
-            if (more.length) {
+            if (more.m_length) {
                 //printf("." "sending more data %u bytes\n", more.length);
-                tcpcon_send_packet(tcpcon, tcb, 0x18, more.payload, more.length, 0);
-                tcb->seqno_me += (uint32_t)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,
@@ -1197,6 +1211,11 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon,
 
         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,
diff --git a/src/proto-vnc.c b/src/proto-vnc.c
index b79478e..584d904 100644
--- a/src/proto-vnc.c
+++ b/src/proto-vnc.c
@@ -142,8 +142,8 @@ vnc_parse(  const struct Banner1 *banner1,
                         "RFB 003.008\n",
                     };
                     unsigned version = pstate->sub.vnc.version % 10;
-                    more->payload = response[version];
-                    more->length = 12;
+                    
+                    tcp_transmit(more, response[version], 12, 0);
 
                     if (version < 7)
                         /* Version 3.3: the server selects either "none" or
@@ -190,8 +190,7 @@ vnc_parse(  const struct Banner1 *banner1,
                 else if (pstate->sub.vnc.sectype == 1) {
                     /* v3.3 sectype=none
                      * We move immediately to ClientInit stage */
-                    more->payload = "\x01";
-                    more->length = 1;
+                    tcp_transmit(more, "\x01", 1, 0);
                     state = RFB_SERVERINIT;
                 } else
                     state = STATE_DONE;
@@ -201,8 +200,7 @@ vnc_parse(  const struct Banner1 *banner1,
                 pstate->sub.vnc.sectype |= px[i];
                 if (pstate->sub.vnc.sectype == 0) {
                     /* security ok, move to client init */
-                    more->payload = "\x01";
-                    more->length = 1;
+                    tcp_transmit(more, "\x01", 1, 0);
                     state = RFB_SERVERINIT;
                 } else {
                     /* error occurred, so grab error message */
@@ -241,16 +239,13 @@ vnc_parse(  const struct Banner1 *banner1,
                     banout_append(banout, PROTO_VNC_RFB, "]", AUTO_LEN);
                     if (pstate->sub.vnc.version < 7) {
                         state = RFB_SERVERINIT;
-                        more->payload = "\x01";
-                        more->length = 1;
+                        tcp_transmit(more, "\x01", 1, 0);
                     } else if (pstate->sub.vnc.version == 7) {
                         state = RFB_SERVERINIT;
-                        more->payload = "\x01\x01";
-                        more->length = 2;
+                        tcp_transmit(more, "\x01\x01", 2, 0);
                     } else {
                         state = RFB_SECURITYRESULT;
-                        more->payload = "\x01";
-                        more->length = 1;
+                        tcp_transmit(more, "\x01", 1, 0);
                     }
                 } else {
                     banout_append(banout, PROTO_VNC_RFB, "/", AUTO_LEN);
diff --git a/src/proto-x509.c b/src/proto-x509.c
index 2c99c71..6d991db 100644
--- a/src/proto-x509.c
+++ b/src/proto-x509.c
@@ -86,11 +86,13 @@ TBSCertificate  ::=  SEQUENCE  {
         }
  */
 #include "proto-x509.h"
+#include "proto-spnego.h"
 #include "proto-banout.h"
 #include "masscan-app.h"
 #include "smack.h"
 #include "logger.h"
 #include <assert.h>
+#include <ctype.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -208,7 +210,7 @@ convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src)
     /* 'for all text characters' */
     while (*src) {
         const char *next_src;
-        unsigned long id;
+        unsigned id;
         unsigned count;
         unsigned i;
 
@@ -217,7 +219,7 @@ convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src)
             src++;
 
         /* parse integer */
-        id = strtoul(src, (char**)&next_src, 0);
+        id = (unsigned)strtoul(src, (char**)&next_src, 0);
         if (src == next_src)
             break; /* invalid integer, programming error */
         else
@@ -467,6 +469,8 @@ enum X509state {
     ALGOID0_TAG,    ALGOID0_LEN,    ALGOID0_LENLEN,
     ALGOID1_TAG,    ALGOID1_LEN,    ALGOID1_LENLEN,     ALGOID1_CONTENTS0, ALGOID1_CONTENTS1,
     ENC_TAG,        ENC_LEN,        ENC_LENLEN,         ENC_CONTENTS,
+    
+    
     PADDING=254,
     ERROR=0xFFFFFFFF,
 };
@@ -1054,6 +1058,314 @@ x509_decode(struct CertDecode *x,
     if (x->state != 0xFFFFFFFF)
         x->state = state;
 }
+void
+spnego_decode(struct SpnegoDecode *spnego,
+            const unsigned char *px, size_t length,
+            struct BannerOutput *banout)
+{
+    struct CertDecode *x = spnego->x509;
+    unsigned i;
+    unsigned state = x->state;
+    
+    enum {
+        /*NegotiationToken ::= CHOICE {
+            negTokenInit    [0] NegTokenInit,
+            negTokenResp    [1] NegTokenResp
+        }*/
+        NegotiationToken_tag, len, lenlen,
+        
+        NegTokenInit_tag,
+        NegTokenInit_choice,
+        NegTokenResp_tag,
+        NegTokenResp_choice,
+        mechType_tag,
+        
+        negState_tag,
+        supportedMech_tag,
+        responseToken_tag,
+        mechListMIC_tag,
+        
+        mechTypes_tag,
+        reqFlags_tag,
+        mechToken_tag,
+        
+        mechToken_content,
+        responseToken_content,
+        mechToken_content2,
+        responseToken_content2,
+
+        UnknownContents,
+        
+        
+    };
+    
+#define GOTO_ERROR(state, i, length) (state)=0xFFFFFFFF;(i)=(length);continue
+    
+    /* 'for all bytes in the current fragment ...'
+     *   'process that byte, causing a state-transition '
+     */
+    for (i=0; i<length; i++) {
+        
+        
+        /*
+         * If we've reached the end of the current field, then we need to
+         * pop up the stack and resume parsing the parent field. Since we
+         * reach the end of several levels simultaneously, we may need to
+         * pop several levels at once
+         */
+        while (x->stack.remainings[0] == 0) {
+            if (x->stack.depth == 0)
+                return;
+            state = ASN1_pop(x);
+        }
+        
+        /*
+         * Decrement the current 'remaining' length field.
+         */
+        x->stack.remainings[0]--;
+        
+        /*
+         * Jump to the current current state
+         */
+        switch (state) {
+            case NegotiationToken_tag:
+                x->brother_state = UnknownContents;
+                switch (px[i]) {
+                    case 0xa0:
+                        x->child_state = NegTokenInit_tag;
+                        break;
+                    case 0xa1:
+                        x->child_state = NegTokenResp_tag;
+                        break;
+                    case 0x60:
+                        x->child_state = mechType_tag;
+                        break;
+                    default:
+                        x->child_state = UnknownContents;
+                        break;
+                }
+                state = len;
+                break;
+                
+            case NegTokenResp_choice:
+                /*
+                 NegTokenResp ::= SEQUENCE {
+                 negState       [0] ENUMERATED {
+                    accept-completed    (0),
+                    accept-incomplete   (1),
+                    reject              (2),
+                    request-mic         (3)
+                    }                                 OPTIONAL,
+                 -- REQUIRED in the first reply from the target
+                 supportedMech   [1] MechType      OPTIONAL,
+                 -- present only in the first reply from the target
+                 responseToken   [2] OCTET STRING  OPTIONAL,
+                 mechListMIC     [3] OCTET STRING  OPTIONAL,
+                 ...
+                 }*/
+                x->brother_state = NegTokenResp_choice;
+                switch (px[i]) {
+                    case 0xa0:
+                        x->child_state = negState_tag;
+                        break;
+                    case 0xa1:
+                        x->child_state = supportedMech_tag;
+                        break;
+                    case 0xa2:
+                        x->child_state = responseToken_tag;
+                        break;
+                    case 0xa3:
+                        x->child_state = mechListMIC_tag;
+                        break;
+                    default:
+                        x->child_state = UnknownContents;
+                        break;
+                }
+                state = len;
+                break;
+                
+            case NegTokenResp_tag:
+                if (px[i] != 0x30) {
+                    x->brother_state = UnknownContents;
+                    x->child_state = UnknownContents;
+                } else {
+                    x->brother_state = UnknownContents;
+                    x->child_state = NegTokenResp_choice;
+                }
+                state = len;
+                break;
+                
+            case NegTokenInit_choice:
+                /*
+                 NegTokenInit ::= SEQUENCE {
+                 mechTypes       [0] MechTypeList,
+                 reqFlags        [1] ContextFlags  OPTIONAL,
+                 -- inherited from RFC 2478 for backward compatibility,
+                 -- RECOMMENDED to be left out
+                 mechToken       [2] OCTET STRING  OPTIONAL,
+                 mechListMIC     [3] OCTET STRING  OPTIONAL,
+                 ...
+                 }
+                 }*/
+                x->brother_state = NegTokenInit_choice;
+                switch (px[i]) {
+                    case 0xa0:
+                        x->child_state = mechTypes_tag;
+                        break;
+                    case 0xa1:
+                        x->child_state = reqFlags_tag;
+                        break;
+                    case 0xa2:
+                        x->child_state = mechToken_tag;
+                        break;
+                    case 0xa3:
+                        x->child_state = mechListMIC_tag;
+                        break;
+                    default:
+                        x->child_state = UnknownContents;
+                        break;
+                }
+                state = len;
+                break;
+            
+            case NegTokenInit_tag:
+                if (px[i] != 0x30) {
+                    x->brother_state = UnknownContents;
+                    x->child_state = UnknownContents;
+                } else {
+                    x->brother_state = UnknownContents;
+                    x->child_state = NegTokenInit_choice;
+                }
+                state = len;
+                break;
+
+            case mechType_tag:
+                if (px[i] == 0x06) {
+                    x->brother_state = NegotiationToken_tag;
+                    x->child_state = UnknownContents;
+                } else {
+                    x->brother_state = NegotiationToken_tag;
+                    x->child_state = UnknownContents;
+                }
+                state = len;
+                break;
+            
+            case negState_tag:
+            case supportedMech_tag:
+            case mechListMIC_tag:
+            case mechTypes_tag:
+            case reqFlags_tag:
+                x->brother_state = UnknownContents;
+                x->child_state = UnknownContents;
+                state = len;
+                break;
+            
+            case responseToken_tag:
+                x->brother_state = UnknownContents;
+                x->child_state = responseToken_content;
+                state = len;
+                break;
+                
+            case mechToken_tag:
+                x->brother_state = UnknownContents;
+                x->child_state = mechToken_content;
+                state = len;
+                break;
+            
+            case mechToken_content:
+            case mechToken_content2:
+                break;
+             
+                /************************************************************************
+                 ************************************************************************
+                 ************************************************************************
+                 ************************************************************************
+                 ************************************************************************
+                 ************************************************************************
+                 ************************************************************************
+                 */
+            case responseToken_content:
+                ntlmssp_decode_init(&spnego->ntlmssp, x->stack.remainings[0] + 1);
+                state = responseToken_content2;
+                /* fall through */
+            case responseToken_content2:
+            {
+                size_t new_max = length - i;
+                
+                if (new_max > x->stack.remainings[0] + 1)
+                    new_max = x->stack.remainings[0] + 1;
+                
+                ntlmssp_decode(&spnego->ntlmssp, px+i, new_max, banout);
+                
+                x->stack.remainings[0] -= (new_max - 1);
+                if (x->stack.remainings[0] == 0) {
+                    if (spnego->ntlmssp.buf)
+                        free(spnego->ntlmssp.buf);
+                }
+            }
+                break;
+                
+            case len:
+                /* We do the same processing for all the various length fields.
+                 * There are three possible length fields:
+                 * 0x7F - for lengths 127 and below
+                 * 0x81 XX - for lengths 127 to 255
+                 * 0x82 XX XX - for length 256 to 65535
+                 * This state processes the first byte, and if it's an extended
+                 * field, switches to the correspondign xxx_LENLEN state
+                 */
+                if (px[i] & 0x80) {
+                    x->u.tag.length_of_length = px[i]&0x7F;
+                    x->u.tag.remaining = 0;
+                    state = lenlen;
+                    break;
+                } else {
+                    x->u.tag.remaining = px[i];
+                    ASN1_push(x, x->brother_state, x->u.tag.remaining);
+                    state = x->child_state;
+                    memset(&x->u, 0, sizeof(x->u));
+                    break;
+                }
+                break;
+            case lenlen:
+                /* We process all multibyte lengths the same way in this
+                 * state.
+                 */
+                
+                /* [ASN1-DER-LENGTH]
+                 * Check for strict DER compliance, which says that there should
+                 * be no leading zero bytes */
+                if (x->u.tag.remaining == 0 && px[i] == 0)
+                    x->is_der_failure = 1;
+                
+                /* parse this byte */
+                x->u.tag.remaining = (x->u.tag.remaining)<<8 | px[i];
+                x->u.tag.length_of_length--;
+                
+                /* If we aren't finished yet, loop around and grab the next */
+                if (x->u.tag.length_of_length)
+                    continue;
+                
+                /* [ASN1-DER-LENGTH]
+                 * Check for strict DER compliance, which says that for lengths
+                 * 127 and below, we need only 1 byte to encode it, not many */
+                if (x->u.tag.remaining < 128)
+                    x->is_der_failure = 1;
+                
+                /*
+                 * We have finished parsing the tag-length fields, and are now
+                 * ready to parse the 'value'. Push the current state on the
+                 * stack, then decend into the child field.
+                 */
+                ASN1_push(x, x->brother_state, x->u.tag.remaining);
+                state = x->child_state;
+                memset(&x->u, 0, sizeof(x->u));
+                break;
+            default:
+                ;
+        }
+    }
+}
 
 
 /****************************************************************************
@@ -1069,3 +1381,12 @@ x509_decode_init(struct CertDecode *x, size_t length)
     memset(x, 0, sizeof(*x));
     ASN1_push(x, 0xFFFFFFFF, length);
 }
+void
+spnego_decode_init(struct SpnegoDecode *x, size_t length)
+{
+    memset(x, 0, sizeof(*x));
+    
+    ASN1_push(x->x509, 0xFFFFFFFF, length);
+}
+
+
diff --git a/src/proto-x509.h b/src/proto-x509.h
index 638be84..e078695 100644
--- a/src/proto-x509.h
+++ b/src/proto-x509.h
@@ -47,6 +47,9 @@ struct CertDecode {
     struct {
         unsigned type;
     } subject;
+
+    unsigned child_state;
+    unsigned brother_state;
     
     /**
      * This union contains the intermediate/partial values as we are decoding
diff --git a/xcode4/masscan.xcodeproj/project.pbxproj b/xcode4/masscan.xcodeproj/project.pbxproj
index b36144a..8dce0a3 100644
--- a/xcode4/masscan.xcodeproj/project.pbxproj
+++ b/xcode4/masscan.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		110ED16820CB0BC200690C91 /* proto-ntlmssp.c in Sources */ = {isa = PBXBuildFile; fileRef = 110ED16720CB0BC200690C91 /* proto-ntlmssp.c */; };
 		11126597197A086B00DC5987 /* out-unicornscan.c in Sources */ = {isa = PBXBuildFile; fileRef = 11126596197A086B00DC5987 /* out-unicornscan.c */; };
 		112A871A1F9D8DF200D4D240 /* out-ndjson.c in Sources */ = {isa = PBXBuildFile; fileRef = 112A87191F9D8DF200D4D240 /* out-ndjson.c */; };
 		11420DD319A2D47A00DB5BFE /* proto-vnc.c in Sources */ = {isa = PBXBuildFile; fileRef = 11420DD219A2D47A00DB5BFE /* proto-vnc.c */; };
@@ -108,6 +109,9 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		110ED16520CA6A8300690C91 /* proto-spnego.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-spnego.h"; sourceTree = "<group>"; };
+		110ED16620CB0BC200690C91 /* proto-ntlmssp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-ntlmssp.h"; sourceTree = "<group>"; };
+		110ED16720CB0BC200690C91 /* proto-ntlmssp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "proto-ntlmssp.c"; sourceTree = "<group>"; };
 		11126596197A086B00DC5987 /* out-unicornscan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "out-unicornscan.c"; sourceTree = "<group>"; };
 		112A87191F9D8DF200D4D240 /* out-ndjson.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "out-ndjson.c"; sourceTree = "<group>"; };
 		113AD3B818208A1900D5E067 /* masscan-status.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "masscan-status.h"; sourceTree = "<group>"; };
@@ -391,6 +395,9 @@
 		11B360CA1F9016C00020F3A3 /* proto */ = {
 			isa = PBXGroup;
 			children = (
+				110ED16720CB0BC200690C91 /* proto-ntlmssp.c */,
+				110ED16620CB0BC200690C91 /* proto-ntlmssp.h */,
+				110ED16520CA6A8300690C91 /* proto-spnego.h */,
 				11DE129420ABC2650041135D /* proto-smb.h */,
 				11DE129520ABC2650041135D /* proto-smb.c */,
 				119AB2042051FFED008E4DDD /* proto-memcached.c */,
@@ -580,6 +587,7 @@
 				11A921DC17DBCC7E00DDFD32 /* main.c in Sources */,
 				11A921DD17DBCC7E00DDFD32 /* out-binary.c in Sources */,
 				11A921DE17DBCC7E00DDFD32 /* out-null.c in Sources */,
+				110ED16820CB0BC200690C91 /* proto-ntlmssp.c in Sources */,
 				11A921DF17DBCC7E00DDFD32 /* out-text.c in Sources */,
 				11A921E017DBCC7E00DDFD32 /* out-xml.c in Sources */,
 				11A921E117DBCC7E00DDFD32 /* output.c in Sources */,
-- 
GitLab