DdnsAutoLogin

Aus Synology Wiki
Wechseln zu: Navigation, Suche

In diesem Artikel geht es um die Möglichkeit, sich bei DDNS-Diensten, wie beispielsweise NoIP.com, mittels Python-Script automatisch anmelden zu können, um das Löschen der Hosts nach 30 Tagen zu verhindern.

Der offizielle Weg wäre sich bei dem jeweiligen Dienst einen Pro Account für meistens 25$ im Jahr zu holen, um das Löschen der Hosts nach 30 Tagen zu verhindern.

Voraussetzungen

  • DSM >= 3.2
  • Python >= 2.6 (nicht kompatibel mit 3.x!)

Python 2.x kann problemlos über das DSM-Paketzentrum gedownloadet und auch bei installiertem Python 3.x parallel installiert und betrieben werden.

Das Script

Dyn.com (ehemals DynDns.org)

#!/bin/python2

import urllib
import urllib2
import cookielib
import getopt
import sys
import logging

def getRandHTMLResponse(response):
    target = "<form id=\'login"
    targetresponse = "<div id=\'loginbox\'"

    response = response[response.find(targetresponse):len(response)]
    return response[response.find(target)+len(target):response.find(target)+len(target):response.find(target)+len(target)+4]

def getHiddenRandHTMLResponse(response):
    target = "<input type=\'hidden\' name=\'multiform\' value=\'"
    targetresponse = "<div id=\'loginbox\'"
    parsedres = response[response.find(targetresponse):len(response)]
    return parsedres[parsedres.find(target)+len(target):parsedres.find(target)+len(target)+34]

def checkLogin(response):
    target = "<title>My Dyn Account</title>"
    if response.find(target) == -1:
        return False
    return True

def usage():
    print "usage: ./dyndnsAutoLogin [options]"
    print ""
    print "options:"
    print "-h, --help 	 show this help message and exit"
    print "-u, --username   set your DynDns login_username"
    print "-p, --password   set your DynDns login_password"
    print ""
    print "example:"
    print "./dyndnsAutoLogin -u username -p password"

class HTMLSession:
    cj = None
    opener = None
    txHeaders = None
    
    def __init__(self, txHeaders):
        #The CookieJar will hold any cookies necessary throughout the login process.
        self.cj = cookielib.MozillaCookieJar()
        self.txHeaders = txHeaders
        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj))
        urllib2.install_opener(self.opener)

    def setHeaders(self, txheaders):
        self.txHeaders = txHeaders

    def getHeaders(self):
        return self.txHeaders

    def openURI(self, uri, txdata):
        try:
            req = urllib2.Request(uri, txdata, self.txHeaders)
            # create a request object

            handle = urllib2.urlopen(req)
            # and open it to return a handle on the url

        except IOError as e:
            print 'we failed to open "%s".' % uri

            if hasattr(e, 'code'):
                print 'We failed with error code - %s.' % e.code
                logging.error('We failed with error code - %s.' % e.code)
            elif hasattr(e, 'reason'):
                print "The error object has the following 'reason' attribute :"
                print e.reason
                print "This usually means the server doesn't exist,'"
                print "is down, or we don't have an internet connection."
                return None
        else:
            return handle.read()

