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