Fritzbox-change-port.py

From Fritz!Box

Jump to: navigation, search

Contents

[edit] Portfreigabe über die Kommandozeile

Mit Hilfe des Skripts `fritzbox-change-port.py` lassen sich, mit ein paar Anpassungen, beliebige Portweiterleitung festlegen. Je nach verwendeter Version der FritzBox muss das Skript evtl. angepasst werden, aber ich versuche, (zumindest für die FritzBox 7170) das Skript immer aktuell zu halten und hier reinzustellen.

Neuste, unterstützte FritzBox-Versionen: 29.04.67

[edit] Hintergrund

Ich verwende das Tool hauptsächlich für Zeiten, wenn ich meinen Server mal herunterfahren muss, um Wartungarbeiten durchzuführen. In diesem Fall leite ich die Portweiterleitungen für den HTTP- und HTTPS-Server dann auf einen temporären Ausweichserver um, bis der eigentliche Server wieder online ist. Von daher ist das Skript eben genau für diese beiden Portweiterleitungen ausgelegt, kann jedoch leicht angepasst werden.

Ich musste dieses Skript entwickeln, da der Zugriff über Telnet auf die FritzBox leider nicht ordentlich geskriptet werden konnte, die Installation eines SSH-Server die FritzBox abstürtzen ließ und das manuelle Ändern der FritzBox-Konfigurationsdateien (zumindest bei Portweiterleitungen) immer einen Neustart der FritzBox erforderte, was für meinen Fall nicht akzeptabel war.

[edit] Hinweise

  • Wichtig: Das Skript kommt ohne Garantie auf Funktionalität. Es funktioniert bei mir und ich stelle es nur online, damit es evtl. anderen Leuten helfen kann, falls sie ähnliche Funktionalität benötigen.
  • Das Skript benötigt Python (es läuft bei mir mit Python 2.4).
  • Momentan geht das Skript davon aus, dass die FritzBox ein Passwort hat. Verwendet sie keins, so muss der entsprechende Teil auskommentiert/entfernt werden.
  • Die zu änderenden Portfreigabe müssen bereits existieren.
  • Die Nummer der Portfreigaben entsprechen der Indexnummer (beginnend bei 0) auf der Seite "Portfreigaben" in der FritzBox-WebUI. D.h. die erste Portfreigabe hat die Nummer "0", die zweite die Nummer "1" usw.

Vorschläge und Anmerkungen zur Verbesserung des Skripts sind gerne willkommen. Diese bitte auf der Diskussionsseite posten.

[edit] Das Skript

Da das Wiki leider keinen Upload von Textdateien erlaubt, kommt der Quellcode hier direkt rein.

[edit] Version 3

  • Unterstützte FB-Versionen: 29.04.67, 29.04.70
  • Datum: 14.04.2009

Changelog:

  • Möglichkeit hinzugefügt, per Option ("--check-version") zu prüfen, ob die FritzBox-Version vom Skript unterstützt wird
  • Auf neuste FritzBox-Version angepasst.
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Verändert die Ziel-IP der Portforwarding der Fritz-Box für HTTP und HTTPS.
#
# Version: 3
#
# Neuste Version unter:
#  http://www.wehavemorefun.de/fritzbox/index.php/Fritzbox-change-port.py
#
# HINWEIS: Um rauszufinden, welche Daten tatsächlich gesendet werden, muss ein
#   Paketsniffer (z.B. Wireshark) verwendet werden. Dabei reicht es, sich die 
#   POST-Daten beim Login und beim Speichern einer Portfreigabe anzusehen.
#
# Copyright (c) 2008-2009 Sebastian Krysmanski
#
# Permission is hereby granted, free of charge, to any person obtaining a copy 
# of this software and associated documentation files (the "Software"), to deal 
# in the Software without restriction, including without limitation the rights 
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
# copies of the Software, and to permit persons to whom the Software is 
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
# SOFTWARE.
#

import urllib
import re
import sys

# URL zum Fritz-Box-Web-Interface
FRITZBOX_URL = "http://fritz.box/cgi-bin/webcm"
# URL zur UPnP-Desc-Datei der FritzBox
FRITZBOX_UPNP_INFO_URL = "http://fritz.box:49000/igddesc.xml"
# Liste aller unterstützten Versionen
SUPPORTED_VERSIONS = ("29.04.67", "29.04.70")

