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