mIRC Home    About    Download    Register    News    Help

Print Thread
#174347 06/04/07 12:07 AM
Joined: Dec 2002
Posts: 155
S
Strider Offline OP
Vogon poet
OP Offline
Vogon poet
S
Joined: Dec 2002
Posts: 155
I'm in the process of making a DLL-free script that uses the port mapping (forwarding) feature of UPnP-enabled Internet Gateways (routers) for Identd and DCC.

It's already working with Identd (with my NETGEAR router, at least) by opening the Identd port on ^LOGIN and closing it on CONNECT.

The thing is that I need some ideas on how to make it work with DCC sends, since I need to know the port that mIRC is listening on (to open it) before the connection is established, and then which port was used (to close it) after the connection ends or fails.

Thanks to the fact that $send doesn't have a "localport" property, so far, I've only thought of capturing the DCC Send PRIVMSG with a "/debug" window to extract the port mIRC is listening on and then close the port on FILESENT or SENDFAIL (somehow finding out which port was used), but this doesn't convince me entirely.

Can somebody give me a better DLL-free idea?

Strider #174436 07/04/07 07:00 PM
Joined: Feb 2006
Posts: 65
F
Babel fish
Offline
Babel fish
F
Joined: Feb 2006
Posts: 65
one, i'm gonna want that script, i could use it.

1st off, once the connection has been made. u dont need the port mapped. so doing it on /debug, and waiting 30 secs won't break the send.


FYI: not sure if u know this but the dcc raw format goes like this:
PRIVMSG NICK : $+ $chr(1) $+ DCC SEND filename ipaddress port filesize

with the ip address being in long format.


known on irc as MrStonedOne
read my full post before replying or dont reply. tl;dr isn't allowed here
FaiNT #174449 07/04/07 11:02 PM
Joined: Dec 2002
Posts: 155
S
Strider Offline OP
Vogon poet
OP Offline
Vogon poet
S
Joined: Dec 2002
Posts: 155
Originally Posted By: FaiNT
1st off, once the connection has been made. u dont need the port mapped. so doing it on /debug, and waiting 30 secs won't break the send.

Thanks. I finished the script yesterday (to some extent) and instead of waiting 30 seconds, I "unmap" the port immediately after the connection is established.

Here is the script that works for basic needs with my NETGEAR router:

Code:
;UPnP Script v1.0