def GetFritzBoxVersion():
  socket = urllib.urlopen(FRITZBOX_UPNP_INFO_URL)
  lines = socket.readlines()
  socket.close()

  fritzVersion = None
  for line in lines:
    result = re.search(r"<friendlyName>.+\(UI\) ([^<]+)</friendlyName>", line)
    if result:
      fritzVersion = result.group(1)
      break

  return fritzVersion


def CallFritzBox(data):
  enc_data = urllib.urlencode(data)
  socket = urllib.urlopen(FRITZBOX_URL, enc_data)
  lines = socket.readlines()
  socket.close()
  return lines
  
def Login(password):
  # Daten zum Anmelden per Passwort an die Fritzbox
  # Quelle: Werden gesendet, wenn ein Passwort auf der Startseite eingegeben wird.
  data = { 
      'getpage' : '../html/index_inhalt.html',
      'errorpage' : '../html/index_inhalt.html',
      'var:pagename' : 'home',
      'var:menu' : 'home',
      'login:command/password' : password
    }

  line = "".join(CallFritzBox(data))
  result = re.search(r"<p class=\"errorMessage\">(.+?)</p>", line)
  if result:
    # Es ist ein Fehler aufgetreten; Nachricht zurückgeben
    errorMsg = result.group(1)
    errorMsg = errorMsg.replace("FEHLER: ", "")
    return errorMsg

  return None

  
def ChangePortForwardingRule(ruleNumber, isActivated, name, protocol, port, endport, toIP, toPort):
  # Daten zum Ändern eines Ports
  # Quelle: Wenn man die Änderungen an einer Portweiterleitung speichert (also
  #   nach dem Eingeben der neuen Informationen).
  ruleID = 'rule' + str(int(ruleNumber))
  data = { 
      'getpage' : '../html/de/menus/menu2.html',
      'errorpage' : '../html/de/menus/menu2.html',
      'var:pagename' : 'portfw',
      'var:errorpagename' : 'portrule',
      'var:menu' : 'internet',
      'var:rule' : ruleID,
      'var:isnew' : '0',
      'var:isexp' : '0',
      'forwardrules:settings/' + ruleID + '/activated' : '1',
      'forwardrules:settings/' + ruleID + '/description' : name,
      'forwardrules:settings/' + ruleID + '/protocol' : protocol,
      'forwardrules:settings/' + ruleID + '/port' : port,
      'forwardrules:settings/' + ruleID + '/endport' : endport,
      'forwardrules:settings/' + ruleID + '/fwip' : toIP,
      'forwardrules:settings/' + ruleID + '/fwport' : toPort
    }
  CallFritzBox(data)
  
#
# Main-Sektion
#

if (len(sys.argv) != 2 or sys.argv[1] != "--check-version") and len(sys.argv) != 3:
  print("Verwendung: " + sys.argv[0] + " <Passwort> <ToIP>")
  print("            " + sys.argv[0] + " --check-version")
  sys.exit(1)
  
# Nach FritzBox-Version suchen
print "Prüfe FritzBox-Version"
fritzVersion = GetFritzBoxVersion()

if fritzVersion == None:
  print "Error: Die Version der FritzBox konnte nicht ermittelt werden."
  sys.exit(1)
if fritzVersion not in SUPPORTED_VERSIONS:
  print "Error: Nicht unterstützte FritzBox-Version: " + fritzVersion
  sys.exit(1)

print "Unterstützte Version gefunden: " + fritzVersion

if len(sys.argv) == 2:
  sys.exit(0)

print "Einloggen..."
errorMsg = Login(sys.argv[1])
if errorMsg != None:
  print "Es ist ein Fehler beim Login aufgetreten:"
  print errorMsg
  sys.exit(1)

# Portforwardings umlegen
print "Ändere Portforwarding 'HTTP-Server'..."
ChangePortForwardingRule(0, True, 'HTTP-Server', 'TCP', 80, 80, sys.argv[2], 80)
print "Ändere Portforwarding 'HTTPS-Server'..."
ChangePortForwardingRule(1, True, 'HTTPS-Server', 'TCP', 443, 443, sys.argv[2], 443)

print "Fertig"

[edit] Version 2

  • Unterstützte FB-Versionen: 29.04.67
  • Datum: 31.12.2008

