changing time measurement from milliseconds to microseconds
[oweals/gnunet.git] / src / gns / plugin_block_gns.c
1 /*
2      This file is part of GNUnet
3      (C) 2010, 2012 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 /**
22  * @file gns/plugin_block_gns.c
23  * @brief blocks used for GNS records
24  * @author Martin Schanzenbach
25  */
26
27 #include "platform.h"
28 #include "gnunet_block_plugin.h"
29 #include "gnunet_namestore_service.h"
30 #include "block_gns.h"
31 #include "gnunet_signatures.h"
32 #include "gns_common.h"
33
34 /**
35  * Number of bits we set per entry in the bloomfilter.
36  * Do not change! -from fs
37  */
38 #define BLOOMFILTER_K 16
39
40 /**
41  * Function called to validate a reply or a request.  For
42  * request evaluation, simply pass "NULL" for the reply_block.
43  * Note that it is assumed that the reply has already been
44  * matched to the key (and signatures checked) as it would
45  * be done with the "get_key" function.
46  *
47  * @param cls closure
48  * @param type block type
49  * @param query original query (hash)
50  * @param bf pointer to bloom filter associated with query; possibly updated (!)
51  * @param bf_mutator mutation value for bf
52  * @param xquery extrended query data (can be NULL, depending on type)
53  * @param xquery_size number of bytes in xquery
54  * @param reply_block response to validate
55  * @param reply_block_size number of bytes in reply block
56  * @return characterization of result
57  */
58 static enum GNUNET_BLOCK_EvaluationResult
59 block_plugin_gns_evaluate (void *cls, enum GNUNET_BLOCK_Type type,
60                           const struct GNUNET_HashCode * query,
61                           struct GNUNET_CONTAINER_BloomFilter **bf,
62                           int32_t bf_mutator, const void *xquery,
63                           size_t xquery_size, const void *reply_block,
64                           size_t reply_block_size)
65 {
66   const struct GNSNameRecordBlock *nrb;
67   const char* name;
68   const char *name_end;
69   const char *rd_data;
70   struct GNUNET_HashCode query_key;
71   struct GNUNET_HashCode mhash;
72   struct GNUNET_HashCode chash;
73   struct GNUNET_CRYPTO_ShortHashCode pkey_hash;
74   struct GNUNET_CRYPTO_HashAsciiEncoded xor_exp;
75   struct GNUNET_CRYPTO_HashAsciiEncoded xor_got;
76   uint32_t rd_count;
77   size_t rd_len;
78   size_t name_len;
79   uint32_t record_xquery;
80   unsigned int record_match;
81   
82   //GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "RB SIZE %d\n", reply_block_size);
83
84   if (type != GNUNET_BLOCK_TYPE_GNS_NAMERECORD)
85     return GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED;
86   if (reply_block == NULL)
87   {
88     /**
89      *  check if request is valid
90      *  FIXME we could check for the record types here
91      **/
92     if (xquery_size < sizeof(uint32_t))
93     {
94       GNUNET_break_op (0);
95       return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
96     }
97     return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
98   }
99   
100   /* this is a reply */
101
102   nrb = (const struct GNSNameRecordBlock *)reply_block;
103   name = (const char*) &nrb[1];
104   name_end = memchr (name, 0, reply_block_size - sizeof (struct GNSNameRecordBlock));
105   if (NULL == name_end)
106   {
107     GNUNET_break_op (0);
108     return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
109   }
110   name_len = (name_end - name) + 1;
111   GNUNET_CRYPTO_short_hash (&nrb->public_key,
112                             sizeof(nrb->public_key),
113                             &pkey_hash);
114   GNUNET_GNS_get_key_for_record (name, &pkey_hash, &query_key);
115   
116   GNUNET_CRYPTO_hash_to_enc (&query_key, &xor_exp);
117   GNUNET_CRYPTO_hash_to_enc (query, &xor_got);
118
119   //GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
120   //           "BLOCK_TEST for %s got %s expected %s\n",
121   //           name, (char*) &xor_got, (char*) &xor_exp);
122
123   /* Check query key against public key */
124   if (0 != GNUNET_CRYPTO_hash_cmp(query, &query_key))
125   {
126     GNUNET_break_op (0);
127     return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
128   }
129   
130   record_match = 0;
131   rd_count = ntohl(nrb->rd_count);
132   rd_data = &name[name_len];
133   rd_len = reply_block_size - (name_len
134                                + sizeof(struct GNSNameRecordBlock));
135   {
136     struct GNUNET_NAMESTORE_RecordData rd[rd_count];
137     unsigned int i;
138     uint64_t exp = UINT64_MAX;
139     struct GNUNET_TIME_Absolute et = GNUNET_TIME_UNIT_FOREVER_ABS;
140     
141     if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_len,
142                                                                rd_data,
143                                                                rd_count,
144                                                                rd))
145     {
146       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
147                  "Data invalid (%d bytes, %d records)\n", rd_len, rd_count);
148       return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
149     }
150
151     if (xquery_size < sizeof(uint32_t))
152       record_xquery = 0;
153     else
154       record_xquery = ntohl(*((uint32_t*)xquery));
155     
156     for (i=0; i<rd_count; i++)
157     {     
158       GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
159       exp = GNUNET_MIN (exp, rd[i].expiration_time);
160       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
161                  "Got record of size %d expiration %u\n",
162      rd[i].data_size, rd[i].expiration_time);
163       if ((record_xquery != 0)
164           && (rd[i].record_type == record_xquery))
165       {
166         record_match++;
167       }
168     }
169     et.abs_value_us = exp;
170     
171     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
172                 "Verifying signature of %d records for name %s with expiration of %s\n",
173                 rd_count, name, 
174                 GNUNET_STRINGS_absolute_time_to_string (et));
175
176     if (GNUNET_OK != 
177         GNUNET_NAMESTORE_verify_signature (&nrb->public_key,
178                                            et,
179                                            name,
180                                            rd_count,
181                                            rd,
182                                            &nrb->signature))
183     {
184       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
185                   "Signature invalid for %s\n", name);
186       GNUNET_break_op (0);
187       return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
188     }
189   }
190   
191   if (NULL != bf)
192   {
193     GNUNET_CRYPTO_hash (reply_block, reply_block_size, &chash);
194     GNUNET_BLOCK_mingle_hash (&chash, bf_mutator, &mhash);
195     if (NULL != *bf)
196     {
197       if (GNUNET_YES == GNUNET_CONTAINER_bloomfilter_test(*bf, &mhash))
198         return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
199     }
200     else
201     {
202       *bf = GNUNET_CONTAINER_bloomfilter_init(NULL, 8, BLOOMFILTER_K);
203     }
204     GNUNET_CONTAINER_bloomfilter_add(*bf, &mhash);
205   }
206   return GNUNET_BLOCK_EVALUATION_OK_MORE;
207 }
208
209
210 /**
211  * Function called to obtain the key for a block.
212  *
213  * @param cls closure
214  * @param type block type
215  * @param block block to get the key for
216  * @param block_size number of bytes in block
217  * @param key set to the key (query) for the given block
218  * @return GNUNET_OK on success, GNUNET_SYSERR if type not supported
219  *         (or if extracting a key from a block of this type does not work)
220  */
221 static int
222 block_plugin_gns_get_key (void *cls, enum GNUNET_BLOCK_Type type,
223                          const void *block, size_t block_size,
224                          struct GNUNET_HashCode * key)
225 {
226   struct GNUNET_CRYPTO_ShortHashCode pkey_hash;
227   const struct GNSNameRecordBlock *nrb = block;
228   const char *name;
229
230   if (type != GNUNET_BLOCK_TYPE_GNS_NAMERECORD)
231     return GNUNET_SYSERR;
232   name = (const char *) &nrb[1];
233   if (NULL == memchr (name, '\0', 
234                       block_size - sizeof (struct GNSNameRecordBlock)))
235   {
236     /* malformed, no 0-termination in name */
237     GNUNET_break_op (0);
238     return GNUNET_SYSERR; 
239   }
240   GNUNET_CRYPTO_short_hash (&nrb->public_key,
241                             sizeof (struct GNUNET_CRYPTO_EccPublicKey),
242                             &pkey_hash);
243   GNUNET_GNS_get_key_for_record (name, &pkey_hash, key);
244   return GNUNET_OK;
245 }
246
247
248 /**
249  * Entry point for the plugin.
250  */
251 void *
252 libgnunet_plugin_block_gns_init (void *cls)
253 {
254   static enum GNUNET_BLOCK_Type types[] =
255   {
256     GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
257     GNUNET_BLOCK_TYPE_ANY       /* end of list */
258   };
259   struct GNUNET_BLOCK_PluginFunctions *api;
260
261   api = GNUNET_malloc (sizeof (struct GNUNET_BLOCK_PluginFunctions));
262   api->evaluate = &block_plugin_gns_evaluate;
263   api->get_key = &block_plugin_gns_get_key;
264   api->types = types;
265   return api;
266 }
267
268
269 /**
270  * Exit point from the plugin.
271  */
272 void *
273 libgnunet_plugin_block_gns_done (void *cls)
274 {
275   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
276
277   GNUNET_free (api);
278   return NULL;
279 }
280
281 /* end of plugin_block_gns.c */