3613664d6dc1b301d86cdd9a5da70148ab2fa9dd
[oweals/gnunet.git] / src / testbed / gnunet-daemon-testbed-underlay.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--2013 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       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       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
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_testing_lib.h"
33 #include <sqlite3.h>
34
35 /**
36  * Logging shorthand
37  */
38 #define LOG(type,...)                           \
39   GNUNET_log (type, __VA_ARGS__)
40
41 /**
42  * Debug logging shorthand
43  */
44 #define DEBUG(...)                              \
45   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
46
47
48 #define LOG_SQLITE_ERROR(ret)                                           \
49   LOG (GNUNET_ERROR_TYPE_ERROR, "sqlite error: %s", sqlite3_errstr (ret))
50
51
52 /**
53  * Allow access from the peers read from the whitelist
54  */
55 #define ACCESS_ALLOW 1
56
57 /**
58  * Deny access from the peers read from the blacklist
59  */
60 #define ACCESS_DENY 0
61
62 /**
63  * The map to store the peer identities to allow/deny
64  */
65 static struct GNUNET_CONTAINER_MultiPeerMap *map;
66
67
68 /**
69  * The map to store the peer identities to allow/deny
70  */
71 static struct GNUNET_CONTAINER_MultiPeerMap *blacklist_map;
72
73 /**
74  * The database connection
75  */
76 static struct sqlite3 *db;
77
78 /**
79  * The array of peer identities we read from whitelist/blacklist
80  */
81 static struct GNUNET_PeerIdentity *ilist;
82
83 /**
84  * The blacklist handle we obtain from transport when we register ourselves for
85  * access control
86  */
87 struct GNUNET_TRANSPORT_Blacklist *bh;
88
89 /**
90  * The peer ID map
91  */
92 static struct GNUNET_DISK_MapHandle *idmap;
93
94 /**
95  * The hostkeys data
96  */
97 static struct GNUNET_PeerIdentity *hostkeys;
98
99 /**
100  * The number of hostkeys in the hostkeys array
101  */
102 static unsigned int num_hostkeys;
103
104 /**
105  * Task for shutdown
106  */
107 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
108
109 /**
110  * Are we allowing or denying access from peers
111  */
112 static int mode;
113
114
115 /**
116  * @ingroup hashmap
117  * Iterator over hash map entries.
118  *
119  * @param cls closure
120  * @param key current key code
121  * @param value value in the hash map
122  * @return #GNUNET_YES if we should continue to
123  *         iterate,
124  *         #GNUNET_NO if not.
125  */
126 static int
127 iterator (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
128 {
129   GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (map, key,
130                                                                      value));
131   return GNUNET_YES;
132 }
133
134
135 /**
136  * Cleaup and destroy the map
137  */
138 static void
139 cleanup_map ()
140 {
141   if (NULL != map)
142   {
143     GNUNET_assert (GNUNET_SYSERR != GNUNET_CONTAINER_multipeermap_iterate (map,
144                                                                            &iterator,
145                                                                            NULL));
146     GNUNET_CONTAINER_multipeermap_destroy (map);
147     map = NULL;
148   }
149 }
150
151
152 /**
153  * Shutdown task to cleanup our resources and exit.
154  *
155  * @param cls NULL
156  * @param tc scheduler task context
157  */
158 static void
159 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
160 {
161   cleanup_map ();
162   if (NULL != bh)
163     GNUNET_TRANSPORT_blacklist_cancel (bh);
164 }
165
166
167 /**
168  * Function that decides if a connection is acceptable or not.
169  *
170  * @param cls closure
171  * @param pid peer to approve or disapproave
172  * @return GNUNET_OK if the connection is allowed, GNUNET_SYSERR if not
173  */
174 static int
175 check_access (void *cls, const struct GNUNET_PeerIdentity * pid)
176 {
177   int contains;
178
179   if (NULL != map)
180     contains = GNUNET_CONTAINER_multipeermap_contains (map, pid);
181   else
182     contains = GNUNET_NO;
183   if (ACCESS_DENY == mode)
184     return (contains) ? GNUNET_SYSERR : GNUNET_OK;
185   return (contains) ? GNUNET_OK : GNUNET_SYSERR;
186 }
187
188
189 /**
190  * Setup the access control by reading the given file containing peer identities
191  * and then establishing blacklist handler with the peer's transport service
192  *
193  * @param fname the filename to read the list of peer identities
194  * @param cfg the configuration for connecting to the peer's transport service
195  */
196 static void
197 setup_ac (const char *fname, const struct GNUNET_CONFIGURATION_Handle *cfg)
198 {
199   uint64_t fsize;
200   unsigned int npeers;
201   unsigned int cnt;
202
203   GNUNET_assert (GNUNET_OK != GNUNET_DISK_file_size (fname, &fsize, GNUNET_NO,
204                                                      GNUNET_YES));
205   if (0 != (fsize % sizeof (struct GNUNET_PeerIdentity)))
206   {
207     GNUNET_break (0);
208     return;
209   }
210   npeers = fsize / sizeof (struct GNUNET_PeerIdentity);
211   if (0 != npeers)
212   {
213     map = GNUNET_CONTAINER_multipeermap_create (npeers, GNUNET_YES);
214     ilist = GNUNET_malloc_large (fsize);
215     GNUNET_assert (fsize == GNUNET_DISK_fn_read (fname, ilist, fsize));
216   }
217   for (cnt = 0; cnt < npeers; cnt++)
218   {
219     if (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put (map, &ilist[cnt],
220                                                             &ilist[cnt],
221                                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
222     {
223       cleanup_map ();
224       GNUNET_free (ilist);
225       return;
226     }
227   }
228   shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
229                                                 &do_shutdown, NULL);
230   bh = GNUNET_TRANSPORT_blacklist (cfg, &check_access, NULL);
231 }
232
233
234 /**
235  * Function to blacklist a peer
236  *
237  * @param offset the offset where to find the peer's hostkey in the array of hostkeys
238  */
239 static void
240 blacklist_peer (unsigned int offset)
241 {
242   struct GNUNET_CRYPTO_EddsaPrivateKey private_key;
243   struct GNUNET_PeerIdentity id;
244
245   (void) memcpy (&private_key, &hostkeys[offset], sizeof (private_key));
246   GNUNET_CRYPTO_eddsa_key_get_public (&private_key, &id.public_key);
247   GNUNET_break (GNUNET_OK ==
248                 GNUNET_CONTAINER_multipeermap_put (map, &id, &id,
249                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
250
251 }
252
253 /**
254  * Blacklist peer
255  */
256 struct ListRow
257 {
258   /**
259    * Next ptr
260    */
261   struct ListRow *next;
262
263   /**
264    * The offset where to find the hostkey for the peer
265    */
266   unsigned int id;
267 };
268
269
270 /**
271  * Function to add a peer to the blacklist
272  *
273  * @param head the head of the list
274  * @param id the id of the peer to add
275  */
276 static void
277 listrow_add (struct ListRow *head, unsigned int id)
278 {
279   struct ListRow *bp;
280
281   bp = GNUNET_new (struct ListRow);
282   bp->id = id;
283   bp->next = head;
284   head = bp;
285 }
286
287
288 /**
289  * Add peers in the blacklist to the blacklist map
290  */
291 static int
292 map_populate (struct ListRow *head,
293               struct GNUNET_CONTAINER_MultiPeerMap *map,
294               const struct GNUNET_PeerIdentity *hostkeys)
295 {
296   struct ListRow *row;
297   int ret;
298
299   while (NULL != (row = head))
300   {
301     if (head->id >= num_hostkeys)
302     {
303       LOG (GNUNET_ERROR_TYPE_WARNING, "Hostkey index %u out of max range %u\n",
304            row->id, num_hostkeys);
305     }
306     head = row->next;
307     ret = GNUNET_CONTAINER_multipeermap_put (blacklist_map, &hostkeys[row->id],
308                                        (void *) &hostkeys[row->id],
309                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
310     if (GNUNET_OK != ret)
311       return GNUNET_SYSERR;
312   }
313   return GNUNET_OK;
314 }
315
316
317 /**
318  * Function to load keys
319  */
320 static int
321 load_keys (const struct GNUNET_CONFIGURATION_Handle *c)
322 {
323   char *data_dir;
324   char *idfile;
325   struct GNUNET_DISK_FileHandle *fd;
326   uint64_t fsize;
327
328   data_dir = NULL;
329   idfile = NULL;
330   fd = NULL;
331   fsize = 0;
332   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
333   GNUNET_asprintf (&idfile, "%s/testing_hostkeys.ecc", data_dir);
334   GNUNET_free (data_dir);
335   data_dir = NULL;
336   if (GNUNET_OK !=
337       GNUNET_DISK_file_size (idfile, &fsize, GNUNET_YES, GNUNET_YES))
338   {
339     GNUNET_free (idfile);
340     return GNUNET_SYSERR;
341   }
342   if (0 != (fsize % GNUNET_TESTING_HOSTKEYFILESIZE))
343   {
344     LOG (GNUNET_ERROR_TYPE_ERROR,
345          _("Incorrect hostkey file format: %s\n"), idfile);
346     GNUNET_free (idfile);
347     return GNUNET_SYSERR;
348   }
349   fd = GNUNET_DISK_file_open (idfile, GNUNET_DISK_OPEN_READ,
350                               GNUNET_DISK_PERM_NONE);
351   if (NULL == fd)
352   {
353     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", idfile);
354     GNUNET_free (idfile);
355     return GNUNET_SYSERR;
356   }
357   GNUNET_free (idfile);
358   idfile = NULL;
359   hostkeys = (struct GNUNET_PeerIdentity *)
360       GNUNET_DISK_file_map (fd, &idmap, GNUNET_DISK_MAP_TYPE_READ, fsize);
361   if (NULL == hostkeys)
362     num_hostkeys = fsize / GNUNET_TESTING_HOSTKEYFILESIZE;
363   return GNUNET_OK;
364 }
365
366
367 static int
368 db_read_blacklist (sqlite3 *dbfile, unsigned int pid, struct ListRow **blacklist_rows)
369 {
370   static const char *query_bl = "SELECT (id, oid) FROM blacklist WHERE (id == ?);";
371   static struct sqlite3_stmt *stmt_bl;
372   int nrows;
373   int peer_id;
374   int ret;
375
376   if (SQLITE_OK != (ret = sqlite3_prepare_v2 (db, query_bl, -1, &stmt_bl, NULL)))
377   {
378     LOG_SQLITE_ERROR (ret);
379     return GNUNET_SYSERR;
380   }
381   if (SQLITE_OK != (ret = sqlite3_bind_int (stmt_bl, 1, pid)))
382   {
383     LOG_SQLITE_ERROR (ret);
384     sqlite3_finalize (stmt_bl);
385     return GNUNET_SYSERR;
386   }
387   nrows = 0;
388   do
389   {
390     ret = sqlite3_step (stmt_bl);
391     if (SQLITE_ROW != ret)
392       break;
393     peer_id = sqlite3_column_int (stmt_bl, 1);
394     listrow_add (*blacklist_rows, peer_id);
395     nrows++;
396   } while (1);
397   sqlite3_finalize (stmt_bl);
398   stmt_bl = NULL;
399   return nrows;
400 }
401
402
403 /**
404  * Main function that will be run.
405  *
406  * @param cls closure
407  * @param args remaining command-line arguments
408  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
409  * @param c configuration
410  */
411 static void
412 run (void *cls, char *const *args, const char *cfgfile,
413      const struct GNUNET_CONFIGURATION_Handle *c)
414 {
415   char *dbfile;
416   struct ListRow *blacklist_rows;
417   unsigned long long pid;
418   unsigned int nrows;
419   int ret;
420
421   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (c, "TESTBED",
422                                                             "PEERID", &pid))
423   {
424     GNUNET_break (0);
425     return;
426   }
427   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "TESTBED",
428                                                             "UNDERLAY_DB",
429                                                             &dbfile))
430   {
431     GNUNET_break (0);
432     return;
433   }
434   if (SQLITE_OK != (ret = sqlite3_open_v2 (dbfile, &db, SQLITE_OPEN_READONLY, NULL)))
435   {
436     LOG_SQLITE_ERROR (ret);
437     GNUNET_free (dbfile);
438     return;
439   }
440   DEBUG ("Opened database %s\n", dbfile);
441   GNUNET_free (dbfile);
442   dbfile = NULL;
443   blacklist_rows = NULL;
444   nrows = db_read_blacklist (db, pid, &blacklist_rows);
445   if (-1 == nrows)
446     goto close_db;
447   if (nrows > 0)
448   {
449     blacklist_map = GNUNET_CONTAINER_multipeermap_create (nrows, GNUNET_YES);
450     if (GNUNET_OK != load_keys (c))
451     {
452       goto close_db;
453     }
454   }
455   /* process whitelist */
456   GNUNET_break (0);             /* TODO */
457
458  close_db:
459   GNUNET_break (GNUNET_OK == sqlite3_close (db));
460   return;
461 }
462
463
464 /**
465  * The main function.
466  *
467  * @param argc number of arguments from the command line
468  * @param argv command line arguments
469  * @return 0 ok, 1 on error
470  */
471 int
472 main (int argc, char *const *argv)
473 {
474   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
475     GNUNET_GETOPT_OPTION_END
476   };
477   int ret;
478
479   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
480     return 2;
481 #ifdef SQLITE_CONFIG_MMAP_SIZE
482   (void) sqlite3_config (SQLITE_CONFIG_MMAP_SIZE, 512000, 256000000);
483 #endif
484   ret =
485       (GNUNET_OK ==
486        GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-testbed-underlay",
487                            _
488                            ("Daemon to restrict underlay network in testbed deployments"),
489                            options, &run, NULL)) ? 0 : 1;
490   GNUNET_free ((void*) argv);
491   return ret;
492 }