user handbook: Grammar correction.
[oweals/gnunet.git] / src / regex / regex_block_lib.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012,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  * @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  * @return #GNUNET_OK if the proof is valid for the given key.
122  */
123 int
124 REGEX_BLOCK_check_proof (const char *proof,
125                          size_t proof_len,
126                          const struct GNUNET_HashCode *key)
127 {
128   struct GNUNET_HashCode key_check;
129
130   if ( (NULL == proof) || (NULL == key))
131   {
132     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Proof check failed, was NULL.\n");
133     return GNUNET_NO;
134   }
135   GNUNET_CRYPTO_hash (proof, proof_len, &key_check);
136   return (0 ==
137           GNUNET_CRYPTO_hash_cmp (key, &key_check)) ? GNUNET_OK : GNUNET_NO;
138 }
139
140
141 /**
142  * Struct to keep track of the xquery while iterating all the edges in a block.
143  */
144 struct CheckEdgeContext
145 {
146   /**
147    * Xquery: string we are looking for.
148    */
149   const char *xquery;
150
151   /**
152    * Has any edge matched the xquery so far? (GNUNET_OK / GNUNET_NO)
153    */
154   int found;
155
156 };
157
158
159 /**
160  * Iterator over all edges in a block, checking for a presence of a given query.
161  *
162  * @param cls Closure, (xquery context).
163  * @param token Token that follows to next state.
164  * @param len Lenght of token.
165  * @param key Hash of next state.
166  *
167  * @return #GNUNET_YES, to keep iterating
168  */
169 static int
170 check_edge (void *cls,
171             const char *token,
172             size_t len,
173             const struct GNUNET_HashCode *key)
174 {
175   struct CheckEdgeContext *ctx = cls;
176
177   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
178               "edge %.*s [%u]: %s\n",
179               (int) len,
180               token,
181               (unsigned int) len,
182               GNUNET_h2s (key));
183   if (NULL == ctx->xquery)
184     return GNUNET_YES;
185   if (strlen (ctx->xquery) < len)
186     return GNUNET_YES; /* too long */
187   if (0 == strncmp (ctx->xquery, token, len))
188     ctx->found = GNUNET_OK;
189   return GNUNET_YES; /* keep checking for malformed data! */
190 }
191
192
193 /**
194  * Check if the regex block is well formed, including all edges.
195  *
196  * @param block The start of the block.
197  * @param size The size of the block.
198  * @param query the query for the block
199  * @param xquery String describing the edge we are looking for.
200  *               Can be NULL in case this is a put block.
201  * @return #GNUNET_OK in case it's fine.
202  *         #GNUNET_NO in case the xquery exists and is not found (IRRELEVANT).
203  *         #GNUNET_SYSERR if the block is invalid.
204  */
205 int
206 REGEX_BLOCK_check (const struct RegexBlock *block,
207                    size_t size,
208                    const struct GNUNET_HashCode *query,
209                    const char *xquery)
210 {
211   struct GNUNET_HashCode key;
212   struct CheckEdgeContext ctx;
213   int res;
214
215   LOG (GNUNET_ERROR_TYPE_DEBUG,
216        "Block check\n");
217   if (GNUNET_OK !=
218       REGEX_BLOCK_get_key (block, size,
219                            &key))
220   {
221     GNUNET_break_op (0);
222     return GNUNET_SYSERR;
223   }
224   if (NULL != query &&
225       0 != memcmp (&key,
226                    query,
227                    sizeof (struct GNUNET_HashCode)))
228   {
229     GNUNET_break_op (0);
230     return GNUNET_SYSERR;
231   }
232   if ( (GNUNET_YES == ntohs (block->is_accepting)) &&
233        ( (NULL == xquery) || ('\0' == xquery[0]) ) )
234   {
235     LOG (GNUNET_ERROR_TYPE_DEBUG,
236          "  out! Is accepting: %u, xquery %p\n",
237          ntohs(block->is_accepting),
238          xquery);
239     return GNUNET_OK;
240   }
241   ctx.xquery = xquery;
242   ctx.found = GNUNET_NO;
243   res = REGEX_BLOCK_iterate (block, size, &check_edge, &ctx);
244   if (GNUNET_SYSERR == res)
245     return GNUNET_SYSERR;
246   if (NULL == xquery)
247     return GNUNET_YES;
248   LOG (GNUNET_ERROR_TYPE_DEBUG, "Result %d\n", ctx.found);
249   return ctx.found;
250 }
251
252
253 /**
254  * Obtain the key that a particular block is to be stored under.
255  *
256  * @param block block to get the key from
257  * @param block_len number of bytes in block
258  * @param key where to store the key
259  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block is malformed
260  */
261 int
262 REGEX_BLOCK_get_key (const struct RegexBlock *block,
263                      size_t block_len,
264                      struct GNUNET_HashCode *key)
265 {
266   uint16_t len;
267   const struct GNUNET_HashCode *destinations;
268   const struct EdgeInfo *edges;
269   uint16_t num_destinations;
270   uint16_t num_edges;
271   size_t total;
272
273   if (block_len < sizeof (struct RegexBlock))
274   {
275     GNUNET_break_op (0);
276     return GNUNET_SYSERR;
277   }
278   num_destinations = ntohs (block->num_destinations);
279   num_edges = ntohs (block->num_edges);
280   len = ntohs (block->proof_len);
281   destinations = (const struct GNUNET_HashCode *) &block[1];
282   edges = (const struct EdgeInfo *) &destinations[num_destinations];
283   total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges * sizeof (struct EdgeInfo) + len;
284   if (block_len < total)
285   {
286     GNUNET_break_op (0);
287     return GNUNET_SYSERR;
288   }
289   GNUNET_CRYPTO_hash (&edges[num_edges], len, key);
290   return GNUNET_OK;
291 }
292
293
294 /**
295  * Iterate over all edges of a block of a regex state.
296  *
297  * @param block Block to iterate over.
298  * @param size Size of @a block.
299  * @param iterator Function to call on each edge in the block.
300  * @param iter_cls Closure for the @a iterator.
301  * @return #GNUNET_SYSERR if an error has been encountered.
302  *         #GNUNET_OK if no error has been encountered.
303  *           Note that if the iterator stops the iteration by returning
304  *         #GNUNET_NO, the block will no longer be checked for further errors.
305  *           The return value will be GNUNET_OK meaning that no errors were
306  *         found until the edge last notified to the iterator, but there might
307  *         be errors in further edges.
308  */
309 int
310 REGEX_BLOCK_iterate (const struct RegexBlock *block,
311                      size_t size,
312                      REGEX_INTERNAL_EgdeIterator iterator,
313                      void *iter_cls)
314 {
315   uint16_t len;
316   const struct GNUNET_HashCode *destinations;
317   const struct EdgeInfo *edges;
318   const char *aux;
319   uint16_t num_destinations;
320   uint16_t num_edges;
321   size_t total;
322   unsigned int n;
323   size_t off;
324
325   LOG (GNUNET_ERROR_TYPE_DEBUG, "Block iterate\n");
326   if (size < sizeof (struct RegexBlock))
327   {
328     GNUNET_break_op (0);
329     return GNUNET_SYSERR;
330   }
331   num_destinations = ntohs (block->num_destinations);
332   num_edges = ntohs (block->num_edges);
333   len = ntohs (block->proof_len);
334   destinations = (const struct GNUNET_HashCode *) &block[1];
335   edges = (const struct EdgeInfo *) &destinations[num_destinations];
336   aux = (const char *) &edges[num_edges];
337   total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges * sizeof (struct EdgeInfo) + len;
338   if (size < total)
339   {
340     GNUNET_break_op (0);
341     return GNUNET_SYSERR;
342   }
343   for (n=0;n<num_edges;n++)
344     total += ntohs (edges[n].token_length);
345   if (size != total)
346   {
347     fprintf (stderr, "Expected %u, got %u\n",
348              (unsigned int) size,
349              (unsigned int) total);
350     GNUNET_break_op (0);
351     return GNUNET_SYSERR;
352   }
353   off = len;
354   LOG (GNUNET_ERROR_TYPE_DEBUG,
355        "Start iterating block of size %u, proof %u, off %u edges %u\n",
356        size, len, off, n);
357   /* &aux[off] always points to our token */
358   for (n=0;n<num_edges;n++)
359   {
360     LOG (GNUNET_ERROR_TYPE_DEBUG,
361          "Edge %u/%u, off %u tokenlen %u (%.*s)\n",
362          n+1, num_edges, off,
363          ntohs (edges[n].token_length), ntohs (edges[n].token_length),
364          &aux[off]);
365     if (NULL != iterator)
366       if (GNUNET_NO == iterator (iter_cls,
367                                  &aux[off],
368                                  ntohs (edges[n].token_length),
369                                  &destinations[ntohs (edges[n].destination_index)]))
370         return GNUNET_OK;
371     off += ntohs (edges[n].token_length);
372   }
373   return GNUNET_OK;
374 }
375
376
377 /**
378  * Construct a regex block to be stored in the DHT.
379  *
380  * @param proof proof string for the block
381  * @param num_edges number of edges in the block
382  * @param edges the edges of the block
383  * @param accepting is this an accepting state
384  * @param rsize set to the size of the returned block (OUT-only)
385  * @return the regex block, NULL on error
386  */
387 struct RegexBlock *
388 REGEX_BLOCK_create (const char *proof,
389                     unsigned int num_edges,
390                     const struct REGEX_BLOCK_Edge *edges,
391                     int accepting,
392                     size_t *rsize)
393 {
394   struct RegexBlock *block;
395   struct GNUNET_HashCode destinations[1024]; /* 1024 = 64k/64 bytes/key == absolute MAX */
396   uint16_t destination_indices[num_edges];
397   struct GNUNET_HashCode *dests;
398   struct EdgeInfo *edgeinfos;
399   size_t off;
400   size_t len;
401   size_t total;
402   size_t slen;
403   unsigned int unique_destinations;
404   unsigned int j;
405   unsigned int i;
406   char *aux;
407
408   len = strlen (proof);
409   if (len > UINT16_MAX)
410   {
411     GNUNET_break (0);
412     return NULL;
413   }
414   unique_destinations = 0;
415   total = sizeof (struct RegexBlock) + len;
416   for (i=0;i<num_edges;i++)
417   {
418     slen = strlen (edges[i].label);
419     if (slen > UINT16_MAX)
420     {
421       GNUNET_break (0);
422       return NULL;
423     }
424     total += slen;
425     for (j=0;j<unique_destinations;j++)
426       if (0 == memcmp (&destinations[j],
427                        &edges[i].destination,
428                        sizeof (struct GNUNET_HashCode)))
429         break;
430     if (j >= 1024)
431     {
432       GNUNET_break (0);
433       return NULL;
434     }
435     destination_indices[i] = j;
436     if (j == unique_destinations)
437       destinations[unique_destinations++] = edges[i].destination;
438   }
439   total += num_edges * sizeof (struct EdgeInfo) + unique_destinations * sizeof (struct GNUNET_HashCode);
440   if (total >= GNUNET_CONSTANTS_MAX_BLOCK_SIZE)
441   {
442     GNUNET_break (0);
443     return NULL;
444   }
445   block = GNUNET_malloc (total);
446   block->proof_len = htons (len);
447   block->is_accepting = htons (accepting);
448   block->num_edges = htons (num_edges);
449   block->num_destinations = htons (unique_destinations);
450   dests = (struct GNUNET_HashCode *) &block[1];
451   GNUNET_memcpy (dests, destinations, sizeof (struct GNUNET_HashCode) * unique_destinations);
452   edgeinfos = (struct EdgeInfo *) &dests[unique_destinations];
453   aux = (char *) &edgeinfos[num_edges];
454   off = len;
455   GNUNET_memcpy (aux, proof, len);
456   for (i=0;i<num_edges;i++)
457   {
458     slen = strlen (edges[i].label);
459     edgeinfos[i].token_length = htons ((uint16_t) slen);
460     edgeinfos[i].destination_index = htons (destination_indices[i]);
461     GNUNET_memcpy (&aux[off],
462             edges[i].label,
463             slen);
464     off += slen;
465   }
466   *rsize = total;
467   return block;
468 }
469
470
471 /* end of regex_block_lib.c */