alias upnp.debugpermanent {
  if ($isid) return $false
}
on *:start: {
  upnp.start
  if ($upnp.debugpermanent) .debug -i upnp.dcc upnp.dccdebug
}
on *:unload: {
  unset %upnp.*
}
on *:exit: {
  unset %upnp.*
}
on ^*:logon:*: {
  if ($readini($mircini,ident,active) == yes) upnp.addPortMapping $readini($mircini,ident,port)
  set %upnp.IdentdMap $true
}
on *:error:*: {
  if ($readini($mircini,ident,active) == yes) && (%upnp.IdentdMap) upnp.delPortMapping $readini($mircini,ident,port)
}
on *:connect: {
  if ($readini($mircini,ident,active) == yes) upnp.delPortMapping $readini($mircini,ident,port)
  unset %upnp.IdentdMap
}
alias debug {
  if ((%upnp.debuguse) || ($upnp.debugpermanent)) && ($gettok($debug,-1,92) == upnp.dcc) {
    linesep $iif($active == Status Window,-s,$active)
    echo -a -UPNP- Debug is in use.
    linesep $iif($active == Status Window,-s,$active)
  }
  else {
    debug $1-
    if ($0 > 0) set %upnp.debugparams $$1-
  }
}
alias upnp.dccgetnum {
  if (!$isid) return
  var %i = 0
  if ($$1 == send) {
    while (%i <= $send(0)) {
      if ($send(%i).wid == $$2) return %i
      inc %i
    }
  }
  else {
    while (%i <= $chat(0)) {
      if ($chat(%i).wid == $$2) return %i
      inc %i
    }
  }
  return 0
}
alias upnp.dcccheck {
  var %upnp.dccnum = $upnp.dccgetnum($1,$$2)
  var %upnp.dccstatus = $iif($1 == send,$send(%upnp.dccnum).status,$chat(%upnp.dccnum).status)
  if ($istok(failed:active:sent:inactive,%upnp.dccstatus,58)) || (%upnp.dccnum == 0) {
    .timerupnp.dcc $+ $$3 off
    upnp.delPortMapping $3
  }
}
alias upnp.dccdebug {
  tokenize 32 $1-
  if ($1 != ->) return
  if ($istok($+(:,$chr(1),DCC SEND,/,:,$chr(1),DCC CHAT),$5-6,47)) {
    var %upnp.debugport = $remove($9,$chr(1))
    upnp.addPortMapping %upnp.debugport
    .timerupnp.dcc $+ %upnp.debugport 0 0 upnp.dcccheck $6 $iif($6 == send,$send($send($4,0)).wid,$chat($chat($4,0)).wid) %upnp.debugport
    if (!$upnp.debugpermanent) {
      unset %upnp.debuguse
      .debug off
      if (%upnp.debugbefore) {
        .debug %upnp.debugparamsbefore
        unset %upnp.debugbefore
        unset %upnp.debugparamsbefore
      }
    }  
  }
  return
}
alias dcc {
  if ($isid) return
  if ($istok(send:chat,$1,58)) && (!$upnp.debugpermanent) {
    if ($debug != $null) {
      set %upnp.debugparamsbefore %upnp.debugparams      
      .debug off
      set %upnp.debugbefore $true
    }
    unset %upnp.dccdebuguse
    .debug -i upnp.dcc upnp.dccdebug
    set %upnp.debuguse $true
  }
  if ($gettok($debug,-1,92) != upnp.dcc) && ($upnp.debugpermanent) {
    .debug -i upnp.dcc upnp.dccdebug
  }
  dcc $1-
}
alias upnp.start {
  unset %upnp.*
  var %upnp.discip = 239.255.255.250
  var %upnp.disclocalport = $rand(10000,63999)
  while (!$portfree(%upnp.disclocalport)) {
    %upnp.disclocalport = $rand(10000,63999)
  }
  echo $color(other) -gs -UPnP- Searching for Internet Gateway...
  sockclose upnp.discover
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900 $+(M-SEARCH * HTTP/1.1,$crlf,Host: 239.255.255.250:1900,$crlf,Man: "ssdp:discover",$crlf,ST: urn:schemas-upnp-org:service:WANIPConnection:1,$crlf,MX: 2,$str($crlf,2))
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900 $+(M-SEARCH * HTTP/1.1,$crlf,Host: 239.255.255.250:1900,$crlf,Man: "ssdp:discover",$crlf,ST: urn:schemas-upnp-org:service:WANPPPConnection:1,$crlf,MX: 2,$str($crlf,2))
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900
}
alias upnp.getLocalInfo {
  set %upnp.localprevip $ip
  set %upnp.localprevhost $host
  unset %upnp.localticks  
  .localinfo -h
  .timerupnp.localip off
  set %upnp.localtimeout 5000
  set %upnp.localticks $ticks
  .timerupnp.localip 0 0 upnp.getLocalInfo.2
}
alias upnp.getLocalInfo.2 {  
  set %upnp.localip $ip
  if (%upnp.localip == $null) {
    if ($calc($ticks - %upnp.localticks) > %upnp.localtimeout) {
      echo $color(other) -gs -UPnP- Local IP not found
      .timerupnp.localip off      
      if (%upnp.localprevip != $null) {
        .localinfo $iif(%upnp.localprevhost == $null,%upnp.localprevip,%upnp.localprevhost) %upnp.localprevip
      }
    }
  }
  else {
    echo $color(other) -gs -UPnP- Local IP: $ip
    .timerupnp.localip off
    if (%upnp.localprevip != $null) {
      .localinfo $iif(%upnp.localprevhost == $null,%upnp.localprevip,%upnp.localprevhost) %upnp.localprevip
    }
  }
}
on *:udpread:upnp.discover: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if ($1 == $null) {
      echo $color(other) -gs -UPnP- Internet Gateway with %upnp.st service found at $sock($sockname).saddr
      upnp.getLocalInfo
      upnp.getDeviceInfo %upnp.discURL
      sockclose $sockname
      return
    }
    if ($gettok($1,1,58) == LOCATION) set %upnp.discURL $remove($gettok($1-,2-,58),$chr(32))
    if ($gettok($1,1,58) == ST) set %upnp.st $remove($gettok($1-,2-,58),$chr(32))
    sockread %info
  }
}
alias upnp.getDeviceInfo {
  echo $color(other) -gs -UPnP- Getting Service Info...
  var %upnp.devip = $gettok($gettok($$1,2,47),1,58)
  var %upnp.devport = $gettok($gettok($1,2,47),2,58)
  sockclose upnp.devinfo
  sockopen upnp.devinfo %upnp.devip %upnp.devport
  sockmark upnp.devinfo $+($chr(47),$gettok($1,3-,47))
}

on 1:sockopen:upnp.devinfo: {
  if ($sockerr > 0) return
  set %upnp.devinfoMode HTTP
  set %upnp.getCURL $false
  sockwrite -nt $sockname GET $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Accept-Language: en
  sockwrite -nt $sockname
}
on 1:sockread:upnp.devinfo: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.devinfoMode == XML) {
      if (<URLBase> isin $1-) {
        set %upnp.servinfoBase $upnp.getXMLValue($1-,<URLBase>)
        echo $color(other) -gs -UPnP- Service Base URL: %upnp.servinfoBase
      }
      if (!%upnp.getCURL) {
        if ($+(<serviceType>,%upnp.st,</serviceType>) isin $1-) set %upnp.getCURL $true
      }
      if (%upnp.getCURL) { 
        if (<controlURL> isin $1-) {          
          set %upnp.servinfoURL $upnp.getXMLValue($1-,<controlURL>)
          set %upnp.getCURL $false
          echo $color(other) -gs -UPnP- Service Control URL: %upnp.servinfoURL
          upnp.getServiceExternalIP $+(%upnp.servinfoBase,%upnp.servinfoURL)
        }
      }
    }
    if ($1 == $null) && (%upnp.devinfoMode == HTTP) set %upnp.devinfoMode XML
    sockread %info
  }
}
alias upnp.getServiceExternalIP {
  echo $color(other) -gs -UPnP- Getting Service External IP Address...
  var %upnp.servip = $gettok($gettok($$1,2,47),1,58)
  var %upnp.servport = $gettok($gettok($1,2,47),2,58)
  sockclose upnp.servIP
  sockopen upnp.servIP %upnp.servip %upnp.servport
  sockmark upnp.servIP $+($chr(47),$gettok($1,3-,47))
}

on 1:sockopen:upnp.servIP: {
  if ($sockerr > 0) return
  set %upnp.servIPMode HTTP
  sockwrite -nt $sockname POST $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Content-Length: $iif(PPP isin %upnp.st,273,272)
  sockwrite -nt $sockname Content-Type: text/xml; charset="utf-8"
  sockwrite -nt $sockname SOAPAction: $+(",%upnp.st,#GetExternalIPAddress")
  sockwrite -nt $sockname
  sockwrite -nt $sockname <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  sockwrite -nt $sockname <s:Body>
  sockwrite -nt $sockname $+(<u:GetExternalIPAddress xmlns:u=",%upnp.st,"></u:GetExternalIPAddress>)
  sockwrite -nt $sockname </s:Body>
  sockwrite -nt $sockname </s:Envelope>
  sockwrite -nt $sockname
}
on 1:sockread:upnp.servIP: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.servIPMode == XML) {
      if (<NewExternalIPAddress> isin $1-) {
        set %upnp.externalIP $upnp.getXMLValue($1-,<NewExternalIPAddress>)
        echo $color(other) -gs -UPnP- Service External IP Address: %upnp.externalIP
      }
    }
    if ($1 == $null) && (%upnp.servIPMode == HTTP) set %upnp.servIPMode XML
    sockread %info
  }
}
alias upnp.addPortMapping {
  var %upnp.lp = $iif($0 > 1,$2,$$1)
  var %upnp.curl = $iif($0 > 1,$1,$+(%upnp.servinfoBase,%upnp.servinfoURL))
  if (%upnp.localip == $null) {
    echo $color(other) -gs -UPnP- Cannot map port %upnp.lp $+ . Local IP Address is unknown
    return
  }
  echo $color(other) -gs -UPnP- Mapping port %upnp.lp to %upnp.localip $+ ...
  var %upnp.pmi = $gettok($gettok(%upnp.curl,2,47),1,58)
  var %upnp.pmp = $gettok($gettok(%upnp.curl,2,47),2,58)
  set %upnp.pmport %upnp.lp
  set %upnp.pmlease 0
  sockclose upnp.pm
  sockopen upnp.pm %upnp.pmi %upnp.pmp
  sockmark upnp.pm $+($chr(47),$gettok(%upnp.curl,3-,47))
}

on 1:sockopen:upnp.pm: {
  if ($sockerr > 0) return
  set %upnp.pmMode HTTP
  sockwrite -nt $sockname POST $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Content-Length: $calc(533 + $len(%upnp.st) + ($len(%upnp.pmport) * 4) + ($len(%upnp.localip) * 2) + $len(%upnp.pmlease))
  sockwrite -nt $sockname Content-Type: text/xml; charset="utf-8"
  sockwrite -nt $sockname SOAPAction: $+(",%upnp.st,#AddPortMapping")
  sockwrite -nt $sockname
  sockwrite -nt $sockname <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  sockwrite -nt $sockname <s:Body>
  sockwrite -nt $sockname $+(<u:AddPortMapping xmlns:u=",%upnp.st,">)
  sockwrite -nt $sockname <NewRemoteHost></NewRemoteHost>
  sockwrite -nt $sockname $+(<NewExternalPort>,%upnp.pmport,</NewExternalPort>)
  sockwrite -nt $sockname <NewProtocol>TCP</NewProtocol>
  sockwrite -nt $sockname $+(<NewInternalPort>,%upnp.pmport,</NewInternalPort>)
  sockwrite -nt $sockname $+(<NewInternalClient>,%upnp.localip,</NewInternalClient>)
  sockwrite -nt $sockname <NewEnabled>1</NewEnabled>
  sockwrite -nt $sockname $+(<NewPortMappingDescription>mIRC $chr(40),%upnp.localip,:,%upnp.pmport,$chr(41),$chr(32),%upnp.pmport,$chr(32),TCP</NewPortMappingDescription>)
  sockwrite -nt $sockname $+(<NewLeaseDuration>,%upnp.pmlease,</NewLeaseDuration>)
  sockwrite -nt $sockname </u:AddPortMapping>
  sockwrite -nt $sockname </s:Body>
  sockwrite -nt $sockname </s:Envelope>
  sockwrite -nt $sockname
}
on 1:sockread:upnp.pm: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.pmMode == HTTP) {
      if (HTTP/1.1 200 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Added: $+(mIRC $chr(40),%upnp.localip,:,%upnp.pmport,$chr(41),$chr(32),%upnp.pmport,$chr(32),TCP,$chr(32))
        sockclose $sockname
        return
      }
      if (HTTP/1.1 500 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Add Error
        sockclose $sockname
        return
      }
      if ($1 == $null) set %upnp.pmMode XML
    }
    if (%upnp.pmMode == XML) {
      ;if ($+(<u:AddPortMappingResponse xmlns:u=",%upnp.st,"/>) isin $1-) {
      ;}
    }
    sockread %info
  }
}
alias upnp.delPortMapping {
  var %upnp.lp = $iif($0 > 1,$2,$$1)
  var %upnp.curl = $iif($0 > 1,$1,$+(%upnp.servinfoBase,%upnp.servinfoURL))
  echo $color(other) -gs -UPnP- Deleting Port Mapping for port %upnp.lp $+ ...
  var %upnp.pmdi = $gettok($gettok(%upnp.curl,2,47),1,58)
  var %upnp.pmdp = $gettok($gettok(%upnp.curl,2,47),2,58)
  set %upnp.pmdport %upnp.lp
  sockclose upnp.pmd
  sockopen upnp.pmd %upnp.pmdi %upnp.pmdp
  sockmark upnp.pmd $+($chr(47),$gettok(%upnp.curl,3-,47))
}

