Annonce

Réduire
Aucune annonce.

Utilitaire de Redirection de Ports via UPnP

Réduire
X
  • Filtre
  • Heure
  • Afficher
Tout nettoyer
nouveaux messages

  • Utilitaire de Redirection de Ports via UPnP

    Bonjour,

    Dans un précèdent sujet, j'avais mis en oeuvre un moyen de contournement de pare-feu par injection de processus. Dans cet exemple, il s'agissait de mettre en écoute un serveur UDP en se faisant passer pour Firefox de sorte que Windows n'émette aucun avertissement. Parmi les problèmes soulevés par une telle technique "d'infiltration", il y avait évidemment le fait qu'ouvrir un port sur un PC ne permet pas de l'atteindre depuis internet. Il faut encore passer le routeur ou la box, c'est-à-dire rediriger le port qui va bien vers la machine en question.

    J'avais écrit à ce propos que ce n'est pas impossible. Je pensais bien évidemment à l'Universal Plug and Play (UPnP), car c'est justement ce qu'il permet, entre autres choses. Un programme quelconque peut en effet, sans droit particulier, y-compris sous système UN*X, ouvrir un port TCP ou UDP sur la passerelle et le rediriger vers la machine hôte. Ce n'est pas un cauchemar, c'est juste la réalité. Toutes les box du marché ont l'UPnP activé par défaut. ​

    J'y suis donc allé de mon propre code, le but étant de comprendre le mieux possible comment tout cela fonctionne (car il existe quelques utilitaires très efficaces dans ce domaine comme nous verrons plus loin, mon programme n'apporte rien de nouveau). Commençons par voir les fonctionnalités essentielles d'un tel logiciel :

    Code:
    E:\Prog\Upnp>urpu
    
    Usage :
    
       urpu [-s] add {TCP ou UDP} port_externe port_interne [IP_ext] [IP_int]
            -> Ajoute une règle de redirection UPnP.
    
       urpu [-s] del {TCP ou UDP} port_externe [IP_ext]
            -> Supprime une règle de redirection UPnP.
    
       urpu [-s] list
            -> Liste les règles de redirection UPnP existantes.
    
       urpu [-s] show [Type]
            -> Montre les dispositifs détectés.
               Type est l'urn sans le préfixe 'urn:schemas-upnp-org:device:'.
               Si pas de Type : liste tous les dispositifs racines.
    
       urpu [-s] dump  Fichier [Type]
       urpu [-s] dumpx Fichier TypeExact
            -> Ecrit le document XML du/des dispositif(s) dans 'Fichier'.
               dump  : Si pas de Type, sélectionne la passerelle.
               dumpx : nécessite l'urn exacte, avec préfixe.
    
    -s : mode silence, aucune sortie sur la console (sauf list et show).
    Note : . est valide comme paramètre IP ; signifie IP laissée vide.
    Pour le moment nous allons voir les commandes add, del et list.

    Avec add, on peut, par exemple, rediriger le port TCP 8000 vers TCP 8000 de la machine hôte (192.168.1.9)
    Code:
    E:\Prog\Upnp>urpu add tcp 8000 8000
    
    Utilitaire de Redirection de Ports par UPnP
    
    - Initialisation de la bibliothèque COM... Ok.
    - Création d'un objet DeviceFinder... Ok.
    - Recherche d'une passerelle (demande quelques secondes)... Ok.
    - Obtention de l'adresse IP locale : 192.168.1.9
    - Recherche du serviceId : WANIPConn1... Ok.
    - Exécution de AddPortMapping()... Ok.
    Avec list, on peut vérifier que la redirection a bien été enregistrée :

    Code:
    E:\Prog\Upnp>urpu list
    
    Utilitaire de Redirection de Ports par UPnP
    
    - Initialisation de la bibliothèque COM... Ok.
    - Création d'un objet DeviceFinder... Ok.
    - Recherche d'une passerelle (demande quelques secondes)... Ok.
    - Recherche du serviceId : WANIPConn1... Ok.
    - Exécution(s) de GetGenericPortMappingEntry()... Ok.
    
     Description Proto  PortExt  PortInt     IP interne       IP externe    Active
    ------------------------------------------------------------------------------
                  TCP    8000     8000      192.168.1.9                      True
    ------------------------------------------------------------------------------

    Avec urpu del tcp 8000, on efface cette redirection.



    Règles d'utilisation d'UPnP pour la redirection de port

    UPnP intègre des règles d'utilisation dont certaines sont qualifiées de "règles de sécurité" :
    • La liste de redirection UPnP est différente de celle offerte par NAT (port forwarding). Cette dernière étant celle où il faut se connecter sur la box (en général par http ou https) et écrire manuellement la redirection de port. En conséquence, il n'est pas possible d'affecter la table NAT par UPnP ; en particulier, on ne peut pas effacer une redirection de port NAT.
    • Rediriger un numéro de port inférieur à 1024 n'est pas autorisé. Ces ports sont réservés à l'utilisation de protocoles connus (well-known ports).
    • Il n'est pas possible de rediriger un port sur une autre machine que celle d'où provient la commande UPnP. C'est pourquoi urpu s'efforce de déterminer l'adresse IP locale et de la mettre en place par défaut dans la redirection. Par exemple, l'IP de mon PC étant 192.168.1.9, si j'essaie de placer une redirection sur 192.168.1.10, voici la réponse de la passerelle :
    Code:
    E:\Prog\Upnp>;urpu add tcp 8000 8000 . 192.168.1.10
    (...)
    - Exécution de AddPortMapping()... Erreur : 0x25E
      -> Action not authorized
    • Toujours dans la même idée, si une redirection est créée depuis une machine donnée, seule celle-ci a le droit de l'effacer (la discrimination se fait sur la base de l'adresse IP du demandeur par rapport à l'adresse IP de redirection de la règle).
    • Il est possible de rediriger un port pour une seule IP externe. Mais pour effacer cette règle, il faudra préciser cette IP externe. Par exemple, si je fais urpu add udp 7522 7522 163.172.59.161 et qu’ensuite je souhaite l’enlever :
    Code:
    E:\Prog\Upnp>urpu del udp 7522
    (...)
    - Exécution de DeletePortMapping()... Erreur : 0x2CA
      -> NoSuchEntryInArray
    
    E:\Prog\Upnp>urpu del udp 7522 163.172.59.161
    (...)
    - Exécution de DeletePortMapping()... Ok.
    Dernière modification par Icarus, 28 mai 2018, 20h23.

  • #2
    Quelques explications sur l'UPnP

    On trouvera toutes sortes de liens donnant des informations plus ou moins approfondies, notamment https://fr.wikipedia.org/wiki/Universal_Plug_and_Play ainsi que le standard UPnP V2.

    Je déconseille de se plonger dans le standard, non qu'il soit mal écrit, mais parce qu'il englobe tous les aspects du sujet en utilisant des concepts plutôt abstraits ou qui demandent un backgroung solide concernant certains protocoles réseau. Cela dit, c'est la bible. C'est ce texte - avec quelques autres - qui impose comment les choses doivent se passer.

    Pour aborder l’UPnP, il est préférable de commencer par la pratique avec les Developer Tools For UPnP. Parmi ces outils, il y a DeviceSpy :



    DeviceSpy permet non seulement de lister tous les équipements compatibles UPnP du réseau mais aussi d'aller examiner en profondeur ce qu'ils offrent comme fonctionnalités. Mieux encore, on peut exécuter des commandes. Ainsi, par exemple, il est possible de rediriger un port sur la passerelle à partir de ce logiciel.

    En quelques mots et pour ce qui nous intéresse, voilà comment cela fonctionne : UPnP utilise exclusivement http sur IP (le type de réseau sous-jacent est indifférent), que ce soit http/UDP ou http/TCP.

    Lorsqu'un programme lance une recherche, il émet un paquet UDP sur le port 1900 en diffusion à l’aide d’une IP réservée à cet usage : 239.255.255.250 (cf. protocole SSDP). La requête peut demander un type de dispositif précis ou pas.

    Les appareils (devices) qui reçoivent cette demande et qui sont concernés doivent répondre via UDP en spécifiant une URL. Celle-ci va permettre au programme effectuant la recherche de charger le document XML du dispositif via http/TCP. Ce document va donner les caractéristiques du dispositif ainsi que la liste des services qu'il propose. Il peut contenir des dispositifs enfants dits "intégrés" (embeded). Ceux-ci, à leur tour, peuvent proposer des services et contenir d'autres dispositifs intégrés.

    Dans l'optique de contrôler un dispositif, le programme doit trouver le service qui l'intéresse (les noms et les caractéristiques sont définis dans le standard). Une fois ce service trouvé, il exécute une requête (toujours en http/TCP) pour obtenir un autre document XML appelé Service Control Protocol Description (SCPD). Ce document va contenir la description des "variables d'états" et fonctions que le service propose. Il énumère et type les variables ainsi que les arguments de chaque fonction. Dans le document du dispositif, parmi les paramètres du service, on trouve le ControlURL qui va permettre d'exécuter ces fonctions (encore via TCP/http). Les commandes et leurs paramètres sont passés via une requête structurée XML (SOAP).

    Pour mieux se rendre compte du déroulement des opérations, on pourra employer un outil de capture de paquets tel que Wireshark et utiliser la règle de filtrage suivante : not arp and !(udp.port == 53) and ip.addr==a.b.c.d and (ip.host==a.e.f.g or ip.host==239.255.255.250)
    a.b.c.d est l'IP de la machine depuis laquelle on opère et a.e.f.g est l'IP de la passerelle.
    Lancer la capture puis, immédiatement, exécuter urpu list (ou DeviceSpy).

    Voici les deux premiers paquets. La requête en diffusion du programme demandeur et la réponse de la passerelle :
    Code:
    No.     Time           Source                Destination           Protocol Length Info
         13 2.272368       192.168.1.9           239.255.255.250       SSDP     173    M-SEARCH * HTTP/1.1
    
    Frame 13: 173 bytes on wire (1384 bits), 173 bytes captured (1384 bits) on interface 0
    Ethernet II, Src: Micro-St_59:8b:35 (44:8a:5b:59:8b:35), Dst: IPv4mcast_7f:ff:fa (01:00:5e:7f:ff:fa)
    Internet Protocol Version 4, Src: 192.168.1.9, Dst: 239.255.255.250
    User Datagram Protocol, Src Port: 53790, Dst Port: 1900
    Simple Service Discovery Protocol
        M-SEARCH * HTTP/1.1\r\n
        Host:239.255.255.250:1900\r\n
        ST:urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n
        Man:"ssdp:discover"\r\n
        MX:3\r\n
        \r\n
    
    
    No.     Time           Source                Destination           Protocol Length Info
         16 2.747040       192.168.1.1           192.168.1.9           UDP      508    36394 → 53790 Len=466
    
    Frame 16: 508 bytes on wire (4064 bits), 508 bytes captured (4064 bits) on interface 0
    Ethernet II, Src: AnovFran_11:bd:3b (b8:26:6c:11:bd:3b), Dst: Micro-St_59:8b:35 (44:8a:5b:59:8b:35)
    Internet Protocol Version 4, Src: 192.168.1.1, Dst: 192.168.1.9
    User Datagram Protocol, Src Port: 36394, Dst Port: 53790
    Data (466 bytes)
    
    HTTP/1.1 200 OK\r\n
    CACHE-CONTROL: max-age=1800\r\n
    DATE: Sat, 24 Mar 2018 21:18:09 GMT\r\n
    EXT:\r\n
    LOCATION: http://192.168.1.1:60000/aeaf9faa/gatedesc1.xml\r\n
    OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01\r\n
    01-NLS: 11c3013c-1dd2-11b2-b8f6-eaa47c3c298a\r\n
    SERVER: Unspecified, UPnP/1.0, SoftAtHome\r\n
    X-User-Agent: redsonic\r\n
    ST: urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n
    USN: uuid:aa75d287-0773-330e-80aa-d50bf5b6d9af::urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n
    \r\n
    Le programme (urpu en l'occurence) cherche un dispositif de type urn:schemas-upnp-org:device:WANConnectionDevice:1 et la passerelle répond en donnant l'URL où se trouve son document XML via l'entête LOCATION:. urn signifie Uniform Resource Name. Il est découpé par le délimiteur ':'. En seconde position on trouve le nom de domaine (organisme à l'origine du nom), en troisième le type de ressource (ici c'est le plus souvent device ou service), en quatrième le nom standardisé du dispositif ou du service et en dernier, la version d'UPnP demandée / servie (1 ou 2).


    Utilisation avancée d'urpu

    Maintenant qu'on a quelque peu soulevé le voile d'UPnP en intégrant des notions telles que l'urn, le document XML d'un dispositif et le SCPD d'un service, on peut aller un peu plus loin dans l'usage de l'utilitaire.
    • urpu show permet de lister tous les dispositifs UPnP détectés sur le réseau. Il utilise une sorte d'urn spéciale : upnp:rootdevice. Elle permet de lister tous les dispositifs racines (c'est-à-dire tous les dispositifs sauf les enfants).
    Code:
    E:\Prog\Upnp>urpu show
    (...)
    - Recherche des dispositifs (demande quelques secondes)... Ok.
    
    Orange Livebox [urn:schemas-upnp-org:device:InternetGatewayDevice:2]
    Samsung C48x Series (192.168.1.10) [urn:schemas-upnp-org:device:Printer:1]
    décodeur TV d'Orange [urn:schemas-upnp-org:device:Basic:1]
    • On peut spécifier un type de dispositif particulier. Par exemple : urpu show Printer:1.
    Code:
    E:\Prog\Upnp>urpu show Printer:1
    (...)
    Samsung C48x Series (192.168.1.10) [urn:schemas-upnp-org:device:Printer:1]
    • urpu dump et urpu dumpx permettent de recueillir les documents XML de dispositifs ou les SCPD de services (le SCPD ne peut être obtenu que sur Windows >= 8). Utilisé tel quel, urpu dump a.xml va écrire le document XML de la passerelle dans le fichier a.xml.

      On peut indiquer que l'on souhaite avoir le ou les documents XML d'un autre type de dispositif. Par exemple : urpu dump a.xml Basic:1. En interne, tout comme avec la commande show, le programme ajoute le préfixe 'urn:schemas-upnp-org:device:' au paramètre 'Basic:1' pour faire la recherche.

      Si l'on souhaite extraire le document d'un dispositif hors domaine 'schemas-upnp-org' ou obtenir le SCPD d'un service, il faut employer dumpx qui va passer le paramètre de recherche sans le modifier. Par exemple, nous pouvons recueillir le SCPD du service connecté de la passerelle (ADSL ici) :
    Code:
    C:/>urpu dumpx s.xml urn:schemas-upnp-org:service:WANPPPConnection:1
    
    Utilitaire de Redirection de Ports par UPnP
    
    - Initialisation de la bibliothèque COM... Ok.
    - Création d'un objet DeviceFinder... Ok.
    - Service : urn:schemas-upnp-org:service:WANPPPConnection:1
    - Recherche du/des Service(s) (demande quelques secondes)... Ok.
    - Ouverture du fichier désigné... Ok.
    - Obtention du document XML N°1... Ok.
    - Ecriture du document dans le fichier désigné... Ok.
    Dernière modification par Icarus, 27 mars 2018, 18h36.

    Commentaire


    • #3
      Programmes et bibliothèques existants employant UPnP

      Ici, le monde se divise en deux. Il y a d'une part ceux qui utilisent l'API dédiée Microsoft (UPnP Control Point API) et de l'autre, ceux qui utilisent des solutions plus ou moins natives de serveur / client SSDP plus un agent http / SOAP (ainsi qu'un analyseur XML au milieu de tout ça). Ces dernières solutions sont les plus coûteuses en termes de temps et de lignes de code mais elles ont l'énorme avantage d'être fiables en plus d'être potentiellement portables sous n'importe quel OS. Nous parlerons de l'API Microsoft un peu plus loin...

      La bibliothèque de loin la plus populaire est écrite en C. Il s'agit de miniupnpc, elle peut être utilisée sous Windows ou UN*X. Un exemple d'utilisation de la bibliothèque est fourni avec (upnpc-static). Il s'agit d'un utilitaire comparable à urpu. La seule chose qu'on puisse lui reprocher c'est qu'il a été victime de son succès et par contrecoup, il est reconnu comme une menace par la plupart des antivirus ; même par mon Avast gratuit, c'est dire...

      On trouve également une bibliothèque qui a la réputation d'être plus complète mais aussi plus lourde et surtout qui n'est pas compatible avec Windows : libpupnp.

      Enfin, on trouvera de nombreux programmes (souvent en Visual C++, utilisant l'API Microsoft) avec parfois une bibliothèque.

      La plupart des utilitaires de redirection de port par UPnP que j'ai testé chez moi n'ont pas marché. Seuls upnpc et DeviceSpy se sont avérés fonctionnels.


      Conception d'un utilitaire de redirection de port par UPnP

      Il était hors de question que je me contente d'utiliser une bibliothèque toute faite comme minipunpc, aussi performante soit-elle. Je ne pouvais pas non plus prendre le temps d'écrire tout ou partie des éléments nécessaires pour utiliser UPnP, le travail aurait été titanesque. J'ai donc choisi d'employer l'API Microsoft Control Point. Cette API, comme beaucoup d'autres sous Windows, utilise la technologie Component Object Model (COM).

      Autant l'avouer tout de suite : je déteste COM. Maintenant encore, après ces quelques mille lignes de code, j'ai comme un frisson désagréable qui me parcourt l'échine rien que d'y penser. Cela ne date d'aujourd'hui mais de la première fois (et la seule) où j'ai écrit un jeu sous DirectX. Et oui, DirectX utilise COM...

      COM est un sortilège maléfique qui donne l'impression d'écrire en Visual Basic quel que soit le langage qu'on utilise. Il s'affiche de prime abord sous de faux airs de simplicité avant de vous entraîner dans les méandres inextricables d’un code dénué de sens. Tout ce qui paraissait aller de soi devient alors d'une lourdeur indicible. On ne peut pas en ressortir indemne.

      Je ne suis pas particulièrement masochiste. Ce qui m'a plu au départ dans ce concept est l'incapacité du compilateur 32 bits de MinGW-w64 à comprendre le Control Point API de Microsoft. J'ai mis quelques temps avant de parvenir à lui faire digérer les premières lignes de code (voir le paragraphe "Compilation du programme"). Comme j'avais commencé, j'ai continué puis j’ai terminé le job.

      Laissons pour l’instant le "côté COM" pour évoquer les principes utilisés :
      1. Recherche de la passerelle. Plutôt que chercher le dispositif racine (urn:schemas-upnp-org:device:InternetGatewayDevice:1), le programme demande directement le dispositif enfant qui contient le service d’intérêt. Ce dispositif, urn:schemas-upnp-org:device:WANConnectionDevice:1, est celui qui est connecté à Internet.
      2. Recherche dans le dispositif WANConnectionDevice:1 du service qui offre les fonctions de redirection. Le nom de ce service peut varier mais l'un de ses paramètres, le ServiceId doit être urn:upnp-org:serviceId:WANIPConn1. C'est sur cette base que la recherche est effectuée.
      3. Une fois que le service est trouvé, il suffit d'appeler la fonction qui va bien : AddPortMapping() pour urpu add, DeletePortMapping() pour urpu del ou GetGenericPortMappingEntry() pour urpu list.
      On remarquera que les recherches effectuées concernent des dispositifs et services d’UPnP version 1. Ceci car toutes les box n'ont pas la version 2 et, qui plus est, elle n'apporte rien pour ce qui nous occupe. Par ailleurs, UPnP version 2 stipule qu’il convient de présenter une interface de version 1 si telle est la version demandée (rétrocompatibilité).

      L'API de Microsoft rend ces appels sinon faciles mais en tout cas possibles avec peu de lignes de code. Voyons superficiellement comment cela se présente (ce n’est pas le code exact qui se trouve dans urpu) :
      Code:
      Log("- Initialisation de la bibliothèque COM");
      HRESULT hr = CoInitialize(NULL); // Single-thread apartment
      if (FAILED(hr)) Echec(hr); else Succes();
      
      IUPnPDeviceFinder* pDeviceFinder;
      Log("- Création d'un objet DeviceFinder");        
      hr = CoCreateInstance(
                                  CLSID_UPnPDeviceFinder,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IUPnPDeviceFinder,
                                  (void**) &pDeviceFinder
      );
      pcECHEC_REUSSITE(hr); // Teste échec ou réussite selon que hr = S_OK ou pas
      
      Log("- Recherche d'une passerelle (demande quelques secondes)");
      IUPnPDevices* piuDevices ;
      BSTR bstr = SysAllocString(L"urn:schemas-upnp-org:device:WANConnectionDevice:1");
      hr = pDeviceFinder->FindByType(bstr, 0, &piuDevices);
      SysFreeString(bstr);
      pcECHEC(hr);
      On commence par initialiser la chose COM puis on demande poliment une interface IUPnPDeviceFinder (je laisse au lecteur le soin d’apprécier l’élégance de CoCreateInstance()). Ensuite, l’objet DeviceFinder en main (si j’ose dire), on effectue la recherche pour trouver le dispositif enfant de la passerelle « qui contient le service qui contient les fonctions ». Passons le type de chaîne de caractères imposée par COM, BSTR qui est en fait composée d’une wchar_t* (1 caractère = 2 octets). Passons aussi qu’il faille la créer et la détruire avec des fonctions précises. Passons égalementNon, je m’enfonce là. :-D

      Bref, on appelle la fonction FindByType() et on récupère une collection de dispositifs (IUPnPDevices) répondant au type WANConnectionDevice:1. C’est une collection car rien n’interdit qu’il y ait plusieurs passerelles connectées (même si ce n’est pas le cas chez un particulier). Là, on se dit parfait, il n’y a plus qu’à récupérer notre dispositif (IUPnPDevice) avec un truc du genre piuDevices[0]. Et non...

      Non, madame, monsieur, vous n’êtes pas en train de faire du vulgaire C++ avec des tableaux simplistes ou ces std::vector si communs et d’un ennui mortel. Vous faites du COM et, à ce titre, vous êtes au sommet De IUnknown en tout cas. Je conseille aux codeurs/codeuses sensibles de passer le paragraphe qui suit :
      Code:
      // Enumération -> Interface IUnknown pouvant être énumérée
      IUnknown* pUnk;
      hr = piuDevices->get__NewEnum(&pUnk);
      piuDevices->Release();
      pcECHEC(hr);
      
      // Demande une interface d'énumération (IEnumUnknown) à partir de pUnk
      IUnknown* piuItem; IEnumUnknown* pieUn;
      hr = pUnk->QueryInterface(IID_IEnumUnknown, (void**) &pieUn);
      pUnk->Release();
      pcECHEC(hr);
      
      // Enumération du contenu de piuDevices
      CDev cdev; IUPnPDevice* pDev;
      while (pieUn->Next(1, &piuItem, NULL) == S_OK)
      {
                hr = piuItem->QueryInterface(IID_IUPnPDevice, (void**) &pDev);
                piuItem->Release();
                if (hr == S_OK) cdev.push_back(pDev);
      }
      pieUn->Release();
      Il faut savoir que toute interface COM hérite de IUnknown. C’est le type d’objet commun à tous les autres. Tout ce que l’on peut faire sur IUPnPDevices est de demander un objet d’énumération avec la fonction get__NewEnum(). Elle renvoie une interface IUnknown qu’il faut en premier lieu convertir en interface d’énumération IEnumUnknown grâce à la fonction QueryInterface(). Cette interface donne accès à la fonction Next() qui permet enfin de parcourir notre collection de devices. A partir de là, on peut recueillir un par un nos dispositifs sous la forme de IUnknown qu’on va finalement convertir en IUPnPDevice, toujours avec QueryInterface(). Ici, on stocke les pointeurs IUPnPDevice* dans le vecteur cdev (std::vector<UPnPDevice*>) afin de les traiter par la suite.

      Reste à obtenir l’objet du service convoité. Cette fois, heureusement, si on récupère également une collection (de services), il est possible de retrouver celui qui nous intéresse via la fonction get_Item() qui l’extrait selon son ServiceId (dont nous avons déjà parlé). Il n’y a donc pas besoin d'énumérer :
      Code:
      IUPnPDevice* pDev = cdev[0]; // Par simplification pour la démo
      Log("- Recherche du serviceId : WANIPConn1");
      IUPnPServices* pServices; IUPnPService* pServ;
      hr = pDev->get_Services(&pServices);
      if (hr == S_OK)
       {
            long lN;
            pServices->get_Count(&lN);
            if (lN != 0)
            {
                  bstr = SysAllocString(L"urn:upnp-org:serviceId:WANIPConn1");
                  hr = pServices->get_Item(bstr, &pServ);
                  SysFreeString(bstr);
             }
             pServices->Release();
      }
      On récupère les services de notre dispositif dans pServices et on extrait celui qui a WANIPConn1 comme ServiceId. Il se retrouve alors dans pServ.

      Dès lors, il suffit d’appeler la fonction InvokeAction() offerte par IUPnPService pour exécuter une commande UPnP. Je ne puis aborder les détails de sa mise en oeuvre effective car le passage des paramètres, sauce COM, est d’une complexité effarante. Les personnes intéressées pourront se rapporter au code ci –joint qui est amplement commenté.



      Compilation du programme

      Si ce n’est déjà fait, il faut installer MinWG-w64.
      • Télécharger l'installateur de minGW-W64. Le compilateur 32 bits est le seul nécessaire ici.
      • Lancer l'installateur et choisir les paramètres : Version = laisser le défaut, Architecture = i686, Thread = win32, Exception = dwarf, Buil Revison = laisser le défaut. Par défaut, il va s'installer dans C:\Program Files (x86)\mingw-w64\i686-7.2.0-win32-dwarf-rt_v5-rev1\.
      Ensuite, il faut effectuer quelques petites bidouilles pour que g++ prenne en charge l’API Control Point de Microsoft :
      • Télécharger les sources en pièce jointe de ce message.
      • Télécharger llibuuid.lib : http://dl.free.fr/iRMbzHMRM (llibuuid.lib est trop volumineux pour être téléchargé ici).
      • Placer le fichier UPnP.h* dans mingw-w64\i686-...\mingw32\i686-w64-mingw32\include.
      • Placer le fichier libuuid.lib* dans mingw-w64\i686-...\mingw32\i686-w64-mingw32\lib.
      Le reste n’est qu’une simple utilisation de g++ :
      • Placer les autres fichiers de la pièce jointe (AideALC.cpp et urpu.cpp) dans un répertoire.
      • Ouvrir une ligne de commande et se placer dans le répertoire créé précédemment.
      • Taper set path=C:\Program Files (x86)\mingw-w64\i686-7.2.0-win32-dwarf-rt_v5-rev1\mingw32\bin (Entrée).
      • Taper g++ -static -s -o urpu.exe urpu.cpp -l ole32 -l oleaut32 -llibuuid -lws2_32 (Entrée). Il faut enlever l'espace entre le 'l' et le 'o' dans "-l ole32" et "-l oleaut32". J'ai adopté cette notation car le forum supprime tout "l o l" sans espaces, même entre balises (bug ?).
      (*) Ces fichiers proviennent du dernier SDK de Microsoft (uuid.lib est renommé en libuuid.lib pour convenir aux conventions de nommage de g++). L'entête UPnP.h est absente des includes du compilateur et la bibliothèque fournie libuuid.a n'est pas suffisamment à jour pour supporter l'API UPnP Control Point.

      Note : la commande Set path fait référence à l'emplacement par défaut du compilateur 32 bits (avec un Windows 10 64 bits standard). A rectifier, selon la place où se trouvent vraiment les binaires de minGW-W64.


      Conclusions

      Le principal problème rencontré pendant le développement de ce logiciel a été de parvenir à le faire tester sur des box différentes. D'ores et déjà, je sais qu’il ne fonctionne pas chez deux personnes ayant des freebox (modèle 6 et "crystal"). N'ayant pas accès à ces box, je ne peux que constater le problème sans pouvoir fournir de solution. Et ce d'autant plus que je subodore un souci, non dans mon code, mais dans l'API Microsoft (sans vouloir médire).

      Il fonctionne en revanche avec :Au final, je suis frustré de constater qu’il n’est probablement pas opérationnel sur des box aussi répandues que celles de free. Je tâcherai d'y retravailler afin de trouver une solution.

      Quoiqu'il en soit, l'intérêt principal était de comprendre au mieux le protocole UPnP. A cette occasion, j'ai découvert que mon décodeur TV ouvrait régulièrement une multitude de ports dans ma box (a priori pour l'enregistrement d'émission TV à distance dont je n'ai absolument pas besoin). Du coup, j'ai laissé l'UPnP désactivé et je conseille à tous de faire de même (sauf besoin précis).

      Cette technologie existe depuis longtemps et avait déjà, à sa sortie, provoqué un tollé en raison des évidents problèmes de sécurité qu’elle génère (et je ne parle pas des failles qui l’ont accompagnée). Le standard prévoit bien des mesures de sécurité mais elles sont si complexes à mettre en œuvre qu’aucune n’a été implémentée dans les faits.

      Ne serait-il pas possible, au niveau d’une passerelle par exemple, de demander à l’utilisateur un mot de passe pour accéder aux commandes UPnP (genre les 10 premières caractères de la clé Wifi) ? Cela limiterait l’ouverture de ports par un logiciel indésirable. Il faut dire que ce faisant, cela condamnerait l’esprit de l’UPnP au niveau du dialogue entre dispositifs. A chaque ajout d’un appareil sur le réseau, il faudrait entrer ce mot de passe… Enfin, ce n’était qu’une simple réflexion personnelle.
      Fichiers attachés
      Dernière modification par Icarus, 27 mars 2018, 19h14.

      Commentaire

      Chargement...
      X