Multicast Sender & Empfänger mit Python

Die Kommunikation über eine Punkt-zu-Punkt-Verbindung ist in den meisten Anwendungen der richtige Weg. Was aber wenn mehrere Empfänger die gleichen Informationen erhalten wollen? Da zeigt sich dann ein grosser Nachteil der Punkt-zu-Punkt-Verbindungen. Pro Empfänger muss eine Verbindung hergestellt werden um dann die gleichen Informationen mehrfach zu verschicken, was zu Engpässen in der Bandbreite führen kann. Um dies zu vermeiden, kann mit Hilfe von Multicast eine sogenannte Punkt-zu-Gruppe-Verbindung hergestellt werden. Der Sender verschickt eine einzige Nachricht an eine ganze Gruppe von Empfängern und die Netzwerkinfrastruktur sorgt dafür, dass jeder interessierte Empfänger die Informationen auch erhält. Die Kommunikation geschieht über eine spezielle Multicast-Adresse (Multicast-Group) die aus dem IPv4-Bereich 224.0.0.0 bis 239.255.255.255 stammen muss. Dieser IP-Bereich ist für Multicast reserviert und wird von Routern und Switches speziell behandelt. Die Multicast-Pakete werden über UDP versendet.

Ein gutes Beispiel eines Multicast Senders und Empfänger habe ich auf der Webseite von Doug Hellmann gefunden.

Der Multicast Sender
In diesem Beispiel müssen sich die Empfänger nicht beim Sender anmelden, daher kann der Sender nicht wissen wie viele Antworten zu erwarten sind. Aus diesem Grund wartet der Sender solange auf eine Antwort bis der festgelegte Socket-Timeout erreicht wurde.

Hier die wichtigsten Code-Zeilen:


# Create the datagram socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Set a timeout so the socket does not block indefinitely when trying
# to receive data. (in sec flaot)
sock.settimeout(1.0)

# Set the time-to-live for messages to 1 so they do not go past the
# local network segment.
ttl = struct.pack('b', 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)

try:        
	# Send data to the multicast group
	print >>sys.stderr, 'sending "%s"' % message
	sent = sock.sendto(message, multicast_group)

	# Look for responses from all recipients
	while True:
		print >>sys.stderr, 'waiting to receive'
		try:
			data, server = sock.recvfrom(16)
		except socket.timeout:
			print >>sys.stderr, 'timed out, no more responses'
			break
		else:
			print >>sys.stderr, 'received "%s" from %s' % (data, server)        
finally:
	print >>sys.stderr, 'closing socket'
	sock.close()

Der Multicast Empfänger
Der Empfänger lauscht auf allen Netzwerkinterfaces auf Daten seiner Multicast-Gruppe und schickt eine Empfangsbestätigung zurück.

Hier die wichtigsten Code-Zeilen:


# The first step to establishing a multicast receiver is to create 
# the UDP socket.
server_address = ('', 10000)

# Create the socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Bind to the server address
sock.bind(server_address)

# Tell the operating system to add the socket to 
# the multicast group on all interfaces.
group = socket.inet_aton(multicast_group)
mreq = struct.pack('4sL', group, socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)


# Receive/respond loop
while True:
	print >>sys.stderr, '\nwaiting to receive message'
	data, address = sock.recvfrom(1024)
	
	print >>sys.stderr, 'received %s bytes from %s' % (len(data), address)
	print >>sys.stderr, data

	print >>sys.stderr, 'sending acknowledgement to', address
	sock.sendto('ack', address)

Source Code-Dateien:

Die 7zip-Datei enthält die Dateien:
– MulticastServer.py (Sender)
– MulticastClient.py (Empfänger)
Python – Source Code

Eine ausführbare Version (exe) der Dateien, welche eine vorhandene Installation von Python 2.7.2 erfordert:
– MulticastServer.exe (Sender)
– MulticastClient.exe (Empfänger)
Kompilierte Multicast Beispiele (x64)

