|
@@ -11,6 +11,7 @@
|
|
|
|
|
|
* Portions Copyright (c) 1985, 1993
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
+ * Portions Copyright © 2021 mirabilos <m@mirbsd.org>
|
|
|
*
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
* modification, are permitted provided that the following conditions
|
|
@@ -395,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;
|
|
@@ -416,6 +409,13 @@ 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)
|
|
|
+#define __decode_dotted(packet,offset,packet_len,dest,dest_len) \
|
|
|
+ dn_expand((packet), (packet) + (packet_len), (packet) + (offset), \
|
|
|
+ (dest), (dest_len))
|
|
|
|
|
|
|
|
|
* Theory of operation.
|
|
@@ -552,116 +552,6 @@ void __decode_header(unsigned char *data,
|
|
|
#endif
|
|
|
|
|
|
|
|
|
-#ifdef L_encoded
|
|
|
-
|
|
|
-
|
|
|
- This routine is fairly dumb, and doesn't attempt to compress
|
|
|
- the data */
|
|
|
-int __encode_dotted(const char *dotted, unsigned char *dest, int maxlen)
|
|
|
-{
|
|
|
- unsigned used = 0;
|
|
|
-
|
|
|
- while (dotted && *dotted) {
|
|
|
- char *c = strchr(dotted, '.');
|
|
|
- int l = c ? c - dotted : strlen(dotted);
|
|
|
-
|
|
|
-
|
|
|
- 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
|
|
|
-
|
|
|
-
|
|
|
-#ifdef L_decoded
|
|
|
-
|
|
|
-
|
|
|
- This routine understands compressed data. */
|
|
|
-int __decode_dotted(const unsigned char *packet,
|
|
|
- int offset,
|
|
|
- int packet_len,
|
|
|
- char *dest,
|
|
|
- int dest_len)
|
|
|
-{
|
|
|
- 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++;
|
|
|
-
|
|
|
- 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;
|
|
|
-
|
|
|
-
|
|
|
- if (measure)
|
|
|
- total++;
|
|
|
-
|
|
|
- DPRINTF("Total decode len = %d\n", total);
|
|
|
-
|
|
|
- return total;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
#ifdef L_encodeq
|
|
|
|
|
|
int __encode_question(const struct resolv_question *q,
|
|
@@ -1204,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;
|
|
@@ -1446,6 +1337,7 @@ int __dns_lookup(const char *name,
|
|
|
goto fail1;
|
|
|
}
|
|
|
pos = HFIXEDSZ;
|
|
|
+
|
|
|
for (j = 0; j < h.qdcount; j++) {
|
|
|
DPRINTF("Skipping question %d at %d\n", j, pos);
|
|
|
i = __length_question(packet + pos, packet_len - pos);
|
|
@@ -1460,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);
|
|
@@ -1467,12 +1360,15 @@ int __dns_lookup(const char *name,
|
|
|
DPRINTF("failed decode %d\n", i);
|
|
|
|
|
|
* 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;
|
|
@@ -1502,6 +1398,10 @@ int __dns_lookup(const char *name,
|
|
|
++a->add_count;
|
|
|
}
|
|
|
}
|
|
|
+ if (!num_answers) {
|
|
|
+ h_errno = NO_RECOVERY;
|
|
|
+ goto fail1;
|
|
|
+ }
|
|
|
|
|
|
|
|
|
DPRINTF("Answer name = |%s|\n", a->dotted);
|
|
@@ -2468,7 +2368,7 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen,
|
|
|
|
|
|
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;
|
|
|
}
|
|
@@ -2477,6 +2377,10 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen,
|
|
|
if (a.atype == T_PTR) {
|
|
|
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;
|
|
@@ -3041,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.
|