summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-18 23:12:40 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-18 23:12:40 +0000
commitac1087a727a472804dc929a35b140407b7e38472 (patch)
treebba3f5c6b5807118433e1f77d83ce066c7994f46
parentd83d5f98e1c6be6d363abf208d7f304fc356a285 (diff)
libc/inet/resolv.c:
Collapse __length_dotted into __length_question (the sole user of it). Make __length_question and __decode_answer static, they are used only once by only one function. Delete __decode_question, it is unused. All in all, four less .o files in libc.a. Document what __dns_lookup returns (length of the packet). Propagate packet len into __decode_answer, __length_question, __decode_dotted and check that we do not use data past the end of the packet. Rename some variables/parameters to better names (len -> packet_len, data -> packet etc). Add mini-doc how DNS packets look like. Style cleanup.
-rw-r--r--libc/inet/Makefile.in4
-rw-r--r--libc/inet/resolv.c741
2 files changed, 467 insertions, 278 deletions
diff --git a/libc/inet/Makefile.in b/libc/inet/Makefile.in
index dd17633ef..87d3203b8 100644
--- a/libc/inet/Makefile.in
+++ b/libc/inet/Makefile.in
@@ -34,8 +34,8 @@ endif
# multi source resolv.c
resolv_CSRC += \
- encodeh.c decodeh.c encoded.c decoded.c lengthd.c \
- encodeq.c decodeq.c encodea.c decodea.c lengthq.c \
+ encodeh.c decodeh.c encoded.c decoded.c \
+ encodeq.c encodea.c \
dnslookup.c opennameservers.c closenameservers.c \
read_etc_hosts_r.c get_hosts_byaddr_r.c get_hosts_byname_r.c \
getnameinfo.c \
diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c
index a8dace7ce..109ae020f 100644
--- a/libc/inet/resolv.c
+++ b/libc/inet/resolv.c
@@ -9,7 +9,6 @@
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*/
-
/*
* Portions Copyright (c) 1985, 1993
* The Regents of the University of California. All rights reserved.
@@ -38,7 +37,6 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-
/*
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
@@ -58,7 +56,6 @@
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
-
/*
* Portions Copyright (c) 1996-1999 by Internet Software Consortium.
*
@@ -75,32 +72,25 @@
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
-
/*
- *
* 5-Oct-2000 W. Greathouse wgreathouse@smva.com
- * Fix memory leak and memory corruption.
- * -- Every name resolution resulted in
- * a new parse of resolv.conf and new
- * copy of nameservers allocated by
- * strdup.
- * -- Every name resolution resulted in
- * a new read of resolv.conf without
- * resetting index from prior read...
- * resulting in exceeding array bounds.
- *
- * Limit nameservers read from resolv.conf
+ * Fix memory leak and memory corruption.
+ * -- Every name resolution resulted in
+ * a new parse of resolv.conf and new
+ * copy of nameservers allocated by
+ * strdup.
+ * -- Every name resolution resulted in
+ * a new read of resolv.conf without
+ * resetting index from prior read...
+ * resulting in exceeding array bounds.
*
- * Add "search" domains from resolv.conf
- *
- * Some systems will return a security
- * signature along with query answer for
- * dynamic DNS entries.
- * -- skip/ignore this answer
- *
- * Include arpa/nameser.h for defines.
- *
- * General cleanup
+ * Limit nameservers read from resolv.conf.
+ * Add "search" domains from resolv.conf.
+ * Some systems will return a security
+ * signature along with query answer for
+ * dynamic DNS entries -- skip/ignore this answer.
+ * Include arpa/nameser.h for defines.
+ * General cleanup.
*
* 20-Jun-2001 Michal Moskal <malekith@pld.org.pl>
* partial IPv6 support (i.e. gethostbyname2() and resolve_address2()
@@ -132,15 +122,173 @@
* 7-Sep-2004 Erik Andersen <andersen@codepoet.org>
* Added gethostent_r()
*
+ * 2008, 2009 Denys Vlasenko <vda.linux@googlemail.com>
+ * Cleanups, fixes, readability, more cleanups and more fixes.
*/
-
/* Nota bene:
- The whole resolver code has several (severe) problems:
- - it doesn't even build without IPv4, i.e. !UCLIBC_HAS_IPV4 but only IPv6
- - it is way too big
-
- Both points above are considered bugs, patches/reimplementations welcome.
-*/
+ * The whole resolver code has several (severe) problems:
+ * - it doesn't even build without IPv4, i.e. !UCLIBC_HAS_IPV4 but only IPv6
+ * - it is way too big
+ *
+ * Both points above are considered bugs, patches/reimplementations welcome.
+ */
+/* RFC 1035
+...
+Whenever an octet represents a numeric quantity, the left most bit
+in the diagram is the high order or most significant bit.
+That is, the bit labeled 0 is the most significant bit.
+...
+
+4.1.1. Header section format
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ID |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ |QR| OPCODE |AA|TC|RD|RA| 0 0 0| RCODE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QDCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ANCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | NSCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ARCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ID 16 bit random identifier assigned by querying peer.
+ Used to match query/response.
+QR message is a query (0), or a response (1).
+OPCODE 0 standard query (QUERY)
+ 1 inverse query (IQUERY)
+ 2 server status request (STATUS)
+AA Authoritative Answer - this bit is valid in responses.
+ Responding name server is an authority for the domain name
+ in question section. Answer section may have multiple owner names
+ because of aliases. The AA bit corresponds to the name which matches
+ the query name, or the first owner name in the answer section.
+TC TrunCation - this message was truncated.
+RD Recursion Desired - this bit may be set in a query and
+ is copied into the response. If RD is set, it directs
+ the name server to pursue the query recursively.
+ Recursive query support is optional.
+RA Recursion Available - this be is set or cleared in a
+ response, and denotes whether recursive query support is
+ available in the name server.
+RCODE Response code.
+ 0 No error condition
+ 1 Format error
+ 2 Server failure - server was unable to process the query
+ due to a problem with the name server.
+ 3 Name Error - meaningful only for responses from
+ an authoritative name server. The referenced domain name
+ does not exist.
+ 4 Not Implemented.
+ 5 Refused.
+QDCOUNT number of entries in the question section.
+ANCOUNT number of records in the answer section.
+NSCOUNT number of records in the authority records section.
+ARCOUNT number of records in the additional records section.
+
+4.1.2. Question section format
+
+The section contains QDCOUNT (usually 1) entries, each of this format:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / QNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QTYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QCLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+QNAME a domain name represented as a sequence of labels, where
+ each label consists of a length octet followed by that
+ number of octets. The domain name terminates with the
+ zero length octet for the null label of the root. Note
+ that this field may be an odd number of octets; no
+ padding is used.
+QTYPE a two octet type of the query.
+ 1 a host address [REQ_A const]
+ 2 an authoritative name server
+ 3 a mail destination (Obsolete - use MX)
+ 4 a mail forwarder (Obsolete - use MX)
+ 5 the canonical name for an alias
+ 6 marks the start of a zone of authority
+ 7 a mailbox domain name (EXPERIMENTAL)
+ 8 a mail group member (EXPERIMENTAL)
+ 9 a mail rename domain name (EXPERIMENTAL)
+ 10 a null RR (EXPERIMENTAL)
+ 11 a well known service description
+ 12 a domain name pointer [REQ_PTR const]
+ 13 host information
+ 14 mailbox or mail list information
+ 15 mail exchange
+ 16 text strings
+ 0x1c IPv6?
+ 252 a request for a transfer of an entire zone
+ 253 a request for mailbox-related records (MB, MG or MR)
+ 254 a request for mail agent RRs (Obsolete - see MX)
+ 255 a request for all records
+QCLASS a two octet code that specifies the class of the query.
+ 1 the Internet
+ (others are historic only)
+ 255 any class
+
+4.1.3. Resource record format
+
+The answer, authority, and additional sections all share the same format:
+a variable number of resource records, where the number of records
+is specified in the corresponding count field in the header.
+Each resource record has this format:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / /
+ / NAME /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | CLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TTL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RDLENGTH |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ / RDATA /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+NAME a domain name to which this resource record pertains.
+TYPE two octets containing one of the RR type codes. This
+ field specifies the meaning of the data in the RDATA field.
+CLASS two octets which specify the class of the data in the RDATA field.
+TTL a 32 bit unsigned integer that specifies the time interval
+ (in seconds) that the record may be cached.
+RDLENGTH a 16 bit integer, length in octets of the RDATA field.
+RDATA a variable length string of octets that describes the resource.
+ The format of this information varies according to the TYPE
+ and CLASS of the resource record.
+ If the TYPE is A and the CLASS is IN, it's a 4 octet IP address.
+
+4.1.4. Message compression
+
+In order to reduce the size of messages, domain names can be compressed.
+An entire domain name or a list of labels at the end of a domain name
+is replaced with a pointer to a prior occurance of the same name.
+
+The pointer takes the form of a two octet sequence:
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | 1 1| OFFSET |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+The first two bits are ones. This allows a pointer to be distinguished
+from a label, since the label must begin with two zero bits because
+labels are restricted to 63 octets or less. The OFFSET field specifies
+an offset from the start of the message (i.e., the first octet
+of the ID field in the domain header).
+A zero offset specifies the first byte of the ID field, etc.
+Domain name in a message can be represented as either:
+ - a sequence of labels ending in a zero octet
+ - a pointer
+ - a sequence of labels ending with a pointer
+ */
#define __FORCE_GLIBC
#include <features.h>
@@ -210,7 +358,7 @@
/* Structs */
struct resolv_header {
int id;
- int qr,opcode,aa,tc,rd,ra,rcode;
+ int qr, opcode, aa, tc, rd, ra, rcode;
int qdcount;
int ancount;
int nscount;
@@ -218,18 +366,18 @@ struct resolv_header {
};
struct resolv_question {
- char * dotted;
+ char *dotted;
int qtype;
int qclass;
};
struct resolv_answer {
- char * dotted;
+ char *dotted;
int atype;
int aclass;
int ttl;
int rdlength;
- const unsigned char * rdata;
+ const unsigned char *rdata;
int rdoffset;
char* buf;
size_t buflen;
@@ -272,42 +420,54 @@ extern const struct sockaddr_in6 __local_nameserver attribute_hidden;
/* function prototypes */
-extern int __get_hosts_byname_r(const char * name, int type,
- struct hostent * result_buf,
- char * buf, size_t buflen,
- struct hostent ** result,
- int * h_errnop) attribute_hidden;
-extern int __get_hosts_byaddr_r(const char * addr, int len, int type,
- struct hostent * result_buf,
- char * buf, size_t buflen,
- struct hostent ** result,
- int * h_errnop) attribute_hidden;
-extern FILE * __open_etc_hosts(void) attribute_hidden;
-extern int __read_etc_hosts_r(FILE *fp, const char * name, int type,
- enum etc_hosts_action action,
- struct hostent * result_buf,
- char * buf, size_t buflen,
- struct hostent ** result,
- int * h_errnop) attribute_hidden;
-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 * const message, int offset,
- char * dest, int maxlen) attribute_hidden;
-extern int __length_dotted(const unsigned char * const message, int offset) attribute_hidden;
-extern int __encode_header(struct resolv_header * h, unsigned char * dest, int maxlen) attribute_hidden;
-extern void __decode_header(unsigned char * data, struct resolv_header * h) attribute_hidden;
-extern int __encode_question(const struct resolv_question * const q,
- unsigned char * dest, int maxlen) attribute_hidden;
-extern int __decode_question(const unsigned char * const message, int offset,
- struct resolv_question * q) attribute_hidden;
-extern int __encode_answer(struct resolv_answer * a,
- unsigned char * dest, int maxlen) attribute_hidden;
-extern int __decode_answer(const unsigned char * message, int offset,
- int len, struct resolv_answer * a) attribute_hidden;
-extern int __length_question(const unsigned char * const message, int offset) attribute_hidden;
+extern int __get_hosts_byname_r(const char *name,
+ int type,
+ struct hostent *result_buf,
+ char *buf,
+ size_t buflen,
+ struct hostent **result,
+ int *h_errnop) attribute_hidden;
+extern int __get_hosts_byaddr_r(const char *addr,
+ int len,
+ int type,
+ struct hostent *result_buf,
+ char *buf,
+ size_t buflen,
+ struct hostent **result,
+ int *h_errnop) attribute_hidden;
+extern FILE *__open_etc_hosts(void) attribute_hidden;
+extern int __read_etc_hosts_r(FILE *fp,
+ const char *name,
+ int type,
+ enum etc_hosts_action action,
+ struct hostent *result_buf,
+ char *buf,
+ size_t buflen,
+ struct hostent **result,
+ int *h_errnop) attribute_hidden;
+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;
+extern void __decode_header(unsigned char *data,
+ struct resolv_header *h) attribute_hidden;
+extern int __encode_question(const struct resolv_question *q,
+ unsigned char *dest,
+ int maxlen) attribute_hidden;
+extern int __encode_answer(struct resolv_answer *a,
+ unsigned char *dest,
+ int maxlen) attribute_hidden;
extern void __open_nameservers(void) attribute_hidden;
extern void __close_nameservers(void) attribute_hidden;
@@ -427,7 +587,8 @@ int attribute_hidden __encode_header(struct resolv_header *h, unsigned char *des
#ifdef L_decodeh
-void attribute_hidden __decode_header(unsigned char *data, struct resolv_header *h)
+void attribute_hidden __decode_header(unsigned char *data,
+ struct resolv_header *h)
{
h->id = (data[0] << 8) | data[1];
h->qr = (data[2] & 0x80) ? 1 : 0;
@@ -488,39 +649,53 @@ int attribute_hidden __encode_dotted(const char *dotted, unsigned char *dest, in
/* Decode a dotted string from nameserver transport-level encoding.
This routine understands compressed data. */
-int attribute_hidden __decode_dotted(const unsigned char * const data, int offset,
- char *dest, int maxlen)
+int attribute_hidden __decode_dotted(const unsigned char *packet,
+ int offset,
+ int packet_len,
+ char *dest,
+ int dest_len)
{
- int l;
+ unsigned b;
bool measure = 1;
unsigned total = 0;
unsigned used = 0;
- if (!data)
+ if (!packet)
return -1;
- while ((l = data[offset++])) {
+ while (1) {
+ if (offset >= packet_len)
+ return -1;
+ b = packet[offset++];
+ if (b == 0)
+ break;
+
if (measure)
total++;
- if ((l & 0xc0) == (0xc0)) {
+
+ if ((b & 0xc0) == 0xc0) {
+ if (offset >= packet_len)
+ return -1;
if (measure)
total++;
/* compressed item, redirect */
- offset = ((l & 0x3f) << 8) | data[offset];
+ offset = ((b & 0x3f) << 8) | packet[offset];
measure = 0;
continue;
}
- if ((used + l + 1) >= maxlen)
+ if (used + b + 1 >= dest_len)
return -1;
+ if (offset + b + 1 >= packet_len)
+ return -1;
+ memcpy(dest + used, packet + offset, b);
+ offset += b;
+ used += b;
- memcpy(dest + used, data + offset, l);
- offset += l;
- used += l;
if (measure)
- total += l;
+ total += b;
- if (data[offset] != 0)
+ if (packet[offset] != 0)
dest[used++] = '.';
else
dest[used++] = '\0';
@@ -537,35 +712,11 @@ int attribute_hidden __decode_dotted(const unsigned char * const data, int offse
#endif
-#ifdef L_lengthd
-
-/* Returns -1 only if data == NULL */
-int attribute_hidden __length_dotted(const unsigned char * const data, int offset)
-{
- int orig_offset = offset;
- int l;
-
- if (!data)
- return -1;
-
- while ((l = data[offset++])) {
- if ((l & 0xc0) == (0xc0)) {
- offset++;
- break;
- }
-
- offset += l;
- }
-
- return offset - orig_offset;
-}
-#endif
-
-
#ifdef L_encodeq
-int attribute_hidden __encode_question(const struct resolv_question * const q,
- unsigned char *dest, int maxlen)
+int attribute_hidden __encode_question(const struct resolv_question *q,
+ unsigned char *dest,
+ int maxlen)
{
int i;
@@ -589,47 +740,6 @@ int attribute_hidden __encode_question(const struct resolv_question * const q,
#endif
-#ifdef L_decodeq
-
-int attribute_hidden __decode_question(const unsigned char * const message, int offset,
- struct resolv_question *q)
-{
- char temp[256];
- int i;
-
- i = __decode_dotted(message, offset, temp, sizeof(temp));
- if (i < 0)
- return i;
-
- offset += i;
-
-/*TODO: what if strdup fails? */
- q->dotted = strdup(temp);
- q->qtype = (message[offset + 0] << 8) | message[offset + 1];
- q->qclass = (message[offset + 2] << 8) | message[offset + 3];
-
- return i + 4;
-}
-#endif
-
-
-#ifdef L_lengthq
-
-/* Returns -1 only if message == NULL */
-int attribute_hidden __length_question(const unsigned char * const message, int offset)
-{
- int i;
-
- /* returns -1 only if message == NULL */
- i = __length_dotted(message, offset);
- if (i < 0)
- return i;
-
- return i + 4;
-}
-#endif
-
-
#ifdef L_encodea
int attribute_hidden __encode_answer(struct resolv_answer *a, unsigned char *dest, int maxlen)
@@ -663,64 +773,21 @@ int attribute_hidden __encode_answer(struct resolv_answer *a, unsigned char *des
#endif
-#ifdef L_decodea
-
-int attribute_hidden __decode_answer(const unsigned char *message, int offset,
- int len, struct resolv_answer *a)
-{
- char temp[256];
- int i;
-
- DPRINTF("decode_answer(start): off %d, len %d\n", offset, len);
- i = __decode_dotted(message, offset, temp, sizeof(temp));
- if (i < 0)
- return i;
-
- message += offset + i;
- len -= i + RRFIXEDSZ + offset;
- if (len < 0) {
- DPRINTF("decode_answer: off %d, len %d, i %d\n", offset, len, i);
- return len;
- }
-
-/* TODO: what if strdup fails? */
- a->dotted = strdup(temp);
- a->atype = (message[0] << 8) | message[1];
- message += 2;
- a->aclass = (message[0] << 8) | message[1];
- message += 2;
- a->ttl = (message[0] << 24) |
- (message[1] << 16) | (message[2] << 8) | (message[3] << 0);
- message += 4;
- a->rdlength = (message[0] << 8) | message[1];
- message += 2;
- a->rdata = message;
- a->rdoffset = offset + i + RRFIXEDSZ;
-
- DPRINTF("i=%d,rdlength=%d\n", i, a->rdlength);
-
- if (len < a->rdlength)
- return -1;
- return i + RRFIXEDSZ + a->rdlength;
-}
-#endif
-
-
#ifdef CURRENTLY_UNUSED
#ifdef L_encodep
int __encode_packet(struct resolv_header *h,
- struct resolv_question **q,
- struct resolv_answer **an,
- struct resolv_answer **ns,
- struct resolv_answer **ar,
- unsigned char *dest, int maxlen) attribute_hidden;
+ struct resolv_question **q,
+ struct resolv_answer **an,
+ struct resolv_answer **ns,
+ struct resolv_answer **ar,
+ unsigned char *dest, int maxlen) attribute_hidden;
int __encode_packet(struct resolv_header *h,
- struct resolv_question **q,
- struct resolv_answer **an,
- struct resolv_answer **ns,
- struct resolv_answer **ar,
- unsigned char *dest, int maxlen)
+ struct resolv_question **q,
+ struct resolv_answer **an,
+ struct resolv_answer **ns,
+ struct resolv_answer **ar,
+ unsigned char *dest, int maxlen)
{
int i, total = 0;
unsigned j;
@@ -785,9 +852,16 @@ int __decode_packet(unsigned char *data, struct resolv_header *h)
#ifdef L_formquery
-int __form_query(int id, const char *name, int type, unsigned char *packet, int maxlen);
-int __form_query(int id, const char *name, int type, unsigned char *packet,
- int maxlen)
+int __form_query(int id,
+ const char *name,
+ int type,
+ unsigned char *packet,
+ int maxlen);
+int __form_query(int id,
+ const char *name,
+ int type,
+ unsigned char *packet,
+ int maxlen)
{
struct resolv_header h;
struct resolv_question q;
@@ -1034,12 +1108,87 @@ void attribute_hidden __close_nameservers(void)
#ifdef L_dnslookup
+/* Helpers */
+static int __length_question(const unsigned char *data, int maxlen)
+{
+ const unsigned char *start;
+ unsigned b;
+
+ if (!data)
+ return -1;
+
+ start = data;
+ while (1) {
+ if (maxlen <= 0)
+ return -1;
+ b = *data++;
+ if (b == 0)
+ break;
+ if ((b & 0xc0) == 0xc0) {
+ /* It's a "compressed" name. */
+ data++; /* skip lsb of redirected offset */
+ maxlen -= 2;
+ break;
+ }
+ data += b;
+ maxlen -= (b + 1); /* account for data++ above */
+ }
+ /* Up to here we were skipping encoded name */
+
+ /* Account for QTYPE and QCLASS fields */
+ if (maxlen < 4)
+ return -1;
+ return data - start + 2 + 2;
+}
+
+static int __decode_answer(const unsigned char *message, /* packet */
+ int offset,
+ int len, /* total packet len */
+ struct resolv_answer *a)
+{
+ char temp[256];
+ int i;
+
+ DPRINTF("decode_answer(start): off %d, len %d\n", offset, len);
+ i = __decode_dotted(message, offset, len, temp, sizeof(temp));
+ if (i < 0)
+ return i;
+
+ message += offset + i;
+ len -= i + RRFIXEDSZ + offset;
+ if (len < 0) {
+ DPRINTF("decode_answer: off %d, len %d, i %d\n", offset, len, i);
+ return len;
+ }
+
+/* TODO: what if strdup fails? */
+ a->dotted = strdup(temp);
+ a->atype = (message[0] << 8) | message[1];
+ message += 2;
+ a->aclass = (message[0] << 8) | message[1];
+ message += 2;
+ a->ttl = (message[0] << 24) |
+ (message[1] << 16) | (message[2] << 8) | (message[3] << 0);
+ message += 4;
+ a->rdlength = (message[0] << 8) | message[1];
+ message += 2;
+ a->rdata = message;
+ a->rdoffset = offset + i + RRFIXEDSZ;
+
+ DPRINTF("i=%d,rdlength=%d\n", i, a->rdlength);
+
+ if (len < a->rdlength)
+ return -1;
+ return i + RRFIXEDSZ + a->rdlength;
+}
+
/* On entry:
* a.buf(len) = auxiliary buffer for IP addresses after first one
* a.add_count = how many additional addresses are there already
* outpacket = where to save ptr to raw packet? can be NULL
* On exit:
* ret < 0: error, all other data is not valid
+ * ret >= 0: length of reply packet
* a.add_count & a.buf: updated
* a.rdlength: length of addresses (4 bytes for IPv4)
* *outpacket: updated (packet is malloced, you need to free it)
@@ -1050,15 +1199,17 @@ void attribute_hidden __close_nameservers(void)
* appended. (why the filed is called "dotted" I have no idea)
* This is a malloced string. May be NULL because strdup failed.
*/
-int attribute_hidden __dns_lookup(const char *name, int type,
- unsigned char **outpacket,
- struct resolv_answer *a)
+int attribute_hidden __dns_lookup(const char *name,
+ int type,
+ unsigned char **outpacket,
+ struct resolv_answer *a)
{
/* Protected by __resolv_lock: */
static int last_ns_num = 0;
static uint16_t last_id = 1;
- int i, j, len, fd, pos, rc;
+ int i, j, fd, rc;
+ int packet_len;
int name_len;
#ifdef USE_SELECT
struct timeval tv;
@@ -1095,6 +1246,7 @@ int attribute_hidden __dns_lookup(const char *name, int type,
DPRINTF("Looking up type %d answer for '%s'\n", type, name);
retries_left = 0; /* for compiler */
do {
+ int pos;
unsigned reply_timeout;
if (fd != -1) {
@@ -1166,7 +1318,7 @@ int attribute_hidden __dns_lookup(const char *name, int type,
j = __encode_question(&q, packet+i, PACKETSZ-i);
if (j < 0)
goto fail;
- len = i + j;
+ packet_len = i + j;
/* send packet */
DPRINTF("On try %d, sending query to port %d\n",
@@ -1184,9 +1336,9 @@ int attribute_hidden __dns_lookup(const char *name, int type,
/* retry */
/*continue; */
}
- DPRINTF("Xmit packet len:%d id:%d qr:%d\n", len, h.id, h.qr);
+ DPRINTF("Xmit packet len:%d id:%d qr:%d\n", packet_len, h.id, h.qr);
/* no error check - if it fails, we time out on recv */
- send(fd, packet, len, 0);
+ send(fd, packet, packet_len, 0);
#ifdef USE_SELECT
/*TODO: use _res.retrans*/
@@ -1217,8 +1369,28 @@ int attribute_hidden __dns_lookup(const char *name, int type,
/*TODO: better timeout accounting?*/
reply_timeout -= 1000;
#endif
- len = recv(fd, packet, PACKETSZ, MSG_DONTWAIT);
- if (len < HFIXEDSZ) {
+
+/* vda: a bogus response seen in real world (caused SEGV in uclibc):
+ * "ping www.google.com" sending AAAA query and getting
+ * response with one answer... with answer part missing!
+ * Fixed by thorough checks for not going past the packet's end.
+ */
+#ifdef DEBUG
+ {
+ static const char test_query[32] = "\0\2\1\0\0\1\0\0\0\0\0\0\3www\6google\3com\0\0\34\0\1";
+ static const char test_respn[32] = "\0\2\201\200\0\1\0\1\0\0\0\0\3www\6google\3com\0\0\34\0\1";
+ pos = memcmp(packet + 2, test_query + 2, 30);
+ packet_len = recv(fd, packet, PACKETSZ, MSG_DONTWAIT);
+ if (pos == 0) {
+ packet_len = 32;
+ memcpy(packet + 2, test_respn + 2, 30);
+ }
+ }
+#else
+ packet_len = recv(fd, packet, PACKETSZ, MSG_DONTWAIT);
+#endif
+
+ if (packet_len < HFIXEDSZ) {
/* too short!
* it's just a bogus packet from somewhere */
bogus_packet:
@@ -1227,7 +1399,7 @@ int attribute_hidden __dns_lookup(const char *name, int type,
goto try_next_server;
}
__decode_header(packet, &h);
- DPRINTF("id = %d, qr = %d\n", h.id, h.qr);
+ DPRINTF("len:%d id:%d qr:%d\n", packet_len, h.id, h.qr);
if (h.id != local_id || !h.qr) {
/* unsolicited */
goto bogus_packet;
@@ -1266,22 +1438,26 @@ int attribute_hidden __dns_lookup(const char *name, int type,
/* Code below won't work correctly with h.ancount == 0, so... */
if (h.ancount <= 0) {
- h_errno = NO_DATA; /* is this correct code? */
+ h_errno = NO_DATA; /* [is this correct code to check for?] */
goto fail1;
}
pos = HFIXEDSZ;
for (j = 0; j < h.qdcount; j++) {
DPRINTF("Skipping question %d at %d\n", j, pos);
- /* returns -1 only if packet == NULL (can't happen) */
- i = __length_question(packet, pos);
- DPRINTF("Length of question %d is %d\n", j, i);
+ i = __length_question(packet + pos, packet_len - pos);
+ if (i < 0) {
+ DPRINTF("Packet'question section "
+ "is truncated, trying next server\n");
+ goto try_next_server;
+ }
pos += i;
+ DPRINTF("Length of question %d is %d\n", j, i);
}
DPRINTF("Decoding answer at pos %d\n", pos);
first_answer = 1;
- for (j = 0; j < h.ancount && pos < len; j++) {
- i = __decode_answer(packet, pos, len, &ma);
+ for (j = 0; j < h.ancount; j++) {
+ i = __decode_answer(packet, pos, packet_len, &ma);
if (i < 0) {
DPRINTF("failed decode %d\n", i);
/* If the message was truncated but we have
@@ -1333,7 +1509,7 @@ int attribute_hidden __dns_lookup(const char *name, int type,
else
free(packet);
free(lookup);
- return len;
+ return packet_len;
try_next_server:
/* Try next nameserver */
@@ -1366,13 +1542,13 @@ FILE * __open_etc_hosts(void)
int attribute_hidden __read_etc_hosts_r(
FILE * fp,
- const char * name,
+ const char *name,
int type,
enum etc_hosts_action action,
- struct hostent * result_buf,
- char * buf, size_t buflen,
- struct hostent ** result,
- int * h_errnop)
+ struct hostent *result_buf,
+ char *buf, size_t buflen,
+ struct hostent **result,
+ int *h_errnop)
{
struct in_addr **addr_list = NULL;
struct in_addr *in = NULL;
@@ -1509,11 +1685,13 @@ int attribute_hidden __read_etc_hosts_r(
#ifdef L_get_hosts_byname_r
-int attribute_hidden __get_hosts_byname_r(const char * name, int type,
- struct hostent * result_buf,
- char * buf, size_t buflen,
- struct hostent ** result,
- int * h_errnop)
+int attribute_hidden __get_hosts_byname_r(const char *name,
+ int type,
+ struct hostent *result_buf,
+ char *buf,
+ size_t buflen,
+ struct hostent **result,
+ int *h_errnop)
{
return __read_etc_hosts_r(NULL, name, type, GET_HOSTS_BYNAME,
result_buf, buf, buflen, result, h_errnop);
@@ -1523,11 +1701,14 @@ int attribute_hidden __get_hosts_byname_r(const char * name, int type,
#ifdef L_get_hosts_byaddr_r
-int attribute_hidden __get_hosts_byaddr_r(const char * addr, int len, int type,
- struct hostent * result_buf,
- char * buf, size_t buflen,
- struct hostent ** result,
- int * h_errnop)
+int attribute_hidden __get_hosts_byaddr_r(const char *addr,
+ int len,
+ int type,
+ struct hostent *result_buf,
+ char *buf,
+ size_t buflen,
+ struct hostent **result,
+ int *h_errnop)
{
#ifndef __UCLIBC_HAS_IPV6__
char ipaddr[INET_ADDRSTRLEN];
@@ -1562,9 +1743,13 @@ int attribute_hidden __get_hosts_byaddr_r(const char * addr, int len, int type,
#ifdef L_getnameinfo
-int getnameinfo(const struct sockaddr *sa, socklen_t addrlen, char *host,
- socklen_t hostlen, char *serv, socklen_t servlen,
- unsigned int flags)
+int getnameinfo(const struct sockaddr *sa,
+ socklen_t addrlen,
+ char *host,
+ socklen_t hostlen,
+ char *serv,
+ socklen_t servlen,
+ unsigned flags)
{
int serrno = errno;
unsigned ok;
@@ -1786,12 +1971,12 @@ libc_hidden_def(getnameinfo)
*
* When examples were run, /etc/resolv.conf contained "search com" line.
*/
-int gethostbyname_r(const char * name,
- struct hostent * result_buf,
- char * buf,
+int gethostbyname_r(const char *name,
+ struct hostent *result_buf,
+ char *buf,
size_t buflen,
- struct hostent ** result,
- int * h_errnop)
+ struct hostent **result,
+ int *h_errnop)
{
struct in_addr **addr_list;
char **alias;
@@ -1799,6 +1984,7 @@ int gethostbyname_r(const char * name,
unsigned char *packet;
struct resolv_answer a;
int i;
+ int packet_len;
int wrong_af = 0;
*result = NULL;
@@ -1898,8 +2084,8 @@ int gethostbyname_r(const char * name,
* we'll need space of one in_addr + two addr_list[] elems */
a.buflen = buflen - ((sizeof(addr_list[0]) * 2 + sizeof(struct in_addr)));
a.add_count = 0;
- i = __dns_lookup(name, T_A, &packet, &a);
- if (i < 0) {
+ packet_len = __dns_lookup(name, T_A, &packet, &a);
+ if (packet_len < 0) {
*h_errnop = HOST_NOT_FOUND;
DPRINTF("__dns_lookup returned < 0\n");
return TRY_AGAIN;
@@ -1975,11 +2161,11 @@ libc_hidden_def(gethostbyname_r)
int gethostbyname2_r(const char *name,
int family,
- struct hostent * result_buf,
- char * buf,
+ struct hostent *result_buf,
+ char *buf,
size_t buflen,
- struct hostent ** result,
- int * h_errnop)
+ struct hostent **result,
+ int *h_errnop)
{
#ifndef __UCLIBC_HAS_IPV6__
return family == (AF_INET)
@@ -2080,9 +2266,11 @@ int gethostbyname2_r(const char *name,
/* TODO: why it's so different from gethostbyname_r (IPv4 case)? */
memset(&a, '\0', sizeof(a));
for (;;) {
+ int packet_len;
+
/* Hmm why we memset(a) to zeros only once? */
- i = __dns_lookup(buf, T_AAAA, &packet, &a);
- if (i < 0) {
+ packet_len = __dns_lookup(buf, T_AAAA, &packet, &a);
+ if (packet_len < 0) {
*h_errnop = HOST_NOT_FOUND;
return TRY_AGAIN;
}
@@ -2097,7 +2285,7 @@ int gethostbyname2_r(const char *name,
*h_errnop = NO_RECOVERY;
return -1;
}
- i = __decode_dotted(packet, a.rdoffset, buf, buflen);
+ i = __decode_dotted(packet, a.rdoffset, packet_len, buf, buflen);
free(packet);
if (i < 0) {
*h_errnop = NO_RECOVERY;
@@ -2128,11 +2316,12 @@ libc_hidden_def(gethostbyname2_r)
#ifdef L_gethostbyaddr_r
-int gethostbyaddr_r(const void *addr, socklen_t addrlen, int type,
- struct hostent * result_buf,
- char * buf, size_t buflen,
- struct hostent ** result,
- int * h_errnop)
+int gethostbyaddr_r(const void *addr, socklen_t addrlen,
+ int type,
+ struct hostent *result_buf,
+ char *buf, size_t buflen,
+ struct hostent **result,
+ int *h_errnop)
{
struct in_addr *in;
@@ -2141,6 +2330,7 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, int type,
unsigned char *packet;
struct resolv_answer a;
int i;
+ int packet_len;
int nest = 0;
*result = NULL;
@@ -2235,8 +2425,8 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, int type,
memset(&a, '\0', sizeof(a));
for (;;) {
/* Hmm why we memset(a) to zeros only once? */
- i = __dns_lookup(buf, T_PTR, &packet, &a);
- if (i < 0) {
+ packet_len = __dns_lookup(buf, T_PTR, &packet, &a);
+ if (packet_len < 0) {
*h_errnop = HOST_NOT_FOUND;
return TRY_AGAIN;
}
@@ -2252,7 +2442,7 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, int type,
return -1;
}
/* Decode CNAME into buf, feed it to __dns_lookup() again */
- i = __decode_dotted(packet, a.rdoffset, buf, buflen);
+ i = __decode_dotted(packet, a.rdoffset, packet_len, buf, buflen);
free(packet);
if (i < 0) {
*h_errnop = NO_RECOVERY;
@@ -2261,7 +2451,7 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, int type,
}
if (a.atype == T_PTR) { /* ADDRESS */
- i = __decode_dotted(packet, a.rdoffset, buf, buflen);
+ i = __decode_dotted(packet, a.rdoffset, packet_len, buf, buflen);
free(packet);
result_buf->h_name = buf;
result_buf->h_addrtype = type;
@@ -2807,7 +2997,7 @@ int res_query(const char *dname, int class, int type,
unsigned char *answer, int anslen)
{
int i;
- unsigned char * packet = NULL;
+ unsigned char *packet = NULL;
struct resolv_answer a;
if (!dname || class != 1 /* CLASS_IN */) {
@@ -2849,7 +3039,8 @@ libc_hidden_def(res_query)
int res_search(const char *name, int class, int type, u_char *answer,
int anslen)
{
- const char *cp, * const *domain;
+ const char *cp;
+ char **domain;
HEADER *hp = (HEADER *)(void *)answer;
unsigned dots;
unsigned state;
@@ -2908,9 +3099,7 @@ int res_search(const char *name, int class, int type, u_char *answer,
) {
bool done = 0;
- for (domain = (const char * const *)_res_dnsrch;
- *domain && !done;
- domain++) {
+ for (domain = _res_dnsrch; *domain && !done; domain++) {
ret = res_querydomain(name, *domain, class, type,
answer, anslen);
@@ -2998,7 +3187,7 @@ int res_search(const char *name, int class, int type, u_char *answer,
* removing a trailing dot from name if domain is NULL.
*/
int res_querydomain(const char *name, const char *domain, int class, int type,
- u_char * answer, int anslen)
+ u_char *answer, int anslen)
{
char nbuf[MAXDNAME];
const char *longname = nbuf;
@@ -3019,7 +3208,7 @@ int res_querydomain(const char *name, const char *domain, int class, int type,
__UCLIBC_MUTEX_UNLOCK(__resolv_lock);
if (!(_res_options & RES_INIT)) {
res_init(); /* our res_init never fails */
- goto again:
+ goto again;
}
if (_res_options & RES_DEBUG)
printf(";; res_querydomain(%s, %s, %d, %d)\n",