@ -87,6 +87,7 @@ static int open_udp_socket(ares_channel channel, struct server_state *server);
static int same_questions ( const unsigned char * qbuf , int qlen ,
static int same_questions ( const unsigned char * qbuf , int qlen ,
const unsigned char * abuf , int alen ) ;
const unsigned char * abuf , int alen ) ;
static int same_address ( struct sockaddr * sa , struct ares_addr * aa ) ;
static int same_address ( struct sockaddr * sa , struct ares_addr * aa ) ;
static int has_opt_rr ( const unsigned char * abuf , int alen ) ;
static void end_query ( ares_channel channel , struct query * query , int status ,
static void end_query ( ares_channel channel , struct query * query , int status ,
unsigned char * abuf , int alen ) ;
unsigned char * abuf , int alen ) ;
@ -608,14 +609,14 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
return ;
return ;
packetsz = PACKETSZ ;
packetsz = PACKETSZ ;
/* If we use EDNS and server answers with one of these RCODES , the protocol
/* If we use EDNS and server answers with FORMERR without an OPT RR , the protocol
* extension is not understood by the responder . We must retry the query
* extension is not understood by the responder . We must retry the query
* without EDNS enabled .
* without EDNS enabled .
*/
*/
if ( channel - > flags & ARES_FLAG_EDNS )
if ( channel - > flags & ARES_FLAG_EDNS )
{
{
packetsz = channel - > ednspsz ;
packetsz = channel - > ednspsz ;
if ( rcode = = NOTIMP | | rcode = = FORMERR | | rcode = = SERVFAIL )
if ( rcode = = FORMERR & & has_opt_rr ( abuf , alen ) ! = 1 )
{
{
int qlen = ( query - > tcplen - 2 ) - EDNSFIXEDSZ ;
int qlen = ( query - > tcplen - 2 ) - EDNSFIXEDSZ ;
channel - > flags ^ = ARES_FLAG_EDNS ;
channel - > flags ^ = ARES_FLAG_EDNS ;
@ -1354,6 +1355,83 @@ static int same_address(struct sockaddr *sa, struct ares_addr *aa)
return 0 ; /* different */
return 0 ; /* different */
}
}
/* search for an OPT RR in the response */
static int has_opt_rr ( const unsigned char * abuf , int alen )
{
unsigned int qdcount , ancount , nscount , arcount , i ;
const unsigned char * aptr ;
int status ;
if ( alen < HFIXEDSZ )
return - 1 ;
/* Parse the answer header. */
qdcount = DNS_HEADER_QDCOUNT ( abuf ) ;
ancount = DNS_HEADER_ANCOUNT ( abuf ) ;
nscount = DNS_HEADER_NSCOUNT ( abuf ) ;
arcount = DNS_HEADER_ARCOUNT ( abuf ) ;
aptr = abuf + HFIXEDSZ ;
/* skip the questions */
for ( i = 0 ; i < qdcount ; i + + )
{
char * name ;
long len ;
status = ares_expand_name ( aptr , abuf , alen , & name , & len ) ;
if ( status ! = ARES_SUCCESS )
return - 1 ;
ares_free_string ( name ) ;
if ( aptr + len + QFIXEDSZ > abuf + alen )
return - 1 ;
aptr + = len + QFIXEDSZ ;
}
/* skip the ancount and nscount */
for ( i = 0 ; i < ancount + nscount ; i + + )
{
char * name ;
long len ;
status = ares_expand_name ( aptr , abuf , alen , & name , & len ) ;
if ( status ! = ARES_SUCCESS )
return - 1 ;
ares_free_string ( name ) ;
if ( aptr + len + RRFIXEDSZ > abuf + alen )
return - 1 ;
aptr + = len ;
int dlen = DNS_RR_LEN ( aptr ) ;
aptr + = RRFIXEDSZ ;
if ( aptr + dlen > abuf + alen )
return - 1 ;
aptr + = dlen ;
}
/* search for rr type (41) - opt */
for ( i = 0 ; i < arcount ; i + + )
{
char * name ;
long len ;
status = ares_expand_name ( aptr , abuf , alen , & name , & len ) ;
if ( status ! = ARES_SUCCESS )
return - 1 ;
ares_free_string ( name ) ;
if ( aptr + len + RRFIXEDSZ > abuf + alen )
return - 1 ;
aptr + = len ;
if ( DNS_RR_TYPE ( aptr ) = = ns_t_opt )
return 1 ;
int dlen = DNS_RR_LEN ( aptr ) ;
aptr + = RRFIXEDSZ ;
if ( aptr + dlen > abuf + alen )
return - 1 ;
aptr + = dlen ;
}
return 0 ;
}
static void end_query ( ares_channel channel , struct query * query , int status ,
static void end_query ( ares_channel channel , struct query * query , int status ,
unsigned char * abuf , int alen )
unsigned char * abuf , int alen )
{
{