dirmngr: Keep track of domains used for WKD queries

* dirmngr/domaininfo.c: New file.
* dirmngr/Makefile.am (dirmngr_SOURCES): Add file.
* dirmngr/server.c (cmd_wkd_get): Check whether the domain is already
known and tell domaininfo about the results.
--

This adds a registry for domain information to eventually avoid
useless queries for domains which do not support WKD.  The missing
part is a background task to check whether a queried domain supports
WKD at all and to expire old entries.

Signed-off-by: Werner Koch <wk@gnupg.org>
(cherry picked from commit 65038e6852)
This commit is contained in:
Werner Koch 2017-11-13 16:09:32 +01:00
parent 3e72143023
commit 6c1dcd79cf
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 302 additions and 0 deletions

View File

@ -16,6 +16,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0+
## Process this file with automake to produce Makefile.in
@ -57,6 +59,7 @@ noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h
dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \
certcache.c certcache.h \
domaininfo.c \
loadswdb.c \
cdb.h cdblib.c misc.c dirmngr-err.h \
ocsp.c ocsp.h validate.c validate.h \

View File

@ -17,6 +17,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0+
*/
#include <config.h>
@ -1871,6 +1873,7 @@ handle_signal (int signo)
case SIGUSR1:
cert_cache_print_stats ();
domaininfo_print_stats ();
break;
case SIGUSR2:

View File

@ -17,6 +17,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0+
*/
#ifndef DIRMNGR_H
@ -248,4 +250,14 @@ gpg_error_t gnupg_http_tls_verify_cb (void *opaque,
gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force);
/*-- domaininfo.c --*/
void domaininfo_print_stats (void);
int domaininfo_is_wkd_not_supported (const char *domain);
void domaininfo_set_no_name (const char *domain);
void domaininfo_set_wkd_supported (const char *domain);
void domaininfo_set_wkd_not_supported (const char *domain);
void domaininfo_set_wkd_not_found (const char *domain);
#endif /*DIRMNGR_H*/

244
dirmngr/domaininfo.c Normal file
View File

@ -0,0 +1,244 @@
/* domaininfo.c - Gather statistics about accessed domains
* Copyright (C) 2017 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0+
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "dirmngr.h"
#define NO_OF_DOMAINBUCKETS 103
/* Object to keep track of a domain name. */
struct domaininfo_s
{
struct domaininfo_s *next;
unsigned int no_name:1; /* Domain name not found. */
unsigned int wkd_not_found:1; /* A WKD query failed. */
unsigned int wkd_supported:1; /* One WKD entry was found. */
unsigned int wkd_not_supported:1; /* Definitely does not support WKD. */
char name[1];
};
typedef struct domaininfo_s *domaininfo_t;
/* And the hashed array. */
static domaininfo_t domainbuckets[NO_OF_DOMAINBUCKETS];
/* The hash function we use. Must not call a system function. */
static inline u32
hash_domain (const char *domain)
{
const unsigned char *s = (const unsigned char*)domain;
u32 hashval = 0;
u32 carry;
for (; *s; s++)
{
if (*s == '.')
continue;
hashval = (hashval << 4) + *s;
if ((carry = (hashval & 0xf0000000)))
{
hashval ^= (carry >> 24);
hashval ^= carry;
}
}
return hashval % NO_OF_DOMAINBUCKETS;
}
void
domaininfo_print_stats (void)
{
int bidx;
domaininfo_t di;
int count, no_name, wkd_not_found, wkd_supported, wkd_not_supported;
for (bidx = 0; bidx < NO_OF_DOMAINBUCKETS; bidx++)
{
count = no_name = wkd_not_found = wkd_supported = wkd_not_supported = 0;
for (di = domainbuckets[bidx]; di; di = di->next)
{
count++;
if (di->no_name)
no_name++;
if (di->wkd_not_found)
wkd_not_found++;
if (di->wkd_supported)
wkd_supported++;
if (di->wkd_not_supported)
wkd_not_supported++;
}
if (count)
log_info ("domaininfo: chain %3d length=%d nn=%d nf=%d s=%d ns=%d\n",
bidx, count, no_name,
wkd_not_found, wkd_supported, wkd_not_supported);
}
}
/* Return true if DOMAIN definitely does not support WKD. Noet that
* DOMAIN is expected to be lowercase. */
int
domaininfo_is_wkd_not_supported (const char *domain)
{
domaininfo_t di;
for (di = domainbuckets[hash_domain (domain)]; di; di = di->next)
if (!strcmp (di->name, domain))
return !!di->wkd_not_supported;
return 0; /* We don't know. */
}
/* Core update function. DOMAIN is expected to be lowercase.
* CALLBACK is called to update the existing or the newly inserted
* item. */
static void
insert_or_update (const char *domain,
void (*callback)(domaininfo_t di, int insert_mode))
{
domaininfo_t di;
domaininfo_t di_new;
u32 hash;
hash = hash_domain (domain);
for (di = domainbuckets[hash]; di; di = di->next)
if (!strcmp (di->name, domain))
{
callback (di, 0); /* Update */
return;
}
di_new = xtrycalloc (1, sizeof *di + strlen (domain));
if (!di_new)
return; /* Out of core - we ignore this. */
/* Need to do another lookup because the malloc is a system call and
* thus the hash array may have been changed by another thread. */
for (di = domainbuckets[hash]; di; di = di->next)
if (!strcmp (di->name, domain))
{
callback (di, 0); /* Update */
xfree (di_new);
return;
}
callback (di_new, 1); /* Insert */
di = di_new;
di->next = domainbuckets[hash];
domainbuckets[hash] = di;
}
/* Helper for domaininfo_set_no_name. */
static void
set_no_name_cb (domaininfo_t di, int insert_mode)
{
(void)insert_mode;
di->no_name = 1;
/* Obviously the domain is in this case also not supported. */
di->wkd_not_supported = 1;
/* The next should already be 0 but we clear it anyway in the case
* of a temporary DNS failure. */
di->wkd_supported = 0;
}
/* Mark DOMAIN as not existent. */
void
domaininfo_set_no_name (const char *domain)
{
insert_or_update (domain, set_no_name_cb);
}
/* Helper for domaininfo_set_wkd_supported. */
static void
set_wkd_supported_cb (domaininfo_t di, int insert_mode)
{
(void)insert_mode;
di->wkd_supported = 1;
/* The next will already be set unless the domain enabled WKD in the
* meantime. Thus we need to clear it. */
di->wkd_not_supported = 0;
}
/* Mark DOMAIN as supporting WKD. */
void
domaininfo_set_wkd_supported (const char *domain)
{
insert_or_update (domain, set_wkd_supported_cb);
}
/* Helper for domaininfo_set_wkd_not_supported. */
static void
set_wkd_not_supported_cb (domaininfo_t di, int insert_mode)
{
(void)insert_mode;
di->wkd_not_supported = 1;
di->wkd_supported = 0;
}
/* Mark DOMAIN as not supporting WKD queries (e.g. no policy file). */
void
domaininfo_set_wkd_not_supported (const char *domain)
{
insert_or_update (domain, set_wkd_not_supported_cb);
}
/* Helper for domaininfo_set_wkd_not_found. */
static void
set_wkd_not_found_cb (domaininfo_t di, int insert_mode)
{
/* Set the not found flag but there is no need to do this if we
* already know that the domain either does not support WKD or we
* know that it supports WKD. */
if (insert_mode)
di->wkd_not_found = 1;
else if (!di->wkd_not_supported && !di->wkd_supported)
di->wkd_not_found = 1;
/* Better clear this flag in case we had a DNS failure in the
* past. */
di->no_name = 0;
}
/* Update a counter for DOMAIN to keep track of failed WKD queries. */
void
domaininfo_set_wkd_not_found (const char *domain)
{
insert_or_update (domain, set_wkd_not_found_cb);
}

