1 #pragma once
2 
3 #include <boost/algorithm/string/classification.hpp>
4 #include <boost/algorithm/string/split.hpp>
5 #include <boost/asio/ip/address.hpp>
6 #include <boost/asio/ip/address_v4.hpp>
7 #include <boost/asio/ip/address_v6.hpp>
8 
9 #include <string>
10 
11 namespace redfish
12 {
13 namespace ip_util
14 {
15 
16 /**
17  * @brief Converts boost::asio::ip::address to string
18  * Will automatically convert IPv4-mapped IPv6 address back to IPv4.
19  *
20  * @param[in] ipAddr IP address to convert
21  *
22  * @return IP address string
23  */
24 inline std::string toString(const boost::asio::ip::address& ipAddr)
25 {
26     if (ipAddr.is_v6() && ipAddr.to_v6().is_v4_mapped())
27     {
28         return boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped,
29                                                 ipAddr.to_v6())
30             .to_string();
31     }
32     return ipAddr.to_string();
33 }
34 
35 /**
36  * @brief Helper function that verifies IP address to check if it is in
37  *        proper format. If bits pointer is provided, also calculates active
38  *        bit count for Subnet Mask.
39  *
40  * @param[in]  ip     IP that will be verified
41  * @param[out] bits   Calculated mask in bits notation
42  *
43  * @return true in case of success, false otherwise
44  */
45 inline bool ipv4VerifyIpAndGetBitcount(const std::string& ip,
46                                        uint8_t* bits = nullptr)
47 {
48     std::vector<std::string> bytesInMask;
49 
50     boost::split(bytesInMask, ip, boost::is_any_of("."));
51 
52     static const constexpr int ipV4AddressSectionsCount = 4;
53     if (bytesInMask.size() != ipV4AddressSectionsCount)
54     {
55         return false;
56     }
57 
58     if (bits != nullptr)
59     {
60         *bits = 0;
61     }
62 
63     char* endPtr = nullptr;
64     long previousValue = 255;
65     bool firstZeroInByteHit = false;
66     for (const std::string& byte : bytesInMask)
67     {
68         if (byte.empty())
69         {
70             return false;
71         }
72 
73         // Use strtol instead of stroi to avoid exceptions
74         long value = std::strtol(byte.c_str(), &endPtr, 10);
75 
76         // endPtr should point to the end of the string, otherwise given string
77         // is not 100% number
78         if (*endPtr != '\0')
79         {
80             return false;
81         }
82 
83         // Value should be contained in byte
84         if (value < 0 || value > 255)
85         {
86             return false;
87         }
88 
89         if (bits != nullptr)
90         {
91             // Mask has to be continuous between bytes
92             if (previousValue != 255 && value != 0)
93             {
94                 return false;
95             }
96 
97             // Mask has to be continuous inside bytes
98             firstZeroInByteHit = false;
99 
100             // Count bits
101             for (long bitIdx = 7; bitIdx >= 0; bitIdx--)
102             {
103                 if ((value & (1L << bitIdx)) != 0)
104                 {
105                     if (firstZeroInByteHit)
106                     {
107                         // Continuity not preserved
108                         return false;
109                     }
110                     (*bits)++;
111                 }
112                 else
113                 {
114                     firstZeroInByteHit = true;
115                 }
116             }
117         }
118 
119         previousValue = value;
120     }
121 
122     return true;
123 }
124 
125 } // namespace ip_util
126 } // namespace redfish
127