on 1:sockopen:upnp.pmd: {
  if ($sockerr > 0) return
  set %upnp.pmdMode HTTP
  sockwrite -nt $sockname POST $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Content-Length: $calc(324 + $len(%upnp.st) + $len(%upnp.pmport))
  sockwrite -nt $sockname Content-Tupe: text/xml; charset="utf-8"
  sockwrite -nt $sockname SOAPAction: $+(",%upnp.st,#DeletePortMapping")
  sockwrite -nt $sockname
  sockwrite -nt $sockname <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  sockwrite -nt $sockname <s:Body>
  sockwrite -nt $sockname $+(<u:DeletePortMapping xmlns:u=",%upnp.st,">)
  sockwrite -nt $sockname <NewRemoteHost></NewRemoteHost>
  sockwrite -nt $sockname $+(<NewExternalPort>,%upnp.pmdport,</NewExternalPort>)
  sockwrite -nt $sockname <NewProtocol>TCP</NewProtocol>
  sockwrite -nt $sockname </u:DeletePortMapping>
  sockwrite -nt $sockname </s:Body>
  sockwrite -nt $sockname </s:Envelope>
  sockwrite -nt $sockname
}
on 1:sockread:upnp.pmd: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.pmdMode == HTTP) {
      if (HTTP/1.1 200 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Deleted for port %upnp.pmdport
        sockclose $sockname
        return
      }
      if (HTTP/1.1 500 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Delete Error
        sockclose $sockname
        return
      }
      if ($1 == $null) set %upnp.pmdMode XML
    }
    if (%upnp.pmdMode == XML) {
      ;if ($+(<u:DeletePortMappingResponse xmlns:u=",%upnp.st,"/>) isin $1-) {
      ;}
    }
    sockread %info
  }
}
alias upnp.getXMLValue {
  if (!$isid) return  
  var %rest = $right($$1-,$calc(0 - ($pos($1-,$2) + $len($2) - 1)))
  var %tag = $2
  if ($pos(%rest,<) != 1) return $gettok(%rest,1,$asc(<))
  else return $null
}

The "upnp.addPortMapping <port>" and "upnp.delPortMapping <port>" are the two aliases that do the "magic".

Please note that running the initialization commands is VITAL.

I added a nice little feature:

- If $upnp.debugpermanent (on top of the code) returns $true, then the script will take over and need to be using the output of /debug the whole time.

- If $upnp.debugpermanent returns $false, the script will only take over /debug while waiting for the DCC message to be sent (in order to capture the port) and then it will restore /debug to its previous state (if it was activated), which allows it to coexist with other scripts that use /debug. When using this mode, you cannot use the "Resend" button after a DCC transfer fails, and you cannot use the DCC Send toolbar button to initiate a transfer, since mIRC won't trigger the /dcc custom alias.

I've tried my best to test it, but it certainly does need a lot more of testing and error checking. For example, using "recycled" DCC Send/Chat windows will generate a conflict if there is a newer DCC Send/Chat window for the same nick.

There are some things that should probably be added, like some popup menus to enable/disable it for DCC/Identd, a control list of ports that the script has open (to close them at any given time, because this script uses leases of "0", thanks to my router not supporting non-zero leases), an alias to close all the open ports (using the previous list) on EXIT so the don't stay permanently open, hash tables instead of variables, etc.

Also, the XML parser might be a little "weak".

- EDIT: Corrected a little important typo bug in "on 1:sockread:upnp.pm:"

Last edited by Strider; 07/04/07 11:23 PM.
Strider #174453 07/04/07 11:15 PM
Joined: Dec 2002
Posts: 155
S
Strider Offline OP
Vogon poet
OP Offline
Vogon poet
S
Joined: Dec 2002
Posts: 155
Oh, also. If everything goes well after the initialization, you will get an output like this one:

