summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--domain2name.c64
-rw-r--r--main.c302
2 files changed, 199 insertions, 167 deletions
diff --git a/domain2name.c b/domain2name.c
index 7c84fbb..4f305fb 100644
--- a/domain2name.c
+++ b/domain2name.c
@@ -2,16 +2,18 @@ int domain2name_len (const char * s, int l) { /* TODO make domain2name FAIL at e
int r = 1; /* ending terminator */ /* make functions FAIL at label.length > 63 */
int o = 0; /* label offset */ /* currently domain2name never fails */
for (int i = 0; i < l; i++) { /* NOTE when using BOTH _len, check that they are */
- if (s[i] == '.') { /* NOT negative. d2n_len will fail in d future */
+ if (s[i] == '.') { /* NOT negative. d2n_len may fail in d future */
if (!o) /* double period or starting period, label is empty */
- break;
+ break; /* we could return -1 here if r == 1 */
o = 0;
continue;
}
if (!o) /* label has started */
r++;
- r++;
- o++;
+ if (o < 63) { /* we cap label length at 64 bytes. we could return -2 here. */
+ r++;
+ o++;
+ }
}
return r;
}
@@ -38,42 +40,56 @@ int domain2name (char * n /* at least _len bytes */, const char * s, int l) { /*
*w++ = '\0'; /* luckily this makes domain2name kind of safe for handling as a string (: */
return w-n; /* we return number of bytes written */
} /* no compression, it's 2022, net bandwidth is unlimited. n2d OFC does decompress ptrs acc2 std. */
-int name2domain_len (const char * u /* >= 512 bytes */, const char * n /* name */) {
-#define N2DO(x) ((x) & ~(1 << 7 & 1 << 6)) /* pointer offset */
+int name2domain_len (const char * u, int s /* size of u */, const char * n /* name */) {
+#define N2DO(x) (ntohs(x) & ~(1 << 15 | 1 << 14)) /* pointer offset */
int r = 0;
- if (n < u+512 && *n == '\0') {
+ char * f = alloca(s/8+1);
+ memset(f, '\0', s/8+1);
+ if (n < u+s && *n == '\0') {
return 2;
}
- while (n < u+512) {
+ while (n < u+s) {
if (*n & 1 << 7) {
if (!(*n & 1 << 6))
return -1; /* 10xx xxxx not implemented - reserved for future use */
- n = u + N2DO(*n);
+ if (n+1 >= u+s)
+ return -2; /* malformed packet */
+ if (f[(n-u)/8] & 1 << (n-u)%8)
+ return -3; /* infinite pointer loop detected */
+ f[(n-u)/8] |= 1 << (n-u)%8;
+ n = u + N2DO(*(uint16_t *) n);
continue;
}
if (*n & 1 << 6)
- return -2; /* 01xx xxxx not implemented - reserved for future use */
+ return -4; /* 01xx xxxx not implemented - reserved for future use */
if (!*n)
return r+1;
r += *n+1;
n += *n+1;
}
- return -3; /* malformed packet */
+ return -5; /* malformed packet */
} /* returns number of bytes needed for buffer, passed as the first argument of name2domain(). */
-const char * name2domain (char * d /* >= _len B */, const char * u /* >= 512 B */, const char * n) {
+const char * name2domain (char * d /* >= _len B */, const char * u, int s, const char * n) {
char * w = d; /* if d is NULL nothing is written and last byte of name is returned */
const char * r = NULL;
- if (n < u+512 && *n == '\0') {
+ char * f = alloca(s/8+1);
+ memset(f, '\0', s/8+1);
+ if (n < u+s && *n == '\0') {
*w++ = '.';
*w++ = '\0';
return n;
}
- while (n < u+512) {
+ while (n < u+s) {
if (*n & 1 << 7) {
if (!(*n & 1 << 6))
return NULL; /* 10xx xxxx N/I - reserved for future use as per RFC */
- n = u + N2DO(*n);
- r = n;
+ if (n+1 >= u+s)
+ return NULL;
+ r = n+1;
+ if (f[(n-u)/8] & 1 << (n-u)%8)
+ return NULL; /* infinite pointer loop detected */
+ f[(n-u)/8] |= 1 << (n-u)%8;
+ n = u + N2DO(*(uint16_t *) n);
continue;
}
if (*n & 1 << 6)
@@ -85,7 +101,7 @@ const char * name2domain (char * d /* >= _len B */, const char * u /* >= 512 B *
}
const char * x = n+*n;
n++;
- if (!(x < u+512))
+ if (!(x < u+s))
return NULL; /* malformed packet */
while (n <= x)
if (w)
@@ -100,20 +116,20 @@ const char * name2domain (char * d /* >= _len B */, const char * u /* >= 512 B *
int normalizedomain_len (const char * s, int l) {
int ž = domain2name_len(s, l);
if (ž < 0)
- return -4;
+ return -6;
char * b = alloca(ž);
if (domain2name(b, s, l) != ž)
- return -5;
- return name2domain_len(b, b);
+ return -7;
+ return name2domain_len(b, ž, b);
}
int normalizedomain (char * d /* at least _len bytes */, const char * s, int l) {
int ž = domain2name_len(s, l);
if (ž < 0)
- return -4;
+ return -6;
char * b = alloca(ž);
if (domain2name(b, s, l) != ž)
- return -5;
- if (!name2domain(d, b, b))
- return -6;
+ return -7;
+ if (!name2domain(d, b, ž, b))
+ return -8;
return 0;
}
diff --git a/main.c b/main.c
index 58fe58e..41b18dc 100644
--- a/main.c
+++ b/main.c
@@ -9,14 +9,15 @@
#include <sys/stat.h> /* open(2) */
#include <fcntl.h>
#include <errno.h> /* errno(3) */
-#include <arpa/inet.h> /* byteorder(3) */
+#include <arpa/inet.h> /* htons(3) */
#include <netdb.h> /* getaddrinfo(3) */
#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 <sys/prctl.h> */ /* prctl(2) */
+/* #include <sys/wait.h> */ /* waitpid(2) */
+#include <poll.h> /* poll(2) */
#include "domain2name.c"
#include "host.c"
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -45,7 +46,7 @@
/* 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
- POINTER 8 bits: first two bits one, then 6 bits as offset from first byte of packet
+ POINTER 16 bits: first two bits one, then 14 bits as offset from first byte of packet
STRING a single byte for defining length (0-256 - all eight bits) and then string of chars
DOMAIN i.) one or more LABLEN followed by ASCII label (no dots) end with either LABLEN 0
or a POINTER that points to some LABLEN somewhere else in the packet -OR-:
@@ -72,6 +73,7 @@ ANSWER:
NAME DOMAIN
TYPE same as QTYPE
CLASS same description as QCLASS, except class 255 ANY is not allowed here
+ TTL 32 bits: unsigned intager of seconds. for this period the record is valid.
RDLEN 16 bits: length of RDATA field - this is a number 0-65536, no two zero bits
RDATA A: 4 bytes IP address NS: DOMAIN CNAME: DOMAIN
SOA: NAME-ns1 NAME-email 32b-serial 32b-refresh 32b-retry 32b-expire 32b-nxdomainttl
@@ -82,12 +84,12 @@ ANSWER:
GLOBAL HEADER: 24 bytes
MAGIC 32 bits: 0xA1B2C3D4 timestamp is s and micros 0xA1B23C4D timestamp is s and nanos
MAJOR 16 bits: version 2 https://tools.ietf.org/id/draft-gharris-opsawg-pcap-00.html
- MINOR 16 bits: version 4 _/ /-------------------------------------------------\
- RESERV1 32 bits: unused and set to 0 < http://en.wikipedia.org/wiki/Frame_check_sequence|
- RESERV2 32 bits: unused and set to 0 \--------------------------------------------\ ^ |
- SNAPLEN 32 bits: larger or equal to size of largest capture of a single packet |/ \|
- FCS 4 bits: if last bit is 1, first 3 tell nr of bytes of FCS appended to every packet/
- LINKTYP 28 bits: pkt type //tcpdump.org/linktypes.html 101 IPv4/v6 1 ether | |
+ MINOR 16 bits: version 4 /-----------------------------------------------\
+ RESERV1 32 bits: unused and set to 0 | //en.wikipedia.org/wiki/Frame_check_sequence |
+ RESERV2 32 bits: unused and set to 0 \--------------------------------------------\ |
+ SNAPLEN 32 bits: larger or equal to size of largest capture of a single packet |
+ FCS 4 bits: if last bit is 1, first 3 tell nr of bytes of FCS appended to every packet
+ LINKTYP 28 bits: pkt type //tcpdump.org/linktypes.html 101 IPv4/v6 1 ether
PACKET HEADER: 16 bytes
SECONDS 32 bits: UNIX timestamp
SUBSECS 32 bits: nanoseconds elapsed since the second, can also be microseconds - see MAGIC
@@ -220,6 +222,17 @@ struct header {
uint16_t arcount __attribute__((packed));
char data[]; /* ignored for char[] */
} __attribute__((packed));
+struct rr { /* name is omitted, first byte of struct is first byte of type */
+ uint16_t type __attribute__((packed));
+ uint16_t class __attribute__((packed));
+ uint32_t ttl __attribute__((packed));
+ uint16_t len __attribute__((packed));
+ char data[]; /* ignored for char[] */
+} __attribute__((packed));
+struct question {
+ uint16_t type __attribute__((packed));
+ uint16_t class __attribute__((packed));
+} __attribute__((packed));
int logudp (int o /* fd */, struct sockaddr_in s, struct sockaddr_in d, char * u, size_t l /* d */) {
if (o == -1)
return -1;
@@ -263,7 +276,7 @@ int logudp (int o /* fd */, struct sockaddr_in s, struct sockaddr_in d, char * u
}
return LOGUDP_L;
}
-struct in_addr parse_a (const char * u /* >=512B */, const char * d, int l, int n) {
+struct in_addr parse_a (const char * u, int s /* buffer size of u */, const char * d, int l, int n) {
struct in_addr r = { /* this uses heap and does not fix heap if realloc failed. */
.s_addr = 0 /* returns 0.0.0.0 in case of error or if no more results for n. */
};
@@ -273,59 +286,59 @@ struct in_addr parse_a (const char * u /* >=512B */, const char * d, int l, int
char * ž = alloca(y);
if (normalizedomain(ž, d, l) < 0)
return r;
- int š = ntohs(((const struct header *) u)->ancount);
+ const struct header * h = (const struct header *) u;
+ int q = ntohs(h->qdcount);
+ int a = ntohs(h->ancount+h->nscount+h->arcount);
char * o = NULL;
const char * c = u+sizeof(struct header);
- while (š--) { /* there's got to be a cleaner way of */
- int č = name2domain_len(u, c); /* doing this ... someone please review */
- if (č < 0) /* all those functions and PLEASE run */
- goto r; /* this program AS UNPRIVILEDGED AS */
- if (!(o = realloc(o, č))) /* POSSIBLE! */
+ while (q--) {
+ int č = name2domain_len(u, s, c);
+ if (č < 0)
+ goto r;
+ if (!(o = realloc(o, č)))
+ goto r;
+ if (!(c = name2domain(o, u, s, c)))
goto r;
- if (!(c = name2domain(o, u, c)))
+ struct question * ć = (struct question *) (c+1);
+ if ((c += sizeof(*ć)+1) >= u+512)
goto r;
- if (c+11 >= u+512)
+ } /* we skip over any questions */
+ while (a--) {
+ int č = name2domain_len(u, s, c);
+ if (č < 0)
+ goto r;
+ if (!(o = realloc(o, č)))
+ goto r;
+ if (!(c = name2domain(o, u, s, c)))
+ goto r;
+ struct rr * ć = (struct rr *) (c+1);
+ if (c+sizeof(*ć) >= u+512)
+ goto r;
+ if ((c += ntohs(ć->len)+sizeof(*ć)+1) >= u+512)
goto r;
if (y != č)
- goto c;
+ continue;
if (memcmp(o, ž, y))
- goto c;
- if (ntohs(*(uint16_t *) c+1) != A)
- goto c;
- if (ntohs(*(uint16_t *) c+3) != In)
- goto c;
- if (ntohs(*(uint16_t *) c+5) != 4) /* this is actually a malformed packet, */
- goto c; /* A resource record must be four bytes */
+ continue;
+ if (ntohs(ć->type) != A)
+ continue;
+ if (ntohs(ć->class) != In)
+ continue;
+ if (ntohs(ć->len) != 4) /* this is actually a malformed packet, */
+ continue; /* A resource record must be four bytes */
if (n--)
- goto c;
- r.s_addr = *(in_addr_t *) c+7;
+ continue;
+ r.s_addr = *(in_addr_t *) (ć->data);
goto r;
-c:
- c += ntohs(*(uint16_t *) c+5)+7;
- if (c >= u+512)
- goto r;
- continue;
- }
+ } /* we scroll over answers now and treat all three types the same, waiting for our A */
r:
free(o);
return r;
} /* returns nth IP of A record for domain d of length l in response packet u or 0.0.0.0 for err */
int finish = 0;
-int c = -1; /* child process */
void handler () {
- if (++finish >= 3) {
- if (c != -1)
- kill(c, SIGHUP);
+ if (++finish >= 3)
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)");
- fprintf(stderr, "recv child received signal\n");
}
int main (int argc, char ** argv) {
int r = 2;
@@ -369,7 +382,7 @@ int main (int argc, char ** argv) {
case 'o':
if ((o = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 00664)) == -1) {
perror("open(optarg, O_CREAT | O_TRUNC | O_WRONLY)");
- r = 3;
+ r = 1;
goto r;
}
struct pcap_global g = {
@@ -383,7 +396,7 @@ int main (int argc, char ** argv) {
};
if (write(o, &g, sizeof g) == -1) {
perror("write(o, &g, sizeof g)");
- r = 4;
+ r = 2;
goto r;
}
break;
@@ -399,7 +412,7 @@ int main (int argc, char ** argv) {
case -1:
if (!(l = argc-optind)) {
fprintf(stderr, "specify targets to scan :: " HELP, argv[0]);
- r = 5;
+ r = 3;
goto r;
}
n = alloca(l*sizeof *n);
@@ -410,14 +423,14 @@ int main (int argc, char ** argv) {
goto o;
case '?':
fprintf(stderr, "unknown option :: " HELP, argv[0]);
- r = 6;
+ r = 4;
goto r;
case ':':
fprintf(stderr, "missing option argument :: " HELP, argv[0]);
- r = 7;
+ r = 5;
goto r;
default:
- r = 8;
+ r = 6;
goto r;
}
}
@@ -427,88 +440,34 @@ o:
fprintf(stderr, "resolving %s ... ", d);
if ((e = resolve(d, &a.s_addr))) {
fprintf(stderr, "failed: %s\n", gai_strerror(e));
- r = 9;
+ r = 7;
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 = 10;
+ r = 8;
goto r;
}
if (bind(s, (struct sockaddr *) &b, sizeof(struct sockaddr))) {
perror("bind(s, (struct sokaddr *) &b, sizeof(struct sockaddr))");
- r = 11;
- goto r;
- }
- if ((c = fork()) == -1) {
- perror("fork()");
- r = 12;
+ r = 9;
goto r;
}
- if (!c) {
- signal(SIGINT, child_handler);
- signal(SIGTERM, child_handler);
- while (1) {
- char u[512]; /* max dns packet */
- struct sockaddr_in f;
- socklen_t č = sizeof f;
- int š;
- if ((š = recvfrom(s, u, 512, lp.tv_sec ? MSG_DONTWAIT : 0,
- (struct sockaddr *) &f, &č)) == -1) {
- if (errno != EWOULDBLOCK) {
- perror("recvfrom(s, u, 512, lp.tv_sec ? MSG_DONTWAIT : 0...");
- 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;
- }
- int ž;
- if ((ž = logudp(o, f, b, u, š)) < -1) {
- fprintf(stderr, "logudp(o, f, b, u, š) == %d\n", ž);
- return 3;
- }
- fprintf(stderr, "received response from %s\n", inet_ntoa(f.sin_addr));
- } /* TODO: parse the response with parse_a etc above this line (: */
- return 0;
- }
+ struct timespec lp = { /* last packet */
+ .tv_sec = 0
+ };
while (!finish) {
if (!(h = host(n[i], ++j)).s_addr) {
if (++i >= l) {
- int s;
fprintf(stderr, "finished sending, waiting for last replies\n");
- if (kill(c, SIGINT) == -1) { /* child waits */
- perror("kill(c, SIGINT)");
- r = 13;
- goto r;
- }
- c = -1;
- if (wait(&s) == -1) {
- perror("wait(&s)");
- r = 14;
- goto r;
- }
- if (WIFEXITED(s) && !WEXITSTATUS(s)) {
- r = 0;
+ if (clock_gettime(CLOCK_MONOTONIC, &lp) == -1) {
+ perror("clock_gettime(CLOCK_MONOTONIC, &z)");
+ r = 10;
goto r;
}
- r = 15;
- goto r;
+ goto i;
}
else
h = host(n[i], (j = 0));
@@ -527,12 +486,16 @@ o:
.arcount = 0
};
int v = domain2name_len(d, strlen(d));
+#define L (sizeof h + v + 2*2)
if (v < 0) {
- r = 16;
+ r = 11;
goto r;
}
-#define L (sizeof h + v + 2*2)
- char * u = alloca(L);
+ if (L > 65535) { /* pebkac, there'll be no error message here */
+ r = 12;
+ goto r;
+ }
+ char u[65535]; /* max udp packet, alloca in a loop would be bad (not scope based) */
char * c;
uint16_t y = htons(A);
uint16_t k = htons(In);
@@ -543,43 +506,96 @@ o:
int ž;
if ((ž = logudp(o, b, e, u, L)) < -1) {
fprintf(stderr, "logudp(o, b, e, u, L) == %d\n", ž);
- r = 17;
+ r = 13;
goto r;
}
if (sendto(s, u, L, 0, (struct sockaddr *) &e, sizeof(struct sockaddr)) == -1) {
perror("sendto(s, u, L, 0, (struct sockaddr *) &e, sizeof(struct sockaddr))");
- r = 18;
+ r = 14;
goto r;
}
- if (usleep(t)) {
- perror("usleep(t)");
- r = 19;
+ struct timespec z;
+i:
+ if (clock_gettime(CLOCK_MONOTONIC, &z) == -1) {
+ perror("clock_gettime(CLOCK_MONOTONIC, &z)");
+ r = 15;
goto r;
}
- }
- 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, " ");
+ if ((z.tv_sec*1000000 + z.tv_nsec/1000) - (lp.tv_sec*1000000 + lp.tv_nsec/1000) > w
+ && lp.tv_sec) {
+ fprintf(stderr, "no more packets were received for -w microseconds. done.\n");
+ r = 0;
+ goto r;
+ }
+ struct pollfd q = {
+ .fd = s,
+ .events = POLLIN
+ };
+ int p;
+ if ((p = poll(&q, 1, t/1000 == 0 ? 1 : t/1000)) == -1) {
+ perror("poll(&q, 1, t/1000 == 0 ? 1 : t/1000)");
+ r = 16;
+ goto r;
}
- strcat(x, "\n");
- write(STDIN_FILENO, x, strlen(x)); /* vsi tele strlen in strcat niso najboljša pot */
+ if (!p) {
+ if (lp.tv_sec)
+ goto i;
+ else
+ continue;
+ }
+ if (q.revents & POLLERR || q.revents & POLLHUP || q.revents & POLLNVAL) {
+ r = 17;
+ goto r;
+ }
+ struct sockaddr_in f;
+ socklen_t č = sizeof f;
+ while (1) {
+ int š;
+ if ((š = recvfrom(s, u, 65535, MSG_DONTWAIT, (struct sockaddr *) &f, &č))
+ == -1) {
+ if (errno != EWOULDBLOCK) {
+ perror("recvfrom(s, u, 65535, MSG_DONTWAIT, (struct sock...");
+ r = 18;
+ goto r;
+ }
+ break;
+ }
+ if (lp.tv_sec)
+ lp = z; /* this loop ends nearly in an instant */
+ if ((ž = logudp(o, f, b, u, š)) < -1) {
+ fprintf(stderr, "logudp(o, f, b, u, š) == %d\n", ž);
+ return 3;
+ }
+ fprintf(stderr, "received response from %s\n", inet_ntoa(f.sin_addr));
+ ž = 0;
+ struct in_addr i = parse_a(u, 65535, d, strlen(d), ž++);
+ if (i.s_addr == a.s_addr) /* if we go back to multithread, change to write. */
+ printf("WORKING %s\n", inet_ntoa(f.sin_addr));
+ if (i.s_addr && i.s_addr != a.s_addr)
+ printf("LYING %s WITH %s\n", inet_ntoa(f.sin_addr), inet_ntoa(i));
+
+ }
+ if (z.tv_sec)
+ goto i;
}
+r:
+ if (!r && j != -1) { /* TODO: tell EXACT packets that were sent before termination. */
+ char * x = alloca(l*31+strlen("SCANNED \n0")); /* currently, if scan was */
+ strcpy(x, "SCANNED "); /* terminated, only networks before */
+ for (int m = 0; m < (finish ? i : l); m++) { /* network at which scan was */
+ strcat(x, inet_ntoa(n[m].addr)); /* terminated are reported to be */
+ strcat(x, "/"); /* scanned, not mentioning the */
+ strcat(x, inet_ntoa(n[m].mask)); /* part of the last not mentioned */
+ strcat(x, " "); /* network that was scanned. */
+ } /* this may lead to statistical */
+ strcat(x, "\n"); /* issues because it would appear */
+ write(STDIN_FILENO, x, strlen(x)); /* as if we received packets from */
+ } /* hosts we haven't queried yet. */
if (s != -1)
if (close(s))
perror("close(s)");
if (o != -1)
if (close(o))
perror("close(o)");
- if (c != -1)
- if (kill(c, SIGKILL) == -1)
- perror("kill(c, SIGKILL)");
return r;
}