Starten des Clients:
(Client empfängt Daten der Multicast-Gruppe 224.0.1.2)


>MulticastClient.exe 224.0.1.2

Starten des Servers und gleichzeitiges verschicken von Daten:
(Server verschickt ein “Hallo Multicast!” an die Multicast-Gruppe 224.0.1.2)


>MulticastServer.exe "Hallo Multicast!" 224.0.1.2

Multicast Server und Client
Multicast Server und Client


– Python: Socket-Dokumentation
– Wikipedia: Multicast

Dateien zippen mit Hilfe von Python

Um mit Hilfe des Moduls “zipfile” eine Zipdatei zu erstellen und eine Datei hinzuzufügen, braucht es nur 4 Zeilen Python Code.


import zipfile
zipper = zipfile.ZipFile(zipfilename, 'w')
zipper.write(filename,filenameArchive,zipfile.ZIP_DEFLATED)
zipper.close()

Erklärung zu den Codezeilen:

  1. Importieren des zip Moduls
  2. Es wird ein Objekt zipper erstellt, welches auf die Zipdatei “zipfilename” zeigt. Mit dem Flag ‘w’ wird die Datei im Schreibmodus geöffnet
  3. Der Zipdatei wird die Datei “filename” hinzugefügt und mit dem Namen “filenameArchive” abgelegt. Die Konstante ‘zipfile.ZIP_DEFLATED’ sorgt dafür, dass die Datei komprimiert gespeichert wird
  4. Wenn alle Dateien hinzugefügt wurden, muss die Methode .close() aufgerufen werden. Ansonsten werden nicht alle wichtigen Informationen in die Datei geschrieben und die Zipdatei wird nicht lesbar sein

Soll einer vorhandenen Zipdatei eine weitere Datei hinzugefügt werden, ändert sich nur das Flag in Zeile 2 von ‘w’ nach ‘a’. (w = write, a = append)

Work with ZIP archives – Python Doc

Anzeigen der Spaltennamen einer Tabelle, bei einer Sqlite Datenbank, mit Python

Die Spaltennamen einer Tabelle können mit dem “Cursor” herausgelesen werden. Dazu verwendet man das Attribut “description” des Cursors. Dieses Attribut enthält alle Spaltennamen der letzten Abfrage, auch wenn diese keine Resultate zurückgeliefert hat.


import sqlite3

#connect to in memory db
connection = sqlite3.connect(':memory:')
mycursor = connection.cursor()

# Create table
mycursor.execute("create table spydb (id INTEGER PRIMARY KEY ASC, ipsend text, iprecv text)")

#select on table
mycursor.execute('select * from spydb order by id desc')

#get col names with the description attribute
names = [tuple[0] for tuple in mycursor.description]
print names;

#close the cursor
mycursor.close()

In der Zeile 14 erhalten wir vom Attribut “description” ein 7-Tupel pro Spalte. Dabei enthält das erste Element eines Tupels den Spaltennamen und die anderen 6 Elemente sind vom Typ “none”.

Die Ausgabe der Zeile 15:

 ['id', 'ipsend', 'iprecv'] 

Verwendung von sqlite3 in Python

Beim Entwickeln von Python-Scripts benötigt man oft eine Möglichkeit, Daten schnell und komfortabel zu speichern. Dazu eignet sich die Programmbibliothek SQLite hervorragend. Bei SQLite befindet sich die gesamte relationale Datenbank in einer einzigen Datei, da dieses System für die Verwendung im Embedded-Bereich entworfen wurde. Dabei unterstützt SQLite viele SQL-Sprachbefehle, welche im SQL-92-Standard festgelegt wurden. Das sind Funktionen wie Transaktionen, Views, Subselects und Trigger. Um SQLite in Python zu verwenden müssen wir die Bibliothek erst einbinden:


import sqlite3

Der nächste Schritt ist das Erstellen einer Verbindung zu der SQLite-Datenbank. Dies geschieht mit Hilfe eines “connection objects” das die Datenbank repräsentiert.


