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