def main(argv):
    username = "meine-dyn.com-eMail@beispiel.de"
    password = "mein-dyn.com-Login-Passwort"
    logfile = "/var/log/dyndnsAutoLogin.log"
    hiddenval = ""
    theurl = "https://account.dyn.com/entrance/"
    thelogouturl = "https://account.dyn.com/entrance/?__logout=1"
    txdata = None
    txheaders =  {'User-agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
    # fake a user agent, some websites (like google) don't like automated exploration
    logging.basicConfig(filename=logfile,level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y/%m/%d %H:%M:%S')

    try:
        opts, args = getopt.getopt(argv, "hu:p:", ["help", "username=","password="])
    except getopt.GetoptError:
        usage()
        logging.warning('Manual login with incorrect parameters')
        exit(2)
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage()
            exit(2)
        elif opt in ("-u", "--username"):
            username = arg
        elif opt in ("-p", "--password"):
            password = arg

    myhtmlsession = HTMLSession(txheaders)
    response = myhtmlsession.openURI(theurl, None)

    if response == None:
        sys.exit(0)

    hiddenval = getHiddenRandHTMLResponse(response)
    txdata = urllib.urlencode({'username':username, 'password':password, 'multiform':hiddenval, 'submit': "Log in"})

    response = myhtmlsession.openURI(theurl, txdata)
    if response == None:
        sys.exit(0)
    
    #we should sleep here for about 10 seconds.
    if checkLogin(response):
        print 'We have succesfully logged into DynDNS.'
        logging.info('We have succesfully logged into DynDNS.')
    else:
        print 'Login failed'
        logging.info('Login failed')

    response = myhtmlsession.openURI(thelogouturl, None)
    if response == None:
        sys.exit(0)

if __name__ == "__main__":
    main(sys.argv[1:])

NoIP.com

#!/bin/python2

import urllib
import urllib2
import cookielib
import getopt
import sys
import logging

def getHiddenRandHTMLResponse(response):
    target = "<input type=\"hidden\" name=\"_token\" value=\""
    targetresponse = "<div id=\"sign-up-wrap\""
    parsedres = response[response.find(targetresponse):len(response)]
    return parsedres[parsedres.find(target)+len(target):parsedres.find(target)+len(target)+40]

def checkLogin(response):
    target = "<title>  My No-IP"
    if response.find(target) == -1:
        return False
    return True

def usage():
    print "usage: ./noipAutoLogin [options]"
    print ""
    print "options:"
    print "-h, --help 	 show this help message and exit"
    print "-u, --username   set your NoIP login_username"
    print "-p, --password   set your NoIP login_password"
    print ""
    print "example:"
    print "./noipAutoLogin -u username -p password"

class HTMLSession:
    cj = None
    opener = None
    txHeaders = None
    
    def __init__(self, txHeaders):
        #The CookieJar will hold any cookies necessary throughout the login process.
        self.cj = cookielib.MozillaCookieJar()
        self.txHeaders = txHeaders
        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj))
        urllib2.install_opener(self.opener)

    def setHeaders(self, txheaders):
        self.txHeaders = txHeaders

    def getHeaders(self):
        return self.txHeaders

    def openURI(self, uri, txdata):
        try:
            req = urllib2.Request(uri, txdata, self.txHeaders)
            # create a request object

            handle = urllib2.urlopen(req)
            # and open it to return a handle on the url

        except IOError as e:
            print 'we failed to open "%s".' % uri

            if hasattr(e, 'code'):
                print 'We failed with error code - %s.' % e.code
                logging.error('We failed with error code - %s.' % e.code)
            elif hasattr(e, 'reason'):
                print "The error object has the following 'reason' attribute :"
                print e.reason
                print "This usually means the server doesn't exist,'"
                print "is down, or we don't have an internet connection."
                return None
        else:
            return handle.read()

