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
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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
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
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:
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:
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:
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:
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:
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:
00400
00401 ss << "spica" << "://[" << this->address.to_string() << "]/";
00402
00403 break;
00404
00405 case ss_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:
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:
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:
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:
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:
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:
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
00641 break;
00642 }
00643 }
00644 };
00645 };
00646 };
00647
00648 std::ostream &operator << (std::ostream &os, const castor::net::NetAddress &x);
00649
00650 #endif