diff --git a/src/main-conf.c b/src/main-conf.c
index f16ecc616a2a4547b66e016a9616392f1e71a531..af8fff38006a1ca852b5091f538187b999011fdc 100644
--- a/src/main-conf.c
+++ b/src/main-conf.c
@@ -30,6 +30,10 @@
 #define min(a,b) ((a)<(b)?(a):(b))
 #endif
 
+static void masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all);
+static void masscan_set_parameter(struct Masscan *masscan, const char *name, const char *value);
+
+
 /***************************************************************************
  ***************************************************************************/
 /*static struct Range top_ports_tcp[] = {
@@ -240,15 +244,17 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i)
     else
         sprintf_s(zzz, sizeof(zzz), "[%u]", i);
 
-    fprintf(fp, "adapter%s = %s\n", zzz, masscan->nic[i].ifname);
-    if (masscan->nic[i].src.ip.first == masscan->nic[i].src.ip.last)
+    if (masscan->nic[i].ifname && masscan->nic[i].ifname[0])
+        fprintf(fp, "adapter%s = %s\n", zzz, masscan->nic[i].ifname);
+    
+    if (masscan->nic[i].src.ip.first+1 == masscan->nic[i].src.ip.last)
         fprintf(fp, "adapter-ip%s = %u.%u.%u.%u\n", zzz,
             (masscan->nic[i].src.ip.first>>24)&0xFF,
             (masscan->nic[i].src.ip.first>>16)&0xFF,
             (masscan->nic[i].src.ip.first>> 8)&0xFF,
             (masscan->nic[i].src.ip.first>> 0)&0xFF
             );
-    else
+    else if (masscan->nic[i].src.ip.first+1 < masscan->nic[i].src.ip.last)
         fprintf(fp, "adapter-ip%s = %u.%u.%u.%u-%u.%u.%u.%u\n", zzz,
             (masscan->nic[i].src.ip.first>>24)&0xFF,
             (masscan->nic[i].src.ip.first>>16)&0xFF,
@@ -260,13 +266,14 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i)
             (masscan->nic[i].src.ip.last>> 0)&0xFF
             );
 
-    fprintf(fp, "adapter-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz,
-            masscan->nic[i].my_mac[0],
-            masscan->nic[i].my_mac[1],
-            masscan->nic[i].my_mac[2],
-            masscan->nic[i].my_mac[3],
-            masscan->nic[i].my_mac[4],
-            masscan->nic[i].my_mac[5]);
+    if (masscan->nic[i].my_mac_count)
+        fprintf(fp, "adapter-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz,
+                masscan->nic[i].my_mac[0],
+                masscan->nic[i].my_mac[1],
+                masscan->nic[i].my_mac[2],
+                masscan->nic[i].my_mac[3],
+                masscan->nic[i].my_mac[4],
+                masscan->nic[i].my_mac[5]);
     if (masscan->nic[i].router_ip) {
         fprintf(fp, "router-ip%s = %u.%u.%u.%u\n", zzz,
             (masscan->nic[i].router_ip>>24)&0xFF,
@@ -274,8 +281,8 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i)
             (masscan->nic[i].router_ip>> 8)&0xFF,
             (masscan->nic[i].router_ip>> 0)&0xFF
             );
-    } else
-    fprintf(fp, "router-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz,
+    } else if (memcmp(masscan->nic[i].router_mac, "\0\0\0\0\0\0", 6) != 0)
+        fprintf(fp, "router-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz,
             masscan->nic[i].router_mac[0],
             masscan->nic[i].router_mac[1],
             masscan->nic[i].router_mac[2],
@@ -285,192 +292,6 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i)
 
 }
 
-/***************************************************************************
- * Prints the current configuration to the command-line then exits.
- * Use#1: create a template file of all setable parameters.
- * Use#2: make sure your configuration was interpreted correctly.
- ***************************************************************************/
-static void
-masscan_echo(struct Masscan *masscan, FILE *fp)
-{
-    unsigned i;
-    unsigned l = 0;
-
-    fprintf(fp, "rate = %10.2f\n", masscan->max_rate);
-    fprintf(fp, "randomize-hosts = true\n");
-    fprintf(fp, "seed = %" PRIu64 "\n", masscan->seed);
-    fprintf(fp, "shard = %u/%u\n", masscan->shard.one, masscan->shard.of);
-    if (masscan->is_banners)
-        fprintf(fp, "banners = true\n");
-    if (masscan->is_arp)
-        fprintf(fp, "arp = true\n");
-    if (masscan->is_noreset)
-        fprintf(fp, "noreset = true\n");
-
-    fprintf(fp, "# ADAPTER SETTINGS\n");
-    if (masscan->nic_count == 0)
-        masscan_echo_nic(masscan, fp, 0);
-    else {
-        for (i=0; i<masscan->nic_count; i++)
-            masscan_echo_nic(masscan, fp, i);
-    }
-
-
-    /*
-     * Output information
-     */
-    fprintf(fp, "# OUTPUT/REPORTING SETTINGS\n");
-    switch (masscan->output.format) {
-    case Output_Interactive:fprintf(fp, "output-format = interactive\n"); break;
-    case Output_List:       fprintf(fp, "output-format = list\n"); break;
-    case Output_Unicornscan:fprintf(fp, "output-format = unicornscan\n"); break;
-    case Output_XML:        fprintf(fp, "output-format = xml\n"); break;
-    case Output_Binary:     fprintf(fp, "output-format = binary\n"); break;
-    case Output_Grepable:   fprintf(fp, "output-format = grepable\n"); break;
-    case Output_JSON:       fprintf(fp, "output-format = json\n"); break;
-    case Output_NDJSON:     fprintf(fp, "output-format = ndjson\n"); break;
-    case Output_Certs:      fprintf(fp, "output-format = certs\n"); break;
-    case Output_None:       fprintf(fp, "output-format = none\n"); break;
-    case Output_Redis:
-        fprintf(fp, "output-format = redis\n");
-        fprintf(fp, "redis = %u.%u.%u.%u:%u\n",
-            (unsigned char)(masscan->redis.ip>>24),
-            (unsigned char)(masscan->redis.ip>>16),
-            (unsigned char)(masscan->redis.ip>> 8),
-            (unsigned char)(masscan->redis.ip>> 0),
-            masscan->redis.port);
-        break;
-
-    default:
-        fprintf(fp, "output-format = unknown(%u)\n", masscan->output.format);
-        break;
-    }
-    fprintf(fp, "show = %s,%s,%s\n",
-            masscan->output.is_show_open?"open":"",
-            masscan->output.is_show_closed?"closed":"",
-            masscan->output.is_show_host?"host":""
-            );
-    if (!masscan->output.is_show_open)
-        fprintf(fp, "noshow = open\n");
-    fprintf(fp, "output-filename = %s\n", masscan->output.filename);
-    if (masscan->output.is_append)
-        fprintf(fp, "output-append = true\n");
-    fprintf(fp, "rotate = %u\n", masscan->output.rotate.timeout);
-    fprintf(fp, "rotate-dir = %s\n", masscan->output.rotate.directory);
-    fprintf(fp, "rotate-offset = %u\n", masscan->output.rotate.offset);
-    fprintf(fp, "rotate-filesize = %" PRIu64 "\n", masscan->output.rotate.filesize);
-    fprintf(fp, "pcap = %s\n", masscan->pcap_filename);
-
-    /*
-     * Targets
-     */
-
-    fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n");
-    fprintf(fp, "retries = %u\n", masscan->retries);
-    fprintf(fp, "ports = ");
-    /* Disable comma generation for the first element */
-    l = 0;
-    for (i=0; i<masscan->ports.count; i++) {
-        struct Range range = masscan->ports.list[i];
-        do {
-            struct Range rrange = range;
-            unsigned done = 0;
-            if (l)
-                fprintf(fp, ",");
-            l = 1;
-            if (rrange.begin >= Templ_ICMP_echo) {
-                rrange.begin -= Templ_ICMP_echo;
-                rrange.end -= Templ_ICMP_echo;
-                fprintf(fp,"I:");
-                done = 1;
-            } else if (rrange.begin >= Templ_SCTP) {
-                rrange.begin -= Templ_SCTP;
-                rrange.end -= Templ_SCTP;
-                fprintf(fp,"S:");
-                range.begin = Templ_ICMP_echo;
-            } else if (rrange.begin >= Templ_UDP) {
-                rrange.begin -= Templ_UDP;
-                rrange.end -= Templ_UDP;
-                fprintf(fp,"U:");
-                range.begin = Templ_SCTP;
-            } else
-                range.begin = Templ_UDP;
-            rrange.end = min(rrange.end, 65535);
-            if (rrange.begin == rrange.end)
-                fprintf(fp, "%u", rrange.begin);
-            else
-                fprintf(fp, "%u-%u", rrange.begin, rrange.end);
-            if (done)
-                break;
-        } while (range.begin <= range.end);
-    }
-    fprintf(fp, "\n");
-    for (i=0; i<masscan->targets.count; i++) {
-        struct Range range = masscan->targets.list[i];
-        fprintf(fp, "range = ");
-        fprintf(fp, "%u.%u.%u.%u",
-            (range.begin>>24)&0xFF,
-            (range.begin>>16)&0xFF,
-            (range.begin>> 8)&0xFF,
-            (range.begin>> 0)&0xFF
-            );
-        if (range.begin != range.end) {
-            unsigned cidr_bits = count_cidr_bits(range);
-
-            if (cidr_bits) {
-                fprintf(fp, "/%u", cidr_bits);
-            } else
-            fprintf(fp, "-%u.%u.%u.%u",
-                (range.end>>24)&0xFF,
-                (range.end>>16)&0xFF,
-                (range.end>> 8)&0xFF,
-                (range.end>> 0)&0xFF
-                );
-        }
-        fprintf(fp, "\n");
-    }
-
-    fprintf(fp, "\n");
-    if (masscan->http_user_agent)
-        fprintf(    fp,
-                "http-user-agent = %.*s\n",
-                masscan->http_user_agent_length,
-                masscan->http_user_agent);
-
-    for (i=0; i<sizeof(masscan->http_headers)/sizeof(masscan->http_headers[0]); i++) {
-        if (masscan->http_headers[i].header_name == 0)
-            continue;
-        fprintf(    fp,
-                    "http-header[%s] = %.*s\n",
-                    masscan->http_headers[i].header_name,
-                    masscan->http_headers[i].header_value_length,
-                masscan->http_headers[i].header_value);
-    }
-
-
-    fprintf(fp, "%scapture = cert\n", masscan->is_capture_cert?"":"no");
-    fprintf(fp, "%scapture = html\n", masscan->is_capture_html?"":"no");
-    fprintf(fp, "%scapture = heartbleed\n", masscan->is_capture_heartbleed?"":"no");
-    fprintf(fp, "%scapture = ticketbleed\n", masscan->is_capture_ticketbleed?"":"no");
-    
-    if (masscan->is_hello_ssl) {
-        fprintf(fp, "hello = ssl\n");
-    }
-
-    /*
-     *  TCP payloads
-     */
-    fprintf(fp, "\n");
-    fprintf(fp, "min-packet = %u\n", masscan->min_packet_size);
-
-    {
-        struct TcpCfgPayloads *pay;
-        for (pay = masscan->tcp_payloads; pay; pay = pay->next) {
-            fprintf(fp, "hello-string[%u] = %s\n",
-                pay->port, pay->payload_base64);
-        }
-    }
-}
 
 /***************************************************************************
  ***************************************************************************/
@@ -493,10 +314,8 @@ masscan_save_state(struct Masscan *masscan)
         return;
     }
 
-    fprintf(fp, "\n# resume information\n");
-    fprintf(fp, "resume-index = %" PRIu64 "\n", masscan->resume.index);
-
-    masscan_echo(masscan, fp);
+    
+    masscan_echo(masscan, fp, 0);
 
     fclose(fp);
 }
@@ -976,16 +795,683 @@ config_top_ports(struct Masscan *masscan, unsigned n)
         65129,65389};
     struct RangeList *ports = &masscan->ports;
 
