diff options
-rw-r--r-- | domain2name.c | 99 | ||||
-rw-r--r-- | domain2name_test.c | 9 | ||||
-rw-r--r-- | main.c | 156 |
3 files changed, 216 insertions, 48 deletions
diff --git a/domain2name.c b/domain2name.c index 46fd2a3..7c84fbb 100644 --- a/domain2name.c +++ b/domain2name.c @@ -1,8 +1,8 @@ -int domain2name_len (const char * s, int l) { - int r = 1; /* ending terminator */ - int o = 0; /* label offset */ - for (int i = 0; i < l; i++) { - if (s[i] == '.') { +int domain2name_len (const char * s, int l) { /* TODO make domain2name FAIL at empty label (..) */ + 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 (!o) /* double period or starting period, label is empty */ break; o = 0; @@ -15,9 +15,9 @@ int domain2name_len (const char * s, int l) { } return r; } -int domain2name (char * d, const char * s, int l) { /* l is length of s */ - char * c = d; /* where to write the label size when done with label */ - char * w = d; +int domain2name (char * n /* at least _len bytes */, const char * s, int l) { /* l is length of s */ + char * c = n; /* where to write the label size when done with label */ + char * w = n; int o = 0; /* label offset */ for (int i = 0; i <= l /* yes, we go one more ... */; i++) { if (i == l /* ... here */ || s[i] == '.') { /* end of label or end of last label */ @@ -36,5 +36,84 @@ int domain2name (char * d, const char * s, int l) { /* l is length of s */ *c = 63; /* it at 63 bytes */ } *w++ = '\0'; /* luckily this makes domain2name kind of safe for handling as a string (: */ - return w-d; /* we return number of bytes written */ -} /* d must be allocated for at least _len. */ + 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 r = 0; + if (n < u+512 && *n == '\0') { + return 2; + } + while (n < u+512) { + if (*n & 1 << 7) { + if (!(*n & 1 << 6)) + return -1; /* 10xx xxxx not implemented - reserved for future use */ + n = u + N2DO(*n); + continue; + } + if (*n & 1 << 6) + return -2; /* 01xx xxxx not implemented - reserved for future use */ + if (!*n) + return r+1; + r += *n+1; + n += *n+1; + } + return -3; /* 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) { + 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') { + *w++ = '.'; + *w++ = '\0'; + return n; + } + while (n < u+512) { + 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; + continue; + } + if (*n & 1 << 6) + return NULL; /* 01xx xxxx N/I - reserved for future use as per RFC */ + if (!*n) { /* end of name */ + if (w) + *w++ = '\0'; + return r ? r : n; + } + const char * x = n+*n; + n++; + if (!(x < u+512)) + return NULL; /* malformed packet */ + while (n <= x) + if (w) + *w++ = *n++; + else + n++; + if (w) + *w++ = '.'; + } + return NULL; /* malformed packet */ +} /* Returns ptr to last byte of name - '\0' or dnsptr. Ret. NULL on fail (_len also returned < 0) */ +int normalizedomain_len (const char * s, int l) { + int ž = domain2name_len(s, l); + if (ž < 0) + return -4; + char * b = alloca(ž); + if (domain2name(b, s, l) != ž) + return -5; + 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; + char * b = alloca(ž); + if (domain2name(b, s, l) != ž) + return -5; + if (!name2domain(d, b, b)) + return -6; + return 0; +} diff --git a/domain2name_test.c b/domain2name_test.c index f0a6898..c88aa0a 100644 --- a/domain2name_test.c +++ b/domain2name_test.c @@ -10,6 +10,15 @@ int main (int argc, char * const argv[]) { int r, s = domain2name_len(argv[1], strlen(argv[1])); char * b = alloca(s); r = domain2name(b, argv[1], strlen(argv[1])); + fprintf(stderr, "strlen %d name2domain_len %d\n", strlen(argv[1]), name2domain_len(b, b)); + int l = name2domain_len(b, b); + if (l < 0) { + fprintf(stderr, "name2domain_len error: %d\n", l); + } else { + char * d = alloca(l); + name2domain(d, b, b); + fprintf(stderr, "name2domain: %s\n", d); + } printf("%4dB %4dB (-:\n", s, r); /* xxd has 16 bytes per row */ printf("%s", b); fflush(stdout); @@ -90,7 +90,7 @@ GLOBAL HEADER: 24 bytes LINKTYP 28 bits: pkt type //tcpdump.org/linktypes.html 101 IPv4/v6 1 ether | | PACKET HEADER: 16 bytes SECONDS 32 bits: UNIX timestamp - NANOSEC 32 bits: nanoseconds elapsed since the second, can also be microseconds - see MAGIC + SUBSECS 32 bits: nanoseconds elapsed since the second, can also be microseconds - see MAGIC CAPTURE 32 bits: number of bytes captured from the packet following the header ORIGLEN 32 bits: number of bytes of the original packet size (can be more than CAPTURE) */ @@ -115,10 +115,10 @@ HEADER: Option byte zero denotes an end of options, NOOP is option number 1 PADDING variable: zero bytes ensuring header is aligned into 32 bit words */ -/* UDP PACKET: HEADER DATA https://www.ietf.org/rfc/rfc768.txt +/* UDP PACKET: HEADER (8 bytes) DATA https://www.ietf.org/rfc/rfc768.txt SRCPORT 16 bits DSTPORT 16 bits - LENGTH 16 bits: size of packet including header in bytes + LENGTH 16 bits: size of packet including header in bytes+8 CHCKSUM 16 bits: same algo as IP, data: pseudoheader (srcip dstip 0x0011 LENGTH) header data */ #define MICROSECOND 0xA1B2C3D4 @@ -141,8 +141,8 @@ struct pcap_packet { uint32_t capture_length __attribute__((packed)); uint32_t original_length __attribute__((packed)); } __attribute__((packed)); -#define LOW_DELAY (1 << 0) -#define HIGH_THROUGHPUT (1 << 1) +#define LOW_DELAY (1 << 4) +#define HIGH_THROUGHPUT (1 << 3) #define HIGH_RELIABILITY (1 << 2) #define ROUTINE (0 << 5) #define PRIORITY (1 << 5) @@ -153,9 +153,9 @@ struct pcap_packet { #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 EVIL (1 << 15) #define DF (1 << 14) -#define MF (1 << 15) +#define MF (1 << 13) #define ICMP 1 #define TCP 6 #define UDP 17 @@ -164,7 +164,7 @@ struct ip { 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)); - uint8_t foffset; /* fragment offset in 64 bit words. also or here any flags: EVIL, DF, MF */ + uint16_t foffset /* 64b wrds */ __attribute__((packed)); /* or 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 */ @@ -196,20 +196,20 @@ enum class { He, Any = 255 }; -#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 RESPONSE (1 << 15) /* :FLAGS */ +#define QUESTION (0 << 15) +#define QUERY (0 << 11) +#define IQUERY (1 << 11) +#define STATUS (1 << 12) +#define AA (1 << 10) +#define TC (1 << 9) +#define RD (1 << 8) +#define RA (1 << 7) +#define SUCCESS (0 << 0) +#define FORMAT_ERROR (1 << 0) +#define SERVFAIL (1 << 1) +#define NXDOMAIN (FORMAT_ERROR | SERVFAIL) +#define NI (1 << 2) #define FORBIDDEN (NXDOMAIN | FORMAT_ERROR) struct header { uint16_t xid __attribute__((packed)); @@ -231,13 +231,13 @@ int logudp (int o /* fd */, struct sockaddr_in s, struct sockaddr_in d, char * u struct pcap_packet p = { .seconds = t.tv_sec, .subseconds = t.tv_nsec, - .capture_length = l, - .original_length = l + .capture_length = sizeof(struct ip) + l + 4*2, + .original_length = sizeof(struct ip) + l + 4*2 }; struct ip i = { .headlen = 5 | HEADLENOR, .srvtype = ROUTINE, - .length = htons(8+l), + .length = htons(8+l+sizeof i), .identifier = 0x6969, .foffset = 0, .ttl = 69, @@ -249,7 +249,7 @@ int logudp (int o /* fd */, struct sockaddr_in s, struct sockaddr_in d, char * u #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 v = htons(l); + uint16_t v = htons(l+8); c = (char *) memcpy(b, &p, sizeof p) + sizeof p; c = (char *) memcpy(c, &i, sizeof i) + sizeof i; c = (char *) memcpy(c, &s.sin_port, 2) + 2; @@ -263,6 +263,53 @@ 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 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. */ + }; + int y = normalizedomain_len(d, l); + if (y < 0) + return r; + char * ž = alloca(y); + if (normalizedomain(ž, d, l) < 0) + return r; + int š = ntohs(((const struct header *) u)->ancount); + 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! */ + goto r; + if (!(c = name2domain(o, u, c))) + goto r; + if (c+11 >= u+512) + goto r; + if (y != č) + goto c; + 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 */ + if (n--) + goto c; + r.s_addr = *(in_addr_t *) c+7; + goto r; +c: + c += ntohs(*(uint16_t *) c+5)+7; + if (c >= u+512) + goto r; + continue; + } +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 () { @@ -278,6 +325,7 @@ struct timespec lp = { /* last packet */ 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; @@ -403,12 +451,14 @@ o: signal(SIGINT, child_handler); signal(SIGTERM, child_handler); while (1) { - char b[512]; /* max dns packet */ + char u[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){ + int š; + if ((š = recvfrom(s, u, 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..."); + perror("recvfrom(s, u, 512, lp.tv_sec ? MSG_DONTWAIT : 0..."); return 1; } struct timespec z; @@ -428,26 +478,36 @@ o: 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; } while (!finish) { - if (!(t = host(n[i], ++j).s_addr)) { + if (!(h = 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 (kill(c, SIGINT) == -1) { /* child waits */ + perror("kill(c, SIGINT)"); + r = 13; + goto r; + } + c = -1; if (wait(&s) == -1) { perror("wait(&s)"); - r = 13; + r = 14; goto r; } if (WIFEXITED(s) && !WEXITSTATUS(s)) { r = 0; goto r; } - r = 14; + r = 15; goto r; } else @@ -467,6 +527,10 @@ o: .arcount = 0 }; int v = domain2name_len(d, strlen(d)); + if (v < 0) { + r = 16; + goto r; + } #define L (sizeof h + v + 2*2) char * u = alloca(L); char * c; @@ -476,9 +540,22 @@ o: 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); + int ž; + if ((ž = logudp(o, b, e, u, L)) < -1) { + fprintf(stderr, "logudp(o, b, e, u, L) == %d\n", ž); + r = 17; + 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; + goto r; + } + if (usleep(t)) { + perror("usleep(t)"); + r = 19; + goto r; + } } if (finish) kill(c, SIGTERM); /* stop receiving */ @@ -486,7 +563,7 @@ 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++) { + 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)); @@ -501,5 +578,8 @@ r: if (o != -1) if (close(o)) perror("close(o)"); + if (c != -1) + if (kill(c, SIGKILL) == -1) + perror("kill(c, SIGKILL)"); return r; } |