Ignore:
Timestamp:
Jun 20, 2016 10:10:42 AM (3 years ago)
Author:
nanang
Message:

Close #1927: IPv6 support in DNS SRV:

  • support DNS A and AAAA resolution for each target in DNS SRV record
  • support fallback to DNS A and DNS AAAA resolution when DNS SRV record is not available
  • support IPv6 nameservers.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib-util/src/pjlib-util/srv_resolver.c

    r5311 r5349  
    3838}; 
    3939 
     40#pragma pack(1) 
    4041struct srv_target 
    4142{ 
    4243    struct common           common; 
     44    struct common           common_aaaa; 
    4345    pj_dns_srv_async_query *parent; 
    4446    pj_str_t                target_name; 
    4547    pj_dns_async_query     *q_a; 
     48    pj_dns_async_query     *q_aaaa; 
    4649    char                    target_buf[PJ_MAX_HOSTNAME]; 
    4750    pj_str_t                cname; 
    4851    char                    cname_buf[PJ_MAX_HOSTNAME]; 
    49     unsigned                port; 
     52    pj_uint16_t             port; 
    5053    unsigned                priority; 
    5154    unsigned                weight; 
    5255    unsigned                sum; 
    5356    unsigned                addr_cnt; 
    54     pj_in_addr              addr[ADDR_MAX_COUNT]; 
     57    pj_sockaddr             addr[ADDR_MAX_COUNT];/**< Address family and IP.*/ 
    5558}; 
     59#pragma pack() 
    5660 
    5761struct pj_dns_srv_async_query 
     
    136140    query_job->def_port = (pj_uint16_t)def_port; 
    137141 
     142    /* Normalize query job option PJ_DNS_SRV_RESOLVE_AAAA_ONLY */ 
     143    if (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) 
     144        query_job->option |= PJ_DNS_SRV_RESOLVE_AAAA; 
     145 
    138146    /* Start the asynchronous query_job */ 
    139147 
     
    177185            pj_dns_resolver_cancel_query(srv->q_a, PJ_FALSE); 
    178186            srv->q_a = NULL; 
     187            has_pending = PJ_TRUE; 
     188        } 
     189        if (srv->q_aaaa) { 
     190            pj_dns_resolver_cancel_query(srv->q_aaaa, PJ_FALSE); 
     191            srv->q_aaaa = NULL; 
    179192            has_pending = PJ_TRUE; 
    180193        } 
     
    315328    } 
    316329 
    317     /* Check for Additional Info section if A records are available, and 
    318      * fill in the IP address (so that we won't need to resolve the A  
     330    /* Check for Additional Info section if A/AAAA records are available, and 
     331     * fill in the IP address (so that we won't need to resolve the A/AAAA  
    319332     * record with another DNS query_job).  
    320333     */ 
     
    323336        unsigned j; 
    324337 
    325         if (rr->type != PJ_DNS_TYPE_A) 
     338        /* Skip non-A/AAAA record */ 
     339        if (rr->type != PJ_DNS_TYPE_A && rr->type != PJ_DNS_TYPE_AAAA) 
    326340            continue; 
    327341 
    328         /* Yippeaiyee!! There is an "A" record!  
     342        /* Also skip if: 
     343         * - it is A record and app only want AAAA record, or 
     344         * - it is AAAA record and app does not want AAAA record 
     345         */ 
     346        if ((rr->type == PJ_DNS_TYPE_A && 
     347            (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)!=0) || 
     348            (rr->type == PJ_DNS_TYPE_AAAA && 
     349            (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)==0)) 
     350        { 
     351            continue; 
     352        }            
     353 
     354        /* Yippeaiyee!! There is an "A/AAAA" record!  
    329355         * Update the IP address of the corresponding SRV record. 
    330356         */ 
    331357        for (j=0; j<query_job->srv_cnt; ++j) { 
    332             if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0 && 
    333                 query_job->srv[j].addr_cnt < ADDR_MAX_COUNT) 
    334             { 
     358            if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0 
     359                && query_job->srv[j].addr_cnt < ADDR_MAX_COUNT) 
     360            { 
    335361                unsigned cnt = query_job->srv[j].addr_cnt; 
    336                 query_job->srv[j].addr[cnt].s_addr = rr->rdata.a.ip_addr.s_addr; 
     362                if (rr->type == PJ_DNS_TYPE_A) { 
     363                    pj_sockaddr_init(pj_AF_INET(), 
     364                                        &query_job->srv[j].addr[cnt], NULL, 
     365                                        query_job->srv[j].port); 
     366                    query_job->srv[j].addr[cnt].ipv4.sin_addr = 
     367                                                rr->rdata.a.ip_addr; 
     368                } else { 
     369                    pj_sockaddr_init(pj_AF_INET6(), 
     370                                        &query_job->srv[j].addr[cnt], NULL, 
     371                                        query_job->srv[j].port); 
     372                    query_job->srv[j].addr[cnt].ipv6.sin6_addr = 
     373                                                rr->rdata.aaaa.ip_addr; 
     374                } 
     375 
    337376                /* Only increment host_resolved once per SRV record */ 
    338377                if (query_job->srv[j].addr_cnt == 0) 
    339378                    ++query_job->host_resolved; 
     379 
    340380                ++query_job->srv[j].addr_cnt; 
    341381                break; 
     
    355395        } 
    356396        */ 
     397         
    357398    } 
    358399 
     
    363404    for (i=0; i<query_job->srv_cnt; ++i) { 
    364405        pj_in_addr addr; 
     406        pj_in6_addr addr6; 
    365407 
    366408        if (query_job->srv[i].addr_cnt != 0) { 
     
    369411        } 
    370412 
    371         if (pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name, 
     413        if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)==0 && 
     414            pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name, 
    372415                         &addr) == PJ_SUCCESS) 
    373416        { 
    374             query_job->srv[i].addr[query_job->srv[i].addr_cnt++] = addr; 
     417            unsigned cnt = query_job->srv[i].addr_cnt; 
     418            pj_sockaddr_init(pj_AF_INET(), &query_job->srv[i].addr[cnt], 
     419                             NULL, query_job->srv[i].port); 
     420            query_job->srv[i].addr[cnt].ipv4.sin_addr = addr; 
     421            ++query_job->srv[i].addr_cnt; 
     422            ++query_job->host_resolved; 
     423        } else if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)!=0 && 
     424                   pj_inet_pton(pj_AF_INET6(), &query_job->srv[i].target_name, 
     425                                &addr6) == PJ_SUCCESS) 
     426        { 
     427            unsigned cnt = query_job->srv[i].addr_cnt; 
     428            pj_sockaddr_init(pj_AF_INET6(), &query_job->srv[i].addr[cnt], 
     429                             NULL, query_job->srv[i].port); 
     430            query_job->srv[i].addr[cnt].ipv6.sin6_addr = addr6; 
     431            ++query_job->srv[i].addr_cnt; 
    375432            ++query_job->host_resolved; 
    376433        } 
     
    388445 
    389446    for (i=0; i<query_job->srv_cnt; ++i) { 
    390         char addr[PJ_INET_ADDRSTRLEN]; 
     447        char addr[PJ_INET6_ADDRSTRLEN]; 
    391448 
    392449        if (query_job->srv[i].addr_cnt != 0) { 
    393             pj_inet_ntop(pj_AF_INET(), &query_job->srv[i].addr[0], 
    394                          addr, sizeof(addr)); 
     450            pj_sockaddr_print(&query_job->srv[i].addr[0], 
     451                         addr, sizeof(addr), 2); 
    395452        } else 
    396453            pj_ansi_strcpy(addr, "-"); 
     
    408465 
    409466 
    410 /* Start DNS A record queries for all SRV records in the query_job structure */ 
     467/* Start DNS A and/or AAAA record queries for all SRV records in 
     468 * the query_job structure. 
     469 */ 
    411470static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job) 
    412471{ 
     
    415474 
    416475    query_job->dns_state = PJ_DNS_TYPE_A; 
     476 
    417477    for (i=0; i<query_job->srv_cnt; ++i) { 
    418478        struct srv_target *srv = &query_job->srv[i]; 
     
    424484 
    425485        srv->common.type = PJ_DNS_TYPE_A; 
     486        srv->common_aaaa.type = PJ_DNS_TYPE_AAAA; 
    426487        srv->parent = query_job; 
     488 
     489        status = PJ_SUCCESS; 
     490 
     491        /* Start DNA A record query */ 
     492        if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) == 0) 
     493        { 
     494            status = pj_dns_resolver_start_query(query_job->resolver, 
     495                                                 &srv->target_name, 
     496                                                 PJ_DNS_TYPE_A, 0, 
     497                                                 &dns_callback, 
     498                                                 &srv->common, &srv->q_a); 
     499        } 
     500 
     501        /* Start DNA AAAA record query */ 
     502        if (status == PJ_SUCCESS && 
     503            (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0) 
     504        { 
     505            status = pj_dns_resolver_start_query(query_job->resolver, 
     506                                                 &srv->target_name, 
     507                                                 PJ_DNS_TYPE_AAAA, 0, 
     508                                                 &dns_callback, 
     509                                                 &srv->common_aaaa, &srv->q_aaaa); 
     510        } 
    427511 
    428512        /* See also #1809: dns_callback() will be invoked synchronously when response 
     
    431515         * returning false error, so don't use that variable for counting errors. 
    432516         */ 
    433         status = pj_dns_resolver_start_query(query_job->resolver, 
    434                                              &srv->target_name, 
    435                                              PJ_DNS_TYPE_A, 0, 
    436                                              &dns_callback, 
    437                                              srv, &srv->q_a); 
    438517        if (status != PJ_SUCCESS) { 
    439518            query_job->host_resolved++; 
     
    465544        srv = (struct srv_target*) common; 
    466545        query_job = srv->parent; 
     546    } else if (common->type == PJ_DNS_TYPE_AAAA) { 
     547        srv = (struct srv_target*)((pj_int8_t*)common-sizeof(struct common)); 
     548        query_job = srv->parent; 
    467549    } else { 
    468550        pj_assert(!"Unexpected user data!"); 
     
    475557        /* We are getting SRV response */ 
    476558 
     559        /* Clear the outstanding job */ 
    477560        query_job->q_srv = NULL; 
    478561 
     
    508591         */ 
    509592        if (query_job->srv_cnt == 0) { 
     593            unsigned new_option = 0; 
     594 
    510595            /* Looks like we aren't getting any SRV responses. 
    511596             * Resolve the original target as A record by creating a  
     
    514599            PJ_LOG(4, (query_job->objname,  
    515600                       "DNS SRV resolution failed for %.*s, trying " 
    516                        "resolving A record for %.*s", 
     601                       "resolving A/AAAA record for %.*s", 
    517602                       (int)query_job->full_name.slen,  
    518603                       query_job->full_name.ptr, 
     
    527612            query_job->srv[i].weight = 0; 
    528613            query_job->srv[i].port = query_job->def_port; 
    529         }  
     614 
     615            /* Update query_job resolution option based on fallback option */ 
     616            if (query_job->option & PJ_DNS_SRV_FALLBACK_AAAA) 
     617                new_option |= (PJ_DNS_SRV_RESOLVE_AAAA | 
     618                               PJ_DNS_SRV_RESOLVE_AAAA_ONLY); 
     619            if (query_job->option & PJ_DNS_SRV_FALLBACK_A) 
     620                new_option &= (~PJ_DNS_SRV_RESOLVE_AAAA_ONLY); 
     621             
     622            query_job->option = new_option; 
     623        } 
    530624         
    531625 
    532         /* Resolve server hostnames (DNS A record) for hosts which don't have 
    533          * A record yet. 
     626        /* Resolve server hostnames (DNS A/AAAA record) for hosts which 
     627         * don't have A/AAAA record yet. 
    534628         */ 
    535629        if (query_job->host_resolved != query_job->srv_cnt) { 
     
    545639 
    546640    } else if (query_job->dns_state == PJ_DNS_TYPE_A) { 
    547  
    548         /* Clear the outstanding job */ 
    549         srv->q_a = NULL; 
     641        pj_bool_t is_type_a, srv_completed; 
     642 
     643        /* Clear outstanding job */ 
     644        if (common->type == PJ_DNS_TYPE_A) { 
     645            srv_completed = (srv->q_aaaa == NULL); 
     646            srv->q_a = NULL; 
     647        } else if (common->type == PJ_DNS_TYPE_AAAA) { 
     648            srv_completed = (srv->q_a == NULL); 
     649            srv->q_aaaa = NULL; 
     650        } else { 
     651            pj_assert(!"Unexpected job type"); 
     652            query_job->last_error = status = PJ_EINVALIDOP; 
     653            goto on_error; 
     654        } 
     655 
     656        is_type_a = (common->type == PJ_DNS_TYPE_A); 
    550657 
    551658        /* Check that we really have answer */ 
    552659        if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) { 
    553             char addr[PJ_INET_ADDRSTRLEN]; 
    554             pj_dns_a_record rec; 
     660            char addr[PJ_INET6_ADDRSTRLEN]; 
     661            pj_dns_addr_record rec; 
    555662 
    556663            /* Parse response */ 
    557             status = pj_dns_parse_a_response(pkt, &rec); 
     664            status = pj_dns_parse_addr_response(pkt, &rec); 
    558665            if (status != PJ_SUCCESS) 
    559666                goto on_error; 
     
    562669 
    563670            /* Update CNAME alias, if present. */ 
    564             if (rec.alias.slen) { 
     671            if (srv->cname.slen==0 && rec.alias.slen) { 
    565672                pj_assert(rec.alias.slen <= (int)sizeof(srv->cname_buf)); 
    566673                srv->cname.ptr = srv->cname_buf; 
    567674                pj_strcpy(&srv->cname, &rec.alias); 
    568             } else { 
    569                 srv->cname.slen = 0; 
     675            //} else { 
     676                //srv->cname.slen = 0; 
    570677            } 
    571678 
    572679            /* Update IP address of the corresponding hostname or CNAME */ 
    573             if (srv->addr_cnt < ADDR_MAX_COUNT) { 
    574                 srv->addr[srv->addr_cnt++].s_addr = rec.addr[0].s_addr; 
    575  
    576                 PJ_LOG(5,(query_job->objname,  
    577                           "DNS A for %.*s: %s", 
    578                           (int)srv->target_name.slen,  
    579                           srv->target_name.ptr, 
    580                           pj_inet_ntop2(pj_AF_INET(), &rec.addr[0], 
    581                                         addr, sizeof(addr)))); 
    582             } 
    583  
    584             /* Check for multiple IP addresses */ 
    585             for (i=1; i<rec.addr_count && srv->addr_cnt < ADDR_MAX_COUNT; ++i) 
     680            for (i=0; i<rec.addr_count && srv->addr_cnt<ADDR_MAX_COUNT; ++i) 
    586681            { 
    587                 srv->addr[srv->addr_cnt++].s_addr = rec.addr[i].s_addr; 
    588  
    589                 PJ_LOG(5,(query_job->objname,  
    590                           "Additional DNS A for %.*s: %s", 
    591                           (int)srv->target_name.slen,  
    592                           srv->target_name.ptr, 
    593                           pj_inet_ntop2(pj_AF_INET(), &rec.addr[i], 
    594                                         addr, sizeof(addr)))); 
     682                pj_bool_t added = PJ_FALSE; 
     683 
     684                if (is_type_a && rec.addr[i].af == pj_AF_INET()) { 
     685                    pj_sockaddr_init(pj_AF_INET(), &srv->addr[srv->addr_cnt], 
     686                                     NULL, srv->port); 
     687                    srv->addr[srv->addr_cnt].ipv4.sin_addr = 
     688                                     rec.addr[i].ip.v4; 
     689                    added = PJ_TRUE; 
     690                } else if (!is_type_a && rec.addr[i].af == pj_AF_INET6()) { 
     691                    pj_sockaddr_init(pj_AF_INET6(), &srv->addr[srv->addr_cnt], 
     692                                     NULL, srv->port); 
     693                    srv->addr[srv->addr_cnt].ipv6.sin6_addr = 
     694                                     rec.addr[i].ip.v6; 
     695                    added = PJ_TRUE; 
     696                } else { 
     697                    /* Mismatched address family, e.g: getting IPv6 address in 
     698                     * DNS A query resolution. 
     699                     */ 
     700                    PJ_LOG(4,(query_job->objname,  
     701                              "Bad address family in DNS %s query for %.*s", 
     702                              (is_type_a? "A" : "AAAA"), 
     703                              (int)srv->target_name.slen,  
     704                              srv->target_name.ptr)); 
     705                } 
     706 
     707                if (added) { 
     708                    PJ_LOG(5,(query_job->objname,  
     709                              "DNS %s for %.*s: %s", 
     710                              (is_type_a? "A" : "AAAA"), 
     711                              (int)srv->target_name.slen,  
     712                              srv->target_name.ptr, 
     713                              pj_sockaddr_print(&srv->addr[srv->addr_cnt], 
     714                                                addr, sizeof(addr), 2))); 
     715 
     716                    ++srv->addr_cnt; 
     717                } 
    595718            } 
    596719 
     
    603726            /* Log error */ 
    604727            pj_strerror(status, errmsg, sizeof(errmsg)); 
    605             PJ_LOG(4,(query_job->objname, "DNS A record resolution failed: %s",  
     728            PJ_LOG(4,(query_job->objname, 
     729                      "DNS %s record resolution failed: %s", 
     730                      (is_type_a? "A" : "AAAA"), 
    606731                      errmsg)); 
    607732        } 
    608733 
    609         ++query_job->host_resolved; 
     734        /* Increment host resolved count when both DNS A and AAAA record 
     735         * queries for this server are completed. 
     736         */ 
     737        if (srv_completed) 
     738            ++query_job->host_resolved; 
    610739 
    611740    } else { 
     
    624753            unsigned j; 
    625754            struct srv_target *srv2 = &query_job->srv[i]; 
     755            pj_dns_addr_record *s = &srv_rec.entry[srv_rec.count].server; 
    626756 
    627757            srv_rec.entry[srv_rec.count].priority = srv2->priority; 
     
    635765            pj_assert(srv2->addr_cnt <= PJ_DNS_MAX_IP_IN_A_REC); 
    636766 
    637             for (j=0; j<srv2->addr_cnt; ++j) { 
    638                 srv_rec.entry[srv_rec.count].server.addr[j].s_addr =  
    639                     srv2->addr[j].s_addr; 
    640                 ++srv_rec.entry[srv_rec.count].server.addr_count; 
     767            for (j=0; j<srv2->addr_cnt; ++j) {           
     768                s->addr[j].af = srv2->addr[j].addr.sa_family; 
     769                if (s->addr[j].af == pj_AF_INET()) 
     770                    s->addr[j].ip.v4 = srv2->addr[j].ipv4.sin_addr; 
     771                else 
     772                    s->addr[j].ip.v6 = srv2->addr[j].ipv6.sin6_addr; 
     773                ++s->addr_count; 
    641774            } 
    642775 
     
    681814                  status, 
    682815                  pj_strerror(status,errmsg,sizeof(errmsg)).ptr)); 
     816 
     817        /* Cancel any pending query */ 
     818        pj_dns_srv_cancel_query(query_job, PJ_FALSE); 
     819 
    683820        (*query_job->cb)(query_job->token, status, NULL); 
    684821        return; 
Note: See TracChangeset for help on using the changeset viewer.