使用api指定DNS去解析域名-linux c代码

 

背景需求:使用指定dns服务器去解析域名可以使用”nslookup www.baidu.com  114.114.114.114“,但如果使用api接口实现则没有现成的代码。

DNS协议参考:http://c.biancheng.net/view/6457.html

简易方法: nslookup www.baidu.com 114.114.114.114|grep Address: |awk '{print $2}'|sed -n '2p'

实现方法:向指定的DNS服务器发送UDP数据,然后将返回的数据按照DNS协议进行解析。

参考网友代码实现使用指定dns ip去解析域名。基本功能已经完成,健壮性需要再调试。

/**
 * Parse data chunk into dns name
 * @param domain      域名
 * @param dnsip         dnsip   目前支持ipv4的dnsip
 * @param flag            解析ip的类型。4:ipv4 ;   6:ipv6
 * @param resolved    解析结果
 */

int nslookup_domain(char* domain, char* dnsip, int flag, char* resolved_output)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#define DNS_HOST  0x01
#define DNS_CNAME 0x05
#define DNS_IPV6  0x1c

int socketfd;
struct sockaddr_in dest;


static int is_pointer(int in)
{
    return ((in & 0xc0) == 0xc0);
}


int creat_dns_socket( char* dnsip )
{
    socketfd = socket(AF_INET , SOCK_DGRAM , 0);
    if(socketfd < 0)
    {
        perror("create socket failed");
        return -1;
    }
    bzero(&dest , sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(53);
    dest.sin_addr.s_addr = inet_addr(dnsip);
    return 0;
}
static void generate_question(const char *dns_name, int flag, unsigned char *buf , int *len)
{
    char *pos;
    unsigned char *ptr;
    int n;

    *len = 0;
    ptr = buf;
    pos = (char*)dns_name;
    for(;;)
    {
        n = strlen(pos) - (strstr(pos , ".") ? strlen(strstr(pos , ".")) : 0);
        *ptr ++ = (unsigned char)n;
        memcpy(ptr , pos , n);
        *len += n + 1;
        ptr += n;
        if(!strstr(pos , "."))
        {
            *ptr = (unsigned char)0;
            ptr ++;
            *len += 1;
            break;
        }
        pos += n + 1;
    }
    if(flag == 6)
        *((unsigned short*)ptr) = htons(28);  //1 ipv4; 28 ipv6
    else
        *((unsigned short*)ptr) = htons(1);  //1 ipv4; 28 ipv6

    *len += 2;
    ptr += 2;
    *((unsigned short*)ptr) = htons(1);
    *len += 2;
}

static void send_dns_request(const char *domain, int flag)
{

    unsigned char request[256];
    unsigned char *ptr = request;
    unsigned char question[128];
    int question_len;

    generate_question(domain , flag, question , &question_len);

    *((unsigned short*)ptr) = htons(0xff00);  //Transaction ID

    ptr += 2;
    *((unsigned short*)ptr) = htons(0x0100);  //Flags: 0x0100 Standard query

    ptr += 2;
    *((unsigned short*)ptr) = htons(1);       //Questions:1

    ptr += 2;
    *((unsigned short*)ptr) = 0;              //Answer RRs:0

    ptr += 2;
    *((unsigned short*)ptr) = 0;              //Authority RRs:0

    ptr += 2;
    *((unsigned short*)ptr) = 0;              //Additional RRs:0

    ptr += 2;
    memcpy(ptr , question , question_len);

    ptr += question_len;
    sendto(socketfd , request , question_len + 12 , 0, (struct sockaddr*)&dest , sizeof(struct sockaddr));
}

/**
 * Parse data chunk into dns name
 * @param chunk The complete response chunk
 * @param ptr The pointer points to data
 * @param out This will be filled with dns name
 * @param len This will be filled with the length of dns name
 */
static void parse_dns_name(unsigned char *chunk, unsigned char *ptr , char *out , int *len)
{
    int n , flag;
    char *pos = out + (*len);

    for(;;)
    {
        flag = (int)ptr[0];
        if(flag == 0)
            break;
        if(is_pointer(flag))
        {
            n = (int)ptr[1];
            ptr = chunk + n;
            parse_dns_name(chunk , ptr , out , len);
            break;
        }
        else
        {
            ptr ++;
            memcpy(pos , ptr , flag);
            pos += flag;
            ptr += flag;
            *len += flag;
            if((int)ptr[0] != 0)
            {
                memcpy(pos , "." , 1);
                pos += 1;
                (*len) += 1;
            }
        }
    }

}

static void parse_dns_response(char* resolved_output)
{

    unsigned char buf[1024];
    unsigned char *ptr = buf;
    struct sockaddr_in addr;

    int  i , n, flag , querys , answers;
    int type , datalen , len;
    int ttl =0;
    int curttl = 0;
    char cname[128] , aname[128] , ip[64] ;
    unsigned char netip[4];
    unsigned char netip6[24];
    char ipv6[64];
    size_t addr_len = sizeof(struct sockaddr_in);

    n = recvfrom(socketfd , buf , sizeof(buf) , 0, (struct sockaddr*)&addr , (socklen_t*)&addr_len);
    if(n == -1)
    {
        printf("recvfrom is failed.");
        return;
    }

    ptr += 4; /* move ptr to Questions; Transaction ID+Flags */
    querys = ntohs(*((unsigned short*)ptr));

    ptr += 2; /* move ptr to Answer RRs */
    answers = ntohs(*((unsigned short*)ptr));

    ptr += 6; /* move ptr to Querys */

    /* move over Querys */
    for(i= 0 ; i < querys ; i ++)
    {
        for(;;)
        {
            flag = (int)ptr[0];
            ptr += (flag + 1);
            if(flag == 0)
                break;
        }
        ptr += 4;
    }

    /* now ptr points to Answers */
    for(i = 0 ; i < answers ; i ++)
    {
        bzero(aname , sizeof(aname));
        len = 0;
        parse_dns_name(buf , ptr , aname , &len);
        ptr += 2; /* move ptr to Type*/
        type = htons(*((unsigned short*)ptr));
        ptr += 4; /* move ptr to Time to live */
        ttl = htonl(*((unsigned int*)ptr));
        ptr += 4; /* move ptr to Data lenth */
        datalen = ntohs(*((unsigned short*)ptr));
        ptr += 2; /* move ptr to Data*/
        if(type == DNS_CNAME)
        {
            bzero(cname , sizeof(cname));
            len = 0;
            parse_dns_name(buf , ptr , cname , &len);
            // printf("%s is an alias for %s\n" , aname , cname);
            ptr += datalen;
        }
        if(type == DNS_HOST)
        {
            bzero(ip , sizeof(ip));
            if(datalen == 4)
            {
                memcpy(netip , ptr , datalen);
                inet_ntop(AF_INET , netip , ip , sizeof(struct sockaddr));

 

                /* printf("%s has address %s\n" , aname , ip);
                 printf("\tTime to live: %d minutes , %d seconds\n", ttl / 60 , ttl % 60);*/
                if(curttl < ttl)
                {
                    memcpy(resolved_output, ip, strlen(ip));
                    curttl = ttl;
                    /*printf("ip[%s]\n", ip);
                    printf("resolved_output[%s]\n", resolved_output);*/
                }
            }
            ptr += datalen;
        }
        if(type == DNS_IPV6)
        {
            bzero(ipv6 , sizeof(ipv6));
            if(datalen == 16)
            {
                memcpy(netip6 , ptr , datalen);
                inet_ntop(AF_INET6 , netip6 , ipv6 , 46);
                /* printf("%s has address %s\n" , aname , ipv6);
                 printf("\tTime to live: %d minutes , %d seconds\n", ttl / 60 , ttl % 60);*/
                if(curttl < ttl)
                {
                    memcpy(resolved_output, ipv6, strlen(ipv6));
                    curttl = ttl;

                    /*    printf("ipv6[%s]\n", ipv6);
                        printf("resolved_output[%s]\n", resolved_output);*/
                }
            }
            ptr += datalen;
        }

    }
    ptr += 2;
}

int nslookup_domain(char* domain, char* dnsip, int flag, char* resolved_output)
{
    if(domain == NULL || dnsip == NULL || resolved_output == NULL)
    {
        printf("input param is empty.please check it.");
        return -1;
    }

    if( 0 != creat_dns_socket(dnsip) )
    {
        return -1;
    }

    send_dns_request(domain, flag);
    parse_dns_response(resolved_output);
    return 0;
}

int main(int argc , char *argv[])
{
    char domain[] = "www.163.com";
    char dnsip[] = "114.114.114.114";
    char resolved_output[100] = "";
    nslookup_domain(domain, dnsip, 4, resolved_output);
    printf("\nv4[%s]\n", resolved_output);

    memset(resolved_output, 0, sizeof(resolved_output));
    nslookup_domain(domain, dnsip, 6, resolved_output);
    printf("\nv6[%s]\n", resolved_output);

    return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值