1111/*
1212 * Portions Copyright (c) 1985, 1993
1313 * The Regents of the University of California. All rights reserved.
14+ * Portions Copyright © 2021 mirabilos <m@mirbsd.org>
1415 *
1516 * Redistribution and use in source and binary forms, with or without
1617 * modification, are permitted provided that the following conditions
@@ -395,14 +396,6 @@ extern int __dns_lookup(const char *name,
395396 int type ,
396397 unsigned char * * outpacket ,
397398 struct resolv_answer * a ) attribute_hidden ;
398- extern int __encode_dotted (const char * dotted ,
399- unsigned char * dest ,
400- int maxlen ) attribute_hidden ;
401- extern int __decode_dotted (const unsigned char * packet ,
402- int offset ,
403- int packet_len ,
404- char * dest ,
405- int dest_len ) attribute_hidden ;
406399extern int __encode_header (struct resolv_header * h ,
407400 unsigned char * dest ,
408401 int maxlen ) attribute_hidden ;
@@ -416,6 +409,13 @@ extern int __encode_answer(struct resolv_answer *a,
416409 int maxlen ) attribute_hidden ;
417410extern void __open_nameservers (void ) attribute_hidden ;
418411extern void __close_nameservers (void ) attribute_hidden ;
412+ extern int __hnbad (const char * dotted ) attribute_hidden ;
413+
414+ #define __encode_dotted (dotted ,dest ,maxlen ) \
415+ dn_comp((dotted), (dest), (maxlen), NULL, NULL)
416+ #define __decode_dotted (packet ,offset ,packet_len ,dest ,dest_len ) \
417+ dn_expand((packet), (packet) + (packet_len), (packet) + (offset), \
418+ (dest), (dest_len))
419419
420420/*
421421 * Theory of operation.
@@ -552,116 +552,6 @@ void __decode_header(unsigned char *data,
552552#endif /* L_decodeh */
553553
554554
555- #ifdef L_encoded
556-
557- /* Encode a dotted string into nameserver transport-level encoding.
558- This routine is fairly dumb, and doesn't attempt to compress
559- the data */
560- int __encode_dotted (const char * dotted , unsigned char * dest , int maxlen )
561- {
562- unsigned used = 0 ;
563-
564- while (dotted && * dotted ) {
565- char * c = strchr (dotted , '.' );
566- int l = c ? c - dotted : strlen (dotted );
567-
568- /* two consecutive dots are not valid */
569- if (l == 0 )
570- return -1 ;
571-
572- if (l >= (maxlen - used - 1 ))
573- return -1 ;
574-
575- dest [used ++ ] = l ;
576- memcpy (dest + used , dotted , l );
577- used += l ;
578-
579- if (!c )
580- break ;
581- dotted = c + 1 ;
582- }
583-
584- if (maxlen < 1 )
585- return -1 ;
586-
587- dest [used ++ ] = 0 ;
588-
589- return used ;
590- }
591- #endif /* L_encoded */
592-
593-
594- #ifdef L_decoded
595-
596- /* Decode a dotted string from nameserver transport-level encoding.
597- This routine understands compressed data. */
598- int __decode_dotted (const unsigned char * packet ,
599- int offset ,
600- int packet_len ,
601- char * dest ,
602- int dest_len )
603- {
604- unsigned b ;
605- bool measure = 1 ;
606- unsigned total = 0 ;
607- unsigned used = 0 ;
608- unsigned maxiter = 256 ;
609-
610- if (!packet )
611- return -1 ;
612-
613- dest [0 ] = '\0' ;
614- while (-- maxiter ) {
615- if (offset >= packet_len )
616- return -1 ;
617- b = packet [offset ++ ];
618- if (b == 0 )
619- break ;
620-
621- if (measure )
622- total ++ ;
623-
624- if ((b & 0xc0 ) == 0xc0 ) {
625- if (offset >= packet_len )
626- return -1 ;
627- if (measure )
628- total ++ ;
629- /* compressed item, redirect */
630- offset = ((b & 0x3f ) << 8 ) | packet [offset ];
631- measure = 0 ;
632- continue ;
633- }
634-
635- if (used + b + 1 >= dest_len )
636- return -1 ;
637- if (offset + b >= packet_len )
638- return -1 ;
639- memcpy (dest + used , packet + offset , b );
640- offset += b ;
641- used += b ;
642-
643- if (measure )
644- total += b ;
645-
646- if (packet [offset ] != 0 )
647- dest [used ++ ] = '.' ;
648- else
649- dest [used ++ ] = '\0' ;
650- }
651- if (!maxiter )
652- return -1 ;
653-
654- /* The null byte must be counted too */
655- if (measure )
656- total ++ ;
657-
658- DPRINTF ("Total decode len = %d\n" , total );
659-
660- return total ;
661- }
662- #endif /* L_decoded */
663-
664-
665555#ifdef L_encodeq
666556
667557int __encode_question (const struct resolv_question * q ,
@@ -1204,6 +1094,7 @@ int __dns_lookup(const char *name,
12041094 bool ends_with_dot ;
12051095 bool contains_dot ;
12061096 sockaddr46_t sa ;
1097+ int num_answers ;
12071098
12081099 fd = -1 ;
12091100 lookup = NULL ;
@@ -1446,6 +1337,7 @@ int __dns_lookup(const char *name,
14461337 goto fail1 ;
14471338 }
14481339 pos = HFIXEDSZ ;
1340+ /*XXX TODO: check that question matches query (and qdcount==1?) */
14491341 for (j = 0 ; j < h .qdcount ; j ++ ) {
14501342 DPRINTF ("Skipping question %d at %d\n" , j , pos );
14511343 i = __length_question (packet + pos , packet_len - pos );
@@ -1460,19 +1352,23 @@ int __dns_lookup(const char *name,
14601352 DPRINTF ("Decoding answer at pos %d\n" , pos );
14611353
14621354 first_answer = 1 ;
1355+ num_answers = 0 ;
14631356 a -> dotted = NULL ;
14641357 for (j = 0 ; j < h .ancount ; j ++ ) {
14651358 i = __decode_answer (packet , pos , packet_len , & ma );
14661359 if (i < 0 ) {
14671360 DPRINTF ("failed decode %d\n" , i );
14681361 /* If the message was truncated but we have
14691362 * decoded some answers, pretend it's OK */
1470- if (j && h .tc )
1363+ if (num_answers && h .tc )
14711364 break ;
14721365 goto try_next_server ;
14731366 }
14741367 pos += i ;
14751368
1369+ if (__hnbad (ma .dotted ))
1370+ break ;
1371+ ++ num_answers ;
14761372 if (first_answer ) {
14771373 ma .buf = a -> buf ;
14781374 ma .buflen = a -> buflen ;
@@ -1502,6 +1398,10 @@ int __dns_lookup(const char *name,
15021398 ++ a -> add_count ;
15031399 }
15041400 }
1401+ if (!num_answers ) {
1402+ h_errno = NO_RECOVERY ;
1403+ goto fail1 ;
1404+ }
15051405
15061406 /* Success! */
15071407 DPRINTF ("Answer name = |%s|\n" , a -> dotted );
@@ -2468,7 +2368,7 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen,
24682368 /* Decode CNAME into buf, feed it to __dns_lookup() again */
24692369 i = __decode_dotted (packet , a .rdoffset , packet_len , buf , buflen );
24702370 free (packet );
2471- if (i < 0 ) {
2371+ if (i < 0 || __hnbad ( buf ) ) {
24722372 * h_errnop = NO_RECOVERY ;
24732373 return -1 ;
24742374 }
@@ -2477,6 +2377,10 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen,
24772377 if (a .atype == T_PTR ) { /* ADDRESS */
24782378 i = __decode_dotted (packet , a .rdoffset , packet_len , buf , buflen );
24792379 free (packet );
2380+ if (__hnbad (buf )) {
2381+ * h_errnop = NO_RECOVERY ;
2382+ return -1 ;
2383+ }
24802384 result_buf -> h_name = buf ;
24812385 result_buf -> h_addrtype = type ;
24822386 result_buf -> h_length = addrlen ;
@@ -3041,6 +2945,51 @@ int ns_name_pton(const char *src, u_char *dst, size_t dstsiz)
30412945}
30422946libc_hidden_def (ns_name_pton )
30432947
2948+ /*
2949+ * __hnbad(dotted)
2950+ * Check whether a name is valid enough for DNS. The rules, as
2951+ * laid down by glibc, are:
2952+ * - printable input string
2953+ * - converts to label notation
2954+ * - each label only contains [0-9a-zA-Z_-], up to 63 octets
2955+ * - first label doesn’t begin with ‘-’
2956+ * This both is weaker than Unix hostnames (e.g. it allows
2957+ * underscores and leading/trailing hyphen-minus) and stronger
2958+ * than general (e.g. a leading “*.” is valid sometimes), take care.
2959+ * return:
2960+ * 0 if the name is ok
2961+ */
2962+ int __hnbad (const char * dotted )
2963+ {
2964+ unsigned char c , n , * cp ;
2965+ unsigned char buf [NS_MAXCDNAME ];
2966+
2967+ cp = (unsigned char * )dotted ;
2968+ while ((c = * cp ++ ))
2969+ if (c < 0x21 || c > 0x7E )
2970+ return (1 );
2971+ if (ns_name_pton (dotted , buf , sizeof (buf )) < 0 )
2972+ return (2 );
2973+ if (buf [0 ] > 0 && buf [1 ] == '-' )
2974+ return (3 );
2975+ cp = buf ;
2976+ while ((n = * cp ++ )) {
2977+ if (n > 63 )
2978+ return (4 );
2979+ while (n -- ) {
2980+ c = * cp ++ ;
2981+ if (c < '-' ||
2982+ (c > '-' && c < '0' ) ||
2983+ (c > '9' && c < 'A' ) ||
2984+ (c > 'Z' && c < '_' ) ||
2985+ (c > '_' && c < 'a' ) ||
2986+ c > 'z' )
2987+ return (5 );
2988+ }
2989+ }
2990+ return (0 );
2991+ }
2992+
30442993/*
30452994 * ns_name_unpack(msg, eom, src, dst, dstsiz)
30462995 * Unpack a domain name from a message, source may be compressed.
0 commit comments