-    if (masscan->scan_type.tcp) {
-        for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++)
-            rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]);
+    if (masscan->scan_type.tcp) {
+        for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++)
+            rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]);
+    }
+    if (masscan->scan_type.udp) {
+        for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++)
+            rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]);
+    }
+}
+
+/***************************************************************************
+ ***************************************************************************/
+static int
+isInteger(const char *value)
+{
+    size_t i;
+    
+    if (value == NULL)
+        return 0;
+    
+    for (i=0; value[i]; i++)
+        if (!isdigit(value[i]&0xFF))
+            return 0;
+    return 1;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+typedef int (*SET_PARAMETER)(struct Masscan *masscan, const char *name, const char *value);
+enum {CONF_OK, CONF_WARN, CONF_ERR};
+
+static int SET_arpscan(struct Masscan *masscan, const char *name, const char *value)
+{
+    struct Range range;
+    if (masscan->echo) {
+        if (masscan->scan_type.arp || masscan->echo_all)
+            fprintf(masscan->echo, "arpscan = %s\n", masscan->scan_type.arp?"true":"false");
+        return 0;
+    }
+    range.begin = Templ_ARP;
+    range.end = Templ_ARP;
+    rangelist_add_range(&masscan->ports, range.begin, range.end);
+    masscan_set_parameter(masscan, "router-mac", "ff-ff-ff-ff-ff-ff");
+    masscan->scan_type.arp = 1;
+    LOG(5, "--arpscan\n");
+    return CONF_OK;
+}
+
+static int SET_banners(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->is_banners || masscan->echo_all)
+            fprintf(masscan->echo, "banners = %s\n", masscan->is_banners?"true":"false");
+       return 0;
+    }
+    masscan->is_banners = parseBoolean(value);
+    return CONF_OK;
+}
+
+static int SET_capture(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (!masscan->is_capture_cert || masscan->echo_all)
+            fprintf(masscan->echo, "%scapture = cert\n", masscan->is_capture_cert?"":"no");
+        if (masscan->is_capture_html || masscan->echo_all)
+            fprintf(masscan->echo, "%scapture = html\n", masscan->is_capture_html?"":"no");
+        if (masscan->is_capture_heartbleed || masscan->echo_all)
+            fprintf(masscan->echo, "%scapture = heartbleed\n", masscan->is_capture_heartbleed?"":"no");
+        if (masscan->is_capture_ticketbleed || masscan->echo_all)
+            fprintf(masscan->echo, "%scapture = ticketbleed\n", masscan->is_capture_ticketbleed?"":"no");
+        return 0;
+    }
+    if (EQUALS("capture", name)) {
+        if (EQUALS("cert", value))
+            masscan->is_capture_cert = 1;
+        else if (EQUALS("html", value))
+            masscan->is_capture_html = 1;
+        else if (EQUALS("heartbleed", value))
+            masscan->is_capture_heartbleed = 1;
+        else if (EQUALS("ticketbleed", value))
+            masscan->is_capture_ticketbleed = 1;
+        else {
+            fprintf(stderr, "FAIL: %s: unknown capture type\n", value);
+            return CONF_ERR;
+        }
+    } else if (EQUALS("nocapture", name)) {
+        if (EQUALS("cert", value))
+            masscan->is_capture_cert = 0;
+        else if (EQUALS("html", value))
+            masscan->is_capture_html = 0;
+        else if (EQUALS("heartbleed", value))
+            masscan->is_capture_heartbleed = 0;
+        else if (EQUALS("ticketbleed", value))
+            masscan->is_capture_ticketbleed = 0;
+        else {
+            fprintf(stderr, "FAIL: %s: unknown nocapture type\n", value);
+            return CONF_ERR;
+        }
+    }
+    return CONF_OK;
+}
+
+static int SET_hello(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->is_hello_ssl) {
+            fprintf(masscan->echo, "hello = ssl\n");
+        }
+        return 0;
+    }
+    if (EQUALS("ssl", value))
+        masscan->is_hello_ssl = 1;
+    else {
+        fprintf(stderr, "FAIL: %s: unknown hello type\n", value);
+        return CONF_ERR;
+    }
+    return CONF_OK;
+}
+
+static int SET_hello_file(struct Masscan *masscan, const char *name, const char *value)
+{
+    unsigned index;
+    FILE *fp;
+    int x;
+    char buf[16384];
+    char buf2[16384];
+    size_t bytes_read;
+    size_t bytes_encoded;
+    char foo[64];
+
+    if (masscan->echo) {
+        //Echoed as a string "hello-string" that was originally read
+        //from a file, not the "hello-filename"
+        return 0;
+    }
+    
+    index = ARRAY(name);
+    if (index >= 65536) {
+        fprintf(stderr, "%s: bad index\n", name);
+        return CONF_ERR;
+    }
+
+    /* When connecting via TCP, send this file */
+    x = fopen_s(&fp, value, "rb");
+    if (x != 0) {
+        LOG(0, "[FAILED] could not read hello file\n");
+        perror(value);
+        return CONF_ERR;
+    }
+    
+    bytes_read = fread(buf, 1, sizeof(buf), fp);
+    if (bytes_read == 0) {
+        LOG(0, "[FAILED] could not read hello file\n");
+        perror(value);
+        fclose(fp);
+        return CONF_ERR;
+    }
+    fclose(fp);
+    
+    bytes_encoded = base64_encode(buf2, sizeof(buf2)-1, buf, bytes_read);
+    buf2[bytes_encoded] = '\0';
+    
+    sprintf_s(foo, sizeof(foo), "hello-string[%u]", (unsigned)index);
+    
+    masscan_set_parameter(masscan, foo, buf2);
+
+    return CONF_OK;
+}
+
+static int SET_hello_string(struct Masscan *masscan, const char *name, const char *value)
+{
+    unsigned index;
+    char *value2;
+    struct TcpCfgPayloads *pay;
+
+    if (masscan->echo) {
+        for (pay = masscan->tcp_payloads; pay; pay = pay->next) {
+            fprintf(masscan->echo, "hello-string[%u] = %s\n",
+                    pay->port, pay->payload_base64);
+        }
+        return 0;
+    }
+    
+    index = ARRAY(name);
+    if (index >= 65536) {
+        fprintf(stderr, "%s: bad index\n", name);
+        exit(1);
+    }
+
+    
+    value2 = (char*)malloc(strlen(value)+1);
+    memcpy(value2, value, strlen(value)+1);
+    
+    pay = (struct TcpCfgPayloads *)malloc(sizeof(*pay));
+    
+    pay->payload_base64 = value2;
+    pay->port = index;
+    pay->next = masscan->tcp_payloads;
+    masscan->tcp_payloads = pay;
+    return CONF_OK;
+}
+
+static int SET_hello_timeout(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->tcp_hello_timeout || masscan->echo_all)
+            fprintf(masscan->echo, "hello-timeout = %u\n", masscan->tcp_hello_timeout);
+        return 0;
+    }
+    masscan->tcp_hello_timeout = (unsigned)parseInt(value);
+    return CONF_OK;
+}
+
+static int SET_min_packet(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->min_packet_size != 60 || masscan->echo_all)
+            fprintf(masscan->echo, "min-packet = %u\n", masscan->min_packet_size);
+        return 0;
+    }
+    masscan->min_packet_size = (unsigned)parseInt(value);
+    return CONF_OK;
+}
+
+
+static int SET_nobanners(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        return 0;
+    }
+    masscan->is_banners = !parseBoolean(value);
+    return CONF_OK;
+}
+
+static int SET_noreset(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->is_noreset || masscan->echo_all)
+            fprintf(masscan->echo, "noreset = %s\n", masscan->is_noreset?"true":"false");
+        return 0;
+    }
+    masscan->is_noreset = parseBoolean(value);
+    return CONF_OK;
+}
+
+static int SET_output_append(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->output.is_append || masscan->echo_all)
+            fprintf(masscan->echo, "output-append = %s\n",
+                    masscan->output.is_append?"true":"false");
+        return 0;
+    }
+    if (EQUALS("overwrite", name) || !parseBoolean(value))
+        masscan->output.is_append = 0;
+    else
+        masscan->output.is_append = 1;
+    return CONF_OK;
+}
+
+static int SET_output_filename(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->output.filename[0] || masscan->echo_all)
+            fprintf(masscan->echo, "output-filename = %s\n", masscan->output.filename);
+        return 0;
+    }
+    if (masscan->output.format == 0)
+        masscan->output.format = Output_XML; /*TODO: Why is the default XML?*/
+    strcpy_s(masscan->output.filename,
+             sizeof(masscan->output.filename),
+             value);
+    return CONF_OK;
+}
+
+static int SET_output_format(struct Masscan *masscan, const char *name, const char *value)
+{
+    enum OutputFormat x = 0;
+    if (masscan->echo) {
+        FILE *fp = masscan->echo;
+        switch (masscan->output.format) {
+            case Output_Default:    if (masscan->echo_all) fprintf(fp, "output-format = interactive\n"); break;
+            case Output_Interactive:fprintf(fp, "output-format = interactive\n"); break;
+            case Output_List:       fprintf(fp, "output-format = list\n"); break;
+            case Output_Unicornscan:fprintf(fp, "output-format = unicornscan\n"); break;
+            case Output_XML:        fprintf(fp, "output-format = xml\n"); break;
+            case Output_Binary:     fprintf(fp, "output-format = binary\n"); break;
+            case Output_Grepable:   fprintf(fp, "output-format = grepable\n"); break;
+            case Output_JSON:       fprintf(fp, "output-format = json\n"); break;
+            case Output_NDJSON:     fprintf(fp, "output-format = ndjson\n"); break;
+            case Output_Certs:      fprintf(fp, "output-format = certs\n"); break;
+            case Output_None:       fprintf(fp, "output-format = none\n"); break;
+            case Output_Redis:
+                fprintf(fp, "output-format = redis\n");
+                fprintf(fp, "redis = %u.%u.%u.%u:%u\n",
+                        (unsigned char)(masscan->redis.ip>>24),
+                        (unsigned char)(masscan->redis.ip>>16),
+                        (unsigned char)(masscan->redis.ip>> 8),
+                        (unsigned char)(masscan->redis.ip>> 0),
+                        masscan->redis.port);
+                break;
+                
+            default:
+                fprintf(fp, "output-format = unknown(%u)\n", masscan->output.format);
+                break;
+        }
+        return 0;
+    }
+    if (EQUALS("unknown(0)", value))        x = Output_Interactive;
+    else if (EQUALS("interactive", value))  x = Output_Interactive;
+    else if (EQUALS("list", value))         x = Output_List;
+    else if (EQUALS("unicornscan", value))  x = Output_Unicornscan;
+    else if (EQUALS("xml", value))          x = Output_XML;
+    else if (EQUALS("binary", value))       x = Output_Binary;
+    else if (EQUALS("greppable", value))    x = Output_Grepable;
+    else if (EQUALS("grepable", value))     x = Output_Grepable;
+    else if (EQUALS("json", value))         x = Output_JSON;
+    else if (EQUALS("ndjson", value))       x = Output_NDJSON;
+    else if (EQUALS("certs", value))        x = Output_Certs;
+    else if (EQUALS("none", value))         x = Output_None;
+    else if (EQUALS("redis", value))        x = Output_Redis;
+    else {
+        LOG(0, "FAIL: unknown output-format: %s\n", value);
+        LOG(0, "  hint: 'binary', 'xml', 'grepable', ...\n");
+        return CONF_ERR;
+    }
+    masscan->output.format = x;
+
+    return CONF_OK;
+}
+
+static int SET_output_noshow(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->echo_all) {
+            fprintf(masscan->echo, "output-noshow = %s%s%s\n",
+                    (!masscan->output.is_show_open)?"open,":"",
+                    (!masscan->output.is_show_closed)?"closed,":"",
+                    (!masscan->output.is_show_host)?"host,":""
+                    );
+        }
+        return 0;
+    }
+    for (;;) {
+        const char *val2 = value;
+        unsigned val2_len = INDEX_OF(val2, ',');
+        if (val2_len == 0)
+            break;
+        if (EQUALSx("open", val2, val2_len))
+            masscan->output.is_show_open = 0;
+        else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
+            masscan->output.is_show_closed = 0;
+        else if (EQUALSx("open", val2, val2_len))
+            masscan->output.is_show_host = 0;
+        else if (EQUALSx("all",val2,val2_len)) {
+            masscan->output.is_show_open = 0;
+            masscan->output.is_show_host = 0;
+            masscan->output.is_show_closed = 0;
+        }
+        else {
+            LOG(0, "FAIL: unknown 'noshow' spec: %.*s\n", val2_len, val2);
+            exit(1);
+        }
+        value += val2_len;
+        while (*value == ',')
+            value++;
+    }
+    return CONF_OK;
+}
+
+static int SET_output_show(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->echo_all) {
+            fprintf(masscan->echo, "output-show = %s%s%s\n",
+                    masscan->output.is_show_open?"open,":"",
+                    masscan->output.is_show_closed?"closed,":"",
+                    masscan->output.is_show_host?"host,":""
+                    );
+        }
+        return 0;
+    }
+    for (;;) {
+        const char *val2 = value;
+        unsigned val2_len = INDEX_OF(val2, ',');
+        if (val2_len == 0)
+            break;
+        if (EQUALSx("open", val2, val2_len))
+            masscan->output.is_show_open = 1;
+        else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
+            masscan->output.is_show_closed = 1;
+        else if (EQUALSx("open", val2, val2_len))
+            masscan->output.is_show_host = 1;
+        else if (EQUALSx("all",val2,val2_len)) {
+            masscan->output.is_show_open = 1;
+            masscan->output.is_show_host = 1;
+            masscan->output.is_show_closed = 1;
+        }
+        else {
+            LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2);
+            exit(1);
+        }
+        value += val2_len;
+        while (*value == ',')
+            value++;
+    }
+    return CONF_OK;
+}
+static int SET_output_show_open(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        return 0;
+    }
+    /* "open" "open-only" */
+    masscan->output.is_show_open = 1;
+    masscan->output.is_show_closed = 0;
+    masscan->output.is_show_host = 0;
+    return CONF_OK;
+}
+static int SET_pcap_filename(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->pcap_filename && masscan->pcap_filename[0])
+            fprintf(masscan->echo, "pcap-filename = %s\n", masscan->pcap_filename);
+        return 0;
+    }
+    if (value)
+        strcpy_s(masscan->pcap_filename, sizeof(masscan->pcap_filename), value);
+    return CONF_OK;
+}
+
+static int SET_randomize_hosts(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        //fprintf(masscan->echo, "randomize-hosts = true\n");
+        return 0;
+    }
+    return CONF_OK;
+}
+
+
+static int SET_rate(struct Masscan *masscan, const char *name, const char *value)
+{
+    double rate = 0.0;
+    double point = 10.0;
+    unsigned i;
+    
+    if (masscan->echo) {
+        fprintf(masscan->echo, "rate = %-10.2f\n", masscan->max_rate);
+        return 0;
+    }
+    
+    for (i=0; value[i] && value[i] != '.'; i++) {
+        char c = value[i];
+        if (c < '0' || '9' < c) {
+            fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value);
+            return CONF_ERR;
+        }
+        rate = rate * 10.0 + (c - '0');
+    }
+    
+    if (value[i] == '.') {
+        i++;
+        while (value[i]) {
+            char c = value[i];
+            if (c < '0' || '9' < c) {
+                fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n",
+                        name, value);
+                return CONF_ERR;
+            }
+            rate += (c - '0')/point;
+            point /= 10.0;
+            value++;
+        }
+    }
+    
+    masscan->max_rate = rate;
+    return CONF_OK;
+}
+
+static int SET_resume_count(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->resume.count || masscan->echo_all) {
+            fprintf(masscan->echo, "resume-count = %" PRIu64 "\n", masscan->resume.count);
+        }
+        return 0;
+    }
+    masscan->resume.count = parseInt(value);
+    return CONF_OK;
+}
+
+static int SET_resume_index(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->resume.index  || masscan->echo_all) {
+            fprintf(masscan->echo, "\n# resume information\n");
+            fprintf(masscan->echo, "resume-index = %" PRIu64 "\n", masscan->resume.index);
+        }
+        return 0;
+    }
+    masscan->resume.index = parseInt(value);
+    return CONF_OK;
+}
+
+static int SET_retries(struct Masscan *masscan, const char *name, const char *value)
+{
+    uint64_t x;
+    
+    if (masscan->echo) {
+        if (masscan->retries || masscan->echo_all)
+            fprintf(masscan->echo, "retries = %u\n", masscan->retries);
+        return 0;
+    }
+    x = strtoul(value, 0, 0);
+    if (x >= 1000) {
+        fprintf(stderr, "FAIL: retries=<n>: expected number less than 1000\n");
+        return CONF_ERR;
+    }
+    masscan->retries = (unsigned)x;
+    return CONF_OK;
+    
+}
+
+static int SET_rotate_time(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->output.rotate.timeout || masscan->echo_all)
+            fprintf(masscan->echo, "rotate = %u\n", masscan->output.rotate.timeout);
+        return 0;
+    }
+    masscan->output.rotate.timeout = (unsigned)parseTime(value);
+    return CONF_OK;
+}
+static int SET_rotate_directory(struct Masscan *masscan, const char *name, const char *value)
+{
+    char *p;
+    if (masscan->echo) {
+        if (memcmp(masscan->output.rotate.directory, ".",2) != 0 || masscan->echo_all) {
+            fprintf(masscan->echo, "rotate-dir = %s\n", masscan->output.rotate.directory);
+        }
+        return 0;
+    }
+    strcpy_s(   masscan->output.rotate.directory,
+             sizeof(masscan->output.rotate.directory),
+             value);
+    /* strip trailing slashes */
+    p = masscan->output.rotate.directory;
+    while (*p && (p[strlen(p)-1] == '/' || p[strlen(p)-1] == '/'))
+        p[strlen(p)-1] = '\0';
+    return CONF_OK;
+}
+static int SET_rotate_offset(struct Masscan *masscan, const char *name, const char *value)
+{
+    /* Time offset, otherwise output files are aligned to nearest time
+     * interval, e.g. at the start of the hour for "hourly" */
+    if (masscan->echo) {
+        if (masscan->output.rotate.offset || masscan->echo_all)
+            fprintf(masscan->echo, "rotate-offset = %u\n", masscan->output.rotate.offset);
+        return 0;
+    }
+    masscan->output.rotate.offset = (unsigned)parseTime(value);
+    return CONF_OK;
+}
+static int SET_rotate_filesize(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        if (masscan->output.rotate.filesize || masscan->echo_all)
+            fprintf(masscan->echo, "rotate-size = %" PRIu64 "\n", masscan->output.rotate.filesize);
+        return 0;
+    }
+    masscan->output.rotate.filesize = parseSize(value);
+    return CONF_OK;
+    
+}
+
+
+
+static int SET_seed(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        fprintf(masscan->echo, "seed = %" PRIu64 "\n", masscan->seed);
+        return 0;
+    }
+    if (EQUALS("time", value))
+        masscan->seed = time(0);
+    else
+        masscan->seed = parseInt(value);
+    return CONF_OK;
+}
+
+static int SET_space(struct Masscan *masscan, const char *name, const char *value)
+{
+    if (masscan->echo) {
+        fprintf(masscan->echo, "\n");
+        return 0;
     }
-    if (masscan->scan_type.udp) {
-        for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++)
-            rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]);
+    return CONF_OK;
+}
+
+static int SET_shard(struct Masscan *masscan, const char *name, const char *value)
+{
+    unsigned one = 0;
+    unsigned of = 0;
+
+    if (masscan->echo) {
+        if ((masscan->shard.one != 1 && masscan->shard.of != 1)  || masscan->echo_all)
+            fprintf(masscan->echo, "shard = %u/%u\n", masscan->shard.one, masscan->shard.of);
+        return 0;
+    }
+    while (isdigit(*value))
+        one = one*10 + (*(value++)) - '0';
+    while (ispunct(*value))
+        value++;
+    while (isdigit(*value))
+        of = of*10 + (*(value++)) - '0';
+    
+    if (one < 1) {
+        LOG(0, "FAIL: shard index can't be zero\n");
+        LOG(0, "hint   it goes like 1/4 2/4 3/4 4/4\n");
+        return CONF_ERR;
+    }
+    if (one > of) {
+        LOG(0, "FAIL: shard spec is wrong\n");
+        LOG(0, "hint   it goes like 1/4 2/4 3/4 4/4\n");
+        return CONF_ERR;
     }
+    masscan->shard.one = one;
+    masscan->shard.of = of;
+    return CONF_OK;
 }
 