def main(argv):
    username = "meine-NoIP-eMail@beispiel.de"
    password = "mein-NoIP-Login-Passwort"
    logfile = "/var/logs/noipAutoLogin.log"
    hiddenval = ""
    theurl = "https://www.noip.com/login"
    thelogouturl = "https://www.noip.com/logout"
    txdata = None
    txheaders =  {'User-agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
    # fake a user agent, some websites (like google) don't like automated exploration
    logging.basicConfig(filename=logfile,level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y/%m/%d %H:%M:%S')

    try:
        opts, args = getopt.getopt(argv, "hu:p:", ["help", "username=","password="])
    except getopt.GetoptError:
        usage()
        logging.warning('Manual login with incorrect parameters')
        exit(2)
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage()
            exit(2)
        elif opt in ("-u", "--username"):
            username = arg
        elif opt in ("-p", "--password"):
            password = arg

    myhtmlsession = HTMLSession(txheaders)
    response = myhtmlsession.openURI(theurl, None)
    if response == None:
        sys.exit(0)

    hiddenval = getHiddenRandHTMLResponse(response)
    txdata = urllib.urlencode({'username':username, 'password':password, 'Login':"1", 'submit_login_page':"1", '_token':hiddenval, 'Login': "Sign In"})
    response = myhtmlsession.openURI(theurl, txdata)
    if response == None:
        sys.exit(0)
    
    #we should sleep here for about 10 seconds.
    if checkLogin(response):
        print 'We have succesfully logged into NoIP.'
        logging.info('We have succesfully logged into NoIP.')
    else:
        print 'Login failed'
        logging.info('Login failed')

    response = myhtmlsession.openURI(thelogouturl, None)
    if response == None:
        sys.exit(0)

if __name__ == "__main__":
    main(sys.argv[1:])

Wer noch DSM <= 4.x benutzt muss die erste Zeile mit folgender ersetzen:

#!/volume1/@appstore/Python/usr/local/bin/python

Wer noch DSM = 5.x benutzt muss die erste Zeile mit folgender ersetzen:

#!/usr/local/packages/@appstore/Python/usr/local/bin/python

Weitere Script-Wünsche für andere DDNS-Anbieter können gerne im Forum erfragt werden: Link

Einstellungen und Parameter

In der "def main" (ca. Zeile 81) lassen sich folgende Einstellungen treffen:

  • username: Dein DDNS Benutzername
  • password: Dein DDNS Passwort
  • logfile: Der Pfad zum Logfile (Standardmäßig /var/log/ddnsAutoLogin.log, hier empfiehlt sich aber ein Pfad, der auch über die FileStation erreichbar ist, um das Logfile auch mit der DSM auslesen, bzw. herunterladen zu können. Beispielsweise einen Gemeinsamen Ordner. "/volume1/Pfad/zum/Gemeinsamen/Ordner/ddnsAutoLogin.log")
  • hiddenval: Wert für versteckte Elemente in Formularen. (Ist zufallsgeneriert und wird vom Script auf der Seite automatisch ausgelesen und eingetragen)
  • theurl: Die URL zur Login-Seite
  • thelogouturl: Die URL zur Logout-Seite
  • txdata: Muss auf "None" stehen!
  • txheaders: Als welcher Browser sich das Script ausgeben soll (hier Mozilla/4.0)

Wichtig ist hier alles in doppelten Anführungszeichen zu schreiben ( " " )

Für Konsolen-Benutzer: Benutzername und Passwort können auch über die jeweiligen Parameter (-u username -p password) dem Script übergeben werden.

Installation und Einbinden mittels DSM Aufgabenplaner

  • Das Script in die Zwischenablage kopieren und in einen Editor seiner Wahl einfügen. (Der Editor muss "Line ending = LF" unterstützen und es muss auch eingestellt sein! Im selben Zug kann man auch schauen ob der Editor Tabs durch 4 Leerzeichen ersetzt und dies auch einstellen. Wenn man das Script hier rauskopiert entfällt das, da die Tabs schon ersetzt wurden.)
  • Einstellungen (Benutzername, Passwort und Logfile) eintragen
  • Das Script mit dem Namen "ddnsAutoLogin" speichern (Wichtig: ohne Dateiendung und wie schon erwähnt mit "Line ending = LF"!)
  • Kopieren des Scripts auf die NAS. Am besten mit der FileStation oder über eine Netzfreigabe in einen Ordner seiner Wahl. (Ich habe es bei mir nach /volume1/install/scripts/ kopiert)
  • Aufrufen des Aufgabenplaners in der DSM-Systemsteuerung
  1. Erstellen / Benutzer-definiertes Skript
  2. Allgemeine Einstellungen / Vorgang: ddnsAutoLogin (Beliebiger Name für den Aufgabenplan)
  3. Allgemeine Einstellungen / Benutzer: root
  4. Befehl ausführen: /volume1/install/scripts/ddnsAutoLogin (hier den richtigen absoluten Pfad und Namen zu seinem vorher gespeicherten Script)
  5. Unter Zeitplan nun das gewünschte Intervall einstellen (Ich empfehle einmal täglich. Mindestens jedoch mehr als einmal im Monat!)
  6. Fertig!

Um zu prüfen ob das Script ordnungsgemäß funktioniert liest man einfach in seiner Logfile (/var/log/ddnsAutoLogin.log) nach. Diese sieht so aus:

2013/05/30 00:00:06 - INFO - We have succesfully logged into DynDNS.
2013/05/30 08:00:05 - INFO - We have succesfully logged into DynDNS.
2013/05/30 16:00:04 - INFO - We have succesfully logged into DynDNS.
2013/05/30 17:36:43 - INFO - Login failed

Häufige Fehler

  • Beim Kopieren bzw. Bearbeiten des Scripts wurden Tabs anstelle von Leerzeichen verwendet (1 Tab muss durch 4 Leerzeichen ersetzt werden!)
  • Der Benutzername, das Passwort oder die Logfile wurde ohne doppelte Anführungszeichen ( " " ) in das Script eingetragen
  • Das Logging funktioniert nicht bei gesetztem relativen Pfad (logfile = "./ddnsAutoLogin.log") wenn man den Aufgabenplaner benutzt.
  • Falsche Python Version installiert. Python < 2.6 wirft folgende Fehlermeldung:
DiskStation> ./ddnsAutoLogin 
./ddnsAutoLogin:66: Warning: 'as' will become a reserved keyword in Python 2.6
  File "./ddnsAutoLogin", line 66
    except IOError as e:
                    ^
SyntaxError: invalid syntax

Wenn Python mittels Paketmanager installiert wurde (>=2.6) und dieser Fehler kommt stimmt möglicherweise der interne symbolische Link auf python2.7 nicht bzw. wurde nicht gesetzt. Dann mal probieren die erste Zeile mit folgender zu ersetzen:

#!/usr/local/packages/@appstore/Python/usr/local/bin/python2.7


  • Das Script lässt sich nicht ausführen und die Konsole meldet ein
/bin/sh: ./ddnsAutoLogin: not found

Hierfür kann es mehrere Ursachen geben:

  1. Das Script wurde von einem nicht vorhandenen Pfad heraus aufgerufen. Einfach mal versuchen den vollständigen Pfad anzugeben:
    DiskStation> /volume1/install/scripts/ddnsAutoLogin
  2. Die erste Zeile im Script, die sogenannte Shebang-Zeile (oder auch Magic Line) ist nicht richtig.
    #!/usr/local/packages/@appstore/Python/usr/local/bin/python
    in diesem Zug prüfen ob python (bzw python2.7) auch im Ordner /usr/local/packages/@appstore/Python/usr/local/bin/ vorhanden ist. Den Ordner sich auf der Konsole einfach mal listen lassen:
    DiskStation> ls -la /usr/local/packages/@appstore/Python/usr/local/bin/
    Hier sind vorallem folgende Einträge wichtig:
    lrwxrwxrwx 1 root root       9 2013-07-11 15:54 python -> python2.7
    lrwxrwxrwx 1 root root       9 2013-07-11 15:54 python2 -> python2.7
    -rwxr-xr-x 1 root root 2794888 2013-05-31 14:02 python2.7
  3. Der verwendete Editor hat das falsche "Line ending" benutzt. Statt LF (UNIX) wurde CRLF (Windows) verwendet. In diesem Fall sieht ein
    DiskStation> cat /volume1/install/scripts/ddnsAutoLogin
    dann so aus:
    #!/usr/local/packages/@appstore/Python/usr/local/bin/python^M
    ^M
    import urllib^M
    import urllib2^M
    import cookielib^M
    import getopt^M
    import sys^M
    import logging^M
    ^M
    ...
    
    Die fehlerhaften Zeilenumbrüche "^M" müssen dann entweder händisch, mit vi beispielsweise, entfernt werfen oder das Script muss mit den richtigen Line endings erneut kopiert werden!


3x3cut0r 12:27, 15. Jun. 2014 (CEST)