目录
1 Hostname
1.1 主机名称
1.1.1 Hostname, IP, Domain Name, DNS
这四个概念通常比较容易混淆,我们将他们之间的关系用一张图表示:
-
DNS表示将Hostname转换成IP。
-
IP有IPv4和IPv6,IPv6不仅仅解决IPv4数量不够的问题,还有传输上的提升。
-
Domain Name(域名)是网络的域的名字,域表示不同的区域。“root-level” name servers知道所有“top-level domains(TLDs,顶级域名,包括.com,.org,.edu等)”。域名分等级,从右到左,等级细分,由“.”分开。
-
machine name是机器的名字,加上其所在的域名就是hostname。
1.1.2 5个套接口坐标
一个socket由5个坐标完全确定:
-
address family(地址簇):规定了地址的格式。通常用AF_INET,对应的地址格式是(IP,port)
-
type(类型):常用的有SOCK_DGRAM(UDP)和SOCK_STREAM(TCP)。
-
protocol:常用的有IPROTO_UDP和TPPROTO_TCP。
-
IP。
-
port。
1.1.3 IPv4 vs. IPv6
IPv6不仅仅解决IPv4数量不够的问题,还有传输上的提升。IPv4到IPv6的过渡需要开发者写更General的代码来兼容。
1.2 进阶IP解析: getaddrinfo()
为了平滑的完成IPv4到IPv6的转变带来的代码上的不一致,socket模块提供了一个统一的接口方法getaddrinfo()
,它是该模块的顶层方法。
getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
,返回5个套接口坐标。
1.2 getaddrinfo()
1.2.1 基本用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#1.基本用法
infolist = socket.getaddrinfo('gatech.edu','www')
pprint(infolist)
info = infolist[0]
sock = socket.socket(*info[0:3])
pprint(info[4])
sock.connect(info[4])
#output:
[(<AddressFamily.AF_INET: 2>,
<SocketKind.SOCK_DGRAM: 2>,
17,
'',
('130.207.160.173', 80)),
(<AddressFamily.AF_INET: 2>,
<SocketKind.SOCK_STREAM: 1>,
6,
'',
('130.207.160.173', 80))]
1.2.2 不同Flag
1.2.2.1 AI_ADDRCONFIG, AI_V4MAPPED
1
2
3
4
#2_Connect to a Service
info = socket.getaddrinfo('iana.org','www',0,socket.SOCK_STREAM,0,socket.AI_ADDRCONFIG|socket.AI_V4MAPPED)
print(info)
#output: [(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('192.0.43.8', 80))]
1.2.2.2 AI_CANONNAME
1
2
3
4
5
#3_Canonical Hostname
info = socket.getaddrinfo('iana.org','www',0,socket.SOCK_STREAM,0,socket.AI_CANONNAME)
print(info)
#output:[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, 'iana.org', ('192.0.43.8', 80)), (<AddressFamily.AF_INET6: 30>, <SocketKind.SOCK_STREAM: 1>, 6, 'iana.org', ('2001:500:88:200::8', 80, 0, 0))]
1.2.2.3 Other Flags
略。
1.2.3 Primitive Name Service Routines
1
2
3
4
5
6
7
8
9
10
11
12
13
import socket
print(socket.gethostname()) #LALdeMacBook-Pro.local
print(socket.getfqdn()) #laldemacbook-pro.local
print(socket.gethostbyname('cern.ch')) #188.184.9.234
print(socket.gethostbyaddr('188.184.9.234')) #('webrlb01.cern.ch', ['234.9.184.188.in-addr.arpa'], ['188.184.9.234'])
print(socket.getprotobyname('UDP')) #17
print(socket.getservbyname('www')) #80
print(socket.getservbyport(80)) #http
print(socket.gethostbyname(socket.getfqdn())) #192.168.0.101
1.3 应用到自己代码
将上面所有put together,可以写一个更general的client方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)
except socket.gaierror as e:
print("Name service failure:",e.args[1])
sys.exit(1)
else:
print("infolist get successful: {}".format(infolist))
info = infolist[0]
sock = socket.socket(*info[0:3])
try:
sock.connect(*info[4:])
except socket.error as e:
print('client connectio error: {}'.format(e))
else:
print('{} is listening on port 80'.format(info[4:]))
if __name__=='__main__':
parser = argparse.ArgumentParser()
parser.add_argument('hostname',help='the hostname or IP for connection')
arg = parser.parse_args()
connect_to(arg.hostname)
2 DNS
DNS(Domain Name Service): The Domain Name System (DNS) is a hierarchical decentralized naming system for computers, services, or any resource connected to the Internet or a private network. DNS is in Application Layer of Network Stack, using UDP.
2.1 为什么不用原始DNS
DNS一次请求的路径:本地DNS->root DNS->top-level DNS(TLD)->Authoritive DNS。
你可以在terminal中输入whois
命令,来直接向TLD请求DNS。
1
2
3
4
5
6
7
8
9
10
$ whois python.org
Domain Name:PYTHON.ORG
Created On:27-Mar-1995 05:00:00 UTC
Last Updated On:07-Sep-2006 20:50:54 UTC
Expiration Date:28-Mar-2016 05:00:00 UTC
...
Registrant Name:Python Software Foundation
...
Name Server:NS2.XS4ALL.NL
Name Server:NS.XS4ALL.NL
由于完整的DNS请求路径要经过4词请求,因此DNS比较费时间。通常依赖OS的DNS服务要好于自己使用原始DNS,因为OS有cache,可以提供更好的服务。
2.2 用python来请求DNS
唯一一种情况开发者需要自己请求DNS的情况:邮件服务器或者邮件客服想直接发送邮件而不通过本地邮件中转。
2.2.1 dnspython3 module
dnspython3是建立在os上的DNS module。可用于上述情况。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 lookup in DNS',nargs='?',default='python.org')
arg = parser.parse_args()
lookup(arg.name)
#output:
#python.org. 34208 IN A 23.253.135.79
#python.org. 46294 IN AAAA 2001:4802:7901:0:e60a:1375:0:6
#python.org. 600 IN MX 50 mail.python.org.
#python.org. 82077 IN NS ns1.p11.dynect.net.
#python.org. 82077 IN NS ns2.p11.dynect.net.
#python.org. 82077 IN NS ns3.p11.dynect.net.
#python.org. 82077 IN NS ns4.p11.dynect.net.
5种DNS query:
-
‘A’表示IPv4,如果你要通过IPv4,HTT访问python.org,需要访问23.253.135.79;
-
‘AAAA’表示IPv6,如果你要通过IPv6,HTT访问python.org,需要访问2001:4802:7901:0:e60a:1375:0:6;
-
‘CNAME’表示Canonical Name,即别名,这里没有;
-
‘MX’表示邮件地址,如果你要发@python.org邮件,需要发到mail.python.org;
-
‘NS’表示name server,如果你要请求python.org的子域名,需要发到ns1.p11.dynect.net或者ns1-4:
2.2.2 解析邮件域名
略