+
+
+
+struct ConfigParameter {
+    const char *name;
+    SET_PARAMETER set;
+    unsigned flags;
+    const char *alts[6];
+};
+enum {F_NONE, F_BOOL};
+struct ConfigParameter config_parameters[] = {
+    {"resume-index",    SET_resume_index},
+    {"resume-count",    SET_resume_count},
+    {"seed",            SET_seed},
+    {"arpscan",         SET_arpscan,            F_BOOL, {"arp",0}},
+    {"randomize-hosts", SET_randomize_hosts,    F_BOOL},
+    {"rate",            SET_rate,               0, {"max-rate",0}},
+    {"shard",           SET_shard,              0, {"shards",0}},
+    {"banners",         SET_banners,            F_BOOL, {"banner",0}},
+    {"nobanners",       SET_nobanners,          F_BOOL, {"nobanner",0}},
+    {"retries",         SET_retries,            0, {"retry", "max-retries", "max-retry", 0}},
+    {"noreset",         SET_noreset,            F_BOOL},
+    {"pcap-filename",   SET_pcap_filename,      0, {"pcap",0}},
+    {"hello",           SET_hello},
+    {"hello-file",      SET_hello_file,         0, {"hello-filename",0}},
+    {"hello-string",    SET_hello_string},
+    {"hello-timeout",   SET_hello_timeout},
+    {"min-packet",      SET_min_packet,         0, {"min-pkt",0}},
+    {"capture",         SET_capture},
+    {"SPACE",           SET_space},
+    {"output-filename", SET_output_filename,    0, {"output-file",0}},
+    {"output-format",   SET_output_format},
+    {"output-show",     SET_output_show,        0, {"output-status", "show",0}},
+    {"output-noshow",   SET_output_noshow,      0, {"noshow",0}},
+    {"output-show-open",SET_output_show_open,   F_BOOL, {"open", "open-only", 0}},
+    {"output-append",   SET_output_append,      0, {"append-output",0}},
+    {"rotate",          SET_rotate_time,        0, {"output-rotate", "rotate-output", "rotate-time", 0}},
+    {"rotate-dir",      SET_rotate_directory,   0, {"output-rotate-dir", "rotate-directory", 0}},
+    {"rotate-offset",   SET_rotate_offset,      0, {"output-rotate-offset", 0}},
+    {"rotate-size",     SET_rotate_filesize,    0, {"output-rotate-filesize", "rotate-filesize", 0}},
+    
+    {"SPACE",           SET_space},
+    {0}
+};
+
 /***************************************************************************
  * Called either from the "command-line" parser when it sees a --parm,
  * or from the "config-file" parser for normal options.
@@ -999,7 +1485,36 @@ masscan_set_parameter(struct Masscan *masscan,
         fprintf(stderr, "%s: bad index\n", name);
         exit(1);
     }
+    
+    /*
+     * NEW:
+     * Go through configured list of parameters
+     */
+    {
+        size_t i;
+        
+        for (i=0; config_parameters[i].name; i++) {
+            if (EQUALS(config_parameters[i].name, name)) {
+                config_parameters[i].set(masscan, name, value);
+                return;
+            } else {
+                size_t j;
+                for (j=0; config_parameters[i].alts[j]; j++) {
+                    if (EQUALS(config_parameters[i].alts[j], name)) {
+                        config_parameters[i].set(masscan, name, value);
+                        return;
+                    }
+                }
+            }
+        }
+    }
 
