22
Socket-Programmierung mit IPv6 Christian Kauhaus gocept gmbh & co. kg PyCon DE Leipzig, 7. Oktober 2011 Socket-Programmierung mit IPv6 · Christian Kauhaus · 1

Socket Programmierung mit IPv6

Embed Size (px)

DESCRIPTION

Vortrag zur Socket-Programmierung von Dual-Stack-Anwendungen in Python von der PyCon DE 2011 Leipzig. http://de.pycon.org/2011/schedule/sessions/42/ http://blip.tv/episode/5632211

Citation preview

Page 1: Socket Programmierung mit IPv6

Socket-Programmierung mit IPv6

Christian Kauhaus

gocept gmbh & co. kg

PyCon DE Leipzig, 7. Oktober 2011

Socket-Programmierung mit IPv6 · Christian Kauhaus · 1

Page 2: Socket Programmierung mit IPv6

Übersicht

..1 Einleitung

..2 Aktive Socketverbindungen (Clients)

..3 Passive Socketverbindungen (Server)

..4 IP-Adressen

..5 Zusammenfassung

Socket-Programmierung mit IPv6 · Christian Kauhaus · 2

Page 3: Socket Programmierung mit IPv6

EinleitungAnti-Motivation

Brauche ich IPv6-Socketprogrammierung?In der Regel nicht.

IPv6-Support umsonst durch:• Bibliotheken (urllib etc.)• Frameworks (Twisted etc.)• vorgelagerter Server (nginx etc.)

Aber wenn ich es doch mal brauche..?

Socket-Programmierung mit IPv6 · Christian Kauhaus · 3

Page 4: Socket Programmierung mit IPv6

Durchgehendes BeispielChristians DATE-Protokoll

Simples Protokoll, um das aktuelle Datum abzufragen• Client sagt: DATE• Server sagt: DATE YYYY-MM-DD

class DateHandler(socketserver.BaseRequestHandler):

def handle(self):if self.request.recv(1024).strip() == 'DATE':

self.request.send('DATE %s\r\n' %datetime.date.today())

else:self.request.send('ERROR\r\n')

class DateSocketServer(socketserver.ThreadingMixIn,socketserver.TCPServer):

allow_reuse_address = True

Socket-Programmierung mit IPv6 · Christian Kauhaus · 4

Page 5: Socket Programmierung mit IPv6

Aktive SocketverbindungenDate-Client nur mit IPv4

»Old school« Implementierung

class DateClient(object):

def __init__(self, hostname, port=9000, timeout=15):address = socket.gethostbyname(hostname)self.socket = socket.socket(socket.AF_INET,

socket.SOCK_STREAM, 0)self.socket.settimeout(timeout)self.socket.connect((address, port))

def query(self):self.socket.send('DATE\r\n')response = self.socket.recv(1024)print 'C: response is %s' % response.strip()

Socket-Programmierung mit IPv6 · Christian Kauhaus · 5

Page 6: Socket Programmierung mit IPv6

Verhalten des Single-Stack-ClientsIgnoriert IPv6-Adressen komplett

Shell-Demo:• test.local löst zu IPv4- und IPv6-Adressen auf• server6 test.local lauscht auf allen Adressen• client4 test.local verbindet sich über IPv4

Socket-Programmierung mit IPv6 · Christian Kauhaus · 6

Page 7: Socket Programmierung mit IPv6

Programmiertip 1: getaddrinfo()Schnittstelle zwischen Sockets und Außenwelt

getaddrinfo() nutzen. Immer!

• Namensauflösung• Validierung der Eingabe• Filtern nach Socket-Typen• address family, socket type, IP protocol

Anti-Patternaddress = socket.gethostbyname(host)

.

.

.

GAI

Socket-Programmierung mit IPv6 · Christian Kauhaus · 7

Page 8: Socket Programmierung mit IPv6

Programmiertip 1: getaddrinfo()Schnittstelle zwischen Sockets und Außenwelt

