summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main.c347
1 files changed, 214 insertions, 133 deletions
diff --git a/main.c b/main.c
index 02ca7dd..fd77759 100644
--- a/main.c
+++ b/main.c
@@ -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)");