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_Handle *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 struct GNUNET_IDENTITY_Ego *ego,
198 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
199 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
203 if (NULL != ltr->longest_match)
205 /* Final case: TLD matches one of our egos */
206 // FIXME: eat all of the match (not just TLD!)
207 if (0 == strcmp (ltr->name, ltr->longest_match))
209 /* name matches ego name perfectly, only "@" remains */
210 strcpy (ltr->name, GNUNET_GNS_EMPTY_LABEL_AT);
214 GNUNET_assert (strlen (ltr->longest_match) < strlen (ltr->name));
215 ltr->name[strlen (ltr->name) - strlen (ltr->longest_match) - 1] = '\0';
218 /* if the name is of the form 'label' (and not 'label.SUBDOMAIN'), never go to the DHT */
219 GNUNET_free (ltr->longest_match);
220 ltr->longest_match = NULL;
221 if (NULL == strchr (ltr->name, (unsigned char) '.'))
222 ltr->options = GNUNET_GNS_LO_NO_DHT;
224 ltr->options = GNUNET_GNS_LO_LOCAL_MASTER;
226 GNUNET_IDENTITY_ego_get_public_key (ltr->longest_match_ego, &pkey);
227 GNUNET_IDENTITY_disconnect (ltr->id_co);
229 lookup_with_public_key (ltr, &pkey);
233 /* no matching ego found */
234 GNUNET_IDENTITY_disconnect (ltr->id_co);
236 ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_NO, 0, NULL);
237 GNUNET_GNS_lookup_with_tld_cancel (ltr);
241 else if (NULL != name)
243 if ((strlen (name) <= strlen (ltr->name)) &&
244 (0 == strcmp (name, <r->name[strlen (ltr->name) - strlen (name)])) &&
245 ((strlen (name) == strlen (ltr->name)) ||
246 ('.' == ltr->name[strlen (ltr->name) - strlen (name) - 1])) &&
247 ((NULL == ltr->longest_match) ||
248 (strlen (name) > strlen (ltr->longest_match))))
250 /* found better match, update! */
251 GNUNET_free_non_null (ltr->longest_match);
252 ltr->longest_match = GNUNET_strdup (name);
253 ltr->longest_match_ego = ego;
260 * Perform an asynchronous lookup operation on the GNS,
261 * determining the zone using the TLD of the given name
262 * and the current configuration to resolve TLDs to zones.
264 * @param handle handle to the GNS service
265 * @param name the name to look up, including TLD
266 * @param type the record type to look up
267 * @param options local options for the lookup
268 * @param proc processor to call on result
269 * @param proc_cls closure for @a proc
270 * @return handle to the get request, NULL on error (i.e. bad configuration)
272 struct GNUNET_GNS_LookupWithTldRequest *
273 GNUNET_GNS_lookup_with_tld (struct GNUNET_GNS_Handle *handle,
276 enum GNUNET_GNS_LocalOptions options,
277 GNUNET_GNS_LookupResultProcessor2 proc,
280 struct GNUNET_GNS_LookupWithTldRequest *ltr;
284 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
286 ltr = GNUNET_new (struct GNUNET_GNS_LookupWithTldRequest);
287 ltr->gns_handle = handle;
288 ltr->name = GNUNET_strdup (name);
290 ltr->options = options;
291 ltr->lookup_proc = proc;
292 ltr->lookup_proc_cls = proc_cls;
293 /* start with trivial case: TLD is zkey */
294 tld = get_tld (ltr->name);
296 GNUNET_CRYPTO_ecdsa_public_key_from_string (tld, strlen (tld), &pkey))
298 eat_tld (ltr->name, tld);
299 lookup_with_public_key (ltr, &pkey);
303 /* second case: domain is mapped in our configuration file */
304 for (const char *domain = name; NULL != domain;
305 domain = strchr (domain, (unsigned char) '.'))
307 if ('.' == domain[0])
309 GNUNET_asprintf (&dot_tld, ".%s", domain);
310 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (handle->cfg,
316 GNUNET_CRYPTO_ecdsa_public_key_from_string (zonestr,
320 GNUNET_log_config_invalid (
321 GNUNET_ERROR_TYPE_ERROR,
324 _ ("Expected a base32-encoded public zone key\n"));
325 GNUNET_free (zonestr);
326 GNUNET_free (dot_tld);
327 GNUNET_free (ltr->name);
331 eat_tld (ltr->name, &dot_tld[1]);
332 GNUNET_free (zonestr);
333 GNUNET_free (dot_tld);
334 lookup_with_public_key (ltr, &pkey);
337 GNUNET_free (dot_tld);
339 /* FIXME: this call is still shitty slow to do the longest
340 suffix if we have thousands of egos. We should modify
341 the IDENTITY API to do the longest suffix matching
342 inside of the identity service and not do an O(n) IPC! */
344 GNUNET_IDENTITY_connect (ltr->gns_handle->cfg, &identity_zone_cb, ltr);
345 if (NULL == ltr->id_co)
347 GNUNET_free (ltr->name);
356 * Cancel pending lookup request
358 * @param ltr the lookup request to cancel
359 * @return closure from the lookup result processor
362 GNUNET_GNS_lookup_with_tld_cancel (struct GNUNET_GNS_LookupWithTldRequest *ltr)
364 void *ret = ltr->lookup_proc_cls;
366 if (NULL != ltr->id_co)
368 GNUNET_IDENTITY_disconnect (ltr->id_co);
373 GNUNET_GNS_lookup_cancel (ltr->lr);
376 GNUNET_free_non_null (ltr->longest_match);
377 GNUNET_free (ltr->name);
382 /* end of gns_tld_api.c */