summaryrefslogtreecommitdiffstats
path: root/domain2name.c
blob: 9faaf3e639e4c45e3993d5e8a6f5f807bd52d216 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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 may fail in d future */
			if (!o) /* double period or starting period, label is empty */
				break; /* we could return -1 here if r == 1 */
			o = 0;
			continue;
		}
		if (!o) /* label has started */
			r++;
		if (o < 63) { /* we cap label length at 64 bytes. we could return -2 here. */
			r++;
			o++;
		}
	}
	return r;
}
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 */
			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-n; /* we return number of bytes written */
} __attribute__((nonnull))
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;
	char f[s/8+1];
	memset(f, '\0', s/8+1);
	if (n < u+s && *n == '\0') {
		return 2;
	}
	while (n < u+s) {
		if (*n & 1 << 7) {
			if (!(*n & 1 << 6))
				return -1; /* 10xx xxxx not implemented - reserved for future use */
			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 -4; /* 01xx xxxx not implemented - reserved for future use */
		if (!*n)
			return r+1;
		r += *n+1;
		n += *n+1;
	}
	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, 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;
	char f[s/8+1];
	memset(f, '\0', s/8+1);
	if (n < u+s && *n == '\0') {
		*w++ = '.';
		*w++ = '\0';
		return n;
	}
	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 */
			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)
			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+s))
			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 -6;
	char b[ž];
	if (domain2name(b, s, l) != ž)
		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 -6;
	char b[ž];
	if (domain2name(b, s, l) != ž)
		return -7;
	if (!name2domain(d, b, ž, b))
		return -8;
	return 0;
}