Beispiel:>>> socket.getaddrinfo('www.python.org', 80, socket.AF_UNSPEC)[(10, 1, 6, '', ('2001:888:2000:d::a2', 80, 0, 0)),(10, 2, 17, '', ('2001:888:2000:d::a2', 80, 0, 0)),(10, 3, 0, '', ('2001:888:2000:d::a2', 80, 0, 0)),(2, 1, 6, '', ('82.94.164.162', 80)),(2, 2, 17, '', ('82.94.164.162', 80)),(2, 3, 0, '', ('82.94.164.162', 80))]

• address family, z. B. 10 = AF_INET6• socket type, z. B. 1 = SOCK_STREAM• IP protocol, z. B. 6 = SOL_TCP• socket address: host, port, flow id, scope

Socket-Programmierung mit IPv6 · Christian Kauhaus · 8

Page 9: Socket Programmierung mit IPv6

Programmiertip 2: 1:n-AuflösungEin Host, mehrere Adressen

Einem Hostnamen sind mehrere Adressen zugeordnet.

• IPv4- und IPv6-Adresse• mehrere IPv6-Adressen für Multi-Homed Hosts• getaddrinfo() gibt eine Liste zurück• Reihenfolge nach absteigender Priorität

Anti-Patternres = socket.getaddrinfo(...)sock.connect(res[0][4])

.

.

.

1:n

Socket-Programmierung mit IPv6 · Christian Kauhaus · 9

Page 10: Socket Programmierung mit IPv6

Programmiertip 3: Connect-LoopAlle Adressen durchprobieren

Abbruch nur, wenn keine Adresse erreichbar ist.

• Verbindung kommt zustande: fertig• Fehler, aber noch Adressen übrig: weitermachen• Fehler bei letzter Adresse: letzten Fehler eskalieren

Anti-Patterntry:

sock.connect(sockaddr)except socket.error as e:

sys.exit(1)

.

.

.

Loop

Socket-Programmierung mit IPv6 · Christian Kauhaus · 10

Page 11: Socket Programmierung mit IPv6

Dual-Stack-CodeDate-Client mit IPv4 und IPv6

class DateClient(object):

def __init__(self, hostname, port=9000, timeout=15):self.socket = Noneself.connect(hostname, port, timeout)

def query(self):self.socket.send(b'DATE\r\n')response = self.socket.recv(1024).decode()print('C: response is {}'.format(response.strip()))

Socket-Programmierung mit IPv6 · Christian Kauhaus · 11

Page 12: Socket Programmierung mit IPv6

Dual-Stack-CodeDate-Client mit IPv4 und IPv6 (Fortsetzung)

def connect(self, host, port, timeout):exception = Nonefor (af, socktype, proto, cname, sockaddr

) in socket.getaddrinfo(host, port, socket.AF_UNSPEC,socket.SOCK_STREAM, 0):

try:self.socket = socket.socket(af, socktype, proto)self.socket.settimeout(timeout)self.socket.connect(sockaddr)return

except socket.error as e:exception = e

raise exception

.

.

.

GAI

.

.

1:n

.

.

Loop

Socket-Programmierung mit IPv6 · Christian Kauhaus · 12

Page 13: Socket Programmierung mit IPv6

Verhalten des Dual-Stack-ClientsVerbindung mit IPv6

Shell-Demo:• test.local löst zu IPv4- und IPv6-Adressen auf• server6 test.local lauscht auf allen Adressen• client6 test.local verbindet sich über IPv6• server4 test.local lauscht nur auf IPv4-Adresse• client6 test.local probiert IPv6 und verbindet sich dann

über IPv4

Socket-Programmierung mit IPv6 · Christian Kauhaus · 13

Page 14: Socket Programmierung mit IPv6

Passive SocketverbindungenDate-Server mit IPv4

class DateServer(object):

def __init__(self, listen_addresses, port):self.listen_addresses = listen_addressesself.port = portself.threads = list(self.create_threads())

def create_threads(self):for host_or_address in self.listen_addresses:

address = socket.gethostbyname(host_or_address)server = DateSocketServer((address, self.port),

DateHandler)yield threading.Thread(target=server.serve_forever)

def run(self):for thread in self.threads:

thread.start()for thread in self.threads:

thread.join()

