- Add reverse resolution with limited functionality
[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 != GNUNET_CRYPTO_eddsa_public_key_from_string ((char*) s_peer,
283                                                                      strlen (s_peer),
284                                                                      &vpn->peer.public_key))
285         {
286           GNUNET_free (vpn);
287           *data_size = 0;
288           return GNUNET_SYSERR;
289         }
290         vpn->proto = htons ((uint16_t) proto);
291         strcpy ((char*)&vpn[1], s_serv);
292         return GNUNET_OK;
293       }
294     case GNUNET_GNSRECORD_TYPE_BOX:
295       {
296         struct GNUNET_GNSRECORD_BoxRecord *box;
297         size_t rest;
298         unsigned int protocol;
299         unsigned int service;
300         unsigned int record_type;
301         void *bval;
302         size_t bval_size;
303
304         if (3 != SSCANF (s,
305                          "%u %u %u ",
306                          &protocol,
307                          &service,
308                          &record_type))
309         {
310           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
311                       _("Unable to parse BOX record string `%s'\n"),
312                       s);
313           return GNUNET_SYSERR;
314         }
315         rest = snprintf (NULL, 0,
316                          "%u %u %u ",
317                          protocol,
318                          service,
319                          record_type);
320         if (GNUNET_OK !=
321             GNUNET_GNSRECORD_string_to_value (record_type,
322                                               &s[rest],
323                                               &bval,
324                                               &bval_size))
325           return GNUNET_SYSERR;
326         *data_size = sizeof (struct GNUNET_GNSRECORD_BoxRecord) + bval_size;
327         *data = box = GNUNET_malloc (*data_size);
328         box->protocol = htons (protocol);
329         box->service = htons (service);
330         box->record_type = htonl (record_type);
331         GNUNET_memcpy (&box[1],
332                        bval,
333                        bval_size);
334         GNUNET_free (bval);
335         return GNUNET_OK;
336       }
337     case GNUNET_GNSRECORD_TYPE_REVERSE:
338       {
339         struct GNUNET_GNSRECORD_ReverseRecord *rev;
340         char known_by[253 + 1];
341         struct GNUNET_TIME_Absolute expiration;
342
343         /* TODO: From crypto_ecc.c
344          * Why is this not a constant???
345          */
346         size_t enclen = (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) * 8;
347         if (enclen % 5 > 0)
348           enclen += 5 - enclen % 5;
349         enclen /= 5; /* 260/5 = 52 */
350         char pkey_str[enclen + 1];
351
352         if (3 != SSCANF (s,
353                          "%253s %52s %"SCNu64,
354                          known_by,
355                          pkey_str,
356                          &expiration.abs_value_us))
357         {
358           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
359                       _("Unable to parse REVERSE record string `%s'\n"),
360                       s);
361           return GNUNET_SYSERR;
362         }
363         *data_size = sizeof (struct GNUNET_GNSRECORD_ReverseRecord) + strlen (known_by) + 1;
364         *data = rev = GNUNET_malloc (*data_size);
365         GNUNET_CRYPTO_ecdsa_public_key_from_string (pkey_str,
366                                                     strlen (pkey_str),
367                                                     &rev->pkey);
368         rev->expiration = expiration;
369         GNUNET_memcpy (&rev[1],
370                        known_by,
371                        strlen (known_by));
372         return GNUNET_OK;
373       }
374     default:
375       return GNUNET_SYSERR;
376   }
377 }
378
379
380 /**
381  * Mapping of record type numbers to human-readable
382  * record type names.
383  */
384 static struct {
385   const char *name;
386   uint32_t number;
387 } gns_name_map[] = {
388   { "PKEY",  GNUNET_GNSRECORD_TYPE_PKEY },
389   { "NICK",  GNUNET_GNSRECORD_TYPE_NICK },
390   { "LEHO",  GNUNET_GNSRECORD_TYPE_LEHO },
391   { "VPN", GNUNET_GNSRECORD_TYPE_VPN },
392   { "GNS2DNS", GNUNET_GNSRECORD_TYPE_GNS2DNS },
393   { "BOX", GNUNET_GNSRECORD_TYPE_BOX },
394   { "REVERSE", GNUNET_GNSRECORD_TYPE_REVERSE },
395   { NULL, UINT32_MAX }
396 };
397
398
399 /**
400  * Convert a type name (i.e. "AAAA") to the corresponding number.
401  *
402  * @param cls closure, unused
403  * @param gns_typename name to convert
404  * @return corresponding number, UINT32_MAX on error
405  */
406 static uint32_t
407 gns_typename_to_number (void *cls,
408                         const char *gns_typename)
409 {
410   unsigned int i;
411
412   i=0;
413   while ( (NULL != gns_name_map[i].name) &&
414           (0 != strcasecmp (gns_typename,
415                             gns_name_map[i].name)) )
416     i++;
417   return gns_name_map[i].number;
418 }
419
420
421 /**
422  * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
423  *
424  * @param cls closure, unused
425  * @param type number of a type to convert
426  * @return corresponding typestring, NULL on error
427  */
428 static const char *
429 gns_number_to_typename (void *cls,
430                         uint32_t type)
431 {
432   unsigned int i;
433
434   i=0;
435   while ( (NULL != gns_name_map[i].name) &&
436           (type != gns_name_map[i].number) )
437     i++;
438   return gns_name_map[i].name;
439 }
440
441
442 /**
443  * Entry point for the plugin.
444  *
445  * @param cls NULL
446  * @return the exported block API
447  */
448 void *
449 libgnunet_plugin_gnsrecord_gns_init (void *cls)
450 {
451   struct GNUNET_GNSRECORD_PluginFunctions *api;
452
453   api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
454   api->value_to_string = &gns_value_to_string;
455   api->string_to_value = &gns_string_to_value;
456   api->typename_to_number = &gns_typename_to_number;
457   api->number_to_typename = &gns_number_to_typename;
458   return api;
459 }
460
461
462 /**
463  * Exit point from the plugin.
464  *
465  * @param cls the return value from #libgnunet_plugin_block_test_init()
466  * @return NULL
467  */
468 void *
469 libgnunet_plugin_gnsrecord_gns_done (void *cls)
470 {
471   struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
472
473   GNUNET_free (api);
474   return NULL;
475 }
476
477 /* end of plugin_gnsrecord_gns.c */