sources/network/ipaddress.cpp

branch
protocol5
changeset 195
be953e1621d9
parent 190
90bf9049e5eb
equal deleted inserted replaced
176:060a13878ca0 195:be953e1621d9
1 /* 1 /*
2 Copyright 2014 - 2016 Teemu Piippo 2 Copyright 2014 - 2021 Teemu Piippo
3 All rights reserved. 3 All rights reserved.
4 4
5 Redistribution and use in source and binary forms, with or without 5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions 6 modification, are permitted provided that the following conditions
7 are met: 7 are met:
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

mercurial