+    /*
+     * OLD:
+     * Configure the old parameters, the ones we don't have in the new config
+     * system yet (see the NEW part above).
+     * TODO: transition all these old params to the new system
+     */
     if (EQUALS("conf", name) || EQUALS("config", name)) {
         masscan_read_config_file(masscan, value);
     } else if (EQUALS("adapter", name) || EQUALS("if", name) || EQUALS("interface", name)) {
@@ -1040,7 +1555,7 @@ masscan_set_parameter(struct Masscan *masscan,
 
         masscan->nic[index].src.ip.first = range.begin;
         masscan->nic[index].src.ip.last = range.end;
-        masscan->nic[index].src.ip.range = (uint64_t)range.end - range.begin + 1;
+        masscan->nic[index].src.ip.range = range.end - range.begin + 1;
     } else if (EQUALS("adapter-port", name) || EQUALS("source-port", name)
                || EQUALS("src-port", name)) {
         /* Send packets FROM this port number */
@@ -1124,38 +1639,6 @@ masscan_set_parameter(struct Masscan *masscan,
 
         masscan->nic[index].router_ip = range.begin;
     }
-    else if (EQUALS("rate", name) || EQUALS("max-rate", name) ) {
-        double rate = 0.0;
-        double point = 10.0;
-        unsigned i;
-
-        for (i=0; value[i] && value[i] != '.'; i++) {
-            char c = value[i];
-            if (c < '0' || '9' < c) {
-                fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value);
-                return;
-            }
-            rate = rate * 10.0 + (c - '0');
-        }
-
-        if (value[i] == '.') {
-            i++;
-            while (value[i]) {
-                char c = value[i];
-                if (c < '0' || '9' < c) {
-                    fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n",
-                            name, value);
-                    return;
-                }
-                rate += (c - '0')/point;
-                point /= 10.0;
-                value++;
-            }
-        }
-
-        masscan->max_rate = rate;
-
-    }
     else if (EQUALS("udp-ports", name) || EQUALS("udp-port", name)) {
         unsigned is_error = 0;
         masscan->scan_type.udp = 1;
@@ -1196,73 +1679,26 @@ masscan_set_parameter(struct Masscan *masscan,
             fprintf(stderr, "err\n");
             exit(1);
         }
-    }
-    else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) {
+    } else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) {
         unsigned is_error = 0;
         rangelist_parse_ports(&masscan->exclude_port, value, &is_error, 0);
         if (is_error) {
             LOG(0, "FAIL: bad exclude port: %s\n", value);
             exit(1);
         }
-    } else if (EQUALS("arp", name) || EQUALS("arpscan", name)) {
-        /* Add ICMP ping request */
-        struct Range range;
-        range.begin = Templ_ARP;
-        range.end = Templ_ARP;
-        rangelist_add_range(&masscan->ports, range.begin, range.end);
-        masscan_set_parameter(masscan, "router-mac", "ff-ff-ff-ff-ff-ff");
-        masscan->is_arp = 1; /* needs additional flag */
-        LOG(5, "--arpscan\n");
-    } else if (EQUALS("noreset", name)) {
-        if (value && value[0])
-            masscan->is_noreset = parseBoolean(value);
-        else
-            masscan->is_noreset = 1;
     } else if (EQUALS("bpf", name)) {
         size_t len = strlen(value) + 1;
         if (masscan->bpf_filter)
             free(masscan->bpf_filter);
         masscan->bpf_filter = (char*)malloc(len);
         memcpy(masscan->bpf_filter, value, len);
-    } else if (EQUALS("hello", name)) {
-        if (EQUALS("ssl", value))
-            masscan->is_hello_ssl = 1;
-        else {
-            fprintf(stderr, "FAIL: %s: unknown hello type\n", value);
-            exit(1);
-        }
-    } else if (EQUALS("capture", name)) {
-        if (EQUALS("cert", value))
-            masscan->is_capture_cert = 1;
-        else if (EQUALS("html", value))
-            masscan->is_capture_html = 1;
-        else if (EQUALS("heartbleed", value))
-            masscan->is_capture_heartbleed = 1;
-        else if (EQUALS("ticketbleed", value))
-            masscan->is_capture_ticketbleed = 1;
-        else {
-            fprintf(stderr, "FAIL: %s: unknown capture type\n", value);
-            exit(1);
-        }
-    } else if (EQUALS("nocapture", name)) {
-        if (EQUALS("cert", value))
-            masscan->is_capture_cert = 0;
-        else if (EQUALS("html", value))
-            masscan->is_capture_html = 0;
-        else if (EQUALS("heartbleed", value))
-            masscan->is_capture_heartbleed = 0;
-        else if (EQUALS("ticketbleed", value))
-            masscan->is_capture_ticketbleed = 0;
-        else {
-            fprintf(stderr, "FAIL: %s: unknown capture type\n", value);
-            exit(1);
-        }
     } else if (EQUALS("ping", name) || EQUALS("ping-sweep", name)) {
         /* Add ICMP ping request */
         struct Range range;
         range.begin = Templ_ICMP_echo;
         range.end = Templ_ICMP_echo;
         rangelist_add_range(&masscan->ports, range.begin, range.end);
+        masscan->scan_type.ping = 1;
         LOG(5, "--ping\n");
     } else if (EQUALS("range", name) || EQUALS("ranges", name)
                || EQUALS("ip", name) || EQUALS("ipv4", name)
@@ -1321,31 +1757,20 @@ masscan_set_parameter(struct Masscan *masscan,
         }
         if (masscan->op == 0)
             masscan->op = Operation_Scan;
-    } else if (EQUALS("append-output", name) || EQUALS("output-append", name)) {
-        if (EQUALS("overwrite", name))
-            masscan->output.is_append = 0;
-        else
-            masscan->output.is_append = 1;
     } else if (EQUALS("badsum", name)) {
         masscan->nmap.badsum = 1;
     } else if (EQUALS("banner1", name)) {
         banner1_test(value);
         exit(1);
-    } else if (EQUALS("banners", name) || EQUALS("banner", name)) {
-        masscan->is_banners = 1;
-    } else if (EQUALS("nobanners", name) || EQUALS("nobanner", name)) {
-        masscan->is_banners = 0;
     } else if (EQUALS("blackrock-rounds", name)) {
         masscan->blackrock_rounds = (unsigned)parseInt(value);
     } else if (EQUALS("connection-timeout", name) || EQUALS("tcp-timeout", name)) {
-        /* The timeout for "banners" TCP connections */
+        /* The timeout for banners TCP connections */
         masscan->tcp_connection_timeout = (unsigned)parseInt(value);
-    } else if (EQUALS("hello-timeout", name)) {
-        masscan->tcp_hello_timeout = (unsigned)parseInt(value);
     } else if (EQUALS("datadir", name)) {
         strcpy_s(masscan->nmap.datadir, sizeof(masscan->nmap.datadir), value);
     } else if (EQUALS("data-length", name)) {
-        unsigned x = strtoul(value, 0, 0);
+        unsigned x = (unsigned)strtoul(value, 0, 0);
         if (x >= 1514 - 14 - 40) {
             fprintf(stderr, "error: %s=<n>: expected number less than 1500\n", name);
         } else {
@@ -1359,8 +1784,8 @@ masscan_set_parameter(struct Masscan *masscan,
         fprintf(stderr, "nmap(%s): unsupported: DNS lookups too synchronous\n",
                 name);
         exit(1);
-    } else if (EQUALS("echo", name)) {
-        masscan_echo(masscan, stdout);
+    } else if (EQUALS("echo", name) || EQUALS("echo-all", name)) {
+        masscan_echo(masscan, stdout, EQUALS("echo-all", name));
         exit(0);
     } else if (EQUALS("excludefile", name)) {
         unsigned count1 = masscan->exclude_ip.count;
@@ -1381,51 +1806,6 @@ masscan_set_parameter(struct Masscan *masscan,
         masscan_set_parameter(masscan, "no-capture", "cert");
         masscan_set_parameter(masscan, "no-capture", "ticketbleed");
         masscan_set_parameter(masscan, "banners", "true");
-    } else if (EQUALS("hello-file", name)) {
-        /* When connecting via TCP, send this file */
-        FILE *fp;
-        int x;
-        char buf[16384];
-        char buf2[16384];
-        size_t bytes_read;
-        size_t bytes_encoded;
-        char foo[64];
-
-        x = fopen_s(&fp, value, "rb");
-        if (x != 0) {
-            LOG(0, "[FAILED] could not read hello file\n");
-            perror(value);
-            exit(1);
-        }
-
-        bytes_read = fread(buf, 1, sizeof(buf), fp);
-        if (bytes_read == 0) {
-            LOG(0, "[FAILED] could not read hello file\n");
-            perror(value);
-            fclose(fp);
-            exit(1);
-        }
-        fclose(fp);
-
-        bytes_encoded = base64_encode(buf2, sizeof(buf2)-1, buf, bytes_read);
-        buf2[bytes_encoded] = '\0';
-
-        sprintf_s(foo, sizeof(foo), "hello-string[%u]", (unsigned)index);
-
-        masscan_set_parameter(masscan, foo, buf2);
-    } else if (EQUALS("hello-string", name)) {
-        char *value2;
-        struct TcpCfgPayloads *pay;
-
-        value2 = (char*)malloc(strlen(value)+1);
-        memcpy(value2, value, strlen(value)+1);
-
-        pay = (struct TcpCfgPayloads *)malloc(sizeof(*pay));
-        
-        pay->payload_base64 = value2;
-        pay->port = index;
-        pay->next = masscan->tcp_payloads;
-        masscan->tcp_payloads = pay;
     } else if (EQUALS("host-timeout", name)) {
         fprintf(stderr, "nmap(%s): unsupported: this is an asynchronous tool, so no timeouts\n", name);
         exit(1);
@@ -1493,12 +1873,6 @@ masscan_set_parameter(struct Masscan *masscan,
     } else if (EQUALS("log-errors", name)) {
         fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name);
         exit(1);
-    } else if (EQUALS("min-packet", name) || EQUALS("min-pkt", name)) {
-        masscan->min_packet_size = (unsigned)parseInt(value);
-    } else if (EQUALS("max-retries", name)) {
-        masscan_set_parameter(masscan, "retries", value);
-    } else if (EQUALS("max-rate", name)) {
-        masscan_set_parameter(masscan, "rate", value);
     } else if (EQUALS("min-hostgroup", name) || EQUALS("max-hostgroup", name)) {
         fprintf(stderr, "nmap(%s): unsupported: we randomize all the groups!\n", name);
         exit(1);
@@ -1537,92 +1911,12 @@ masscan_set_parameter(struct Masscan *masscan,
         /* Run in "offline" mode where it thinks it's sending packets, but
          * it's not */
         masscan->is_offline = 1;
-    } else if (EQUALS("open", name) || EQUALS("open-only", name)) {
-        masscan->output.is_show_open = 1;
-        masscan->output.is_show_closed = 0;
-        masscan->output.is_show_host = 0;
-    } else if (EQUALS("output-status", name) || EQUALS("show", name)) {
-        for (;;) {
-            const char *val2 = value;
-            unsigned val2_len = INDEX_OF(val2, ',');
-            if (val2_len == 0)
-                break;
-            if (EQUALSx("open", val2, val2_len))
-                masscan->output.is_show_open = 1;
-            else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
-                masscan->output.is_show_closed = 1;
-            else if (EQUALSx("open", val2, val2_len))
-                masscan->output.is_show_host = 1;
-            else if (EQUALSx("all",val2,val2_len)) {
-                masscan->output.is_show_open = 1;
-                masscan->output.is_show_host = 1;
-                masscan->output.is_show_closed = 1;
-            }
-            else {
-                LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2);
-                exit(1);
-            }
-            value += val2_len;
-            while (*value == ',')
-                value++;
-        }
-    } else if (EQUALS("noshow", name)) {
-        for (;;) {
-            const char *val2 = value;
-            unsigned val2_len = INDEX_OF(val2, ',');
-            if (val2_len == 0)
-                break;
-            if (EQUALSx("open", val2, val2_len))
-                masscan->output.is_show_open = 0;
-            else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len))
-                masscan->output.is_show_closed = 0;
-            else if (EQUALSx("open", val2, val2_len))
-                masscan->output.is_show_host = 0;
-            else {
-                LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2);
-                exit(1);
-            }
-            value += val2_len;
-            while (*value == ',')
-                value++;
-        }
     } else if (EQUALS("osscan-limit", name)) {
         fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name);
         exit(1);
     } else if (EQUALS("osscan-guess", name)) {
         fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name);
         exit(1);