-UPnP- Searching for Internet Gateway...
-UPnP- Internet Gateway with urn:schemas-upnp-org:service:WANIPConnection:1 service found at 192.168.1.1
-UPnP- Getting Service Info...
-UPnP- Local IP: 192.168.111.111
-UPnP- Service Base URL: http://192.168.1.1:80/
-UPnP- Service Control URL: Public_UPNP_C3
-UPnP- Getting Service External IP Address...
-UPnP- Service External IP Address: 111.111.111.111

Strider #180916 15/07/07 12:19 AM
Joined: Dec 2002
Posts: 155
S
Strider Offline OP
Vogon poet
OP Offline
Vogon poet
S
Joined: Dec 2002
Posts: 155
In case someone is still interested, I quit working on this script a couple of days after my last post in this topic, but here is my last version:

Code:
;UPnP Script v1.22

alias upnp.debugpermanent {
  if ($isid) return $false
}
on *:start: {
  upnp.start
  if ($upnp.debugpermanent) .debug -i upnp.dcc upnp.dccdebug
}
on *:unload: {
  unset %upnp.*
}
on *:exit: {
  unset %upnp.*
}
on ^*:logon:*: {
  if ($serverip == 84.16.238.88) halt
  if ($readini($mircini,ident,active) == yes) upnp.addPortMapping TCP $readini($mircini,ident,port)
  set %upnp.IdentdMap $true
}
on *:error:*: {
  if ($serverip == 84.16.238.88) halt
  if ($readini($mircini,ident,active) == yes) && (%upnp.IdentdMap) upnp.delPortMapping TCP $readini($mircini,ident,port)
}
on *:connect: {
  if ($serverip == 84.16.238.88) halt
  if ($readini($mircini,ident,active) == yes) upnp.delPortMapping TCP $readini($mircini,ident,port)
  unset %upnp.IdentdMap
}
alias debug {
  if ((%upnp.debuguse) || ($upnp.debugpermanent)) && ($gettok($debug,-1,92) == upnp.dcc) {
    linesep $iif($active == Status Window,-s,$active)
    echo -a -UPNP- Debug is in use.
    linesep $iif($active == Status Window,-s,$active)
  }
  else {
    debug $1-
    if ($0 > 0) set %upnp.debugparams $$1-
  }
}
alias upnp.dccgetnum {
  if (!$isid) return
  var %i = 0
  if ($$1 == send) {
    while (%i <= $send(0)) {
      if ($send(%i).wid == $$2) return %i
      inc %i
    }
  }
  else {
    while (%i <= $chat(0)) {
      if ($chat(%i).wid == $$2) return %i
      inc %i
    }
  }
  return 0
}
alias upnp.dcccheck {
  var %upnp.dccnum = $upnp.dccgetnum($1,$$2)
  var %upnp.dccstatus = $iif($1 == send,$send(%upnp.dccnum).status,$chat(%upnp.dccnum).status)
  if ($istok(failed:active:sent:inactive,%upnp.dccstatus,58)) || (%upnp.dccnum == 0) {
    .timerupnp.dcc $+ $$3 off
    upnp.delPortMapping TCP $3
  }
}
alias upnp.dccdebug {
  tokenize 32 $1-
  if ($1 != ->) return
  if ($istok($+(:,$chr(1),DCC SEND,/,:,$chr(1),DCC CHAT),$5-6,47)) {
    var %upnp.debugport = $remove($9,$chr(1))
    upnp.addPortMapping TCP %upnp.debugport
    .timerupnp.dcc $+ %upnp.debugport 0 0 upnp.dcccheck $6 $iif($6 == send,$send($send($4,0)).wid,$chat($chat($4,0)).wid) %upnp.debugport
    if (!$upnp.debugpermanent) {
      unset %upnp.debuguse
      .debug off
      if (%upnp.debugbefore) {
        .debug %upnp.debugparamsbefore
        unset %upnp.debugbefore
        unset %upnp.debugparamsbefore
      }
    }  
  }
  return
}
alias dcc {
  if ($isid) return
  if ($istok(send:chat,$1,58)) && (!$upnp.debugpermanent) {
    if ($debug != $null) {
      set %upnp.debugparamsbefore %upnp.debugparams      
      .debug off
      set %upnp.debugbefore $true
    }
    unset %upnp.dccdebuguse
    .debug -i upnp.dcc upnp.dccdebug
    set %upnp.debuguse $true
  }
  if ($gettok($debug,-1,92) != upnp.dcc) && ($upnp.debugpermanent) {
    .debug -i upnp.dcc upnp.dccdebug
  }
  dcc $1-
}
alias upnp.start {
  unset %upnp.*
  var %upnp.discip = 239.255.255.250
  var %upnp.disclocalport = $rand(10000,63999)
  while (!$portfree(%upnp.disclocalport)) {
    %upnp.disclocalport = $rand(10000,63999)
  }
  echo $color(other) -gs -UPnP- Searching for Internet Gateway...
  sockclose upnp.discover
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900 $+(M-SEARCH * HTTP/1.1,$crlf,Host: 239.255.255.250:1900,$crlf,Man: "ssdp:discover",$crlf,ST: urn:schemas-upnp-org:service:WANIPConnection:1,$crlf,MX: 2,$str($crlf,2))
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900 $+(M-SEARCH * HTTP/1.1,$crlf,Host: 239.255.255.250:1900,$crlf,Man: "ssdp:discover",$crlf,ST: urn:schemas-upnp-org:service:WANPPPConnection:1,$crlf,MX: 2,$str($crlf,2))
  sockudp -kt upnp.discover %upnp.disclocalport %upnp.discip 1900
}
alias upnp.getLocalInfo {
  set %upnp.localprevip $ip
  set %upnp.localprevhost $host
  unset %upnp.localticks  
  .localinfo -h
  .timerupnp.localip off
  set %upnp.localtimeout 5000
  set %upnp.localticks $ticks
  .timerupnp.localip 0 0 upnp.getLocalInfo.2
}
alias upnp.getLocalInfo.2 {  
  set %upnp.localip $ip
  if (%upnp.localip == $null) {
    if ($calc($ticks - %upnp.localticks) > %upnp.localtimeout) {
      echo $color(other) -gs -UPnP- Local IP not found
      .timerupnp.localip off      
      if (%upnp.localprevip != $null) {
        .localinfo $iif(%upnp.localprevhost == $null,%upnp.localprevip,%upnp.localprevhost) %upnp.localprevip
      }
    }
  }
  else {
    echo $color(other) -gs -UPnP- Local IP: $ip
    .timerupnp.localip off
    if (%upnp.localprevip != $null) {
      .localinfo $iif(%upnp.localprevhost == $null,%upnp.localprevip,%upnp.localprevhost) %upnp.localprevip
    }
  }
}
on *:udpread:upnp.discover: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if ($1 == $null) {
      echo $color(other) -gs -UPnP- Internet Gateway with %upnp.st service found at $sock($sockname).saddr
      upnp.getLocalInfo
      upnp.getDeviceInfo %upnp.discURL
      sockclose $sockname
      return
    }
    if ($gettok($1,1,58) == LOCATION) set %upnp.discURL $remove($gettok($1-,2-,58),$chr(32))
    if ($gettok($1,1,58) == ST) set %upnp.st $remove($gettok($1-,2-,58),$chr(32))
    sockread %info
  }
}
alias upnp.getDeviceInfo {
  echo $color(other) -gs -UPnP- Getting Service Info...
  var %upnp.devip = $gettok($gettok($$1,2,47),1,58)
  var %upnp.devport = $gettok($gettok($1,2,47),2,58)
  sockclose upnp.devinfo
  sockopen upnp.devinfo %upnp.devip %upnp.devport
  sockmark upnp.devinfo $+($chr(47),$gettok($1,3-,47))
}

