DynDns-“Ersatz”

Vor einiger Zeit habe ich mir einen RaspberryPi zugelegt und wollte den jetzt als kleinen Server verwenden. Als erstes Projekt bot sich hierfür ein voip-Server mit murmur als Kandidat meiner Wahl an. Auf das Einrichten soll später in einem anderen Artikel nocheinmal genauer eingegangen werden, hier geht es um ein ganz anderes Problem: Als Alice-Kunde mit dem Alice IAD 4421 als Router habe ich in der Routerkonfiguration keinerlei Möglichkeit einen DynDns-Dienst einzurichten. Mit anderen Worten: mein schöner Server funktioniert zwar ganz toll, nur aus dem Internet ist er nicht zu finden.

Wenn es also keine elegante Standardmethode gibt, muss eben eigenes Gefrickel her und so habe ich mir meinen eigenen kleinen “DynDns-Dienst” gebastelt:

Zunächst muss man verstehen, wie ein solcher Dienst überhaupt grundsätzlich arbeitet:

Da sich bei den Nutzern des Dienstes auf täglicher Basis die IP-Adressen ändern, bietet ein solcher Dyn-Dns-dienst (wie zum Beispiel no-ip.com) einen Service an, welcher über eine Subdomain ihrer eigenen Seite immer die jeweils aktuelle IP für den jeweiligen Benutzer bereithält. An die aktuelle IP kommen sie dabei durch den Nutzer selber: dieser muss nach der Erstellung eines Accounts (Im Beispiel jetzt einfach mal “test”) beim entsprechenden Anbieter dessen Daten in seinen Router eintragen. Dieser sendet daraufhin in periodischen Abständen immer wieder Daten an den Dienstanbieter, wodurch dieser immer die jeweils aktuelle IP-Adresse des Geräts bekommt. Bei Aufruf der URL test.no-ip.com bekommt dann ein dritter ebenjene IP mitgeteilt und kann sich mit dem Gerät verbinden, obwohl dessen IP-Adresse sich ständig ändert.

Überlegungen

Wie soll unser eigener DynDns-Dienst jetzt also funktionieren? Im großen und ganzen wie das original, nur machen wir es uns etwas einfacher  und verzichten auf großartige DNS-spielereien:

Ein Cronjob auf dem Pi ruft in periodischen Zeitabständen ein Php-Skript auf unserem Webspace auf. Dieses kennt dadurch die aktuelle IP-Adresse des Pi und speichert diese zwischen. Will nun ein anderes Gerät diese Adresse wissen, stellt es eine entsprechende Anfrage an das PHP-Skript und bekommt diese mitgeteilt.

Maßnahmen auf dem Webspace

Wir wollen ein kleines PHP-Script erstellen, welches das folgende beherrscht: Es soll per GET-Parameter ein Kommando entgegennehmen, welches bestimmt, ob die IP-Adresse gespeichert oder ausgegeben werden soll. Wird die Speichern-Option aufgerufen, soll das Script eine Textdatei auf dem Server öffnen und die IP-Adresse des Aufrufenden hineinschreiben. Wird die ausgeben-Option aufgerufen soll wiederrum die Textdatei geöffnet und die enthaltene IP-Adresse ausgegeben werden.

<?php
    error_reporting(E_ALL);

    //Aufrufe werden mitgeschrieben, um herauszufinden, ob der Cronjob auch wirklich immer seine Arbeit tut.
    //Außerdem wollen wir eventuell wissen, ob unauthorisierte auf das Script zugreifen.
    $timestamp = time();
    $datum = date("d.m.Y",$timestamp);
    $uhrzeit = date("H:i:s",$timestamp);
    $handle = fopen("access.log", "a") or die('Datei kann nicht zum Schreiben geöffnet werden');
    fwrite($handle, $datum." - ".$uhrzeit." Uhr : ".$_SERVER['REMOTE_ADDR'] ." - " . $_SERVER["HTTP_USER_AGENT"]. "\n");
    fclose($handle);

    //Der eigentliche Teil des Scripts
    if($_GET["write"]==1){
        //write ist 1, also zeichne die IP auf
        $dateiname = "ip.txt";
        $handle = fopen($dateiname, "w+") or die('Datei kann nicht zum Schreiben geöffnet werden');
        fwrite($handle, $_SERVER['REMOTE_ADDR'] . "\n");
        fclose($handle);
    }
    elseif($_GET["write"]==0){
        //write ist 0, also gebe IP aus
        readfile ('ipadress.txt');
    }
?>

Maßnahmen auf dem Pi

Zunächst richten wir uns einmal den Cronjob auf dem Pi ein:

Über Putty oder einfach direkt am pi öffnen wir uns eine Konsole und loggen uns als Benutzer Pi ein.

