UDP to TCP

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
py_lo
User
Beiträge: 60
Registriert: Freitag 20. März 2009, 09:12

Sonntag 18. März 2018, 00:32

Hallo liebe Python Freunde,

ich habe ein kleines Projekt bei dem ich wortwörtlich fest stecke und nicht mehr weiter weiß,
vielleicht kann mir einer von Euch weiter helfen.

Also Kurzfassung:
Ich möchte an einem entfernten System Wartungsarbeiten durchführen können mit einer Software,
die auf Port 5555 UDP sendet/empfängt.
Ich habe einen SSH Tunnel aufgebaut und entsprechend konfiguriert - funktioniert auch bestens.

Nun kann ich ich ja kein UDP-Verkehr direkt per SSH übertragen - auf der Gegenseite, ein Linux
System erledige ich die Konvertierung per socat.
Für meinen Windows Rechner wollte ich mir ein kleines Python Script schreiben und habe mich einem
CODE snippet aus dem Netz bedient - siehe unten.

Das Script empfängt also brav auf UDP Port 5555 und leitet es an TCP 5555 (dem Tunnel) weiter -
kommt an der Gegenseite an, Antwort wird zurück geschickt und vom Script wieder in ein
UDP Packet gepackt damit die Software die ebenfalls auf dem Localhost Rechner läuft die
Daten wieder empfängt.

Leider lauscht das Script ja ebenfalls auf UDP 5555 auf Localhost und ich habe eine schöne
Datenschleife gebaut.
Irgendwie stehe ich da gerade auf dem Schlauch, mir fehlt da der Lösungsansatz.

Über Ideen wäre ich sehr dankbar....

Code: Alles auswählen

import socket, time
from threading import Thread

class Udp2Tcp (Thread):
    def __init__ (self):
        Thread.__init__(self)

        self._port = 5555
        self._dst = '127.0.0.1'
        self._dst_port = 5555

        self.daemon = True
        self.start()

    def _init(self):
        self.udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.udp.bind(('127.0.0.1', self._port))

        self.tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp.connect((self._dst, self._dst_port))
        self.tcp.setblocking(True)

    def run(self):
        self._init()
        while True:
            try:
                data, address = self.udp.recvfrom(1024)
                if len(data) > 0:
                    
                    print("Received {0} bytes via udp from {1}".format(len(data), address))
                    self.tcp.sendall(data)
                data2 = self.tcp.recv(1024)
                if len(data2) > 0:
                    print("Received {0} bytes via tcp".format(len(data2)))
                    print(data)
                    self.udp.sendto(data2, (self._dst, self._port))   
                    #self.udp.sendall(data, (self._dst, self._port))
            except Exception as e:
                print(e)

        self._deinit()

    def _deinit(self):
        self.tcp.shutdown(socket.SHUT_RDWR)
        self.udp.shutdown(socket.SHUT_RDWR)

if __name__ == '__main__':
    print ("Opening gateway ...")
    v = Udp2Tcp()
    time.sleep(120)
print ("Closing gateway ...")
Benutzeravatar
sls
User
Beiträge: 167
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Tannhauser Gate

Sonntag 18. März 2018, 11:52

Hi,

klingt etwas abenteuerlich. Wenn du sowieso SSH-Zugriff hast, warum routest du den Traffic für UDP Port 5555 nicht direkt über diesen Tunnel? Das Umbiegen und versenden via TCP 5555 ergibt für mich erstmal keinen Sinn. Die Schleife spricht für mich erstmal dafür, dass du bei TCP den selben Source und Destination Port verwendest, was per se nicht funktioniert.

Bei TCP lauscht eine Applikation auf einem Port (in deinem Beispiel 5555) über welchen eingehende Verbindungen entgegen genommen werden und versendet über einen i.d.R. dynamischen Port an den Destination Endpoint (welcher wiederrum an Port 5555 "lauscht").

Versendet deine Applikation nun lokal via 127.0.0.1:5555, wird sie sich selbst eine Nachricht schicken. Ungünstig.

Mfg
With great processing power comes great responsibility.
Sirius3
User
Beiträge: 8108
Registriert: Sonntag 21. Oktober 2012, 17:20

Sonntag 18. März 2018, 12:42

@py_lo: man kann nicht einfach so UDP-Pakete in einen TCP-Stream umwandeln. Wie schon die Namen sagen, gibt es bei UDP Pakete und TCP ist einfach nur ein Strom an Daten. Die Information, wie lang das Paket ist, muß also im Strom zusätzlich kodiert werden. Zudem brauchst Du zwei Threads, wenn Du bidirektional Daten austauschen willst.

@sls: bei den beiden Ports handelt es sich ja um UDP-Port 5555 und TCP-Port 5555, also zwei unterschiedliche Protokolle.
py_lo
User
Beiträge: 60
Registriert: Freitag 20. März 2009, 09:12

Sonntag 18. März 2018, 12:55

@Sirius3

