remove 'illegal' (non-reentrant) log logic from signal handler
[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  * Handle to a lookup request
43  */
44 struct GNUNET_GNS_LookupWithTldRequest
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_EgoSuffixLookup *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, (unsigned char) '.');
112   if (NULL == tld)
113     tld = name;
114   else
115     tld++; /* skip the '.' */
116   return tld;
117 }
118
119
120 /**
121  * Eat the "TLD" (last bit) of the given @a name.
122  *
123  * @param[in,out] name a name
124  * @param tld what to eat (can be more than just the tld)
125  */
126 static void
127 eat_tld (char *name, const char *tld)
128 {
129   GNUNET_assert (0 < strlen (name));
130   if ((NULL == tld) || (strlen (name) == strlen (tld)))
131   {
132     strcpy (name, GNUNET_GNS_EMPTY_LABEL_AT);
133   }
134   else
135   {
136     GNUNET_assert (strlen (tld) < strlen (name));
137     name[strlen (name) - strlen (tld) - 1] = '\0';
138   }
139 }
140
141
142 /**
143  * Function called with the result of a GNS lookup.
144  *
145  * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
146  * @param rd_count number of records returned
147  * @param rd array of @a rd_count records with the results
148  */
149 static void
150 process_lookup_result (void *cls,
151                        uint32_t rd_count,
152                        const struct GNUNET_GNSRECORD_Data *rd)
153 {
154   struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
155
156   ltr->lr = NULL;
157   ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_YES, rd_count, rd);
158   GNUNET_GNS_lookup_with_tld_cancel (ltr);
159 }
160
161
162 /**
163  * Perform the actual resolution, starting with the zone
164  * identified by the given public key.
165  *
166  * @param pkey public key to use for the zone, can be NULL
167  */
168 static void
169 lookup_with_public_key (struct GNUNET_GNS_LookupWithTldRequest *ltr,
170                         const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
171 {
172   ltr->lr = GNUNET_GNS_lookup (ltr->gns_handle,
173                                ltr->name,
174                                pkey,
175                                ltr->type,
176                                ltr->options,
177                                &process_lookup_result,
178                                ltr);
179 }
180
181
182 /**
183  * Method called to with the ego we are to use for the lookup,
184  * when the ego is determined by a name.
185  *
186  * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
187  * @param ego ego handle, NULL at the end of the iteration
188  * @param ctx context we could store data to associate with @e ego
189  * @param name name of the ego
190  */
191 static void
192 identity_zone_cb (void *cls,
193                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
194                   const char *ego_name)
195 {
196   struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
197   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
198
199   ltr->id_co = NULL;
200   if (NULL == priv)
201   {
202     /* no matching ego found */
203     ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_NO, 0, NULL);
204     return;
205   }
206   /* Final case: TLD matches one of our egos */
207   if (0 == strcmp (ltr->name, ego_name))
208   {
209     /* name matches ego name perfectly, only "@" remains */
210     strcpy (ltr->name, GNUNET_GNS_EMPTY_LABEL_AT);
211   }
212   else
213   {
214     GNUNET_assert (strlen (ego_name) < strlen (ltr->name));
215     ltr->name[strlen (ltr->name) - strlen (ego_name) - 1] = '\0';
216   }
217   /* if the name is of the form 'label' (and not 'label.SUBDOMAIN'), never go to the DHT */
218   if (NULL == strchr (ltr->name, (unsigned char) '.'))
219     ltr->options = GNUNET_GNS_LO_NO_DHT;
220   else
221     ltr->options = GNUNET_GNS_LO_LOCAL_MASTER;
222   GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pkey);
223   lookup_with_public_key (ltr, &pkey);
224 }
225
226
227 /**
228  * Perform an asynchronous lookup operation on the GNS,
229  * determining the zone using the TLD of the given name
230  * and the current configuration to resolve TLDs to zones.
231  *
232  * @param handle handle to the GNS service
233  * @param name the name to look up, including TLD (in UTF-8 encoding)
234  * @param type the record type to look up
235  * @param options local options for the lookup
236  * @param proc processor to call on result
237  * @param proc_cls closure for @a proc
238  * @return handle to the get request, NULL on error (i.e. bad configuration)
239  */
240 struct GNUNET_GNS_LookupWithTldRequest *
241 GNUNET_GNS_lookup_with_tld (struct GNUNET_GNS_Handle *handle,
242                             const char *name,
243                             uint32_t type,
244                             enum GNUNET_GNS_LocalOptions options,
245                             GNUNET_GNS_LookupResultProcessor2 proc,
246                             void *proc_cls)
247 {
248   struct GNUNET_GNS_LookupWithTldRequest *ltr;
249   const char *tld;
250   char *dot_tld;
251   char *zonestr;
252   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
253
254   ltr = GNUNET_new (struct GNUNET_GNS_LookupWithTldRequest);
255   ltr->gns_handle = handle;
256   ltr->name = GNUNET_strdup (name);
257   ltr->type = type;
258   ltr->options = options;
259   ltr->lookup_proc = proc;
260   ltr->lookup_proc_cls = proc_cls;
261   /* start with trivial case: TLD is zkey */
262   tld = get_tld (ltr->name);
263   if (GNUNET_OK ==
264       GNUNET_CRYPTO_ecdsa_public_key_from_string (tld, strlen (tld), &pkey))
265   {
266     eat_tld (ltr->name, tld);
267     lookup_with_public_key (ltr, &pkey);
268     return ltr;
269   }
270
271   /* second case: domain is mapped in our configuration file */
272   for (const char *domain = name; NULL != domain;
273        domain = strchr (domain, (unsigned char) '.'))
274   {
275     if ('.' == domain[0])
276       domain++;
277     GNUNET_asprintf (&dot_tld, ".%s", domain);
278     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (handle->cfg,
279                                                             "gns",
280                                                             dot_tld,
281                                                             &zonestr))
282     {
283       if (GNUNET_OK !=
284           GNUNET_CRYPTO_ecdsa_public_key_from_string (zonestr,
285                                                       strlen (zonestr),
286                                                       &pkey))
287       {
288         GNUNET_log_config_invalid (
289           GNUNET_ERROR_TYPE_ERROR,
290           "gns",
291           dot_tld,
292           _ ("Expected a base32-encoded public zone key\n"));
293         GNUNET_free (zonestr);
294         GNUNET_free (dot_tld);
295         GNUNET_free (ltr->name);
296         GNUNET_free (ltr);
297         return NULL;
298       }
299       eat_tld (ltr->name, &dot_tld[1]);
300       GNUNET_free (zonestr);
301       GNUNET_free (dot_tld);
302       lookup_with_public_key (ltr, &pkey);
303       return ltr;
304     }
305     GNUNET_free (dot_tld);
306   }
307   ltr->id_co =
308     GNUNET_IDENTITY_ego_lookup_by_suffix (ltr->gns_handle->cfg,
309                                           ltr->name,
310                                           &identity_zone_cb,
311                                           ltr);
312   if (NULL == ltr->id_co)
313   {
314     GNUNET_free (ltr->name);
315     GNUNET_free (ltr);
316     return NULL;
317   }
318   return ltr;
319 }
320
321
322 /**
323  * Cancel pending lookup request
324  *
325  * @param ltr the lookup request to cancel
326  * @return closure from the lookup result processor
327  */
328 void *
329 GNUNET_GNS_lookup_with_tld_cancel (struct GNUNET_GNS_LookupWithTldRequest *ltr)
330 {
331   void *ret = ltr->lookup_proc_cls;
332
333   if (NULL != ltr->id_co)
334   {
335     GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (ltr->id_co);
336     ltr->id_co = NULL;
337   }
338   if (NULL != ltr->lr)
339   {
340     GNUNET_GNS_lookup_cancel (ltr->lr);
341     ltr->lr = NULL;
342   }
343   GNUNET_free_non_null (ltr->longest_match);
344   GNUNET_free (ltr->name);
345   GNUNET_free (ltr);
346   return ret;
347 }
348
349
350 /* end of gns_tld_api.c */