ignore in lint/
[oweals/gnunet.git] / src / gnsrecord / gnsrecord_serialization.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-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 /**
22  * @file gnsrecord/gnsrecord_serialization.c
23  * @brief API to serialize and deserialize GNS records
24  * @author Martin Schanzenbach
25  * @author Matthias Wachs
26  * @author Christian Grothoff
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_signatures.h"
32 #include "gnunet_arm_service.h"
33 #include "gnunet_gnsrecord_lib.h"
34 #include "gnunet_dnsparser_lib.h"
35 #include "gnunet_tun_lib.h"
36
37
38 #define LOG(kind,...) GNUNET_log_from (kind, "gnsrecord",__VA_ARGS__)
39
40 /**
41  * Set to 1 to check that all records are well-formed (can be converted
42  * to string) during serialization/deserialization.
43  */
44 #define DEBUG_GNSRECORDS 0
45
46 GNUNET_NETWORK_STRUCT_BEGIN
47
48
49 /**
50  * Internal format of a record in the serialized form.
51  */
52 struct NetworkRecord
53 {
54
55   /**
56    * Expiration time for the DNS record; relative or absolute depends
57    * on @e flags, network byte order.
58    */
59   uint64_t expiration_time GNUNET_PACKED;
60
61   /**
62    * Number of bytes in 'data', network byte order.
63    */
64   uint32_t data_size GNUNET_PACKED;
65
66   /**
67    * Type of the GNS/DNS record, network byte order.
68    */
69   uint32_t record_type GNUNET_PACKED;
70
71   /**
72    * Flags for the record, network byte order.
73    */
74   uint32_t flags GNUNET_PACKED;
75
76 };
77
78 GNUNET_NETWORK_STRUCT_END
79
80
81 /**
82  * Calculate how many bytes we will need to serialize the given
83  * records.
84  *
85  * @param rd_count number of records in the rd array
86  * @param rd array of #GNUNET_GNSRECORD_Data with @a rd_count elements
87  * @return the required size to serialize, -1 on error
88  */
89 ssize_t
90 GNUNET_GNSRECORD_records_get_size (unsigned int rd_count,
91                                    const struct GNUNET_GNSRECORD_Data *rd)
92 {
93   size_t ret;
94
95   if (0 == rd_count)
96     return 0;
97   
98   ret = sizeof (struct NetworkRecord) * rd_count;
99   for (unsigned int i=0;i<rd_count;i++)
100   {
101     if ((ret + rd[i].data_size) < ret)
102     {
103       GNUNET_break (0);
104       return -1;
105     }
106     ret += rd[i].data_size;
107 #if DEBUG_GNSRECORDS
108     {
109       char *str;
110
111       str = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
112                                               rd[i].data,
113                                               rd[i].data_size);
114       if (NULL == str)
115       {
116         GNUNET_break_op (0);
117         return -1;
118       }
119       GNUNET_free (str);
120     }
121 #endif
122   }
123   if (ret > SSIZE_MAX)
124   {
125     GNUNET_break (0);
126     return -1;
127   }
128   //Do not pad PKEY
129   if (GNUNET_GNSRECORD_TYPE_PKEY == rd->record_type)
130     return ret;
131   /**
132    * Efficiently round up to the next
133    * power of 2 for padding
134    * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
135    */
136   ret--;
137   ret |= ret >> 1;
138   ret |= ret >> 2;
139   ret |= ret >> 4;
140   ret |= ret >> 8;
141   ret |= ret >> 16;
142   ret++;
143   return (ssize_t) ret;
144 }
145
146
147 /**
148  * Serialize the given records to the given destination buffer.
149  *
150  * @param rd_count number of records in the rd array
151  * @param rd array of #GNUNET_GNSRECORD_Data with @a rd_count elements
152  * @param dest_size size of the destination array
153  * @param dest where to write the result
154  * @return the size of serialized records, -1 if records do not fit
155  */
156 ssize_t
157 GNUNET_GNSRECORD_records_serialize (unsigned int rd_count,
158                                     const struct GNUNET_GNSRECORD_Data *rd,
159                                     size_t dest_size,
160                                     char *dest)
161 {
162   struct NetworkRecord rec;
163   size_t off;
164
165   off = 0;
166   for (unsigned int i=0;i<rd_count;i++)
167   {
168     LOG (GNUNET_ERROR_TYPE_DEBUG,
169          "Serializing record %u with flags %d and expiration time %llu\n",
170          i,
171          rd[i].flags,
172          (unsigned long long) rd[i].expiration_time);
173     rec.expiration_time = GNUNET_htonll (rd[i].expiration_time);
174     rec.data_size = htonl ((uint32_t) rd[i].data_size);
175     rec.record_type = htonl (rd[i].record_type);
176     rec.flags = htonl (rd[i].flags);
177     if ( (off + sizeof (rec) > dest_size) ||
178          (off + sizeof (rec) < off) )
179     {
180       GNUNET_break (0);
181       return -1;
182     }
183     GNUNET_memcpy (&dest[off],
184                    &rec,
185                    sizeof (rec));
186     off += sizeof (rec);
187     if ( (off + rd[i].data_size > dest_size) ||
188          (off + rd[i].data_size < off) )
189     {
190       GNUNET_break (0);
191       return -1;
192     }
193     GNUNET_memcpy (&dest[off],
194                    rd[i].data,
195                    rd[i].data_size);
196     off += rd[i].data_size;
197 #if DEBUG_GNSRECORDS
198     {
199       char *str;
200
201       str = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
202                                               rd[i].data,
203                                               rd[i].data_size);
204       if (NULL == str)
205       {
206         GNUNET_break_op (0);
207         return -1;
208       }
209       GNUNET_free (str);
210     }
211 #endif
212   }
213   memset (&dest[off],
214           0,
215           dest_size-off);
216   return dest_size;
217 }
218
219
220 /**
221  * Deserialize the given records to the given destination.
222  *
223  * @param len size of the serialized record data
224  * @param src the serialized record data
225  * @param rd_count number of records in the rd array
226  * @param dest where to put the data
227  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
228  */
229 int
230 GNUNET_GNSRECORD_records_deserialize (size_t len,
231                                       const char *src,
232                                       unsigned int rd_count,
233                                       struct GNUNET_GNSRECORD_Data *dest)
234 {
235   struct NetworkRecord rec;
236   size_t off;
237
238   off = 0;
239   for (unsigned int i=0;i<rd_count;i++)
240   {
241     if ( (off + sizeof (rec) > len) ||
242          (off + sizeof (rec) < off) )
243     {
244       GNUNET_break_op (0);
245       return GNUNET_SYSERR;
246     }
247     GNUNET_memcpy (&rec,
248                    &src[off],
249                    sizeof (rec));
250     dest[i].expiration_time = GNUNET_ntohll (rec.expiration_time);
251     dest[i].data_size = ntohl ((uint32_t) rec.data_size);
252     dest[i].record_type = ntohl (rec.record_type);
253     dest[i].flags = ntohl (rec.flags);
254     off += sizeof (rec);
255     if ( (off + dest[i].data_size > len) ||
256          (off + dest[i].data_size < off) )
257     {
258       GNUNET_break_op (0);
259       return GNUNET_SYSERR;
260     }
261     dest[i].data = &src[off];
262     off += dest[i].data_size;
263 #if GNUNET_EXTRA_LOGGING
264     {
265       char *str;
266
267       str = GNUNET_GNSRECORD_value_to_string (dest[i].record_type,
268                                               dest[i].data,
269                                               dest[i].data_size);
270       if (NULL == str)
271       {
272         GNUNET_break_op (0);
273         return GNUNET_SYSERR;
274       }
275       GNUNET_free (str);
276     }
277 #endif
278     LOG (GNUNET_ERROR_TYPE_DEBUG,
279          "Deserialized record %u with flags %d and expiration time %llu\n",
280          i,
281          dest[i].flags,
282          (unsigned long long) dest[i].expiration_time);
283   }
284   return GNUNET_OK;
285 }
286
287
288 /* end of gnsrecord_serialization.c */