Skip to content

Commit b9f4144

Browse files
committed
DHCP: Sanitize incoming hostnames for RFC1053 conformance
Meross Smart Ambient Light This product is clearly not RFC2132 compliant, so we need to sanitize the incoming hostnames. Add tests to ensure we can fix pretty much anything provided it starts with a valid character.
1 parent a0052da commit b9f4144

File tree

9 files changed

+220
-2
lines changed

9 files changed

+220
-2
lines changed
File renamed without changes.

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ DISTSIGN= ${DISTFILE}.asc
1717

1818
CLEANFILES+= *.tar.xz
1919

20-
.PHONY: hooks import import-bsd
20+
.PHONY: hooks import import-bsd test
2121

2222
.SUFFIXES: .in
2323

@@ -116,4 +116,7 @@ _import-src: clean
116116
import-src:
117117
${MAKE} _import-src DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi`
118118

119+
test:
120+
${MAKE} -C test $@
121+
119122
include Makefile.inc

src/.clang-format-ignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/common.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,92 @@ encode_rfc1035(const char *src, uint8_t *dst)
210210
return len;
211211
}
212212

213+
/* Returns zero if the src is completely valid, otherwise non-zero. */
214+
int
215+
sanitize_rfc1035(char *src)
216+
{
217+
char *start, *p, *l;
218+
int err = 0;
219+
size_t nlabels = 1;
220+
221+
for (start = p = src; *p != '\0'; p++) {
222+
if (p - start > NS_MAXCDNAME) {
223+
*p = '\0';
224+
errno = E2BIG;
225+
return -1;
226+
}
227+
if (isalpha((int)*p) || isdigit((int)*p))
228+
continue;
229+
230+
if (*p == '.') {
231+
if (p == src || ++nlabels > NS_MAXLABELS) {
232+
*p = '\0';
233+
err = 1;
234+
break;
235+
}
236+
237+
/* Find the last non invalid character.
238+
* We know at least the start is valid. */
239+
for (l = p - 1; l >= src; l--) {
240+
if (*l != '-')
241+
break;
242+
}
243+
/* We need to remove the last invalid charaters. */
244+
if (l != p - 1) {
245+
l++;
246+
memmove(l, p, strlen(p) + 1);
247+
p = l;
248+
err = 1;
249+
}
250+
/* Ensure the label doesn't exceed limits */
251+
if (p - src > NS_MAXLABEL) {
252+
memmove(src + NS_MAXLABEL, p, strlen(p) + 1);
253+
p = src + NS_MAXLABEL;
254+
err = 1;
255+
}
256+
/* src now points to the new label. */
257+
src = p + 1;
258+
continue;
259+
}
260+
261+
/* Invalid character.
262+
* If at the start of a label, just stop */
263+
if (p == src) {
264+
*p = '\0';
265+
err = 1;
266+
break;
267+
}
268+
/* Replace with a - */
269+
*p = '-';
270+
err = 1;
271+
}
272+
273+
/* Find the last non invalid character.
274+
* We know at least the start is valid. */
275+
for (l = p - 1; l >= src; l--) {
276+
if (*l != '-')
277+
break;
278+
}
279+
if (l != p - 1) {
280+
*(l + 1) = '\0';
281+
err = 1;
282+
}
283+
284+
/* Trim any trailing dot */
285+
if (*l == '.') {
286+
*l-- = '\0';
287+
err = 1;
288+
}
289+
290+
/* Ensure the label doesn't exceed limits */
291+
if (l + 1 - src >= NS_MAXLABEL) {
292+
*(src + NS_MAXLABEL) = '\0';
293+
err = 1;
294+
}
295+
296+
return err;
297+
}
298+
213299
const char *
214300
hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
215301
{

src/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191

9292
ssize_t decode_rfc1035(char *, size_t, const uint8_t *, size_t);
9393
size_t encode_rfc1035(const char *, uint8_t *);
94+
int sanitize_rfc1035(char *);
9495
const char *hwaddr_ntoa(const void *, size_t, char *, size_t);
9596
size_t hwaddr_aton(uint8_t *, const char *);
9697
struct in_addr;

src/dhcp.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,11 @@ dhcp_handlebootp(struct interface *ifp, struct bootp *bootp, size_t len,
15781578
if (lease->dl_hostname[0] != '\0')
15791579
lease->dl_flags |= DL_HOSTNAME;
15801580
}
1581+
if (lease->dl_flags & DL_HOSTNAME) {
1582+
sanitize_rfc1035(lease->dl_hostname);
1583+
if (lease->dl_hostname[0] == '\0')
1584+
lease->dl_flags &= ~DL_HOSTNAME;
1585+
}
15811586
break;
15821587
}
15831588

test/Makefile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
TOP= ..
2+
include ${TOP}/iconfig.mk
3+
4+
CSTD?= c11
5+
CFLAGS+= -std=${CSTD}
6+
CPPFLAGS+= -I${TOP} -I${TOP}/src -I ${TOP}/vendor
7+
LDFLAGS+= -rdynamic
8+
9+
DEPEND!= test -e .depend && echo ".depend" || echo ""
10+
11+
HOSTNAME_SRCS= hostname.c ../src/common.c
12+
HOSTNAME_OBJS+= ${HOSTNAME_SRCS:.c=.o}
13+
14+
SRCS= ${HOSTNAME_SRCS}
15+
OBJS= ${HOSTNAME_OBJS}
16+
17+
CLEANFILES+= *.tar.xz
18+
19+
20+
all: ${TOP}/config.h hostname
21+
for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done
22+
23+
.c.o:
24+
${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
25+
26+
hostname: ${HOSTNAME_OBJS}
27+
${CC} ${LDFLAGS} -o $@ ${HOSTNAME_OBJS} ${LDADD}
28+
29+
test: hostname
30+
./hostname
31+
32+
clean:
33+
rm -f -- ${OBJS} hostname ${hostname}.core ${HOSTNAME_OBJS} ${CLEANFILES}
34+
for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done
35+
36+
lint: _lint
37+
38+
include ${TOP}/src/Makefile.inc

test/hostname

41.3 KB
Binary file not shown.

test/hostname.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
#include "common.h"
6+
7+
static void
8+
test(const char *src, const char *expect)
9+
{
10+
char *h = strdup(src);
11+
12+
if (h == NULL) {
13+
(void)fprintf(stderr, "strdup %s", src);
14+
exit(EXIT_FAILURE);
15+
}
16+
17+
sanitize_rfc1035(h);
18+
if (strcmp(h, expect) != 0) {
19+
(void)fprintf(stderr, "%s: %s != %s\n", src, expect, h);
20+
(void)fprintf(stderr, "HOSTNAME TEST FAILURE\n");
21+
exit(EXIT_FAILURE);
22+
}
23+
(void)fprintf(stderr, "%s: %s\n", src, expect);
24+
}
25+
26+
int
27+
main(void)
28+
{
29+
char tst[2048], expect[2048];
30+
size_t i, j;
31+
32+
(void)fprintf(stderr, "TESTING WE CAN SANITIZE HOSTNAMES FOR RFC1035\n");
33+
34+
test("valid.hostname", "valid.hostname");
35+
test("invalid hostname", "invalid-hostname");
36+
test(" invalid hostname", "");
37+
test("invalid hostname ", "invalid-hostname");
38+
test("valid.invalid hostname.valid", "valid.invalid-hostname.valid");
39+
test("valid. invalid hostname.valid", "valid");
40+
test("valid.invalid hostname .invalid .valid",
41+
"valid.invalid-hostname.invalid.valid");
42+
test(".invalid hostname ", "");
43+
test("invalid hostname.", "invalid-hostname");
44+
test("invalid hostname. ", "invalid-hostname");
45+
test("invalid hostname..", "invalid-hostname");
46+
47+
/* Test for one character too long for a max label */
48+
test(
49+
"valid.1234567890123456789012345678901234567890123456789012345678901234",
50+
"valid.123456789012345678901234567890123456789012345678901234567890123");
51+
test(
52+
"valid.1234567890123456789012345678901234567890123456789012345678901234.valid",
53+
"valid.123456789012345678901234567890123456789012345678901234567890123.valid");
54+
55+
/* Test for one too many labels */
56+
for (i = 0, j = 0; j < 128; i++) {
57+
if (i % 2 == 0)
58+
tst[i] = 'a';
59+
else {
60+
tst[i] = '.';
61+
j++;
62+
}
63+
}
64+
tst[i] = '\0';
65+
memcpy(expect, tst, i);
66+
// Expect to be trimmed by one
67+
expect[i - 1] = '\0';
68+
test(tst, expect);
69+
70+
/* Test for one character too big for the whole domain. */
71+
for (i = 0, j = 0; i <= 256; i++, j++) {
72+
if (j == 63) {
73+
tst[i] = '.';
74+
j = 0;
75+
} else
76+
tst[i] = 'a';
77+
}
78+
tst[i] = '\0';
79+
memcpy(expect, tst, i);
80+
// Expect to be trimmed by one
81+
expect[i - 1] = '\0';
82+
test(tst, expect);
83+
84+
(void)fprintf(stderr, "HOSTNAME TESTS PASS\n");
85+
exit(EXIT_SUCCESS);
86+
}

0 commit comments

Comments
 (0)