ClusterGeoIpService.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * ZeroTier One - Network Virtualization Everywhere
  3. * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. * --
  19. *
  20. * You can be released from the requirements of the license by purchasing
  21. * a commercial license. Buying such a license is mandatory as soon as you
  22. * develop commercial closed-source software that incorporates or links
  23. * directly against ZeroTier software without disclosing the source code
  24. * of your own application.
  25. */
  26. #ifdef ZT_ENABLE_CLUSTER
  27. #include <math.h>
  28. #include <cmath>
  29. #include "ClusterGeoIpService.hpp"
  30. #include "../node/Utils.hpp"
  31. #include "../osdep/OSUtils.hpp"
  32. #define ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY 10000
  33. namespace ZeroTier {
  34. ClusterGeoIpService::ClusterGeoIpService() :
  35. _pathToCsv(),
  36. _ipStartColumn(-1),
  37. _ipEndColumn(-1),
  38. _latitudeColumn(-1),
  39. _longitudeColumn(-1),
  40. _lastFileCheckTime(0),
  41. _csvModificationTime(0),
  42. _csvFileSize(0)
  43. {
  44. }
  45. ClusterGeoIpService::~ClusterGeoIpService()
  46. {
  47. }
  48. bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z)
  49. {
  50. Mutex::Lock _l(_lock);
  51. if ((_pathToCsv.length() > 0)&&((OSUtils::now() - _lastFileCheckTime) > ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY)) {
  52. _lastFileCheckTime = OSUtils::now();
  53. if ((_csvFileSize != OSUtils::getFileSize(_pathToCsv.c_str()))||(_csvModificationTime != OSUtils::getLastModified(_pathToCsv.c_str())))
  54. _load(_pathToCsv.c_str(),_ipStartColumn,_ipEndColumn,_latitudeColumn,_longitudeColumn);
  55. }
  56. /* We search by looking up the upper bound of the sorted vXdb vectors
  57. * and then iterating down for a matching IP range. We stop when we hit
  58. * the beginning or an entry whose start and end are before the IP we
  59. * are searching. */
  60. if ((ip.ss_family == AF_INET)&&(_v4db.size() > 0)) {
  61. _V4E key;
  62. key.start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr));
  63. std::vector<_V4E>::const_iterator i(std::upper_bound(_v4db.begin(),_v4db.end(),key));
  64. while (i != _v4db.begin()) {
  65. --i;
  66. if ((key.start >= i->start)&&(key.start <= i->end)) {
  67. x = i->x;
  68. y = i->y;
  69. z = i->z;
  70. //printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
  71. return true;
  72. } else if ((key.start > i->start)&&(key.start > i->end))
  73. break;
  74. }
  75. } else if ((ip.ss_family == AF_INET6)&&(_v6db.size() > 0)) {
  76. _V6E key;
  77. memcpy(key.start,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
  78. std::vector<_V6E>::const_iterator i(std::upper_bound(_v6db.begin(),_v6db.end(),key));
  79. while (i != _v6db.begin()) {
  80. --i;
  81. const int s_vs_s = memcmp(key.start,i->start,16);
  82. const int s_vs_e = memcmp(key.start,i->end,16);
  83. if ((s_vs_s >= 0)&&(s_vs_e <= 0)) {
  84. x = i->x;
  85. y = i->y;
  86. z = i->z;
  87. //printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
  88. return true;
  89. } else if ((s_vs_s > 0)&&(s_vs_e > 0))
  90. break;
  91. }
  92. }
  93. return false;
  94. }
  95. void ClusterGeoIpService::_parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
  96. {
  97. std::vector<std::string> ls(OSUtils::split(line,",\t","\\","\"'"));
  98. if ( ((ipStartColumn >= 0)&&(ipStartColumn < (int)ls.size()))&&
  99. ((ipEndColumn >= 0)&&(ipEndColumn < (int)ls.size()))&&
  100. ((latitudeColumn >= 0)&&(latitudeColumn < (int)ls.size()))&&
  101. ((longitudeColumn >= 0)&&(longitudeColumn < (int)ls.size())) ) {
  102. InetAddress ipStart(ls[ipStartColumn].c_str(),0);
  103. InetAddress ipEnd(ls[ipEndColumn].c_str(),0);
  104. const double lat = strtod(ls[latitudeColumn].c_str(),(char **)0);
  105. const double lon = strtod(ls[longitudeColumn].c_str(),(char **)0);
  106. if ((ipStart.ss_family == ipEnd.ss_family)&&(ipStart)&&(ipEnd)&&(std::isfinite(lat))&&(std::isfinite(lon))) {
  107. const double latRadians = lat * 0.01745329251994; // PI / 180
  108. const double lonRadians = lon * 0.01745329251994; // PI / 180
  109. const double cosLat = cos(latRadians);
  110. const int x = (int)round((-6371.0) * cosLat * cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers
  111. const int y = (int)round(6371.0 * sin(latRadians));
  112. const int z = (int)round(6371.0 * cosLat * sin(lonRadians));
  113. if (ipStart.ss_family == AF_INET) {
  114. v4db.push_back(_V4E());
  115. v4db.back().start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipStart)->sin_addr.s_addr));
  116. v4db.back().end = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipEnd)->sin_addr.s_addr));
  117. v4db.back().lat = (float)lat;
  118. v4db.back().lon = (float)lon;
  119. v4db.back().x = x;
  120. v4db.back().y = y;
  121. v4db.back().z = z;
  122. //printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
  123. } else if (ipStart.ss_family == AF_INET6) {
  124. v6db.push_back(_V6E());
  125. memcpy(v6db.back().start,reinterpret_cast<const struct sockaddr_in6 *>(&ipStart)->sin6_addr.s6_addr,16);
  126. memcpy(v6db.back().end,reinterpret_cast<const struct sockaddr_in6 *>(&ipEnd)->sin6_addr.s6_addr,16);
  127. v6db.back().lat = (float)lat;
  128. v6db.back().lon = (float)lon;
  129. v6db.back().x = x;
  130. v6db.back().y = y;
  131. v6db.back().z = z;
  132. //printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
  133. }
  134. }
  135. }
  136. }
  137. long ClusterGeoIpService::_load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
  138. {
  139. // assumes _lock is locked
  140. FILE *f = fopen(pathToCsv,"rb");
  141. if (!f)
  142. return -1;
  143. std::vector<_V4E> v4db;
  144. std::vector<_V6E> v6db;
  145. v4db.reserve(16777216);
  146. v6db.reserve(16777216);
  147. char buf[4096];
  148. char linebuf[1024];
  149. unsigned int lineptr = 0;
  150. for(;;) {
  151. int n = (int)fread(buf,1,sizeof(buf),f);
  152. if (n <= 0)
  153. break;
  154. for(int i=0;i<n;++i) {
  155. if ((buf[i] == '\r')||(buf[i] == '\n')||(buf[i] == (char)0)) {
  156. if (lineptr) {
  157. linebuf[lineptr] = (char)0;
  158. _parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
  159. }
  160. lineptr = 0;
  161. } else if (lineptr < (unsigned int)sizeof(linebuf))
  162. linebuf[lineptr++] = buf[i];
  163. }
  164. }
  165. if (lineptr) {
  166. linebuf[lineptr] = (char)0;
  167. _parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
  168. }
  169. fclose(f);
  170. if ((v4db.size() > 0)||(v6db.size() > 0)) {
  171. std::sort(v4db.begin(),v4db.end());
  172. std::sort(v6db.begin(),v6db.end());
  173. _pathToCsv = pathToCsv;
  174. _ipStartColumn = ipStartColumn;
  175. _ipEndColumn = ipEndColumn;
  176. _latitudeColumn = latitudeColumn;
  177. _longitudeColumn = longitudeColumn;
  178. _lastFileCheckTime = OSUtils::now();
  179. _csvModificationTime = OSUtils::getLastModified(pathToCsv);
  180. _csvFileSize = OSUtils::getFileSize(pathToCsv);
  181. _v4db.swap(v4db);
  182. _v6db.swap(v6db);
  183. return (long)(_v4db.size() + _v6db.size());
  184. } else {
  185. return 0;
  186. }
  187. }
  188. } // namespace ZeroTier
  189. #endif // ZT_ENABLE_CLUSTER
  190. /*
  191. int main(int argc,char **argv)
  192. {
  193. char buf[1024];
  194. ZeroTier::ClusterGeoIpService gip;
  195. printf("loading...\n");
  196. gip.load("/Users/api/Code/ZeroTier/Infrastructure/root-servers/zerotier-one/cluster-geoip.csv",0,1,5,6);
  197. printf("... done!\n"); fflush(stdout);
  198. while (gets(buf)) { // unsafe, testing only
  199. ZeroTier::InetAddress addr(buf,0);
  200. printf("looking up: %s\n",addr.toString().c_str()); fflush(stdout);
  201. int x = 0,y = 0,z = 0;
  202. if (gip.locate(addr,x,y,z)) {
  203. //printf("%s: %d,%d,%d\n",addr.toString().c_str(),x,y,z); fflush(stdout);
  204. } else {
  205. printf("%s: not found!\n",addr.toString().c_str()); fflush(stdout);
  206. }
  207. }
  208. return 0;
  209. }
  210. */