diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 347 |
1 files changed, 214 insertions, 133 deletions
@@ -14,6 +14,9 @@ #include <stdlib.h> /* atoi(3) */ #include <string.h> /* strchr(3) */ #include <time.h> /* clock_gettime(2) */ +#include <signal.h> /* signal(2) */ +#include <sys/prctl.h> /* prctl(2) */ +#include <sys/wait.h> /* waitpid(2) */ #include "domain2name.c" #include "host.c" #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -24,18 +27,21 @@ #define XSTR(x) #x #define STR(x) XSTR(x) #define HELP "find recursive DNS resolvers on IPv4 networks\n" \ - "%s [-a ipv4] [-b ipv4] [-d domain] [-h] [-o filename] [-p port] network1 [network2 ...]\n" \ + "%s [-a ip] [-b ip] [-d domain] [-h] [-o file] [-p port] [-t μs] [-w μs] net1 [net2 ...]\n" \ " -a Specify the IPv4 of the -d domain to be used instead of getaddrinfo(3).\n" \ " -b Bind on a specific interface, defined by IPv4. Default is any interface.\n" \ " -d Specify the domain name to be used in queries that has a single A record.\n" \ " -h Show this help and exit.\n" \ " -o Output PCAP to filename. Any existing file is truncated. No IP/UDP checksums.\n" \ " -p Set the source port number to use instead of a dynamically asigned one.\n" \ + " -t Number of microseconds to wait between sent packets. (default 1000 - 64 KB/s)\n" \ + " -w Finish after μs after recv'd last packet when done sending. (default 1000000)\n" \ "Network addresses are optionally followed by slash and netmask, otherwise networks are\n" \ "understood as single host addresses. Both network names and netmasks can be domains to\n" \ "be looked up or IP dot-notation addresses. Mask can also be a bit prefix (/32 default).\n" \ "When scanning the Internet please make sure that you specify your own domain instead\n" \ - "of the default one (dnsfind.sijanec.eu).\n" + "of the default one (dnsfind.sijanec.eu).\n" \ + "It would take a day to scan the entire address space with the default timings.\n" /* DNS PACKET: HEADER QUESTION ANSWER AUTHORITY ADDITIONAL datatracker.ietf.org/doc/html/rfc1035 DEFINITIONS: (those appear somewhere in the packet, packet does not start with definitions!) LABLEN 8 bits: first two bits zero, then 6 bits length of label @@ -46,8 +52,8 @@ DEFINITIONS: (those appear somewhere in the packet, packet does not start with d ii.) a POINTER that points to some LABLEN somewhere else in the packet HEADER: 12 bytes XID 16 bits: random string to be matched in response to prevent cache poisoning - / QR 1 bit : what type is this packet? 0 query 1 response - | OPCODE 4 bits: type of query 0 std 1 invrs 2 srvst 3-15 reserved + / QR 1 bit : what type is this packet? 0 quest 1 response + | OPCODE 4 bits: type of query 0 query 1 iquer 2 sstat 3-15 reserved 1 byte AA 1 bit : is response authoritative? 0 no 1 yes | TC 1 bit : was response truncated? 0 no 1 yes \ RD 1 bit : does query desire recursion? 0 no 1 yes @@ -119,87 +125,53 @@ HEADER: #define NANOSECOND 0xA1B23C4D #define PCAPMAJ 2 #define PCAPMIN 4 -enum linktype { - Ethernet = 1, - Ip = 101 -}; +#define ETHERNET = 1, +#define IP 101 struct pcap_global { uint32_t subsecond __attribute__((packed)); uint16_t major __attribute__((packed)); uint16_t minor __attribute__((packed)); uint32_t reserved[2] __attribute__((packed)); uint32_t snaplen __attribute__((packed)); - unsigned int fcs : 4 __attribute__((packed)); - enum linktype linktype : 28 __attribute__((packed)); + uint32_t linktype __attribute__((packed)); /* FCS in included here for order */ } __attribute__((packed)); -#define microseconds subseconds -#define nanoseconds subseconds struct pcap_packet { uint32_t seconds __attribute__((packed)); uint32_t subseconds __attribute__((packed)); uint32_t capture_length __attribute__((packed)); uint32_t original_length __attribute__((packed)); } __attribute__((packed)); -enum precedence { - Routine, - Priority, - Immediate, - Flash, - Flash_override, - Critical, - Inetctrl, - Netctrl -}; -enum srvtype { - Low_delay = 1 << 0, - High_throughput = 1 << 1, - High_reliability = 1 << 2, - Reserved_0 = 1 << 3, - Reserved_1 = 1 << 4 -}; -enum ip_flags { - Evil = 1 << 0, - Df = 1 << 1, - Mf = 1 << 2 -}; -enum protocol { - Icmp = 1, - Tcp = 6, - Udp = 17 -}; +#define LOW_DELAY (1 << 0) +#define HIGH_THROUGHPUT (1 << 1) +#define HIGH_RELIABILITY (1 << 2) +#define ROUTINE (0 << 5) +#define PRIORITY (1 << 5) +#define IMMEDIATE (1 << 6) +#define FLASH (PRIORITY | IMMEDIATE) +#define FLASH_OVERRIDE (1 << 7) +#define CRITICAL (FLASH_OVERRIDE | PRIORITY) +#define INETCTRL (FLASH_OVERRIDE | IMMEDIATE) +#define NETCTRL (FLASH_OVERRIDE | FLASH) +#define HEADLENOR (1 << 6) /* always bitwiseOR the headlen with this to apply the version number */ +#define EVIL (1 << 13) +#define DF (1 << 14) +#define MF (1 << 15) +#define ICMP 1 +#define TCP 6 +#define UDP 17 struct ip { - unsigned int version : 4 __attribute__((packed)); - unsigned int headlen : 4 __attribute__((packed)); /* measured in 32 bit words */ - enum precedence precedence : 3 __attribute__((packed)); /* without optionts it's min 5 */ - enum srvtype srvtype : 5 __attribute__((packed)); + uint8_t headlen; /* length of header in 32 bit words (min 5, max 15 = 60B). see HEADLENOR. */ + uint8_t srvtype; /* OR here: LOW_DELAY, HIGH_THROUGHPUT, HIGH_RELIABILITY, ROUTINE, ... */ uint16_t length __attribute__((packed)); /* header + data in 8 bit words */ uint16_t identifier __attribute__((packed)); - enum ip_flags flags : 3 __attribute__((packed)); - unsigned int foffset : 13 __attribute__((packed)); /* fragment offset - 64 bit words */ - uint8_t ttl /* ignored for uint8_t */; - enum protocol protocol : 8 __attribute__((packed)); - uint16_t checksum __attribute__((packed)); + uint8_t foffset; /* fragment offset in 64 bit words. also or here any flags: EVIL, DF, MF */ + uint8_t ttl; /* ignored for uint8_t */ + uint8_t protocol; /* ignored for uint8_t */ + uint16_t checksum; /* ignoref for uint8_t */ struct in_addr src __attribute__((packed)); struct in_addr dst __attribute__((packed)); /* ----------- 20 bytes */ - char options[] /* ignored for char[] */; + char options[]; /* ignored for char[] */ } __attribute__((packed)); -enum qr { - Question, - Response -}; -enum opcode { - Query, - Iquery, - Stauts -}; -enum rcode { - Success, - Format_error, - Servfail, - Nxdomain, - Ni, - Forbidden -}; enum type { A = 1, Ns, @@ -224,90 +196,91 @@ enum class { He, Any = 255 }; -enum dns_flags { - Aa = 1 << 0, - Tc = 1 << 1, - Rd = 1 << 2, - Ra = 1 << 3, - Z0 = 1 << 4, - Z1 = 1 << 5, - Z2 = 1 << 6 -}; +#define RESPONSE (1 << 0) /* :FLAGS */ +#define QUESTION (0 << 0) +#define QUERY (1 << 1) /* must be set, even with response, this shows that it's normal query type */ +#define IQUERY (1 << 2) +#define STATUS (QUERY | IQUERY) +#define AA (1 << 5) +#define TC (1 << 6) +#define RD (1 << 7) +#define RA (1 << 8) +#define SUCCESS (1 << 12) +#define FORMAT_ERROR (1 << 13) +#define SERVFAIL (SUCCESS | FORMAT_ERROR) +#define NXDOMAIN (1 << 14) +#define NI (NXDOMAIN | SUCCESS) +#define FORBIDDEN (NXDOMAIN | FORMAT_ERROR) struct header { uint16_t xid __attribute__((packed)); - enum qr qr : 1 __attribute__((packed)); - enum opcode opcode : 4 __attribute__((packed)); - enum dns_flags flags : 7 __attribute__((packed)); - enum rcode rcode : 4 __attribute__((packed)); + uint16_t flags __attribute__((packed)); uint16_t qdcount __attribute__((packed)); uint16_t ancount __attribute__((packed)); uint16_t nscount __attribute__((packed)); uint16_t arcount __attribute__((packed)); - char data[] /* ignored for char[] */; + char data[]; /* ignored for char[] */ } __attribute__((packed)); -struct question { - char * qname __attribute__((packed)); - enum type qtype : 16 __attribute__((packed)); - enum class qclass : 16 __attribute__((packed)); -} __attribute__((packed)); -struct answer { - char * name __attribute__((packed)); - enum class class : 16 __attribute__((packed)); - uint16_t rdlen __attribute__((packed)); - char * rdata __attribute__((packed)); -}; -struct udp { - struct sockaddr_in src; - struct sockaddr_in dst; - size_t len; /* of data only */ - char * data; -}; -int logudp (int o /* file descriptor */, struct udp u) { +int logudp (int o /* fd */, struct sockaddr_in s, struct sockaddr_in d, char * u, size_t l /* d */) { + if (o == -1) + return -1; struct timespec t; if (clock_gettime(CLOCK_REALTIME, &t) == -1) { perror("clock_gettime(CLOCK_REALTIME, &t)"); - return -1; + return -2; } struct pcap_packet p = { .seconds = t.tv_sec, .subseconds = t.tv_nsec, - .capture_length = u.len, - .original_length = u.len + .capture_length = l, + .original_length = l }; struct ip i = { - .version = 4, - .headlen = 5, - .precedence = Routine, - .srvtype = 0, - .length = htons(8+u.len), + .headlen = 5 | HEADLENOR, + .srvtype = ROUTINE, + .length = htons(8+l), .identifier = 0x6969, - .flags = 0, .foffset = 0, .ttl = 69, - .protocol = Udp, + .protocol = UDP, .checksum = 0, /* wireshark does not validate, at least not on debian 11 */ - .src = u.src.sin_addr, - .dst = u.dst.sin_addr + .src = s.sin_addr, + .dst = d.sin_addr }; -#define LOGUDP_L (sizeof p + sizeof i + 4*2 + u.len) +#define LOGUDP_L (sizeof p + sizeof i + 4*2 + l) char * c, * b = alloca(LOGUDP_L); /* to do in one write (thread safe) */ char * n = "\0"; /* as per udp rfc when no checksum was calculated (-: */ - uint16_t l = htons(u.len); + uint16_t v = htons(l); c = (char *) memcpy(b, &p, sizeof p) + sizeof p; c = (char *) memcpy(c, &i, sizeof i) + sizeof i; - c = (char *) memcpy(c, &u.src.sin_port, 2) + 2; - c = (char *) memcpy(c, &u.dst.sin_port, 2) + 2; - c = (char *) memcpy(c, &l, 2) + 2; + c = (char *) memcpy(c, &s.sin_port, 2) + 2; + c = (char *) memcpy(c, &d.sin_port, 2) + 2; + c = (char *) memcpy(c, &v, 2) + 2; c = (char *) memcpy(c, &n, 2) + 2; - c = (char *) memcpy(c, u.data, u.len) + u.len; + c = (char *) memcpy(c, u, l) + l; if (write(o, b, LOGUDP_L) == -1) { /* atomic and thread safe, as per posix */ perror("write(" STR(LOGUDP_L) ")"); - return -2; + return -3; } - return 0; + return LOGUDP_L; +} +int finish = 0; +int c = -1; /* child process */ +void handler () { + if (++finish >= 3) { + if (c != -1) + kill(c, SIGHUP); + exit(1); + } +} +struct timespec lp = { /* last packet */ + .tv_sec = 0 +}; +void child_handler () { + if (clock_gettime(CLOCK_MONOTONIC, &lp) == -1) + perror("clock_gettime(CLOCK_MONOTONIC, &lp)"); } int main (int argc, char ** argv) { - int r = 0; + int r = 2; struct in_addr a = { .s_addr = 0 }; @@ -321,12 +294,17 @@ int main (int argc, char ** argv) { char * d = "dnsfind.sijanec.eu"; int s = -1; /* socket */ int o = -1; /* output file */ - char buf[512]; /* max dns packet */ - int c; /* child process */ struct in_net * n; /* networks */ int l; /* count of networks */ + int i = 0; /* network index */ + int j = -1; /* host in network index */ + int t = 1000; + int w = 1000000; + struct in_addr h; /* host to scan */ + signal(SIGINT, handler); + signal(SIGTERM, handler); while (1) { - switch (getopt(argc, argv, ":a:b:d:h")) { + switch (getopt(argc, argv, ":a:b:d:ho:p:t:w:")) { case 'a': inet_aton(optarg, &a); break; @@ -341,9 +319,9 @@ int main (int argc, char ** argv) { r = 0; goto r; case 'o': - if ((o = open(optarg, O_CREAT | O_TRUNC | O_WRONLY)) == -1) { + if ((o = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 00664)) == -1) { perror("open(optarg, O_CREAT | O_TRUNC | O_WRONLY)"); - r = 1; + r = 3; goto r; } struct pcap_global g = { @@ -353,25 +331,30 @@ int main (int argc, char ** argv) { .reserved[0] = 0, .reserved[1] = 0, .snaplen = 65535, - .fcs = 0, - .linktype = Ip + .linktype = IP }; if (write(o, &g, sizeof g) == -1) { perror("write(o, &g, sizeof g)"); - r = 2; + r = 4; goto r; } break; case 'p': b.sin_port = htons(atoi(optarg)); break; + case 't': + t = atoi(optarg); + break; + case 'w': + w = atoi(optarg); + break; case -1: if (!(l = argc-optind)) { fprintf(stderr, "specify targets to scan :: " HELP, argv[0]); - r = 3; + r = 5; goto r; } - n = alloca(l*sizeof(*n)); + n = alloca(l*sizeof *n); for (int i = optind; i < argc; i++) { int w = i-optind; n[w] = str2net(argv[i]); @@ -385,6 +368,9 @@ int main (int argc, char ** argv) { fprintf(stderr, "missing option argument :: " HELP, argv[0]); r = 7; goto r; + default: + r = 8; + goto r; } } o: @@ -393,27 +379,122 @@ o: fprintf(stderr, "resolving %s ... ", d); if ((e = resolve(d, &a.s_addr))) { fprintf(stderr, "failed: %s\n", gai_strerror(e)); - r = 8; + r = 9; goto r; } fprintf(stderr, " %s\n", inet_ntoa(a)); } if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { perror("socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)"); - r = 8; + r = 10; goto r; } if (bind(s, (struct sockaddr *) &b, sizeof(struct sockaddr))) { perror("bind(s, (struct sokaddr *) &b, sizeof(struct sockaddr))"); - r = 10; + r = 11; goto r; } if ((c = fork()) == -1) { perror("fork()"); - r = 11; + r = 12; goto r; } + if (!c) { + signal(SIGINT, child_handler); + signal(SIGTERM, child_handler); + while (1) { + char b[512]; /* max dns packet */ + struct sockaddr_in f; + socklen_t č = sizeof f; + if (recvfrom(s,b,512,!lp.tv_sec?MSG_DONTWAIT:0,(struct sockaddr*)&f,&č)==-1){ + if (errno != EWOULDBLOCK) { + perror("recvfrom(s,b,512,!lp.tv_sec?MSG_DONTWAIT:0,(str..."); + return 1; + } + struct timespec z; + if (clock_gettime(CLOCK_MONOTONIC, &z) == -1) { + perror("clock_gettime(CLOCK_MONOTONIC, &z)"); + return 2; + } + if ((z.tv_sec*1000000 + z.tv_nsec/1000) + - (lp.tv_sec*1000000 + lp.tv_nsec/1000) > w) { + fprintf(stderr, "no more packets are received. timeout.\n"); + return 0; + } + continue; + } + if (lp.tv_sec) + if (clock_gettime(CLOCK_MONOTONIC, &lp) == -1) { + perror("clock_gettime(CLOCK_MONOTONIC, &lp)"); + return 2; + } + fprintf(stderr, "received response from %s\n", inet_ntoa(f.sin_addr)); + } + return 0; + } + while (!finish) { + if (!(t = host(n[i], ++j).s_addr)) { + if (++i >= l) { + int s; + fprintf(stderr, "finished sending, waiting for last replies\n"); + kill(c, SIGINT); /* child waits */ + if (wait(&s) == -1) { + perror("wait(&s)"); + r = 13; + goto r; + } + if (WIFEXITED(s) && !WEXITSTATUS(s)) { + r = 0; + goto r; + } + r = 14; + goto r; + } + else + h = host(n[i], (j = 0)); + } + struct sockaddr_in e = { + .sin_family = AF_INET, + .sin_port = htons(53), + .sin_addr = h + }; + struct header h = { + .xid = 0x6969, /* oh no, cache poisoning, whatever'll I do */ + .flags = htons(QUESTION | QUERY | RD), + .qdcount = htons(1), + .ancount = 0, + .nscount = 0, + .arcount = 0 + }; + int v = domain2name_len(d, strlen(d)); +#define L (sizeof h + v + 2*2) + char * u = alloca(L); + char * c; + uint16_t y = htons(A); + uint16_t k = htons(In); + c = (char *) memcpy(u, &h, sizeof h) + sizeof h; + c += domain2name(c, d, strlen(d)); + c = (char *) memcpy(c, &y, 2) + 2; + c = (char *) memcpy(c, &k, 2) + 2; + logudp(o, b, e, u, L); + sendto(s, u, L, 0, (struct sockaddr *) &e, sizeof(struct sockaddr)); + usleep(t); + } + if (finish) + kill(c, SIGTERM); /* stop receiving */ r: + if (!r && j != -1) { + char * x = alloca(l*31+strlen("SCANNED \n0")); + strcpy(x, "SCANNED "); + for (int m = 0; m < finish ? i : l; m++) { + strcat(x, inet_ntoa(n[m].addr)); + strcat(x, "/"); + strcat(x, inet_ntoa(n[m].mask)); + strcat(x, " "); + } + strcat(x, "\n"); + write(STDIN_FILENO, x, strlen(x)); /* vsi tele strlen in strcat niso najboljša pot */ + } if (s != -1) if (close(s)) perror("close(s)"); |