www.cs.newpaltz.edu

Download Report

Transcript www.cs.newpaltz.edu

Chapter 4
net_py
Domains

Top-level Domains: .edu, .com., .ca, etc

Domain Name: newpaltz.edu, etc


Fully Qualified Domain Name: wyvern.cs.newpaltz.edu.
Owning a domain name gives you the right to create FQDNs
with your Domain Name at the end. Technically any name that
ends with a '.' is also considered fully qualified.
Hostname: wyvern.
net_py
/etc/resolv.conf

How domain name searches start:
(GeoIP)[pletcha@archimedes pypcap-1.1]$ cat /etc/resolv.conf
# Generated by NetworkManager
domain newpaltz.edu
search cs.newpaltz.edu acsl.newpaltz.edu newpaltz.edu
nameserver 137.140.1.98
nameserver 137.140.1.102

If your search name is not fully qualified search begins by
searching for using the name servers specified.

<your search name>

<your search name>.cs.newpaltz.edu

<your search name>.acsl.newpaltz.edu

<your search name>.newpaltz.edu
net_py
Example:


Searching for my.website.cam will search for

my.website.cam

my.website.cam.cs.newpaltz.edu

my.website.cam.acsl.newpaltz.edu

my.website.cam.newpaltz.edu
But searching for my.website.cam. will search for

my.website.cam
only.
net_py
Example 2:


Searching for argos searches for:

argos.cs.newpaltz.edu
# fails

argos.acsl.newpaltz.edu
# fails

argos.newpaltz.edu
# succeeds
This is why you can use host names only on campus.
net_py
Socket Names Used By:







mysocket.accept(): returns a tuple whose 2nd entry is a
remote address
mysocket.bind(address): binds to a local address so
outgoing packets have this source address.
mysocket.connect(address): indicates the remote address
that packets using this connection must either go to or come
from.
mysocket.getpeername(): Returns the remote address the
socket is connected to
mysocket.getsocketname(): returns the address of this
socket
mysocket.recvfrom(): UDP: returns data and the address of
the sender
mysocket.sendto(data,address):
UDP, indicates the receiver
net_py
address.
Five Socket Properties:


socket.AF_INET: Internet Address Family => kind of network
and transport – UDP or TCP.
socket.AF_UNIX: like the internet but between processes on
the same host

socket.SOCK_DGRAM: packet service (UDP for AF_INET)

socket.SOCK_STREAM: stream service (TCP for AF_INET)
3rd argument, not used n this book

socket.IPPROTO_TCP: use TCP for SOCK_STREAM.

socket.IPPROTO_UDP: use TCP for SOCK_DGRAM
net_py
IPv6:

Will replace IPv4 when we run our ot IPv4 IP addresses.
>>> import socket
>>> print socket.has_ipv6()
# tells you if your machine is capable of ipv6; not
# if it is enabled.

IPv6 has more services than IPv4.
net_py
Address Resolution:

