diff options
-rw-r--r-- | domain2name.c | 64 | ||||
-rw-r--r-- | main.c | 302 |
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; } @@ -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; } |