summaryrefslogtreecommitdiff
path: root/service/ClusterGeoIpService.hpp
blob: ff2fcdb8acbebb08a9946c87c5d542aaf47bee0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * ZeroTier One - Network Virtualization Everywhere
 * Copyright (C) 2011-2016  ZeroTier, Inc.  https://www.zerotier.com/
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#ifndef ZT_CLUSTERGEOIPSERVICE_HPP
#define ZT_CLUSTERGEOIPSERVICE_HPP

#ifdef ZT_ENABLE_CLUSTER

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <vector>
#include <string>
#include <algorithm>

#include "../node/Constants.hpp"
#include "../node/Mutex.hpp"
#include "../node/NonCopyable.hpp"
#include "../node/InetAddress.hpp"

namespace ZeroTier {

/**
 * Loads a GeoIP CSV into memory for fast lookup, reloading as needed
 *
 * This was designed around the CSV from https://db-ip.com but can be used
 * with any similar GeoIP CSV database that is presented in the form of an
 * IP range and lat/long coordinates.
 *
 * It loads the whole database into memory, which can be kind of large. If
 * the CSV file changes, the changes are loaded automatically.
 */
class ClusterGeoIpService : NonCopyable
{
public:
	ClusterGeoIpService();
	~ClusterGeoIpService();

	/**
	 * Load or reload CSV file
	 *
	 * CSV column indexes start at zero. CSVs can be quoted with single or
	 * double quotes. Whitespace before or after commas is ignored. Backslash
	 * may be used for escaping whitespace as well.
	 *
	 * @param pathToCsv Path to (uncompressed) CSV file
	 * @param ipStartColumn Column with IP range start
	 * @param ipEndColumn Column with IP range end (inclusive)
	 * @param latitudeColumn Column with latitude
	 * @param longitudeColumn Column with longitude
	 * @return Number of valid records loaded or -1 on error (invalid file, not found, etc.)
	 */
	inline long load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
	{
		Mutex::Lock _l(_lock);
		return _load(pathToCsv,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
	}

	/**
	 * Attempt to locate an IP
	 *
	 * This returns true if x, y, and z are set. If the return value is false
	 * the values of x, y, and z are undefined.
	 *
	 * @param ip IPv4 or IPv6 address
	 * @param x Reference to variable to receive X
	 * @param y Reference to variable to receive Y
	 * @param z Reference to variable to receive Z
	 * @return True if coordinates were set
	 */
	bool locate(const InetAddress &ip,int &x,int &y,int &z);

	/**
	 * @return True if IP database/service is available for queries (otherwise locate() will always be false)
	 */
	inline bool available() const
	{
		Mutex::Lock _l(_lock);
		return ((_v4db.size() + _v6db.size()) > 0);
	}

private:
	struct _V4E
	{
		uint32_t start;
		uint32_t end;
		float lat,lon;
		int16_t x,y,z;

		inline bool operator<(const _V4E &e) const { return (start < e.start); }
	};

	struct _V6E
	{
		uint8_t start[16];
		uint8_t end[16];
		float lat,lon;
		int16_t x,y,z;

		inline bool operator<(const _V6E &e) const { return (memcmp(start,e.start,16) < 0); }
	};

	static void _parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);
	long _load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);

	std::string _pathToCsv;
	int _ipStartColumn;
	int _ipEndColumn;
	int _latitudeColumn;
	int _longitudeColumn;

	uint64_t _lastFileCheckTime;
	uint64_t _csvModificationTime;
	int64_t _csvFileSize;

	std::vector<_V4E> _v4db;
	std::vector<_V6E> _v6db;

	Mutex _lock;
};

} // namespace ZeroTier

#endif // ZT_ENABLE_CLUSTER

#endif