UNIX网络编程笔记(7)—名字与地址转换

UNIX网络编程笔记(7)—名字与地址转换

这一章就明显没有前面的TCP和UDP重要了,看得也比较糙,试了几个函数,了解了一些DNS服务器的工作原理,比如递归啊,迭代啊,ISP DNS等等。。。

(一)概述

一般情况下,我们以数值地址表示主机,如127.0.0.1本地回路或206.6.226.33书中的例子,用数值端口号来标识服务器,例如用端口13来标识标准的daytime服务器,本章讲述地名字和数值地址之间的转换,相关的函数有:gethostbynamegethostbyaddr在主机名字与IPv4地址之间的转换等等。


(二)域名系统

域名系统,也就是我们说的DNS,全名叫Domain Name System,可以用于主机名字和IP地址之间的映射。www.baidu.com这样的名字被称作全限定域名(FQDN),我们也可以使用简单的名字,例如localhost等等。

这里有一个小插曲,关于Ubuntu的域名解析系统,这里直接参考Ubuntu nameserver 127.0.1.1

2.1 DNS替代法

不使用DNS也可以直接获取名字和地址信息,常用的替代方法有:

1.静态主机文件 /etc/hosts 文件
2.NIS(Network Information System)网络信息系统。
3.轻权目录访问协议(Lightweight Directory Access Protocol,LDAP)

关于hosts再简单聊一聊关于“墙”的东西,这也是以前了解的一些东西。以前的“墙”做的比较简单,大概是这样的:
GFW会对所有经过骨干出口路由的在UDP53端口(也就是DNS开放端口)的域名进行检测,一旦发现黑名单里的域名,它就会伪装成目标域名的解析服务器给查询者返回虚假结果。由于 UDP是一种无连接不可靠的协议,查询者只能接受最先返回的结果。

以前“墙”还不那么高的时候,可以通过静态主机文件也就是hosts文件编辑主机名/域名 和对应IP这样就绕过了DNS服务器这一层了,不过GFW也是在不断发展的,这个办法已经行不通了,因为http协议是明文的,GFW还会对明文中的关键字过滤掉,而https是加密的,所以https+hosts的办法就可以翻墙了,不过因为GFW的其他一些手段,这个办法仅限教育网IPv6~ LOL

ps:hosts的请求级别比DNS服务器高,所以会优先查host


(三)gethostbyname函数和gethostbyaddr

简单的区别是前者通过主机名得到IP,而后者通过二进制IP得到主机名。

#include <netdb.h>
struct hostent * gethostbyname(const char *hostname);
//如果出错则返回NULL指针

如果调用成功就会返回一个hostent指针,所指向的结构包含查找主机的所有IPv4地址,局限时只能返回IPv4地址。
hostent结构如下:

struct hostent{
    char * h_name;//正式主机名
    char **h_aliases;//别名
    int h_addrtypes;//AF_INET
    int h_length;//4
    char **h_addr_list;//IP
}

h_length的大小是4,这里的4是指4个字节,对于一个点分十进制IPv4地址,比如说:“192.169.1.1”这样一个字符串,在IPv4中以4个整数(二进制来表示),一个字节8位可以表示的范围是0~255,所以只需要4个字节就可以表示了。而对于一个字符串形式的点分十进制ipv4地址,则至少需要16个字节(参考形式xxx.xxx.xxx.xxx还要加一个字节的结束符)。

通过一个图可以了解hostent的结构:

这里写图片描述

gethostbyname的测试代码

参照UNP中的代码,把包裹函数拆开。

