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