on 1:sockopen:upnp.devinfo: {
  if ($sockerr > 0) return
  set %upnp.devinfoMode HTTP
  set %upnp.getCURL $false
  sockwrite -nt $sockname GET $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Accept-Language: en
  sockwrite -nt $sockname
}
on *:sockread:upnp.devinfo: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.devinfoMode == XML) {
      if (<URLBase> isin $1-) {
        set %upnp.servinfoBase $upnp.getXMLValue($1-,<URLBase>)
        echo $color(other) -gs -UPnP- Service Base URL: %upnp.servinfoBase
      }
      if (!%upnp.getCURL) {
        if ($+(<serviceType>,%upnp.st,</serviceType>) isin $1-) set %upnp.getCURL $true
      }
      if (%upnp.getCURL) { 
        if (<controlURL> isin $1-) {          
          set %upnp.servinfoURL $upnp.getXMLValue($1-,<controlURL>)
          set %upnp.getCURL $false
          echo $color(other) -gs -UPnP- Service Control URL: %upnp.servinfoURL
          upnp.getServiceExternalIP $+(%upnp.servinfoBase,%upnp.servinfoURL)
        }
      }
    }
    if ($1 == $null) && (%upnp.devinfoMode == HTTP) set %upnp.devinfoMode XML
    sockread %info
  }
}
alias upnp.getServiceExternalIP {
  echo $color(other) -gs -UPnP- Getting Service External IP Address...
  var %upnp.servip = $gettok($gettok($$1,2,47),1,58)
  var %upnp.servport = $gettok($gettok($1,2,47),2,58)
  sockclose upnp.servIP
  sockopen upnp.servIP %upnp.servip %upnp.servport
  sockmark upnp.servIP $+($chr(47),$gettok($1,3-,47))
}

