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