sources/network/ipaddress.cpp

changeset 186
9330b93d9946
parent 185
e83ec58cc458
child 188
5fc32e4b2a8c
equal deleted inserted replaced
185:e83ec58cc458 186:9330b93d9946
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

mercurial