connection = sqlite3.connect('c:\\temp\spy.db')

Es existiert die Möglichkeit die Datenbank im Memory zu erzeugen, dazu wird anstelle des Pfades zur Datenbank “:memory:” verwendet.

Hier ein simples Beispiel bei dem eine Tabelle mit dem Namen “spydb” erstellt wird. Die Tabelle enthält 3 Spalten: id mit dem Autowert, ipsend und iprecv als Textfeld.
In der Zeile 8 und 9 werden Daten in die Tabelle geschrieben und in Zeile 12 mit einem “commit” die Transaktion ausgeführt.


# create a cursor to work with the database
mycursor = connection.cursor()

# create a new table
mycursor.execute("create table spydb (id INTEGER PRIMARY KEY ASC, ipsend text, iprecv text)")

# insert data to the table
mycursor.execute("insert into spydb (ipsend , iprecv ) values('10.0.0.1','10.0.0.2')")
mycursor.execute("insert into spydb (ipsend , iprecv ) values('10.0.0.3','10.0.0.15')")

# save the changes
connection.commit();

# insert data to the table
mycursor.execute("insert into spydb (ipsend , iprecv ) values('10.0.13.99','10.0.0.15')")

# rollback the changes
connection.rollback();

mycursor.execute('select * from spydb order by id desc')
for row in mycursor:
    print row

# close the cursor if we are finish
mycursor.close()

Wenn wir nun auf der Zeile 20 alle Daten aus der Tabelle auswählen und ausgeben, sieht dies wie folgt aus:

In der Zeile 15 wird ein weiterer Datensatz hinzugefügt, dieser wird jedoch nicht ausgegeben, da die Transaktion rückgängig (Zeile 18) gemacht wurde.

Python Docs

DNS – Blacklists abfragen mit Python

Ein einfaches Python-Skript zum Abfragen von DNS-Blacklists, welche adressbasiert sind.
Bei adressbasierten Blacklists wird die zu überprüfende IP-Adresse, in umgekehrter Reihenfolge, an die Domain angehängt. Dann wird eine DNS-Abfrage auf einen A-Record gestartet. Wird ein A-Record zurück geliefert, gilt die IP-Adresse als gelistet. Der A-Record wird eine IP vom Typ 127.0.0.x aufweisen.

Wenn man wissen möchte warum eine IP-Adresse in die Liste aufgenommen wurde, kann der TXT-Record abgefragt werden. In diesem Record wird vom Blacklist Betreiber oft der Grund für die Aufnahme hinterlegt.


SHOW_DEBUG_MSG = 0

import string
import sys
import socket

'''
@return: 0    If the ip-address is not blacklisted
@return: 1    If the ip-address is blacklisted
'''
def checkIfBlacklisted( ip, blacklistURL):
if SHOW_DEBUG_MSG: print "Checking %s on %s" %(ip,blacklistURL)

# turn ip "a.b.c.d" into "d.c.b.a.DNS_BLACKLIST_DOMAIN"
iplist = string.split(ip, ".")
iplist.reverse()
ip = string.join(iplist, ".")
ip += "." + blacklistURL

try:
#if blacklisted, we receive a IP in the range 127.0.0.x
ret = socket.gethostbyname( ip )
if SHOW_DEBUG_MSG: print "Blacklisted! returned: %s" %ret
return 1
except socket.gaierror, message:
if SHOW_DEBUG_MSG: print "Not blacklisted [%s]" % message
return 0

if __name__ == "__main__":
if len(sys.argv) > 2:
print checkIfBlacklisted( sys.argv[1] , sys.argv[2])

DNSBlacklistChecker.py

Zum Ausführen:


python script x.x.x.x zen.spamhaus.org

Mögliche Blacklists:

  • no-more-funn.moensted.dk
  • dnsbl.sorbs.net
  • zen.spamhaus.org
  • spews.dnsbl.net.au
  • dnsbl.NJABL.org
  • proxies.relays.monkeys.com
  • list.dsbl.org