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
49 struct GNUNET_GNS_Handle *gns_handle;
52 * processor to call on lookup result
54 GNUNET_GNS_LookupResultProcessor2 lookup_proc;
57 * Domain name we are resolving.
62 * @e lookup_proc closure
64 void *lookup_proc_cls;
67 * Underlying GNS lookup.
69 struct GNUNET_GNS_LookupRequest *lr;
72 * Lookup an ego with the identity service.
74 struct GNUNET_IDENTITY_EgoSuffixLookup *id_co;
77 * Name of the longest matching ego found so far.
78 * Must be freed on termination.
83 * Ego corresponding to @e longest_match.
85 struct GNUNET_IDENTITY_Ego *longest_match_ego;
88 * Desired result record type.
95 enum GNUNET_GNS_LocalOptions options;
100 * Obtain the TLD of the given @a name.
103 * @return the part of @a name after the last ".",
104 * or @a name if @a name does not contain a "."
107 get_tld (const char *name)
111 tld = strrchr (name, (unsigned char) '.');
115 tld++; /* skip the '.' */
121 * Eat the "TLD" (last bit) of the given @a name.
123 * @param[in,out] name a name
124 * @param tld what to eat (can be more than just the tld)
127 eat_tld (char *name, const char *tld)
129 GNUNET_assert (0 < strlen (name));
130 if ((NULL == tld) || (strlen (name) == strlen (tld)))
132 strcpy (name, GNUNET_GNS_EMPTY_LABEL_AT);
136 GNUNET_assert (strlen (tld) < strlen (name));
137 name[strlen (name) - strlen (tld) - 1] = '\0';
143 * Function called with the result of a GNS lookup.
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
150 process_lookup_result (void *cls,
152 const struct GNUNET_GNSRECORD_Data *rd)
154 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
157 ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_YES, rd_count, rd);
158 GNUNET_GNS_lookup_with_tld_cancel (ltr);
163 * Perform the actual resolution, starting with the zone
164 * identified by the given public key.
166 * @param pkey public key to use for the zone, can be NULL
169 lookup_with_public_key (struct GNUNET_GNS_LookupWithTldRequest *ltr,
170 const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
172 ltr->lr = GNUNET_GNS_lookup (ltr->gns_handle,
177 &process_lookup_result,
183 * Method called to with the ego we are to use for the lookup,
184 * when the ego is determined by a name.
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
192 identity_zone_cb (void *cls,
193 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
194 const char *ego_name)
196 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
197 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
202 /* no matching ego found */
203 ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_NO, 0, NULL);
206 /* Final case: TLD matches one of our egos */
207 if (0 == strcmp (ltr->name, ego_name))
209 /* name matches ego name perfectly, only "@" remains */
210 strcpy (ltr->name, GNUNET_GNS_EMPTY_LABEL_AT);
214 GNUNET_assert (strlen (ego_name) < strlen (ltr->name));
215 ltr->name[strlen (ltr->name) - strlen (ego_name) - 1] = '\0';
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;
221 ltr->options = GNUNET_GNS_LO_LOCAL_MASTER;
222 GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pkey);
223 lookup_with_public_key (ltr, &pkey);
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.
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)
240 struct GNUNET_GNS_LookupWithTldRequest *
241 GNUNET_GNS_lookup_with_tld (struct GNUNET_GNS_Handle *handle,
244 enum GNUNET_GNS_LocalOptions options,
245 GNUNET_GNS_LookupResultProcessor2 proc,
248 struct GNUNET_GNS_LookupWithTldRequest *ltr;
252 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
254 ltr = GNUNET_new (struct GNUNET_GNS_LookupWithTldRequest);
255 ltr->gns_handle = handle;
256 ltr->name = GNUNET_strdup (name);
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);
264 GNUNET_CRYPTO_ecdsa_public_key_from_string (tld, strlen (tld), &pkey))
266 eat_tld (ltr->name, tld);
267 lookup_with_public_key (ltr, &pkey);
271 /* second case: domain is mapped in our configuration file */
272 for (const char *domain = name; NULL != domain;
273 domain = strchr (domain, (unsigned char) '.'))
275 if ('.' == domain[0])
277 GNUNET_asprintf (&dot_tld, ".%s", domain);
278 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (handle->cfg,
284 GNUNET_CRYPTO_ecdsa_public_key_from_string (zonestr,
288 GNUNET_log_config_invalid (
289 GNUNET_ERROR_TYPE_ERROR,
292 _ ("Expected a base32-encoded public zone key\n"));
293 GNUNET_free (zonestr);
294 GNUNET_free (dot_tld);
295 GNUNET_free (ltr->name);
299 eat_tld (ltr->name, &dot_tld[1]);
300 GNUNET_free (zonestr);
301 GNUNET_free (dot_tld);
302 lookup_with_public_key (ltr, &pkey);
305 GNUNET_free (dot_tld);
308 GNUNET_IDENTITY_ego_lookup_by_suffix (ltr->gns_handle->cfg,
312 if (NULL == ltr->id_co)
314 GNUNET_free (ltr->name);
323 * Cancel pending lookup request
325 * @param ltr the lookup request to cancel
326 * @return closure from the lookup result processor
329 GNUNET_GNS_lookup_with_tld_cancel (struct GNUNET_GNS_LookupWithTldRequest *ltr)
331 void *ret = ltr->lookup_proc_cls;
333 if (NULL != ltr->id_co)
335 GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (ltr->id_co);
340 GNUNET_GNS_lookup_cancel (ltr->lr);
343 GNUNET_free_non_null (ltr->longest_match);
344 GNUNET_free (ltr->name);
350 /* end of gns_tld_api.c */