on 1:sockopen:upnp.servIP: {
  if ($sockerr > 0) return
  set %upnp.servIPMode HTTP
  sockwrite -nt $sockname POST $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Content-Length: $iif(PPP isin %upnp.st,273,272)
  sockwrite -nt $sockname Content-Type: text/xml; charset="utf-8"
  sockwrite -nt $sockname SOAPAction: $+(",%upnp.st,#GetExternalIPAddress")
  sockwrite -nt $sockname
  sockwrite -nt $sockname <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  sockwrite -nt $sockname <s:Body>
  sockwrite -nt $sockname $+(<u:GetExternalIPAddress xmlns:u=",%upnp.st,"></u:GetExternalIPAddress>)
  sockwrite -nt $sockname </s:Body>
  sockwrite -nt $sockname </s:Envelope>
  sockwrite -nt $sockname
}
on *:sockread:upnp.servIP: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.servIPMode == XML) {
      if (<NewExternalIPAddress> isin $1-) {
        set %upnp.externalIP $upnp.getXMLValue($1-,<NewExternalIPAddress>)
        echo $color(other) -gs -UPnP- Service External IP Address: %upnp.externalIP
      }
    }
    if ($1 == $null) && (%upnp.servIPMode == HTTP) set %upnp.servIPMode XML
    sockread %info
  }
}
alias upnp.addPortMapping {
  var %upnp.lp = $iif($0 > 2,$3,$$2)
  var %upnp.prot = $upper($iif($0 > 2,$2,$1))
  var %upnp.curl = $iif($0 > 2,$1,$+(%upnp.servinfoBase,%upnp.servinfoURL))
  if (%upnp.localip == $null) {
    echo $color(other) -gs -UPnP- Cannot map port %upnp.lp $+($chr(40),%upnp.prot,$chr(41)) $+ . Local IP Address is unknown
    return
  }
  echo $color(other) -gs -UPnP- Mapping port %upnp.lp $+($chr(40),%upnp.prot,$chr(41)) to %upnp.localip $+ ...
  var %upnp.pmi = $gettok($gettok(%upnp.curl,2,47),1,58)
  var %upnp.pmp = $gettok($gettok(%upnp.curl,2,47),2,58)
  set %upnp.pmport %upnp.lp
  set %upnp.pmprot %upnp.prot
  set %upnp.pmlease 0
  sockclose upnp.pm
  sockopen upnp.pm %upnp.pmi %upnp.pmp
  sockmark upnp.pm $+($chr(47),$gettok(%upnp.curl,3-,47))
}
on 1:sockopen:upnp.pm: {
  if ($sockerr > 0) return
  set %upnp.pmMode HTTP
  sockwrite -nt $sockname POST $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Content-Length: $calc(527 + $len(%upnp.st) + ($len(%upnp.pmport) * 4) + ($len(%upnp.pmprot) * 2) + ($len(%upnp.localip) * 2) + $len(%upnp.pmlease))
  sockwrite -nt $sockname Content-Type: text/xml; charset="utf-8"
  sockwrite -nt $sockname SOAPAction: $+(",%upnp.st,#AddPortMapping")
  sockwrite -nt $sockname
  sockwrite -nt $sockname <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  sockwrite -nt $sockname <s:Body>
  sockwrite -nt $sockname $+(<u:AddPortMapping xmlns:u=",%upnp.st,">)
  sockwrite -nt $sockname <NewRemoteHost></NewRemoteHost>
  sockwrite -nt $sockname $+(<NewExternalPort>,%upnp.pmport,</NewExternalPort>)
  sockwrite -nt $sockname $+(<NewProtocol>,%upnp.pmprot,</NewProtocol>)
  sockwrite -nt $sockname $+(<NewInternalPort>,%upnp.pmport,</NewInternalPort>)
  sockwrite -nt $sockname $+(<NewInternalClient>,%upnp.localip,</NewInternalClient>)
  sockwrite -nt $sockname <NewEnabled>1</NewEnabled>
  sockwrite -nt $sockname $+(<NewPortMappingDescription>mIRC $chr(40),%upnp.localip,:,%upnp.pmport,$chr(41),$chr(32),%upnp.pmport,$chr(32),%upnp.pmprot,</NewPortMappingDescription>)
  sockwrite -nt $sockname $+(<NewLeaseDuration>,%upnp.pmlease,</NewLeaseDuration>)
  sockwrite -nt $sockname </u:AddPortMapping>
  sockwrite -nt $sockname </s:Body>
  sockwrite -nt $sockname </s:Envelope>
  sockwrite -nt $sockname
}
on *:sockread:upnp.pm: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.pmMode == HTTP) {
      if (HTTP/1.1 200 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Added: $+(mIRC $chr(40),%upnp.localip,:,%upnp.pmport,$chr(41),$chr(32),%upnp.pmport,$chr(32),%upnp.pmprot)
        sockclose $sockname
        return
      }
      if (HTTP/1.1 500 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Add Error
        sockclose $sockname
        return
      }
      if ($1 == $null) set %upnp.pmMode XML
    }
    if (%upnp.pmMode == XML) {
      ;if ($+(<u:AddPortMappingResponse xmlns:u=",%upnp.st,"/>) isin $1-) {
      ;}
    }
    sockread %info
  }
}
alias upnp.delPortMapping {
  var %upnp.lp = $iif($0 > 2,$3,$$2)
  var %upnp.prot = $upper($iif($0 > 2,$2,$1))
  var %upnp.curl = $iif($0 > 2,$1,$+(%upnp.servinfoBase,%upnp.servinfoURL))
  echo $color(other) -gs -UPnP- Deleting Port Mapping for port %upnp.lp $+($chr(40),%upnp.prot,$chr(41)) $+ ...
  var %upnp.pmdi = $gettok($gettok(%upnp.curl,2,47),1,58)
  var %upnp.pmdp = $gettok($gettok(%upnp.curl,2,47),2,58)
  set %upnp.pmdport %upnp.lp
  set %upnp.pmdprot %upnp.prot
  sockclose upnp.pmd
  sockopen upnp.pmd %upnp.pmdi %upnp.pmdp
  sockmark upnp.pmd $+($chr(47),$gettok(%upnp.curl,3-,47))
}

