2 address_cache.c -- Manage cache of recently seen addresses
3 Copyright (C) 2018 Guus Sliepen <guus@tinc-vpn.org>
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 2 of the License, or
8 (at your option) any later version.
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.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "address_cache.h"
28 static const unsigned int NOT_CACHED = -1;
30 // Find edges pointing to this node, and use them to build a list of unique, known addresses.
31 static struct addrinfo *get_known_addresses(node_t *n) {
32 struct addrinfo *ai = NULL;
33 struct addrinfo *oai = NULL;
35 for splay_each(edge_t, e, &n->edge_tree) {
42 for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
43 if(!sockaddrcmp(&e->reverse->address, (sockaddr_t *)aip->ai_addr)) {
54 ai = xzalloc(sizeof(*ai));
55 ai->ai_family = e->reverse->address.sa.sa_family;
56 ai->ai_socktype = SOCK_STREAM;
57 ai->ai_protocol = IPPROTO_TCP;
58 ai->ai_addrlen = SALEN(e->reverse->address.sa);
59 ai->ai_addr = xmalloc(ai->ai_addrlen);
60 memcpy(ai->ai_addr, &e->reverse->address, ai->ai_addrlen);
67 static void free_known_addresses(struct addrinfo *ai) {
68 for(struct addrinfo *aip = ai, *next; aip; aip = next) {
75 static unsigned int find_cached(address_cache_t *cache, const sockaddr_t *sa) {
76 for(unsigned int i = 0; i < cache->data.used; i++)
77 if(!sockaddrcmp(&cache->data.address[i], sa)) {
84 void add_recent_address(address_cache_t *cache, const sockaddr_t *sa) {
85 // Check if it's already cached
86 unsigned int pos = find_cached(cache, sa);
88 // It's in the first spot, so nothing to do
93 // Shift everything, move/add the address to the first slot
94 if(pos == NOT_CACHED) {
95 if(cache->data.used < MAX_CACHED_ADDRESSES) {
99 pos = cache->data.used - 1;
102 memmove(&cache->data.address[1], &cache->data.address[0], pos * sizeof(cache->data.address[0]));
104 cache->data.address[0] = *sa;
107 char fname[PATH_MAX];
108 snprintf(fname, sizeof(fname), "%s" SLASH "cache" SLASH "%s", confbase, cache->node->name);
109 FILE *fp = fopen(fname, "wb");
112 fwrite(&cache->data, sizeof(cache->data), 1, fp);
117 const sockaddr_t *get_recent_address(address_cache_t *cache) {
118 // Check if there is an address in our cache of recently seen addresses
119 if(cache->tried < cache->data.used) {
120 return &cache->data.address[cache->tried++];
123 // Next, check any recently seen addresses not in our cache
124 while(cache->tried == cache->data.used) {
126 cache->aip = cache->ai = get_known_addresses(cache->node);
131 sockaddr_t *sa = (sockaddr_t *)cache->aip->ai_addr;
132 cache->aip = cache->aip->ai_next;
134 if(find_cached(cache, sa) != NOT_CACHED) {
140 free_known_addresses(cache->ai);
148 // Otherwise, check if there are any known Address statements
149 if(!cache->config_tree) {
150 cache->config_tree = create_configuration();
151 read_host_config(cache->config_tree, cache->node->name, false);
152 cache->cfg = lookup_config(cache->config_tree, "Address");
155 while(cache->cfg && !cache->aip) {
156 char *address, *port;
158 get_config_string(cache->cfg, &address);
160 char *space = strchr(address, ' ');
163 port = xstrdup(space + 1);
166 if(!get_config_string(lookup_config(cache->config_tree, "Port"), &port)) {
167 port = xstrdup("655");
172 free_known_addresses(cache->ai);
175 cache->aip = cache->ai = str2addrinfo(address, port, SOCK_STREAM);
178 struct addrinfo *ai = NULL;
180 for(; cache->aip; cache->aip = cache->aip->ai_next) {
181 struct addrinfo *oai = ai;
183 ai = xzalloc(sizeof(*ai));
184 ai->ai_family = cache->aip->ai_family;
185 ai->ai_socktype = cache->aip->ai_socktype;
186 ai->ai_protocol = cache->aip->ai_protocol;
187 ai->ai_addrlen = cache->aip->ai_addrlen;
188 ai->ai_addr = xmalloc(ai->ai_addrlen);
189 memcpy(ai->ai_addr, cache->aip->ai_addr, ai->ai_addrlen);
193 freeaddrinfo(cache->ai);
194 cache->aip = cache->ai = ai;
200 cache->cfg = lookup_config_next(cache->config_tree, cache->cfg);
205 sockaddr_t *sa = (sockaddr_t *)cache->aip->ai_addr;
207 cache->aip = cache->aip->ai_next;
210 free_known_addresses(cache->ai);
215 // We're all out of addresses.
216 exit_configuration(&cache->config_tree);
220 address_cache_t *open_address_cache(node_t *node) {
221 address_cache_t *cache = xmalloc(sizeof(*cache));
224 // Try to open an existing address cache
225 char fname[PATH_MAX];
226 snprintf(fname, sizeof(fname), "%s" SLASH "cache" SLASH "%s", confbase, node->name);
227 FILE *fp = fopen(fname, "rb");
229 if(!fp || fread(&cache->data, sizeof(cache->data), 1, fp) != 1 || cache->data.version != ADDRESS_CACHE_VERSION) {
230 memset(&cache->data, 0, sizeof(cache->data));
237 // Ensure we have a valid state
238 cache->config_tree = NULL;
243 cache->data.version = ADDRESS_CACHE_VERSION;
245 if(cache->data.used > MAX_CACHED_ADDRESSES) {
246 cache->data.used = 0;
252 void reset_address_cache(address_cache_t *cache, const sockaddr_t *sa) {
254 add_recent_address(cache, sa);
257 if(cache->config_tree) {
258 exit_configuration(&cache->config_tree);
262 free_known_addresses(cache->ai);
265 cache->config_tree = NULL;
272 void close_address_cache(address_cache_t *cache) {
273 if(cache->config_tree) {
274 exit_configuration(&cache->config_tree);
278 free_known_addresses(cache->ai);