@cruzer*: Ein Server muss nicht auf das „Ping” bei abgelaufener TTL antworten. Wenn er das nicht tut, wartest Du bis zum St. Nummerleins-Tag auf die Antwort. Du wirst also nach einer gewissen Zeit aufhören müssen auf eine Antwort zu warten. Dazu gibt es die `settimeout()`-Methode auf `socket`-Objekten.
Konvention ist vier Leerzeichen pro Einrückebene.
Den Rückgabewert von `traceroute()` mit ``print`` ausgeben macht keinen Sinn, da die Funktion keinen sinnvollen Rückgabewert liefert.
Die Namen `dest` und `destenation` nebeneinander zu haben ist verwirrend. Klar beschreiben beide das Ziel, aber man kann nicht erkennen wo der Unterschied in Wert und Bedeutung ist. `destenation` ist eine IP, das könnte man auch im Namen kenntlich machen. Und dann braucht man `dest` auch nicht mehr abkürzen, sondern könnte es `destenation` nennen. Würde sich auch prima als Argument für die Funktion machen. `max_hops` auch — mit einem Defaultwert.
In der ``while``-Schleife wird `ttl` von 1 bis `max_hops` hochgezählt — das ist eigentlich ein Job für eine ``for``-Schleife.
Die beiden Zuweisungen unter dem Kommentar ``# erstelle leere Variablen`` sind unnötig.
Das ``except`` auf Ebene des Schleifenkörpers ist keine angemessene Fehlerbehandlung. Und die gehört auch nicht in die Funktion. Man würde hier besser die Ausnahme die Funktion abbrechen lassen und die Fehlermeldung beim Aufrufer ausgeben lassen. Und dann auch mit der Ausnahme selbst und nicht einfach so ein generisches „da war ein Fehler” ausgeben, was dem Benutzer nicht wirklich hilft herauszufinden was die Ursache war.
Wenn man das ``except`` weg lässt, bleibt noch das ``finally`` — hier kann man mit `contextlib.closing()` und der ``with``-Anweisung arbeiten.
Kommentare sollten dem Leser einen Mehrwert liefern und nicht das offensichtliche aus dem Quelltext noch einmal wiederholen. Einige der eigentlich überflüssigen Kommmentare enthalten auch Fehler. Zum Beispiel ist `current_adr` kein „array” und es werden (maximal) 512 Bytes empfangen und nicht 512 Bits.
Das `current_addr` in der Schleife mal an ein Tupel mit IP und Portnummer und mal nur an eine IP gebunden wird ist verwirrend. Ähnliches gilt für `current_name`. Das mit den Tupel sollte man sich sparen und eine Umbenennung von `current_addr` zu `current_ip` sagt besser aus was der Wert bedeutet.
`curr_host` sollte wohl `current_host` heissen. Dieses ``if``/``else`` macht aber sowieso keinen Sinn, weil `current_ip` an der Stelle nicht `None` sein kann. Der „Sternchen-Fall” beim normalen ``traceroute``-Befehl ist wenn der Server bei dem das Paket wegen der TTL „gestorben” ist, gar nicht antwortet. In dem Fall hat man aber schon viel früher die Information das keine IP verfügbar ist, denn dann hat man für den `gethostbyaddr()` ja schon keine Argument und braucht diese Methode gar nicht erst aufrufen.
Eine Meldung wenn das Ziel innerhalb der `max_hops` Versuche nicht erreicht wurde, wäre nett. Da bietet sich ein ``else``-Zweig bei der Schleife an, da diese im Erfolgsfall mit ``break`` verlassen wird.
Muss man die Sockets in der Schleife eigentlich ständig neu erstellen? Bei funktioniert es auch wenn ich die beiden Sockets nur einmal vor der Schleife erstelle.
Das könnte dann insgesamt so aussehen:
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
import socket
from contextlib import closing
def traceroute(destination, max_hops=30):
destination_ip = socket.gethostbyname(destination)
port = 33434
icmp = socket.getprotobyname('icmp')
udp = socket.getprotobyname('udp')
with closing(
socket.socket(socket.AF_INET, socket.SOCK_DGRAM, udp)
) as send_socket:
with (
closing(socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp))
) as receive_socket:
receive_socket.bind(('', port))
receive_socket.settimeout(3)
for ttl in xrange(1, max_hops + 1):
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
send_socket.sendto('', (destination_ip, port))
try:
_data, (current_ip, _port) = receive_socket.recvfrom(512)
except socket.timeout:
current_ip = None
if current_ip:
try:
current_name, _, _ = socket.gethostbyaddr(current_ip)
except socket.error:
current_name = current_ip
current_host = '{0} ({1})'.format(current_name, current_ip)
else:
current_host = '*'
print '{0:d}\t{1}'.format(ttl, current_host)
if current_ip == destination_ip:
break
else:
print 'Ziel nicht in {0} hops erreicht.'.format(max_hops)
def main():
try:
traceroute('python-forum.de')
except socket.error as error:
print error
if __name__ == '__main__':
main()
Man könnte jetzt noch das Verfolgen der Route von der Ausgabe trennen, in dem man Beispielsweise eine Generatorfunktion aus `traceroute()` macht, welche die einzelnen IPs auf der Strecke liefert.