-    } else if (EQUALS("output-format", name)) {
-        enum OutputFormat x = 0;
-        if (EQUALS("unknown(0)", value)) {
-            x = Output_Interactive;
-        }
-        else if (EQUALS("interactive", value))  x = Output_Interactive;
-        else if (EQUALS("list", value))         x = Output_List;
-        else if (EQUALS("unicornscan", value))  x = Output_Unicornscan;
-        else if (EQUALS("xml", value))          x = Output_XML;
-        else if (EQUALS("binary", value))       x = Output_Binary;
-        else if (EQUALS("greppable", value))    x = Output_Grepable;
-        else if (EQUALS("grepable", value))     x = Output_Grepable;
-        else if (EQUALS("json", value))         x = Output_JSON;
-        else if (EQUALS("ndjson", value))       x = Output_NDJSON;
-        else if (EQUALS("certs", value))        x = Output_Certs;
-        else if (EQUALS("none", value))         x = Output_None;
-        else if (EQUALS("redis", value))        x = Output_Redis;
-        else {
-            LOG(0, "FAIL: unknown output-format: %s\n", value);
-            LOG(0, "  hint: 'binary', 'xml', 'grepable', ...\n");
-            exit(1);
-        }
-        masscan->output.format = x;
-    } else if (EQUALS("output-filename", name) || EQUALS("output-file", name)) {
-        if (masscan->output.format == 0)
-            masscan->output.format = Output_XML;
-        strcpy_s(masscan->output.filename,
-                 sizeof(masscan->output.filename), 
-                 value);
-    } else if (EQUALS("pcap", name)) {
-        strcpy_s(masscan->pcap_filename, sizeof(masscan->pcap_filename), value);
     } else if (EQUALS("packet-trace", name) || EQUALS("trace-packet", name)) {
         masscan->nmap.packet_trace = 1;
     } else if (EQUALS("privileged", name) || EQUALS("unprivileged", name)) {
@@ -1633,9 +1927,6 @@ masscan_set_parameter(struct Masscan *masscan,
     } else if (EQUALS("port-ratio", name)) {
         fprintf(stderr, "nmap(%s): unsupported\n", name);
         exit(1);
-    } else if (EQUALS("randomize-hosts", name)) {
-        /* already do that */
-        ;
     } else if (EQUALS("readrange", name) || EQUALS("readranges", name)) {
         masscan->op = Operation_ReadRange;
     } else if (EQUALS("reason", name)) {
@@ -1655,7 +1946,7 @@ masscan_set_parameter(struct Masscan *masscan,
             while (offset < max_offset && isspace(value[offset]))
                 offset++;
             if (offset+1 < max_offset && value[offset] == ':' && isdigit(value[offset+1]&0xFF)) {
-                port = strtoul(value+offset+1, 0, 0);
+                port = (unsigned)strtoul(value+offset+1, 0, 0);
                 if (port > 65535 || port == 0) {
                     LOG(0, "FAIL: bad redis port: %s\n", value+offset+1);
                     exit(1);
@@ -1674,34 +1965,6 @@ masscan_set_parameter(struct Masscan *masscan,
     } else if (EQUALS("resume", name)) {
         masscan_read_config_file(masscan, value);
         masscan_set_parameter(masscan, "output-append", "true");
-    } else if (EQUALS("resume-index", name)) {
-        masscan->resume.index = parseInt(value);
-    } else if (EQUALS("resume-count", name)) {
-        masscan->resume.count = parseInt(value);
-    } else if (EQUALS("retries", name) || EQUALS("retry", name)) {
-        unsigned x = strtoul(value, 0, 0);
-        if (x >= 1000) {
-            fprintf(stderr, "error: retries=<n>: expected number less than 1000\n");
-        } else {
-            masscan->retries = x;
-        }
-    } else if (EQUALS("rotate-output", name) || EQUALS("rotate", name) 
-        || EQUALS("ouput-rotate", name) || EQUALS("rotate-time", name) ) {
-        masscan->output.rotate.timeout = (unsigned)parseTime(value);
-    } else if (EQUALS("rotate-offset", name) || EQUALS("ouput-rotate-offset", name)) {
-        masscan->output.rotate.offset = (unsigned)parseTime(value);
-    } else if (EQUALS("rotate-size", name) || EQUALS("rotate-filesize", name)) {
-        masscan->output.rotate.filesize = parseSize(value);
-    } else if (EQUALS("rotate-dir", name) || EQUALS("rotate-directory", name) || EQUALS("ouput-rotate-dir", name)) {
-        char *p;
-        strcpy_s(   masscan->output.rotate.directory,
-                    sizeof(masscan->output.rotate.directory),
-                    value);
-
-        /* strip trailing slashes */
-        p = masscan->output.rotate.directory;
-        while (*p && (p[strlen(p)-1] == '/' || p[strlen(p)-1] == '/'))
-            p[strlen(p)-1] = '\0';
     } else if (EQUALS("script", name)) {
         if (EQUALS("heartbleed", value)) {
             masscan_set_parameter(masscan, "heartbleed", "true");
@@ -1740,11 +2003,6 @@ masscan_set_parameter(struct Masscan *masscan,
     } else if (EQUALS("scanflags", name)) {
         fprintf(stderr, "nmap(%s): TCP scan flags not yet supported\n", name);
         exit(1);
-    } else if (EQUALS("seed", name)) {
-        if (EQUALS("time", value))
-            masscan->seed = time(0);
-        else
-            masscan->seed = parseInt(value);
     } else if (EQUALS("sendq", name) || EQUALS("sendqueue", name)) {
         masscan->is_sendq = 1;
     } else if (EQUALS("send-eth", name)) {
@@ -1760,31 +2018,6 @@ masscan_set_parameter(struct Masscan *masscan,
         return;
     } else if (EQUALS("source-port", name) || EQUALS("sourceport", name)) {
         masscan_set_parameter(masscan, "adapter-port", value);
-    } else if (EQUALS("shard", name) || EQUALS("shards", name)) {
-        unsigned one = 0;
-        unsigned of = 0;
-
-        while (isdigit(*value))
-            one = one*10 + (*(value++)) - '0';
-        while (ispunct(*value))
-            value++;
-        while (isdigit(*value))
-            of = of*10 + (*(value++)) - '0';
-
-        if (one < 1) {
-            LOG(0, "FAIL: shard index can't be zero\n");
-            LOG(0, "hint   it goes like 1/4 2/4 3/4 4/4\n");
-            exit(1);
-        }
-        if (one > of) {
-            LOG(0, "FAIL: shard spec is wrong\n");
-            LOG(0, "hint   it goes like 1/4 2/4 3/4 4/4\n");
-            exit(1);
-        }
-
-        masscan->shard.one = one;
-        masscan->shard.of = of;
-
     } else if (EQUALS("nobacktrace", name) || EQUALS("backtrace", name)) {
         ;
     } else if (EQUALS("no-stylesheet", name)) {
@@ -1798,7 +2031,10 @@ masscan_set_parameter(struct Masscan *masscan,
         exit(1);
     } else if (EQUALS("top-ports", name)) {
         unsigned n = (unsigned)parseInt(value);
-        config_top_ports(masscan, n);
+        if (!isInteger(value))
+            n = 100;
+        LOG(2, "top-ports = %u\n", n);
+        masscan->top_ports = n;
     } else if (EQUALS("traceroute", name)) {
         fprintf(stderr, "nmap(%s): unsupported\n", name);
         exit(1);
@@ -1809,7 +2045,7 @@ masscan_set_parameter(struct Masscan *masscan,
         if (EQUALS("csv", value))
             masscan->is_test_csv = 0;
     } else if (EQUALS("ttl", name)) {
-        unsigned x = strtoul(value, 0, 0);
+        unsigned x = (unsigned)strtoul(value, 0, 0);
         if (x >= 256) {
             fprintf(stderr, "error: %s=<n>: expected number less than 256\n", name);
         } else {
@@ -1842,6 +2078,7 @@ masscan_set_parameter(struct Masscan *masscan,
         masscan_set_parameter(masscan, "stylesheet", "http://nmap.org/svn/docs/nmap.xsl");
     } else {
         fprintf(stderr, "CONF: unknown config option: %s=%s\n", name, value);
+        exit(1);
     }
 }
 
@@ -1853,7 +2090,7 @@ static int
 is_singleton(const char *name)
 {
     static const char *singletons[] = {
-        "echo", "selftest", "self-test", "regress",
+        "echo", "echo-all", "selftest", "self-test", "regress",
         "benchmark",
         "system-dns", "traceroute", "version",
         "version-light",
@@ -1863,12 +2100,10 @@ is_singleton(const char *name)
         "packet-trace", "release-memory",
         "log-errors", "append-output", "webxml", "no-stylesheet",
         "no-stylesheet", "heartbleed", "ticketbleed",
-        "send-eth", "send-ip", "iflist", "randomize-hosts",
+        "send-eth", "send-ip", "iflist",
         "nmap", "trace-packet", "pfring", "sendq",
-        "banners", "banner", "nobanners", "nobanner",
         "offline", "ping", "ping-sweep", "nobacktrace", "backtrace",
-        "arp",  "infinite", "nointeractive", "interactive", "status", "nostatus",
-        "arpscan", "noreset", 
+        "infinite", "nointeractive", "interactive", "status", "nostatus",
         "read-range", "read-ranges", "readrange", "read-ranges",
         0};
     size_t i;
@@ -1877,6 +2112,20 @@ is_singleton(const char *name)
         if (EQUALS(singletons[i], name))
             return 1;
     }
+    
+    for (i=0; config_parameters[i].name; i++) {
+        if (EQUALS(config_parameters[i].name, name)) {
+            return (config_parameters[i].flags & F_BOOL) == F_BOOL;
+        } else {
+            size_t j;
+            for (j=0; config_parameters[i].alts[j]; j++) {
+                if (EQUALS(config_parameters[i].alts[j], name)) {
+                    return (config_parameters[i].flags & F_BOOL) == F_BOOL;
+                }
+            }
+        }
+    }
+    
     return 0;
 }
 
@@ -1934,6 +2183,21 @@ masscan_command_line(struct Masscan *masscan, int argc, char *argv[])
         if (argv[i][0] == '-' && argv[i][1] == '-') {
             if (strcmp(argv[i], "--help") == 0) {
                 masscan_help();
+            } else if (EQUALS("top-ports", argv[i]+2)) {
+                /* special handling here since the following parameter
+                 * is optional */
+                const char *value = "1000";
+                unsigned n;
+                
+                /* Only consume the next parameter if it's a number,
+                 * otherwise default to 10000 */
+                if (i+1 < argc && isInteger(argv[i+1])) {
+                    value = argv[++i];
+                }
+                n = (unsigned)parseInt(value);
+                LOG(2, "top-ports = %u\n", n);
+                masscan->top_ports = n;
+               
             } else if (EQUALS("readscan", argv[i]+2)) {
                 /* Read in a binary file instead of scanning the network*/
                 masscan->op = Operation_ReadScan;
@@ -2269,8 +2533,150 @@ masscan_command_line(struct Masscan *masscan, int argc, char *argv[])
          */
         masscan_set_parameter(masscan, "range", argv[i]);
     }
+    
+    /*
+     * If no other "scan type" found, then default to TCP
+     */
+    if (masscan->scan_type.udp == 0 && masscan->scan_type.sctp == 0
+        && masscan->scan_type.ping == 0 && masscan->scan_type.arp == 0)
+        masscan->scan_type.tcp = 1;
+    
+    /*
+     * If "top-ports" specified, then add all those ports. This may be in
+     * addition to any other ports
+     */
+    if (masscan->top_ports) {
+        config_top_ports(masscan, masscan->top_ports);
+    }
+}
+
+/***************************************************************************
+ * Prints the current configuration to the command-line then exits.
+ * Use#1: create a template file of all setable parameters.
+ * Use#2: make sure your configuration was interpreted correctly.
+ ***************************************************************************/
+static void
+masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all)
+{
+    unsigned i;
+    unsigned l = 0;
+    
+    /*
+     * NEW:
+     * Print all configuration parameters
+     */
+    masscan->echo = fp;
+    masscan->echo_all = is_echo_all;
+    for (i=0; config_parameters[i].name; i++) {
+        config_parameters[i].set(masscan, 0, 0);
+    }
+    masscan->echo = 0;
+    masscan->echo_all = 0;
+    
+    /*
+     * OLD:
+     * Things here below are the old way of echoing parameters.
+     * TODO: cleanup this code, replacing with the new way.
+     */
+    if (masscan->nic_count == 0)
+        masscan_echo_nic(masscan, fp, 0);
+    else {
+        for (i=0; i<masscan->nic_count; i++)
+            masscan_echo_nic(masscan, fp, i);
+    }
+    
+    
+    /*
+     * Targets
+     */
+    fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n");
+    fprintf(fp, "ports = ");
+    /* Disable comma generation for the first element */
+    l = 0;
+    for (i=0; i<masscan->ports.count; i++) {
+        struct Range range = masscan->ports.list[i];
+        do {
+            struct Range rrange = range;
+            unsigned done = 0;
+            if (l)
+                fprintf(fp, ",");
+            l = 1;
+            if (rrange.begin >= Templ_ICMP_echo) {
+                rrange.begin -= Templ_ICMP_echo;
+                rrange.end -= Templ_ICMP_echo;
+                fprintf(fp,"I:");
+                done = 1;
+            } else if (rrange.begin >= Templ_SCTP) {
+                rrange.begin -= Templ_SCTP;
+                rrange.end -= Templ_SCTP;
+                fprintf(fp,"S:");
+                range.begin = Templ_ICMP_echo;
+            } else if (rrange.begin >= Templ_UDP) {
+                rrange.begin -= Templ_UDP;
+                rrange.end -= Templ_UDP;
+                fprintf(fp,"U:");
+                range.begin = Templ_SCTP;
+            } else
+                range.begin = Templ_UDP;
+            rrange.end = min(rrange.end, 65535);
+            if (rrange.begin == rrange.end)
+                fprintf(fp, "%u", rrange.begin);
+            else
+                fprintf(fp, "%u-%u", rrange.begin, rrange.end);
+            if (done)
+                break;
+        } while (range.begin <= range.end);
+    }
+    fprintf(fp, "\n");
+    for (i=0; i<masscan->targets.count; i++) {
+        struct Range range = masscan->targets.list[i];
+        fprintf(fp, "range = ");
+        fprintf(fp, "%u.%u.%u.%u",
+                (range.begin>>24)&0xFF,
+                (range.begin>>16)&0xFF,
+                (range.begin>> 8)&0xFF,
+                (range.begin>> 0)&0xFF
+                );
+        if (range.begin != range.end) {
+            unsigned cidr_bits = count_cidr_bits(range);
+            
+            if (cidr_bits) {
+                fprintf(fp, "/%u", cidr_bits);
+            } else
+                fprintf(fp, "-%u.%u.%u.%u",
+                        (range.end>>24)&0xFF,
+                        (range.end>>16)&0xFF,
+                        (range.end>> 8)&0xFF,
+                        (range.end>> 0)&0xFF
+                        );
+        }
+        fprintf(fp, "\n");
+    }
+    
+    fprintf(fp, "\n");
+    if (masscan->http_user_agent)
+        fprintf(    fp,
+                "http-user-agent = %.*s\n",
+                masscan->http_user_agent_length,
+                masscan->http_user_agent);
+    
+    for (i=0; i<sizeof(masscan->http_headers)/sizeof(masscan->http_headers[0]); i++) {
+        if (masscan->http_headers[i].header_name == 0)
+            continue;
+        fprintf(    fp,
+                "http-header[%s] = %.*s\n",
+                masscan->http_headers[i].header_name,
+                masscan->http_headers[i].header_value_length,
+                masscan->http_headers[i].header_value);
+    }
+    
+    
+    
+    
+    
 }
 
+
 /***************************************************************************
  * remove leading/trailing whitespace
  ***************************************************************************/
diff --git a/src/main.c b/src/main.c
index 750a497f040c2e911389ebfc40395b78acf29240..0df770af49be02a8bffe4c3cc4a50e95064dbf46 100644
--- a/src/main.c
+++ b/src/main.c
@@ -786,7 +786,7 @@ receive_thread(void *v)
                      * than port scanning them */
 
                     /* If we aren't doing an ARP scan, then ignore ARP responses */
-                    if (!masscan->is_arp)
+                    if (!masscan->scan_type.arp)
                         break;
 
                     /* If this response isn't in our range, then ignore it */
diff --git a/src/masscan-app.c b/src/masscan-app.c
index a7d88a464b66625d39b44c9e196a180f4f9b7331..9241c409a5a6c7f06082114e915a40649bce0d25 100644
--- a/src/masscan-app.c
+++ b/src/masscan-app.c
@@ -21,6 +21,7 @@ masscan_app_to_string(enum ApplicationProtocol proto)
     case PROTO_SNMP: return "snmp";
     case PROTO_NBTSTAT: return "nbtstat";
     case PROTO_SSL3:    return "ssl";
+    case PROTO_SMB:     return "smb";
     case PROTO_SMTP:    return "smtp";
     case PROTO_POP3:    return "pop";
     case PROTO_IMAP4:   return "imap";
@@ -61,6 +62,7 @@ masscan_string_to_app(const char *str)
         {"ssh2",    PROTO_SSH2},
         {"nbtstat", PROTO_NBTSTAT},
         {"ssl",     PROTO_SSL3},
+        {"smb",     PROTO_SMB},
         {"pop",     PROTO_POP3},
         {"imap",    PROTO_IMAP4},
         {"x509",    PROTO_X509_CERT},
diff --git a/src/masscan-app.h b/src/masscan-app.h
index 854ed6621cf6e33fdf11a4eca0295acb66c2b662..3dcfaffc0f4f86c734ed59634e6d3e3ad5fc27e1 100644
--- a/src/masscan-app.h
+++ b/src/masscan-app.h
@@ -16,6 +16,7 @@ enum ApplicationProtocol {
     PROTO_SNMP,             /* simple network management protocol, udp/161 */
     PROTO_NBTSTAT,          /* netbios, udp/137 */
     PROTO_SSL3,
+    PROTO_SMB,              /* SMB tcp/139 and tcp/445 */
     PROTO_SMTP,
     PROTO_POP3,
     PROTO_IMAP4,
@@ -30,6 +31,10 @@ enum ApplicationProtocol {
     PROTO_VNC_RFB,
     PROTO_SAFE,
     PROTO_MEMCACHED,
+    
+    
+    
+    PROTO_end_of_list /* must be last one */
 };
 
 const char *
diff --git a/src/masscan.h b/src/masscan.h
index 234388e90101088fe5d89011a2ada556d41945c3..b6d19da3b4c172d951a4fdb9dc0f9ddd81917b06 100644
--- a/src/masscan.h
+++ b/src/masscan.h
@@ -42,6 +42,7 @@ enum Operation {
  * outputing simultaneously.
  */
 enum OutputFormat {
+    Output_Default      = 0x0000,
     Output_Interactive  = 0x0001,   /* --interactive, print to cmdline */
     Output_List         = 0x0002,
     Output_Binary       = 0x0004,   /* -oB, "binary", the primary format */
@@ -100,8 +101,21 @@ struct Masscan
         unsigned tcp:1;
         unsigned udp:1;
         unsigned sctp:1;
-        unsigned ping:1;
+        unsigned ping:1; /* --ping, ICMP echo */
+        unsigned arp:1; /* --arp, local ARP scan */
     } scan_type;
+    
+    /**
+     * After scan type has been configured, add these ports
+     */
+    unsigned top_ports;
+    
+    /**
+     * Temporary file to echo parameters to, used for saving configuration
+     * to a file
+     */
+    FILE *echo;
+    unsigned echo_all;
 
     /**
      * One or more network adapters that we'll use for scanning. Each adapter
@@ -117,7 +131,7 @@ struct Masscan
         unsigned char router_mac[6];
         unsigned router_ip;
         int link_type; /* libpcap definitions */
-        unsigned char my_mac_count;
+        unsigned char my_mac_count; /*is there a MAC address? */
         unsigned vlan_id;
         unsigned is_vlan:1;
     } nic[8];
@@ -174,8 +188,7 @@ struct Masscan
     unsigned is_sendq:1;        /* --sendq */
     unsigned is_banners:1;      /* --banners */
     unsigned is_offline:1;      /* --offline */
-    unsigned is_arp:1;          /* --arp */
-    unsigned is_noreset:1;      /* --noreset */
+    unsigned is_noreset:1;      /* --noreset, don't transmit RST */
     unsigned is_gmt:1;          /* --gmt, all times in GMT */
     unsigned is_capture_cert:1; /* --capture cert */
     unsigned is_capture_html:1; /* --capture html */
diff --git a/src/proto-banner1.c b/src/proto-banner1.c
index b3bb5871b6fd252108c709035558f6d254082c3e..1bc30d3748d6634971d5765ac2c18644b8e3cdc6 100644
--- a/src/proto-banner1.c
+++ b/src/proto-banner1.c
@@ -8,6 +8,7 @@
 #include "proto-banner1.h"
 #include "proto-http.h"
 #include "proto-ssl.h"
+#include "proto-smb.h"
 #include "proto-ssh.h"
 #include "proto-ftp.h"
 #include "proto-smtp.h"
@@ -24,6 +25,16 @@
 
 
 struct Patterns patterns[] = {
+    {"\x00\x00" "**" "\xff" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS},
+    {"\x00\x00" "**" "\xfe" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS},
+    
+    {"\x83\x00\x00\x01\x80", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening on called name */
+    {"\x83\x00\x00\x01\x81", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening for calling name */
+    {"\x83\x00\x00\x01\x82", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name not present */
+    {"\x83\x00\x00\x01\x83", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name present, but insufficient resources */
+    {"\x83\x00\x00\x01\x8f", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Unspecified error */
+
+    /* ...the remainder can be in any order */
     {"SSH-1.",      6, PROTO_SSH1, SMACK_ANCHOR_BEGIN},
     {"SSH-2.",      6, PROTO_SSH2, SMACK_ANCHOR_BEGIN},
     {"HTTP/1.",     7, PROTO_HTTP, SMACK_ANCHOR_BEGIN},
@@ -31,6 +42,7 @@ struct Patterns patterns[] = {
     {"220 ",        4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 1},
     {"+OK ",        4, PROTO_POP3, SMACK_ANCHOR_BEGIN},
     {"* OK ",       5, PROTO_IMAP4, SMACK_ANCHOR_BEGIN},
+    {"521 ",        4, PROTO_SMTP, SMACK_ANCHOR_BEGIN},
     {"\x16\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN},
     {"\x16\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN},
     {"\x16\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN},
@@ -141,7 +153,7 @@ banner1_parse(
                              banout,
                              more);
             break;
-        case PROTO_SMTP:
+    case PROTO_SMTP:
             banner_smtp.parse(   banner1,
                               banner1->http_fields,
                               tcb_state,
@@ -197,6 +209,15 @@ banner1_parse(
                         banout,
                         more);
         break;
+    case PROTO_SMB:
+        banner_smb1.parse(
+                        banner1,
+                        banner1->http_fields,
+                        tcb_state,
+                        px, length,
+                        banout,
+                        more);
+        break;
     case PROTO_VNC_RFB:
         banner_vnc.parse(    banner1,
                              banner1->http_fields,
@@ -238,7 +259,9 @@ banner1_create(void)
     memset(b, 0, sizeof(*b));
 
     /*
-     * These patterns match the start of the TCP stream
+     * This creates a pattern-matching blob for heuristically determining
+     * a protocol that runs on wrong ports, such as how FTP servers
+     * often respond with "220 " or VNC servers respond with "RFB".
      */
     b->smack = smack_create("banner1", SMACK_CASE_INSENSITIVE);
     for (i=0; patterns[i].pattern; i++)
@@ -251,12 +274,26 @@ banner1_create(void)
     smack_compile(b->smack);
 
 
+    /* 
+     * This goes down the list of all the TCP protocol handlers and initializes
+     * them.
+     */
+    banner_ftp.init(b);
     banner_http.init(b);
-    banner_vnc.init(b);
+    banner_imap4.init(b);
     banner_memcached.init(b);
+    banner_pop3.init(b);
+    banner_smtp.init(b);
+    banner_ssh.init(b);
+    banner_ssl.init(b);
+    banner_smb0.init(b);
+    banner_smb1.init(b);
+    banner_vnc.init(b);
 
     b->tcp_payloads[80] = &banner_http;
     b->tcp_payloads[8080] = &banner_http;
+    b->tcp_payloads[139] = (void*)&banner_smb0;
+    b->tcp_payloads[445] = (void*)&banner_smb1;
     
     b->tcp_payloads[443] = (void*)&banner_ssl;   /* HTTP/s */
     b->tcp_payloads[465] = (void*)&banner_ssl;   /* SMTP/s */
@@ -437,7 +474,13 @@ banner1_selftest()
             fprintf(stderr, "SSL banner: selftest failed\n");
             return 1;
         }
-
+        
+        x = banner_smb1.selftest();
+        if (x) {
+            fprintf(stderr, "SMB banner: selftest failed\n");
+            return 1;
+        }
+        
         x = banner_http.selftest();
         if (x) {
             fprintf(stderr, "HTTP banner: selftest failed\n");
diff --git a/src/proto-banner1.h b/src/proto-banner1.h
index 0eb1998dc91178b37b01341cf9ea3bd401aad1ef..21a572dc3feb1e68dc4ec74b299bb5207e61840d 100644
--- a/src/proto-banner1.h
+++ b/src/proto-banner1.h
@@ -3,11 +3,21 @@
 #include <stdint.h>
 #define STATE_DONE 0xFFFFFFFF
 #include <stdio.h>
+#include "masscan-app.h"
 #include "proto-banout.h"
 #include "proto-x509.h"
 
 struct InteractiveData;
+struct Banner1;
+struct ProtocolState;
 
+typedef void (*BannerParser)(
+              const struct Banner1 *banner1,
+              void *banner1_private,
+              struct ProtocolState *stream_state,
+              const unsigned char *px, size_t length,
+              struct BannerOutput *banout,
+              struct InteractiveData *more);
 struct Banner1
 {
     struct SMACK *smack;
@@ -25,6 +35,8 @@ struct Banner1
     unsigned is_poodle_sslv3:1;
 
     struct ProtocolParserStream *tcp_payloads[65536];
+    
+    BannerParser parser[PROTO_end_of_list];
 };
 
 struct BannerBase64
@@ -123,6 +135,48 @@ struct MEMCACHEDSTUFF {
     unsigned match;
 };
 
+struct Smb72_Negotiate {
+    uint16_t DialectIndex;
+    uint16_t SecurityMode;
+    uint64_t SystemTime;
+    uint32_t Capabilities;
+    uint16_t ServerTimeZone;
+    uint8_t  ChallengeLength;
+    uint8_t  ChallengeOffset;
+};
+
+struct SMBSTUFF {
+    unsigned char nbt_type;
+    unsigned char nbt_flags;
+    unsigned 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 Smb72_Negotiate negotiate;
+    } parms1;
+    
+    union {
+        
+    } pkt;
+};
+
 struct ProtocolState {
     unsigned state;
     unsigned remaining;
@@ -139,6 +193,7 @@ struct ProtocolState {
         struct SMTPSTUFF smtp;
         struct POP3STUFF pop3;
         struct MEMCACHEDSTUFF memcached;
+        struct SMBSTUFF smb;
     } sub;
 };
 
diff --git a/src/proto-banout.c b/src/proto-banout.c
index ec07b26ef4f4fc0bb2294bf5105c404b2d578e88..e24553e39f4ac8c9d2740d23e7fdcd6bfb3c07a4 100644
--- a/src/proto-banout.c
+++ b/src/proto-banout.c
@@ -37,6 +37,7 @@ banout_release(struct BannerOutput *banout)
         free(banout->next);
         banout->next = next;
     }
+    banout_init(banout);
 }
 
 
diff --git a/src/proto-smb.c b/src/proto-smb.c
new file mode 100644
index 0000000000000000000000000000000000000000..28663a1c65f9f8ef3769c6b2cbc7810b18857f5c
--- /dev/null
+++ b/src/proto-smb.c
@@ -0,0 +1,860 @@
+/*
+    SMB parser
+ 
+ */
+#include "proto-smb.h"
+#include "proto-interactive.h"
+#include "unusedparm.h"
+#include "masscan-app.h"
+#include "siphash24.h"
+#include "string_s.h"
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stddef.h>
+
+
+/*
+    "NT LM 0.12"    -   Win2k
+    "SMB 2.002"     0x0202      Vista
+    "SMB 2.???"     0x02FF      Win7, Windows 2008
+    "PC NETWORK PROGRAM 1.0"    MS-DOS
+    "MICROSOFT NETWORKS 1.03"   MS-DOS
+    "MICROSOFT NETWORKS 3.0"    MS-DOS
+    "LANMAN 1.0"                OS/2
+    "LM1.2X002"                 OS/2
+ */
+/*
+PC NETWORK PROGRAM 1.0
+LANMAN1.0
+Windows for Workgroups 3.1a
+LM1.2X002
+LANMAN2.1
+NT LM 0.12
+SMB 2.002
+SMB 2.???
+Samba
+XENIX CORE
+*/
+/*
+  References:
+ http://pubs.opengroup.org/onlinepubs/9697999099/toc.pdf
+ */
+
+struct SmbParams {
+    unsigned short command;
+    unsigned short external_offset;
+    unsigned char external_length;
+    unsigned char internal_type;
+    unsigned short internal_offset;
+};
+
+enum InternalType {
+    IT_uint0,
+    IT_uint8,
+    IT_uint16,
+    IT_uint32,
+    IT_uint64,
+};
+
+struct SmbParams params[] = {
+/*
+ USHORT DialectIndex;
+ UCHAR SecurityMode;
+ USHORT MaxMpxCount;
+ USHORT MaxNumberVcs;
+ ULONG MaxBufferSize;
+ ULONG MaxRawSize;
+ ULONG SessionKey;
+ ULONG Capabilities;
+ FILETIME SystemTime;
+ SHORT ServerTimeZone;
+ UCHAR ChallengeLength;
+ */
+    {0x72,  0,   2, IT_uint16, offsetof(struct Smb72_Negotiate, DialectIndex)},
+    {0x72,  2,   1, IT_uint8,  offsetof(struct Smb72_Negotiate, SecurityMode)},
+    //{0x72,  3,   2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxMpxCount)},
+    //{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, 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)},
+    
+    {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
+};
+
+
+/*****************************************************************************
+ *
+ * ****** WARNING: UGLY HACK !!!! ******
+ *
+ * This code is an ugly hack so I can express SMB parameters (word_count)
+ * headers as a structure instead of writing individual parsers for them.
+ * This code makes no sense. If you find a bug in it, it's probably worth
+ * rewriting rather than figure out its convoluted logic. No really, I mean
+ * this.
+ *
+ *****************************************************************************/
+static size_t
+smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max)
+{
+    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);
+    
+    //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 (; offset < max; offset++, smb->smb1.param_offset++) {
+        again:
+        
+        //printf("\n%u/%u %u\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c);
+        
+        /* If we've gone past our header, just continue consuming bytes */
+        if (params[c].command != smb->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) {
+            c++;
+            goto again;
+        }
+        /* Haven't reached the next field yet */
+        if (params[c].external_offset > smb->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]);
+        
+        /* Shift the type, because all fields little-endian */
+        switch (params[c].internal_type) {
+            case IT_uint0:
+            default:
+                break;
+            case IT_uint8:
+            {
+                uint8_t *x = memberat(uint8_t, &smb->parms1, params[c].internal_offset);
+                *x = px[offset];
+            }
+                break;
+            case IT_uint16:
+            {
+                uint16_t *x = memberat(uint16_t, &smb->parms1, params[c].internal_offset);
+                //*x <<= 8;
+                *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8);
+            }
+                break;
+            case IT_uint32:
+            {
+                uint32_t *x = memberat(uint32_t, &smb->parms1, params[c].internal_offset);
+                //*x <<= 8;
+                *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8);
+            }
+                break;
+            case IT_uint64:
+            {
+                uint64_t *x = memberat(uint64_t, &smb->parms1, params[c].internal_offset);
+                //*x <<= 8;
+                *x |= (uint64_t)px[offset] << (uint64_t)((smb->smb1.param_offset - params[c].external_offset)*8);
+            }
+                break;
+        }
+        
+    }
+    
+    /* Return the number of bytes processed */
+    return offset - original_offset;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static const unsigned long long TICKS_PER_SECOND = 10000000LL;
+static const unsigned long long EPOCH_DIFFERENCE = 11644473600LL;
+static time_t
+convert_windows_time(long long int filetime)
+{
+    unsigned long long seconds = filetime / TICKS_PER_SECOND;
+    seconds -= EPOCH_DIFFERENCE;
+    return (time_t)seconds;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+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)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->smb1.byte_state;
+    
+    
+    if (max > offset + (smb->smb1.byte_count - smb->smb1.byte_offset))
+        max = offset + (smb->smb1.byte_count - smb->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;
+                } else {
+                    state = D_NEGOT_DOMAINA;
+                }
+                offset--;
+            } else
+                smb->parms1.negotiate.ChallengeLength--;
+            break;
+        case D_NEGOT_DOMAIN1:
+        case D_NEGOT_NAME1:
+            smb->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, ' ');
+                state++;
+            } else {
+                name_append_char(banout, smb->smb1.unicode_char);
+                state--;
+            }
+            break;
+        default:
+            break;
+    }
+    
+    smb->smb1.byte_state = state;
+    smb->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,
+                 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;
+    enum {
+        NBT_TYPE,
+        NBT_FLAGS,
+        NBT_LEN1,
+        NBT_LEN2,
+        NBT_ERR,
+        NBT_DRAIN,
+        
+        SMB_VER,
+        SMB1_VER_S, SMB1_VER_M, SMB1_VER_B,
+        
+        SMB1_CMD,
+        SMB1_STATUS1, SMB1_STATUS2, SMB1_STATUS3, SMB1_STATUS4,
+        SMB1_FLAGS1,
+        SMB1_FLAGS2,
+        SMB1_FLAGS3,
+        SMB1_PID1, SMB1_PID2,
+        SMB1_SIG1, SMB1_SIG2, SMB1_SIG3, SMB1_SIG4,
+        SMB1_SIG5, SMB1_SIG6, SMB1_SIG7, SMB1_SIG8,
+        SMB1_RSVD1,SMB1_RSVD2,
+        SMB1_TID1, SMB1_TID2,
+        SMB1_PID3, SMB1_PID4,
+        SMB1_UID1, SMB1_UID2,
+        SMB1_MID1, SMB1_MID2,
+        SMB1_WORD_COUNT,
+        SMB1_PARAMETERS,
+        SMB1_BYTE_COUNT1,
+        SMB1_BYTE_COUNT2,
+        SMB1_DATA,
+        SMB1_DATA_AFTER,
+        /*
+            UCHAR Protocol[4];
+            UCHAR Command;
+            SMB_ERROR Status;
+            UCHAR Flags;
+            USHORT Flags2;
+            USHORT PIDHigh;
+            UCHAR SecurityFeatures[8];
+            USHORT Reserved;
+            USHORT TID;
+            USHORT PIDLow;
+            USHORT UID;
+            USHORT MID;
+        */
+        
+        
+        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;
+    }
+    
+    
+    /*
+     * `for all bytes in the segment`
+     *   `do a state transition for that byte `
+     */
+    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);
+                    state = SMB1_VER_S;
+                    break;
+                case 0xFE:
+                    banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN);
+                    state = SMB2_VER_S;
+                    break;
+                default:
+                    banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN);
+                    state = NBT_UNKNOWN;
+            }
+            break;
+        case SMB1_VER_S:
+        case SMB2_VER_S:
+            
+            if (px[i] != 'S')
+                state = NBT_UNKNOWN;
+            else
+                state++;
+            break;
+        case SMB1_VER_M:
+        case SMB2_VER_M:
+            if (px[i] != 'M')
+                state = NBT_UNKNOWN;
+            else
+                state++;
+            break;
+        case SMB1_VER_B:
+        case SMB2_VER_B:
+            if (px[i] != 'B')
+                state = NBT_UNKNOWN;
+            else
+                state++;
+            break;
+            
+        case SMB1_CMD:
+            memset(&smb->smb1, 0, sizeof(smb->smb1));
+            smb->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];
+            state++;
+            break;
+        case SMB1_FLAGS1:
+            smb->smb1.flags1 = px[i];
+            state++;
+            break;
+        case SMB1_FLAGS2:
+        case SMB1_FLAGS3:
+            smb->smb1.flags2 <<= 8;
+            smb->smb1.flags2 |= px[i];
+            state++;
+            break;
+        case SMB1_PID1: case SMB1_PID2:
+            smb->smb1.pid <<= 8;
+            smb->smb1.pid |= px[i];
+            state++;
+            break;
+        case SMB1_SIG1: case SMB1_SIG2: case SMB1_SIG3: case SMB1_SIG4:
+        case SMB1_SIG5: case SMB1_SIG6: case SMB1_SIG7: case SMB1_SIG8:
+            state++;
+            break;
+        case SMB1_RSVD1:case SMB1_RSVD2:
+            state++;
+            break;
+        case SMB1_TID1: case SMB1_TID2:
+            smb->smb1.tid <<= 8;
+            smb->smb1.tid |= px[i];
+            state++;
+            break;
+        case SMB1_PID3: case SMB1_PID4:
+            smb->smb1.pid <<= 8;
+            smb->smb1.pid |= px[i];
+            state++;
+            break;
+        case SMB1_UID1: case SMB1_UID2:
+            smb->smb1.uid <<= 8;
+            smb->smb1.uid |= px[i];
+            state++;
+            break;
+        case SMB1_MID1: case SMB1_MID2:
+            smb->smb1.mid <<= 8;
+            smb->smb1.mid |= px[i];
+            state++;
+            break;
+        case SMB1_WORD_COUNT:
+            smb->smb1.param_length = px[i]*2;
+            state++;
+            break;
+        case SMB1_PARAMETERS:
+            /* Transfer control to a sub-parser, which may consume zero
+             * or more bytes, up to the end of the parameters field
+             * (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)
+                break;
+            
+            /* We've reached the end of the parameters field, so go onto 
+             * read the byte-count/data field */
+            state = SMB1_BYTE_COUNT1;
+            
+            /* Unconsume the next byte. The "word-count" field may have been
+             * zero when we get to this state, so therefore the logic needs
+             * to be written to handle this. That means when we loop around
+             * again, we need to counter-act the fact that we will automatically
+             * increment the index, so we substract one from it here. */
+            i--;
+            
+            /* Process the parameter/word-count field according to what it
+             * actually contained
+             * TODO: I should make this a function, but I'm lazy 
+             */
+            switch (smb->smb1.command) {
+                case 0x72:
+                {
+                    char str[64] = "(err)";
+                    time_t timestamp = convert_windows_time(smb->parms1.negotiate.SystemTime);
+                    struct tm tm = {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);
+                    
+                }
+                    break;
+                default:
+                    banout_append(banout, PROTO_SMB, "-- ", AUTO_LEN);
+                    smb->smb1.byte_state = D_UNKNOWN;
+            }
+            
+            break;
+            
+        case SMB1_BYTE_COUNT1:
+            smb->smb1.byte_count = px[i];
+            state++;
+            break;
+        case SMB1_BYTE_COUNT2:
+            smb->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) {
+                state = SMB1_DATA_AFTER;
+                i--; /* unconsume byte because of auto-increment */
+            }
+            break;
+            
+        case SMB1_DATA_AFTER:
+            if (i < max) {
+                ;
+            } else {
+                state = 0;
+                i--;
+            }
+            break;
+    
+        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;
+    }
+
+    pstate->state = state;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static void *
+smb_init(struct Banner1 *banner1)
+{
+    UNUSEDPARM(banner1);
+    return 0;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+
+static const char
+smb0_hello_template[] = {
+    0x81, 0x00, 0x00, 0x44, 0x20, 0x43, 0x4b, 0x46,
+    0x44, 0x45, 0x4e, 0x45, 0x43, 0x46, 0x44, 0x45,
+    0x46, 0x46, 0x43, 0x46, 0x47, 0x45, 0x46, 0x46,
+    0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+    0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x45,
+    0x44, 0x46, 0x43, 0x45, 0x46, 0x45, 0x46, 0x45,
+    0x44, 0x45, 0x49, 0x45, 0x46, 0x46, 0x43, 0x43,
+    0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+    0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00,
+
+    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, 0x2f, 0xff, 0x53, 0x4d, 0x42,
+    0x72, 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, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02,
+    0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e,
+    0x31, 0x32, 0x00
+};
+
+/*****************************************************************************
+ *****************************************************************************/
+static int
+smb_selftest(void)
+{
+    struct Banner1 *banner1;
+    struct ProtocolState state[1];
+    struct BannerOutput banout1[1];
+    struct InteractiveData more;
+    int x;
+    size_t i;
+
+    unsigned char packet_bytes[] = {
+        0x00, 0x00, 0x00, 0x69, 0xff, 0x53, 0x4d, 0x42,
+        0x72, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0xc0,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+        0xff, 0xff, 0x00, 0x00, 0x11, 0x00, 0x00, 0x03,
+        0x10, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xfc, 0xe3, 0x01, 0x00, 0x1f, 0xac, 0xe7, 0x7f,
+        0x8a, 0xf0, 0xd3, 0x01, 0xf0, 0x00, 0x08, 0x24,
+        0x00, 0xc2, 0xe5, 0x34, 0x10, 0xfd, 0x29, 0xa7,
+        0x75, 0x42, 0x00, 0x4e, 0x00, 0x43, 0x00, 0x00,
+        0x00, 0x53, 0x00, 0x48, 0x00, 0x49, 0x00, 0x50,
+        0x00, 0x42, 0x00, 0x41, 0x00, 0x52, 0x00, 0x42,
+        0x00, 0x4f, 0x00, 0x00, 0x00,
+
+        /*0x00, 0x00, 0x00, 0x90, 0xff, 0x53, 0x4d, 0x42,
+        0x73, 0x00, 0x00, 0x00, 0x00, 0x98, 0x03, 0x80,
+        0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06,
+        0xe8, 0xfc, 0x00, 0x00, 0x00, 0x08, 0xfe, 0xca,
+        0x00, 0x08, 0x00, 0x00, 0x03, 0x75, 0x00, 0x81,
+        0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x57, 0x00,
+        0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+        0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x35, 0x00,
+        0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00,
+        0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+        0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, 0x00,
+        0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, 0x00,
+        0x4c, 0x00, 0x41, 0x00, 0x4e, 0x00, 0x20, 0x00,
+        0x4d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00,
+        0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
+        0x52, 0x00, 0x45, 0x00, 0x53, 0x00, 0x45, 0x00,
+        0x41, 0x00, 0x55, 0x00, 0x00, 0x03, 0xff, 0x00,
+        0x90, 0x00, 0x01, 0x00, 0x06, 0x00, 0x49, 0x50,
+        0x43, 0x00, 0x00, 0x00*/
+
+    };
+    
+    banner1 = banner1_create();
+    banout_init(banout1);
+    memset(&state[0], 0, sizeof(state[0]));
+    
+    smb_parse_record(banner1,
+                     0,
+                     state,
+                     packet_bytes,
+                     sizeof(packet_bytes),
+                     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);
+    
+    /*
+     *  LET'S FUZZ THIS CRAP!!!
+     *
+     * We are going to re-parse the response packet as many times as needed,
+     * each time flipping one bit in the packet. This should crash the
+     * parser if it has such a bug that will crash it.
+     */
+    for (i=2; i< 5 && i<sizeof(packet_bytes); i++) {
+        size_t j;
+        
+        for (j=0; j<8; j++) {
+            size_t flip = 1<<j;
+            
+            packet_bytes[i] ^= flip;
+            
+            banner1 = banner1_create();
+            banout_init(banout1);
+            memset(&state[0], 0, sizeof(state[0]));
+            
+            smb_parse_record(banner1,
+                             0,
+                             state,
+                             packet_bytes,
+                             sizeof(packet_bytes),
+                             banout1,
+                             &more);
+            banner1_destroy(banner1);
+            banout_release(banout1);
+
+            packet_bytes[i] ^= flip;
+            
+        }
+    }
+    return 0;
+}
+
+/*****************************************************************************
+ * This is the 'plugin' structure that registers callbacks for this parser in
+ * the main system.
+ *****************************************************************************/
+struct ProtocolParserStream banner_smb0 = {
+    "smb", 139, smb0_hello_template, sizeof(smb0_hello_template), 0,
+    smb_selftest,
+    smb_init,
+    smb_parse_record,
+};
+struct ProtocolParserStream banner_smb1 = {
+    "smb", 445, smb1_hello_template, sizeof(smb1_hello_template), 0,
+    smb_selftest,
+    smb_init,
+    smb_parse_record,
+};
+
diff --git a/src/proto-smb.h b/src/proto-smb.h
new file mode 100644
index 0000000000000000000000000000000000000000..98649e838c1c421860a8162371d046633641f539
--- /dev/null
+++ b/src/proto-smb.h
@@ -0,0 +1,8 @@
+#ifndef PROTO_SMB_H
+#define PROTO_SMB_H
+#include "proto-banner1.h"
+
+extern struct ProtocolParserStream banner_smb0;
+extern struct ProtocolParserStream banner_smb1;
+
+#endif
diff --git a/src/proto-tcp.c b/src/proto-tcp.c
index 1a81a3cc58c2b011fe6d9564c1f50ee486b6537d..f04f1df118ef18383893152d56ceca6aad74125f 100644
--- a/src/proto-tcp.c
+++ b/src/proto-tcp.c
@@ -313,7 +313,7 @@ tcpcon_set_parameter(struct TCP_ConnectionTable *tcpcon,
             fprintf(stderr, "tcpcon: parmeter: expected array []: %s\n", name);
             exit(1);
         }
-        port = strtoul(p+1, 0, 0);
+        port = (unsigned)strtoul(p+1, 0, 0);
 
         x = banner1->tcp_payloads[port];
         if (x == NULL) {
@@ -460,6 +460,42 @@ enum DestroyReason {
 
 };
 
+/***************************************************************************
+ * Flush all the banners asssociated with this TCP connection. This always
+ * called when TCB is destroyed. This may also be called earlier, such
+ * as when a FIN is received.
+ ***************************************************************************/
+static void
+tcpcon_flush_banners(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb)
+{
+    struct BannerOutput *banout;
+    
+    /* Go through and print all the banners. Some protocols have 
+     * multiple banners. For example, web servers have both
+     * HTTP and HTML banners, and SSL also has several 
+     * X.509 certificate banners */
+    for (banout = &tcb->banout; banout != NULL; banout = banout->next) {
+        if (banout->length && banout->protocol) {
+            tcpcon->report_banner(
+                                  tcpcon->out,
+                                  global_now,
+                                  tcb->ip_them,
+                                  6, /*TCP protocol*/
+                                  tcb->port_them,
+                                  banout->protocol & 0x0FFFFFFF,
+                                  tcb->ttl,
+                                  banout->banner,
+                                  banout->length);
+        }
+    }
+    
+    /*
+     * Free up all the banners.
+     */
+    banout_release(&tcb->banout);
+
+}
+
 /***************************************************************************
  * Destroy a TCP connection entry. We have to unlink both from the
  * TCB-table as well as the timeout-table.
@@ -473,8 +509,7 @@ tcpcon_destroy_tcb(
 {
     unsigned index;
     struct TCP_Control_Block **r_entry;
-    struct BannerOutput *banout;
-
+    
     UNUSEDPARM(reason);
 
 //printf("." "tcb age = %u-sec, reason=%u                                   \n", time(0) - tcb->when_created, reason);
@@ -512,27 +547,10 @@ tcpcon_destroy_tcb(
 
     /*
      * Print out any banners associated with this TCP session. Most of the
-     * time, there'll only be one.
+     * time, there'll only be one. After printing them out, delete the
+     * banners.
      */
-    for (banout = &tcb->banout; banout != NULL; banout = banout->next) {
-        if (banout->length && banout->protocol) {
-            tcpcon->report_banner(
-                tcpcon->out,
-                global_now,
-                tcb->ip_them,
-                6, /*TCP protocol*/
-                tcb->port_them,
-                banout->protocol & 0x0FFFFFFF,
-                tcb->ttl,
-                banout->banner,
-                banout->length);
-        }
-    }
-
-    /*
-     * If there are multiple banners, then free the additional ones
-     */
-    banout_release(&tcb->banout);
+    tcpcon_flush_banners(tcpcon, tcb);
 
     /*
      * Unlink this from the timeout system.
@@ -1238,6 +1256,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon,
             0x11,
             0, 0, 0);
         tcb->seqno_me++;
+        tcpcon_flush_banners(tcpcon, tcb);
         break;
     
     case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_TIMEOUT:
diff --git a/src/ranges.c b/src/ranges.c
index df53fd36ac6e2f3d204d65c5108c61218ae11284..2a636eb04f10ec380493a62b3fc1dfcd6eebec41 100644
--- a/src/ranges.c
+++ b/src/ranges.c
@@ -642,11 +642,11 @@ rangelist_parse_ports(struct RangeList *ports, const char *string, unsigned *is_
         if (!isdigit(p[0] & 0xFF))
             break;
 
-        port = strtoul(p, &p, 0);
+        port = (unsigned)strtoul(p, &p, 0);
         end = port;
         if (*p == '-') {
             p++;
-            end = strtoul(p, &p, 0);
+            end = (unsigned)strtoul(p, &p, 0);
         }
 
         if (port > 0xFFFF || end > 0xFFFF || end < port) {
diff --git a/src/rawsock-getif.c b/src/rawsock-getif.c
index 77d6e2259afb8bf722f2ee0e8a13b9b6609f40c4..4da549255d3fd32f8d7d641e3cc8fb83d65e0d5c 100644
--- a/src/rawsock-getif.c
+++ b/src/rawsock-getif.c
@@ -43,7 +43,7 @@ int
 rawsock_get_default_interface(char *ifname, size_t sizeof_ifname)
 {
     int fd;
-    int seq = time(0);
+    int seq = (int)time(0);
     int err;
     struct rt_msghdr *rtm;
     size_t sizeof_buffer;
diff --git a/src/rawsock-getroute.c b/src/rawsock-getroute.c
index 95af493774fc3f0e29ae05804bf8fdb60a15a679..cbc399ad085b0d442795199e0e13792baa41dc4a 100644
--- a/src/rawsock-getroute.c
+++ b/src/rawsock-getroute.c
@@ -99,7 +99,7 @@ int
 rawsock_get_default_gateway(const char *ifname, unsigned *ipv4)
 {
     int fd;
-    int seq = time(0);
+    int seq = (int)time(0);
     int err;
     struct rt_msghdr *rtm;
     size_t sizeof_buffer;
diff --git a/src/rawsock-pcapfile.c b/src/rawsock-pcapfile.c
index cd879bacab2dbb63f2a4ac3aadc74cf8d546e0c3..d6c583f1a5915b7cf8ae5c593e3073f84c406637 100644
--- a/src/rawsock-pcapfile.c
+++ b/src/rawsock-pcapfile.c
@@ -613,7 +613,7 @@ struct PcapFile *pcapfile_openread(const char *capfilename)
 
     /* Read the first frame's timestamp */
     {
-        int loc;
+        long loc;
         char tsbuf[8];
         size_t x;
 
diff --git a/src/smack.h b/src/smack.h
index 4fce5562ad105289cf9d53ae7c6556fa0a5ae142..7238267cc8ce9d4aa6cbb86d29d95edaef35584d 100644
--- a/src/smack.h
+++ b/src/smack.h
@@ -14,6 +14,7 @@ enum {
     SMACK_ANCHOR_BEGIN  = 0x01,
     SMACK_ANCHOR_END    = 0x02,
     SMACK_SNMP_HACK     = 0x04,
+    SMACK_WILDCARDS     = 0x08,
 };
 
 enum {
diff --git a/src/smack1.c b/src/smack1.c
index 34f72a90a3e1ebed609a4a689212d0d923a26c09..149eceb2c873aede5de60f885e48e8dfb4a1a17b 100644
--- a/src/smack1.c
+++ b/src/smack1.c
@@ -212,6 +212,8 @@ struct SmackPattern
     unsigned                is_anchor_end:1;
 
     unsigned                is_snmp_hack:1;
+    
+    unsigned                is_wildcards:1;
 };
 
 
@@ -673,6 +675,7 @@ smack_add_pattern(
     pat->is_anchor_begin = ((flags & SMACK_ANCHOR_BEGIN) > 0);
     pat->is_anchor_end = ((flags & SMACK_ANCHOR_END) > 0);
     pat->is_snmp_hack = ((flags & SMACK_SNMP_HACK) > 0);
+    pat->is_wildcards = ((flags & SMACK_WILDCARDS) > 0);
     pat->id = id;
     pat->pattern = make_copy_of_pattern(pattern, pattern_length, smack->is_nocase);
     if (pat->is_anchor_begin)
@@ -1050,6 +1053,59 @@ smack_stage3_sort(struct SMACK *smack)
     smack->m_match_limit = start;
 }
 
+/****************************************************************************
+ *
+ * KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE
+ *
+ * This function currently only works in a very narrow case, for the SMB
+ * parser, where all the patterns are "anchored" and none overlap with the
+ * the SMB patterns. This allows us to modify existing states with the
+ * the wildcards, without adding new states. Do do this right we need
+ * to duplicate states in order to track wildcards
+ ****************************************************************************/
+static void
+smack_fixup_wildcards(struct SMACK *smack)
+{
+    size_t i;
+    
+    for (i=0; i<smack->m_pattern_count; i++) {
+        size_t j;
+        struct SmackPattern *pat = smack->m_pattern_list[i];
+        
+        /* skip patterns that aren't wildcards */
+        if (!pat->is_wildcards)
+            continue;
+        
+        /* find the state leading up to the wilcard * character */
+        for (j=0; j<pat->pattern_length; j++) {
+            unsigned row = 0;
+            unsigned offset = 0;
+            size_t row_size = (1 << smack->row_shift);
+             transition_t *table;
+            transition_t next_pattern;
+            transition_t base_state = (smack->is_anchor_begin?1:0);
+            size_t k;
+            
+            /* Skip non-wildcard characters */
+            if (pat->pattern[j] != '*')
+                continue;
+            
+            /* find the current 'row' */
+            while (offset < j)
+                smack_search_next(smack, &row, pat->pattern, &offset, (unsigned)j);
+            
+            row = row & 0xFFFFFF;
+            table = smack->table + (row << smack->row_shift);
+            next_pattern = table[smack->char_to_symbol['*']];
+            
+            for (k=0; k<row_size; k++) {
+                if (table[k] == base_state)
+                    table[k] = next_pattern;
+            }
+        }
+    }
+
+}
 /****************************************************************************
  ****************************************************************************/
 void
@@ -1106,12 +1162,18 @@ smack_compile(struct SMACK *smack)
         swap_rows(smack, BASE_STATE, UNANCHORED_STATE);
     }
 
+    /* prettify table for debugging */
     smack_stage3_sort(smack);
 
     /*
      * Build the final table we use for evaluation
      */
     smack_stage4_make_final_table(smack);
+    
+    /*
+     * Fixup the wildcard states
+     */
+    smack_fixup_wildcards(smack);
 
     /*
      * Get rid of the original pattern tables, since we no longer need them.
diff --git a/xcode4/masscan.xcodeproj/project.pbxproj b/xcode4/masscan.xcodeproj/project.pbxproj
index c008f4f3c58633d6c5c7e1d4a396dc916e3b3296..b36144acc29fd3cdf1db64863cb23fb32072acde 100644
--- a/xcode4/masscan.xcodeproj/project.pbxproj
+++ b/xcode4/masscan.xcodeproj/project.pbxproj
@@ -89,6 +89,7 @@
 		11C936C31EDCE77F0023D32E /* in-filter.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936BF1EDCE77F0023D32E /* in-filter.c */; };
 		11C936C41EDCE77F0023D32E /* in-report.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C11EDCE77F0023D32E /* in-report.c */; };
 		11C936C71EDCE8B40023D32E /* rawsock-pcap.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C51EDCE8B40023D32E /* rawsock-pcap.c */; };
+		11DE129620ABC2650041135D /* proto-smb.c in Sources */ = {isa = PBXBuildFile; fileRef = 11DE129520ABC2650041135D /* proto-smb.c */; };
 		11E76DB41889BC5200061F45 /* pixie-backtrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 11E76DB21889BC5200061F45 /* pixie-backtrace.c */; };
 		11F9375419F1A54200C1947F /* script-sslv3.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375319F1A54200C1947F /* script-sslv3.c */; };
 		11F9375719F1AD5000C1947F /* script-heartbleed.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375619F1AD5000C1947F /* script-heartbleed.c */; };
