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