From e08bcb809667aac7216a4ea9fbf8f71b07b830a9 Mon Sep 17 00:00:00 2001 From: Robert David Graham Date: Sun, 19 Jan 2014 01:26:02 -0500 Subject: [PATCH] sctp --- src/main.c | 10 +- src/masscan-status.h | 2 + src/out-binary.c | 2 + src/output.c | 14 +++ src/output.h | 4 + src/proto-icmp.c | 16 +++ src/proto-preprocess.c | 7 +- src/proto-sctp.c | 202 +++++++++++++++++++++++++++++++++++ src/proto-sctp.h | 28 +++++ src/templ-pkt.c | 83 ++++++++++---- vs10/masscan.vcxproj | 2 + vs10/masscan.vcxproj.filters | 6 ++ 12 files changed, 351 insertions(+), 25 deletions(-) create mode 100644 src/proto-sctp.c create mode 100644 src/proto-sctp.h diff --git a/src/main.c b/src/main.c index a9f6d9a5..3a3a54c5 100644 --- a/src/main.c +++ b/src/main.c @@ -49,6 +49,7 @@ #include "proto-x509.h" #include "crypto-base64.h" /* base64 encode/decode */ #include "pixie-backtrace.h" +#include "proto-sctp.h" #include #include @@ -390,8 +391,8 @@ transmit_thread(void *v) /*aka. scanning_thread() */ ip_me = src_ip; port_me = src_port; } - cookie = syn_cookie(ip_them, port_them, ip_me, port_me); - + cookie = syn_cookie(ip_them, port_them&0xFFFF, ip_me, port_me); +//printf("0x%08x 0x%08x 0x%04x 0x%08x 0x%04x \n", cookie, ip_them, port_them, ip_me, port_me); /* * SEND THE PROBE * This is sorta the entire point of the program, but little @@ -714,6 +715,7 @@ receive_thread(void *v) /* verify: my IP address */ if (!is_my_ip(&parms->src, ip_me)) continue; +//printf("0x%08x 0x%08x 0x%04x 0x%08x 0x%04x \n", cookie, ip_them, port_them, ip_me, port_me); /* @@ -766,6 +768,9 @@ receive_thread(void *v) case FOUND_ICMP: handle_icmp(out, secs, px, length, &parsed); continue; + case FOUND_SCTP: + handle_sctp(out, secs, px, length, cookie, &parsed); + break; case FOUND_TCP: /* fall down to below */ break; @@ -1481,6 +1486,7 @@ int main(int argc, char *argv[]) */ { int x = 0; + x += sctp_selftest(); x += base64_selftest(); x += banner1_selftest(); x += output_selftest(); diff --git a/src/masscan-status.h b/src/masscan-status.h index a08351b5..41206eac 100644 --- a/src/masscan-status.h +++ b/src/masscan-status.h @@ -8,6 +8,8 @@ enum PortStatus { Port_IcmpEchoResponse, Port_UdpOpen, Port_UdpClosed, + Port_SctpOpen, + Port_SctpClosed, Port_ArpOpen, }; diff --git a/src/out-binary.c b/src/out-binary.c index d6ebd75d..9c3c2956 100644 --- a/src/out-binary.c +++ b/src/out-binary.c @@ -59,12 +59,14 @@ binary_out_status(struct Output *out, FILE *fp, time_t timestamp, switch (status) { case Port_Open: case Port_UdpOpen: + case Port_SctpOpen: case Port_IcmpEchoResponse: case Port_ArpOpen: foo[0] = Out_Open; break; case Port_Closed: case Port_UdpClosed: + case Port_SctpClosed: foo[0] = Out_Closed; break; default: diff --git a/src/output.c b/src/output.c index 1ba3d425..feb71edb 100644 --- a/src/output.c +++ b/src/output.c @@ -58,6 +58,8 @@ proto_from_status(unsigned status) case Port_UdpOpen: return "udp"; case Port_UdpClosed: return "udp"; case Port_ArpOpen: return "arp"; + case Port_SctpOpen: return "sctp"; + case Port_SctpClosed: return "sctp"; default: return "err"; } } @@ -76,6 +78,8 @@ status_string(int x) case Port_Closed: return "closed"; case Port_UdpOpen: return "open"; case Port_UdpClosed: return "closed"; + case Port_SctpOpen: return "open"; + case Port_SctpClosed: return "closed"; case Port_IcmpEchoResponse: return "open"; case Port_ArpOpen: return "open"; default: return "unknown"; @@ -604,12 +608,14 @@ output_report_status(struct Output *out, time_t timestamp, int status, case Port_Open: case Port_IcmpEchoResponse: case Port_UdpOpen: + case Port_SctpOpen: case Port_ArpOpen: default: break; case Port_Closed: case Port_UdpClosed: + case Port_SctpClosed: return; } @@ -677,6 +683,14 @@ output_report_status(struct Output *out, time_t timestamp, int status, if (out->is_open_only) return; break; + case Port_SctpOpen: + out->counts.sctp.open++; + break; + case Port_SctpClosed: + out->counts.sctp.closed++; + if (out->is_open_only) + return; + break; case Port_ArpOpen: out->counts.arp.open++; break; diff --git a/src/output.h b/src/output.h index 4316e568..69f3154f 100644 --- a/src/output.h +++ b/src/output.h @@ -68,6 +68,10 @@ struct Output uint64_t open; uint64_t closed; } udp; + struct { + uint64_t open; + uint64_t closed; + } sctp; struct { uint64_t echo; uint64_t timestamp; diff --git a/src/proto-icmp.c b/src/proto-icmp.c index 302995b8..7e34bc5e 100644 --- a/src/proto-icmp.c +++ b/src/proto-icmp.c @@ -98,8 +98,14 @@ handle_icmp(struct Output *out, time_t timestamp, case 3: /* destination unreachable */ switch (code) { case 0: /* net unreachable */ + /* We get these a lot while port scanning, often a flood coming + * back from broken/misconfigured networks */ + break; case 1: /* host unreachable */ + /* This means the router doesn't exist */ + break; case 2: /* protocol unreachable */ + /* The host exists, but it doesn't support SCTP */ break; case 3: /* port unreachable */ if (length - parsed->transport_offset > 8) { @@ -140,6 +146,16 @@ handle_icmp(struct Output *out, time_t timestamp, 0, px[parsed->ip_offset + 8]); break; + case 132: + output_report_status( + out, + timestamp, + Port_SctpClosed, + ip_them2, + port_them2, + 0, + px[parsed->ip_offset + 8]); + break; } } diff --git a/src/proto-preprocess.c b/src/proto-preprocess.c index 4095898d..f4a9f928 100644 --- a/src/proto-preprocess.c +++ b/src/proto-preprocess.c @@ -175,7 +175,12 @@ preprocess_frame(const unsigned char *px, unsigned length, unsigned link_type, parse_sctp: { - VERIFY_REMAINING(4, FOUND_SCTP); + VERIFY_REMAINING(12, FOUND_SCTP); + info->port_src = ex16be(px+offset+0); + info->port_dst = ex16be(px+offset+2); + info->app_offset = offset + 12; + info->app_length = length - info->app_offset; + assert(info->app_length < 2000); return 1; } diff --git a/src/proto-sctp.c b/src/proto-sctp.c new file mode 100644 index 00000000..a5b58ab2 --- /dev/null +++ b/src/proto-sctp.c @@ -0,0 +1,202 @@ +#include "proto-sctp.h" +#include "proto-preprocess.h" +#include "masscan-status.h" +#include "output.h" +#include +#include + + +#define CRC32C_POLY 0x1EDC6F41 +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +static unsigned long crc_c[256] = +{ +0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, +0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, +0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, +0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, +0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, +0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, +0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, +0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, +0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, +0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, +0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, +0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, +0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, +0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, +0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, +0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, +0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, +0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, +0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, +0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, +0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, +0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, +0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, +0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, +0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, +0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, +0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, +0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, +0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, +0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, +0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, +0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, +0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, +0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, +0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, +0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, +0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, +0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, +0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, +0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, +0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, +0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, +0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, +0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, +0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, +0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, +0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, +0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, +0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, +0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, +0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, +0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, +0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, +0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, +0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, +0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, +0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, +0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, +0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, +0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, +0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, +0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, +0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, +0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + + + + + +unsigned +sctp_checksum(const void *vbuffer, size_t length) +{ + const unsigned char *buffer = (const unsigned char *)vbuffer; + unsigned i; + unsigned crc32 = (unsigned)~0; + unsigned result; + unsigned char byte0,byte1,byte2,byte3; + + for (i = 0; i < 8; i++) { + CRC32C(crc32, buffer[i]); + } + + CRC32C(crc32, 0); + CRC32C(crc32, 0); + CRC32C(crc32, 0); + CRC32C(crc32, 0); + + for (i = 12; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} + + +/***************************************************************************** + *****************************************************************************/ +void +handle_sctp(struct Output *out, time_t timestamp, + const unsigned char *px, unsigned length, + unsigned cookie, + struct PreprocessedInfo *parsed) +{ + unsigned ip_them; + unsigned port_them = parsed->port_src; + unsigned verification_tag; + unsigned offset = parsed->transport_offset; + + ip_them = parsed->ip_src[0]<<24 | parsed->ip_src[1]<<16 + | parsed->ip_src[2]<< 8 | parsed->ip_src[3]<<0; + + verification_tag = px[offset + 4] << 24 | px[offset + 5] << 16 | + px[offset + 6] << 8 | px[offset + 7]; + if (cookie != verification_tag) + return; + + if (offset + 16 > length) + return; + + switch (px[offset + 12]) { + case 2: /* init ack */ + output_report_status( + out, + timestamp, + Port_SctpOpen, + ip_them, + port_them, + 0, + 0); + break; + case 6: /* abort */ + output_report_status( + out, + timestamp, + Port_SctpClosed, + ip_them, + port_them, + 0, + px[offset + 12]); + break; + default: + ; + } + + + +} + + +int +sctp_selftest(void) +{ + const char testcase[] = + "\xd1\x60\x00\x50\x00\x00\x00\x00\x58\xe4\x5d\x36\x01\x00\x00\x14" + "\x9e\x8d\x52\x25\x00\x00\x80\x00\x00\x0a\x08\x00\x46\x1a\xdf\x3d"; + + unsigned xsum; + + xsum = sctp_checksum(testcase, 32); + + printf("0x%08x\n", xsum); + + return 0; +} \ No newline at end of file diff --git a/src/proto-sctp.h b/src/proto-sctp.h new file mode 100644 index 00000000..7c48bbf3 --- /dev/null +++ b/src/proto-sctp.h @@ -0,0 +1,28 @@ +#ifndef PROTO_SCTP_H +#define PROTO_SCTP_H +#include + +struct PreprocessedInfo; +struct Output; + +/** + * Calculate the "CRC32c" checksum used in SCTP. This is a non-destructive + * checksum that skips the checksum field itself. + */ +unsigned +sctp_checksum(const void *vbuffer, size_t length); + +/** + * Handle incoming SCTP response + */ +void +handle_sctp(struct Output *out, time_t timestamp, + const unsigned char *px, unsigned length, + unsigned cookie, + struct PreprocessedInfo *parsed); + +int +sctp_selftest(void); + + +#endif diff --git a/src/templ-pkt.c b/src/templ-pkt.c index 7daf5c0a..8306c0d5 100644 --- a/src/templ-pkt.c +++ b/src/templ-pkt.c @@ -10,9 +10,9 @@ #include "templ-pkt.h" #include "templ-port.h" #include "proto-preprocess.h" +#include "proto-sctp.h" #include "string_s.h" #include "pixie-timer.h" -#include "proto-preprocess.h" #include "logger.h" #include "templ-payloads.h" #include "syn-cookie.h" @@ -73,18 +73,26 @@ static unsigned char default_sctp_template[] = "\x08\x00" /* Ethernet type: IPv4 */ "\x45" /* IP type */ "\x00" - "\x00\x1c" /* total length = 40 bytes */ + "\x00\x34" /* total length = 52 bytes */ "\x00\x00" /* identification */ "\x00\x00" /* fragmentation flags */ - "\xFF\x11" /* TTL=255, proto=UDP */ - "\xFF\xFF" /* checksum */ + "\xFF\x84" /* TTL=255, proto = SCTP */ + "\x00\x00" /* checksum */ "\0\0\0\0" /* source address */ "\0\0\0\0" /* destination address */ - "\xfe\xdc" /* source port */ - "\0\0" /* destination port */ - "\0\0\0\0" /* checksum */ - "\0\0\0\0" /* length */ + "\x00\x00" /* source port */ + "\x00\x00" /* destination port */ + "\x00\x00\x00\x00" /* verification tag */ + "\x58\xe4\x5d\x36" /* checksum */ + "\x01" /* type = init */ + "\x00" /* flags = none */ + "\x00\x14" /* length = 20 */ + "\x9e\x8d\x52\x25" /* initiate tag */ + "\x00\x00\x80\x00" /* receiver window credit */ + "\x00\x0a" /* outbound streams = 10 */ + "\x08\x00" /* inbound streams = 2048 */ + "\x46\x1a\xdf\x3d" /* initial TSN */ ; @@ -169,7 +177,7 @@ static unsigned char default_arp_template[] = static unsigned ip_header_checksum(const unsigned char *px, unsigned offset, unsigned max_offset) { - unsigned header_length = (px[offset]>>2)&0xFC; + unsigned header_length = (px[offset]&0xF) * 4; unsigned xsum = 0; unsigned i; @@ -481,6 +489,9 @@ udp_payload_fixup(struct TemplatePacket *tmpl, unsigned port, unsigned seqno) /*************************************************************************** + * This is the function that formats the transmitted packets for probing + * machines. It takes a template for the protocol (usually a TCP SYN + * packet), then sets the destination IP address and port numbers. ***************************************************************************/ void template_set_target( @@ -495,6 +506,8 @@ template_set_target( uint64_t xsum; unsigned ip_id; struct TemplatePacket *tmpl = NULL; + unsigned xsum2; + unsigned xsum3; /* * Find out which packet template to use. This is because we can @@ -575,20 +588,25 @@ template_set_target( xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = ~xsum; -#if 0 - xsum = *(unsigned*)&px[offset_ip+0]; - xsum += *(unsigned*)&px[offset_ip+4]; - xsum += *(unsigned*)&px[offset_ip+8]; - xsum += *(unsigned*)&px[offset_ip+12]; - xsum += *(unsigned*)&px[offset_ip+16]; - xsum = (xsum >> 16) + (xsum & 0xFFFF); - xsum = (xsum >> 16) + (xsum & 0xFFFF); - xsum = (xsum >> 16) + (xsum & 0xFFFF); - xsum = ~xsum; -#endif + px[offset_ip+10] = (unsigned char)(0); + px[offset_ip+11] = (unsigned char)(0); - px[offset_ip+10] = (unsigned char)(xsum >> 8); - px[offset_ip+11] = (unsigned char)(xsum & 0xFF); + xsum2 = (unsigned)~ip_header_checksum(px, offset_ip, tmpl->length); + + + xsum3 = *(unsigned*)&px[offset_ip+0]; + xsum3 += *(unsigned*)&px[offset_ip+4]; + xsum3 += *(unsigned*)&px[offset_ip+8]; + xsum3 += *(unsigned*)&px[offset_ip+12]; + xsum3 += *(unsigned*)&px[offset_ip+16]; + xsum3 = (xsum3 >> 16) + (xsum3 & 0xFFFF); + xsum3 = (xsum3 >> 16) + (xsum3 & 0xFFFF); + xsum3 = (xsum3 >> 16) + (xsum3 & 0xFFFF); + xsum3 = (~xsum3) & 0xFFFF; + + + px[offset_ip+10] = (unsigned char)(xsum2 >> 8); + px[offset_ip+11] = (unsigned char)(xsum2 & 0xFF); /* @@ -646,6 +664,21 @@ template_set_target( px[offset_tcp+7] = (unsigned char)(xsum >> 0); break; case Proto_SCTP: + px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); + px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); + px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); + px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); + + px[offset_tcp+16] = (unsigned char)(seqno >> 24); + px[offset_tcp+17] = (unsigned char)(seqno >> 16); + px[offset_tcp+18] = (unsigned char)(seqno >> 8); + px[offset_tcp+19] = (unsigned char)(seqno >> 0); + + xsum = sctp_checksum(px + offset_tcp, tmpl->length - offset_tcp); + px[offset_tcp+ 8] = (unsigned char)(xsum >> 24); + px[offset_tcp+ 9] = (unsigned char)(xsum >> 16); + px[offset_tcp+10] = (unsigned char)(xsum >> 8); + px[offset_tcp+11] = (unsigned char)(xsum >> 0); break; case Proto_ICMP_ping: case Proto_ICMP_timestamp: @@ -789,6 +822,12 @@ _template_init( tmpl->checksum_tcp = udp_checksum(tmpl); tmpl->proto = Proto_UDP; break; + case 132: /* SCTP */ + tmpl->checksum_tcp = sctp_checksum( + tmpl->packet + tmpl->offset_tcp, + tmpl->length - tmpl->offset_tcp); + tmpl->proto = Proto_SCTP; + break; } /* diff --git a/vs10/masscan.vcxproj b/vs10/masscan.vcxproj index 8c4b3bf0..deb8a6ae 100644 --- a/vs10/masscan.vcxproj +++ b/vs10/masscan.vcxproj @@ -40,6 +40,7 @@ + @@ -112,6 +113,7 @@ + diff --git a/vs10/masscan.vcxproj.filters b/vs10/masscan.vcxproj.filters index 8fd8e314..b2df1400 100644 --- a/vs10/masscan.vcxproj.filters +++ b/vs10/masscan.vcxproj.filters @@ -219,6 +219,9 @@ Source Files\proto + + Source Files\proto + @@ -380,6 +383,9 @@ Source Files\proto + + Source Files\proto +