a2de3d4f8ba948948d8f4bbe1f23546173fde9ea
[oweals/gnunet.git] / src / regex / regex_block_lib.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012,2013 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @author Bartlomiej Polot
22  * @file regex/regex_block_lib.c
23  * @brief functions for manipulating non-accept blocks stored for
24  *        regex in the DHT
25  */
26 #include "platform.h"
27 #include "regex_block_lib.h"
28 #include "gnunet_constants.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind,"regex-bck",__VA_ARGS__)
31
32 GNUNET_NETWORK_STRUCT_BEGIN
33
34 /**
35  * Information for each edge.
36  */
37 struct EdgeInfo
38 {
39   /**
40    * Index of the destination of this edge in the
41    * unique destinations array.
42    */
43   uint16_t destination_index GNUNET_PACKED;
44
45   /**
46    * Number of bytes the token for this edge takes in the
47    * token area.
48    */
49   uint16_t token_length GNUNET_PACKED;
50 };
51
52
53 /**
54  * @brief Block to announce a regex state.
55  */
56 struct RegexBlock
57 {
58
59   /**
60    * Length of the proof regex string.
61    */
62   uint16_t proof_len GNUNET_PACKED;
63
64   /**
65    * Is this state an accepting state?
66    */
67   int16_t is_accepting GNUNET_PACKED;
68
69   /**
70    * Number of edges parting from this state.
71    */
72   uint16_t num_edges GNUNET_PACKED;
73
74   /**
75    * Nubmer of unique destinations reachable from this state.
76    */
77   uint16_t num_destinations GNUNET_PACKED;
78
79   /* followed by 'struct GNUNET_HashCode[num_destinations]' */
80
81   /* followed by 'struct EdgeInfo[edge_destination_indices]' */
82
83   /* followed by 'char proof[n_proof]', NOT 0-terminated */
84
85   /* followed by 'char tokens[num_edges][edge_info[k].token_length]';
86      essentially all of the tokens one after the other in the
87      order of the edges; tokens are NOT 0-terminated */
88
89 };
90
91
92 GNUNET_NETWORK_STRUCT_END
93
94
95 /**
96  * Test if this block is marked as being an accept state.
97  *
98  * @param block block to test
99  * @param size number of bytes in block
100  * @return GNUNET_YES if the block is accepting, GNUNET_NO if not
101  */ 
102 int
103 GNUNET_BLOCK_is_accepting (const struct RegexBlock *block,
104                            size_t size)
105 {
106   if (size < sizeof (struct RegexBlock))
107   {
108     GNUNET_break_op (0);
109     return GNUNET_SYSERR;
110   }
111   return ntohs (block->is_accepting);
112 }
113
114
115 /**
116  * Check if the given 'proof' matches the given 'key'.
117  *
118  * @param proof partial regex of a state
119  * @param proof_len number of bytes in 'proof'
120  * @param key hash of a state.
121  *
122  * @return GNUNET_OK if the proof is valid for the given key.
123  */
124 int
125 REGEX_BLOCK_check_proof (const char *proof,
126                          size_t proof_len,
127                          const struct GNUNET_HashCode *key)
128 {
129   struct GNUNET_HashCode key_check;
130
131   if ( (NULL == proof) || (NULL == key))
132   {
133     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Proof check failed, was NULL.\n");
134     return GNUNET_NO;
135   }
136   GNUNET_CRYPTO_hash (proof, proof_len, &key_check);
137   return (0 ==
138           GNUNET_CRYPTO_hash_cmp (key, &key_check)) ? GNUNET_OK : GNUNET_NO;
139 }
140
141
142 /**
143  * Struct to keep track of the xquery while iterating all the edges in a block.
144  */
145 struct CheckEdgeContext
146 {
147   /**
148    * Xquery: string we are looking for.
149    */
150   const char *xquery;
151
152   /**
153    * Has any edge matched the xquery so far? (GNUNET_OK / GNUNET_NO)
154    */
155   int found;
156
157 };
158
159
160 /**
161  * Iterator over all edges in a block, checking for a presence of a given query.
162  *
163  * @param cls Closure, (xquery context).
164  * @param token Token that follows to next state.
165  * @param len Lenght of token.
166  * @param key Hash of next state.
167  * 
168  * @return GNUNET_YES, to keep iterating
169  */
170 static int
171 check_edge (void *cls,
172             const char *token,
173             size_t len,
174             const struct GNUNET_HashCode *key)
175 {
176   struct CheckEdgeContext *ctx = cls;
177
178   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
179               "edge %.*s [%u]: %s->%s\n",
180               (int) len, token, len, GNUNET_h2s(key));
181   if (NULL == ctx->xquery)
182     return GNUNET_YES;
183   if (strlen (ctx->xquery) < len)
184     return GNUNET_YES; /* too long */
185   if (0 == strncmp (ctx->xquery, token, len))
186     ctx->found = GNUNET_OK;
187   return GNUNET_YES; /* keep checking for malformed data! */
188 }
189
190
191 /**
192  * Check if the regex block is well formed, including all edges.
193  *
194  * @param block The start of the block.
195  * @param size The size of the block.
196  * @param query the query for the block
197  * @param xquery String describing the edge we are looking for.
198  *               Can be NULL in case this is a put block.
199  *
200  * @return GNUNET_OK in case it's fine.
201  *         GNUNET_NO in case the xquery exists and is not found (IRRELEVANT).
202  *         GNUNET_SYSERR if the block is invalid.
203  */
204 int
205 REGEX_BLOCK_check (const struct RegexBlock *block,
206                    size_t size,
207                    const struct GNUNET_HashCode *query,
208                    const char *xquery)
209 {
210   struct GNUNET_HashCode key;
211   struct CheckEdgeContext ctx;
212   int res;
213
214   LOG (GNUNET_ERROR_TYPE_DEBUG, "Block check\n");
215   if (GNUNET_OK != 
216       REGEX_BLOCK_get_key (block, size,
217                            &key))
218   {
219     GNUNET_break_op (0);
220     return GNUNET_SYSERR;
221   }
222   if (NULL != query &&
223       0 != memcmp (&key,
224                    query,
225                    sizeof (struct GNUNET_HashCode)))
226   {
227     GNUNET_break_op (0);
228     return GNUNET_SYSERR;
229   }
230   if ( (GNUNET_YES == ntohs (block->is_accepting)) &&
231        ( (NULL == xquery) || ('\0' == xquery[0]) ) )
232   {
233     LOG (GNUNET_ERROR_TYPE_DEBUG,
234        "  out! Is accepting: %u, xquery %p\n",
235        ntohs(block->is_accepting), xquery);
236     return GNUNET_OK;
237   }
238   ctx.xquery = xquery;
239   ctx.found = GNUNET_NO;
240   res = REGEX_BLOCK_iterate (block, size, &check_edge, &ctx);
241   if (GNUNET_SYSERR == res)
242     return GNUNET_SYSERR;
243   if (NULL == xquery)
244     return GNUNET_YES;
245   LOG (GNUNET_ERROR_TYPE_DEBUG, "Result %d\n", ctx.found);
246   return ctx.found;
247 }
248
249
250 /**
251  * Obtain the key that a particular block is to be stored under.
252  *
253  * @param block block to get the key from
254  * @param block_len number of bytes in block
255  * @param key where to store the key
256  * @return GNUNET_OK on success, GNUNET_SYSERR if the block is malformed
257  */
258 int
259 REGEX_BLOCK_get_key (const struct RegexBlock *block,
260                      size_t block_len,
261                      struct GNUNET_HashCode *key)
262 {
263   uint16_t len;
264   const struct GNUNET_HashCode *destinations;
265   const struct EdgeInfo *edges;
266   uint16_t num_destinations;
267   uint16_t num_edges;
268   size_t total;
269
270   if (block_len < sizeof (struct RegexBlock)) 
271   {
272     GNUNET_break_op (0);
273     return GNUNET_SYSERR;
274   }  
275   num_destinations = ntohs (block->num_destinations);
276   num_edges = ntohs (block->num_edges);
277   len = ntohs (block->proof_len);
278   destinations = (const struct GNUNET_HashCode *) &block[1];
279   edges = (const struct EdgeInfo *) &destinations[num_destinations];
280   total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges + sizeof (struct EdgeInfo) + len;  
281   if (block_len < total)
282   {
283     GNUNET_break_op (0);
284     return GNUNET_SYSERR;
285   }
286   GNUNET_CRYPTO_hash (&edges[num_edges], len, key);
287   return GNUNET_OK;
288 }
289
290
291 /**
292  * Iterate over all edges of a block of a regex state.
293  *
294  * @param block Block to iterate over.
295  * @param size Size of block.
296  * @param iterator Function to call on each edge in the block.
297  * @param iter_cls Closure for the iterator.
298  *
299  * @return GNUNET_SYSERR if an error has been encountered.
300  *         GNUNET_OK if no error has been encountered.
301  *           Note that if the iterator stops the iteration by returning
302  *         GNUNET_NO, the block will no longer be checked for further errors.
303  *           The return value will be GNUNET_OK meaning that no errors were
304  *         found until the edge last notified to the iterator, but there might
305  *         be errors in further edges.
306  */
307 int
308 REGEX_BLOCK_iterate (const struct RegexBlock *block,
309                      size_t size,
310                      REGEX_INTERNAL_EgdeIterator iterator,
311                      void *iter_cls)
312 {
313   uint16_t len;
314   const struct GNUNET_HashCode *destinations;
315   const struct EdgeInfo *edges;
316   const char *aux;
317   uint16_t num_destinations;
318   uint16_t num_edges;
319   size_t total;
320   unsigned int n;
321   size_t off;
322
323   LOG (GNUNET_ERROR_TYPE_DEBUG, "Block iterate\n");
324   if (size < sizeof (struct RegexBlock)) 
325   {
326     GNUNET_break_op (0);
327     return GNUNET_SYSERR;
328   }
329   num_destinations = ntohs (block->num_destinations);
330   num_edges = ntohs (block->num_edges);
331   len = ntohs (block->proof_len);
332   destinations = (const struct GNUNET_HashCode *) &block[1];
333   edges = (const struct EdgeInfo *) &destinations[num_destinations];
334   aux = (const char *) &edges[num_edges];
335   total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges * sizeof (struct EdgeInfo) + len;
336   if (size < total) 
337   {
338     GNUNET_break_op (0);
339     return GNUNET_SYSERR;
340   }
341   for (n=0;n<num_edges;n++)
342     total += ntohs (edges[n].token_length);    
343   if (size != total) 
344   {
345     fprintf (stderr, "Expected %u, got %u\n",
346              (unsigned int) size,
347              (unsigned int) total);
348     GNUNET_break_op (0);
349     return GNUNET_SYSERR;
350   }
351   off = len;
352   LOG (GNUNET_ERROR_TYPE_DEBUG,
353        "Start iterating block of size %u, proof %u, off %u edges %u\n",
354        size, len, off, n);
355   /* &aux[off] always points to our token */
356   for (n=0;n<num_edges;n++)
357   {
358     LOG (GNUNET_ERROR_TYPE_DEBUG,
359          " Edge %u/%u, off %u tokenlen %u (%.*s)\n", n+1, num_edges, off,
360          ntohs (edges[n].token_length), ntohs (edges[n].token_length),
361          &aux[off]);
362     if (NULL != iterator)
363       if (GNUNET_NO == iterator (iter_cls, 
364                                  &aux[off], 
365                                  ntohs (edges[n].token_length),
366                                  &destinations[ntohs (edges[n].destination_index)]))
367         return GNUNET_OK;
368     off += ntohs (edges[n].token_length);
369   }
370   return GNUNET_OK;
371 }
372
373
374 /**
375  * Construct a regex block to be stored in the DHT.
376  *
377  * @param proof proof string for the block
378  * @param num_edges number of edges in the block
379  * @param edges the edges of the block
380  * @param accepting is this an accepting state
381  * @param rsize set to the size of the returned block (OUT-only)
382  * @return the regex block, NULL on error
383  */
384 struct RegexBlock *
385 REGEX_BLOCK_create (const char *proof,
386                     unsigned int num_edges,
387                     const struct REGEX_BLOCK_Edge *edges,
388                     int accepting,
389                     size_t *rsize)
390 {
391   struct RegexBlock *block;
392   struct GNUNET_HashCode destinations[1024]; /* 1024 = 64k/64 bytes/key == absolute MAX */
393   uint16_t destination_indices[num_edges];
394   struct GNUNET_HashCode *dests;
395   struct EdgeInfo *edgeinfos;
396   size_t off;
397   size_t len;
398   size_t total;
399   size_t slen;
400   unsigned int unique_destinations;
401   unsigned int j;
402   unsigned int i;
403   char *aux;
404
405   len = strlen (proof);
406   if (len > UINT16_MAX) 
407   {
408     GNUNET_break (0);
409     return NULL;
410   }
411   unique_destinations = 0;
412   total = sizeof (struct RegexBlock) + len;
413   for (i=0;i<num_edges;i++)
414   {
415     slen = strlen (edges[i].label);
416     if (slen > UINT16_MAX) 
417     {
418       GNUNET_break (0);
419       return NULL;
420     }
421     total += slen;
422     for (j=0;j<unique_destinations;j++)
423       if (0 == memcmp (&destinations[j],
424                        &edges[i].destination,
425                        sizeof (struct GNUNET_HashCode)))
426         break;
427     if (j >= 1024)
428     {
429       GNUNET_break (0);
430       return NULL;
431     }
432     destination_indices[i] = j;
433     if (j == unique_destinations)
434       destinations[unique_destinations++] = edges[i].destination;
435   }
436   total += num_edges * sizeof (struct EdgeInfo) + unique_destinations * sizeof (struct GNUNET_HashCode);
437   if (total >= GNUNET_CONSTANTS_MAX_BLOCK_SIZE)
438   {
439     GNUNET_break (0);
440     return NULL;
441   }
442   block = GNUNET_malloc (total);
443   block->proof_len = htons (len);
444   block->is_accepting = htons (accepting);
445   block->num_edges = htons (num_edges);
446   block->num_destinations = htons (unique_destinations);
447   dests = (struct GNUNET_HashCode *) &block[1];
448   memcpy (dests, destinations, sizeof (struct GNUNET_HashCode) * unique_destinations);
449   edgeinfos = (struct EdgeInfo *) &dests[unique_destinations];
450   aux = (char *) &edgeinfos[num_edges];
451   off = len;
452   memcpy (aux, proof, len); 
453   for (i=0;i<num_edges;i++)
454   {
455     slen = strlen (edges[i].label);
456     edgeinfos[i].token_length = htons ((uint16_t) slen);
457     edgeinfos[i].destination_index = htons (destination_indices[i]);
458     memcpy (&aux[off],
459             edges[i].label,
460             slen);
461     off += slen;
462   }
463   *rsize = total;
464   return block;
465 }
466
467
468 /* end of regex_block_lib.c */