IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Conversion d'adresses IP/MAC (string) vers uint


Sujet :

C++

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    309
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 309
    Points : 148
    Points
    148
    Par défaut Conversion d'adresses IP/MAC (string) vers uint
    Bonjour,

    J'ai effectué pas mal de recherches (notamment sur ce forum) et trouvé bon nombres de références à mon problème mais je bloque toujours sur la conversion d'une adresse IP saisie par l'utilisateur vers son utilisation par libnet.

    Le format d'entrée est un std::string et libnet utilise des pointeurs uint8_t pour manipuler les adresses.

    Comme c'est un problème que je rencontre fréquemment, je me suis créé une classe pour gérer les adresses IP et MAC.

    La fonction incriminée est inspirée d'un code trouvé sur ce forum :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    ADDRESS::ADDRESS(const string &addr)
    {
    	string byte[8]; 
    	istringstream iss;
    	int nb_sep = 0;
    	uint16_t value = 0; // maximum 2 octets dans le cas ipv6
    	uint32_t ipv4 = 0;
     
    	// split des octets dans un tableau (type string)
     
    	for( size_t i=0; i<addr.size(); i++ )
    	{
    		if( nb_sep > 7 ) // le tableau d'octet ne permet pas de gerer plus de 8 elements (une IPv6)
    			throw BadFormat(); 
    		if( isxdigit(addr[i]) )
    			byte[nb_sep] += addr[i];
    		else if( addr[i] == '.' || addr[i] == ':' )
    			nb_sep++;
    		else
    			throw BadFormat();
    	}
     
        // traitement en fonction du type d'adresse
     
        switch(nb_sep)
        {
    		case 3: // 3 separateurs pour une IPv4
    			this->type = IPv4;
    			for( int i=0; i<=nb_sep; i++ )
    			{
    				value = atoi( byte[i].c_str() ); // conversion en entier
    				//cout << "IP/ i: " << i << " byte[i]: " << byte[i] << " value: " << value << endl;
    				ipv4 += (value << (i*8)); // processeur little endian (intel)
    				//ipv4 += (value << (24-(i*8))); // processeur big endian (sparc)
    			}
    			cout << "IP ipv4 (int): " << ipv4 << endl;
    			this->address = (uint8_t *) &ipv4;
    			break;
     
    		case 5: // 5 separateurs pour une MAC
    			this->type = MAC;
    			cout << "A venir\n";
    			break;
     
    		case 7: // 7 separateurs pour une IPv6 complete
    			this->type = IPv6;
    			cout << "IPv6 non implemented yet !\n";
    			break;
     
    		default:
    			throw BadFormat();
    	}
    }
    Cette fonction lit octet par octet l'adresse envoyée et détecte le format (IPv4, Mac ou IPv6) en fonction du nombre d'éléments. Ensuite, un traitement spécifique est mis en oeuvre. Pour le moment je bloque sur IPv4.

    En testant cette fonction avec "ADDRESS(192.168.0.1)", j'obtiens :
    IP ipv4 (int): 16820416
    Ce qui correspond bien à : "1×256^3+168×256+192"

    Pourtant, lors de l'injection wireshark me retourne une addresse en 160.81.222.9

    Si je hardcode cette même adresse avec sa représentation hexadécimale "\xc0\xa8\x00\x01\x00" et que je déclare un pointeur uint8_t dessus, l'injection est alors correcte.

    J'avoue avoir beaucoup de mal à débugger ce problème qui, à mon avis vient de là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->address = (uint8_t *) &ipv4;
    Si quelqu'un à une idée, je suis preneur.

  2. #2
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    309
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 309
    Points : 148
    Points
    148
    Par défaut
    En approfondissant un peu (ce que j'aurais dû faire avant de poster), je peux confirmer que mon retour de conversion est... comment dire ? Aléatoire ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->address = (uint8_t *) &ipv4;
    En affichant les valeurs des pointeurs après 2 appels successifs à ma fonction (arguments 192.168.0.254 et 192.168.0.1) j'obtiens :
    --- premier appel ---
    IP ipv4 (int): 4261456064
    IP ipv4 (ptr32): 0xbfb3ae4c
    IP ipv4 (ptr8): ��
    --- second appel ---
    IP ipv4 (int): 16820416
    IP ipv4 (ptr32): 0xbfb3ae4c
    IP ipv4 (ptr8): ��
    L'IP est retournée dans le uint32_t ipv4 (et le calcul semble correct).

    Cependant, je m'étonne de récupérer deux fois le même pointeur uint32_t*. J'imaginais plutôt que, comme j'appelle deux fois ma fonction, je réserve deux espaces mémoire différents pour ma variable ipv4 et donc je devrais obtenir deux adresses mémoire différentes au lieu de "0xbfb3ae4c" à chaque fois.

    Mon test montre assez clairement qu'il y a également un problème avec mon cast d'un uint32_t* (retrouné par &ipv4) dans un uint8_t*.

    J'ai beau relire la FAQ C et autres tutoriels sur les pointeurs. Je n'arrive pas vraiment à comprendre ce que j'ai fait... Pour moi la taille d'un pointeur ne dépend que l'architecture et non du type de données pointées.

    Merci à ceux qui auront un peu de temps de m'éclairer sur le sujet. Je suis un peu perdu là.

  3. #3
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->address = (uint8_t *) &ipv4;
    ipv4 est une variable locale donc à la fin de ton constructeur, elle n'existera plus et address pointera alors vers une zone mémoire dont le contenu est indéterminé.

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    309
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 309
    Points : 148
    Points
    148
    Par défaut
    Effectivement. Je comprends donc qu'il s'agit plutôt d'un problème de conception de ma classe ADDRESS.
    Mon attribut address devrait plus être un uint32_t. La conversion vers uint8_t* aurait alors sa place avant l'appel aux fonctions de libnet.

    Mais j'imagine qu'il va me rester le problème de la conversion en question (uint32_t => uint8_t*).

    Merci. Cela m'avait échappé.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    A vrai dire, j'ai l'impression que tu te fais beaucoup de mal pour pas grand chose...

    D'abord, parce que je suis persuadé qu'il doit exister "quelque part" les fonctions de conversions "qui vont bien" (après tout, tous les navigateurs sont capables de comprendre une adresse sous la forme de "http://192.168.0.1" ).

    Ensuite, parce qu'il serait beaucoup plus simple de passer par les flux de conversion qui sont fournis par le standard.

    Prenons, par exemple, une adresse IPV4...

    Elle est représentée sous la forme de quatre valeurs non signées, séparées par des points, qui correspondent chacune à un sous ensemble de 8 bits d'un entier.

    Tu pourrais déjà commencer par remplacer les points dans la chaine de caractères par des espaces, ce qui permettrait d'avoir les quatre valeurs correctement séparée.
    A la mode c++03, il faudrait passer par un foncteur, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    struct isPoint
    {
        bool operator()(const char c) const
        {
            return c=='.';
        }
    };
    void foo(std::string const & addressString)
    {
        /* je l'ai passée sous la forme d'une référence constante, j'en fais une copie */
       std::string temp(addressString);
      std::replace_if(temp.begin(),temp.end(),isPoint(),' ');
    }
    En C++11, tu pourrais passer par une expression lambda
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    void foo(std::string const & addressString)
    {
        /* je l'ai passée sous la forme d'une référence constante, j'en fais une copie */
       std::string temp(addressString);
      std::replace_if(str.begin(), str.end(),
                       [](const char c){return c =='.';},' ');
    }
    La logique pour s'assurer que la chaine de caractères est "correctement formatée" pour notre usage sera exactement la même (à l'exception du séparateur) pour les adresses mac et les adresses IPV6

    Une fois que tu en est là, tu as quasiment tout fait

    Les différences qui restent sont, sommes toutes, assez triviales :
    • IPV4 représente les valeurs en décimal, alors que Mac et IPV6 les représente sous la forme hexadécimal.
    • IPV4 regroupe 4 valeurs codées sur 8 bits
    • Mac regroupe six ou de 8 groupes de 8 bits selon la norme utilisée
    • IPV6 regroupe six groupes de 16 bits.
    Nous pourrions donc déjà très facilement faire la distinction entre IPV4 et IPV6/Mac "simplement" en comptant le nombre d'espaces qui constituent la chaine : si on en a trois, on a une adresse IPV4, si on en a cinq, on a soit une adresse IPV6, soit une adresse Mac.

    Ceci dit, il faut peut etre prendre en compte le fait qu'il existe deux normes d'adresse Mac : EUI-48 et EUI-64.

    Il n'est donc pas impossible de se retrouver avec une adresse mac composée de 8 groupes de 8 bits (et donc, de 7 espaces une fois toutes les modifications effectuées
    Cette distinction va déjà nous permettre de récupérer correctement les différentes valeurs dans notre fonction qui pourrait commencer à ressembler à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    void foo(std::string const & ipV4String)
    {
         std::string temp(addressString);
        /* effectue le remplacement de : par un espace pour Mac et IPV6 */
       std::replace_if(temp.begin(), temp.end(),
                       [](const char c){return c ==':';},' ');
        /* effectue le remplacement de . par un espace pour IPV4 */
       std::replace_if(temp.begin(), temp.end(),
                       [](const char c){return c =='.';},' ');
       size_t count = std::count_if(temp.begin(), temp.end(),
                       [](const char c){return c ==' ';});
        switch(count)
        {
            case 3:
            std::cout<<"c'est une adresse ipv4"<<std::endl;
            break;
            case 5:
            std::cout<<"c'est une adresse mac EUI-48 "<<std::endl;
            break;
            case 7:
            std::cout<<"c'est une adresse mac EUI-64 ou IPV6 "<<std::endl;
            break;
            default:
                throw BadStringFormat(); // à implémenter par toi meme
        }
    }
    Puis, nous n'avons plus qu'à laisser les flux de conversion faire leur travail en veillant, lorsque l'on a affaire à une adresse ipv6 ou mac (pour l'instant, on ne sait pas encore si c'est l'un ou l'autre ) à ce qu'il travaille en hexédacimal.

    Le plus facile pour récupérer les valeurs consiste, selon moi, à essayer de les récupérer dans un unsigned short et à les placer dans un tableau.

    Si je choisi ce type particulier, c'est parce que:
    • si on essaye de récupérer un unsigned char, on n'aura qu'un caractère à la fois sans qu'il n'ait été converti
    • unsigned short est le plus petit type dont on ait la certitude qu'il sera suffisamment grand pour représenter toutes les valeurs que l'on risque d'avoir (de 0 à 0xFFFF)
    Mais bon, j'aurais pu "jouer la certitude" et décider de travailler avec un unsigned int

    La fonction commencerait alors à ressembler à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    void foo(std::string const & addressString)
    {
         std::string temp(addressString);
        /* première étape */
        /* effectue le remplacement de : par un espace pour Mac et IPV6 */
       std::replace_if(temp.begin(), temp.end(),
                       [](const char c){return c ==':';},' ');
        /* effectue le remplacement de . par un espace pour IPV4 */
       std::replace_if(temp.begin(), temp.end(),
                       [](const char c){return c =='.';},' ');
       size_t count = std::count_if(temp.begin(), temp.end(),
                       [](const char c){return c ==' ';});
        /* deuxième étape */
        std::vector<unsigned short> tab;
        unsigned int recup;
        std::stringstream ss;
        ss<<temp;
        switch(count)
        {
            case 3:
            while(ss>>recup)
            {
                tab.push_back(recup);
            }
            break;
            case 5:
            case 7;
            while(ss>>std::hex>>recup)
            {
                tab.push_back(recup);
            }
            break;
            default:
                throw BadStringFormat(); // à implémenter par toi meme
        }
    }
    Nous avons maintenant "tout ce qu'il faut" pour déterminer le type de notre adresse une bonne fois pour toutes...:
    Si on a quatre valeurs, c'est une adresse IPV4
    Si on a six valeurs exclusivement plus petites que 0xFF (255 en décimal ) c'est une adrese MAC
    Si on a six valeurs dont au moins une est plus grande que 0xFF (255 en décimal), c'est une adresse IPV6(je ne connais pas les specifications exactes d'IPV6, donc, j'espère ne pas me tromper )

    Cela pourrait donc prendre une forme proche de
    tion commencerait alors à ressembler à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    void foo(std::string const & AddressString)
    {
         std::string temp(addressString);
        /* première étape */
        /* effectue le remplacement de : par un espace pour Mac et IPV6 */
       std::replace_if(temp.begin(), temp.end(),
                       [](const char c){return c ==':';},' ');
        /* effectue le remplacement de . par un espace pour IPV4 */
       std::replace_if(temp.begin(), temp.end(),
                       [](const char c){return c =='.';},' ');
       size_t count = std::count_if(temp.begin(), temp.end(),
                       [](const char c){return c ==' ';});
        /* deuxième étape */
        std::vector<unsigned short> tab;
        unsigned int recup;
        std::stringstream ss;
        ss<<temp;
        switch(count)
        {
            case 3:
            while(ss>>recup)
            {
                tab.push_back(recup);
            }
            break;
            case 5:
            case 7:
            while(ss>>std::hex>>recup)
            {
                tab.push_back(recup);
            }
            break;
            default:
                throw BadStringFormat(); // à implémenter par toi meme
        }
        /* troisième étape */
        if(tab.size() == 4)
        {
            std:cout<<"C'est definitiviement une adresse IPV4"<<std::endl;
        }
        else if(tab.size() == 6)
        {
            std:cout<<"C'est une adresse MAC EUI-48"<<std::endl;
        }
        else if (std::count_if(tab.begin(),tab.end(), 
                              [](unsigned short i){return i<0xFF;}) != 0)
        {
            std:cout<<"C'est definitiviement une adresse IPV6"<<std::endl;
        }
        else
        {
            std:cout<<"C'est une adresse MAC EUI-68"<<std::endl;
        }
     
    }
    Maintenant, on a "tout ce qu'il faut" pour travailler correctement

    Je ne crois cependant pas qu'il soit opportun de tout gérer dans une seule grande classe, car, si l'on peut utiliser une adresse IPV4 ou une adresse IPV6 de manière sensiblement similaire, l'adresse MAC a une utilisation clairement spécifique.

    J'envisagerais donc plus de créer une hiérarchie de classes basée sur une classe abstraite "AbstractAddress" dont hériteraient les classes MacAddress pour représenter les adresses Mac, Ipv4Address pour représenter les adresses Ipv4 et Ipv6Address pour représenter les adresses Ipv6, le tout aidé par une fabrique qui se chargera de choisir le type qui "va bien" en fonction de la chaine de caractères passée:

    D'habitude, je commence par présenter la hiérarchie de classes, mais, comme nous avons tout ce qu'il faut pour déterminer le type de l'adresse auquel on a affaire, je vais plutot commencer par la fabrique

    Cela pourrait prendre une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    class AddressFactory
    {
        public:
            AbstractAddress* createAddress(std::string const & addressString)
            {  
                 /* les 3 étapes peuvent se faire ici */
     
             /* mais, comme j'aime respecter SRP, ca devient */
             //première étape: on remplace les caractères
             std::string temp(replacePoints(addressString));
             //deuxième étape : on récupère les valeurs numériques
             computeStringValues(temp);
             // troisième étape : on crée l'adresse du type adéquat et on la renvoie
             if(values_.size() == 4)
                 return createIpv4();
             else if(values_.size() == 6)
                 return createMac();
             else if (std::count_if(values_.begin(),values_.end(), 
                              [](unsigned short i){return i<0xFF;}) != 0)
                 return createIpv6();
             return createMac();
            }
        private:
            std::string replacePoint(std::string const & toChange) const
            {
                 std::string temp(toChange);
                /* effectue le remplacement de : par un espace pour Mac et IPV6 */
               std::replace_if(temp.begin(), temp.end(),
                               [](const char c){return c ==':';},' ');
                /* effectue le remplacement de . par un espace pour IPV4 */
               std::replace_if(temp.begin(), temp.end(),
                           [](const char c){return c =='.';},' ');
               return temp;
            }
            void computeStringValues(std::string const & toCompute)
            {
               size_t count = std::count_if(toCompute.begin(), 
                                   toCompute.end(),
                               [](const char c){return c ==' ';});
                switch(count)
                {
                    case 3:
                        computeDecimalValues(toCompute);
                        break;
                    case 5:
                    case 7:
                          computeHexaValues(toCompute)
                          break;
                    default:
                        throw BadStringFormat(); // à implémenter par toi meme
                }
            }
            void computeDecimalValues(std::string const & toCompute)
            {
                std::stringstream ss;
                ss<<toCompute;
                unsigned short recup;
                while(ss>>recup) 
                    values_.push_back(recup);
            }
            void computeDecimalValues(std::string const & toCompute)
            {
                std::stringstream ss;
                ss<<toCompute;
                unsigned short recup;
                while(ss>>std::hex>>recup) 
                    values_.push_back(recup);
            }
            AbstractAddress * createIpv4() const
            {
                 /* Une adresse IPV4 peut etre représentée par un entier
                  * non signé codé sur 32 bits.
                  *
                  * Tout ce qu'il faut faire, c'est donc de créer cette valeur.
                  * Par facilité, je la crées au format "little endian", c'est à dire
                  * que les bits de poids fort représentent les valeurs les plus
                  * importantes
                  */
                  uint32_t value = 0;
                  for(auto i : values_)
                  {
                      value<<8; // on décale tout de 8 bits
                      value = value | i; // et on rajoute la valeur de l'élément actuel
                  }
                  return new Ipv4Address(value);
            }
            AbstractAddress * createIpv6() const
            {
                 /* Même principe pour les IPV6, mais sur un entier codé sur 128 bits
                  */
                  if(values_.size() != 6)
                      throw BadValueCountForIpv6(); // pour le cas où je me serais trompé
                  uint128_t value = 0;
                  for(auto i : values_)
                  {
                      value<<16; //ici, on décale tout de 16 bits car on peut avoir des valeurs > 0xFF
                      value = value | i; // et on rajoute la valeur de l'élément actuel
                  }
                  return new Ipv6Address(value);
            }
            AbstractAddress * createMac() const
            {
                 if(    values_.size() != 6 
                     && values_.size() != 8)
                      throw BadValueCountForMac(); // pour le cas où je me serais trompé
                 /* EUI-48 est composé de 48 bits alors que EUI-64 bits est composé
                  * de 64 bits.
                  * Mais comme on ne dispose pas d'un type codé sur 48 bits, nous
                  * allons tout représenter sur la forme d'un entier 64 bits.
                  * Nous allons "simplement" déterminer la norme effective sur base
                  * du nombre de valeurs dont on dispose et la représenter sous la
                  * forme d'une énumértion ;)
                  */
                  uint64_t value = 0;
                  for(auto i : values_)
                  {
                      value<<8 des valeurs > 0xFF
                      value = value | i; // et on rajoute la valeur de l'élément actuel
                  }
                  return new MacAddress(value, values_.size() == 6? EUI48 : EUI64 );
            }
            std::vetor<unsigned_short> values_;
    };
    Nous pourrions alors créer une énumération qui précise la norme utilisée et, surtout, le nombre de bytes de la représentation.
    Elles prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    enum AddressNorme
    {
        IPV4 = 4,
        EUI48 = 6,
        EUI64 = 8,
        IPV6 = 64
    };
    Et nous aurions donc notre classe AbstractAddress qui ressemblerait à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class AbstractAddress
    {
        public:
            AbstractAddress(){}
            virtual ~AbstractAddress(){}
            /* renvoie une chaine qui correspond à celle que l'on a utilisé pour
             * créer l'adresse
             */
            virtual const std::string toString() const = 0;
    };
    Les adresses IPV4 serait gérée sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class Ipv4Address : public AbstractAddress
    {
        public:
            Ipv4Address(uint_32 value):value_(value){}
            virtual const std::string toString() const 
            {
                 unsigned char * temp=reinterpret_cast<unsigned char *>(value_);
                 std::stringstream ss;
                 for(int i = 0; i<4; ++i)
                 {
                     ss<<temp[i];
                     if(i!=4)
                          ss<<'.';
                }
                return ss.str();
            }
            uint32_t value() const{return value_;}
            /* comme j'ai décidé de coder la valeur en "petit boutisme", il faut
             * sans doute donner le moyen de la convertir en "grand boutisme",
             * car c'est la norme "network" ;)
             */
            uint32_t toNetwork() const
            {
                return htonl(value_);
            }
        private:
            uint32_t value;
    };
    Les adresses IPV6 sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Ipv6Address : public AbstractAddress
    {
        public:
            Ipv6Address(uint128_t value):value_(value){}
            virtual const std::string toString() const 
            {
                 uint16_t * temp=reinterpret_cast<uint16_t *>(value_);
                 std::stringstream ss;
                 for(int i = 0; i<8; ++i)
                 {
                     ss<<hex<<temp[i];
                     if(i!=4)
                          ss<<':';
                }
                return ss.str();
            }
            uint128_t value() const{return value_;}
            }
        private:
            uint128_t value;
    };
    et enfin, les adresses Mac ressembleraient à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class MacAddress : public AbstractAddress
    {
        public:
            MacAddress(uin64_t value, AddressNorme norme):value_(value),
            norme_(norme){}
            virtual const std::string toString() const 
            {
                 uint8_t * temp=reinterpret_cast<uint8_t *>(value_);
                 std::stringstream ss;
                 for(int i = 0; i<(int)norme_; ++i)
                 {
                     ss<<hex<<temp[i];
                     if(i!=norme)
                          ss<<':';
                }
                return ss.str();
            }
            uint64_t value() const{return value_;}
            }
        private:
            uint64_t value;
            AddressNorme norme_;
    };
    Tu remarqueras que je n'ai pas introduit la possibilité de savoir à quel type d'adresse tu as affaire, et c'est fait expres.

    En effet, le fait de pouvoir écrire un code qui ressemblerait à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::string str;
    /* ... */ 
    AbstractAddress * ptr = AddressFactory.createAddress(str);
    if(ptr->type() == quelqueChose /*(sans doute l'une des valeurs énumérées de AddressNorme) */)
    {
    }
    n'a pour seul intérêt que de te laisser coder la suite d'une manière qui t'amènera au final à quelque chose de parfaitement ingérable, car tu passera ton temps à faire des tests sur le type et des transtypages.

    Je n'ai pas introduit dans la hiérarchie "ce qu'il faut" pour pouvoir le faire, mais dis toi que, de manière générale, il est largement préférable de recourir au double dispatch de manière à récupérer le type dynamique

    Mais donc, en applicant le double dispatch, tu devrais pouvoir en arriver à gérer les différents types d'adresse sous des formes proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void foo(Ipv4Address const & adr);
    void foo(Ipv6Address const & adr);
    void bar(MacAddress const & adr);
    et tu pourrais donc, si le besoin s'en fait sentir, travailler sous une forme proche de (je le montre pour Ipv4Address, mais le principe est le même pour les autres, hein )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void foo(Ipv4Address const & adr)
    {
        uint32_t value = adr.value();
        uint8_t * temp = reinterpret_cast<uint8_t *>(&value);
        // avec someFunction qui aurait comme prototype
        // void someFunction(uint8_t * datas)
        someFunction(temp);
    }
    "hope it helps"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    309
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 309
    Points : 148
    Points
    148
    Par défaut
    Salut,

    Je ne sais pas trop quoi dire fasse à toutes ces informations pertinentes. Je vais commencer simple : MERCI.

    Je n'ai pas de raison valable de ré-inventer la roue. C'est juste que je ne programme pas très souvent et je n'ai que très peu de recul sur l'état de l'art en C++.
    En plus, j'ai un goût assez prononcé pour l'inutile...

    Le problème de départ, qui est à l'origine de ma classe ADDRESS, se limite vraiment à une conversion de représentation d'adresses IP de std::string vers uint8_t *. Mais, comme je le rencontre systématiquement dès que je fais une appli qui manipule des IP, j'ai décidé d'apporter une réponse définitive à ce problème !

    Mes quelques lectures de ton post m'ont permis d'y voir plein d'idées très intéressantes. Je te remercie en particulier d'avoir préciser les différentes étapes de réflexion qui t'amène à une proposition finale très complète.

    Ceci étant dit, il y a quelques concepts que je vais devoir approfondir avant de te faire un retour sur mon implémentation de tout ça.

    Merci encore.

Discussions similaires

  1. [C++] conversion classe string vers float
    Par agrosjea dans le forum C++
    Réponses: 5
    Dernier message: 14/03/2007, 13h45
  2. Conversion std::string vers numérique
    Par mister3957 dans le forum SL & STL
    Réponses: 9
    Dernier message: 26/02/2007, 20h40
  3. conversion string vers une date
    Par fatati dans le forum C++Builder
    Réponses: 7
    Dernier message: 14/12/2006, 10h09
  4. Conversion String vers Byte
    Par zulianithomas dans le forum Delphi
    Réponses: 8
    Dernier message: 07/10/2006, 10h09
  5. conversion string vers int
    Par mathher dans le forum C++
    Réponses: 4
    Dernier message: 14/04/2006, 17h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo