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__)
42 * Handle to a lookup request
44 struct GNUNET_GNS_LookupWithTldRequest
50 struct GNUNET_GNS_Handle *gns_handle;
53 * processor to call on lookup result
55 GNUNET_GNS_LookupResultProcessor2 lookup_proc;
58 * Domain name we are resolving.
63 * @e lookup_proc closure
65 void *lookup_proc_cls;
68 * Underlying GNS lookup.
70 struct GNUNET_GNS_LookupRequest *lr;
73 * Lookup an ego with the identity service.
75 struct GNUNET_IDENTITY_EgoSuffixLookup *id_co;
78 * Name of the longest matching ego found so far.
79 * Must be freed on termination.
84 * Ego corresponding to @e longest_match.
86 struct GNUNET_IDENTITY_Ego *longest_match_ego;
89 * Desired result record type.
96 enum GNUNET_GNS_LocalOptions options;
101 * Obtain the TLD of the given @a name.
104 * @return the part of @a name after the last ".",
105 * or @a name if @a name does not contain a "."
108 get_tld (const char *name)
112 tld = strrchr (name, (unsigned char) '.');
116 tld++; /* skip the '.' */
122 * Eat the "TLD" (last bit) of the given @a name.
124 * @param[in,out] name a name
125 * @param tld what to eat (can be more than just the tld)
128 eat_tld (char *name, const char *tld)
130 GNUNET_assert (0 < strlen (name));
131 if ((NULL == tld) || (strlen (name) == strlen (tld)))
133 strcpy (name, GNUNET_GNS_EMPTY_LABEL_AT);
137 GNUNET_assert (strlen (tld) < strlen (name));
138 name[strlen (name) - strlen (tld) - 1] = '\0';
144 * Function called with the result of a GNS lookup.
146 * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
147 * @param rd_count number of records returned
148 * @param rd array of @a rd_count records with the results
151 process_lookup_result (void *cls,
153 const struct GNUNET_GNSRECORD_Data *rd)
155 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
158 ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_YES, rd_count, rd);
159 GNUNET_GNS_lookup_with_tld_cancel (ltr);
164 * Perform the actual resolution, starting with the zone
165 * identified by the given public key.
167 * @param pkey public key to use for the zone, can be NULL
170 lookup_with_public_key (struct GNUNET_GNS_LookupWithTldRequest *ltr,
171 const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
173 ltr->lr = GNUNET_GNS_lookup (ltr->gns_handle,
178 &process_lookup_result,
184 * Method called to with the ego we are to use for the lookup,
185 * when the ego is determined by a name.
187 * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
188 * @param ego ego handle, NULL at the end of the iteration
189 * @param ctx context we could store data to associate with @e ego
190 * @param name name of the ego
193 identity_zone_cb (void *cls,
194 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
195 const char *ego_name)
197 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
198 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
203 /* no matching ego found */
204 ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_NO, 0, NULL);
207 /* Final case: TLD matches one of our egos */
208 if (0 == strcmp (ltr->name, ego_name))
210 /* name matches ego name perfectly, only "@" remains */
211 strcpy (ltr->name, GNUNET_GNS_EMPTY_LABEL_AT);
215 GNUNET_assert (strlen (ego_name) < strlen (ltr->name));
216 ltr->name[strlen (ltr->name) - strlen (ego_name) - 1] = '\0';
218 /* if the name is of the form 'label' (and not 'label.SUBDOMAIN'), never go to the DHT */
219 if (NULL == strchr (ltr->name, (unsigned char) '.'))
220 ltr->options = GNUNET_GNS_LO_NO_DHT;
222 ltr->options = GNUNET_GNS_LO_LOCAL_MASTER;
223 GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pkey);
224 lookup_with_public_key (ltr, &pkey);
229 * Perform an asynchronous lookup operation on the GNS,
230 * determining the zone using the TLD of the given name
231 * and the current configuration to resolve TLDs to zones.
233 * @param handle handle to the GNS service
234 * @param name the name to look up, including TLD
235 * @param type the record type to look up
236 * @param options local options for the lookup
237 * @param proc processor to call on result
238 * @param proc_cls closure for @a proc
239 * @return handle to the get request, NULL on error (i.e. bad configuration)
241 struct GNUNET_GNS_LookupWithTldRequest *
242 GNUNET_GNS_lookup_with_tld (struct GNUNET_GNS_Handle *handle,
245 enum GNUNET_GNS_LocalOptions options,
246 GNUNET_GNS_LookupResultProcessor2 proc,
249 struct GNUNET_GNS_LookupWithTldRequest *ltr;
253 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
255 ltr = GNUNET_new (struct GNUNET_GNS_LookupWithTldRequest);
256 ltr->gns_handle = handle;
257 ltr->name = GNUNET_strdup (name);
259 ltr->options = options;
260 ltr->lookup_proc = proc;
261 ltr->lookup_proc_cls = proc_cls;
262 /* start with trivial case: TLD is zkey */
263 tld = get_tld (ltr->name);
265 GNUNET_CRYPTO_ecdsa_public_key_from_string (tld, strlen (tld), &pkey))
267 eat_tld (ltr->name, tld);
268 lookup_with_public_key (ltr, &pkey);
272 /* second case: domain is mapped in our configuration file */
273 for (const char *domain = name; NULL != domain;
274 domain = strchr (domain, (unsigned char) '.'))
276 if ('.' == domain[0])
278 GNUNET_asprintf (&dot_tld, ".%s", domain);
279 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (handle->cfg,
285 GNUNET_CRYPTO_ecdsa_public_key_from_string (zonestr,
289 GNUNET_log_config_invalid (
290 GNUNET_ERROR_TYPE_ERROR,
293 _ ("Expected a base32-encoded public zone key\n"));
294 GNUNET_free (zonestr);
295 GNUNET_free (dot_tld);
296 GNUNET_free (ltr->name);
300 eat_tld (ltr->name, &dot_tld[1]);
301 GNUNET_free (zonestr);
302 GNUNET_free (dot_tld);
303 lookup_with_public_key (ltr, &pkey);
306 GNUNET_free (dot_tld);
309 GNUNET_IDENTITY_ego_lookup_by_suffix (ltr->gns_handle->cfg,
313 if (NULL == ltr->id_co)
315 GNUNET_free (ltr->name);
324 * Cancel pending lookup request
326 * @param ltr the lookup request to cancel
327 * @return closure from the lookup result processor
330 GNUNET_GNS_lookup_with_tld_cancel (struct GNUNET_GNS_LookupWithTldRequest *ltr)
332 void *ret = ltr->lookup_proc_cls;
334 if (NULL != ltr->id_co)
336 GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (ltr->id_co);
341 GNUNET_GNS_lookup_cancel (ltr->lr);
344 GNUNET_free_non_null (ltr->longest_match);
345 GNUNET_free (ltr->name);
350 /* end of gns_tld_api.c */