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