From b425d6d92dc83c397d49f2b723f5db393f23c485 Mon Sep 17 00:00:00 2001
From: Robert Graham <robert_david_graham@yahoo.com>
Date: Wed, 7 Mar 2018 23:51:00 -0500
Subject: [PATCH] memcached

---
 src/masscan-app.c            |  18 +-
 src/masscan-app.h            |   1 +
 src/proto-banner1.c          |  12 +
 src/proto-banner1.h          |   7 +
 src/proto-memcached.c        | 430 +++++++++++++++++++++++++++++++++++
 src/proto-memcached.h        |  28 +++
 src/proto-udp.c              |   6 +
 src/templ-payloads.c         |   5 +-
 vs10/masscan.vcxproj         |   2 +
 vs10/masscan.vcxproj.filters |   6 +
 10 files changed, 506 insertions(+), 9 deletions(-)
 create mode 100644 src/proto-memcached.c
 create mode 100644 src/proto-memcached.h

diff --git a/src/masscan-app.c b/src/masscan-app.c
index edd86d8..a7d88a4 100644
--- a/src/masscan-app.c
+++ b/src/masscan-app.c
@@ -34,6 +34,7 @@ masscan_app_to_string(enum ApplicationProtocol proto)
     case PROTO_TICKETBLEED:    return "ticketbleed";
     case PROTO_VNC_RFB: return "vnc";
     case PROTO_SAFE:    return "safe";
+    case PROTO_MEMCACHED: return "memcached";
             
     default:
         sprintf_s(tmp, sizeof(tmp), "(%u)", proto);
@@ -63,15 +64,16 @@ masscan_string_to_app(const char *str)
         {"pop",     PROTO_POP3},
         {"imap",    PROTO_IMAP4},
         {"x509",    PROTO_X509_CERT},
-        {"zeroaccess", PROTO_UDP_ZEROACCESS},
-        {"title", PROTO_HTML_TITLE},
-        {"html", PROTO_HTML_FULL},
-        {"ntp", PROTO_NTP},
-        {"vuln", PROTO_VULN},
-        {"heartbleed", PROTO_HEARTBLEED},
+        {"zeroaccess",  PROTO_UDP_ZEROACCESS},
+        {"title",       PROTO_HTML_TITLE},
+        {"html",        PROTO_HTML_FULL},
+        {"ntp",         PROTO_NTP},
+        {"vuln",        PROTO_VULN},
+        {"heartbleed",  PROTO_HEARTBLEED},
         {"ticketbleed", PROTO_TICKETBLEED},
-        {"vnc", PROTO_VNC_RFB},
-        {"safe",    PROTO_SAFE},
+        {"vnc",         PROTO_VNC_RFB},
+        {"safe",        PROTO_SAFE},
+        {"memcached",   PROTO_MEMCACHED},
         {0,0}
     };
     size_t i;
diff --git a/src/masscan-app.h b/src/masscan-app.h
index 07cc863..854ed66 100644
--- a/src/masscan-app.h
+++ b/src/masscan-app.h
@@ -29,6 +29,7 @@ enum ApplicationProtocol {
     PROTO_TICKETBLEED,
     PROTO_VNC_RFB,
     PROTO_SAFE,
+    PROTO_MEMCACHED,
 };
 
 const char *
diff --git a/src/proto-banner1.c b/src/proto-banner1.c
index b2e2e5c..6257765 100644
--- a/src/proto-banner1.c
+++ b/src/proto-banner1.c
@@ -14,6 +14,7 @@
 #include "proto-imap4.h"
 #include "proto-pop3.h"
 #include "proto-vnc.h"
+#include "proto-memcached.h"
 #include "masscan-app.h"
 #include <ctype.h>
 #include <stdlib.h>
