SimpleSocket  0.1
Some wrappers that make using sockets easier in C++
comboaddress.hh
1 #pragma once
2 
3 #include <string>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7 #include <iostream>
8 #include <sys/socket.h>
9 #include <netdb.h>
10 #include <stdexcept>
11 #include <sstream>
12 #include <tuple>
13 #include <string.h>
14 
15 int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret);
16 int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret);
17 
18 constexpr uint32_t chtonl(uint32_t s)
19 {
20  return (
21  ((s & 0x000000FF) << 24) | ((s & 0x0000FF00) << 8)
22  | ((s & 0xFF000000) >> 24) | ((s & 0x00FF0000) >> 8)
23  );
24 }
25 
26 constexpr struct sockaddr_in operator "" _ipv4(const char* p, size_t l)
27 {
28  struct sockaddr_in ret={};
29  ret.sin_addr.s_addr=0;
30  ret.sin_family=AF_INET;
31 
32  uint8_t octet=0;
33 
34  size_t n=0;
35  for(; n < l; ++n) {
36  if(p[n]==':')
37  break;
38  if(p[n]=='.') {
39  ret.sin_addr.s_addr*=0x100;
40  ret.sin_addr.s_addr+=octet;
41  octet=0;
42  }
43  else {
44  octet*=10;
45  octet+=p[n]-'0';
46  }
47  }
48  ret.sin_addr.s_addr*=0x100;
49  ret.sin_addr.s_addr+=octet;
50 
51  ret.sin_addr.s_addr = chtonl(ret.sin_addr.s_addr);
52 
53  if(p[n]==':') {
54  for(++n; n < l; ++n) {
55  ret.sin_port*=10;
56  ret.sin_port += p[n]-'0';
57  }
58  ret.sin_port = 0x100 * (ret.sin_port&0xff) + (ret.sin_port/0x100);
59  }
60  return ret;
61 
62 }
63 
78 union ComboAddress {
79  struct sockaddr_in sin;
80  struct sockaddr_in sin4;
81  struct sockaddr_in6 sin6;
82 
84  bool operator==(const ComboAddress& rhs) const
85  {
86  if(std::tie(sin4.sin_family, sin4.sin_port) != std::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
87  return false;
88  if(sin4.sin_family == AF_INET)
89  return sin4.sin_addr.s_addr == rhs.sin4.sin_addr.s_addr;
90  else
91  return memcmp(&sin6.sin6_addr.s6_addr, &rhs.sin6.sin6_addr.s6_addr, sizeof(sin6.sin6_addr.s6_addr))==0;
92  }
93 
95  bool operator!=(const ComboAddress& rhs) const
96  {
97  return(!operator==(rhs));
98  }
99 
101  bool operator<(const ComboAddress& rhs) const
102  {
103  if(sin4.sin_family == 0) {
104  return false;
105  }
106  if(std::tie(sin4.sin_family, sin4.sin_port) < std::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
107  return true;
108  if(std::tie(sin4.sin_family, sin4.sin_port) > std::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
109  return false;
110 
111  if(sin4.sin_family == AF_INET)
112  return sin4.sin_addr.s_addr < rhs.sin4.sin_addr.s_addr;
113  else
114  return memcmp(&sin6.sin6_addr.s6_addr, &rhs.sin6.sin6_addr.s6_addr, sizeof(sin6.sin6_addr.s6_addr)) < 0;
115  }
116 
117  bool operator>(const ComboAddress& rhs) const
118  {
119  return rhs.operator<(*this);
120  }
121  /*
122  struct addressOnlyHash
123  {
124  uint32_t operator()(const ComboAddress& ca) const
125  {
126  const unsigned char* start;
127  int len;
128  if(ca.sin4.sin_family == AF_INET) {
129  start =(const unsigned char*)&ca.sin4.sin_addr.s_addr;
130  len=4;
131  }
132  else {
133  start =(const unsigned char*)&ca.sin6.sin6_addr.s6_addr;
134  len=16;
135  }
136  return burtle(start, len, 0);
137  }
138  };
139  */
140 
142  struct addressOnlyLessThan: public std::binary_function<ComboAddress, ComboAddress, bool>
143  {
144  bool operator()(const ComboAddress& a, const ComboAddress& b) const
145  {
146  if(a.sin4.sin_family < b.sin4.sin_family)
147  return true;
148  if(a.sin4.sin_family > b.sin4.sin_family)
149  return false;
150  if(a.sin4.sin_family == AF_INET)
151  return a.sin4.sin_addr.s_addr < b.sin4.sin_addr.s_addr;
152  else
153  return memcmp(&a.sin6.sin6_addr.s6_addr, &b.sin6.sin6_addr.s6_addr, sizeof(a.sin6.sin6_addr.s6_addr)) < 0;
154  }
155  };
157  struct addressOnlyEqual: public std::binary_function<ComboAddress, ComboAddress, bool>
158  {
159  bool operator()(const ComboAddress& a, const ComboAddress& b) const
160  {
161  if(a.sin4.sin_family != b.sin4.sin_family)
162  return false;
163  if(a.sin4.sin_family == AF_INET)
164  return a.sin4.sin_addr.s_addr == b.sin4.sin_addr.s_addr;
165  else
166  return !memcmp(&a.sin6.sin6_addr.s6_addr, &b.sin6.sin6_addr.s6_addr, sizeof(a.sin6.sin6_addr.s6_addr));
167  }
168  };
169 
171  socklen_t getSocklen() const
172  {
173  if(sin4.sin_family == AF_INET)
174  return sizeof(sin4);
175  else
176  return sizeof(sin6);
177  }
178 
181  {
182  sin4.sin_family=AF_INET;
183  sin4.sin_addr.s_addr=0;
184  sin4.sin_port=0;
185  }
186 
188  ComboAddress(const struct sockaddr *sa, socklen_t salen) {
189  setSockaddr(sa, salen);
190  }
191 
193  ComboAddress(const struct sockaddr_in6 *sa) {
194  setSockaddr((const struct sockaddr*)sa, sizeof(struct sockaddr_in6));
195  }
196 
198  ComboAddress(const struct sockaddr_in *sa) {
199  setSockaddr((const struct sockaddr*)sa, sizeof(struct sockaddr_in));
200  }
202  ComboAddress(const struct sockaddr_in& sa) {
203  setSockaddr((const struct sockaddr*)&sa, sizeof(struct sockaddr_in));
204  }
205 
206  void setSockaddr(const struct sockaddr *sa, socklen_t salen) {
207  if (salen > sizeof(struct sockaddr_in6)) throw std::runtime_error("ComboAddress can't handle other than sockaddr_in or sockaddr_in6");
208  memcpy(this, sa, salen);
209  }
210 
222  explicit ComboAddress(const std::string& str, uint16_t port=0)
223  {
224  memset(&sin6, 0, sizeof(sin6));
225  sin4.sin_family = AF_INET;
226  sin4.sin_port = 0;
227  if(makeIPv4sockaddr(str, &sin4)) {
228  sin6.sin6_family = AF_INET6;
229  if(makeIPv6sockaddr(str, &sin6) < 0)
230  throw std::runtime_error("Unable to convert presentation address '"+ str +"'");
231 
232  }
233  if(!sin4.sin_port) // 'str' overrides port!
234  sin4.sin_port=htons(port);
235  }
237  void setPort(uint16_t port)
238  {
239  sin4.sin_port = htons(port);
240  }
242  bool isIPv6() const
243  {
244  return sin4.sin_family == AF_INET6;
245  }
246 
248  bool isIPv4() const
249  {
250  return sin4.sin_family == AF_INET;
251  }
252 
254  bool isMappedIPv4() const
255  {
256  if(sin4.sin_family!=AF_INET6)
257  return false;
258 
259  int n=0;
260  const unsigned char*ptr = (unsigned char*) &sin6.sin6_addr.s6_addr;
261  for(n=0; n < 10; ++n)
262  if(ptr[n])
263  return false;
264 
265  for(; n < 12; ++n)
266  if(ptr[n]!=0xff)
267  return false;
268 
269  return true;
270  }
271 
274  {
275  if(!isMappedIPv4())
276  throw std::runtime_error("ComboAddress can't map non-mapped IPv6 address back to IPv4");
277  ComboAddress ret;
278  ret.sin4.sin_family=AF_INET;
279  ret.sin4.sin_port=sin4.sin_port;
280 
281  const unsigned char*ptr = (unsigned char*) &sin6.sin6_addr.s6_addr;
282  ptr+=(sizeof(sin6.sin6_addr.s6_addr) - sizeof(ret.sin4.sin_addr.s_addr));
283  memcpy(&ret.sin4.sin_addr.s_addr, ptr, sizeof(ret.sin4.sin_addr.s_addr));
284  return ret;
285  }
286 
288  std::string toString() const
289  {
290  char host[1024];
291  if(sin4.sin_family && !getnameinfo((struct sockaddr*) this, getSocklen(), host, sizeof(host),0, 0, NI_NUMERICHOST))
292  return host;
293  else
294  return "invalid";
295  }
296 
298  std::string toStringWithPort() const
299  {
300  if(sin4.sin_family==AF_INET)
301  return toString() + ":" + std::to_string(ntohs(sin4.sin_port));
302  else
303  return "["+toString() + "]:" + std::to_string(ntohs(sin4.sin_port));
304  }
305 
306  void truncate(unsigned int bits);
307 };
308 
309 
310 
313 class Netmask
314 {
315 public:
316  Netmask()
317  {
318  d_network.sin4.sin_family=0; // disable this doing anything useful
319  d_network.sin4.sin_port = 0; // this guarantees d_network compares identical
320  d_mask=0;
321  d_bits=0;
322  }
323 
324  Netmask(const ComboAddress& network, uint8_t bits=0xff)
325  {
326  d_network = network;
327 
328  if(bits > 128)
329  bits = (network.sin4.sin_family == AF_INET) ? 32 : 128;
330 
331  d_bits = bits;
332  if(d_bits<32)
333  d_mask=~(0xFFFFFFFF>>d_bits);
334  else
335  d_mask=0xFFFFFFFF; // not actually used for IPv6
336  }
337 
339  Netmask(const std::string &mask)
340  {
341  std::pair<std::string,std::string> split; // =splitField(mask,'/');
342  d_network=ComboAddress(split.first);
343 
344  if(!split.second.empty()) {
345  d_bits = (uint8_t)atoi(split.second.c_str());
346  if(d_bits<32)
347  d_mask=~(0xFFFFFFFF>>d_bits);
348  else
349  d_mask=0xFFFFFFFF;
350  }
351  else if(d_network.sin4.sin_family==AF_INET) {
352  d_bits = 32;
353  d_mask = 0xFFFFFFFF;
354  }
355  else {
356  d_bits=128;
357  d_mask=0; // silence silly warning - d_mask is unused for IPv6
358  }
359  }
360 
361  bool match(const ComboAddress& ip) const
362  {
363  return match(&ip);
364  }
365 
367  bool match(const ComboAddress *ip) const
368  {
369  if(d_network.sin4.sin_family != ip->sin4.sin_family) {
370  return false;
371  }
372  if(d_network.sin4.sin_family == AF_INET) {
373  return match4(htonl((unsigned int)ip->sin4.sin_addr.s_addr));
374  }
375  if(d_network.sin6.sin6_family == AF_INET6) {
376  uint8_t bytes=d_bits/8, n;
377  const uint8_t *us=(const uint8_t*) &d_network.sin6.sin6_addr.s6_addr;
378  const uint8_t *them=(const uint8_t*) &ip->sin6.sin6_addr.s6_addr;
379 
380  for(n=0; n < bytes; ++n) {
381  if(us[n]!=them[n]) {
382  return false;
383  }
384  }
385  // still here, now match remaining bits
386  uint8_t bits= d_bits % 8;
387  uint8_t mask= (uint8_t) ~(0xFF>>bits);
388 
389  return((us[n] & mask) == (them[n] & mask));
390  }
391  return false;
392  }
393 
395  bool match(const std::string &ip) const
396  {
397  ComboAddress address(ip);
398  return match(&address);
399  }
400 
402  bool match4(uint32_t ip) const
403  {
404  return (ip & d_mask) == (ntohl(d_network.sin4.sin_addr.s_addr) & d_mask);
405  }
406 
407  std::string toString() const
408  {
409  return d_network.toString()+"/"+std::to_string((unsigned int)d_bits);
410  }
411 
412  std::string toStringNoMask() const
413  {
414  return d_network.toString();
415  }
416  const ComboAddress& getNetwork() const
417  {
418  return d_network;
419  }
420  const ComboAddress getMaskedNetwork() const
421  {
422  ComboAddress result(d_network);
423  if(isIpv4()) {
424  result.sin4.sin_addr.s_addr = htonl(ntohl(result.sin4.sin_addr.s_addr) & d_mask);
425  }
426  else if(isIpv6()) {
427  size_t idx;
428  uint8_t bytes=d_bits/8;
429  uint8_t *us=(uint8_t*) &result.sin6.sin6_addr.s6_addr;
430  uint8_t bits= d_bits % 8;
431  uint8_t mask= (uint8_t) ~(0xFF>>bits);
432 
433  if (bytes < sizeof(result.sin6.sin6_addr.s6_addr)) {
434  us[bytes] &= mask;
435  }
436 
437  for(idx = bytes + 1; idx < sizeof(result.sin6.sin6_addr.s6_addr); ++idx) {
438  us[idx] = 0;
439  }
440  }
441  return result;
442  }
443  int getBits() const
444  {
445  return d_bits;
446  }
447  bool isIpv6() const
448  {
449  return d_network.sin6.sin6_family == AF_INET6;
450  }
451  bool isIpv4() const
452  {
453  return d_network.sin4.sin_family == AF_INET;
454  }
455 
456  bool operator<(const Netmask& rhs) const
457  {
458  return std::tie(d_network, d_bits) < std::tie(rhs.d_network, rhs.d_bits);
459  }
460 
461  bool operator==(const Netmask& rhs) const
462  {
463  return std::tie(d_network, d_bits) == std::tie(rhs.d_network, rhs.d_bits);
464  }
465 
466  bool empty() const
467  {
468  return d_network.sin4.sin_family==0;
469  }
470 
471 private:
472  ComboAddress d_network;
473  uint32_t d_mask;
474  uint8_t d_bits;
475 };
476 
477 
Definition: comboaddress.hh:78
bool operator!=(const ComboAddress &rhs) const
Inequality.
Definition: comboaddress.hh:95
Netmask(const std::string &mask)
Constructor supplies the mask, which cannot be changed.
Definition: comboaddress.hh:339
std::string toString() const
Returns a string (human) represntation of the address.
Definition: comboaddress.hh:288
bool match(const ComboAddress *ip) const
If this IP address in socket address matches.
Definition: comboaddress.hh:367
ComboAddress(const std::string &str, uint16_t port=0)
Definition: comboaddress.hh:222
ComboAddress mapToIPv4() const
Extract the IPv4 address from a mapped IPv6 address.
Definition: comboaddress.hh:273
bool isIPv4() const
Is this an IPv4 address?
Definition: comboaddress.hh:248
Convenience comparator that compares regardless of port.
Definition: comboaddress.hh:142
bool isMappedIPv4() const
Is this an ffff:: style IPv6 address?
Definition: comboaddress.hh:254
ComboAddress(const struct sockaddr_in *sa)
Make a ComboAddress from a traditional sockaddr_in.
Definition: comboaddress.hh:198
Definition: comboaddress.hh:313
ComboAddress(const struct sockaddr *sa, socklen_t salen)
Make a ComboAddress from a traditional sockaddr.
Definition: comboaddress.hh:188
bool match4(uint32_t ip) const
If this IP address in native format matches.
Definition: comboaddress.hh:402
bool operator<(const ComboAddress &rhs) const
This ordering is intended to be fast and strict, but not necessarily human friendly.
Definition: comboaddress.hh:101
socklen_t getSocklen() const
it is vital to pass the correct socklen to the kernel
Definition: comboaddress.hh:171
bool operator==(const ComboAddress &rhs) const
Tests for equality, including port number. IPv4 and IPv6 are never equal.
Definition: comboaddress.hh:84
void setPort(uint16_t port)
Sets port, deals with htons for you.
Definition: comboaddress.hh:237
ComboAddress(const struct sockaddr_in &sa)
Make a ComboAddress from a traditional sockaddr.
Definition: comboaddress.hh:202
ComboAddress(const struct sockaddr_in6 *sa)
Make a ComboAddress from a traditional sockaddr_in6.
Definition: comboaddress.hh:193
bool match(const std::string &ip) const
If this ASCII IP address matches.
Definition: comboaddress.hh:395
bool isIPv6() const
Is this an IPv6 address?
Definition: comboaddress.hh:242
Convenience comparator that compares regardless of port.
Definition: comboaddress.hh:157
std::string toStringWithPort() const
Returns a string (human) represntation of the address, including port.
Definition: comboaddress.hh:298
ComboAddress()
Initializes an &#39;empty&#39;, impossible, ComboAddress.
Definition: comboaddress.hh:180