2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016, 2018 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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
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"
38 #define LOG(kind,...) GNUNET_log_from (kind, "gns-tld-api",__VA_ARGS__)
43 * Handle to a lookup request
45 struct GNUNET_GNS_LookupWithTldRequest
51 struct GNUNET_GNS_Handle *gns_handle;
54 * processor to call on lookup result
56 GNUNET_GNS_LookupResultProcessor2 lookup_proc;
59 * Domain name we are resolving.
64 * @e lookup_proc closure
66 void *lookup_proc_cls;
69 * Underlying GNS lookup.
71 struct GNUNET_GNS_LookupRequest *lr;
74 * Lookup an ego with the identity service.
76 struct GNUNET_IDENTITY_Handle *id_co;
79 * Name of the longest matching ego found so far.
80 * Must be freed on termination.
85 * Ego corresponding to @e longest_match.
87 struct GNUNET_IDENTITY_Ego *longest_match_ego;
90 * Desired result record type.
97 enum GNUNET_GNS_LocalOptions options;
102 * Obtain the TLD of the given @a name.
105 * @return the part of @a name after the last ".",
106 * or @a name if @a name does not contain a "."
109 get_tld (const char *name)
114 (unsigned char) '.');
118 tld++; /* skip the '.' */
124 * Eat the "TLD" (last bit) of the given @a name.
126 * @param[in,out] name a name
127 * @param tld what to eat (can be more than just the tld)
133 GNUNET_assert (0 < strlen (name));
137 GNUNET_GNS_EMPTY_LABEL_AT);
141 GNUNET_assert (strlen (tld) < strlen (name));
142 name[strlen(name) - strlen(tld) - 1] = '\0';
148 * Function called with the result of a GNS lookup.
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
155 process_lookup_result (void *cls,
157 const struct GNUNET_GNSRECORD_Data *rd)
159 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
162 ltr->lookup_proc (ltr->lookup_proc_cls,
166 GNUNET_GNS_lookup_with_tld_cancel (ltr);
171 * Perform the actual resolution, starting with the zone
172 * identified by the given public key.
174 * @param pkey public key to use for the zone, can be NULL
177 lookup_with_public_key (struct GNUNET_GNS_LookupWithTldRequest *ltr,
178 const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
180 ltr->lr = GNUNET_GNS_lookup (ltr->gns_handle,
185 &process_lookup_result,
191 * Method called to with the ego we are to use for the lookup,
192 * when the ego is determined by a name.
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
200 identity_zone_cb (void *cls,
201 struct GNUNET_IDENTITY_Ego *ego,
205 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
206 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
210 if (NULL != ltr->longest_match)
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,
217 /* name matches ego name perfectly, only "@" remains */
219 GNUNET_GNS_EMPTY_LABEL_AT);
223 GNUNET_assert (strlen (ltr->longest_match) < strlen (ltr->name));
224 ltr->name[strlen(ltr->name) - strlen (ltr->longest_match) - 1] = '\0';
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;
234 ltr->options = GNUNET_GNS_LO_LOCAL_MASTER;
236 GNUNET_IDENTITY_ego_get_public_key (ltr->longest_match_ego,
238 GNUNET_IDENTITY_disconnect (ltr->id_co);
240 lookup_with_public_key (ltr,
245 /* no matching ego found */
246 GNUNET_IDENTITY_disconnect (ltr->id_co);
248 ltr->lookup_proc (ltr->lookup_proc_cls,
252 GNUNET_GNS_lookup_with_tld_cancel (ltr);
256 else if (NULL != name)
258 if ( (strlen (name) <= strlen (ltr->name)) &&
260 <r->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)) ) )
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;
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.
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)
288 struct GNUNET_GNS_LookupWithTldRequest *
289 GNUNET_GNS_lookup_with_tld (struct GNUNET_GNS_Handle *handle,
292 enum GNUNET_GNS_LocalOptions options,
293 GNUNET_GNS_LookupResultProcessor2 proc,
296 struct GNUNET_GNS_LookupWithTldRequest *ltr;
300 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
302 ltr = GNUNET_new (struct GNUNET_GNS_LookupWithTldRequest);
303 ltr->gns_handle = handle;
304 ltr->name = GNUNET_strdup (name);
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);
312 GNUNET_CRYPTO_ecdsa_public_key_from_string (tld,
318 lookup_with_public_key (ltr,
323 /* second case: domain is mapped in our configuration file */
324 for (const char *domain = name;
326 domain = strchr (domain,
327 (unsigned char) '.'))
329 if ('.' == domain[0])
331 GNUNET_asprintf (&dot_tld,
335 GNUNET_CONFIGURATION_get_value_string (handle->cfg,
341 GNUNET_CRYPTO_ecdsa_public_key_from_string (zonestr,
345 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
348 _("Expected a base32-encoded public zone key\n"));
349 GNUNET_free (zonestr);
350 GNUNET_free (dot_tld);
351 GNUNET_free (ltr->name);
357 GNUNET_free (zonestr);
358 GNUNET_free (dot_tld);
359 lookup_with_public_key (ltr,
363 GNUNET_free (dot_tld);
366 ltr->id_co = GNUNET_IDENTITY_connect (ltr->gns_handle->cfg,
369 if (NULL == ltr->id_co)
371 GNUNET_free (ltr->name);
380 * Cancel pending lookup request
382 * @param ltr the lookup request to cancel
383 * @return closure from the lookup result processor
386 GNUNET_GNS_lookup_with_tld_cancel (struct GNUNET_GNS_LookupWithTldRequest *ltr)
388 void *ret = ltr->lookup_proc_cls;
390 if (NULL != ltr->id_co)
392 GNUNET_IDENTITY_disconnect (ltr->id_co);
397 GNUNET_GNS_lookup_cancel (ltr->lr);
400 GNUNET_free_non_null (ltr->longest_match);
401 GNUNET_free (ltr->name);
406 /* end of gns_tld_api.c */