@@ -49,6 +50,7 @@ struct Patterns patterns[] = {
     {"RFB 004.000\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* Intel AMT KVM */
     {"RFB 004.001\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* RealVNC 4.6 */
     {"RFB 004.002\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8},
+    {"STAT pid ",      9, PROTO_MEMCACHED,SMACK_ANCHOR_BEGIN}, /* memcached stat response */
     {0,0}
 };
 
@@ -203,6 +205,14 @@ banner1_parse(
                              banout,
                              more);
         break;
+    case PROTO_MEMCACHED:
+        banner_memcached.parse(    banner1,
+                             banner1->http_fields,
+                             tcb_state,
+                             px, length,
+                             banout,
+                             more);
+        break;
     default:
         fprintf(stderr, "banner1: internal error\n");
         break;
@@ -242,6 +252,8 @@ banner1_create(void)
 
 
     banner_http.init(b);
+    banner_vnc.init(b);
+    banner_memcached.init(b);
 
     b->tcp_payloads[80] = &banner_http;
     b->tcp_payloads[8080] = &banner_http;
diff --git a/src/proto-banner1.h b/src/proto-banner1.h
index a4b754a..0eb1998 100644
--- a/src/proto-banner1.h
+++ b/src/proto-banner1.h
@@ -13,6 +13,8 @@ struct Banner1
     struct SMACK *smack;
     struct SMACK *http_fields;
     struct SMACK *html_fields;
+    struct SMACK *memcached_responses;
+    struct SMACK *memcached_stats;
 
     unsigned is_capture_html:1;
     unsigned is_capture_cert:1;
@@ -117,6 +119,10 @@ struct POP3STUFF {
     unsigned is_last:1;
 };
 
+struct MEMCACHEDSTUFF {
+    unsigned match;
+};
+
 struct ProtocolState {
     unsigned state;
     unsigned remaining;
@@ -132,6 +138,7 @@ struct ProtocolState {
         struct FTPSTUFF ftp;
         struct SMTPSTUFF smtp;
         struct POP3STUFF pop3;
+        struct MEMCACHEDSTUFF memcached;
     } sub;
 };
 
diff --git a/src/proto-memcached.c b/src/proto-memcached.c
new file mode 100644
index 0000000..458e90a
--- /dev/null
+++ b/src/proto-memcached.c
@@ -0,0 +1,430 @@
+/*
+    memcached banner check 
+*/
+
+#include "proto-memcached.h"
+#include "proto-banner1.h"
+#include "smack.h"
+#include "unusedparm.h"
+#include "masscan-app.h"
+#include "output.h"
+#include "proto-interactive.h"
+#include "proto-preprocess.h"
+#include "proto-ssl.h"
+#include "syn-cookie.h"
+#include "templ-port.h"
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+struct SMACK *sm_memcached_responses;
+struct SMACK *sm_memcached_stats;
+
+enum {
+    MC_ERROR,
+    MC_CLIENT_ERROR,
+    MC_SERVER_ERROR,
+    MC_STORED,
+    MC_NOT_STORED,
+    MC_EXISTS,
+    MC_NOT_FOUND,
+    MC_END,
+    MC_VALUE,
+    MC_DELETED,
+    MC_TOUCHED,
+    MC_OK,
+    MC_BUSY,
+    MC_BADCLASS,
+    MC_NOSPARE,
+    MC_NOTFULL,
+    MC_UNSAFE,
+    MC_SAME,
+    MC_STAT,
+    MC_empty,
+};
+static struct Patterns memcached_responses[] = {
+    {"ERROR",          0, MC_ERROR,          SMACK_ANCHOR_BEGIN},
+    {"CLIENT_ERROR",   0, MC_CLIENT_ERROR,   SMACK_ANCHOR_BEGIN},
+    {"SERVER_ERROR",   0, MC_SERVER_ERROR,   SMACK_ANCHOR_BEGIN},
+    {"STORED",         0, MC_STORED,         SMACK_ANCHOR_BEGIN},
+    {"NOT_STORED",     0, MC_NOT_STORED,     SMACK_ANCHOR_BEGIN},
+    {"EXISTS",         0, MC_EXISTS,         SMACK_ANCHOR_BEGIN},
+    {"NOT_FOUND",      0, MC_NOT_FOUND,      SMACK_ANCHOR_BEGIN},
+    {"END",            0, MC_END,            SMACK_ANCHOR_BEGIN},
+    {"VALUE",          0, MC_VALUE,          SMACK_ANCHOR_BEGIN},
+    {"DELETED",        0, MC_DELETED,        SMACK_ANCHOR_BEGIN},
+    {"TOUCHED",        0, MC_TOUCHED,        SMACK_ANCHOR_BEGIN},
+    {"OK",             0, MC_OK,             SMACK_ANCHOR_BEGIN},
+    {"BUSY",           0, MC_BUSY,           SMACK_ANCHOR_BEGIN},
+    {"BADCLASS",       0, MC_BADCLASS,       SMACK_ANCHOR_BEGIN},
+    {"NOSPARE",        0, MC_NOSPARE,        SMACK_ANCHOR_BEGIN},
+    {"NOTFULL",        0, MC_NOTFULL,        SMACK_ANCHOR_BEGIN},
+    {"UNSAFE",         0, MC_UNSAFE,         SMACK_ANCHOR_BEGIN},
+    {"SAME",           0, MC_SAME,           SMACK_ANCHOR_BEGIN},
+    {"STAT",           0, MC_STAT,           SMACK_ANCHOR_BEGIN},
+    {"",               0, MC_empty,          SMACK_ANCHOR_BEGIN},
+    {0,0,0,0}
+};
+
+enum {
+    MS_PID,
+    MS_UPTIME,
+    MS_TIME,
+    MS_VERSION,
+    MS_POINTER_SIZE,
+    MS_RUSAGE_USER,
+    MS_RUSAGE_SYSTEM,
+    MS_CURR_TIMES,
+    MS_TOTAL_ITEMS,
+    MS_BYTES,
+    MS_MAX_CONNECTIONS,
+    MS_CURR_CONNECTIONS,
+    MS_TOTAL_CONNECTIONS,
+};
+
+static struct Patterns memcached_stats[] = {
+{"pid",                  0, MS_PID,                SMACK_ANCHOR_BEGIN},
+{"uptime",               0, MS_UPTIME,             SMACK_ANCHOR_BEGIN},
+{"time",                 0, MS_TIME,               SMACK_ANCHOR_BEGIN},
+{"version",              0, MS_VERSION,            SMACK_ANCHOR_BEGIN},
+{"pointer_size",         0, MS_POINTER_SIZE,       SMACK_ANCHOR_BEGIN},
+{"rusage_user",          0, MS_RUSAGE_USER,        SMACK_ANCHOR_BEGIN},
+{"rusage_system",        0, MS_RUSAGE_SYSTEM,      SMACK_ANCHOR_BEGIN},
+{"curr_items",           0, MS_CURR_TIMES,         SMACK_ANCHOR_BEGIN},
+{"total_items",          0, MS_TOTAL_ITEMS,        SMACK_ANCHOR_BEGIN},
+{"bytes",                0, MS_BYTES,              SMACK_ANCHOR_BEGIN},
+{"max_connections",      0, MS_MAX_CONNECTIONS,    SMACK_ANCHOR_BEGIN},
+{"curr_connections",     0, MS_CURR_CONNECTIONS,   SMACK_ANCHOR_BEGIN},
+{"total_connections",    0, MS_TOTAL_CONNECTIONS,  SMACK_ANCHOR_BEGIN},
+{0,0,0,0}
+};
+
+/***************************************************************************
+ ***************************************************************************/
+static void
+memcached_tcp_parse(  
+          const struct Banner1 *banner1,
+          void *banner1_private,
+          struct ProtocolState *pstate,
+          const unsigned char *px, size_t length,
+          struct BannerOutput *banout,
+          struct InteractiveData *more)
+{
+    unsigned state = pstate->state;
+    unsigned i;
+    struct MEMCACHEDSTUFF *memcached = &pstate->sub.memcached;
+    size_t id;
+
+    UNUSEDPARM(banner1_private);
+    UNUSEDPARM(banner1);
+    UNUSEDPARM(more);
+
+    if (sm_memcached_responses == 0)
+        return;
+
+    for (i=0; i<length; i++) {
+        switch (state) {
+            case 0: /* command */
+                memcached->match = 0;
+                /* drop through */
+            case 1:
+                id = smack_search_next(
+                        sm_memcached_responses,
+                        &memcached->match,
+                        px, &i, (unsigned)length);
+                i--;
+                switch (id) {
+                case SMACK_NOT_FOUND:
+                    /* continue processing */
+                    break;
+                case MC_STAT:
+                    if (px[i] == '\n')
+                        state = 2; /* premature end of line */
+                    else
+                        state = 100;
+                    break;
+                case MC_END:
+                    state = 3;
+                    break;
+                default:
+                    state = 2;
+                }
+                break;
+
+            /* We've reached the end of input */
+            case 3:
+                i = length;
+                break;
+
+            /* Ignore until end of line */
+            case 2:
+                while (i < length && px[i] != '\n')
+                    i++;
+                if (px[i] == '\n')
+                    state = 0;
+                break;
+            
+            /* process stat */
+            case 100:
+            case 200:
+                if (px[i] == '\n')
+                    state = 0;
+                else if (isspace(px[i]))
+                    continue; /* stay in this space until end of whitespace */
+                else {
+                    state++;
+                    memcached->match = 0;
+                    i--;
+                }
+                break;
+            case 101:
+                id = smack_search_next(
+                        sm_memcached_stats,
+                        &memcached->match,
+                        px, &i, (unsigned)length);
+                i--;
+                switch (id) {
+                case SMACK_NOT_FOUND:
+                    /* continue processing */
+                    break;
+                case MS_UPTIME:
+                case MS_TIME:
+                case MS_VERSION:
+                    banout_append(banout, PROTO_MEMCACHED, memcached_stats[id].pattern, AUTO_LEN);
+                    if (px[i] == '\n')
+                        state = 0;
+                    state = 200;
+                    banout_append_char(banout, PROTO_MEMCACHED, '=');
+                    break;
+                default:
+                    if (px[i] == '\n')
+                        state = 0;
+                    else
+                        state = 2;
+                }
+                break;
+
+            case 201:
+                if (px[i] == '\r')
+                    continue;
+                else if (px[i] == '\n') {
+                    banout_append_char(banout, PROTO_MEMCACHED, ' ');
+                    state = 0;
+                    break;
+                } else
+                    banout_append_char(banout, PROTO_MEMCACHED, px[i]);
+                break;
+        }
+    }
+    pstate->state = state;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+static void *
+memcached_init(struct Banner1 *b)
+{
+    unsigned i;
+
+    /*
+     * These match response codes
+     */
+    b->memcached_responses = smack_create("memcached-responses", SMACK_CASE_INSENSITIVE);
+    for (i=0; memcached_responses[i].pattern; i++) {
+        char *tmp;
+        unsigned j;
+        size_t len;
+
+        len = strlen(memcached_responses[i].pattern);
+        tmp = malloc(len + 2);
+        memcpy(tmp, memcached_responses[i].pattern, len);
+        tmp[len+1] = '\0';
+
+        /* Add all patterns 4 times, once each for the possible whitespace */
+        for (j=0; j<4; j++) {
+            tmp[len] = " \t\r\n"[j];
+            smack_add_pattern(
+                          b->memcached_responses,
+                          tmp,
+                          len+1,
+                          memcached_responses[i].id,
+                          memcached_responses[i].is_anchored);
+        }
+
+        free(tmp);
+    }
+    smack_compile(b->memcached_responses);
+    sm_memcached_responses = b->memcached_responses;
+
+    /*
+     * These match stats we might be interested in
+     */
+    b->memcached_stats = smack_create("memcached-stats", SMACK_CASE_INSENSITIVE);
+    for (i=0; memcached_stats[i].pattern; i++) {
+        char *tmp;
+        unsigned j;
+        size_t len;
+
+        len = strlen(memcached_stats[i].pattern);
+        tmp = malloc(len + 2);
+        memcpy(tmp, memcached_stats[i].pattern, len);
+        tmp[len+1] = '\0';
+
+        /* Add all patterns 4 times, once each for the possible whitespace */
+        for (j=0; j<4; j++) {
+            tmp[len] = " \t\r\n"[j];
+            smack_add_pattern(
+                          b->memcached_stats,
+                          tmp,
+                          len+1,
+                          memcached_stats[i].id,
+                          memcached_stats[i].is_anchored);
+        }
+
+        free(tmp);
+    }
+    smack_compile(b->memcached_stats);
+    sm_memcached_stats = b->memcached_stats;
+
+    return b->http_fields;
+}
+
+
+/***************************************************************************
+ ***************************************************************************/
+unsigned
+memcached_udp_parse(struct Output *out, time_t timestamp,
+            const unsigned char *px, unsigned length,
+            struct PreprocessedInfo *parsed,
+            uint64_t entropy
+            )
+{
+    unsigned ip_them;
+    unsigned ip_me;
+    unsigned port_them = parsed->port_src;
+    unsigned port_me = parsed->port_dst;
+    unsigned request_id = 0;
+    unsigned sequence_num = 0;
+    unsigned total_dgrams = 0;
+    unsigned reserved = 0;
+    unsigned cookie = 0;
+    struct BannerOutput banout[1];
+
+    /* All memcached responses will be at least 8 bytes */
+    if (length < 8)
+        return 0;
+
+    /*
+    The frame header is 8 bytes long, as follows (all values are 16-bit integers
+    in network byte order, high byte first):
+
+    0-1 Request ID
+    2-3 Sequence number
+    4-5 Total number of datagrams in this message
+    6-7 Reserved for future use; must be 0
+    */
+    request_id = px[0]<<8 | px[1];
+    sequence_num = px[2]<<8 | px[3];
+    total_dgrams = px[4]<<8 | px[5];
+    reserved = px[6]<<8 | px[7];
+
+    /* Ignore high sequence numbers. This should be zero normally */
+    if (sequence_num > 100)
+        return 0;
+
+    /* Ignore too many dgrams, should be one normally */
+    if (total_dgrams > 100)
+        return 0;
+
+    /* Make sure reserved field is zero */
+    if (reserved != 0)
+        return 0;
+
+    /* Grab IP addresses */
+    ip_them = parsed->ip_src[0]<<24 | parsed->ip_src[1]<<16
+            | parsed->ip_src[2]<< 8 | parsed->ip_src[3]<<0;
+    ip_me = parsed->ip_dst[0]<<24 | parsed->ip_dst[1]<<16
+            | parsed->ip_dst[2]<< 8 | parsed->ip_dst[3]<<0;
+
+    /* Validate the "syn-cookie" style information. In the case of SNMP,
+     * this will be held in the "request-id" field. If the cookie isn't
+     * a good one, then we'll ignore the response */
+    cookie = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy);
+    /*if ((seqno&0xffff) != request_id)
+        return 1;*/
+
+    /* Initialize the "banner output" module that we'll use to print
+     * pretty text in place of the raw packet */
+    banout_init(banout);
+
+    /* Parse the remainder of the packet as if this were TCP */
+    {
+        struct ProtocolState stuff[1];
+
+        memset(stuff, 0, sizeof(stuff[0]));
+
+        memcached_tcp_parse(
+            0, 0,
+            stuff, px+8, length-8, banout, 
+            0);
+    }
+
+    if ((cookie&0xffff) != request_id)
+        banout_append(banout, PROTO_MEMCACHED, " IP-MISMATCH", AUTO_LEN);
+            
+    /* Print the banner information, or save to a file, depending */
+    output_report_banner(
+        out, timestamp,
+        ip_them, 17 /*udp*/, parsed->port_src,
+        PROTO_MEMCACHED,
+        parsed->ip_ttl,
+        banout_string(banout, PROTO_MEMCACHED),
+        banout_string_length(banout, PROTO_MEMCACHED));
+
+    /* Free memory for the banner, if there was any allocated */
+    banout_release(banout);
+
+    return 0;
+}
+
+/****************************************************************************
+ ****************************************************************************/
+unsigned
+memcached_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno)
+{
+    /*
+    The frame header is 8 bytes long, as follows (all values are 16-bit integers
+    in network byte order, high byte first):
+
+    0-1 Request ID
+    2-3 Sequence number
+    4-5 Total number of datagrams in this message
+    6-7 Reserved for future use; must be 0
+    */
+
+    if (length < 2)
+        return 0;
+
+    px[0] = (unsigned char)(seqno >> 8);
+    px[1] = (unsigned char)(seqno >> 0);
+
+
+    return 0;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+static int
+memcached_selftest(void)
+{
+    return 0;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+const struct ProtocolParserStream banner_memcached = {
+    "memcached", 11211, "stats\r\n", 7, 0,
+    memcached_selftest,
+    memcached_init,
+    memcached_tcp_parse,
+};
diff --git a/src/proto-memcached.h b/src/proto-memcached.h
new file mode 100644
index 0000000..af8a766
--- /dev/null
+++ b/src/proto-memcached.h
@@ -0,0 +1,28 @@
+#ifndef PROTO_MEMCACHED_H
+#define PROTO_MEMCACHED_H
+#include "proto-banner1.h"
+struct Output;
+struct PreprocessedInfo;
+
+/*
+ * For sending TCP requests and parsing TCP responses.
+ */
+extern const struct ProtocolParserStream banner_memcached;
+
+/*
+ * For parsing UDP responses
+ */
+unsigned
+memcached_udp_parse(struct Output *out, time_t timestamp,
+            const unsigned char *px, unsigned length,
+            struct PreprocessedInfo *parsed,
+            uint64_t entropy
+            );
+
+/* 
+ * For creating UDP request
+ */
+unsigned
+memcached_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno);
+
+#endif
diff --git a/src/proto-udp.c b/src/proto-udp.c
index c9850ce..ce41a95 100644
--- a/src/proto-udp.c
+++ b/src/proto-udp.c
@@ -2,6 +2,7 @@
 #include "proto-dns.h"
 #include "proto-netbios.h"
 #include "proto-snmp.h"
+#include "proto-memcached.h"
 #include "proto-ntp.h"
 #include "proto-zeroaccess.h"
 #include "proto-preprocess.h"
@@ -42,6 +43,11 @@ handle_udp(struct Output *out, time_t timestamp,
         case 161:
             status = handle_snmp(out, timestamp, px, length, parsed, entropy);
             break;
+        case 11211:
+            px += parsed->app_offset;
+            length = parsed->app_length;
+            status = memcached_udp_parse(out, timestamp, px, length, parsed, entropy);
+            break;
         case 16464:
         case 16465:
         case 16470:
diff --git a/src/templ-payloads.c b/src/templ-payloads.c
index 8914c55..a79db0d 100644
--- a/src/templ-payloads.c
+++ b/src/templ-payloads.c
@@ -17,6 +17,7 @@
 #include "logger.h"
 #include "proto-zeroaccess.h"   /* botnet p2p protocol */
 #include "proto-snmp.h"
+#include "proto-memcached.h"
 #include "proto-ntp.h"
 #include "proto-dns.h"
 
@@ -104,7 +105,9 @@ struct Payload2 hard_coded_payloads[] = {
         "Content-Length: 0\r\n"
     },
 
-    {11211, 65536, 15, 0, 0,
+    /* memcached "stats" request. This looks for memcached systems that can
+     * be used for DDoS amplifiers */
+    {11211, 65536, 15, 0, memcached_udp_set_cookie,
         "\x00\x00\x00\x00\x00\x01\x00\x00stats\r\n"
     },
 
diff --git a/vs10/masscan.vcxproj b/vs10/masscan.vcxproj
index 0c22cac..2534578 100644
--- a/vs10/masscan.vcxproj
+++ b/vs10/masscan.vcxproj
@@ -51,6 +51,7 @@
     <ClCompile Include="..\src\proto-icmp.c" />
     <ClCompile Include="..\src\proto-imap4.c" />
     <ClCompile Include="..\src\proto-interactive.c" />
+    <ClCompile Include="..\src\proto-memcached.c" />
     <ClCompile Include="..\src\proto-netbios.c" />
     <ClCompile Include="..\src\proto-ntp.c" />
     <ClCompile Include="..\src\proto-pop3.c" />
@@ -139,6 +140,7 @@
     <ClInclude Include="..\src\proto-icmp.h" />
     <ClInclude Include="..\src\proto-imap4.h" />
     <ClInclude Include="..\src\proto-interactive.h" />
+    <ClInclude Include="..\src\proto-memcached.h" />
     <ClInclude Include="..\src\proto-netbios.h" />
     <ClInclude Include="..\src\proto-ntp.h" />
     <ClInclude Include="..\src\proto-pop3.h" />
diff --git a/vs10/masscan.vcxproj.filters b/vs10/masscan.vcxproj.filters
index 0d269f5..efbcef0 100644
--- a/vs10/masscan.vcxproj.filters
+++ b/vs10/masscan.vcxproj.filters
@@ -288,6 +288,9 @@
     <ClCompile Include="..\src\out-ndjson.c">
       <Filter>Source Files\output</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\proto-memcached.c">
+      <Filter>Source Files\proto</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\proto-arp.h">
@@ -491,6 +494,9 @@
     <ClInclude Include="..\src\rawsock-pcap.h">
       <Filter>Source Files\rawsock</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\proto-memcached.h">
+      <Filter>Source Files\proto</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\README.md" />
-- 
GitLab