-logging
[oweals/gnunet.git] / src / regex / gnunet-service-regex.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file regex/gnunet-service-regex.c
23  * @brief service to advertise capabilities described as regex and to
24  *        lookup capabilities by regex
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "regex_internal_lib.h"
30 #include "regex_ipc.h"
31
32
33 /**
34  * Information about one of our clients.
35  */
36 struct ClientEntry
37 {
38
39   /**
40    * Kept in DLL.
41    */
42   struct ClientEntry *next;
43
44   /**
45    * Kept in DLL.
46    */
47   struct ClientEntry *prev;
48
49   /**
50    * Handle identifying the client.
51    */
52   struct GNUNET_SERVER_Client *client;
53
54   /**
55    * Search handle (if this client is searching).
56    */
57   struct REGEX_INTERNAL_Search *sh;
58
59   /**
60    * Announcement handle (if this client is announcing).
61    */
62   struct REGEX_INTERNAL_Announcement *ah;
63
64   /**
65    * Refresh frequency for announcements.
66    */
67   struct GNUNET_TIME_Relative frequency;
68
69   /**
70    * Task for re-announcing.
71    */
72   struct GNUNET_SCHEDULER_Task * refresh_task;
73
74 };
75
76
77 /**
78  * Connection to the DHT.
79  */
80 static struct GNUNET_DHT_Handle *dht;
81
82 /**
83  * Handle for doing statistics.
84  */
85 static struct GNUNET_STATISTICS_Handle *stats;
86
87 /**
88  * Head of list of clients.
89  */
90 static struct ClientEntry *client_head;
91
92 /**
93  * End of list of clients.
94  */
95 static struct ClientEntry *client_tail;
96
97 /**
98  * Our notification context, used to send back results to the client.
99  */
100 static struct GNUNET_SERVER_NotificationContext *nc;
101
102 /**
103  * Private key for this peer.
104  */
105 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
106
107
108 /**
109  * A client disconnected.  Remove all of its data structure entries.
110  *
111  * @param cls closure, NULL
112  * @param client identification of the client
113  */
114 static void
115 handle_client_disconnect (void *cls,
116                           struct GNUNET_SERVER_Client *client)
117 {
118   struct ClientEntry *ce;
119   struct ClientEntry *nx;
120
121   nx = client_head;
122   for (ce = nx; NULL != ce; ce = nx)
123   {
124     nx = ce->next;
125     if (ce->client == client)
126     {
127       if (NULL != ce->refresh_task)
128       {
129         GNUNET_SCHEDULER_cancel (ce->refresh_task);
130         ce->refresh_task = NULL;
131       }
132       if (NULL != ce->ah)
133       {
134         REGEX_INTERNAL_announce_cancel (ce->ah);
135         ce->ah = NULL;
136       }
137       if (NULL != ce->sh)
138       {
139         REGEX_INTERNAL_search_cancel (ce->sh);
140         ce->sh = NULL;
141       }
142       GNUNET_CONTAINER_DLL_remove (client_head,
143                                    client_tail,
144                                    ce);
145       GNUNET_free (ce);
146     }
147   }
148 }
149
150
151 /**
152  * Task run during shutdown.
153  *
154  * @param cls unused
155  * @param tc unused
156  */
157 static void
158 cleanup_task (void *cls,
159               const struct GNUNET_SCHEDULER_TaskContext *tc)
160 {
161   struct ClientEntry *ce;
162
163   while (NULL != (ce = client_head))
164     handle_client_disconnect (NULL,
165                               ce->client);
166   GNUNET_DHT_disconnect (dht);
167   dht = NULL;
168   GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
169   stats = NULL;
170   GNUNET_SERVER_notification_context_destroy (nc);
171   nc = NULL;
172   GNUNET_free (my_private_key);
173   my_private_key = NULL;
174 }
175
176
177 /**
178  * Periodic task to refresh our announcement of the regex.
179  *
180  * @param cls the 'struct ClientEntry' of the client that triggered the
181  *        announcement
182  * @param tc scheduler context
183  */
184 static void
185 reannounce (void *cls,
186             const struct GNUNET_SCHEDULER_TaskContext *tc)
187 {
188   struct ClientEntry *ce = cls;
189
190   REGEX_INTERNAL_reannounce (ce->ah);
191   ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
192                                                    &reannounce,
193                                                    ce);
194 }
195
196
197 /**
198  * Handle ANNOUNCE message.
199  *
200  * @param cls closure
201  * @param client identification of the client
202  * @param message the actual message
203  */
204 static void
205 handle_announce (void *cls,
206                  struct GNUNET_SERVER_Client *client,
207                  const struct GNUNET_MessageHeader *message)
208 {
209   const struct AnnounceMessage *am;
210   const char *regex;
211   struct ClientEntry *ce;
212   uint16_t size;
213
214   size = ntohs (message->size);
215   am = (const struct AnnounceMessage *) message;
216   regex = (const char *) &am[1];
217   if ( (size <= sizeof (struct AnnounceMessage)) ||
218        ('\0' != regex[size - sizeof (struct AnnounceMessage) - 1]) )
219   {
220     GNUNET_break (0);
221     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
222     return;
223   }
224
225   ce = GNUNET_new (struct ClientEntry);
226   ce->client = client;
227   ce->frequency = GNUNET_TIME_relative_ntoh (am->refresh_delay);
228   ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
229                                                    &reannounce,
230                                                    ce);
231   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232               "Starting to announce regex `%s' every %s\n",
233               regex,
234               GNUNET_STRINGS_relative_time_to_string (ce->frequency,
235                                                       GNUNET_NO));
236   ce->ah = REGEX_INTERNAL_announce (dht,
237                                     my_private_key,
238                                     regex,
239                                     ntohs (am->compression),
240                                     stats);
241   if (NULL == ce->ah)
242   {
243     GNUNET_break (0);
244     GNUNET_SCHEDULER_cancel (ce->refresh_task);
245     GNUNET_free (ce);
246     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
247     return;
248   }
249   GNUNET_CONTAINER_DLL_insert (client_head,
250                                client_tail,
251                                ce);
252   GNUNET_SERVER_receive_done (client, GNUNET_OK);
253 }
254
255
256 /**
257  * Handle result, pass it back to the client.
258  *
259  * @param cls the struct ClientEntry of the client searching
260  * @param id Peer providing a regex that matches the string.
261  * @param get_path Path of the get request.
262  * @param get_path_length Lenght of @a get_path.
263  * @param put_path Path of the put request.
264  * @param put_path_length Length of the @a put_path.
265  */
266 static void
267 handle_search_result (void *cls,
268                       const struct GNUNET_PeerIdentity *id,
269                       const struct GNUNET_PeerIdentity *get_path,
270                       unsigned int get_path_length,
271                       const struct GNUNET_PeerIdentity *put_path,
272                       unsigned int put_path_length)
273 {
274   struct ClientEntry *ce = cls;
275   struct ResultMessage *result;
276   struct GNUNET_PeerIdentity *gp;
277   uint16_t size;
278
279   if ( (get_path_length >= 65536) ||
280        (put_path_length >= 65536) ||
281        ( (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity))
282        + sizeof (struct ResultMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
283   {
284     GNUNET_break (0);
285     return;
286   }
287   size = (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity) + sizeof (struct ResultMessage);
288   result = GNUNET_malloc (size);
289   result->header.size = htons (size);
290   result->header.type = htons (GNUNET_MESSAGE_TYPE_REGEX_RESULT);
291   result->get_path_length = htons ((uint16_t) get_path_length);
292   result->put_path_length = htons ((uint16_t) put_path_length);
293   result->id = *id;
294   gp = &result->id;
295   memcpy (&gp[1],
296           get_path,
297           get_path_length * sizeof (struct GNUNET_PeerIdentity));
298   memcpy (&gp[1 + get_path_length],
299           put_path,
300           put_path_length * sizeof (struct GNUNET_PeerIdentity));
301   GNUNET_SERVER_notification_context_unicast (nc,
302                                               ce->client,
303                                               &result->header, GNUNET_NO);
304   GNUNET_free (result);
305 }
306
307
308 /**
309  * Handle SEARCH message.
310  *
311  * @param cls closure
312  * @param client identification of the client
313  * @param message the actual message
314  */
315 static void
316 handle_search (void *cls,
317                struct GNUNET_SERVER_Client *client,
318                const struct GNUNET_MessageHeader *message)
319 {
320   const struct RegexSearchMessage *sm;
321   const char *string;
322   struct ClientEntry *ce;
323   uint16_t size;
324
325   size = ntohs (message->size);
326   sm = (const struct RegexSearchMessage *) message;
327   string = (const char *) &sm[1];
328   if ( (size <= sizeof (struct RegexSearchMessage)) ||
329        ('\0' != string[size - sizeof (struct RegexSearchMessage) - 1]) )
330   {
331     GNUNET_break (0);
332     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
333     return;
334   }
335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336               "Starting to search for `%s'\n",
337               string);
338   ce = GNUNET_new (struct ClientEntry);
339   ce->client = client;
340   ce->sh = REGEX_INTERNAL_search (dht,
341                                   string,
342                                   &handle_search_result,
343                                   ce,
344                                   stats);
345   if (NULL == ce->sh)
346   {
347     GNUNET_break (0);
348     GNUNET_free (ce);
349     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
350     return;
351   }
352   GNUNET_CONTAINER_DLL_insert (client_head,
353                                client_tail,
354                                ce);
355   GNUNET_SERVER_notification_context_add (nc, client);
356   GNUNET_SERVER_receive_done (client, GNUNET_OK);
357 }
358
359
360 /**
361  * Process regex requests.
362  *
363  * @param cls closure
364  * @param server the initialized server
365  * @param cfg configuration to use
366  */
367 static void
368 run (void *cls, struct GNUNET_SERVER_Handle *server,
369      const struct GNUNET_CONFIGURATION_Handle *cfg)
370 {
371   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
372     {&handle_announce, NULL, GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE, 0},
373     {&handle_search, NULL, GNUNET_MESSAGE_TYPE_REGEX_SEARCH, 0},
374     {NULL, NULL, 0, 0}
375   };
376
377   my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
378   if (NULL == my_private_key)
379   {
380     GNUNET_SCHEDULER_shutdown ();
381     return;
382   }
383   dht = GNUNET_DHT_connect (cfg, 1024);
384   if (NULL == dht)
385   {
386     GNUNET_free (my_private_key);
387     my_private_key = NULL;
388     GNUNET_SCHEDULER_shutdown ();
389     return;
390   }
391   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
392                                 &cleanup_task,
393                                 NULL);
394   nc = GNUNET_SERVER_notification_context_create (server, 1);
395   stats = GNUNET_STATISTICS_create ("regex", cfg);
396   GNUNET_SERVER_add_handlers (server, handlers);
397   GNUNET_SERVER_disconnect_notify (server,
398                                    &handle_client_disconnect,
399                                    NULL);
400 }
401
402
403 /**
404  * The main function for the regex service.
405  *
406  * @param argc number of arguments from the command line
407  * @param argv command line arguments
408  * @return 0 ok, 1 on error
409  */
410 int
411 main (int argc, char *const *argv)
412 {
413   return (GNUNET_OK ==
414           GNUNET_SERVICE_run (argc, argv, "regex",
415                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
416 }
417
418 /* end of gnunet-service-regex.c */