glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / core / gnunet-service-core_typemap.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011-2014 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
16 /**
17  * @file core/gnunet-service-core_typemap.c
18  * @brief management of map that specifies which message types this peer supports
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include "gnunet_util_lib.h"
23 #include "gnunet_transport_service.h"
24 #include "gnunet-service-core.h"
25 #include "gnunet-service-core_sessions.h"
26 #include "gnunet-service-core_typemap.h"
27 #include <zlib.h>
28
29
30 /**
31  * A type map describing which messages a given neighbour is able
32  * to process.
33  */
34 struct GSC_TypeMap
35 {
36   uint32_t bits[(UINT16_MAX + 1) / 32];
37 };
38
39 /**
40  * Bitmap of message types this peer is able to handle.
41  */
42 static struct GSC_TypeMap my_type_map;
43
44 /**
45  * Counters for message types this peer is able to handle.
46  */
47 static uint8_t map_counters[UINT16_MAX + 1];
48
49 /**
50  * Current hash of our (uncompressed) type map.
51  * Lazily computed when needed.
52  */
53 static struct GNUNET_HashCode my_tm_hash;
54
55 /**
56  * Is #my_tm_hash() current with respect to our type map?
57  */
58 static int hash_current;
59
60
61 /**
62  * Our type map changed, recompute its hash.
63  */
64 static void
65 rehash_typemap ()
66 {
67   hash_current = GNUNET_NO;
68 }
69
70
71 /**
72  * Hash the contents of a type map.
73  *
74  * @param tm map to hash
75  * @param hc where to store the hash code
76  */
77 void
78 GSC_TYPEMAP_hash (const struct GSC_TypeMap *tm,
79                   struct GNUNET_HashCode *hc)
80 {
81   GNUNET_CRYPTO_hash (tm,
82                       sizeof (struct GSC_TypeMap),
83                       hc);
84 }
85
86
87 /**
88  * Check if the given hash matches our current type map.
89  *
90  * @param hc hash code to check if it matches our type map
91  * @return #GNUNET_YES if the hash matches, #GNUNET_NO if not
92  */
93 int
94 GSC_TYPEMAP_check_hash (const struct GNUNET_HashCode *hc)
95 {
96   if (GNUNET_NO == hash_current)
97   {
98     GSC_TYPEMAP_hash (&my_type_map,
99                       &my_tm_hash);
100     hash_current = GNUNET_YES;
101   }
102   return (0 == memcmp (hc, &my_tm_hash, sizeof (struct GNUNET_HashCode)))
103     ? GNUNET_YES : GNUNET_NO;
104 }
105
106
107 /**
108  * Compute a type map message for this peer.
109  *
110  * @return this peers current type map message.
111  */
112 struct GNUNET_MessageHeader *
113 GSC_TYPEMAP_compute_type_map_message ()
114 {
115   char *tmp;
116   uLongf dlen;
117   struct GNUNET_MessageHeader *hdr;
118
119 #ifdef compressBound
120   dlen = compressBound (sizeof (my_type_map));
121 #else
122   dlen = sizeof (my_type_map) + (sizeof (my_type_map) / 100) + 20;
123   /* documentation says 100.1% oldSize + 12 bytes, but we
124    * should be able to overshoot by more to be safe */
125 #endif
126   hdr = GNUNET_malloc (dlen + sizeof (struct GNUNET_MessageHeader));
127   tmp = (char *) &hdr[1];
128   if ((Z_OK !=
129        compress2 ((Bytef *) tmp, &dlen, (const Bytef *) &my_type_map,
130                   sizeof (my_type_map), 9)) || (dlen >= sizeof (my_type_map)))
131   {
132     /* compression failed, use uncompressed map */
133     dlen = sizeof (my_type_map);
134     GNUNET_memcpy (tmp, &my_type_map, sizeof (my_type_map));
135     hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP);
136   }
137   else
138   {
139     /* compression worked, use compressed map */
140     hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP);
141   }
142   hdr->size = htons ((uint16_t) dlen + sizeof (struct GNUNET_MessageHeader));
143   return hdr;
144 }
145
146
147 /**
148  * Extract a type map from a TYPE_MAP message.
149  *
150  * @param msg a type map message
151  * @return NULL on error
152  */
153 struct GSC_TypeMap *
154 GSC_TYPEMAP_get_from_message (const struct GNUNET_MessageHeader *msg)
155 {
156   struct GSC_TypeMap *ret;
157   uint16_t size;
158   uLongf dlen;
159
160   size = ntohs (msg->size);
161   switch (ntohs (msg->type))
162   {
163   case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP:
164     GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# type maps received"),
165                               1, GNUNET_NO);
166     if (size != sizeof (struct GSC_TypeMap))
167     {
168       GNUNET_break_op (0);
169       return NULL;
170     }
171     ret = GNUNET_new (struct GSC_TypeMap);
172     GNUNET_memcpy (ret, &msg[1], sizeof (struct GSC_TypeMap));
173     return ret;
174   case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP:
175     GNUNET_STATISTICS_update (GSC_stats,
176                               gettext_noop ("# type maps received"),
177                               1,
178                               GNUNET_NO);
179     ret = GNUNET_new (struct GSC_TypeMap);
180     dlen = sizeof (struct GSC_TypeMap);
181     if ((Z_OK !=
182          uncompress ((Bytef *) ret, &dlen, (const Bytef *) &msg[1],
183                      (uLong) size)) || (dlen != sizeof (struct GSC_TypeMap)))
184     {
185       GNUNET_break_op (0);
186       GNUNET_free (ret);
187       return NULL;
188     }
189     return ret;
190   default:
191     GNUNET_break (0);
192     return NULL;
193   }
194 }
195
196
197 /**
198  * Send my type map to all connected peers (it got changed).
199  */
200 static void
201 broadcast_my_type_map ()
202 {
203   struct GNUNET_MessageHeader *hdr;
204
205   hdr = GSC_TYPEMAP_compute_type_map_message ();
206   GNUNET_STATISTICS_update (GSC_stats,
207                             gettext_noop ("# updates to my type map"),
208                             1,
209                             GNUNET_NO);
210   GSC_SESSIONS_broadcast_typemap (hdr);
211   GNUNET_free (hdr);
212 }
213
214
215 /**
216  * Add a set of types to our type map.
217  *
218  * @param types array of message types supported by this peer
219  * @param tlen number of entries in @a types
220  */
221 void
222 GSC_TYPEMAP_add (const uint16_t *types,
223                  unsigned int tlen)
224 {
225   unsigned int i;
226   int changed;
227
228   changed = GNUNET_NO;
229   for (i = 0; i < tlen; i++)
230   {
231     if (0 == map_counters[types[i]]++)
232     {
233       my_type_map.bits[types[i] / 32] |= (1 << (types[i] % 32));
234       changed = GNUNET_YES;
235     }
236   }
237   if (GNUNET_YES == changed)
238   {
239     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
240                 "Typemap changed, broadcasting!\n");
241     rehash_typemap ();
242     broadcast_my_type_map ();
243   }
244 }
245
246
247 /**
248  * Remove a set of types from our type map.
249  *
250  * @param types array of types to remove
251  * @param tlen length of the @a types array
252  */
253 void
254 GSC_TYPEMAP_remove (const uint16_t *types,
255                     unsigned int tlen)
256 {
257   int changed;
258
259   changed = GNUNET_NO;
260   for (unsigned int i = 0; i < tlen; i++)
261   {
262     if (0 == --map_counters[types[i]])
263     {
264       my_type_map.bits[types[i] / 32] &= ~(1 << (types[i] % 32));
265       changed = GNUNET_YES;
266     }
267   }
268   if (GNUNET_YES == changed)
269   {
270     rehash_typemap ();
271     broadcast_my_type_map ();
272   }
273 }
274
275
276 /**
277  * Test if any of the types from the types array is in the
278  * given type map.
279  *
280  * @param tmap map to test
281  * @param types array of types
282  * @param tcnt number of entries in @a types
283  * @return #GNUNET_YES if a type is in the map, #GNUNET_NO if not
284  */
285 int
286 GSC_TYPEMAP_test_match (const struct GSC_TypeMap *tmap,
287                         const uint16_t *types,
288                         unsigned int tcnt)
289 {
290   if (NULL == tmap)
291     return GNUNET_NO;
292   if (0 == tcnt)
293     return GNUNET_YES;          /* matches all */
294   for (unsigned int i = 0; i < tcnt; i++)
295     if (0 != (tmap->bits[types[i] / 32] & (1 << (types[i] % 32))))
296       return GNUNET_YES;
297   return GNUNET_NO;
298 }
299
300
301 /**
302  * Add additional types to a given typemap.
303  *
304  * @param tmap map to extend (not changed)
305  * @param types array of types to add
306  * @param tcnt number of entries in @a types
307  * @return updated type map (fresh copy)
308  */
309 struct GSC_TypeMap *
310 GSC_TYPEMAP_extend (const struct GSC_TypeMap *tmap,
311                     const uint16_t *types,
312                     unsigned int tcnt)
313 {
314   struct GSC_TypeMap *ret;
315
316   ret = GNUNET_new (struct GSC_TypeMap);
317   if (NULL != tmap)
318     GNUNET_memcpy (ret, tmap, sizeof (struct GSC_TypeMap));
319   for (unsigned int i = 0; i < tcnt; i++)
320     ret->bits[types[i] / 32] |= (1 << (types[i] % 32));
321   return ret;
322 }
323
324
325 /**
326  * Create an empty type map.
327  *
328  * @return an empty type map
329  */
330 struct GSC_TypeMap *
331 GSC_TYPEMAP_create ()
332 {
333   return GNUNET_new (struct GSC_TypeMap);
334 }
335
336
337 /**
338  * Free the given type map.
339  *
340  * @param tmap a type map
341  */
342 void
343 GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap)
344 {
345   GNUNET_free (tmap);
346 }
347
348
349 /**
350  * Initialize typemap subsystem.
351  */
352 void
353 GSC_TYPEMAP_init ()
354 {
355   /* nothing to do */
356 }
357
358
359 /**
360  * Shutdown typemap subsystem.
361  */
362 void
363 GSC_TYPEMAP_done ()
364 {
365   /* nothing to do */
366 }
367
368 /* end of gnunet-service-core_typemap.c */