diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 155 |
1 files changed, 92 insertions, 63 deletions
@@ -13,20 +13,24 @@ #include <netdb.h> /* getaddrinfo(3) */ #include <stdlib.h> /* atoi(3) */ #include <string.h> /* strchr(3) */ +#include <time.h> /* clock_gettime(2) */ #include "domain2name.c" +#include "host.c" #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MAXDOMAIN 255 #define QUERYDOMAIN "http://sijanec.eu/link?r=.sijanec.eu." #define EXPECTEDA 93.103.235.126 +#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" \ " -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.\n" \ - " -p Set the source port number to use instead of a dynamically asigned one\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" \ "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" \ @@ -105,10 +109,16 @@ 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 + SRCPORT 16 bits + DSTPORT 16 bits + LENGTH 16 bits: size of packet including header in bytes + CHCKSUM 16 bits: same algo as IP, data: pseudoheader (srcip dstip 0x0011 LENGTH) header data +*/ #define MICROSECOND 0xA1B2C3D4 #define NANOSECOND 0xA1B23C4D -#define PCAPVERMAJ 2 -#define PCAPVERMIN 4 +#define PCAPMAJ 2 +#define PCAPMIN 4 enum linktype { Ethernet = 1, Ip = 101 @@ -153,25 +163,25 @@ enum ip_flags { Mf = 1 << 2 }; enum protocol { - ICMP = 1, - TCP = 6, - UDP = 17 + Icmp = 1, + Tcp = 6, + Udp = 17 }; struct ip { unsigned int version : 4 __attribute__((packed)); - unsigned int headlen : 4 __attribute__((packed)); - enum precedence precedence : 3 __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)); - uint16_t length __attribute__((packed)); + 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)); + 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)); struct in_addr src __attribute__((packed)); - struct in_addr dst __attribute__((packed)); - char options[] /* ignored for char[] */; /* options, padding and data */ + struct in_addr dst __attribute__((packed)); /* ----------- 20 bytes */ + char options[] /* ignored for char[] */; } __attribute__((packed)); enum qr { Question, @@ -246,25 +256,55 @@ struct answer { uint16_t rdlen __attribute__((packed)); char * rdata __attribute__((packed)); }; -struct in_net { - struct in_addr addr; - struct in_addr mask; +struct udp { + struct sockaddr_in src; + struct sockaddr_in dst; + size_t len; /* of data only */ + char * data; }; -int resolve (const char * d, uint32_t * r) { - struct addrinfo hints = { - .ai_family = AF_INET, - .ai_socktype = SOCK_DGRAM, - .ai_flags = 0, - .ai_protocol = 0, - .ai_canonname = NULL, - .ai_addr = NULL, - .ai_next = NULL +int logudp (int o /* file descriptor */, struct udp u) { + struct timespec t; + if (clock_gettime(CLOCK_REALTIME, &t) == -1) { + perror("clock_gettime(CLOCK_REALTIME, &t)"); + return -1; + } + struct pcap_packet p = { + .seconds = t.tv_sec, + .subseconds = t.tv_nsec, + .capture_length = u.len, + .original_length = u.len + }; + struct ip i = { + .version = 4, + .headlen = 5, + .precedence = Routine, + .srvtype = 0, + .length = htons(8+u.len), + .identifier = 0x6969, + .flags = 0, + .foffset = 0, + .ttl = 69, + .protocol = Udp, + .checksum = 0, /* wireshark does not validate, at least not on debian 11 */ + .src = u.src.sin_addr, + .dst = u.dst.sin_addr }; - struct addrinfo * result; - int ret = getaddrinfo(d, NULL, &hints, &result); - *r = ((struct sockaddr_in *) result->ai_addr)->sin_addr.s_addr; /* ah yes, C */ - freeaddrinfo(result); - return ret; +#define LOGUDP_L (sizeof p + sizeof i + 4*2 + u.len) + 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); + 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, &n, 2) + 2; + c = (char *) memcpy(c, u.data, u.len) + u.len; + if (write(o, b, LOGUDP_L) == -1) { /* atomic and thread safe, as per posix */ + perror("write(" STR(LOGUDP_L) ")"); + return -2; + } + return 0; } int main (int argc, char ** argv) { int r = 0; @@ -306,7 +346,21 @@ int main (int argc, char ** argv) { r = 1; goto r; } - write(o, "", 1); + struct pcap_global g = { + .subsecond = NANOSECOND, + .major = PCAPMAJ, + .minor = PCAPMIN, + .reserved[0] = 0, + .reserved[1] = 0, + .snaplen = 65535, + .fcs = 0, + .linktype = Ip + }; + if (write(o, &g, sizeof g) == -1) { + perror("write(o, &g, sizeof g)"); + r = 2; + goto r; + } break; case 'p': b.sin_port = htons(atoi(optarg)); @@ -314,47 +368,22 @@ int main (int argc, char ** argv) { case -1: if (!(l = argc-optind)) { fprintf(stderr, "specify targets to scan :: " HELP, argv[0]); - r = 2; + r = 3; goto r; } n = alloca(l*sizeof(*n)); for (int i = optind; i < argc; i++) { int w = i-optind; - char * m = strchr(argv[i], '/'); - int e; - if (m) - *m++ = '\0'; - else - m = "32"; - fprintf(stderr, "network %d: resolving %s ... ", w, argv[i]); - if ((e = resolve(argv[i], &n[w].addr.s_addr))) { - fprintf(stderr, "failed: %s\n", gai_strerror(e)); - r = 3; - goto r; - } - fprintf(stderr, " %s mask %s ...", inet_ntoa(n[w].addr), m); - char * p; - int x = strtoll(m, &p, 10); - n[w].mask.s_addr = 0; - for (int j = 0; j < x && j < 32; j++) - n[w].mask.s_addr = n[w].mask.s_addr >> 1 | 1 << 31; - n[w].mask.s_addr = htonl(n[w].mask.s_addr); - if (*p) - if ((e = resolve(m, &n[w].mask.s_addr))) { - fprintf(stderr, "no: %s\n", gai_strerror(e)); - r = 4; - goto r; - } - fprintf(stderr, " %s\n", inet_ntoa(n[w].mask)); + n[w] = str2net(argv[i]); } goto o; case '?': fprintf(stderr, "unknown option :: " HELP, argv[0]); - r = 5; + r = 6; goto r; case ':': fprintf(stderr, "missing option argument :: " HELP, argv[0]); - r = 6; + r = 7; goto r; } } @@ -364,7 +393,7 @@ o: fprintf(stderr, "resolving %s ... ", d); if ((e = resolve(d, &a.s_addr))) { fprintf(stderr, "failed: %s\n", gai_strerror(e)); - r = 7; + r = 8; goto r; } fprintf(stderr, " %s\n", inet_ntoa(a)); @@ -376,12 +405,12 @@ o: } if (bind(s, (struct sockaddr *) &b, sizeof(struct sockaddr))) { perror("bind(s, (struct sokaddr *) &b, sizeof(struct sockaddr))"); - r = 9; + r = 10; goto r; } if ((c = fork()) == -1) { perror("fork()"); - r = 10; + r = 11; goto r; } r: |