InetAddr.cc

Go to the documentation of this file.
00001 
00022 #include "Common/Compat.h"
00023 #include "Common/Serialization.h"
00024 
00025 #include <cstdlib>
00026 #include <cstring>
00027 
00028 extern "C" {
00029 #include <arpa/inet.h>
00030 #include <netdb.h>
00031 #include <sys/socket.h>
00032 #include <sys/types.h>
00033 #include <sigar.h>
00034 }
00035 
00036 #include "Logger.h"
00037 #include "InetAddr.h"
00038 #include "StringExt.h"
00039 
00040 namespace Hypertable {
00041 
00042 InetAddr::InetAddr() {
00043   HT_EXPECT(sizeof(sockaddr_in) == sizeof(InetAddr), Error::UNPOSSIBLE);
00044   memset(this, 0, sizeof(InetAddr));
00045 }
00046 
00047 InetAddr::InetAddr(const String &host, uint16_t port) {
00048   HT_EXPECT(sizeof(sockaddr_in) == sizeof(InetAddr), Error::UNPOSSIBLE);
00049   HT_EXPECT(initialize(this, host.c_str(), port), Error::BAD_DOMAIN_NAME);
00050 }
00051 
00052 InetAddr::InetAddr(const String &endpoint) {
00053   HT_EXPECT(sizeof(sockaddr_in) == sizeof(InetAddr), Error::UNPOSSIBLE);
00054   HT_EXPECT(initialize(this, endpoint.c_str()), Error::BAD_DOMAIN_NAME);
00055 }
00056 
00057 InetAddr::InetAddr(uint32_t ip32, uint16_t port) {
00058   HT_EXPECT(sizeof(sockaddr_in) == sizeof(InetAddr), Error::UNPOSSIBLE);
00059   initialize(this, ip32, port);
00060 }
00061 
00062 bool InetAddr::initialize(sockaddr_in *addr, const char *host, uint16_t port) {
00063   memset(addr, 0, sizeof(struct sockaddr_in));
00064 
00065   if (parse_ipv4(host, port, *addr)) {
00066     return true;
00067   }
00068   else {
00069 #if defined(__linux__)
00070     // Let's hope this is not broken in the glibc we're using
00071     struct hostent hent, *he = 0;
00072     char hbuf[2048];
00073     int err;
00074 
00075     if (gethostbyname_r(host, &hent, hbuf, sizeof(hbuf), &he, &err) != 0
00076        || he == 0) {
00077       HT_ERRORF("gethostbyname '%s': error: %d", host, err);
00078       return false;
00079     }
00080 #elif defined(__APPLE__) || defined(__sun__) || defined(__FreeBSD__)
00081     // This is supposed to be safe on Darwin (despite the man page)
00082     // and FreeBSD, as it's implemented with thread local storage.
00083     struct hostent *he = gethostbyname(host);
00084 
00085     if (he == 0) {
00086       String errmsg = (String)"gethostbyname(\"" + host + "\")";
00087       herror(errmsg.c_str());
00088       HT_THROW(Error::BAD_DOMAIN_NAME, errmsg);
00089       return false;
00090     }
00091 #else
00092 #error TODO
00093 #endif
00094     memcpy(&addr->sin_addr.s_addr, he->h_addr_list[0], sizeof(uint32_t));
00095     if (addr->sin_addr.s_addr == 0) {
00096       uint8_t *ip = (uint8_t *)&addr->sin_addr.s_addr;
00097       ip[0] = 127;
00098       ip[3] = 1;
00099     }
00100   }
00101   addr->sin_family = AF_INET;
00102   addr->sin_port = htons(port);
00103   return true;
00104 }
00105 
00106 bool
00107 InetAddr::parse_ipv4(const char *ipin, uint16_t port, sockaddr_in &addr,
00108                      int base) {
00109   uint8_t *ip = (uint8_t *)&addr.sin_addr.s_addr;
00110   const char *ipstr = ipin, *end = ipin + strlen(ipin);
00111   char *last;
00112   int64_t n = strtoll(ipstr, &last, base);
00113 
00114   addr.sin_family = AF_INET;
00115   addr.sin_port = htons(port);
00116 
00117   if (last == end && n > 0 && n < UINT32_MAX) {
00118     addr.sin_addr.s_addr = htonl(n);
00119     return true;
00120   }
00121   *ip++ = n;
00122 
00123   if (last > end || *last != '.')
00124     return false;
00125 
00126   ipstr = last + 1;
00127   *ip++ = strtol(ipstr, &last, base);
00128 
00129   if (last >= end || *last != '.')
00130     return false;
00131 
00132   ipstr = last + 1;
00133   *ip++ = strtol(ipstr, &last, base);
00134 
00135   if (last >= end || *last != '.')
00136     return false;
00137 
00138   ipstr = last + 1;
00139   *ip++ = strtol(ipstr, &last, base);
00140 
00141   if (last != end)
00142     return false;
00143 
00144   if (addr.sin_addr.s_addr == 0) {
00145     uint8_t *ip = (uint8_t *)&addr.sin_addr.s_addr;
00146     ip[0] = 127;
00147     ip[3] = 1;
00148   }
00149 
00150   return true;
00151 }
00152 
00153 Endpoint InetAddr::parse_endpoint(const char *endpoint, int default_port) {
00154   const char *colon = strchr(endpoint, ':');
00155 
00156   if (colon) {
00157     String host = String(endpoint, colon - endpoint);
00158     return Endpoint(host, atoi(colon + 1));
00159   }
00160   return Endpoint(endpoint, default_port);
00161 }
00162 
00163 bool InetAddr::initialize(sockaddr_in *addr, const char *addr_str) {
00164   Endpoint e = parse_endpoint(addr_str);
00165 
00166   if (e.port)
00167     return initialize(addr, e.host.c_str(), e.port);
00168 
00169   return initialize(addr, "localhost", atoi(addr_str));
00170 }
00171 
00172 bool InetAddr::initialize(sockaddr_in *addr, uint32_t haddr, uint16_t port) {
00173   memset(addr, 0 , sizeof(sockaddr_in));
00174   addr->sin_family = AF_INET;
00175   addr->sin_addr.s_addr = htonl(haddr);
00176   addr->sin_port = htons(port);
00177   return true;
00178 }
00179 
00180 size_t InetAddr::encoded_length() const {
00181   return 8;
00182 }
00183 
00184 void InetAddr::encode(uint8_t **bufp) const {
00185   *(*bufp)++ = sizeof(sockaddr_in);
00186   *(*bufp)++ = sin_family;
00187   Serialization::encode_i16(bufp, sin_port);
00188   Serialization::encode_i32(bufp, sin_addr.s_addr);
00189 }
00190 
00191 void InetAddr::decode(const uint8_t **bufp, size_t *remainp) {
00192   Serialization::decode_i8(bufp, remainp);
00193   sin_family = Serialization::decode_i8(bufp, remainp);
00194   sin_port = Serialization::decode_i16(bufp, remainp);
00195   sin_addr.s_addr = Serialization::decode_i32(bufp, remainp);
00196 }
00197 
00198 
00199 String InetAddr::format(const sockaddr_in &addr, int sep) {
00200   // inet_ntoa is not thread safe on many platforms and deprecated
00201   const uint8_t *ip = (uint8_t *)&addr.sin_addr.s_addr;
00202   return Hypertable::format("%d.%d.%d.%d%c%d", (int)ip[0], (int)ip[1],
00203       (int)ip[2],(int)ip[3], sep, (int)ntohs(addr.sin_port));
00204 }
00205 
00206 String InetAddr::format_ipaddress(const sockaddr_in &addr) {
00207   // inet_ntoa is not thread safe on many platforms and deprecated
00208   const uint8_t *ip = (uint8_t *)&addr.sin_addr.s_addr;
00209   return Hypertable::format("%d.%d.%d.%d", (int)ip[0], (int)ip[1],
00210                             (int)ip[2],(int)ip[3]);
00211 }
00212 
00213 String InetAddr::hex(const sockaddr_in &addr, int sep) {
00214   return Hypertable::format("%x%c%x", ntohl(addr.sin_addr.s_addr), sep,
00215                             ntohs(addr.sin_port));
00216 }
00217 
00218 const char *InetAddr::string_format(String &addr_str, const sockaddr_in &addr) {
00219   addr_str = InetAddr::format(addr);
00220   return addr_str.c_str();
00221 }
00222 
00223 std::ostream &operator<<(std::ostream &out, const Endpoint &e) {
00224   out << e.host <<':'<< e.port;
00225   return out;
00226 }
00227 
00228 std::ostream &operator<<(std::ostream &out, const sockaddr_in &a) {
00229   out << InetAddr::format(a);
00230   return out;
00231 }
00232 
00233 } // namespace Hypertable