summaryrefslogblamecommitdiffstats
path: root/domain2name.c
blob: 9faaf3e639e4c45e3993d5e8a6f5f807bd52d216 (plain) (tree)








































                                                                                                     
                          




























































































                                                                                                     
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;
}