on 1:sockopen:upnp.pmd: {
  if ($sockerr > 0) return
  set %upnp.pmdMode HTTP
  sockwrite -nt $sockname POST $sock($sockname).mark HTTP/1.1
  sockwrite -nt $sockname Host: $+($sock($sockname).ip,$chr(58),$sock($sockname).port)
  sockwrite -nt $sockname Connection: close
  sockwrite -nt $sockname Content-Length: $calc(321 + $len(%upnp.st) + $len(%upnp.pmdport) + $len(%upnp.pmdprot))
  sockwrite -nt $sockname Content-Tupe: text/xml; charset="utf-8"
  sockwrite -nt $sockname SOAPAction: $+(",%upnp.st,#DeletePortMapping")
  sockwrite -nt $sockname
  sockwrite -nt $sockname <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  sockwrite -nt $sockname <s:Body>
  sockwrite -nt $sockname $+(<u:DeletePortMapping xmlns:u=",%upnp.st,">)
  sockwrite -nt $sockname <NewRemoteHost></NewRemoteHost>
  sockwrite -nt $sockname $+(<NewExternalPort>,%upnp.pmdport,</NewExternalPort>)
  sockwrite -nt $sockname $+(<NewProtocol>,%upnp.pmdprot,</NewProtocol>)
  sockwrite -nt $sockname </u:DeletePortMapping>
  sockwrite -nt $sockname </s:Body>
  sockwrite -nt $sockname </s:Envelope>
  sockwrite -nt $sockname
}
on *:sockread:upnp.pmd: {
  if ($sockerr > 0) return
  sockread %info
  while ($sockbr > 0) {
    tokenize 32 %info
    if (%upnp.pmdMode == HTTP) {
      if (HTTP/1.1 200 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Deleted for port %upnp.pmdport $+($chr(40),%upnp.pmdprot,$chr(41))
        sockclose $sockname
        return
      }
      if (HTTP/1.1 500 * iswm $1-) {
        echo $color(other) -gs -UPnP- Port Mapping Delete Error
        sockclose $sockname
        return
      }
      if ($1 == $null) set %upnp.pmdMode XML
    }
    if (%upnp.pmdMode == XML) {
      ;if ($+(<u:DeletePortMappingResponse xmlns:u=",%upnp.st,"/>) isin $1-) {
      ;}
    }
    sockread %info
  }
}
alias upnp.getXMLValue {
  if (!$isid) return  
  var %rest = $right($$1-,$calc(0 - ($pos($1-,$2) + $len($2) - 1)))
  var %tag = $2
  if ($pos(%rest,<) != 1) return $gettok(%rest,1,$asc(<))
  else return $null
}


I remember fixing a bug in the "on 1:sockopen:upnp.pmd" event (there was a "%upnp.pmport" that should have been "%upnp.pmdport") and changing the parameters that both the upnp.addPortMapping and upnp.delPortMapping methods take; they now take [Control URL] <TCP|UDP> <port>.

I've been using it for a couple of months now without having any problems. In case this script is unable to close a port it opened thanks to a crash or something similar, you can manually close it with the upnp.delPortMapping method. Hell, you can even close ports that were left open by another application. Ports opened by UPnP usually show up in your router's configuration page, and if you have a mess for whatever reason (for example, sometimes MSN Messenger and BT clients will leave a lot of ports open), unplugging your router should be enough to clear them all.

Strider #181019 16/07/07 05:14 PM
Joined: Jul 2007
Posts: 2
J
Bowl of petunias
Offline
Bowl of petunias
J
Joined: Jul 2007
Posts: 2
Quote:
In case someone is still interested, I quit working on this script a couple of days after my last post in this topic, but here is my last version:

This script is fantastic. I hope you'll keep on developing it!

Jaak #181020 16/07/07 05:27 PM
Joined: Dec 2002
Posts: 3,547
S
Hoopy frood
Offline
Hoopy frood
S
Joined: Dec 2002
Posts: 3,547
Originally Posted By: Jaak

This script is fantastic. I hope you'll keep on developing it!


In case someone is still interested, I quit working on this script a couple of days after my last post in this topic, but here is my last version:

SladeKraven #181047 16/07/07 11:49 PM
Joined: Jul 2007
Posts: 2
J
Bowl of petunias
Offline
Bowl of petunias
J
Joined: Jul 2007
Posts: 2
Originally Posted By: SladeKraven
I quit working on this script a couple of days after my last post in this topic, but here is my last version:

No, really?

Strider #181052 17/07/07 12:50 AM
Joined: Nov 2006
Posts: 1,559
H
Hoopy frood
Offline
Hoopy frood
H
Joined: Nov 2006
Posts: 1,559
/me presses ctrl-break


Link Copied to Clipboard