Discard large metadata items first
[oweals/gnunet.git] / src / gns / plugin_block_gns.c
index f31b9a9c4f2098f88c328ac5c4a435257e3836a7..50ad0121599b3c3b03947a36dbc0cd7454010c39 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet
-     (C) 2010 Christian Grothoff (and other contributing authors)
+     (C) 2010, 2012 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -29,6 +29,7 @@
 #include "gnunet_namestore_service.h"
 #include "block_gns.h"
 #include "gnunet_signatures.h"
+#include "gns_common.h"
 
 /**
  * Number of bits we set per entry in the bloomfilter.
@@ -36,9 +37,6 @@
  */
 #define BLOOMFILTER_K 16
 
-//Not taken until now
-#define GNUNET_BLOCK_TYPE_GNS_NAMERECORD 11
-
 /**
  * Function called to validate a reply or a request.  For
  * request evaluation, simply pass "NULL" for the reply_block.
  */
 static enum GNUNET_BLOCK_EvaluationResult
 block_plugin_gns_evaluate (void *cls, enum GNUNET_BLOCK_Type type,
-                          const GNUNET_HashCode * query,
+                          const struct GNUNET_HashCode * query,
                           struct GNUNET_CONTAINER_BloomFilter **bf,
                           int32_t bf_mutator, const void *xquery,
                           size_t xquery_size, const void *reply_block,
                           size_t reply_block_size)
 {
+  const struct GNSNameRecordBlock *nrb;
+  const char* name;
+  const char *name_end;
+  const char *rd_data;
+  struct GNUNET_HashCode query_key;
+  struct GNUNET_HashCode mhash;
+  struct GNUNET_HashCode chash;
+  struct GNUNET_CRYPTO_ShortHashCode pkey_hash;
+  struct GNUNET_CRYPTO_HashAsciiEncoded xor_exp;
+  struct GNUNET_CRYPTO_HashAsciiEncoded xor_got;
+  uint32_t rd_count;
+  size_t rd_len;
+  size_t name_len;
+  uint32_t record_xquery;
+  unsigned int record_match;
+  
+  //GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "RB SIZE %d\n", reply_block_size);
+
   if (type != GNUNET_BLOCK_TYPE_GNS_NAMERECORD)
     return GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED;
+  if (reply_block == NULL)
+  {
+    /**
+     *  check if request is valid
+     *  FIXME we could check for the record types here
+     **/
+    if (xquery_size < sizeof(uint32_t))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
+    }
+    return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
+  }
   
-  char* name;
-  GNUNET_HashCode pkey_hash;
-  GNUNET_HashCode query_pkey;
-  GNUNET_HashCode name_hash;
-  struct GNSNameRecordBlock *nrb;
-  struct GNSRecordBlock *rb;
-  uint32_t rd_count;
+  /* this is a reply */
 
-  nrb = (struct GNSNameRecordBlock *)reply_block;
-
-  name = (char*)&nrb[1];
-
-  GNUNET_CRYPTO_hash(&nrb->public_key,
-                     sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
-                     &pkey_hash);
+  nrb = (const struct GNSNameRecordBlock *)reply_block;
+  name = (const char*) &nrb[1];
+  name_end = memchr (name, 0, reply_block_size - sizeof (struct GNSNameRecordBlock));
+  if (NULL == name_end)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+  }
+  name_len = (name_end - name) + 1;
+  GNUNET_CRYPTO_short_hash (&nrb->public_key,
+                           sizeof(nrb->public_key),
+                           &pkey_hash);
+  GNUNET_GNS_get_key_for_record (name, &pkey_hash, &query_key);
+  
+  GNUNET_CRYPTO_hash_to_enc (&query_key, &xor_exp);
+  GNUNET_CRYPTO_hash_to_enc (query, &xor_got);
 
-  GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
+  //GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+  //           "BLOCK_TEST for %s got %s expected %s\n",
+  //           name, (char*) &xor_got, (char*) &xor_exp);
 
-  GNUNET_CRYPTO_hash_xor(query, &name_hash, &query_pkey);
+  /* Check query key against public key */
+  if (0 != GNUNET_CRYPTO_hash_cmp(query, &query_key))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+  }
   
-  //Check query key against public key
-  if (0 != GNUNET_CRYPTO_hash_cmp(&query_pkey, &pkey_hash))
-    return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
-
+  record_match = 0;
   rd_count = ntohl(nrb->rd_count);
+  rd_data = &name[name_len];
+  rd_len = reply_block_size - (name_len
+                               + sizeof(struct GNSNameRecordBlock));
+  {
+    struct GNUNET_NAMESTORE_RecordData rd[rd_count];
+    unsigned int i;
+    uint64_t exp = UINT64_MAX;
+    struct GNUNET_TIME_Absolute et = GNUNET_TIME_UNIT_FOREVER_ABS;
+    
+    if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_len,
+                                                               rd_data,
+                                                               rd_count,
+                                                               rd))
+    {
+      GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+                 "Data invalid (%d bytes, %d records)\n", rd_len, rd_count);
+      return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+    }
 