Changelog:

  • Die Version der FritzBox wird jetzt vor dem Login abgefragt.
  • Falls das Passwort für den Login falsch ist, wird das Skript jetzt abgebrochen.
  • Auf neuste FritzBox-Version angepasst.
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Verändert die Ziel-IP der Portforwarding der Fritz-Box für HTTP und HTTPS.
#
# HINWEIS: Um rauszufinden, welche Daten tatsächlich gesendet werden, muss ein
#   Paketsniffer (z.B. Wireshark) verwendet werden. Dabei reicht es, sich die 
#   POST-Daten beim Login und beim Speichern einer Portfreigabe anzusehen.
#
# Copyright (c) 2008 Sebastian Krysmanski
#
# Permission is hereby granted, free of charge, to any person obtaining a copy 
# of this software and associated documentation files (the "Software"), to deal 
# in the Software without restriction, including without limitation the rights 
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
# copies of the Software, and to permit persons to whom the Software is 
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
# SOFTWARE.
#

import urllib
import re
import sys

# URL zum Fritz-Box-Web-Interface
FRITZBOX_URL = "http://fritz.box/cgi-bin/webcm"
# URL zur UPnP-Desc-Datei der FritzBox
FRITZBOX_UPNP_INFO_URL = "http://fritz.box:49000/igddesc.xml"
# Liste aller unterstützten Versionen
SUPPORTED_VERSIONS = ("29.04.67")

def GetFritzBoxVersion():
  socket = urllib.urlopen(FRITZBOX_UPNP_INFO_URL)
  lines = socket.readlines()
  socket.close()

  fritzVersion = None
  for line in lines:
    result = re.search(r"<friendlyName>.+\(UI\) ([^<]+)</friendlyName>", line)
    if result:
      fritzVersion = result.group(1)
      break

  return fritzVersion


def CallFritzBox(data):
  enc_data = urllib.urlencode(data)
  socket = urllib.urlopen(FRITZBOX_URL, enc_data)
  lines = socket.readlines()
  socket.close()
  return lines
  
def Login(password):
  # Daten zum Anmelden per Passwort an die Fritzbox
  # Quelle: Werden gesendet, wenn ein Passwort auf der Startseite eingegeben wird.
  data = { 
      'getpage' : '../html/index_inhalt.html',
      'errorpage' : '../html/index_inhalt.html',
      'var:pagename' : 'home',
      'var:menu' : 'home',
      'login:command/password' : password
    }

  line = "".join(CallFritzBox(data))
  result = re.search(r"<p class=\"errorMessage\">(.+?)</p>", line)
  if result:
    # Es ist ein Fehler aufgetreten; Nachricht zurückgeben
    errorMsg = result.group(1)
    errorMsg = errorMsg.replace("FEHLER: ", "")
    return errorMsg

  return None

  
def ChangePortForwardingRule(ruleNumber, isActivated, name, protocol, port, endport, toIP, toPort):
  # Daten zum Ändern eines Ports
  # Quelle: Wenn man die Änderungen an einer Portweiterleitung speichert (also
  #   nach dem Eingeben der neuen Informationen).
  ruleID = 'rule' + str(int(ruleNumber))
  data = { 
      'getpage' : '../html/de/menus/menu2.html',
      'errorpage' : '../html/de/menus/menu2.html',
      'var:pagename' : 'portfw',
      'var:errorpagename' : 'portrule',
      'var:menu' : 'internet',
      'var:rule' : ruleID,
      'var:isnew' : '0',
      'var:isexp' : '0',
      'forwardrules:settings/' + ruleID + '/activated' : '1',
      'forwardrules:settings/' + ruleID + '/description' : name,
      'forwardrules:settings/' + ruleID + '/protocol' : protocol,
      'forwardrules:settings/' + ruleID + '/port' : port,
      'forwardrules:settings/' + ruleID + '/endport' : endport,
      'forwardrules:settings/' + ruleID + '/fwip' : toIP,
      'forwardrules:settings/' + ruleID + '/fwport' : toPort
    }
  CallFritzBox(data)
  
#
# Main-Sektion
#

if len(sys.argv) != 3:
  print("Verwendung: " + sys.argv[0] + " <Passwort> <ToIP>")
  sys.exit(1)

# Nach FritzBox-Version suchen
print "Prüfe FritzBox-Version"
fritzVersion = GetFritzBoxVersion()

if fritzVersion == None:
  print "Error: Die Version der FritzBox konnte nicht ermittelt werden."
  sys.exit(1)
if fritzVersion not in SUPPORTED_VERSIONS:
  print "Error: Nicht unterstützte FritzBox-Version: " + fritzVersion
  sys.exit(1)

print "Unterstützte Version gefunden: " + fritzVersion

print "Einloggen..."
errorMsg = Login(sys.argv[1])
if errorMsg != None:
  print "Es ist ein Fehler beim Login aufgetreten:"
  print errorMsg
  sys.exit(1)

