UPnP - Facile avec Rebol
coccinelle7-Sep-2006/17:53:51+2:00
J'adore Rebol.

Souvent, je pense que certain domaines sont réservés aux gourou et me semble bien mystérieux. Mais avec Rebol, je m'amuse à les explorer et je m'apper£çois que ce n'est pas si compliqué, en tout cas, bien moins compliqué qu'avec d'autres langages qui pourtant offre une foultitude de packages tous plus compliqués les uns que les autres (suivez mon regard vers Java).

Alors en primeur et pour le plaisir, voici un petit script qui permet d'obtenir l'adresse public de son routeur préféré.

Pour cela, il m'a fallu mettre les mains dans le camboui UPnP, multicast XML et SOAP. Tout cela n'est finalement pas si compliqué avec Rebol. Pour le plaisir des yeux et à toute fin utile vous trouverez dans ce script un fonction qui charge de l'xml dans une structure block/objet imbriquée à ma sauce. utile aussi, il y a un peu de code qui crée une enveloppe SOAP, éventuellement à isoler dans une fonction. Tout cela est bien petit, 222 lignes ce qui est très peu.

Naturellement, je vais l'étendre et l'améliorer pour pouvoir mapper dynamiquement un port vers mon PC, ce qui est l'intention initiale.

Marco.
coccinelle7-Sep-2006/17:55:08+2:00
J'oubliais ...

... le script :

REBOL [
Title: "UPnP - Internet Gateway Device"
Date: 07-Sep-2006
Name: "UPnP - IGD"
Version: 0.1
File: %UPnP-IGD.r
Author: Marco
Owner: Marco
Rights: "Public Domaine"
]

all*: :all

ssdp-search: func [
   "Search for an UPnP device"
   /all "Search all device"
   /type
       ST [string!] {Search Target must be one of the following single URI (default ssdp:all):
         ssdp:all
            Search for all devices and services.
         upnp:rootdevice
            Search for root devices only.
         uuid:device-UUID
            Search for a particular device. Device UUID specified by UPnP vendor.
         urn:schemas-upnp-org:device:deviceType:v
            Search for any device of this type. Device type and version defined by UPnP Forum working committee.
         urn:schemas-upnp-org:service:serviceType:v
            Search for any service of this type. Service type and version defined by UPnP Forum working committee.
}
   /max-wait
      MX [integer!] {Maximum wait in second (default 3).
         Device responses should be delayed a random duration between 0 and this many seconds to balance load for the control point when it processes responses.
         This vue should be increased if a large number of devices are expected to respond or if network latencies are expected to be significant.
         Specified by UPnP vendor.
}
   /local port rule result RC device
][

   unless ST [ST: "ssdp:all"]
   unless MX [MX: 3]
   port: open/binary udp://239.255.255.250:1900
   set-modes port compose/deep [
      multicast-ttl: 4
   ]
   insert port rejoin [
      {M-SEARCH * HTTP/1.1} crlf
      {HOST: 239.255.255.250:1900} crlf
      {MAN: "ssdp:discover"} crlf
      {MX: } MX crlf
      {ST: } ST crlf
      crlf
   ]
   device: copy []
   while [wait [port MX]][
      parse replace/all copy port crlf newline [
         {HTTP/1.1 } copy RC to newline newline
         result: to end (result: parse-header none result)
      ]
      unless "200 OK" = RC [
         close port
         to-error reform ["UPnP error (search) :" RC]
      ]
      result: xml-load to-url result/LOCATION
      append/only device result/<root>/i
      unless all [break]
   ]
   close port
   unless all [return pick device 1]
   unless empty? device [return device]
   none
]

xml-load: func [
{Loads an XML file and return nested block/object with item (i) and attribute (a)}
source [file! url! string! any-block! binary!]
/local item result stack
][
   result: copy []
   stack: reduce [result]
   parse load/markup source [ any [
      set item tag! (
         case [
            #"/" = first item [
               remove/part stack 1
            ]
            find "/?" last item [
               remove back tail item
               item: to-block item
               item/1: to-string item/1
               if #"?" = first item/1 [append item/1 #"?"]
               append stack/1 reduce [
                  to-tag item/1
                  item: context [
                     a: copy next item
                     i: copy []
                  ]
               ]
               item: item/a
               forskip item 2 [
                  item/1: to-word head remove back tail to-string item/1
               ]
            ]
            true [
               item: to-block item
               append stack/1 reduce [
                  to-tag item/1
                  item: context [
                     a: new-line/all/skip copy next item true 2
                     i: new-line/all/skip copy [] true 2
                  ]
               ]
               insert/only stack item/i
               item: item/a
               forskip item 2 [
                  item/1: to-word head remove back tail to-string item/1
               ]
            ]
         ]
      )
   |
      set item string! (
         unless #"^/" = first item [
            append stack/1 item
         ]
      )
   ]]
   result
]

