Susanne: "403 Forbidden" bei fsockopen() + fputs(GET / HTTP/1.0)

Hallo an alle,

bei der Programmierung eines Moduls zur Überprüfung der Ereichbarkeit von URLs habe ich ein Problem, zu dem ich keine Lösung finde:

Wenn ich mit fsockopen() eine TCP-Verbindung herstelle (wegen Proxyserver) und den HTTP-Request danach mit fputs() sende, reagieren manche Server so als hätte ich statt des GET Befehls einen PUT Befehl geschickt. Dementsprechend erhalte ich die Meldung 403 Forbidden.

Auf die PHP-Erweiterung Socketfunktionen kann ich nicht zurückgreifen, da es sich hier um eine Anwendung für Kunden handelt und nicht davon ausgegangen werden kann, dass deren Provider diese Erweiterung zur Verfügung stellen.

Hat irgendjemand eine Idee dazu???
Es ist wirklich wichtig....!

Grüße
Sue

  1. Hi,

    vielleicht solltest du zuerst mal erklären, was du eigentlich für ein Problem hast. Zuerst verlangst du eine Aufklärung über das Verhalten von fsockopen(), und dann schreibst du irgendwas davon, dass diese Erweiterung nicht verfügbar sein wird???

    E7

    1. Hi E7,

      vielleicht hätte ich das besser erklären sollen, aber die ich dachte es sei klar, dass die PHP-Erweiterung "Socketfunktionen" nicht zu den Grundfunktionen von PHP zählen, zu denen auch fsockopen() gehört. Die "Socketfunktionen" sind eine Erweiterung von PHP, die beim Compilieren integriert werden kann und mit der man mehr Möglichkeiten in Verbindung mit Sockets hat.

      Was ich vorhabe hatte ich eigentlich erklärt:
      Es geht um die Überprüfung der Erreichbarkeit bzw. Existenz von URLs per TCP-Verbindung und HTTP-Request.

      Die einfachste Möglichkeit ist die Funktion fopen($url, "r");

      Wenn man sich aber hinter einer Firewall (Proxyserver) befindet, muss man sich zunächst mit diesem verbinden und über ihn den HTTP-Request absenden. Vorgehensweise:

      $fp = fsockopen("tcp://".$proxyhost, $proxyport, &$errno, &$errstr, 30);
         if ($fp)
         {
            fputs($fp, "GET ".$url." HTTP/1.0\n\n");
            while (!feof($fp))
            {
               $reply = fgets($fp, $length_in_byte);
            }
         fclose($fp);
         }

      Genau dieses fputs($fp, "GET ".$url." HTTP/1.0\n\n") macht aber bei einigen wenigen Server Probleme, da diese den Request scheinbar als PUT und nicht als GET Request ansehen und darauf mit 403 Forbidden reagieren.

      Darauf gekommen bin ich nach 2 Tagen verzweifelter Sucherei und Testerei, indem ich mir mal die Befehlsfolge eines Browsers angesehen habe. Hierzu gibt es eine gute Seite, auf der man beliebige HTTP-Befehle auswählen und an irgendeinen Server senden kann. Danach wird angezeigt, was der Server antwortet. Die URL dieser Seite (falls es mal jemand braucht): http://www.bolege.de/whoiam/

      Nun kenne ich zwar den Grund meines Problems, aber leider keine Lösung. Die einzige Möglichkeit, mit PHP direkt HTTP-Requests abzusetzten scheint über diese PHP-Erweitung "Socketfunktionen" möglich zu sein und genau diese kann ich nicht benutzen.

      Kann mit irgendjemand einen Tipp geben oder geht hier wirklich gar nichts???

      Grüße
      Sue

      1. Moin!

        vielleicht hätte ich das besser erklären sollen, aber die ich dachte es sei klar, dass die PHP-Erweiterung "Socketfunktionen" nicht zu den Grundfunktionen von PHP zählen, zu denen auch fsockopen() gehört. Die "Socketfunktionen" sind eine Erweiterung von PHP, die beim Compilieren integriert werden kann und mit der man mehr Möglichkeiten in Verbindung mit Sockets hat.

        Du meinst http://de.php.net/manual/en/ref.sockets.php? Diese Funktionen helfen dir bei deinem Problem gar nichts.

        Die einfachste Möglichkeit ist die Funktion fopen($url, "r");

        Wenn man sich aber hinter einer Firewall (Proxyserver) befindet, muss man sich zunächst mit diesem verbinden und über ihn den HTTP-Request absenden.

        $fp = fsockopen("tcp://".$proxyhost, $proxyport, &$errno, &$errstr, 30);
           if ($fp)
           {
              fputs($fp, "GET ".$url." HTTP/1.0\n\n");
              while (!feof($fp))
              {
                 $reply = fgets($fp, $length_in_byte);
              }
           fclose($fp);
           }

        Genau dieses fputs($fp, "GET ".$url." HTTP/1.0\n\n") macht aber bei einigen wenigen Server Probleme, da diese den Request scheinbar als PUT und nicht als GET Request ansehen und darauf mit 403 Forbidden reagieren.

        Was steht in $url drin? Welcher Request kommt tatsächlich beim Server an? Welcher Request kommt beim Proxy an?

        Wenn du den Proxy kontaktierst und nach einer URL befragst, und du erhälst ein mangelhaftes Ergebnis, dann gibt es dafür nur zwei mögliche Gründe:

        1. Du hast den Proxy falsch gefragt.
        2. Der Proxy, auf den du keinerlei Einfluß hast, hat den Server falsch gefragt.

        Wenn du den Proxy standardgemäß korrekt fragst, dann sollte er seinerseits den Server korrekt fragen und folglich ein ordentliches Ergebnis bringen.

        403 forbidden ist jedenfalls Anzeige für alle möglichen Fehler, nicht nur, weil du PUT statt GET verwendet hast. Auch wenn dein Proxy explizit ausgesperrt wurde, würde diese Meldung kommen.

        Was mir noch auffällt: Du sendest keine "Host:"-Angabe an den Proxy. Damit kann es passieren, dass der Proxy auch keine an den Server sendet (wenngleich er diese aus der URL-Angabe konstruieren könnte), und VirtualHosts haben dann schon mal so ihre Probleme damit.

        In HTTP/1.0 ist "Host" eigentlich nicht vorgesehen, wird aber verwendet, und in HTTP/1.1 ist es zwingend vorgeschrieben.

        - Sven Rautenberg

        --
        "Beim Stuff für's Web gibts kein Material, was sonst das Zeugs ist, aus dem die Sachen sind."
        (fastix®, 13. Oktober 2003, 02:26 Uhr -> </archiv/2003/10/60137/#m338340>)
        1. Hallo Sven,

          erstmal vielen Dank für Deine Antwort.

          Wenn du den Proxy kontaktierst und nach einer URL befragst, und du erhälst ein mangelhaftes Ergebnis, dann gibt es dafür nur zwei mögliche Gründe:

          1. Du hast den Proxy falsch gefragt.
          2. Der Proxy, auf den du keinerlei Einfluß hast, hat den Server falsch gefragt.

          Ja, ich werde mir wohl am Montag doch mal die Logdateien vom Proxy vornehmen. Heute war ich nach der stressigen Woche einfach zu entnervt....

          403 forbidden ist jedenfalls Anzeige für alle möglichen Fehler, nicht nur, weil du PUT statt GET verwendet hast. Auch wenn dein Proxy explizit ausgesperrt wurde, würde diese Meldung kommen.

          Hab' mir auf der Heimfahrt schon überlegt, dass das wohl ein Denkfehler war - kommt davon, wenn man schon langsam viereckige Augen hat. :-)

          Was mir noch auffällt: Du sendest keine "Host:"-Angabe an den Proxy. Damit kann es passieren, dass der Proxy auch keine an den Server sendet (wenngleich er diese aus der URL-Angabe konstruieren könnte), und VirtualHosts haben dann schon mal so ihre Probleme damit.

          DAS verstehe ich nun nicht so ganz. Wo bzw. wie soll ich denn dem Proxy einen Host übergeben???

          Grüße
          Sue

          1. Moin!

            Was mir noch auffällt: Du sendest keine "Host:"-Angabe an den Proxy. Damit kann es passieren, dass der Proxy auch keine an den Server sendet (wenngleich er diese aus der URL-Angabe konstruieren könnte), und VirtualHosts haben dann schon mal so ihre Probleme damit.

            DAS verstehe ich nun nicht so ganz. Wo bzw. wie soll ich denn dem Proxy einen Host übergeben???

            Schau mal bei Gelegenheit etwas genauer in den HTTP-Standard. Schließlich sprichst du mit deinem Proxy HTTP, oder (ohne Proxy) direkt mit Servern. Also wäre es gut, wenn du korrektes HTTP mit den Servern sprichst.

            Zentraler Bestandteil der aktuellen Standards ist, dass du jedem Server (Situation ohne Proxy) nicht nur dein "GET _url-Wert_ HTTP/1.0" sagst, sondern in der nächsten Zeile noch mitteilst, welcher der möglicherweise vorhandenen virtuellen Hosts denn gemeint ist:

            Als Text (den du mit fputs() senden mußt:
            -----schnipp-----
            GET / HTTP/1.0
            Host: www.example.com

            -----schnapp-----

            Der Ende dieses Requests liegt vor, wenn zweimal Leerzeilen gesendet wurden ("\r\n\r\n").

            Nur auf diese Weise kannst du sicher sein, wirklich mit dem richtigen Server zu sprechen. Natürlich hat eigentlich jeder Server eine URL "/" - wenn du die testest, und auf dem Server sind mehrere virtuelle Hosts, und du gibst keinen Host im Request an, dann wird der standard-virtuelle Host genommen. Das ist u.U. eine ganz andere Website, als die, die du abfragen willst.

            Weil jedermann den Domainnamen in eine IP auflösen kann, und dann statt der Domain diese IP-Adresse verwenden kann, um den Server mal zu befragen, dabei dann mit dem standard-virtuellen Host verbunden wird - könnte man auf diese Weise herausfinden, wer sich denn noch so auf dem Server befindet. Deshalb wird dieser standard-virtuelle Server von Hostern gerne leergelassen bzw. eben mit 403 forbidden abgeschlossen. Als Zeichen, dass man irgendwas versucht hat, was nicht definiert ist.

            Und genau das paßt gut auf deine Beschreibung, was schiefläuft. Deshalb: Teste mal in einem Browser, was der fragliche 403-Server sagt, wenn du ihn nicht mit Namen, sondern mit IP aufrufst.

            - Sven Rautenberg

            --
            "Beim Stuff für's Web gibts kein Material, was sonst das Zeugs ist, aus dem die Sachen sind."
            (fastix®, 13. Oktober 2003, 02:26 Uhr -> </archiv/2003/10/60137/#m338340>)
      2. Moin,

        Genau dieses fputs($fp, "GET ".$url." HTTP/1.0\n\n") macht aber bei einigen wenigen Server Probleme, da diese den Request scheinbar als PUT und nicht als GET Request ansehen und darauf mit 403 Forbidden reagieren.

        Kann mit irgendjemand einen Tipp geben oder geht hier wirklich gar nichts???

        Also dass aus deinem GET ein PUT werden sollte mag ich noch nicht richtig glauben. Und wenn, dann solltest du mal ein ernstes Wort mit deinem Proxy reden.

        Ich würde zur weiteren Diagnose vorschlagen: Sieh dir die Logs des Proxies an. Installiere einen Sniffer (ethereal zum Beispiel) und überprüfe was von deinem Skript wirklich gesendet wird (vergleiche das mit dem was dein Browser wirklich sendet). Verwende den Sniffer möglichst auch auf der anderen Seite des Proxies und sieh dir an was dort aus deinen Anfragen geworden ist.

        --
        Henryk Plötz
        Grüße aus Berlin
        ~~~~~~~~ Un-CDs, nein danke! http://www.heise.de/ct/cd-register/ ~~~~~~~~
        ~~ Help Microsoft fight software piracy: Give Linux to a friend today! ~~
        1. Hallo Henry,

          auch dir vielen Dank für die Antwort und den Tipp mit Ethereal!

          Ich werde mir am Montag mal den Proxy, also die Logdateien vornehmen und schauen, was dabei rauskommt. Wahrscheinlich liegt das Problem einfach in dem von Andreas angeführten verbotenen Directory-Listing ohne Weiterleitung auf eine Startdatei wie z. B. index.html. Nur wie ich das lösen soll, weiß ich auch noch nicht...

          Grüße
          Sue

  2. Hallo!

    Wenn ich mit fsockopen() eine TCP-Verbindung herstelle (wegen Proxyserver) und den HTTP-Request danach mit fputs() sende, reagieren manche Server so als hätte ich statt des GET Befehls einen PUT Befehl geschickt. Dementsprechend erhalte ich die Meldung 403 Forbidden.

    Die Schlussforderung verstehe ich nicht, wenn dem wirklich so wäre udn es wäre kein PUT erlaubt, dann würde vermutlich eher ein

    405 (Method Not Allowed)

    oder ein

    501 (Not Implemented)

    kommen.

    Ich rate einfach mal ins Blau hinein, dass Du einen Request an / sendest, dieser Server aber kein Directory-Listing erlaubt und nicht automatisch auf eine andere Datei wie index.html weiterleitet.
    Öffne mal genau den Request-String über den Browser, was passiert da genau? Bleibt der unverändert in der Adresszeile?

    Und wenn as nicht hilft überprüfe mit Ethereal was genau Dein Rechner sendet und was genau der andere Server antwortet.

    Grüße
    Andreas

    1. Hallo Andreas,

      vielen Dank für die Tipps.

      Ich rate einfach mal ins Blau hinein, dass Du einen Request an / sendest, dieser Server aber kein Directory-Listing erlaubt und nicht automatisch auf eine andere Datei wie index.html weiterleitet.

      Das habe ich mir auf dem Heimweg dann auch überlegt - ich war heute Mittag wohl einfach schon zu verbohrt um noch klar zu denken ...

      Öffne mal genau den Request-String über den Browser, was passiert da genau? Bleibt der unverändert in der Adresszeile?

      Nein, bei der Adresse die das Problem verursacht bzw. bei der es aufgefallen ist, liegt die eigentliche Webpräsenz in einem Unterordner. Die Startdatei (default.html) wird überhaupt nicht angezeigt und scheint lediglich die Weiterleitung zu enthalten.
      Ich werde mir am Montag auf jeden Fall die Logdateien vom Proxy vornehmen.

      Und wenn as nicht hilft überprüfe mit Ethereal was genau Dein Rechner sendet und was genau der andere Server antwortet.

      Werde ich versuchen.

      Aber was, wenn es wirklich nur am Directory-Listing ohne Weiterleitung auf die Startdatei liegt (was ich inzwischen vermute)??? Wie kann ich verhindern, dass bei jedem Prüflauf des Moduls solche URLs als fehlerhaft gekennzeichnet werden?

      Grüße
      Sue

      1. Hi!

        Ich rate einfach mal ins Blau hinein, dass Du einen Request an / sendest, dieser Server aber kein Directory-Listing erlaubt und nicht automatisch auf eine andere Datei wie index.html weiterleitet.

        Das habe ich mir auf dem Heimweg dann auch überlegt - ich war heute Mittag wohl einfach schon zu verbohrt um noch klar zu denken ...

        Nein, ich glaube eher Svens Version ist die richtige, daran hatte ich nicht gedacht.

        Öffne mal genau den Request-String über den Browser, was passiert da genau? Bleibt der unverändert in der Adresszeile?

        Nein, bei der Adresse die das Problem verursacht bzw. bei der es aufgefallen ist, liegt die eigentliche Webpräsenz in einem Unterordner. Die Startdatei (default.html) wird überhaupt nicht angezeigt und scheint lediglich die Weiterleitung zu enthalten.

        Ja, aber wenn eine Weiterleitung gekommen wäre hättest Du das mit Deinem PHP-Script auch merken müssen, denn dann hätte irgendwas mit 200 oder 3xx zurückkommen müssen.

        Und wenn as nicht hilft überprüfe mit Ethereal was genau Dein Rechner sendet und was genau der andere Server antwortet.

        Werde ich versuchen.

        Aber was, wenn es wirklich nur am Directory-Listing ohne Weiterleitung auf die Startdatei liegt (was ich inzwischen vermute)??? Wie kann ich verhindern, dass bei jedem Prüflauf des Moduls solche URLs als fehlerhaft gekennzeichnet werden?

        Dieses Problem kannst Du dadurch vermeiden dass Du wirklich nur gültige Resourcen abfragst. Du musst jedesmal die URL finden, die den Statuscode 200 zurückgibt, die wird es immer geben, denn sonst könnte der Browser nichts anzeigen, in diesen Sonderfällen kann es halt sein dass es sich hierbei nicht um / oder /index.html handelt, sondern ggfs. ganz was anderes. Du könntest darüber hinaus noch Location-Headern bei 3xx Statuscodes folgen die Dich auf eine andere Adresse verweisen folgen, oder irgendwelche anderen Aktionen auslösen (Dich benachrichtigen...)

        Aber vermutlich wird das Senden eines Host-Headers das Problem lösen, wie Sven schon sagte.

        Grüße
        Andreas