Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / gns / plugin_gnsrecord_gns.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2013, 2014, 2016 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file gns/plugin_gnsrecord_gns.c
23  * @brief gnsrecord plugin to provide the API for fundamental GNS records
24  *                  This includes the VPN record because GNS resolution
25  *                  is expected to understand VPN records and (if needed)
26  *                  map the result to A/AAAA.
27  * @author Christian Grothoff
28  */
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_gnsrecord_lib.h"
32 #include "gnunet_dnsparser_lib.h"
33 #include "gnunet_gnsrecord_plugin.h"
34 #include <inttypes.h>
35
36
37 /**
38  * Convert the 'value' of a record to a string.
39  *
40  * @param cls closure, unused
41  * @param type type of the record
42  * @param data value in binary encoding
43  * @param data_size number of bytes in @a data
44  * @return NULL on error, otherwise human-readable representation of the value
45  */
46 static char *
47 gns_value_to_string (void *cls,
48                      uint32_t type,
49                      const void *data,
50                      size_t data_size)
51 {
52   const char *cdata;
53
54   switch (type)
55   {
56   case GNUNET_GNSRECORD_TYPE_PKEY:
57     if (data_size != sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))
58       return NULL;
59     return GNUNET_CRYPTO_ecdsa_public_key_to_string (data);
60   case GNUNET_GNSRECORD_TYPE_NICK:
61     return GNUNET_strndup (data, data_size);
62   case GNUNET_GNSRECORD_TYPE_LEHO:
63     return GNUNET_strndup (data, data_size);
64   case GNUNET_GNSRECORD_TYPE_GNS2DNS:
65     {
66       char *ns;
67       char *ip;
68       size_t off;
69       char *nstr;
70
71       off = 0;
72       ns = GNUNET_DNSPARSER_parse_name (data,
73                                         data_size,
74                                         &off);
75       ip = GNUNET_DNSPARSER_parse_name (data,
76                                         data_size,
77                                         &off);
78       if ( (NULL == ns) ||
79            (NULL == ip) ||
80            (off != data_size) )
81       {
82         GNUNET_break_op (0);
83         GNUNET_free_non_null (ns);
84         GNUNET_free_non_null (ip);
85         return NULL;
86       }
87       GNUNET_asprintf (&nstr,
88                        "%s@%s",
89                        ns,
90                        ip);
91       GNUNET_free_non_null (ns);
92       GNUNET_free_non_null (ip);
93       return nstr;
94     }
95   case GNUNET_GNSRECORD_TYPE_VPN:
96     {
97       struct GNUNET_TUN_GnsVpnRecord vpn;
98       char* vpn_str;
99
100       cdata = data;
101       if ( (data_size <= sizeof (vpn)) ||
102            ('\0' != cdata[data_size - 1]) )
103         return NULL; /* malformed */
104       /* need to memcpy for alignment */
105       memcpy (&vpn,
106               data,
107               sizeof (vpn));
108       GNUNET_asprintf (&vpn_str,
109                        "%u %s %s",
110                        (unsigned int) ntohs (vpn.proto),
111                        (const char*) GNUNET_i2s_full (&vpn.peer),
112                        (const char*) &cdata[sizeof (vpn)]);
113       return vpn_str;
114     }
115   case GNUNET_GNSRECORD_TYPE_BOX:
116     {
117       struct GNUNET_GNSRECORD_BoxRecord box;
118       uint32_t rt;
119       char *box_str;
120       char *ival;
121
122       cdata = data;
123       if (data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
124         return NULL; /* malformed */
125       memcpy (&box,
126               data,
127               sizeof (box));
128       rt = ntohl (box.record_type);
129       ival = GNUNET_GNSRECORD_value_to_string (rt,
130                                                &cdata[sizeof (box)],
131                                                data_size - sizeof (box));
132       if (NULL == ival)
133         return NULL; /* malformed */
134       GNUNET_asprintf (&box_str,
135                        "%u %u %u %s",
136                        (unsigned int) ntohs (box.protocol),
137                        (unsigned int) ntohs (box.service),
138                        (unsigned int) rt,
139                        ival);
140       GNUNET_free (ival);
141       return box_str;
142     }
143   case GNUNET_GNSRECORD_TYPE_REVERSE:
144     {
145       struct GNUNET_GNSRECORD_ReverseRecord rev;
146       char *rev_str;
147       char *pkey_str;
148
149       if (data_size < sizeof (struct GNUNET_GNSRECORD_ReverseRecord))
150         return NULL; /* malformed */
151
152       memcpy (&rev,
153               data,
154               sizeof (rev));
155       cdata = data;
156       pkey_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&rev.pkey);
157
158       GNUNET_asprintf (&rev_str,
159                        "%s %s %"SCNu64,
160                        &cdata[sizeof (rev)],
161                        pkey_str,
162                        rev.expiration.abs_value_us);
163       GNUNET_free (pkey_str);
164       return rev_str;
165
166     }
167   default:
168     return NULL;
169   }
170 }
171
172
173 /**
174  * Convert human-readable version of a 'value' of a record to the binary
175  * representation.
176  *
177  * @param cls closure, unused
178  * @param type type of the record
179  * @param s human-readable string
180  * @param data set to value in binary encoding (will be allocated)
181  * @param data_size set to number of bytes in @a data
182  * @return #GNUNET_OK on success
183  */
184 static int
185 gns_string_to_value (void *cls,
186                      uint32_t type,
187                      const char *s,
188                      void **data,
189                      size_t *data_size)
190 {
191   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
192
193   if (NULL == s)
194     return GNUNET_SYSERR;
195   switch (type)
196   {
197
198     case GNUNET_GNSRECORD_TYPE_PKEY:
199       if (GNUNET_OK !=
200           GNUNET_CRYPTO_ecdsa_public_key_from_string (s, strlen (s), &pkey))
201       {
202         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
203                     _("Unable to parse PKEY record `%s'\n"),
204                     s);
205         return GNUNET_SYSERR;
206       }
207       *data = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPublicKey);
208       GNUNET_memcpy (*data, &pkey, sizeof (pkey));
209       *data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
210       return GNUNET_OK;
211
212     case GNUNET_GNSRECORD_TYPE_NICK:
213       *data = GNUNET_strdup (s);
214       *data_size = strlen (s);
215       return GNUNET_OK;
216     case GNUNET_GNSRECORD_TYPE_LEHO:
217       *data = GNUNET_strdup (s);
218       *data_size = strlen (s);
219       return GNUNET_OK;
220     case GNUNET_GNSRECORD_TYPE_GNS2DNS:
221       {
222         char nsbuf[514];
223         char *cpy;
224         char *at;
225         size_t off;
226
227         cpy = GNUNET_strdup (s);
228         at = strchr (cpy, '@');
229         if (NULL == at)
230         {
231           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
232                       _("Unable to parse GNS2DNS record `%s'\n"),
233                       s);
234           GNUNET_free (cpy);
235           return GNUNET_SYSERR;
236         }
237         *at = '\0';
238         at++;
239
240         off = 0;
241         if ( (GNUNET_OK !=
242               GNUNET_DNSPARSER_builder_add_name (nsbuf,
243                                                  sizeof (nsbuf),
244                                                  &off,
245                                                  cpy)) ||
246              (GNUNET_OK !=
247               GNUNET_DNSPARSER_builder_add_name (nsbuf,
248                                                  sizeof (nsbuf),
249                                                  &off,
250                                                  at)) )
251         {
252           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
253                       _("Failed to serialize GNS2DNS record with value `%s'\n"),
254                       s);
255           GNUNET_free (cpy);
256           return GNUNET_SYSERR;
257         }
258         GNUNET_free (cpy);
259         *data_size = off;
260         *data = GNUNET_malloc (off);
261         GNUNET_memcpy (*data, nsbuf, off);
262         return GNUNET_OK;
263       }
264     case GNUNET_GNSRECORD_TYPE_VPN:
265       {
266         struct GNUNET_TUN_GnsVpnRecord *vpn;
267         char s_peer[103 + 1];
268         char s_serv[253 + 1];
269         unsigned int proto;
270
271         if (3 != SSCANF (s,
272                          "%u %103s %253s",
273                          &proto, s_peer, s_serv))
274         {
275           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
276                       _("Unable to parse VPN record string `%s'\n"),
277                       s);
278           return GNUNET_SYSERR;
279         }
280         *data_size = sizeof (struct GNUNET_TUN_GnsVpnRecord) + strlen (s_serv) + 1;
281         *data = vpn = GNUNET_malloc (*data_size);
282         if (GNUNET_OK !=
283             GNUNET_CRYPTO_eddsa_public_key_from_string ((char*) s_peer,
284                                                         strlen (s_peer),
285                                                         &vpn->peer.public_key))
286         {
287           GNUNET_free (vpn);
288           *data_size = 0;
289           return GNUNET_SYSERR;
290         }
291         vpn->proto = htons ((uint16_t) proto);
292         strcpy ((char*)&vpn[1], s_serv);
293         return GNUNET_OK;
294       }
295     case GNUNET_GNSRECORD_TYPE_BOX:
296       {
297         struct GNUNET_GNSRECORD_BoxRecord *box;
298         size_t rest;
299         unsigned int protocol;
300         unsigned int service;
301         unsigned int record_type;
302         void *bval;
303         size_t bval_size;
304
305         if (3 != SSCANF (s,
306                          "%u %u %u ",
307                          &protocol,
308                          &service,
309                          &record_type))
310         {
311           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
312                       _("Unable to parse BOX record string `%s'\n"),
313                       s);
314           return GNUNET_SYSERR;
315         }
316         rest = snprintf (NULL, 0,
317                          "%u %u %u ",
318                          protocol,
319                          service,
320                          record_type);
321         if (GNUNET_OK !=
322             GNUNET_GNSRECORD_string_to_value (record_type,
323                                               &s[rest],
324                                               &bval,
325                                               &bval_size))
326           return GNUNET_SYSERR;
327         *data_size = sizeof (struct GNUNET_GNSRECORD_BoxRecord) + bval_size;
328         *data = box = GNUNET_malloc (*data_size);
329         box->protocol = htons (protocol);
330         box->service = htons (service);
331         box->record_type = htonl (record_type);
332         GNUNET_memcpy (&box[1],
333                        bval,
334                        bval_size);
335         GNUNET_free (bval);
336         return GNUNET_OK;
337       }
338     case GNUNET_GNSRECORD_TYPE_REVERSE:
339       {
340         struct GNUNET_GNSRECORD_ReverseRecord *rev;
341         char known_by[253 + 1];
342         struct GNUNET_TIME_Absolute expiration;
343
344         /* TODO: From crypto_ecc.c
345          * Why is this not a constant???
346          */
347         size_t enclen = (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) * 8;
348         if (enclen % 5 > 0)
349           enclen += 5 - enclen % 5;
350         enclen /= 5; /* 260/5 = 52 */
351         char pkey_str[enclen + 1];
352
353         if (3 != SSCANF (s,
354                          "%253s %52s %"SCNu64,
355                          known_by,
356                          pkey_str,
357                          &expiration.abs_value_us))
358         {
359           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
360                       _("Unable to parse REVERSE record string `%s'\n"),
361                       s);
362           return GNUNET_SYSERR;
363         }
364         *data_size = sizeof (struct GNUNET_GNSRECORD_ReverseRecord) + strlen (known_by) + 1;
365         *data = rev = GNUNET_malloc (*data_size);
366         if (GNUNET_OK !=
367             GNUNET_CRYPTO_ecdsa_public_key_from_string (pkey_str,
368                                                         strlen (pkey_str),
369                                                         &rev->pkey))
370         {
371           GNUNET_free (rev);
372           return GNUNET_SYSERR;
373         }
374         rev->expiration = expiration;
375         GNUNET_memcpy (&rev[1],
376                        known_by,
377                        strlen (known_by));
378         return GNUNET_OK;
379       }
380     default:
381       return GNUNET_SYSERR;
382   }
383 }
384
385
386 /**
387  * Mapping of record type numbers to human-readable
388  * record type names.
389  */
390 static struct {
391   const char *name;
392   uint32_t number;
393 } gns_name_map[] = {
394   { "PKEY",  GNUNET_GNSRECORD_TYPE_PKEY },
395   { "NICK",  GNUNET_GNSRECORD_TYPE_NICK },
396   { "LEHO",  GNUNET_GNSRECORD_TYPE_LEHO },
397   { "VPN", GNUNET_GNSRECORD_TYPE_VPN },
398   { "GNS2DNS", GNUNET_GNSRECORD_TYPE_GNS2DNS },
399   { "BOX", GNUNET_GNSRECORD_TYPE_BOX },
400   { "REVERSE", GNUNET_GNSRECORD_TYPE_REVERSE },
401   { NULL, UINT32_MAX }
402 };
403
404
405 /**
406  * Convert a type name (i.e. "AAAA") to the corresponding number.
407  *
408  * @param cls closure, unused
409  * @param gns_typename name to convert
410  * @return corresponding number, UINT32_MAX on error
411  */
412 static uint32_t
413 gns_typename_to_number (void *cls,
414                         const char *gns_typename)
415 {
416   unsigned int i;
417
418   i=0;
419   while ( (NULL != gns_name_map[i].name) &&
420           (0 != strcasecmp (gns_typename,
421                             gns_name_map[i].name)) )
422     i++;
423   return gns_name_map[i].number;
424 }
425
426
427 /**
428  * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
429  *
430  * @param cls closure, unused
431  * @param type number of a type to convert
432  * @return corresponding typestring, NULL on error
433  */
434 static const char *
435 gns_number_to_typename (void *cls,
436                         uint32_t type)
437 {
438   unsigned int i;
439
440   i=0;
441   while ( (NULL != gns_name_map[i].name) &&
442           (type != gns_name_map[i].number) )
443     i++;
444   return gns_name_map[i].name;
445 }
446
447
448 /**
449  * Entry point for the plugin.
450  *
451  * @param cls NULL
452  * @return the exported block API
453  */
454 void *
455 libgnunet_plugin_gnsrecord_gns_init (void *cls)
456 {
457   struct GNUNET_GNSRECORD_PluginFunctions *api;
458
459   api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
460   api->value_to_string = &gns_value_to_string;
461   api->string_to_value = &gns_string_to_value;
462   api->typename_to_number = &gns_typename_to_number;
463   api->number_to_typename = &gns_number_to_typename;
464   return api;
465 }
466
467
468 /**
469  * Exit point from the plugin.
470  *
471  * @param cls the return value from #libgnunet_plugin_block_test_init()
472  * @return NULL
473  */
474 void *
475 libgnunet_plugin_gnsrecord_gns_done (void *cls)
476 {
477   struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
478
479   GNUNET_free (api);
480   return NULL;
481 }
482
483 /* end of plugin_gnsrecord_gns.c */