44 typedef ADDRINFOA AddrInfo; |
44 typedef ADDRINFOA AddrInfo; |
45 #else |
45 #else |
46 typedef struct addrinfo AddrInfo; |
46 typedef struct addrinfo AddrInfo; |
47 #endif |
47 #endif |
48 |
48 |
49 // ----------------------------------------------------------------------------- |
49 net::octet_t net::ip_octet(const net::ip_address& address, unsigned char n) |
50 // |
50 { |
51 IPAddress::IPAddress() : |
51 assert(n < 4); |
52 host (0), |
52 return (address.host >> ((3 - n) * 8)) & 0xFF; |
53 port (0) {} |
53 } |
54 |
54 |
55 // ----------------------------------------------------------------------------- |
55 void net::ip_set_octet(net::ip_address* address, unsigned char n, net::octet_t octet) |
56 // |
56 { |
57 IPAddress::IPAddress (unsigned long host, unsigned short port) : |
57 // TODO: make a big-endian version |
58 host (host), |
58 assert(n < 4); |
59 port (port) {} |
59 address->host &= ~(0xFF << (3 - n) * 8); |
|
60 address->host |= octet << ((3 - n) * 8); |
|
61 } |
60 |
62 |
61 // ----------------------------------------------------------------------------- |
63 std::string net::ip_address_to_string(const net::ip_address& address) |
62 // |
64 { |
63 IPAddress::IPAddress (const IPAddress& other) : |
65 const auto octet = [&](unsigned char n) { return ip_octet(address, n); }; |
64 host (other.host), |
66 std::string val = sprintf ("%u.%u.%u.%u:%u", octet(0), octet(1), octet(2), octet(3), address.port); |
65 port (other.port) {} |
67 return val; |
|
68 } |
66 |
69 |
67 // ----------------------------------------------------------------------------- |
70 int net::ip_compare(const net::ip_address &one, const net::ip_address &other) |
68 // |
|
69 String IPAddress::to_string (WithPort withport) const |
|
70 { |
71 { |
71 String val; |
72 if (one.host != other.host) |
|
73 { |
|
74 return one.host - other.host; |
|
75 } |
|
76 return one.port - other.port; |
|
77 } |
72 |
78 |
73 if (withport == WITH_PORT) |
79 bool net::ip_address::operator<(const ip_address& other) const |
74 val.sprintf ("%u.%u.%u.%u:%u", octet (0), octet (1), octet (2), octet (3), port); |
80 { |
75 else |
81 return ip_compare(*this, other) < 0; |
76 val.sprintf ("%u.%u.%u.%u", octet (0), octet (1), octet (2), octet (3)); |
82 } |
77 |
83 |
78 return val; |
84 bool net::ip_address::operator==(const net::ip_address &other) const |
|
85 { |
|
86 return this->host == other.host and this->port == other.port; |
79 } |
87 } |
80 |
88 |
81 // ----------------------------------------------------------------------------- |
89 // ----------------------------------------------------------------------------- |
82 // |
90 // |
83 unsigned char IPAddress::octet (int n) const |
91 sockaddr_in net::ip_address_to_sockaddr_in(const net::ip_address& address) |
84 { |
|
85 return (host >> ((3 - n) * 8)) & 0xFF; |
|
86 } |
|
87 |
|
88 // ----------------------------------------------------------------------------- |
|
89 // |
|
90 void IPAddress::set_octet (int n, unsigned char oct) |
|
91 { |
|
92 // TODO: make a big-endian version |
|
93 host &= ~(0xFF << (3 - n) * 8); |
|
94 host |= oct << ((3 - n) * 8); |
|
95 } |
|
96 |
|
97 // ----------------------------------------------------------------------------- |
|
98 // |
|
99 bool IPAddress::compare (const IPAddress& other) const |
|
100 { |
|
101 for (int i : range(4)) |
|
102 { |
|
103 if (octet (i) != other.octet (i)) |
|
104 return false; |
|
105 } |
|
106 |
|
107 if (port != 0 |
|
108 and other.port != 0 |
|
109 and port != other.port) |
|
110 { |
|
111 return false; |
|
112 } |
|
113 |
|
114 return true; |
|
115 } |
|
116 |
|
117 // ----------------------------------------------------------------------------- |
|
118 // |
|
119 bool IPAddress::operator< (const IPAddress& other) const |
|
120 { |
|
121 for (int i : range(4)) |
|
122 { |
|
123 if (octet (i) != other.octet (i)) |
|
124 return octet (i) < other.octet (i); |
|
125 } |
|
126 |
|
127 return port < other.port; |
|
128 } |
|
129 |
|
130 // ----------------------------------------------------------------------------- |
|
131 // |
|
132 sockaddr_in IPAddress::to_sockaddr_in() const |
|
133 { |
92 { |
134 sockaddr_in claddr; |
93 sockaddr_in claddr; |
135 memset (&claddr, 0, sizeof claddr); |
94 memset (&claddr, 0, sizeof claddr); |
136 claddr.sin_addr.s_addr = htonl (host); |
95 claddr.sin_addr.s_addr = htonl(address.host); |
137 claddr.sin_port = htons (port); |
96 claddr.sin_port = htons(address.port); |
138 claddr.sin_family = AF_INET; |
97 claddr.sin_family = AF_INET; |
139 return claddr; |
98 return claddr; |
140 } |
99 } |
141 |
100 |
142 // ----------------------------------------------------------------------------- |
101 std::optional<unsigned short> net::ip_parse_port(const char* port_string, std::ostream& errorStream) |
143 // |
|
144 IPAddress IPAddress::from_string (String input) |
|
145 { |
102 { |
146 unsigned int parts[4]; |
103 std::optional<long> opt = to_int(port_string); |
147 int colonpos = input.find (":"); |
104 if (opt.has_value()) |
148 String addressString = colonpos == -1 ? input : input.mid (0, colonpos); |
105 { |
149 IPAddress value; |
106 if (*opt >= 0 and *opt < 65536) |
|
107 { |
|
108 return static_cast<unsigned short>(opt.value()); |
|
109 } |
|
110 else |
|
111 { |
|
112 return {}; |
|
113 errorStream << "port is not in range"s; |
|
114 } |
|
115 } |
|
116 else |
|
117 { |
|
118 return {}; |
|
119 errorStream << "could not parse port"s; |
|
120 } |
|
121 } |
150 |
122 |
|
123 std::optional<net::ip_address> net::ip_resolve_hostname(const std::string& node, std::ostream& errorStream) |
|
124 { |
|
125 AddrInfo hints; |
|
126 AddrInfo* lookup; |
|
127 memset(&hints, 0, sizeof hints); |
|
128 hints.ai_family = AF_INET; |
|
129 std::optional<net::ip_address> result = {}; |
|
130 if (::getaddrinfo(node.data(), nullptr, &hints, &lookup) != 0) |
|
131 { |
|
132 errorStream << "unknown host "s + node; |
|
133 } |
|
134 else |
|
135 { |
|
136 assert (lookup != nullptr); |
|
137 sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(lookup[0].ai_addr); |
|
138 result = net::ip_address{ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port)}; |
|
139 ::freeaddrinfo(lookup); |
|
140 } |
|
141 return result; |
|
142 } |
|
143 |
|
144 std::optional<net::ip_address> net::ip_resolve(const std::string& input_string, std::ostream& errorStream) |
|
145 { |
|
146 int colonpos = input_string.find(":"); |
|
147 std::string addressString = colonpos == -1 ? input_string : mid(input_string, 0, colonpos); |
|
148 std::optional<net::ip_address> value; |
151 // Try scanf the IPv4 host first |
149 // Try scanf the IPv4 host first |
152 if (sscanf (addressString, "%u.%u.%u.%u", &parts[0], &parts[1], &parts[2], &parts[3])) |
150 int parts[4]; |
|
151 if (std::sscanf(addressString.data(), "%d.%d.%d.%d", &parts[0], &parts[1], &parts[2], &parts[3])) |
153 { |
152 { |
154 for (int i : range(4)) |
153 value = net::ip_address{}; |
155 value.set_octet (i, parts[i]); |
154 for (unsigned char i = 0; i < 4; i += 1) |
|
155 { |
|
156 if (parts[i] >= 0 and parts[i] < 256) |
|
157 { |
|
158 ip_set_octet(&*value, i, parts[i]); |
|
159 } |
|
160 else |
|
161 { |
|
162 value.reset(); |
|
163 errorStream << "IP address value out of range"; |
|
164 break; |
|
165 } |
|
166 } |
156 } |
167 } |
157 else |
168 else |
158 { |
169 { |
159 // Possibly a hostname, try resolve it |
170 // Possibly a hostname, try resolve it |
160 value = IPAddress::resolve (addressString); |
171 value = ip_resolve_hostname(addressString, errorStream); |
161 } |
172 } |
162 |
173 if (value.has_value() and colonpos != -1) |
163 if (colonpos != -1) |
174 { |
164 value.port = (unsigned short) input.mid (colonpos + 1, -1).toInt(); |
175 std::optional<unsigned short> port_opt = ip_parse_port(&input_string.data()[colonpos + 1], errorStream); |
165 |
176 if (port_opt.has_value()) |
|
177 { |
|
178 value->port = *port_opt; |
|
179 } |
|
180 else |
|
181 { |
|
182 value.reset(); |
|
183 } |
|
184 } |
166 return value; |
185 return value; |
167 } |
186 } |
168 |
187 |
169 // ----------------------------------------------------------------------------- |
|
170 // |
|
171 IPAddress IPAddress::resolve (String node) |
|
172 { |
|
173 AddrInfo hints; |
|
174 AddrInfo* lookup; |
|
175 memset (&hints, 0, sizeof hints); |
|
176 hints.ai_family = AF_INET; |
|
177 |
|
178 if (getaddrinfo (node, nullptr, &hints, &lookup) != 0) |
|
179 throw StringParseError ("unknown host " + node); |
|
180 |
|
181 IPAddress result; |
|
182 assert (lookup != nullptr); |
|
183 sockaddr_in* addr = reinterpret_cast<sockaddr_in*> (lookup[0].ai_addr); |
|
184 result.host = ntohl (addr->sin_addr.s_addr); |
|
185 result.port = ntohs (addr->sin_port); |
|
186 freeaddrinfo (lookup); |
|
187 return result; |
|
188 } |
|
189 |
|
190 END_ZFC_NAMESPACE |
188 END_ZFC_NAMESPACE |