Socket-Programmierung mit IPv6 · Christian Kauhaus · 14

Page 15: Socket Programmierung mit IPv6

Verhalten des Single-Stack-ServersVerbindung mit IPv4

Shell-Demo:• test.local löst zu IPv4- und IPv6-Adressen auf• server4 test.local lauscht nur auf IPv4-Adresse• client6 test.local verbindet sich über IPv4

Socket-Programmierung mit IPv6 · Christian Kauhaus · 15

Page 16: Socket Programmierung mit IPv6

Dual-Stack-CodeDate-Server mit IPv4 und IPv6

create_threads() muss mehrere Server pro Listen-Itemerzeugen:

class DateServer(object):

def __init__(self, listen_addresses, port):self.listen_addresses = listen_addressesself.port = portself.threads = list(self.create_threads())

def create_threads(self):servers = []for host_or_address in self.listen_addresses:

servers += self.connect_servers(host_or_address)for server in servers:

yield threading.Thread(target=server.serve_forever)

Socket-Programmierung mit IPv6 · Christian Kauhaus · 16

Page 17: Socket Programmierung mit IPv6

Dual-Stack-CodeDate-Server mit IPv4 und IPv6 (Fortsetzung)

def connect_servers(self, host_or_address):servers = []exception = Nonefor (af, socktype, proto, cname, sockaddr

) in socket.getaddrinfo(host_or_address, self.port, socket.AF_UNSPEC,socket.SOCK_STREAM, 0, socket.AI_PASSIVE):

try:servers.append(DateSocketServer(

sockaddr[0:2], DateHandler, True, af))except socket.error as e:

exception = econtinue

if not servers:raise exception

return servers

.

.

.

GAI

.

.

1:n

.

.

Loop

Socket-Programmierung mit IPv6 · Christian Kauhaus · 17

Page 18: Socket Programmierung mit IPv6

Dual-Stack-CodeDate-Server mit IPv4 und IPv6 (Fortsetzung)

TCPServer erweitern, um address_family im Konstruktorfallweise zu setzen:

class DateSocketServer(socketserver.ThreadingMixIn,socketserver.TCPServer):

allow_reuse_address = True

def __init__(self, server_address, RequestHandlerClass,bind_and_activate=True,address_family=socket.AF_INET):

self.address_family = address_familysuper().__init__(server_address, RequestHandlerClass,

bind_and_activate)

Socket-Programmierung mit IPv6 · Christian Kauhaus · 18

Page 19: Socket Programmierung mit IPv6

Verhalten des Dual-Stack-ServersVerbindung mit IPv6 und IPv4

Shell-Demo:• test.local löst zu IPv4- und IPv6-Adressen auf• server6 test.local lauscht auf allen Adressen• client6 test.local verbindet sich über IPv6• client4 test.local verbindet sich über IPv4

Socket-Programmierung mit IPv6 · Christian Kauhaus · 19

Page 20: Socket Programmierung mit IPv6

IP-AdressenGar nicht so simpel

IP-Adressen als Stringsentweder:

• unbehandelt durchreichen(z. B. getaddrinfo(), Datenbanken, Logging)

oder:• »richtig« parsen und manipulieren (z. B. netaddr, IPy)

Anti-Patternre.match(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', ipaddress)

Socket-Programmierung mit IPv6 · Christian Kauhaus · 20

Page 21: Socket Programmierung mit IPv6

ZusammenfassungSocket-Programmierung mit IPv6

• Sockets richtig erzeugen• getaddrinfo()• 1:n Adressauflösung• Connect-Loop

• Externe Repräsentation von IP-Adressen mit Respektbehandeln

• Dual-Stack Code schreiben! :-)

Socket-Programmierung mit IPv6 · Christian Kauhaus · 21

Page 22: Socket Programmierung mit IPv6

Vielen Dank!Fragen?

E-Mail [email protected] [email protected]

Hosting http://gocept.net/auf Jobsuche? http://gocept.com/das-unternehmen/karriere

API definition RFC 3493IPv6 porting http://owend.corp.he.net/ipv6/

Socket-Programmierung mit IPv6 · Christian Kauhaus · 22