Merge branch 'master' of git+ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / regex / gnunet-service-regex.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013 GNUnet e.V.
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  */
156 static void
157 cleanup_task (void *cls)
158 {
159   struct ClientEntry *ce;
160
161   while (NULL != (ce = client_head))
162     handle_client_disconnect (NULL,
163                               ce->client);
164   GNUNET_DHT_disconnect (dht);
165   dht = NULL;
166   GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
167   stats = NULL;
168   GNUNET_SERVER_notification_context_destroy (nc);
169   nc = NULL;
170   GNUNET_free (my_private_key);
171   my_private_key = NULL;
172 }
173
174
175 /**
176  * Periodic task to refresh our announcement of the regex.
177  *
178  * @param cls the `struct ClientEntry *` of the client that triggered the
179  *        announcement
180  */
181 static void
182 reannounce (void *cls)
183 {
184   struct ClientEntry *ce = cls;
185
186   REGEX_INTERNAL_reannounce (ce->ah);
187   ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
188                                                    &reannounce,
189                                                    ce);
190 }
191
192
193 /**
194  * Handle ANNOUNCE message.
195  *
196  * @param cls closure
197  * @param client identification of the client
198  * @param message the actual message
199  */
200 static void
201 handle_announce (void *cls,
202                  struct GNUNET_SERVER_Client *client,
203                  const struct GNUNET_MessageHeader *message)
204 {
205   const struct AnnounceMessage *am;
206   const char *regex;
207   struct ClientEntry *ce;
208   uint16_t size;
209
210   size = ntohs (message->size);
211   am = (const struct AnnounceMessage *) message;
212   regex = (const char *) &am[1];
213   if ( (size <= sizeof (struct AnnounceMessage)) ||
214        ('\0' != regex[size - sizeof (struct AnnounceMessage) - 1]) )
215   {
216     GNUNET_break (0);
217     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
218     return;
219   }
220
221   ce = GNUNET_new (struct ClientEntry);
222   ce->client = client;
223   ce->frequency = GNUNET_TIME_relative_ntoh (am->refresh_delay);
224   ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
225                                                    &reannounce,
226                                                    ce);
227   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228               "Starting to announce regex `%s' every %s\n",
229               regex,
230               GNUNET_STRINGS_relative_time_to_string (ce->frequency,
231                                                       GNUNET_NO));
232   ce->ah = REGEX_INTERNAL_announce (dht,
233                                     my_private_key,
234                                     regex,
235                                     ntohs (am->compression),
236                                     stats);
237   if (NULL == ce->ah)
238   {
239     GNUNET_break (0);
240     GNUNET_SCHEDULER_cancel (ce->refresh_task);
241     GNUNET_free (ce);
242     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
243     return;
244   }
245   GNUNET_CONTAINER_DLL_insert (client_head,
246                                client_tail,
247                                ce);
248   GNUNET_SERVER_receive_done (client, GNUNET_OK);
249 }
250
251
252 /**
253  * Handle result, pass it back to the client.
254  *
255  * @param cls the struct ClientEntry of the client searching
256  * @param id Peer providing a regex that matches the string.
257  * @param get_path Path of the get request.
258  * @param get_path_length Lenght of @a get_path.
259  * @param put_path Path of the put request.
260  * @param put_path_length Length of the @a put_path.
261  */
262 static void
263 handle_search_result (void *cls,
264                       const struct GNUNET_PeerIdentity *id,
265                       const struct GNUNET_PeerIdentity *get_path,
266                       unsigned int get_path_length,
267                       const struct GNUNET_PeerIdentity *put_path,
268                       unsigned int put_path_length)
269 {
270   struct ClientEntry *ce = cls;
271   struct ResultMessage *result;
272   struct GNUNET_PeerIdentity *gp;
273   uint16_t size;
274
275   if ( (get_path_length >= 65536) ||
276        (put_path_length >= 65536) ||
277        ( (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity))
278        + sizeof (struct ResultMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
279   {
280     GNUNET_break (0);
281     return;
282   }
283   size = (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity) + sizeof (struct ResultMessage);
284   result = GNUNET_malloc (size);
285   result->header.size = htons (size);
286   result->header.type = htons (GNUNET_MESSAGE_TYPE_REGEX_RESULT);
287   result->get_path_length = htons ((uint16_t) get_path_length);
288   result->put_path_length = htons ((uint16_t) put_path_length);
289   result->id = *id;
290   gp = &result->id;
291   GNUNET_memcpy (&gp[1],
292           get_path,
293           get_path_length * sizeof (struct GNUNET_PeerIdentity));
294   GNUNET_memcpy (&gp[1 + get_path_length],
295           put_path,
296           put_path_length * sizeof (struct GNUNET_PeerIdentity));
297   GNUNET_SERVER_notification_context_unicast (nc,
298                                               ce->client,
299                                               &result->header, GNUNET_NO);
300   GNUNET_free (result);
301 }
302
303
304 /**
305  * Handle SEARCH message.
306  *
307  * @param cls closure
308  * @param client identification of the client
309  * @param message the actual message
310  */
311 static void
312 handle_search (void *cls,
313                struct GNUNET_SERVER_Client *client,
314                const struct GNUNET_MessageHeader *message)
315 {
316   const struct RegexSearchMessage *sm;
317   const char *string;
318   struct ClientEntry *ce;
319   uint16_t size;
320
321   size = ntohs (message->size);
322   sm = (const struct RegexSearchMessage *) message;
323   string = (const char *) &sm[1];
324   if ( (size <= sizeof (struct RegexSearchMessage)) ||
325        ('\0' != string[size - sizeof (struct RegexSearchMessage) - 1]) )
326   {
327     GNUNET_break (0);
328     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
329     return;
330   }
331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
332               "Starting to search for `%s'\n",
333               string);
334   ce = GNUNET_new (struct ClientEntry);
335   ce->client = client;
336   ce->sh = REGEX_INTERNAL_search (dht,
337                                   string,
338                                   &handle_search_result,
339                                   ce,
340                                   stats);
341   if (NULL == ce->sh)
342   {
343     GNUNET_break (0);
344     GNUNET_free (ce);
345     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
346     return;
347   }
348   GNUNET_CONTAINER_DLL_insert (client_head,
349                                client_tail,
350                                ce);
351   GNUNET_SERVER_notification_context_add (nc, client);
352   GNUNET_SERVER_receive_done (client, GNUNET_OK);
353 }
354
355
356 /**
357  * Process regex requests.
358  *
359  * @param cls closure
360  * @param server the initialized server
361  * @param cfg configuration to use
362  */
363 static void
364 run (void *cls, struct GNUNET_SERVER_Handle *server,
365      const struct GNUNET_CONFIGURATION_Handle *cfg)
366 {
367   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
368     {&handle_announce, NULL, GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE, 0},
369     {&handle_search, NULL, GNUNET_MESSAGE_TYPE_REGEX_SEARCH, 0},
370     {NULL, NULL, 0, 0}
371   };
372
373   my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
374   if (NULL == my_private_key)
375   {
376     GNUNET_SCHEDULER_shutdown ();
377     return;
378   }
379   dht = GNUNET_DHT_connect (cfg, 1024);
380   if (NULL == dht)
381   {
382     GNUNET_free (my_private_key);
383     my_private_key = NULL;
384     GNUNET_SCHEDULER_shutdown ();
385     return;
386   }
387   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
388                                  NULL);
389   nc = GNUNET_SERVER_notification_context_create (server, 1);
390   stats = GNUNET_STATISTICS_create ("regex", cfg);
391   GNUNET_SERVER_add_handlers (server, handlers);
392   GNUNET_SERVER_disconnect_notify (server,
393                                    &handle_client_disconnect,
394                                    NULL);
395 }
396
397
398 /**
399  * The main function for the regex service.
400  *
401  * @param argc number of arguments from the command line
402  * @param argv command line arguments
403  * @return 0 ok, 1 on error
404  */
405 int
406 main (int argc, char *const *argv)
407 {
408   return (GNUNET_OK ==
409           GNUNET_SERVICE_run (argc, argv, "regex",
410                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
411 }
412
413 /* end of gnunet-service-regex.c */