1 // Copyright (c) 2011, Robert Escriva
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 //     * Redistributions of source code must retain the above copyright notice,
8 //       this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above copyright
10 //       notice, this list of conditions and the following disclaimer in the
11 //       documentation and/or other materials provided with the distribution.
12 //     * Neither the name of po6 nor the names of its contributors may be used
13 //       to endorse or promote products derived from this software without
14 //       specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 // POSSIBILITY OF SUCH DAMAGE.
27 
28 #ifndef po6_net_ipaddr_h_
29 #define po6_net_ipaddr_h_
30 
31 // C
32 #include <assert.h>
33 
34 #ifdef _MSC_VER
35 #include <WinSock2.h>
36 #include <ws2tcpip.h>
37 #include <stdint.h>
38 typedef uint32_t in_addr_t;
39 typedef uint16_t in_port_t;
40 #else
41 // POSIX
42 #include <arpa/inet.h>
43 #include <errno.h>
44 #include <netinet/in.h>
45 #include <sys/socket.h>
46 #endif
47 
48 #if defined(__FreeBSD__)
49 #include <sys/types.h>
50 #endif
51 
52 // STL
53 #include <string>
54 
55 // po6
56 #include <po6/error.h>
57 
58 namespace po6
59 {
60 namespace net
61 {
62 
63 class ipaddr
64 {
65     public:
66 #ifdef _MSC_VER
ANY()67         static ipaddr ANY() { return ipaddr((in_addr_t)INADDR_ANY); }
68 #else
69         static ipaddr ANY() { return ipaddr(INADDR_ANY); }
70 #endif
71         static uint64_t hash(const ipaddr& ip);
72 
73     public:
74         ipaddr();
75         explicit ipaddr(const char* addr);
76         explicit ipaddr(const std::string& addr);
77         explicit ipaddr(const in_addr& ipv4);
78         explicit ipaddr(const in_addr_t& ipv4);
79         explicit ipaddr(const in6_addr& ipv6);
80         ipaddr(const ipaddr& other);
81         ~ipaddr() throw ();
82 
83     public:
84         int family() const;
85         void pack(struct sockaddr* addr, socklen_t* addrlen, in_port_t port) const;
86         void pack(struct sockaddr_in* addr, in_port_t port) const;
87         void pack(struct sockaddr_in6* addr, in_port_t port) const;
v4addr()88         const in_addr& v4addr() const { assert(m_family == AF_INET); return m_ip.v4; }
v6addr()89         const in6_addr& v6addr() const { assert(m_family == AF_INET6); return m_ip.v6; }
90         int compare(const ipaddr& rhs) const;
91 
92     public:
93         void set(const char* addr);
94         void set(const std::string& s);
95         void set(const in_addr& ipv4);
96         void set(const in6_addr& ipv6);
97 
98     public:
99         ipaddr& operator = (const ipaddr& rhs);
100         bool operator < (const ipaddr& rhs) const;
101         bool operator <= (const ipaddr& rhs) const;
102         bool operator == (const ipaddr& rhs) const;
103         bool operator != (const ipaddr& rhs) const;
104         bool operator >= (const ipaddr& rhs) const;
105         bool operator > (const ipaddr& rhs) const;
106 
107     private:
108         friend std::ostream& operator << (std::ostream& lhs, const ipaddr& rhs);
109 
110     private:
111         int m_family;
112         union
113         {
114             in_addr v4;
115             in6_addr v6;
116         } m_ip;
117 };
118 
119 inline uint64_t
hash(const ipaddr & ip)120 ipaddr :: hash(const ipaddr& ip)
121 {
122     uint64_t ret = 0;
123 
124     if (ip.m_family == AF_INET)
125     {
126         ret = ip.m_ip.v4.s_addr;
127     }
128     else if (ip.m_family == AF_INET6)
129     {
130         const uint8_t* addr = &ip.m_ip.v6.s6_addr[0];
131         uint64_t low = 0;
132         uint64_t high = 0;
133         memmove(&low, addr, sizeof(uint64_t));
134         memmove(&high, addr + 8, sizeof(uint64_t));
135         return low ^ high;
136     }
137 
138     return ret;
139 }
140 
141 inline
ipaddr()142 ipaddr :: ipaddr()
143     : m_family(AF_UNSPEC)
144     , m_ip()
145 {
146 }
147 
148 inline
ipaddr(const char * addr)149 ipaddr :: ipaddr(const char* addr)
150     : m_family(AF_UNSPEC)
151     , m_ip()
152 {
153     set(addr);
154 }
155 
156 inline
ipaddr(const std::string & addr)157 ipaddr :: ipaddr(const std::string& addr)
158     : m_family(AF_UNSPEC)
159     , m_ip()
160 {
161     set(addr);
162 }
163 
164 inline
ipaddr(const in_addr & ipv4)165 ipaddr :: ipaddr(const in_addr& ipv4)
166     : m_family(AF_UNSPEC)
167     , m_ip()
168 {
169     set(ipv4);
170 }
171 
172 inline
ipaddr(const in_addr_t & ipv4)173 ipaddr :: ipaddr(const in_addr_t& ipv4)
174     : m_family(AF_UNSPEC)
175     , m_ip()
176 {
177     in_addr ia;
178     ia.s_addr = ipv4;
179     set(ia);
180 }
181 
182 inline
ipaddr(const in6_addr & ipv6)183 ipaddr :: ipaddr(const in6_addr& ipv6)
184     : m_family(AF_UNSPEC)
185     , m_ip()
186 {
187     set(ipv6);
188 }
189 
190 inline
ipaddr(const ipaddr & other)191 ipaddr :: ipaddr(const ipaddr& other)
192     : m_family(other.m_family)
193     , m_ip(other.m_ip)
194 {
195 }
196 
197 inline
~ipaddr()198 ipaddr :: ~ipaddr() throw ()
199 {
200 }
201 
202 inline int
family()203 ipaddr :: family() const
204 {
205     return m_family;
206 }
207 
208 inline void
pack(struct sockaddr * addr,socklen_t * addrlen,in_port_t port)209 ipaddr :: pack(struct sockaddr* addr, socklen_t* addrlen, in_port_t port) const
210 {
211     memset(addr, 0, *addrlen);
212 
213     if (m_family == AF_UNSPEC)
214     {
215         throw std::logic_error("cannot pack a sockaddr with AF_UNSPEC");
216     }
217     else if (m_family == AF_INET && *addrlen >= sizeof(sockaddr_in))
218     {
219         pack(reinterpret_cast<sockaddr_in*>(addr), port);
220         *addrlen = sizeof(sockaddr_in);
221     }
222     else if (m_family == AF_INET6 && *addrlen >= sizeof(sockaddr_in6))
223     {
224         pack(reinterpret_cast<sockaddr_in6*>(addr), port);
225         *addrlen = sizeof(sockaddr_in6);
226     }
227     else
228     {
229         throw std::length_error("insufficiently sized sockaddr");
230     }
231 }
232 
233 inline void
pack(struct sockaddr_in * addr,in_port_t port)234 ipaddr :: pack(struct sockaddr_in* addr, in_port_t port) const
235 {
236     if (m_family != AF_INET)
237     {
238         throw std::logic_error("Cannot pack non-AF_INET sockaddr into sockaddr_in.");
239     }
240 
241     addr->sin_family = AF_INET;
242     addr->sin_port = htons(port);
243     memmove(&addr->sin_addr, &m_ip.v4, sizeof(in_addr));
244 }
245 
246 inline void
pack(struct sockaddr_in6 * addr,in_port_t port)247 ipaddr :: pack(struct sockaddr_in6* addr, in_port_t port) const
248 {
249     if (m_family != AF_INET6)
250     {
251         throw std::logic_error("Cannot pack non-AF_INET6 sockaddr into sockaddr_in6.");
252     }
253 
254     addr->sin6_family = AF_INET6;
255     addr->sin6_port = htons(port);
256     memmove(&addr->sin6_addr, &m_ip.v6, sizeof(in6_addr));
257 }
258 
259 inline int
compare(const ipaddr & rhs)260 ipaddr :: compare(const ipaddr& rhs) const
261 {
262     const ipaddr& lhs(*this);
263 
264     if (lhs.m_family != rhs.m_family)
265     {
266         return lhs.m_family - rhs.m_family;
267     }
268 
269     if (lhs.m_family == AF_INET)
270     {
271         return memcmp(&lhs.m_ip.v4, &rhs.m_ip.v4, sizeof(in_addr));
272     }
273     else if (lhs.m_family == AF_INET6)
274     {
275         return memcmp(&lhs.m_ip.v6, &rhs.m_ip.v6, sizeof(in6_addr));
276     }
277     else
278     {
279         return 0;
280     }
281 }
282 
283 inline void
set(const char * addr)284 ipaddr :: set(const char* addr)
285 {
286     in_addr ipv4;
287     in6_addr ipv6;
288 
289     if (inet_pton(AF_INET, addr, &ipv4) > 0)
290     {
291         set(ipv4);
292     }
293     else if (inet_pton(AF_INET6, addr, &ipv6) > 0)
294     {
295         set(ipv6);
296     }
297     else
298     {
299 #ifdef _MSC_VER
300         errno = GetLastError();
301 #endif
302         if (errno != 0 && errno != EAFNOSUPPORT)
303         {
304             throw po6::error(errno);
305         }
306         else
307         {
308             throw std::invalid_argument("malformed address");
309         }
310     }
311 }
312 
313 inline void
set(const std::string & s)314 ipaddr :: set(const std::string& s)
315 {
316     set(s.c_str());
317 }
318 
319 inline void
set(const in_addr & ipv4)320 ipaddr :: set(const in_addr& ipv4)
321 {
322     m_family = AF_INET;
323     memmove(&m_ip.v4, &ipv4, sizeof(in_addr));
324 }
325 
326 inline void
set(const in6_addr & ipv6)327 ipaddr :: set(const in6_addr& ipv6)
328 {
329     m_family = AF_INET6;
330     memmove(&m_ip.v6, &ipv6, sizeof(in6_addr));
331 }
332 
333 inline ipaddr&
334 ipaddr :: operator = (const ipaddr& rhs)
335 {
336     if (this != &rhs)
337     {
338         m_family = rhs.m_family;
339         memmove(&m_ip, &rhs.m_ip, sizeof(m_ip));
340     }
341 
342     return *this;
343 }
344 
345 inline bool
346 ipaddr :: operator < (const ipaddr& rhs) const
347 {
348     return compare(rhs) < 0;
349 }
350 
351 inline bool
352 ipaddr :: operator <= (const ipaddr& rhs) const
353 {
354     return compare(rhs) <= 0;
355 }
356 
357 inline bool
358 ipaddr :: operator == (const ipaddr& rhs) const
359 {
360     return compare(rhs) == 0;
361 }
362 
363 inline bool
364 ipaddr :: operator != (const ipaddr& rhs) const
365 {
366     return compare(rhs) != 0;
367 }
368 
369 inline bool
370 ipaddr :: operator >= (const ipaddr& rhs) const
371 {
372     return compare(rhs) >= 0;
373 }
374 
375 inline bool
376 ipaddr :: operator > (const ipaddr& rhs) const
377 {
378     return compare(rhs) > 0;
379 }
380 
381 inline std::ostream&
382 operator << (std::ostream& lhs, const ipaddr& rhs)
383 {
384     if (rhs.m_family == AF_UNSPEC)
385     {
386         lhs << "UNSPECIFIED";
387     }
388     else if (rhs.m_family == AF_INET)
389     {
390         char repr[INET_ADDRSTRLEN];
391 
392 #ifdef _MSC_VER
393         if (!inet_ntop(AF_INET, (PVOID)&rhs.m_ip.v4, repr, INET_ADDRSTRLEN))
394 #else
395         if (!inet_ntop(AF_INET, &rhs.m_ip.v4, repr, INET_ADDRSTRLEN))
396 #endif
397         {
398             throw std::logic_error("inet_ntop failed, but should never fail.");
399         }
400 
401         lhs << std::string(repr);
402     }
403     else if (rhs.m_family == AF_INET6)
404     {
405         char repr[INET6_ADDRSTRLEN];
406 
407 #ifdef _MSC_VER
408         if (!inet_ntop(AF_INET6, (PVOID)&rhs.m_ip.v6, repr, INET6_ADDRSTRLEN))
409 #else
410         if (!inet_ntop(AF_INET6, &rhs.m_ip.v6, repr, INET6_ADDRSTRLEN))
411 #endif
412         {
413             throw std::logic_error("inet_ntop failed, but should never fail.");
414         }
415 
416         lhs << std::string(repr);
417     }
418 
419     return lhs;
420 }
421 
422 inline std::istream&
423 operator >> (std::istream& lhs, ipaddr& rhs)
424 {
425     try
426     {
427         std::string s;
428         lhs >> s;
429         rhs.set(s);
430     }
catch(po6::error & e)431     catch (po6::error& e)
432     {
433         lhs.setstate(std::istream::failbit);
434     }
catch(std::invalid_argument & e)435     catch (std::invalid_argument& e)
436     {
437         lhs.setstate(std::istream::failbit);
438     }
439 
440     return lhs;
441 }
442 
443 } // namespace net
444 } // namespace po6
445 
446 #endif // po6_net_ipaddr_h_
447