Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| /* | |
| main | |
| This includes: | |
| * main() | |
| * transmit_thread() - transmits probe packets | |
| * receive_thread() - receives response packets | |
| You'll be wanting to study the transmit/receive threads, because that's | |
| where all the action is. | |
| This is the lynch-pin of the entire program, so it includes a heckuva lot | |
| of headers, and the functions have a lot of local variables. I'm trying | |
| to make this file relative "flat" this way so that everything is visible. | |
| */ | |
| #include "masscan.h" | |
| #include "masscan-status.h" /* open or closed */ | |
| #include "rand-blackrock.h" /* the BlackRock shuffling func */ | |
| #include "rand-lcg.h" /* the LCG randomization func */ | |
| #include "templ-pkt.h" /* packet template, that we use to send */ | |
| #include "rawsock.h" /* api on top of Linux, Windows, Mac OS X*/ | |
| #include "logger.h" /* adjust with -v command-line opt */ | |
| #include "main-status.h" /* printf() regular status updates */ | |
| #include "main-throttle.h" /* rate limit */ | |
| #include "main-dedup.h" /* ignore duplicate responses */ | |
| #include "main-ptrace.h" /* for nmap --packet-trace feature */ | |
| #include "proto-arp.h" /* for responding to ARP requests */ | |
| #include "proto-banner1.h" /* for snatching banners from systems */ | |
| #include "proto-tcp.h" /* for TCP/IP connection table */ | |
| #include "proto-preprocess.h" /* quick parse of packets */ | |
| #include "proto-icmp.h" /* handle ICMP responses */ | |
| #include "proto-udp.h" /* handle UDP responses */ | |
| #include "syn-cookie.h" /* for SYN-cookies on send */ | |
| #include "output.h" /* for outputing results */ | |
| #include "rte-ring.h" /* producer/consumer ring buffer */ | |
| #include "rawsock-pcapfile.h" /* for saving pcap files w/ raw packets */ | |
| #include "smack.h" /* Aho-corasick state-machine pattern-matcher */ | |
| #include "pixie-timer.h" /* portable time functions */ | |
| #include "pixie-threads.h" /* portable threads */ | |
| #include "templ-payloads.h" /* UDP packet payloads */ | |
| #include "proto-snmp.h" /* parse SNMP responses */ | |
| #include "templ-port.h" | |
| #include "in-binary.h" /* covert binary output to XML/JSON */ | |
| #include "main-globals.h" /* all the global variables in the program */ | |
| #include "proto-zeroaccess.h" | |
| #include "siphash24.h" | |
| #include <assert.h> | |
| #include <limits.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #include <stdlib.h> | |
| #include <signal.h> | |
| #include <stdint.h> | |
| #if defined(WIN32) | |
| #include <WinSock.h> | |
| #if defined(_MSC_VER) | |
| #pragma comment(lib, "Ws2_32.lib") | |
| #endif | |
| #else | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <unistd.h> | |
| #endif | |
| /* | |
| * yea I know globals suck | |
| */ | |
| unsigned control_c_pressed = 0; | |
| static unsigned control_c_pressed_again = 0; | |
| time_t global_now; | |
| static unsigned global_wait = 10; | |
| /*************************************************************************** | |
| * We create a pair of transmit/receive threads for each network adapter. | |
| * This structure contains the parameters we send to each pair. | |
| ***************************************************************************/ | |
| struct ThreadPair { | |
| /** This points to the central configuration. Note that it's 'const', | |
| * meaning that the thread cannot change the contents. That'd be | |
| * unsafe */ | |
| const struct Masscan *masscan; | |
| /** The adapter used by the thread-pair. Normally, thread-pairs have | |
| * their own network adapter, especially when doing PF_RING | |
| * clustering. */ | |
| struct Adapter *adapter; | |
| /** | |
| * The thread-pair use a "packet_buffer" and "transmit_queue" to | |
| * send packets to each other. That's because when doing things | |
| * like banner-checking, the receive-thread needs to respond to | |
| * things like syn-acks received from the target. However, the | |
| * receive-thread cannot transmit packets, so it uses this ring | |
| * in order to send the packets to the transmit thread for | |
| * transmission. | |
| */ | |
| PACKET_QUEUE *packet_buffers; | |
| PACKET_QUEUE *transmit_queue; | |
| /** | |
| * The index of the network adapter that we are using for this | |
| * thread-pair. This is an index into the "masscan->nic[]" | |
| * array. | |
| * | |
| * NOTE: this is also the "thread-id", because we create one | |
| * transmit/receive thread pair per NIC. | |
| */ | |
| unsigned nic_index; | |
| /** | |
| * This is an optimized binary-search when looking up IP addresses | |
| * based on the index. When scanning the entire Internet, the target | |
| * list is broken into thousands of subranges as we exclude certain | |
| * ranges. Doing a lookup for each IP address is slow, so this 'picker' | |
| * system speeds it up. | |
| */ | |
| unsigned *picker; | |
| /** | |
| * A copy of the master 'index' variable. This is just advisory for | |
| * other threads, to tell them how far we've gotten. | |
| */ | |
| uint64_t my_index; | |
| /* This is used both by the transmit and receive thread for | |
| * formatting packets */ | |
| struct TemplateSet tmplset[1]; | |
| /** | |
| * The current IP address we are using for transmit/receive. | |
| */ | |
| struct Source src; | |
| unsigned char adapter_mac[6]; | |
| unsigned char router_mac[6]; | |
| unsigned done_transmitting; | |
| unsigned done_receiving; | |
| double pt_start; | |
| struct Throttler throttler[1]; | |
| uint64_t *total_synacks; | |
| uint64_t *total_tcbs; | |
| uint64_t *total_syns; | |
| }; | |
| /*************************************************************************** | |
| * The recieve thread doesn't transmit packets. Instead, it queues them | |
| * up on the transmit thread. Every so often, the transmit thread needs | |
| * to flush this transmit queue and send everything. | |
| * | |
| * This is an inherent design issue trying to send things as batches rather | |
| * than individually. It increases latency, but increases performance. We | |
| * don't really care about latency. | |
| ***************************************************************************/ | |
| static void | |
| flush_packets(struct Adapter *adapter, | |
| PACKET_QUEUE *packet_buffers, | |
| PACKET_QUEUE *transmit_queue, | |
| uint64_t *packets_sent, | |
| uint64_t *batchsize) | |
| { | |
| /* | |
| * Send a batch of queued packets | |
| */ | |
| for ( ; (*batchsize); (*batchsize)--) { | |
| int err; | |
| struct PacketBuffer *p; | |
| /* | |
| * Get the next packet from the transmit queue. This packet was | |
| * put there by a receive thread, and will contain things like | |
| * an ACK or an HTTP request | |
| */ | |
| err = rte_ring_sc_dequeue(transmit_queue, (void**)&p); | |
| if (err) { | |
| break; /* queue is empty, nothing to send */ | |
| } | |
| /* | |
| * Actually send the packet | |
| */ | |
| rawsock_send_packet(adapter, p->px, (unsigned)p->length, 1); | |
| /* | |
| * Now that we are done with the packet, put it on the free list | |
| * of buffers that the transmit thread can reuse | |
| */ | |
| for (err=1; err; ) { | |
| err = rte_ring_sp_enqueue(packet_buffers, p); | |
| if (err) { | |
| LOG(0, "transmit queue full (should be impossible)\n"); | |
| pixie_usleep(10000); | |
| } | |
| } | |
| /* | |
| * Remember that we sent a packet, which will be used in | |
| * throttling. | |
| */ | |
| (*packets_sent)++; | |
| } | |
| } | |
| /*************************************************************************** | |
| * We support a range of source IP/port. This function converts that | |
| * range into useful variables we can use to pick things form that range. | |
| ***************************************************************************/ | |
| static void | |
| get_sources(const struct Masscan *masscan, | |
| unsigned nic_index, | |
| unsigned *src_ip, | |
| unsigned *src_ip_mask, | |
| unsigned *src_port, | |
| unsigned *src_port_mask) | |
| { | |
| const struct Source *src = &masscan->nic[nic_index].src; | |
| *src_ip = src->ip.first; | |
| *src_ip_mask = src->ip.last - src->ip.first; | |
| *src_port = src->port.first; | |
| *src_port_mask = src->port.last - src->port.first; | |
| } | |
| /*************************************************************************** | |
| * This thread spews packets as fast as it can | |
| * | |
| * THIS IS WHERE ALL THE EXCITEMENT HAPPENS!!!! | |
| * 90% of CPU cycles are in the function. | |
| * | |
| ***************************************************************************/ | |
| static void | |
| transmit_thread(void *v) /*aka. scanning_thread() */ | |
| { | |
| struct ThreadPair *parms = (struct ThreadPair *)v; | |
| uint64_t i; | |
| uint64_t start; | |
| uint64_t end; | |
| const struct Masscan *masscan = parms->masscan; | |
| unsigned retries = masscan->retries; | |
| unsigned rate = (unsigned)masscan->max_rate; | |
| unsigned r = retries + 1; | |
| uint64_t range; | |
| struct BlackRock blackrock; | |
| uint64_t count_ips = rangelist_count(&masscan->targets); | |
| struct Throttler *throttler = parms->throttler; | |
| struct TemplateSet pkt_template = templ_copy(parms->tmplset); | |
| unsigned *picker = parms->picker; | |
| struct Adapter *adapter = parms->adapter; | |
| uint64_t packets_sent = 0; | |
| unsigned increment = (masscan->shard.of-1) + masscan->nic_count; | |
| unsigned src_ip; | |
| unsigned src_ip_mask; | |
| unsigned src_port; | |
| unsigned src_port_mask; | |
| uint64_t seed = masscan->seed; | |
| uint64_t repeats = 0; /* --infinite repeats */ | |
| uint64_t total_syns = 0; | |
| parms->total_syns = &total_syns; | |
| get_sources(masscan, parms->nic_index, | |
| &src_ip, &src_ip_mask, | |
| &src_port, &src_port_mask); | |
| LOG(1, "xmit: starting transmit thread #%u\n", parms->nic_index); | |
| /* "THROTTLER" rate-limits how fast we transmit, set with the | |
| * --max-rate parameter */ | |
| throttler_start(throttler, masscan->max_rate/masscan->nic_count); | |
| infinite: | |
| /* Create the shuffler/randomizer. This creates the 'range' variable, | |
| * which is simply the number of IP addresses times the number of | |
| * ports */ | |
| range = rangelist_count(&masscan->targets) | |
| * rangelist_count(&masscan->ports); | |
| blackrock_init(&blackrock, range, seed); | |
| /* Calculate the 'start' and 'end' of a scan. One reason to do this is | |
| * to support --shard, so that multiple machines can co-operate on | |
| * the same scan. Another reason to do this is so that we can bleed | |
| * a little bit past the end when we have --retries. Yet another | |
| * thing to do here is deal with multiple network adapters, which | |
| * is essentially the same logic as shards. */ | |
| start = masscan->resume.index + (masscan->shard.one-1) + parms->nic_index; | |
| end = range; | |
| if (masscan->resume.count && end > start + masscan->resume.count) | |
| end = start + masscan->resume.count; | |
| end += retries * rate; | |
| /* ----------------- | |
| * the main loop | |
| * -----------------*/ | |
| LOG(3, "xmit: starting main loop: [%llu..%llu]\n", start, end); | |
| for (i=start; i<end; ) { | |
| uint64_t batch_size; | |
| /* | |
| * Do a batch of many packets at a time. That because per-packet | |
| * throttling is expensive at 10-million pps, so we reduce the | |
| * per-packet cost by doing batches. At slower rates, the batch | |
| * size will always be one. (--max-rate) | |
| */ | |
| batch_size = throttler_next_batch(throttler, packets_sent); | |
| /* | |
| * Transmit packets from other thread, when doing --banners. This | |
| * takes priority over sending SYN packets. If there is so much | |
| * activity grabbing banners that we cannot transmit more SYN packets, | |
| * then "batch_size" will get decremented to zero, and we won't be | |
| * able to transmit SYN packets. | |
| */ | |
| flush_packets(adapter, parms->packet_buffers, parms->transmit_queue, | |
| &packets_sent, &batch_size); | |
| /* | |
| * Transmit a bunch of packets. At any rate slower than 100,000 | |
| * packets/second, the 'batch_size' is likely to be 1 | |
| */ | |
| while (batch_size && i < end) { | |
| uint64_t xXx; | |
| unsigned ip_them; | |
| unsigned port_them; | |
| unsigned ip_me; | |
| unsigned port_me; | |
| uint64_t cookie; | |
| /* | |
| * RANDOMIZE THE TARGET: | |
| * This is kinda a tricky bit that picks a random IP and port | |
| * number in order to scan. We monotonically increment the | |
| * index 'i' from [0..range]. We then shuffle (randomly transmog) | |
| * that index into some other, but unique/1-to-1, number in the | |
| * same range. That way we visit all targets, but in a random | |
| * order. Then, once we've shuffled the index, we "pick" the | |
| * the IP address and port that the index refers to. | |
| */ | |
| xXx = (i + (r--) * rate); | |
| if (rate > range) | |
| xXx %= range; | |
| else | |
| while (xXx >= range) | |
| xXx -= range; | |
| xXx = blackrock_shuffle(&blackrock, xXx); | |
| ip_them = rangelist_pick2(&masscan->targets, xXx % count_ips, picker); | |
| port_them = rangelist_pick(&masscan->ports, xXx / count_ips); | |
| /* | |
| * SYN-COOKIE LOGIC | |
| * Figure out the source IP/port, and the SYN cookie | |
| */ | |
| if (src_ip_mask > 1 || src_port_mask > 1) { | |
| uint64_t ck = syn_cookie((unsigned)(i+repeats), | |
| (unsigned)((i+repeats)>>32), | |
| (unsigned)xXx, (unsigned)(xXx>>32)); | |
| port_me = src_port + (ck & src_port_mask); | |
| ip_me = src_ip + ((ck>>16) & src_ip_mask); | |
| } else { | |
| ip_me = src_ip; | |
| port_me = src_port; | |
| } | |
| cookie = syn_cookie(ip_them, port_them, ip_me, port_me); | |
| /* | |
| * SEND THE PROBE | |
| * This is sorta the entire point of the program, but little | |
| * exciting happens here. The thing to note that this may | |
| * be a "raw" transmit that bypasses the kernel, meaning | |
| * we can call this function millions of times a second. | |
| */ | |
| rawsock_send_probe( | |
| adapter, | |
| ip_them, port_them, | |
| ip_me, port_me, | |
| (unsigned)cookie, | |
| !batch_size, /* flush queue on last packet in batch */ | |
| &pkt_template | |
| ); | |
| batch_size--; | |
| packets_sent++; | |
| total_syns++; | |
| /* | |
| * SEQUENTIALLY INCREMENT THROUGH THE RANGE | |
| * Yea, I know this is a puny 'i++' here, but it's a core feature | |
| * of the system that is linearly increments through the range, | |
| * but produces from that a shuffled sequence of targets (as | |
| * described above). Because we are linearly incrementing this | |
| * number, we can do lots of creative stuff, like doing clever | |
| * retransmits and sharding. | |
| */ | |
| if (r == 0) { | |
| i += increment; /* <------ increment by 1 normally, more with shards/nics */ | |
| r = retries + 1; | |
| } | |
| } /* end of batch */ | |
| /* If the user pressed <ctrl-c>, then we need to exit. but, in case | |
| * the user wants to --resume the scan later, we save the current | |
| * state in a file */ | |
| if (control_c_pressed) { | |
| break; | |
| } | |
| /* save our current location for resuming, if the user pressed | |
| * <ctrl-c> to exit early */ | |
| parms->my_index = i; | |
| } | |
| /* | |
| * --infinite | |
| * For load testing, go around and do this again | |
| */ | |
| if (masscan->is_infinite && !control_c_pressed) { | |
| seed++; | |
| repeats++; | |
| goto infinite; | |
| } | |
| /* | |
| * We are done transmitting. However, response packets will take several | |
| * seconds to arrive. Therefore, sit in short loop waiting for those | |
| * packets to arrive. Pressing <ctrl-c> a second time will exit this | |
| * prematurely. | |
| */ | |
| while (!control_c_pressed_again) { | |
| unsigned k; | |
| uint64_t batch_size; | |
| for (k=0; k<1000; k++) { | |
| /* | |
| * Only send a few packets at a time, throttled according to the max | |
| * --max-rate set by the user | |
| */ | |
| batch_size = throttler_next_batch(throttler, packets_sent); | |
| /* Transmit packets from the receive thread */ | |
| flush_packets( adapter, | |
| parms->packet_buffers, | |
| parms->transmit_queue, | |
| &packets_sent, | |
| &batch_size); | |
| pixie_usleep(1000); | |
| } | |
| } | |
| /* Thread is about to exit */ | |
| parms->done_transmitting = 1; | |
| LOG(1, "xmit: stopping transmit thread #%u\n", parms->nic_index); | |
| } | |
| /*************************************************************************** | |
| ***************************************************************************/ | |
| static unsigned | |
| is_nic_port(const struct Masscan *masscan, unsigned ip) | |
| { | |
| unsigned i; | |
| for (i=0; i<masscan->nic_count; i++) | |
| if (is_my_port(&masscan->nic[i].src, ip)) | |
| return 1; | |
| return 0; | |
| } | |
| /*************************************************************************** | |
| * | |
| * Asynchronous receive thread | |
| * | |
| * The transmit and receive threads run independently of each other. There | |
| * is no record what was transmitted. Instead, the transmit thread sets a | |
| * "SYN-cookie" in transmitted packets, which the receive thread will then | |
| * use to match up requests with responses. | |
| ***************************************************************************/ | |
| static void | |
| receive_thread(void *v) | |
| { | |
| struct ThreadPair *parms = (struct ThreadPair *)v; | |
| const struct Masscan *masscan = parms->masscan; | |
| struct Adapter *adapter = parms->adapter; | |
| struct Output *out; | |
| struct DedupTable *dedup; | |
| struct PcapFile *pcapfile = NULL; | |
| struct TCP_ConnectionTable *tcpcon = 0; | |
| uint64_t total_synacks = 0; | |
| uint64_t total_tcbs = 0; | |
| parms->total_synacks = &total_synacks; | |
| parms->total_tcbs = &total_tcbs; | |
| LOG(1, "recv: start receive thread #%u\n", parms->nic_index); | |
| /* Lock this thread to a CPU. Transmit threads are on even CPUs, | |
| * receive threads on odd CPUs */ | |
| if (pixie_cpu_get_count() > 1) { | |
| unsigned cpu_count = pixie_cpu_get_count(); | |
| unsigned cpu = parms->nic_index * 2 + 1; | |
| while (cpu >= cpu_count) { | |
| cpu -= cpu_count; | |
| cpu++; | |
| } | |
| pixie_cpu_set_affinity(cpu); | |
| } | |
| /* | |
| * If configured, open a --pcap file for saving raw packets. This is | |
| * so that we can debug scans, but also so that we can look at the | |
| * strange things people send us. Note that we don't record transmitted | |
| * packets, just the packets we've received. | |
| */ | |
| if (masscan->pcap_filename[0]) { | |
| pcapfile = pcapfile_openwrite(masscan->pcap_filename, 1); | |
| } | |
| /* | |
| * Open output. This is where results are reported when saving | |
| * the --output-format to the --output-filename | |
| */ | |
| out = output_create(masscan, parms->nic_index); | |
| /* | |
| * Create deduplication table. This is so when somebody sends us | |
| * multiple responses, we only record the first one. | |
| */ | |
| dedup = dedup_create(); | |
| /* | |
| * Create a TCP connection table for interacting with live | |
| * connections when doing --banners | |
| */ | |
| if (masscan->is_banners) { | |
| tcpcon = tcpcon_create_table( | |
| (size_t)((masscan->max_rate/5) / masscan->nic_count), | |
| parms->transmit_queue, | |
| parms->packet_buffers, | |
| &parms->tmplset->pkts[Proto_TCP], | |
| output_report_banner, | |
| out, | |
| masscan->tcb.timeout | |
| ); | |
| if (masscan->http_user_agent_length) | |
| tcpcon_set_parameter( tcpcon, | |
| "http-user-agent", | |
| masscan->http_user_agent_length, | |
| masscan->http_user_agent); | |
| } | |
| /* | |
| * In "offline" mode, we don't have any receive threads, so simply | |
| * wait until transmitter thread is done then go to the end | |
| */ | |
| if (masscan->is_offline) { | |
| while (!control_c_pressed_again) | |
| pixie_usleep(10000); | |
| parms->done_receiving = 1; | |
| goto end; | |
| } | |
| /* | |
| * Receive packets. This is where we catch any responses and print | |
| * them to the terminal. | |
| */ | |
| LOG(1, "begin receive thread\n"); | |
| while (!control_c_pressed_again) { | |
| int status; | |
| unsigned length; | |
| unsigned secs; | |
| unsigned usecs; | |
| const unsigned char *px; | |
| int err; | |
| unsigned x; | |
| struct PreprocessedInfo parsed; | |
| unsigned ip_me; | |
| unsigned port_me; | |
| unsigned ip_them; | |
| unsigned port_them; | |
| unsigned seqno_me; | |
| unsigned seqno_them; | |
| unsigned cookie; | |
| /* | |
| * RECEIVE | |
| * | |
| * This is the boring part of actually receiving a packet | |
| */ | |
| err = rawsock_recv_packet( | |
| adapter, | |
| &length, | |
| &secs, | |
| &usecs, | |
| &px); | |
| if (err != 0) { | |
| if (tcpcon) | |
| tcpcon_timeouts(tcpcon, (unsigned)time(0), 0); | |
| continue; | |
| } | |
| /* | |
| * Do any TCP event timeouts based on the current timestamp from | |
| * the packet. For example, if the connection has been open for | |
| * around 10 seconds, we'll close the connection. (--banners) | |
| */ | |
| if (tcpcon) { | |
| tcpcon_timeouts(tcpcon, secs, usecs); | |
| } | |
| if (length > 1514) | |
| continue; | |
| /* | |
| * "Preprocess" the response packet. This means to go through and | |
| * figure out where the TCP/IP headers are and the locations of | |
| * some fields, like IP address and port numbers. | |
| */ | |
| x = preprocess_frame(px, length, 1, &parsed); | |
| if (!x) | |
| continue; /* corrupt packet */ | |
| ip_me = parsed.ip_dst[0]<<24 | parsed.ip_dst[1]<<16 | |
| | parsed.ip_dst[2]<< 8 | parsed.ip_dst[3]<<0; | |
| ip_them = parsed.ip_src[0]<<24 | parsed.ip_src[1]<<16 | |
| | parsed.ip_src[2]<< 8 | parsed.ip_src[3]<<0; | |
| port_me = parsed.port_dst; | |
| port_them = parsed.port_src; | |
| seqno_them = TCP_SEQNO(px, parsed.transport_offset); | |
| seqno_me = TCP_ACKNO(px, parsed.transport_offset); | |
| cookie = syn_cookie(ip_them, port_them, ip_me, port_me) & 0xFFFFFFFF; | |
| /* verify: my IP address */ | |
| if (!is_my_ip(&parms->src, ip_me)) | |
| continue; | |
| /* | |
| * Handle non-TCP protocols | |
| */ | |
| switch (parsed.found) { | |
| case FOUND_ARP: | |
| LOGip(2, ip_them, 0, "-> ARP [%u] \n", px[parsed.found_offset]); | |
| switch (px[parsed.found_offset + 6]<<8 | px[parsed.found_offset+7]) { | |
| case 1: /* request */ | |
| /* This function will transmit a "reply" to somebody's ARP request | |
| * for our IP address (as part of our user-mode TCP/IP). | |
| * Since we completely bypass the TCP/IP stack, we have to handle ARPs | |
| * ourself, or the router will lose track of us.*/ | |
| arp_response( ip_me, | |
| parms->adapter_mac, | |
| px, length, | |
| parms->packet_buffers, | |
| parms->transmit_queue); | |
| break; | |
| case 2: /* response */ | |
| /* This is for "arp scan" mode, where we are ARPing targets rather | |
| * than port scanning them */ | |
| /* If we aren't doing an ARP scan, then ignore ARP responses */ | |
| if (!masscan->is_arp) | |
| break; | |
| /* If this response isn't in our range, then ignore it */ | |
| if (!rangelist_is_contains(&masscan->targets, ip_them)) | |
| break; | |
| /* Ignore duplicates */ | |
| if (dedup_is_duplicate(dedup, ip_them, 0, ip_me, 0)) | |
| continue; | |
| /* ...everything good, so now report this response */ | |
| handle_arp(out, secs, px, length, &parsed); | |
| break; | |
| } | |
| continue; | |
| case FOUND_UDP: | |
| case FOUND_DNS: | |
| if (!is_nic_port(masscan, port_me)) | |
| continue; | |
| if (parms->masscan->nmap.packet_trace) | |
| packet_trace(stdout, parms->pt_start, px, length, 0); | |
| handle_udp(out, secs, px, length, &parsed); | |
| continue; | |
| case FOUND_ICMP: | |
| handle_icmp(out, secs, px, length, &parsed); | |
| continue; | |
| case FOUND_TCP: | |
| /* fall down to below */ | |
| break; | |
| default: | |
| continue; | |
| } | |
| /* verify: my port number */ | |
| if (!is_my_port(&parms->src, port_me)) | |
| continue; | |
| if (parms->masscan->nmap.packet_trace) | |
| packet_trace(stdout, parms->pt_start, px, length, 0); | |
| /* Save raw packet in --pcap file */ | |
| if (pcapfile) { | |
| pcapfile_writeframe( | |
| pcapfile, | |
| px, | |
| length, | |
| length, | |
| secs, | |
| usecs); | |
| } | |
| { | |
| char buf[64]; | |
| LOGip(5, ip_them, port_them, "-> TCP ackno=0x%08x flags=0x%02x(%s)\n", | |
| seqno_me, | |
| TCP_FLAGS(px, parsed.transport_offset), | |
| reason_string(TCP_FLAGS(px, parsed.transport_offset), buf, sizeof(buf))); | |
| } | |
| /* If recording --banners, create a new "TCP Control Block (TCB)" */ | |
| if (tcpcon) { | |
| struct TCP_Control_Block *tcb; | |
| /* does a TCB already exist for this connection? */ | |
| tcb = tcpcon_lookup_tcb(tcpcon, | |
| ip_me, ip_them, | |
| port_me, port_them); | |
| if (TCP_IS_SYNACK(px, parsed.transport_offset)) { | |
| if (cookie != seqno_me - 1) { | |
| LOG(2, "%u.%u.%u.%u - bad cookie: ackno=0x%08x expected=0x%08x\n", | |
| (ip_them>>24)&0xff, (ip_them>>16)&0xff, (ip_them>>8)&0xff, (ip_them>>0)&0xff, | |
| seqno_me-1, cookie); | |
| continue; | |
| } | |
| if (tcb == NULL) { | |
| tcb = tcpcon_create_tcb(tcpcon, | |
| ip_me, ip_them, | |
| port_me, port_them, | |
| seqno_me, seqno_them+1); | |
| total_tcbs++; | |
| } | |
| tcpcon_handle(tcpcon, tcb, TCP_WHAT_SYNACK, | |
| 0, 0, secs, usecs, seqno_them+1); | |
| } else if (tcb) { | |
| /* If this is an ACK, then handle that first */ | |
| if (TCP_IS_ACK(px, parsed.transport_offset)) { | |
| tcpcon_handle(tcpcon, tcb, TCP_WHAT_ACK, | |
| 0, seqno_me, secs, usecs, seqno_them); | |
| } | |
| /* If this contains payload, handle that */ | |
| if (parsed.app_length) { | |
| tcpcon_handle(tcpcon, tcb, TCP_WHAT_DATA, | |
| px + parsed.app_offset, parsed.app_length, | |
| secs, usecs, seqno_them); | |
| } | |
| /* If this is a FIN, handle that. Note that ACK + | |
| * payload + FIN can come together */ | |
| if (TCP_IS_FIN(px, parsed.transport_offset) | |
| && !TCP_IS_RST(px, parsed.transport_offset)) { | |
| tcpcon_handle(tcpcon, tcb, TCP_WHAT_FIN, | |
| 0, 0, secs, usecs, seqno_them); | |
| } | |
| /* If this is a RST, then we'll be closing the connection */ | |
| if (TCP_IS_RST(px, parsed.transport_offset)) { | |
| tcpcon_handle(tcpcon, tcb, TCP_WHAT_RST, | |
| 0, 0, secs, usecs, seqno_them); | |
| } | |
| } else if (TCP_IS_FIN(px, parsed.transport_offset)) { | |
| /* | |
| * NO TCB! | |
| * This happens when we've sent a FIN, deleted our connection, | |
| * but the other side didn't get the packet. | |
| */ | |
| if (!TCP_IS_RST(px, parsed.transport_offset)) | |
| tcpcon_send_FIN( | |
| tcpcon, | |
| ip_me, ip_them, | |
| port_me, port_them, | |
| seqno_them, seqno_me); | |
| } | |
| } | |
| if (TCP_IS_SYNACK(px, parsed.transport_offset)) { | |
| /* figure out the status */ | |
| status = Port_Unknown; | |
| if ((px[parsed.transport_offset+13] & 0x2) == 0x2) | |
| status = Port_Open; | |
| if ((px[parsed.transport_offset+13] & 0x4) == 0x4) { | |
| status = Port_Closed; | |
| } | |
| /* verify: syn-cookies */ | |
| if (cookie != seqno_me - 1) { | |
| LOG(5, "%u.%u.%u.%u - bad cookie: ackno=0x%08x expected=0x%08x\n", | |
| (ip_them>>24)&0xff, (ip_them>>16)&0xff, | |
| (ip_them>>8)&0xff, (ip_them>>0)&0xff, | |
| seqno_me-1, cookie); | |
| continue; | |
| } | |
| /* verify: ignore duplicates */ | |
| if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) | |
| continue; | |
| total_synacks++; | |
| /* | |
| * This is where we do the output | |
| */ | |
| output_report_status( | |
| out, | |
| global_now, | |
| status, | |
| ip_them, | |
| port_them, | |
| px[parsed.transport_offset + 13], /* tcp flags */ | |
| px[parsed.ip_offset + 8] /* ttl */ | |
| ); | |
| /* | |
| * Send RST so other side isn't left hanging (only doing this in | |
| * complete stateless mode where we aren't tracking banners) | |
| */ | |
| if (tcpcon == NULL) | |
| tcp_send_RST( | |
| &parms->tmplset->pkts[Proto_TCP], | |
| parms->packet_buffers, | |
| parms->transmit_queue, | |
| ip_them, ip_me, | |
| port_them, port_me, | |
| 0, seqno_me); | |
| } | |
| } | |
| LOG(1, "recv: end receive thread #%u\n", parms->nic_index); | |
| /* | |
| * cleanup | |
| */ | |
| end: | |
| if (tcpcon) | |
| tcpcon_destroy_table(tcpcon); | |
| dedup_destroy(dedup); | |
| output_destroy(out); | |
| if (pcapfile) | |
| pcapfile_close(pcapfile); | |
| for (;;) { | |
| void *p; | |
| int err; | |
| err = rte_ring_sc_dequeue(parms->packet_buffers, (void**)&p); | |
| if (err == 0) | |
| free(p); | |
| else | |
| break; | |
| } | |
| /* Thread is about to exit */ | |
| parms->done_receiving = 1; | |
| } | |
| /*************************************************************************** | |
| * We trap the <ctrl-c> so that instead of exiting immediately, we sit in | |
| * a loop for a few seconds waiting for any late response. But, the user | |
| * can press <ctrl-c> a second time to exit that waiting. | |
| ***************************************************************************/ | |
| static void control_c_handler(int x) | |
| { | |
| if (control_c_pressed == 0) { | |
| fprintf(stderr, | |
| "waiting %u seconds to exit..." | |
| " \n", | |
| global_wait); | |
| fflush(stderr); | |
| control_c_pressed = 1+x; | |
| } else | |
| control_c_pressed_again = 1; | |
| } | |
| /*************************************************************************** | |
| * Called from main() to initiate the scan. | |
| * Launches the 'transmit_thread()' and 'receive_thread()' and waits for | |
| * them to exit. | |
| ***************************************************************************/ | |
| static int | |
| main_scan(struct Masscan *masscan) | |
| { | |
| struct ThreadPair parms_array[8]; | |
| uint64_t count_ips; | |
| uint64_t count_ports; | |
| uint64_t range; | |
| unsigned index; | |
| unsigned *picker; | |
| time_t now = time(0); | |
| struct Status status; | |
| uint64_t min_index = UINT64_MAX; | |
| memset(parms_array, 0, sizeof(parms_array)); | |
| /* | |
| * Initialize the task size | |
| */ | |
| count_ips = rangelist_count(&masscan->targets); | |
| if (count_ips == 0) { | |
| LOG(0, "FAIL: target IP address list empty\n"); | |
| LOG(0, " [hint] try something like \"--range 10.0.0.0/8\"\n"); | |
| LOG(0, " [hint] try something like \"--range 192.168.0.100-192.168.0.200\"\n"); | |
| return 1; | |
| } | |
| count_ports = rangelist_count(&masscan->ports); | |
| if (count_ports == 0) { | |
| LOG(0, "FAIL: no ports were specified\n"); | |
| LOG(0, " [hint] try something like \"-p80,8000-9000\"\n"); | |
| LOG(0, " [hint] try something like \"--ports 0-65535\"\n"); | |
| return 1; | |
| } | |
| range = count_ips * count_ports + (uint64_t)(masscan->retries * masscan->max_rate); | |
| /* | |
| * If doing an ARP scan, then don't allow port scanning | |
| */ | |
| if (rangelist_is_contains(&masscan->ports, Templ_ARP)) { | |
| if (masscan->ports.count != 1) { | |
| LOG(0, "FAIL: cannot arpscan and portscan at the same time\n"); | |
| return 1; | |
| } | |
| } | |
| /* | |
| * If the IP address range is very big, then require that that the | |
| * user apply an exclude range | |
| */ | |
| if (count_ips > 1000000000ULL && rangelist_count(&masscan->exclude_ip) == 0) { | |
| LOG(0, "FAIL: range too big, need confirmation\n"); | |
| LOG(0, " [hint] to prevent acccidents, at least one --exclude must be specified\n"); | |
| LOG(0, " [hint] use \"--exclude 255.255.255.255\" as a simple confirmation\n"); | |
| exit(1); | |
| } | |
| /* | |
| * trim the nmap UDP payloads down to only those ports we are using. This | |
| * makes lookups faster at high packet rates. | |
| */ | |
| payloads_trim(masscan->payloads, &masscan->ports); | |
| /* Optimize target selection so it's a quick binary search instead | |
| * of walking large memory tables. When we scan the entire Internet | |
| * our --excludefile will chop up our pristine 0.0.0.0/0 range into | |
| * hundreds of subranges. This scans through them faster. */ | |
| picker = rangelist_pick2_create(&masscan->targets); | |
| /* | |
| * Start scanning threats for each adapter | |
| */ | |
| for (index=0; index<masscan->nic_count; index++) { | |
| struct ThreadPair *parms = &parms_array[index]; | |
| int err; | |
| parms->masscan = masscan; | |
| parms->nic_index = index; | |
| parms->picker = picker; | |
| parms->my_index = masscan->resume.index; | |
| parms->done_transmitting = 0; | |
| parms->done_receiving = 0; | |
| /* needed for --packet-trace option so that we know when we started | |
| * the scan */ | |
| parms->pt_start = 1.0 * pixie_gettime() / 1000000.0; | |
| /* | |
| * Turn the adapter on, and get the running configuration | |
| */ | |
| err = masscan_initialize_adapter( | |
| masscan, | |
| index, | |
| parms->adapter_mac, | |
| parms->router_mac | |
| ); | |
| if (err != 0) | |
| exit(1); | |
| parms->adapter = masscan->nic[index].adapter; | |
| if (masscan->nic[index].src.ip.range == 0) { | |
| LOG(0, "FAIL: failed to detect IP of interface\n"); | |
| LOG(0, " [hint] did you spell the name correctly?\n"); | |
| LOG(0, " [hint] if it has no IP address, manually set with \"--adapter-ip 192.168.100.5\"\n"); | |
| exit(1); | |
| } | |
| /* | |
| * Initialize the TCP packet template. The way this works is that we parse | |
| * an existing TCP packet, and use that as the template for scanning. Then, | |
| * we adjust the template with additional features, such as the IP address | |
| * and so on. | |
| */ | |
| template_packet_init( | |
| parms->tmplset, | |
| parms->adapter_mac, | |
| parms->router_mac, | |
| masscan->payloads); | |
| /* | |
| * Set the "source port" of everything we transmit. | |
| */ | |
| if (masscan->nic[index].src.port.range == 0) { | |
| unsigned port = 40000 + now % 20000; | |
| masscan->nic[index].src.port.first = port; | |
| masscan->nic[index].src.port.last = port; | |
| masscan->nic[index].src.port.range = 1; | |
| } | |
| parms->src = masscan->nic[index].src; | |
| #if 0 | |
| if (masscan->nic[index].adapter_port == 0x10000) | |
| masscan->nic[index].adapter_port = 40000 + now % 20000; | |
| template_set_source_port( parms->tmplset, | |
| masscan->nic[index].adapter_port); | |
| /* | |
| * Read back what we've set | |
| */ | |
| parms->adapter_port = template_get_source_port(parms->tmplset); | |
| #endif | |
| /* | |
| * Set the "TTL" (IP time-to-live) of everything we send. | |
| */ | |
| if (masscan->nmap.ttl) | |
| template_set_ttl(parms->tmplset, masscan->nmap.ttl); | |
| /* | |
| * trap <ctrl-c> to pause | |
| */ | |
| signal(SIGINT, control_c_handler); | |
| /* | |
| * Allocate packet buffers for sending | |
| */ | |
| #define BUFFER_COUNT 16384 | |
| parms->packet_buffers = rte_ring_create(BUFFER_COUNT, RING_F_SP_ENQ|RING_F_SC_DEQ); | |
| parms->transmit_queue = rte_ring_create(BUFFER_COUNT, RING_F_SP_ENQ|RING_F_SC_DEQ); | |
| { | |
| unsigned i; | |
| for (i=0; i<BUFFER_COUNT-1; i++) { | |
| struct PacketBuffer *p; | |
| p = (struct PacketBuffer *)malloc(sizeof(*p)); | |
| if (p == NULL) | |
| exit(1); | |
| err = rte_ring_sp_enqueue(parms->packet_buffers, p); | |
| if (err) { | |
| /* I dunno why but I can't queue all 256 packets, just 255 */ | |
| LOG(0, "packet_buffers: enqueue: error %d\n", err); | |
| } | |
| } | |
| } | |
| /* | |
| * Start the scanning thread. | |
| * THIS IS WHERE THE PROGRAM STARTS SPEWING OUT PACKETS AT A HIGH | |
| * RATE OF SPEED. | |
| */ | |
| pixie_begin_thread(transmit_thread, 0, parms); | |
| /* | |
| * Start the MATCHING receive thread. Transmit and receive threads | |
| * come in matching pairs. | |
| */ | |
| pixie_begin_thread(receive_thread, 0, parms); | |
| } | |
| /* | |
| * Print helpful text | |
| */ | |
| { | |
| char buffer[80]; | |
| struct tm x; | |
| now = time(0); | |
| gmtime_s(&x, &now); | |
| strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S GMT", &x); | |
| LOG(0, "\nStarting masscan 1.0 (http://bit.ly/14GZzcT) at %s\n", buffer); | |
| LOG(0, " -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth\n"); | |
| LOG(0, "Initiating SYN Stealth Scan\n"); | |
| LOG(0, "Scanning %u hosts [%u port%s/host]\n", | |
| (unsigned)count_ips, (unsigned)count_ports, (count_ports==1)?"":"s"); | |
| } | |
| /* | |
| * Now wait for <ctrl-c> to be pressed OR for threads to exit | |
| */ | |
| status_start(&status); | |
| status.is_infinite = masscan->is_infinite; | |
| while (!control_c_pressed) { | |
| unsigned i; | |
| double rate = 0; | |
| uint64_t total_tcbs = 0; | |
| uint64_t total_synacks = 0; | |
| uint64_t total_syns = 0; | |
| /* Find the minimum index of all the threads */ | |
| min_index = UINT64_MAX; | |
| for (i=0; i<masscan->nic_count; i++) { | |
| struct ThreadPair *parms = &parms_array[i]; | |
| if (min_index > parms->my_index) | |
| min_index = parms->my_index; | |
| rate += parms->throttler->current_rate; | |
| if (parms->total_tcbs) | |
| total_tcbs += *parms->total_tcbs; | |
| if (parms->total_synacks) | |
| total_synacks += *parms->total_synacks; | |
| if (parms->total_syns) | |
| total_syns += *parms->total_syns; | |
| } | |
| if (min_index >= range && !masscan->is_infinite) { | |
| control_c_pressed = 1; | |
| } | |
| /* | |
| * update screen about once per second with statistics, | |
| * namely packets/second. | |
| */ | |
| status_print(&status, min_index, range, rate, | |
| total_tcbs, total_synacks, total_syns); | |
| /* Sleep for almost a second */ | |
| pixie_mssleep(750); | |
| } | |
| /* | |
| * If we haven't completed the scan, then save the resume | |
| * information. | |
| */ | |
| if (min_index < count_ips * count_ports) { | |
| masscan->resume.index = min_index; | |
| masscan_save_state(masscan); | |
| } | |
| /* | |
| * Now wait for all threads to exit | |
| */ | |
| now = time(0); | |
| for (;;) { | |
| unsigned transmit_count = 0; | |
| unsigned receive_count = 0; | |
| unsigned i; | |
| pixie_mssleep(750); | |
| status_print(&status, masscan->resume.index, range, 0, 0, 0, 0); | |
| if (time(0) - now >= masscan->wait) | |
| control_c_pressed_again = 1; | |
| for (i=0; i<masscan->nic_count; i++) { | |
| struct ThreadPair *parms = &parms_array[i]; | |
| transmit_count += parms->done_transmitting; | |
| receive_count += parms->done_receiving; | |
| } | |
| if (transmit_count < masscan->nic_count) | |
| continue; | |
| control_c_pressed = 1; | |
| control_c_pressed_again = 1; | |
| if (receive_count < masscan->nic_count) | |
| continue; | |
| break; | |
| } | |
| /* | |
| * Now cleanup everything | |
| */ | |
| status_finish(&status); | |
| rangelist_pick2_destroy(picker); | |
| return 0; | |
| } | |
| /*************************************************************************** | |
| ***************************************************************************/ | |
| int main(int argc, char *argv[]) | |
| { | |
| struct Masscan masscan[1]; | |
| unsigned i; | |
| #if defined(WIN32) | |
| {WSADATA x; WSAStartup(0x101, &x);} | |
| #endif | |
| global_now = time(0); | |
| /* | |
| * Initialize those defaults that aren't zero | |
| */ | |
| memset(masscan, 0, sizeof(*masscan)); | |
| masscan->is_interactive = 1; | |
| masscan->seed = time(0); /* a predictable, but 'random' seed */ | |
| masscan->wait = 10; /* how long to wait for responses when done */ | |
| masscan->max_rate = 100.0; /* max rate = hundred packets-per-second */ | |
| masscan->nic_count = 1; | |
| masscan->shard.one = 1; | |
| masscan->shard.of = 1; | |
| masscan->payloads = payloads_create(); | |
| strcpy_s( masscan->rotate_directory, | |
| sizeof(masscan->rotate_directory), | |
| "."); | |
| /* | |
| * On non-Windows systems, read the defaults from the file in | |
| * the /etc directory. These defaults will contain things | |
| * like the output directory, max packet rates, and so on. Most | |
| * importanlty, the master "--excludefile" might be placed here, | |
| * so that blacklisted ranges won't be scanned, even if the user | |
| * makes a mistake | |
| */ | |
| #if !defined(WIN32) | |
| if (access("/etc/masscan/masscan.conf", 0) == 0) { | |
| masscan_read_config_file(masscan, "/etc/masscan/masscan.conf"); | |
| } | |
| #endif | |
| /* | |
| * Read in the configuration from the command-line. We are looking for | |
| * either options or a list of IPv4 address ranges. | |
| */ | |
| masscan_command_line(masscan, argc, argv); | |
| /* We need to do a separate "raw socket" initialization step. This is | |
| * for Windows and PF_RING. */ | |
| rawsock_init(); | |
| /* Init some protocol parser data structures */ | |
| snmp_init(); | |
| /* Set randomization seed for SYN-cookies */ | |
| syn_set_entropy(masscan->seed); | |
| /* | |
| * Apply excludes. People ask us not to scan them, so we maintain a list | |
| * of their ranges, and when doing wide scans, add the exclude list to | |
| * prevent them from being scanned. | |
| */ | |
| { | |
| uint64_t range = rangelist_count(&masscan->targets) * rangelist_count(&masscan->ports); | |
| uint64_t range2; | |
| rangelist_exclude(&masscan->targets, &masscan->exclude_ip); | |
| rangelist_exclude(&masscan->ports, &masscan->exclude_port); | |
| //rangelist_remove_range2(&masscan->targets, range_parse_ipv4("224.0.0.0/4", 0, 0)); | |
| range2 = rangelist_count(&masscan->targets) * rangelist_count(&masscan->ports); | |
| if (range != 0 && range2 == 0) { | |
| LOG(0, "FAIL: no ranges left to scan\n"); | |
| LOG(0, " ...all ranges overlapped something in an excludefile range\n"); | |
| exit(1); | |
| } | |
| if (range2 != range && masscan->resume.index) { | |
| LOG(0, "FAIL: Attempted to add additional 'exclude' ranges after scan start.\n"); | |
| LOG(0, " ...This messes things up the scan randomization, so you have to restart scan\n"); | |
| exit(1); | |
| } | |
| } | |
| /* | |
| * Once we've read in the configuration, do the operation that was | |
| * specified | |
| */ | |
| switch (masscan->op) { | |
| case Operation_Default: | |
| /* Print usage info and exit */ | |
| masscan_usage(); | |
| break; | |
| case Operation_Scan: | |
| /* | |
| * THIS IS THE NORMAL THING | |
| */ | |
| return main_scan(masscan); | |
| case Operation_ListScan: | |
| /* Create a randomized list of IP addresses */ | |
| main_listscan(masscan); | |
| return 0; | |
| case Operation_List_Adapters: | |
| /* List the network adapters we might want to use for scanning */ | |
| rawsock_list_adapters(); | |
| break; | |
| case Operation_DebugIF: | |
| for (i=0; i<masscan->nic_count; i++) | |
| rawsock_selftest_if(masscan->nic[i].ifname); | |
| return 0; | |
| case Operation_ReadScan: | |
| { | |
| unsigned start; | |
| unsigned stop; | |
| /* find first file */ | |
| for (start=1; start<(unsigned)argc; start++) { | |
| if (memcmp(argv[start], "--readscan", 10) == 0) { | |
| start++; | |
| break; | |
| } | |
| } | |
| /* find last file */ | |
| for (stop=start+1; stop<(unsigned)argc && argv[stop][0] != '-'; stop++) | |
| ; | |
| convert_binary_files(masscan, start, stop, argv); | |
| } | |
| break; | |
| case Operation_Selftest: | |
| /* | |
| * Do a regression test of all the significant units | |
| */ | |
| { | |
| int x = 0; | |
| x += banner1_selftest(); | |
| x += output_selftest(); | |
| x += siphash24_selftest(); | |
| x += snmp_selftest(); | |
| x += payloads_selftest(); | |
| x += blackrock_selftest(); | |
| x += rawsock_selftest(); | |
| x += lcg_selftest(); | |
| x += template_selftest(); | |
| x += ranges_selftest(); | |
| x += pixie_time_selftest(); | |
| x += rte_ring_selftest(); | |
| x += smack_selftest(); | |
| x += mainconf_selftest(); | |
| x += zeroaccess_selftest(); | |
| if (x != 0) { | |
| /* one of the selftests failed, so return error */ | |
| fprintf(stderr, "regression test: failed :( \n"); | |
| return 1; | |
| } else { | |
| fprintf(stderr, "regression test: success!\n"); | |
| return 0; | |
| } | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |