glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / regex / plugin_block_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/plugin_block_regex.c
18  * @brief blocks used for regex storage and search
19  * @author Bartlomiej Polot
20  */
21 #include "platform.h"
22 #include "gnunet_block_plugin.h"
23 #include "gnunet_block_group_lib.h"
24 #include "block_regex.h"
25 #include "regex_block_lib.h"
26 #include "gnunet_signatures.h"
27
28
29 /**
30  * Number of bits we set per entry in the bloomfilter.
31  * Do not change!
32  */
33 #define BLOOMFILTER_K 16
34
35
36 /**
37  * How big is the BF we use for REGEX blocks?
38  */
39 #define REGEX_BF_SIZE 8
40
41
42 /**
43  * Create a new block group.
44  *
45  * @param ctx block context in which the block group is created
46  * @param type type of the block for which we are creating the group
47  * @param nonce random value used to seed the group creation
48  * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
49  * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
50  * @param va variable arguments specific to @a type
51  * @return block group handle, NULL if block groups are not supported
52  *         by this @a type of block (this is not an error)
53  */
54 static struct GNUNET_BLOCK_Group *
55 block_plugin_regex_create_group (void *cls,
56                                  enum GNUNET_BLOCK_Type type,
57                                  uint32_t nonce,
58                                  const void *raw_data,
59                                  size_t raw_data_size,
60                                  va_list va)
61 {
62   unsigned int bf_size;
63   const char *guard;
64
65   guard = va_arg (va, const char *);
66   if (0 == strcmp (guard,
67                    "seen-set-size"))
68     bf_size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned int),
69                                                            BLOOMFILTER_K);
70   else if (0 == strcmp (guard,
71                         "filter-size"))
72     bf_size = va_arg (va, unsigned int);
73   else
74   {
75     GNUNET_break (0);
76     bf_size = REGEX_BF_SIZE;
77   }
78   GNUNET_break (NULL == va_arg (va, const char *));
79   return GNUNET_BLOCK_GROUP_bf_create (cls,
80                                        bf_size,
81                                        BLOOMFILTER_K,
82                                        type,
83                                        nonce,
84                                        raw_data,
85                                        raw_data_size);
86 }
87
88
89 /**
90  * Function called to validate a reply or a request of type
91  * #GNUNET_BLOCK_TYPE_REGEX.
92  * For request evaluation, pass "NULL" for the reply_block.
93  * Note that it is assumed that the reply has already been
94  * matched to the key (and signatures checked) as it would
95  * be done with the #GNUNET_BLOCK_get_key() function.
96  *
97  * @param cls closure
98  * @param type block type
99  * @param bg block group to evaluate against
100  * @param eo control flags
101  * @param query original query (hash)
102  * @param xquery extrended query data (can be NULL, depending on type)
103  * @param xquery_size number of bytes in @a xquery
104  * @param reply_block response to validate
105  * @param reply_block_size number of bytes in @a reply_block
106  * @return characterization of result
107  */
108 static enum GNUNET_BLOCK_EvaluationResult
109 evaluate_block_regex (void *cls,
110                       enum GNUNET_BLOCK_Type type,
111                       struct GNUNET_BLOCK_Group *bg,
112                       enum GNUNET_BLOCK_EvaluationOptions eo,
113                       const struct GNUNET_HashCode *query,
114                       const void *xquery,
115                       size_t xquery_size,
116                       const void *reply_block,
117                       size_t reply_block_size)
118 {
119   struct GNUNET_HashCode chash;
120
121   if (NULL == reply_block)
122   {
123     if (0 != xquery_size)
124       {
125         const char *s;
126
127         s = (const char *) xquery;
128         if ('\0' != s[xquery_size - 1]) /* must be valid 0-terminated string */
129           {
130             GNUNET_break_op (0);
131             return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
132           }
133       }
134     return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
135   }
136   if (0 != xquery_size)
137   {
138     const char *s;
139
140     s = (const char *) xquery;
141     if ('\0' != s[xquery_size - 1]) /* must be valid 0-terminated string */
142     {
143       GNUNET_break_op (0);
144       return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
145     }
146   }
147   else if (NULL != query)
148   {
149     /* xquery is required for regex GETs, at least an empty string */
150     GNUNET_break_op (0);
151     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "type %d, query %p, xquery %p\n",
152                 type, query, xquery);
153     return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
154   }
155   switch (REGEX_BLOCK_check (reply_block,
156                              reply_block_size,
157                              query,
158                              xquery))
159   {
160     case GNUNET_SYSERR:
161       GNUNET_break_op(0);
162       return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
163     case GNUNET_NO:
164       /* xquery missmatch, can happen */
165       return GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT;
166     default:
167       break;
168   }
169   GNUNET_CRYPTO_hash (reply_block,
170                       reply_block_size,
171                       &chash);
172   if (GNUNET_YES ==
173       GNUNET_BLOCK_GROUP_bf_test_and_set (bg,
174                                           &chash))
175     return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
176   return GNUNET_BLOCK_EVALUATION_OK_MORE;
177 }
178
179
180 /**
181  * Function called to validate a reply or a request of type
182  * #GNUNET_BLOCK_TYPE_REGEX_ACCEPT.
183  * For request evaluation, pass "NULL" for the reply_block.
184  * Note that it is assumed that the reply has already been
185  * matched to the key (and signatures checked) as it would
186  * be done with the #GNUNET_BLOCK_get_key() function.
187  *
188  * @param cls closure
189  * @param type block type
190  * @param bg block group to evaluate against
191  * @param eo control flags
192  * @param query original query (hash)
193  * @param xquery extrended query data (can be NULL, depending on type)
194  * @param xquery_size number of bytes in @a xquery
195  * @param reply_block response to validate
196  * @param reply_block_size number of bytes in @a reply_block
197  * @return characterization of result
198  */
199 static enum GNUNET_BLOCK_EvaluationResult
200 evaluate_block_regex_accept (void *cls,
201                              enum GNUNET_BLOCK_Type type,
202                              struct GNUNET_BLOCK_Group *bg,
203                              enum GNUNET_BLOCK_EvaluationOptions eo,
204                              const struct GNUNET_HashCode *query,
205                              const void *xquery,
206                              size_t xquery_size, const void *reply_block,
207                              size_t reply_block_size)
208 {
209   const struct RegexAcceptBlock *rba;
210   struct GNUNET_HashCode chash;
211
212   if (0 != xquery_size)
213   {
214     GNUNET_break_op (0);
215     return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
216   }
217   if (NULL == reply_block)
218     return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
219   if (sizeof (struct RegexAcceptBlock) != reply_block_size)
220   {
221     GNUNET_break_op(0);
222     return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
223   }
224   rba = reply_block;
225   if (ntohl (rba->purpose.size) !=
226       sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
227       sizeof (struct GNUNET_TIME_AbsoluteNBO) +
228       sizeof (struct GNUNET_HashCode))
229   {
230     GNUNET_break_op(0);
231     return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
232   }
233   if (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (rba->expiration_time)).rel_value_us)
234   {
235     /* technically invalid, but can happen without an error, so
236        we're nice by reporting it as a 'duplicate' */
237     return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
238   }
239   if (GNUNET_OK !=
240       GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT,
241                                 &rba->purpose,
242                                 &rba->signature,
243                                 &rba->peer.public_key))
244   {
245     GNUNET_break_op(0);
246     return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
247   }
248   GNUNET_CRYPTO_hash (reply_block,
249                       reply_block_size,
250                       &chash);
251   if (GNUNET_YES ==
252       GNUNET_BLOCK_GROUP_bf_test_and_set (bg,
253                                           &chash))
254     return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
255   return GNUNET_BLOCK_EVALUATION_OK_MORE;
256 }
257
258
259 /**
260  * Function called to validate a reply or a request.  For
261  * request evaluation, simply pass "NULL" for the reply_block.
262  * Note that it is assumed that the reply has already been
263  * matched to the key (and signatures checked) as it would
264  * be done with the #GNUNET_BLOCK_get_key() function.
265  *
266  * @param cls closure
267  * @param ctx block context
268  * @param type block type
269  * @param bg group to evaluate against
270  * @param eo control flags
271  * @param query original query (hash)
272  * @param xquery extrended query data (can be NULL, depending on type)
273  * @param xquery_size number of bytes in xquery
274  * @param reply_block response to validate
275  * @param reply_block_size number of bytes in reply block
276  * @return characterization of result
277  */
278 static enum GNUNET_BLOCK_EvaluationResult
279 block_plugin_regex_evaluate (void *cls,
280                              struct GNUNET_BLOCK_Context *ctx,
281                              enum GNUNET_BLOCK_Type type,
282                              struct GNUNET_BLOCK_Group *bg,
283                              enum GNUNET_BLOCK_EvaluationOptions eo,
284                              const struct GNUNET_HashCode *query,
285                              const void *xquery,
286                              size_t xquery_size,
287                              const void *reply_block,
288                              size_t reply_block_size)
289 {
290   enum GNUNET_BLOCK_EvaluationResult result;
291
292   switch (type)
293   {
294     case GNUNET_BLOCK_TYPE_REGEX:
295       result = evaluate_block_regex (cls,
296                                      type,
297                                      bg,
298                                      eo,
299                                      query,
300                                      xquery, xquery_size,
301                                      reply_block, reply_block_size);
302       break;
303     case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
304       result = evaluate_block_regex_accept (cls,
305                                             type,
306                                             bg,
307                                             eo,
308                                             query,
309                                             xquery, xquery_size,
310                                             reply_block, reply_block_size);
311       break;
312
313     default:
314       result = GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED;
315   }
316   return result;
317 }
318
319
320 /**
321  * Function called to obtain the key for a block.
322  *
323  * @param cls closure
324  * @param type block type
325  * @param block block to get the key for
326  * @param block_size number of bytes in @a block
327  * @param key set to the key (query) for the given block
328  * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
329  *         (or if extracting a key from a block of this type does not work)
330  */
331 static int
332 block_plugin_regex_get_key (void *cls,
333                             enum GNUNET_BLOCK_Type type,
334                             const void *block,
335                             size_t block_size,
336                             struct GNUNET_HashCode *key)
337 {
338   switch (type)
339   {
340     case GNUNET_BLOCK_TYPE_REGEX:
341       if (GNUNET_OK !=
342           REGEX_BLOCK_get_key (block, block_size,
343                                key))
344       {
345         GNUNET_break_op (0);
346         return GNUNET_NO;
347       }
348       return GNUNET_OK;
349     case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
350       if (sizeof (struct RegexAcceptBlock) != block_size)
351       {
352         GNUNET_break_op (0);
353         return GNUNET_NO;
354       }
355       *key = ((struct RegexAcceptBlock *) block)->key;
356       return GNUNET_OK;
357     default:
358       GNUNET_break (0);
359       return GNUNET_SYSERR;
360   }
361 }
362
363
364 /**
365  * Entry point for the plugin.
366  */
367 void *
368 libgnunet_plugin_block_regex_init (void *cls)
369 {
370   static enum GNUNET_BLOCK_Type types[] =
371   {
372     GNUNET_BLOCK_TYPE_REGEX,
373     GNUNET_BLOCK_TYPE_REGEX_ACCEPT,
374     GNUNET_BLOCK_TYPE_ANY       /* end of list */
375   };
376   struct GNUNET_BLOCK_PluginFunctions *api;
377
378   api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
379   api->evaluate = &block_plugin_regex_evaluate;
380   api->get_key = &block_plugin_regex_get_key;
381   api->create_group = &block_plugin_regex_create_group;
382   api->types = types;
383   return api;
384 }
385
386
387 /**
388  * Exit point from the plugin.
389  */
390 void *
391 libgnunet_plugin_block_regex_done (void *cls)
392 {
393   struct GNUNET_BLOCK_PluginFunctions *api = cls;
394
395   GNUNET_free (api);
396   return NULL;
397 }
398
399 /* end of plugin_block_regex.c */