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