View File

@ -18,6 +18,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0+
*/
#include <config.h>
@ -833,11 +835,13 @@ cmd_wkd_get (assuan_context_t ctx, char *line)
char *mbox = NULL;
char *domainbuf = NULL;
char *domain; /* Points to mbox or domainbuf. */
char *domain_orig;/* Points to mbox. */
char sha1buf[20];
char *uri = NULL;
char *encodedhash = NULL;
int opt_submission_addr;
int opt_policy_flags;
int is_wkd_query; /* True if this is a real WKD query. */
int no_log = 0;
char portstr[20] = { 0 };
@ -846,6 +850,7 @@ cmd_wkd_get (assuan_context_t ctx, char *line)
if (has_option (line, "--quick"))
ctrl->timeout = opt.connect_quick_timeout;
line = skip_options (line);
is_wkd_query = !(opt_policy_flags || opt_submission_addr);
mbox = mailbox_from_userid (line);
if (!mbox || !(domain = strchr (mbox, '@')))
@ -854,6 +859,18 @@ cmd_wkd_get (assuan_context_t ctx, char *line)
goto leave;
}
*domain++ = 0;
domain_orig = domain;
/* First check whether we already know that the domain does not
* support WKD. */
if (is_wkd_query)
{
if (domaininfo_is_wkd_not_supported (domain_orig))
{
err = gpg_error (GPG_ERR_NO_DATA);
goto leave;
}
}
/* Check for SRV records. */
if (1)
@ -962,6 +979,29 @@ cmd_wkd_get (assuan_context_t ctx, char *line)
err = ks_action_fetch (ctrl, uri, outfp);
es_fclose (outfp);
ctrl->server_local->inhibit_data_logging = 0;
/* Register the result under the domain name of MBOX. */
switch (gpg_err_code (err))
{
case 0:
domaininfo_set_wkd_supported (domain_orig);
break;
case GPG_ERR_NO_NAME:
/* There is no such domain. */
domaininfo_set_no_name (domain_orig);
break;
case GPG_ERR_NO_DATA:
if (is_wkd_query) /* Mark that - we will latter do a check. */
domaininfo_set_wkd_not_found (domain_orig);
else if (opt_policy_flags) /* No policy file - no support. */
domaininfo_set_wkd_not_supported (domain_orig);
break;
default:
/* Don't register other errors. */
break;
}
}
}