Panda3D
socket_address.cxx
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file socket_address.h
10  * @author rdb
11  * @date 2016-06-17
12  */
13 
14 #include "socket_address.h"
15 #include "config_downloader.h"
16 
17 /**
18  * This function will take a port and string-based TCP address and initialize
19  * the address with this information. Returns true on success; on failure, it
20  * returns false and the address may be undefined.
21  */
23 set_host(const std::string &hostname, unsigned short port) {
24  // hmm inet_addr4 does not resolve 255.255.255.255 on ME98 ?? * HACK * ??
25  if (hostname == "255.255.255.255") {
26  return set_broadcast(port);
27  }
28 
29  struct addrinfo hints, *res = nullptr;
30  memset(&hints, 0, sizeof(hints));
31  hints.ai_flags = AI_ADDRCONFIG;
32  hints.ai_family = support_ipv6 ? AF_UNSPEC : AF_INET;
33 
34  if (getaddrinfo(hostname.c_str(), nullptr, &hints, &res)) {
35  return false;
36  }
37 
38  nassertr(res->ai_addrlen <= sizeof(_storage), false);
39  memcpy(&_storage, res->ai_addr, res->ai_addrlen);
40  freeaddrinfo(res);
41 
42  _addr4.sin_port = htons(port);
43  return true;
44 }
45 
46 /**
47  * Initializes the address from a string specifying both the address and port,
48  * separated by a colon. An IPv6 address must be enclosed in brackets.
49  */
51 set_host(const std::string &hostname) {
52  std::string::size_type pos = hostname.rfind(':');
53  if (pos == std::string::npos) {
54  return false;
55  }
56 
57  std::string::size_type host_begin = 0;
58  std::string::size_type host_end = pos;
59 
60  // Strip spaces.
61  while (host_begin < host_end && isspace(hostname[host_begin])) {
62  ++host_begin;
63  }
64 
65  while (host_begin < host_end && isspace(hostname[host_end - 1])) {
66  --host_end;
67  }
68 
69  if (host_begin < host_end && hostname[host_begin] == '[') {
70  // Looks like an IPv6 address; extract from the brackets.
71  host_begin += 1;
72  if (hostname[host_end - 1] == ']') {
73  host_end -= 1;
74  } else {
75  return false;
76  }
77  }
78 
79  std::string host = hostname.substr(host_begin, host_end - host_begin);
80  std::string port = hostname.substr(pos + 1, 100);
81 
82  unsigned short port_dig = (unsigned short)atoi(port.c_str());
83  return set_host(host, port_dig);
84 }
85 
86 /**
87  * Return the IP address portion in dot notation string.
88  */
89 std::string Socket_Address::
90 get_ip() const {
91  char buf[48];
92  buf[0] = 0;
93 
94  if (_storage.ss_family == AF_INET) {
95  getnameinfo(&_addr, sizeof(sockaddr_in), buf, sizeof(buf), nullptr, 0, NI_NUMERICHOST);
96 
97  } else if (_storage.ss_family == AF_INET6) {
98  getnameinfo(&_addr, sizeof(sockaddr_in6), buf, sizeof(buf), nullptr, 0, NI_NUMERICHOST);
99 
100  } else {
101  nassert_raise("unsupported address family");
102  }
103 
104  return std::string(buf);
105 }
106 
107 /**
108  * Return the ip address/port in dot notation string. If this is an IPv6
109  * address, it will be enclosed in square brackets.
110  */
111 std::string Socket_Address::
112 get_ip_port() const {
113  char buf[100]; // 100 is more than enough for any ip address:port combo..
114  buf[0] = 0;
115 
116  if (_storage.ss_family == AF_INET) {
117  getnameinfo(&_addr, sizeof(sockaddr_in), buf, sizeof(buf), nullptr, 0, NI_NUMERICHOST);
118  sprintf(buf + strlen(buf), ":%hu", get_port());
119 
120  } else if (_storage.ss_family == AF_INET6) {
121  // Protect the IPv6 address within square brackets.
122  buf[0] = '[';
123  getnameinfo(&_addr, sizeof(sockaddr_in6), buf + 1, sizeof(buf) - 1, nullptr, 0, NI_NUMERICHOST);
124  sprintf(buf + strlen(buf), "]:%hu", get_port());
125 
126  } else {
127  nassert_raise("unsupported address family");
128  }
129 
130  return std::string(buf);
131 }
132 
133 /**
134  * Returns a raw 32-bit unsigned integer representing the IPv4 address.
135  * @deprecated Does not work with IPv6 addresses.
136  */
137 unsigned long Socket_Address::
139  if (_addr.sa_family == AF_INET) {
140  return _addr4.sin_addr.s_addr;
141  }
142  if (_addr.sa_family == AF_INET6) {
143  // Okay, if we got here, something probably went wrong, but let's see if
144  // we can offer a meaningful translation for mapped addresses.
145  uint32_t *parts = (uint32_t *)&_addr6.sin6_addr;
146  if (parts[0] == 0 && parts[1] == 0 && (parts[2] == 0 || parts[2] == htonl(0xffff))) {
147  if (parts[2] == 0 && parts[3] == htonl(1)) {
148  // Special exception for localhost.
149  return 0x1000007f;
150  } else {
151  return parts[3];
152  }
153  }
154  }
155  nassert_raise("GetIPAddressRaw() can only be called on an IPv4 address");
156  return 0;
157 }
unsigned long GetIPAddressRaw() const
Returns a raw 32-bit unsigned integer representing the IPv4 address.
std::string get_ip_port() const
Return the ip address/port in dot notation string.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool set_broadcast(unsigned short port)
Set to the broadcast address and a specified port.
unsigned short get_port() const
Get the port portion as an integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_ip() const
Return the IP address portion in dot notation string.
bool set_host(const std::string &hostname, unsigned short port)
This function will take a port and string-based TCP address and initialize the address with this info...