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   default:
144     return NULL;
145   }
146 }
147
148
149 /**
150  * Convert human-readable version of a 'value' of a record to the binary
151  * representation.
152  *
153  * @param cls closure, unused
154  * @param type type of the record
155  * @param s human-readable string
156  * @param data set to value in binary encoding (will be allocated)
157  * @param data_size set to number of bytes in @a data
158  * @return #GNUNET_OK on success
159  */
160 static int
161 gns_string_to_value (void *cls,
162                      uint32_t type,
163                      const char *s,
164                      void **data,
165                      size_t *data_size)
166 {
167   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
168
169   if (NULL == s)
170     return GNUNET_SYSERR;
171   switch (type)
172   {
173
174     case GNUNET_GNSRECORD_TYPE_PKEY:
175       if (GNUNET_OK !=
176           GNUNET_CRYPTO_ecdsa_public_key_from_string (s, strlen (s), &pkey))
177       {
178         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
179                     _("Unable to parse PKEY record `%s'\n"),
180                     s);
181         return GNUNET_SYSERR;
182       }
183       *data = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPublicKey);
184       GNUNET_memcpy (*data, &pkey, sizeof (pkey));
185       *data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
186       return GNUNET_OK;
187
188     case GNUNET_GNSRECORD_TYPE_NICK:
189       *data = GNUNET_strdup (s);
190       *data_size = strlen (s);
191       return GNUNET_OK;
192     case GNUNET_GNSRECORD_TYPE_LEHO:
193       *data = GNUNET_strdup (s);
194       *data_size = strlen (s);
195       return GNUNET_OK;
196     case GNUNET_GNSRECORD_TYPE_GNS2DNS:
197       {
198         char nsbuf[514];
199         char *cpy;
200         char *at;
201         size_t off;
202
203         cpy = GNUNET_strdup (s);
204         at = strchr (cpy, '@');
205         if (NULL == at)
206         {
207           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208                       _("Unable to parse GNS2DNS record `%s'\n"),
209                       s);
210           GNUNET_free (cpy);
211           return GNUNET_SYSERR;
212         }
213         *at = '\0';
214         at++;
215
216         off = 0;
217         if ( (GNUNET_OK !=
218               GNUNET_DNSPARSER_builder_add_name (nsbuf,
219                                                  sizeof (nsbuf),
220                                                  &off,
221                                                  cpy)) ||
222              (GNUNET_OK !=
223               GNUNET_DNSPARSER_builder_add_name (nsbuf,
224                                                  sizeof (nsbuf),
225                                                  &off,
226                                                  at)) )
227         {
228           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
229                       _("Failed to serialize GNS2DNS record with value `%s'\n"),
230                       s);
231           GNUNET_free (cpy);
232           return GNUNET_SYSERR;
233         }
234         GNUNET_free (cpy);
235         *data_size = off;
236         *data = GNUNET_malloc (off);
237         GNUNET_memcpy (*data, nsbuf, off);
238         return GNUNET_OK;
239       }
240     case GNUNET_GNSRECORD_TYPE_VPN:
241       {
242         struct GNUNET_TUN_GnsVpnRecord *vpn;
243         char s_peer[103 + 1];
244         char s_serv[253 + 1];
245         unsigned int proto;
246
247         if (3 != SSCANF (s,
248                          "%u %103s %253s",
249                          &proto, s_peer, s_serv))
250         {
251           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
252                       _("Unable to parse VPN record string `%s'\n"),
253                       s);
254           return GNUNET_SYSERR;
255         }
256         *data_size = sizeof (struct GNUNET_TUN_GnsVpnRecord) + strlen (s_serv) + 1;
257         *data = vpn = GNUNET_malloc (*data_size);
258         if (GNUNET_OK !=
259             GNUNET_CRYPTO_eddsa_public_key_from_string ((char*) s_peer,
260                                                         strlen (s_peer),
261                                                         &vpn->peer.public_key))
262         {
263           GNUNET_free (vpn);
264           *data_size = 0;
265           return GNUNET_SYSERR;
266         }
267         vpn->proto = htons ((uint16_t) proto);
268         strcpy ((char*)&vpn[1], s_serv);
269         return GNUNET_OK;
270       }
271     case GNUNET_GNSRECORD_TYPE_BOX:
272       {
273         struct GNUNET_GNSRECORD_BoxRecord *box;
274         size_t rest;
275         unsigned int protocol;
276         unsigned int service;
277         unsigned int record_type;
278         void *bval;
279         size_t bval_size;
280
281         if (3 != SSCANF (s,
282                          "%u %u %u ",
283                          &protocol,
284                          &service,
285                          &record_type))
286         {
287           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
288                       _("Unable to parse BOX record string `%s'\n"),
289                       s);
290           return GNUNET_SYSERR;
291         }
292         rest = snprintf (NULL, 0,
293                          "%u %u %u ",
294                          protocol,
295                          service,
296                          record_type);
297         if (GNUNET_OK !=
298             GNUNET_GNSRECORD_string_to_value (record_type,
299                                               &s[rest],
300                                               &bval,
301                                               &bval_size))
302           return GNUNET_SYSERR;
303         *data_size = sizeof (struct GNUNET_GNSRECORD_BoxRecord) + bval_size;
304         *data = box = GNUNET_malloc (*data_size);
305         box->protocol = htons (protocol);
306         box->service = htons (service);
307         box->record_type = htonl (record_type);
308         GNUNET_memcpy (&box[1],
309                        bval,
310                        bval_size);
311         GNUNET_free (bval);
312         return GNUNET_OK;
313       }
314     default:
315       return GNUNET_SYSERR;
316   }
317 }
318
319
320 /**
321  * Mapping of record type numbers to human-readable
322  * record type names.
323  */
324 static struct {
325   const char *name;
326   uint32_t number;
327 } gns_name_map[] = {
328   { "PKEY",  GNUNET_GNSRECORD_TYPE_PKEY },
329   { "NICK",  GNUNET_GNSRECORD_TYPE_NICK },
330   { "LEHO",  GNUNET_GNSRECORD_TYPE_LEHO },
331   { "VPN", GNUNET_GNSRECORD_TYPE_VPN },
332   { "GNS2DNS", GNUNET_GNSRECORD_TYPE_GNS2DNS },
333   { "BOX", GNUNET_GNSRECORD_TYPE_BOX },
334   { NULL, UINT32_MAX }
335 };
336
337
338 /**
339  * Convert a type name (i.e. "AAAA") to the corresponding number.
340  *
341  * @param cls closure, unused
342  * @param gns_typename name to convert
343  * @return corresponding number, UINT32_MAX on error
344  */
345 static uint32_t
346 gns_typename_to_number (void *cls,
347                         const char *gns_typename)
348 {
349   unsigned int i;
350
351   i=0;
352   while ( (NULL != gns_name_map[i].name) &&
353           (0 != strcasecmp (gns_typename,
354                             gns_name_map[i].name)) )
355     i++;
356   return gns_name_map[i].number;
357 }
358
359
360 /**
361  * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
362  *
363  * @param cls closure, unused
364  * @param type number of a type to convert
365  * @return corresponding typestring, NULL on error
366  */
367 static const char *
368 gns_number_to_typename (void *cls,
369                         uint32_t type)
370 {
371   unsigned int i;
372
373   i=0;
374   while ( (NULL != gns_name_map[i].name) &&
375           (type != gns_name_map[i].number) )
376     i++;
377   return gns_name_map[i].name;
378 }
379
380
381 /**
382  * Entry point for the plugin.
383  *
384  * @param cls NULL
385  * @return the exported block API
386  */
387 void *
388 libgnunet_plugin_gnsrecord_gns_init (void *cls)
389 {
390   struct GNUNET_GNSRECORD_PluginFunctions *api;
391
392   api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
393   api->value_to_string = &gns_value_to_string;
394   api->string_to_value = &gns_string_to_value;
395   api->typename_to_number = &gns_typename_to_number;
396   api->number_to_typename = &gns_number_to_typename;
397   return api;
398 }
399
400
401 /**
402  * Exit point from the plugin.
403  *
404  * @param cls the return value from #libgnunet_plugin_block_test_init()
405  * @return NULL
406  */
407 void *
408 libgnunet_plugin_gnsrecord_gns_done (void *cls)
409 {
410   struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
411
412   GNUNET_free (api);
413   return NULL;
414 }
415
416 /* end of plugin_gnsrecord_gns.c */