$search
00001 00002 #ifndef CASTOR_NET_NETADDRESS_H 00003 #define CASTOR_NET_NETADDRESS_H 1 00004 00005 #include <string> 00006 #include <vector> 00007 #include <asio.hpp> 00008 #include <boost/algorithm/string.hpp> 00009 #include <castor/Exception.h> 00010 00011 /* 00012 * Spica addressing scheme 00013 * 00014 * Generic: <spica-specific> 00015 * <protocol>://<address>:<explicit port> (0x00) 00016 * 00017 * Spica-specific: <spica-specific> 00018 * spica+<protocol>://<address> (0x01) 00019 * spica+<protocol>://<address>[:<spica port>] (0x02) 00020 * spica+<protocol>://<address>[/cidr] (0x03) 00021 * 00022 * 00023 * Layout: 00024 * 00025 * <uchar:spica-specific><uchar:protocol><ushort:port><uchar:address-length><uchar*:address> 00026 * 00027 * 0x00 <uchar:protocol> <ushort:port> <uchar:address-length> <uchar*:address> 00028 * 0x01 <uchar:protocol> <uchar:address-length> <uchar*:address> 00029 * 0x02 <uchar:protocol> <ushort:port> <uchar:address-length> <uchar*:address> 00030 * 0x03 <uchar:protocol> <uchar:cidr> <uchar:address-length> <uchar*:address> 00031 * 00032 */ 00033 00034 namespace castor { 00035 00036 namespace net { 00037 00038 class NetAddress; 00039 00040 typedef std::vector<unsigned char> Bytes; 00041 typedef boost::shared_ptr<Bytes> BytesPtr; 00042 typedef boost::shared_ptr<NetAddress> NetAddressPtr; 00043 00044 class NetAddress { 00045 00046 public: 00047 00048 enum SpicaSpecific { 00049 00050 ss_none = 0x00, 00051 ss_plain = 0x01, 00052 ss_host = 0x02, 00053 ss_network = 0x03, 00054 ss_undecided = 0x04, 00055 }; 00056 00057 private: 00058 00059 SpicaSpecific spicaSpecific; 00060 unsigned char protocol; 00061 unsigned short port; 00062 unsigned char cidr; 00063 asio::ip::address address; 00064 00065 protected: 00066 00067 NetAddress() { 00068 00069 this->spicaSpecific = ss_none; 00070 00071 this->protocol = 0; 00072 this->port = 0; 00073 this->cidr = 0; 00074 } 00075 00076 NetAddress(SpicaSpecific spicaSpecific, unsigned char protocol, asio::ip::address address, unsigned short port, unsigned char cidr) { 00077 00078 this->spicaSpecific = spicaSpecific; 00079 00080 this->address = address; 00081 this->protocol = protocol; 00082 this->port = port; 00083 this->cidr = cidr; 00084 } 00085 00086 public: 00087 00088 static NetAddress create(const std::string spec) throw(Exception) { 00089 00090 NetAddress result; 00091 00092 std::string address = spec; 00093 00094 boost::algorithm::trim_if(address, boost::is_any_of("/")); 00095 00096 size_t pos = address.find("://"); 00097 00098 if (pos == std::string::npos) { 00099 std::ostringstream ss; 00100 ss << "NetAddress: invalid address specifier (" << spec << ")"; 00101 throw Exception(ss.str()); 00102 } 00103 00104 // process protocol part 00105 if (boost::istarts_with(address, "spica")) { 00106 00107 size_t plusPos = address.find("+"); 00108 00109 if (plusPos != std::string::npos) { 00110 00111 result.spicaSpecific = ss_undecided; 00112 result.protocol = NetAddress::string_to_protocol(address.substr(plusPos + 1, pos - plusPos - 1)); 00113 00114 } else { 00115 result.spicaSpecific = ss_plain; 00116 result.protocol = 0; 00117 } 00118 00119 } else { 00120 00121 result.spicaSpecific = ss_none; 00122 result.protocol = NetAddress::string_to_protocol(address.substr(0, pos)); 00123 } 00124 00125 address = address.substr(pos + 3, address.size() - pos - 3); 00126 00127 // process the host 00128 00129 size_t bracketStartPos = address.find("["); 00130 size_t bracketEndPos = address.find("["); 00131 size_t colonPos = address.rfind(":"); 00132 size_t slashPos = address.rfind("/"); 00133 00134 if (bracketStartPos != std::string::npos) { 00135 00136 std::string specAddress = address.substr(bracketStartPos + 1, bracketEndPos - bracketStartPos - 1); 00137 00138 result.address = asio::ip::address::from_string(specAddress); 00139 00140 if ((colonPos != std::string::npos) && (colonPos > bracketEndPos)) { 00141 00142 std::string specPort = address.substr(colonPos + 1, address.size() - colonPos - 1); 00143 00144 result.port = atoi(specPort.c_str()); 00145 00146 if (result.spicaSpecific == ss_plain) { 00147 throw Exception("NetAddress: a port may not be specified in spica plain address mode"); 00148 } 00149 00150 if (result.spicaSpecific == ss_undecided) { 00151 result.spicaSpecific = ss_host; 00152 } 00153 00154 } else if ((slashPos != std::string::npos) && (slashPos > bracketEndPos)) { 00155 00156 std::string specCidr = address.substr(slashPos + 1, address.size() - slashPos - 1); 00157 00158 result.cidr = atoi(specCidr.c_str()); 00159 00160 if (result.spicaSpecific != ss_undecided) { 00161 throw Exception("NetAddress: a network may not be specified in non-spica or spica plain address mode"); 00162 } 00163 00164 result.spicaSpecific = ss_network; 00165 00166 } else { 00167 if (result.spicaSpecific == ss_undecided) { 00168 result.spicaSpecific = ss_plain; 00169 } 00170 } 00171 00172 } else { 00173 00174 if (colonPos != std::string::npos) { 00175 00176 result.address = asio::ip::address::from_string(address.substr(0, colonPos)); 00177 result.port = atoi(address.substr(colonPos + 1, address.size() - colonPos - 1).c_str()); 00178 00179 if (result.spicaSpecific == ss_plain) { 00180 throw Exception("NetAddress: a port may not be specified in spica plain address mode"); 00181 } 00182 00183 if (result.spicaSpecific == ss_undecided) { 00184 result.spicaSpecific = ss_host; 00185 } 00186 00187 } else if (slashPos != std::string::npos) { 00188 00189 result.address = asio::ip::address::from_string(address.substr(0, slashPos)); 00190 result.cidr = atoi(address.substr(slashPos + 1, address.size() - slashPos - 1).c_str()); 00191 00192 if (result.spicaSpecific != ss_undecided) { 00193 throw Exception("NetAddress: a network may not be specified in non-spica or spica plain address mode"); 00194 } 00195 00196 result.spicaSpecific = ss_network; 00197 00198 } else { 00199 result.address = asio::ip::address::from_string(address); 00200 00201 if (result.spicaSpecific == ss_undecided) { 00202 00203 if (result.protocol != 0) { 00204 result.spicaSpecific = ss_host; 00205 } else { 00206 result.spicaSpecific = ss_plain; 00207 } 00208 } 00209 } 00210 } 00211 00212 return result; 00213 } 00214 00215 static NetAddress create(unsigned char protocol, asio::ip::address address, unsigned short port) { 00216 return NetAddress(ss_none, protocol, address, port, 0); 00217 } 00218 00219 static NetAddress createPlain(unsigned char protocol, asio::ip::address address) { 00220 return NetAddress(ss_plain, protocol, address, 0, 0); 00221 } 00222 00223 static NetAddress createHost(unsigned char protocol, asio::ip::address address, unsigned short port) { 00224 return NetAddress(ss_host, protocol, address, port, 0); 00225 } 00226 00227 static NetAddress createNetwork(unsigned char protocol, asio::ip::address address, unsigned char cidr) { 00228 return NetAddress(ss_network, protocol, address, 0, cidr); 00229 } 00230 00231 static NetAddress create(const BytesPtr bytes) throw() { 00232 NetAddress result; 00233 result.parse(&(*bytes)[0], bytes->size()); 00234 return result; 00235 } 00236 00237 static NetAddress create(const unsigned char *data, size_t size) throw() { 00238 NetAddress result; 00239 result.parse(data, size); 00240 return result; 00241 } 00242 00243 NetAddress(const NetAddress &other) { 00244 00245 this->spicaSpecific = other.spicaSpecific; 00246 00247 this->protocol = other.protocol; 00248 this->address = other.address; 00249 this->port = other.port; 00250 this->cidr = other.cidr; 00251 } 00252 00253 bool isSpicaSpecific() const { return this->spicaSpecific != ss_none; } 00254 bool isPlain() const { return this->spicaSpecific == ss_plain; } 00255 bool isHost() const { return this->spicaSpecific == ss_host; } 00256 bool isNetwork() const { return this->spicaSpecific == ss_network; } 00257 00258 unsigned char getProtocol() const { return this->protocol; } 00259 00260 unsigned short getPort() const { 00261 if (this->spicaSpecific == ss_host) { 00262 return this->port; 00263 } 00264 return 0; 00265 } 00266 00267 unsigned char getCidr() const { 00268 if (this->spicaSpecific == ss_network) { 00269 return this->cidr; 00270 } 00271 return 0; 00272 } 00273 00274 const asio::ip::address &getAddress() const { 00275 return this->address; 00276 } 00277 00278 void setAddress(asio::ip::address addr_) { 00279 this->address = addr_; 00280 00281 } 00282 00283 BytesPtr getBytes() const { 00284 00285 BytesPtr result; 00286 00287 size_t offset = 0; 00288 size_t length = (this->address.is_v4() ? 4 : 16); 00289 00290 switch (this->spicaSpecific) { 00291 00292 case ss_none: // generic address => 0x00 <uchar: protocol> <ushort:port> <uchar:address-length> <uchar*:address> 00293 { 00294 00295 size_t size = 5 + length; 00296 unsigned short port = asio::detail::socket_ops::network_to_host_short(this->port); 00297 00298 result = BytesPtr(new Bytes(size)); 00299 00300 (*result)[0] = (unsigned char)ss_none; 00301 (*result)[1] = (unsigned char)this->protocol; 00302 (*result)[2] = (unsigned char)((port & 0xff00) >> 8); 00303 (*result)[3] = (unsigned char)(port & 0x00ff); 00304 (*result)[4] = (unsigned char)length; 00305 00306 offset = 5; 00307 } 00308 00309 break; 00310 00311 case ss_plain: // spica plain => 0x01 <uchar:protocol> <uchar:address-length> <uchar*:address> 00312 { 00313 00314 size_t size = 3 + length; 00315 00316 result = BytesPtr(new Bytes(size)); 00317 00318 (*result)[0] = (unsigned char)ss_plain; 00319 (*result)[1] = (unsigned char)this->protocol; 00320 (*result)[2] = (unsigned char)length; 00321 00322 offset = 3; 00323 } 00324 00325 break; 00326 00327 case ss_host: // spica host => 0x02 <uchar:protocol> <ushort:port> <uchar:address-length> <uchar*:address> 00328 { 00329 00330 size_t size = 5 + length; 00331 unsigned short port = asio::detail::socket_ops::network_to_host_short(this->port); 00332 00333 result = BytesPtr(new Bytes(size)); 00334 00335 (*result)[0] = (unsigned char)ss_host; 00336 (*result)[1] = (unsigned char)this->protocol; 00337 (*result)[2] = (unsigned char)((port & 0xff00) >> 8); 00338 (*result)[3] = (unsigned char)(port & 0x00ff); 00339 (*result)[4] = (unsigned char)length; 00340 00341 offset = 5; 00342 } 00343 00344 break; 00345 00346 case ss_network: // spica network => 0x03 <uchar:protocol> <uchar:cidr> <uchar:address-length> <uchar*:address> 00347 { 00348 00349 size_t size = 4 + length; 00350 00351 result = BytesPtr(new Bytes(size)); 00352 00353 (*result)[0] = (unsigned char)ss_network; 00354 (*result)[1] = (unsigned char)this->protocol; 00355 (*result)[2] = this->cidr; 00356 (*result)[3] = (unsigned char)length; 00357 00358 offset = 4; 00359 } 00360 00361 break; 00362 00363 default: 00364 throw Exception("NetAddress: unknown value for SpicaSpecific"); 00365 00366 } 00367 00368 if (this->address.is_v4()) { 00369 00370 asio::ip::address_v4::bytes_type b = this->address.to_v4().to_bytes(); 00371 for (size_t i = 0; i < b.size(); i++) (*result)[offset + i] = b[i]; 00372 00373 } else { 00374 asio::ip::address_v6::bytes_type b = this->address.to_v6().to_bytes(); 00375 for (size_t i = 0; i < b.size(); i++) (*result)[offset + i] = b[i]; 00376 } 00377 00378 return result; 00379 } 00380 00381 std::string str() const { 00382 00383 std::ostringstream ss; 00384 00385 switch (this->spicaSpecific) { 00386 00387 case ss_none: // generic address 00388 00389 ss << protocol_to_string(this->protocol) << "://[" << this->address.to_string() << "]"; 00390 00391 if (this->port > 0) { 00392 ss << ":" << this->port; 00393 } 00394 00395 ss << "/"; 00396 00397 break; 00398 00399 case ss_plain: // spica plain 00400 00401 ss << "spica" << "://[" << this->address.to_string() << "]/"; 00402 00403 break; 00404 00405 case ss_host: // spica host 00406 00407 ss << "spica+"<< protocol_to_string(this->protocol) << "://[" << this->address.to_string() << "]"; 00408 00409 if (this->port > 0) { 00410 ss << ":" << this->port; 00411 } 00412 00413 ss << "/"; 00414 00415 break; 00416 00417 case ss_network: // spica network 00418 00419 ss << "spica+" << protocol_to_string(this->protocol) << "://[" << this->address.to_string() << "]"; 00420 00421 if (this->cidr > 0) { 00422 ss << "/" << (int)this->cidr; 00423 } 00424 00425 break; 00426 00427 default: // should not be reached 00428 00429 ss << "NetAddress::str(): this should not be reached"; 00430 00431 break; 00432 } 00433 00434 return "." + ss.str(); 00435 } 00436 00437 inline static std::string protocol_to_string(int protocol) { 00438 switch (protocol) { 00439 case IPPROTO_IP: return "ip"; 00440 case IPPROTO_TCP: return "tcp"; 00441 case IPPROTO_UDP: return "udp"; 00442 case IPPROTO_IPV6: return "ipv6"; 00443 } 00444 return ""; 00445 } 00446 00447 inline static unsigned char string_to_protocol(const std::string protocol) { 00448 if (boost::iequals(protocol, "ip")) { 00449 return (unsigned char)IPPROTO_IP; 00450 } else if (boost::iequals(protocol, "tcp")) { 00451 return (unsigned char)IPPROTO_TCP; 00452 } else if (boost::iequals(protocol, "udp")) { 00453 return (unsigned char)IPPROTO_UDP; 00454 } else if (boost::iequals(protocol, "ipv6")) { 00455 return (unsigned char)IPPROTO_IPV6; 00456 } 00457 return 0; 00458 } 00459 00460 bool equals(const NetAddress &other) const { 00461 return (*this == other); 00462 } 00463 00464 NetAddress &operator=(const NetAddress &other) { 00465 00466 if (this != &other) { 00467 00468 this->spicaSpecific = other.spicaSpecific; 00469 00470 this->protocol = other.protocol; 00471 this->address = other.address; 00472 this->port = other.port; 00473 this->cidr = other.cidr; 00474 } 00475 00476 return *this; 00477 } 00478 00479 bool operator==(const NetAddress &other) const { 00480 00481 if (this->spicaSpecific != other.spicaSpecific) return false; 00482 if (this->address != other.address) return false; 00483 if ((this->spicaSpecific != ss_plain) && 00484 (this->spicaSpecific != ss_network) && 00485 (this->port != other.port)) return false; 00486 if ((this->spicaSpecific == ss_network) && 00487 (this->cidr != other.cidr)) return false; 00488 00489 return true; 00490 } 00491 00492 bool operator!=(const NetAddress &other) const { 00493 return !(*this == other); 00494 } 00495 00496 protected: 00497 00498 void parse(const unsigned char *data, size_t size) throw() { 00499 00500 if (data == NULL) throw castor::Exception("NetAddress: address data is NULL"); 00501 if (size < 1) throw castor::Exception("NetAddress: address too short"); 00502 00503 this->spicaSpecific = (SpicaSpecific)data[0]; 00504 00505 switch (this->spicaSpecific) { 00506 00507 case ss_none: // generic address => 0x00 <uchar:protocol> <ushort:port> <uchar:address-length> <uchar*:address> 00508 { 00509 00510 if (size < 5) throw castor::Exception("NetAddress: address too short for a generic address"); 00511 00512 this->protocol = data[1]; 00513 this->port = asio::detail::socket_ops::host_to_network_short(((unsigned short)data[2] << 8) | 00514 ((unsigned short)data[3])); 00515 00516 size_t length = (size_t)data[4]; 00517 00518 if ((length + 5) != size) throw castor::Exception("NetAddress: address too short for a generic address"); 00519 if ((length != 4) && (length != 16)) throw castor::Exception("NetAddress: address is neither an ipv4 nor an ipv6 address"); 00520 00521 if (length == 4) { 00522 00523 asio::ip::address_v4::bytes_type a; 00524 for (size_t i = 0; i < length; i++) { 00525 a[i] = data[5 + i]; 00526 } 00527 this->address = asio::ip::address_v4(a); 00528 00529 } else { 00530 00531 asio::ip::address_v6::bytes_type a; 00532 for (size_t i = 0; i < length; i++) { 00533 a[i] = data[5 + i]; 00534 } 00535 this->address = asio::ip::address_v6(a); 00536 } 00537 } 00538 00539 break; 00540 00541 case ss_plain: // spica plain => 0x01 <uchar:protocol> <uchar:address-length> <uchar*:address> 00542 { 00543 00544 if (size < 3) throw castor::Exception("NetAddress: address too short for a spica plain address"); 00545 00546 this->protocol = data[1]; 00547 00548 size_t length = (size_t)data[2]; 00549 00550 if ((length + 3) != size) throw castor::Exception("NetAddress: address too short for a spica plain address"); 00551 if ((length != 4) && (length != 16)) throw castor::Exception("NetAddress: address is neither an ipv4 nor an ipv6 address"); 00552 00553 if (length == 4) { 00554 00555 asio::ip::address_v4::bytes_type a; 00556 for (size_t i = 0; i < length; i++) { 00557 a[i] = data[3 + i]; 00558 } 00559 this->address = asio::ip::address_v4(a); 00560 00561 } else { 00562 00563 asio::ip::address_v6::bytes_type a; 00564 for (size_t i = 0; i < length; i++) { 00565 a[i] = data[3 + i]; 00566 } 00567 this->address = asio::ip::address_v6(a); 00568 } 00569 } 00570 00571 break; 00572 00573 case ss_host: // spica host => 0x02 <uchar:protocol> <ushort:port> <uchar:address-length> <uchar*:address> 00574 { 00575 00576 if (size < 5) throw castor::Exception("NetAddress: address too short for a spica host address"); 00577 00578 this->protocol = data[1]; 00579 this->port = asio::detail::socket_ops::host_to_network_short(((unsigned short)data[2] << 8) | 00580 ((unsigned short)data[3])); 00581 00582 size_t length = (size_t)data[4]; 00583 00584 if ((length + 5) != size) throw castor::Exception("NetAddress: address too short for a spica host address"); 00585 if ((length != 4) && (length != 16)) throw castor::Exception("NetAddress: address is neither an ipv4 nor an ipv6 address"); 00586 00587 if (length == 4) { 00588 00589 asio::ip::address_v4::bytes_type a; 00590 for (size_t i = 0; i < length; i++) { 00591 a[i] = data[5 + i]; 00592 } 00593 this->address = asio::ip::address_v4(a); 00594 00595 } else { 00596 00597 asio::ip::address_v6::bytes_type a; 00598 for (size_t i = 0; i < length; i++) { 00599 a[i] = data[5 + i]; 00600 } 00601 this->address = asio::ip::address_v6(a); 00602 } 00603 } 00604 00605 break; 00606 00607 case ss_network: // spica network => 0x03 <uchar:protocol> <uchar:cidr> <uchar:address-length> <uchar*:address> 00608 { 00609 00610 if (size < 4) throw castor::Exception("NetAddress: address too short for a spica network address"); 00611 00612 this->protocol = data[1]; 00613 this->cidr = data[2]; 00614 size_t length = (size_t)data[3]; 00615 00616 if ((length + 4) != size) throw castor::Exception("NetAddress: address too short for a spica network address"); 00617 if ((length != 4) && (length != 16)) throw castor::Exception("NetAddress: address is neither an ipv4 nor an ipv6 address"); 00618 00619 if (length == 4) { 00620 00621 asio::ip::address_v4::bytes_type a; 00622 for (size_t i = 0; i < length; i++) { 00623 a[i] = data[4 + i]; 00624 } 00625 this->address = asio::ip::address_v4(a); 00626 00627 } else { 00628 00629 asio::ip::address_v6::bytes_type a; 00630 for (size_t i = 0; i < length; i++) { 00631 a[i] = data[4 + i]; 00632 } 00633 this->address = asio::ip::address_v6(a); 00634 } 00635 } 00636 00637 break; 00638 00639 case ss_undecided: 00640 // should not be reached 00641 break; 00642 } 00643 } 00644 }; 00645 }; 00646 }; 00647 00648 std::ostream &operator << (std::ostream &os, const castor::net::NetAddress &x); 00649 00650 #endif /* CASTOR_NET_NETADDRESS_H */