soap-invoke: func [
   url [url!]
   serviceType [string!]
   actionName [string!]
   argument [block!]
   /local port rule result RC body
][
   body: copy ""
   foreach [name value] argument [
      repend body [
      tab tab tab tab"<u:" name { xmlns:u="} serviceType {">} value {</u:} name {>} crlf
      ]
   ]
   body: rejoin [
      {<s:Envelope} crlf
    tab {xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"} crlf
    tab   {s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"} crlf
    {>} crlf
       tab tab   {<s:Body>} crlf
      tab   tab   tab   {<u:} actionName { xmlns:u="} serviceType {">} crlf
      body
      tab   tab   tab   {</u:} actionName {">} crlf
       tab   tab   tab   {</s:Body>} crlf
      {</s:Envelope>} crlf
   ]
   url: decode-url url
   port: open/binary rejoin [tcp:// url/host ":" url/port-id]
   insert port rejoin [
      {POST /} url/path url/target { HTTP/1.1} crlf
      {HOST: } url/host ":" url/port-id crlf
      {CONTENT-LENGTH: } length? body crlf
      {CONTENT-TYPE: text/xml; charset="utf-8"} crlf
      {SOAPACTION: "} serviceType "#" actionName {"} crlf
      crlf
      body
   ]
   either port = wait [port 1][
      parse replace/all copy port crlf newline [
         {HTTP/1.1 } copy RC to newline newline
         result: to end
      ]
      close port
   ][
      close port
      to-error reform ["SOAP error (invoke) : No response"]
   ]
   result: parse-header none result
   result/content: xml-load result/content
   unless "200 OK" = RC [
      to-error reform ["SOAP error (invoke):" RC]
   ]
   argument: copy []
   foreach [item1 item2] result/content/<s:envelope>/i/<s:body>/i/(rejoin [<u:> actionName 'response])/i [
      repend argument [to-word to-string item1 item2/i/1]
   ]
   argument
]

upnp-GetExternalIPAdress: func [
   URLBase [url! string!]
   service [block!]
   /local
][
   url: to-url rejoin [URLBase service/<controlURL>/i/1]
   to-tuple second soap-invoke url "urn:schemas-upnp-org:service:WANIPConnection:1" "GetExternalIPAddress" []
]

print "Starting ..."

either root: ssdp-search/type "urn:schemas-upnp-org:service:WANIPConnection:1" [
   service: root/<device>/i/<devicelist>/i/<device>/i/<deviceList>/i/<device>/i/<serviceList>/i/<service>/i
   print [
      "Found :" newline
      tab "urlBase :" root/<urlBase>/i/1 newline
      tab "friendlyName :" root/<device>/i/<friendlyName>/i/1 newline
      tab "deviceType :" root/<device>/i/<deviceType>/i/1 newline
      tab "externalIPAdress :" upnp-GetExternalIPAdress root/<urlBase>/i/1 service
   ]
   ask "Done"
][
   ask "No device"
]
GreG7-Sep-2006/23:35:17+2:00
Je ne connais pas UpnP, peux-tu donner un exemple de ce a quoi ca sert?
Merci!
Greg
Coccinelle8-Sep-2006/8:26:40+2:00
Hello Greg,

UPnP = Universal Plug & Play.

C'est un standard qui permet de converser avec des équipement sur le réseau. Ces device peuvent être aussi bien des imprimantes que des caméras ou des passerelles internet (modem/routeur).

Le standard défini les fonctions suivantes :
- découvertes des équipements
- description des équipements
- controle des équipements
- notification d'événement

Dans mon cas, je veux que les connections TCP entrante sur le port 80 depuis Internet soit routées sur mon PC où tourne un serveur web. Comme l'adresse IP de mon PC est attribuée automatiquement (DHCP), cela ne fonctionne pas si je configure le NAT sur ma passerelle Internet.

UPnP me permet de le faire dynamiquement et de rendre la chose transparente pour l'utilisateur.

Plusieurs programmes utilisent UPnP pour résoudre ce problème, à ma connaissance, MSN Messenger, JAlbum et BearShare.

Un serveur web intégrant DynDNS et UPnP est une solution extrèmement simple pour avoir son serveur web à la maison sans trop de soucis.

Marco.
coccinelle10-Sep-2006/21:57:40+2:00
J'ai publié un pilote sur la librairie ici : http://www.rebol.org/cgi-bin/cgiwrap/rebol/view-script.r?script=upnp-igd.r

Marco.
GreG11-Sep-2006/21:02:37+2:00
Excellent!
Je vais integrer ca a la prochaine version de PhotosReViewer qui integre un serveur web.
Je me suis toujours demande comment les MSN et autres faisaient!
Super contribution!
ZeBrain11-Sep-2006/23:05:17+2:00
Euh ... UPnP c pas un peu passoire ? Il me semble que ça crée de gros trous de sécurité.
ZeBrain11-Sep-2006/23:30:51+2:00
Euh ... UPnP c pas un peu passoire ? Il me semble que ça crée de gros trous de sécurité.
coccinelle12-Sep-2006/11:04:38+2:00
ZeBrain,

Effectivement, UPnP permet à des programmes d'ouvrir des ports automatiquement, mais c'est à l'utilisateur de choisir s'il autorise UPnP ou pas, comme c'est à lui d'utiliser un firewall ou pas.

Greg,

Comme je l'ai dit, c'est un pilote et j'ai encore quelques problèmes avec le mapping des ports, çà marche, mais çà bloque un peu et je ne comprends pas pourquoi. Bon, j'ai un Linksys et c'est pas top top pour certains truc.

Je ne sais pas si tu l'a déjà fait, mais tu pourrais aussi intégrer le client DynDns de Shad dans ton serveur comme çà pour simplifer le déploiement de PhotosReViewer sur un PC.
GreG13-Sep-2006/21:53:28+2:00
Un lien (qui marche) sur le client DynDns de Shad?

UPnP me parait une option interessante, comme tu le dis, c'est a l'utilisateur de choisir.
GreG13-Sep-2006/21:56:25+2:00
Est-ce que toutes les applis du type MSN, AltME, eMule utilisent UPnP ou existe-t-il d'autres mecanismes?
Je suis completement novice sur ce sujet!
Coccinelle14-Sep-2006/0:02:05+2:00
Pour le lien sur le client dyndns (rdyndns-client.r), on en parle ici http://www.codeur.org/forum/message.php?ID_Sujet=2323

Mais je ne crois pas que les liens sur le framework soient bon.

J'ai le source si tu veux, mais entre temps, je vais suggérer à Shad de le publier sur rebol.org

Tu peux aussi me contacter sur AltMe (pseudo Coccinelle) et je te le donne

L'idéal, c'est d'avoir son routeur qui se charge de cela surtout si l'on a plusieurs ordi sur son réseau ce qui est le cas chez moi.

Moi aussi je suis novice en matière d'UPnP et je ne sais pas s'il y a d'autres solutions.

Marco.
Reb-Kodeur15-Sep-2006/7:44:43+2:00
Ne pas oublier que le LinkSys possède en son sein un service dyndns.org

Mon routeur est donc tjrs atteignable à la même adresse, à condition et je me souviens pour avoir farfouillé ds les forums le concernant d'avoir un compte sur dyndns.org et pas sur un de ses 'miroirs' (DynDNS offrant d'autres types noms de dns)
coccinelle15-Sep-2006/8:09:23+2:00
Effectivement mon Linksys (WAG354) a un client dyndns mais le problème c'est qu'il m'a fallu mettre à jour le firmware pour qu'il fonctionne et là ce fût un peu stressant lorsqu'il m'a donné un message d'erreur alors que tout s'était bien passé.

Maintenant ça marche et le service UPnP qui avait des erreurs est aussi corrigé.

Marco.
Reb-Kodeur15-Sep-2006/8:46:23+2:00
Peux-tu publier le code afin que nous puissions tester..
Coccinelle15-Sep-2006/19:33:49+2:00
J'ai publié le pilote UPnP sur la librairie ici : http://www.rebol.org/cgi-bin/cgiwrap/rebol/view-script.r?script=upnp-igd.r

Cela va évoluer, mais j'attends avec impatience de savoir comment réagit vos routeurs.

Marco.
GreG20-Nov-2006/21:47:45+1:00
C'est fait en java mais ca peut toujours servir pour le projet rebol:

http://www.sbbi.net/site/upnp/

Login required to Post.


Powered by RebelBB and REBOL 2.7.8.4.2