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