UNIX网络编程——名字与地址转换
域名系统
域名系统(DNS)主要用于主机名字与IP地址之间的映射。
主机名既可以是一个简单名字,例如 solaris 或 bsdi ,也可以是一个全限定域名(FQDN),例如 solaris.unpbook.com 。
资源记录
DNS中的条目称为资源记录(RR)。
- A , A记录把一个主机名映射成一个32位的IPv4地址。
- AAAA , 称为“四A”(quad A)记录的AAAA记录把一个主机名映射成一个128位的 IPv6 地址。
- PTR, 称为“指针记录”的PTR记录把 IP 地址映射成主机名。
- MX , MX记录把一个主机指定作为给定主机的“邮件交换器”。
- CNAME , CNAME 代表“canonical name”(规范名字),它的常见用法是为常用的服务(例如 ftp 和 www)指派 CNAME 记录。
解析器和名字服务器
每个组织机构往往运行一个或多个名字服务器,它们通常就是所谓的 BIND(Berkeley Internet Name Domain 的简称) 程序。
解析器代码通过读取其系统相关配置文件确定本组织机构的名字服务器们的所在位置。
文件 /etc/resolv.conf 通常包含本地名字服务器主机的 IP 地址。
解析器使用 UDP 向本地名字服务器发出查询。
DNS 替代方法
不使用 DNS 也可能获取名字和地址信息。
常见的替代方法有静态主机文件(通常是 /etc/hosts 文件)、网络信息系统(Network Information System, NIS)以及轻权目录访问协议(Lightweight Directory Access Protocol, LDAP)。
gethostbyname 函数
#include <netdb.h>
struct hostent *gethostbyname(const char*hostname);
返回:若成功则为非空指针,若出错则为NULL且设置 h_errno
struct hostent
{
char *h_name;// 主机名
char **h_aliases;// 别名列表
int h_addrtype;
int h_length;// IP地址个数
char **h_addr_list;// IP地址列表
};
当发生错误时,它不设置 errno 变量,而是将全局整数变量 h_errno 设置为在头文件 <netdb.h>中定义的下列常值之一:
- HOST_NOT_FOUND;
- TRY_AGAIN;
- NO_RECOVERY;
- NO_DATA(等同于 NO_ADDRESS)。
NO_DATA 错误表示指定的名字有效。
gethostbyaddr 函数
#include <netdb.h>
struct hostent *gethostbyaddr(const char *addr, sock_len_t len, int family);
返回:若成功则为非空指针,若出错则为NULL且设置 h_errno
gethostbyaddr 函数试图由一个二进制的 IP 地址找到相应的主机名,与 gethostbyname 的行为刚好相反。
getservbyname 和 getservbyport 函数
服务:提供某种功能
协议:以TCP或UDP方式进行服务提供
服务需基于某个传输协议,在某个指定端口提供服务
服务配置文件:/etc/services
但有些服务即使在此配置文件中配置了,但实际主机可能并未启用.
有些服务,实际起作用,但并没在此配置文件下进行配置.
此配置文件中的配置也无法保证一定和实际相符.
故,只能是提供一个参考价值.
#include <netdb.h>
struct servent* getservbyname(
const char* servname, // 服务名
const char* protoname);// 服务所用协议
返回:若成功则为非空指针,若出错则为 NULL
本函数返回的非空指针指向如下的 servent 结构。
struct servent
{
char *s_name;// 服务名
char **s_aliases;// 别名列表
int s_port;// 服务端口号,以网络字节序表示.后续可直接使用
char *s_proto;// 服务所用协议
};
函数 getservbyport 用于根据给定端口号和可选协议查找相应服务。
#include <netdb.h>
struct servent *getservbyport(
int port,// 服务端口号,需传递网络字节序的参数,可用htons
const char *protoname);// 服务所用协议
返回:若成功则为非空指针,若出错则为 NULL
getaddrinfo 函数
#include <netdb.h>
int getaddrinfo(
// 主机名/点分形式的IPv4,Ipv6地址
const char *hostname,
// 服务名/服务端口号
const char *service,
// 可以为NULL
// 用于填入对期望返回信息类型的暗示
const struct addrinfo *hints,
struct addrinfo **result);
返回:若成功则为0,若出错则为非0
struct addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
char* ai_canonname;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
struct addrinfo *ai_next;
};
hints 结构中调用者可以设置的成员有:
- ai_flags (零个或多个或在一起的 AI_xxx);
- ai_family (某个 AF_xxx值);
- ai_socktype (某个 SOCK_xxx值);
- ai_protocol 。
其中 ai_flags 成员可用的标志值及其含义如下:
- AI_PASSIVE 套接字用于被动打开
- AI_CANONNAME 返回主机的规范名字
- AI_NUMERICHOST hostname必须是地址串
- AI_NUMERICSERV service需为十进制端口号数串
- AI_V4MAPPED 要求返回IPV6但没有IPV6地址时,也允许返回IPV4地址[会被映射到IPV6地址]
- AI_ALL
- AI_ADDRCONFIG
服务器进程使用getaddrinfo时,一般只指定service,同时hints中指定AI_PASSIVE,这样返回的struct sockaddr结构中IP部分类似INADDR_ANY[IPV4]/IN6ADDR_ANY_INIT取得的那样。
客户进程使用getaddrinfo获取用于套接字创建及本地绑定目的时,对主机名不设置下,同时hints中不含AI_PASSIVE,这样返回的struct sockaddr结构中IP部分类似0::1/127.0.0.1取得的那样。
struct sockaddr_storage 是一个套接字地址结构,大小可容纳IPV4/IPV6/域套接字等各类型地址对象,且满足它们的对齐限制。
gai_strerror 函数
#include <netdb.h>
const char *gai_strerror(int error);
返回:指向错误描述消息字符串的指针
freeaddrinfo 函数
由 getaddrinfo 返回的所有存储空间都是动态获取的(如, 来自 malloc 调用),包括 addrindo 结构、ai_addr 结构和 ai_canonname 字符串。
这些存储空间通过调用 freeaddrinfo 返还给系统。
#include <netdb.h>
void freeaddrinfo(struct addrinfo *ai);
ai 参数应指向由 getaddrinfo 返回的第一个 addrinfo 结构。
这个链表中的所有结构以及由它们指向的任何动态存储空间(如套接字地址结构和规范主机名)都被释放掉。
getaddrinfo 函数:IPv6
getnameinfo 函数
getnameinfo 和 getaddrinfo 的互补函数,它以一个套接字地址为参数,返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串。
#include <netdb.h>
int getnameinfo(
// 指向一个套接字地址结构
const struct sockaddr* sockaddr,
socklen_t addrlen,
char *host,
socklen_t hostlen,
char *serv,
socklen_t servlen,
int flags);
返回:若成功则为0,若出错则为非0
学习参考资料:
《UNIX网络编程 卷1:套接字联网API》 第3版