From 8d9634346817a65f4ec927f3bc87a2dded7791bf Mon Sep 17 00:00:00 2001 From: mirabilos Date: Mon, 9 Aug 2021 03:30:47 +0200 Subject: libc/inet/resolv.c: use dn_{comp,expand} for __{en,de}code_dotted Minimal-invasive change: just ifdeffing away the older code. There is no reason to have two different sets of functions doing the same thing, one used in half the code and another, doing less escaping, in the other half; just use one. Signed-off-by: mirabilos --- libc/inet/resolv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index ed2e0d2fa..b2cca27d1 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -11,6 +11,7 @@ /* * Portions Copyright (c) 1985, 1993 * The Regents of the University of California. All rights reserved. + * Portions Copyright © 2021 mirabilos * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -559,6 +560,9 @@ void __decode_header(unsigned char *data, the data */ int __encode_dotted(const char *dotted, unsigned char *dest, int maxlen) { +#ifndef WHY_UCLIBC_WHY_DID_YOU_DO_THIS + return (dn_comp(dotted, dest, maxlen, NULL, NULL)); +#else unsigned used = 0; while (dotted && *dotted) { @@ -587,6 +591,7 @@ int __encode_dotted(const char *dotted, unsigned char *dest, int maxlen) dest[used++] = 0; return used; +#endif } #endif /* L_encoded */ @@ -601,6 +606,9 @@ int __decode_dotted(const unsigned char *packet, char *dest, int dest_len) { +#ifndef WHY_UCLIBC_WHY_DID_YOU_DO_THIS + return (dn_expand(packet, packet + packet_len, packet + offset, dest, dest_len)); +#else unsigned b; bool measure = 1; unsigned total = 0; @@ -658,6 +666,7 @@ int __decode_dotted(const unsigned char *packet, DPRINTF("Total decode len = %d\n", total); return total; +#endif } #endif /* L_decoded */ -- cgit v1.2.3 From dc68c2fce026e91f56636dd65b732509f6af29a5 Mon Sep 17 00:00:00 2001 From: mirabilos Date: Mon, 9 Aug 2021 03:33:00 +0200 Subject: actually remove the ifdeffed-out code from the previous commit Signed-off-by: mirabilos --- libc/inet/resolv.c | 91 ------------------------------------------------------ 1 file changed, 91 deletions(-) diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index b2cca27d1..f02781b0b 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -560,38 +560,7 @@ void __decode_header(unsigned char *data, the data */ int __encode_dotted(const char *dotted, unsigned char *dest, int maxlen) { -#ifndef WHY_UCLIBC_WHY_DID_YOU_DO_THIS return (dn_comp(dotted, dest, maxlen, NULL, NULL)); -#else - unsigned used = 0; - - while (dotted && *dotted) { - char *c = strchr(dotted, '.'); - int l = c ? c - dotted : strlen(dotted); - - /* two consecutive dots are not valid */ - if (l == 0) - return -1; - - if (l >= (maxlen - used - 1)) - return -1; - - dest[used++] = l; - memcpy(dest + used, dotted, l); - used += l; - - if (!c) - break; - dotted = c + 1; - } - - if (maxlen < 1) - return -1; - - dest[used++] = 0; - - return used; -#endif } #endif /* L_encoded */ @@ -606,67 +575,7 @@ int __decode_dotted(const unsigned char *packet, char *dest, int dest_len) { -#ifndef WHY_UCLIBC_WHY_DID_YOU_DO_THIS return (dn_expand(packet, packet + packet_len, packet + offset, dest, dest_len)); -#else - unsigned b; - bool measure = 1; - unsigned total = 0; - unsigned used = 0; - unsigned maxiter = 256; - - if (!packet) - return -1; - - dest[0] = '\0'; - while (--maxiter) { - if (offset >= packet_len) - return -1; - b = packet[offset++]; - if (b == 0) - break; - - if (measure) - total++; - - if ((b & 0xc0) == 0xc0) { - if (offset >= packet_len) - return -1; - if (measure) - total++; - /* compressed item, redirect */ - offset = ((b & 0x3f) << 8) | packet[offset]; - measure = 0; - continue; - } - - if (used + b + 1 >= dest_len) - return -1; - if (offset + b >= packet_len) - return -1; - memcpy(dest + used, packet + offset, b); - offset += b; - used += b; - - if (measure) - total += b; - - if (packet[offset] != 0) - dest[used++] = '.'; - else - dest[used++] = '\0'; - } - if (!maxiter) - return -1; - - /* The null byte must be counted too */ - if (measure) - total++; - - DPRINTF("Total decode len = %d\n", total); - - return total; -#endif } #endif /* L_decoded */ -- cgit v1.2.3 From e966cf302aa3b7fc0748d5cdb510d2757bbff4a5 Mon Sep 17 00:00:00 2001 From: mirabilos Date: Mon, 9 Aug 2021 03:36:58 +0200 Subject: make __{en,de}code_dotted into macros they merely call dn_{comp,expand} slightly rearranging the arguments Signed-off-by: mirabilos --- libc/inet/Makefile.in | 2 +- libc/inet/decoded.c | 8 -------- libc/inet/encoded.c | 8 -------- libc/inet/resolv.c | 41 ++++++----------------------------------- 4 files changed, 7 insertions(+), 52 deletions(-) delete mode 100644 libc/inet/decoded.c delete mode 100644 libc/inet/encoded.c diff --git a/libc/inet/Makefile.in b/libc/inet/Makefile.in index 91c417cc2..5e43f2303 100644 --- a/libc/inet/Makefile.in +++ b/libc/inet/Makefile.in @@ -26,7 +26,7 @@ CSRC-$(V4_OR_V6) += \ inet_lnaof.c inet_netof.c # multi source resolv.c CSRC-$(V4_OR_V6) += \ - encodeh.c decodeh.c encoded.c decoded.c \ + encodeh.c decodeh.c \ encodeq.c encodea.c \ read_etc_hosts_r.c \ dnslookup.c opennameservers.c closenameservers.c \ diff --git a/libc/inet/decoded.c b/libc/inet/decoded.c deleted file mode 100644 index 378cbfad7..000000000 --- a/libc/inet/decoded.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2000-2006 Erik Andersen - * - * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. - */ - -#define L_decoded -#include RESOLVER diff --git a/libc/inet/encoded.c b/libc/inet/encoded.c deleted file mode 100644 index 27f92becd..000000000 --- a/libc/inet/encoded.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2000-2006 Erik Andersen - * - * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. - */ - -#define L_encoded -#include RESOLVER diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index f02781b0b..ced3b6542 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -396,14 +396,6 @@ extern int __dns_lookup(const char *name, int type, unsigned char **outpacket, struct resolv_answer *a) attribute_hidden; -extern int __encode_dotted(const char *dotted, - unsigned char *dest, - int maxlen) attribute_hidden; -extern int __decode_dotted(const unsigned char *packet, - int offset, - int packet_len, - char *dest, - int dest_len) attribute_hidden; extern int __encode_header(struct resolv_header *h, unsigned char *dest, int maxlen) attribute_hidden; @@ -418,6 +410,12 @@ extern int __encode_answer(struct resolv_answer *a, extern void __open_nameservers(void) attribute_hidden; extern void __close_nameservers(void) attribute_hidden; +#define __encode_dotted(dotted,dest,maxlen) \ + dn_comp((dotted), (dest), (maxlen), NULL, NULL) +#define __decode_dotted(packet,offset,packet_len,dest,dest_len) \ + dn_expand((packet), (packet) + (packet_len), (packet) + (offset), \ + (dest), (dest_len)) + /* * Theory of operation. * @@ -553,33 +551,6 @@ void __decode_header(unsigned char *data, #endif /* L_decodeh */ -#ifdef L_encoded - -/* Encode a dotted string into nameserver transport-level encoding. - This routine is fairly dumb, and doesn't attempt to compress - the data */ -int __encode_dotted(const char *dotted, unsigned char *dest, int maxlen) -{ - return (dn_comp(dotted, dest, maxlen, NULL, NULL)); -} -#endif /* L_encoded */ - - -#ifdef L_decoded - -/* Decode a dotted string from nameserver transport-level encoding. - This routine understands compressed data. */ -int __decode_dotted(const unsigned char *packet, - int offset, - int packet_len, - char *dest, - int dest_len) -{ - return (dn_expand(packet, packet + packet_len, packet + offset, dest, dest_len)); -} -#endif /* L_decoded */ - - #ifdef L_encodeq int __encode_question(const struct resolv_question *q, -- cgit v1.2.3 From 0f822af0445e5348ce7b7bd8ce1204244f31d174 Mon Sep 17 00:00:00 2001 From: mirabilos Date: Mon, 9 Aug 2021 03:37:31 +0200 Subject: =?UTF-8?q?libc/inet/resolv.c:=20add=20=5F=5Fhnbad=20to=20check=20?= =?UTF-8?q?DNS=20entries=20for=20validity=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … using the same rules glibc does also call __hnbad in some places to check answers --- libc/inet/resolv.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index ced3b6542..8bbd7c7cd 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -409,6 +409,7 @@ extern int __encode_answer(struct resolv_answer *a, int maxlen) attribute_hidden; extern void __open_nameservers(void) attribute_hidden; extern void __close_nameservers(void) attribute_hidden; +extern int __hnbad(const char *dotted) attribute_hidden; #define __encode_dotted(dotted,dest,maxlen) \ dn_comp((dotted), (dest), (maxlen), NULL, NULL) @@ -1093,6 +1094,7 @@ int __dns_lookup(const char *name, bool ends_with_dot; bool contains_dot; sockaddr46_t sa; + int num_answers; fd = -1; lookup = NULL; @@ -1335,6 +1337,7 @@ int __dns_lookup(const char *name, goto fail1; } pos = HFIXEDSZ; + /*XXX TODO: check that question matches query (and qdcount==1?) */ for (j = 0; j < h.qdcount; j++) { DPRINTF("Skipping question %d at %d\n", j, pos); i = __length_question(packet + pos, packet_len - pos); @@ -1349,6 +1352,7 @@ int __dns_lookup(const char *name, DPRINTF("Decoding answer at pos %d\n", pos); first_answer = 1; + num_answers = 0; a->dotted = NULL; for (j = 0; j < h.ancount; j++) { i = __decode_answer(packet, pos, packet_len, &ma); @@ -1356,12 +1360,15 @@ int __dns_lookup(const char *name, DPRINTF("failed decode %d\n", i); /* If the message was truncated but we have * decoded some answers, pretend it's OK */ - if (j && h.tc) + if (num_answers && h.tc) break; goto try_next_server; } pos += i; + if (__hnbad(ma.dotted)) + break; + ++num_answers; if (first_answer) { ma.buf = a->buf; ma.buflen = a->buflen; @@ -1391,6 +1398,10 @@ int __dns_lookup(const char *name, ++a->add_count; } } + if (!num_answers) { + h_errno = NO_RECOVERY; + goto fail1; + } /* Success! */ DPRINTF("Answer name = |%s|\n", a->dotted); @@ -2357,7 +2368,7 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, /* Decode CNAME into buf, feed it to __dns_lookup() again */ i = __decode_dotted(packet, a.rdoffset, packet_len, buf, buflen); free(packet); - if (i < 0) { + if (i < 0 || __hnbad(buf)) { *h_errnop = NO_RECOVERY; return -1; } @@ -2366,6 +2377,10 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, if (a.atype == T_PTR) { /* ADDRESS */ i = __decode_dotted(packet, a.rdoffset, packet_len, buf, buflen); free(packet); + if (__hnbad(buf)) { + *h_errnop = NO_RECOVERY; + return -1; + } result_buf->h_name = buf; result_buf->h_addrtype = type; result_buf->h_length = addrlen; @@ -2930,6 +2945,51 @@ int ns_name_pton(const char *src, u_char *dst, size_t dstsiz) } libc_hidden_def(ns_name_pton) +/* + * __hnbad(dotted) + * Check whether a name is valid enough for DNS. The rules, as + * laid down by glibc, are: + * - printable input string + * - converts to label notation + * - each label only contains [0-9a-zA-Z_-], up to 63 octets + * - first label doesn’t begin with ‘-’ + * This both is weaker than Unix hostnames (e.g. it allows + * underscores and leading/trailing hyphen-minus) and stronger + * than general (e.g. a leading “*.” is valid sometimes), take care. + * return: + * 0 if the name is ok + */ +int __hnbad(const char *dotted) +{ + unsigned char c, n, *cp; + unsigned char buf[NS_MAXCDNAME]; + + cp = (unsigned char *)dotted; + while ((c = *cp++)) + if (c < 0x21 || c > 0x7E) + return (1); + if (ns_name_pton(dotted, buf, sizeof(buf)) < 0) + return (2); + if (buf[0] > 0 && buf[1] == '-') + return (3); + cp = buf; + while ((n = *cp++)) { + if (n > 63) + return (4); + while (n--) { + c = *cp++; + if (c < '-' || + (c > '-' && c < '0') || + (c > '9' && c < 'A') || + (c > 'Z' && c < '_') || + (c > '_' && c < 'a') || + c > 'z') + return (5); + } + } + return (0); +} + /* * ns_name_unpack(msg, eom, src, dst, dstsiz) * Unpack a domain name from a message, source may be compressed. -- cgit v1.2.3