One way of avoiding specifying destination IP addresses, etc,
is to let the socket module tell you what you need to know.
>>> import socket
>>> infolist = socket.getaddrinfo('gatech.edu','www')
>>> pprint infolist
[(1,2,6,'',('130.207.244.244','80'),
(1,2,17,'',('130.207.244.244','80')
# all the interfaces on .244.244 associated with 'www'
>>> ftpca = infolist[0] # == (1,2,6,'',('130.207.244.244','80')
>>> ftp = ftpca[0:3] # == (2,1,6)
>>> s = socket.socket(*ftp) # unpack a list with *
# s = socket.socket(ftp[0],ftp[1]) would do just as well
>>> s.connect(ftpca[4]) # ('130.207.244.244','80')

We are asking, “How can we connect to the web server on
host gatech.edu?”
net_py
NOTE:



HTTPD officially supports TCP (6) and UDP (17).
gatech.edu is an alias for this host. It also has a “cannonical”
name. but we didn't ask for this so it wasn't returned.
Calling socket.getaddrinfo() we don't need to use
socket.AF_INET, etc, in our code.
net_py
Asking socket.getaddrinfo() for help in binding.

Problem: Provide an address for bind() without deciding this
yourself.
Example: Suppose you want to open a server (passive) using port 1060
>>> from socket import getaddrinfo
>>> import socket
>>> getaddrinfo(None,1060,0,socket.SOCK_STREAM,0,socket.AI_PASSIVE)
[(2, 1, 6, '', ('0.0.0.0', 1060)), (10, 1, 6, '', ('::', 1060, 0, 0))]
>>> getaddrinfo(None,1060,0,socket.SOCK_DGRAM,0,socket.AI_PASSIVE)
[(2, 2, 17, '', ('0.0.0.0', 1060)), (10, 2, 17, '', ('::', 1060, 0, 0))]
# we are asking here, where to bind to. We get an IPv4 and an IPv6 answer
# in both cases, TCP and UDP.
net_py
Asking socket.getaddrinfo() for help in binding.

Problem: Want a particular address for bind().
Example: Suppose you want to open a server (passive) using port 1060
and a known local IP address.
>>> from socket import getaddrinfo
>>> import socket
>>> getaddrinfo('127.0.0.1',1060,0,socket.SOCK_STREAM,0)
[(2, 1, 6, '', ('127.0.0.1', 1060))]
>>> getaddrinfo(localhost,1060,0,socket.SOCK_STREAM,0)
[(2, 1, 6, '', ('::1', 1060,0,0)), (2, 1,6, '', ('127.0.0.1', 1060))]
# we are asking here, where to bind to. We get an IPv4 and an IPv6 answer
net_py
Asking getaddrinfo() about services

The rest of the uses of getaddrinfo() are outward looking.

socket.AI_ADDRCONFIG: filter out things you can't use

socket.AI_AI_V4MAPPED: get IPV4 only
>>> getaddrinfo('ftp.kernel.org','ftp',0,socket.SOCK_STREAM,0,
...
socket.AI_ADDRCONFIG | socket.AI_V4MAPPED)
[(2, 1, 6, '', ('149.20.4.69', 21))]

If you don't specify certain address types you get multiple
addresses
>>> getaddrinfo('iana.org','www',0,socket.SOCK_STREAM,0)
[(2, 1, 6, '', ('192.0.43.8', 80)), (10, 1, 6, '', ('2001:500:88:200::8', 80, 0, 0))]
net_py
Asking getaddrinfo() about services

The rest of the uses of getaddrinfo() are outward looking.

socket.AI_ADDRCONFIG: filter out things you can't use

socket.AI_AI_V4MAPPED: get IPV4 only
>>> getaddrinfo('ftp.kernel.org','ftp',0,socket.SOCK_STREAM,0,
...
socket.AI_ADDRCONFIG | socket.AI_V4MAPPED)
[(2, 1, 6, '', ('149.20.4.69', 21))]

If you don't specify certain address types you get multiple
addresses
>>> getaddrinfo('iana.org','www',0,socket.SOCK_STREAM,0)
[(2, 1, 6, '', ('192.0.43.8', 80)), (10, 1, 6, '', ('2001:500:88:200::8', 80, 0, 0))]
net_py
Alternative Methods:

Older methods are hard-wired for IPv4 so should be avoided.
gethostbyname()
getfqdn()
gethostnbyaddr()
getprotobyname(0
getservbyname()
getservbyport()
socket.gethostbyname(socket.getfqdn())
# gives you the primary IP address of this machine
net_py
Using getaddrinfo() and getsockaddr():
#!/usr/bin/env python
# Foundations of Python Network Programming - Chapter 4 - www_ping.py
# Find the WWW service of an arbitrary host using getaddrinfo().
import socket, sys
if len(sys.argv) != 2:
print >>sys.stderr, 'usage: www_ping.py <hostname_or_ip>'
sys.exit(2)
hostname_or_ip = sys.argv[1]
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, e:
print 'Name service failure:', e.args[1]
sys.exit(1)
net_py
Using getaddrinfo() and getsockaddr():
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, e:
print 'Network failure:', e.args[1]
else:
print 'Success: host', info[3], 'is listening on port 80'
net_py
Just to be sure 1:
#!/usr/bin/env python
# Foundations of Python Network Programming - Chapter 4 - forward_reverse.py
import socket, sys
if len(sys.argv) != 2:
print >>sys.stderr, 'usage: forward_reverse.py <hostname>'
sys.exit(2)
hostname = sys.argv[1]
try:
infolist = socket.getaddrinfo(
hostname, 0, 0, socket.SOCK_STREAM, 0,
socket.AI_ADDRCONFIG | socket.AI_V4MAPPED | socket.AI_CANONNAME )
except socket.gaierror, e:
print 'Forward name service failure:', e.args[1]
sys.exit(1)
info = infolist[0] # choose the first, if there are several addresses
canonical = info[3]
socketname = info[4]
ip = socketname[0]
net_py
Just to be sure 2:
if not canonical:
print 'WARNING! The IP address', ip, 'has no reverse name'
sys.exit(1)
print hostname, 'has IP address', ip
print ip, 'has the canonical hostname', canonical
# Lowercase for case-insensitive comparison, and chop off hostnames.
forward = hostname.lower().split('.')
reverse = canonical.lower().split('.')
if forward == reverse:
print 'Wow, the names agree completely!'
sys.exit(0)
net_py
Just to be sure 3:
# Truncate the domain names, which now look like ['www', mit', 'edu'],
# to the same length and compare. Failing that, be willing to try a
# compare with the first element (the hostname?) lopped off if both of
# they are the same length.
length = min(len(forward), len(reverse))
if (forward[-length:] == reverse[-length:]
or (len(forward) == len(reverse)
and forward[-length+1:] == reverse[-length+1:]
and len(forward[-2]) > 2)): # avoid thinking '.co.uk' means a match!
print 'The forward and reverse names have a lot in common'
else:
print 'WARNING! The reverse name belongs to a different organization'
net_py
Domain Name:
List of labels in
path of nodes
DNS
used for reverse
(addr->name)
lookup
net_py
Reverse lookup
[pletcha@archimedes 04]$ host 137.140.1.48
48.1.140.137.in-addr.arpa domain name pointer www.newpaltz.edu.
net_py
DNS Packet
net_py
DNS Packet
Q/R: 0/1
Opcode: Name or pointer
AA: Answer is authoritative(1)
TC: truncated
RD: Recursion desired (1)
RA: Recursion available (1)
rcode: ) - ok, 3 – invalid name from AA
net_py
DNS Query
net_py
Wireshark look
net_py
DNS Query Response
net_py
Wireshark look:
net_py
How to Interpret Previous Slide:



Blue area is the entire response packet.
Bytes 2D-35 are the number of Questions (1), Answers (1),
Authoritative Answers (2), Additional Answers(2).
Byte 36 is the length of the first label in the string
48.1.140.137.in-addr.arpa

Query Type: 00 0C

Query Class: 00 01 (ends on byte 54)
And so on ...
net_py
Handling Repeated Strings:



Bytes 55 and 56 ought to be the beginning of the same IP
address string repeated. Instead there are two bytes – C0 0C.
When you see most significant four bits of a byte as C this
indicates that the next 12 bits are a pointer into the response
packet.
In our case the pointer is 0C. So count C bytes into the packet
and guess where this gets you – right to the beginning of the
original version of
48.1.140.137.in-addr.arpa
namely, 02 34 38 01 31 03 31 34 30 03 ...
And so on ...
net_py
When to use DNS directly:


The authors of our text suggest we stick to getaddrinfo() for all
our addressing needs except one – find the mail server for a
remote email address.
Suppose you want to send an email to someone but your own
mail server is down. Since you normally use your own email
server to route the email to the destination mail server, your
email can't be sent unless you can by-pass your own email
server.
net_py
Email Servers: What protocols are involved in email.
“your” mail server
“their” mail server
SMTP
POP3 or
IMAP
SMTP
their client:
perhaps not
running right
now
your client:
browser,
thunderbird
net_py
Email Servers: How to by-pass your local mail server.
“your” mail server
POP3 or
IMAP
SMTP
“their” mail server
SMTP
SMTP
their client:
perhaps not
running right
now
your client:
a python program
that knows how to
send an email to a
remote mail server
(Chapter 13)
net_py
How to send an email without your local email server:



Ask the DNS server for a remote domain name for the MX
resource.
The reply should come back with the domain name and/or IP
address of the mail server for that domain.
Build up your email message with the necessary header fields
and send it off to the remote email server (port numbers: 25
and 465).
net_py
How to find a remote Mail Server:

Ask the DNS server for a remote domain name for the MX
resource.
[pletcha@archimedes 04]$ cat dns_mx.py
#!/usr/bin/env python
# Foundations of Python Network Programming - Chapter 4 - dns_mx.py
# Looking up a mail domain - the part of an email address after the `@`
import sys, DNS
if len(sys.argv) != 2:
print >>sys.stderr, 'usage: dns_basic.py <hostname>'
sys.exit(2)
net_py
def resolve_hostname(hostname, indent=0):
"""Print an A or AAAA record for `hostname`; follow CNAMEs if necessary."""
indent = indent + 4
istr = ' ' * indent
request = DNS.Request()
reply = request.req(name=sys.argv[1], qtype=DNS.Type.A)
if reply.answers:
for answer in reply.answers:
print istr, 'Hostname', hostname, '= A', answer['data']
return
reply = request.req(name=sys.argv[1], qtype=DNS.Type.AAAA)
if reply.answers:
for answer in reply.answers:
print istr, 'Hostname', hostname, '= AAAA', answer['data']
return
reply = request.req(name=sys.argv[1], qtype=DNS.Type.CNAME)
if reply.answers:
cname = reply.answers[0]['data']
print istr, 'Hostname', hostname, 'is an alias for', cname
resolve_hostname(cname, indent)
return
print istr, 'ERROR: no records for', hostname
net_py
def resolve_email_domain(domain):
"""Print mail server IP addresses for an email address @ `domain`."""
request = DNS.Request()
reply = request.req(name=sys.argv[1], qtype=DNS.Type.MX)
if reply.answers:
print 'The domain %r has explicit MX records!' % (domain,)
print 'Try the servers in this order:'
datalist = [ answer['data'] for answer in reply.answers ]
datalist.sort() # lower-priority integers go first
for data in datalist:
priority = data[0]
hostname = data[1]
print 'Priority:', priority, ' Hostname:', hostname
resolve_hostname(hostname)
else:
print 'Drat, this domain has no explicit MX records'
print 'We will have to try resolving it as an A, AAAA, or CNAME'
resolve_hostname(domain)
DNS.DiscoverNameServers()
resolve_email_domain(sys.argv[1])
net_py
Send a simple email:

Following slide, from Chapter 13, sends a simple email
directly to the remote email server.
Exercise: Combine the previous two programs.
net_py
Sending a simple email message:
[pletcha@archimedes 13]$ cat simple.py
#!/usr/bin/env python
# Basic SMTP transmission - Chapter 13 - simple.py
import sys, smtplib
if len(sys.argv) < 4:
print "usage: %s server fromaddr toaddr [toaddr...]" % sys.argv[0]
sys.exit(2)
server, fromaddr, toaddrs = sys.argv[1], sys.argv[2], sys.argv[3:]
message = """To: %s
From: %s
Subject: Test Message from simple.py
Hello,
This is a test message sent to you from the simple.py program
in Foundations of Python Network Programming.
""" % (', '.join(toaddrs), fromaddr)
s = smtplib.SMTP(server)
s.sendmail(fromaddr, toaddrs, message)
print "Message successfully sent to %d recipient(s)" % len(toaddrs)
net_py