1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6 /* System */
7 #include <pthread.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <signal.h>
12 #include <unistd.h>
13
14 /* Libowfat */
15 #include "byte.h"
16 #include "scan.h"
17 #include "ip6.h"
18 #include "mmap.h"
19
20 /* Opentracker */
21 #include "trackerlogic.h"
22 #include "ot_accesslist.h"
23 #include "ot_vector.h"
24
25 /* GLOBAL VARIABLES */
26 #ifdef WANT_ACCESSLIST
27 char *g_accesslist_filename;
28 static ot_hash *g_accesslist;
29 static size_t g_accesslist_size;
30 static pthread_mutex_t g_accesslist_mutex;
31
vector_compare_hash(const void * hash1,const void * hash2)32 static int vector_compare_hash(const void *hash1, const void *hash2 ) {
33 return memcmp( hash1, hash2, OT_HASH_COMPARE_SIZE );
34 }
35
36 /* Read initial access list */
accesslist_readfile(void)37 static void accesslist_readfile( void ) {
38 ot_hash *info_hash, *accesslist_new = NULL;
39 char *map, *map_end, *read_offs;
40 size_t maplen;
41
42 if( ( map = mmap_read( g_accesslist_filename, &maplen ) ) == NULL ) {
43 char *wd = getcwd( NULL, 0 );
44 fprintf( stderr, "Warning: Can't open accesslist file: %s (but will try to create it later, if necessary and possible).\nPWD: %s\n", g_accesslist_filename, wd );
45 free( wd );
46 return;
47 }
48
49 /* You need at least 41 bytes to pass an info_hash, make enough room
50 for the maximum amount of them */
51 info_hash = accesslist_new = malloc( ( maplen / 41 ) * 20 );
52 if( !accesslist_new ) {
53 fprintf( stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", ( maplen / 41 ) * 20 );
54 return;
55 }
56
57 /* No use to scan if there's not enough room for another full info_hash */
58 map_end = map + maplen - 40;
59 read_offs = map;
60
61 /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*" */
62 while( read_offs <= map_end ) {
63 int i;
64 for( i=0; i<(int)sizeof(ot_hash); ++i ) {
65 int eger1 = scan_fromhex( read_offs[ 2*i ] );
66 int eger2 = scan_fromhex( read_offs[ 1 + 2*i ] );
67 if( eger1 < 0 || eger2 < 0 )
68 break;
69 (*info_hash)[i] = eger1 * 16 + eger2;
70 }
71
72 if( i == sizeof(ot_hash) ) {
73 read_offs += 40;
74
75 /* Append accesslist to accesslist vector */
76 if( read_offs == map_end || scan_fromhex( *read_offs ) < 0 )
77 ++info_hash;
78 }
79
80 /* Find start of next line */
81 while( read_offs <= map_end && *(read_offs++) != '\n' );
82 }
83 #ifdef _DEBUG
84 fprintf( stderr, "Added %zd info_hashes to accesslist\n", (size_t)(info_hash - accesslist_new) );
85 #endif
86
87 mmap_unmap( map, maplen);
88
89 qsort( accesslist_new, info_hash - accesslist_new, sizeof( *info_hash ), vector_compare_hash );
90
91 /* Now exchange the accesslist vector in the least race condition prone way */
92 pthread_mutex_lock(&g_accesslist_mutex);
93 free( g_accesslist );
94 g_accesslist = accesslist_new;
95 g_accesslist_size = info_hash - accesslist_new;
96 pthread_mutex_unlock(&g_accesslist_mutex);
97 }
98
accesslist_hashisvalid(ot_hash hash)99 int accesslist_hashisvalid( ot_hash hash ) {
100 void *exactmatch;
101
102 /* Lock should hardly ever be contended */
103 pthread_mutex_lock(&g_accesslist_mutex);
104 exactmatch = bsearch( hash, g_accesslist, g_accesslist_size, OT_HASH_COMPARE_SIZE, vector_compare_hash );
105 pthread_mutex_unlock(&g_accesslist_mutex);
106
107 #ifdef WANT_ACCESSLIST_BLACK
108 return exactmatch == NULL;
109 #else
110 return exactmatch != NULL;
111 #endif
112 }
113
accesslist_worker(void * args)114 static void * accesslist_worker( void * args ) {
115 int sig;
116 sigset_t signal_mask;
117
118 sigemptyset(&signal_mask);
119 sigaddset(&signal_mask, SIGHUP);
120
121 (void)args;
122
123 while( 1 ) {
124
125 /* Initial attempt to read accesslist */
126 accesslist_readfile( );
127
128 /* Wait for signals */
129 while( sigwait (&signal_mask, &sig) != 0 && sig != SIGHUP );
130 }
131 return NULL;
132 }
133
134 static pthread_t thread_id;
accesslist_init()135 void accesslist_init( ) {
136 pthread_mutex_init(&g_accesslist_mutex, NULL);
137 pthread_create( &thread_id, NULL, accesslist_worker, NULL );
138 }
139
accesslist_deinit(void)140 void accesslist_deinit( void ) {
141 pthread_cancel( thread_id );
142 pthread_mutex_destroy(&g_accesslist_mutex);
143 free( g_accesslist );
144 g_accesslist = 0;
145 g_accesslist_size = 0;
146 }
147 #endif
148
address_in_net(const ot_ip6 address,const ot_net * net)149 int address_in_net( const ot_ip6 address, const ot_net *net ) {
150 int bits = net->bits;
151 int result = memcmp( address, &net->address, bits >> 3 );
152 if( !result && ( bits & 7 ) )
153 result = ( ( 0x7f00 >> ( bits & 7 ) ) & address[bits>>3] ) - net->address[bits>>3];
154 return result == 0;
155 }
156
set_value_for_net(const ot_net * net,ot_vector * vector,const void * value,const size_t member_size)157 void *set_value_for_net( const ot_net *net, ot_vector *vector, const void *value, const size_t member_size ) {
158 size_t i;
159 int exactmatch;
160
161 /* Caller must have a concept of ot_net in it's member */
162 if( member_size < sizeof(ot_net) )
163 return 0;
164
165 /* Check each net in vector for overlap */
166 uint8_t *member = ((uint8_t*)vector->data);
167 for( i=0; i<vector->size; ++i ) {
168 if( address_in_net( *(ot_ip6*)member, net ) ||
169 address_in_net( net->address, (ot_net*)member ) )
170 return 0;
171 member += member_size;
172 }
173
174 member = vector_find_or_insert( vector, (void*)net, member_size, sizeof(ot_net), &exactmatch );
175 if( member ) {
176 memcpy( member, net, sizeof(ot_net));
177 memcpy( member + sizeof(ot_net), value, member_size - sizeof(ot_net));
178 }
179
180 return member;
181 }
182
183 /* Takes a vector filled with { ot_net net, uint8_t[x] value };
184 Returns value associated with the net, or NULL if not found */
get_value_for_net(const ot_ip6 address,const ot_vector * vector,const size_t member_size)185 void *get_value_for_net( const ot_ip6 address, const ot_vector *vector, const size_t member_size ) {
186 int exactmatch;
187 /* This binary search will return a pointer to the first non-containing network... */
188 ot_net *net = binary_search( address, vector->data, vector->size, member_size, sizeof(ot_ip6), &exactmatch );
189 if( !net )
190 return NULL;
191 /* ... so we'll need to move back one step unless we've exactly hit the first address in network */
192 if( !exactmatch && ( (void*)net > vector->data ) )
193 --net;
194 if( !address_in_net( address, net ) )
195 return NULL;
196 return (void*)net;
197 }
198
199 #ifdef WANT_FULLLOG_NETWORKS
200 static ot_vector g_lognets_list;
201 ot_log *g_logchain_first, *g_logchain_last;
202
203 static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER;
loglist_add_network(const ot_net * net)204 void loglist_add_network( const ot_net *net ) {
205 pthread_mutex_lock(&g_lognets_list_mutex);
206 set_value_for_net( net, &g_lognets_list, NULL, sizeof(ot_net));
207 pthread_mutex_unlock(&g_lognets_list_mutex);
208 }
209
loglist_reset()210 void loglist_reset( ) {
211 pthread_mutex_lock(&g_lognets_list_mutex);
212 free( g_lognets_list.data );
213 g_lognets_list.data = 0;
214 g_lognets_list.size = g_lognets_list.space = 0;
215 pthread_mutex_unlock(&g_lognets_list_mutex);
216 }
217
loglist_check_address(const ot_ip6 address)218 int loglist_check_address( const ot_ip6 address ) {
219 int result;
220 pthread_mutex_lock(&g_lognets_list_mutex);
221 result = ( NULL != get_value_for_net( address, &g_lognets_list, sizeof(ot_net)) );
222 pthread_mutex_unlock(&g_lognets_list_mutex);
223 return result;
224 }
225 #endif
226
227 #ifdef WANT_IP_FROM_PROXY
228 typedef struct {
229 ot_net *proxy;
230 ot_vector networks;
231 } ot_proxymap;
232
233 static ot_vector g_proxies_list;
234 static pthread_mutex_t g_proxies_list_mutex = PTHREAD_MUTEX_INITIALIZER;
235
proxylist_add_network(const ot_net * proxy,const ot_net * net)236 int proxylist_add_network( const ot_net *proxy, const ot_net *net ) {
237 ot_proxymap *map;
238 int exactmatch, result = 1;
239 pthread_mutex_lock(&g_proxies_list_mutex);
240
241 /* If we have a direct hit, use and extend the vector there */
242 map = binary_search( proxy, g_proxies_list.data, g_proxies_list.size, sizeof(ot_proxymap), sizeof(ot_net), &exactmatch );
243
244 if( !map || !exactmatch ) {
245 /* else see, if we've got overlapping networks
246 and get a new empty vector if not */
247 ot_vector empty;
248 memset( &empty, 0, sizeof( ot_vector ) );
249 map = set_value_for_net( proxy, &g_proxies_list, &empty, sizeof(ot_proxymap));
250 }
251
252 if( map && set_value_for_net( net, &map->networks, NULL, sizeof(ot_net) ) )
253 result = 1;
254
255 pthread_mutex_unlock(&g_proxies_list_mutex);
256 return result;
257 }
258
proxylist_check_proxy(const ot_ip6 proxy,const ot_ip6 address)259 int proxylist_check_proxy( const ot_ip6 proxy, const ot_ip6 address ) {
260 int result = 0;
261 ot_proxymap *map;
262
263 pthread_mutex_lock(&g_proxies_list_mutex);
264
265 if( ( map = get_value_for_net( proxy, &g_proxies_list, sizeof(ot_proxymap) ) ) )
266 if( !address || get_value_for_net( address, &map->networks, sizeof(ot_net) ) )
267 result = 1;
268
269 pthread_mutex_unlock(&g_proxies_list_mutex);
270 return result;
271 }
272
273 #endif
274
275 static ot_ip6 g_adminip_addresses[OT_ADMINIP_MAX];
276 static ot_permissions g_adminip_permissions[OT_ADMINIP_MAX];
277 static unsigned int g_adminip_count = 0;
278
accesslist_blessip(ot_ip6 ip,ot_permissions permissions)279 int accesslist_blessip( ot_ip6 ip, ot_permissions permissions ) {
280 if( g_adminip_count >= OT_ADMINIP_MAX )
281 return -1;
282
283 memcpy(g_adminip_addresses + g_adminip_count,ip,sizeof(ot_ip6));
284 g_adminip_permissions[ g_adminip_count++ ] = permissions;
285
286 #ifdef _DEBUG
287 {
288 char _debug[512];
289 int off = snprintf( _debug, sizeof(_debug), "Blessing ip address " );
290 off += fmt_ip6c(_debug+off, ip );
291
292 if( permissions & OT_PERMISSION_MAY_STAT ) off += snprintf( _debug+off, 512-off, " may_fetch_stats" );
293 if( permissions & OT_PERMISSION_MAY_LIVESYNC ) off += snprintf( _debug+off, 512-off, " may_sync_live" );
294 if( permissions & OT_PERMISSION_MAY_FULLSCRAPE ) off += snprintf( _debug+off, 512-off, " may_fetch_fullscrapes" );
295 if( permissions & OT_PERMISSION_MAY_PROXY ) off += snprintf( _debug+off, 512-off, " may_proxy" );
296 if( !permissions ) off += snprintf( _debug+off, sizeof(_debug)-off, " nothing\n" );
297 _debug[off++] = '.';
298 write( 2, _debug, off );
299 }
300 #endif
301
302 return 0;
303 }
304
accesslist_isblessed(ot_ip6 ip,ot_permissions permissions)305 int accesslist_isblessed( ot_ip6 ip, ot_permissions permissions ) {
306 unsigned int i;
307 for( i=0; i<g_adminip_count; ++i )
308 if( !memcmp( g_adminip_addresses + i, ip, sizeof(ot_ip6)) && ( g_adminip_permissions[ i ] & permissions ) )
309 return 1;
310 return 0;
311 }
312
313 const char *g_version_accesslist_c = "$Source$: $Revision$\n";
314