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