Python网络编程笔记(三):套接字名和DNS

套接字名

域名服务(DNS, Domain Name Service): 域名和真实IP地址的映射。

套接字的5个坐标

  • 地址族(Adress Family),比如 AF_INET, AF_UNIX
  • 套接字类型(Socket Type), 比如SOCK_DGRAM, SOCK_STREAM
  • 协议 (protocol), 前两个确定后协议可选很少,所以一般不写,或者写0,表示自动选择,比如IPPROTO_TCP, IPPROTO_UDP
  • 主机名
  • 端口号

IPV6的特殊情况:
IPV6中套接字名不止包括主机名、端口号,还包括“流”信息,“范围”标识等额外坐标。

现代地址解析-getaddrinfo()的使用

套接字需要的5个坐标都可以得到。生产环境代码很少使用 AF_INET 这类socket模块的常量,采用的是 getaddrinfo()

>>> from pprint import pprint // 打印元组列表好看
// 调用
>>> infolist = socket.getaddrinfo('gatech.edu', 'www')
// 等同于
>>> infolist = socket.getaddrinfo('gatech.edu', 80)
// 返回
>>> pprint(infolist)
[(2, 1, 6, '', ('130.208.77.55', 80)),
 (2, 2, 17, '', ('130.208.77.55', 80)]
>>> info = infolist[0]
// 元组前三项分别为地址族、套接字类型、协议,用来初始化套接字
>>> s = socket.socket(*info[0:3])
// 元组第5项为(主机名,端口), 可用来调用
>>> s.connect(info[4])

使用 getaddrinfo() 为服务器绑定端口

参数:getaddrinfo(host, port, address_family, sock_type, protocl, flag) 如果某个字段为数字,可以使用0代表通配符

场景:正在创建套接字或者希望客户端从一个可预计的地址连接至其他主机, 需要bind()

用法:把主机名设为 None, 127.0.0.1, localhost等,提供端口号、套接字类型等。

>>> from socket import getaddrinfo
// 想用 TCP 来支持 smtp 数据传输的话,应该通过 bind() 把套接字绑定到哪个地址
>>> getaddrinfo(None, 'smtp', 0, socket.SOCK_STREAM, 0, socket.AI_PSASSIVE)
// 返回告诉我们可以bind到本机的任何IPV4或IPV6地址
[(2, 1, 6, '', ('0.0.0.0', 25)), (10, 1, 6, '', ('::', 25, 0, 0))]

使用 getaddrinfo() 连接服务

场景:connect()sendto()

参数:

  1. AI_ADDRCONFIG 标记: 过滤计算机无法连接的地址
  2. AI_V4MAPPED 标记:本机只有IPV6,但是连接的服务只支持IPV4,指定后,会将服务的IPV4地址编码为可用的IPV6地址。

用法:

>>> getaddrinfo('ftp.kernel.org', 'ftp', 0, socket.SOCK_STREAM, 0,
...           socket.AI_ADDRCONFIG | socket.AI_V4MAPPED)
[(2, 1, 6, '', ('204.13.33.3', 21)),
 (2, 1, 6, '', ('204.13.33.4', 21)]

返回的列表是有顺序的,经过了负载均衡,一般选择第一个。

使用 getaddrinfo() 请求规范主机名

反向DNS查询:IP映射到主机名。

风险:

  1. IP地址拥有者可以随意设置主机名。因此要验证。
  2. 耗时。

用法:设置 AI_CANNONNAME 标志, 返回项第四项为规范主机名。

其他 getaddrinfo() 标记

总结下之前的:

  • AI_PASSIVE 标记:被动的,用于 bind(),节点为null时,返回通配地址,否则返回回环地址。
  • AI_ADDRCONFIG 标记: 过滤计算机无法连接的地址
  • AI_V4MAPPED 标记:本机只有IPV6,但是连接的服务只支持IPV4,指定后,会将服务的IPV4地址编码为可用的IPV6地址。
  • AI_CANNONNAME 标记, 返回项第四项为规范主机名。

其他:

  • AI_ALL: 与 AI_V4MAPPED 结合使用,表示包含已知的与目标主机的所有地址。
  • AI_NUMERICHOST: 调用的节点名必须是 IPV4 或者 IPV6 地址,不是字符串
  • AI_NUMRICSERV: 禁用"www"这种端口,只用"80"这种。

getaddrinfo() 之前经常使用的原始名称服务程序

// 返回主机名
socket.gethostname()
socket.getfqdn()

// 对IPV4主机名和IP地址互换
socket.gethostbyname('cern.h')
socket.gethostbyaddr('128.189.22.1')

// 查询协议号和端口号
>>> socket.getprotobyname('UDP')
17
>>> socket.getservbyname('www')
80
>>> socket.getservbyport(80)
'www'

// 获取机器主IP地址
socket.gethostbyname(socket.getfqdn())
// 调用都可能失败,要做好二手准备。

代码

// 使用 `getaddrinfo()`创建并连接套接字

#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter04/www_ping.py
# Find the WWW service of an arbitrary host using getaddrinfo().

import argparse, socket, sys

def connect_to(hostname_or_ip):
    try:
        infolist = socket.getaddrinfo(
            hostname_or_ip, 'www', 0, socket.SOCK_STREAM, 0,
            socket.AI_ADDRCONFIG | socket.AI_V4MAPPED | socket.AI_CANONNAME,
            )
    // getaddrinfo失败后特殊错误 gaierror
    except socket.gaierror as e:
        print('Name service failure:', e.args[1])
        sys.exit(1)

    info = infolist[0]  # per standard recommendation, try the first one
    socket_args = info[0:3]
    address = info[4]
    s = socket.socket(*socket_args)
    try:
        s.connect(address)
    except socket.error as e:
        print('Network failure:', e.args[1])
    else:
        print('Success: host', info[3], 'is listening on port 80')

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Try connecting to port 80')
    parser.add_argument('hostname', help='hostname that you want to contact')
    connect_to(parser.parse_args().hostname)

DNS 协议

  • 目的:解析主机名,返回IP地址
  • 标准:RFC 1034和 RFC 1035
  • 传输层协议:UDP/IP 与 TCP/IP
  • 端口号:53
  • 库:第三方,包括 dnspython3

DNS查询只有在缓存、多播DNS等失败后才会启动DNS服务器,毕竟这很耗时。

使用 Python 进行 DNS 查询

// 一个包含递归的简单 DNS 查询
#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter04/dns_basic.py
# Basic DNS query

import argparse, dns.resolver

def lookup(name):
    for qtype in 'A', 'AAAA', 'CNAME', 'MX', 'NS':
        answer = dns.resolver.query(name, qtype, raise_on_no_answer=False)
        if answer.rrset is not None:
            print(answer.rrset)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Resolve a name using DNS')
    parser.add_argument('name', help='name that you want to look up in DNS')
    lookup(parser.parse_args().name)

answer.rrset返回格式:查询名称,有效时间,类型(IN, 表示互联网地址响应), 记录的类型qtype(A: IPV4, AAAA: IPV6, NS: name service, MX: 邮件服务器)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容