Anschließend verschaffen wir uns als erstes einmal root-Rechte:

   1:  sudo -s

Nun müssen wir die sogenannte Crontab bearbeiten, um Cron mitzuteilen, was es zu tun hat.

   1:  crontab -e

Es sollte sich daraufhin der Texteditor (Bei mir ist das Nano) mit einer Textdatei öffnen. In dieser müssen wir jetzt erstmal nach ganz unten Scrollen und wechseln hier in eine neue, leere Zeile. Der Cronjob, den wir hinzufügen wollen, sieht folgendermaßen aus:

*/10 * * * * wget --spider  "http://euredomain.de/dns/logip.php?write=1"

Kurz erklärt tut er das folgende: Er ruft alle 10 Minuten (*/10 , andere Intervalle sind natürlich möglich) wget im spider-Modus (Dieser bewirkt, dass wget keinerlei Daten auf dem Pi speichert) auf und übergibt diesem wiederrum die URL zu eurem PHP-Script. Wget ruft das Script auf und übermittelt diesem dadurch wiederum die IP-Adresse des Pi. Für eine genauere Beschreibung des Befehlsformats für Cronjobs kann ich http://www.adminschoice.com/crontab-quick-reference/ empfehlen)

 

Maßnahmen beim Client

Um auf das Beispiel Mumble und Murmur zurückzukommen, wollen wir jetzt noch ein kleines Programm basteln, welches die IP-Adresse von unserem PHP-Script ausgeben lässt und Mumble anweist, mit dem Server unter dieser IP Verbindung aufzunehmen:

Im entsprechenden Formular benötigt man zusätzlich die folgenden Steuerelemente:

1 x Button: Button1

1 x Textbox: TextBox1

Die Textbox dient dazu, sich einen Benutzernamen auszusuchen, unter dem man sich mit dem Server verbinden will. Das Programm macht dabei Gebrauch von den sogenannten mumble-URLs (Anleitung dazu siehe unter http://mumble.sourceforge.net/Mumble_URL )

Public Class Form1
    Public working As Boolean 'Wir wollen nicht, dass mehrmaliges Klicken auf den Button Probleme macht, daher führen wir eine Kontrollvariable ein

    Public Function CheckIPRegExp(ByVal sIP As String) As Boolean
        Dim pattern As String = "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
        Dim check As New Regex(pattern)
        Return check.IsMatch(sIP, 0)
    End Function

    ' Umlaute und Sonderzeichen verschlüsseln
    ' 
    ' UsePlusRatherThanHexForSpace:
    '  False:  Leerzeichen als %32 verschlüsseln
    '  True :  Leerzeichen als + verschlüsseln
    '  Quelle: http://www.vbarchiv.net/tipps/tipp_139-urlencode-und-urldecode.html
    Public Function URLEncode(ByVal StringToEncode As String, Optional ByVal _
      UsePlusRatherThanHexForSpace As Boolean = False) As String

        Dim TempAns As String = ""
        Dim CurChr As Integer

        CurChr = 1
        Do Until CurChr - 1 = Len(StringToEncode)
            Select Case Asc(Mid$(StringToEncode, CurChr, 1))
                Case 48 To 57, 65 To 90, 97 To 122
                    TempAns = TempAns & Mid$(StringToEncode, CurChr, 1)
                Case 32
                    If UsePlusRatherThanHexForSpace = True Then
                        TempAns = TempAns & "+"
                    Else
                        TempAns = TempAns & "%" & Hex(32)
                    End If
                Case Else
                    TempAns = TempAns & "%" & Hex(Asc(Mid$(StringToEncode, _
                      CurChr, 1)))
            End Select
            CurChr = CurChr + 1
        Loop
        URLEncode = TempAns
    End Function

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If working = True Then
            MsgBox("Abfrage läuft noch, bitte warten!")
            Exit Sub
        End If

        working = True

        Dim link As String = ""

        Try
            Dim wr As WebRequest = WebRequest.Create("http://euredomain.de/dns/logip.php?write=0")
            wr.Credentials = CredentialCache.DefaultCredentials
            wr.Timeout = 5000
            CType(wr, HttpWebRequest).UserAgent = "Whatever - " & TextBox1.Text

            Dim response As WebResponse = wr.GetResponse()
            Dim str As New StreamReader(response.GetResponseStream)
            Dim resp As String = str.ReadLine

            If CheckIPRegExp(resp) Then
                link = "mumble://" & URLEncode(TextBox1.Text) & "@" & resp & "?version=1.2.0"
                Process.Start(link)
            Else
                MsgBox("Fehler beim abrufen der IP, bitte später erneut versuchen!")
            End If

            working = False

        Catch ex As Exception
            MsgBox("Fehler beim abrufen der IP, bitte später erneut versuchen!")
            working = False
        End Try
    End Sub
End Class

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.