//testgethostbyname.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <signal.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
    char * ptr;
    char ** pptr;
    struct hostent *hptr;
    //#define INET_ADDRSTRLEN 16 //xxx.xxx.xxx.xxx
    char str[INET_ADDRSTRLEN];
    while(--argc > 0)
    {
        ++argv;
        ptr=*argv;
        if((hptr=gethostbyname(ptr))==NULL)
        {
            printf("gethostbyname error,NULL ptr returned\r\n");
            return -1;
        }
        printf("official hostname: %s\r\n",hptr->h_name);
        printf("h_length = %d\r\n",hptr->h_length);

        for(pptr=hptr->h_aliases;*pptr != NULL;pptr ++)
        {
            printf("alias: %s\r\n",*pptr);
        }

        if(AF_INET==hptr->h_addrtype)
        {
            pptr=hptr->h_addr_list;
            for(;*pptr!=NULL;pptr++)
            {
                const char * cpstr=inet_ntop(AF_INET,*pptr,str,sizeof(str));
                if(NULL == cpstr)
                {
                    printf("inet_ntop error NULL returned \r\n");
                    return -1;
                }
                printf("address: %s\r\n",cpstr);
            }
        }
        else
        {
            printf("unknown address type\r\n");
            return -1;
        }
    }//end while
    return 0;
}

(四)getservbyname和getservbyport函数

这两个函数就跟端口号相关了,从TCP和UDP编程的例子我们可以知道,服务器通过bind特定的端口号来提供服务,这其中也有众所周知的服务和其对应的端口号,通过查看/etc/service文件。
这种函数从名字就能知道其含义了。以getservbyname为例

#include<netdb.h>
struct servent * getservbyname(const char *servname,const char *protoname);

第一个参数是服务的名字,例如“daytime”,“ftp”,这些都是可以在/etc/service文件中找到的,第二个参数是具体协议,可以是TCP,可以是UDP,也可以是NULL,如果某个服务仅仅支持单个协议例如FTP只支持TCP,那使用NULL就会默认为TCP。

getservbyname 和 gethostbyname结合使用

书中给出的示例客户端,该客户端有如下功能:我们只需要给出主机名服务名就可以获取服务器提供的服务,如果服务器正在运行的话,当然为了简单起见,这里以daytime 13/tcp 为例,在客户端程序只有简单的read,所以我们完全可以把自己之前写的TCP回射服务器程序添加到/etc/services里卖去,例如:

zxecho      1024/tcp

我们可以做如下调用:

./newdaytimetcpcli localhost zxecho
或者
./newdaytimetcpcli localhost daytime

前提是,绑定1024端口的服务器或者绑定daytime服务端口的服务器运行,并向已连接套接字发送一些数据。

4.1 代码

//newdaytimetcpcli.c

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

#define MAXLINE 1024

int main(int argc,char **argv)
{
    int sockfd;
    int n;
    struct sockaddr_in servaddr;

    struct hostent *hp;
    struct servent *sp;
    char **pptr;
    char recvline[MAXLINE];


    if(argc != 3)
    {
        printf("usage:daytimetcpcli1 <hostname> <service>\r\n");
        return -1;
    }
    hp=gethostbyname(argv[1]);
    if(NULL==hp)
    {
        printf("gethostbyname error! NULL returned\r\n");
        return -1;
    }
    else
    {
        pptr=hp->h_addr_list;
    }

    sp=getservbyname(argv[2],"tcp");
    if(NULL==sp)
    {
        printf("gerservbyname error! NULL returned\r\n");
        return -1;
    }
    for(;*pptr!=NULL;pptr++)
    {
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
        {
            printf("socket error \r\n");
            return -1;
        }
        memset(&servaddr,0x00,sizeof(servaddr));
        servaddr.sin_family=AF_INET;
        servaddr.sin_port=sp->s_port;//network byte order
        memcpy(&servaddr.sin_addr,(struct in_addr *)*pptr,sizeof(struct in_addr));

        if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))==0)
        {
            printf("connect success\r\n");
            break;//connect success
        }
        else
        {
            printf("connect error\r\n");
            close(sockfd);
        }
    }
    if(NULL==*pptr)
    {
        printf("unable to connect\r\n");
        return -1;
    }
    while((n=read(sockfd,recvline,MAXLINE))>0)
    {
        recvline[n]='\0';
        fputs(recvline,stdout);
    }
    return 0;
}

(五)总结

这一章蛮僵的,以至于后面几个函数看看就算了,懒得测试了。
本章的主要收获到不在于那几个函数,如何调用又花式的获取主机名,ip地址等等,感觉了解就好。
不过因为牵涉到DNS,顺带看了看相关内容,什么DNS劫持啊,GFW,之类。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值