@@ -261,6 +262,8 @@
 		11C936C21EDCE77F0023D32E /* in-report.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "in-report.h"; sourceTree = "<group>"; };
 		11C936C51EDCE8B40023D32E /* rawsock-pcap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "rawsock-pcap.c"; sourceTree = "<group>"; };
 		11C936C61EDCE8B40023D32E /* rawsock-pcap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "rawsock-pcap.h"; sourceTree = "<group>"; };
+		11DE129420ABC2650041135D /* proto-smb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-smb.h"; sourceTree = "<group>"; };
+		11DE129520ABC2650041135D /* proto-smb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "proto-smb.c"; sourceTree = "<group>"; };
 		11E76DB21889BC5200061F45 /* pixie-backtrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pixie-backtrace.c"; sourceTree = "<group>"; };
 		11E76DB31889BC5200061F45 /* pixie-backtrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "pixie-backtrace.h"; sourceTree = "<group>"; };
 		11F9375319F1A54200C1947F /* script-sslv3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "script-sslv3.c"; sourceTree = "<group>"; };
@@ -388,6 +391,8 @@
 		11B360CA1F9016C00020F3A3 /* proto */ = {
 			isa = PBXGroup;
 			children = (
+				11DE129420ABC2650041135D /* proto-smb.h */,
+				11DE129520ABC2650041135D /* proto-smb.c */,
 				119AB2042051FFED008E4DDD /* proto-memcached.c */,
 				119AB2052051FFED008E4DDD /* proto-memcached.h */,
 				11A921AC17DBCC7E00DDFD32 /* proto-arp.c */,
@@ -604,6 +609,7 @@
 				11A921F817DBCC7E00DDFD32 /* syn-cookie.c in Sources */,
 				11A921F917DBCC7E00DDFD32 /* templ-pkt.c in Sources */,
 				11A921FA17DBCC7E00DDFD32 /* xring.c in Sources */,
+				11DE129620ABC2650041135D /* proto-smb.c in Sources */,
 				11B2DD9E17DE4DD8007FC363 /* templ-payloads.c in Sources */,
 				11AC80ED17E0DAD4001BCE3A /* proto-http.c in Sources */,
 				11AC80EE17E0DAD4001BCE3A /* proto-icmp.c in Sources */,