-  struct GNUNET_NAMESTORE_RecordData rd[rd_count];
-  int i = 0;
-  rb = (struct GNSRecordBlock*)(&nrb[1] + strlen(name));
+    if (xquery_size < sizeof(uint32_t))
+      record_xquery = 0;
+    else
+      record_xquery = ntohl(*((uint32_t*)xquery));
+    
+    for (i=0; i<rd_count; i++)
+    {     
+      GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
+      exp = GNUNET_MIN (exp, rd[i].expiration_time);
+      GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+                "Got record of size %d expiration %u\n",
+     rd[i].data_size, rd[i].expiration_time);
+      if ((record_xquery != 0)
+          && (rd[i].record_type == record_xquery))
+      {
+        record_match++;
+      }
+    }
+    et.abs_value = exp;
+    
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Verifying signature of %d records for name %s with expiration of %u\n",
+               rd_count, name, et.abs_value);
 
-  for (i=0; i<rd_count; i++)
-  {
-    rd[i].record_type = ntohl(rb->type);
-    rd[i].expiration =
-      GNUNET_TIME_absolute_ntoh(rb->expiration);
-    rd[i].data_size = ntohl(rb->data_length);
-    rd[i].flags = ntohl(rb->flags);
-    rd[i].data = (char*)&rb[1];
-    rb = &rb[1] + rd[i].data_size;
+    if (GNUNET_OK != 
+       GNUNET_NAMESTORE_verify_signature (&nrb->public_key,
+                                          et,
+                                          name,
+                                          rd_count,
+                                          rd,
+                                          &nrb->signature))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+                 "Signature invalid for %s\n", name);
+      GNUNET_break_op (0);
+      return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+    }
   }
-
-  if (GNUNET_OK != GNUNET_NAMESTORE_verify_signature (&nrb->public_key,
-                                                      name,
-                                                      nrb->rd_count,
-                                                      rd,
-                                                      &nrb->signature))
+  
+  if (NULL != bf)
   {
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Signature invalid\n");
-    return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
+    GNUNET_CRYPTO_hash (reply_block, reply_block_size, &chash);
+    GNUNET_BLOCK_mingle_hash (&chash, bf_mutator, &mhash);
+    if (NULL != *bf)
+    {
+      if (GNUNET_YES == GNUNET_CONTAINER_bloomfilter_test(*bf, &mhash))
+        return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
+    }
+    else
+    {
+      *bf = GNUNET_CONTAINER_bloomfilter_init(NULL, 8, BLOOMFILTER_K);
+    }
+    GNUNET_CONTAINER_bloomfilter_add(*bf, &mhash);
   }
-
-  //Cache FIXME we need a static function here to namestore?
-  /*GNUNET_NAMESTORE_record_put (handle, //FIXME where do i get this from?
-                               &pkey_hash,
-                               name,
-                               expiration, //FIXME uh where do i get this from?
-                               rd_count,
-                               rd,
-                               signature,
-                               NULL, //cont
-                               NULL); //cls*/
-  return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
+  return GNUNET_BLOCK_EVALUATION_OK_MORE;
 }
 
 
@@ -147,22 +220,26 @@ block_plugin_gns_evaluate (void *cls, enum GNUNET_BLOCK_Type type,
 static int
 block_plugin_gns_get_key (void *cls, enum GNUNET_BLOCK_Type type,
                          const void *block, size_t block_size,
-                         GNUNET_HashCode * key)
+                         struct GNUNET_HashCode * key)
 {
-  if (type != GNUNET_BLOCK_TYPE_GNS_NAMERECORD)
-    return GNUNET_NO;
-  GNUNET_HashCode name_hash;
-  GNUNET_HashCode pkey_hash;
-  struct GNSNameRecordBlock *nrb = (struct GNSNameRecordBlock *)block;
-
-  GNUNET_CRYPTO_hash(&nrb[1], strlen((char*)&nrb[1]), &name_hash);
-  GNUNET_CRYPTO_hash(&nrb->public_key,
-                     sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
-                     &pkey_hash);
+  struct GNUNET_CRYPTO_ShortHashCode pkey_hash;
+  const struct GNSNameRecordBlock *nrb = block;
+  const char *name;
 
-  GNUNET_CRYPTO_hash_xor(&name_hash, &pkey_hash, key);
-  
-  //FIXME calculate key from name and hash(pkey) here
+  if (type != GNUNET_BLOCK_TYPE_GNS_NAMERECORD)
+    return GNUNET_SYSERR;
+  name = (const char *) &nrb[1];
+  if (NULL == memchr (name, '\0', 
+                     block_size - sizeof (struct GNSNameRecordBlock)))
+  {
+    /* malformed, no 0-termination in name */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR; 
+  }
+  GNUNET_CRYPTO_short_hash (&nrb->public_key,
+                           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                           &pkey_hash);
+  GNUNET_GNS_get_key_for_record (name, &pkey_hash, key);
   return GNUNET_OK;
 }