Verstehe nicht waum das nicht funktionieren soll - unter Linux geht das ja ohne weiteres:

ssh -fN -R server:10023:localhost:5555 user@server # Tunnel zum Relay Server
socat TCP4-LISTEN:5555,reuseaddr,fork UDP:target:5555 # TCP<>UDP zum <target>

Hier kommt alles per ssh an port 5555 an und wird per netcat oder socat wie hier nach <target> UDP umgeleitet,
und das ganze funktioniert auch bidirektional.
Mein Problem auf der "client" Seite ist leider - abgesehen davon dass es Windows ist, das <target> = localhost ist.

@sls: SSH kann per se nu TCP tunneln.
__deets__
User
Beiträge: 3110
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 18. März 2018, 13:43

Es geht, aber nur zufällig. Könnte auch nicht klappen. Hängt zb an der MTU deiner Verbindung.

Zu deinem eigentlichen Problem: ich würde mal probieren zb dein relay nur auf localhost zu binden. Dann bleibt deiner Software das eigentliche Interface. Oder du kannst vll (geht unter Linux, kA ob Windows das auch kann) eine virtuelles/alias Interface anlegen. Last but not least sollte das ja zb über eine virtuelle Maschine gehen.
Sirius3
User
Beiträge: 8108
Registriert: Sonntag 21. Oktober 2012, 17:20

Sonntag 18. März 2018, 13:47

@Sirius3: socat ist nur unidirectional. Also nur TCP -> UDP. Und warum willst Du selbst was programmieren, wenn es doch schon socat gibt. Netzwerk-Programmierung ist nichts einfaches zu verstehen, wenn man sowas funktionierend und fehlerfrei hinbekommen will.
py_lo
User
Beiträge: 60
Registriert: Freitag 20. März 2009, 09:12

Sonntag 18. März 2018, 14:02

@__deets__:
Der relay ist auf localhost gebunden - auf der Windows Seite, in meinem Beispiel war das nur der Tunnel zum Linux System.
Ich möchte eben keine virtuelle Netzwerkkarte etc. soll später nicht nur von mir genutzt werden.

@Sirius3:
socat ist definitiv bi-direktional, sonst würde es bei mir ja auch nicht funktionieren.
Bekomme vom UDP Target über den Tunnel eine Rückmeldung.
__deets__
User
Beiträge: 3110
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 18. März 2018, 14:15

@py_lo was du möchtest ist eines. Was geht was anderes. Unter Linux kann man sockets mit einem flag zum reuse des Ports binden, damit können mehrere Programme auf ein und demselben Port sitzen. Wenn das unter Windows nicht geht, dann ist dein Wunsch Makulatur.

Scheint aber zu gehen: https://stackoverflow.com/questions/172 ... -same-port

Musst du ausprobieren ob es wirklich klappt. Unter Linux kommen dann zb alle Pakete bei jedem gebundenen
Socket an.
py_lo
User
Beiträge: 60
Registriert: Freitag 20. März 2009, 09:12

Sonntag 18. März 2018, 14:27

Okay, danke ich schau nochmal.
Nur, dass wir uns richtig verstehen -die Kommunikation funktiontiert grundsätzlich, nur muss ich
dem Script beibringen selbst gesendete UDP Pakete an localhost (vom Tunnel, TCP kommend) nicht als Pakete zu werten,
die wieder nach TCP weiter geleitet werden sollen.
Sirius3
User
Beiträge: 8108
Registriert: Sonntag 21. Oktober 2012, 17:20

Sonntag 18. März 2018, 14:34

@__deets__: die Pakete kommen nicht bei jedem gebundenen Socket an, sondern bei dem, der sie als erstes liest.

@py_lo: socat scheint ja, alles mögliche zu können, und was da im Hintergrund abläuft ist, ist ziemlich mysteriös. Was definitiv nicht geht, ist einfach so TCP-Streams in UPD-Pakete aufzuteilen. Dazu braucht man ein sauberes Protokoll auf TCP-Seite; was da socat macht, ist unklar.

Was für ein Gerät möchtest Du denn über UDP ansteuern und gibt es da nicht schon was fertiges?
py_lo
User
Beiträge: 60
Registriert: Freitag 20. März 2009, 09:12

Sonntag 18. März 2018, 15:48

Es sind tatsächlich keine "Streams" sonder eher kleine Datenpakete mit einer Länge < 32 Bytes, die per UDP
übertragen werden.
Sie dienen für mein Verständnis nur dazu das Gerät in den Programmiermodus zu versetzen und Dinge wie
Fortschritt der Programmierung etc. an die Software zurück zu melden.
Alles eigentlich sehr primitiv... aber der Protokoll ist unbekannt.

Die Software kommuniziert über einen zweiten TCP Kanal und schickt dort die eigentlichen Daten.
Leider geht das wiederrum nur wenn das Gerät vorher per UDP das "Magic" kommando bekommen hat.

Die Software soll einfach denken das Gerät ist lokal vorhanden...
Antworten