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