Sunday, March 15, 2009

UPnP via python

Controlling your DSL Router via UPnP

When I was looking for a commandline tool to create port mappings on my broadband/DSL router, I detected the miranda.py script on http://www.sourcesec.com/2008/11/07/miranda-upnp-administration-tool/. I needed some functionality not yet provided by the script, namely, the possibility of passing arguments to host send commands. Usually, the script asks nicely for all required arguments; this is hard to automate. Using the patch provided below, a command such as
host send 0 WANConnectionDevice WANIPConnection AddPortMapping ssh2 0 192.168.32.32  1  22  80.80.80.80 tcp  22
is now possible. This also allows bash scripting:
cat <<EOF | miranda.py -s ~/.miranda/struct_data.mir
host get 0
host send 0 WANConnectionDevice WANIPConnection AddPortMapping ssh2 0 192.168.32.32  1  22  80.80.80.80 tcp  22
EOF
I also fixed a nondescript exception message when no host had yet been loaded/found. Have fun with it,
yours,
Sebastian
--- miranda.py.orig 2009-03-15 13:51:03.000000000 +0100
+++ miranda.py 2009-03-15 13:58:37.000000000 +0100
@@ -1065,11 +1065,15 @@
    #Send SOAP requests
    index = False
    inArgCounter = 0
-
-   if argc != 6:
+   
+   #sbr: allow to pass arguments to the call
+   numReqArgs = 6
+   if argc < numReqArgs:
     showHelp(argv[0])
     return
+   # number of arguments in addition to the send cmd
    else:
+    extraArgs = argc-numReqArgs
     try:
      index = int(argv[2])
     except:
@@ -1078,7 +1082,12 @@
     deviceName = argv[3]
     serviceName = argv[4]
     actionName = argv[5]
-    hostInfo = hp.ENUM_HOSTS[index]
+    try:
+     hostInfo = hp.ENUM_HOSTS[index]
+    except Exception, e:
+     print "You need to discover or load some hosts, first"
+     return
+   
     actionArgs = False
     sendArgs = {}
     retTags = []
@@ -1106,11 +1115,18 @@
      print "Are you sure you've specified the correct action?"
      return False
 
+    extraArgsUsed = 0
     for argName,argVals in actionArgs.iteritems():
      actionStateVar = argVals['relatedStateVariable']
      stateVar = hostInfo['deviceList'][deviceName]['services'][serviceName]['serviceStateVariables'][actionStateVar]
 
      if argVals['direction'].lower() == 'in':
+         if extraArgs-extraArgsUsed > 0:
+      arg = argv[numReqArgs+extraArgsUsed]
+      print "Using ", arg, " for ", argName
+      sendArgs[argName] = (arg,stateVar['dataType'])
+      extraArgsUsed += 1
+         else:
       print "Required argument:" 
       print "\tArgument Name: ",argName
       print "\tData Type:     ",stateVar['dataType']

6 comments:

fengfk2008 said...
This comment has been removed by a blog administrator.
宜蘭民宿 said...
This comment has been removed by a blog administrator.
fengfk2008 said...
This comment has been removed by a blog administrator.
fengfk2008 said...
This comment has been removed by a blog administrator.
fengfk2008 said...
This comment has been removed by a blog administrator.
Jamieson Christian said...

Got it to work, with a couple potholes along the way.

1. The patch was generated with --ignore-whitespace, which causes problems with an indentation-sensitive language like Python. I had to manually go through and fix the indentation to account for changes in nesting.

2. If the last command piped into Miranda is not "quit", Miranda will throw a fatal exception (unexpected end-of-file) when it runs out of piped input.

Other than that, the changes work great and have really helped me automate my UPnP setup. Thanks!