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