# Portforwardings umlegen
print "Ändere Portforwarding 'HTTP-Server'..."
ChangePortForwardingRule(0, True, 'HTTP-Server', 'TCP', 80, 80, sys.argv[2], 80)
print "Ändere Portforwarding 'HTTPS-Server'..."
ChangePortForwardingRule(1, True, 'HTTPS-Server', 'TCP', 443, 443, sys.argv[2], 443)

print "Fertig"

[edit] Version 1

  • Unterstützte FB-Versionen: 29.04.59
  • Datum: 10.10.2008
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Verändert die Ziel-IP der Portforwarding der Fritz-Box für HTTP und HTTPS.
#
# HINWEIS: Um rauszufinden, welche Daten tatsächlich gesendet werden, muss ein
#   Paketsniffer (z.B. Wireshark) verwendet werden. Dabei reicht es, sich die 
#   POST-Daten beim Login und beim Speichern einer Portfreigabe anzusehen.
#
# Copyright (c) 2008 Sebastian Krysmanski
#
# Permission is hereby granted, free of charge, to any person obtaining a copy 
# of this software and associated documentation files (the "Software"), to deal 
# in the Software without restriction, including without limitation the rights 
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
# copies of the Software, and to permit persons to whom the Software is 
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
# SOFTWARE.
#

import urllib
import re
import sys

# URL zum Fritz-Box-Web-Interface
FRITZBOX_URL = "http://fritz.box/cgi-bin/webcm"

def CallFritzBox(data):
  enc_data = urllib.urlencode(data)
  socket = urllib.urlopen(FRITZBOX_URL, enc_data)
  lines = socket.readlines()
  socket.close()
  return lines
  
def Login(password):
  # Daten zum Anmelden per Passwort an die Fritzbox
  data = { 
      'getpage' : '../html/de/menus/menu2.html',
      'errorpage' : '../html/index.html',
      'var:lang' : 'de',
      'var:pagename' : 'home',
      'var:menu' : 'home',
      'login:command/password' : password
    }

  return CallFritzBox(data)
  
def ChangePortForwardingRule(ruleNumber, isActivated, name, protocol, port, endport, toIP, toPort):
  # Daten zum Ändern eines Ports
  ruleID = 'rule' + str(int(ruleNumber))
  data = { 
      'getpage' : '../html/de/menus/menu2.html',
      'errorpage' : '../html/de/menus/menu2.html',
      'var:lang' : 'de',
      'var:pagename' : 'portfw',
      'var:errorpagename' : 'portrule',
      'var:menu' : 'internet',
      'var:rule' : ruleID,
      'var:isnew' : '0',
      'var:isexp' : '0',
      'forwardrules:settings/' + ruleID + '/activated' : '1',
      'forwardrules:settings/' + ruleID + '/description' : name,
      'forwardrules:settings/' + ruleID + '/protocol' : protocol,
      'forwardrules:settings/' + ruleID + '/port' : port,
      'forwardrules:settings/' + ruleID + '/endport' : endport,
      'forwardrules:settings/' + ruleID + '/fwip' : toIP,
      'forwardrules:settings/' + ruleID + '/fwport' : toPort
    }
  CallFritzBox(data)
  
#
# Main-Sektion
#

if len(sys.argv) != 3:
  print("Verwendung: " + sys.argv[0] + " <Passwort> <ToIP>")
  sys.exit(1)

# Einloggen
print "Einloggen..."
indexPageLines = Login(sys.argv[1])

# Nicht FritzBox-Version suchen
print "Prüfe FritzBox-Version"
fritzVersion = None
for line in indexPageLines:
  result = re.search(r"Firmware-Version ([^<]+)<", line)
  if result:    
    fritzVersion = result.group(1)
    break

if fritzVersion == None:
  print "Error: Die Version der FritzBox konnte nicht ermittelt werden."
  sys.exit(1)
if fritzVersion != "29.04.59":
  print "Error: Nicht unterstützte FritzBox-Version: " + fritzVersion
  sys.exit(1)

print "Unterstützte Version gefunden: " + fritzVersion

# Portforwardings umlegen
print "Ändere Portforwarding 'HTTP-Server'..."
ChangePortForwardingRule(0, True, 'HTTP-Server', 'TCP', 80, 80, sys.argv[2], 80)
print "Ändere Portforwarding 'HTTPS-Server'..."
ChangePortForwardingRule(1, True, 'HTTPS-Server', 'TCP', 443, 443, sys.argv[2], 443)

print "Fertig"
Personal tools