/** \file Utility.cpp ** \date 2004-02-13 ** \author grymse@alhem.net **/ /* Copyright (C) 2004-2007 Anders Hedstrom This library is made available under the terms of the GNU GPL. If you would like to use this library in a closed-source application, a separate license agreement is available. For information about the closed-source license agreement for the C++ sockets library, please visit http://www.alhem.net/Sockets/license.html and/or email license@alhem.net. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Utility.h" #include "Parse.h" #include "Ipv4Address.h" #include "Ipv6Address.h" #include "RandomNumber.h" #include "Base64.h" #include #ifdef _WIN32 #include #else #include #include #endif #include #include #ifdef SOCKETS_NAMESPACE namespace SOCKETS_NAMESPACE { #endif // statics std::string Utility::m_host; bool Utility::m_local_resolved = false; ipaddr_t Utility::m_ip = 0; std::string Utility::m_addr; #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 struct in6_addr Utility::m_local_ip6; std::string Utility::m_local_addr6; #endif #endif std::string Utility::base64(const std::string& str_in) { std::string str; Base64 m_b; m_b.encode(str_in, str, false); // , false == do not add cr/lf return str; } std::string Utility::base64d(const std::string& str_in) { std::string str; Base64 m_b; m_b.decode(str_in, str); return str; } std::string Utility::l2string(long l) { std::string str; char tmp[100]; sprintf(tmp,"%ld",l); str = tmp; return str; } std::string Utility::bigint2string(uint64_t l) { std::string str; uint64_t tmp = l; while (tmp) { uint64_t a = tmp % 10; str = (char)(a + 48) + str; tmp /= 10; } if (!str.size()) { str = "0"; } return str; } uint64_t Utility::atoi64(const std::string& str) { uint64_t l = 0; for (size_t i = 0; i < str.size(); i++) { l = l * 10 + str[i] - 48; } return l; } unsigned int Utility::hex2unsigned(const std::string& str) { unsigned int r = 0; for (size_t i = 0; i < str.size(); i++) { r = r * 16 + str[i] - 48 - ((str[i] >= 'A') ? 7 : 0) - ((str[i] >= 'a') ? 32 : 0); } return r; } /* * Encode string per RFC1738 URL encoding rules * tnx rstaveley */ std::string Utility::rfc1738_encode(const std::string& src) { static char hex[] = "0123456789ABCDEF"; std::string dst; for (size_t i = 0; i < src.size(); i++) { if (isalnum(src[i])) { dst += src[i]; } else if (src[i] == ' ') { dst += '+'; } else { unsigned char c = static_cast(src[i]); dst += '%'; dst += hex[c / 16]; dst += hex[c % 16]; } } return dst; } // rfc1738_encode /* * Decode string per RFC1738 URL encoding rules * tnx rstaveley */ std::string Utility::rfc1738_decode(const std::string& src) { std::string dst; for (size_t i = 0; i < src.size(); i++) { if (src[i] == '%' && isxdigit(src[i + 1]) && isxdigit(src[i + 2])) { char c1 = src[++i]; char c2 = src[++i]; c1 = c1 - 48 - ((c1 >= 'A') ? 7 : 0) - ((c1 >= 'a') ? 32 : 0); c2 = c2 - 48 - ((c2 >= 'A') ? 7 : 0) - ((c2 >= 'a') ? 32 : 0); dst += (char)(c1 * 16 + c2); } else if (src[i] == '+') { dst += ' '; } else { dst += src[i]; } } return dst; } // rfc1738_decode bool Utility::isipv4(const std::string& str) { int dots = 0; // %! ignore :port? for (size_t i = 0; i < str.size(); i++) { if (str[i] == '.') dots++; else if (!isdigit(str[i])) return false; } if (dots != 3) return false; return true; } bool Utility::isipv6(const std::string& str) { size_t qc = 0; size_t qd = 0; for (size_t i = 0; i < str.size(); i++) { qc += (str[i] == ':') ? 1 : 0; qd += (str[i] == '.') ? 1 : 0; } if (qc > 7) { return false; } if (qd && qd != 3) { return false; } Parse pa(str,":."); std::string tmp = pa.getword(); while (tmp.size()) { if (tmp.size() > 4) { return false; } for (size_t i = 0; i < tmp.size(); i++) { if (tmp[i] < '0' || (tmp[i] > '9' && tmp[i] < 'A') || (tmp[i] > 'F' && tmp[i] < 'a') || tmp[i] > 'f') { return false; } } // tmp = pa.getword(); } return true; } bool Utility::u2ip(const std::string& str, ipaddr_t& l) { struct sockaddr_in sa; bool r = Utility::u2ip(str, sa); memcpy(&l, &sa.sin_addr, sizeof(l)); return r; } #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 bool Utility::u2ip(const std::string& str, struct in6_addr& l) { struct sockaddr_in6 sa; bool r = Utility::u2ip(str, sa); l = sa.sin6_addr; return r; } #endif #endif void Utility::l2ip(const ipaddr_t ip, std::string& str) { struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; memcpy(&sa.sin_addr, &ip, sizeof(sa.sin_addr)); Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); } void Utility::l2ip(const in_addr& ip, std::string& str) { struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr = ip; Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); } #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 void Utility::l2ip(const struct in6_addr& ip, std::string& str,bool mixed) { char slask[100]; // l2ip temporary *slask = 0; unsigned int prev = 0; bool skipped = false; bool ok_to_skip = true; if (mixed) { unsigned short x; unsigned short addr16[8]; memcpy(addr16, &ip, sizeof(addr16)); for (size_t i = 0; i < 6; i++) { x = ntohs(addr16[i]); if (*slask && (x || !ok_to_skip || prev)) strcat(slask,":"); if (x || !ok_to_skip) { sprintf(slask + strlen(slask),"%x", x); if (x && skipped) ok_to_skip = false; } else { skipped = true; } prev = x; } x = ntohs(addr16[6]); sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255); x = ntohs(addr16[7]); sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255); } else { struct sockaddr_in6 sa; memset(&sa, 0, sizeof(sa)); sa.sin6_family = AF_INET6; sa.sin6_addr = ip; Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); return; } str = slask; } int Utility::in6_addr_compare(in6_addr a,in6_addr b) { for (size_t i = 0; i < 16; i++) { if (a.s6_addr[i] < b.s6_addr[i]) return -1; if (a.s6_addr[i] > b.s6_addr[i]) return 1; } return 0; } #endif #endif void Utility::ResolveLocal() { char h[256]; // get local hostname and translate into ip-address *h = 0; gethostname(h,255); { if (Utility::u2ip(h, m_ip)) { Utility::l2ip(m_ip, m_addr); } } #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 memset(&m_local_ip6, 0, sizeof(m_local_ip6)); { if (Utility::u2ip(h, m_local_ip6)) { Utility::l2ip(m_local_ip6, m_local_addr6); } } #endif #endif m_host = h; m_local_resolved = true; } const std::string& Utility::GetLocalHostname() { if (!m_local_resolved) { ResolveLocal(); } return m_host; } ipaddr_t Utility::GetLocalIP() { if (!m_local_resolved) { ResolveLocal(); } return m_ip; } const std::string& Utility::GetLocalAddress() { if (!m_local_resolved) { ResolveLocal(); } return m_addr; } #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 const struct in6_addr& Utility::GetLocalIP6() { if (!m_local_resolved) { ResolveLocal(); } return m_local_ip6; } const std::string& Utility::GetLocalAddress6() { if (!m_local_resolved) { ResolveLocal(); } return m_local_addr6; } #endif #endif void Utility::SetEnv(const std::string& var,const std::string& value) { #if (defined(SOLARIS8) || defined(SOLARIS)) { static std::map vmap; if (vmap.find(var) != vmap.end()) { delete[] vmap[var]; } vmap[var] = new char[var.size() + 1 + value.size() + 1]; sprintf(vmap[var], "%s=%s", var.c_str(), value.c_str()); putenv( vmap[var] ); } #elif defined _WIN32 { std::string slask = var + "=" + value; _putenv( (char *)slask.c_str()); } #else setenv(var.c_str(), value.c_str(), 1); #endif } std::string Utility::Sa2String(struct sockaddr *sa) { #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 if (sa -> sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; std::string tmp; Utility::l2ip(sa6 -> sin6_addr, tmp); return tmp + ":" + Utility::l2string(ntohs(sa6 -> sin6_port)); } #endif #endif if (sa -> sa_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; ipaddr_t a; memcpy(&a, &sa4 -> sin_addr, 4); std::string tmp; Utility::l2ip(a, tmp); return tmp + ":" + Utility::l2string(ntohs(sa4 -> sin_port)); } return ""; } void Utility::GetTime(struct timeval *p) { #ifdef _WIN32 FILETIME ft; // Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). GetSystemTimeAsFileTime(&ft); uint64_t tt; memcpy(&tt, &ft, sizeof(tt)); tt /= 10; // make it usecs p->tv_sec = (long)tt / 1000000; p->tv_usec = (long)tt % 1000000; #else gettimeofday(p, NULL); #endif } std::auto_ptr Utility::CreateAddress(struct sockaddr *sa,socklen_t sa_len) { switch (sa -> sa_family) { case AF_INET: if (sa_len == sizeof(struct sockaddr_in)) { struct sockaddr_in *p = (struct sockaddr_in *)sa; return std::auto_ptr(new Ipv4Address(*p)); } break; #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 case AF_INET6: if (sa_len == sizeof(struct sockaddr_in6)) { struct sockaddr_in6 *p = (struct sockaddr_in6 *)sa; return std::auto_ptr(new Ipv6Address(*p)); } break; #endif #endif } return std::auto_ptr(NULL); } bool Utility::u2ip(const std::string& host, struct sockaddr_in& sa, int ai_flags) { memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; #ifdef NO_GETADDRINFO if ((ai_flags & AI_NUMERICHOST) != 0 || isipv4(host)) { Parse pa((char *)host.c_str(), "."); union { struct { unsigned char b1; unsigned char b2; unsigned char b3; unsigned char b4; } a; ipaddr_t l; } u; u.a.b1 = static_cast(pa.getvalue()); u.a.b2 = static_cast(pa.getvalue()); u.a.b3 = static_cast(pa.getvalue()); u.a.b4 = static_cast(pa.getvalue()); memcpy(&sa.sin_addr, &u.l, sizeof(sa.sin_addr)); return true; } #ifndef LINUX struct hostent *he = gethostbyname( host.c_str() ); if (!he) { return false; } memcpy(&sa.sin_addr, he -> h_addr, sizeof(sa.sin_addr)); #else struct hostent he; struct hostent *result = NULL; int myerrno = 0; char buf[2000]; int n = gethostbyname_r(host.c_str(), &he, buf, sizeof(buf), &result, &myerrno); if (n || !result) { return false; } if (he.h_addr_list && he.h_addr_list[0]) memcpy(&sa.sin_addr, he.h_addr, 4); else return false; #endif return true; #else struct addrinfo hints; memset(&hints, 0, sizeof(hints)); // AI_NUMERICHOST // AI_CANONNAME // AI_PASSIVE - server // AI_ADDRCONFIG // AI_V4MAPPED // AI_ALL // AI_NUMERICSERV hints.ai_flags = ai_flags; hints.ai_family = AF_INET; hints.ai_socktype = 0; hints.ai_protocol = 0; struct addrinfo *res; if (Utility::isipv4(host)) hints.ai_flags |= AI_NUMERICHOST; int n = getaddrinfo(host.c_str(), NULL, &hints, &res); if (!n) { static RandomNumber prng( true ); std::vector vec; struct addrinfo *ai = res; while (ai) { if (ai -> ai_addrlen == sizeof(sa)) vec.push_back( ai ); prng.next(); // ai = ai -> ai_next; } if (!vec.size()) return false; ai = vec[prng.next() % vec.size()]; { memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen); } freeaddrinfo(res); return true; } std::string error = "Error: "; #ifndef __CYGWIN__ error += gai_strerror(n); #endif return false; #endif // NO_GETADDRINFO } #ifdef ENABLE_IPV6 #ifdef IPPROTO_IPV6 bool Utility::u2ip(const std::string& host, struct sockaddr_in6& sa, int ai_flags) { memset(&sa, 0, sizeof(sa)); sa.sin6_family = AF_INET6; #ifdef NO_GETADDRINFO if ((ai_flags & AI_NUMERICHOST) != 0 || isipv6(host)) { std::list vec; size_t x = 0; for (size_t i = 0; i <= host.size(); i++) { if (i == host.size() || host[i] == ':') { std::string s = host.substr(x, i - x); // if (strstr(s.c_str(),".")) // x.x.x.x { Parse pa(s,"."); char slask[100]; // u2ip temporary hex2string conversion unsigned long b0 = static_cast(pa.getvalue()); unsigned long b1 = static_cast(pa.getvalue()); unsigned long b2 = static_cast(pa.getvalue()); unsigned long b3 = static_cast(pa.getvalue()); sprintf(slask,"%lx",b0 * 256 + b1); vec.push_back(slask); sprintf(slask,"%lx",b2 * 256 + b3); vec.push_back(slask); } else { vec.push_back(s); } // x = i + 1; } } size_t sz = vec.size(); // number of byte pairs size_t i = 0; // index in in6_addr.in6_u.u6_addr16[] ( 0 .. 7 ) unsigned short addr16[8]; for (std::list::iterator it = vec.begin(); it != vec.end(); it++) { std::string bytepair = *it; if (bytepair.size()) { addr16[i++] = htons(Utility::hex2unsigned(bytepair)); } else { addr16[i++] = 0; while (sz++ < 8) { addr16[i++] = 0; } } } memcpy(&sa.sin6_addr, addr16, sizeof(addr16)); return true; } #ifdef SOLARIS int errnum = 0; struct hostent *he = getipnodebyname( host.c_str(), AF_INET6, 0, &errnum ); #else struct hostent *he = gethostbyname2( host.c_str(), AF_INET6 ); #endif if (!he) { return false; } memcpy(&sa.sin6_addr,he -> h_addr_list[0],he -> h_length); #ifdef SOLARIS free(he); #endif return true; #else struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_flags = ai_flags; hints.ai_family = AF_INET6; hints.ai_socktype = 0; hints.ai_protocol = 0; struct addrinfo *res; if (Utility::isipv6(host)) hints.ai_flags |= AI_NUMERICHOST; int n = getaddrinfo(host.c_str(), NULL, &hints, &res); if (!n) { static RandomNumber prng( true ); std::vector vec; struct addrinfo *ai = res; while (ai) { if (ai -> ai_addrlen == sizeof(sa)) vec.push_back( ai ); prng.next(); // ai = ai -> ai_next; } if (!vec.size()) return false; ai = vec[prng.next() % vec.size()]; { memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen); } freeaddrinfo(res); return true; } std::string error = "Error: "; #ifndef __CYGWIN__ error += gai_strerror(n); #endif return false; #endif // NO_GETADDRINFO } #endif // IPPROTO_IPV6 #endif // ENABLE_IPV6 bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, int flags) { std::string service; return Utility::reverse(sa, sa_len, hostname, service, flags); } bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, std::string& service, int flags) { hostname = ""; service = ""; #ifdef NO_GETADDRINFO switch (sa -> sa_family) { case AF_INET: if (flags & NI_NUMERICHOST) { union { struct { unsigned char b1; unsigned char b2; unsigned char b3; unsigned char b4; } a; ipaddr_t l; } u; struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; memcpy(&u.l, &sa_in -> sin_addr, sizeof(u.l)); char tmp[100]; sprintf(tmp, "%u.%u.%u.%u", u.a.b1, u.a.b2, u.a.b3, u.a.b4); hostname = tmp; return true; } else { struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin_addr, sizeof(sa_in -> sin_addr), AF_INET); if (h) { hostname = h -> h_name; return true; } } break; #ifdef ENABLE_IPV6 case AF_INET6: if (flags & NI_NUMERICHOST) { char slask[100]; // l2ip temporary *slask = 0; unsigned int prev = 0; bool skipped = false; bool ok_to_skip = true; { unsigned short addr16[8]; struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; memcpy(addr16, &sa_in6 -> sin6_addr, sizeof(addr16)); for (size_t i = 0; i < 8; i++) { unsigned short x = ntohs(addr16[i]); if (*slask && (x || !ok_to_skip || prev)) strcat(slask,":"); if (x || !ok_to_skip) { sprintf(slask + strlen(slask),"%x", x); if (x && skipped) ok_to_skip = false; } else { skipped = true; } prev = x; } } if (!*slask) strcpy(slask, "::"); hostname = slask; return true; } else { // %! TODO: ipv6 reverse lookup struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)sa; struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin6_addr, sizeof(sa_in -> sin6_addr), AF_INET6); if (h) { hostname = h -> h_name; return true; } } break; #endif } return false; #else char host[NI_MAXHOST]; char serv[NI_MAXSERV]; // NI_NOFQDN // NI_NUMERICHOST // NI_NAMEREQD // NI_NUMERICSERV // NI_DGRAM int n = getnameinfo(sa, sa_len, host, sizeof(host), serv, sizeof(serv), flags); if (n) { // EAI_AGAIN // EAI_BADFLAGS // EAI_FAIL // EAI_FAMILY // EAI_MEMORY // EAI_NONAME // EAI_OVERFLOW // EAI_SYSTEM return false; } hostname = host; service = serv; return true; #endif // NO_GETADDRINFO } bool Utility::u2service(const std::string& name, int& service, int ai_flags) { #ifdef NO_GETADDRINFO // %! return false; #else struct addrinfo hints; service = 0; memset(&hints, 0, sizeof(hints)); // AI_NUMERICHOST // AI_CANONNAME // AI_PASSIVE - server // AI_ADDRCONFIG // AI_V4MAPPED // AI_ALL // AI_NUMERICSERV hints.ai_flags = ai_flags; hints.ai_family = AF_UNSPEC; hints.ai_socktype = 0; hints.ai_protocol = 0; struct addrinfo *res; int n = getaddrinfo(NULL, name.c_str(), &hints, &res); if (!n) { service = res -> ai_protocol; freeaddrinfo(res); return true; } return false; #endif // NO_GETADDRINFO } unsigned long Utility::ThreadID() { #ifdef _WIN32 return GetCurrentThreadId(); #else return (unsigned long)pthread_self(); #endif } std::string Utility::ToLower(const std::string& str) { std::string r; for (size_t i = 0; i < str.size(); i++) { if (str[i] >= 'A' && str[i] <= 'Z') r += str[i] | 32; else r += str[i]; } return r; } std::string Utility::ToUpper(const std::string& str) { std::string r; for (size_t i = 0; i < str.size(); i++) { if (str[i] >= 'a' && str[i] <= 'z') r += (char)(str[i] - 32); else r += str[i]; } return r; } std::string Utility::ToString(double d) { char tmp[100]; sprintf(tmp, "%f", d); return tmp; } #ifdef SOCKETS_NAMESPACE } #endif