mboost-dp1

Redirect incoming connection


Gå til bund
Gravatar #1 - Stewart
21. feb. 2012 08:11
Hej,

Jeg vil lige starte med at sige at jeg ikke er nogen haj til VB.NET, jeg er først lige ved at lære det nu.
Men jeg har nedenstående kode, hvor jeg har en TcpListener og det virker fint nok, jeg kan sagtens forbinde til den.
Det jeg gerne vil, er at i stedet for at den skal forbinde til den server, så skal den "redirectes" over til en anden server på en anden port.
Kan dette lade sig gøre og i så fald, hvordan ?


Const portNumber As Integer = 6666
Dim ipAdd As IPAddress = IPAddress.Parse("127.0.0.1")
Dim TcpListener As New TcpListener(ipAdd, portNumber)
TcpListener.Start()
Console.WriteLine("Waiting for connection...")
Dim tcpClient As TcpClient = TcpListener.AcceptTcpClient()
Console.WriteLine("Connection accepted.")
Gravatar #2 - kasperd
21. feb. 2012 09:29
Jeg kender ikke det sprog du bruger. Men det kører øjensynligt ovenpå en socket API. Det du efterspørger kan man ikke med socket APIen.

Det du kan gøre er at have to sockets, en fra klienten og en anden til den server du vil sende trafikken videre til. Alt hvad du læser på den ene socket skal du så skrive på den anden socket.

Det mest tricky i at implementere ovenstående er at få en korrekt håndtering af end of stream. Da der bruges en enkelt socket til kommunikation i begge retninger og man skal kunne håndtere at den ene retning lukkes før den anden har man brug for et kald der kan lukke en socket i den ene retning. I posix hedder det kald shutdown. Det hedder sikkert noget lignende i det sprog du bruger.

Når du på den ene socket har set end of stream skal du bruge shutdown kaldet til at lukke den anden socket for skrivning. På den måde vil den anden ende af den socket også se end of stream.

Du skal huske på at du har set end of stream på den ene socket, så du ikke længere prøver på at læse fra den.

Når du så ser end of stream på den anden socket kan du lukke dem begge.

Ovenstående metode har den konsekvens at den server du forbinder til vil se din IP adresse som klientadresse og altså ikke IP adressen på den klient der forbinder til din kode.

I nogle tilfælde er det ønskværdigt, i andre tilfælde ville man hellere have at serveren så den oprindelige IP adresse. Hvis der kommunikeres HTTP over dine sockets kan du indsætte en x-forwarded-for header for at angive klientens adresse. Hvis du bruger en anden protokol, så er det vist de færreste der har noget der ligner x-forwarded-for.

En helt anden metode er at implementere det vha. af NAT. Så skal det foregå på pakkeniveau, og du skal ikke bruge en tcplistener. Sidstnævnte afhænger dog af den netværksopsætning du bruger, så jeg kan ikke komme med nogen forslag til hvordan du gør det, uden at vide hvordan netværket du vil bruge det på ser ud.
Gravatar #3 - arne_v
22. feb. 2012 17:37
#1

Jeg er meget enig med Kasper men jeg vil nok forklare det lidt anderledes.

Der er ikke support for redirect i rå TCP, så du vil blive nødt til selv at implementere redirect i din protokol oven på TCP.

VB.NET kode:


Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Sockets

Namespace E
Public Class Program
Public Shared Sub Main(args As String())
Dim srv As New TcpListener(IPAddress.Any, 6666)
srv.Start()
Using cli As TcpClient = srv.AcceptTcpClient()
Dim sw As New StreamWriter(cli.GetStream())
sw.WriteLine("Please try address nnn.nnn.nnn.nnn port mmmm instead!")
sw.Close()
End Using
srv.Stop()
End Sub
End Class
End Namespace


Hvis det er HTTP, du bruger så er der indbygget redirect i protokollen og du kan bruge dette.

I så fald bør du dig bruge HttpListener og ikke TcpListener for at undgå at skulle kode for meget selv.

VB.NET kode:


Imports System
Imports System.IO
Imports System.Net

Namespace E
Public Class Program
Public Shared Sub Main(args As String())
Using srv As New HttpListener()
srv.Prefixes.Add("http://localhost:6666/")
srv.Start()
Dim ctx As HttpListenerContext = srv.GetContext()
Dim req As HttpListenerRequest = ctx.Request
Dim resp As HttpListenerResponse = ctx.Response
resp.StatusCode = 301
resp.RedirectLocation = "http://www.google.com/"
resp.Close()
srv.Stop()
End Using
End Sub
End Class
End Namespace


I de fleste realistiske servere ville der så være en løkke således at server programmet kan servicere mere end en enkelt klient.
Gravatar #4 - Stewart
28. feb. 2012 11:39
Det er TCP, og det jeg prøver på, er at redirecte alle RDP connection over til en anden server.
Gravatar #5 - kasperd
28. feb. 2012 13:01
Stewart (4) skrev:
det jeg prøver på, er at redirecte alle RDP connection over til en anden server.
Uden at vide hvordan dit netværk ser ud kan jeg ikke sige om det er bedst at gøre det på et NAT lag under TCP eller på applikationslaget ovenpå TCP.

