Advanced Winsock: AcceptEx und DoS-Angriffe



  • Das getsockopt wird dir die Millisekunden zurückgeben die der Client bereits verbunden ist auch wenn noch keine Daten empfangen wurden.



  • Ups, sind wohl Sekunden.

    Das getsockopt wird dir nicht erst nach der AcceptEx Completion einen posiiven Wert liefern.



  • Laut Doku bekommt man nen Wert nachdem die Connection aufgebaut wurde (auch bevor Daten angekommen sind). Davor nen Fehler.

    Ich finde das allerdings trotzdem alles viel zu kompliziert.

    Vor allem muss man parallel Unmengen von AcceptEx Aufrufen pending haben wenn man nicht möchte dass langsame Clients den Server ausbremsen. Auch mit solchen Abfragen um extrem langsame Clients rauszuwerfen.

    Ohne Receive-Buffer bzw. mit dem guten alten accept() ist das nicht nötig.



  • > Ohne Receive-Buffer bzw. mit dem guten alten accept() ist das nicht nötig.

    Dafür ist accept() blockierend, nicht asynchron/overlapped und langsam.

    > Vor allem muss man parallel Unmengen von AcceptEx Aufrufen pending haben wenn man nicht möchte dass langsame Clients den Server ausbremsen.

    Ja, das Feature füge ich zu meinem Server jetzt mal hinzu. Neben dem Socket-Pool (dann spar ich mir die teure Erstellung von neuen Sockets beim AcceptEx()).

    > Ich finde das allerdings trotzdem alles viel zu kompliziert.

    Niemand hat behauptet, dass es einfach ist. 😉 Beste Performance hat seinen Preis.

    Ich habe btw. einen Weg gefunden, den ich aber erst morgen teste. Wenn ich den listen-Socket mit einem Event assoziiere (per WSAEventSelect()), das bei FD_ACCEPT signaled ist, dann weiß ich schon vor dem Aufruf von AcceptEx(), dass ein Client im Anmarsch (in der Listen-Queue) ist. Nach dem Ereignis gehe ich alle Sockets durch, die gerade in einer AcceptEx()-Operation stecken und prüfe mit getsockopt, ob und wie lange sie schon verbunden sind. Das könnte glatt funktionieren.

    Nun habe ich ein anderes Problem: Der Kernel-Mode belegt beim Aufruf von AcceptEx() Ressourcen, die (aus Performance-Gründen) nicht freigegeben werden, sollte die Overlapped-Operation abgebrochen werden. Auf Deutsch: Ich kann nicht einfach DisconnectEx() oder closesocket() auf einen Socket setzten, der im AcceptEx()-Pending steckt, aber noch keine Daten gesendet hat, sonst bekomme ich Memory-Leaks. Was kann ich da tun?



  • Ad aCTa schrieb:

    > Ohne Receive-Buffer bzw. mit dem guten alten accept() ist das nicht nötig.

    Dafür ist accept() blockierend, nicht asynchron/overlapped und langsam.

    Alter Schwede. Dann verwende AcceptEx, aber gieb KEINEN Receive Buffer mit.

    > Vor allem muss man parallel Unmengen von AcceptEx Aufrufen pending haben wenn man nicht möchte dass langsame Clients den Server ausbremsen.

    Ja, das Feature füge ich zu meinem Server jetzt mal hinzu. Neben dem Socket-Pool (dann spar ich mir die teure Erstellung von neuen Sockets beim AcceptEx()).

    Hast du es gemessen? Hast du einen Ahnung davon wie wenig das "teure" Erstellen eines Sockets im Vergleich zu anderen Operationen kostet?

    > Ich finde das allerdings trotzdem alles viel zu kompliziert.

    Niemand hat behauptet, dass es einfach ist. 😉 Beste Performance hat seinen Preis.

    Dir ist nicht mehr zu helfen.

    Ich habe btw. einen Weg gefunden, den ich aber erst morgen teste. Wenn ich den listen-Socket mit einem Event assoziiere (per WSAEventSelect()), das bei FD_ACCEPT signaled ist, dann weiß ich schon vor dem Aufruf von AcceptEx(), dass ein Client im Anmarsch (in der Listen-Queue) ist. Nach dem Ereignis gehe ich alle Sockets durch, die gerade in einer AcceptEx()-Operation stecken und prüfe mit getsockopt, ob und wie lange sie schon verbunden sind. Das könnte glatt funktionieren.

    Nun habe ich ein anderes Problem: Der Kernel-Mode belegt beim Aufruf von AcceptEx() Ressourcen, die (aus Performance-Gründen) nicht freigegeben werden, sollte die Overlapped-Operation abgebrochen werden. Auf Deutsch: Ich kann nicht einfach DisconnectEx() oder closesocket() auf einen Socket setzten, der im AcceptEx()-Pending steckt, aber noch keine Daten gesendet hat, sonst bekomme ich Memory-Leaks. Was kann ich da tun?

    Wo hast du denn die Info her?

    Ich finde es aber echt witzig wie realitäts-resistent du bist. Lass den Receive-Buffer bei AcceptEx weg, und alles wird gut. Aber ne. Du brauchst ja die bessere Performance. Obwohl du nichtmal nen Vergleich hast um wieviel es wirklich um ist. Naja. Jeder wie er meint.



  • Warum gönnst du ihm nicht den Spaß optimieren? 😡 😃 😡



  • > Wo hast du denn die Info her?

    Aus Network Programming For Microsoft Windows, eines der besten Bücher über WinSock, die es gibt. 🙄

    > Hast du es gemessen? Hast du einen Ahnung davon wie wenig das "teure" Erstellen eines Sockets im Vergleich zu anderen Operationen kostet?

    Relativ viel. Wesentlich weniger als ein WSARecv(). Und wie soll ich bitte messen, wenn ich nicht mal ein Programm habe? 😃 Der Accept-Prozess muss so kurz wie möglich sein. Da braucht man nicht noch Socket-Erstellung. (Wozu alte Sockets nicht wiederverwenden?)

    > Dir ist nicht mehr zu helfen.

    Doch, meine Frage ist immer noch offen.



  • Hi

    Und wie soll ich bitte messen, wenn ich nicht mal ein Programm habe?

    Verzeihe mir, aber bist du so oder tust du nur so?
    kannst ja die Zeit messen ! Wo ist das Problem ?

    sta = clock();
    end = clock();

    differenz vergleichen ! Und schon hast du die Zeit. 🙄 😉
    Und glaube mir Hustbaer hat recht ! Solltest lieber mal andere Operationen optimieren !

    lowbyte



  • > Wo ist das Problem ?

    Du hast mich da nicht ganz verstanden. Da ich noch keinen funktionierenden Server mit Io-Completionports programmiert habe, mit dem ich die Zeitmessung durchführen könnte, kann ich logischerweise auch noch nichts messen. Und selbst wenn ich einen Timer eingebaut hätte: die Zeit läge um 1ms, egal, ob ich select() oder Io-Completionports nutze, da das Loopback sehr schnell ist. Einen Server über X Indirektionen, auf dem ich das effektiv testen könnte, habe ich einfach nicht. 😃

    > Und glaube mir Hustbaer hat recht !

    Natürlich hat er Recht. Das, was ich hier baue wird nie 100000000 Clients gleichzeitig behandeln müssen. Aber trotzdem will ich die Technik dahinter verstehen. Ich weiß nicht, warum alle damit ein Problem haben. 🙄

    > Solltest lieber mal andere Operationen optimieren !

    Da gibt's nichts zu optimieren. Wenn die Verbindung von TCP erst einmal besteht, geht das alles recht schnell. Der teuerste Prozess ist der Verbindungsaufbau, den will ich optimieren.



  • Hi

    Der teuerste Prozess ist der Verbindungsaufbau, den will ich optimieren.

    So ein schwachsin !
    Was willst du da Optimieren.. du willst mir nicht sagen das dieser kleine aber nur winzige unterschied was ausmacht ?!!!
    Ich habe es getestet, und es bringt dir nichts absolut nicht. Weil sich nie 1000 Host auf einmal verbinden.Und ausserdem ist der Verbindungsaufbau nur initial, und wird wärend der Session nicht mehr ausgeführt, darum verstehe ich die Optimierungen((minimal) weniger geht nicht mehr) in diesem Fall nicht.

    Komprimierst du dein stream ? MTU(Maximum Transmission Unit) ? etc. etc. das sind alles Sachen die man sich mal überlegen sollte ! Und wie sieht es mit Encryption aus ? Da, solltest du beim Thema Sicherheit mal anfangen !!

    Und ich denke jetz giebt es nichts mehr zu sagen !

    lowbyte

    lowbyte



  • Hi

    Der teuerste Prozess ist der Verbindungsaufbau, den will ich optimieren.

    😃 😃

    Das war aber nicht dein ernst ?

    lowbyte



  • > Ich habe es getestet,

    Auf Localhost? Ja toll, das Loopback ist ja wohl keine reale Bedingung.

    > du willst mir nicht sagen das dieser kleine aber nur winzige unterschied was ausmacht ?!!!

    Doch, will ich.

    MSDN: A program can make a connection to a socket more quickly using AcceptEx instead of the accept function.

    NPFMW:
    Perhaps the most useful extension API for scalable TCP/IP servers is AcceptEx.
    [...] So the socket handle for the client needs to be created before posting the AcceptEx call. This is necessary because socket creation is expensive, and if a server is interested in handling client connections as fast as possible, it needs to have a pool of sockets already created on which new connections will be assigned.

    > solltest du beim Thema Sicherheit mal anfangen !!

    Ja, genau das steht im Thread-Titel, ich wollte diese große Sicherheitslücke stopfen, und dafür habe ich einen Weg gefunden. Sobald ich getesteten Code habe, kann ich ihn ja mal präsentieren.



  • Ich halte es zwar immer noch für Unsinn, aber wenn du unbedingt willst kannst du man probieren ob das Receive-Timeout (SO_RCVTIMEO) auch bei AcceptEx greift.

    Was du auch nicht zu verstehen scheinst: ausser dass minimal mehr CPU Zeit verbraten wird, macht es für die Geschwindigkeit der Verbindungsherstellung keinen Unterschied ob du accept, AcceptEx oder AcceptEx mit einem Receive-Buffer verwendest.

    Da gibt's nichts zu optimieren. Wenn die Verbindung von TCP erst einmal besteht, geht das alles recht schnell. Der teuerste Prozess ist der Verbindungsaufbau, den will ich optimieren.

    Sorry, aber das ist auch Unsinn. Mach dich mal über Dinge wie Delayed ACK und Nagles Algorithms schlau.



  • Präsentier mal deinen Code bitte! 🙂



  • Lad dir den OllyDebugger runter: http://www.ollydbg.de/

    Debugg deine Connect Funktionen oder was weiss ich was du da alles hast und Optimier die viel spass. ^^

    Hier hat wer so etwas mit der strcmp Funktion gemacht:

    http://www.masm32.com/board/index.php?PHPSESSID=de82d816a4afabcbe608a5b512dc126f&topic=2508.0



  • OllyDbg habe ich bereits und nutze ihn auch manchmal. 😉

    @ sehenwoller:

    Das sind mittlerweile ~400 Zeilen, die ich hier erst mal nicht poste.

    > Was du auch nicht zu verstehen scheinst: ausser dass minimal mehr CPU Zeit verbraten wird, macht es für die Geschwindigkeit der Verbindungsherstellung keinen Unterschied ob du accept, AcceptEx oder AcceptEx mit einem Receive-Buffer verwendest.

    Ja keine Ahnung... Ich will es trotzdem selber messen und die Skalierbarkeit sehen. Ich nehme deine Meinung jetzt einfach mal so hin. 🙂

    So, jetzt ist die Sache schon ziemlich komplex geworden. Da ein AcceptEx() zu wenig ist, will ich nun mehrere haben, und so soll das ganze nun so funktionieren:

    Ich setzte eine obere und untere Grenze für laufende AcceptEx-Calls (also solche, bei denen noch kein Client in Bedienung ist), die alle ein eigenes OVERLAPPED mit Event haben. Mit WASWaitForMultipleEvents() komme ich an den Index des Overlapped-Arrays und kann mit dem auf den Index des Accept-Socket-Arrays schließen und den Client weiter behandeln.
    Jetzt bleibt noch das selbe Problem wie oben: was, wenn jemand alle AcceptEx belegt (keine Daten sendet)? Natürlich das selbe nochmal: Mit WSAEventSelect und FD_ACCEPT den Listen-Socket assoziieren. Nur, jetzt bin ich etwas darüber verwirrt, wann FD_ACCEPT signalisiert wird. Im magischen Buche steht:

    On Windows 2000 and later versions, Winsock provides a mechanism for determining if an application is running behind in posting adequate AcceptEx calls. When creating the listening socket, associate it with an event by using the WSAEventSelect API call and registering for FD_ACCEPT notification. If there are no pending AcceptEx operations but there are incoming client connections (accepted by the system according to the backlog value), then the event will be signaled. This can even be used as an indication to post additional AcceptEx operations.

    Das heißt, dass FD_ACCEPT nur signaled ist, wenn ein Client statt im Accept in der Backlog-Queue landet? Also wenn es kein accept gibt, welches den Client aufnimmt. Bisher dachte ich, dass jeder Client zunächst in die Backlog-Queue kommt (unabhängig, ob ein accept da ist oder nicht), dadurch FD_ACCEPT signaled ist und dann evtl. ein Accept aufgerufen wird. 😕



  • Okay, meine Vermutung hat sich bestätigt. 🙂 FD_ACCEPT tritt nur ein, wenn ein Client in der Queue landet, und das passiert wirklich nur, wenn kein Accept-Call läuft.
    Jetzt läuft das ganze schon ganz chick, nur eröffnet sich jetzt ein kleines neues Problemfeld: was fange ich mit dem Socket hat, hinter dem ein Angreifer sitzt und den AcceptEx-Call und den Socket blockiert? Die erste Antwort wäre closesocket(), nur müsste ich dann im Rahmen den Accept-Prozesses mit WSASocket() einen neuen erstellen, und das wird bei vielen Sockets etwas zu teuer.
    Nun habe ich DisconnectEx() gefunden. Nur irgendwie ist die Funktion an einer Stelle doof: sie findet ihre Completion erst nach 120s (Wartezeit zwischen Disconnect und dem Re-Use (!) des Handles). Also 120s Funkstille am Server? Es ist ja in dem Moment kein Socket-Handle mehr frei! Das kann nicht gut gehen. Kann man einen Client irgendwie ganz schnell einfach los werden, ohne das Handle zu opfern?



  • Ad aCTa schrieb:

    Die erste Antwort wäre closesocket(), nur müsste ich dann im Rahmen den Accept-Prozesses mit WSASocket() einen neuen erstellen, und das wird bei vielen Sockets etwas zu teuer.

    Hast du es denn ausprobiert?
    Nein?
    Eben.
    Aber warum einfach wenns kompliziert auch geht.



  • Auch bei closessocket() sollten sich die Sockets danach noch einige Zeit im TIME_WAIT State befinden.



  • Du kannst mal versuchen den Socket mittels setsockopt auf "no linger" zu stellen bevor du eine Verbindung schnell loswerden willst. Dann sollte IMHO DisconnectEx auch "sofort" fertig werden.


Anmelden zum Antworten