first batch of license fixes (boring)
[oweals/gnunet.git] / src / testbed / gnunet-daemon-testbed-underlay.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2008--2013 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       Affero General Public License for more details.
14 */
15
16
17 /**
18  * @file testbed/gnunet-daemon-testbed-blacklist.c
19  * @brief daemon to restrict incoming connections from other peers at the
20  *          transport layer of a peer
21  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
22  */
23
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_transport_service.h"
27 #include "gnunet_transport_manipulation_service.h"
28 #include "gnunet_ats_service.h"
29 #include "gnunet_testing_lib.h"
30 #include <sqlite3.h>
31
32 /**
33  * Logging shorthand
34  */
35 #define LOG(type,...)                           \
36   GNUNET_log (type, __VA_ARGS__)
37
38 /**
39  * Debug logging shorthand
40  */
41 #define DEBUG(...)                              \
42   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
43
44 /**
45  * Log an error message at log-level 'level' that indicates
46  * a failure of the command 'cmd' on file 'filename'
47  * with the message given by strerror(errno).
48  */
49 #define LOG_SQLITE(db, msg, level, cmd)                                 \
50   do {                                                                  \
51     GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), \
52                      cmd, __FILE__,__LINE__, sqlite3_errmsg(db));  \
53     if (msg != NULL)                                                    \
54       GNUNET_asprintf(msg, _("`%s' failed at %s:%u with error: %s"), cmd, \
55                       __FILE__, __LINE__, sqlite3_errmsg(db));     \
56   } while(0)
57
58
59 /**
60  * The map to store the peer identities to allow/deny
61  */
62 static struct GNUNET_CONTAINER_MultiPeerMap *map;
63
64 /**
65  * The database connection
66  */
67 static struct sqlite3 *db;
68
69 /**
70  * The blacklist handle we obtain from transport when we register ourselves for
71  * access control
72  */
73 static struct GNUNET_TRANSPORT_Blacklist *bh;
74
75 /**
76  * The hostkeys file
77  */
78 struct GNUNET_DISK_FileHandle *hostkeys_fd;
79
80 /**
81  * The hostkeys map
82  */
83 static struct GNUNET_DISK_MapHandle *hostkeys_map;
84
85 /**
86  * The hostkeys data
87  */
88 static void *hostkeys_data;
89
90 /**
91  * Handle to the transport service.  This is used for setting link metrics
92  */
93 static struct GNUNET_TRANSPORT_ManipulationHandle *transport;
94
95 /**
96  * The number of hostkeys in the hostkeys array
97  */
98 static unsigned int num_hostkeys;
99
100
101 /**
102  * @ingroup hashmap
103  * Iterator over hash map entries.
104  *
105  * @param cls closure
106  * @param key current key code
107  * @param value value in the hash map
108  * @return #GNUNET_YES if we should continue to
109  *         iterate,
110  *         #GNUNET_NO if not.
111  */
112 static int
113 iterator (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
114 {
115   GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (map, key,
116                                                                      value));
117   return GNUNET_YES;
118 }
119
120
121 /**
122  * Cleaup and destroy the map
123  */
124 static void
125 cleanup_map ()
126 {
127   if (NULL != map)
128   {
129     GNUNET_assert (GNUNET_SYSERR != GNUNET_CONTAINER_multipeermap_iterate (map,
130                                                                            &iterator,
131                                                                            NULL));
132     GNUNET_CONTAINER_multipeermap_destroy (map);
133     map = NULL;
134   }
135 }
136
137
138 /**
139  * Function that decides if a connection is acceptable or not.
140  *
141  * @param cls closure
142  * @param pid peer to approve or disapproave
143  * @return GNUNET_OK if the connection is allowed, GNUNET_SYSERR if not
144  */
145 static int
146 check_access (void *cls, const struct GNUNET_PeerIdentity * pid)
147 {
148   int contains;
149
150   GNUNET_assert (NULL != map);
151   contains = GNUNET_CONTAINER_multipeermap_contains (map, pid);
152   if (GNUNET_YES == contains)
153   {
154     DEBUG ("Permitting `%s'\n", GNUNET_i2s (pid));
155     return GNUNET_OK;
156   }
157   DEBUG ("Not permitting `%s'\n", GNUNET_i2s (pid));
158   return GNUNET_SYSERR;
159 }
160
161
162 static int
163 get_identity (unsigned int offset,
164               struct GNUNET_PeerIdentity *id)
165 {
166   struct GNUNET_CRYPTO_EddsaPrivateKey private_key;
167
168   if (offset >= num_hostkeys)
169     return GNUNET_SYSERR;
170   GNUNET_memcpy (&private_key,
171                  hostkeys_data + (offset * GNUNET_TESTING_HOSTKEYFILESIZE),
172                  GNUNET_TESTING_HOSTKEYFILESIZE);
173   GNUNET_CRYPTO_eddsa_key_get_public (&private_key,
174                                       &id->public_key);
175   return GNUNET_OK;
176 }
177
178
179 /**
180  * Whilelist entry
181  */
182 struct WhiteListRow
183 {
184   /**
185    * Next ptr
186    */
187   struct WhiteListRow *next;
188
189   /**
190    * The offset where to find the hostkey for the peer
191    */
192   unsigned int id;
193
194   /**
195    * Latency to be assigned to the link
196    */
197   int latency;
198
199 };
200
201
202 /**
203  * Function to load keys
204  */
205 static int
206 load_keys (const struct GNUNET_CONFIGURATION_Handle *c)
207 {
208   char *data_dir;
209   char *idfile;
210   uint64_t fsize;
211
212   data_dir = NULL;
213   idfile = NULL;
214   fsize = 0;
215   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
216   GNUNET_asprintf (&idfile, "%s/testing_hostkeys.ecc", data_dir);
217   GNUNET_free (data_dir);
218   data_dir = NULL;
219   if (GNUNET_OK !=
220       GNUNET_DISK_file_size (idfile, &fsize, GNUNET_YES, GNUNET_YES))
221   {
222     GNUNET_free (idfile);
223     return GNUNET_SYSERR;
224   }
225   if (0 != (fsize % GNUNET_TESTING_HOSTKEYFILESIZE))
226   {
227     LOG (GNUNET_ERROR_TYPE_ERROR,
228          _("Incorrect hostkey file format: %s\n"), idfile);
229     GNUNET_free (idfile);
230     return GNUNET_SYSERR;
231   }
232   hostkeys_fd = GNUNET_DISK_file_open (idfile, GNUNET_DISK_OPEN_READ,
233                                        GNUNET_DISK_PERM_NONE);
234   if (NULL == hostkeys_fd)
235   {
236     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", idfile);
237     GNUNET_free (idfile);
238     return GNUNET_SYSERR;
239   }
240   GNUNET_free (idfile);
241   idfile = NULL;
242   hostkeys_data = GNUNET_DISK_file_map (hostkeys_fd,
243                                         &hostkeys_map,
244                                         GNUNET_DISK_MAP_TYPE_READ,
245                                         fsize);
246   if (NULL == hostkeys_data)
247   {
248
249     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mmap");
250     return GNUNET_SYSERR;
251   }
252   num_hostkeys = fsize / GNUNET_TESTING_HOSTKEYFILESIZE;
253   return GNUNET_OK;
254 }
255
256
257 /**
258  * Function to unload keys
259  */
260 static void
261 unload_keys ()
262 {
263   if (NULL != hostkeys_map)
264   {
265     GNUNET_assert (NULL != hostkeys_data);
266     GNUNET_DISK_file_unmap (hostkeys_map);
267     hostkeys_map = NULL;
268     hostkeys_data = NULL;
269   }
270   if (NULL != hostkeys_fd)
271   {
272     GNUNET_DISK_file_close (hostkeys_fd);
273     hostkeys_fd = NULL;
274   }
275 }
276
277
278 /**
279  * Shutdown task to cleanup our resources and exit.
280  *
281  * @param cls NULL
282  */
283 static void
284 do_shutdown (void *cls)
285 {
286   if (NULL != transport)
287   {
288     GNUNET_TRANSPORT_manipulation_disconnect (transport);
289     transport = NULL;
290   }
291   cleanup_map ();
292   unload_keys ();
293   if (NULL != bh)
294     GNUNET_TRANSPORT_blacklist_cancel (bh);
295 }
296
297
298 /**
299  * Function to read whitelist rows from the database
300  *
301  * @param db the database connection
302  * @param pid the identity of this peer
303  * @param wl_rows where to store the retrieved whitelist rows
304  * @return GNUNET_SYSERR upon error OR the number of rows retrieved
305  */
306 static int
307 db_read_whitelist (struct sqlite3 *db, int pid, struct WhiteListRow **wl_rows)
308 {
309   static const char *query_wl = "SELECT oid, latency FROM whitelist WHERE (id == ?);";
310   struct sqlite3_stmt *stmt_wl;
311   struct WhiteListRow *lr;
312   int nrows;
313   int ret;
314
315   if (SQLITE_OK != (ret = sqlite3_prepare_v2 (db, query_wl, -1, &stmt_wl, NULL)))
316   {
317     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
318     return GNUNET_SYSERR;
319   }
320   if (SQLITE_OK != (ret = sqlite3_bind_int (stmt_wl, 1, pid)))
321   {
322     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_int");
323     sqlite3_finalize (stmt_wl);
324     return GNUNET_SYSERR;
325   }
326   nrows = 0;
327   do
328   {
329     ret = sqlite3_step (stmt_wl);
330     if (SQLITE_ROW != ret)
331       break;
332     nrows++;
333     lr = GNUNET_new (struct WhiteListRow);
334     lr->id = sqlite3_column_int (stmt_wl, 0);
335     lr->latency = sqlite3_column_int (stmt_wl, 1);
336     lr->next = *wl_rows;
337     *wl_rows = lr;
338   } while (1);
339   sqlite3_finalize (stmt_wl);
340   return nrows;
341 }
342
343
344 /**
345  * Main function that will be run.
346  *
347  * @param cls closure
348  * @param args remaining command-line arguments
349  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
350  * @param c configuration
351  */
352 static void
353 run (void *cls, char *const *args, const char *cfgfile,
354      const struct GNUNET_CONFIGURATION_Handle *c)
355 {
356   char *dbfile;
357   struct WhiteListRow *wl_head;
358   struct WhiteListRow *wl_entry;
359   struct GNUNET_PeerIdentity identity;
360   struct GNUNET_ATS_Properties prop;
361   struct GNUNET_TIME_Relative delay;
362   unsigned long long pid;
363   unsigned int nrows;
364   int ret;
365
366   if (GNUNET_OK !=
367       GNUNET_CONFIGURATION_get_value_number (c, "TESTBED",
368                                              "PEERID", &pid))
369   {
370     GNUNET_break (0);
371     return;
372   }
373   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "TESTBED-UNDERLAY",
374                                                             "DBFILE",
375                                                             &dbfile))
376   {
377     GNUNET_break (0);
378     return;
379   }
380   if (SQLITE_OK != (ret = sqlite3_open_v2 (dbfile, &db, SQLITE_OPEN_READONLY, NULL)))
381   {
382     if (NULL != db)
383     {
384       LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite_open_v2");
385       GNUNET_break (SQLITE_OK == sqlite3_close (db));
386     }
387     else
388       LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot open sqlite file %s\n", dbfile);
389     GNUNET_free (dbfile);
390     return;
391   }
392   DEBUG ("Opened database %s\n", dbfile);
393   GNUNET_free (dbfile);
394   dbfile = NULL;
395   wl_head = NULL;
396   if (GNUNET_OK != load_keys (c))
397       goto close_db;
398
399   transport = GNUNET_TRANSPORT_manipulation_connect (c);
400   if (NULL == transport)
401   {
402     GNUNET_break (0);
403     return;
404   }
405   /* read and process whitelist */
406   nrows = 0;
407   wl_head = NULL;
408   nrows = db_read_whitelist (db, pid, &wl_head);
409   if ((GNUNET_SYSERR == nrows) || (0 == nrows))
410   {
411     GNUNET_TRANSPORT_manipulation_disconnect (transport);
412     goto close_db;
413   }
414   map = GNUNET_CONTAINER_multipeermap_create (nrows, GNUNET_NO);
415   while (NULL != (wl_entry = wl_head))
416   {
417     wl_head = wl_entry->next;
418     delay.rel_value_us = wl_entry->latency;
419     memset (&prop, 0, sizeof (prop));
420     GNUNET_assert (GNUNET_OK == get_identity (wl_entry->id, &identity));
421     GNUNET_break (GNUNET_OK ==
422                   GNUNET_CONTAINER_multipeermap_put (map, &identity, &identity,
423                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
424     DEBUG ("Setting %u ms latency to peer `%s'\n",
425            wl_entry->latency,
426            GNUNET_i2s (&identity));
427     GNUNET_TRANSPORT_manipulation_set (transport,
428                                        &identity,
429                                        &prop,
430                                        delay,
431                                        delay);
432     GNUNET_free (wl_entry);
433   }
434   bh = GNUNET_TRANSPORT_blacklist (c, &check_access, NULL);
435   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
436
437  close_db:
438   GNUNET_break (SQLITE_OK == sqlite3_close (db));
439 }
440
441
442 /**
443  * The main function.
444  *
445  * @param argc number of arguments from the command line
446  * @param argv command line arguments
447  * @return 0 ok, 1 on error
448  */
449 int
450 main (int argc, char *const *argv)
451 {
452   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
453     GNUNET_GETOPT_OPTION_END
454   };
455   int ret;
456
457   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
458     return 2;
459 #ifdef SQLITE_CONFIG_MMAP_SIZE
460   (void) sqlite3_config (SQLITE_CONFIG_MMAP_SIZE, 512000, 256000000);
461 #endif
462   ret =
463       (GNUNET_OK ==
464        GNUNET_PROGRAM_run (argc, argv, "testbed-underlay",
465                            _
466                            ("Daemon to restrict underlay network in testbed deployments"),
467                            options, &run, NULL)) ? 0 : 1;
468   GNUNET_free ((void*) argv);
469   return ret;
470 }