summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--domain2name.c40
-rw-r--r--domain2name_test.c16
-rw-r--r--domain2name_testcase.c14
-rw-r--r--main.c190
-rw-r--r--test.c8
6 files changed, 269 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cba7efc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+a.out
diff --git a/domain2name.c b/domain2name.c
new file mode 100644
index 0000000..46fd2a3
--- /dev/null
+++ b/domain2name.c
@@ -0,0 +1,40 @@
+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] == '.') {
+ if (!o) /* double period or starting period, label is empty */
+ break;
+ o = 0;
+ continue;
+ }
+ if (!o) /* label has started */
+ r++;
+ r++;
+ o++;
+ }
+ 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 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 */
+ if (!o) /* double period or starting period, label is empty */
+ break;
+ if (o <= 63) /* max length of label (six bits) */
+ *c = o;
+ o = 0;
+ continue;
+ }
+ if (!o++) /* label has started */
+ c = w++; /* to be filled with length */
+ if (o <= 63)
+ *w++ = s[i];
+ if (o == 64) /* if this label is too long, instead of writing it, we silently cap */
+ *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. */
diff --git a/domain2name_test.c b/domain2name_test.c
new file mode 100644
index 0000000..f0a6898
--- /dev/null
+++ b/domain2name_test.c
@@ -0,0 +1,16 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "domain2name.c"
+int main (int argc, char * const argv[]) {
+ if (argc != 2) {
+ fprintf(stderr, "%s domain\n", argv[0]);
+ return 1;
+ }
+ int r, s = domain2name_len(argv[1], strlen(argv[1]));
+ char * b = alloca(s);
+ r = domain2name(b, argv[1], strlen(argv[1]));
+ printf("%4dB %4dB (-:\n", s, r); /* xxd has 16 bytes per row */
+ printf("%s", b);
+ fflush(stdout);
+}
diff --git a/domain2name_testcase.c b/domain2name_testcase.c
new file mode 100644
index 0000000..dfb7334
--- /dev/null
+++ b/domain2name_testcase.c
@@ -0,0 +1,14 @@
+#define D2NL(x,y) assert(domain2name_len(x, strlen(x)) == y); assert(domain2name_len(x ".", strlen(x ".")) == y); assert(domain2name_len("." x, strlen("." x)) == 1)
+D2NL("", 1);
+D2NL("localhost", 11);
+D2NL("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 2002);
+D2NL("a", 3);
+D2NL("sijanec.eu", 12);
+D2NL("splet.sijanec.eu", 18);
+D2NL("splet.sijanec..eu", 15);
+D2NL(".", 1);
+D2NL("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 2001);
+D2NL("a...........................", 3);
+D2NL("splet..sijanec..eu", 7);
+D2NL("splet..sijanec.eu", 7);
+D2NL("193.2.1.66.sijanec.eu", 23);
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..ec94e3f
--- /dev/null
+++ b/main.c
@@ -0,0 +1,190 @@
+#include <sys/socket.h> /* udp(7) */ /* TODO: random XID */
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <poll.h> /* poll(2) */
+#include <sys/types.h> /* socket(2) */
+#include <sys/socket.h>
+#include <unistd.h> /* close(2) */
+#include <stdio.h> /* perror(3) */
+#include <sys/stat.h> /* open(2) */
+#include <fcntl.h>
+#include <errno.h> /* errno(3) */
+#include <arpa/inet.h> /* byteorder(3) */
+#include <string.h> /* strlen(3) */
+#include "domain2name.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 HELP "find recursive DNS resolvers on IPv4 networks\n" \
+ "%s [-h] [-d domain] [-a ipv4] network1 [network2 [network3 ...]]\n" \
+ " -a Specify the IPv4 of the -d domain to be used instead of getaddrinfo(3).\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" \
+ "Networks are specified as domain names or IPv4 addresses followed by a forward slash\n" \
+ "and the netmask in decimal (0-32) or dot notation. For example: example.com/32. \n" \
+ "When scanning the Internet please make sure that you specify your own domain instead\n" \
+ "of the default one (f@sijanec.eu-http://sijanec.eu/link?r=.sijanec.eu). Make sure it\n" \
+ "somehow contains your contact information so the network/server administrator when\n" \
+ "looking at the logs will be able to contact you.\n"
+/* DNS PACKET: HEADER (12 bytes) QUESTION ANSWER AUTHORITY ADDITIONAL
+
+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
+ 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-:
+ ii.) a POINTER that points to some LABLEN somewhere else in the packet
+
+HEADER:
+ XID 16 bits: random string to be matched in response to prevent cache poisoning
+ / QR 1 bit : what type is this packet? 0 query 1 response
+ | OPCODE 4 bits: type of query 0 std 1 invrs 2 srvst 3-15 reserved
+1 byte AA 1 bit : is response authoritative? 0 no 1 yes
+ | TC 1 bit : was response truncated? 0 no 1 yes
+ \ RD 1 bit : does query desire recursion? 0 no 1 yes
+ / RA 1 bit : does response server recurse? 0 no 1 yes
+1 byte Z 3 bits: reserved for future 0 only option
+ \ RCODE 4 bits: error condition 0 ok 1 fmter 2 srvfa 3 nxdom 4 N/I 5 forbidden
+ QDCOUNT 16 bits: number of questions
+ ANCOUNT 16 bits: number of answers
+ NSCOUNT 16 bits: authority section (where to ask for actual response - NS RECORDS)
+ ARCOUNT 16 bits: additional section (glue records)
+QUESTION:
+ QNAME DOMAIN
+ QTYPE 16 bits: 1 A 2 NS 5 CNAME 6 SOA 10 NULL 12 PTR 13 HINFO 15 MX 16 TXT ...
+ QCLASS 16 bits: 1 INTERNET 2 CSNET (obsolete) 3 CHAOS 4 HESIOD 255 ANY ...
+ANSWER:
+ NAME DOMAIN
+ TYPE same as QTYPE
+ CLASS same description as QCLASS, except class 255 ANY is not allowed here
+ 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
+ NULL: any data up to RDLEN PTR: DOMAIN HINFO: STRING-CPU, STRING-OS
+ MX: 16 bit preference, NAME-like domain TXT: one or more STRING
+
+
+*/
+enum qr {
+ Query,
+ Response
+};
+enum opcode {
+ Query,
+ Iquery,
+ Stauts
+};
+enum rcode {
+ Success,
+ Format_error,
+ Servfail,
+ Nxdomain,
+ Ni,
+ Forbidden
+};
+enum type {
+ A = 1,
+ Ns,
+ Md,
+ Mf,
+ Cname,
+ Soa,
+ Mb,
+ Mg,
+ Mr,
+ Null,
+ Wks,
+ Ptr,
+ Hinfo,
+ Minfo,
+ Mx,
+ Txt
+}
+enum class {
+ In = 1,
+ Cs,
+ Ch,
+ He,
+ Any = 255
+}
+struct header {
+ uint16_t xid;
+ enum qr qr : 1;
+ enum opcode opcode : 4;
+ unsigned int aa : 1;
+ unsigned int tc : 1;
+ unsigned int rd : 1;
+ unsigned int ra : 1;
+ unsigned int z : 3;
+ enum rcode rcode : 4;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+};
+struct question {
+ char * qname;
+ enum qtype qtype : 16;
+ enum qclass qclass : 16;
+};
+struct answer {
+ char * name;
+ enum class class : 16;
+ uint16_t rdlen;
+ char * rdata;
+}
+int main (int argc, char ** argv) {
+ int r = 0;
+ while (t) {
+ switch (getopt(argc, argv, ":a:d:h")) {
+ /* TODO */
+ case 'h':
+ printf(HELP, argv[0]);
+ r = 0;
+ goto r;
+ case -1:
+ d = optarg;
+ if (!d || !*d) {
+ fprintf(stderr, "specify the domain :: " HELP, argv[0]);
+ r = 4;
+ goto r;
+ }
+ if (strlen(d) > MAXDOMAIN) {
+ fprintf(stderr, "domain is too long - max "#MAXDOMAIN".\n");
+ r = 5;
+ goto r;
+ }
+ t = 0;
+ break;
+ case '?':
+ fprintf(stderr, "unknown option :: " HELP, argv[0]);
+ if (db != -1)
+ close(db);
+ r = 6;
+ goto r;
+ case ':':
+ fprintf(stderr, "missing option argument :: " HELP, argv[0]);
+ r = 7;
+ goto r;
+ }
+ }
+r:
+ if (s != -1)
+ if (close(s))
+ perror("close(s)");
+ }
+ return r;
+}
+int handle () {
+ char buf[512]; /* we don't support EDNS0 here, so max DNS packet is 512 bytes */
+ recvfrom();
+}
+int query () {
+ sendto();
+}
+int erase (char * dbfn, int dbfd, char * db) {
+
+}
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..eaa490e
--- /dev/null
+++ b/test.c
@@ -0,0 +1,8 @@
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include "domain2name.c"
+int main (void) {
+#include "domain2name_testcase.c"
+ return 0;
+}