fix #4383
[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
35
36 /**
37  * Convert the 'value' of a record to a string.
38  *
39  * @param cls closure, unused
40  * @param type type of the record
41  * @param data value in binary encoding
42  * @param data_size number of bytes in @a data
43  * @return NULL on error, otherwise human-readable representation of the value
44  */
45 static char *
46 gns_value_to_string (void *cls,
47                      uint32_t type,
48                      const void *data,
49                      size_t data_size)
50 {
51   const char *cdata;
52
53   switch (type)
54   {
55   case GNUNET_GNSRECORD_TYPE_PKEY:
56     if (data_size != sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))
57       return NULL;
58     return GNUNET_CRYPTO_ecdsa_public_key_to_string (data);
59   case GNUNET_GNSRECORD_TYPE_NICK:
60     return GNUNET_strndup (data, data_size);
61   case GNUNET_GNSRECORD_TYPE_LEHO:
62     return GNUNET_strndup (data, data_size);
63   case GNUNET_GNSRECORD_TYPE_GNS2DNS:
64     {
65       char *ns;
66       char *ip;
67       size_t off;
68       char *nstr;
69
70       off = 0;
71       ns = GNUNET_DNSPARSER_parse_name (data,
72                                         data_size,
73                                         &off);
74       ip = GNUNET_DNSPARSER_parse_name (data,
75                                         data_size,
76                                         &off);
77       if ( (NULL == ns) ||
78            (NULL == ip) ||
79            (off != data_size) )
80       {
81         GNUNET_break_op (0);
82         GNUNET_free_non_null (ns);
83         GNUNET_free_non_null (ip);
84         return NULL;
85       }
86       GNUNET_asprintf (&nstr,
87                        "%s@%s",
88                        ns,
89                        ip);
90       GNUNET_free_non_null (ns);
91       GNUNET_free_non_null (ip);
92       return nstr;
93     }
94   case GNUNET_GNSRECORD_TYPE_VPN:
95     {
96       struct GNUNET_TUN_GnsVpnRecord vpn;
97       char* vpn_str;
98
99       cdata = data;
100       if ( (data_size <= sizeof (vpn)) ||
101            ('\0' != cdata[data_size - 1]) )
102         return NULL; /* malformed */
103       /* need to memcpy for alignment */
104       memcpy (&vpn,
105               data,
106               sizeof (vpn));
107       GNUNET_asprintf (&vpn_str,
108                        "%u %s %s",
109                        (unsigned int) ntohs (vpn.proto),
110                        (const char*) GNUNET_i2s_full (&vpn.peer),
111                        (const char*) &cdata[sizeof (vpn)]);
112       return vpn_str;
113     }
114   case GNUNET_GNSRECORD_TYPE_BOX:
115     {
116       struct GNUNET_GNSRECORD_BoxRecord box;
117       uint32_t rt;
118       char *box_str;
119       char *ival;
120
121       cdata = data;
122       if (data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
123         return NULL; /* malformed */
124       memcpy (&box,
125               data,
126               sizeof (box));
127       rt = ntohl (box.record_type);
128       ival = GNUNET_GNSRECORD_value_to_string (rt,
129                                                &cdata[sizeof (box)],
130                                                data_size - sizeof (box));
131       if (NULL == ival)
132         return NULL; /* malformed */
133       GNUNET_asprintf (&box_str,
134                        "%u %u %u %s",
135                        (unsigned int) ntohs (box.protocol),
136                        (unsigned int) ntohs (box.service),
137                        (unsigned int) rt,
138                        ival);
139       GNUNET_free (ival);
140       return box_str;
141     }
142   default:
143     return NULL;
144   }
145 }
146
147
148 /**
149  * Convert human-readable version of a 'value' of a record to the binary
150  * representation.
151  *
152  * @param cls closure, unused
153  * @param type type of the record
154  * @param s human-readable string
155  * @param data set to value in binary encoding (will be allocated)
156  * @param data_size set to number of bytes in @a data
157  * @return #GNUNET_OK on success
158  */
159 static int
160 gns_string_to_value (void *cls,
161                      uint32_t type,
162                      const char *s,
163                      void **data,
164                      size_t *data_size)
165 {
166   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
167
168   if (NULL == s)
169     return GNUNET_SYSERR;
170   switch (type)
171   {
172
173   case GNUNET_GNSRECORD_TYPE_PKEY:
174     if (GNUNET_OK !=
175         GNUNET_CRYPTO_ecdsa_public_key_from_string (s, strlen (s), &pkey))
176     {
177       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178            _("Unable to parse PKEY record `%s'\n"),
179            s);
180       return GNUNET_SYSERR;
181     }
182     *data = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPublicKey);
183     GNUNET_memcpy (*data, &pkey, sizeof (pkey));
184     *data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
185     return GNUNET_OK;
186
187   case GNUNET_GNSRECORD_TYPE_NICK:
188     *data = GNUNET_strdup (s);
189     *data_size = strlen (s);
190     return GNUNET_OK;
191   case GNUNET_GNSRECORD_TYPE_LEHO:
192     *data = GNUNET_strdup (s);
193     *data_size = strlen (s);
194     return GNUNET_OK;
195   case GNUNET_GNSRECORD_TYPE_GNS2DNS:
196     {
197       char nsbuf[514];
198       char *cpy;
199       char *at;
200       size_t off;
201
202       cpy = GNUNET_strdup (s);
203       at = strchr (cpy, '@');
204       if (NULL == at)
205       {
206         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
207                     _("Unable to parse GNS2DNS record `%s'\n"),
208                     s);
209         GNUNET_free (cpy);
210         return GNUNET_SYSERR;
211       }
212       *at = '\0';
213       at++;
214
215       off = 0;
216       if ( (GNUNET_OK !=
217             GNUNET_DNSPARSER_builder_add_name (nsbuf,
218                                                sizeof (nsbuf),
219                                                &off,
220                                                cpy)) ||
221            (GNUNET_OK !=
222             GNUNET_DNSPARSER_builder_add_name (nsbuf,
223                                                sizeof (nsbuf),
224                                                &off,
225                                                at)) )
226       {
227         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
228                     _("Failed to serialize GNS2DNS record with value `%s'\n"),
229                     s);
230         GNUNET_free (cpy);
231         return GNUNET_SYSERR;
232       }
233       GNUNET_free (cpy);
234       *data_size = off;
235       *data = GNUNET_malloc (off);
236       GNUNET_memcpy (*data, nsbuf, off);
237       return GNUNET_OK;
238     }
239   case GNUNET_GNSRECORD_TYPE_VPN:
240     {
241       struct GNUNET_TUN_GnsVpnRecord *vpn;
242       char s_peer[103 + 1];
243       char s_serv[253 + 1];
244       unsigned int proto;
245
246       if (3 != SSCANF (s,
247                        "%u %103s %253s",
248                        &proto, s_peer, s_serv))
249       {
250         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
251                     _("Unable to parse VPN record string `%s'\n"),
252                     s);
253         return GNUNET_SYSERR;
254       }
255       *data_size = sizeof (struct GNUNET_TUN_GnsVpnRecord) + strlen (s_serv) + 1;
256       *data = vpn = GNUNET_malloc (*data_size);
257       if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string ((char*) s_peer,
258                                                                    strlen (s_peer),
259                                                                    &vpn->peer.public_key))
260       {
261         GNUNET_free (vpn);
262         *data_size = 0;
263         return GNUNET_SYSERR;
264       }
265       vpn->proto = htons ((uint16_t) proto);
266       strcpy ((char*)&vpn[1], s_serv);
267       return GNUNET_OK;
268     }
269   case GNUNET_GNSRECORD_TYPE_BOX:
270     {
271       struct GNUNET_GNSRECORD_BoxRecord *box;
272       size_t rest;
273       unsigned int protocol;
274       unsigned int service;
275       unsigned int record_type;
276       void *bval;
277       size_t bval_size;
278
279       if (3 != SSCANF (s,
280                        "%u %u %u ",
281                        &protocol,
282                        &service,
283                        &record_type))
284       {
285         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
286                     _("Unable to parse BOX record string `%s'\n"),
287                     s);
288         return GNUNET_SYSERR;
289       }
290       rest = snprintf (NULL, 0,
291                        "%u %u %u ",
292                        protocol,
293                        service,
294                        record_type);
295       if (GNUNET_OK !=
296           GNUNET_GNSRECORD_string_to_value (record_type,
297                                             &s[rest],
298                                             &bval,
299                                             &bval_size))
300         return GNUNET_SYSERR;
301       *data_size = sizeof (struct GNUNET_GNSRECORD_BoxRecord) + bval_size;
302       *data = box = GNUNET_malloc (*data_size);
303       box->protocol = htons (protocol);
304       box->service = htons (service);
305       box->record_type = htonl (record_type);
306       GNUNET_memcpy (&box[1],
307               bval,
308               bval_size);
309       GNUNET_free (bval);
310       return GNUNET_OK;
311     }
312   default:
313     return GNUNET_SYSERR;
314   }
315 }
316
317
318 /**
319  * Mapping of record type numbers to human-readable
320  * record type names.
321  */
322 static struct {
323   const char *name;
324   uint32_t number;
325 } gns_name_map[] = {
326   { "PKEY",  GNUNET_GNSRECORD_TYPE_PKEY },
327   { "NICK",  GNUNET_GNSRECORD_TYPE_NICK },
328   { "LEHO",  GNUNET_GNSRECORD_TYPE_LEHO },
329   { "VPN", GNUNET_GNSRECORD_TYPE_VPN },
330   { "GNS2DNS", GNUNET_GNSRECORD_TYPE_GNS2DNS },
331   { "BOX", GNUNET_GNSRECORD_TYPE_BOX },
332   { NULL, UINT32_MAX }
333 };
334
335
336 /**
337  * Convert a type name (i.e. "AAAA") to the corresponding number.
338  *
339  * @param cls closure, unused
340  * @param gns_typename name to convert
341  * @return corresponding number, UINT32_MAX on error
342  */
343 static uint32_t
344 gns_typename_to_number (void *cls,
345                         const char *gns_typename)
346 {
347   unsigned int i;
348
349   i=0;
350   while ( (NULL != gns_name_map[i].name) &&
351           (0 != strcasecmp (gns_typename,
352                             gns_name_map[i].name)) )
353     i++;
354   return gns_name_map[i].number;
355 }
356
357
358 /**
359  * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
360  *
361  * @param cls closure, unused
362  * @param type number of a type to convert
363  * @return corresponding typestring, NULL on error
364  */
365 static const char *
366 gns_number_to_typename (void *cls,
367                         uint32_t type)
368 {
369   unsigned int i;
370
371   i=0;
372   while ( (NULL != gns_name_map[i].name) &&
373           (type != gns_name_map[i].number) )
374     i++;
375   return gns_name_map[i].name;
376 }
377
378
379 /**
380  * Entry point for the plugin.
381  *
382  * @param cls NULL
383  * @return the exported block API
384  */
385 void *
386 libgnunet_plugin_gnsrecord_gns_init (void *cls)
387 {
388   struct GNUNET_GNSRECORD_PluginFunctions *api;
389
390   api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
391   api->value_to_string = &gns_value_to_string;
392   api->string_to_value = &gns_string_to_value;
393   api->typename_to_number = &gns_typename_to_number;
394   api->number_to_typename = &gns_number_to_typename;
395   return api;
396 }
397
398
399 /**
400  * Exit point from the plugin.
401  *
402  * @param cls the return value from #libgnunet_plugin_block_test_init()
403  * @return NULL
404  */
405 void *
406 libgnunet_plugin_gnsrecord_gns_done (void *cls)
407 {
408   struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
409
410   GNUNET_free (api);
411   return NULL;
412 }
413
414 /* end of plugin_gnsrecord_gns.c */