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