REST: nothing triggers rest
[oweals/gnunet.git] / src / gns / gns_tld_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2013, 2016, 2018 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      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20 /**
21  * @file gns/gns_tld_api.c
22  * @brief library to access the GNS service, including TLD lookup
23  * @author Martin Schanzenbach
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_arm_service.h"
30 #include "gnunet_identity_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_dht_service.h"
34 #include "gns.h"
35 #include "gns_api.h"
36
37
38 #define LOG(kind,...) GNUNET_log_from (kind, "gns-tld-api",__VA_ARGS__)
39
40
41
42 /**
43  * Handle to a lookup request
44  */
45 struct GNUNET_GNS_LookupWithTldRequest
46 {
47
48   /**
49    * handle to gns
50    */
51   struct GNUNET_GNS_Handle *gns_handle;
52
53   /**
54    * processor to call on lookup result
55    */
56   GNUNET_GNS_LookupResultProcessor2 lookup_proc;
57
58   /**
59    * Domain name we are resolving.
60    */
61   char *name;
62
63   /**
64    * @e lookup_proc closure
65    */
66   void *lookup_proc_cls;
67
68   /**
69    * Underlying GNS lookup.
70    */
71   struct GNUNET_GNS_LookupRequest *lr;
72
73   /**
74    * Lookup an ego with the identity service.
75    */
76   struct GNUNET_IDENTITY_Handle *id_co;
77
78   /**
79    * Name of the longest matching ego found so far.
80    * Must be freed on termination.
81    */
82   char *longest_match;
83
84   /**
85    * Ego corresponding to @e longest_match.
86    */
87   struct GNUNET_IDENTITY_Ego *longest_match_ego;
88
89   /**
90    * Desired result record type.
91    */
92   uint32_t type;
93
94   /**
95    * Lookup options.
96    */
97   enum GNUNET_GNS_LocalOptions options;
98 };
99
100
101 /**
102  * Obtain the TLD of the given @a name.
103  *
104  * @param name a name
105  * @return the part of @a name after the last ".",
106  *         or @a name if @a name does not contain a "."
107  */
108 static const char *
109 get_tld (const char *name)
110 {
111   const char *tld;
112
113   tld = strrchr (name,
114                  (unsigned char) '.');
115   if (NULL == tld)
116     tld = name;
117   else
118     tld++; /* skip the '.' */
119   return tld;
120 }
121
122
123 /**
124  * Eat the "TLD" (last bit) of the given @a name.
125  *
126  * @param[in,out] name a name
127  * @param tld what to eat (can be more than just the tld)
128  */
129 static void
130 eat_tld (char *name,
131          const char *tld)
132 {
133   GNUNET_assert (0 < strlen (name));
134   if (NULL == tld)
135   {
136     strcpy (name,
137             GNUNET_GNS_EMPTY_LABEL_AT);
138   }
139   else
140   {
141     GNUNET_assert (strlen (tld) < strlen (name));
142     name[strlen(name) - strlen(tld) - 1] = '\0';
143   }
144 }
145
146
147 /**
148  * Function called with the result of a GNS lookup.
149  *
150  * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
151  * @param rd_count number of records returned
152  * @param rd array of @a rd_count records with the results
153  */
154 static void
155 process_lookup_result (void *cls,
156                        uint32_t rd_count,
157                        const struct GNUNET_GNSRECORD_Data *rd)
158 {
159   struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
160
161   ltr->lr = NULL;
162   ltr->lookup_proc (ltr->lookup_proc_cls,
163                     GNUNET_YES,
164                     rd_count,
165                     rd);
166   GNUNET_GNS_lookup_with_tld_cancel (ltr);
167 }
168
169
170 /**
171  * Perform the actual resolution, starting with the zone
172  * identified by the given public key.
173  *
174  * @param pkey public key to use for the zone, can be NULL
175  */
176 static void
177 lookup_with_public_key (struct GNUNET_GNS_LookupWithTldRequest *ltr,
178                         const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
179 {
180   ltr->lr = GNUNET_GNS_lookup (ltr->gns_handle,
181                                ltr->name,
182                                pkey,
183                                ltr->type,
184                                ltr->options,
185                                &process_lookup_result,
186                                ltr);
187 }
188
189
190 /**
191  * Method called to with the ego we are to use for the lookup,
192  * when the ego is determined by a name.
193  *
194  * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
195  * @param ego ego handle, NULL at the end of the iteration
196  * @param ctx context we could store data to associate with @e ego
197  * @param name name of the ego
198  */
199 static void
200 identity_zone_cb (void *cls,
201                   struct GNUNET_IDENTITY_Ego *ego,
202                   void **ctx,
203                   const char *name)
204 {
205   struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
206   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
207
208   if (NULL == ego)
209   {
210     if (NULL != ltr->longest_match)
211     {
212       /* Final case: TLD matches one of our egos */
213       // FIXME: eat all of the match (not just TLD!)
214       if (0 == strcmp (ltr->name,
215                        ltr->longest_match))
216       {
217         /* name matches ego name perfectly, only "@" remains */
218         strcpy (ltr->name,
219                 GNUNET_GNS_EMPTY_LABEL_AT);
220       }
221       else
222       {
223         GNUNET_assert (strlen (ltr->longest_match) < strlen (ltr->name));
224         ltr->name[strlen(ltr->name) - strlen (ltr->longest_match) - 1] = '\0';
225       }
226
227       /* if the name is of the form 'label' (and not 'label.SUBDOMAIN'), never go to the DHT */
228       GNUNET_free (ltr->longest_match);
229       ltr->longest_match = NULL;
230       if (NULL == strchr (ltr->name,
231                           (unsigned char) '.'))
232         ltr->options = GNUNET_GNS_LO_NO_DHT;
233       else
234         ltr->options = GNUNET_GNS_LO_LOCAL_MASTER;
235
236       GNUNET_IDENTITY_ego_get_public_key (ltr->longest_match_ego,
237                                           &pkey);
238       GNUNET_IDENTITY_disconnect (ltr->id_co);
239       ltr->id_co = NULL;
240       lookup_with_public_key (ltr,
241                               &pkey);
242     }
243     else
244     {
245       /* no matching ego found */
246       GNUNET_IDENTITY_disconnect (ltr->id_co);
247       ltr->id_co = NULL;
248       ltr->lookup_proc (ltr->lookup_proc_cls,
249                         GNUNET_NO,
250                         0,
251                         NULL);
252       GNUNET_GNS_lookup_with_tld_cancel (ltr);
253     }
254     return;
255   }
256   else if (NULL != name)
257   {
258     if ( (strlen (name) <= strlen (ltr->name)) &&
259          (0 == strcmp (name,
260                        &ltr->name[strlen(ltr->name) - strlen (name)])) &&
261          ( (strlen (name) == strlen (ltr->name)) ||
262            ('.' == ltr->name[strlen(ltr->name) - strlen (name) - 1]) ) &&
263          ( (NULL == ltr->longest_match) ||
264            (strlen (name) > strlen (ltr->longest_match)) ) )
265     {
266       /* found better match, update! */
267       GNUNET_free_non_null (ltr->longest_match);
268       ltr->longest_match = GNUNET_strdup (name);
269       ltr->longest_match_ego = ego;
270     }
271   }
272 }
273
274
275 /**
276  * Perform an asynchronous lookup operation on the GNS,
277  * determining the zone using the TLD of the given name
278  * and the current configuration to resolve TLDs to zones.
279  *
280  * @param handle handle to the GNS service
281  * @param name the name to look up, including TLD
282  * @param type the record type to look up
283  * @param options local options for the lookup
284  * @param proc processor to call on result
285  * @param proc_cls closure for @a proc
286  * @return handle to the get request, NULL on error (i.e. bad configuration)
287  */
288 struct GNUNET_GNS_LookupWithTldRequest *
289 GNUNET_GNS_lookup_with_tld (struct GNUNET_GNS_Handle *handle,
290                             const char *name,
291                             uint32_t type,
292                             enum GNUNET_GNS_LocalOptions options,
293                             GNUNET_GNS_LookupResultProcessor2 proc,
294                             void *proc_cls)
295 {
296   struct GNUNET_GNS_LookupWithTldRequest *ltr;
297   const char *tld;
298   char *dot_tld;
299   char *zonestr;
300   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
301
302   ltr = GNUNET_new (struct GNUNET_GNS_LookupWithTldRequest);
303   ltr->gns_handle = handle;
304   ltr->name = GNUNET_strdup (name);
305   ltr->type = type;
306   ltr->options = options;
307   ltr->lookup_proc = proc;
308   ltr->lookup_proc_cls = proc_cls;
309   /* start with trivial case: TLD is zkey */
310   tld = get_tld (ltr->name);
311   if (GNUNET_OK ==
312       GNUNET_CRYPTO_ecdsa_public_key_from_string (tld,
313                                                   strlen (tld),
314                                                   &pkey))
315   {
316     eat_tld (ltr->name,
317              tld);
318     lookup_with_public_key (ltr,
319                             &pkey);
320     return ltr;
321   }
322
323   /* second case: domain is mapped in our configuration file */
324   for (const char *domain = name;
325        NULL != domain;
326        domain = strchr (domain,
327                         (unsigned char) '.'))
328   {
329     if ('.' == domain[0])
330       domain++;
331     GNUNET_asprintf (&dot_tld,
332                      ".%s",
333                      domain);
334     if (GNUNET_OK ==
335         GNUNET_CONFIGURATION_get_value_string (handle->cfg,
336                                                "gns",
337                                                dot_tld,
338                                                &zonestr))
339     {
340       if (GNUNET_OK !=
341           GNUNET_CRYPTO_ecdsa_public_key_from_string (zonestr,
342                                                       strlen (zonestr),
343                                                       &pkey))
344       {
345         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
346                                    "gns",
347                                    dot_tld,
348                                    _("Expected a base32-encoded public zone key\n"));
349         GNUNET_free (zonestr);
350         GNUNET_free (dot_tld);
351         GNUNET_free (ltr->name);
352         GNUNET_free (ltr);
353         return NULL;
354       }
355       eat_tld (ltr->name,
356                &dot_tld[1]);
357       GNUNET_free (zonestr);
358       GNUNET_free (dot_tld);
359       lookup_with_public_key (ltr,
360                               &pkey);
361       return ltr;
362     }
363     GNUNET_free (dot_tld);
364   }
365
366   ltr->id_co = GNUNET_IDENTITY_connect (ltr->gns_handle->cfg,
367                                         &identity_zone_cb,
368                                         ltr);
369   if (NULL == ltr->id_co)
370   {
371     GNUNET_free (ltr->name);
372     GNUNET_free (ltr);
373     return NULL;
374   }
375   return ltr;
376 }
377
378
379 /**
380  * Cancel pending lookup request
381  *
382  * @param ltr the lookup request to cancel
383  * @return closure from the lookup result processor
384  */
385 void *
386 GNUNET_GNS_lookup_with_tld_cancel (struct GNUNET_GNS_LookupWithTldRequest *ltr)
387 {
388   void *ret = ltr->lookup_proc_cls;
389
390   if (NULL != ltr->id_co)
391   {
392     GNUNET_IDENTITY_disconnect (ltr->id_co);
393     ltr->id_co = NULL;
394   }
395   if (NULL != ltr->lr)
396   {
397     GNUNET_GNS_lookup_cancel (ltr->lr);
398     ltr->lr = NULL;
399   }
400   GNUNET_free_non_null (ltr->longest_match);
401   GNUNET_free (ltr->name);
402   GNUNET_free (ltr);
403   return ret;
404 }
405
406 /* end of gns_tld_api.c */