Begge dele kan lade sig gøre. Jeg ved ikke hvordan man sætter den slags op på Windows. Havde det været Linux ville jeg enten bruge iptables til at gøre det med NAT eller xinetd hvis jeg havde brug for en løsning på applikationslaget.
Gravatar #6 - kasperd
28. feb. 2012 15:46
kasperd (5) skrev:
Havde det været Linux ville jeg enten bruge iptables til at gøre det med NAT eller xinetd hvis jeg havde brug for en løsning på applikationslaget.
Ved nærmere eftertanke, så så jeg engang at en af mine venner brugte et værktøj til Windows, der kunne sætte forwardings op.

Jeg ved ikke med sikkerhed om det gjorde brug af noget NAT funktionalitet i Windows, eller om det implementerede forwardings på applikationslaget. Da det vist nok var en standalone applikation tror jeg det implementerede forwardings på applikationslaget.

Jeg kan ikke huske hvad programmet hed.
Gravatar #8 - Stewart
2. mar. 2012 07:26
#7 det kunne muligvis godt bruges :)
Gravatar #9 - kasperd
2. mar. 2012 08:13
hundeboll (7) skrev:
http://www.quantumg.net/portforward.php
Jeg kiggede på sourcekoden til kommandolinje versionen, og opdagede en bug i håndteringen af lukning af forbindelser.

Jeg ved godt nok ikke, hvad closesocket gør, men eftersom han kalder samme funktion på begge sockets må det nødvendigvis gøre noget forkert på den ene af de to.

Jeg ved ikke præcist hvad der deles mellem tråde, men eftersom der efter accept af en forbindelse vil være tre tråde (hovedtråden og en til håndtering af data i hver retning), og to af de tre tråde kalder closesocket, må der nødvendigvis være en bug mere.

Hvis sockets kopieres ved start af tråde vil de to child tråde begge lukke deres eksemplar, men hovedtråden lukker ikke sit eksemplar og vil dermed lække sockets indtil det ikke kan håndtere flere tråde.

Hvis sockets ikke kopieres men derimod deles mellem trådene, så vil hver child tråd prøve at lukke de samme sockets. Det vil sige at det vil forsøge at lukke en socket, der allerede er lukket.

Hvis en socket i den API han bruger håndteres med file descriptor numre ligesom på Unix vil det nok ofte gå godt, men i enkelte tilfælde kan der ske mærkelige ting.

Den ene tråd vil lukke de to descriptors, den anden tråd vil på det tidspunkt sandsynligvis være blokkeret i et read kald. Den vil nok hurtigt få et end of stream og derefter forsøge at lukke de to descriptors. Men det vil ikke længere være de samme descriptors. Hvis numrene er blevet genbrugt i mellemtiden vil den lukke descriptors hørende til en anden nyligt oprettet forwarding.

Derudover er det ikke kønt at der er to forskellige funktioner til de to tråde, som gør praktisk taget det samme. Hvis han lige ville have ofret to linier mere på koden til at starte trådene kunne han helt have undværet den ene af de to funktioner.
Gravatar #10 - arne_v
2. mar. 2012 13:40
kasperd (9) skrev:
Jeg ved godt nok ikke, hvad closesocket gør,


WinSock closesocket svarer til *nix close på en socket.

Gravatar #11 - kasperd
3. mar. 2012 16:38
arne_v (10) skrev:
WinSock closesocket svarer til *nix close på en socket.
Vil det sige at winsock også bruger en integer i user space til at angive hvilken socket der refereres til, og opbevarer alle andre datastrukturer relateret til denne socket i kernen? Det er i hvert fald den eneste måde man kan opnå præcist samme semantik. Eller mener du med "svarer til" blot at de ca. gør det samme?

Ved du tilfældigvis hvordan man lukker en socket for skrivning uden at samtidigt lukke den for læsning med winsock?

Jeg tog også et kig på koden til den grafiske udgave, og nogle af de bugs jeg påpegede er retter deri, men ikke dem alle. Der er stadig bugs med håndtering af en halvlukket forbindelse, og den prøver stadigvæk at lukke hver socket to gange.

Synkronisering mellem de to tråde ved afslutning er implementeret ved at den ene har en løkke hvor den sover i 10 sekunder ad gangen imens den venter på at den anden afslutter. Ikke specielt kønt, men det kan da godt være det virker.

Hvis man tilføjer de nødvendige kald til at lukke hver socket for skrivning på det rigtige tidspunkt og flytter kaldene til at lukke de to sockets ind i den gren af koden som kun køres af den ene tråd, når den anden er afsluttet, så vil man have noget der så vidt jeg kan gennemskue burde virke.
Gravatar #12 - arne_v
3. mar. 2012 16:57
#11

Ja.

int sd;
...
sd=socket(AF_INET,SOCK_STREAM,0);
...
#ifdef win32
closesocket(sd);
#else
close(sd);
#endif


Gravatar #13 - arne_v
3. mar. 2012 17:02
#11

Winsock har shutdown med samme argumenter som på *nix og jeg vil formode at man bruger dem til at lukke for kun send eller kun receive.
Gravatar #14 - kasperd
3. mar. 2012 17:08
arne_v (13) skrev:
Winsock har shutdown med samme argumenter som på *nix og jeg vil formode at man bruger dem til at lukke for kun send eller kun receive.
Dokumentation her: http://msdn.microsoft.com/en-us/library/windows/de...

Eneste forskel er tilsyneladende navnene på de forskellige værdier. På winsock skal man skrive SD_SEND i stedet for SHUT_WR.

Hvis der er nogen som har mulighed for at compilere koden kan de jo prøve at foretage de ændringer, jeg har skitseret for at rette fejlene.
Gå til top

Opret dig som bruger i dag

Det er gratis, og du binder dig ikke til noget.

Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.

Opret Bruger Login