summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host.c95
-rw-r--r--host_test.c9
-rw-r--r--main.c155
3 files changed, 196 insertions, 63 deletions
diff --git a/host.c b/host.c
new file mode 100644
index 0000000..b6e68dd
--- /dev/null
+++ b/host.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <netdb.h>
+struct in_net {
+ struct in_addr addr;
+ struct in_addr mask;
+};
+/* not needed begin */
+#define POPCNT(y) int popcnt##x (uint##y##_t x) { \
+ int c = 0; \
+ for (int i = 0; i < y; i++) \
+ if (1 << i & x) \
+ c++; \
+ return c; \
+}
+POPCNT(32)
+unsigned int power2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648 }; /* ah yes, libmath */
+/* not needed end */
+struct in_addr host (struct in_net n, int h /* consecutive number of the host in the network */) {
+ n.addr.s_addr = ntohl(n.addr.s_addr & n.mask.s_addr);
+ n.mask.s_addr = ntohl(n.mask.s_addr);
+ int c = 1;
+ int s = 0;
+ for (int i = 0; i < 32; i++)
+ if (1 << i & ~n.mask.s_addr) {
+ if (1 << s++ & h)
+ n.addr.s_addr |= 1 << i;
+ c = c * 2;
+ }
+ if (h >= c)
+ n.addr.s_addr = 0;
+ n.addr.s_addr = htonl(n.addr.s_addr);
+ return n.addr;
+}
+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
+ };
+ 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;
+}
+struct in_net str2net (char * s) { /* blocking, resolving */
+ struct in_net r = {
+ .mask = {
+ .s_addr = 0
+ },
+ .addr = {
+ .s_addr = 0
+ }
+ };
+ char * m = strchr(s, '/');
+ char o = '\0';
+ int e;
+ if (m) {
+ o = *m;
+ *m++ = '\0';
+ } else
+ m = "32";
+ fprintf(stderr, "str2net: resolving %s ... ", s);
+ if ((e = resolve(s, &r.addr.s_addr))) {
+ fprintf(stderr, "failed: %s\n", gai_strerror(e));
+ goto r;
+ }
+ fprintf(stderr, " %s mask %s ...", inet_ntoa(r.addr), m);
+ char * p;
+ int x = strtoll(m, &p, 10);
+ r.mask.s_addr = 0;
+ for (int j = 0; j < x && j < 32; j++)
+ r.mask.s_addr = r.mask.s_addr >> 1 | 1 << 31;
+ r.mask.s_addr = htonl(r.mask.s_addr);
+ if (*p)
+ if ((e = resolve(m, &r.mask.s_addr))) {
+ fprintf(stderr, "no: %s\n", gai_strerror(e));
+ goto r;
+ }
+ fprintf(stderr, " %s\n", inet_ntoa(r.mask));
+r:
+ if (o)
+ *--m = o;
+ return r;
+}
diff --git a/host_test.c b/host_test.c
new file mode 100644
index 0000000..0584686
--- /dev/null
+++ b/host_test.c
@@ -0,0 +1,9 @@
+#include "host.c"
+int main (int argc, char ** argv) {
+ if (argc != 3) {
+ fprintf(stderr, "%s network hostnumber\n", argv[0]);
+ return 1;
+ }
+ printf("%s\n", inet_ntoa(host(str2net(argv[1]), atoi(argv[2]))));
+ return 0;
+}
diff --git a/main.c b/main.c
index e490d7b..02ca7dd 100644
--- a/main.c
+++ b/main.c
@@ -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: