Copyright (C) 1998-2003 Hallvard B. Furuseth.
Copyright (C) 1992-1996 Regents of the University of Michigan.
Copyright (C) 2000 Dimitrios Souflis
+ Copyright (C) 2008,2009,2010,2012-2016 William Ahern
Authors with a FSF copyright assignment
Stefan Tomanek <tomanek@internet-sicherheit.de>
2014-01-30:20140129234449.GY30808@zirkel.wertarbyte.de:
+Tobias Mueller <muelli@cryptobitch.de>
+2016-11-23:1479937342.11180.3.camel@cryptobitch.de:
+
Werner Koch <wk@gnupg.org>
2013-03-29:87620ahchj.fsf@vigenere.g10code.de:
The gpg-zip documentation is based on the manpage for gpg-zip, written
by Colin Tuckley and Daniel Leidert for the GNU/Debian distribution.
+The DNS resolver code is libdns by William Ahern; see COPYING.other.
+
The test driver is based on TinySCHEME by Dimitrios Souflis and
-available under a permissive license. For the terms see the file
-tests/gpgscm/LICENSE.TinySCHEME.
+available under a permissive license; see COPYING.other.
Copyright
Note that some files are under a combination of the GNU Lesser General
Public License, version 3 and the GNU General Public License, version
2. A few files carry an all permissive license note as found at the
-bottom of this file. Some small files are distributed under the
-Creative Commons Zero (CC0-1.0-Universal) license which basically puts
-them into the public domain.
+bottom of this file. A few files are distributed under permissive
+licenses as listed in the file COPYING.other. Some other small files
+are distributed under the Creative Commons Zero license (see file
+COPYING.CC0) which basically puts them into the public domain.
=========
--- /dev/null
+# COPYING.other -*- org -*-
+#+TITLE: List of code with permissive licenses as used by GnuPG.
+#+STARTUP: showall
+
+* DNS resolver (dirmngr/dns.c)
+
+ dns.c - Recursive, Reentrant DNS Resolver.
+ --------------------------------------------------------------------------
+ Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+* TinySCHEME (tests/gpgscm/LICENSE.TinySCHEME)
+
+ Copyright (c) 2000, Dimitrios Souflis
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of Dimitrios Souflis nor the names of the
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
EXTRA_DIST = build-aux/config.rpath build-aux/potomo autogen.sh autogen.rc \
ChangeLog-2011 po/ChangeLog-2011 build-aux/ChangeLog-2011 \
- VERSION README.GIT build-aux/gitlog-to-changelog COPYING.CC0 \
+ VERSION README.GIT build-aux/gitlog-to-changelog \
+ COPYING.CC0 COPYING.other \
build-aux/git-log-fix build-aux/git-log-footer \
build-aux/getswdb.sh \
build-aux/speedo.mk \
+Noteworthy changes in version 2.1.17 (2016-12-20)
+-------------------------------------------------
+
+ * gpg: By default new keys expire after 2 years.
+
+ * gpg: New command --quick-set-expire to conveniently change the
+ expiration date of keys.
+
+ * gpg: Option and command names have been changed for easier
+ comprehension. The old names are still available as aliases.
+
+ * gpg: Improved the TOFU trust model.
+
+ * gpg: New option --default-new-key-algo.
+
+ * scd: Support OpenPGP card V3 for RSA.
+
+ * dirmngr: Support for the ADNS library has been removed. Instead
+ William Ahern's Libdns is now source included and used on all
+ platforms. This enables Tor support on all platforms. The new
+ option --standard-resolver can be used to disable this code at
+ runtime. In case of build problems the new configure option
+ --disable-libdns can be used to build without Libdns.
+
+ * dirmngr: Lazily launch ldap reaper thread.
+
+ * tools: New options --check and --status-fd for gpg-wks-client.
+
+ * The UTF-8 byte order mark is now skipped when reading conf files.
+
+ * Fixed many bugs and regressions.
+
+ * Major improvements to the test suite. For example it is possible
+ to run the external test suite of GPGME.
+
+
Noteworthy changes in version 2.1.16 (2016-11-18)
-------------------------------------------------
We are currently maintaining three branches of GnuPG:
- - 2.1 (i.e. this release) is the latest development with a lot of
+ - 2.1 (i.e. this release) is the latest stable version with a lot of
new features.
- - 2.0 is the current stable version for general use.
+ - 2.0 is an often used stable version. This branch will reach
+ end-of-life on 2017-12-31.
- 1.4 is the old standalone version which is most suitable for older
or embedded platforms.
Add other options as needed.
+*** Systems without a full C99 compiler
+
+ If you run into problems with your compiler complaining about dns.c
+ you may use
+
+ ./configure --disable-libdns
+
+ Add other options as needed.
+
+
* MIGRATION from 1.4 or 2.0 to 2.1
The major change in 2.1 is gpg-agent taking care of the OpenPGP
/* Bit values for the --debug option. */
-#define DBG_COMMAND_VALUE 1 /* debug commands i/o */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_IPC_VALUE 1024 /* Enable Assuan debugging. */
/* Test macros for the debug option. */
-#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
- { DBG_COMMAND_VALUE, "command" },
{ DBG_MPI_VALUE , "mpi" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_MEMORY_VALUE , "memory" },
else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_IPC_VALUE;
else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
- opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
+ opt.debug = DBG_IPC_VALUE;
else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
- opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
- |DBG_CACHE_VALUE);
+ opt.debug = (DBG_IPC_VALUE | DBG_CACHE_VALUE);
else if (!strcmp (debug_level, "guru") || numok)
{
opt.debug = ~0;
}
/* Initialize the secure memory. */
- gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+ gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0);
maybe_setuid = 0;
/*
pid_t pid;
#endif
- initialize_modules ();
-
/* Remove the DISPLAY variable so that a pinentry does not
default to a specific display. There is still a default
display when gpg-agent was started using --display or a
parent_pid = getpid ();
fflush (NULL);
+
#ifdef HAVE_W32_SYSTEM
+
(void)csh_style;
(void)nodetach;
+ initialize_modules ();
+
#else /*!HAVE_W32_SYSTEM*/
+
pid = fork ();
if (pid == (pid_t)-1)
{
break; /* ready */
/* Do not accept new connections but keep on running the
- loop to cope with the timer events. */
+ * loop to cope with the timer events.
+ *
+ * Note that we do not close the listening socket because a
+ * client trying to connect to that socket would instead
+ * restart a new dirmngr instance - which is unlikely the
+ * intention of a shutdown. */
FD_ZERO (&fdset);
+ nfd = -1;
+ if (my_inotify_fd != -1)
+ {
+ FD_SET (my_inotify_fd, &fdset);
+ nfd = my_inotify_fd;
+ }
}
/* POSIX says that fd_set should be implemented as a structure,
--with-zlib=@SYSROOT@
--with-regex=@SYSROOT@
--with-npth-prefix=@SYSROOT@
- --with-adns=@SYSROOT@
--disable-g13
"
;;
--- /dev/null
+#!/bin/sh
+# Append a signature to an existing detached signature.
+# Copyright (C) 2016 g10 Code GmbH
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+set -e
+PGM="$(basename $0)"
+GPGV=gpgv
+
+# Prints usage information.
+usage()
+{
+ cat <<EOF
+Usage: $PGM TARBALL NEWSIGNATURE
+Append a signature to an existing detached signature.
+Options:
+ --verbose Print some extra information.
+ --help Print this help.
+EOF
+ exit $1
+}
+
+#
+# Parse options
+#
+verbose=""
+while [ $# -gt 0 ]; do
+ case "$1" in
+ # Set up `optarg'.
+ --*=*)
+ optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'`
+ ;;
+ *)
+ optarg=""
+ ;;
+ esac
+
+ case $1 in
+ --help|-h)
+ usage 0
+ ;;
+ --verbose|-v)
+ verbose="-v"
+ ;;
+ --)
+ break
+ ;;
+ -*)
+ usage 1 1>&2
+ ;;
+ *)
+ break;
+ ;;
+ esac
+ shift
+done
+
+if [ $# -ne 2 ]; then
+ usage 1 1>&2
+fi
+tarball="$1"
+tarballsig="$1".sig
+newsig="$2"
+
+[ -n "$verbose" ] && echo "tarball: $tarball"
+[ -n "$verbose" ] && echo "sig ...: $tarballsig"
+[ -n "$verbose" ] && echo "newsig : $newsig"
+
+if ! $GPGV --version >/dev/null 2>/dev/null ; then
+ echo "${PGM}: Command \"gpgv\" is not installed" >&2
+ exit 1
+fi
+
+distsigkey="/usr/local/share/gnupg/distsigkey.gpg"
+if [ ! -f "$distsigkey" ]; then
+ distsigkey="/usr/share/gnupg/distsigkey.gpg"
+fi
+if [ ! -f "$distsigkey" ]; then
+ echo "${PGM}: File \"$distsigkey\" is not installed" >&2
+ exit 1
+fi
+
+if ! $GPGV $verbose --keyring "$distsigkey" \
+ -- "$tarballsig" "$tarball" 2>/dev/null ; then
+ echo "${PGM}: Existing signature '$tarballsig' does not verify" >&2
+ exit 1
+fi
+
+if ! $GPGV $verbose --keyring "$distsigkey" \
+ -- "$newsig" "$tarball" 2>/dev/null; then
+ echo "${PGM}: New signature '$newsig' does not verify" >&2
+ exit 1
+fi
+
+cat "$newsig" >> "$tarballsig"
+
+if ! $GPGV $verbose --keyring "$distsigkey" \
+ -- "$tarballsig" "$tarball"; then
+ echo "${PGM}: Update signature '$tarballsig' does not verify" >&2
+ exit 1
+fi
$prev_date_line = $date_line;
@prev_coauthors = @coauthors;
- # Omit "Co-authored-by..." and "Signed-off-by..." lines.
- @line = grep !/^Signed-off-by: .*>$/, @line;
- @line = grep !/^Co-authored-by: /, @line;
+ # Omit keyword lines like "Signed-off-by:" or "GnuPG-bug-id:"
+ @line = grep !/^[A-Z][A-Za-z]+-[a-z-]+: /, @line;
# Remove everything after a line with 2 dashes at the beginning.
if ($tear_off)
help:
@echo 'usage: make -f speedo.mk TARGET'
@echo ' with TARGET being one of:'
- @echo ' help This help'
- @echo ' native Native build of the GnuPG core'
- @echo ' native-gui Ditto but with pinentry and GPA'
- @echo ' w32-installer Build a Windows installer'
- @echo ' w32-source Pack a source archive'
- @echo ' w32-release Build a Windows release'
+ @echo ' help This help'
+ @echo ' native Native build of the GnuPG core'
+ @echo ' native-gui Ditto but with pinentry and GPA'
+ @echo ' w32-installer Build a Windows installer'
+ @echo ' w32-source Pack a source archive'
+ @echo ' w32-release Build a Windows release'
+ @echo ' w32-sign-installer Sign the installer'
@echo
@echo 'You may append INSTALL_PREFIX=<dir> for native builds.'
@echo 'Prepend TARGET with "git-" to build from GIT repos.'
$(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 SELFCHECK=0 \
installer-from-source
+w32-sign-installer: check-tools
+ $(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 SELFCHECK=0 \
+ sign-installer
+
w32-release-offline: check-tools
$(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 SELFCHECK=0 \
CUSTOM_SWDB=1 pkgrep=${HOME}/b pkg10rep=${HOME}/b \
# Use this to override the installaion directory for native builds.
INSTALL_PREFIX=none
+# The Authenticode key used to sign the Windows installer
+AUTHENTICODE_KEY=${HOME}/.gnupg/g10code-authenticode-key.p12
+
# Directory names.
# They must be absolute, as we switch directories pretty often.
$(extra_installer_options) $(w32src)/inst.nsi
@echo "Ready: $(idir)/$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).exe"
+
+define MKSWDB_commands
+ ( pref="#+macro: gnupg21_w32_" ;\
+ echo "$${pref}ver $(INST_VERSION)_$(BUILD_DATESTR)" ;\
+ echo "$${pref}date $(2)" ;\
+ echo "$${pref}size $$(wc -c <$(1)|awk '{print int($$1/1024)}')k";\
+ echo "$${pref}sha1 $$(sha1sum <$(1)|cut -d' ' -f1)" ;\
+ echo "$${pref}sha2 $$(sha256sum <$(1)|cut -d' ' -f1)" ;\
+ ) | tee $(1).swdb
+endef
+
+
# Build the installer from the source tarball.
installer-from-source: dist-source
(set -e;\
$(MAKE) -f build-aux/speedo.mk this-w32-installer SELFCHECK=0;\
reldate="$$(date -u +%Y-%m-%d)" ;\
exefile="$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).exe" ;\
- mv "PLAY/inst/$$exefile" ../.. ;\
+ cp "PLAY/inst/$$exefile" ../.. ;\
+ exefile="../../$$exefile" ;\
+ $(call MKSWDB_commands,$${exefile},$${reldate}); \
+ )
+
+# This target repeats some of the installer-from-source steps but it
+# is intended to be called interactively, so that the passphrase can be
+# entered.
+sign-installer:
+ @(set -e; \
+ cd PLAY-release; \
+ cd $(INST_NAME)-$(INST_VERSION); \
+ reldate="$$(date -u +%Y-%m-%d)" ;\
+ exefile="$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).exe" ;\
+ echo "speedo: /*" ;\
+ echo "speedo: * Signing installer" ;\
+ echo "speedo: * Key: $(AUTHENTICODE_KEY)";\
+ echo "speedo: */" ;\
+ osslsigncode sign -pkcs12 $(AUTHENTICODE_KEY) -askpass \
+ -h sha256 -in "PLAY/inst/$$exefile" -out "../../$$exefile" ;\
exefile="../../$$exefile" ;\
- ( pref="#+macro: gnupg21_w32_" ;\
- echo "$${pref}ver $(INST_VERSION)_$(BUILD_DATESTR)" ;\
- echo "$${pref}date $${reldate}" ;\
- echo "$${pref}size $$(wc -c <$$exefile|awk '{print int($$1/1024)}')k";\
- echo "$${pref}sha1 $$(sha1sum <$$exefile|cut -d' ' -f1)" ;\
- echo "$${pref}sha2 $$(sha256sum <$$exefile|cut -d' ' -f1)" ;\
- ) | tee $$exefile.swdb ;\
+ $(call MKSWDB_commands,$${exefile},$${reldate}); \
+ echo "speedo: /*" ;\
+ echo "speedo: * Verification result" ;\
+ echo "speedo: */" ;\
+ osslsigncode verify $${exefile} \
)
+
+
endif
# }}} W32
# If we are reinstalling, try to kill a possible running gpa using
# an already installed gpa.
ifFileExists "$INSTDIR\bin\launch-gpa.exe" 0 no_uiserver
- ExecWait '"$INSTDIR\bin\launch-gpa" --stop-server'
+ nsExec::ExecToLog '"$INSTDIR\bin\launch-gpa" "--stop-server"'
no_uiserver:
# If we are reinstalling, try to kill a possible running agent using
# an already installed gpgconf.
+
ifFileExists "$INSTDIR\bin\gpgconf.exe" 0 no_gpgconf
- ExecWait '"$INSTDIR\bin\gpgconf" --kill dirmngr'
- ExecWait '"$INSTDIR\bin\gpgconf" --kill gpg-agent'
+ nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "dirmngr"'
+ nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "gpg-agent"'
no_gpgconf:
# RegDLL can't be used for 64 bit and InstallLib seems to be a
# registry hack.
ClearErrors
- ExecWait '"$SYSDIR\regsvr32" /s "$INSTDIR\bin\gpgex6.dll"'
+ nsExec::ExecToLog '"$SYSDIR\regsvr32" "/s" "$INSTDIR\bin\gpgex6.dll"'
ifErrors 0 +2
MessageBox MB_OK "$(T_GPGEX_RegFailed) (64 bit)"
Section "-un.gnupglast"
ifFileExists "$INSTDIR\bin\launch-gpa.exe" 0 no_uiserver
- ExecWait '"$INSTDIR\bin\launch-gpa" --stop-server'
+ nsExec::ExecToLog '"$INSTDIR\bin\launch-gpa" "--stop-server"'
no_uiserver:
ifFileExists "$INSTDIR\bin\gpgconf.exe" 0 no_gpgconf
- ExecWait '"$INSTDIR\bin\gpgconf" --kill gpg-agent'
- ExecWait '"$INSTDIR\bin\gpgconf" --kill dirmngr'
+ nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "gpg-agent"'
+ nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "dirmngr"'
no_gpgconf:
SectionEnd
Delete /REBOOTOK "$INSTDIR\bin\gpgex.dll"
${If} ${RunningX64}
- ExecWait '"$SYSDIR\regsvr32" /u /s "$INSTDIR\bin\gpgex6.dll"'
+ nsExec::ExecToLog '"$SYSDIR\regsvr32" "/u" "/s" "$INSTDIR\bin\gpgex6.dll"'
Delete /REBOOTOK "$INSTDIR\bin\gpgex6.dll"
${EndIf}
SectionEnd
Copyright (C) 1998-2003 Hallvard B. Furuseth.
Copyright (C) 1992-1996 Regents of the University of Michigan.
Copyright (C) 2000 Dimitrios Souflis
+ Copyright (C) 2008,2009,2010,2012-2016 William Ahern
GnuPG is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+libdns is part of the GnuPG package and is
+
+ Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
SQLite has
been put into the public-domain by its author D. Richard Hipp:
char *buffer = NULL;
size_t buflen = 0;
int in_alias=0;
+ int unread_buf[3]; /* We use an int so that we can store EOF. */
+ int unread_buf_count = 0;
if (!fp) /* Divert to to arg_parse() in this case. */
return arg_parse (arg, opts);
initialize (arg, filename, lineno);
+ /* If the LINENO is zero we assume that we are at the start of a
+ * file and we skip over a possible Byte Order Mark. */
+ if (!*lineno)
+ {
+ unread_buf[0] = getc (fp);
+ unread_buf[1] = getc (fp);
+ unread_buf[2] = getc (fp);
+ if (unread_buf[0] != 0xef
+ || unread_buf[1] != 0xbb
+ || unread_buf[2] != 0xbf)
+ unread_buf_count = 3;
+ }
+
/* Find the next keyword. */
state = i = 0;
for (;;)
{
- c = getc (fp);
+ if (unread_buf_count)
+ c = unread_buf[3 - unread_buf_count--];
+ else
+ c = getc (fp);
if (c == '\n' || c== EOF )
{
if ( c != EOF )
}
+/* If set, 'gnupg_module_name' returns modules from that build
+ * directory. */
+static char *gnupg_build_directory;
+
+/* For sanity checks. */
+static int gnupg_module_name_called;
+
+
+/* Set NEWDIR as the new build directory. This will make
+ * 'gnupg_module_name' return modules from that build directory. Must
+ * be called before any invocation of 'gnupg_module_name', and must
+ * not be called twice. It can be used by test suites to make sure
+ * the components from the build directory are used instead of
+ * potentially outdated installed ones. */
+void
+gnupg_set_builddir (const char *newdir)
+{
+ log_assert (! gnupg_module_name_called);
+ log_assert (! gnupg_build_directory);
+ gnupg_build_directory = xtrystrdup (newdir);
+}
+
+
+/* If no build directory has been configured, try to set it from the
+ * environment. We only do this in development builds to avoid
+ * increasing the set of influential environment variables and hence
+ * the attack surface of production builds. */
+static void
+gnupg_set_builddir_from_env (void)
+{
+#ifdef IS_DEVELOPMENT_VERSION
+ if (gnupg_build_directory)
+ return;
+
+ gnupg_build_directory = getenv ("GNUPG_BUILDDIR");
+#endif
+}
+
+
/* Return the file name of a helper tool. WHICH is one of the
GNUPG_MODULE_NAME_foo constants. */
const char *
gnupg_module_name (int which)
{
-#define X(a,b) do { \
+ gnupg_set_builddir_from_env ();
+ gnupg_module_name_called = 1;
+
+#define X(a,b,c) do { \
static char *name; \
if (!name) \
- name = xstrconcat (gnupg_ ## a (), DIRSEP_S b EXEEXT_S, NULL); \
+ name = gnupg_build_directory \
+ ? xstrconcat (gnupg_build_directory, \
+ DIRSEP_S b DIRSEP_S c EXEEXT_S, NULL) \
+ : xstrconcat (gnupg_ ## a (), DIRSEP_S c EXEEXT_S, NULL); \
return name; \
} while (0)
#ifdef GNUPG_DEFAULT_AGENT
return GNUPG_DEFAULT_AGENT;
#else
- X(bindir, "gpg-agent");
+ X(bindir, "agent", "gpg-agent");
#endif
case GNUPG_MODULE_NAME_PINENTRY:
#ifdef GNUPG_DEFAULT_SCDAEMON
return GNUPG_DEFAULT_SCDAEMON;
#else
- X(libexecdir, "scdaemon");
+ X(libexecdir, "scd", "scdaemon");
#endif
case GNUPG_MODULE_NAME_DIRMNGR:
#ifdef GNUPG_DEFAULT_DIRMNGR
return GNUPG_DEFAULT_DIRMNGR;
#else
- X(bindir, DIRMNGR_NAME);
+ X(bindir, "dirmngr", DIRMNGR_NAME);
#endif
case GNUPG_MODULE_NAME_PROTECT_TOOL:
#ifdef GNUPG_DEFAULT_PROTECT_TOOL
return GNUPG_DEFAULT_PROTECT_TOOL;
#else
- X(libexecdir, "gpg-protect-tool");
+ X(libexecdir, "agent", "gpg-protect-tool");
#endif
case GNUPG_MODULE_NAME_DIRMNGR_LDAP:
#ifdef GNUPG_DEFAULT_DIRMNGR_LDAP
return GNUPG_DEFAULT_DIRMNGR_LDAP;
#else
- X(libexecdir, "dirmngr_ldap");
+ X(libexecdir, "dirmngr", "dirmngr_ldap");
#endif
case GNUPG_MODULE_NAME_CHECK_PATTERN:
- X(libexecdir, "gpg-check-pattern");
+ X(libexecdir, "tools", "gpg-check-pattern");
case GNUPG_MODULE_NAME_GPGSM:
- X(bindir, "gpgsm");
+ X(bindir, "sm", "gpgsm");
case GNUPG_MODULE_NAME_GPG:
#if USE_GPG2_HACK
- X(bindir, GPG_NAME "2");
-#else
- X(bindir, GPG_NAME);
+ if (! gnupg_build_directory)
+ X(bindir, "g10", GPG_NAME "2");
+ else
#endif
+ X(bindir, "g10", GPG_NAME);
case GNUPG_MODULE_NAME_GPGV:
#if USE_GPG2_HACK
- X(bindir, GPG_NAME "v2");
-#else
- X(bindir, GPG_NAME "v");
+ if (! gnupg_build_directory)
+ X(bindir, "g10", GPG_NAME "v2");
+ else
#endif
+ X(bindir, "g10", GPG_NAME "v");
case GNUPG_MODULE_NAME_CONNECT_AGENT:
- X(bindir, "gpg-connect-agent");
+ X(bindir, "tools", "gpg-connect-agent");
case GNUPG_MODULE_NAME_GPGCONF:
- X(bindir, "gpgconf");
+ X(bindir, "tools", "gpgconf");
default:
BUG ();
block_filter_ctx_t;
-/* Global flag to tell whether special file names are enabled. See
- gpg.c for an explanation of these file names. FIXME: This does not
- belong in the iobuf subsystem. */
-static int special_names_enabled;
-
/* Local prototypes. */
static int underflow (iobuf_t a, int clear_pending_eof);
static int underflow_target (iobuf_t a, int clear_pending_eof, size_t target);
return a;
}
-void
-iobuf_enable_special_filenames (int yes)
-{
- special_names_enabled = yes;
-}
-
-
-/* See whether the filename has the form "-&nnnn", where n is a
- non-zero number. Returns this number or -1 if it is not the
- case. */
-static int
-check_special_filename (const char *fname)
-{
- if (special_names_enabled && fname && *fname == '-' && fname[1] == '&')
- {
- int i;
-
- fname += 2;
- for (i = 0; digitp (fname+i); i++)
- ;
- if (!fname[i])
- return atoi (fname);
- }
- return -1;
-}
-
int
iobuf_is_pipe_filename (const char *fname)
{
if (!fname || (*fname=='-' && !fname[1]) )
return 1;
- return check_special_filename (fname) != -1;
+ return check_special_filename (fname, 0, 1) != -1;
}
+
static iobuf_t
do_open (const char *fname, int special_filenames,
int use, const char *opentype, int mode700)
}
else if (!fname)
return NULL;
- else if (special_filenames && (fd = check_special_filename (fname)) != -1)
+ else if (special_filenames
+ && (fd = check_special_filename (fname, 0, 1)) != -1)
return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1),
opentype);
else
#endif
EXTERN_UNLESS_MAIN_MODULE int iobuf_debug_mode;
-/* Whether iobuf_open, iobuf_create and iobuf_is_pipefilename
- recognize special filenames. Special filenames are of the form
- "-&nnnn" where n is a positive integer. The integer corresponds to
- a file descriptor. Note: these functions always recognize the
- special filename '-', which corresponds to standard input. */
-void iobuf_enable_special_filenames (int yes);
/* Returns whether the specified filename corresponds to a pipe. In
particular, this function checks if FNAME is "-" and, if special
- filenames are enabled (see iobuf_enable_special_filenames), whether
+ filenames are enabled (see check_special_filename), whether
FNAME is a special filename. */
int iobuf_is_pipe_filename (const char *fname);
* supplied variable is updated by the parsed flags.
*
* If STRING is NULL the enabled debug flags are printed.
+ *
+ * See doc/DETAILS for a summary of used debug options.
*/
int
parse_debug_flag (const char *string, unsigned int *debugvar,
}
-/* Return the Libgcrypt name for for the gpg curve NAME if supported.
- * If R_ALGO is not NULL the required OpenPGP public key algo or 0 is
- * stored at that address. NULL is returned if the curev is not
- * supported. */
+/* Return the Libgcrypt name for the gpg curve NAME if supported. If
+ * R_ALGO is not NULL the required OpenPGP public key algo or 0 is
+ * stored at that address. If R_NBITS is not NULL the nominal bitsize
+ * of the curves is stored there. NULL is returned if the curve is
+ * not supported. */
const char *
-openpgp_is_curve_supported (const char *name, int *r_algo)
+openpgp_is_curve_supported (const char *name, int *r_algo,
+ unsigned int *r_nbits)
{
int idx;
if (r_algo)
*r_algo = 0;
+ if (r_nbits)
+ *r_nbits = 0;
for (idx = 0; idx < DIM (oidtable) && oidtable[idx].name; idx++)
{
if ((!strcmp (name, oidtable[idx].name)
{
if (r_algo)
*r_algo = oidtable[idx].pubkey_algo;
+ if (r_nbits)
+ *r_nbits = oidtable[idx].nbits;
return oidtable[idx].name;
}
}
/* sysutils.c - system helpers
* Copyright (C) 1991-2001, 2003-2004,
* 2006-2008 Free Software Foundation, Inc.
- * Copyright (C) 2013-2014 Werner Koch
+ * Copyright (C) 2013-2016 Werner Koch
*
* This file is part of GnuPG.
*
#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
+/* Flag to tell whether special file names are enabled. See gpg.c for
+ * an explanation of these file names. */
+static int allow_special_filenames;
+
static GPGRT_INLINE gpg_error_t
my_error_from_syserror (void)
}
+/* Allow the use of special "-&nnn" style file names. */
+void
+enable_special_filenames (void)
+{
+ allow_special_filenames = 1;
+}
+
/* Return a string which is used as a kind of process ID. */
const byte *
}
+/* Check whether FNAME has the form "-&nnnn", where N is a non-zero
+ * number. Returns this number or -1 if it is not the case. If the
+ * caller wants to use the file descriptor for writing FOR_WRITE shall
+ * be set to 1. If NOTRANSLATE is set the Windows spefic mapping is
+ * not done. */
+int
+check_special_filename (const char *fname, int for_write, int notranslate)
+{
+ if (allow_special_filenames
+ && fname && *fname == '-' && fname[1] == '&')
+ {
+ int i;
+
+ fname += 2;
+ for (i=0; digitp (fname+i); i++ )
+ ;
+ if (!fname[i])
+ return notranslate? atoi (fname)
+ /**/ : translate_sys2libc_fd_int (atoi (fname), for_write);
+ }
+ return -1;
+}
+
/* Replacement for tmpfile(). This is required because the tmpfile
function of Windows' runtime library is broken, insecure, ignores
void trap_unaligned (void);
int disable_core_dumps (void);
int enable_core_dumps (void);
+void enable_special_filenames (void);
const unsigned char *get_session_marker (size_t *rlen);
unsigned int get_uint_nonce (void);
/*int check_permissions (const char *path,int extension,int checkonly);*/
void gnupg_usleep (unsigned int usecs);
int translate_sys2libc_fd (gnupg_fd_t fd, int for_write);
int translate_sys2libc_fd_int (int fd, int for_write);
+int check_special_filename (const char *fname, int for_write, int notranslate);
FILE *gnupg_tmpfile (void);
void gnupg_reopen_std (const char *pgmname);
void gnupg_allow_set_foregound_window (pid_t pid);
* libgpg-error version. Define them here.
* Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21)
*/
+#if GPG_ERROR_VERSION_NUMBER < 0x011a00 /* 1.26 */
+# define GPG_ERR_UNKNOWN_FLAG 309
+# define GPG_ERR_INV_ORDER 310
+# define GPG_ERR_ALREADY_FETCHED 311
+# define GPG_ERR_TRY_LATER 312
+# define GPG_ERR_SYSTEM_BUG 666
+# define GPG_ERR_DNS_UNKNOWN 711
+# define GPG_ERR_DNS_SECTION 712
+# define GPG_ERR_DNS_ADDRESS 713
+# define GPG_ERR_DNS_NO_QUERY 714
+# define GPG_ERR_DNS_NO_ANSWER 715
+# define GPG_ERR_DNS_CLOSED 716
+# define GPG_ERR_DNS_VERIFY 717
+# define GPG_ERR_DNS_TIMEOUT 718
+#endif
/* Hash function used with libksba. */
const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits);
const char *openpgp_oid_to_curve (const char *oid, int canon);
const char *openpgp_enum_curves (int *idxp);
-const char *openpgp_is_curve_supported (const char *name, int *r_algo);
+const char *openpgp_is_curve_supported (const char *name,
+ int *r_algo, unsigned int *r_nbits);
/*-- homedir.c --*/
#define GNUPG_MODULE_NAME_GPGV 12
const char *gnupg_module_name (int which);
void gnupg_module_name_flush_some (void);
+void gnupg_set_builddir (const char *newdir);
logging subsystem. */
void setup_libgcrypt_logging (void);
-/* Print an out of core emssage and die. */
+/* Print an out of core message and die. */
void xoutofcore (void);
/* Same as estream_asprintf but die on memory failure. */
m4_define([mym4_package],[gnupg])
m4_define([mym4_major], [2])
m4_define([mym4_minor], [1])
-m4_define([mym4_micro], [16])
+m4_define([mym4_micro], [17])
# To start a new development series, i.e a new major or minor number
# you need to mark an arbitrary commit before the first beta release
have_sqlite=no
have_npth=no
have_libusb=no
-have_adns=no
+have_system_resolver=no
gnupg_have_ldap="n/a"
use_zip=yes
use_exec=yes
use_trust_models=yes
use_tofu=yes
+use_libdns=yes
card_support=yes
use_ccid_driver=auto
dirmngr_auto_start=yes
AC_MSG_ERROR([both --disable-trust-models and --enable-tofu given])
fi
+AC_MSG_CHECKING([whether to enable libdns])
+AC_ARG_ENABLE(libdns,
+ AC_HELP_STRING([--disable-libdns],
+ [do not build with libdns support]),
+ use_libdns=$enableval, use_libdns=yes)
+AC_MSG_RESULT($use_libdns)
+if test x"$use_libdns" = xyes ; then
+ AC_DEFINE(USE_LIBDNS, 1, [Build with integrated libdns support])
+fi
+AM_CONDITIONAL(USE_LIBDNS, test "$use_libdns" = yes)
#
#
-# Check for ADNS.
-#
-_cppflags="${CPPFLAGS}"
-_ldflags="${LDFLAGS}"
-AC_ARG_WITH(adns,
- AC_HELP_STRING([--with-adns=DIR],
- [look for the adns library in DIR]),
- [if test -d "$withval"; then
- CPPFLAGS="${CPPFLAGS} -I$withval/include"
- LDFLAGS="${LDFLAGS} -L$withval/lib"
- fi])
-if test "$with_adns" != "no"; then
- AC_CHECK_HEADERS(adns.h,AC_CHECK_LIB(adns, adns_init_strcfg,[have_adns=yes]))
- AC_CHECK_FUNCS(adns_free)
- AC_MSG_CHECKING([if adns supports adns_if_tormode])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
- #include <adns.h>
- adns_initflags flags = adns_if_tormode;
- ]],[])],[adns_if_tormode=yes],[adns_if_tormode=no])
- AC_MSG_RESULT($adns_if_tormode)
- if test x"$adns_if_tormode" = xyes; then
- AC_DEFINE(HAVE_ADNS_IF_TORMODE,1,[define if adns_if_tormode is available])
- if test "$show_tor_support" != "no"; then
- show_tor_support=yes
- fi
- fi
-fi
-CPPFLAGS=${_cppflags}
-LDFLAGS=${_ldflags}
-if test "$have_adns" = "yes"; then
- ADNSLIBS="-ladns"
-fi
-
-
+# Check standard resolver functions.
#
-# Now try for the resolver functions so we can use DNS for SRV, PA and CERT.
-#
-AC_ARG_ENABLE(dns-srv,
- AC_HELP_STRING([--disable-dns-srv],
- [disable the use of DNS SRV in HKP and HTTP]),
- use_dns_srv=$enableval,use_dns_srv=yes)
-
-AC_ARG_ENABLE(dns-cert,
- AC_HELP_STRING([--disable-dns-cert],
- [disable the use of CERT records in DNS]),
- use_dns_cert=$enableval,use_dns_cert=yes)
-
-if test x"$use_dns_srv" = xyes || test x"$use_dns_cert" = xyes; then
+if test "$build_dirmngr" = "yes"; then
_dns_save_libs=$LIBS
LIBS=""
- if test x"$have_adns" = xyes ; then
- # We prefer ADNS.
- DNSLIBS="$ADNSLIBS"
- AC_DEFINE(USE_ADNS,1,[Use ADNS as resolver library.])
-
- if test x"$use_dns_srv" = xyes ; then
- AC_DEFINE(USE_DNS_SRV,1)
- fi
+ # Find the system resolver which can always be enabled with
+ # the dirmngr option --standard-resolver.
- if test x"$use_dns_cert" = xyes ; then
- AC_DEFINE(USE_DNS_CERT,1,[define to use DNS CERT])
- fi
- else
- # With no ADNS find the system resolver.
+ # the double underscore thing is a glibc-ism?
+ AC_SEARCH_LIBS(res_query,resolv bind,,
+ AC_SEARCH_LIBS(__res_query,resolv bind,,have_resolver=no))
+ AC_SEARCH_LIBS(dn_expand,resolv bind,,
+ AC_SEARCH_LIBS(__dn_expand,resolv bind,,have_resolver=no))
- # the double underscore thing is a glibc-ism?
- AC_SEARCH_LIBS(res_query,resolv bind,,
- AC_SEARCH_LIBS(__res_query,resolv bind,,have_resolver=no))
- AC_SEARCH_LIBS(dn_expand,resolv bind,,
- AC_SEARCH_LIBS(__dn_expand,resolv bind,,have_resolver=no))
+ # macOS renames dn_skipname into res_9_dn_skipname in <resolv.h>,
+ # and for some reason fools us into believing we don't need
+ # -lresolv even if we do. Since the test program checking for the
+ # symbol does not include <resolv.h>, we need to check for the
+ # renamed symbol explicitly.
+ AC_SEARCH_LIBS(res_9_dn_skipname,resolv bind,,
+ AC_SEARCH_LIBS(dn_skipname,resolv bind,,
+ AC_SEARCH_LIBS(__dn_skipname,resolv bind,,have_resolver=no)))
- # macOS renames dn_skipname into res_9_dn_skipname in <resolv.h>,
- # and for some reason fools us into believing we don't need
- # -lresolv even if we do. Since the test program checking for the
- # symbol does not include <resolv.h>, we need to check for the
- # renamed symbol explicitly.
- AC_SEARCH_LIBS(res_9_dn_skipname,resolv bind,,
- AC_SEARCH_LIBS(dn_skipname,resolv bind,,
- AC_SEARCH_LIBS(__dn_skipname,resolv bind,,have_resolver=no)))
-
- if test x"$have_resolver" != xno ; then
+ if test x"$have_resolver" != xno ; then
# Make sure that the BIND 4 resolver interface is workable before
# enabling any code that calls it. At some point I'll rewrite the
# code to use the BIND 8 resolver API.
- # We might also want to use adns instead. Problem with ADNS is that
- # it does not support v6.
+ # We might also want to use libdns instead.
- AC_MSG_CHECKING([whether the resolver is usable])
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
+ AC_MSG_CHECKING([whether the resolver is usable])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>]],
dn_skipname(0,0);
dn_expand(0,0,0,0,0);
]])],have_resolver=yes,have_resolver=no)
- AC_MSG_RESULT($have_resolver)
+ AC_MSG_RESULT($have_resolver)
- # This is Apple-specific and somewhat bizarre as they changed the
- # define in bind 8 for some reason.
+ # This is Apple-specific and somewhat bizarre as they changed the
+ # define in bind 8 for some reason.
- if test x"$have_resolver" != xyes ; then
- AC_MSG_CHECKING(
- [whether I can make the resolver usable with BIND_8_COMPAT])
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define BIND_8_COMPAT
+ if test x"$have_resolver" != xyes ; then
+ AC_MSG_CHECKING(
+ [whether I can make the resolver usable with BIND_8_COMPAT])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define BIND_8_COMPAT
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
res_query("foo.bar",C_IN,T_A,answer,PACKETSZ);
dn_skipname(0,0); dn_expand(0,0,0,0,0);
]])],[have_resolver=yes ; need_compat=yes])
- AC_MSG_RESULT($have_resolver)
- fi
+ AC_MSG_RESULT($have_resolver)
fi
+ fi
- if test x"$have_resolver" = xyes ; then
- DNSLIBS=$LIBS
-
- if test x"$use_dns_srv" = xyes ; then
- AC_DEFINE(USE_DNS_SRV,1,[define to use DNS SRV])
- fi
-
- if test x"$use_dns_cert" = xyes ; then
- AC_DEFINE(USE_DNS_CERT,1,[define to use DNS CERT])
- fi
+ if test x"$have_resolver" = xyes ; then
+ AC_DEFINE(HAVE_SYSTEM_RESOLVER,1,[The system's resolver is usable.])
+ DNSLIBS="$DNSLIBS $LIBS"
+ if test x"$need_compat" = xyes ; then
+ AC_DEFINE(BIND_8_COMPAT,1,[an Apple OSXism])
+ fi
+ if test "$use_libdns" = yes; then
+ show_tor_support=yes
+ fi
+ elif test "$use_libdns" = yes; then
+ show_tor_support=yes
+ else
+ AC_MSG_WARN([[
+***
+*** The system's DNS resolver is not usable.
+*** Dirmngr functionality is limited.
+***]])
+ show_tor_support="${show_tor_support} (no system resolver)"
+ fi
- if test x"$need_compat" = xyes ; then
- AC_DEFINE(BIND_8_COMPAT,1,[an Apple OSXism])
- fi
- else
- use_dns_srv=no
- use_dns_cert=no
+ if test "$have_w32_system" = yes; then
+ if test "$use_libdns" = yes; then
+ DNSLIBS="$DNSLIBS -liphlpapi"
fi
fi
AC_SUBST(DNSLIBS)
-AM_CONDITIONAL(USE_DNS_SRV, test x"$use_dns_srv" = xyes)
-
#
# Check for LDAP
#
# Setup gcc specific options
#
+USE_C99_CFLAGS=
AC_MSG_NOTICE([checking for cc features])
if test "$GCC" = yes; then
mycflags=
fi
CFLAGS="$mycflags $mycflags_save"
+ if test "$use_libdns" = yes; then
+ # dirmngr/dns.{c,h} require C99 and GNU extensions. */
+ USE_C99_CFLAGS="-std=gnu99"
+ fi
fi
+AC_SUBST(USE_C99_CFLAGS)
+
#
# This is handy for debugging so the compiler doesn't rearrange
tests/gpgscm/Makefile
tests/openpgp/Makefile
tests/migrations/Makefile
+tests/gpgme/Makefile
tests/pkits/Makefile
g10/gpg.w32-manifest
])
Dirmngr auto start: $dirmngr_auto_start
Readline support: $gnupg_cv_have_readline
LDAP support: $gnupg_have_ldap
- DNS SRV support: $use_dns_srv
TLS support: $use_tls_library
TOFU support: $use_tofu
Tor support: $show_tor_support
include $(top_srcdir)/am/cmacros.am
-AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) \
+AM_CFLAGS = $(USE_C99_CFLAGS) \
+ $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) \
$(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS) $(NTBTLS_CFLAGS) \
$(LIBGNUTLS_CFLAGS)
ks-action.c ks-action.h ks-engine.h \
ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c ks-engine-kdns.c
+if USE_LIBDNS
+dirmngr_SOURCES += dns.c dns.h
+endif
+
if USE_LDAP
dirmngr_SOURCES += ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \
ldap-wrapper.h ldap-parse-uri.c ldap-parse-uri.h \
t_common_src = t-support.h
+if USE_LIBDNS
+t_common_src += dns.c dns.h
+endif
t_common_ldadd = $(libcommon) $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \
$(GPG_ERROR_LIBS) $(NETLIBS) \
$(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) \
# http tests
-t_http_SOURCES = t-http.c http.c dns-stuff.c
-t_http_CFLAGS = -DWITHOUT_NPTH=1 \
+t_http_SOURCES = $(t_common_src) t-http.c http.c dns-stuff.c
+t_http_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
$(LIBGCRYPT_CFLAGS) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS) \
$(GPG_ERROR_CFLAGS)
t_http_LDADD = $(t_common_ldadd) \
t-ldap-parse-uri.c ldap-parse-uri.c ldap-parse-uri.h \
http.c dns-stuff.c \
$(ldap_url) $(t_common_src)
-t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 \
+t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
$(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS)
-t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 \
+t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
$(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
-t_dns_stuff_SOURCES = t-dns-stuff.c dns-stuff.c
+t_dns_stuff_SOURCES = $(t_common_src) t-dns-stuff.c dns-stuff.c
t_dns_stuff_LDADD = $(t_common_ldadd) $(DNSLIBS)
$(PROGRAMS) : $(libcommon) $(libcommonpth)
oKeyServer,
oNameServer,
oDisableCheckOwnSocket,
+ oStandardResolver,
+ oRecursiveResolver,
+ oResolverTimeout,
aTest
};
ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"),
ARGPARSE_s_n (oHonorHTTPProxy, "honor-http-proxy", "@"),
ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"),
+ ARGPARSE_s_n (oStandardResolver, "standard-resolver", "@"),
+ ARGPARSE_s_n (oRecursiveResolver, "recursive-resolver", "@"),
+ ARGPARSE_s_i (oResolverTimeout, "resolver-timeout", "@"),
ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
"of all commands and options)\n")),
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_HASHING_VALUE, "hashing" },
{ DBG_IPC_VALUE , "ipc" },
+ { DBG_DNS_VALUE , "dns" },
+ { DBG_NETWORK_VALUE, "network" },
{ DBG_LOOKUP_VALUE , "lookup" },
{ 77, NULL } /* 77 := Do not exit on "help" or "?". */
};
FREE_STRLIST (opt.keyserver);
/* Note: We do not allow resetting of opt.use_tor at runtime. */
disable_check_own_socket = 0;
+ enable_standard_resolver (0);
+ set_dns_timeout (0);
return 1;
}
case oUseTor: opt.use_tor = 1; break;
+ case oStandardResolver: enable_standard_resolver (1); break;
+ case oRecursiveResolver: enable_recursive_resolver (1); break;
+
case oKeyServer:
if (*pargs->r.ret_str)
add_to_strlist (&opt.keyserver, pargs->r.ret_str);
set_dns_nameserver (pargs->r.ret_str);
break;
+ case oResolverTimeout:
+ set_dns_timeout (pargs->r.ret_int);
+ break;
+
default:
return 0; /* Not handled. */
}
+ set_dns_verbose (opt.verbose, !!DBG_DNS);
+
return 1; /* Handled. */
}
thread_init ();
cert_cache_init ();
crl_cache_init ();
-#if USE_LDAP
- ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
http_register_netactivity_cb (netactivity_action);
start_command_handler (ASSUAN_INVALID_FD);
shutdown_reaper ();
thread_init ();
cert_cache_init ();
crl_cache_init ();
-#if USE_LDAP
- ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
http_register_netactivity_cb (netactivity_action);
handle_connections (3);
- assuan_sock_close (3);
shutdown_reaper ();
}
#endif /*HAVE_W32_SYSTEM*/
thread_init ();
cert_cache_init ();
crl_cache_init ();
-#if USE_LDAP
- ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
http_register_netactivity_cb (netactivity_action);
handle_connections (fd);
- assuan_sock_close (fd);
shutdown_reaper ();
}
else if (cmd == aListCRLs)
/* Just list the CRL cache and exit. */
if (argc)
wrong_args ("--list-crls");
-#if USE_LDAP
- ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
crl_cache_init ();
crl_cache_list (es_stdout);
}
thread_init ();
cert_cache_init ();
crl_cache_init ();
-#if USE_LDAP
- ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
if (!argc)
rc = crl_cache_load (&ctrlbuf, NULL);
else
thread_init ();
cert_cache_init ();
crl_cache_init ();
-#if USE_LDAP
- ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
rc = crl_fetch (&ctrlbuf, argv[0], &reader);
if (rc)
log_error (_("fetching CRL from '%s' failed: %s\n"),
es_printf ("use-tor:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("keyserver:%lu:\n", flags | GC_OPT_FLAG_NONE);
+ es_printf ("nameserver:%lu:\n", flags | GC_OPT_FLAG_NONE);
+ es_printf ("resolver-timeout:%lu:%u\n",
+ flags | GC_OPT_FLAG_DEFAULT, 0);
}
cleanup ();
return !!rc;
{
crl_cache_deinit ();
cert_cache_deinit (1);
+ reload_dns_stuff (1);
#if USE_LDAP
ldapserver_list_free (opt.ldapservers);
crl_cache_deinit ();
cert_cache_init ();
crl_cache_init ();
+ reload_dns_stuff (0);
}
#endif /*HAVE_INOTIFY_INIT*/
-/* Main loop in daemon mode. */
+/* Main loop in daemon mode. Note that LISTEN_FD will be owned by
+ * this function. */
static void
handle_connections (assuan_fd_t listen_fd)
{
struct timespec curtime;
struct timespec timeout;
int saved_errno;
-#ifdef HAVE_INOTIFY_INIT
- int my_inotify_fd;
-#endif /*HAVE_INOTIFY_INIT*/
+ int my_inotify_fd = -1;
npth_attr_init (&tattr);
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
FD_ZERO (&fdset);
FD_SET (FD2INT (listen_fd), &fdset);
nfd = FD2INT (listen_fd);
-#ifdef HAVE_INOTIFY_INIT
if (my_inotify_fd != -1)
{
FD_SET (my_inotify_fd, &fdset);
if (my_inotify_fd > nfd)
nfd = my_inotify_fd;
}
-#endif /*HAVE_INOTIFY_INIT*/
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
break; /* ready */
/* Do not accept new connections but keep on running the
- loop to cope with the timer events. */
+ * loop to cope with the timer events.
+ *
+ * Note that we do not close the listening socket because a
+ * client trying to connect to that socket would instead
+ * restart a new dirmngr instance - which is unlikely the
+ * intention of a shutdown. */
+ /* assuan_sock_close (listen_fd); */
+ /* listen_fd = -1; */
FD_ZERO (&fdset);
+ nfd = -1;
+ if (my_inotify_fd != -1)
+ {
+ FD_SET (my_inotify_fd, &fdset);
+ nfd = my_inotify_fd;
+ }
}
/* Take a copy of the fdset. */
close (my_inotify_fd);
#endif /*HAVE_INOTIFY_INIT*/
npth_attr_destroy (&tattr);
+ if (listen_fd != -1)
+ assuan_sock_close (fd);
cleanup ();
log_info ("%s %s stopped\n", strusage(11), strusage(13));
}
#define DBG_X509_VALUE 1 /* debug x.509 parsing */
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
+#define DBG_DNS_VALUE 16 /* debug DNS calls. */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_CACHE_VALUE 64 /* debug the caching */
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_IPC_VALUE 1024 /* debug assuan communication */
+#define DBG_NETWORK_VALUE 2048 /* debug network I/O. */
#define DBG_LOOKUP_VALUE 8192 /* debug lookup details */
#define DBG_X509 (opt.debug & DBG_X509_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_DNS (opt.debug & DBG_DNS_VALUE)
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
+#define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE)
#define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE)
/* A simple list of certificate references. */
/* dns-stuff.c - DNS related code including CERT RR (rfc-4398)
* Copyright (C) 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
- * Copyright (C) 2005, 2006, 2009, 2015 Werner Koch
+ * Copyright (C) 2005, 2006, 2009, 2015. 2016 Werner Koch
*
* This file is part of GnuPG.
*
#include <config.h>
#include <sys/types.h>
#ifdef HAVE_W32_SYSTEM
+# define WIN32_LEAN_AND_MEAN
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# endif
# include <windows.h>
+# include <iphlpapi.h>
#else
-# include <netinet/in.h>
-# include <arpa/nameser.h>
-# include <resolv.h>
+# if HAVE_SYSTEM_RESOLVER
+# include <netinet/in.h>
+# include <arpa/nameser.h>
+# include <resolv.h>
+# endif
# include <netdb.h>
#endif
#include <string.h>
#include <unistd.h>
-#ifdef USE_ADNS
-# include <adns.h>
-#endif
-#if !defined(HAVE_GETADDRINFO) && !defined(USE_ADNS)
-# error Either getaddrinfo or the ADNS library is required.
+
+/* William Ahern's DNS library, included as a source copy. */
+#ifdef USE_LIBDNS
+# include "dns.h"
#endif
+/* dns.c has a dns_p_free but it is not exported. We use our own
+ * wrapper here so that we do not accidentally use xfree which would
+ * be wrong for dns.c allocated data. */
+#define dns_free(a) free ((a))
+
+
#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */
# undef USE_NPTH
#endif
# include <npth.h>
#endif
+#include "./dirmngr-err.h"
#include "util.h"
#include "host2net.h"
#include "dns-stuff.h"
# define AI_ADDRCONFIG 0
#endif
-/* Provide a replacement function for older ADNS versions. */
-#ifndef HAVE_ADNS_FREE
-# define adns_free(a) free ((a))
-#endif
-
/* Not every installation has gotten around to supporting SRVs or
CERTs yet... */
#ifndef T_SRV
# define T_CERT 37
#endif
-/* ADNS has no support for CERT yet. */
-#define my_adns_r_cert 37
+/* The standard SOCKS and TOR ports. */
+#define SOCKS_PORT 1080
+#define TOR_PORT 9050
+#define TOR_PORT2 9150 /* (Used by the Tor browser) */
-/* The default nameserver used with ADNS in Tor mode. */
+/* The default nameserver used in Tor mode. */
#define DEFAULT_NAMESERVER "8.8.8.8"
+/* The default timeout in seconds for libdns requests. */
+#define DEFAULT_TIMEOUT 30
+
+
+/* Two flags to enable verbose and debug mode. */
+static int opt_verbose;
+static int opt_debug;
+
+/* The timeout in seconds for libdns requests. */
+static int opt_timeout;
+
+/* If set force the use of the standard resolver. */
+static int standard_resolver;
+
+/* If set use recursive resolver when available. */
+static int recursive_resolver;
/* If set Tor mode shall be used. */
static int tor_mode;
(40 should be sufficient for v6 but we add some extra for a scope.) */
static char tor_nameserver[40+20];
-/* A string to hold the credentials presented to Tor. */
-#ifdef USE_ADNS
-static char tor_credentials[50];
+/* Two strings to hold the credentials presented to Tor. */
+static char tor_socks_user[30];
+static char tor_socks_password[20];
+
+
+#ifdef USE_LIBDNS
+/* Libdns gobal data. */
+struct libdns_s
+{
+ struct dns_resolv_conf *resolv_conf;
+ struct dns_hosts *hosts;
+ struct dns_hints *hints;
+
+ struct sockaddr_storage socks_host;
+} libdns;
+
+/* If this flag is set, libdns shall be reinited for the next use. */
+static int libdns_reinit_pending;
+
+/* The Tor port to be used. */
+static int libdns_tor_port;
+
+#endif /*USE_LIBDNS*/
+
+
+/* Calling this function with YES set to True forces the use of the
+ * standard resolver even if dirmngr has been built with support for
+ * an alternative resolver. */
+void
+enable_standard_resolver (int yes)
+{
+ standard_resolver = yes;
+}
+
+
+/* Return true if the standard resolver is used. */
+int
+standard_resolver_p (void)
+{
+ return standard_resolver;
+}
+
+
+/* Calling this function with YES switches libdns into recursive mode.
+ * It has no effect on the standard resolver. */
+void
+enable_recursive_resolver (int yes)
+{
+ recursive_resolver = yes;
+ libdns_reinit_pending = 1;
+}
+
+
+/* Return true iff the recursive resolver is used. */
+int
+recursive_resolver_p (void)
+{
+#if USE_LIBDNS
+ return !standard_resolver && recursive_resolver;
+#else
+ return 0;
#endif
+}
+
/* Sets the module in Tor mode. Returns 0 is this is possible or an
error code. */
gpg_error_t
enable_dns_tormode (int new_circuit)
{
- (void) new_circuit;
-
-#if defined(USE_DNS_CERT) && defined(USE_ADNS)
-# if HAVE_ADNS_IF_TORMODE
- if (!*tor_credentials || new_circuit)
- {
- static unsigned int counter;
-
- gpgrt_snprintf (tor_credentials, sizeof tor_credentials,
- "dirmngr-%lu:p%u",
- (unsigned long)getpid (), counter);
- counter++;
- }
- tor_mode = 1;
- return 0;
-# endif
-#endif
+ if (!*tor_socks_user || new_circuit)
+ {
+ static unsigned int counter;
- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ gpgrt_snprintf (tor_socks_user, sizeof tor_socks_user,
+ "dirmngr-%lu", (unsigned long)getpid ());
+ gpgrt_snprintf (tor_socks_password, sizeof tor_socks_password,
+ "p%u", counter);
+ counter++;
+ }
+ tor_mode = 1;
+ return 0;
+}
+
+
+/* Set verbosity and debug mode for this module. */
+void
+set_dns_verbose (int verbose, int debug)
+{
+ opt_verbose = verbose;
+ opt_debug = debug;
+}
+
+
+/* Set the timeout for libdns requests to SECONDS. A value of 0 sets
+ * the default timeout and values are capped at 10 minutes. */
+void
+set_dns_timeout (int seconds)
+{
+ if (!seconds)
+ seconds = DEFAULT_TIMEOUT;
+ else if (seconds < 1)
+ seconds = 1;
+ else if (seconds > 600)
+ seconds = 600;
+
+ opt_timeout = seconds;
}
strncpy (tor_nameserver, ipaddr? ipaddr : DEFAULT_NAMESERVER,
sizeof tor_nameserver -1);
tor_nameserver[sizeof tor_nameserver -1] = 0;
+ libdns_reinit_pending = 1;
+ libdns_tor_port = 0; /* Start again with the default port. */
}
}
+#ifndef HAVE_W32_SYSTEM
+/* Return H_ERRNO mapped to a gpg-error code. Will never return 0. */
+static gpg_error_t
+get_h_errno_as_gpg_error (void)
+{
+ gpg_err_code_t ec;
+
+ switch (h_errno)
+ {
+ case HOST_NOT_FOUND: ec = GPG_ERR_UNKNOWN_HOST; break;
+ case TRY_AGAIN: ec = GPG_ERR_TRY_LATER; break;
+ case NO_RECOVERY: ec = GPG_ERR_SERVER_FAILED; break;
+ case NO_DATA: ec = GPG_ERR_NO_DATA; break;
+ default: ec = GPG_ERR_UNKNOWN_ERRNO; break;
+ }
+ return gpg_error (ec);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
static gpg_error_t
map_eai_to_gpg_error (int ec)
{
return err;
}
-#ifdef USE_ADNS
+
+#ifdef USE_LIBDNS
static gpg_error_t
-map_adns_status_to_gpg_error (adns_status status)
+libdns_error_to_gpg_error (int serr)
{
gpg_err_code_t ec;
- switch (status)
+ switch (serr)
{
- /* case adns_s_netunreach: ec = GPG_ERR_ENETUNREACH; break; */
- default: ec = GPG_ERR_GENERAL; break;
+ case 0: ec = 0; break;
+
+ case DNS_ENOBUFS: ec = GPG_ERR_BUFFER_TOO_SHORT; break;
+ case DNS_EILLEGAL: ec = GPG_ERR_INV_OBJ; break;
+ case DNS_EORDER: ec = GPG_ERR_INV_ORDER; break;
+ case DNS_ESECTION: ec = GPG_ERR_DNS_SECTION; break;
+ case DNS_EUNKNOWN: ec = GPG_ERR_DNS_UNKNOWN; break;
+ case DNS_EADDRESS: ec = GPG_ERR_DNS_ADDRESS; break;
+ case DNS_ENOQUERY: ec = GPG_ERR_DNS_NO_QUERY; break;
+ case DNS_ENOANSWER:ec = GPG_ERR_DNS_NO_ANSWER; break;
+ case DNS_EFETCHED: ec = GPG_ERR_ALREADY_FETCHED; break;
+ case DNS_ESERVICE: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case DNS_ENONAME: ec = GPG_ERR_NO_NAME; break;
+ case DNS_EFAIL: ec = GPG_ERR_SERVER_FAILED; break;
+ case DNS_ECONNFIN: ec = GPG_ERR_DNS_CLOSED; break;
+ case DNS_EVERIFY: ec = GPG_ERR_DNS_VERIFY; break;
+
+ default:
+ if (serr >= 0)
+ ec = gpg_err_code_from_errno (serr);
+ else
+ ec = GPG_ERR_DNS_UNKNOWN;
+ break;
}
return gpg_error (ec);
}
-#endif /*USE_ADNS*/
+#endif /*USE_LIBDNS*/
-#ifdef USE_ADNS
-/* Init ADNS and store the new state at R_STATE. Returns 0 on
- success; prints an error message and returns an error code on
- failure. */
+#ifdef USE_LIBDNS
+/* Initialize libdns. Returns 0 on success; prints a diagnostic and
+ * returns an error code on failure. */
static gpg_error_t
-my_adns_init (adns_state *r_state)
+libdns_init (void)
{
- gpg_error_t err = 0;
- int ret;
+ gpg_error_t err;
+ struct libdns_s ld;
+ int derr;
+ char *cfgstr = NULL;
- if (tor_mode)
+ if (libdns.resolv_conf)
+ return 0; /* Already initialized. */
+
+ memset (&ld, 0, sizeof ld);
+
+ ld.resolv_conf = dns_resconf_open (&derr);
+ if (!ld.resolv_conf)
{
- char *cfgstr;
+ err = libdns_error_to_gpg_error (derr);
+ log_error ("failed to allocate DNS resconf object: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ if (tor_mode)
+ {
if (!*tor_nameserver)
set_dns_nameserver (NULL);
- cfgstr = xtryasprintf ("nameserver %s\n"
- "options adns_tormode adns_sockscred:%s",
- tor_nameserver, tor_credentials);
+ if (!libdns_tor_port)
+ libdns_tor_port = TOR_PORT;
+
+ cfgstr = xtryasprintf ("[%s]:53", tor_nameserver);
+ if (!cfgstr)
+ err = gpg_error_from_syserror ();
+ else
+ err = libdns_error_to_gpg_error
+ (dns_resconf_pton (&ld.resolv_conf->nameserver[0], cfgstr));
+ if (err)
+ log_error ("failed to set nameserver '%s': %s\n",
+ cfgstr, gpg_strerror (err));
+ if (err)
+ goto leave;
+
+ ld.resolv_conf->options.tcp = DNS_RESCONF_TCP_SOCKS;
+
+ xfree (cfgstr);
+ cfgstr = xtryasprintf ("[%s]:%d", "127.0.0.1", libdns_tor_port);
if (!cfgstr)
err = gpg_error_from_syserror ();
else
+ err = libdns_error_to_gpg_error
+ (dns_resconf_pton (&ld.socks_host, cfgstr));
+ if (err)
{
- ret = adns_init_strcfg (r_state, adns_if_debug /*adns_if_noerrprint*/, NULL, cfgstr);
- if (ret)
- err = gpg_error_from_errno (ret);
- xfree (cfgstr);
+ log_error ("failed to set socks server '%s': %s\n",
+ cfgstr, gpg_strerror (err));
+ goto leave;
+ }
+ }
+ else
+ {
+#ifdef HAVE_W32_SYSTEM
+ ULONG ninfo_len;
+ PFIXED_INFO ninfo;
+ PIP_ADDR_STRING pip;
+ int idx;
+
+ ninfo_len = 2048;
+ ninfo = xtrymalloc (ninfo_len);
+ if (!ninfo)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ if (GetNetworkParams (ninfo, &ninfo_len))
+ {
+ log_error ("GetNetworkParms failed: %s\n", w32_strerror (-1));
+ err = gpg_error (GPG_ERR_GENERAL);
+ xfree (ninfo);
+ goto leave;
+ }
+
+ for (idx=0, pip = &(ninfo->DnsServerList);
+ pip && idx < DIM (ld.resolv_conf->nameserver);
+ pip = pip->Next)
+ {
+ if (opt_debug)
+ log_debug ("dns: dnsserver[%d] '%s'\n", idx, pip->IpAddress.String);
+ err = libdns_error_to_gpg_error
+ (dns_resconf_pton (&ld.resolv_conf->nameserver[idx],
+ pip->IpAddress.String));
+ if (err)
+ log_error ("failed to set nameserver[%d] '%s': %s\n",
+ idx, pip->IpAddress.String, gpg_strerror (err));
+ else
+ idx++;
+ }
+ xfree (ninfo);
+
+#else /* Unix */
+ const char *fname;
+
+ fname = "/etc/resolv.conf";
+ err = libdns_error_to_gpg_error
+ (dns_resconf_loadpath (ld.resolv_conf, fname));
+ if (err)
+ {
+ log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err));
+ goto leave;
+ }
+
+ fname = "/etc/nsswitch.conf";
+ err = libdns_error_to_gpg_error
+ (dns_nssconf_loadpath (ld.resolv_conf, fname));
+ if (err)
+ {
+ log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err));
+ goto leave;
}
+
+#endif /* Unix */
+ }
+
+ ld.hosts = dns_hosts_open (&derr);
+ if (!ld.hosts)
+ {
+ log_error ("failed to load hosts file: %s\n", gpg_strerror (err));
+ err = libdns_error_to_gpg_error (derr);
+ goto leave;
+ }
+
+ /* dns_hints_local for stub mode, dns_hints_root for recursive. */
+ ld.hints = (recursive_resolver
+ ? dns_hints_root (ld.resolv_conf, &derr)
+ : dns_hints_local (ld.resolv_conf, &derr));
+ if (!ld.hints)
+ {
+ log_error ("failed to load DNS hints: %s\n", gpg_strerror (err));
+ err = libdns_error_to_gpg_error (derr);
+ goto leave;
+ }
+
+ /* All fine. Make the data global. */
+ libdns = ld;
+
+ leave:
+ xfree (cfgstr);
+ return err;
+}
+#endif /*USE_LIBDNS*/
+
+
+#ifdef USE_LIBDNS
+/* Deinitialize libdns. */
+static void
+libdns_deinit (void)
+{
+ struct libdns_s ld;
+
+ if (!libdns.resolv_conf)
+ return; /* Not initialized. */
+
+ ld = libdns;
+ memset (&libdns, 0, sizeof libdns);
+ dns_hints_close (ld.hints);
+ dns_hosts_close (ld.hosts);
+ dns_resconf_close (ld.resolv_conf);
+}
+#endif /*USE_LIBDNS*/
+
+
+/* SIGHUP action handler for this module. With FORCE set objects are
+ * all immediately released. */
+void
+reload_dns_stuff (int force)
+{
+ if (force)
+ {
+#ifdef USE_LIBDNS
+ libdns_deinit ();
+#endif
+ libdns_reinit_pending = 0;
}
else
+ libdns_reinit_pending = 1;
+}
+
+
+#ifdef USE_LIBDNS
+/*
+ * Initialize libdns if needed and open a dns_resolver context.
+ * Returns 0 on success and stores the new context at R_RES. On
+ * failure an error code is returned and NULL stored at R_RES.
+ */
+static gpg_error_t
+libdns_res_open (struct dns_resolver **r_res)
+{
+ gpg_error_t err;
+ struct dns_resolver *res;
+ int derr;
+
+ *r_res = NULL;
+
+ if (libdns_reinit_pending)
{
- ret = adns_init (r_state, adns_if_noerrprint, NULL);
- if (ret)
- err = gpg_error_from_errno (ret);
+ libdns_reinit_pending = 0;
+ libdns_deinit ();
}
+ err = libdns_init ();
if (err)
+ return err;
+
+ if (!opt_timeout)
+ set_dns_timeout (0);
+
+ res = dns_res_open (libdns.resolv_conf, libdns.hosts, libdns.hints, NULL,
+ dns_opts (.socks_host = &libdns.socks_host,
+ .socks_user = tor_socks_user,
+ .socks_password = tor_socks_password ),
+ &derr);
+ if (!res)
+ return libdns_error_to_gpg_error (derr);
+
+ *r_res = res;
+ return 0;
+}
+#endif /*USE_LIBDNS*/
+
+
+#ifdef USE_LIBDNS
+/* Helper to test whether we need totry again after having swicthed
+ * the Tor port. */
+static int
+libdns_switch_port_p (gpg_error_t err)
+{
+ if (tor_mode && gpg_err_code (err) == GPG_ERR_ECONNREFUSED
+ && libdns_tor_port == TOR_PORT)
{
- log_error ("error initializing adns: %s\n", gpg_strerror (err));
- return err;
+ /* Switch port and try again. */
+ if (opt_debug)
+ log_debug ("dns: switching from SOCKS port %d to %d\n",
+ TOR_PORT, TOR_PORT2);
+ libdns_tor_port = TOR_PORT2;
+ libdns_reinit_pending = 1;
+ return 1;
}
return 0;
}
-#endif /*USE_ADNS*/
+#endif /*USE_LIBDNS*/
-#ifdef USE_ADNS
-/* Resolve a name using the ADNS library. See resolve_dns_name for
- the description. */
+#ifdef USE_LIBDNS
+/* Wrapper around dns_res_submit. */
static gpg_error_t
-resolve_name_adns (const char *name, unsigned short port,
- int want_family, int want_socktype,
- dns_addrinfo_t *r_dai, char **r_canonname)
+libdns_res_submit (struct dns_resolver *res, const char *qname,
+ enum dns_type qtype, enum dns_class qclass)
{
- gpg_error_t err = 0;
- int ret;
+ return libdns_error_to_gpg_error (dns_res_submit (res, qname, qtype, qclass));
+}
+#endif /*USE_LIBDNS*/
+
+
+#ifdef USE_LIBDNS
+/* Standard event handling loop. */
+gpg_error_t
+libdns_res_wait (struct dns_resolver *res)
+{
+ gpg_error_t err;
+
+ while ((err = libdns_error_to_gpg_error (dns_res_check (res)))
+ && gpg_err_code (err) == GPG_ERR_EAGAIN)
+ {
+ if (dns_res_elapsed (res) > opt_timeout)
+ {
+ err = gpg_error (GPG_ERR_DNS_TIMEOUT);
+ break;
+ }
+
+ my_unprotect ();
+ dns_res_poll (res, 1);
+ my_protect ();
+ }
+
+ return err;
+}
+#endif /*USE_LIBDNS*/
+
+
+#ifdef USE_LIBDNS
+static gpg_error_t
+resolve_name_libdns (const char *name, unsigned short port,
+ int want_family, int want_socktype,
+ dns_addrinfo_t *r_dai, char **r_canonname)
+{
+ gpg_error_t err;
dns_addrinfo_t daihead = NULL;
dns_addrinfo_t dai;
- adns_state state;
- adns_answer *answer = NULL;
- int count;
-
- (void)want_family;
+ struct dns_resolver *res = NULL;
+ struct dns_addrinfo *ai = NULL;
+ struct addrinfo hints;
+ struct addrinfo *ent;
+ char portstr_[21];
+ char *portstr = NULL;
+ int derr;
*r_dai = NULL;
if (r_canonname)
*r_canonname = NULL;
- if (want_socktype != SOCK_STREAM && want_socktype != SOCK_DGRAM)
- return gpg_error (GPG_ERR_ESOCKTNOSUPPORT);
-
- err = my_adns_init (&state);
- if (err)
- return err;
+ memset (&hints, 0, sizeof hints);
+ hints.ai_family = want_family;
+ hints.ai_socktype = want_socktype;
+ hints.ai_flags = AI_ADDRCONFIG;
+ if (r_canonname)
+ hints.ai_flags |= AI_CANONNAME;
- my_unprotect ();
- ret = adns_synchronous (state, name, adns_r_addr,
- adns_qf_quoteok_query, &answer);
- my_protect ();
- if (ret)
+ if (port)
{
- err = gpg_error (gpg_err_code_from_errno (ret));
- log_error ("DNS query failed: %s\n", gpg_strerror (err));
- goto leave;
+ snprintf (portstr_, sizeof portstr_, "%hu", port);
+ portstr = portstr_;
}
- err = gpg_error (GPG_ERR_NOT_FOUND);
- if (answer->status != adns_s_ok || answer->type != adns_r_addr)
+ err = libdns_res_open (&res);
+ if (err)
+ goto leave;
+
+ ai = dns_ai_open (name, portstr, 0, &hints, res, &derr);
+ if (!ai)
{
- err = map_adns_status_to_gpg_error (answer->status);
- if (gpg_err_code (err) == GPG_ERR_GENERAL)
- err = gpg_error (GPG_ERR_NOT_FOUND);
- log_error ("DNS query returned an error: %s (%s)\n",
- adns_strerror (answer->status),
- adns_errabbrev (answer->status));
+ err = libdns_error_to_gpg_error (derr);
goto leave;
}
- if (r_canonname && answer->cname)
+ /* Loop over all records. */
+ for (;;)
{
- *r_canonname = xtrystrdup (answer->cname);
- if (!*r_canonname)
+ err = libdns_error_to_gpg_error (dns_ai_nextent (&ent, ai));
+ if (gpg_err_code (err) == GPG_ERR_ENOENT)
{
- err = gpg_error_from_syserror ();
- goto leave;
+ if (daihead)
+ err = 0; /* We got some results, we're good. */
+ break; /* Ready. */
}
- }
+ if (gpg_err_code (err) == GPG_ERR_EAGAIN)
+ {
+ if (dns_ai_elapsed (ai) > opt_timeout)
+ {
+ err = gpg_error (GPG_ERR_DNS_TIMEOUT);
+ goto leave;
+ }
- for (count = 0; count < answer->nrrs; count++)
- {
- int len;
- adns_rr_addr *addr;
+ my_unprotect ();
+ dns_ai_poll (ai, 1);
+ my_protect ();
+ continue;
+ }
+ if (err)
+ goto leave;
- len = answer->rrs.addr[count].len;
- addr = &answer->rrs.addr[count];
- if (addr->addr.sa.sa_family != AF_INET6
- && addr->addr.sa.sa_family != AF_INET)
- continue;
+ if (r_canonname && ! *r_canonname && ent && ent->ai_canonname)
+ {
+ *r_canonname = xtrystrdup (ent->ai_canonname);
+ if (!*r_canonname)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
- dai = xtrymalloc (sizeof *dai + len - 1);
- if (!dai)
+ dai = xtrymalloc (sizeof *dai + ent->ai_addrlen -1);
+ if (dai == NULL)
{
err = gpg_error_from_syserror ();
goto leave;
}
- dai->family = addr->addr.sa.sa_family;
- dai->socktype = want_socktype == SOCK_STREAM? SOCK_STREAM : SOCK_DGRAM;
- dai->protocol = want_socktype == SOCK_STREAM? IPPROTO_TCP : IPPROTO_UDP;
- dai->addrlen = len;
- memcpy (dai->addr, &addr->addr.sa, len);
- ((struct sockaddr_in *) dai->addr)->sin_port = htons (port);
+
+ dai->family = ent->ai_family;
+ dai->socktype = ent->ai_socktype;
+ dai->protocol = ent->ai_protocol;
+ dai->addrlen = ent->ai_addrlen;
+ memcpy (dai->addr, ent->ai_addr, ent->ai_addrlen);
dai->next = daihead;
daihead = dai;
- err = 0;
- }
+
+ xfree (ent);
+ }
leave:
- adns_free (answer);
- adns_finish (state);
+ dns_ai_close (ai);
+ dns_res_close (res);
+
if (err)
{
if (r_canonname)
}
else
*r_dai = daihead;
+
return err;
}
-#endif /*USE_ADNS*/
+#endif /*USE_LIBDNS*/
-#ifndef USE_ADNS
/* Resolve a name using the standard system function. */
static gpg_error_t
resolve_name_standard (const char *name, unsigned short port,
*r_dai = daihead;
return err;
}
-#endif /*!USE_ADNS*/
/* Resolve an address using the standard system function. */
int want_family, int want_socktype,
dns_addrinfo_t *r_ai, char **r_canonname)
{
-#ifdef USE_ADNS
- return resolve_name_adns (name, port, want_family, want_socktype,
- r_ai, r_canonname);
-#else
- return resolve_name_standard (name, port, want_family, want_socktype,
- r_ai, r_canonname);
-#endif
+ gpg_error_t err;
+
+#ifdef USE_LIBDNS
+ if (!standard_resolver)
+ {
+ err = resolve_name_libdns (name, port, want_family, want_socktype,
+ r_ai, r_canonname);
+ if (err && libdns_switch_port_p (err))
+ err = resolve_name_libdns (name, port, want_family, want_socktype,
+ r_ai, r_canonname);
+ }
+ else
+#endif /*USE_LIBDNS*/
+ err = resolve_name_standard (name, port, want_family, want_socktype,
+ r_ai, r_canonname);
+ if (opt_debug)
+ log_debug ("dns: resolve_dns_name(%s): %s\n", name, gpg_strerror (err));
+ return err;
}
resolve_dns_addr (const struct sockaddr *addr, int addrlen,
unsigned int flags, char **r_name)
{
-#ifdef USE_ADNS_disabled_for_now
- return resolve_addr_adns (addr, addrlen, flags, r_name);
-#else
return resolve_addr_standard (addr, addrlen, flags, r_name);
-#endif
}
}
-/* Returns 0 on success or an error code. If a PGP CERT record was
- found, the malloced data is returned at (R_KEY, R_KEYLEN) and
- the other return parameters are set to NULL/0. If an IPGP CERT
- record was found the fingerprint is stored as an allocated block at
- R_FPR and its length at R_FPRLEN; an URL is is allocated as a
- string and returned at R_URL. If WANT_CERTTYPE is 0 this function
- returns the first CERT found with a supported type; it is expected
- that only one CERT record is used. If WANT_CERTTYPE is one of the
- supported certtypes only records with this certtype are considered
- and the first found is returned. (R_KEY,R_KEYLEN) are optional. */
-gpg_error_t
-get_dns_cert (const char *name, int want_certtype,
- void **r_key, size_t *r_keylen,
- unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
+/* libdns version of get_dns_cert. */
+#ifdef USE_LIBDNS
+static gpg_error_t
+get_dns_cert_libdns (const char *name, int want_certtype,
+ void **r_key, size_t *r_keylen,
+ unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
{
-#ifdef USE_DNS_CERT
-#ifdef USE_ADNS
gpg_error_t err;
- int ret;
- adns_state state;
- adns_answer *answer = NULL;
- unsigned int ctype;
- int count;
+ struct dns_resolver *res = NULL;
+ struct dns_packet *ans = NULL;
+ struct dns_rr rr;
+ struct dns_rr_i rri;
+ char host[DNS_D_MAXNAME + 1];
+ int derr;
+ int qtype;
+
+ /* Gte the query type from WANT_CERTTYPE (which in general indicates
+ * the subtype we want). */
+ qtype = (want_certtype < DNS_CERTTYPE_RRBASE
+ ? T_CERT
+ : (want_certtype - DNS_CERTTYPE_RRBASE));
+
+
+ err = libdns_res_open (&res);
+ if (err)
+ goto leave;
- if (r_key)
- *r_key = NULL;
- if (r_keylen)
- *r_keylen = 0;
- *r_fpr = NULL;
- *r_fprlen = 0;
- *r_url = NULL;
+ if (dns_d_anchor (host, sizeof host, name, strlen (name)) >= sizeof host)
+ {
+ err = gpg_error (GPG_ERR_ENAMETOOLONG);
+ goto leave;
+ }
- err = my_adns_init (&state);
+ err = libdns_res_submit (res, name, qtype, DNS_C_IN);
if (err)
- return err;
+ goto leave;
- my_unprotect ();
- ret = adns_synchronous (state, name,
- (adns_r_unknown
- | (want_certtype < DNS_CERTTYPE_RRBASE
- ? my_adns_r_cert
- : (want_certtype - DNS_CERTTYPE_RRBASE))),
- adns_qf_quoteok_query, &answer);
- my_protect ();
- if (ret)
+ err = libdns_res_wait (res);
+ if (err)
+ goto leave;
+
+ ans = dns_res_fetch (res, &derr);
+ if (!ans)
{
- err = gpg_error (gpg_err_code_from_errno (ret));
- /* log_error ("DNS query failed: %s\n", gpg_strerror (err)); */
- adns_finish (state);
- return err;
+ err = libdns_error_to_gpg_error (derr);
+ goto leave;
}
- if (answer->status != adns_s_ok)
+
+ /* Check the rcode. */
+ switch (dns_p_rcode (ans))
{
- /* log_error ("DNS query returned an error: %s (%s)\n", */
- /* adns_strerror (answer->status), */
- /* adns_errabbrev (answer->status)); */
- err = map_adns_status_to_gpg_error (answer->status);
- if (gpg_err_code (err) == GPG_ERR_GENERAL)
- err = gpg_error (GPG_ERR_NOT_FOUND);
- goto leave;
+ case DNS_RC_NOERROR: break;
+ case DNS_RC_NXDOMAIN: err = gpg_error (GPG_ERR_NO_NAME); break;
+ default: err = GPG_ERR_SERVER_FAILED; break;
}
+ if (err)
+ goto leave;
+
+ memset (&rri, 0, sizeof rri);
+ dns_rr_i_init (&rri, ans);
+ rri.section = DNS_S_ALL & ~DNS_S_QD;
+ rri.name = host;
+ rri.type = qtype;
err = gpg_error (GPG_ERR_NOT_FOUND);
- for (count = 0; count < answer->nrrs; count++)
+ while (dns_rr_grep (&rr, 1, &rri, ans, &derr))
{
- int datalen = answer->rrs.byteblock[count].len;
- const unsigned char *data = answer->rrs.byteblock[count].data;
-
- /* First check for our generic RR hack. */
- if (datalen
- && want_certtype >= DNS_CERTTYPE_RRBASE
- && ((want_certtype - DNS_CERTTYPE_RRBASE)
- == (answer->type & ~adns_r_unknown)))
+ unsigned char *rp = ans->data + rr.rd.p;
+ unsigned short len = rr.rd.len;
+ u16 subtype;
+
+ if (!len)
{
- /* Found the requested record - return it. */
- *r_key = xtrymalloc (datalen);
- if (!*r_key)
- err = gpg_error_from_syserror ();
- else
- {
- memcpy (*r_key, data, datalen);
- *r_keylen = datalen;
- err = 0;
- }
- goto leave;
+ /* Definitely too short - skip. */
}
-
- if (datalen < 5)
- continue; /* Truncated CERT record - skip. */
-
- ctype = buf16_to_uint (data);
- /* (key tag and algorithm fields are not required.) */
- data += 5;
- datalen -= 5;
-
- if (want_certtype && want_certtype != ctype)
- ; /* Not of the requested certtype. */
- else if (ctype == DNS_CERTTYPE_PGP && datalen >= 11 && r_key && r_keylen)
+ else if (want_certtype >= DNS_CERTTYPE_RRBASE
+ && rr.type == (want_certtype - DNS_CERTTYPE_RRBASE)
+ && r_key)
{
- /* CERT type is PGP. Gpg checks for a minimum length of 11,
- thus we do the same. */
- *r_key = xtrymalloc (datalen);
+ *r_key = xtrymalloc (len);
if (!*r_key)
err = gpg_error_from_syserror ();
else
{
- memcpy (*r_key, data, datalen);
- *r_keylen = datalen;
+ memcpy (*r_key, rp, len);
+ *r_keylen = len;
err = 0;
}
goto leave;
}
- else if (ctype == DNS_CERTTYPE_IPGP && datalen && datalen < 1023
- && datalen >= data[0] + 1 && r_fpr && r_fprlen && r_url)
+ else if (want_certtype >= DNS_CERTTYPE_RRBASE)
+ {
+ /* We did not found the requested RR - skip. */
+ }
+ else if (rr.type == T_CERT && len > 5)
{
- /* CERT type is IPGP. We made sure that the data is
- plausible and that the caller requested this
- information. */
- *r_fprlen = data[0];
- if (*r_fprlen)
+ /* We got a CERT type. */
+ subtype = buf16_to_u16 (rp);
+ rp += 2; len -= 2;
+
+ /* Skip the CERT key tag and algo which we don't need. */
+ rp += 3; len -= 3;
+
+ if (want_certtype && want_certtype != subtype)
+ ; /* Not the requested subtype - skip. */
+ else if (subtype == DNS_CERTTYPE_PGP && len && r_key && r_keylen)
{
- *r_fpr = xtrymalloc (*r_fprlen);
- if (!*r_fpr)
+ /* PGP subtype */
+ *r_key = xtrymalloc (len);
+ if (!*r_key)
+ err = gpg_error_from_syserror ();
+ else
{
- err = gpg_error_from_syserror ();
- goto leave;
+ memcpy (*r_key, rp, len);
+ *r_keylen = len;
+ err = 0;
}
- memcpy (*r_fpr, data + 1, *r_fprlen);
+ goto leave;
}
- else
- *r_fpr = NULL;
-
- if (datalen > *r_fprlen + 1)
+ else if (subtype == DNS_CERTTYPE_IPGP
+ && len && len < 1023 && len >= rp[0] + 1)
{
- *r_url = xtrymalloc (datalen - (*r_fprlen + 1) + 1);
- if (!*r_url)
+ /* IPGP type */
+ *r_fprlen = rp[0];
+ if (*r_fprlen)
{
- err = gpg_error_from_syserror ();
- xfree (*r_fpr);
- *r_fpr = NULL;
- goto leave;
+ *r_fpr = xtrymalloc (*r_fprlen);
+ if (!*r_fpr)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (*r_fpr, rp+1, *r_fprlen);
+ }
+ else
+ *r_fpr = NULL;
+
+ if (len > *r_fprlen + 1)
+ {
+ *r_url = xtrymalloc (len - (*r_fprlen + 1) + 1);
+ if (!*r_url)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (*r_fpr);
+ *r_fpr = NULL;
+ goto leave;
+ }
+ memcpy (*r_url, rp + *r_fprlen + 1, len - (*r_fprlen + 1));
+ (*r_url)[len - (*r_fprlen + 1)] = 0;
}
- memcpy (*r_url,
- data + (*r_fprlen + 1), datalen - (*r_fprlen + 1));
- (*r_url)[datalen - (*r_fprlen + 1)] = '\0';
+ else
+ *r_url = NULL;
+
+ err = 0;
+ goto leave;
}
else
- *r_url = NULL;
-
- err = 0;
- goto leave;
+ {
+ /* Unknown subtype or record too short - skip. */
+ }
+ }
+ else
+ {
+ /* Not a requested type - skip. */
}
}
leave:
- adns_free (answer);
- adns_finish (state);
+ dns_free (ans);
+ dns_res_close (res);
return err;
+}
+#endif /*USE_LIBDNS*/
-#else /*!USE_ADNS*/
+/* Standard resolver version of get_dns_cert. */
+static gpg_error_t
+get_dns_cert_standard (const char *name, int want_certtype,
+ void **r_key, size_t *r_keylen,
+ unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
+{
+#ifdef HAVE_SYSTEM_RESOLVER
gpg_error_t err;
unsigned char *answer;
int r;
u16 count;
- if (r_key)
- *r_key = NULL;
- if (r_keylen)
- *r_keylen = 0;
- *r_fpr = NULL;
- *r_fprlen = 0;
- *r_url = NULL;
-
/* Allocate a 64k buffer which is the limit for an DNS response. */
answer = xtrymalloc (65536);
if (!answer)
if (dlen > *r_fprlen + 1)
{
*r_url = xtrymalloc (dlen - (*r_fprlen + 1) + 1);
- if (!*r_fpr)
+ if (!*r_url)
{
err = gpg_error_from_syserror ();
xfree (*r_fpr);
xfree (answer);
return err;
-#endif /*!USE_ADNS */
-#else /* !USE_DNS_CERT */
+#else /*!HAVE_SYSTEM_RESOLVER*/
+
(void)name;
+ (void)want_certtype;
+ (void)r_key;
+ (void)r_keylen;
+ (void)r_fpr;
+ (void)r_fprlen;
+ (void)r_url;
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+#endif /*!HAVE_SYSTEM_RESOLVER*/
+}
+
+
+/* Returns 0 on success or an error code. If a PGP CERT record was
+ found, the malloced data is returned at (R_KEY, R_KEYLEN) and
+ the other return parameters are set to NULL/0. If an IPGP CERT
+ record was found the fingerprint is stored as an allocated block at
+ R_FPR and its length at R_FPRLEN; an URL is is allocated as a
+ string and returned at R_URL. If WANT_CERTTYPE is 0 this function
+ returns the first CERT found with a supported type; it is expected
+ that only one CERT record is used. If WANT_CERTTYPE is one of the
+ supported certtypes only records with this certtype are considered
+ and the first found is returned. (R_KEY,R_KEYLEN) are optional. */
+gpg_error_t
+get_dns_cert (const char *name, int want_certtype,
+ void **r_key, size_t *r_keylen,
+ unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
+{
+ gpg_error_t err;
+
if (r_key)
*r_key = NULL;
if (r_keylen)
*r_fprlen = 0;
*r_url = NULL;
- return gpg_error (GPG_ERR_NOT_SUPPORTED);
-#endif
+#ifdef USE_LIBDNS
+ if (!standard_resolver)
+ {
+ err = get_dns_cert_libdns (name, want_certtype, r_key, r_keylen,
+ r_fpr, r_fprlen, r_url);
+ if (err && libdns_switch_port_p (err))
+ err = get_dns_cert_libdns (name, want_certtype, r_key, r_keylen,
+ r_fpr, r_fprlen, r_url);
+ }
+ else
+#endif /*USE_LIBDNS*/
+ err = get_dns_cert_standard (name, want_certtype, r_key, r_keylen,
+ r_fpr, r_fprlen, r_url);
+
+ if (opt_debug)
+ log_debug ("dns: get_dns_cert(%s): %s\n", name, gpg_strerror (err));
+ return err;
}
-#ifdef USE_DNS_SRV
+
static int
priosort(const void *a,const void *b)
{
}
-int
-getsrv (const char *name,struct srventry **list)
+/* Libdns based helper for getsrv. Note that it is expected that NULL
+ * is stored at the address of LIST and 0 is stored at the address of
+ * R_COUNT. */
+#ifdef USE_LIBDNS
+static gpg_error_t
+getsrv_libdns (const char *name, struct srventry **list, unsigned int *r_count)
{
- int srvcount=0;
+ gpg_error_t err;
+ struct dns_resolver *res = NULL;
+ struct dns_packet *ans = NULL;
+ struct dns_rr rr;
+ struct dns_rr_i rri;
+ char host[DNS_D_MAXNAME + 1];
+ int derr;
+ unsigned int srvcount = 0;
+
+ err = libdns_res_open (&res);
+ if (err)
+ goto leave;
+
+ if (dns_d_anchor (host, sizeof host, name, strlen (name)) >= sizeof host)
+ {
+ err = gpg_error (GPG_ERR_ENAMETOOLONG);
+ goto leave;
+ }
+
+ err = libdns_res_submit (res, name, DNS_T_SRV, DNS_C_IN);
+ if (err)
+ goto leave;
+
+ err = libdns_res_wait (res);
+ if (err)
+ goto leave;
+
+ ans = dns_res_fetch (res, &derr);
+ if (!ans)
+ {
+ err = libdns_error_to_gpg_error (derr);
+ goto leave;
+ }
+
+ /* Check the rcode. */
+ switch (dns_p_rcode (ans))
+ {
+ case DNS_RC_NOERROR: break;
+ case DNS_RC_NXDOMAIN: err = gpg_error (GPG_ERR_NO_NAME); break;
+ default: err = GPG_ERR_SERVER_FAILED; break;
+ }
+ if (err)
+ goto leave;
+
+ memset (&rri, 0, sizeof rri);
+ dns_rr_i_init (&rri, ans);
+ rri.section = DNS_S_ALL & ~DNS_S_QD;
+ rri.name = host;
+ rri.type = DNS_T_SRV;
+
+ while (dns_rr_grep (&rr, 1, &rri, ans, &derr))
+ {
+ struct dns_srv dsrv;
+ struct srventry *srv;
+ struct srventry *newlist;
+
+ err = libdns_error_to_gpg_error (dns_srv_parse(&dsrv, &rr, ans));
+ if (err)
+ goto leave;
+
+ newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
+ if (!newlist)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ *list = newlist;
+ memset (&(*list)[srvcount], 0, sizeof(struct srventry));
+ srv = &(*list)[srvcount];
+ srvcount++;
+ srv->priority = dsrv.priority;
+ srv->weight = dsrv.weight;
+ srv->port = dsrv.port;
+ mem2str (srv->target, dsrv.target, sizeof srv->target);
+ }
+
+ *r_count = srvcount;
+
+ leave:
+ if (err)
+ {
+ xfree (*list);
+ *list = NULL;
+ }
+ dns_free (ans);
+ dns_res_close (res);
+ return err;
+}
+#endif /*USE_LIBDNS*/
+
+
+/* Standard resolver based helper for getsrv. Note that it is
+ * expected that NULL is stored at the address of LIST and 0 is stored
+ * at the address of R_COUNT. */
+static gpg_error_t
+getsrv_standard (const char *name,
+ struct srventry **list, unsigned int *r_count)
+{
+#ifdef HAVE_SYSTEM_RESOLVER
+ union {
+ unsigned char ans[2048];
+ HEADER header[1];
+ } res;
+ unsigned char *answer = res.ans;
+ HEADER *header = res.header;
+ unsigned char *pt, *emsg;
+ int r, rc;
+ u16 dlen;
+ unsigned int srvcount = 0;
u16 count;
- int i, rc;
+ /* Do not allow a query using the standard resolver in Tor mode. */
+ if (tor_mode)
+ return gpg_error (GPG_ERR_NOT_ENABLED);
+
+ my_unprotect ();
+ r = res_query (name, C_IN, T_SRV, answer, sizeof res.ans);
+ my_protect ();
+ if (r < 0)
+ return get_h_errno_as_gpg_error ();
+ if (r < sizeof (HEADER))
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+ if (r > sizeof res.ans)
+ return gpg_error (GPG_ERR_SYSTEM_BUG);
+ if (header->rcode != NOERROR || !(count=ntohs (header->ancount)))
+ return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found. */
+
+ emsg = &answer[r];
+ pt = &answer[sizeof(HEADER)];
+
+ /* Skip over the query */
+ rc = dn_skipname (pt, emsg);
+ if (rc == -1)
+ goto fail;
+
+ pt += rc + QFIXEDSZ;
+
+ while (count-- > 0 && pt < emsg)
+ {
+ struct srventry *srv;
+ u16 type, class;
+ struct srventry *newlist;
+
+ newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
+ if (!newlist)
+ goto fail;
+ *list = newlist;
+ memset (&(*list)[srvcount], 0, sizeof(struct srventry));
+ srv = &(*list)[srvcount];
+ srvcount++;
+
+ rc = dn_skipname (pt, emsg); /* The name we just queried for. */
+ if (rc == -1)
+ goto fail;
+ pt += rc;
+
+ /* Truncated message? */
+ if ((emsg-pt) < 16)
+ goto fail;
+
+ type = buf16_to_u16 (pt);
+ pt += 2;
+ /* We asked for SRV and got something else !? */
+ if (type != T_SRV)
+ goto fail;
+
+ class = buf16_to_u16 (pt);
+ pt += 2;
+ /* We asked for IN and got something else !? */
+ if (class != C_IN)
+ goto fail;
+
+ pt += 4; /* ttl */
+ dlen = buf16_to_u16 (pt);
+ pt += 2;
+
+ srv->priority = buf16_to_ushort (pt);
+ pt += 2;
+ srv->weight = buf16_to_ushort (pt);
+ pt += 2;
+ srv->port = buf16_to_ushort (pt);
+ pt += 2;
+
+ /* Get the name. 2782 doesn't allow name compression, but
+ * dn_expand still works to pull the name out of the packet. */
+ rc = dn_expand (answer, emsg, pt, srv->target, sizeof srv->target);
+ if (rc == 1 && srv->target[0] == 0) /* "." */
+ {
+ xfree(*list);
+ *list = NULL;
+ return 0;
+ }
+ if (rc == -1)
+ goto fail;
+ pt += rc;
+ /* Corrupt packet? */
+ if (dlen != rc+6)
+ goto fail;
+ }
+
+ *r_count = srvcount;
+ return 0;
+
+ fail:
+ xfree (*list);
*list = NULL;
+ return gpg_error (GPG_ERR_GENERAL);
-#ifdef USE_ADNS
- {
- adns_state state;
- adns_answer *answer = NULL;
+#else /*!HAVE_SYSTEM_RESOLVER*/
- if (my_adns_init (&state))
- return -1;
+ (void)name;
+ (void)list;
+ (void)r_count;
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
- my_unprotect ();
- rc = adns_synchronous (state, name, adns_r_srv, adns_qf_quoteok_query,
- &answer);
- my_protect ();
- if (rc)
- {
- log_error ("DNS query failed: %s\n", strerror (rc));
- adns_finish (state);
- return -1;
- }
- if (answer->status != adns_s_ok
- || answer->type != adns_r_srv || !answer->nrrs)
- {
- log_error ("DNS query returned an error or no records: %s (%s)\n",
- adns_strerror (answer->status),
- adns_errabbrev (answer->status));
- adns_free (answer);
- adns_finish (state);
- return 0;
- }
+#endif /*!HAVE_SYSTEM_RESOLVER*/
+}
- for (count = 0; count < answer->nrrs; count++)
- {
- struct srventry *srv = NULL;
- struct srventry *newlist;
-
- if (strlen (answer->rrs.srvha[count].ha.host) >= sizeof srv->target)
- {
- log_info ("hostname in SRV record too long - skipped\n");
- continue;
- }
-
- newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
- if (!newlist)
- goto fail;
- *list = newlist;
- memset (&(*list)[srvcount], 0, sizeof(struct srventry));
- srv = &(*list)[srvcount];
- srvcount++;
-
- srv->priority = answer->rrs.srvha[count].priority;
- srv->weight = answer->rrs.srvha[count].weight;
- srv->port = answer->rrs.srvha[count].port;
- strcpy (srv->target, answer->rrs.srvha[count].ha.host);
- }
- adns_free (answer);
- adns_finish (state);
- }
-#else /*!USE_ADNS*/
- {
- union {
- unsigned char ans[2048];
- HEADER header[1];
- } res;
- unsigned char *answer = res.ans;
- HEADER *header = res.header;
- unsigned char *pt, *emsg;
- int r;
- u16 dlen;
-
- /* Do not allow a query using the standard resolver in Tor mode. */
- if (tor_mode)
- return -1;
-
- my_unprotect ();
- r = res_query (name, C_IN, T_SRV, answer, sizeof answer);
- my_protect ();
- if (r < sizeof (HEADER) || r > sizeof answer
- || header->rcode != NOERROR || !(count=ntohs (header->ancount)))
- return 0; /* Error or no record found. */
-
- emsg = &answer[r];
- pt = &answer[sizeof(HEADER)];
-
- /* Skip over the query */
- rc = dn_skipname (pt, emsg);
- if (rc == -1)
- goto fail;
-
- pt += rc + QFIXEDSZ;
-
- while (count-- > 0 && pt < emsg)
- {
- struct srventry *srv=NULL;
- u16 type,class;
- struct srventry *newlist;
-
- newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
- if (!newlist)
- goto fail;
- *list = newlist;
- memset(&(*list)[srvcount],0,sizeof(struct srventry));
- srv=&(*list)[srvcount];
- srvcount++;
-
- rc = dn_skipname(pt,emsg); /* the name we just queried for */
- if (rc == -1)
- goto fail;
- pt+=rc;
-
- /* Truncated message? */
- if((emsg-pt)<16)
- goto fail;
-
- type = buf16_to_u16 (pt);
- pt += 2;
- /* We asked for SRV and got something else !? */
- if(type!=T_SRV)
- goto fail;
-
- class = buf16_to_u16 (pt);
- pt += 2;
- /* We asked for IN and got something else !? */
- if(class!=C_IN)
- goto fail;
-
- pt += 4; /* ttl */
- dlen = buf16_to_u16 (pt);
- pt += 2;
-
- srv->priority = buf16_to_ushort (pt);
- pt += 2;
- srv->weight = buf16_to_ushort (pt);
- pt += 2;
- srv->port = buf16_to_ushort (pt);
- pt += 2;
-
- /* Get the name. 2782 doesn't allow name compression, but
- dn_expand still works to pull the name out of the
- packet. */
- rc = dn_expand(answer,emsg,pt,srv->target, sizeof srv->target);
- if (rc == 1 && srv->target[0] == 0) /* "." */
- {
- xfree(*list);
- *list = NULL;
- return 0;
- }
- if (rc == -1)
- goto fail;
- pt += rc;
- /* Corrupt packet? */
- if (dlen != rc+6)
- goto fail;
- }
- }
-#endif /*!USE_ADNS*/
+/* Note that we do not return NONAME but simply store 0 at R_COUNT. */
+gpg_error_t
+get_dns_srv (const char *name, struct srventry **list, unsigned int *r_count)
+{
+ gpg_error_t err;
+ unsigned int srvcount;
+ int i;
+
+ *list = NULL;
+ *r_count = 0;
+ srvcount = 0;
+#ifdef USE_LIBDNS
+ if (!standard_resolver)
+ {
+ err = getsrv_libdns (name, list, &srvcount);
+ if (err && libdns_switch_port_p (err))
+ err = getsrv_libdns (name, list, &srvcount);
+ }
+ else
+#endif /*USE_LIBDNS*/
+ err = getsrv_standard (name, list, &srvcount);
+
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_NO_NAME)
+ err = 0;
+ goto leave;
+ }
/* Now we have an array of all the srv records. */
}
}
- return srvcount;
+ leave:
+ if (opt_debug)
+ {
+ if (err)
+ log_debug ("dns: getsrv(%s): %s\n", name, gpg_strerror (err));
+ else
+ log_debug ("dns: getsrv(%s) -> %u records\n", name, srvcount);
+ }
+ if (!err)
+ *r_count = srvcount;
+ return err;
+}
+
- fail:
- xfree(*list);
- *list=NULL;
- return -1;
+\f
+#ifdef USE_LIBDNS
+/* libdns version of get_dns_cname. */
+gpg_error_t
+get_dns_cname_libdns (const char *name, char **r_cname)
+{
+ gpg_error_t err;
+ struct dns_resolver *res;
+ struct dns_packet *ans = NULL;
+ struct dns_cname cname;
+ int derr;
+
+ err = libdns_res_open (&res);
+ if (err)
+ goto leave;
+
+ err = libdns_res_submit (res, name, DNS_T_CNAME, DNS_C_IN);
+ if (err)
+ goto leave;
+
+ err = libdns_res_wait (res);
+ if (err)
+ goto leave;
+
+ ans = dns_res_fetch (res, &derr);
+ if (!ans)
+ {
+ err = libdns_error_to_gpg_error (derr);
+ goto leave;
+ }
+
+ /* Check the rcode. */
+ switch (dns_p_rcode (ans))
+ {
+ case DNS_RC_NOERROR: break;
+ case DNS_RC_NXDOMAIN: err = gpg_error (GPG_ERR_NO_NAME); break;
+ default: err = GPG_ERR_SERVER_FAILED; break;
+ }
+ if (err)
+ goto leave;
+
+ /* Parse the result into CNAME. */
+ err = libdns_error_to_gpg_error (dns_p_study (ans));
+ if (err)
+ goto leave;
+
+ if (!dns_d_cname (&cname, sizeof cname, name, strlen (name), ans, &derr))
+ {
+ err = libdns_error_to_gpg_error (derr);
+ goto leave;
+ }
+
+ /* Copy result. */
+ *r_cname = xtrystrdup (cname.host);
+ if (!*r_cname)
+ err = gpg_error_from_syserror ();
+
+ leave:
+ dns_free (ans);
+ dns_res_close (res);
+ return err;
}
-#endif /*USE_DNS_SRV*/
+#endif /*USE_LIBDNS*/
+/* Standard resolver version of get_dns_cname. */
gpg_error_t
-get_dns_cname (const char *name, char **r_cname)
+get_dns_cname_standard (const char *name, char **r_cname)
{
+#ifdef HAVE_SYSTEM_RESOLVER
gpg_error_t err;
int rc;
+ union {
+ unsigned char ans[2048];
+ HEADER header[1];
+ } res;
+ unsigned char *answer = res.ans;
+ HEADER *header = res.header;
+ unsigned char *pt, *emsg;
+ int r;
+ char *cname;
+ int cnamesize = 1025;
+ u16 count;
- *r_cname = NULL;
-
-#ifdef USE_ADNS
- {
- adns_state state;
- adns_answer *answer = NULL;
+ /* Do not allow a query using the standard resolver in Tor mode. */
+ if (tor_mode)
+ return -1;
- if (my_adns_init (&state))
- return gpg_error (GPG_ERR_GENERAL);
+ my_unprotect ();
+ r = res_query (name, C_IN, T_CERT, answer, sizeof res.ans);
+ my_protect ();
+ if (r < 0)
+ return get_h_errno_as_gpg_error ();
+ if (r < sizeof (HEADER))
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+ if (r > sizeof res.ans)
+ return gpg_error (GPG_ERR_SYSTEM_BUG);
+ if (header->rcode != NOERROR || !(count=ntohs (header->ancount)))
+ return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found. */
+ if (count != 1)
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+
+ emsg = &answer[r];
+ pt = &answer[sizeof(HEADER)];
+ rc = dn_skipname (pt, emsg);
+ if (rc == -1)
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+
+ pt += rc + QFIXEDSZ;
+ if (pt >= emsg)
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+
+ rc = dn_skipname (pt, emsg);
+ if (rc == -1)
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+ pt += rc + 2 + 2 + 4;
+ if (pt+2 >= emsg)
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+ pt += 2; /* Skip rdlen */
+
+ cname = xtrymalloc (cnamesize);
+ if (!cname)
+ return gpg_error_from_syserror ();
- my_unprotect ();
- rc = adns_synchronous (state, name, adns_r_cname, adns_qf_quoteok_query,
- &answer);
- my_protect ();
- if (rc)
- {
- err = gpg_error (gpg_err_code_from_errno (rc));
- log_error ("DNS query failed: %s\n", gpg_strerror (err));
- adns_finish (state);
- return err;
- }
- if (answer->status != adns_s_ok
- || answer->type != adns_r_cname || answer->nrrs != 1)
- {
- err = map_adns_status_to_gpg_error (answer->status);
- log_error ("DNS query returned an error or no records: %s (%s)\n",
- adns_strerror (answer->status),
- adns_errabbrev (answer->status));
- adns_free (answer);
- adns_finish (state);
- return err;
- }
- *r_cname = xtrystrdup (answer->rrs.str[0]);
- if (!*r_cname)
+ rc = dn_expand (answer, emsg, pt, cname, cnamesize -1);
+ if (rc == -1)
+ {
+ xfree (cname);
+ return gpg_error (GPG_ERR_SERVER_FAILED);
+ }
+ *r_cname = xtryrealloc (cname, strlen (cname)+1);
+ if (!*r_cname)
+ {
err = gpg_error_from_syserror ();
- else
- err = 0;
+ xfree (cname);
+ return err;
+ }
+ return 0;
- adns_free (answer);
- adns_finish (state);
- return err;
- }
-#else /*!USE_ADNS*/
- {
- union {
- unsigned char ans[2048];
- HEADER header[1];
- } res;
- unsigned char *answer = res.ans;
- HEADER *header = res.header;
- unsigned char *pt, *emsg;
- int r;
- char *cname;
- int cnamesize = 1025;
- u16 count;
-
- /* Do not allow a query using the standard resolver in Tor mode. */
- if (tor_mode)
- return -1;
-
- r = res_query (name, C_IN, T_CERT, answer, sizeof answer);
- if (r < sizeof (HEADER) || r > sizeof answer)
- return gpg_error (GPG_ERR_SERVER_FAILED);
- if (header->rcode != NOERROR || !(count=ntohs (header->ancount)))
- return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found. */
- if (count != 1)
- return gpg_error (GPG_ERR_SERVER_FAILED);
+#else /*!HAVE_SYSTEM_RESOLVER*/
- emsg = &answer[r];
- pt = &answer[sizeof(HEADER)];
- rc = dn_skipname (pt, emsg);
- if (rc == -1)
- return gpg_error (GPG_ERR_SERVER_FAILED);
+ (void)name;
+ (void)r_cname;
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- pt += rc + QFIXEDSZ;
- if (pt >= emsg)
- return gpg_error (GPG_ERR_SERVER_FAILED);
+#endif /*!HAVE_SYSTEM_RESOLVER*/
+}
- rc = dn_skipname (pt, emsg);
- if (rc == -1)
- return gpg_error (GPG_ERR_SERVER_FAILED);
- pt += rc + 2 + 2 + 4;
- if (pt+2 >= emsg)
- return gpg_error (GPG_ERR_SERVER_FAILED);
- pt += 2; /* Skip rdlen */
- cname = xtrymalloc (cnamesize);
- if (!cname)
- return gpg_error_from_syserror ();
+gpg_error_t
+get_dns_cname (const char *name, char **r_cname)
+{
+ gpg_error_t err;
+
+ *r_cname = NULL;
- rc = dn_expand (answer, emsg, pt, cname, cnamesize -1);
- if (rc == -1)
- {
- xfree (cname);
- return gpg_error (GPG_ERR_SERVER_FAILED);
- }
- *r_cname = xtryrealloc (cname, strlen (cname)+1);
- if (!*r_cname)
- {
- err = gpg_error_from_syserror ();
- xfree (cname);
- return err;
- }
- return 0;
- }
-#endif /*!USE_ADNS*/
+#ifdef USE_LIBDNS
+ if (!standard_resolver)
+ {
+ err = get_dns_cname_libdns (name, r_cname);
+ if (err && libdns_switch_port_p (err))
+ err = get_dns_cname_libdns (name, r_cname);
+ return err;
+ }
+#endif /*USE_LIBDNS*/
+
+ err = get_dns_cname_standard (name, r_cname);
+ if (opt_debug)
+ log_debug ("get_dns_cname(%s)%s%s\n", name,
+ err ? ": " : " -> ",
+ err ? gpg_strerror (err) : *r_cname);
+ return err;
}
};
+/* Set verbosity and debug mode for this module. */
+void set_dns_verbose (int verbose, int debug);
+
+/* Set the timeout for libdns requests to SECONDS. */
+void set_dns_timeout (int seconds);
+
+/* Calling this function with YES set to True forces the use of the
+ * standard resolver even if dirmngr has been built with support for
+ * an alternative resolver. */
+void enable_standard_resolver (int yes);
+
+/* Return true if the standard resolver is used. */
+int standard_resolver_p (void);
+
+/* Calling this function with YES switches libdns into recursive mode.
+ * It has no effect on the standard resolver. */
+void enable_recursive_resolver (int yes);
+
+/* Return true iff the recursive resolver is used. */
+int recursive_resolver_p (void);
+
/* Calling this function switches the DNS code into Tor mode if
possibe. Return 0 on success. */
gpg_error_t enable_dns_tormode (int new_circuit);
next DNS query. Note that this is only used in Tor mode. */
void set_dns_nameserver (const char *ipaddr);
+/* SIGHUP action handler for this module. */
+void reload_dns_stuff (int force);
void free_dns_addrinfo (dns_addrinfo_t ai);
unsigned char **r_fpr, size_t *r_fprlen,
char **r_url);
-
-int getsrv (const char *name,struct srventry **list);
+/* Return an array of SRV records. */
+gpg_error_t get_dns_srv (const char *name,
+ struct srventry **list, unsigned int *r_count);
#endif /*GNUPG_DIRMNGR_DNS_STUFF_H*/
--- /dev/null
+/* ==========================================================================
+ * dns.c - Recursive, Reentrant DNS Resolver.
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#if HAVE_CONFIG_H
+#include "config.h"
+#elif !defined _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <limits.h> /* INT_MAX */
+#include <stdarg.h> /* va_list va_start va_end */
+#include <stddef.h> /* offsetof() */
+#ifdef _WIN32
+/* JW: This breaks our mingw build: #define uint32_t unsigned int */
+#else
+#include <stdint.h> /* uint32_t */
+#endif
+#include <stdlib.h> /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */
+#include <stdio.h> /* FILE fopen(3) fclose(3) getc(3) rewind(3) vsnprintf(3) */
+#include <string.h> /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */
+#include <strings.h> /* strcasecmp(3) strncasecmp(3) */
+#include <ctype.h> /* isspace(3) isdigit(3) */
+#include <time.h> /* time_t time(2) difftime(3) */
+#include <signal.h> /* SIGPIPE sigemptyset(3) sigaddset(3) sigpending(2) sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) */
+#include <errno.h> /* errno EINVAL ENOENT */
+#undef NDEBUG
+#include <assert.h> /* assert(3) */
+
+#if _WIN32
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 1024
+#endif
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/time.h> /* gettimeofday(2) */
+#include <sys/types.h> /* FD_SETSIZE socklen_t */
+#include <sys/select.h> /* FD_ZERO FD_SET fd_set select(2) */
+#include <sys/socket.h> /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */
+#if defined(AF_UNIX)
+#include <sys/un.h> /* struct sockaddr_un */
+#endif
+#include <fcntl.h> /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */
+#include <unistd.h> /* _POSIX_THREADS gethostname(3) close(2) */
+#include <poll.h> /* POLLIN POLLOUT */
+#include <netinet/in.h> /* struct sockaddr_in struct sockaddr_in6 */
+#include <arpa/inet.h> /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */
+#include <netdb.h> /* struct addrinfo */
+#endif
+
+#include "dns.h"
+
+
+/*
+ * C O M P I L E R V E R S I O N & F E A T U R E D E T E C T I O N
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p))
+#define DNS_GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && DNS_GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= DNS_GNUC_2VER((M), (m), (p)))
+
+#define DNS_MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p))
+#define DNS_MSC_PREREQ(M, m, p) (_MSC_VER_FULL > 0 && _MSC_VER_FULL >= DNS_MSC_2VER((M), (m), (p)))
+
+#define DNS_SUNPRO_PREREQ(M, m, p) (__SUNPRO_C > 0 && __SUNPRO_C >= 0x ## M ## m ## p)
+
+#if defined __has_builtin
+#define dns_has_builtin(x) __has_builtin(x)
+#else
+#define dns_has_builtin(x) 0
+#endif
+
+#if defined __has_extension
+#define dns_has_extension(x) __has_extension(x)
+#else
+#define dns_has_extension(x) 0
+#endif
+
+#ifndef HAVE___ASSUME
+#define HAVE___ASSUME DNS_MSC_PREREQ(8,0,0)
+#endif
+
+#ifndef HAVE___BUILTIN_TYPES_COMPATIBLE_P
+#define HAVE___BUILTIN_TYPES_COMPATIBLE_P (DNS_GNUC_PREREQ(3,1,1) || __clang__)
+#endif
+
+#ifndef HAVE___BUILTIN_UNREACHABLE
+#define HAVE___BUILTIN_UNREACHABLE (DNS_GNUC_PREREQ(4,5,0) || dns_has_builtin(__builtin_unreachable))
+#endif
+
+#ifndef HAVE_PRAGMA_MESSAGE
+#define HAVE_PRAGMA_MESSAGE (DNS_GNUC_PREREQ(4,4,0) || __clang__ || _MSC_VER)
+#endif
+
+
+/*
+ * C O M P I L E R A N N O T A T I O N S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if __GNUC__
+#define DNS_NOTUSED __attribute__((unused))
+#define DNS_NORETURN __attribute__((noreturn))
+#else
+#define DNS_NOTUSED
+#define DNS_NORETURN
+#endif
+
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wmissing-field-initializers"
+#elif DNS_GNUC_PREREQ(4,6,0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+
+/*
+ * S T A N D A R D M A C R O S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if HAVE___BUILTIN_TYPES_COMPATIBLE_P
+#define dns_same_type(a, b, def) __builtin_types_compatible_p(__typeof__ (a), __typeof__ (b))
+#else
+#define dns_same_type(a, b, def) (def)
+#endif
+#define dns_isarray(a) (!dns_same_type((a), (&(a)[0]), 0))
+/* NB: "_" field silences Sun Studio "zero-sized struct/union" error diagnostic */
+#define dns_inline_assert(cond) ((void)(sizeof (struct { int:-!(cond); int _; })))
+
+#if HAVE___ASSUME
+#define dns_assume(cond) __assume(cond)
+#elif HAVE___BUILTIN_UNREACHABLE
+#define dns_assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
+#else
+#define dns_assume(cond) do { (void)(cond); } while (0)
+#endif
+
+#ifndef lengthof
+#define lengthof(a) (dns_inline_assert(dns_isarray(a)), (sizeof (a) / sizeof (a)[0]))
+#endif
+
+#ifndef endof
+#define endof(a) (dns_inline_assert(dns_isarray(a)), &(a)[lengthof((a))])
+#endif
+
+
+/*
+ * M I S C E L L A N E O U S C O M P A T
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if _WIN32 || _WIN64
+#define PRIuZ "Iu"
+#else
+#define PRIuZ "zu"
+#endif
+
+#ifndef DNS_THREAD_SAFE
+#if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0
+#define DNS_THREAD_SAFE 1
+#else
+#define DNS_THREAD_SAFE 0
+#endif
+#endif
+
+#ifndef HAVE__STATIC_ASSERT
+#define HAVE__STATIC_ASSERT \
+ (dns_has_extension(c_static_assert) || DNS_GNUC_PREREQ(4,6,0) || \
+ __C11FEATURES__ || __STDC_VERSION__ >= 201112L)
+#endif
+
+#ifndef HAVE_STATIC_ASSERT
+#if DNS_GNUC_PREREQ(0,0,0) && !DNS_GNUC_PREREQ(4,6,0)
+#define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */
+#else
+#define HAVE_STATIC_ASSERT (defined static_assert)
+#endif
+#endif
+
+#if HAVE_STATIC_ASSERT
+#define dns_static_assert(cond, msg) static_assert(cond, msg)
+#elif HAVE__STATIC_ASSERT
+#define dns_static_assert(cond, msg) _Static_assert(cond, msg)
+#else
+#define dns_static_assert(cond, msg) extern char DNS_PP_XPASTE(dns_assert_, __LINE__)[sizeof (int[1 - 2*!(cond)])]
+#endif
+
+
+/*
+ * D E B U G M A C R O S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int *dns_debug_p(void) {
+ static int debug;
+
+ return &debug;
+} /* dns_debug_p() */
+
+#if DNS_DEBUG
+
+#undef DNS_DEBUG
+#define DNS_DEBUG dns_debug
+
+#define DNS_SAY_(fmt, ...) \
+ do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0)
+#define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n")
+#define DNS_HAI DNS_SAY("HAI")
+
+#define DNS_SHOW_(P, fmt, ...) do { \
+ if (DNS_DEBUG > 1) { \
+ fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \
+ fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \
+ dns_p_dump((P), stderr); \
+ fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \
+ } \
+} while (0)
+
+#define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "")
+
+#else /* !DNS_DEBUG */
+
+#undef DNS_DEBUG
+#define DNS_DEBUG 0
+
+#define DNS_SAY(...)
+#define DNS_HAI
+#define DNS_SHOW(...)
+
+#endif /* DNS_DEBUG */
+
+#define DNS_CARP(...) DNS_SAY(__VA_ARGS__)
+
+
+/*
+ * V E R S I O N R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+const char *dns_vendor(void) {
+ return DNS_VENDOR;
+} /* dns_vendor() */
+
+
+int dns_v_rel(void) {
+ return DNS_V_REL;
+} /* dns_v_rel() */
+
+
+int dns_v_abi(void) {
+ return DNS_V_ABI;
+} /* dns_v_abi() */
+
+
+int dns_v_api(void) {
+ return DNS_V_API;
+} /* dns_v_api() */
+
+
+/*
+ * E R R O R R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if _WIN32
+
+#define DNS_EINTR WSAEINTR
+#define DNS_EINPROGRESS WSAEINPROGRESS
+#define DNS_EISCONN WSAEISCONN
+#define DNS_EWOULDBLOCK WSAEWOULDBLOCK
+#define DNS_EALREADY WSAEALREADY
+#define DNS_EAGAIN EAGAIN
+#define DNS_ETIMEDOUT WSAETIMEDOUT
+
+#define dns_syerr() ((int)GetLastError())
+#define dns_soerr() ((int)WSAGetLastError())
+
+#else
+
+#define DNS_EINTR EINTR
+#define DNS_EINPROGRESS EINPROGRESS
+#define DNS_EISCONN EISCONN
+#define DNS_EWOULDBLOCK EWOULDBLOCK
+#define DNS_EALREADY EALREADY
+#define DNS_EAGAIN EAGAIN
+#define DNS_ETIMEDOUT ETIMEDOUT
+
+#define dns_syerr() errno
+#define dns_soerr() errno
+
+#endif
+
+
+const char *dns_strerror(int error) {
+ switch (error) {
+ case DNS_ENOBUFS:
+ return "DNS packet buffer too small";
+ case DNS_EILLEGAL:
+ return "Illegal DNS RR name or data";
+ case DNS_EORDER:
+ return "Attempt to push RR out of section order";
+ case DNS_ESECTION:
+ return "Invalid section specified";
+ case DNS_EUNKNOWN:
+ return "Unknown DNS error";
+ case DNS_EADDRESS:
+ return "Invalid textual address form";
+ case DNS_ENOQUERY:
+ return "Bad execution state (missing query packet)";
+ case DNS_ENOANSWER:
+ return "Bad execution state (missing answer packet)";
+ case DNS_EFETCHED:
+ return "Answer already fetched";
+ case DNS_ESERVICE:
+ return "The service passed was not recognized for the specified socket type";
+ case DNS_ENONAME:
+ return "The name does not resolve for the supplied parameters";
+ case DNS_EFAIL:
+ return "A non-recoverable error occurred when attempting to resolve the name";
+ case DNS_ECONNFIN:
+ return "Connection closed";
+ case DNS_EVERIFY:
+ return "Reply failed verification";
+ default:
+ return strerror(error);
+ } /* switch() */
+} /* dns_strerror() */
+
+
+/*
+ * A T O M I C R O U T I N E S
+ *
+ * Use GCC's __atomic built-ins if possible. Unlike the __sync built-ins, we
+ * can use the preprocessor to detect API and, more importantly, ISA
+ * support. We want to avoid linking headaches where the API depends on an
+ * external library if the ISA (e.g. i386) doesn't support lockless
+ * operation.
+ *
+ * TODO: Support C11's atomic API. Although that may require some finesse
+ * with how we define some public types, such as dns_atomic_t and struct
+ * dns_resolv_conf.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef HAVE___ATOMIC_FETCH_ADD
+#define HAVE___ATOMIC_FETCH_ADD (defined __ATOMIC_RELAXED)
+#endif
+
+#ifndef HAVE___ATOMIC_FETCH_SUB
+#define HAVE___ATOMIC_FETCH_SUB HAVE___ATOMIC_FETCH_ADD
+#endif
+
+#ifndef DNS_ATOMIC_FETCH_ADD
+#if HAVE___ATOMIC_FETCH_ADD && __GCC_ATOMIC_LONG_LOCK_FREE == 2
+#define DNS_ATOMIC_FETCH_ADD(i) __atomic_fetch_add((i), 1, __ATOMIC_RELAXED)
+#else
+#pragma message("no atomic_fetch_add available")
+#define DNS_ATOMIC_FETCH_ADD(i) ((*(i))++)
+#endif
+#endif
+
+#ifndef DNS_ATOMIC_FETCH_SUB
+#if HAVE___ATOMIC_FETCH_SUB && __GCC_ATOMIC_LONG_LOCK_FREE == 2
+#define DNS_ATOMIC_FETCH_SUB(i) __atomic_fetch_sub((i), 1, __ATOMIC_RELAXED)
+#else
+#pragma message("no atomic_fetch_sub available")
+#define DNS_ATOMIC_FETCH_SUB(i) ((*(i))--)
+#endif
+#endif
+
+static inline unsigned dns_atomic_fetch_add(dns_atomic_t *i) {
+ return DNS_ATOMIC_FETCH_ADD(i);
+} /* dns_atomic_fetch_add() */
+
+
+static inline unsigned dns_atomic_fetch_sub(dns_atomic_t *i) {
+ return DNS_ATOMIC_FETCH_SUB(i);
+} /* dns_atomic_fetch_sub() */
+
+
+/*
+ * C R Y P T O R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * P R N G
+ */
+
+#ifndef DNS_RANDOM
+#if defined(HAVE_ARC4RANDOM) \
+ || defined(__OpenBSD__) \
+ || defined(__FreeBSD__) \
+ || defined(__NetBSD__) \
+ || defined(__APPLE__)
+#define DNS_RANDOM arc4random
+#elif __linux
+#define DNS_RANDOM random
+#else
+#define DNS_RANDOM rand
+#endif
+#endif
+
+#define DNS_RANDOM_arc4random 1
+#define DNS_RANDOM_random 2
+#define DNS_RANDOM_rand 3
+#define DNS_RANDOM_RAND_bytes 4
+
+#define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM))
+
+#if DNS_RANDOM_OPENSSL
+#include <openssl/rand.h>
+#endif
+
+static unsigned dns_random_(void) {
+#if DNS_RANDOM_OPENSSL
+ unsigned r;
+ _Bool ok;
+
+ ok = (1 == RAND_bytes((unsigned char *)&r, sizeof r));
+ assert(ok && "1 == RAND_bytes()");
+
+ return r;
+#else
+ return DNS_RANDOM();
+#endif
+} /* dns_random_() */
+
+dns_random_f **dns_random_p(void) {
+ static dns_random_f *random_f = &dns_random_;
+
+ return &random_f;
+} /* dns_random_p() */
+
+
+/*
+ * P E R M U T A T I O N G E N E R A T O R
+ */
+
+#define DNS_K_TEA_KEY_SIZE 16
+#define DNS_K_TEA_BLOCK_SIZE 8
+#define DNS_K_TEA_CYCLES 32
+#define DNS_K_TEA_MAGIC 0x9E3779B9U
+
+struct dns_k_tea {
+ uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)];
+ unsigned cycles;
+}; /* struct dns_k_tea */
+
+
+static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) {
+ memcpy(tea->key, key, sizeof tea->key);
+
+ tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES;
+} /* dns_k_tea_init() */
+
+
+static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) {
+ uint32_t y, z, sum, n;
+
+ y = v[0];
+ z = v[1];
+ sum = 0;
+
+ for (n = 0; n < tea->cycles; n++) {
+ sum += DNS_K_TEA_MAGIC;
+ y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]);
+ z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]);
+ }
+
+ w[0] = y;
+ w[1] = z;
+
+ return /* void */;
+} /* dns_k_tea_encrypt() */
+
+
+/*
+ * Permutation generator, based on a Luby-Rackoff Feistel construction.
+ *
+ * Specifically, this is a generic balanced Feistel block cipher using TEA
+ * (another block cipher) as the pseudo-random function, F. At best it's as
+ * strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or
+ * perhaps Bernstein's Salsa20 core; I am naively trying to keep things
+ * simple.
+ *
+ * The generator can create a permutation of any set of numbers, as long as
+ * the size of the set is an even power of 2. This limitation arises either
+ * out of an inherent property of balanced Feistel constructions, or by my
+ * own ignorance. I'll tackle an unbalanced construction after I wrap my
+ * head around Schneier and Kelsey's paper.
+ *
+ * CAVEAT EMPTOR. IANAC.
+ */
+#define DNS_K_PERMUTOR_ROUNDS 8
+
+struct dns_k_permutor {
+ unsigned stepi, length, limit;
+ unsigned shift, mask, rounds;
+
+ struct dns_k_tea tea;
+}; /* struct dns_k_permutor */
+
+
+static inline unsigned dns_k_permutor_powof(unsigned n) {
+ unsigned m, i = 0;
+
+ for (m = 1; m < n; m <<= 1, i++)
+ ;;
+
+ return i;
+} /* dns_k_permutor_powof() */
+
+static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) {
+ uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)];
+ unsigned width, i;
+
+ p->stepi = 0;
+
+ p->length = (high - low) + 1;
+ p->limit = high;
+
+ width = dns_k_permutor_powof(p->length);
+ width += width % 2;
+
+ p->shift = width / 2;
+ p->mask = (1U << p->shift) - 1;
+ p->rounds = DNS_K_PERMUTOR_ROUNDS;
+
+ for (i = 0; i < lengthof(key); i++)
+ key[i] = dns_random();
+
+ dns_k_tea_init(&p->tea, key, 0);
+
+ return /* void */;
+} /* dns_k_permutor_init() */
+
+
+static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) {
+ uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)];
+
+ memset(in, '\0', sizeof in);
+
+ in[0] = k;
+ in[1] = x;
+
+ dns_k_tea_encrypt(&p->tea, in, out);
+
+ return p->mask & out[0];
+} /* dns_k_permutor_F() */
+
+
+static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) {
+ unsigned l[2], r[2];
+ unsigned i;
+
+ i = 0;
+ l[i] = p->mask & (n >> p->shift);
+ r[i] = p->mask & (n >> 0);
+
+ do {
+ l[(i + 1) % 2] = r[i % 2];
+ r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]);
+
+ i++;
+ } while (i < p->rounds - 1);
+
+ return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0);
+} /* dns_k_permutor_E() */
+
+
+DNS_NOTUSED static unsigned dns_k_permutor_D(struct dns_k_permutor *p, unsigned n) {
+ unsigned l[2], r[2];
+ unsigned i;
+
+ i = p->rounds - 1;
+ l[i % 2] = p->mask & (n >> p->shift);
+ r[i % 2] = p->mask & (n >> 0);
+
+ do {
+ i--;
+
+ r[i % 2] = l[(i + 1) % 2];
+ l[i % 2] = r[(i + 1) % 2] ^ dns_k_permutor_F(p, i, l[(i + 1) % 2]);
+ } while (i > 0);
+
+ return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0);
+} /* dns_k_permutor_D() */
+
+
+static unsigned dns_k_permutor_step(struct dns_k_permutor *p) {
+ unsigned n;
+
+ do {
+ n = dns_k_permutor_E(p, p->stepi++);
+ } while (n >= p->length);
+
+ return n + (p->limit + 1 - p->length);
+} /* dns_k_permutor_step() */
+
+
+/*
+ * Simple permutation box. Useful for shuffling rrsets from an iterator.
+ * Uses AES s-box to provide good diffusion.
+ *
+ * Seems to pass muster under runs test.
+ *
+ * $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done
+ * $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }'
+ * library(lawstat)
+ * runs.test(scan(file="/tmp/out"))
+ * EOF
+ */
+static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) {
+ static const unsigned char sbox[256] =
+ { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+ unsigned char a, b;
+ unsigned i;
+
+ a = 0xff & (n >> 0);
+ b = 0xff & (n >> 8);
+
+ for (i = 0; i < 4; i++) {
+ a ^= 0xff & s;
+ a = sbox[a] ^ b;
+ b = sbox[b] ^ a;
+ s >>= 8;
+ }
+
+ return ((0xff00 & (a << 8)) | (0x00ff & (b << 0)));
+} /* dns_k_shuffle16() */
+
+/*
+ * S T A T E M A C H I N E R O U T I N E S
+ *
+ * Application code should define DNS_SM_RESTORE and DNS_SM_SAVE, and the
+ * local variable pc.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_SM_ENTER \
+ do { \
+ static const int pc0 = __LINE__; \
+ DNS_SM_RESTORE; \
+ switch (pc0 + pc) { \
+ case __LINE__: (void)0
+
+#define DNS_SM_SAVE_AND_DO(do_statement) \
+ do { \
+ pc = __LINE__ - pc0; \
+ DNS_SM_SAVE; \
+ do_statement; \
+ case __LINE__: (void)0; \
+ } while (0)
+
+#define DNS_SM_YIELD(rv) \
+ DNS_SM_SAVE_AND_DO(return (rv))
+
+#define DNS_SM_EXIT \
+ do { goto leave; } while (0)
+
+#define DNS_SM_LEAVE \
+ leave: (void)0; \
+ DNS_SM_SAVE_AND_DO(break); \
+ } \
+ } while (0)
+
+/*
+ * U T I L I T Y R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_MAXINTERVAL 300
+
+struct dns_clock {
+ time_t sample, elapsed;
+}; /* struct dns_clock */
+
+static void dns_begin(struct dns_clock *clk) {
+ clk->sample = time(0);
+ clk->elapsed = 0;
+} /* dns_begin() */
+
+static time_t dns_elapsed(struct dns_clock *clk) {
+ time_t curtime;
+
+ if ((time_t)-1 == time(&curtime))
+ return clk->elapsed;
+
+ if (curtime > clk->sample)
+ clk->elapsed += (time_t)DNS_PP_MIN(difftime(curtime, clk->sample), DNS_MAXINTERVAL);
+
+ clk->sample = curtime;
+
+ return clk->elapsed;
+} /* dns_elapsed() */
+
+
+DNS_NOTUSED static size_t dns_strnlen(const char *src, size_t m) {
+ size_t n = 0;
+
+ while (*src++ && n < m)
+ ++n;
+
+ return n;
+} /* dns_strnlen() */
+
+
+DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, size_t max) {
+ size_t len = dns_strnlen(src, max), n;
+
+ if (lim > 0) {
+ n = DNS_PP_MIN(lim - 1, len);
+ memcpy(dst, src, n);
+ dst[n] = '\0';
+ }
+
+ return len;
+} /* dns_strnlcpy() */
+
+
+#define DNS_HAVE_SOCKADDR_UN (defined AF_UNIX && !defined _WIN32)
+
+static size_t dns_af_len(int af) {
+ static const size_t table[AF_MAX] = {
+ [AF_INET6] = sizeof (struct sockaddr_in6),
+ [AF_INET] = sizeof (struct sockaddr_in),
+#if DNS_HAVE_SOCKADDR_UN
+ [AF_UNIX] = sizeof (struct sockaddr_un),
+#endif
+ };
+
+ return table[af];
+} /* dns_af_len() */
+
+#define dns_sa_family(sa) (((struct sockaddr *)(sa))->sa_family)
+
+#define dns_sa_len(sa) dns_af_len(dns_sa_family(sa))
+
+
+#define DNS_SA_NOPORT &dns_sa_noport
+static unsigned short dns_sa_noport;
+
+static unsigned short *dns_sa_port(int af, void *sa) {
+ switch (af) {
+ case AF_INET6:
+ return &((struct sockaddr_in6 *)sa)->sin6_port;
+ case AF_INET:
+ return &((struct sockaddr_in *)sa)->sin_port;
+ default:
+ return DNS_SA_NOPORT;
+ }
+} /* dns_sa_port() */
+
+
+static void *dns_sa_addr(int af, const void *sa, socklen_t *size) {
+ switch (af) {
+ case AF_INET6: {
+ struct in6_addr *in6 = &((struct sockaddr_in6 *)sa)->sin6_addr;
+
+ if (size)
+ *size = sizeof *in6;
+
+ return in6;
+ }
+ case AF_INET: {
+ struct in_addr *in = &((struct sockaddr_in *)sa)->sin_addr;
+
+ if (size)
+ *size = sizeof *in;
+
+ return in;
+ }
+ default:
+ if (size)
+ *size = 0;
+
+ return 0;
+ }
+} /* dns_sa_addr() */
+
+
+#if DNS_HAVE_SOCKADDR_UN
+#define DNS_SUNPATHMAX (sizeof ((struct sockaddr_un *)0)->sun_path)
+#endif
+
+DNS_NOTUSED static void *dns_sa_path(void *sa, socklen_t *size) {
+ switch (dns_sa_family(sa)) {
+#if DNS_HAVE_SOCKADDR_UN
+ case AF_UNIX: {
+ char *path = ((struct sockaddr_un *)sa)->sun_path;
+
+ if (size)
+ *size = dns_strnlen(path, DNS_SUNPATHMAX);
+
+ return path;
+ }
+#endif
+ default:
+ if (size)
+ *size = 0;
+
+ return NULL;
+ }
+} /* dns_sa_path() */
+
+
+static int dns_sa_cmp(void *a, void *b) {
+ int cmp, af;
+
+ if ((cmp = dns_sa_family(a) - dns_sa_family(b)))
+ return cmp;
+
+ switch ((af = dns_sa_family(a))) {
+ case AF_INET: {
+ struct in_addr *a4, *b4;
+
+ if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b))))
+ return cmp;
+
+ a4 = dns_sa_addr(af, a, NULL);
+ b4 = dns_sa_addr(af, b, NULL);
+
+ if (ntohl(a4->s_addr) < ntohl(b4->s_addr))
+ return -1;
+ if (ntohl(a4->s_addr) > ntohl(b4->s_addr))
+ return 1;
+
+ return 0;
+ }
+ case AF_INET6: {
+ struct in6_addr *a6, *b6;
+ size_t i;
+
+ if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b))))
+ return cmp;
+
+ a6 = dns_sa_addr(af, a, NULL);
+ b6 = dns_sa_addr(af, b, NULL);
+
+ /* XXX: do we need to use in6_clearscope()? */
+ for (i = 0; i < sizeof a6->s6_addr; i++) {
+ if ((cmp = a6->s6_addr[i] - b6->s6_addr[i]))
+ return cmp;
+ }
+
+ return 0;
+ }
+#if DNS_HAVE_SOCKADDR_UN
+ case AF_UNIX: {
+ char a_path[DNS_SUNPATHMAX + 1], b_path[sizeof a_path];
+
+ dns_strnlcpy(a_path, sizeof a_path, dns_sa_path(a, NULL), DNS_SUNPATHMAX);
+ dns_strnlcpy(b_path, sizeof b_path, dns_sa_path(b, NULL), DNS_SUNPATHMAX);
+
+ return strcmp(a_path, b_path);
+ }
+#endif
+ default:
+ return -1;
+ }
+} /* dns_sa_cmp() */
+
+
+#if _WIN32
+static int dns_inet_pton(int af, const void *src, void *dst) {
+ union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u;
+
+ u.sin.sin_family = af;
+
+ if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u }))
+ return -1;
+
+ switch (af) {
+ case AF_INET6:
+ *(struct in6_addr *)dst = u.sin6.sin6_addr;
+
+ return 1;
+ case AF_INET:
+ *(struct in_addr *)dst = u.sin.sin_addr;
+
+ return 1;
+ default:
+ return 0;
+ }
+} /* dns_inet_pton() */
+
+static const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) {
+ union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u;
+
+ /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */
+ memset(&u, 0, sizeof u);
+
+ u.sin.sin_family = af;
+
+ switch (af) {
+ case AF_INET6:
+ u.sin6.sin6_addr = *(struct in6_addr *)src;
+ break;
+ case AF_INET:
+ u.sin.sin_addr = *(struct in_addr *)src;
+
+ break;
+ default:
+ return 0;
+ }
+
+ if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim))
+ return 0;
+
+ return dst;
+} /* dns_inet_ntop() */
+#else
+#define dns_inet_pton(...) inet_pton(__VA_ARGS__)
+#define dns_inet_ntop(...) inet_ntop(__VA_ARGS__)
+#endif
+
+
+static dns_error_t dns_pton(int af, const void *src, void *dst) {
+ switch (dns_inet_pton(af, src, dst)) {
+ case 1:
+ return 0;
+ case -1:
+ return dns_soerr();
+ default:
+ return DNS_EADDRESS;
+ }
+} /* dns_pton() */
+
+
+static dns_error_t dns_ntop(int af, const void *src, void *dst, unsigned long lim) {
+ return (dns_inet_ntop(af, src, dst, lim))? 0 : dns_soerr();
+} /* dns_ntop() */
+
+
+size_t dns_strlcpy(char *dst, const char *src, size_t lim) {
+ char *d = dst;
+ char *e = &dst[lim];
+ const char *s = src;
+
+ if (d < e) {
+ do {
+ if ('\0' == (*d++ = *s++))
+ return s - src - 1;
+ } while (d < e);
+
+ d[-1] = '\0';
+ }
+
+ while (*s++ != '\0')
+ ;;
+
+ return s - src - 1;
+} /* dns_strlcpy() */
+
+
+size_t dns_strlcat(char *dst, const char *src, size_t lim) {
+ char *d = memchr(dst, '\0', lim);
+ char *e = &dst[lim];
+ const char *s = src;
+ const char *p;
+
+ if (d && d < e) {
+ do {
+ if ('\0' == (*d++ = *s++))
+ return d - dst - 1;
+ } while (d < e);
+
+ d[-1] = '\0';
+ }
+
+ p = s;
+
+ while (*s++ != '\0')
+ ;;
+
+ return lim + (s - p - 1);
+} /* dns_strlcat() */
+
+
+static void *dns_reallocarray(void *p, size_t nmemb, size_t size, dns_error_t *error) {
+ void *rp;
+
+ if (nmemb > 0 && SIZE_MAX / nmemb < size) {
+ *error = EOVERFLOW;
+ return NULL;
+ }
+
+ if (!(rp = realloc(p, nmemb * size)))
+ *error = (errno)? errno : EINVAL;
+
+ return rp;
+} /* dns_reallocarray() */
+
+
+#if _WIN32
+
+static char *dns_strsep(char **sp, const char *delim) {
+ char *p;
+
+ if (!(p = *sp))
+ return 0;
+
+ *sp += strcspn(p, delim);
+
+ if (**sp != '\0') {
+ **sp = '\0';
+ ++*sp;
+ } else
+ *sp = NULL;
+
+ return p;
+} /* dns_strsep() */
+
+#else
+#define dns_strsep(...) strsep(__VA_ARGS__)
+#endif
+
+
+#if _WIN32
+#define strcasecmp(...) _stricmp(__VA_ARGS__)
+#define strncasecmp(...) _strnicmp(__VA_ARGS__)
+#endif
+
+
+static inline _Bool dns_isalpha(unsigned char c) {
+ return isalpha(c);
+} /* dns_isalpha() */
+
+static inline _Bool dns_isdigit(unsigned char c) {
+ return isdigit(c);
+} /* dns_isdigit() */
+
+static inline _Bool dns_isalnum(unsigned char c) {
+ return isalnum(c);
+} /* dns_isalnum() */
+
+static inline _Bool dns_isspace(unsigned char c) {
+ return isspace(c);
+} /* dns_isspace() */
+
+static inline _Bool dns_isgraph(unsigned char c) {
+ return isgraph(c);
+} /* dns_isgraph() */
+
+
+static int dns_poll(int fd, short events, int timeout) {
+ fd_set rset, wset;
+
+ if (!events)
+ return 0;
+
+ if (fd < 0 || (unsigned)fd >= FD_SETSIZE)
+ return EINVAL;
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+
+ if (events & DNS_POLLIN)
+ FD_SET(fd, &rset);
+
+ if (events & DNS_POLLOUT)
+ FD_SET(fd, &wset);
+
+ select(fd + 1, &rset, &wset, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : NULL);
+
+ return 0;
+} /* dns_poll() */
+
+
+#if !_WIN32
+DNS_NOTUSED static int dns_sigmask(int how, const sigset_t *set, sigset_t *oset) {
+#if DNS_THREAD_SAFE
+ return pthread_sigmask(how, set, oset);
+#else
+ return (0 == sigprocmask(how, set, oset))? 0 : errno;
+#endif
+} /* dns_sigmask() */
+#endif
+
+
+static size_t dns_send(int fd, const void *src, size_t len, int flags, dns_error_t *error) {
+ long n = send(fd, src, len, flags);
+
+ if (n < 0) {
+ *error = dns_soerr();
+ return 0;
+ } else {
+ *error = 0;
+ return n;
+ }
+} /* dns_send() */
+
+static size_t dns_recv(int fd, void *dst, size_t lim, int flags, dns_error_t *error) {
+ long n = recv(fd, dst, lim, flags);
+
+ if (n < 0) {
+ *error = dns_soerr();
+ return 0;
+ } else if (n == 0) {
+ *error = (lim > 0)? DNS_ECONNFIN : EINVAL;
+ return 0;
+ } else {
+ *error = 0;
+ return n;
+ }
+} /* dns_recv() */
+
+static size_t dns_send_nopipe(int fd, const void *src, size_t len, int flags, dns_error_t *_error) {
+#if _WIN32 || !defined SIGPIPE || defined SO_NOSIGPIPE
+ return dns_send(fd, src, len, flags, _error);
+#elif defined MSG_NOSIGNAL
+ return dns_send(fd, src, len, (flags|MSG_NOSIGNAL), _error);
+#elif _POSIX_REALTIME_SIGNALS > 0 /* require sigtimedwait */
+ /*
+ * SIGPIPE handling similar to the approach described in
+ * http://krokisplace.blogspot.com/2010/02/suppressing-sigpipe-in-library.html
+ */
+ sigset_t pending, blocked, piped;
+ size_t count;
+ int error;
+
+ sigemptyset(&pending);
+ sigpending(&pending);
+
+ if (!sigismember(&pending, SIGPIPE)) {
+ sigemptyset(&piped);
+ sigaddset(&piped, SIGPIPE);
+ sigemptyset(&blocked);
+
+ if ((error = dns_sigmask(SIG_BLOCK, &piped, &blocked)))
+ goto error;
+ }
+
+ count = dns_send(fd, src, len, flags, &error);
+
+ if (!sigismember(&pending, SIGPIPE)) {
+ int saved = error;
+
+ if (!count && error == EPIPE) {
+ while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR)
+ ;;
+ }
+
+ if ((error = dns_sigmask(SIG_SETMASK, &blocked, NULL)))
+ goto error;
+
+ error = saved;
+ }
+
+ *_error = error;
+ return count;
+error:
+ *_error = error;
+ return 0;
+#else
+#error "unable to suppress SIGPIPE"
+ return dns_send(fd, src, len, flags, _error);
+#endif
+} /* dns_send_nopipe() */
+
+
+static dns_error_t dns_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) {
+ if (0 != connect(fd, addr, addrlen))
+ return dns_soerr();
+ return 0;
+} /* dns_connect() */
+
+
+#define DNS_FOPEN_STDFLAGS "rwabt+"
+
+static dns_error_t dns_fopen_addflag(char *dst, const char *src, size_t lim, int fc) {
+ char *p = dst, *pe = dst + lim;
+
+ /* copy standard flags */
+ while (*src && strchr(DNS_FOPEN_STDFLAGS, *src)) {
+ if (!(p < pe))
+ return ENOMEM;
+ *p++ = *src++;
+ }
+
+ /* append flag to standard flags */
+ if (!(p < pe))
+ return ENOMEM;
+ *p++ = fc;
+
+ /* copy remaining mode string, including '\0' */
+ do {
+ if (!(p < pe))
+ return ENOMEM;
+ } while ((*p++ = *src++));
+
+ return 0;
+} /* dns_fopen_addflag() */
+
+static FILE *dns_fopen(const char *path, const char *mode, dns_error_t *_error) {
+ FILE *fp;
+ char mode_cloexec[32];
+ int error;
+
+ assert(path && mode && *mode);
+ if (!*path) {
+ error = EINVAL;
+ goto error;
+ }
+
+#if _WIN32 || _WIN64
+ if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'N')))
+ goto error;
+ if (!(fp = fopen(path, mode_cloexec)))
+ goto syerr;
+#else
+ if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'e')))
+ goto error;
+ if (!(fp = fopen(path, mode_cloexec))) {
+ if (errno != EINVAL)
+ goto syerr;
+ if (!(fp = fopen(path, mode)))
+ goto syerr;
+ }
+#endif
+
+ return fp;
+syerr:
+ error = dns_syerr();
+error:
+ *_error = error;
+
+ return NULL;
+} /* dns_fopen() */
+
+
+struct dns_hxd_lines_i {
+ int pc;
+ size_t p;
+};
+
+#define DNS_SM_RESTORE \
+ do { \
+ pc = state->pc; \
+ sp = src + state->p; \
+ se = src + len; \
+ } while (0)
+#define DNS_SM_SAVE \
+ do { \
+ state->p = sp - src; \
+ state->pc = pc; \
+ } while (0)
+
+static size_t dns_hxd_lines(void *dst, size_t lim, const unsigned char *src, size_t len, struct dns_hxd_lines_i *state) {
+ static const unsigned char hex[] = "0123456789abcdef";
+ static const unsigned char tmpl[] = " | |\n";
+ unsigned char ln[sizeof tmpl];
+ const unsigned char *sp, *se;
+ unsigned char *h, *g;
+ unsigned i, n;
+ int pc;
+
+ DNS_SM_ENTER;
+
+ while (sp < se) {
+ memcpy(ln, tmpl, sizeof ln);
+
+ h = &ln[2];
+ g = &ln[53];
+
+ for (n = 0; n < 2; n++) {
+ for (i = 0; i < 8 && se - sp > 0; i++, sp++) {
+ h[0] = hex[0x0f & (*sp >> 4)];
+ h[1] = hex[0x0f & (*sp >> 0)];
+ h += 3;
+
+ *g++ = (dns_isgraph(*sp))? *sp : '.';
+ }
+
+ h++;
+ }
+
+ n = dns_strlcpy(dst, (char *)ln, lim);
+ DNS_SM_YIELD(n);
+ }
+
+ DNS_SM_EXIT;
+ DNS_SM_LEAVE;
+
+ return 0;
+}
+
+#undef DNS_SM_SAVE
+#undef DNS_SM_RESTORE
+
+/*
+ * A R I T H M E T I C R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_CHECK_OVERFLOW(error, r, f, ...) \
+ do { \
+ uintmax_t _r; \
+ *(error) = f(&_r, __VA_ARGS__); \
+ *(r) = _r; \
+ } while (0)
+
+static dns_error_t dns_clamp_overflow(uintmax_t *r, uintmax_t n, uintmax_t clamp) {
+ if (n > clamp) {
+ *r = clamp;
+ return ERANGE;
+ } else {
+ *r = n;
+ return 0;
+ }
+} /* dns_clamp_overflow() */
+
+static dns_error_t dns_add_overflow(uintmax_t *r, uintmax_t a, uintmax_t b, uintmax_t clamp) {
+ if (~a < b) {
+ *r = DNS_PP_MIN(clamp, ~UINTMAX_C(0));
+ return ERANGE;
+ } else {
+ return dns_clamp_overflow(r, a + b, clamp);
+ }
+} /* dns_add_overflow() */
+
+static dns_error_t dns_mul_overflow(uintmax_t *r, uintmax_t a, uintmax_t b, uintmax_t clamp) {
+ if (a > 0 && UINTMAX_MAX / a < b) {
+ *r = DNS_PP_MIN(clamp, ~UINTMAX_C(0));
+ return ERANGE;
+ } else {
+ return dns_clamp_overflow(r, a * b, clamp);
+ }
+} /* dns_mul_overflow() */
+
+/*
+ * F I X E D - S I Z E D B U F F E R R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_B_INIT(src, n) { \
+ (unsigned char *)(src), \
+ (unsigned char *)(src), \
+ (unsigned char *)(src) + (n), \
+}
+
+#define DNS_B_FROM(src, n) DNS_B_INIT((src), (n))
+#define DNS_B_INTO(src, n) DNS_B_INIT((src), (n))
+
+struct dns_buf {
+ const unsigned char *base;
+ unsigned char *p;
+ const unsigned char *pe;
+ dns_error_t error;
+ size_t overflow;
+}; /* struct dns_buf */
+
+static inline size_t
+dns_b_tell(struct dns_buf *b)
+{
+ return b->p - b->base;
+}
+
+static inline dns_error_t
+dns_b_setoverflow(struct dns_buf *b, size_t n, dns_error_t error)
+{
+ b->overflow += n;
+ return b->error = error;
+}
+
+DNS_NOTUSED static struct dns_buf *
+dns_b_into(struct dns_buf *b, void *src, size_t n)
+{
+ *b = (struct dns_buf)DNS_B_INTO(src, n);
+
+ return b;
+}
+
+static dns_error_t
+dns_b_putc(struct dns_buf *b, unsigned char uc)
+{
+ if (!(b->p < b->pe))
+ return dns_b_setoverflow(b, 1, DNS_ENOBUFS);
+
+ *b->p++ = uc;
+
+ return 0;
+}
+
+static dns_error_t
+dns_b_pputc(struct dns_buf *b, unsigned char uc, size_t p)
+{
+ size_t pe = b->pe - b->base;
+ if (pe <= p)
+ return dns_b_setoverflow(b, p - pe + 1, DNS_ENOBUFS);
+
+ *((unsigned char *)b->base + p) = uc;
+
+ return 0;
+}
+
+static inline dns_error_t
+dns_b_put16(struct dns_buf *b, uint16_t u)
+{
+ return dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0);
+}
+
+static inline dns_error_t
+dns_b_pput16(struct dns_buf *b, uint16_t u, size_t p)
+{
+ if (dns_b_pputc(b, u >> 8, p) || dns_b_pputc(b, u >> 0, p + 1))
+ return b->error;
+
+ return 0;
+}
+
+DNS_NOTUSED static inline dns_error_t
+dns_b_put32(struct dns_buf *b, uint32_t u)
+{
+ return dns_b_putc(b, u >> 24), dns_b_putc(b, u >> 16),
+ dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0);
+}
+
+static dns_error_t
+dns_b_put(struct dns_buf *b, const void *src, size_t len)
+{
+ size_t n = DNS_PP_MIN((size_t)(b->pe - b->p), len);
+
+ memcpy(b->p, src, n);
+ b->p += n;
+
+ if (n < len)
+ return dns_b_setoverflow(b, len - n, DNS_ENOBUFS);
+
+ return 0;
+}
+
+static dns_error_t
+dns_b_puts(struct dns_buf *b, const void *src)
+{
+ return dns_b_put(b, src, strlen(src));
+}
+
+DNS_NOTUSED static inline dns_error_t
+dns_b_fmtju(struct dns_buf *b, const uintmax_t u, const unsigned width)
+{
+ size_t digits, padding, overflow;
+ uintmax_t r;
+ unsigned char *tp, *te, tc;
+
+ digits = 0;
+ r = u;
+ do {
+ digits++;
+ r /= 10;
+ } while (r);
+
+ padding = width - DNS_PP_MIN(digits, width);
+ overflow = (digits + padding) - DNS_PP_MIN((size_t)(b->pe - b->p), (digits + padding));
+
+ while (padding--) {
+ dns_b_putc(b, '0');
+ }
+
+ digits = 0;
+ tp = b->p;
+ r = u;
+ do {
+ if (overflow < ++digits)
+ dns_b_putc(b, '0' + (r % 10));
+ r /= 10;
+ } while (r);
+
+ te = b->p;
+ while (tp < te) {
+ tc = *--te;
+ *te = *tp;
+ *tp++ = tc;
+ }
+
+ return b->error;
+}
+
+static void
+dns_b_popc(struct dns_buf *b)
+{
+ if (b->overflow && !--b->overflow)
+ b->error = 0;
+ if (b->p > b->base)
+ b->p--;
+}
+
+static inline const char *
+dns_b_tolstring(struct dns_buf *b, size_t *n)
+{
+ if (b->p < b->pe) {
+ *b->p = '\0';
+ *n = b->p - b->base;
+
+ return (const char *)b->base;
+ } else if (b->p > b->base) {
+ if (b->p[-1] != '\0') {
+ dns_b_setoverflow(b, 1, DNS_ENOBUFS);
+ b->p[-1] = '\0';
+ }
+ *n = &b->p[-1] - b->base;
+
+ return (const char *)b->base;
+ } else {
+ *n = 0;
+
+ return "";
+ }
+}
+
+static inline const char *
+dns_b_tostring(struct dns_buf *b)
+{
+ size_t n;
+ return dns_b_tolstring(b, &n);
+}
+
+static inline size_t
+dns_b_strlen(struct dns_buf *b)
+{
+ size_t n;
+ dns_b_tolstring(b, &n);
+ return n;
+}
+
+static inline size_t
+dns_b_strllen(struct dns_buf *b)
+{
+ size_t n = dns_b_strlen(b);
+ return n + b->overflow;
+}
+
+DNS_NOTUSED static const struct dns_buf *
+dns_b_from(const struct dns_buf *b, const void *src, size_t n)
+{
+ *(struct dns_buf *)b = (struct dns_buf)DNS_B_FROM(src, n);
+
+ return b;
+}
+
+static inline int
+dns_b_getc(const struct dns_buf *_b, const int eof)
+{
+ struct dns_buf *b = (struct dns_buf *)_b;
+
+ if (!(b->p < b->pe))
+ return dns_b_setoverflow(b, 1, DNS_EILLEGAL), eof;
+
+ return *b->p++;
+}
+
+static inline intmax_t
+dns_b_get16(const struct dns_buf *b, const intmax_t eof)
+{
+ intmax_t n;
+
+ n = (dns_b_getc(b, 0) << 8);
+ n |= (dns_b_getc(b, 0) << 0);
+
+ return (!b->overflow)? n : eof;
+}
+
+DNS_NOTUSED static inline intmax_t
+dns_b_get32(const struct dns_buf *b, const intmax_t eof)
+{
+ intmax_t n;
+
+ n = (dns_b_get16(b, 0) << 16);
+ n |= (dns_b_get16(b, 0) << 0);
+
+ return (!b->overflow)? n : eof;
+}
+
+static inline dns_error_t
+dns_b_move(struct dns_buf *dst, const struct dns_buf *_src, size_t n)
+{
+ struct dns_buf *src = (struct dns_buf *)_src;
+ size_t src_n = DNS_PP_MIN((size_t)(src->pe - src->p), n);
+ size_t src_r = n - src_n;
+
+ dns_b_put(dst, src->p, src_n);
+ src->p += src_n;
+
+ if (src_r)
+ return dns_b_setoverflow(src, src_r, DNS_EILLEGAL);
+
+ return dst->error;
+}
+
+/*
+ * T I M E R O U T I N E S
+ *
+ * Most functions still rely on the older time routines defined in the
+ * utility routines section, above.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_TIME_C(n) UINT64_C(n)
+#define DNS_TIME_INF (~DNS_TIME_C(0))
+
+typedef uint64_t dns_time_t;
+typedef dns_time_t dns_microseconds_t;
+
+static dns_error_t dns_time_add(dns_time_t *r, dns_time_t a, dns_time_t b) {
+ int error;
+ DNS_CHECK_OVERFLOW(&error, r, dns_add_overflow, a, b, DNS_TIME_INF);
+ return error;
+}
+
+static dns_error_t dns_time_mul(dns_time_t *r, dns_time_t a, dns_time_t b) {
+ int error;
+ DNS_CHECK_OVERFLOW(&error, r, dns_mul_overflow, a, b, DNS_TIME_INF);
+ return error;
+}
+
+static dns_error_t dns_time_diff(dns_time_t *r, dns_time_t a, dns_time_t b) {
+ if (a < b) {
+ *r = DNS_TIME_C(0);
+ return ERANGE;
+ } else {
+ *r = a - b;
+ return 0;
+ }
+}
+
+static dns_microseconds_t dns_ts2us(const struct timespec *ts, _Bool rup) {
+ if (ts) {
+ dns_time_t sec = DNS_PP_MAX(0, ts->tv_sec);
+ dns_time_t nsec = DNS_PP_MAX(0, ts->tv_nsec);
+ dns_time_t usec = nsec / 1000;
+ dns_microseconds_t r;
+
+ if (rup && nsec % 1000 > 0)
+ usec++;
+ dns_time_mul(&r, sec, DNS_TIME_C(1000000));
+ dns_time_add(&r, r, usec);
+
+ return r;
+ } else {
+ return DNS_TIME_INF;
+ }
+} /* dns_ts2us() */
+
+static struct timespec *dns_tv2ts(struct timespec *ts, const struct timeval *tv) {
+ if (tv) {
+ ts->tv_sec = tv->tv_sec;
+ ts->tv_nsec = tv->tv_usec * 1000;
+
+ return ts;
+ } else {
+ return NULL;
+ }
+} /* dns_tv2ts() */
+
+static size_t dns_utime_print(void *_dst, size_t lim, dns_microseconds_t us) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+
+ dns_b_fmtju(&dst, us / 1000000, 1);
+ dns_b_putc(&dst, '.');
+ dns_b_fmtju(&dst, us % 1000000, 6);
+
+ return dns_b_strllen(&dst);
+} /* dns_utime_print() */
+
+/*
+ * P A C K E T R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+unsigned dns_p_count(struct dns_packet *P, enum dns_section section) {
+ unsigned count;
+
+ switch (section) {
+ case DNS_S_QD:
+ return ntohs(dns_header(P)->qdcount);
+ case DNS_S_AN:
+ return ntohs(dns_header(P)->ancount);
+ case DNS_S_NS:
+ return ntohs(dns_header(P)->nscount);
+ case DNS_S_AR:
+ return ntohs(dns_header(P)->arcount);
+ default:
+ count = 0;
+
+ if (section & DNS_S_QD)
+ count += ntohs(dns_header(P)->qdcount);
+ if (section & DNS_S_AN)
+ count += ntohs(dns_header(P)->ancount);
+ if (section & DNS_S_NS)
+ count += ntohs(dns_header(P)->nscount);
+ if (section & DNS_S_AR)
+ count += ntohs(dns_header(P)->arcount);
+
+ return count;
+ }
+} /* dns_p_count() */
+
+
+struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) {
+ if (!P)
+ return 0;
+
+ assert(size >= offsetof(struct dns_packet, data) + 12);
+
+ memset(P, 0, sizeof *P);
+ P->size = size - offsetof(struct dns_packet, data);
+ P->end = 12;
+
+ memset(P->data, '\0', 12);
+
+ return P;
+} /* dns_p_init() */
+
+
+static struct dns_packet *dns_p_reset(struct dns_packet *P) {
+ return dns_p_init(P, offsetof(struct dns_packet, data) + P->size);
+} /* dns_p_reset() */
+
+
+static unsigned short dns_p_qend(struct dns_packet *P) {
+ unsigned short qend = 12;
+ unsigned i, count = dns_p_count(P, DNS_S_QD);
+
+ for (i = 0; i < count && qend < P->end; i++) {
+ if (P->end == (qend = dns_d_skip(qend, P)))
+ goto invalid;
+
+ if (P->end - qend < 4)
+ goto invalid;
+
+ qend += 4;
+ }
+
+ return DNS_PP_MIN(qend, P->end);
+invalid:
+ return P->end;
+} /* dns_p_qend() */
+
+
+struct dns_packet *dns_p_make(size_t len, int *error) {
+ struct dns_packet *P;
+ size_t size = dns_p_calcsize(len);
+
+ if (!(P = dns_p_init(malloc(size), size)))
+ *error = dns_syerr();
+
+ return P;
+} /* dns_p_make() */
+
+
+static void dns_p_free(struct dns_packet *P) {
+ free(P);
+} /* dns_p_free() */
+
+
+/* convience routine to free any existing packet before storing new packet */
+static struct dns_packet *dns_p_setptr(struct dns_packet **dst, struct dns_packet *src) {
+ dns_p_free(*dst);
+
+ *dst = src;
+
+ return src;
+} /* dns_p_setptr() */
+
+
+static struct dns_packet *dns_p_movptr(struct dns_packet **dst, struct dns_packet **src) {
+ dns_p_setptr(dst, *src);
+
+ *src = NULL;
+
+ return *dst;
+} /* dns_p_movptr() */
+
+
+int dns_p_grow(struct dns_packet **P) {
+ struct dns_packet *tmp;
+ size_t size;
+ int error;
+
+ if (!*P) {
+ if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error)))
+ return error;
+
+ return 0;
+ }
+
+ size = dns_p_sizeof(*P);
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size |= size >> 8;
+ size++;
+
+ if (size > 65536)
+ return DNS_ENOBUFS;
+
+ if (!(tmp = realloc(*P, dns_p_calcsize(size))))
+ return dns_syerr();
+
+ tmp->size = size;
+ *P = tmp;
+
+ return 0;
+} /* dns_p_grow() */
+
+
+struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) {
+ if (!P)
+ return 0;
+
+ P->end = DNS_PP_MIN(P->size, P0->end);
+
+ memcpy(P->data, P0->data, P->end);
+
+ return P;
+} /* dns_p_copy() */
+
+
+struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) {
+ size_t bufsiz = DNS_PP_MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0));
+ struct dns_packet *M;
+ enum dns_section section;
+ struct dns_rr rr, mr;
+ int error, copy;
+
+ if (!A && B) {
+ A = B;
+ Amask = Bmask;
+ B = 0;
+ }
+
+merge:
+ if (!(M = dns_p_make(bufsiz, &error)))
+ goto error;
+
+ for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) {
+ if (A && (section & Amask)) {
+ dns_rr_foreach(&rr, A, .section = section) {
+ if ((error = dns_rr_copy(M, &rr, A)))
+ goto error;
+ }
+ }
+
+ if (B && (section & Bmask)) {
+ dns_rr_foreach(&rr, B, .section = section) {
+ copy = 1;
+
+ dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) {
+ if (!(copy = dns_rr_cmp(&rr, B, &mr, M)))
+ break;
+ }
+
+ if (copy && (error = dns_rr_copy(M, &rr, B)))
+ goto error;
+ }
+ }
+ }
+
+ return M;
+error:
+ dns_p_setptr(&M, NULL);
+
+ if (error == DNS_ENOBUFS && bufsiz < 65535) {
+ bufsiz = DNS_PP_MIN(65535, bufsiz * 2);
+
+ goto merge;
+ }
+
+ *error_ = error;
+
+ return 0;
+} /* dns_p_merge() */
+
+
+static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t);
+
+void dns_p_dictadd(struct dns_packet *P, unsigned short dn) {
+ unsigned short lp, lptr, i;
+
+ lp = dn;
+
+ while (lp < P->end) {
+ if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) {
+ lptr = ((0x3f & P->data[lp + 0]) << 8)
+ | ((0xff & P->data[lp + 1]) << 0);
+
+ for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) {
+ if (P->dict[i] == lptr) {
+ P->dict[i] = dn;
+
+ return;
+ }
+ }
+ }
+
+ lp = dns_l_skip(lp, P->data, P->end);
+ }
+
+ for (i = 0; i < lengthof(P->dict); i++) {
+ if (!P->dict[i]) {
+ P->dict[i] = dn;
+
+ break;
+ }
+ }
+} /* dns_p_dictadd() */
+
+
+int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) {
+ size_t end = P->end;
+ int error;
+
+ if ((error = dns_d_push(P, dn, dnlen)))
+ goto error;
+
+ if (P->size - P->end < 4)
+ goto nobufs;
+
+ P->data[P->end++] = 0xff & (type >> 8);
+ P->data[P->end++] = 0xff & (type >> 0);
+
+ P->data[P->end++] = 0xff & (class >> 8);
+ P->data[P->end++] = 0xff & (class >> 0);
+
+ if (section == DNS_S_QD)
+ goto update;
+
+ if (P->size - P->end < 6)
+ goto nobufs;
+
+ if (type != DNS_T_OPT)
+ ttl = DNS_PP_MIN(ttl, 0x7fffffffU);
+ P->data[P->end++] = ttl >> 24;
+ P->data[P->end++] = ttl >> 16;
+ P->data[P->end++] = ttl >> 8;
+ P->data[P->end++] = ttl >> 0;
+
+ if ((error = dns_any_push(P, (union dns_any *)any, type)))
+ goto error;
+
+update:
+ switch (section) {
+ case DNS_S_QD:
+ if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR))
+ goto order;
+
+ if (!P->memo.qd.base && (error = dns_p_study(P)))
+ goto error;
+
+ dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1);
+
+ P->memo.qd.end = P->end;
+ P->memo.an.base = P->end;
+ P->memo.an.end = P->end;
+ P->memo.ns.base = P->end;
+ P->memo.ns.end = P->end;
+ P->memo.ar.base = P->end;
+ P->memo.ar.end = P->end;
+
+ break;
+ case DNS_S_AN:
+ if (dns_p_count(P, DNS_S_NS|DNS_S_AR))
+ goto order;
+
+ if (!P->memo.an.base && (error = dns_p_study(P)))
+ goto error;
+
+ dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1);
+
+ P->memo.an.end = P->end;
+ P->memo.ns.base = P->end;
+ P->memo.ns.end = P->end;
+ P->memo.ar.base = P->end;
+ P->memo.ar.end = P->end;
+
+ break;
+ case DNS_S_NS:
+ if (dns_p_count(P, DNS_S_AR))
+ goto order;
+
+ if (!P->memo.ns.base && (error = dns_p_study(P)))
+ goto error;
+
+ dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1);
+
+ P->memo.ns.end = P->end;
+ P->memo.ar.base = P->end;
+ P->memo.ar.end = P->end;
+
+ break;
+ case DNS_S_AR:
+ if (!P->memo.ar.base && (error = dns_p_study(P)))
+ goto error;
+
+ dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1);
+
+ P->memo.ar.end = P->end;
+
+ if (type == DNS_T_OPT && !P->memo.opt.p) {
+ P->memo.opt.p = end;
+ P->memo.opt.maxudp = class;
+ P->memo.opt.ttl = ttl;
+ }
+
+ break;
+ default:
+ error = DNS_ESECTION;
+
+ goto error;
+ } /* switch() */
+
+ return 0;
+nobufs:
+ error = DNS_ENOBUFS;
+
+ goto error;
+order:
+ error = DNS_EORDER;
+
+ goto error;
+error:
+ P->end = end;
+
+ return error;
+} /* dns_p_push() */
+
+#define DNS_SM_RESTORE do { pc = state->pc; error = state->error; } while (0)
+#define DNS_SM_SAVE do { state->error = error; state->pc = pc; } while (0)
+
+struct dns_p_lines_i {
+ int pc;
+ enum dns_section section;
+ struct dns_rr rr;
+ int error;
+};
+
+static size_t dns_p_lines_fmt(void *dst, size_t lim, dns_error_t *_error, const char *fmt, ...) {
+ va_list ap;
+ int error = 0, n;
+
+ va_start(ap, fmt);
+ if ((n = vsnprintf(dst, lim, fmt, ap)) < 0)
+ error = errno;
+ va_end(ap);
+
+ *_error = error;
+ return DNS_PP_MAX(n, 0);
+} /* dns_p_lines_fmt() */
+
+#define DNS_P_LINE(...) \
+ do { \
+ len = dns_p_lines_fmt(dst, lim, &error, __VA_ARGS__); \
+ if (len == 0 && error) \
+ goto error; \
+ DNS_SM_YIELD(len); \
+ } while (0)
+
+static size_t dns_p_lines(void *dst, size_t lim, dns_error_t *_error, struct dns_packet *P, struct dns_rr_i *I, struct dns_p_lines_i *state) {
+ int error, pc;
+ size_t len;
+
+ *_error = 0;
+
+ DNS_SM_ENTER;
+
+ DNS_P_LINE(";; [HEADER]\n");
+ DNS_P_LINE(";; qid : %d\n", ntohs(dns_header(P)->qid));
+ DNS_P_LINE(";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr);
+ DNS_P_LINE(";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode);
+ DNS_P_LINE(";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa);
+ DNS_P_LINE(";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc);
+ DNS_P_LINE(";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd);
+ DNS_P_LINE(";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra);
+ DNS_P_LINE(";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P));
+
+ while (dns_rr_grep(&state->rr, 1, I, P, &error)) {
+ if (state->section != state->rr.section) {
+ DNS_P_LINE("\n");
+ DNS_P_LINE(";; [%s:%d]\n", dns_strsection(state->rr.section), dns_p_count(P, state->rr.section));
+ }
+
+ if (!(len = dns_rr_print(dst, lim, &state->rr, P, &error)))
+ goto error;
+ dns_strlcat(dst, "\n", lim);
+ DNS_SM_YIELD(len + 1);
+
+ state->section = state->rr.section;
+ }
+
+ if (error)
+ goto error;
+
+ DNS_SM_EXIT;
+error:
+ for (;;) {
+ *_error = error;
+ DNS_SM_YIELD(0);
+ }
+
+ DNS_SM_LEAVE;
+
+ *_error = 0;
+ return 0;
+} /* dns_p_lines() */
+
+#undef DNS_P_LINE
+#undef DNS_SM_SAVE
+#undef DNS_SM_RESTORE
+
+static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) {
+ struct dns_p_lines_i lines = { 0 };
+ char line[sizeof (union dns_any) * 2];
+ size_t len;
+ int error;
+
+ while ((len = dns_p_lines(line, sizeof line, &error, P, I, &lines))) {
+ if (len < sizeof line) {
+ fwrite(line, 1, len, fp);
+ } else {
+ fwrite(line, 1, sizeof line - 1, fp);
+ fputc('\n', fp);
+ }
+ }
+} /* dns_p_dump3() */
+
+
+void dns_p_dump(struct dns_packet *P, FILE *fp) {
+ dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp);
+} /* dns_p_dump() */
+
+
+static void dns_s_unstudy(struct dns_s_memo *m)
+ { m->base = 0; m->end = 0; }
+
+static void dns_m_unstudy(struct dns_p_memo *m) {
+ dns_s_unstudy(&m->qd);
+ dns_s_unstudy(&m->an);
+ dns_s_unstudy(&m->ns);
+ dns_s_unstudy(&m->ar);
+ m->opt.p = 0;
+ m->opt.maxudp = 0;
+ m->opt.ttl = 0;
+} /* dns_m_unstudy() */
+
+static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned short base, struct dns_packet *P) {
+ unsigned short count, rp;
+
+ count = dns_p_count(P, section);
+
+ for (rp = base; count && rp < P->end; count--)
+ rp = dns_rr_skip(rp, P);
+
+ m->base = base;
+ m->end = rp;
+
+ return 0;
+} /* dns_s_study() */
+
+static int dns_m_study(struct dns_p_memo *m, struct dns_packet *P) {
+ struct dns_rr rr;
+ int error;
+
+ if ((error = dns_s_study(&m->qd, DNS_S_QD, 12, P)))
+ goto error;
+ if ((error = dns_s_study(&m->an, DNS_S_AN, m->qd.end, P)))
+ goto error;
+ if ((error = dns_s_study(&m->ns, DNS_S_NS, m->an.end, P)))
+ goto error;
+ if ((error = dns_s_study(&m->ar, DNS_S_AR, m->ns.end, P)))
+ goto error;
+
+ m->opt.p = 0;
+ m->opt.maxudp = 0;
+ m->opt.ttl = 0;
+ dns_rr_foreach(&rr, P, .type = DNS_T_OPT, .section = DNS_S_AR) {
+ m->opt.p = rr.dn.p;
+ m->opt.maxudp = rr.class;
+ m->opt.ttl = rr.ttl;
+ break;
+ }
+
+ return 0;
+error:
+ dns_m_unstudy(m);
+
+ return error;
+} /* dns_m_study() */
+
+int dns_p_study(struct dns_packet *P) {
+ return dns_m_study(&P->memo, P);
+} /* dns_p_study() */
+
+
+enum dns_rcode dns_p_rcode(struct dns_packet *P) {
+ return 0xfff & ((P->memo.opt.ttl >> 20) | dns_header(P)->rcode);
+} /* dns_p_rcode() */
+
+
+/*
+ * Q U E R Y P A C K E T R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_Q_RD 0x1 /* recursion desired */
+#define DNS_Q_EDNS0 0x2 /* include OPT RR */
+
+static dns_error_t
+dns_q_make2(struct dns_packet **_Q, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass, int qflags)
+{
+ struct dns_packet *Q = NULL;
+ int error;
+
+ if (dns_p_movptr(&Q, _Q)) {
+ dns_p_reset(Q);
+ } else if (!(Q = dns_p_make(DNS_P_QBUFSIZ, &error))) {
+ goto error;
+ }
+
+ if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, qtype, qclass, 0, 0)))
+ goto error;
+
+ dns_header(Q)->rd = !!(qflags & DNS_Q_RD);
+
+ if (qflags & DNS_Q_EDNS0) {
+ struct dns_opt opt = DNS_OPT_INIT(&opt);
+
+ opt.version = 0; /* RFC 6891 version */
+ opt.maxudp = 4096;
+
+ if ((error = dns_p_push(Q, DNS_S_AR, ".", 1, DNS_T_OPT, dns_opt_class(&opt), dns_opt_ttl(&opt), &opt)))
+ goto error;
+ }
+
+ *_Q = Q;
+
+ return 0;
+error:
+ dns_p_free(Q);
+
+ return error;
+}
+
+static dns_error_t
+dns_q_make(struct dns_packet **Q, const char *qname, enum dns_type qtype, enum dns_class qclass, int qflags)
+{
+ return dns_q_make2(Q, qname, strlen(qname), qtype, qclass, qflags);
+}
+
+static dns_error_t
+dns_q_remake(struct dns_packet **Q, int qflags)
+{
+ char qname[DNS_D_MAXNAME + 1];
+ size_t qlen;
+ struct dns_rr rr;
+ int error;
+
+ assert(Q && *Q);
+ if ((error = dns_rr_parse(&rr, 12, *Q)))
+ return error;
+ if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, *Q, &error)))
+ return error;
+ if (qlen >= sizeof qname)
+ return DNS_EILLEGAL;
+ return dns_q_make2(Q, qname, qlen, rr.type, rr.class, qflags);
+}
+
+/*
+ * D O M A I N N A M E R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef DNS_D_MAXPTRS
+#define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */
+#endif
+
+static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) {
+ unsigned short len;
+ unsigned nptrs = 0;
+
+retry:
+ if (src >= end)
+ goto invalid;
+
+ switch (0x03 & (data[src] >> 6)) {
+ case 0x00:
+ len = (0x3f & (data[src++]));
+
+ if (end - src < len)
+ goto invalid;
+
+ if (lim > 0) {
+ memcpy(dst, &data[src], DNS_PP_MIN(lim, len));
+
+ dst[DNS_PP_MIN(lim - 1, len)] = '\0';
+ }
+
+ *nxt = src + len;
+
+ return len;
+ case 0x01:
+ goto invalid;
+ case 0x02:
+ goto invalid;
+ case 0x03:
+ if (++nptrs > DNS_D_MAXPTRS)
+ goto invalid;
+
+ if (end - src < 2)
+ goto invalid;
+
+ src = ((0x3f & data[src + 0]) << 8)
+ | ((0xff & data[src + 1]) << 0);
+
+ goto retry;
+ } /* switch() */
+
+ /* NOT REACHED */
+invalid:
+ *nxt = end;
+
+ return 0;
+} /* dns_l_expand() */
+
+
+static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) {
+ unsigned short len;
+
+ if (src >= end)
+ goto invalid;
+
+ switch (0x03 & (data[src] >> 6)) {
+ case 0x00:
+ len = (0x3f & (data[src++]));
+
+ if (end - src < len)
+ goto invalid;
+
+ return (len)? src + len : end;
+ case 0x01:
+ goto invalid;
+ case 0x02:
+ goto invalid;
+ case 0x03:
+ return end;
+ } /* switch() */
+
+ /* NOT REACHED */
+invalid:
+ return end;
+} /* dns_l_skip() */
+
+
+static _Bool dns_d_isanchored(const void *_src, size_t len) {
+ const unsigned char *src = _src;
+ return len > 0 && src[len - 1] == '.';
+} /* dns_d_isanchored() */
+
+
+static size_t dns_d_ndots(const void *_src, size_t len) {
+ const unsigned char *p = _src, *pe = p + len;
+ size_t ndots = 0;
+
+ while ((p = memchr(p, '.', pe - p))) {
+ ndots++;
+ p++;
+ }
+
+ return ndots;
+} /* dns_d_ndots() */
+
+
+static size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) {
+ unsigned char *dst = dst_;
+ const unsigned char *src = src_;
+ size_t dp = 0, sp = 0;
+ int lc;
+
+ /* trim any leading dot(s) */
+ while (sp < len && src[sp] == '.')
+ sp++;
+
+ for (lc = 0; sp < len; lc = src[sp++]) {
+ /* trim extra dot(s) */
+ if (src[sp] == '.' && lc == '.')
+ continue;
+
+ if (dp < lim)
+ dst[dp] = src[sp];
+
+ dp++;
+ }
+
+ if ((flags & DNS_D_ANCHOR) && lc != '.') {
+ if (dp < lim)
+ dst[dp] = '.';
+
+ dp++;
+ }
+
+ if (lim > 0)
+ dst[DNS_PP_MIN(dp, lim - 1)] = '\0';
+
+ return dp;
+} /* dns_d_trim() */
+
+
+char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) {
+ if (flags & DNS_D_TRIM) {
+ dns_d_trim(dst, lim, src, len, flags);
+ } if (flags & DNS_D_ANCHOR) {
+ dns_d_anchor(dst, lim, src, len);
+ } else {
+ memmove(dst, src, DNS_PP_MIN(lim, len));
+
+ if (lim > 0)
+ ((char *)dst)[DNS_PP_MIN(len, lim - 1)] = '\0';
+ }
+
+ return dst;
+} /* dns_d_init() */
+
+
+size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) {
+ if (len == 0)
+ return 0;
+
+ memmove(dst, src, DNS_PP_MIN(lim, len));
+
+ if (((const char *)src)[len - 1] != '.') {
+ if (len < lim)
+ ((char *)dst)[len] = '.';
+ len++;
+ }
+
+ if (lim > 0)
+ ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0';
+
+ return len;
+} /* dns_d_anchor() */
+
+
+size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) {
+ const char *dot;
+
+ /* XXX: Skip any leading dot. Handles cleaving root ".". */
+ if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1)))
+ return 0;
+
+ len -= dot - (const char *)src;
+
+ /* XXX: Unless root, skip the label's trailing dot. */
+ if (len > 1) {
+ src = ++dot;
+ len--;
+ } else
+ src = dot;
+
+ memmove(dst, src, DNS_PP_MIN(lim, len));
+
+ if (lim > 0)
+ ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0';
+
+ return len;
+} /* dns_d_cleave() */
+
+
+size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error) {
+ struct { unsigned char *b; size_t p, x; } dst, src;
+ unsigned char ch = '.';
+
+ dst.b = dst_;
+ dst.p = 0;
+ dst.x = 1;
+
+ src.b = (unsigned char *)src_;
+ src.p = 0;
+ src.x = 0;
+
+ while (src.x < len) {
+ ch = src.b[src.x];
+
+ if (ch == '.') {
+ if (dst.p < lim)
+ dst.b[dst.p] = (0x3f & (src.x - src.p));
+
+ dst.p = dst.x++;
+ src.p = ++src.x;
+ } else {
+ if (dst.x < lim)
+ dst.b[dst.x] = ch;
+
+ dst.x++;
+ src.x++;
+ }
+ } /* while() */
+
+ if (src.x > src.p) {
+ if (dst.p < lim)
+ dst.b[dst.p] = (0x3f & (src.x - src.p));
+
+ dst.p = dst.x;
+ }
+
+ if (dst.p > 1) {
+ if (dst.p < lim)
+ dst.b[dst.p] = 0x00;
+
+ dst.p++;
+ }
+
+#if 1
+ if (dst.p < lim) {
+ struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b;
+ unsigned i;
+
+ a.p = 0;
+
+ while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) {
+ for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) {
+ b.p = P->dict[i];
+
+ while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) {
+ a.y = a.x;
+ b.y = b.x;
+
+ while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) {
+ a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim);
+ b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end);
+ }
+
+ if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) {
+ dst.b[a.p++] = 0xc0
+ | (0x3f & (b.p >> 8));
+ dst.b[a.p++] = (0xff & (b.p >> 0));
+
+ /* silence static analyzers */
+ dns_assume(a.p > 0);
+
+ return a.p;
+ }
+
+ b.p = b.x;
+ } /* while() */
+ } /* for() */
+
+ a.p = a.x;
+ } /* while() */
+ } /* if () */
+#endif
+
+ if (!dst.p)
+ *error = DNS_EILLEGAL;
+
+ return dst.p;
+} /* dns_d_comp() */
+
+
+unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) {
+ unsigned short len;
+
+ while (src < P->end) {
+ switch (0x03 & (P->data[src] >> 6)) {
+ case 0x00: /* FOLLOWS */
+ len = (0x3f & P->data[src++]);
+
+ if (0 == len) {
+/* success ==> */ return src;
+ } else if (P->end - src > len) {
+ src += len;
+
+ break;
+ } else
+ goto invalid;
+
+ /* NOT REACHED */
+ case 0x01: /* RESERVED */
+ goto invalid;
+ case 0x02: /* RESERVED */
+ goto invalid;
+ case 0x03: /* POINTER */
+ if (P->end - src < 2)
+ goto invalid;
+
+ src += 2;
+
+/* success ==> */ return src;
+ } /* switch() */
+ } /* while() */
+
+invalid:
+ return P->end;
+} /* dns_d_skip() */
+
+
+#include <stdio.h>
+
+size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) {
+ size_t dstp = 0;
+ unsigned nptrs = 0;
+ unsigned char len;
+
+ while (src < P->end) {
+ switch ((0x03 & (P->data[src] >> 6))) {
+ case 0x00: /* FOLLOWS */
+ len = (0x3f & P->data[src]);
+
+ if (0 == len) {
+ if (dstp == 0) {
+ if (dstp < lim)
+ ((unsigned char *)dst)[dstp] = '.';
+
+ dstp++;
+ }
+
+ /* NUL terminate */
+ if (lim > 0)
+ ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0';
+
+/* success ==> */ return dstp;
+ }
+
+ src++;
+
+ if (P->end - src < len)
+ goto toolong;
+
+ if (dstp < lim)
+ memcpy(&((unsigned char *)dst)[dstp], &P->data[src], DNS_PP_MIN(len, lim - dstp));
+
+ src += len;
+ dstp += len;
+
+ if (dstp < lim)
+ ((unsigned char *)dst)[dstp] = '.';
+
+ dstp++;
+
+ nptrs = 0;
+
+ continue;
+ case 0x01: /* RESERVED */
+ goto reserved;
+ case 0x02: /* RESERVED */
+ goto reserved;
+ case 0x03: /* POINTER */
+ if (++nptrs > DNS_D_MAXPTRS)
+ goto toolong;
+
+ if (P->end - src < 2)
+ goto toolong;
+
+ src = ((0x3f & P->data[src + 0]) << 8)
+ | ((0xff & P->data[src + 1]) << 0);
+
+ continue;
+ } /* switch() */
+ } /* while() */
+
+toolong:
+ *error = DNS_EILLEGAL;
+
+ if (lim > 0)
+ ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0';
+
+ return 0;
+reserved:
+ *error = DNS_EILLEGAL;
+
+ if (lim > 0)
+ ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0';
+
+ return 0;
+} /* dns_d_expand() */
+
+
+int dns_d_push(struct dns_packet *P, const void *dn, size_t len) {
+ size_t lim = P->size - P->end;
+ unsigned dp = P->end;
+ int error = DNS_EILLEGAL; /* silence compiler */
+
+ len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error);
+
+ if (len == 0)
+ return error;
+ if (len > lim)
+ return DNS_ENOBUFS;
+
+ P->end += len;
+
+ dns_p_dictadd(P, dp);
+
+ return 0;
+} /* dns_d_push() */
+
+
+size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) {
+ char host[DNS_D_MAXNAME + 1];
+ struct dns_rr_i i;
+ struct dns_rr rr;
+ unsigned depth;
+ int error;
+
+ if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len))
+ { error = ENAMETOOLONG; goto error; }
+
+ for (depth = 0; depth < 7; depth++) {
+ dns_rr_i_init(memset(&i, 0, sizeof i), P);
+
+ i.section = DNS_S_ALL & ~DNS_S_QD;
+ i.name = host;
+ i.type = DNS_T_CNAME;
+
+ if (!dns_rr_grep(&rr, 1, &i, P, &error))
+ break;
+
+ if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P)))
+ goto error;
+ }
+
+ return dns_strlcpy(dst, host, lim);
+error:
+ *error_ = error;
+
+ return 0;
+} /* dns_d_cname() */
+
+
+/*
+ * R E S O U R C E R E C O R D R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) {
+ unsigned char dn[DNS_D_MAXNAME + 1];
+ union dns_any any;
+ size_t len;
+ int error;
+
+ if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error)))
+ return error;
+ else if (len >= sizeof dn)
+ return DNS_EILLEGAL;
+
+ if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q)))
+ return error;
+
+ return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any);
+} /* dns_rr_copy() */
+
+
+int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) {
+ unsigned short p = src;
+
+ if (src >= P->end)
+ goto invalid;
+
+ rr->dn.p = p;
+ rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p;
+
+ if (P->end - p < 4)
+ goto invalid;
+
+ rr->type = ((0xff & P->data[p + 0]) << 8)
+ | ((0xff & P->data[p + 1]) << 0);
+
+ rr->class = ((0xff & P->data[p + 2]) << 8)
+ | ((0xff & P->data[p + 3]) << 0);
+
+ p += 4;
+
+ if (src < dns_p_qend(P)) {
+ rr->section = DNS_S_QUESTION;
+
+ rr->ttl = 0;
+ rr->rd.p = 0;
+ rr->rd.len = 0;
+
+ return 0;
+ }
+
+ if (P->end - p < 4)
+ goto invalid;
+
+ rr->ttl = ((0xff & P->data[p + 0]) << 24)
+ | ((0xff & P->data[p + 1]) << 16)
+ | ((0xff & P->data[p + 2]) << 8)
+ | ((0xff & P->data[p + 3]) << 0);
+ if (rr->type != DNS_T_OPT)
+ rr->ttl = DNS_PP_MIN(rr->ttl, 0x7fffffffU);
+
+ p += 4;
+
+ if (P->end - p < 2)
+ goto invalid;
+
+ rr->rd.len = ((0xff & P->data[p + 0]) << 8)
+ | ((0xff & P->data[p + 1]) << 0);
+ rr->rd.p = p + 2;
+
+ p += 2;
+
+ if (P->end - p < rr->rd.len)
+ goto invalid;
+
+ return 0;
+invalid:
+ return DNS_EILLEGAL;
+} /* dns_rr_parse() */
+
+
+static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) {
+ unsigned short rp, rdlen;
+
+ rp = dns_d_skip(src, P);
+
+ if (P->end - rp < 4)
+ return P->end - src;
+
+ rp += 4; /* TYPE, CLASS */
+
+ if (rp <= dns_p_qend(P))
+ return rp - src;
+
+ if (P->end - rp < 6)
+ return P->end - src;
+
+ rp += 6; /* TTL, RDLEN */
+
+ rdlen = ((0xff & P->data[rp - 2]) << 8)
+ | ((0xff & P->data[rp - 1]) << 0);
+
+ if (P->end - rp < rdlen)
+ return P->end - src;
+
+ rp += rdlen;
+
+ return rp - src;
+} /* dns_rr_len() */
+
+
+unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) {
+ return src + dns_rr_len(src, P);
+} /* dns_rr_skip() */
+
+
+static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) {
+ enum dns_section section;
+ unsigned count, index;
+ unsigned short rp;
+
+ if (src >= P->memo.qd.base && src < P->memo.qd.end)
+ return DNS_S_QD;
+ if (src >= P->memo.an.base && src < P->memo.an.end)
+ return DNS_S_AN;
+ if (src >= P->memo.ns.base && src < P->memo.ns.end)
+ return DNS_S_NS;
+ if (src >= P->memo.ar.base && src < P->memo.ar.end)
+ return DNS_S_AR;
+
+ /* NOTE: Possibly bad memoization. Try it the hard-way. */
+
+ for (rp = 12, index = 0; rp < src && rp < P->end; index++)
+ rp = dns_rr_skip(rp, P);
+
+ section = DNS_S_QD;
+ count = dns_p_count(P, section);
+
+ while (index >= count && section <= DNS_S_AR) {
+ section <<= 1;
+ count += dns_p_count(P, section);
+ }
+
+ return DNS_S_ALL & section;
+} /* dns_rr_section() */
+
+
+static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) {
+ struct dns_rr rr;
+ int error;
+
+ if ((error = dns_rr_parse(&rr, src, P)))
+ return 0;
+
+ return rr.type;
+} /* dns_rr_type() */
+
+
+int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) {
+ char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1];
+ union dns_any any0, any1;
+ int cmp, error;
+ size_t len;
+
+ if ((cmp = r0->type - r1->type))
+ return cmp;
+
+ if ((cmp = r0->class - r1->class))
+ return cmp;
+
+ /*
+ * FIXME: Do label-by-label comparison to handle illegally long names?
+ */
+
+ if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error))
+ || len >= sizeof host0)
+ return -1;
+
+ if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error))
+ || len >= sizeof host1)
+ return 1;
+
+ if ((cmp = strcasecmp(host0, host1)))
+ return cmp;
+
+ if (DNS_S_QD & (r0->section | r1->section)) {
+ if (r0->section == r1->section)
+ return 0;
+
+ return (r0->section == DNS_S_QD)? -1 : 1;
+ }
+
+ if ((error = dns_any_parse(&any0, r0, P0)))
+ return -1;
+
+ if ((error = dns_any_parse(&any1, r1, P1)))
+ return 1;
+
+ return dns_any_cmp(&any0, r0->type, &any1, r1->type);
+} /* dns_rr_cmp() */
+
+
+static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) {
+ struct dns_rr rr1;
+
+ dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) {
+ if (0 == dns_rr_cmp(rr0, P0, &rr1, P1))
+ return 1;
+ }
+
+ return 0;
+} /* dns_rr_exists() */
+
+
+static unsigned short dns_rr_offset(struct dns_rr *rr) {
+ return rr->dn.p;
+} /* dns_rr_offset() */
+
+
+static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) {
+ if (i->section && !(rr->section & i->section))
+ return 0;
+
+ if (i->type && rr->type != i->type && i->type != DNS_T_ALL)
+ return 0;
+
+ if (i->class && rr->class != i->class && i->class != DNS_C_ANY)
+ return 0;
+
+ if (i->name) {
+ char dn[DNS_D_MAXNAME + 1];
+ size_t len;
+ int error;
+
+ if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error))
+ || len >= sizeof dn)
+ return 0;
+
+ if (0 != strcasecmp(dn, i->name))
+ return 0;
+ }
+
+ if (i->data && i->type && rr->section > DNS_S_QD) {
+ union dns_any rd;
+ int error;
+
+ if ((error = dns_any_parse(&rd, rr, P)))
+ return 0;
+
+ if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type))
+ return 0;
+ }
+
+ return 1;
+} /* dns_rr_i_match() */
+
+
+static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) {
+ unsigned short rp;
+ struct dns_rr r0, rr;
+ int error;
+
+ if ((i->section & DNS_S_QD) && P->memo.qd.base)
+ rp = P->memo.qd.base;
+ else if ((i->section & DNS_S_AN) && P->memo.an.base)
+ rp = P->memo.an.base;
+ else if ((i->section & DNS_S_NS) && P->memo.ns.base)
+ rp = P->memo.ns.base;
+ else if ((i->section & DNS_S_AR) && P->memo.ar.base)
+ rp = P->memo.ar.base;
+ else
+ rp = 12;
+
+ for (; rp < P->end; rp = dns_rr_skip(rp, P)) {
+ if ((error = dns_rr_parse(&rr, rp, P)))
+ continue;
+
+ rr.section = dns_rr_section(rp, P);
+
+ if (!dns_rr_i_match(&rr, i, P))
+ continue;
+
+ r0 = rr;
+
+ goto lower;
+ }
+
+ return P->end;
+lower:
+ if (i->sort == &dns_rr_i_packet)
+ return dns_rr_offset(&r0);
+
+ while ((rp = dns_rr_skip(rp, P)) < P->end) {
+ if ((error = dns_rr_parse(&rr, rp, P)))
+ continue;
+
+ rr.section = dns_rr_section(rp, P);
+
+ if (!dns_rr_i_match(&rr, i, P))
+ continue;
+
+ if (i->sort(&rr, &r0, i, P) < 0)
+ r0 = rr;
+ }
+
+ return dns_rr_offset(&r0);
+} /* dns_rr_i_start() */
+
+
+static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) {
+ struct dns_rr r0, r1, rr;
+ int error;
+
+ if ((error = dns_rr_parse(&r0, rp, P)))
+ return P->end;
+
+ r0.section = dns_rr_section(rp, P);
+
+ rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12;
+
+ for (; rp < P->end; rp = dns_rr_skip(rp, P)) {
+ if ((error = dns_rr_parse(&rr, rp, P)))
+ continue;
+
+ rr.section = dns_rr_section(rp, P);
+
+ if (!dns_rr_i_match(&rr, i, P))
+ continue;
+
+ if (i->sort(&rr, &r0, i, P) <= 0)
+ continue;
+
+ r1 = rr;
+
+ goto lower;
+ }
+
+ return P->end;
+lower:
+ if (i->sort == &dns_rr_i_packet)
+ return dns_rr_offset(&r1);
+
+ while ((rp = dns_rr_skip(rp, P)) < P->end) {
+ if ((error = dns_rr_parse(&rr, rp, P)))
+ continue;
+
+ rr.section = dns_rr_section(rp, P);
+
+ if (!dns_rr_i_match(&rr, i, P))
+ continue;
+
+ if (i->sort(&rr, &r0, i, P) <= 0)
+ continue;
+
+ if (i->sort(&rr, &r1, i, P) >= 0)
+ continue;
+
+ r1 = rr;
+ }
+
+ return dns_rr_offset(&r1);
+} /* dns_rr_i_skip() */
+
+
+int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
+ (void)i;
+ (void)P;
+
+ return (int)a->dn.p - (int)b->dn.p;
+} /* dns_rr_i_packet() */
+
+
+int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
+ int cmp;
+
+ (void)i;
+
+ if ((cmp = a->section - b->section))
+ return cmp;
+
+ if (a->type != b->type)
+ return (int)a->dn.p - (int)b->dn.p;
+
+ return dns_rr_cmp(a, P, b, P);
+} /* dns_rr_i_order() */
+
+
+int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
+ int cmp;
+
+ (void)i;
+ (void)P;
+
+ while (!i->state.regs[0])
+ i->state.regs[0] = dns_random();
+
+ if ((cmp = a->section - b->section))
+ return cmp;
+
+ return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]);
+} /* dns_rr_i_shuffle() */
+
+
+struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P) {
+ static const struct dns_rr_i i_initializer;
+
+ (void)P;
+
+ i->state = i_initializer.state;
+ i->saved = i->state;
+
+ return i;
+} /* dns_rr_i_init() */
+
+
+unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) {
+ unsigned count = 0;
+ int error;
+
+ switch (i->state.exec) {
+ case 0:
+ if (!i->sort)
+ i->sort = &dns_rr_i_packet;
+
+ i->state.next = dns_rr_i_start(i, P);
+ i->state.exec++;
+
+ /* FALL THROUGH */
+ case 1:
+ while (count < lim && i->state.next < P->end) {
+ if ((error = dns_rr_parse(rr, i->state.next, P)))
+ goto error;
+
+ rr->section = dns_rr_section(i->state.next, P);
+
+ rr++;
+ count++;
+ i->state.count++;
+
+ i->state.next = dns_rr_i_skip(i->state.next, i, P);
+ } /* while() */
+
+ break;
+ } /* switch() */
+
+ return count;
+error:
+ *error_ = error;
+
+ return count;
+} /* dns_rr_grep() */
+
+
+size_t dns_rr_print(void *_dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *_error) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ union dns_any any;
+ size_t n;
+ int error;
+
+ if (rr->section == DNS_S_QD)
+ dns_b_putc(&dst, ';');
+
+ if (!(n = dns_d_expand(any.ns.host, sizeof any.ns.host, rr->dn.p, P, &error)))
+ goto error;
+ dns_b_put(&dst, any.ns.host, DNS_PP_MIN(n, sizeof any.ns.host - 1));
+
+ if (rr->section != DNS_S_QD) {
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, rr->ttl, 0);
+ }
+
+ dns_b_putc(&dst, ' ');
+ dns_b_puts(&dst, dns_strclass(rr->class));
+ dns_b_putc(&dst, ' ');
+ dns_b_puts(&dst, dns_strtype(rr->type));
+
+ if (rr->section == DNS_S_QD)
+ goto epilog;
+
+ dns_b_putc(&dst, ' ');
+
+ if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P)))
+ goto error;
+
+ n = dns_any_print(dst.p, dst.pe - dst.p, &any, rr->type);
+ dst.p += DNS_PP_MIN(n, (size_t)(dst.pe - dst.p));
+epilog:
+ return dns_b_strllen(&dst);
+error:
+ *_error = error;
+
+ return 0;
+} /* dns_rr_print() */
+
+
+int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) {
+ unsigned long addr;
+
+ if (rr->rd.len != 4)
+ return DNS_EILLEGAL;
+
+ addr = ((0xffU & P->data[rr->rd.p + 0]) << 24)
+ | ((0xffU & P->data[rr->rd.p + 1]) << 16)
+ | ((0xffU & P->data[rr->rd.p + 2]) << 8)
+ | ((0xffU & P->data[rr->rd.p + 3]) << 0);
+
+ a->addr.s_addr = htonl(addr);
+
+ return 0;
+} /* dns_a_parse() */
+
+
+int dns_a_push(struct dns_packet *P, struct dns_a *a) {
+ unsigned long addr;
+
+ if (P->size - P->end < 6)
+ return DNS_ENOBUFS;
+
+ P->data[P->end++] = 0x00;
+ P->data[P->end++] = 0x04;
+
+ addr = ntohl(a->addr.s_addr);
+
+ P->data[P->end++] = 0xffU & (addr >> 24);
+ P->data[P->end++] = 0xffU & (addr >> 16);
+ P->data[P->end++] = 0xffU & (addr >> 8);
+ P->data[P->end++] = 0xffU & (addr >> 0);
+
+ return 0;
+} /* dns_a_push() */
+
+
+size_t dns_a_arpa(void *_dst, size_t lim, const struct dns_a *a) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ unsigned long octets = ntohl(a->addr.s_addr);
+ unsigned i;
+
+ for (i = 0; i < 4; i++) {
+ dns_b_fmtju(&dst, 0xff & octets, 0);
+ dns_b_putc(&dst, '.');
+ octets >>= 8;
+ }
+
+ dns_b_puts(&dst, "in-addr.arpa.");
+
+ return dns_b_strllen(&dst);
+} /* dns_a_arpa() */
+
+
+int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) {
+ if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr))
+ return -1;
+ if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr))
+ return 1;
+
+ return 0;
+} /* dns_a_cmp() */
+
+
+size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) {
+ char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0";
+
+ dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr);
+
+ return dns_strlcpy(dst, addr, lim);
+} /* dns_a_print() */
+
+
+int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) {
+ if (rr->rd.len != sizeof aaaa->addr.s6_addr)
+ return DNS_EILLEGAL;
+
+ memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr);
+
+ return 0;
+} /* dns_aaaa_parse() */
+
+
+int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) {
+ if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr)
+ return DNS_ENOBUFS;
+
+ P->data[P->end++] = 0x00;
+ P->data[P->end++] = 0x10;
+
+ memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr);
+
+ P->end += sizeof aaaa->addr.s6_addr;
+
+ return 0;
+} /* dns_aaaa_push() */
+
+
+int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) {
+ unsigned i;
+ int cmp;
+
+ for (i = 0; i < lengthof(a->addr.s6_addr); i++) {
+ if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i])))
+ return cmp;
+ }
+
+ return 0;
+} /* dns_aaaa_cmp() */
+
+
+size_t dns_aaaa_arpa(void *_dst, size_t lim, const struct dns_aaaa *aaaa) {
+ static const unsigned char hex[16] = "0123456789abcdef";
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ unsigned nyble;
+ int i, j;
+
+ for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) {
+ nyble = aaaa->addr.s6_addr[i];
+
+ for (j = 0; j < 2; j++) {
+ dns_b_putc(&dst, hex[0x0f & nyble]);
+ dns_b_putc(&dst, '.');
+ nyble >>= 4;
+ }
+ }
+
+ dns_b_puts(&dst, "ip6.arpa.");
+
+ return dns_b_strllen(&dst);
+} /* dns_aaaa_arpa() */
+
+
+size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) {
+ char addr[INET6_ADDRSTRLEN + 1] = "::";
+
+ dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr);
+
+ return dns_strlcpy(dst, addr, lim);
+} /* dns_aaaa_print() */
+
+
+int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) {
+ size_t len;
+ int error;
+
+ if (rr->rd.len < 3)
+ return DNS_EILLEGAL;
+
+ mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8))
+ | (0x00ff & (P->data[rr->rd.p + 1] << 0));
+
+ if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error)))
+ return error;
+ else if (len >= sizeof mx->host)
+ return DNS_EILLEGAL;
+
+ return 0;
+} /* dns_mx_parse() */
+
+
+int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) {
+ size_t end, len;
+ int error;
+
+ if (P->size - P->end < 5)
+ return DNS_ENOBUFS;
+
+ end = P->end;
+ P->end += 2;
+
+ P->data[P->end++] = 0xff & (mx->preference >> 8);
+ P->data[P->end++] = 0xff & (mx->preference >> 0);
+
+ if ((error = dns_d_push(P, mx->host, strlen(mx->host))))
+ goto error;
+
+ len = P->end - end - 2;
+
+ P->data[end + 0] = 0xff & (len >> 8);
+ P->data[end + 1] = 0xff & (len >> 0);
+
+ return 0;
+error:
+ P->end = end;
+
+ return error;
+} /* dns_mx_push() */
+
+
+int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) {
+ int cmp;
+
+ if ((cmp = a->preference - b->preference))
+ return cmp;
+
+ return strcasecmp(a->host, b->host);
+} /* dns_mx_cmp() */
+
+
+size_t dns_mx_print(void *_dst, size_t lim, struct dns_mx *mx) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+
+ dns_b_fmtju(&dst, mx->preference, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_puts(&dst, mx->host);
+
+ return dns_b_strllen(&dst);
+} /* dns_mx_print() */
+
+
+size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) {
+ return dns_strlcpy(dst, mx->host, lim);
+} /* dns_mx_cname() */
+
+
+int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) {
+ size_t len;
+ int error;
+
+ if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error)))
+ return error;
+ else if (len >= sizeof ns->host)
+ return DNS_EILLEGAL;
+
+ return 0;
+} /* dns_ns_parse() */
+
+
+int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) {
+ size_t end, len;
+ int error;
+
+ if (P->size - P->end < 3)
+ return DNS_ENOBUFS;
+
+ end = P->end;
+ P->end += 2;
+
+ if ((error = dns_d_push(P, ns->host, strlen(ns->host))))
+ goto error;
+
+ len = P->end - end - 2;
+
+ P->data[end + 0] = 0xff & (len >> 8);
+ P->data[end + 1] = 0xff & (len >> 0);
+
+ return 0;
+error:
+ P->end = end;
+
+ return error;
+} /* dns_ns_push() */
+
+
+int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) {
+ return strcasecmp(a->host, b->host);
+} /* dns_ns_cmp() */
+
+
+size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) {
+ return dns_strlcpy(dst, ns->host, lim);
+} /* dns_ns_print() */
+
+
+size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) {
+ return dns_strlcpy(dst, ns->host, lim);
+} /* dns_ns_cname() */
+
+
+int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) {
+ return dns_ns_parse((struct dns_ns *)cname, rr, P);
+} /* dns_cname_parse() */
+
+
+int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) {
+ return dns_ns_push(P, (struct dns_ns *)cname);
+} /* dns_cname_push() */
+
+
+int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) {
+ return strcasecmp(a->host, b->host);
+} /* dns_cname_cmp() */
+
+
+size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) {
+ return dns_ns_print(dst, lim, (struct dns_ns *)cname);
+} /* dns_cname_print() */
+
+
+size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) {
+ return dns_strlcpy(dst, cname->host, lim);
+} /* dns_cname_cname() */
+
+
+int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) {
+ struct { void *dst; size_t lim; } dn[] =
+ { { soa->mname, sizeof soa->mname },
+ { soa->rname, sizeof soa->rname } };
+ unsigned *ts[] =
+ { &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum };
+ unsigned short rp;
+ unsigned i, j, n;
+ int error;
+
+ /* MNAME / RNAME */
+ if ((rp = rr->rd.p) >= P->end)
+ return DNS_EILLEGAL;
+
+ for (i = 0; i < lengthof(dn); i++) {
+ if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error)))
+ return error;
+ else if (n >= dn[i].lim)
+ return DNS_EILLEGAL;
+
+ if ((rp = dns_d_skip(rp, P)) >= P->end)
+ return DNS_EILLEGAL;
+ }
+
+ /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */
+ for (i = 0; i < lengthof(ts); i++) {
+ for (j = 0; j < 4; j++, rp++) {
+ if (rp >= P->end)
+ return DNS_EILLEGAL;
+
+ *ts[i] <<= 8;
+ *ts[i] |= (0xff & P->data[rp]);
+ }
+ }
+
+ return 0;
+} /* dns_soa_parse() */
+
+
+int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) {
+ void *dn[] = { soa->mname, soa->rname };
+ unsigned ts[] = { (0xffffffff & soa->serial),
+ (0x7fffffff & soa->refresh),
+ (0x7fffffff & soa->retry),
+ (0x7fffffff & soa->expire),
+ (0xffffffff & soa->minimum) };
+ unsigned i, j;
+ size_t end, len;
+ int error;
+
+ end = P->end;
+
+ if ((P->end += 2) >= P->size)
+ goto toolong;
+
+ /* MNAME / RNAME */
+ for (i = 0; i < lengthof(dn); i++) {
+ if ((error = dns_d_push(P, dn[i], strlen(dn[i]))))
+ goto error;
+ }
+
+ /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */
+ for (i = 0; i < lengthof(ts); i++) {
+ if ((P->end += 4) >= P->size)
+ goto toolong;
+
+ for (j = 1; j <= 4; j++) {
+ P->data[P->end - j] = (0xff & ts[i]);
+ ts[i] >>= 8;
+ }
+ }
+
+ len = P->end - end - 2;
+ P->data[end + 0] = (0xff & (len >> 8));
+ P->data[end + 1] = (0xff & (len >> 0));
+
+ return 0;
+toolong:
+ error = DNS_ENOBUFS;
+
+ /* FALL THROUGH */
+error:
+ P->end = end;
+
+ return error;
+} /* dns_soa_push() */
+
+
+int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) {
+ int cmp;
+
+ if ((cmp = strcasecmp(a->mname, b->mname)))
+ return cmp;
+
+ if ((cmp = strcasecmp(a->rname, b->rname)))
+ return cmp;
+
+ if (a->serial > b->serial)
+ return -1;
+ else if (a->serial < b->serial)
+ return 1;
+
+ if (a->refresh > b->refresh)
+ return -1;
+ else if (a->refresh < b->refresh)
+ return 1;
+
+ if (a->retry > b->retry)
+ return -1;
+ else if (a->retry < b->retry)
+ return 1;
+
+ if (a->expire > b->expire)
+ return -1;
+ else if (a->expire < b->expire)
+ return 1;
+
+ if (a->minimum > b->minimum)
+ return -1;
+ else if (a->minimum < b->minimum)
+ return 1;
+
+ return 0;
+} /* dns_soa_cmp() */
+
+
+size_t dns_soa_print(void *_dst, size_t lim, struct dns_soa *soa) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+
+ dns_b_puts(&dst, soa->mname);
+ dns_b_putc(&dst, ' ');
+ dns_b_puts(&dst, soa->rname);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, soa->serial, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, soa->refresh, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, soa->retry, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, soa->expire, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, soa->minimum, 0);
+
+ return dns_b_strllen(&dst);
+} /* dns_soa_print() */
+
+
+int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) {
+ unsigned short rp;
+ unsigned i;
+ size_t n;
+ int error;
+
+ memset(srv, '\0', sizeof *srv);
+
+ rp = rr->rd.p;
+
+ if (rr->rd.len < 7)
+ return DNS_EILLEGAL;
+
+ for (i = 0; i < 2; i++, rp++) {
+ srv->priority <<= 8;
+ srv->priority |= (0xff & P->data[rp]);
+ }
+
+ for (i = 0; i < 2; i++, rp++) {
+ srv->weight <<= 8;
+ srv->weight |= (0xff & P->data[rp]);
+ }
+
+ for (i = 0; i < 2; i++, rp++) {
+ srv->port <<= 8;
+ srv->port |= (0xff & P->data[rp]);
+ }
+
+ if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error)))
+ return error;
+ else if (n >= sizeof srv->target)
+ return DNS_EILLEGAL;
+
+ return 0;
+} /* dns_srv_parse() */
+
+
+int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) {
+ size_t end, len;
+ int error;
+
+ end = P->end;
+
+ if (P->size - P->end < 2)
+ goto toolong;
+
+ P->end += 2;
+
+ if (P->size - P->end < 6)
+ goto toolong;
+
+ P->data[P->end++] = 0xff & (srv->priority >> 8);
+ P->data[P->end++] = 0xff & (srv->priority >> 0);
+
+ P->data[P->end++] = 0xff & (srv->weight >> 8);
+ P->data[P->end++] = 0xff & (srv->weight >> 0);
+
+ P->data[P->end++] = 0xff & (srv->port >> 8);
+ P->data[P->end++] = 0xff & (srv->port >> 0);
+
+ if (0 == (len = dns_d_comp(&P->data[P->end], P->size - P->end, srv->target, strlen(srv->target), P, &error)))
+ goto error;
+ else if (P->size - P->end < len)
+ goto toolong;
+
+ P->end += len;
+
+ if (P->end > 65535)
+ goto toolong;
+
+ len = P->end - end - 2;
+
+ P->data[end + 0] = 0xff & (len >> 8);
+ P->data[end + 1] = 0xff & (len >> 0);
+
+ return 0;
+toolong:
+ error = DNS_ENOBUFS;
+
+ /* FALL THROUGH */
+error:
+ P->end = end;
+
+ return error;
+} /* dns_srv_push() */
+
+
+int dns_srv_cmp(const struct dns_srv *a, const struct dns_srv *b) {
+ int cmp;
+
+ if ((cmp = a->priority - b->priority))
+ return cmp;
+
+ /*
+ * FIXME: We need some sort of random seed to implement the dynamic
+ * weighting required by RFC 2782.
+ */
+ if ((cmp = a->weight - b->weight))
+ return cmp;
+
+ if ((cmp = a->port - b->port))
+ return cmp;
+
+ return strcasecmp(a->target, b->target);
+} /* dns_srv_cmp() */
+
+
+size_t dns_srv_print(void *_dst, size_t lim, struct dns_srv *srv) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+
+ dns_b_fmtju(&dst, srv->priority, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, srv->weight, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, srv->port, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_puts(&dst, srv->target);
+
+ return dns_b_strllen(&dst);
+} /* dns_srv_print() */
+
+
+size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) {
+ return dns_strlcpy(dst, srv->target, lim);
+} /* dns_srv_cname() */
+
+
+unsigned int dns_opt_ttl(const struct dns_opt *opt) {
+ unsigned int ttl = 0;
+
+ ttl |= (0xffU & opt->rcode) << 24;
+ ttl |= (0xffU & opt->version) << 16;
+ ttl |= (0xffffU & opt->flags) << 0;
+
+ return ttl;
+} /* dns_opt_ttl() */
+
+
+unsigned short dns_opt_class(const struct dns_opt *opt) {
+ return opt->maxudp;
+} /* dns_opt_class() */
+
+
+struct dns_opt *dns_opt_init(struct dns_opt *opt, size_t size) {
+ assert(size >= offsetof(struct dns_opt, data));
+
+ opt->size = size - offsetof(struct dns_opt, data);
+ opt->len = 0;
+
+ opt->rcode = 0;
+ opt->version = 0;
+ opt->maxudp = 0;
+
+ return opt;
+} /* dns_opt_init() */
+
+
+static union dns_any *dns_opt_initany(union dns_any *any, size_t size) {
+ return dns_opt_init(&any->opt, size), any;
+} /* dns_opt_initany() */
+
+
+int dns_opt_parse(struct dns_opt *opt, struct dns_rr *rr, struct dns_packet *P) {
+ const struct dns_buf src = DNS_B_FROM(&P->data[rr->rd.p], rr->rd.len);
+ struct dns_buf dst = DNS_B_INTO(opt->data, opt->size);
+ int error;
+
+ opt->rcode = 0xfff & ((rr->ttl >> 20) | dns_header(P)->rcode);
+ opt->version = 0xff & (rr->ttl >> 16);
+ opt->flags = 0xffff & rr->ttl;
+ opt->maxudp = 0xffff & rr->class;
+
+ while (src.p < src.pe) {
+ int code, len;
+
+ if (-1 == (code = dns_b_get16(&src, -1)))
+ return src.error;
+ if (-1 == (len = dns_b_get16(&src, -1)))
+ return src.error;
+
+ switch (code) {
+ default:
+ dns_b_put16(&dst, code);
+ dns_b_put16(&dst, len);
+ if ((error = dns_b_move(&dst, &src, len)))
+ return error;
+ break;
+ }
+ }
+
+ return 0;
+} /* dns_opt_parse() */
+
+
+int dns_opt_push(struct dns_packet *P, struct dns_opt *opt) {
+ const struct dns_buf src = DNS_B_FROM(opt->data, opt->len);
+ struct dns_buf dst = DNS_B_INTO(&P->data[P->end], (P->size - P->end));
+ int error;
+
+ /* rdata length (see below) */
+ if ((error = dns_b_put16(&dst, 0)))
+ goto error;
+
+ /* ... push known options here */
+
+ /* push opaque option data */
+ if ((error = dns_b_move(&dst, &src, (size_t)(src.pe - src.p))))
+ goto error;
+
+ /* rdata length */
+ if ((error = dns_b_pput16(&dst, dns_b_tell(&dst) - 2, 0)))
+ goto error;
+
+#if !DNS_DEBUG_OPT_FORMERR
+ P->end += dns_b_tell(&dst);
+#endif
+
+ return 0;
+error:
+ return error;
+} /* dns_opt_push() */
+
+
+int dns_opt_cmp(const struct dns_opt *a, const struct dns_opt *b) {
+ (void)a;
+ (void)b;
+
+ return -1;
+} /* dns_opt_cmp() */
+
+
+size_t dns_opt_print(void *_dst, size_t lim, struct dns_opt *opt) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ size_t p;
+
+ dns_b_putc(&dst, '"');
+
+ for (p = 0; p < opt->len; p++) {
+ dns_b_putc(&dst, '\\');
+ dns_b_fmtju(&dst, opt->data[p], 3);
+ }
+
+ dns_b_putc(&dst, '"');
+
+ return dns_b_strllen(&dst);
+} /* dns_opt_print() */
+
+
+int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) {
+ return dns_ns_parse((struct dns_ns *)ptr, rr, P);
+} /* dns_ptr_parse() */
+
+
+int dns_ptr_push(struct dns_packet *P, struct dns_ptr *ptr) {
+ return dns_ns_push(P, (struct dns_ns *)ptr);
+} /* dns_ptr_push() */
+
+
+size_t dns_ptr_qname(void *dst, size_t lim, int af, void *addr) {
+ switch (af) {
+ case AF_INET6:
+ return dns_aaaa_arpa(dst, lim, addr);
+ case AF_INET:
+ return dns_a_arpa(dst, lim, addr);
+ default: {
+ struct dns_a a;
+ a.addr.s_addr = INADDR_NONE;
+ return dns_a_arpa(dst, lim, &a);
+ }
+ }
+} /* dns_ptr_qname() */
+
+
+int dns_ptr_cmp(const struct dns_ptr *a, const struct dns_ptr *b) {
+ return strcasecmp(a->host, b->host);
+} /* dns_ptr_cmp() */
+
+
+size_t dns_ptr_print(void *dst, size_t lim, struct dns_ptr *ptr) {
+ return dns_ns_print(dst, lim, (struct dns_ns *)ptr);
+} /* dns_ptr_print() */
+
+
+size_t dns_ptr_cname(void *dst, size_t lim, struct dns_ptr *ptr) {
+ return dns_strlcpy(dst, ptr->host, lim);
+} /* dns_ptr_cname() */
+
+
+int dns_sshfp_parse(struct dns_sshfp *fp, struct dns_rr *rr, struct dns_packet *P) {
+ unsigned p = rr->rd.p, pe = rr->rd.p + rr->rd.len;
+
+ if (pe - p < 2)
+ return DNS_EILLEGAL;
+
+ fp->algo = P->data[p++];
+ fp->type = P->data[p++];
+
+ switch (fp->type) {
+ case DNS_SSHFP_SHA1:
+ if (pe - p < sizeof fp->digest.sha1)
+ return DNS_EILLEGAL;
+
+ memcpy(fp->digest.sha1, &P->data[p], sizeof fp->digest.sha1);
+
+ break;
+ default:
+ break;
+ } /* switch() */
+
+ return 0;
+} /* dns_sshfp_parse() */
+
+
+int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) {
+ unsigned p = P->end, pe = P->size, n;
+
+ if (pe - p < 4)
+ return DNS_ENOBUFS;
+
+ p += 2;
+ P->data[p++] = 0xff & fp->algo;
+ P->data[p++] = 0xff & fp->type;
+
+ switch (fp->type) {
+ case DNS_SSHFP_SHA1:
+ if (pe - p < sizeof fp->digest.sha1)
+ return DNS_ENOBUFS;
+
+ memcpy(&P->data[p], fp->digest.sha1, sizeof fp->digest.sha1);
+ p += sizeof fp->digest.sha1;
+
+ break;
+ default:
+ return DNS_EILLEGAL;
+ } /* switch() */
+
+ n = p - P->end - 2;
+ P->data[P->end++] = 0xff & (n >> 8);
+ P->data[P->end++] = 0xff & (n >> 0);
+ P->end = p;
+
+ return 0;
+} /* dns_sshfp_push() */
+
+
+int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) {
+ int cmp;
+
+ if ((cmp = a->algo - b->algo) || (cmp = a->type - b->type))
+ return cmp;
+
+ switch (a->type) {
+ case DNS_SSHFP_SHA1:
+ return memcmp(a->digest.sha1, b->digest.sha1, sizeof a->digest.sha1);
+ default:
+ return 0;
+ } /* switch() */
+
+ /* NOT REACHED */
+} /* dns_sshfp_cmp() */
+
+
+size_t dns_sshfp_print(void *_dst, size_t lim, struct dns_sshfp *fp) {
+ static const unsigned char hex[16] = "0123456789abcdef";
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ size_t i;
+
+ dns_b_fmtju(&dst, fp->algo, 0);
+ dns_b_putc(&dst, ' ');
+ dns_b_fmtju(&dst, fp->type, 0);
+ dns_b_putc(&dst, ' ');
+
+ switch (fp->type) {
+ case DNS_SSHFP_SHA1:
+ for (i = 0; i < sizeof fp->digest.sha1; i++) {
+ dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 4)]);
+ dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 0)]);
+ }
+
+ break;
+ default:
+ dns_b_putc(&dst, '0');
+
+ break;
+ } /* switch() */
+
+ return dns_b_strllen(&dst);
+} /* dns_sshfp_print() */
+
+
+struct dns_txt *dns_txt_init(struct dns_txt *txt, size_t size) {
+ assert(size > offsetof(struct dns_txt, data));
+
+ txt->size = size - offsetof(struct dns_txt, data);
+ txt->len = 0;
+
+ return txt;
+} /* dns_txt_init() */
+
+
+static union dns_any *dns_txt_initany(union dns_any *any, size_t size) {
+ /* NB: union dns_any is already initialized as struct dns_txt */
+ (void)size;
+ return any;
+} /* dns_txt_initany() */
+
+
+int dns_txt_parse(struct dns_txt *txt, struct dns_rr *rr, struct dns_packet *P) {
+ struct { unsigned char *b; size_t p, end; } dst, src;
+ unsigned n;
+
+ dst.b = txt->data;
+ dst.p = 0;
+ dst.end = txt->size;
+
+ src.b = P->data;
+ src.p = rr->rd.p;
+ src.end = src.p + rr->rd.len;
+
+ while (src.p < src.end) {
+ n = 0xff & P->data[src.p++];
+
+ if (src.end - src.p < n || dst.end - dst.p < n)
+ return DNS_EILLEGAL;
+
+ memcpy(&dst.b[dst.p], &src.b[src.p], n);
+
+ dst.p += n;
+ src.p += n;
+ }
+
+ txt->len = dst.p;
+
+ return 0;
+} /* dns_txt_parse() */
+
+
+int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) {
+ struct { unsigned char *b; size_t p, end; } dst, src;
+ unsigned n;
+
+ dst.b = P->data;
+ dst.p = P->end;
+ dst.end = P->size;
+
+ src.b = txt->data;
+ src.p = 0;
+ src.end = txt->len;
+
+ if (dst.end - dst.p < 2)
+ return DNS_ENOBUFS;
+
+ n = txt->len + ((txt->len + 254) / 255);
+
+ dst.b[dst.p++] = 0xff & (n >> 8);
+ dst.b[dst.p++] = 0xff & (n >> 0);
+
+ while (src.p < src.end) {
+ n = DNS_PP_MIN(255, src.end - src.p);
+
+ if (dst.p >= dst.end)
+ return DNS_ENOBUFS;
+
+ dst.b[dst.p++] = n;
+
+ if (dst.end - dst.p < n)
+ return DNS_ENOBUFS;
+
+ memcpy(&dst.b[dst.p], &src.b[src.p], n);
+
+ dst.p += n;
+ src.p += n;
+ }
+
+ P->end = dst.p;
+
+ return 0;
+} /* dns_txt_push() */
+
+
+int dns_txt_cmp(const struct dns_txt *a, const struct dns_txt *b) {
+ (void)a;
+ (void)b;
+
+ return -1;
+} /* dns_txt_cmp() */
+
+
+size_t dns_txt_print(void *_dst, size_t lim, struct dns_txt *txt) {
+ struct dns_buf src = DNS_B_FROM(txt->data, txt->len);
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ unsigned i;
+
+ if (src.p < src.pe) {
+ do {
+ dns_b_putc(&dst, '"');
+
+ for (i = 0; i < 256 && src.p < src.pe; i++, src.p++) {
+ if (*src.p < 32 || *src.p > 126 || *src.p == '"' || *src.p == '\\') {
+ dns_b_putc(&dst, '\\');
+ dns_b_fmtju(&dst, *src.p, 3);
+ } else {
+ dns_b_putc(&dst, *src.p);
+ }
+ }
+
+ dns_b_putc(&dst, '"');
+ dns_b_putc(&dst, ' ');
+ } while (src.p < src.pe);
+
+ dns_b_popc(&dst);
+ } else {
+ dns_b_putc(&dst, '"');
+ dns_b_putc(&dst, '"');
+ }
+
+ return dns_b_strllen(&dst);
+} /* dns_txt_print() */
+
+
+static const struct dns_rrtype {
+ enum dns_type type;
+ const char *name;
+ union dns_any *(*init)(union dns_any *, size_t);
+ int (*parse)();
+ int (*push)();
+ int (*cmp)();
+ size_t (*print)();
+ size_t (*cname)();
+} dns_rrtypes[] = {
+ { DNS_T_A, "A", 0, &dns_a_parse, &dns_a_push, &dns_a_cmp, &dns_a_print, 0, },
+ { DNS_T_AAAA, "AAAA", 0, &dns_aaaa_parse, &dns_aaaa_push, &dns_aaaa_cmp, &dns_aaaa_print, 0, },
+ { DNS_T_MX, "MX", 0, &dns_mx_parse, &dns_mx_push, &dns_mx_cmp, &dns_mx_print, &dns_mx_cname, },
+ { DNS_T_NS, "NS", 0, &dns_ns_parse, &dns_ns_push, &dns_ns_cmp, &dns_ns_print, &dns_ns_cname, },
+ { DNS_T_CNAME, "CNAME", 0, &dns_cname_parse, &dns_cname_push, &dns_cname_cmp, &dns_cname_print, &dns_cname_cname, },
+ { DNS_T_SOA, "SOA", 0, &dns_soa_parse, &dns_soa_push, &dns_soa_cmp, &dns_soa_print, 0, },
+ { DNS_T_SRV, "SRV", 0, &dns_srv_parse, &dns_srv_push, &dns_srv_cmp, &dns_srv_print, &dns_srv_cname, },
+ { DNS_T_OPT, "OPT", &dns_opt_initany, &dns_opt_parse, &dns_opt_push, &dns_opt_cmp, &dns_opt_print, 0, },
+ { DNS_T_PTR, "PTR", 0, &dns_ptr_parse, &dns_ptr_push, &dns_ptr_cmp, &dns_ptr_print, &dns_ptr_cname, },
+ { DNS_T_TXT, "TXT", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, },
+ { DNS_T_SPF, "SPF", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, },
+ { DNS_T_SSHFP, "SSHFP", 0, &dns_sshfp_parse, &dns_sshfp_push, &dns_sshfp_cmp, &dns_sshfp_print, 0, },
+ { DNS_T_AXFR, "AXFR", 0, 0, 0, 0, 0, 0, },
+}; /* dns_rrtypes[] */
+
+static const struct dns_rrtype *dns_rrtype(enum dns_type type) {
+ const struct dns_rrtype *t;
+
+ for (t = dns_rrtypes; t < endof(dns_rrtypes); t++) {
+ if (t->type == type && t->parse) {
+ return t;
+ }
+ }
+
+ return NULL;
+} /* dns_rrtype() */
+
+
+union dns_any *dns_any_init(union dns_any *any, size_t size) {
+ dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type");
+ return (union dns_any *)dns_txt_init(&any->rdata, size);
+} /* dns_any_init() */
+
+
+static size_t dns_any_sizeof(union dns_any *any) {
+ dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type");
+ return offsetof(struct dns_txt, data) + any->rdata.size;
+} /* dns_any_sizeof() */
+
+static union dns_any *dns_any_reinit(union dns_any *any, const struct dns_rrtype *t) {
+ return (t->init)? t->init(any, dns_any_sizeof(any)) : any;
+} /* dns_any_reinit() */
+
+int dns_any_parse(union dns_any *any, struct dns_rr *rr, struct dns_packet *P) {
+ const struct dns_rrtype *t;
+
+ if ((t = dns_rrtype(rr->type)))
+ return t->parse(dns_any_reinit(any, t), rr, P);
+
+ if (rr->rd.len > any->rdata.size)
+ return DNS_EILLEGAL;
+
+ memcpy(any->rdata.data, &P->data[rr->rd.p], rr->rd.len);
+ any->rdata.len = rr->rd.len;
+
+ return 0;
+} /* dns_any_parse() */
+
+
+int dns_any_push(struct dns_packet *P, union dns_any *any, enum dns_type type) {
+ const struct dns_rrtype *t;
+
+ if ((t = dns_rrtype(type)))
+ return t->push(P, any);
+
+ if (P->size - P->end < any->rdata.len + 2)
+ return DNS_ENOBUFS;
+
+ P->data[P->end++] = 0xff & (any->rdata.len >> 8);
+ P->data[P->end++] = 0xff & (any->rdata.len >> 0);
+
+ memcpy(&P->data[P->end], any->rdata.data, any->rdata.len);
+ P->end += any->rdata.len;
+
+ return 0;
+} /* dns_any_push() */
+
+
+int dns_any_cmp(const union dns_any *a, enum dns_type x, const union dns_any *b, enum dns_type y) {
+ const struct dns_rrtype *t;
+ int cmp;
+
+ if ((cmp = x - y))
+ return cmp;
+
+ if ((t = dns_rrtype(x)))
+ return t->cmp(a, b);
+
+ return -1;
+} /* dns_any_cmp() */
+
+
+size_t dns_any_print(void *_dst, size_t lim, union dns_any *any, enum dns_type type) {
+ const struct dns_rrtype *t;
+ struct dns_buf src, dst;
+
+ if ((t = dns_rrtype(type)))
+ return t->print(_dst, lim, any);
+
+ dns_b_from(&src, any->rdata.data, any->rdata.len);
+ dns_b_into(&dst, _dst, lim);
+
+ dns_b_putc(&dst, '"');
+
+ while (src.p < src.pe) {
+ dns_b_putc(&dst, '\\');
+ dns_b_fmtju(&dst, *src.p++, 3);
+ }
+
+ dns_b_putc(&dst, '"');
+
+ return dns_b_strllen(&dst);
+} /* dns_any_print() */
+
+
+size_t dns_any_cname(void *dst, size_t lim, union dns_any *any, enum dns_type type) {
+ const struct dns_rrtype *t;
+
+ if ((t = dns_rrtype(type)) && t->cname)
+ return t->cname(dst, lim, any);
+
+ return 0;
+} /* dns_any_cname() */
+
+
+/*
+ * E V E N T T R A C I N G R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#include <float.h> /* DBL_MANT_DIG */
+#include <inttypes.h> /* PRIu64 */
+
+/* for default trace ID generation try to fit in lua_Number, usually double */
+#define DNS_TRACE_ID_BITS DNS_PP_MIN(DBL_MANT_DIG, (sizeof (dns_trace_id_t) * CHAR_BIT)) /* assuming FLT_RADIX == 2 */
+#define DNS_TRACE_ID_MASK (((DNS_TRACE_ID_C(1) << (DNS_TRACE_ID_BITS - 1)) - 1) | (DNS_TRACE_ID_C(1) << (DNS_TRACE_ID_BITS - 1)))
+#define DNS_TRACE_ID_PRI PRIu64
+
+static inline dns_trace_id_t dns_trace_mkid(void) {
+ dns_trace_id_t id = 0;
+ unsigned r; /* return type of dns_random() */
+ const size_t id_bit = sizeof id * CHAR_BIT;
+ const size_t r_bit = sizeof r * CHAR_BIT;
+
+ for (size_t n = 0; n < id_bit; n += r_bit) {
+ r = dns_random();
+ id <<= r_bit;
+ id |= r;
+ }
+
+ return DNS_TRACE_ID_MASK & id;
+}
+
+struct dns_trace {
+ dns_atomic_t refcount;
+
+ FILE *fp;
+ dns_trace_id_t id;
+
+ struct {
+ struct dns_trace_cname {
+ char host[DNS_D_MAXNAME + 1];
+ struct sockaddr_storage addr;
+ } base[4];
+ size_t p;
+ } cnames;
+};
+
+static void dns_te_initname(struct sockaddr_storage *ss, int fd, int (*f)(int, struct sockaddr *, socklen_t *)) {
+ socklen_t n = sizeof *ss;
+
+ if (0 != f(fd, (struct sockaddr *)ss, &n))
+ goto unspec;
+
+ if (n > sizeof *ss)
+ goto unspec;
+
+ return;
+unspec:
+ memset(ss, '\0', sizeof *ss);
+ ss->ss_family = AF_UNSPEC;
+}
+
+static void dns_te_initnames(struct sockaddr_storage *local, struct sockaddr_storage *remote, int fd) {
+ dns_te_initname(local, fd, &getsockname);
+ dns_te_initname(remote, fd, &getpeername);
+}
+
+static struct dns_trace_event *dns_te_init(struct dns_trace_event *te, int type) {
+ /* NB: silence valgrind */
+ memset(te, '\0', offsetof(struct dns_trace_event, data));
+ te->type = type;
+ return te;
+}
+
+int dns_trace_abi(void) {
+ return DNS_TRACE_ABI;
+}
+
+struct dns_trace *dns_trace_open(FILE *fp, dns_error_t *error) {
+ static const struct dns_trace trace_initializer = { .refcount = 1 };
+ struct dns_trace *trace;
+
+ if (!(trace = malloc(sizeof *trace)))
+ goto syerr;
+
+ *trace = trace_initializer;
+
+ if (fp) {
+ trace->fp = fp;
+ } else if (!(fp = tmpfile())) {
+ goto syerr;
+ }
+
+ trace->id = dns_trace_mkid();
+
+ return trace;
+syerr:
+ *error = dns_syerr();
+
+ dns_trace_close(trace);
+
+ return NULL;
+} /* dns_trace_open() */
+
+void dns_trace_close(struct dns_trace *trace) {
+ if (!trace || 1 != dns_trace_release(trace))
+ return;
+
+ if (trace->fp)
+ fclose(trace->fp);
+ free(trace);
+} /* dns_trace_close() */
+
+dns_refcount_t dns_trace_acquire(struct dns_trace *trace) {
+ return dns_atomic_fetch_add(&trace->refcount);
+} /* dns_trace_acquire() */
+
+static struct dns_trace *dns_trace_acquire_p(struct dns_trace *trace) {
+ return (trace)? dns_trace_acquire(trace), trace : NULL;
+} /* dns_trace_acquire_p() */
+
+dns_refcount_t dns_trace_release(struct dns_trace *trace) {
+ return dns_atomic_fetch_sub(&trace->refcount);
+} /* dns_trace_release() */
+
+dns_trace_id_t dns_trace_id(struct dns_trace *trace) {
+ return trace->id;
+} /* dns_trace_id() */
+
+dns_trace_id_t dns_trace_setid(struct dns_trace *trace, dns_trace_id_t id) {
+ trace->id = (id)? id : dns_trace_mkid();
+ return trace->id;
+} /* dns_trace_setid() */
+
+struct dns_trace_event *dns_trace_get(struct dns_trace *trace, struct dns_trace_event **tp, dns_error_t *error) {
+ return dns_trace_fget(tp, trace->fp, error);
+} /* dns_trace_get() */
+
+dns_error_t dns_trace_put(struct dns_trace *trace, const struct dns_trace_event *te, const void *data, size_t datasize) {
+ return dns_trace_fput(te, data, datasize, trace->fp);
+} /* dns_trace_put() */
+
+struct dns_trace_event *dns_trace_tag(struct dns_trace *trace, struct dns_trace_event *te) {
+ struct timeval tv;
+
+ te->id = trace->id;
+ gettimeofday(&tv, NULL);
+ dns_tv2ts(&te->ts, &tv);
+ te->abi = DNS_TRACE_ABI;
+
+ return te;
+} /* dns_trace_tag() */
+
+static dns_error_t dns_trace_tag_and_put(struct dns_trace *trace, struct dns_trace_event *te, const void *data, size_t datasize) {
+ return dns_trace_put(trace, dns_trace_tag(trace, te), data, datasize);
+} /* dns_trace_tag_and_put() */
+
+struct dns_trace_event *dns_trace_fget(struct dns_trace_event **tp, FILE *fp, dns_error_t *error) {
+ const size_t headsize = offsetof(struct dns_trace_event, data);
+ struct dns_trace_event tmp, *te;
+ size_t n;
+
+ errno = 0;
+ if (!(n = fread(&tmp, 1, headsize, fp)))
+ goto none;
+ if (n < offsetof(struct dns_trace_event, data))
+ goto some;
+
+ if (!(te = realloc(*tp, DNS_PP_MAX(headsize, tmp.size)))) {
+ *error = errno;
+ return NULL;
+ }
+
+ *tp = te;
+ memcpy(te, &tmp, offsetof(struct dns_trace_event, data));
+
+ if (dns_te_datasize(te)) {
+ errno = 0;
+ if (!(n = fread(te->data, 1, dns_te_datasize(te), fp)))
+ goto none;
+ if (n < dns_te_datasize(te))
+ goto some;
+ }
+
+ return te;
+none:
+ *error = (ferror(fp))? errno : 0;
+ return NULL;
+some:
+ *error = 0;
+ return NULL;
+}
+
+dns_error_t dns_trace_fput(const struct dns_trace_event *te, const void *data, size_t datasize, FILE *fp) {
+ size_t headsize = offsetof(struct dns_trace_event, data);
+ struct dns_trace_event tmp;
+
+ memcpy(&tmp, te, headsize);
+ tmp.size = headsize + datasize;
+
+ /* NB: ignore seek error as fp might not point to a regular file */
+ (void)fseek(fp, 0, SEEK_END);
+
+ if (fwrite(&tmp, 1, headsize, fp) < headsize)
+ return errno;
+ if (fwrite(data, 1, datasize, fp) < datasize)
+ return errno;
+ if (fflush(fp))
+ return errno;
+
+ return 0;
+}
+
+static void dns_trace_setcname(struct dns_trace *trace, const char *host, const struct sockaddr *addr) {
+ struct dns_trace_cname *cname;
+ if (!trace || !trace->fp)
+ return;
+
+ cname = &trace->cnames.base[trace->cnames.p];
+ dns_strlcpy(cname->host, host, sizeof cname->host);
+ memcpy(&cname->addr, addr, DNS_PP_MIN(dns_sa_len(addr), sizeof cname->addr));
+
+ trace->cnames.p = (trace->cnames.p + 1) % lengthof(trace->cnames.base);
+}
+
+static const char *dns_trace_cname(struct dns_trace *trace, const struct sockaddr *addr) {
+ if (!trace || !trace->fp)
+ return NULL;
+
+ /* NB: start search from the write cursor to */
+ for (const struct dns_trace_cname *cname = trace->cnames.base; cname < endof(trace->cnames.base); cname++) {
+ if (0 == dns_sa_cmp((struct sockaddr *)addr, (struct sockaddr *)&cname->addr))
+ return cname->host;
+ }
+
+ return NULL;
+}
+
+static void dns_trace_res_submit(struct dns_trace *trace, const char *qname, enum dns_type qtype, enum dns_class qclass, int error) {
+ struct dns_trace_event te;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_RES_SUBMIT);
+ dns_strlcpy(te.res_submit.qname, qname, sizeof te.res_submit.qname);
+ te.res_submit.qtype = qtype;
+ te.res_submit.qclass = qclass;
+ te.res_submit.error = error;
+ dns_trace_tag_and_put(trace, &te, NULL, 0);
+}
+
+static void dns_trace_res_fetch(struct dns_trace *trace, const struct dns_packet *packet, int error) {
+ struct dns_trace_event te;
+ const void *data;
+ size_t datasize;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_RES_FETCH);
+ data = (packet)? packet->data : NULL;
+ datasize = (packet)? packet->end : 0;
+ te.res_fetch.error = error;
+ dns_trace_tag_and_put(trace, &te, data, datasize);
+}
+
+static void dns_trace_so_submit(struct dns_trace *trace, const struct dns_packet *packet, const struct sockaddr *haddr, int error) {
+ struct dns_trace_event te;
+ const char *cname;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_SO_SUBMIT);
+ memcpy(&te.so_submit.haddr, haddr, DNS_PP_MIN(dns_sa_len(haddr), sizeof te.so_submit.haddr));
+ if ((cname = dns_trace_cname(trace, haddr)))
+ dns_strlcpy(te.so_submit.hname, cname, sizeof te.so_submit.hname);
+ te.so_submit.error = error;
+ dns_trace_tag_and_put(trace, &te, packet->data, packet->end);
+}
+
+static void dns_trace_so_verify(struct dns_trace *trace, const struct dns_packet *packet, int error) {
+ struct dns_trace_event te;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_SO_VERIFY);
+ te.so_verify.error = error;
+ dns_trace_tag_and_put(trace, &te, packet->data, packet->end);
+}
+
+static void dns_trace_so_fetch(struct dns_trace *trace, const struct dns_packet *packet, int error) {
+ struct dns_trace_event te;
+ const void *data;
+ size_t datasize;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_SO_FETCH);
+ data = (packet)? packet->data : NULL;
+ datasize = (packet)? packet->end : 0;
+ te.so_fetch.error = error;
+ dns_trace_tag_and_put(trace, &te, data, datasize);
+}
+
+static void dns_trace_sys_connect(struct dns_trace *trace, int fd, int socktype, const struct sockaddr *dst, int error) {
+ struct dns_trace_event te;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_SYS_CONNECT);
+ dns_te_initname(&te.sys_connect.src, fd, &getsockname);
+ memcpy(&te.sys_connect.dst, dst, DNS_PP_MIN(dns_sa_len(dst), sizeof te.sys_connect.dst));
+ te.sys_connect.socktype = socktype;
+ te.sys_connect.error = error;
+ dns_trace_tag_and_put(trace, &te, NULL, 0);
+}
+
+static void dns_trace_sys_send(struct dns_trace *trace, int fd, int socktype, const void *data, size_t datasize, int error) {
+ struct dns_trace_event te;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_SYS_SEND);
+ dns_te_initnames(&te.sys_send.src, &te.sys_send.dst, fd);
+ te.sys_send.socktype = socktype;
+ te.sys_send.error = error;
+ dns_trace_tag_and_put(trace, &te, data, datasize);
+}
+
+static void dns_trace_sys_recv(struct dns_trace *trace, int fd, int socktype, const void *data, size_t datasize, int error) {
+ struct dns_trace_event te;
+ if (!trace || !trace->fp)
+ return;
+
+ dns_te_init(&te, DNS_TE_SYS_RECV);
+ dns_te_initnames(&te.sys_recv.dst, &te.sys_recv.src, fd);
+ te.sys_recv.socktype = socktype;
+ te.sys_recv.error = error;
+ dns_trace_tag_and_put(trace, &te, data, datasize);
+}
+
+static dns_error_t dns_trace_dump_packet(struct dns_trace *trace, const char *prefix, const unsigned char *data, size_t datasize, FILE *fp) {
+ struct dns_packet *packet = NULL;
+ char *line = NULL, *p;
+ size_t size = 1, skip = 0;
+ struct dns_rr_i records;
+ struct dns_p_lines_i lines;
+ size_t len, count;
+ int error;
+
+ if (!(packet = dns_p_make(datasize, &error)))
+ goto error;
+
+ memcpy(packet->data, data, datasize);
+ packet->end = datasize;
+ (void)dns_p_study(packet);
+resize:
+ if (!(p = dns_reallocarray(line, size, 2, &error)))
+ goto error;
+ line = p;
+ size *= 2;
+
+ memset(&records, 0, sizeof records);
+ memset(&lines, 0, sizeof lines);
+ count = 0;
+
+ while ((len = dns_p_lines(line, size, &error, packet, &records, &lines))) {
+ if (!(len < size)) {
+ skip = count;
+ goto resize;
+ } else if (skip <= count) {
+ fputs(prefix, fp);
+ fwrite(line, 1, len, fp);
+ }
+ count++;
+ }
+
+ if (error)
+ goto error;
+
+ error = 0;
+error:
+ free(line);
+ dns_p_free(packet);
+
+ return error;
+}
+
+static dns_error_t dns_trace_dump_data(struct dns_trace *trace, const char *prefix, const unsigned char *data, size_t datasize, FILE *fp) {
+ struct dns_hxd_lines_i lines = { 0 };
+ char line[128];
+ size_t len;
+
+ while ((len = dns_hxd_lines(line, sizeof line, data, datasize, &lines))) {
+ if (len >= sizeof line)
+ return EOVERFLOW; /* shouldn't be possible */
+ fputs(prefix, fp);
+ fwrite(line, 1, len, fp);
+ }
+
+ return 0;
+}
+
+static dns_error_t dns_trace_dump_addr(struct dns_trace *trace, const char *prefix, const struct sockaddr_storage *ss, FILE *fp) {
+ const void *addr;
+ const char *path;
+ socklen_t len;
+ int error;
+
+ if ((addr = dns_sa_addr(ss->ss_family, (struct sockaddr *)ss, NULL))) {
+ char ip[INET6_ADDRSTRLEN + 1];
+
+ if ((error = dns_ntop(ss->ss_family, addr, ip, sizeof ip)))
+ return error;
+ fprintf(fp, "%s%s\n", prefix, ip);
+ } else if ((path = dns_sa_path((struct sockaddr *)ss, &len))) {
+ fprintf(fp, "%sunix:%.*s", prefix, (int)len, path);
+ } else {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static dns_error_t dns_trace_dump_meta(struct dns_trace *trace, const char *prefix, const struct dns_trace_event *te, dns_microseconds_t elapsed, FILE *fp) {
+ char time_s[48], elapsed_s[48];
+
+ dns_utime_print(time_s, sizeof time_s, dns_ts2us(&te->ts, 0));
+ dns_utime_print(elapsed_s, sizeof elapsed_s, elapsed);
+
+ fprintf(fp, "%sid: %"DNS_TRACE_ID_PRI"\n", prefix, te->id);
+ fprintf(fp, "%sts: %s (%s)\n", prefix, time_s, elapsed_s);
+ fprintf(fp, "%sabi: 0x%x (0x%x)\n", prefix, te->abi, DNS_TRACE_ABI);
+ return 0;
+}
+
+static dns_error_t dns_trace_dump_error(struct dns_trace *trace, const char *prefix, int error, FILE *fp) {
+ fprintf(fp, "%s%d (%s)\n", prefix, error, (error)? dns_strerror(error) : "none");
+ return 0;
+}
+
+dns_error_t dns_trace_dump(struct dns_trace *trace, FILE *fp) {
+ struct dns_trace_event *te = NULL;
+ struct {
+ dns_trace_id_t id;
+ dns_microseconds_t begin, elapsed;
+ } state = { 0 };
+ int error;
+
+ if (!trace || !trace->fp)
+ return EINVAL;
+
+ if (0 != fseek(trace->fp, 0, SEEK_SET))
+ goto syerr;
+
+ while (dns_trace_fget(&te, trace->fp, &error)) {
+ size_t datasize = dns_te_datasize(te);
+ const unsigned char *data = (datasize)? te->data : NULL;
+
+ if (state.id != te->id) {
+ state.id = te->id;
+ state.begin = dns_ts2us(&te->ts, 0);
+ }
+ dns_time_diff(&state.elapsed, dns_ts2us(&te->ts, 0), state.begin);
+
+ switch(te->type) {
+ case DNS_TE_RES_SUBMIT:
+ fprintf(fp, "dns_res_submit:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ fprintf(fp, " qname: %s\n", te->res_submit.qname);
+ fprintf(fp, " qtype: %s\n", dns_strtype(te->res_submit.qtype));
+ fprintf(fp, " qclass: %s\n", dns_strclass(te->res_submit.qclass));
+ dns_trace_dump_error(trace, " error: ", te->res_submit.error, fp);
+ break;
+ case DNS_TE_RES_FETCH:
+ fprintf(fp, "dns_res_fetch:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ dns_trace_dump_error(trace, " error: ", te->res_fetch.error, fp);
+
+ if (data) {
+ fprintf(fp, " packet: |\n");
+ if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp)))
+ goto error;
+ fprintf(fp, " data: |\n");
+ if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp)))
+ goto error;
+ }
+
+ break;
+ case DNS_TE_SO_SUBMIT:
+ fprintf(fp, "dns_so_submit:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ fprintf(fp, " hname: %s\n", te->so_submit.hname);
+ dns_trace_dump_addr(trace, " haddr: ", &te->so_submit.haddr, fp);
+ dns_trace_dump_error(trace, " error: ", te->so_submit.error, fp);
+
+ if (data) {
+ fprintf(fp, " packet: |\n");
+ if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp)))
+ goto error;
+ fprintf(fp, " data: |\n");
+ if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp)))
+ goto error;
+ }
+
+ break;
+ case DNS_TE_SO_VERIFY:
+ fprintf(fp, "dns_so_verify:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ dns_trace_dump_error(trace, " error: ", te->so_verify.error, fp);
+
+ if (data) {
+ fprintf(fp, " packet: |\n");
+ if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp)))
+ goto error;
+ fprintf(fp, " data: |\n");
+ if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp)))
+ goto error;
+ }
+
+ break;
+ case DNS_TE_SO_FETCH:
+ fprintf(fp, "dns_so_fetch:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ dns_trace_dump_error(trace, " error: ", te->so_fetch.error, fp);
+
+ if (data) {
+ fprintf(fp, " packet: |\n");
+ if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp)))
+ goto error;
+ fprintf(fp, " data: |\n");
+ if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp)))
+ goto error;
+ }
+
+ break;
+ case DNS_TE_SYS_CONNECT: {
+ int socktype = te->sys_connect.socktype;
+ fprintf(fp, "dns_sys_connect:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ dns_trace_dump_addr(trace, " src: ", &te->sys_connect.src, fp);
+ dns_trace_dump_addr(trace, " dst: ", &te->sys_connect.dst, fp);
+ fprintf(fp, " socktype: %d (%s)\n", socktype, ((socktype == SOCK_STREAM)? "SOCK_STREAM" : (socktype == SOCK_DGRAM)? "SOCK_DGRAM" : "?"));
+ dns_trace_dump_error(trace, " error: ", te->sys_connect.error, fp);
+
+ break;
+ }
+ case DNS_TE_SYS_SEND: {
+ int socktype = te->sys_send.socktype;
+ fprintf(fp, "dns_sys_send:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ dns_trace_dump_addr(trace, " src: ", &te->sys_send.src, fp);
+ dns_trace_dump_addr(trace, " dst: ", &te->sys_send.dst, fp);
+ fprintf(fp, " socktype: %d (%s)\n", socktype, ((socktype == SOCK_STREAM)? "SOCK_STREAM" : (socktype == SOCK_DGRAM)? "SOCK_DGRAM" : "?"));
+ dns_trace_dump_error(trace, " error: ", te->sys_send.error, fp);
+
+ if (data) {
+ fprintf(fp, " data: |\n");
+ if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp)))
+ goto error;
+ }
+
+ break;
+ }
+ case DNS_TE_SYS_RECV: {
+ int socktype = te->sys_recv.socktype;
+ fprintf(fp, "dns_sys_recv:\n");
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+ dns_trace_dump_addr(trace, " src: ", &te->sys_recv.src, fp);
+ dns_trace_dump_addr(trace, " dst: ", &te->sys_recv.dst, fp);
+ fprintf(fp, " socktype: %d (%s)\n", socktype, ((socktype == SOCK_STREAM)? "SOCK_STREAM" : (socktype == SOCK_DGRAM)? "SOCK_DGRAM" : "?"));
+ dns_trace_dump_error(trace, " error: ", te->sys_recv.error, fp);
+
+ if (data) {
+ fprintf(fp, " data: |\n");
+ if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp)))
+ goto error;
+ }
+
+ break;
+ }
+ default:
+ fprintf(fp, "unknown(0x%.2x):\n", te->type);
+ dns_trace_dump_meta(trace, " ", te, state.elapsed, fp);
+
+ if (data) {
+ fprintf(fp, " data: |\n");
+ if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp)))
+ goto error;
+ }
+
+ break;
+ }
+ }
+
+ goto epilog;
+syerr:
+ error = errno;
+error:
+ (void)0;
+epilog:
+ free(te);
+
+ return error;
+}
+
+/*
+ * H O S T S R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_hosts {
+ struct dns_hosts_entry {
+ char host[DNS_D_MAXNAME + 1];
+ char arpa[73 + 1];
+
+ int af;
+
+ union {
+ struct in_addr a4;
+ struct in6_addr a6;
+ } addr;
+
+ _Bool alias;
+
+ struct dns_hosts_entry *next;
+ } *head, **tail;
+
+ dns_atomic_t refcount;
+}; /* struct dns_hosts */
+
+
+struct dns_hosts *dns_hosts_open(int *error) {
+ static const struct dns_hosts hosts_initializer = { .refcount = 1 };
+ struct dns_hosts *hosts;
+
+ if (!(hosts = malloc(sizeof *hosts)))
+ goto syerr;
+
+ *hosts = hosts_initializer;
+
+ hosts->tail = &hosts->head;
+
+ return hosts;
+syerr:
+ *error = dns_syerr();
+
+ free(hosts);
+
+ return 0;
+} /* dns_hosts_open() */
+
+
+void dns_hosts_close(struct dns_hosts *hosts) {
+ struct dns_hosts_entry *ent, *xnt;
+
+ if (!hosts || 1 != dns_hosts_release(hosts))
+ return;
+
+ for (ent = hosts->head; ent; ent = xnt) {
+ xnt = ent->next;
+
+ free(ent);
+ }
+
+ free(hosts);
+
+ return;
+} /* dns_hosts_close() */
+
+
+dns_refcount_t dns_hosts_acquire(struct dns_hosts *hosts) {
+ return dns_atomic_fetch_add(&hosts->refcount);
+} /* dns_hosts_acquire() */
+
+
+dns_refcount_t dns_hosts_release(struct dns_hosts *hosts) {
+ return dns_atomic_fetch_sub(&hosts->refcount);
+} /* dns_hosts_release() */
+
+
+struct dns_hosts *dns_hosts_mortal(struct dns_hosts *hosts) {
+ if (hosts)
+ dns_hosts_release(hosts);
+
+ return hosts;
+} /* dns_hosts_mortal() */
+
+
+struct dns_hosts *dns_hosts_local(int *error_) {
+ struct dns_hosts *hosts;
+ int error;
+
+ if (!(hosts = dns_hosts_open(&error)))
+ goto error;
+
+ if ((error = dns_hosts_loadpath(hosts, "/etc/hosts")))
+ goto error;
+
+ return hosts;
+error:
+ *error_ = error;
+
+ dns_hosts_close(hosts);
+
+ return 0;
+} /* dns_hosts_local() */
+
+
+#define dns_hosts_issep(ch) (dns_isspace(ch))
+#define dns_hosts_iscom(ch) ((ch) == '#' || (ch) == ';')
+
+int dns_hosts_loadfile(struct dns_hosts *hosts, FILE *fp) {
+ struct dns_hosts_entry ent;
+ char word[DNS_PP_MAX(INET6_ADDRSTRLEN, DNS_D_MAXNAME) + 1];
+ unsigned wp, wc, skip;
+ int ch, error;
+
+ rewind(fp);
+
+ do {
+ memset(&ent, '\0', sizeof ent);
+ wc = 0;
+ skip = 0;
+
+ do {
+ memset(word, '\0', sizeof word);
+ wp = 0;
+
+ while (EOF != (ch = fgetc(fp)) && ch != '\n') {
+ skip |= !!dns_hosts_iscom(ch);
+
+ if (skip)
+ continue;
+
+ if (dns_hosts_issep(ch))
+ break;
+
+ if (wp < sizeof word - 1)
+ word[wp] = ch;
+ wp++;
+ }
+
+ if (!wp)
+ continue;
+
+ wc++;
+
+ switch (wc) {
+ case 0:
+ break;
+ case 1:
+ ent.af = (strchr(word, ':'))? AF_INET6 : AF_INET;
+ skip = (1 != dns_inet_pton(ent.af, word, &ent.addr));
+
+ break;
+ default:
+ if (!wp)
+ break;
+
+ dns_d_anchor(ent.host, sizeof ent.host, word, wp);
+
+ if ((error = dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, (wc > 2))))
+ return error;
+
+ break;
+ } /* switch() */
+ } while (ch != EOF && ch != '\n');
+ } while (ch != EOF);
+
+ return 0;
+} /* dns_hosts_loadfile() */
+
+
+int dns_hosts_loadpath(struct dns_hosts *hosts, const char *path) {
+ FILE *fp;
+ int error;
+
+ if (!(fp = dns_fopen(path, "rt", &error)))
+ return error;
+
+ error = dns_hosts_loadfile(hosts, fp);
+
+ fclose(fp);
+
+ return error;
+} /* dns_hosts_loadpath() */
+
+
+int dns_hosts_dump(struct dns_hosts *hosts, FILE *fp) {
+ struct dns_hosts_entry *ent, *xnt;
+ char addr[INET6_ADDRSTRLEN + 1];
+ unsigned i;
+
+ for (ent = hosts->head; ent; ent = xnt) {
+ xnt = ent->next;
+
+ dns_inet_ntop(ent->af, &ent->addr, addr, sizeof addr);
+
+ fputs(addr, fp);
+
+ for (i = strlen(addr); i < INET_ADDRSTRLEN; i++)
+ fputc(' ', fp);
+
+ fputc(' ', fp);
+
+ fputs(ent->host, fp);
+ fputc('\n', fp);
+ }
+
+ return 0;
+} /* dns_hosts_dump() */
+
+
+int dns_hosts_insert(struct dns_hosts *hosts, int af, const void *addr, const void *host, _Bool alias) {
+ struct dns_hosts_entry *ent;
+ int error;
+
+ if (!(ent = malloc(sizeof *ent)))
+ goto syerr;
+
+ dns_d_anchor(ent->host, sizeof ent->host, host, strlen(host));
+
+ switch ((ent->af = af)) {
+ case AF_INET6:
+ memcpy(&ent->addr.a6, addr, sizeof ent->addr.a6);
+
+ dns_aaaa_arpa(ent->arpa, sizeof ent->arpa, addr);
+
+ break;
+ case AF_INET:
+ memcpy(&ent->addr.a4, addr, sizeof ent->addr.a4);
+
+ dns_a_arpa(ent->arpa, sizeof ent->arpa, addr);
+
+ break;
+ default:
+ error = EINVAL;
+
+ goto error;
+ } /* switch() */
+
+ ent->alias = alias;
+
+ ent->next = 0;
+ *hosts->tail = ent;
+ hosts->tail = &ent->next;
+
+ return 0;
+syerr:
+ error = dns_syerr();
+error:
+ free(ent);
+
+ return error;
+} /* dns_hosts_insert() */
+
+
+struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) {
+ struct dns_packet *P = dns_p_new(512);
+ struct dns_packet *A = 0;
+ struct dns_rr rr;
+ struct dns_hosts_entry *ent;
+ int error, af;
+ char qname[DNS_D_MAXNAME + 1];
+ size_t qlen;
+
+ if ((error = dns_rr_parse(&rr, 12, Q)))
+ goto error;
+
+ if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, Q, &error)))
+ goto error;
+ else if (qlen >= sizeof qname)
+ goto toolong;
+
+ if ((error = dns_p_push(P, DNS_S_QD, qname, qlen, rr.type, rr.class, 0, 0)))
+ goto error;
+
+ switch (rr.type) {
+ case DNS_T_PTR:
+ for (ent = hosts->head; ent; ent = ent->next) {
+ if (ent->alias || 0 != strcasecmp(qname, ent->arpa))
+ continue;
+
+ if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, ent->host)))
+ goto error;
+ }
+
+ break;
+ case DNS_T_AAAA:
+ af = AF_INET6;
+
+ goto loop;
+ case DNS_T_A:
+ af = AF_INET;
+
+loop: for (ent = hosts->head; ent; ent = ent->next) {
+ if (ent->af != af || 0 != strcasecmp(qname, ent->host))
+ continue;
+
+ if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, &ent->addr)))
+ goto error;
+ }
+
+ break;
+ default:
+ break;
+ } /* switch() */
+
+
+ if (!(A = dns_p_copy(dns_p_make(P->end, &error), P)))
+ goto error;
+
+ return A;
+toolong:
+ error = DNS_EILLEGAL;
+error:
+ *error_ = error;
+
+ dns_p_free(A);
+
+ return 0;
+} /* dns_hosts_query() */
+
+
+/*
+ * R E S O L V . C O N F R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_resolv_conf *dns_resconf_open(int *error) {
+ static const struct dns_resolv_conf resconf_initializer = {
+ .lookup = "bf",
+ .family = { AF_INET, AF_INET6 },
+ .options = { .ndots = 1, .timeout = 5, .attempts = 2, .tcp = DNS_RESCONF_TCP_ENABLE, },
+ .iface = { .ss_family = AF_INET },
+ };
+ struct dns_resolv_conf *resconf;
+ struct sockaddr_in *sin;
+
+ if (!(resconf = malloc(sizeof *resconf)))
+ goto syerr;
+
+ *resconf = resconf_initializer;
+
+ sin = (struct sockaddr_in *)&resconf->nameserver[0];
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ sin->sin_port = htons(53);
+#if defined(SA_LEN)
+ sin->sin_len = sizeof *sin;
+#endif
+
+ if (0 != gethostname(resconf->search[0], sizeof resconf->search[0]))
+ goto syerr;
+
+ dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0]));
+ dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0]));
+
+ /*
+ * XXX: If gethostname() returned a string without any label
+ * separator, then search[0][0] should be NUL.
+ */
+
+ dns_resconf_acquire(resconf);
+
+ return resconf;
+syerr:
+ *error = dns_syerr();
+
+ free(resconf);
+
+ return 0;
+} /* dns_resconf_open() */
+
+
+void dns_resconf_close(struct dns_resolv_conf *resconf) {
+ if (!resconf || 1 != dns_resconf_release(resconf))
+ return /* void */;
+
+ free(resconf);
+} /* dns_resconf_close() */
+
+
+dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *resconf) {
+ return dns_atomic_fetch_add(&resconf->_.refcount);
+} /* dns_resconf_acquire() */
+
+
+dns_refcount_t dns_resconf_release(struct dns_resolv_conf *resconf) {
+ return dns_atomic_fetch_sub(&resconf->_.refcount);
+} /* dns_resconf_release() */
+
+
+struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *resconf) {
+ if (resconf)
+ dns_resconf_release(resconf);
+
+ return resconf;
+} /* dns_resconf_mortal() */
+
+
+struct dns_resolv_conf *dns_resconf_local(int *error_) {
+ struct dns_resolv_conf *resconf;
+ int error;
+
+ if (!(resconf = dns_resconf_open(&error)))
+ goto error;
+
+ if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf"))) {
+ /*
+ * NOTE: Both the glibc and BIND9 resolvers ignore a missing
+ * /etc/resolv.conf, defaulting to a nameserver of
+ * 127.0.0.1. See also dns_hints_insert_resconf, and the
+ * default initialization of nameserver[0] in
+ * dns_resconf_open.
+ */
+ if (error != ENOENT)
+ goto error;
+ }
+
+ if ((error = dns_nssconf_loadpath(resconf, "/etc/nsswitch.conf"))) {
+ if (error != ENOENT)
+ goto error;
+ }
+
+ return resconf;
+error:
+ *error_ = error;
+
+ dns_resconf_close(resconf);
+
+ return 0;
+} /* dns_resconf_local() */
+
+
+struct dns_resolv_conf *dns_resconf_root(int *error) {
+ struct dns_resolv_conf *resconf;
+
+ if ((resconf = dns_resconf_local(error)))
+ resconf->options.recurse = 1;
+
+ return resconf;
+} /* dns_resconf_root() */
+
+
+static time_t dns_resconf_timeout(const struct dns_resolv_conf *resconf) {
+ return (time_t)DNS_PP_MIN(INT_MAX, resconf->options.timeout);
+} /* dns_resconf_timeout() */
+
+
+enum dns_resconf_keyword {
+ DNS_RESCONF_NAMESERVER,
+ DNS_RESCONF_DOMAIN,
+ DNS_RESCONF_SEARCH,
+ DNS_RESCONF_LOOKUP,
+ DNS_RESCONF_FILE,
+ DNS_RESCONF_BIND,
+ DNS_RESCONF_CACHE,
+ DNS_RESCONF_FAMILY,
+ DNS_RESCONF_INET4,
+ DNS_RESCONF_INET6,
+ DNS_RESCONF_OPTIONS,
+ DNS_RESCONF_EDNS0,
+ DNS_RESCONF_NDOTS,
+ DNS_RESCONF_TIMEOUT,
+ DNS_RESCONF_ATTEMPTS,
+ DNS_RESCONF_ROTATE,
+ DNS_RESCONF_RECURSE,
+ DNS_RESCONF_SMART,
+ DNS_RESCONF_TCP,
+ DNS_RESCONF_TCPx,
+ DNS_RESCONF_INTERFACE,
+ DNS_RESCONF_ZERO,
+ DNS_RESCONF_ONE,
+ DNS_RESCONF_ENABLE,
+ DNS_RESCONF_ONLY,
+ DNS_RESCONF_DISABLE,
+}; /* enum dns_resconf_keyword */
+
+static enum dns_resconf_keyword dns_resconf_keyword(const char *word) {
+ static const char *words[] = {
+ [DNS_RESCONF_NAMESERVER] = "nameserver",
+ [DNS_RESCONF_DOMAIN] = "domain",
+ [DNS_RESCONF_SEARCH] = "search",
+ [DNS_RESCONF_LOOKUP] = "lookup",
+ [DNS_RESCONF_FILE] = "file",
+ [DNS_RESCONF_BIND] = "bind",
+ [DNS_RESCONF_CACHE] = "cache",
+ [DNS_RESCONF_FAMILY] = "family",
+ [DNS_RESCONF_INET4] = "inet4",
+ [DNS_RESCONF_INET6] = "inet6",
+ [DNS_RESCONF_OPTIONS] = "options",
+ [DNS_RESCONF_EDNS0] = "edns0",
+ [DNS_RESCONF_ROTATE] = "rotate",
+ [DNS_RESCONF_RECURSE] = "recurse",
+ [DNS_RESCONF_SMART] = "smart",
+ [DNS_RESCONF_TCP] = "tcp",
+ [DNS_RESCONF_INTERFACE] = "interface",
+ [DNS_RESCONF_ZERO] = "0",
+ [DNS_RESCONF_ONE] = "1",
+ [DNS_RESCONF_ENABLE] = "enable",
+ [DNS_RESCONF_ONLY] = "only",
+ [DNS_RESCONF_DISABLE] = "disable",
+ };
+ unsigned i;
+
+ for (i = 0; i < lengthof(words); i++) {
+ if (words[i] && 0 == strcasecmp(words[i], word))
+ return i;
+ }
+
+ if (0 == strncasecmp(word, "ndots:", sizeof "ndots:" - 1))
+ return DNS_RESCONF_NDOTS;
+
+ if (0 == strncasecmp(word, "timeout:", sizeof "timeout:" - 1))
+ return DNS_RESCONF_TIMEOUT;
+
+ if (0 == strncasecmp(word, "attempts:", sizeof "attempts:" - 1))
+ return DNS_RESCONF_ATTEMPTS;
+
+ if (0 == strncasecmp(word, "tcp:", sizeof "tcp:" - 1))
+ return DNS_RESCONF_TCPx;
+
+ return -1;
+} /* dns_resconf_keyword() */
+
+
+/** OpenBSD-style "[1.2.3.4]:53" nameserver syntax */
+int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) {
+ struct { char buf[128], *p; } addr = { "", addr.buf };
+ unsigned short port = 0;
+ int ch, af = AF_INET, error;
+
+ while ((ch = *src++)) {
+ switch (ch) {
+ case ' ':
+ /* FALL THROUGH */
+ case '\t':
+ break;
+ case '[':
+ break;
+ case ']':
+ while ((ch = *src++)) {
+ if (dns_isdigit(ch)) {
+ port *= 10;
+ port += ch - '0';
+ }
+ }
+
+ goto inet;
+ case ':':
+ af = AF_INET6;
+
+ /* FALL THROUGH */
+ default:
+ if (addr.p < endof(addr.buf) - 1)
+ *addr.p++ = ch;
+
+ break;
+ } /* switch() */
+ } /* while() */
+inet:
+
+ if ((error = dns_pton(af, addr.buf, dns_sa_addr(af, ss, NULL))))
+ return error;
+
+ port = (!port)? 53 : port;
+ *dns_sa_port(af, ss) = htons(port);
+ dns_sa_family(ss) = af;
+
+ return 0;
+} /* dns_resconf_pton() */
+
+#define dns_resconf_issep(ch) (dns_isspace(ch) || (ch) == ',')
+#define dns_resconf_iscom(ch) ((ch) == '#' || (ch) == ';')
+
+int dns_resconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) {
+ unsigned sa_count = 0;
+ char words[6][DNS_D_MAXNAME + 1];
+ unsigned wp, wc, i, j, n;
+ int ch, error;
+
+ rewind(fp);
+
+ do {
+ memset(words, '\0', sizeof words);
+ wp = 0;
+ wc = 0;
+
+ while (EOF != (ch = getc(fp)) && ch != '\n') {
+ if (dns_resconf_issep(ch)) {
+ if (wp > 0) {
+ wp = 0;
+
+ if (++wc >= lengthof(words))
+ goto skip;
+ }
+ } else if (dns_resconf_iscom(ch)) {
+skip:
+ do {
+ ch = getc(fp);
+ } while (ch != EOF && ch != '\n');
+
+ break;
+ } else if (wp < sizeof words[wc] - 1) {
+ words[wc][wp++] = ch;
+ } else {
+ wp = 0; /* drop word */
+ goto skip;
+ }
+ }
+
+ if (wp > 0)
+ wc++;
+
+ if (wc < 2)
+ continue;
+
+ switch (dns_resconf_keyword(words[0])) {
+ case DNS_RESCONF_NAMESERVER:
+ if (sa_count >= lengthof(resconf->nameserver))
+ continue;
+
+ if ((error = dns_resconf_pton(&resconf->nameserver[sa_count], words[1])))
+ continue;
+
+ sa_count++;
+
+ break;
+ case DNS_RESCONF_DOMAIN:
+ case DNS_RESCONF_SEARCH:
+ memset(resconf->search, '\0', sizeof resconf->search);
+
+ for (i = 1, j = 0; i < wc && j < lengthof(resconf->search); i++, j++)
+ dns_d_anchor(resconf->search[j], sizeof resconf->search[j], words[i], strlen(words[i]));
+
+ break;
+ case DNS_RESCONF_LOOKUP:
+ for (i = 1, j = 0; i < wc && j < lengthof(resconf->lookup); i++) {
+ switch (dns_resconf_keyword(words[i])) {
+ case DNS_RESCONF_FILE:
+ resconf->lookup[j++] = 'f';
+
+ break;
+ case DNS_RESCONF_BIND:
+ resconf->lookup[j++] = 'b';
+
+ break;
+ case DNS_RESCONF_CACHE:
+ resconf->lookup[j++] = 'c';
+
+ break;
+ default:
+ break;
+ } /* switch() */
+ } /* for() */
+
+ break;
+ case DNS_RESCONF_FAMILY:
+ for (i = 1, j = 0; i < wc && j < lengthof(resconf->family); i++) {
+ switch (dns_resconf_keyword(words[i])) {
+ case DNS_RESCONF_INET4:
+ resconf->family[j++] = AF_INET;
+
+ break;
+ case DNS_RESCONF_INET6:
+ resconf->family[j++] = AF_INET6;
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ break;
+ case DNS_RESCONF_OPTIONS:
+ for (i = 1; i < wc; i++) {
+ switch (dns_resconf_keyword(words[i])) {
+ case DNS_RESCONF_EDNS0:
+ resconf->options.edns0 = 1;
+
+ break;
+ case DNS_RESCONF_NDOTS:
+ for (j = sizeof "ndots:" - 1, n = 0; dns_isdigit(words[i][j]); j++) {
+ n *= 10;
+ n += words[i][j] - '0';
+ } /* for() */
+
+ resconf->options.ndots = n;
+
+ break;
+ case DNS_RESCONF_TIMEOUT:
+ for (j = sizeof "timeout:" - 1, n = 0; dns_isdigit(words[i][j]); j++) {
+ n *= 10;
+ n += words[i][j] - '0';
+ } /* for() */
+
+ resconf->options.timeout = n;
+
+ break;
+ case DNS_RESCONF_ATTEMPTS:
+ for (j = sizeof "attempts:" - 1, n = 0; dns_isdigit(words[i][j]); j++) {
+ n *= 10;
+ n += words[i][j] - '0';
+ } /* for() */
+
+ resconf->options.attempts = n;
+
+ break;
+ case DNS_RESCONF_ROTATE:
+ resconf->options.rotate = 1;
+
+ break;
+ case DNS_RESCONF_RECURSE:
+ resconf->options.recurse = 1;
+
+ break;
+ case DNS_RESCONF_SMART:
+ resconf->options.smart = 1;
+
+ break;
+ case DNS_RESCONF_TCP:
+ resconf->options.tcp = DNS_RESCONF_TCP_ONLY;
+
+ break;
+ case DNS_RESCONF_TCPx:
+ switch (dns_resconf_keyword(&words[i][sizeof "tcp:" - 1])) {
+ case DNS_RESCONF_ENABLE:
+ resconf->options.tcp = DNS_RESCONF_TCP_ENABLE;
+
+ break;
+ case DNS_RESCONF_ONE:
+ case DNS_RESCONF_ONLY:
+ resconf->options.tcp = DNS_RESCONF_TCP_ONLY;
+
+ break;
+ case DNS_RESCONF_ZERO:
+ case DNS_RESCONF_DISABLE:
+ resconf->options.tcp = DNS_RESCONF_TCP_DISABLE;
+
+ break;
+ default:
+ break;
+ } /* switch() */
+
+ break;
+ default:
+ break;
+ } /* switch() */
+ } /* for() */
+
+ break;
+ case DNS_RESCONF_INTERFACE:
+ for (i = 0, n = 0; dns_isdigit(words[2][i]); i++) {
+ n *= 10;
+ n += words[2][i] - '0';
+ }
+
+ dns_resconf_setiface(resconf, words[1], n);
+
+ break;
+ default:
+ break;
+ } /* switch() */
+ } while (ch != EOF);
+
+ return 0;
+} /* dns_resconf_loadfile() */
+
+
+int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) {
+ FILE *fp;
+ int error;
+
+ if (!(fp = dns_fopen(path, "rt", &error)))
+ return error;
+
+ error = dns_resconf_loadfile(resconf, fp);
+
+ fclose(fp);
+
+ return error;
+} /* dns_resconf_loadpath() */
+
+
+struct dns_anyconf {
+ char *token[16];
+ unsigned count;
+ char buffer[1024], *tp, *cp;
+}; /* struct dns_anyconf */
+
+
+static void dns_anyconf_reset(struct dns_anyconf *cf) {
+ cf->count = 0;
+ cf->tp = cf->cp = cf->buffer;
+} /* dns_anyconf_reset() */
+
+
+static int dns_anyconf_push(struct dns_anyconf *cf) {
+ if (!(cf->cp < endof(cf->buffer) && cf->count < lengthof(cf->token)))
+ return ENOMEM;
+
+ *cf->cp++ = '\0';
+ cf->token[cf->count++] = cf->tp;
+ cf->tp = cf->cp;
+
+ return 0;
+} /* dns_anyconf_push() */
+
+
+static void dns_anyconf_pop(struct dns_anyconf *cf) {
+ if (cf->count > 0) {
+ --cf->count;
+ cf->tp = cf->cp = cf->token[cf->count];
+ cf->token[cf->count] = 0;
+ }
+} /* dns_anyconf_pop() */
+
+
+static int dns_anyconf_addc(struct dns_anyconf *cf, int ch) {
+ if (!(cf->cp < endof(cf->buffer)))
+ return ENOMEM;
+
+ *cf->cp++ = ch;
+
+ return 0;
+} /* dns_anyconf_addc() */
+
+
+static _Bool dns_anyconf_match(const char *pat, int mc) {
+ _Bool match;
+ int pc;
+
+ if (*pat == '^') {
+ match = 0;
+ ++pat;
+ } else {
+ match = 1;
+ }
+
+ while ((pc = *(const unsigned char *)pat++)) {
+ switch (pc) {
+ case '%':
+ if (!(pc = *(const unsigned char *)pat++))
+ return !match;
+
+ switch (pc) {
+ case 'a':
+ if (dns_isalpha(mc))
+ return match;
+ break;
+ case 'd':
+ if (dns_isdigit(mc))
+ return match;
+ break;
+ case 'w':
+ if (dns_isalnum(mc))
+ return match;
+ break;
+ case 's':
+ if (dns_isspace(mc))
+ return match;
+ break;
+ default:
+ if (mc == pc)
+ return match;
+ break;
+ } /* switch() */
+
+ break;
+ default:
+ if (mc == pc)
+ return match;
+ break;
+ } /* switch() */
+ } /* while() */
+
+ return !match;
+} /* dns_anyconf_match() */
+
+
+static int dns_anyconf_peek(FILE *fp) {
+ int ch;
+ ch = getc(fp);
+ ungetc(ch, fp);
+ return ch;
+} /* dns_anyconf_peek() */
+
+
+static size_t dns_anyconf_skip(const char *pat, FILE *fp) {
+ size_t count = 0;
+ int ch;
+
+ while (EOF != (ch = getc(fp))) {
+ if (dns_anyconf_match(pat, ch)) {
+ count++;
+ continue;
+ }
+
+ ungetc(ch, fp);
+
+ break;
+ }
+
+ return count;
+} /* dns_anyconf_skip() */
+
+
+static size_t dns_anyconf_scan(struct dns_anyconf *cf, const char *pat, FILE *fp, int *error) {
+ size_t len;
+ int ch;
+
+ while (EOF != (ch = getc(fp))) {
+ if (dns_anyconf_match(pat, ch)) {
+ if ((*error = dns_anyconf_addc(cf, ch)))
+ return 0;
+
+ continue;
+ } else {
+ ungetc(ch, fp);
+
+ break;
+ }
+ }
+
+ if ((len = cf->cp - cf->tp)) {
+ if ((*error = dns_anyconf_push(cf)))
+ return 0;
+
+ return len;
+ } else {
+ *error = 0;
+
+ return 0;
+ }
+} /* dns_anyconf_scan() */
+
+
+DNS_NOTUSED static void dns_anyconf_dump(struct dns_anyconf *cf, FILE *fp) {
+ unsigned i;
+
+ fprintf(fp, "tokens:");
+
+ for (i = 0; i < cf->count; i++) {
+ fprintf(fp, " %s", cf->token[i]);
+ }
+
+ fputc('\n', fp);
+} /* dns_anyconf_dump() */
+
+
+enum dns_nssconf_keyword {
+ DNS_NSSCONF_INVALID = 0,
+ DNS_NSSCONF_HOSTS = 1,
+ DNS_NSSCONF_SUCCESS,
+ DNS_NSSCONF_NOTFOUND,
+ DNS_NSSCONF_UNAVAIL,
+ DNS_NSSCONF_TRYAGAIN,
+ DNS_NSSCONF_CONTINUE,
+ DNS_NSSCONF_RETURN,
+ DNS_NSSCONF_FILES,
+ DNS_NSSCONF_DNS,
+ DNS_NSSCONF_MDNS,
+
+ DNS_NSSCONF_LAST,
+}; /* enum dns_nssconf_keyword */
+
+static enum dns_nssconf_keyword dns_nssconf_keyword(const char *word) {
+ static const char *list[] = {
+ [DNS_NSSCONF_HOSTS] = "hosts",
+ [DNS_NSSCONF_SUCCESS] = "success",
+ [DNS_NSSCONF_NOTFOUND] = "notfound",
+ [DNS_NSSCONF_UNAVAIL] = "unavail",
+ [DNS_NSSCONF_TRYAGAIN] = "tryagain",
+ [DNS_NSSCONF_CONTINUE] = "continue",
+ [DNS_NSSCONF_RETURN] = "return",
+ [DNS_NSSCONF_FILES] = "files",
+ [DNS_NSSCONF_DNS] = "dns",
+ [DNS_NSSCONF_MDNS] = "mdns",
+ };
+ unsigned i;
+
+ for (i = 1; i < lengthof(list); i++) {
+ if (list[i] && 0 == strcasecmp(list[i], word))
+ return i;
+ }
+
+ return DNS_NSSCONF_INVALID;
+} /* dns_nssconf_keyword() */
+
+
+static enum dns_nssconf_keyword dns_nssconf_c2k(int ch) {
+ static const char map[] = {
+ ['S'] = DNS_NSSCONF_SUCCESS,
+ ['N'] = DNS_NSSCONF_NOTFOUND,
+ ['U'] = DNS_NSSCONF_UNAVAIL,
+ ['T'] = DNS_NSSCONF_TRYAGAIN,
+ ['C'] = DNS_NSSCONF_CONTINUE,
+ ['R'] = DNS_NSSCONF_RETURN,
+ ['f'] = DNS_NSSCONF_FILES,
+ ['F'] = DNS_NSSCONF_FILES,
+ ['d'] = DNS_NSSCONF_DNS,
+ ['D'] = DNS_NSSCONF_DNS,
+ ['b'] = DNS_NSSCONF_DNS,
+ ['B'] = DNS_NSSCONF_DNS,
+ ['m'] = DNS_NSSCONF_MDNS,
+ ['M'] = DNS_NSSCONF_MDNS,
+ };
+
+ return (ch >= 0 && ch < (int)lengthof(map))? map[ch] : DNS_NSSCONF_INVALID;
+} /* dns_nssconf_c2k() */
+
+
+DNS_PRAGMA_PUSH
+DNS_PRAGMA_QUIET
+
+static int dns_nssconf_k2c(int k) {
+ static const char map[DNS_NSSCONF_LAST] = {
+ [DNS_NSSCONF_SUCCESS] = 'S',
+ [DNS_NSSCONF_NOTFOUND] = 'N',
+ [DNS_NSSCONF_UNAVAIL] = 'U',
+ [DNS_NSSCONF_TRYAGAIN] = 'T',
+ [DNS_NSSCONF_CONTINUE] = 'C',
+ [DNS_NSSCONF_RETURN] = 'R',
+ [DNS_NSSCONF_FILES] = 'f',
+ [DNS_NSSCONF_DNS] = 'b',
+ [DNS_NSSCONF_MDNS] = 'm',
+ };
+
+ return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : '?') : '?';
+} /* dns_nssconf_k2c() */
+
+static const char *dns_nssconf_k2s(int k) {
+ static const char *const map[DNS_NSSCONF_LAST] = {
+ [DNS_NSSCONF_SUCCESS] = "SUCCESS",
+ [DNS_NSSCONF_NOTFOUND] = "NOTFOUND",
+ [DNS_NSSCONF_UNAVAIL] = "UNAVAIL",
+ [DNS_NSSCONF_TRYAGAIN] = "TRYAGAIN",
+ [DNS_NSSCONF_CONTINUE] = "continue",
+ [DNS_NSSCONF_RETURN] = "return",
+ [DNS_NSSCONF_FILES] = "files",
+ [DNS_NSSCONF_DNS] = "dns",
+ [DNS_NSSCONF_MDNS] = "mdns",
+ };
+
+ return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : "") : "";
+} /* dns_nssconf_k2s() */
+
+DNS_PRAGMA_POP
+
+
+int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) {
+ enum dns_nssconf_keyword source, status, action;
+ char lookup[sizeof resconf->lookup] = "", *lp;
+ struct dns_anyconf cf;
+ size_t i;
+ int error;
+
+ while (!feof(fp) && !ferror(fp)) {
+ dns_anyconf_reset(&cf);
+
+ dns_anyconf_skip("%s", fp);
+
+ if (!dns_anyconf_scan(&cf, "%w_", fp, &error))
+ goto nextent;
+
+ if (DNS_NSSCONF_HOSTS != dns_nssconf_keyword(cf.token[0]))
+ goto nextent;
+
+ dns_anyconf_pop(&cf);
+
+ if (!dns_anyconf_skip(": \t", fp))
+ goto nextent;
+
+ *(lp = lookup) = '\0';
+
+ while (dns_anyconf_scan(&cf, "%w_", fp, &error)) {
+ dns_anyconf_skip(" \t", fp);
+
+ if ('[' == dns_anyconf_peek(fp)) {
+ dns_anyconf_skip("[ \t", fp);
+
+ while (dns_anyconf_scan(&cf, "%w_", fp, &error)) {
+ dns_anyconf_skip("= \t", fp);
+ if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) {
+ dns_anyconf_pop(&cf); /* discard status */
+ dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */
+ break;
+ }
+ dns_anyconf_skip(" \t", fp);
+ }
+
+ dns_anyconf_skip("] \t", fp);
+ }
+
+ if ((size_t)(endof(lookup) - lp) < cf.count + 1) /* +1 for '\0' */
+ goto nextsrc;
+
+ source = dns_nssconf_keyword(cf.token[0]);
+
+ switch (source) {
+ case DNS_NSSCONF_DNS:
+ case DNS_NSSCONF_MDNS:
+ case DNS_NSSCONF_FILES:
+ *lp++ = dns_nssconf_k2c(source);
+ break;
+ default:
+ goto nextsrc;
+ }
+
+ for (i = 1; i + 1 < cf.count; i += 2) {
+ status = dns_nssconf_keyword(cf.token[i]);
+ action = dns_nssconf_keyword(cf.token[i + 1]);
+
+ switch (status) {
+ case DNS_NSSCONF_SUCCESS:
+ case DNS_NSSCONF_NOTFOUND:
+ case DNS_NSSCONF_UNAVAIL:
+ case DNS_NSSCONF_TRYAGAIN:
+ *lp++ = dns_nssconf_k2c(status);
+ break;
+ default:
+ continue;
+ }
+
+ switch (action) {
+ case DNS_NSSCONF_CONTINUE:
+ case DNS_NSSCONF_RETURN:
+ break;
+ default:
+ action = (status == DNS_NSSCONF_SUCCESS)
+ ? DNS_NSSCONF_RETURN
+ : DNS_NSSCONF_CONTINUE;
+ break;
+ }
+
+ *lp++ = dns_nssconf_k2c(action);
+ }
+nextsrc:
+ *lp = '\0';
+ dns_anyconf_reset(&cf);
+ }
+nextent:
+ dns_anyconf_skip("^\n", fp);
+ }
+
+ if (*lookup)
+ strncpy(resconf->lookup, lookup, sizeof resconf->lookup);
+
+ return 0;
+} /* dns_nssconf_loadfile() */
+
+
+int dns_nssconf_loadpath(struct dns_resolv_conf *resconf, const char *path) {
+ FILE *fp;
+ int error;
+
+ if (!(fp = dns_fopen(path, "rt", &error)))
+ return error;
+
+ error = dns_nssconf_loadfile(resconf, fp);
+
+ fclose(fp);
+
+ return error;
+} /* dns_nssconf_loadpath() */
+
+
+struct dns_nssconf_source {
+ enum dns_nssconf_keyword source, success, notfound, unavail, tryagain;
+}; /* struct dns_nssconf_source */
+
+typedef unsigned dns_nssconf_i;
+
+static inline int dns_nssconf_peek(const struct dns_resolv_conf *resconf, dns_nssconf_i state) {
+ return (state < lengthof(resconf->lookup) && resconf->lookup[state])? resconf->lookup[state] : 0;
+} /* dns_nssconf_peek() */
+
+static _Bool dns_nssconf_next(struct dns_nssconf_source *src, const struct dns_resolv_conf *resconf, dns_nssconf_i *state) {
+ int source, status, action;
+
+ src->source = DNS_NSSCONF_INVALID;
+ src->success = DNS_NSSCONF_RETURN;
+ src->notfound = DNS_NSSCONF_CONTINUE;
+ src->unavail = DNS_NSSCONF_CONTINUE;
+ src->tryagain = DNS_NSSCONF_CONTINUE;
+
+ while ((source = dns_nssconf_peek(resconf, *state))) {
+ source = dns_nssconf_c2k(source);
+ ++*state;
+
+ switch (source) {
+ case DNS_NSSCONF_FILES:
+ case DNS_NSSCONF_DNS:
+ case DNS_NSSCONF_MDNS:
+ src->source = source;
+ break;
+ default:
+ continue;
+ }
+
+ while ((status = dns_nssconf_peek(resconf, *state)) && (action = dns_nssconf_peek(resconf, *state + 1))) {
+ status = dns_nssconf_c2k(status);
+ action = dns_nssconf_c2k(action);
+
+ switch (action) {
+ case DNS_NSSCONF_RETURN:
+ case DNS_NSSCONF_CONTINUE:
+ break;
+ default:
+ goto done;
+ }
+
+ switch (status) {
+ case DNS_NSSCONF_SUCCESS:
+ src->success = action;
+ break;
+ case DNS_NSSCONF_NOTFOUND:
+ src->notfound = action;
+ break;
+ case DNS_NSSCONF_UNAVAIL:
+ src->unavail = action;
+ break;
+ case DNS_NSSCONF_TRYAGAIN:
+ src->tryagain = action;
+ break;
+ default:
+ goto done;
+ }
+
+ *state += 2;
+ }
+
+ break;
+ }
+done:
+ return src->source != DNS_NSSCONF_INVALID;
+} /* dns_nssconf_next() */
+
+
+static int dns_nssconf_dump_status(int status, int action, unsigned *count, FILE *fp) {
+ switch (status) {
+ case DNS_NSSCONF_SUCCESS:
+ if (action == DNS_NSSCONF_RETURN)
+ return 0;
+ break;
+ default:
+ if (action == DNS_NSSCONF_CONTINUE)
+ return 0;
+ break;
+ }
+
+ fputc(' ', fp);
+
+ if (!*count)
+ fputc('[', fp);
+
+ fprintf(fp, "%s=%s", dns_nssconf_k2s(status), dns_nssconf_k2s(action));
+
+ ++*count;
+
+ return 0;
+} /* dns_nssconf_dump_status() */
+
+
+int dns_nssconf_dump(struct dns_resolv_conf *resconf, FILE *fp) {
+ struct dns_nssconf_source src;
+ dns_nssconf_i i = 0;
+
+ fputs("hosts:", fp);
+
+ while (dns_nssconf_next(&src, resconf, &i)) {
+ unsigned n = 0;
+
+ fprintf(fp, " %s", dns_nssconf_k2s(src.source));
+
+ dns_nssconf_dump_status(DNS_NSSCONF_SUCCESS, src.success, &n, fp);
+ dns_nssconf_dump_status(DNS_NSSCONF_NOTFOUND, src.notfound, &n, fp);
+ dns_nssconf_dump_status(DNS_NSSCONF_UNAVAIL, src.unavail, &n, fp);
+ dns_nssconf_dump_status(DNS_NSSCONF_TRYAGAIN, src.tryagain, &n, fp);
+
+ if (n)
+ fputc(']', fp);
+ }
+
+ fputc('\n', fp);
+
+ return 0;
+} /* dns_nssconf_dump() */
+
+
+int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) {
+ int af = (strchr(addr, ':'))? AF_INET6 : AF_INET;
+ int error;
+
+ if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface, NULL))))
+ return error;
+
+ *dns_sa_port(af, &resconf->iface) = htons(port);
+ resconf->iface.ss_family = af;
+
+ return 0;
+} /* dns_resconf_setiface() */
+
+
+#define DNS_SM_RESTORE \
+ do { \
+ pc = 0xff & (*state >> 0); \
+ srchi = 0xff & (*state >> 8); \
+ ndots = 0xff & (*state >> 16); \
+ } while (0)
+
+#define DNS_SM_SAVE \
+ do { \
+ *state = ((0xff & pc) << 0) \
+ | ((0xff & srchi) << 8) \
+ | ((0xff & ndots) << 16); \
+ } while (0)
+
+size_t dns_resconf_search(void *dst, size_t lim, const void *qname, size_t qlen, struct dns_resolv_conf *resconf, dns_resconf_i_t *state) {
+ unsigned pc, srchi, ndots, len;
+
+ DNS_SM_ENTER;
+
+ /* if FQDN then return as-is and finish */
+ if (dns_d_isanchored(qname, qlen)) {
+ len = dns_d_anchor(dst, lim, qname, qlen);
+ DNS_SM_YIELD(len);
+ DNS_SM_EXIT;
+ }
+
+ ndots = dns_d_ndots(qname, qlen);
+
+ if (ndots >= resconf->options.ndots) {
+ len = dns_d_anchor(dst, lim, qname, qlen);
+ DNS_SM_YIELD(len);
+ }
+
+ while (srchi < lengthof(resconf->search) && resconf->search[srchi][0]) {
+ struct dns_buf buf = DNS_B_INTO(dst, lim);
+ const char *dn = resconf->search[srchi++];
+
+ dns_b_put(&buf, qname, qlen);
+ dns_b_putc(&buf, '.');
+ dns_b_puts(&buf, dn);
+ if (!dns_d_isanchored(dn, strlen(dn)))
+ dns_b_putc(&buf, '.');
+ len = dns_b_strllen(&buf);
+ DNS_SM_YIELD(len);
+ }
+
+ if (ndots < resconf->options.ndots) {
+ len = dns_d_anchor(dst, lim, qname, qlen);
+ DNS_SM_YIELD(len);
+ }
+
+ DNS_SM_LEAVE;
+
+ return dns_strlcpy(dst, "", lim);
+} /* dns_resconf_search() */
+
+#undef DNS_SM_SAVE
+#undef DNS_SM_RESTORE
+
+
+int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) {
+ unsigned i;
+ int af;
+
+ for (i = 0; i < lengthof(resconf->nameserver) && (af = resconf->nameserver[i].ss_family) != AF_UNSPEC; i++) {
+ char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]";
+ unsigned short port;
+
+ dns_inet_ntop(af, dns_sa_addr(af, &resconf->nameserver[i], NULL), addr, sizeof addr);
+ port = ntohs(*dns_sa_port(af, &resconf->nameserver[i]));
+
+ if (port == 53)
+ fprintf(fp, "nameserver %s\n", addr);
+ else
+ fprintf(fp, "nameserver [%s]:%hu\n", addr, port);
+ }
+
+
+ fprintf(fp, "search");
+
+ for (i = 0; i < lengthof(resconf->search) && resconf->search[i][0]; i++)
+ fprintf(fp, " %s", resconf->search[i]);
+
+ fputc('\n', fp);
+
+
+ fputs("; ", fp);
+ dns_nssconf_dump(resconf, fp);
+
+ fprintf(fp, "lookup");
+
+ for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) {
+ switch (resconf->lookup[i]) {
+ case 'b':
+ fprintf(fp, " bind"); break;
+ case 'f':
+ fprintf(fp, " file"); break;
+ case 'c':
+ fprintf(fp, " cache"); break;
+ }
+ }
+
+ fputc('\n', fp);
+
+
+ fprintf(fp, "options ndots:%u timeout:%u attempts:%u", resconf->options.ndots, resconf->options.timeout, resconf->options.attempts);
+
+ if (resconf->options.edns0)
+ fprintf(fp, " edns0");
+ if (resconf->options.rotate)
+ fprintf(fp, " rotate");
+ if (resconf->options.recurse)
+ fprintf(fp, " recurse");
+ if (resconf->options.smart)
+ fprintf(fp, " smart");
+
+ switch (resconf->options.tcp) {
+ case DNS_RESCONF_TCP_ENABLE:
+ break;
+ case DNS_RESCONF_TCP_ONLY:
+ fprintf(fp, " tcp");
+ break;
+ case DNS_RESCONF_TCP_SOCKS:
+ fprintf(fp, " tcp:socks");
+ break;
+ case DNS_RESCONF_TCP_DISABLE:
+ fprintf(fp, " tcp:disable");
+ break;
+ }
+
+ fputc('\n', fp);
+
+
+ if ((af = resconf->iface.ss_family) != AF_UNSPEC) {
+ char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]";
+
+ dns_inet_ntop(af, dns_sa_addr(af, &resconf->iface, NULL), addr, sizeof addr);
+
+ fprintf(fp, "interface %s %hu\n", addr, ntohs(*dns_sa_port(af, &resconf->iface)));
+ }
+
+ return 0;
+} /* dns_resconf_dump() */
+
+
+/*
+ * H I N T S E R V E R R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_hints_soa {
+ unsigned char zone[DNS_D_MAXNAME + 1];
+
+ struct {
+ struct sockaddr_storage ss;
+ unsigned priority;
+ } addrs[16];
+
+ unsigned count;
+
+ struct dns_hints_soa *next;
+}; /* struct dns_hints_soa */
+
+
+struct dns_hints {
+ dns_atomic_t refcount;
+
+ struct dns_hints_soa *head;
+}; /* struct dns_hints */
+
+
+struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf, int *error) {
+ static const struct dns_hints H_initializer;
+ struct dns_hints *H;
+
+ (void)resconf;
+
+ if (!(H = malloc(sizeof *H)))
+ goto syerr;
+
+ *H = H_initializer;
+
+ dns_hints_acquire(H);
+
+ return H;
+syerr:
+ *error = dns_syerr();
+
+ free(H);
+
+ return 0;
+} /* dns_hints_open() */
+
+
+void dns_hints_close(struct dns_hints *H) {
+ struct dns_hints_soa *soa, *nxt;
+
+ if (!H || 1 != dns_hints_release(H))
+ return /* void */;
+
+ for (soa = H->head; soa; soa = nxt) {
+ nxt = soa->next;
+
+ free(soa);
+ }
+
+ free(H);
+
+ return /* void */;
+} /* dns_hints_close() */
+
+
+dns_refcount_t dns_hints_acquire(struct dns_hints *H) {
+ return dns_atomic_fetch_add(&H->refcount);
+} /* dns_hints_acquire() */
+
+
+dns_refcount_t dns_hints_release(struct dns_hints *H) {
+ return dns_atomic_fetch_sub(&H->refcount);
+} /* dns_hints_release() */
+
+
+struct dns_hints *dns_hints_mortal(struct dns_hints *hints) {
+ if (hints)
+ dns_hints_release(hints);
+
+ return hints;
+} /* dns_hints_mortal() */
+
+
+struct dns_hints *dns_hints_local(struct dns_resolv_conf *resconf, int *error_) {
+ struct dns_hints *hints = 0;
+ int error;
+
+ if (resconf)
+ dns_resconf_acquire(resconf);
+ else if (!(resconf = dns_resconf_local(&error)))
+ goto error;
+
+ if (!(hints = dns_hints_open(resconf, &error)))
+ goto error;
+
+ error = 0;
+
+ if (0 == dns_hints_insert_resconf(hints, ".", resconf, &error) && error)
+ goto error;
+
+ dns_resconf_close(resconf);
+
+ return hints;
+error:
+ *error_ = error;
+
+ dns_resconf_close(resconf);
+ dns_hints_close(hints);
+
+ return 0;
+} /* dns_hints_local() */
+
+
+struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) {
+ static const struct {
+ int af;
+ char addr[INET6_ADDRSTRLEN];
+ } root_hints[] = {
+ { AF_INET, "198.41.0.4" }, /* A.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:503:ba3e::2:30" }, /* A.ROOT-SERVERS.NET. */
+ { AF_INET, "192.228.79.201" }, /* B.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:500:84::b" }, /* B.ROOT-SERVERS.NET. */
+ { AF_INET, "192.33.4.12" }, /* C.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:500:2::c" }, /* C.ROOT-SERVERS.NET. */
+ { AF_INET, "199.7.91.13" }, /* D.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:500:2d::d" }, /* D.ROOT-SERVERS.NET. */
+ { AF_INET, "192.203.230.10" }, /* E.ROOT-SERVERS.NET. */
+ { AF_INET, "192.5.5.241" }, /* F.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:500:2f::f" }, /* F.ROOT-SERVERS.NET. */
+ { AF_INET, "192.112.36.4" }, /* G.ROOT-SERVERS.NET. */
+ { AF_INET, "128.63.2.53" }, /* H.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:500:1::803f:235" }, /* H.ROOT-SERVERS.NET. */
+ { AF_INET, "192.36.148.17" }, /* I.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:7FE::53" }, /* I.ROOT-SERVERS.NET. */
+ { AF_INET, "192.58.128.30" }, /* J.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:503:c27::2:30" }, /* J.ROOT-SERVERS.NET. */
+ { AF_INET, "193.0.14.129" }, /* K.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:7FD::1" }, /* K.ROOT-SERVERS.NET. */
+ { AF_INET, "199.7.83.42" }, /* L.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:500:3::42" }, /* L.ROOT-SERVERS.NET. */
+ { AF_INET, "202.12.27.33" }, /* M.ROOT-SERVERS.NET. */
+ { AF_INET6, "2001:DC3::35" }, /* M.ROOT-SERVERS.NET. */
+ };
+ struct dns_hints *hints = 0;
+ struct sockaddr_storage ss;
+ unsigned i;
+ int error, af;
+
+ if (!(hints = dns_hints_open(resconf, &error)))
+ goto error;
+
+ for (i = 0; i < lengthof(root_hints); i++) {
+ af = root_hints[i].af;
+
+ if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss, NULL))))
+ goto error;
+
+ *dns_sa_port(af, &ss) = htons(53);
+ ss.ss_family = af;
+
+ if ((error = dns_hints_insert(hints, ".", (struct sockaddr *)&ss, 1)))
+ goto error;
+ }
+
+ return hints;
+error:
+ *error_ = error;
+
+ dns_hints_close(hints);
+
+ return 0;
+} /* dns_hints_root() */
+
+
+static struct dns_hints_soa *dns_hints_fetch(struct dns_hints *H, const char *zone) {
+ struct dns_hints_soa *soa;
+
+ for (soa = H->head; soa; soa = soa->next) {
+ if (0 == strcasecmp(zone, (char *)soa->zone))
+ return soa;
+ }
+
+ return 0;
+} /* dns_hints_fetch() */
+
+
+int dns_hints_insert(struct dns_hints *H, const char *zone, const struct sockaddr *sa, unsigned priority) {
+ static const struct dns_hints_soa soa_initializer;
+ struct dns_hints_soa *soa;
+ unsigned i;
+
+ if (!(soa = dns_hints_fetch(H, zone))) {
+ if (!(soa = malloc(sizeof *soa)))
+ return dns_syerr();
+ *soa = soa_initializer;
+ dns_strlcpy((char *)soa->zone, zone, sizeof soa->zone);
+
+ soa->next = H->head;
+ H->head = soa;
+ }
+
+ i = soa->count % lengthof(soa->addrs);
+
+ memcpy(&soa->addrs[i].ss, sa, dns_sa_len(sa));
+
+ soa->addrs[i].priority = DNS_PP_MAX(1, priority);
+
+ if (soa->count < lengthof(soa->addrs))
+ soa->count++;
+
+ return 0;
+} /* dns_hints_insert() */
+
+
+static _Bool dns_hints_isinaddr_any(const void *sa) {
+ struct in_addr *addr;
+
+ if (dns_sa_family(sa) != AF_INET)
+ return 0;
+
+ addr = dns_sa_addr(AF_INET, sa, NULL);
+ return addr->s_addr == htonl(INADDR_ANY);
+}
+
+unsigned dns_hints_insert_resconf(struct dns_hints *H, const char *zone, const struct dns_resolv_conf *resconf, int *error_) {
+ unsigned i, n, p;
+ int error;
+
+ for (i = 0, n = 0, p = 1; i < lengthof(resconf->nameserver) && resconf->nameserver[i].ss_family != AF_UNSPEC; i++, n++) {
+ union { struct sockaddr_in sin; } tmp;
+ struct sockaddr *ns;
+
+ /*
+ * dns_resconf_open initializes nameserver[0] to INADDR_ANY.
+ *
+ * Traditionally the semantics of 0.0.0.0 meant the default
+ * interface, which evolved to mean the loopback interface.
+ * See comment block preceding resolv/res_init.c:res_init in
+ * glibc 2.23. As of 2.23, glibc no longer translates
+ * 0.0.0.0 despite the code comment, but it does default to
+ * 127.0.0.1 when no nameservers are present.
+ *
+ * BIND9 as of 9.10.3 still translates 0.0.0.0 to 127.0.0.1.
+ * See lib/lwres/lwconfig.c:lwres_create_addr and the
+ * convert_zero flag. 127.0.0.1 is also the default when no
+ * nameservers are present.
+ */
+ if (dns_hints_isinaddr_any(&resconf->nameserver[i])) {
+ memcpy(&tmp.sin, &resconf->nameserver[i], sizeof tmp.sin);
+ tmp.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ns = (struct sockaddr *)&tmp.sin;
+ } else {
+ ns = (struct sockaddr *)&resconf->nameserver[i];
+ }
+
+ if ((error = dns_hints_insert(H, zone, ns, p)))
+ goto error;
+
+ p += !resconf->options.rotate;
+ }
+
+ return n;
+error:
+ *error_ = error;
+
+ return n;
+} /* dns_hints_insert_resconf() */
+
+
+static int dns_hints_i_cmp(unsigned a, unsigned b, struct dns_hints_i *i, struct dns_hints_soa *soa) {
+ int cmp;
+
+ if ((cmp = soa->addrs[a].priority - soa->addrs[b].priority))
+ return cmp;
+
+ return dns_k_shuffle16(a, i->state.seed) - dns_k_shuffle16(b, i->state.seed);
+} /* dns_hints_i_cmp() */
+
+
+static unsigned dns_hints_i_start(struct dns_hints_i *i, struct dns_hints_soa *soa) {
+ unsigned p0, p;
+
+ p0 = 0;
+
+ for (p = 1; p < soa->count; p++) {
+ if (dns_hints_i_cmp(p, p0, i, soa) < 0)
+ p0 = p;
+ }
+
+ return p0;
+} /* dns_hints_i_start() */
+
+
+static unsigned dns_hints_i_skip(unsigned p0, struct dns_hints_i *i, struct dns_hints_soa *soa) {
+ unsigned pZ, p;
+
+ for (pZ = 0; pZ < soa->count; pZ++) {
+ if (dns_hints_i_cmp(pZ, p0, i, soa) > 0)
+ goto cont;
+ }
+
+ return soa->count;
+cont:
+ for (p = pZ + 1; p < soa->count; p++) {
+ if (dns_hints_i_cmp(p, p0, i, soa) <= 0)
+ continue;
+
+ if (dns_hints_i_cmp(p, pZ, i, soa) >= 0)
+ continue;
+
+ pZ = p;
+ }
+
+
+ return pZ;
+} /* dns_hints_i_skip() */
+
+
+static struct dns_hints_i *dns_hints_i_init(struct dns_hints_i *i, struct dns_hints *hints) {
+ static const struct dns_hints_i i_initializer;
+ struct dns_hints_soa *soa;
+
+ i->state = i_initializer.state;
+
+ do {
+ i->state.seed = dns_random();
+ } while (0 == i->state.seed);
+
+ if ((soa = dns_hints_fetch(hints, i->zone))) {
+ i->state.next = dns_hints_i_start(i, soa);
+ }
+
+ return i;
+} /* dns_hints_i_init() */
+
+
+unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, struct dns_hints_i *i, struct dns_hints *H) {
+ struct dns_hints_soa *soa;
+ unsigned n;
+
+ if (!(soa = dns_hints_fetch(H, i->zone)))
+ return 0;
+
+ n = 0;
+
+ while (i->state.next < soa->count && n < lim) {
+ *sa = (struct sockaddr *)&soa->addrs[i->state.next].ss;
+ *sa_len = dns_sa_len(*sa);
+
+ sa++;
+ sa_len++;
+ n++;
+
+ i->state.next = dns_hints_i_skip(i->state.next, i, soa);
+ }
+
+ return n;
+} /* dns_hints_grep() */
+
+
+struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) {
+ struct dns_packet *A, *P;
+ struct dns_rr rr;
+ char zone[DNS_D_MAXNAME + 1];
+ size_t zlen;
+ struct dns_hints_i i;
+ struct sockaddr *sa;
+ socklen_t slen;
+ int error;
+
+ if (!dns_rr_grep(&rr, 1, dns_rr_i_new(Q, .section = DNS_S_QUESTION), Q, &error))
+ goto error;
+
+ if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error)))
+ goto error;
+ else if (zlen >= sizeof zone)
+ goto toolong;
+
+ P = dns_p_new(512);
+ dns_header(P)->qr = 1;
+
+ if ((error = dns_rr_copy(P, &rr, Q)))
+ goto error;
+
+ if ((error = dns_p_push(P, DNS_S_AUTHORITY, ".", strlen("."), DNS_T_NS, DNS_C_IN, 0, "hints.local.")))
+ goto error;
+
+ do {
+ i.zone = zone;
+
+ dns_hints_i_init(&i, hints);
+
+ while (dns_hints_grep(&sa, &slen, 1, &i, hints)) {
+ int af = sa->sa_family;
+ int rtype = (af == AF_INET6)? DNS_T_AAAA : DNS_T_A;
+
+ if ((error = dns_p_push(P, DNS_S_ADDITIONAL, "hints.local.", strlen("hints.local."), rtype, DNS_C_IN, 0, dns_sa_addr(af, sa, NULL))))
+ goto error;
+ }
+ } while ((zlen = dns_d_cleave(zone, sizeof zone, zone, zlen)));
+
+ if (!(A = dns_p_copy(dns_p_make(P->end, &error), P)))
+ goto error;
+
+ return A;
+toolong:
+ error = DNS_EILLEGAL;
+error:
+ *error_ = error;
+
+ return 0;
+} /* dns_hints_query() */
+
+
+/** ugly hack to support specifying ports other than 53 in resolv.conf. */
+static unsigned short dns_hints_port(struct dns_hints *hints, int af, void *addr) {
+ struct dns_hints_soa *soa;
+ void *addrsoa;
+ socklen_t addrlen;
+ unsigned short port;
+ unsigned i;
+
+ for (soa = hints->head; soa; soa = soa->next) {
+ for (i = 0; i < soa->count; i++) {
+ if (af != soa->addrs[i].ss.ss_family)
+ continue;
+
+ if (!(addrsoa = dns_sa_addr(af, &soa->addrs[i].ss, &addrlen)))
+ continue;
+
+ if (memcmp(addr, addrsoa, addrlen))
+ continue;
+
+ port = *dns_sa_port(af, &soa->addrs[i].ss);
+
+ return (port)? port : htons(53);
+ }
+ }
+
+ return htons(53);
+} /* dns_hints_port() */
+
+
+int dns_hints_dump(struct dns_hints *hints, FILE *fp) {
+ struct dns_hints_soa *soa;
+ char addr[INET6_ADDRSTRLEN];
+ unsigned i;
+ int af, error;
+
+ for (soa = hints->head; soa; soa = soa->next) {
+ fprintf(fp, "ZONE \"%s\"\n", soa->zone);
+
+ for (i = 0; i < soa->count; i++) {
+ af = soa->addrs[i].ss.ss_family;
+
+ if ((error = dns_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss, NULL), addr, sizeof addr)))
+ return error;
+
+ fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss)));
+ }
+ }
+
+ return 0;
+} /* dns_hints_dump() */
+
+
+/*
+ * C A C H E R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static dns_refcount_t dns_cache_acquire(struct dns_cache *cache) {
+ return dns_atomic_fetch_add(&cache->_.refcount);
+} /* dns_cache_acquire() */
+
+
+static dns_refcount_t dns_cache_release(struct dns_cache *cache) {
+ return dns_atomic_fetch_sub(&cache->_.refcount);
+} /* dns_cache_release() */
+
+
+static struct dns_packet *dns_cache_query(struct dns_packet *query, struct dns_cache *cache, int *error) {
+ (void)query;
+ (void)cache;
+ (void)error;
+
+ return NULL;
+} /* dns_cache_query() */
+
+
+static int dns_cache_submit(struct dns_packet *query, struct dns_cache *cache) {
+ (void)query;
+ (void)cache;
+
+ return 0;
+} /* dns_cache_submit() */
+
+
+static int dns_cache_check(struct dns_cache *cache) {
+ (void)cache;
+
+ return 0;
+} /* dns_cache_check() */
+
+
+static struct dns_packet *dns_cache_fetch(struct dns_cache *cache, int *error) {
+ (void)cache;
+ (void)error;
+
+ return NULL;
+} /* dns_cache_fetch() */
+
+
+static int dns_cache_pollfd(struct dns_cache *cache) {
+ (void)cache;
+
+ return -1;
+} /* dns_cache_pollfd() */
+
+
+static short dns_cache_events(struct dns_cache *cache) {
+ (void)cache;
+
+ return 0;
+} /* dns_cache_events() */
+
+
+static void dns_cache_clear(struct dns_cache *cache) {
+ (void)cache;
+
+ return;
+} /* dns_cache_clear() */
+
+
+struct dns_cache *dns_cache_init(struct dns_cache *cache) {
+ static const struct dns_cache c_init = {
+ .acquire = &dns_cache_acquire,
+ .release = &dns_cache_release,
+ .query = &dns_cache_query,
+ .submit = &dns_cache_submit,
+ .check = &dns_cache_check,
+ .fetch = &dns_cache_fetch,
+ .pollfd = &dns_cache_pollfd,
+ .events = &dns_cache_events,
+ .clear = &dns_cache_clear,
+ ._ = { .refcount = 1, },
+ };
+
+ *cache = c_init;
+
+ return cache;
+} /* dns_cache_init() */
+
+
+void dns_cache_close(struct dns_cache *cache) {
+ if (cache)
+ cache->release(cache);
+} /* dns_cache_close() */
+
+
+/*
+ * S O C K E T R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void dns_socketclose(int *fd, const struct dns_options *opts) {
+ if (opts && opts->closefd.cb)
+ opts->closefd.cb(fd, opts->closefd.arg);
+
+ if (*fd != -1) {
+#if _WIN32
+ closesocket(*fd);
+#else
+ close(*fd);
+#endif
+ *fd = -1;
+ }
+} /* dns_socketclose() */
+
+
+#ifndef HAVE_IOCTLSOCKET
+#define HAVE_IOCTLSOCKET (_WIN32 || _WIN64)
+#endif
+
+#ifndef HAVE_SOCK_CLOEXEC
+#define HAVE_SOCK_CLOEXEC (defined SOCK_CLOEXEC)
+#endif
+
+#ifndef HAVE_SOCK_NONBLOCK
+#define HAVE_SOCK_NONBLOCK (defined SOCK_NONBLOCK)
+#endif
+
+#define DNS_SO_MAXTRY 7
+
+static int dns_socket(struct sockaddr *local, int type, int *error_) {
+ int fd = -1, flags, error;
+#if defined FIONBIO
+ unsigned long opt;
+#endif
+
+ flags = 0;
+#if HAVE_SOCK_CLOEXEC
+ flags |= SOCK_CLOEXEC;
+#endif
+#if HAVE_SOCK_NONBLOCK
+ flags |= SOCK_NONBLOCK;
+#endif
+ if (-1 == (fd = socket(local->sa_family, type|flags, 0)))
+ goto soerr;
+
+#if defined F_SETFD && !HAVE_SOCK_CLOEXEC
+ if (-1 == fcntl(fd, F_SETFD, 1))
+ goto syerr;
+#endif
+
+#if defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK
+ if (-1 == (flags = fcntl(fd, F_GETFL)))
+ goto syerr;
+ if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+ goto syerr;
+#elif defined FIONBIO && HAVE_IOCTLSOCKET
+ opt = 1;
+ if (0 != ioctlsocket(fd, FIONBIO, &opt))
+ goto soerr;
+#endif
+
+#if defined SO_NOSIGPIPE
+ if (type != SOCK_DGRAM) {
+ if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof (int)))
+ goto soerr;
+ }
+#endif
+
+ if (local->sa_family != AF_INET && local->sa_family != AF_INET6)
+ return fd;
+
+ if (type != SOCK_DGRAM)
+ return fd;
+
+ /*
+ * FreeBSD, Linux, OpenBSD, OS X, and Solaris use random ports by
+ * default. Though the ephemeral range is quite small on OS X
+ * (49152-65535 on 10.10) and Linux (32768-60999 on 4.4.0, Ubuntu
+ * Xenial). See also RFC 6056.
+ *
+ * TODO: Optionally rely on the kernel to select a random port.
+ */
+ if (*dns_sa_port(local->sa_family, local) == 0) {
+ struct sockaddr_storage tmp;
+ unsigned i, port;
+
+ memcpy(&tmp, local, dns_sa_len(local));
+
+ for (i = 0; i < DNS_SO_MAXTRY; i++) {
+ port = 1025 + (dns_random() % 64510);
+
+ *dns_sa_port(tmp.ss_family, &tmp) = htons(port);
+
+ if (0 == bind(fd, (struct sockaddr *)&tmp, dns_sa_len(&tmp)))
+ return fd;
+ }
+
+ /* NB: continue to next bind statement */
+ }
+
+ if (0 == bind(fd, local, dns_sa_len(local)))
+ return fd;
+
+ /* FALL THROUGH */
+soerr:
+ error = dns_soerr();
+
+ goto error;
+#if (defined F_SETFD && !HAVE_SOCK_CLOEXEC) || (defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK)
+syerr:
+ error = dns_syerr();
+
+ goto error;
+#endif
+error:
+ *error_ = error;
+
+ dns_socketclose(&fd, NULL);
+
+ return -1;
+} /* dns_socket() */
+
+
+enum {
+ DNS_SO_UDP_INIT = 1,
+ DNS_SO_UDP_CONN,
+ DNS_SO_UDP_SEND,
+ DNS_SO_UDP_RECV,
+ DNS_SO_UDP_DONE,
+
+ DNS_SO_TCP_INIT,
+ DNS_SO_TCP_CONN,
+ DNS_SO_TCP_SEND,
+ DNS_SO_TCP_RECV,
+ DNS_SO_TCP_DONE,
+
+ DNS_SO_SOCKS_INIT,
+ DNS_SO_SOCKS_CONN,
+ DNS_SO_SOCKS_HELLO_SEND,
+ DNS_SO_SOCKS_HELLO_RECV,
+ DNS_SO_SOCKS_AUTH_SEND,
+ DNS_SO_SOCKS_AUTH_RECV,
+ DNS_SO_SOCKS_REQUEST_PREPARE,
+ DNS_SO_SOCKS_REQUEST_SEND,
+ DNS_SO_SOCKS_REQUEST_RECV,
+ DNS_SO_SOCKS_REQUEST_RECV_V6,
+ DNS_SO_SOCKS_HANDSHAKE_DONE,
+};
+
+struct dns_socket {
+ struct dns_options opts;
+
+ int udp;
+ int tcp;
+
+ int *old;
+ unsigned onum, olim;
+
+ int type;
+
+ struct sockaddr_storage local, remote;
+
+ struct dns_k_permutor qids;
+
+ struct dns_stat stat;
+
+ struct dns_trace *trace;
+
+ /*
+ * NOTE: dns_so_reset() zeroes everything from here down.
+ */
+ int state;
+
+ unsigned short qid;
+ char qname[DNS_D_MAXNAME + 1];
+ size_t qlen;
+ enum dns_type qtype;
+ enum dns_class qclass;
+
+ struct dns_packet *query;
+ size_t qout;
+
+ /* During a SOCKS handshake the query is temporarily stored
+ * here. */
+ struct dns_packet *query_backup;
+
+ struct dns_clock elapsed;
+
+ struct dns_packet *answer;
+ size_t alen, apos;
+}; /* struct dns_socket */
+
+
+/*
+ * NOTE: Actual closure delayed so that kqueue(2) and epoll(2) callers have
+ * a chance to recognize a state change after installing a persistent event
+ * and where sequential descriptors with the same integer value returned
+ * from _pollfd() would be ambiguous. See dns_so_closefds().
+ */
+static int dns_so_closefd(struct dns_socket *so, int *fd) {
+ int error;
+
+ if (*fd == -1)
+ return 0;
+
+ if (so->opts.closefd.cb) {
+ if ((error = so->opts.closefd.cb(fd, so->opts.closefd.arg))) {
+ return error;
+ } else if (*fd == -1)
+ return 0;
+ }
+
+ if (!(so->onum < so->olim)) {
+ unsigned olim = DNS_PP_MAX(4, so->olim * 2);
+ void *old;
+
+ if (!(old = realloc(so->old, sizeof so->old[0] * olim)))
+ return dns_syerr();
+
+ so->old = old;
+ so->olim = olim;
+ }
+
+ so->old[so->onum++] = *fd;
+ *fd = -1;
+
+ return 0;
+} /* dns_so_closefd() */
+
+
+#define DNS_SO_CLOSE_UDP 0x01
+#define DNS_SO_CLOSE_TCP 0x02
+#define DNS_SO_CLOSE_OLD 0x04
+#define DNS_SO_CLOSE_ALL (DNS_SO_CLOSE_UDP|DNS_SO_CLOSE_TCP|DNS_SO_CLOSE_OLD)
+
+static void dns_so_closefds(struct dns_socket *so, int which) {
+ if (DNS_SO_CLOSE_UDP & which)
+ dns_socketclose(&so->udp, &so->opts);
+ if (DNS_SO_CLOSE_TCP & which)
+ dns_socketclose(&so->tcp, &so->opts);
+ if (DNS_SO_CLOSE_OLD & which) {
+ unsigned i;
+ for (i = 0; i < so->onum; i++)
+ dns_socketclose(&so->old[i], &so->opts);
+ so->onum = 0;
+ free(so->old);
+ so->old = 0;
+ so->olim = 0;
+ }
+} /* dns_so_closefds() */
+
+
+static void dns_so_destroy(struct dns_socket *);
+
+static struct dns_socket *dns_so_init(struct dns_socket *so, const struct sockaddr *local, int type, const struct dns_options *opts, int *error) {
+ static const struct dns_socket so_initializer = { .opts = DNS_OPTS_INITIALIZER, .udp = -1, .tcp = -1, };
+
+ *so = so_initializer;
+ so->type = type;
+
+ if (opts)
+ so->opts = *opts;
+
+ if (local)
+ memcpy(&so->local, local, dns_sa_len(local));
+
+ if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, error)))
+ goto error;
+
+ dns_k_permutor_init(&so->qids, 1, 65535);
+
+ return so;
+error:
+ dns_so_destroy(so);
+
+ return 0;
+} /* dns_so_init() */
+
+
+struct dns_socket *dns_so_open(const struct sockaddr *local, int type, const struct dns_options *opts, int *error) {
+ struct dns_socket *so;
+
+ if (!(so = malloc(sizeof *so)))
+ goto syerr;
+
+ if (!dns_so_init(so, local, type, opts, error))
+ goto error;
+
+ return so;
+syerr:
+ *error = dns_syerr();
+error:
+ dns_so_close(so);
+
+ return 0;
+} /* dns_so_open() */
+
+
+static void dns_so_destroy(struct dns_socket *so) {
+ dns_so_reset(so);
+ dns_so_closefds(so, DNS_SO_CLOSE_ALL);
+ dns_trace_close(so->trace);
+} /* dns_so_destroy() */
+
+
+void dns_so_close(struct dns_socket *so) {
+ if (!so)
+ return;
+
+ dns_so_destroy(so);
+
+ free(so);
+} /* dns_so_close() */
+
+
+void dns_so_reset(struct dns_socket *so) {
+ dns_p_setptr(&so->answer, NULL);
+
+ memset(&so->state, '\0', sizeof *so - offsetof(struct dns_socket, state));
+} /* dns_so_reset() */
+
+
+unsigned short dns_so_mkqid(struct dns_socket *so) {
+ return dns_k_permutor_step(&so->qids);
+} /* dns_so_mkqid() */
+
+
+#define DNS_SO_MINBUF 768
+
+static int dns_so_newanswer(struct dns_socket *so, size_t len) {
+ size_t size = offsetof(struct dns_packet, data) + DNS_PP_MAX(len, DNS_SO_MINBUF);
+ void *p;
+
+ if (!(p = realloc(so->answer, size)))
+ return dns_syerr();
+
+ so->answer = dns_p_init(p, size);
+
+ return 0;
+} /* dns_so_newanswer() */
+
+
+int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host) {
+ struct dns_rr rr;
+ int error = DNS_EUNKNOWN;
+
+ dns_so_reset(so);
+
+ if ((error = dns_rr_parse(&rr, 12, Q)))
+ goto error;
+
+ if (!(so->qlen = dns_d_expand(so->qname, sizeof so->qname, rr.dn.p, Q, &error)))
+ goto error;
+ /*
+ * NOTE: Don't bail if expansion is too long; caller may be
+ * intentionally sending long names. However, we won't be able to
+ * verify it on return.
+ */
+
+ so->qtype = rr.type;
+ so->qclass = rr.class;
+
+ if ((error = dns_so_newanswer(so, (Q->memo.opt.maxudp)? Q->memo.opt.maxudp : DNS_SO_MINBUF)))
+ goto syerr;
+
+ memcpy(&so->remote, host, dns_sa_len(host));
+
+ so->query = Q;
+ so->qout = 0;
+
+ dns_begin(&so->elapsed);
+
+ if (dns_header(so->query)->qid == 0)
+ dns_header(so->query)->qid = dns_so_mkqid(so);
+
+ so->qid = dns_header(so->query)->qid;
+ so->state = (so->opts.socks_host && so->opts.socks_host->ss_family) ? DNS_SO_SOCKS_INIT :
+ (so->type == SOCK_STREAM)? DNS_SO_TCP_INIT : DNS_SO_UDP_INIT;
+
+ so->stat.queries++;
+ dns_trace_so_submit(so->trace, Q, host, 0);
+
+ return 0;
+syerr:
+ error = dns_syerr();
+error:
+ dns_so_reset(so);
+ dns_trace_so_submit(so->trace, Q, host, error);
+ return error;
+} /* dns_so_submit() */
+
+
+static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) {
+ char qname[DNS_D_MAXNAME + 1];
+ size_t qlen;
+ struct dns_rr rr;
+ int error = -1;
+
+ if (P->end < 12)
+ goto reject;
+
+ if (so->qid != dns_header(P)->qid)
+ goto reject;
+
+ if (!dns_p_count(P, DNS_S_QD))
+ goto reject;
+
+ if (0 != dns_rr_parse(&rr, 12, P))
+ goto reject;
+
+ if (rr.type != so->qtype || rr.class != so->qclass)
+ goto reject;
+
+ if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, P, &error)))
+ goto error;
+ else if (qlen >= sizeof qname || qlen != so->qlen)
+ goto reject;
+
+ if (0 != strcasecmp(so->qname, qname))
+ goto reject;
+
+ dns_trace_so_verify(so->trace, P, 0);
+
+ return 0;
+reject:
+ error = DNS_EVERIFY;
+error:
+ DNS_SHOW(P, "rejecting packet (%s)", dns_strerror(error));
+ dns_trace_so_verify(so->trace, P, error);
+
+ return error;
+} /* dns_so_verify() */
+
+
+static _Bool dns_so_tcp_keep(struct dns_socket *so) {
+ struct sockaddr_storage remote;
+
+ if (so->tcp == -1)
+ return 0;
+
+ if (0 != getpeername(so->tcp, (struct sockaddr *)&remote, &(socklen_t){ sizeof remote }))
+ return 0;
+
+ return 0 == dns_sa_cmp(&remote, &so->remote);
+} /* dns_so_tcp_keep() */
+
+
+/* Convenience functions for sending non-DNS data. */
+
+/* Set up everything for sending LENGTH octets. Returns the buffer
+ for the data. */
+static unsigned char *dns_so_tcp_send_buffer(struct dns_socket *so, size_t length) {
+ /* Skip the length octets, we are not doing DNS. */
+ so->qout = 2;
+ so->query->end = length;
+ return so->query->data;
+}
+
+/* Set up everything for receiving LENGTH octets. */
+static void dns_so_tcp_recv_expect(struct dns_socket *so, size_t length) {
+ /* Skip the length octets, we are not doing DNS. */
+ so->apos = 2;
+ so->alen = length;
+}
+
+/* Returns the buffer containing the received data. */
+static unsigned char *dns_so_tcp_recv_buffer(struct dns_socket *so) {
+ return so->answer->data;
+}
+
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warray-bounds"
+#endif
+
+static int dns_so_tcp_send(struct dns_socket *so) {
+ unsigned char *qsrc;
+ size_t qend;
+ int error;
+ size_t n;
+
+ so->query->data[-2] = 0xff & (so->query->end >> 8);
+ so->query->data[-1] = 0xff & (so->query->end >> 0);
+
+ qend = so->query->end + 2;
+
+ while (so->qout < qend) {
+ qsrc = &so->query->data[-2] + so->qout;
+ n = dns_send_nopipe(so->tcp, (void *)qsrc, qend - so->qout, 0, &error);
+ dns_trace_sys_send(so->trace, so->tcp, SOCK_STREAM, qsrc, n, error);
+ if (error)
+ return error;
+ so->qout += n;
+ so->stat.tcp.sent.bytes += n;
+ }
+
+ so->stat.tcp.sent.count++;
+
+ return 0;
+} /* dns_so_tcp_send() */
+
+
+static int dns_so_tcp_recv(struct dns_socket *so) {
+ unsigned char *asrc;
+ size_t aend, alen, n;
+ int error;
+
+ aend = so->alen + 2;
+
+ while (so->apos < aend) {
+ asrc = &so->answer->data[-2];
+
+ n = dns_recv(so->tcp, (void *)&asrc[so->apos], aend - so->apos, 0, &error);
+ dns_trace_sys_recv(so->trace, so->tcp, SOCK_STREAM, &asrc[so->apos], n, error);
+ if (error)
+ return error;
+
+ so->apos += n;
+ so->stat.tcp.rcvd.bytes += n;
+
+ if (so->alen == 0 && so->apos >= 2) {
+ alen = ((0xff & so->answer->data[-2]) << 8)
+ | ((0xff & so->answer->data[-1]) << 0);
+
+ if ((error = dns_so_newanswer(so, alen)))
+ return error;
+
+ so->alen = alen;
+ aend = alen + 2;
+ }
+ }
+
+ so->answer->end = so->alen;
+ so->stat.tcp.rcvd.count++;
+
+ return 0;
+} /* dns_so_tcp_recv() */
+
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+int dns_so_check(struct dns_socket *so) {
+ int error;
+ size_t n;
+ unsigned char *buffer;
+
+retry:
+ switch (so->state) {
+ case DNS_SO_UDP_INIT:
+ so->state++;
+ case DNS_SO_UDP_CONN:
+ error = dns_connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote));
+ dns_trace_sys_connect(so->trace, so->udp, SOCK_DGRAM, (struct sockaddr *)&so->remote, error);
+ if (error)
+ goto error;
+
+ so->state++;
+ case DNS_SO_UDP_SEND:
+ n = dns_send(so->udp, (void *)so->query->data, so->query->end, 0, &error);
+ dns_trace_sys_send(so->trace, so->udp, SOCK_DGRAM, so->query->data, n, error);
+ if (error)
+ goto error;
+
+ so->stat.udp.sent.bytes += n;
+ so->stat.udp.sent.count++;
+
+ so->state++;
+ case DNS_SO_UDP_RECV:
+ n = dns_recv(so->udp, (void *)so->answer->data, so->answer->size, 0, &error);
+ dns_trace_sys_recv(so->trace, so->udp, SOCK_DGRAM, so->answer->data, n, error);
+ if (error)
+ goto error;
+
+ so->answer->end = n;
+ so->stat.udp.rcvd.bytes += n;
+ so->stat.udp.rcvd.count++;
+
+ if ((error = dns_so_verify(so, so->answer)))
+ goto trash;
+
+ so->state++;
+ case DNS_SO_UDP_DONE:
+ if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM)
+ return 0;
+
+ so->state++;
+ case DNS_SO_TCP_INIT:
+ if (dns_so_tcp_keep(so)) {
+ so->state = DNS_SO_TCP_SEND;
+
+ goto retry;
+ }
+
+ if ((error = dns_so_closefd(so, &so->tcp)))
+ goto error;
+
+ if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error)))
+ goto error;
+
+ so->state++;
+ case DNS_SO_TCP_CONN:
+ error = dns_connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote));
+ dns_trace_sys_connect(so->trace, so->tcp, SOCK_STREAM, (struct sockaddr *)&so->remote, error);
+ if (error && error != DNS_EISCONN)
+ goto error;
+
+ so->state++;
+ case DNS_SO_TCP_SEND:
+ if ((error = dns_so_tcp_send(so)))
+ goto error;
+
+ so->state++;
+ case DNS_SO_TCP_RECV:
+ if ((error = dns_so_tcp_recv(so)))
+ goto error;
+
+ so->state++;
+ case DNS_SO_TCP_DONE:
+ /* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */
+ if (so->type != SOCK_STREAM) {
+ if ((error = dns_so_closefd(so, &so->tcp)))
+ goto error;
+ }
+
+ if ((error = dns_so_verify(so, so->answer)))
+ goto error;
+
+ return 0;
+ case DNS_SO_SOCKS_INIT:
+ if ((error = dns_so_closefd(so, &so->tcp)))
+ goto error;
+
+ if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error)))
+ goto error;
+
+ so->state++;
+ case DNS_SO_SOCKS_CONN: {
+ unsigned char method;
+
+ error = dns_connect(so->tcp, (struct sockaddr *)so->opts.socks_host, dns_sa_len(so->opts.socks_host));
+ dns_trace_sys_connect(so->trace, so->tcp, SOCK_STREAM, (struct sockaddr *)so->opts.socks_host, error);
+ if (error && error != DNS_EISCONN)
+ goto error;
+
+ /* We need to do a handshake with the SOCKS server,
+ * but the query is already in the buffer. Move it
+ * out of the way. */
+ dns_p_movptr(&so->query_backup, &so->query);
+
+ /* Create a new buffer for the handshake. */
+ dns_p_grow(&so->query);
+
+ /* Negotiate method. */
+ buffer = dns_so_tcp_send_buffer(so, 3);
+ buffer[0] = 5; /* RFC-1928 VER field. */
+ buffer[1] = 1; /* NMETHODS */
+ if (so->opts.socks_user)
+ method = 2; /* Method: username/password authentication. */
+ else
+ method = 0; /* Method: No authentication required. */
+ buffer[2] = method;
+
+ so->state++;
+ }
+ case DNS_SO_SOCKS_HELLO_SEND:
+ if ((error = dns_so_tcp_send(so)))
+ goto error;
+
+ dns_so_tcp_recv_expect(so, 2);
+ so->state++;
+ case DNS_SO_SOCKS_HELLO_RECV: {
+ unsigned char method;
+
+ if ((error = dns_so_tcp_recv(so)))
+ goto error;
+
+ buffer = dns_so_tcp_recv_buffer(so);
+ method = so->opts.socks_user ? 2 : 0;
+ if (buffer[0] != 5 || buffer[1] != method) {
+ /* Socks server returned wrong version or does
+ not support our requested method. */
+ error = ENOTSUP; /* Fixme: Is there a better errno? */
+ goto error;
+ }
+
+ if (method == 0) {
+ /* No authentication, go ahead and send the
+ request. */
+ so->state = DNS_SO_SOCKS_REQUEST_PREPARE;
+ goto retry;
+ }
+
+ /* Prepare username/password sub-negotiation. */
+ if (! so->opts.socks_password) {
+ error = EINVAL; /* No password given. */
+ goto error;
+ } else {
+ size_t buflen, ulen, plen;
+
+ ulen = strlen(so->opts.socks_user);
+ plen = strlen(so->opts.socks_password);
+ if (!ulen || ulen > 255 || !plen || plen > 255) {
+ error = EINVAL; /* Credentials too long or too short. */
+ goto error;
+ }
+
+ buffer = dns_so_tcp_send_buffer(so, 3 + ulen + plen);
+ buffer[0] = 1; /* VER of the sub-negotiation. */
+ buffer[1] = (unsigned char) ulen;
+ buflen = 2;
+ memcpy (buffer+buflen, so->opts.socks_user, ulen);
+ buflen += ulen;
+ buffer[buflen++] = (unsigned char) plen;
+ memcpy (buffer+buflen, so->opts.socks_password, plen);
+ }
+
+ so->state++;
+ }
+ case DNS_SO_SOCKS_AUTH_SEND:
+ if ((error = dns_so_tcp_send(so)))
+ goto error;
+
+ /* Skip the two length octets, and receive two octets. */
+ dns_so_tcp_recv_expect(so, 2);
+
+ so->state++;
+ case DNS_SO_SOCKS_AUTH_RECV:
+ if ((error = dns_so_tcp_recv(so)))
+ goto error;
+
+ buffer = dns_so_tcp_recv_buffer(so);
+ if (buffer[0] != 1) {
+ /* SOCKS server returned wrong version. */
+ error = EPROTO;
+ goto error;
+ }
+ if (buffer[1]) {
+ /* SOCKS server denied access. */
+ error = EACCES;
+ goto error;
+ }
+
+ so->state++;
+ case DNS_SO_SOCKS_REQUEST_PREPARE:
+ /* Send request details (rfc-1928, 4). */
+ buffer = dns_so_tcp_send_buffer(so, so->remote.ss_family == AF_INET6 ? 22 : 10);
+ buffer[0] = 5; /* VER */
+ buffer[1] = 1; /* CMD = CONNECT */
+ buffer[2] = 0; /* RSV */
+ if (so->remote.ss_family == AF_INET6) {
+ struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&so->remote;
+
+ buffer[3] = 4; /* ATYP = IPv6 */
+ memcpy (buffer+ 4, &addr_in6->sin6_addr.s6_addr, 16); /* DST.ADDR */
+ memcpy (buffer+20, &addr_in6->sin6_port, 2); /* DST.PORT */
+ } else {
+ struct sockaddr_in *addr_in = (struct sockaddr_in *)&so->remote;
+
+ buffer[3] = 1; /* ATYP = IPv4 */
+ memcpy (buffer+4, &addr_in->sin_addr.s_addr, 4); /* DST.ADDR */
+ memcpy (buffer+8, &addr_in->sin_port, 2); /* DST.PORT */
+ }
+
+ so->state++;
+ case DNS_SO_SOCKS_REQUEST_SEND:
+ if ((error = dns_so_tcp_send(so)))
+ goto error;
+
+ /* Expect ten octets. This is the length of the
+ * response assuming a IPv4 address is used. */
+ dns_so_tcp_recv_expect(so, 10);
+ so->state++;
+ case DNS_SO_SOCKS_REQUEST_RECV:
+ if ((error = dns_so_tcp_recv(so)))
+ goto error;
+
+ buffer = dns_so_tcp_recv_buffer(so);
+ if (buffer[0] != 5 || buffer[2] != 0) {
+ /* Socks server returned wrong version or the
+ reserved field is not zero. */
+ error = EPROTO;
+ goto error;
+ }
+ if (buffer[1]) {
+ switch (buffer[1]) {
+ case 0x01: /* general SOCKS server failure. */
+ error = ENETDOWN;
+ break;
+ case 0x02: /* connection not allowed by ruleset. */
+ error = EACCES;
+ break;
+ case 0x03: /* Network unreachable */
+ error = ENETUNREACH;
+ break;
+ case 0x04: /* Host unreachable */
+ error = EHOSTUNREACH;
+ break;
+ case 0x05: /* Connection refused */
+ error = ECONNREFUSED;
+ break;
+ case 0x06: /* TTL expired */
+ error = ETIMEDOUT;
+ break;
+ case 0x08: /* Address type not supported */
+ error = EPROTONOSUPPORT;
+ break;
+ case 0x07: /* Command not supported */
+ default:
+ error = ENOTSUP; /* Fixme: Is there a better error? */
+ break;
+ }
+ goto error;
+ }
+
+ if (buffer[3] == 1) {
+ /* This was indeed an IPv4 address. */
+ so->state = DNS_SO_SOCKS_HANDSHAKE_DONE;
+ goto retry;
+ }
+
+ if (buffer[3] != 4) {
+ error = ENOTSUP;
+ goto error;
+ }
+
+ /* Expect receive twelve octets. This accounts for
+ * the remaining bytes assuming an IPv6 address is
+ * used. */
+ dns_so_tcp_recv_expect(so, 12);
+ so->state++;
+ case DNS_SO_SOCKS_REQUEST_RECV_V6:
+ if ((error = dns_so_tcp_recv(so)))
+ goto error;
+
+ so->state++;
+ case DNS_SO_SOCKS_HANDSHAKE_DONE:
+ /* We have not way to store the actual address used by
+ * the server. Then again, we don't really care. */
+
+ /* Restore the query. */
+ dns_p_movptr(&so->query, &so->query_backup);
+
+ /* Reset cursors. */
+ so->qout = 0;
+ so->apos = 0;
+ so->alen = 0;
+
+ /* SOCKS handshake is done. Proceed with the
+ * lookup. */
+ so->state = DNS_SO_TCP_SEND;
+ goto retry;
+ default:
+ error = DNS_EUNKNOWN;
+
+ goto error;
+ } /* switch() */
+
+trash:
+ DNS_CARP("discarding packet");
+ goto retry;
+error:
+ switch (error) {
+ case DNS_EINTR:
+ goto retry;
+ case DNS_EINPROGRESS:
+ /* FALL THROUGH */
+ case DNS_EALREADY:
+ /* FALL THROUGH */
+#if DNS_EWOULDBLOCK != DNS_EAGAIN
+ case DNS_EWOULDBLOCK:
+ /* FALL THROUGH */
+#endif
+ error = DNS_EAGAIN;
+
+ break;
+ } /* switch() */
+
+ return error;
+} /* dns_so_check() */
+
+
+struct dns_packet *dns_so_fetch(struct dns_socket *so, int *error) {
+ struct dns_packet *answer;
+
+ switch (so->state) {
+ case DNS_SO_UDP_DONE:
+ case DNS_SO_TCP_DONE:
+ answer = so->answer;
+ so->answer = 0;
+ dns_trace_so_fetch(so->trace, answer, 0);
+
+ return answer;
+ default:
+ *error = DNS_EUNKNOWN;
+ dns_trace_so_fetch(so->trace, NULL, *error);
+
+ return 0;
+ }
+} /* dns_so_fetch() */
+
+
+struct dns_packet *dns_so_query(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host, int *error_) {
+ struct dns_packet *A;
+ int error;
+
+ if (!so->state) {
+ if ((error = dns_so_submit(so, Q, host)))
+ goto error;
+ }
+
+ if ((error = dns_so_check(so)))
+ goto error;
+
+ if (!(A = dns_so_fetch(so, &error)))
+ goto error;
+
+ dns_so_reset(so);
+
+ return A;
+error:
+ *error_ = error;
+
+ return 0;
+} /* dns_so_query() */
+
+
+time_t dns_so_elapsed(struct dns_socket *so) {
+ return dns_elapsed(&so->elapsed);
+} /* dns_so_elapsed() */
+
+
+void dns_so_clear(struct dns_socket *so) {
+ dns_so_closefds(so, DNS_SO_CLOSE_OLD);
+} /* dns_so_clear() */
+
+
+static int dns_so_events2(struct dns_socket *so, enum dns_events type) {
+ int events = 0;
+
+ switch (so->state) {
+ case DNS_SO_UDP_CONN:
+ case DNS_SO_UDP_SEND:
+ events |= DNS_POLLOUT;
+
+ break;
+ case DNS_SO_UDP_RECV:
+ events |= DNS_POLLIN;
+
+ break;
+ case DNS_SO_TCP_CONN:
+ case DNS_SO_TCP_SEND:
+ events |= DNS_POLLOUT;
+
+ break;
+ case DNS_SO_TCP_RECV:
+ events |= DNS_POLLIN;
+
+ break;
+ } /* switch() */
+
+ switch (type) {
+ case DNS_LIBEVENT:
+ return DNS_POLL2EV(events);
+ default:
+ return events;
+ } /* switch() */
+} /* dns_so_events2() */
+
+
+int dns_so_events(struct dns_socket *so) {
+ return dns_so_events2(so, so->opts.events);
+} /* dns_so_events() */
+
+
+int dns_so_pollfd(struct dns_socket *so) {
+ switch (so->state) {
+ case DNS_SO_UDP_CONN:
+ case DNS_SO_UDP_SEND:
+ case DNS_SO_UDP_RECV:
+ return so->udp;
+ case DNS_SO_TCP_CONN:
+ case DNS_SO_TCP_SEND:
+ case DNS_SO_TCP_RECV:
+ return so->tcp;
+ } /* switch() */
+
+ return -1;
+} /* dns_so_pollfd() */
+
+
+int dns_so_poll(struct dns_socket *so, int timeout) {
+ return dns_poll(dns_so_pollfd(so), dns_so_events2(so, DNS_SYSPOLL), timeout);
+} /* dns_so_poll() */
+
+
+const struct dns_stat *dns_so_stat(struct dns_socket *so) {
+ return &so->stat;
+} /* dns_so_stat() */
+
+
+struct dns_trace *dns_so_trace(struct dns_socket *so) {
+ return so->trace;
+} /* dns_so_trace() */
+
+
+void dns_so_settrace(struct dns_socket *so, struct dns_trace *trace) {
+ struct dns_trace *otrace = so->trace;
+ so->trace = dns_trace_acquire_p(trace);
+ dns_trace_close(otrace);
+} /* dns_so_settrace() */
+
+
+/*
+ * R E S O L V E R R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+enum dns_res_state {
+ DNS_R_INIT,
+ DNS_R_GLUE,
+ DNS_R_SWITCH, /* (B)IND, (F)ILE, (C)ACHE */
+
+ DNS_R_FILE, /* Lookup in local hosts database */
+
+ DNS_R_CACHE, /* Lookup in application cache */
+ DNS_R_SUBMIT,
+ DNS_R_CHECK,
+ DNS_R_FETCH,
+
+ DNS_R_BIND, /* Lookup in the network */
+ DNS_R_SEARCH,
+ DNS_R_HINTS,
+ DNS_R_ITERATE,
+ DNS_R_FOREACH_NS,
+ DNS_R_RESOLV0_NS, /* Prologue: Setup next frame and recurse */
+ DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */
+ DNS_R_FOREACH_A,
+ DNS_R_QUERY_A,
+ DNS_R_CNAME0_A,
+ DNS_R_CNAME1_A,
+
+ DNS_R_FINISH,
+ DNS_R_SMART0_A,
+ DNS_R_SMART1_A,
+ DNS_R_DONE,
+ DNS_R_SERVFAIL,
+}; /* enum dns_res_state */
+
+
+#define DNS_R_MAXDEPTH 8
+#define DNS_R_ENDFRAME (DNS_R_MAXDEPTH - 1)
+
+struct dns_resolver {
+ struct dns_socket so;
+
+ struct dns_resolv_conf *resconf;
+ struct dns_hosts *hosts;
+ struct dns_hints *hints;
+ struct dns_cache *cache;
+ struct dns_trace *trace;
+
+ dns_atomic_t refcount;
+
+ /* Reset zeroes everything below here. */
+
+ char qname[DNS_D_MAXNAME + 1];
+ size_t qlen;
+
+ enum dns_type qtype;
+ enum dns_class qclass;
+
+ struct dns_clock elapsed;
+
+ dns_resconf_i_t search;
+
+ struct dns_rr_i smart;
+
+ struct dns_packet *nodata; /* answer if nothing better */
+
+ unsigned sp;
+
+ struct dns_res_frame {
+ enum dns_res_state state;
+
+ int error;
+ int which; /* (B)IND, (F)ILE; index into resconf->lookup */
+ int qflags;
+
+ unsigned attempts;
+
+ struct dns_packet *query, *answer, *hints;
+
+ struct dns_rr_i hints_i, hints_j;
+ struct dns_rr hints_ns, ans_cname;
+ } stack[DNS_R_MAXDEPTH];
+}; /* struct dns_resolver */
+
+
+static int dns_res_tcp2type(int tcp) {
+ switch (tcp) {
+ case DNS_RESCONF_TCP_ONLY:
+ case DNS_RESCONF_TCP_SOCKS:
+ return SOCK_STREAM;
+ case DNS_RESCONF_TCP_DISABLE:
+ return SOCK_DGRAM;
+ default:
+ return 0;
+ }
+} /* dns_res_tcp2type() */
+
+struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *_error) {
+ static const struct dns_resolver R_initializer
+ = { .refcount = 1, };
+ struct dns_resolver *R = 0;
+ int type, error;
+
+ /*
+ * Grab ref count early because the caller may have passed us a mortal
+ * reference, and we want to do the right thing if we return early
+ * from an error.
+ */
+ if (resconf)
+ dns_resconf_acquire(resconf);
+ if (hosts)
+ dns_hosts_acquire(hosts);
+ if (hints)
+ dns_hints_acquire(hints);
+ if (cache)
+ dns_cache_acquire(cache);
+
+ /*
+ * Don't try to load it ourselves because a NULL object might be an
+ * error from, say, dns_resconf_root(), and loading
+ * dns_resconf_local() by default would create undesirable surpises.
+ */
+ if (!resconf || !hosts || !hints) {
+ if (!*_error)
+ *_error = EINVAL;
+ goto _error;
+ }
+
+ if (!(R = malloc(sizeof *R)))
+ goto syerr;
+
+ *R = R_initializer;
+ type = dns_res_tcp2type(resconf->options.tcp);
+
+ if (!dns_so_init(&R->so, (struct sockaddr *)&resconf->iface, type, opts, &error))
+ goto error;
+
+ R->resconf = resconf;
+ R->hosts = hosts;
+ R->hints = hints;
+ R->cache = cache;
+
+ return R;
+syerr:
+ error = dns_syerr();
+error:
+ *_error = error;
+_error:
+ dns_res_close(R);
+
+ dns_resconf_close(resconf);
+ dns_hosts_close(hosts);
+ dns_hints_close(hints);
+ dns_cache_close(cache);
+
+ return 0;
+} /* dns_res_open() */
+
+
+struct dns_resolver *dns_res_stub(const struct dns_options *opts, int *error) {
+ struct dns_resolv_conf *resconf = 0;
+ struct dns_hosts *hosts = 0;
+ struct dns_hints *hints = 0;
+ struct dns_resolver *res = 0;
+
+ if (!(resconf = dns_resconf_local(error)))
+ goto epilog;
+
+ if (!(hosts = dns_hosts_local(error)))
+ goto epilog;
+
+ if (!(hints = dns_hints_local(resconf, error)))
+ goto epilog;
+
+ if (!(res = dns_res_open(resconf, hosts, hints, NULL, opts, error)))
+ goto epilog;
+
+epilog:
+ dns_resconf_close(resconf);
+ dns_hosts_close(hosts);
+ dns_hints_close(hints);
+
+ return res;
+} /* dns_res_stub() */
+
+
+static void dns_res_frame_destroy(struct dns_resolver *R, struct dns_res_frame *frame) {
+ (void)R;
+
+ dns_p_setptr(&frame->query, NULL);
+ dns_p_setptr(&frame->answer, NULL);
+ dns_p_setptr(&frame->hints, NULL);
+} /* dns_res_frame_destroy() */
+
+
+static void dns_res_frame_init(struct dns_resolver *R, struct dns_res_frame *frame) {
+ memset(frame, '\0', sizeof *frame);
+
+ /*
+ * NB: Can be invoked from dns_res_open, before R->resconf has been
+ * initialized.
+ */
+ if (R->resconf) {
+ if (!R->resconf->options.recurse)
+ frame->qflags |= DNS_Q_RD;
+ if (R->resconf->options.edns0)
+ frame->qflags |= DNS_Q_EDNS0;
+ }
+} /* dns_res_frame_init() */
+
+
+static void dns_res_frame_reset(struct dns_resolver *R, struct dns_res_frame *frame) {
+ dns_res_frame_destroy(R, frame);
+ dns_res_frame_init(R, frame);
+} /* dns_res_frame_reset() */
+
+
+static dns_error_t dns_res_frame_prepare(struct dns_resolver *R, struct dns_res_frame *F, const char *qname, enum dns_type qtype, enum dns_class qclass) {
+ struct dns_packet *P = NULL;
+
+ if (!(F < endof(R->stack)))
+ return DNS_EUNKNOWN;
+
+ dns_p_movptr(&P, &F->query);
+ dns_res_frame_reset(R, F);
+ dns_p_movptr(&F->query, &P);
+
+ return dns_q_make(&F->query, qname, qtype, qclass, F->qflags);
+} /* dns_res_frame_prepare() */
+
+
+void dns_res_reset(struct dns_resolver *R) {
+ unsigned i;
+
+ dns_so_reset(&R->so);
+ dns_p_setptr(&R->nodata, NULL);
+
+ for (i = 0; i < lengthof(R->stack); i++)
+ dns_res_frame_destroy(R, &R->stack[i]);
+
+ memset(&R->qname, '\0', sizeof *R - offsetof(struct dns_resolver, qname));
+
+ for (i = 0; i < lengthof(R->stack); i++)
+ dns_res_frame_init(R, &R->stack[i]);
+} /* dns_res_reset() */
+
+
+void dns_res_close(struct dns_resolver *R) {
+ if (!R || 1 < dns_res_release(R))
+ return;
+
+ dns_res_reset(R);
+
+ dns_so_destroy(&R->so);
+
+ dns_hints_close(R->hints);
+ dns_hosts_close(R->hosts);
+ dns_resconf_close(R->resconf);
+ dns_cache_close(R->cache);
+ dns_trace_close(R->trace);
+
+ free(R);
+} /* dns_res_close() */
+
+
+dns_refcount_t dns_res_acquire(struct dns_resolver *R) {
+ return dns_atomic_fetch_add(&R->refcount);
+} /* dns_res_acquire() */
+
+
+dns_refcount_t dns_res_release(struct dns_resolver *R) {
+ return dns_atomic_fetch_sub(&R->refcount);
+} /* dns_res_release() */
+
+
+struct dns_resolver *dns_res_mortal(struct dns_resolver *res) {
+ if (res)
+ dns_res_release(res);
+ return res;
+} /* dns_res_mortal() */
+
+
+static struct dns_packet *dns_res_merge(struct dns_packet *P0, struct dns_packet *P1, int *error_) {
+ size_t bufsiz = P0->end + P1->end;
+ struct dns_packet *P[3] = { P0, P1, 0 };
+ struct dns_rr rr[3];
+ int error, copy, i;
+ enum dns_section section;
+
+retry:
+ if (!(P[2] = dns_p_make(bufsiz, &error)))
+ goto error;
+
+ dns_rr_foreach(&rr[0], P[0], .section = DNS_S_QD) {
+ if ((error = dns_rr_copy(P[2], &rr[0], P[0])))
+ goto error;
+ }
+
+ for (section = DNS_S_AN; (DNS_S_ALL & section); section <<= 1) {
+ for (i = 0; i < 2; i++) {
+ dns_rr_foreach(&rr[i], P[i], .section = section) {
+ copy = 1;
+
+ dns_rr_foreach(&rr[2], P[2], .type = rr[i].type, .section = (DNS_S_ALL & ~DNS_S_QD)) {
+ if (0 == dns_rr_cmp(&rr[i], P[i], &rr[2], P[2])) {
+ copy = 0;
+
+ break;
+ }
+ }
+
+ if (copy && (error = dns_rr_copy(P[2], &rr[i], P[i]))) {
+ if (error == DNS_ENOBUFS && bufsiz < 65535) {
+ dns_p_setptr(&P[2], NULL);
+
+ bufsiz = DNS_PP_MAX(65535, bufsiz * 2);
+
+ goto retry;
+ }
+
+ goto error;
+ }
+ } /* foreach(rr) */
+ } /* foreach(packet) */
+ } /* foreach(section) */
+
+ return P[2];
+error:
+ *error_ = error;
+
+ dns_p_free(P[2]);
+
+ return 0;
+} /* dns_res_merge() */
+
+
+static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) {
+ struct dns_packet *P = dns_p_new(512);
+ char qname[DNS_D_MAXNAME + 1];
+ size_t qlen;
+ enum dns_type qtype;
+ struct dns_rr rr;
+ unsigned sp;
+ int error;
+
+ if (!(qlen = dns_d_expand(qname, sizeof qname, 12, Q, &error))
+ || qlen >= sizeof qname)
+ return 0;
+
+ if (!(qtype = dns_rr_type(12, Q)))
+ return 0;
+
+ if ((error = dns_p_push(P, DNS_S_QD, qname, strlen(qname), qtype, DNS_C_IN, 0, 0)))
+ return 0;
+
+ for (sp = 0; sp <= R->sp; sp++) {
+ if (!R->stack[sp].answer)
+ continue;
+
+ dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = qtype, .section = (DNS_S_ALL & ~DNS_S_QD)) {
+ rr.section = DNS_S_AN;
+
+ if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer)))
+ return 0;
+ }
+ }
+
+ if (dns_p_count(P, DNS_S_AN) > 0)
+ goto copy;
+
+ /* Otherwise, look for a CNAME */
+ for (sp = 0; sp <= R->sp; sp++) {
+ if (!R->stack[sp].answer)
+ continue;
+
+ dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = DNS_T_CNAME, .section = (DNS_S_ALL & ~DNS_S_QD)) {
+ rr.section = DNS_S_AN;
+
+ if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer)))
+ return 0;
+ }
+ }
+
+ if (!dns_p_count(P, DNS_S_AN))
+ return 0;
+
+copy:
+ return dns_p_copy(dns_p_make(P->end, &error), P);
+} /* dns_res_glue() */
+
+
+/*
+ * Sort NS records by three criteria:
+ *
+ * 1) Whether glue is present.
+ * 2) Whether glue record is original or of recursive lookup.
+ * 3) Randomly shuffle records which share the above criteria.
+ *
+ * NOTE: Assumes only NS records passed, AND ASSUMES no new NS records will
+ * be added during an iteration.
+ *
+ * FIXME: Only groks A glue, not AAAA glue.
+ */
+static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
+ _Bool glued[2] = { 0 };
+ struct dns_rr x = { 0 }, y = { 0 };
+ struct dns_ns ns;
+ int cmp, error;
+
+ if (!(error = dns_ns_parse(&ns, a, P)))
+ glued[0] = !!dns_rr_grep(&x, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error);
+
+ if (!(error = dns_ns_parse(&ns, b, P)))
+ glued[1] = !!dns_rr_grep(&y, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error);
+
+ if ((cmp = glued[1] - glued[0])) {
+ return cmp;
+ } else if ((cmp = (dns_rr_offset(&y) < i->args[0]) - (dns_rr_offset(&x) < i->args[0]))) {
+ return cmp;
+ } else {
+ return dns_rr_i_shuffle(a, b, i, P);
+ }
+} /* dns_res_nameserv_cmp() */
+
+
+#define dgoto(sp, i) \
+ do { R->stack[(sp)].state = (i); goto exec; } while (0)
+
+static int dns_res_exec(struct dns_resolver *R) {
+ struct dns_res_frame *F;
+ struct dns_packet *P;
+ union {
+ char host[DNS_D_MAXNAME + 1];
+ char name[DNS_D_MAXNAME + 1];
+ struct dns_ns ns;
+ struct dns_cname cname;
+ } u;
+ size_t len;
+ struct dns_rr rr;
+ int error;
+
+exec:
+
+ F = &R->stack[R->sp];
+
+ switch (F->state) {
+ case DNS_R_INIT:
+ F->state++;
+ case DNS_R_GLUE:
+ if (R->sp == 0)
+ dgoto(R->sp, DNS_R_SWITCH);
+
+ if (!F->query)
+ goto noquery;
+
+ if (!(F->answer = dns_res_glue(R, F->query)))
+ dgoto(R->sp, DNS_R_SWITCH);
+
+ if (!(len = dns_d_expand(u.name, sizeof u.name, 12, F->query, &error)))
+ goto error;
+ else if (len >= sizeof u.name)
+ goto toolong;
+
+ dns_rr_foreach(&rr, F->answer, .name = u.name, .type = dns_rr_type(12, F->query), .section = DNS_S_AN) {
+ dgoto(R->sp, DNS_R_FINISH);
+ }
+
+ dns_rr_foreach(&rr, F->answer, .name = u.name, .type = DNS_T_CNAME, .section = DNS_S_AN) {
+ F->ans_cname = rr;
+
+ dgoto(R->sp, DNS_R_CNAME0_A);
+ }
+
+ F->state++;
+ case DNS_R_SWITCH:
+ while (F->which < (int)sizeof R->resconf->lookup && R->resconf->lookup[F->which]) {
+ switch (R->resconf->lookup[F->which++]) {
+ case 'b': case 'B':
+ dgoto(R->sp, DNS_R_BIND);
+ case 'f': case 'F':
+ dgoto(R->sp, DNS_R_FILE);
+ case 'c': case 'C':
+ if (R->cache)
+ dgoto(R->sp, DNS_R_CACHE);
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * FIXME: Examine more closely whether our logic is correct
+ * and DNS_R_SERVFAIL is the correct default response.
+ *
+ * Case 1: We got here because we never got an answer on the
+ * wire. All queries timed-out and we reached maximum
+ * attempts count. See DNS_R_FOREACH_NS. In that case
+ * DNS_R_SERVFAIL is the correct state, unless we want to
+ * return DNS_ETIMEDOUT.
+ *
+ * Case 2: We were a stub resolver and got an unsatisfactory
+ * answer (empty ANSWER section) which caused us to jump
+ * back to DNS_R_SEARCH and ultimately to DNS_R_SWITCH. We
+ * return the answer returned from the wire, which we
+ * stashed in R->nodata.
+ *
+ * Case 3: We reached maximum attempts count as in case #1,
+ * but never got an authoritative response which caused us
+ * to short-circuit. See end of DNS_R_QUERY_A case. We
+ * should probably prepare R->nodata as in case #2.
+ */
+ if (R->sp == 0 && R->nodata) { /* XXX: can we just return nodata regardless? */
+ dns_p_movptr(&F->answer, &R->nodata);
+ dgoto(R->sp, DNS_R_FINISH);
+ }
+
+ dgoto(R->sp, DNS_R_SERVFAIL);
+ case DNS_R_FILE:
+ if (R->sp > 0) {
+ if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error)))
+ goto error;
+
+ if (dns_p_count(F->answer, DNS_S_AN) > 0)
+ dgoto(R->sp, DNS_R_FINISH);
+
+ dns_p_setptr(&F->answer, NULL);
+ } else {
+ R->search = 0;
+
+ while ((len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) {
+ if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags)))
+ goto error;
+
+ if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error)))
+ goto error;
+
+ if (dns_p_count(F->answer, DNS_S_AN) > 0)
+ dgoto(R->sp, DNS_R_FINISH);
+
+ dns_p_setptr(&F->answer, NULL);
+ }
+ }
+
+ dgoto(R->sp, DNS_R_SWITCH);
+ case DNS_R_CACHE:
+ error = 0;
+
+ if (!F->query && (error = dns_q_make(&F->query, R->qname, R->qtype, R->qclass, F->qflags)))
+ goto error;
+
+ if (dns_p_setptr(&F->answer, R->cache->query(F->query, R->cache, &error))) {
+ if (dns_p_count(F->answer, DNS_S_AN) > 0)
+ dgoto(R->sp, DNS_R_FINISH);
+
+ dns_p_setptr(&F->answer, NULL);
+
+ dgoto(R->sp, DNS_R_SWITCH);
+ } else if (error)
+ goto error;
+
+ F->state++;
+ case DNS_R_SUBMIT:
+ if ((error = R->cache->submit(F->query, R->cache)))
+ goto error;
+
+ F->state++;
+ case DNS_R_CHECK:
+ if ((error = R->cache->check(R->cache)))
+ goto error;
+
+ F->state++;
+ case DNS_R_FETCH:
+ error = 0;
+
+ if (dns_p_setptr(&F->answer, R->cache->fetch(R->cache, &error))) {
+ if (dns_p_count(F->answer, DNS_S_AN) > 0)
+ dgoto(R->sp, DNS_R_FINISH);
+
+ dns_p_setptr(&F->answer, NULL);
+
+ dgoto(R->sp, DNS_R_SWITCH);
+ } else if (error)
+ goto error;
+
+ dgoto(R->sp, DNS_R_SWITCH);
+ case DNS_R_BIND:
+ if (R->sp > 0) {
+ if (!F->query)
+ goto noquery;
+
+ dgoto(R->sp, DNS_R_HINTS);
+ }
+
+ R->search = 0;
+
+ F->state++;
+ case DNS_R_SEARCH:
+ /*
+ * XXX: We probably should only apply the domain search
+ * algorithm if R->sp == 0.
+ */
+ if (!(len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search)))
+ dgoto(R->sp, DNS_R_SWITCH);
+
+ if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags)))
+ goto error;
+
+ F->state++;
+ case DNS_R_HINTS:
+ if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error)))
+ goto error;
+
+ F->state++;
+ case DNS_R_ITERATE:
+ dns_rr_i_init(&F->hints_i, F->hints);
+
+ F->hints_i.section = DNS_S_AUTHORITY;
+ F->hints_i.type = DNS_T_NS;
+ F->hints_i.sort = &dns_res_nameserv_cmp;
+ F->hints_i.args[0] = F->hints->end;
+
+ F->state++;
+ case DNS_R_FOREACH_NS:
+ dns_rr_i_save(&F->hints_i);
+
+ /* Load our next nameserver host. */
+ if (!dns_rr_grep(&F->hints_ns, 1, &F->hints_i, F->hints, &error)) {
+ if (++F->attempts < R->resconf->options.attempts)
+ dgoto(R->sp, DNS_R_ITERATE);
+
+ dgoto(R->sp, DNS_R_SWITCH);
+ }
+
+ dns_rr_i_init(&F->hints_j, F->hints);
+
+ /* Assume there are glue records */
+ dgoto(R->sp, DNS_R_FOREACH_A);
+ case DNS_R_RESOLV0_NS:
+ /* Have we reached our max depth? */
+ if (&F[1] >= endof(R->stack))
+ dgoto(R->sp, DNS_R_FOREACH_NS);
+
+ if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints)))
+ goto error;
+ if ((error = dns_res_frame_prepare(R, &F[1], u.ns.host, DNS_T_A, DNS_C_IN)))
+ goto error;
+
+ F->state++;
+
+ dgoto(++R->sp, DNS_R_INIT);
+ case DNS_R_RESOLV1_NS:
+ if (!(len = dns_d_expand(u.host, sizeof u.host, 12, F[1].query, &error)))
+ goto error;
+ else if (len >= sizeof u.host)
+ goto toolong;
+
+ dns_rr_foreach(&rr, F[1].answer, .name = u.host, .type = DNS_T_A, .section = (DNS_S_ALL & ~DNS_S_QD)) {
+ rr.section = DNS_S_AR;
+
+ if ((error = dns_rr_copy(F->hints, &rr, F[1].answer)))
+ goto error;
+
+ dns_rr_i_rewind(&F->hints_i); /* Now there's glue. */
+ }
+
+ dgoto(R->sp, DNS_R_FOREACH_NS);
+ case DNS_R_FOREACH_A: {
+ struct dns_a a;
+ struct sockaddr_in sin;
+
+ /*
+ * NOTE: Iterator initialized in DNS_R_FOREACH_NS because
+ * this state is re-entrant, but we need to reset
+ * .name to a valid pointer each time.
+ */
+ if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints)))
+ goto error;
+
+ F->hints_j.name = u.ns.host;
+ F->hints_j.type = DNS_T_A;
+ F->hints_j.section = DNS_S_ALL & ~DNS_S_QD;
+
+ if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
+ if (!dns_rr_i_count(&F->hints_j))
+ dgoto(R->sp, DNS_R_RESOLV0_NS);
+
+ dgoto(R->sp, DNS_R_FOREACH_NS);
+ }
+
+ if ((error = dns_a_parse(&a, &rr, F->hints)))
+ goto error;
+
+ memset(&sin, '\0', sizeof sin); /* NB: silence valgrind */
+ sin.sin_family = AF_INET;
+ sin.sin_addr = a.addr;
+ if (R->sp == 0)
+ sin.sin_port = dns_hints_port(R->hints, AF_INET, &sin.sin_addr);
+ else
+ sin.sin_port = htons(53);
+
+ if (DNS_DEBUG) {
+ char addr[INET_ADDRSTRLEN + 1];
+ dns_a_print(addr, sizeof addr, &a);
+ dns_header(F->query)->qid = dns_so_mkqid(&R->so);
+ DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp);
+ }
+
+ dns_trace_setcname(R->trace, u.ns.host, (struct sockaddr *)&sin);
+
+ if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin)))
+ goto error;
+
+ F->state++;
+ }
+ case DNS_R_QUERY_A:
+ if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf))
+ dgoto(R->sp, DNS_R_FOREACH_A);
+
+ if ((error = dns_so_check(&R->so)))
+ goto error;
+
+ if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error)))
+ goto error;
+
+ if (DNS_DEBUG) {
+ DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp);
+ }
+
+ if (dns_p_rcode(F->answer) == DNS_RC_FORMERR ||
+ dns_p_rcode(F->answer) == DNS_RC_NOTIMP ||
+ dns_p_rcode(F->answer) == DNS_RC_BADVERS) {
+ /* Temporarily disable EDNS0 and try again. */
+ if (F->qflags & DNS_Q_EDNS0) {
+ F->qflags &= ~DNS_Q_EDNS0;
+ if ((error = dns_q_remake(&F->query, F->qflags)))
+ goto error;
+
+ dgoto(R->sp, DNS_R_FOREACH_A);
+ }
+ }
+
+ if ((error = dns_rr_parse(&rr, 12, F->query)))
+ goto error;
+
+ if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error)))
+ goto error;
+ else if (len >= sizeof u.name)
+ goto toolong;
+
+ dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) {
+ dgoto(R->sp, DNS_R_FINISH); /* Found */
+ }
+
+ dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) {
+ F->ans_cname = rr;
+
+ dgoto(R->sp, DNS_R_CNAME0_A);
+ }
+
+ /*
+ * XXX: The condition here should probably check whether
+ * R->sp == 0, because DNS_R_SEARCH runs regardless of
+ * options.recurse. See DNS_R_BIND.
+ */
+ if (!R->resconf->options.recurse) {
+ /* Make first answer our tentative answer */
+ if (!R->nodata)
+ dns_p_movptr(&R->nodata, &F->answer);
+
+ dgoto(R->sp, DNS_R_SEARCH);
+ }
+
+ dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) {
+ dns_p_movptr(&F->hints, &F->answer);
+
+ dgoto(R->sp, DNS_R_ITERATE);
+ }
+
+ /* XXX: Should this go further up? */
+ if (dns_header(F->answer)->aa)
+ dgoto(R->sp, DNS_R_FINISH);
+
+ /* XXX: Should we copy F->answer to R->nodata? */
+
+ dgoto(R->sp, DNS_R_FOREACH_A);
+ case DNS_R_CNAME0_A:
+ if (&F[1] >= endof(R->stack))
+ dgoto(R->sp, DNS_R_FINISH);
+
+ if ((error = dns_cname_parse(&u.cname, &F->ans_cname, F->answer)))
+ goto error;
+ if ((error = dns_res_frame_prepare(R, &F[1], u.cname.host, dns_rr_type(12, F->query), DNS_C_IN)))
+ goto error;
+
+ F->state++;
+
+ dgoto(++R->sp, DNS_R_INIT);
+ case DNS_R_CNAME1_A:
+ if (!(P = dns_res_merge(F->answer, F[1].answer, &error)))
+ goto error;
+
+ dns_p_setptr(&F->answer, P);
+
+ dgoto(R->sp, DNS_R_FINISH);
+ case DNS_R_FINISH:
+ if (!F->answer)
+ goto noanswer;
+
+ if (!R->resconf->options.smart || R->sp > 0)
+ dgoto(R->sp, DNS_R_DONE);
+
+ R->smart.section = DNS_S_AN;
+ R->smart.type = R->qtype;
+
+ dns_rr_i_init(&R->smart, F->answer);
+
+ F->state++;
+ case DNS_R_SMART0_A:
+ if (&F[1] >= endof(R->stack))
+ dgoto(R->sp, DNS_R_DONE);
+
+ while (dns_rr_grep(&rr, 1, &R->smart, F->answer, &error)) {
+ union {
+ struct dns_ns ns;
+ struct dns_mx mx;
+ struct dns_srv srv;
+ } rd;
+ const char *qname;
+ enum dns_type qtype;
+ enum dns_class qclass;
+
+ switch (rr.type) {
+ case DNS_T_NS:
+ if ((error = dns_ns_parse(&rd.ns, &rr, F->answer)))
+ goto error;
+
+ qname = rd.ns.host;
+ qtype = DNS_T_A;
+ qclass = DNS_C_IN;
+
+ break;
+ case DNS_T_MX:
+ if ((error = dns_mx_parse(&rd.mx, &rr, F->answer)))
+ goto error;
+
+ qname = rd.mx.host;
+ qtype = DNS_T_A;
+ qclass = DNS_C_IN;
+
+ break;
+ case DNS_T_SRV:
+ if ((error = dns_srv_parse(&rd.srv, &rr, F->answer)))
+ goto error;
+
+ qname = rd.srv.target;
+ qtype = DNS_T_A;
+ qclass = DNS_C_IN;
+
+ break;
+ default:
+ continue;
+ } /* switch() */
+
+ if ((error = dns_res_frame_prepare(R, &F[1], qname, qtype, qclass)))
+ goto error;
+
+ F->state++;
+
+ dgoto(++R->sp, DNS_R_INIT);
+ } /* while() */
+
+ /*
+ * NOTE: SMTP specification says to fallback to A record.
+ *
+ * XXX: Should we add a mock MX answer?
+ */
+ if (R->qtype == DNS_T_MX && R->smart.state.count == 0) {
+ if ((error = dns_res_frame_prepare(R, &F[1], R->qname, DNS_T_A, DNS_C_IN)))
+ goto error;
+
+ R->smart.state.count++;
+ F->state++;
+
+ dgoto(++R->sp, DNS_R_INIT);
+ }
+
+ dgoto(R->sp, DNS_R_DONE);
+ case DNS_R_SMART1_A:
+ if (!F[1].answer)
+ goto noanswer;
+
+ /*
+ * FIXME: For CNAME chains (which are typically illegal in
+ * this context), we should rewrite the record host name
+ * to the original smart qname. All the user cares about
+ * is locating that A/AAAA record.
+ */
+ dns_rr_foreach(&rr, F[1].answer, .section = DNS_S_AN, .type = DNS_T_A) {
+ rr.section = DNS_S_AR;
+
+ if (dns_rr_exists(&rr, F[1].answer, F->answer))
+ continue;
+
+ while ((error = dns_rr_copy(F->answer, &rr, F[1].answer))) {
+ if (error != DNS_ENOBUFS)
+ goto error;
+ if ((error = dns_p_grow(&F->answer)))
+ goto error;
+ }
+ }
+
+ dgoto(R->sp, DNS_R_SMART0_A);
+ case DNS_R_DONE:
+ if (!F->answer)
+ goto noanswer;
+
+ if (R->sp > 0)
+ dgoto(--R->sp, F[-1].state);
+
+ break;
+ case DNS_R_SERVFAIL:
+ if (!dns_p_setptr(&F->answer, dns_p_make(DNS_P_QBUFSIZ, &error)))
+ goto error;
+
+ dns_header(F->answer)->qr = 1;
+ dns_header(F->answer)->rcode = DNS_RC_SERVFAIL;
+
+ if ((error = dns_p_push(F->answer, DNS_S_QD, R->qname, strlen(R->qname), R->qtype, R->qclass, 0, 0)))
+ goto error;
+
+ dgoto(R->sp, DNS_R_DONE);
+ default:
+ error = EINVAL;
+
+ goto error;
+ } /* switch () */
+
+ return 0;
+noquery:
+ error = DNS_ENOQUERY;
+
+ goto error;
+noanswer:
+ error = DNS_ENOANSWER;
+
+ goto error;
+toolong:
+ error = DNS_EILLEGAL;
+
+ /* FALL THROUGH */
+error:
+ return error;
+} /* dns_res_exec() */
+
+#undef goto
+
+
+void dns_res_clear(struct dns_resolver *R) {
+ switch (R->stack[R->sp].state) {
+ case DNS_R_CHECK:
+ R->cache->clear(R->cache);
+ break;
+ default:
+ dns_so_clear(&R->so);
+ break;
+ }
+} /* dns_res_clear() */
+
+
+static int dns_res_events2(struct dns_resolver *R, enum dns_events type) {
+ int events;
+
+ switch (R->stack[R->sp].state) {
+ case DNS_R_CHECK:
+ events = R->cache->events(R->cache);
+
+ return (type == DNS_LIBEVENT)? DNS_POLL2EV(events) : events;
+ default:
+ return dns_so_events2(&R->so, type);
+ }
+} /* dns_res_events2() */
+
+
+int dns_res_events(struct dns_resolver *R) {
+ return dns_res_events2(R, R->so.opts.events);
+} /* dns_res_events() */
+
+
+int dns_res_pollfd(struct dns_resolver *R) {
+ switch (R->stack[R->sp].state) {
+ case DNS_R_CHECK:
+ return R->cache->pollfd(R->cache);
+ default:
+ return dns_so_pollfd(&R->so);
+ }
+} /* dns_res_pollfd() */
+
+
+time_t dns_res_timeout(struct dns_resolver *R) {
+ time_t elapsed;
+
+ switch (R->stack[R->sp].state) {
+#if 0
+ case DNS_R_QUERY_AAAA:
+#endif
+ case DNS_R_QUERY_A:
+ elapsed = dns_so_elapsed(&R->so);
+
+ if (elapsed <= dns_resconf_timeout(R->resconf))
+ return R->resconf->options.timeout - elapsed;
+
+ break;
+ default:
+ break;
+ } /* switch() */
+
+ /*
+ * NOTE: We're not in a pollable state, or the user code hasn't
+ * called dns_res_check properly. The calling code is probably
+ * broken. Put them into a slow-burn pattern.
+ */
+ return 1;
+} /* dns_res_timeout() */
+
+
+time_t dns_res_elapsed(struct dns_resolver *R) {
+ return dns_elapsed(&R->elapsed);
+} /* dns_res_elapsed() */
+
+
+int dns_res_poll(struct dns_resolver *R, int timeout) {
+ return dns_poll(dns_res_pollfd(R), dns_res_events2(R, DNS_SYSPOLL), timeout);
+} /* dns_res_poll() */
+
+
+int dns_res_submit2(struct dns_resolver *R, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass) {
+ dns_res_reset(R);
+
+ /* Don't anchor; that can conflict with searchlist generation. */
+ dns_d_init(R->qname, sizeof R->qname, qname, (R->qlen = qlen), 0);
+
+ R->qtype = qtype;
+ R->qclass = qclass;
+
+ dns_begin(&R->elapsed);
+
+ dns_trace_res_submit(R->trace, R->qname, R->qtype, R->qclass, 0);
+
+ return 0;
+} /* dns_res_submit2() */
+
+
+int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass) {
+ return dns_res_submit2(R, qname, strlen(qname), qtype, qclass);
+} /* dns_res_submit() */
+
+
+int dns_res_check(struct dns_resolver *R) {
+ int error;
+
+ if (R->stack[0].state != DNS_R_DONE) {
+ if ((error = dns_res_exec(R)))
+ return error;
+ }
+
+ return 0;
+} /* dns_res_check() */
+
+
+struct dns_packet *dns_res_fetch(struct dns_resolver *R, int *_error) {
+ struct dns_packet *P = NULL;
+ int error;
+
+ if (R->stack[0].state != DNS_R_DONE) {
+ error = DNS_EUNKNOWN;
+ goto error;
+ }
+
+ if (!dns_p_movptr(&P, &R->stack[0].answer)) {
+ error = DNS_EFETCHED;
+ goto error;
+ }
+
+ dns_trace_res_fetch(R->trace, P, 0);
+
+ return P;
+error:
+ *_error = error;
+ dns_trace_res_fetch(R->trace, NULL, error);
+ return NULL;
+} /* dns_res_fetch() */
+
+
+static struct dns_packet *dns_res_fetch_and_study(struct dns_resolver *R, int *_error) {
+ struct dns_packet *P = NULL;
+ int error;
+
+ if (!(P = dns_res_fetch(R, &error)))
+ goto error;
+ if ((error = dns_p_study(P)))
+ goto error;
+
+ return P;
+error:
+ *_error = error;
+
+ dns_p_free(P);
+
+ return NULL;
+} /* dns_res_fetch_and_study() */
+
+
+struct dns_packet *dns_res_query(struct dns_resolver *res, const char *qname, enum dns_type qtype, enum dns_class qclass, int timeout, int *error_) {
+ int error;
+
+ if ((error = dns_res_submit(res, qname, qtype, qclass)))
+ goto error;
+
+ while ((error = dns_res_check(res))) {
+ if (dns_res_elapsed(res) > timeout)
+ error = DNS_ETIMEDOUT;
+
+ if (error != DNS_EAGAIN)
+ goto error;
+
+ if ((error = dns_res_poll(res, 1)))
+ goto error;
+ }
+
+ return dns_res_fetch(res, error_);
+error:
+ *error_ = error;
+
+ return 0;
+} /* dns_res_query() */
+
+
+const struct dns_stat *dns_res_stat(struct dns_resolver *res) {
+ return dns_so_stat(&res->so);
+} /* dns_res_stat() */
+
+
+void dns_res_sethints(struct dns_resolver *res, struct dns_hints *hints) {
+ dns_hints_acquire(hints); /* acquire first in case same hints object */
+ dns_hints_close(res->hints);
+ res->hints = hints;
+} /* dns_res_sethints() */
+
+
+struct dns_trace *dns_res_trace(struct dns_resolver *res) {
+ return res->trace;
+} /* dns_res_trace() */
+
+
+void dns_res_settrace(struct dns_resolver *res, struct dns_trace *trace) {
+ struct dns_trace *otrace = res->trace;
+ res->trace = dns_trace_acquire_p(trace);
+ dns_trace_close(otrace);
+ dns_so_settrace(&res->so, trace);
+} /* dns_res_settrace() */
+
+
+/*
+ * A D D R I N F O R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_addrinfo {
+ struct addrinfo hints;
+ struct dns_resolver *res;
+ struct dns_trace *trace;
+
+ char qname[DNS_D_MAXNAME + 1];
+ enum dns_type qtype;
+ unsigned short qport, port;
+
+ struct {
+ unsigned long todo;
+ int state;
+ int atype;
+ enum dns_type qtype;
+ } af;
+
+ struct dns_packet *answer;
+ struct dns_packet *glue;
+
+ struct dns_rr_i i, g;
+ struct dns_rr rr;
+
+ char cname[DNS_D_MAXNAME + 1];
+ char i_cname[DNS_D_MAXNAME + 1], g_cname[DNS_D_MAXNAME + 1];
+
+ int g_depth;
+
+ int state;
+ int found;
+
+ struct dns_stat st;
+}; /* struct dns_addrinfo */
+
+
+#define DNS_AI_AFMAX 32
+#define DNS_AI_AF2INDEX(af) (1UL << ((af) - 1))
+
+static inline unsigned long dns_ai_af2index(int af) {
+ dns_static_assert(dns_same_type(unsigned long, DNS_AI_AF2INDEX(1), 1), "internal type mismatch");
+ dns_static_assert(dns_same_type(unsigned long, ((struct dns_addrinfo *)0)->af.todo, 1), "internal type mismatch");
+
+ return (af > 0 && af <= DNS_AI_AFMAX)? DNS_AI_AF2INDEX(af) : 0;
+}
+
+static int dns_ai_setaf(struct dns_addrinfo *ai, int af, int qtype) {
+ ai->af.atype = af;
+ ai->af.qtype = qtype;
+
+ ai->af.todo &= ~dns_ai_af2index(af);
+
+ return af;
+} /* dns_ai_setaf() */
+
+#define DNS_SM_RESTORE \
+ do { pc = 0xff & (ai->af.state >> 0); i = 0xff & (ai->af.state >> 8); } while (0)
+#define DNS_SM_SAVE \
+ do { ai->af.state = ((0xff & pc) << 0) | ((0xff & i) << 8); } while (0)
+
+static int dns_ai_nextaf(struct dns_addrinfo *ai) {
+ int i, pc;
+
+ dns_static_assert(AF_UNSPEC == 0, "AF_UNSPEC constant not 0");
+ dns_static_assert(AF_INET <= DNS_AI_AFMAX, "AF_INET constant too large");
+ dns_static_assert(AF_INET6 <= DNS_AI_AFMAX, "AF_INET6 constant too large");
+
+ DNS_SM_ENTER;
+
+ if (ai->res) {
+ /*
+ * NB: On OpenBSD, at least, the types of entries resolved
+ * is the intersection of the /etc/resolv.conf families and
+ * the families permitted by the .ai_type hint. So if
+ * /etc/resolv.conf has "family inet4" and .ai_type
+ * is AF_INET6, then the address ::1 will return 0 entries
+ * even if AI_NUMERICHOST is specified in .ai_flags.
+ */
+ while (i < (int)lengthof(ai->res->resconf->family)) {
+ int af = ai->res->resconf->family[i++];
+
+ if (af == AF_UNSPEC) {
+ DNS_SM_EXIT;
+ } else if (af < 0 || af > DNS_AI_AFMAX) {
+ continue;
+ } else if (!(DNS_AI_AF2INDEX(af) & ai->af.todo)) {
+ continue;
+ } else if (af == AF_INET) {
+ DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A));
+ } else if (af == AF_INET6) {
+ DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA));
+ }
+ }
+ } else {
+ /*
+ * NB: If we get here than AI_NUMERICFLAGS should be set and
+ * order shouldn't matter.
+ */
+ if (DNS_AI_AF2INDEX(AF_INET) & ai->af.todo)
+ DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A));
+ if (DNS_AI_AF2INDEX(AF_INET6) & ai->af.todo)
+ DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA));
+ }
+
+ DNS_SM_LEAVE;
+
+ return dns_ai_setaf(ai, AF_UNSPEC, 0);
+} /* dns_ai_nextaf() */
+
+#undef DNS_SM_RESTORE
+#undef DNS_SM_SAVE
+
+static enum dns_type dns_ai_qtype(struct dns_addrinfo *ai) {
+ return (ai->qtype)? ai->qtype : ai->af.qtype;
+} /* dns_ai_qtype() */
+
+/* JW: This is not defined on mingw. */
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
+
+static dns_error_t dns_ai_parseport(unsigned short *port, const char *serv, const struct addrinfo *hints) {
+ const char *cp = serv;
+ unsigned long n = 0;
+
+ while (*cp >= '0' && *cp <= '9' && n < 65536) {
+ n *= 10;
+ n += *cp++ - '0';
+ }
+
+ if (*cp == '\0') {
+ if (cp == serv || n >= 65536)
+ return DNS_ESERVICE;
+
+ *port = n;
+
+ return 0;
+ }
+
+ if (hints->ai_flags & AI_NUMERICSERV)
+ return DNS_ESERVICE;
+
+ /* TODO: try getaddrinfo(NULL, serv, { .ai_flags = AI_NUMERICSERV }) */
+
+ return DNS_ESERVICE;
+} /* dns_ai_parseport() */
+
+
+struct dns_addrinfo *dns_ai_open(const char *host, const char *serv, enum dns_type qtype, const struct addrinfo *hints, struct dns_resolver *res, int *_error) {
+ static const struct dns_addrinfo ai_initializer;
+ struct dns_addrinfo *ai;
+ int error;
+
+ if (res) {
+ dns_res_acquire(res);
+ } else if (!(hints->ai_flags & AI_NUMERICHOST)) {
+ /*
+ * NOTE: it's assumed that *_error is set from a previous
+ * API function call, such as dns_res_stub(). Should change
+ * this semantic, but it's applied elsewhere, too.
+ */
+ if (!*_error)
+ *_error = EINVAL;
+ return NULL;
+ }
+
+ if (!(ai = malloc(sizeof *ai)))
+ goto syerr;
+
+ *ai = ai_initializer;
+ ai->hints = *hints;
+
+ ai->res = res;
+ res = NULL;
+
+ if (sizeof ai->qname <= dns_strlcpy(ai->qname, host, sizeof ai->qname))
+ { error = ENAMETOOLONG; goto error; }
+
+ ai->qtype = qtype;
+ ai->qport = 0;
+
+ if (serv && (error = dns_ai_parseport(&ai->qport, serv, hints)))
+ goto error;
+ ai->port = ai->qport;
+
+ /*
+ * FIXME: If an explicit A or AAAA record type conflicts with
+ * .ai_family or with resconf.family (i.e. AAAA specified but
+ * AF_INET6 not in interection of .ai_family and resconf.family),
+ * then what?
+ */
+ switch (ai->qtype) {
+ case DNS_T_A:
+ ai->af.todo = DNS_AI_AF2INDEX(AF_INET);
+ break;
+ case DNS_T_AAAA:
+ ai->af.todo = DNS_AI_AF2INDEX(AF_INET6);
+ break;
+ default: /* 0, MX, SRV, etc */
+ switch (ai->hints.ai_family) {
+ case AF_UNSPEC:
+ ai->af.todo = DNS_AI_AF2INDEX(AF_INET) | DNS_AI_AF2INDEX(AF_INET6);
+ break;
+ case AF_INET:
+ ai->af.todo = DNS_AI_AF2INDEX(AF_INET);
+ break;
+ case AF_INET6:
+ ai->af.todo = DNS_AI_AF2INDEX(AF_INET6);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ai;
+syerr:
+ error = dns_syerr();
+error:
+ *_error = error;
+
+ dns_ai_close(ai);
+ dns_res_close(res);
+
+ return NULL;
+} /* dns_ai_open() */
+
+
+void dns_ai_close(struct dns_addrinfo *ai) {
+ if (!ai)
+ return;
+
+ dns_res_close(ai->res);
+ dns_trace_close(ai->trace);
+
+ if (ai->answer != ai->glue)
+ dns_p_free(ai->glue);
+
+ dns_p_free(ai->answer);
+ free(ai);
+} /* dns_ai_close() */
+
+
+static int dns_ai_setent(struct addrinfo **ent, union dns_any *any, enum dns_type type, struct dns_addrinfo *ai) {
+ struct sockaddr *saddr;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ const char *cname;
+ size_t clen;
+
+ switch (type) {
+ case DNS_T_A:
+ saddr = memset(&sin, '\0', sizeof sin);
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(ai->port);
+
+ memcpy(&sin.sin_addr, any, sizeof sin.sin_addr);
+
+ break;
+ case DNS_T_AAAA:
+ saddr = memset(&sin6, '\0', sizeof sin6);
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(ai->port);
+
+ memcpy(&sin6.sin6_addr, any, sizeof sin6.sin6_addr);
+
+ break;
+ default:
+ return EINVAL;
+ } /* switch() */
+
+ if (ai->hints.ai_flags & AI_CANONNAME) {
+ cname = (*ai->cname)? ai->cname : ai->qname;
+ clen = strlen(cname);
+ } else {
+ cname = NULL;
+ clen = 0;
+ }
+
+ if (!(*ent = malloc(sizeof **ent + dns_sa_len(saddr) + ((ai->hints.ai_flags & AI_CANONNAME)? clen + 1 : 0))))
+ return dns_syerr();
+
+ memset(*ent, '\0', sizeof **ent);
+
+ (*ent)->ai_family = saddr->sa_family;
+ (*ent)->ai_socktype = ai->hints.ai_socktype;
+ (*ent)->ai_protocol = ai->hints.ai_protocol;
+
+ (*ent)->ai_addr = memcpy((unsigned char *)*ent + sizeof **ent, saddr, dns_sa_len(saddr));
+ (*ent)->ai_addrlen = dns_sa_len(saddr);
+
+ if (ai->hints.ai_flags & AI_CANONNAME)
+ (*ent)->ai_canonname = memcpy((unsigned char *)*ent + sizeof **ent + dns_sa_len(saddr), cname, clen + 1);
+
+ ai->found++;
+
+ return 0;
+} /* dns_ai_setent() */
+
+
+enum dns_ai_state {
+ DNS_AI_S_INIT,
+ DNS_AI_S_NEXTAF,
+ DNS_AI_S_NUMERIC,
+ DNS_AI_S_SUBMIT,
+ DNS_AI_S_CHECK,
+ DNS_AI_S_FETCH,
+ DNS_AI_S_FOREACH_I,
+ DNS_AI_S_INIT_G,
+ DNS_AI_S_ITERATE_G,
+ DNS_AI_S_FOREACH_G,
+ DNS_AI_S_SUBMIT_G,
+ DNS_AI_S_CHECK_G,
+ DNS_AI_S_FETCH_G,
+ DNS_AI_S_DONE,
+}; /* enum dns_ai_state */
+
+#define dns_ai_goto(which) do { ai->state = (which); goto exec; } while (0)
+
+int dns_ai_nextent(struct addrinfo **ent, struct dns_addrinfo *ai) {
+ struct dns_packet *ans, *glue;
+ struct dns_rr rr;
+ char qname[DNS_D_MAXNAME + 1];
+ union dns_any any;
+ size_t qlen, clen;
+ int error;
+
+ *ent = 0;
+
+exec:
+
+ switch (ai->state) {
+ case DNS_AI_S_INIT:
+ ai->state++;
+ case DNS_AI_S_NEXTAF:
+ if (!dns_ai_nextaf(ai))
+ dns_ai_goto(DNS_AI_S_DONE);
+
+ ai->state++;
+ case DNS_AI_S_NUMERIC:
+ if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) {
+ if (ai->af.atype == AF_INET) {
+ ai->state = DNS_AI_S_NEXTAF;
+ return dns_ai_setent(ent, &any, DNS_T_A, ai);
+ } else {
+ dns_ai_goto(DNS_AI_S_NEXTAF);
+ }
+ }
+
+ if (1 == dns_inet_pton(AF_INET6, ai->qname, &any.aaaa)) {
+ if (ai->af.atype == AF_INET6) {
+ ai->state = DNS_AI_S_NEXTAF;
+ return dns_ai_setent(ent, &any, DNS_T_AAAA, ai);
+ } else {
+ dns_ai_goto(DNS_AI_S_NEXTAF);
+ }
+ }
+
+ if (ai->hints.ai_flags & AI_NUMERICHOST)
+ dns_ai_goto(DNS_AI_S_NEXTAF);
+
+ ai->state++;
+ case DNS_AI_S_SUBMIT:
+ assert(ai->res);
+
+ if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN)))
+ return error;
+
+ ai->state++;
+ case DNS_AI_S_CHECK:
+ if ((error = dns_res_check(ai->res)))
+ return error;
+
+ ai->state++;
+ case DNS_AI_S_FETCH:
+ if (!(ans = dns_res_fetch_and_study(ai->res, &error)))
+ return error;
+ if (ai->glue != ai->answer)
+ dns_p_free(ai->glue);
+ ai->glue = dns_p_movptr(&ai->answer, &ans);
+
+ /* Search generator may have changed the qname. */
+ if (!(qlen = dns_d_expand(qname, sizeof qname, 12, ai->answer, &error)))
+ return error;
+ else if (qlen >= sizeof qname)
+ return DNS_EILLEGAL;
+ if (!dns_d_cname(ai->cname, sizeof ai->cname, qname, qlen, ai->answer, &error))
+ return error;
+
+ dns_strlcpy(ai->i_cname, ai->cname, sizeof ai->i_cname);
+ dns_rr_i_init(&ai->i, ai->answer);
+ ai->i.section = DNS_S_AN;
+ ai->i.name = ai->i_cname;
+ ai->i.type = dns_ai_qtype(ai);
+ ai->i.sort = &dns_rr_i_order;
+
+ ai->state++;
+ case DNS_AI_S_FOREACH_I:
+ if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error))
+ dns_ai_goto(DNS_AI_S_NEXTAF);
+
+ if ((error = dns_any_parse(&any, &rr, ai->answer)))
+ return error;
+
+ ai->port = ai->qport;
+
+ switch (rr.type) {
+ case DNS_T_A:
+ case DNS_T_AAAA:
+ return dns_ai_setent(ent, &any, rr.type, ai);
+ default:
+ if (!(clen = dns_any_cname(ai->cname, sizeof ai->cname, &any, rr.type)))
+ dns_ai_goto(DNS_AI_S_FOREACH_I);
+
+ /*
+ * Find the "real" canonical name. Some authorities
+ * publish aliases where an RFC defines a canonical
+ * name. We trust that the resolver followed any
+ * CNAME chains on it's own, regardless of whether
+ * the "smart" option is enabled.
+ */
+ if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, clen, ai->answer, &error))
+ return error;
+
+ if (rr.type == DNS_T_SRV)
+ ai->port = any.srv.port;
+
+ break;
+ } /* switch() */
+
+ ai->state++;
+ case DNS_AI_S_INIT_G:
+ ai->g_depth = 0;
+
+ ai->state++;
+ case DNS_AI_S_ITERATE_G:
+ dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname);
+ dns_rr_i_init(&ai->g, ai->glue);
+ ai->g.section = DNS_S_ALL & ~DNS_S_QD;
+ ai->g.name = ai->g_cname;
+ ai->g.type = ai->af.qtype;
+
+ ai->state++;
+ case DNS_AI_S_FOREACH_G:
+ if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) {
+ if (dns_rr_i_count(&ai->g) > 0)
+ dns_ai_goto(DNS_AI_S_FOREACH_I);
+ else
+ dns_ai_goto(DNS_AI_S_SUBMIT_G);
+ }
+
+ if ((error = dns_any_parse(&any, &rr, ai->glue)))
+ return error;
+
+ return dns_ai_setent(ent, &any, rr.type, ai);
+ case DNS_AI_S_SUBMIT_G:
+ /* skip if already queried */
+ if (dns_rr_grep(&rr, 1, dns_rr_i_new(ai->glue, .section = DNS_S_QD, .name = ai->g.name, .type = ai->g.type), ai->glue, &error))
+ dns_ai_goto(DNS_AI_S_FOREACH_I);
+ /* skip if we recursed (CNAME chains should have been handled in the resolver) */
+ if (++ai->g_depth > 1)
+ dns_ai_goto(DNS_AI_S_FOREACH_I);
+
+ if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN)))
+ return error;
+
+ ai->state++;
+ case DNS_AI_S_CHECK_G:
+ if ((error = dns_res_check(ai->res)))
+ return error;
+
+ ai->state++;
+ case DNS_AI_S_FETCH_G:
+ if (!(ans = dns_res_fetch_and_study(ai->res, &error)))
+ return error;
+
+ glue = dns_p_merge(ai->glue, DNS_S_ALL, ans, DNS_S_ALL, &error);
+ dns_p_setptr(&ans, NULL);
+ if (!glue)
+ return error;
+
+ if (ai->glue != ai->answer)
+ dns_p_free(ai->glue);
+ ai->glue = glue;
+
+ if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->g.name, strlen(ai->g.name), ai->glue, &error))
+ dns_ai_goto(DNS_AI_S_FOREACH_I);
+
+ dns_ai_goto(DNS_AI_S_ITERATE_G);
+ case DNS_AI_S_DONE:
+ if (ai->found) {
+ return ENOENT; /* TODO: Just return 0 */
+ } else if (ai->answer) {
+ switch (dns_p_rcode(ai->answer)) {
+ case DNS_RC_NOERROR:
+ /* FALL THROUGH */
+ case DNS_RC_NXDOMAIN:
+ return DNS_ENONAME;
+ default:
+ return DNS_EFAIL;
+ }
+ } else {
+ return DNS_EFAIL;
+ }
+ default:
+ return EINVAL;
+ } /* switch() */
+} /* dns_ai_nextent() */
+
+
+time_t dns_ai_elapsed(struct dns_addrinfo *ai) {
+ return (ai->res)? dns_res_elapsed(ai->res) : 0;
+} /* dns_ai_elapsed() */
+
+
+void dns_ai_clear(struct dns_addrinfo *ai) {
+ if (ai->res)
+ dns_res_clear(ai->res);
+} /* dns_ai_clear() */
+
+
+int dns_ai_events(struct dns_addrinfo *ai) {
+ return (ai->res)? dns_res_events(ai->res) : 0;
+} /* dns_ai_events() */
+
+
+int dns_ai_pollfd(struct dns_addrinfo *ai) {
+ return (ai->res)? dns_res_pollfd(ai->res) : -1;
+} /* dns_ai_pollfd() */
+
+
+time_t dns_ai_timeout(struct dns_addrinfo *ai) {
+ return (ai->res)? dns_res_timeout(ai->res) : 0;
+} /* dns_ai_timeout() */
+
+
+int dns_ai_poll(struct dns_addrinfo *ai, int timeout) {
+ return (ai->res)? dns_res_poll(ai->res, timeout) : 0;
+} /* dns_ai_poll() */
+
+
+size_t dns_ai_print(void *_dst, size_t lim, struct addrinfo *ent, struct dns_addrinfo *ai) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ char addr[DNS_PP_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1];
+
+ dns_b_puts(&dst, "[ ");
+ dns_b_puts(&dst, ai->qname);
+ dns_b_puts(&dst, " IN ");
+ if (ai->qtype) {
+ dns_b_puts(&dst, dns_strtype(ai->qtype));
+ } else if (ent->ai_family == AF_INET) {
+ dns_b_puts(&dst, dns_strtype(DNS_T_A));
+ } else if (ent->ai_family == AF_INET6) {
+ dns_b_puts(&dst, dns_strtype(DNS_T_AAAA));
+ } else {
+ dns_b_puts(&dst, "0");
+ }
+ dns_b_puts(&dst, " ]\n");
+
+ dns_b_puts(&dst, ".ai_family = ");
+ switch (ent->ai_family) {
+ case AF_INET:
+ dns_b_puts(&dst, "AF_INET");
+ break;
+ case AF_INET6:
+ dns_b_puts(&dst, "AF_INET6");
+ break;
+ default:
+ dns_b_fmtju(&dst, ent->ai_family, 0);
+ break;
+ }
+ dns_b_putc(&dst, '\n');
+
+ dns_b_puts(&dst, ".ai_socktype = ");
+ switch (ent->ai_socktype) {
+ case SOCK_STREAM:
+ dns_b_puts(&dst, "SOCK_STREAM");
+ break;
+ case SOCK_DGRAM:
+ dns_b_puts(&dst, "SOCK_DGRAM");
+ break;
+ default:
+ dns_b_fmtju(&dst, ent->ai_socktype, 0);
+ break;
+ }
+ dns_b_putc(&dst, '\n');
+
+ dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr, NULL), addr, sizeof addr);
+ dns_b_puts(&dst, ".ai_addr = [");
+ dns_b_puts(&dst, addr);
+ dns_b_puts(&dst, "]:");
+ dns_b_fmtju(&dst, ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)), 0);
+ dns_b_putc(&dst, '\n');
+
+ dns_b_puts(&dst, ".ai_canonname = ");
+ dns_b_puts(&dst, (ent->ai_canonname)? ent->ai_canonname : "[NULL]");
+ dns_b_putc(&dst, '\n');
+
+ return dns_b_strllen(&dst);
+} /* dns_ai_print() */
+
+
+const struct dns_stat *dns_ai_stat(struct dns_addrinfo *ai) {
+ return (ai->res)? dns_res_stat(ai->res) : &ai->st;
+} /* dns_ai_stat() */
+
+
+struct dns_trace *dns_ai_trace(struct dns_addrinfo *ai) {
+ return ai->trace;
+} /* dns_ai_trace() */
+
+
+void dns_ai_settrace(struct dns_addrinfo *ai, struct dns_trace *trace) {
+ struct dns_trace *otrace = ai->trace;
+ ai->trace = dns_trace_acquire_p(trace);
+ dns_trace_close(otrace);
+ if (ai->res)
+ dns_res_settrace(ai->res, trace);
+} /* dns_ai_settrace() */
+
+
+/*
+ * M I S C E L L A N E O U S R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static const struct {
+ char name[16];
+ enum dns_section type;
+} dns_sections[] = {
+ { "QUESTION", DNS_S_QUESTION },
+ { "QD", DNS_S_QUESTION },
+ { "ANSWER", DNS_S_ANSWER },
+ { "AN", DNS_S_ANSWER },
+ { "AUTHORITY", DNS_S_AUTHORITY },
+ { "NS", DNS_S_AUTHORITY },
+ { "ADDITIONAL", DNS_S_ADDITIONAL },
+ { "AR", DNS_S_ADDITIONAL },
+};
+
+const char *(dns_strsection)(enum dns_section section, void *_dst, size_t lim) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ unsigned i;
+
+ for (i = 0; i < lengthof(dns_sections); i++) {
+ if (dns_sections[i].type & section) {
+ dns_b_puts(&dst, dns_sections[i].name);
+ section &= ~dns_sections[i].type;
+ if (section)
+ dns_b_putc(&dst, '|');
+ }
+ }
+
+ if (section || dst.p == dst.base)
+ dns_b_fmtju(&dst, (0xffff & section), 0);
+
+ return dns_b_tostring(&dst);
+} /* dns_strsection() */
+
+
+enum dns_section dns_isection(const char *src) {
+ enum dns_section section = 0;
+ char sbuf[128];
+ char *name, *next;
+ unsigned i;
+
+ dns_strlcpy(sbuf, src, sizeof sbuf);
+ next = sbuf;
+
+ while ((name = dns_strsep(&next, "|+, \t"))) {
+ for (i = 0; i < lengthof(dns_sections); i++) {
+ if (!strcasecmp(dns_sections[i].name, name)) {
+ section |= dns_sections[i].type;
+ break;
+ }
+ }
+ }
+
+ return section;
+} /* dns_isection() */
+
+
+static const struct {
+ char name[8];
+ enum dns_class type;
+} dns_classes[] = {
+ { "IN", DNS_C_IN },
+};
+
+const char *(dns_strclass)(enum dns_class type, void *_dst, size_t lim) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ unsigned i;
+
+ for (i = 0; i < lengthof(dns_classes); i++) {
+ if (dns_classes[i].type == type) {
+ dns_b_puts(&dst, dns_classes[i].name);
+ break;
+ }
+ }
+
+ if (dst.p == dst.base)
+ dns_b_fmtju(&dst, (0xffff & type), 0);
+
+ return dns_b_tostring(&dst);
+} /* dns_strclass() */
+
+
+enum dns_class dns_iclass(const char *name) {
+ unsigned i, class;
+
+ for (i = 0; i < lengthof(dns_classes); i++) {
+ if (!strcasecmp(dns_classes[i].name, name))
+ return dns_classes[i].type;
+ }
+
+ class = 0;
+ while (dns_isdigit(*name)) {
+ class *= 10;
+ class += *name++ - '0';
+ }
+
+ return DNS_PP_MIN(class, 0xffff);
+} /* dns_iclass() */
+
+
+const char *(dns_strtype)(enum dns_type type, void *_dst, size_t lim) {
+ struct dns_buf dst = DNS_B_INTO(_dst, lim);
+ unsigned i;
+
+ for (i = 0; i < lengthof(dns_rrtypes); i++) {
+ if (dns_rrtypes[i].type == type) {
+ dns_b_puts(&dst, dns_rrtypes[i].name);
+ break;
+ }
+ }
+
+ if (dst.p == dst.base)
+ dns_b_fmtju(&dst, (0xffff & type), 0);
+
+ return dns_b_tostring(&dst);
+} /* dns_strtype() */
+
+
+enum dns_type dns_itype(const char *name) {
+ unsigned i, type;
+
+ for (i = 0; i < lengthof(dns_rrtypes); i++) {
+ if (!strcasecmp(dns_rrtypes[i].name, name))
+ return dns_rrtypes[i].type;
+ }
+
+ type = 0;
+ while (dns_isdigit(*name)) {
+ type *= 10;
+ type += *name++ - '0';
+ }
+
+ return DNS_PP_MIN(type, 0xffff);
+} /* dns_itype() */
+
+
+static char dns_opcodes[16][16] = {
+ [DNS_OP_QUERY] = "QUERY",
+ [DNS_OP_IQUERY] = "IQUERY",
+ [DNS_OP_STATUS] = "STATUS",
+ [DNS_OP_NOTIFY] = "NOTIFY",
+ [DNS_OP_UPDATE] = "UPDATE",
+};
+
+static const char *dns__strcode(int code, volatile char *dst, size_t lim) {
+ char _tmp[48] = "";
+ struct dns_buf tmp;
+ size_t p;
+
+ assert(lim > 0);
+ dns_b_fmtju(dns_b_into(&tmp, _tmp, DNS_PP_MIN(sizeof _tmp, lim - 1)), code, 0);
+
+ /* copy downwards so first byte is copied last (see below) */
+ p = (size_t)(tmp.p - tmp.base);
+ dst[p] = '\0';
+ while (p--)
+ dst[p] = _tmp[p];
+
+ return (const char *)dst;
+}
+
+const char *dns_stropcode(enum dns_opcode opcode) {
+ opcode = (unsigned)opcode % lengthof(dns_opcodes);
+
+ if ('\0' == dns_opcodes[opcode][0])
+ return dns__strcode(opcode, dns_opcodes[opcode], sizeof dns_opcodes[opcode]);
+
+ return dns_opcodes[opcode];
+} /* dns_stropcode() */
+
+
+enum dns_opcode dns_iopcode(const char *name) {
+ unsigned opcode;
+
+ for (opcode = 0; opcode < lengthof(dns_opcodes); opcode++) {
+ if (!strcasecmp(name, dns_opcodes[opcode]))
+ return opcode;
+ }
+
+ opcode = 0;
+ while (dns_isdigit(*name)) {
+ opcode *= 10;
+ opcode += *name++ - '0';
+ }
+
+ return DNS_PP_MIN(opcode, 0x0f);
+} /* dns_iopcode() */
+
+
+static char dns_rcodes[32][16] = {
+ [DNS_RC_NOERROR] = "NOERROR",
+ [DNS_RC_FORMERR] = "FORMERR",
+ [DNS_RC_SERVFAIL] = "SERVFAIL",
+ [DNS_RC_NXDOMAIN] = "NXDOMAIN",
+ [DNS_RC_NOTIMP] = "NOTIMP",
+ [DNS_RC_REFUSED] = "REFUSED",
+ [DNS_RC_YXDOMAIN] = "YXDOMAIN",
+ [DNS_RC_YXRRSET] = "YXRRSET",
+ [DNS_RC_NXRRSET] = "NXRRSET",
+ [DNS_RC_NOTAUTH] = "NOTAUTH",
+ [DNS_RC_NOTZONE] = "NOTZONE",
+ /* EDNS(0) extended RCODEs ... */
+ [DNS_RC_BADVERS] = "BADVERS",
+};
+
+const char *dns_strrcode(enum dns_rcode rcode) {
+ rcode = (unsigned)rcode % lengthof(dns_rcodes);
+
+ if ('\0' == dns_rcodes[rcode][0])
+ return dns__strcode(rcode, dns_rcodes[rcode], sizeof dns_rcodes[rcode]);
+
+ return dns_rcodes[rcode];
+} /* dns_strrcode() */
+
+
+enum dns_rcode dns_ircode(const char *name) {
+ unsigned rcode;
+
+ for (rcode = 0; rcode < lengthof(dns_rcodes); rcode++) {
+ if (!strcasecmp(name, dns_rcodes[rcode]))
+ return rcode;
+ }
+
+ rcode = 0;
+ while (dns_isdigit(*name)) {
+ rcode *= 10;
+ rcode += *name++ - '0';
+ }
+
+ return DNS_PP_MIN(rcode, 0xfff);
+} /* dns_ircode() */
+
+
+\f
+/*
+ * C O M M A N D - L I N E / R E G R E S S I O N R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#if DNS_MAIN
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <ctype.h>
+
+#if _WIN32
+#include <getopt.h>
+#endif
+
+#if !_WIN32
+#include <err.h>
+#endif
+
+
+struct {
+ struct {
+ const char *path[8];
+ unsigned count;
+ } resconf, nssconf, hosts, cache;
+
+ const char *qname;
+ enum dns_type qtype;
+
+ int (*sort)();
+
+ const char *trace;
+
+ int verbose;
+
+ struct {
+ struct dns_resolv_conf *resconf;
+ struct dns_hosts *hosts;
+ struct dns_trace *trace;
+ } memo;
+
+ struct sockaddr_storage socks_host;
+ const char *socks_user;
+ const char *socks_password;
+} MAIN = {
+ .sort = &dns_rr_i_packet,
+};
+
+
+static void hexdump(const unsigned char *src, size_t len, FILE *fp) {
+ struct dns_hxd_lines_i lines = { 0 };
+ char line[128];
+
+ while (dns_hxd_lines(line, sizeof line, src, len, &lines)) {
+ fputs(line, fp);
+ }
+} /* hexdump() */
+
+
+DNS_NORETURN static void panic(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+
+#if _WIN32
+ vfprintf(stderr, fmt, ap);
+
+ exit(EXIT_FAILURE);
+#else
+ verrx(EXIT_FAILURE, fmt, ap);
+#endif
+} /* panic() */
+
+#define panic_(fn, ln, fmt, ...) \
+ panic(fmt "%0s", (fn), (ln), __VA_ARGS__)
+#define panic(...) \
+ panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "")
+
+
+static void *grow(unsigned char *p, size_t size) {
+ void *tmp;
+
+ if (!(tmp = realloc(p, size)))
+ panic("realloc(%"PRIuZ"): %s", size, dns_strerror(errno));
+
+ return tmp;
+} /* grow() */
+
+
+static size_t add(size_t a, size_t b) {
+ if (~a < b)
+ panic("%"PRIuZ" + %"PRIuZ": integer overflow", a, b);
+
+ return a + b;
+} /* add() */
+
+
+static size_t append(unsigned char **dst, size_t osize, const void *src, size_t len) {
+ size_t size = add(osize, len);
+
+ *dst = grow(*dst, size);
+ memcpy(*dst + osize, src, len);
+
+ return size;
+} /* append() */
+
+
+static size_t slurp(unsigned char **dst, size_t osize, FILE *fp, const char *path) {
+ size_t size = osize;
+ unsigned char buf[1024];
+ size_t count;
+
+ while ((count = fread(buf, 1, sizeof buf, fp)))
+ size = append(dst, size, buf, count);
+
+ if (ferror(fp))
+ panic("%s: %s", path, dns_strerror(errno));
+
+ return size;
+} /* slurp() */
+
+
+static struct dns_resolv_conf *resconf(void) {
+ struct dns_resolv_conf **resconf = &MAIN.memo.resconf;
+ const char *path;
+ unsigned i;
+ int error;
+
+ if (*resconf)
+ return *resconf;
+
+ if (!(*resconf = dns_resconf_open(&error)))
+ panic("dns_resconf_open: %s", dns_strerror(error));
+
+ if (!MAIN.resconf.count)
+ MAIN.resconf.path[MAIN.resconf.count++] = "/etc/resolv.conf";
+
+ for (i = 0; i < MAIN.resconf.count; i++) {
+ path = MAIN.resconf.path[i];
+
+ if (0 == strcmp(path, "-"))
+ error = dns_resconf_loadfile(*resconf, stdin);
+ else
+ error = dns_resconf_loadpath(*resconf, path);
+
+ if (error)
+ panic("%s: %s", path, dns_strerror(error));
+ }
+
+ for (i = 0; i < MAIN.nssconf.count; i++) {
+ path = MAIN.nssconf.path[i];
+
+ if (0 == strcmp(path, "-"))
+ error = dns_nssconf_loadfile(*resconf, stdin);
+ else
+ error = dns_nssconf_loadpath(*resconf, path);
+
+ if (error)
+ panic("%s: %s", path, dns_strerror(error));
+ }
+
+ if (!MAIN.nssconf.count) {
+ path = "/etc/nsswitch.conf";
+
+ if (!(error = dns_nssconf_loadpath(*resconf, path)))
+ MAIN.nssconf.path[MAIN.nssconf.count++] = path;
+ else if (error != ENOENT)
+ panic("%s: %s", path, dns_strerror(error));
+ }
+
+ return *resconf;
+} /* resconf() */
+
+
+static struct dns_hosts *hosts(void) {
+ struct dns_hosts **hosts = &MAIN.memo.hosts;
+ const char *path;
+ unsigned i;
+ int error;
+
+ if (*hosts)
+ return *hosts;
+
+ if (!MAIN.hosts.count) {
+ MAIN.hosts.path[MAIN.hosts.count++] = "/etc/hosts";
+
+ /* Explicitly test dns_hosts_local() */
+ if (!(*hosts = dns_hosts_local(&error)))
+ panic("%s: %s", "/etc/hosts", dns_strerror(error));
+
+ return *hosts;
+ }
+
+ if (!(*hosts = dns_hosts_open(&error)))
+ panic("dns_hosts_open: %s", dns_strerror(error));
+
+ for (i = 0; i < MAIN.hosts.count; i++) {
+ path = MAIN.hosts.path[i];
+
+ if (0 == strcmp(path, "-"))
+ error = dns_hosts_loadfile(*hosts, stdin);
+ else
+ error = dns_hosts_loadpath(*hosts, path);
+
+ if (error)
+ panic("%s: %s", path, dns_strerror(error));
+ }
+
+ return *hosts;
+} /* hosts() */
+
+
+#if DNS_CACHE
+#include "cache.h"
+
+static struct dns_cache *cache(void) {
+ static struct cache *cache;
+ const char *path;
+ unsigned i;
+ int error;
+
+ if (cache)
+ return cache_resi(cache);
+ if (!MAIN.cache.count)
+ return NULL;
+
+ if (!(cache = cache_open(&error)))
+ panic("%s: %s", MAIN.cache.path[0], dns_strerror(error));
+
+ for (i = 0; i < MAIN.cache.count; i++) {
+ path = MAIN.cache.path[i];
+
+ if (!strcmp(path, "-")) {
+ if ((error = cache_loadfile(cache, stdin, NULL, 0)))
+ panic("%s: %s", path, dns_strerror(error));
+ } else if ((error = cache_loadpath(cache, path, NULL, 0)))
+ panic("%s: %s", path, dns_strerror(error));
+ }
+
+ return cache_resi(cache);
+} /* cache() */
+#else
+static struct dns_cache *cache(void) { return NULL; }
+#endif
+
+
+static struct dns_trace *trace(const char *mode) {
+ static char omode[64] = "";
+ struct dns_trace **trace = &MAIN.memo.trace;
+ FILE *fp;
+ int error;
+
+ if (*trace && 0 == strcmp(omode, mode))
+ return *trace;
+ if (!MAIN.trace)
+ return NULL;
+
+ if (!(fp = fopen(MAIN.trace, mode)))
+ panic("%s: %s", MAIN.trace, strerror(errno));
+ dns_trace_close(*trace);
+ if (!(*trace = dns_trace_open(fp, &error)))
+ panic("%s: %s", MAIN.trace, dns_strerror(error));
+ dns_strlcpy(omode, mode, sizeof omode);
+
+ return *trace;
+}
+
+
+static void print_packet(struct dns_packet *P, FILE *fp) {
+ dns_p_dump3(P, dns_rr_i_new(P, .sort = MAIN.sort), fp);
+
+ if (MAIN.verbose > 2)
+ hexdump(P->data, P->end, fp);
+} /* print_packet() */
+
+
+static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) {
+ struct dns_packet *P = dns_p_new(512);
+ struct dns_packet *Q = dns_p_new(512);
+ enum dns_section section;
+ struct dns_rr rr;
+ int error;
+ union dns_any any;
+ char pretty[sizeof any * 2];
+ size_t len;
+
+ P->end = fread(P->data, 1, P->size, stdin);
+
+ fputs(";; [HEADER]\n", stdout);
+ fprintf(stdout, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr);
+ fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode);
+ fprintf(stdout, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa);
+ fprintf(stdout, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc);
+ fprintf(stdout, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd);
+ fprintf(stdout, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra);
+ fprintf(stdout, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P));
+
+ section = 0;
+
+ dns_rr_foreach(&rr, P, .sort = MAIN.sort) {
+ if (section != rr.section)
+ fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section));
+
+ if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error)))
+ fprintf(stdout, "%s\n", pretty);
+
+ dns_rr_copy(Q, &rr, P);
+
+ section = rr.section;
+ }
+
+ fputs("; ; ; ; ; ; ; ;\n\n", stdout);
+
+ section = 0;
+
+#if 0
+ dns_rr_foreach(&rr, Q, .name = "ns8.yahoo.com.") {
+#else
+ struct dns_rr rrset[32];
+ struct dns_rr_i *rri = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort);
+ unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error);
+
+ for (unsigned i = 0; i < rrcount; i++) {
+ rr = rrset[i];
+#endif
+ if (section != rr.section)
+ fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(Q, rr.section));
+
+ if ((len = dns_rr_print(pretty, sizeof pretty, &rr, Q, &error)))
+ fprintf(stdout, "%s\n", pretty);
+
+ section = rr.section;
+ }
+
+ if (MAIN.verbose > 1) {
+ fprintf(stderr, "orig:%"PRIuZ"\n", P->end);
+ hexdump(P->data, P->end, stdout);
+
+ fprintf(stderr, "copy:%"PRIuZ"\n", Q->end);
+ hexdump(Q->data, Q->end, stdout);
+ }
+
+ return 0;
+} /* parse_packet() */
+
+
+static int parse_domain(int argc, char *argv[]) {
+ char *dn;
+
+ dn = (argc > 1)? argv[1] : "f.l.google.com";
+
+ printf("[%s]\n", dn);
+
+ dn = dns_d_new(dn);
+
+ do {
+ puts(dn);
+ } while (dns_d_cleave(dn, strlen(dn) + 1, dn, strlen(dn)));
+
+ return 0;
+} /* parse_domain() */
+
+
+static int trim_domain(int argc, char **argv) {
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ char name[DNS_D_MAXNAME + 1];
+
+ dns_d_trim(name, sizeof name, *argv, strlen(*argv), DNS_D_ANCHOR);
+
+ puts(name);
+ }
+
+ return 0;
+} /* trim_domain() */
+
+
+static int expand_domain(int argc, char *argv[]) {
+ unsigned short rp = 0;
+ unsigned char *src = NULL;
+ unsigned char *dst;
+ struct dns_packet *pkt;
+ size_t lim = 0, len;
+ int error;
+
+ if (argc > 1)
+ rp = atoi(argv[1]);
+
+ len = slurp(&src, 0, stdin, "-");
+
+ if (!(pkt = dns_p_make(len, &error)))
+ panic("malloc(%"PRIuZ"): %s", len, dns_strerror(error));
+
+ memcpy(pkt->data, src, len);
+ pkt->end = len;
+
+ lim = 1;
+ dst = grow(NULL, lim);
+
+ while (lim <= (len = dns_d_expand(dst, lim, rp, pkt, &error))) {
+ lim = add(len, 1);
+ dst = grow(dst, lim);
+ }
+
+ if (!len)
+ panic("expand: %s", dns_strerror(error));
+
+ fwrite(dst, 1, len, stdout);
+ fflush(stdout);
+
+ free(src);
+ free(dst);
+ free(pkt);
+
+ return 0;
+} /* expand_domain() */
+
+
+static int show_resconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) {
+ unsigned i;
+
+ resconf(); /* load it */
+
+ fputs("; SOURCES\n", stdout);
+
+ for (i = 0; i < MAIN.resconf.count; i++)
+ fprintf(stdout, "; %s\n", MAIN.resconf.path[i]);
+
+ for (i = 0; i < MAIN.nssconf.count; i++)
+ fprintf(stdout, "; %s\n", MAIN.nssconf.path[i]);
+
+ fputs(";\n", stdout);
+
+ dns_resconf_dump(resconf(), stdout);
+
+ return 0;
+} /* show_resconf() */
+
+
+static int show_nssconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) {
+ unsigned i;
+
+ resconf();
+
+ fputs("# SOURCES\n", stdout);
+
+ for (i = 0; i < MAIN.resconf.count; i++)
+ fprintf(stdout, "# %s\n", MAIN.resconf.path[i]);
+
+ for (i = 0; i < MAIN.nssconf.count; i++)
+ fprintf(stdout, "# %s\n", MAIN.nssconf.path[i]);
+
+ fputs("#\n", stdout);
+
+ dns_nssconf_dump(resconf(), stdout);
+
+ return 0;
+} /* show_nssconf() */
+
+
+static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) {
+ unsigned i;
+
+ hosts();
+
+ fputs("# SOURCES\n", stdout);
+
+ for (i = 0; i < MAIN.hosts.count; i++)
+ fprintf(stdout, "# %s\n", MAIN.hosts.path[i]);
+
+ fputs("#\n", stdout);
+
+ dns_hosts_dump(hosts(), stdout);
+
+ return 0;
+} /* show_hosts() */
+
+
+static int query_hosts(int argc, char *argv[]) {
+ struct dns_packet *Q = dns_p_new(512);
+ struct dns_packet *A;
+ char qname[DNS_D_MAXNAME + 1];
+ size_t qlen;
+ int error;
+
+ if (!MAIN.qname)
+ MAIN.qname = (argc > 1)? argv[1] : "localhost";
+ if (!MAIN.qtype)
+ MAIN.qtype = DNS_T_A;
+
+ hosts();
+
+ if (MAIN.qtype == DNS_T_PTR && !strstr(MAIN.qname, "arpa")) {
+ union { struct in_addr a; struct in6_addr a6; } addr;
+ int af = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET;
+
+ if ((error = dns_pton(af, MAIN.qname, &addr)))
+ panic("%s: %s", MAIN.qname, dns_strerror(error));
+
+ qlen = dns_ptr_qname(qname, sizeof qname, af, &addr);
+ } else
+ qlen = dns_strlcpy(qname, MAIN.qname, sizeof qname);
+
+ if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, MAIN.qtype, DNS_C_IN, 0, 0)))
+ panic("%s: %s", qname, dns_strerror(error));
+
+ if (!(A = dns_hosts_query(hosts(), Q, &error)))
+ panic("%s: %s", qname, dns_strerror(error));
+
+ print_packet(A, stdout);
+
+ free(A);
+
+ return 0;
+} /* query_hosts() */
+
+
+static int search_list(int argc, char *argv[]) {
+ const char *qname = (argc > 1)? argv[1] : "f.l.google.com";
+ unsigned long i = 0;
+ char name[DNS_D_MAXNAME + 1];
+
+ printf("[%s]\n", qname);
+
+ while (dns_resconf_search(name, sizeof name, qname, strlen(qname), resconf(), &i))
+ puts(name);
+
+ return 0;
+} /* search_list() */
+
+
+static int permute_set(int argc, char *argv[]) {
+ unsigned lo, hi, i;
+ struct dns_k_permutor p;
+
+ hi = (--argc > 0)? atoi(argv[argc]) : 8;
+ lo = (--argc > 0)? atoi(argv[argc]) : 0;
+
+ fprintf(stderr, "[%u .. %u]\n", lo, hi);
+
+ dns_k_permutor_init(&p, lo, hi);
+
+ for (i = lo; i <= hi; i++)
+ fprintf(stdout, "%u\n", dns_k_permutor_step(&p));
+// printf("%u -> %u -> %u\n", i, dns_k_permutor_E(&p, i), dns_k_permutor_D(&p, dns_k_permutor_E(&p, i)));
+
+ return 0;
+} /* permute_set() */
+
+
+static int shuffle_16(int argc, char *argv[]) {
+ unsigned n, r;
+
+ if (--argc > 0) {
+ n = 0xffff & atoi(argv[argc]);
+ r = (--argc > 0)? (unsigned)atoi(argv[argc]) : dns_random();
+
+ fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r));
+ } else {
+ r = dns_random();
+
+ for (n = 0; n < 65536; n++)
+ fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r));
+ }
+
+ return 0;
+} /* shuffle_16() */
+
+
+static int dump_random(int argc, char *argv[]) {
+ unsigned char b[32];
+ unsigned i, j, n, r;
+
+ n = (argc > 1)? atoi(argv[1]) : 32;
+
+ while (n) {
+ i = 0;
+
+ do {
+ r = dns_random();
+
+ for (j = 0; j < sizeof r && i < n && i < sizeof b; i++, j++) {
+ b[i] = 0xff & r;
+ r >>= 8;
+ }
+ } while (i < n && i < sizeof b);
+
+ hexdump(b, i, stdout);
+
+ n -= i;
+ }
+
+ return 0;
+} /* dump_random() */
+
+
+static int send_query(int argc, char *argv[]) {
+ struct dns_packet *A, *Q = dns_p_new(512);
+ char host[INET6_ADDRSTRLEN + 1];
+ struct sockaddr_storage ss;
+ struct dns_socket *so;
+ int error, type;
+
+ if (argc > 1) {
+ ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET;
+
+ if ((error = dns_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss, NULL))))
+ panic("%s: %s", argv[1], dns_strerror(error));
+
+ *dns_sa_port(ss.ss_family, &ss) = htons(53);
+ } else
+ memcpy(&ss, &resconf()->nameserver[0], dns_sa_len(&resconf()->nameserver[0]));
+
+ if (!dns_inet_ntop(ss.ss_family, dns_sa_addr(ss.ss_family, &ss, NULL), host, sizeof host))
+ panic("bad host address, or none provided");
+
+ if (!MAIN.qname)
+ MAIN.qname = "ipv6.google.com";
+ if (!MAIN.qtype)
+ MAIN.qtype = DNS_T_AAAA;
+
+ if ((error = dns_p_push(Q, DNS_S_QD, MAIN.qname, strlen(MAIN.qname), MAIN.qtype, DNS_C_IN, 0, 0)))
+ panic("dns_p_push: %s", dns_strerror(error));
+
+ dns_header(Q)->rd = 1;
+
+ if (strstr(argv[0], "udp"))
+ type = SOCK_DGRAM;
+ else if (strstr(argv[0], "tcp"))
+ type = SOCK_STREAM;
+ else
+ type = dns_res_tcp2type(resconf()->options.tcp);
+
+ fprintf(stderr, "querying %s for %s IN %s\n", host, MAIN.qname, dns_strtype(MAIN.qtype));
+
+ if (!(so = dns_so_open((struct sockaddr *)&resconf()->iface, type, dns_opts(), &error)))
+ panic("dns_so_open: %s", dns_strerror(error));
+
+ while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) {
+ if (error != DNS_EAGAIN)
+ panic("dns_so_query: %s (%d)", dns_strerror(error), error);
+ if (dns_so_elapsed(so) > 10)
+ panic("query timed-out");
+
+ dns_so_poll(so, 1);
+ }
+
+ print_packet(A, stdout);
+
+ dns_so_close(so);
+
+ return 0;
+} /* send_query() */
+
+
+static int print_arpa(int argc, char *argv[]) {
+ const char *ip = (argc > 1)? argv[1] : "::1";
+ int af = (strchr(ip, ':'))? AF_INET6 : AF_INET;
+ union { struct in_addr a4; struct in6_addr a6; } addr;
+ char host[DNS_D_MAXNAME + 1];
+
+ if (1 != dns_inet_pton(af, ip, &addr) || 0 == dns_ptr_qname(host, sizeof host, af, &addr))
+ panic("%s: invalid address", ip);
+
+ fprintf(stdout, "%s\n", host);
+
+ return 0;
+} /* print_arpa() */
+
+
+static int show_hints(int argc, char *argv[]) {
+ struct dns_hints *(*load)(struct dns_resolv_conf *, int *);
+ const char *which, *how, *who;
+ struct dns_hints *hints;
+ int error;
+
+ which = (argc > 1)? argv[1] : "local";
+ how = (argc > 2)? argv[2] : "plain";
+ who = (argc > 3)? argv[3] : "google.com";
+
+ load = (0 == strcmp(which, "local"))
+ ? &dns_hints_local
+ : &dns_hints_root;
+
+ if (!(hints = load(resconf(), &error)))
+ panic("%s: %s", argv[0], dns_strerror(error));
+
+ if (0 == strcmp(how, "plain")) {
+ dns_hints_dump(hints, stdout);
+ } else {
+ struct dns_packet *query, *answer;
+
+ query = dns_p_new(512);
+
+ if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0)))
+ panic("%s: %s", who, dns_strerror(error));
+
+ if (!(answer = dns_hints_query(hints, query, &error)))
+ panic("%s: %s", who, dns_strerror(error));
+
+ print_packet(answer, stdout);
+
+ free(answer);
+ }
+
+ dns_hints_close(hints);
+
+ return 0;
+} /* show_hints() */
+
+
+static int resolve_query(int argc DNS_NOTUSED, char *argv[]) {
+ _Bool recurse = !!strstr(argv[0], "recurse");
+ struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local;
+ struct dns_resolver *R;
+ struct dns_packet *ans;
+ const struct dns_stat *st;
+ int error;
+
+ if (!MAIN.qname)
+ MAIN.qname = "www.google.com";
+ if (!MAIN.qtype)
+ MAIN.qtype = DNS_T_A;
+
+ resconf()->options.recurse = recurse;
+
+ if (!(R = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(),
+ dns_opts(.socks_host=&MAIN.socks_host,
+ .socks_user=MAIN.socks_user,
+ .socks_password=MAIN.socks_password), &error)))
+ panic("%s: %s", MAIN.qname, dns_strerror(error));
+
+ dns_res_settrace(R, trace("w+b"));
+
+ if ((error = dns_res_submit(R, MAIN.qname, MAIN.qtype, DNS_C_IN)))
+ panic("%s: %s", MAIN.qname, dns_strerror(error));
+
+ while ((error = dns_res_check(R))) {
+ if (error != DNS_EAGAIN)
+ panic("dns_res_check: %s (%d)", dns_strerror(error), error);
+ if (dns_res_elapsed(R) > 30)
+ panic("query timed-out");
+
+ dns_res_poll(R, 1);
+ }
+
+ ans = dns_res_fetch(R, &error);
+ print_packet(ans, stdout);
+ free(ans);
+
+ st = dns_res_stat(R);
+ putchar('\n');
+ printf(";; queries: %"PRIuZ"\n", st->queries);
+ printf(";; udp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.sent.count, st->udp.sent.bytes);
+ printf(";; udp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes);
+ printf(";; tcp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.sent.count, st->tcp.sent.bytes);
+ printf(";; tcp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes);
+
+ dns_res_close(R);
+
+ return 0;
+} /* resolve_query() */
+
+
+static int resolve_addrinfo(int argc DNS_NOTUSED, char *argv[]) {
+ _Bool recurse = !!strstr(argv[0], "recurse");
+ struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local;
+ struct dns_resolver *res = NULL;
+ struct dns_addrinfo *ai = NULL;
+ struct addrinfo ai_hints = { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_CANONNAME };
+ struct addrinfo *ent;
+ char pretty[512];
+ int error;
+
+ if (!MAIN.qname)
+ MAIN.qname = "www.google.com";
+ /* NB: MAIN.qtype of 0 means obey hints.ai_family */
+
+ resconf()->options.recurse = recurse;
+
+ if (!(res = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error)))
+ panic("%s: %s", MAIN.qname, dns_strerror(error));
+
+ if (!(ai = dns_ai_open(MAIN.qname, "80", MAIN.qtype, &ai_hints, res, &error)))
+ panic("%s: %s", MAIN.qname, dns_strerror(error));
+
+ dns_ai_settrace(ai, trace("w+b"));
+
+ do {
+ switch (error = dns_ai_nextent(&ent, ai)) {
+ case 0:
+ dns_ai_print(pretty, sizeof pretty, ent, ai);
+
+ fputs(pretty, stdout);
+
+ free(ent);
+
+ break;
+ case ENOENT:
+ break;
+ case DNS_EAGAIN:
+ if (dns_ai_elapsed(ai) > 30)
+ panic("query timed-out");
+
+ dns_ai_poll(ai, 1);
+
+ break;
+ default:
+ panic("dns_ai_nextent: %s (%d)", dns_strerror(error), error);
+ }
+ } while (error != ENOENT);
+
+ dns_res_close(res);
+ dns_ai_close(ai);
+
+ return 0;
+} /* resolve_addrinfo() */
+
+
+static int dump_trace(int argc DNS_NOTUSED, char *argv[]) {
+ int error;
+
+ if (!MAIN.trace)
+ panic("no trace file specified");
+
+ if ((error = dns_trace_dump(trace("r"), stdout)))
+ panic("dump_trace: %s", dns_strerror(error));
+
+ return 0;
+} /* dump_trace() */
+
+
+static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } port;
+ int fd;
+
+ memset(&port, 0, sizeof port);
+ port.sin.sin_family = AF_INET;
+ port.sin.sin_port = htons(5354);
+ port.sin.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0)))
+ panic("socket: %s", strerror(errno));
+
+ if (0 != bind(fd, &port.sa, sizeof port.sa))
+ panic("127.0.0.1:5353: %s", dns_strerror(errno));
+
+ for (;;) {
+ struct dns_packet *pkt = dns_p_new(512);
+ struct sockaddr_storage ss;
+ socklen_t slen = sizeof ss;
+ ssize_t count;
+#if defined(MSG_WAITALL) /* MinGW issue */
+ int rflags = MSG_WAITALL;
+#else
+ int rflags = 0;
+#endif
+
+ count = recvfrom(fd, (char *)pkt->data, pkt->size, rflags, (struct sockaddr *)&ss, &slen);
+
+ if (!count || count < 0)
+ panic("recvfrom: %s", strerror(errno));
+
+ pkt->end = count;
+
+ dns_p_dump(pkt, stdout);
+
+ (void)sendto(fd, (char *)pkt->data, pkt->end, 0, (struct sockaddr *)&ss, slen);
+ }
+
+ return 0;
+} /* echo_port() */
+
+
+static int isection(int argc, char *argv[]) {
+ const char *name = (argc > 1)? argv[1] : "";
+ int type;
+
+ type = dns_isection(name);
+ name = dns_strsection(type);
+
+ printf("%s (%d)\n", name, type);
+
+ return 0;
+} /* isection() */
+
+
+static int iclass(int argc, char *argv[]) {
+ const char *name = (argc > 1)? argv[1] : "";
+ int type;
+
+ type = dns_iclass(name);
+ name = dns_strclass(type);
+
+ printf("%s (%d)\n", name, type);
+
+ return 0;
+} /* iclass() */
+
+
+static int itype(int argc, char *argv[]) {
+ const char *name = (argc > 1)? argv[1] : "";
+ int type;
+
+ type = dns_itype(name);
+ name = dns_strtype(type);
+
+ printf("%s (%d)\n", name, type);
+
+ return 0;
+} /* itype() */
+
+
+static int iopcode(int argc, char *argv[]) {
+ const char *name = (argc > 1)? argv[1] : "";
+ int type;
+
+ type = dns_iopcode(name);
+ name = dns_stropcode(type);
+
+ printf("%s (%d)\n", name, type);
+
+ return 0;
+} /* iopcode() */
+
+
+static int ircode(int argc, char *argv[]) {
+ const char *name = (argc > 1)? argv[1] : "";
+ int type;
+
+ type = dns_ircode(name);
+ name = dns_strrcode(type);
+
+ printf("%s (%d)\n", name, type);
+
+ return 0;
+} /* ircode() */
+
+
+#define SIZE1(x) { DNS_PP_STRINGIFY(x), sizeof (x) }
+#define SIZE2(x, ...) SIZE1(x), SIZE1(__VA_ARGS__)
+#define SIZE3(x, ...) SIZE1(x), SIZE2(__VA_ARGS__)
+#define SIZE4(x, ...) SIZE1(x), SIZE3(__VA_ARGS__)
+#define SIZE(...) DNS_PP_CALL(DNS_PP_XPASTE(SIZE, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
+
+static int sizes(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) {
+ static const struct { const char *name; size_t size; } type[] = {
+ SIZE(struct dns_header, struct dns_packet, struct dns_rr, struct dns_rr_i),
+ SIZE(struct dns_a, struct dns_aaaa, struct dns_mx, struct dns_ns),
+ SIZE(struct dns_cname, struct dns_soa, struct dns_ptr, struct dns_srv),
+ SIZE(struct dns_sshfp, struct dns_txt, union dns_any),
+ SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i),
+ SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo),
+ SIZE(struct dns_cache), SIZE(size_t), SIZE(void *), SIZE(long)
+ };
+ unsigned i, max;
+
+ for (i = 0, max = 0; i < lengthof(type); i++)
+ max = DNS_PP_MAX(max, strlen(type[i].name));
+
+ for (i = 0; i < lengthof(type); i++)
+ printf("%*s : %"PRIuZ"\n", max, type[i].name, type[i].size);
+
+ return 0;
+} /* sizes() */
+
+
+static const struct { const char *cmd; int (*run)(); const char *help; } cmds[] = {
+ { "parse-packet", &parse_packet, "parse binary packet from stdin" },
+ { "parse-domain", &parse_domain, "anchor and iteratively cleave domain" },
+ { "trim-domain", &trim_domain, "trim and anchor domain name" },
+ { "expand-domain", &expand_domain, "expand domain at offset NN in packet from stdin" },
+ { "show-resconf", &show_resconf, "show resolv.conf data" },
+ { "show-hosts", &show_hosts, "show hosts data" },
+ { "show-nssconf", &show_nssconf, "show nsswitch.conf data" },
+ { "query-hosts", &query_hosts, "query A, AAAA or PTR in hosts data" },
+ { "search-list", &search_list, "generate query search list from domain" },
+ { "permute-set", &permute_set, "generate random permutation -> (0 .. N or N .. M)" },
+ { "shuffle-16", &shuffle_16, "simple 16-bit permutation" },
+ { "dump-random", &dump_random, "generate random bytes" },
+ { "send-query", &send_query, "send query to host" },
+ { "send-query-udp", &send_query, "send udp query to host" },
+ { "send-query-tcp", &send_query, "send tcp query to host" },
+ { "print-arpa", &print_arpa, "print arpa. zone name of address" },
+ { "show-hints", &show_hints, "print hints: show-hints [local|root] [plain|packet]" },
+ { "resolve-stub", &resolve_query, "resolve as stub resolver" },
+ { "resolve-recurse", &resolve_query, "resolve as recursive resolver" },
+ { "addrinfo-stub", &resolve_addrinfo, "resolve through getaddrinfo clone" },
+ { "addrinfo-recurse", &resolve_addrinfo, "resolve through getaddrinfo clone" },
+/* { "resolve-nameinfo", &resolve_query, "resolve as recursive resolver" }, */
+ { "dump-trace", &dump_trace, "dump the contents of a trace file" },
+ { "echo", &echo_port, "server echo mode, for nmap fuzzing" },
+ { "isection", &isection, "parse section string" },
+ { "iclass", &iclass, "parse class string" },
+ { "itype", &itype, "parse type string" },
+ { "iopcode", &iopcode, "parse opcode string" },
+ { "ircode", &ircode, "parse rcode string" },
+ { "sizes", &sizes, "print data structure sizes" },
+};
+
+
+static void print_usage(const char *progname, FILE *fp) {
+ static const char *usage =
+ " [OPTIONS] COMMAND [ARGS]\n"
+ " -c PATH Path to resolv.conf\n"
+ " -n PATH Path to nsswitch.conf\n"
+ " -l PATH Path to local hosts\n"
+ " -z PATH Path to zone cache\n"
+ " -q QNAME Query name\n"
+ " -t QTYPE Query type\n"
+ " -s HOW Sort records\n"
+ " -S ADDR Address of SOCKS server to use\n"
+ " -P PORT Port of SOCKS server to use\n"
+ " -A USER:PASSWORD Credentials for the SOCKS server\n"
+ " -f PATH Path to trace file\n"
+ " -v Be more verbose (-vv show packets; -vvv hexdump packets)\n"
+ " -V Print version info\n"
+ " -h Print this usage message\n"
+ "\n";
+ unsigned i, n, m;
+
+ fputs(progname, fp);
+ fputs(usage, fp);
+
+ for (i = 0, m = 0; i < lengthof(cmds); i++) {
+ if (strlen(cmds[i].cmd) > m)
+ m = strlen(cmds[i].cmd);
+ }
+
+ for (i = 0; i < lengthof(cmds); i++) {
+ fprintf(fp, " %s ", cmds[i].cmd);
+
+ for (n = strlen(cmds[i].cmd); n < m; n++)
+ putc(' ', fp);
+
+ fputs(cmds[i].help, fp);
+ putc('\n', fp);
+ }
+
+ fputs("\nReport bugs to William Ahern <william@25thandClement.com>\n", fp);
+} /* print_usage() */
+
+
+static void print_version(const char *progname, FILE *fp) {
+ fprintf(fp, "%s (dns.c) %.8X\n", progname, dns_v_rel());
+ fprintf(fp, "vendor %s\n", dns_vendor());
+ fprintf(fp, "release %.8X\n", dns_v_rel());
+ fprintf(fp, "abi %.8X\n", dns_v_abi());
+ fprintf(fp, "api %.8X\n", dns_v_api());
+} /* print_version() */
+
+
+static void main_exit(void) {
+ dns_trace_close(MAIN.memo.trace);
+ MAIN.memo.trace = NULL;
+ dns_hosts_close(MAIN.memo.hosts);
+ MAIN.memo.hosts = NULL;
+ dns_resconf_close(MAIN.memo.resconf);
+ MAIN.memo.resconf = NULL;
+} /* main_exit() */
+
+int main(int argc, char **argv) {
+ extern int optind;
+ extern char *optarg;
+ const char *progname = argv[0];
+ unsigned i;
+ int ch;
+
+ atexit(&main_exit);
+
+ while (-1 != (ch = getopt(argc, argv, "q:t:c:n:l:z:s:S:P:A:f:vVh"))) {
+ switch (ch) {
+ case 'c':
+ assert(MAIN.resconf.count < lengthof(MAIN.resconf.path));
+
+ MAIN.resconf.path[MAIN.resconf.count++] = optarg;
+
+ break;
+ case 'n':
+ assert(MAIN.nssconf.count < lengthof(MAIN.nssconf.path));
+
+ MAIN.nssconf.path[MAIN.nssconf.count++] = optarg;
+
+ break;
+ case 'l':
+ assert(MAIN.hosts.count < lengthof(MAIN.hosts.path));
+
+ MAIN.hosts.path[MAIN.hosts.count++] = optarg;
+
+ break;
+ case 'z':
+ assert(MAIN.cache.count < lengthof(MAIN.cache.path));
+
+ MAIN.cache.path[MAIN.cache.count++] = optarg;
+
+ break;
+ case 'q':
+ MAIN.qname = optarg;
+
+ break;
+ case 't':
+ for (i = 0; i < lengthof(dns_rrtypes); i++) {
+ if (0 == strcasecmp(dns_rrtypes[i].name, optarg))
+ { MAIN.qtype = dns_rrtypes[i].type; break; }
+ }
+
+ if (MAIN.qtype)
+ break;
+
+ for (i = 0; dns_isdigit(optarg[i]); i++) {
+ MAIN.qtype *= 10;
+ MAIN.qtype += optarg[i] - '0';
+ }
+
+ if (!MAIN.qtype)
+ panic("%s: invalid query type", optarg);
+
+ break;
+ case 's':
+ if (0 == strcasecmp(optarg, "packet"))
+ MAIN.sort = &dns_rr_i_packet;
+ else if (0 == strcasecmp(optarg, "shuffle"))
+ MAIN.sort = &dns_rr_i_shuffle;
+ else if (0 == strcasecmp(optarg, "order"))
+ MAIN.sort = &dns_rr_i_order;
+ else
+ panic("%s: invalid sort method", optarg);
+
+ break;
+ case 'S': {
+ dns_error_t error;
+ struct dns_resolv_conf *conf = resconf();
+ conf->options.tcp = DNS_RESCONF_TCP_SOCKS;
+
+ MAIN.socks_host.ss_family = (strchr(optarg, ':')) ? AF_INET6 : AF_INET;
+ if ((error = dns_pton(MAIN.socks_host.ss_family, optarg,
+ dns_sa_addr(MAIN.socks_host.ss_family,
+ &MAIN.socks_host, NULL))))
+ panic("%s: %s", optarg, dns_strerror(error));
+
+ *dns_sa_port(MAIN.socks_host.ss_family, &MAIN.socks_host) = htons(1080);
+
+ break;
+ }
+ case 'P':
+ if (! MAIN.socks_host.ss_family)
+ panic("-P without prior -S");
+ *dns_sa_port(MAIN.socks_host.ss_family, &MAIN.socks_host) = htons(atoi(optarg));
+
+ break;
+ case 'A': {
+ char *password;
+ if (! MAIN.socks_host.ss_family)
+ panic("-A without prior -S");
+ if (! (password = strchr(optarg, ':')))
+ panic("Usage: -A USER:PASSWORD");
+ *password = 0;
+ password += 1;
+ MAIN.socks_user = optarg;
+ MAIN.socks_password = password;
+ break;
+ }
+ case 'f':
+ MAIN.trace = optarg;
+
+ break;
+ case 'v':
+ dns_debug = ++MAIN.verbose;
+
+ break;
+ case 'V':
+ print_version(progname, stdout);
+
+ return 0;
+ case 'h':
+ print_usage(progname, stdout);
+
+ return 0;
+ default:
+ print_usage(progname, stderr);
+
+ return EXIT_FAILURE;
+ } /* switch() */
+ } /* while() */
+
+ argc -= optind;
+ argv += optind;
+
+ for (i = 0; i < lengthof(cmds) && argv[0]; i++) {
+ if (0 == strcmp(cmds[i].cmd, argv[0]))
+ return cmds[i].run(argc, argv);
+ }
+
+ print_usage(progname, stderr);
+
+ return EXIT_FAILURE;
+} /* main() */
+
+
+#endif /* DNS_MAIN */
+
+
+/*
+ * pop file-scoped compiler annotations
+ */
+#if __clang__
+#pragma clang diagnostic pop
+#elif DNS_GNUC_PREREQ(4,6,0)
+#pragma GCC diagnostic pop
+#endif
--- /dev/null
+/* ==========================================================================
+ * dns.h - Recursive, Reentrant DNS Resolver.
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2009, 2010, 2012-2015 William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#ifndef DNS_H
+#define DNS_H
+
+#include <stddef.h> /* size_t offsetof() */
+#include <stdint.h> /* uint64_t */
+#include <stdio.h> /* FILE */
+#include <string.h> /* strlen(3) */
+#include <time.h> /* struct timespec time_t */
+
+#if _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/param.h> /* BYTE_ORDER BIG_ENDIAN _BIG_ENDIAN */
+#include <sys/types.h> /* socklen_t */
+#include <sys/socket.h> /* struct socket */
+#include <poll.h> /* POLLIN POLLOUT */
+#include <netinet/in.h> /* struct in_addr struct in6_addr */
+#include <netdb.h> /* struct addrinfo */
+#endif
+
+
+/*
+ * V I S I B I L I T Y
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef DNS_PUBLIC
+#define DNS_PUBLIC
+#endif
+
+
+/*
+ * V E R S I O N
+ *
+ * Vendor: Entity for which versions numbers are relevant. (If forking
+ * change DNS_VENDOR to avoid confusion.)
+ *
+ * Three versions:
+ *
+ * REL Official "release"--bug fixes, new features, etc.
+ * ABI Changes to existing object sizes or parameter types
+ * API Changes that might effect application source.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_VENDOR "william@25thandClement.com"
+
+#define DNS_V_REL 0x20160809
+#define DNS_V_ABI 0x20160608
+#define DNS_V_API 0x20160809
+
+
+DNS_PUBLIC const char *dns_vendor(void);
+
+DNS_PUBLIC int dns_v_rel(void);
+DNS_PUBLIC int dns_v_abi(void);
+DNS_PUBLIC int dns_v_api(void);
+
+
+/*
+ * E R R O R S
+ *
+ * Errors and exceptions are always returned through an int. This should
+ * hopefully make integration easier in the majority of circumstances, and
+ * also cut down on useless compiler warnings.
+ *
+ * System and library errors are returned together. POSIX guarantees that
+ * all system errors are positive integers. Library errors are always
+ * negative integers in the range DNS_EBASE to DNS_ELAST, with the high bits
+ * set to the three magic ASCII characters "dns".
+ *
+ * dns_strerror() returns static English string descriptions of all known
+ * errors, and punts the remainder to strerror(3).
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_EBASE -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64)
+
+#define dns_error_t int /* for documentation only */
+
+enum dns_errno {
+ DNS_ENOBUFS = DNS_EBASE,
+ DNS_EILLEGAL,
+ DNS_EORDER,
+ DNS_ESECTION,
+ DNS_EUNKNOWN,
+ DNS_EADDRESS,
+ DNS_ENOQUERY,
+ DNS_ENOANSWER,
+ DNS_EFETCHED,
+ DNS_ESERVICE, /* EAI_SERVICE */
+ DNS_ENONAME, /* EAI_NONAME */
+ DNS_EFAIL, /* EAI_FAIL */
+ DNS_ECONNFIN,
+ DNS_EVERIFY,
+ DNS_ELAST,
+}; /* dns_errno */
+
+DNS_PUBLIC const char *dns_strerror(dns_error_t);
+
+DNS_PUBLIC int *dns_debug_p(void);
+
+#define dns_debug (*dns_debug_p()) /* was extern int dns_debug before 20160523 API */
+
+
+/*
+ * C O M P I L E R A N N O T A T I O N S
+ *
+ * GCC with -Wextra, and clang by default, complain about overrides in
+ * initializer lists. Overriding previous member initializers is well
+ * defined behavior in C. dns.c relies on this behavior to define default,
+ * overrideable member values when instantiating configuration objects.
+ *
+ * dns_quietinit() guards a compound literal expression with pragmas to
+ * silence these shrill warnings. This alleviates the burden of requiring
+ * third-party projects to adjust their compiler flags.
+ *
+ * NOTE: If you take the address of the compound literal, take the address
+ * of the transformed expression, otherwise the compound literal lifetime is
+ * tied to the scope of the GCC statement expression.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if defined __clang__
+#define DNS_PRAGMA_PUSH _Pragma("clang diagnostic push")
+#define DNS_PRAGMA_QUIET _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"")
+#define DNS_PRAGMA_POP _Pragma("clang diagnostic pop")
+
+#define dns_quietinit(...) \
+ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__ DNS_PRAGMA_POP
+#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+#define DNS_PRAGMA_PUSH _Pragma("GCC diagnostic push")
+#define DNS_PRAGMA_QUIET _Pragma("GCC diagnostic ignored \"-Woverride-init\"")
+#define DNS_PRAGMA_POP _Pragma("GCC diagnostic pop")
+
+/* GCC parses the _Pragma operator less elegantly than clang. */
+#define dns_quietinit(...) \
+ __extension__ ({ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__; DNS_PRAGMA_POP })
+#else
+#define DNS_PRAGMA_PUSH
+#define DNS_PRAGMA_QUIET
+#define DNS_PRAGMA_POP
+#define dns_quietinit(...) __VA_ARGS__
+#endif
+
+#if defined __GNUC__
+#define DNS_PRAGMA_EXTENSION __extension__
+#else
+#define DNS_PRAGMA_EXTENSION
+#endif
+
+
+/*
+ * E V E N T S I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if defined(POLLIN)
+#define DNS_POLLIN POLLIN
+#else
+#define DNS_POLLIN 1
+#endif
+
+#if defined(POLLOUT)
+#define DNS_POLLOUT POLLOUT
+#else
+#define DNS_POLLOUT 2
+#endif
+
+
+/*
+ * See Application Interface below for configuring libevent bitmasks instead
+ * of poll(2) bitmasks.
+ */
+#define DNS_EVREAD 2
+#define DNS_EVWRITE 4
+
+
+#define DNS_POLL2EV(set) \
+ (((set) & DNS_POLLIN)? DNS_EVREAD : 0) | (((set) & DNS_POLLOUT)? DNS_EVWRITE : 0)
+
+#define DNS_EV2POLL(set) \
+ (((set) & DNS_EVREAD)? DNS_POLLIN : 0) | (((set) & DNS_EVWRITE)? DNS_POLLOUT : 0)
+
+
+/*
+ * E N U M E R A T I O N I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+enum dns_section {
+ DNS_S_QD = 0x01,
+#define DNS_S_QUESTION DNS_S_QD
+
+ DNS_S_AN = 0x02,
+#define DNS_S_ANSWER DNS_S_AN
+
+ DNS_S_NS = 0x04,
+#define DNS_S_AUTHORITY DNS_S_NS
+
+ DNS_S_AR = 0x08,
+#define DNS_S_ADDITIONAL DNS_S_AR
+
+ DNS_S_ALL = 0x0f
+}; /* enum dns_section */
+
+
+enum dns_class {
+ DNS_C_IN = 1,
+
+ DNS_C_ANY = 255
+}; /* enum dns_class */
+
+
+enum dns_type {
+ DNS_T_A = 1,
+ DNS_T_NS = 2,
+ DNS_T_CNAME = 5,
+ DNS_T_SOA = 6,
+ DNS_T_PTR = 12,
+ DNS_T_MX = 15,
+ DNS_T_TXT = 16,
+ DNS_T_AAAA = 28,
+ DNS_T_SRV = 33,
+ DNS_T_OPT = 41,
+ DNS_T_SSHFP = 44,
+ DNS_T_SPF = 99,
+ DNS_T_AXFR = 252,
+
+ DNS_T_ALL = 255
+}; /* enum dns_type */
+
+
+enum dns_opcode {
+ DNS_OP_QUERY = 0,
+ DNS_OP_IQUERY = 1,
+ DNS_OP_STATUS = 2,
+ DNS_OP_NOTIFY = 4,
+ DNS_OP_UPDATE = 5,
+}; /* dns_opcode */
+
+
+enum dns_rcode {
+ DNS_RC_NOERROR = 0,
+ DNS_RC_FORMERR = 1,
+ DNS_RC_SERVFAIL = 2,
+ DNS_RC_NXDOMAIN = 3,
+ DNS_RC_NOTIMP = 4,
+ DNS_RC_REFUSED = 5,
+ DNS_RC_YXDOMAIN = 6,
+ DNS_RC_YXRRSET = 7,
+ DNS_RC_NXRRSET = 8,
+ DNS_RC_NOTAUTH = 9,
+ DNS_RC_NOTZONE = 10,
+
+ /* EDNS(0) extended RCODEs */
+ DNS_RC_BADVERS = 16,
+}; /* dns_rcode */
+
+
+/*
+ * NOTE: These string functions need a small buffer in case the literal
+ * integer value needs to be printed and returned. UNLESS this buffer is
+ * SPECIFIED, the returned string has ONLY BLOCK SCOPE.
+ */
+#define DNS_STRMAXLEN 47 /* "QUESTION|ANSWER|AUTHORITY|ADDITIONAL" */
+
+DNS_PUBLIC const char *dns_strsection(enum dns_section, void *, size_t);
+#define dns_strsection3(a, b, c) \
+ dns_strsection((a), (b), (c))
+#define dns_strsection1(a) dns_strsection((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1)
+#define dns_strsection(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strsection, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
+
+DNS_PUBLIC enum dns_section dns_isection(const char *);
+
+DNS_PUBLIC const char *dns_strclass(enum dns_class, void *, size_t);
+#define dns_strclass3(a, b, c) dns_strclass((a), (b), (c))
+#define dns_strclass1(a) dns_strclass((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1)
+#define dns_strclass(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strclass, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
+
+DNS_PUBLIC enum dns_class dns_iclass(const char *);
+
+DNS_PUBLIC const char *dns_strtype(enum dns_type, void *, size_t);
+#define dns_strtype3(a, b, c) dns_strtype((a), (b), (c))
+#define dns_strtype1(a) dns_strtype((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1)
+#define dns_strtype(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strtype, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
+
+DNS_PUBLIC enum dns_type dns_itype(const char *);
+
+DNS_PUBLIC const char *dns_stropcode(enum dns_opcode);
+
+DNS_PUBLIC enum dns_opcode dns_iopcode(const char *);
+
+DNS_PUBLIC const char *dns_strrcode(enum dns_rcode);
+
+DNS_PUBLIC enum dns_rcode dns_ircode(const char *);
+
+
+/*
+ * A T O M I C I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+typedef unsigned long dns_atomic_t;
+
+typedef unsigned long dns_refcount_t; /* must be same value type as dns_atomic_t */
+
+
+/*
+ * C R Y P T O I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+typedef unsigned dns_random_f(void);
+
+DNS_PUBLIC dns_random_f **dns_random_p(void);
+
+#define dns_random (*dns_random_p()) /* was extern unsigned (*dns_random)(void) before 20160523 API */
+
+
+/*
+ * P A C K E T I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_header {
+ unsigned qid:16;
+
+#if (defined BYTE_ORDER && BYTE_ORDER == BIG_ENDIAN) || (defined __sun && defined _BIG_ENDIAN)
+ unsigned qr:1;
+ unsigned opcode:4;
+ unsigned aa:1;
+ unsigned tc:1;
+ unsigned rd:1;
+
+ unsigned ra:1;
+ unsigned unused:3;
+ unsigned rcode:4;
+#else
+ unsigned rd:1;
+ unsigned tc:1;
+ unsigned aa:1;
+ unsigned opcode:4;
+ unsigned qr:1;
+
+ unsigned rcode:4;
+ unsigned unused:3;
+ unsigned ra:1;
+#endif
+
+ unsigned qdcount:16;
+ unsigned ancount:16;
+ unsigned nscount:16;
+ unsigned arcount:16;
+}; /* struct dns_header */
+
+#define dns_header(p) (&(p)->header)
+
+
+#ifndef DNS_P_QBUFSIZ
+#define DNS_P_QBUFSIZ dns_p_calcsize(256 + 4)
+#endif
+
+#ifndef DNS_P_DICTSIZE
+#define DNS_P_DICTSIZE 16
+#endif
+
+struct dns_packet {
+ unsigned short dict[DNS_P_DICTSIZE];
+
+ struct dns_p_memo {
+ struct dns_s_memo {
+ unsigned short base, end;
+ } qd, an, ns, ar;
+
+ struct {
+ unsigned short p;
+ unsigned short maxudp;
+ unsigned ttl;
+ } opt;
+ } memo;
+
+ struct { struct dns_packet *cqe_next, *cqe_prev; } cqe;
+
+ size_t size, end;
+
+ int:16; /* tcp padding */
+
+ DNS_PRAGMA_EXTENSION union {
+ struct dns_header header;
+ unsigned char data[1];
+ };
+}; /* struct dns_packet */
+
+#define dns_p_calcsize(n) (offsetof(struct dns_packet, data) + DNS_PP_MAX(12, (n)))
+
+#define dns_p_sizeof(P) dns_p_calcsize((P)->end)
+
+/** takes size of maximum desired payload */
+#define dns_p_new(n) (dns_p_init((struct dns_packet *)&(union { unsigned char b[dns_p_calcsize((n))]; struct dns_packet p; }){ { 0 } }, dns_p_calcsize((n))))
+
+/** takes size of entire packet structure as allocated */
+DNS_PUBLIC struct dns_packet *dns_p_init(struct dns_packet *, size_t);
+
+/** takes size of maximum desired payload */
+DNS_PUBLIC struct dns_packet *dns_p_make(size_t, int *);
+
+DNS_PUBLIC int dns_p_grow(struct dns_packet **);
+
+DNS_PUBLIC struct dns_packet *dns_p_copy(struct dns_packet *, const struct dns_packet *);
+
+#define dns_p_opcode(P) (dns_header(P)->opcode)
+
+DNS_PUBLIC enum dns_rcode dns_p_rcode(struct dns_packet *);
+
+DNS_PUBLIC unsigned dns_p_count(struct dns_packet *, enum dns_section);
+
+DNS_PUBLIC int dns_p_push(struct dns_packet *, enum dns_section, const void *, size_t, enum dns_type, enum dns_class, unsigned, const void *);
+
+DNS_PUBLIC void dns_p_dictadd(struct dns_packet *, unsigned short);
+
+DNS_PUBLIC struct dns_packet *dns_p_merge(struct dns_packet *, enum dns_section, struct dns_packet *, enum dns_section, int *);
+
+DNS_PUBLIC void dns_p_dump(struct dns_packet *, FILE *);
+
+DNS_PUBLIC int dns_p_study(struct dns_packet *);
+
+
+/*
+ * D O M A I N N A M E I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_D_MAXLABEL 63 /* + 1 '\0' */
+#define DNS_D_MAXNAME 255 /* + 1 '\0' */
+
+#define DNS_D_ANCHOR 1 /* anchor domain w/ root "." */
+#define DNS_D_CLEAVE 2 /* cleave sub-domain */
+#define DNS_D_TRIM 4 /* remove superfluous dots */
+
+#define dns_d_new3(a, b, f) dns_d_init(&(char[DNS_D_MAXNAME + 1]){ 0 }, DNS_D_MAXNAME + 1, (a), (b), (f))
+#define dns_d_new2(a, f) dns_d_new3((a), strlen((a)), (f))
+#define dns_d_new1(a) dns_d_new3((a), strlen((a)), DNS_D_ANCHOR)
+#define dns_d_new(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_d_new, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
+
+DNS_PUBLIC char *dns_d_init(void *, size_t, const void *, size_t, int);
+
+DNS_PUBLIC size_t dns_d_anchor(void *, size_t, const void *, size_t);
+
+DNS_PUBLIC size_t dns_d_cleave(void *, size_t, const void *, size_t);
+
+DNS_PUBLIC size_t dns_d_comp(void *, size_t, const void *, size_t, struct dns_packet *, int *);
+
+DNS_PUBLIC size_t dns_d_expand(void *, size_t, unsigned short, struct dns_packet *, int *);
+
+DNS_PUBLIC unsigned short dns_d_skip(unsigned short, struct dns_packet *);
+
+DNS_PUBLIC int dns_d_push(struct dns_packet *, const void *, size_t);
+
+DNS_PUBLIC size_t dns_d_cname(void *, size_t, const void *, size_t, struct dns_packet *, int *error);
+
+
+/*
+ * R E S O U R C E R E C O R D I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_rr {
+ enum dns_section section;
+
+ struct {
+ unsigned short p;
+ unsigned short len;
+ } dn;
+
+ enum dns_type type;
+ enum dns_class class;
+ unsigned ttl;
+
+ struct {
+ unsigned short p;
+ unsigned short len;
+ } rd;
+}; /* struct dns_rr */
+
+
+DNS_PUBLIC int dns_rr_copy(struct dns_packet *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_rr_parse(struct dns_rr *, unsigned short, struct dns_packet *);
+
+DNS_PUBLIC unsigned short dns_rr_skip(unsigned short, struct dns_packet *);
+
+DNS_PUBLIC int dns_rr_cmp(struct dns_rr *, struct dns_packet *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC size_t dns_rr_print(void *, size_t, struct dns_rr *, struct dns_packet *, int *);
+
+
+#define dns_rr_i_new(P, ...) \
+ dns_rr_i_init(&dns_quietinit((struct dns_rr_i){ 0, __VA_ARGS__ }), (P))
+
+struct dns_rr_i {
+ enum dns_section section;
+ const void *name;
+ enum dns_type type;
+ enum dns_class class;
+ const void *data;
+
+ int follow;
+
+ int (*sort)(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *);
+ unsigned args[2];
+
+ struct {
+ unsigned short next;
+ unsigned short count;
+
+ unsigned exec;
+ unsigned regs[2];
+ } state, saved;
+}; /* struct dns_rr_i */
+
+DNS_PUBLIC int dns_rr_i_packet(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *);
+
+DNS_PUBLIC int dns_rr_i_order(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *);
+
+DNS_PUBLIC int dns_rr_i_shuffle(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *);
+
+DNS_PUBLIC struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *, struct dns_packet *);
+
+#define dns_rr_i_save(i) ((i)->saved = (i)->state)
+#define dns_rr_i_rewind(i) ((i)->state = (i)->saved)
+#define dns_rr_i_count(i) ((i)->state.count)
+
+DNS_PUBLIC unsigned dns_rr_grep(struct dns_rr *, unsigned, struct dns_rr_i *, struct dns_packet *, int *);
+
+#define dns_rr_foreach_(rr, P, ...) \
+ for (struct dns_rr_i DNS_PP_XPASTE(i, __LINE__) = *dns_rr_i_new((P), __VA_ARGS__); dns_rr_grep((rr), 1, &DNS_PP_XPASTE(i, __LINE__), (P), &(int){ 0 }); )
+
+#define dns_rr_foreach(...) dns_rr_foreach_(__VA_ARGS__)
+
+
+/*
+ * A R E S O U R C E R E C O R D
+ */
+
+struct dns_a {
+ struct in_addr addr;
+}; /* struct dns_a */
+
+DNS_PUBLIC int dns_a_parse(struct dns_a *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_a_push(struct dns_packet *, struct dns_a *);
+
+DNS_PUBLIC int dns_a_cmp(const struct dns_a *, const struct dns_a *);
+
+DNS_PUBLIC size_t dns_a_print(void *, size_t, struct dns_a *);
+
+DNS_PUBLIC size_t dns_a_arpa(void *, size_t, const struct dns_a *);
+
+
+/*
+ * AAAA R E S O U R C E R E C O R D
+ */
+
+struct dns_aaaa {
+ struct in6_addr addr;
+}; /* struct dns_aaaa */
+
+DNS_PUBLIC int dns_aaaa_parse(struct dns_aaaa *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_aaaa_push(struct dns_packet *, struct dns_aaaa *);
+
+DNS_PUBLIC int dns_aaaa_cmp(const struct dns_aaaa *, const struct dns_aaaa *);
+
+DNS_PUBLIC size_t dns_aaaa_print(void *, size_t, struct dns_aaaa *);
+
+DNS_PUBLIC size_t dns_aaaa_arpa(void *, size_t, const struct dns_aaaa *);
+
+
+/*
+ * MX R E S O U R C E R E C O R D
+ */
+
+struct dns_mx {
+ unsigned short preference;
+ char host[DNS_D_MAXNAME + 1];
+}; /* struct dns_mx */
+
+DNS_PUBLIC int dns_mx_parse(struct dns_mx *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_mx_push(struct dns_packet *, struct dns_mx *);
+
+DNS_PUBLIC int dns_mx_cmp(const struct dns_mx *, const struct dns_mx *);
+
+DNS_PUBLIC size_t dns_mx_print(void *, size_t, struct dns_mx *);
+
+DNS_PUBLIC size_t dns_mx_cname(void *, size_t, struct dns_mx *);
+
+
+/*
+ * NS R E S O U R C E R E C O R D
+ */
+
+struct dns_ns {
+ char host[DNS_D_MAXNAME + 1];
+}; /* struct dns_ns */
+
+DNS_PUBLIC int dns_ns_parse(struct dns_ns *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_ns_push(struct dns_packet *, struct dns_ns *);
+
+DNS_PUBLIC int dns_ns_cmp(const struct dns_ns *, const struct dns_ns *);
+
+DNS_PUBLIC size_t dns_ns_print(void *, size_t, struct dns_ns *);
+
+DNS_PUBLIC size_t dns_ns_cname(void *, size_t, struct dns_ns *);
+
+
+/*
+ * CNAME R E S O U R C E R E C O R D
+ */
+
+struct dns_cname {
+ char host[DNS_D_MAXNAME + 1];
+}; /* struct dns_cname */
+
+DNS_PUBLIC int dns_cname_parse(struct dns_cname *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_cname_push(struct dns_packet *, struct dns_cname *);
+
+DNS_PUBLIC int dns_cname_cmp(const struct dns_cname *, const struct dns_cname *);
+
+DNS_PUBLIC size_t dns_cname_print(void *, size_t, struct dns_cname *);
+
+DNS_PUBLIC size_t dns_cname_cname(void *, size_t, struct dns_cname *);
+
+
+/*
+ * SOA R E S O U R C E R E C O R D
+ */
+
+struct dns_soa {
+ char mname[DNS_D_MAXNAME + 1];
+ char rname[DNS_D_MAXNAME + 1];
+ unsigned serial, refresh, retry, expire, minimum;
+}; /* struct dns_soa */
+
+DNS_PUBLIC int dns_soa_parse(struct dns_soa *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_soa_push(struct dns_packet *, struct dns_soa *);
+
+DNS_PUBLIC int dns_soa_cmp(const struct dns_soa *, const struct dns_soa *);
+
+DNS_PUBLIC size_t dns_soa_print(void *, size_t, struct dns_soa *);
+
+
+/*
+ * PTR R E S O U R C E R E C O R D
+ */
+
+struct dns_ptr {
+ char host[DNS_D_MAXNAME + 1];
+}; /* struct dns_ptr */
+
+DNS_PUBLIC int dns_ptr_parse(struct dns_ptr *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_ptr_push(struct dns_packet *, struct dns_ptr *);
+
+DNS_PUBLIC int dns_ptr_cmp(const struct dns_ptr *, const struct dns_ptr *);
+
+DNS_PUBLIC size_t dns_ptr_print(void *, size_t, struct dns_ptr *);
+
+DNS_PUBLIC size_t dns_ptr_cname(void *, size_t, struct dns_ptr *);
+
+DNS_PUBLIC size_t dns_ptr_qname(void *, size_t, int, void *);
+
+
+/*
+ * SRV R E S O U R C E R E C O R D
+ */
+
+struct dns_srv {
+ unsigned short priority;
+ unsigned short weight;
+ unsigned short port;
+ char target[DNS_D_MAXNAME + 1];
+}; /* struct dns_srv */
+
+DNS_PUBLIC int dns_srv_parse(struct dns_srv *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_srv_push(struct dns_packet *, struct dns_srv *);
+
+DNS_PUBLIC int dns_srv_cmp(const struct dns_srv *, const struct dns_srv *);
+
+DNS_PUBLIC size_t dns_srv_print(void *, size_t, struct dns_srv *);
+
+DNS_PUBLIC size_t dns_srv_cname(void *, size_t, struct dns_srv *);
+
+
+/*
+ * OPT R E S O U R C E R E C O R D
+ */
+
+#ifndef DNS_OPT_MINDATA
+#define DNS_OPT_MINDATA 256
+#endif
+
+#define DNS_OPT_DNSSEC 0x8000
+
+struct dns_opt {
+ enum dns_rcode rcode;
+ unsigned char version;
+ unsigned short flags;
+
+ union {
+ unsigned short maxsize; /* deprecated as confusing */
+ unsigned short maxudp; /* maximum UDP payload size */
+ };
+
+ size_t size, len;
+ unsigned char data[DNS_OPT_MINDATA];
+}; /* struct dns_opt */
+
+#define DNS_OPT_INIT(opt) { .size = sizeof (*opt) - offsetof(struct dns_opt, data) }
+
+DNS_PUBLIC struct dns_opt *dns_opt_init(struct dns_opt *, size_t);
+
+DNS_PUBLIC int dns_opt_parse(struct dns_opt *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_opt_push(struct dns_packet *, struct dns_opt *);
+
+DNS_PUBLIC int dns_opt_cmp(const struct dns_opt *, const struct dns_opt *);
+
+DNS_PUBLIC size_t dns_opt_print(void *, size_t, struct dns_opt *);
+
+DNS_PUBLIC unsigned int dns_opt_ttl(const struct dns_opt *);
+
+DNS_PUBLIC unsigned short dns_opt_class(const struct dns_opt *);
+
+DNS_PUBLIC dns_error_t dns_opt_data_push(struct dns_opt *, unsigned char, unsigned short, const void *);
+
+
+/*
+ * SSHFP R E S O U R C E R E C O R D
+ */
+
+struct dns_sshfp {
+ enum dns_sshfp_key {
+ DNS_SSHFP_RSA = 1,
+ DNS_SSHFP_DSA = 2,
+ } algo;
+
+ enum dns_sshfp_digest {
+ DNS_SSHFP_SHA1 = 1,
+ } type;
+
+ union {
+ unsigned char sha1[20];
+ } digest;
+}; /* struct dns_sshfp */
+
+DNS_PUBLIC int dns_sshfp_parse(struct dns_sshfp *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_sshfp_push(struct dns_packet *, struct dns_sshfp *);
+
+DNS_PUBLIC int dns_sshfp_cmp(const struct dns_sshfp *, const struct dns_sshfp *);
+
+DNS_PUBLIC size_t dns_sshfp_print(void *, size_t, struct dns_sshfp *);
+
+
+/*
+ * TXT R E S O U R C E R E C O R D
+ */
+
+#ifndef DNS_TXT_MINDATA
+#define DNS_TXT_MINDATA 1024
+#endif
+
+struct dns_txt {
+ size_t size, len;
+ unsigned char data[DNS_TXT_MINDATA];
+}; /* struct dns_txt */
+
+DNS_PUBLIC struct dns_txt *dns_txt_init(struct dns_txt *, size_t);
+
+DNS_PUBLIC int dns_txt_parse(struct dns_txt *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_txt_push(struct dns_packet *, struct dns_txt *);
+
+DNS_PUBLIC int dns_txt_cmp(const struct dns_txt *, const struct dns_txt *);
+
+DNS_PUBLIC size_t dns_txt_print(void *, size_t, struct dns_txt *);
+
+
+/*
+ * ANY R E S O U R C E R E C O R D
+ */
+
+union dns_any {
+ struct dns_a a;
+ struct dns_aaaa aaaa;
+ struct dns_mx mx;
+ struct dns_ns ns;
+ struct dns_cname cname;
+ struct dns_soa soa;
+ struct dns_ptr ptr;
+ struct dns_srv srv;
+ struct dns_opt opt;
+ struct dns_sshfp sshfp;
+ struct dns_txt txt, spf, rdata;
+}; /* union dns_any */
+
+#define DNS_ANY_INIT(any) { .rdata = { .size = sizeof *(any) - offsetof(struct dns_txt, data) } }
+
+DNS_PUBLIC union dns_any *dns_any_init(union dns_any *, size_t);
+
+DNS_PUBLIC int dns_any_parse(union dns_any *, struct dns_rr *, struct dns_packet *);
+
+DNS_PUBLIC int dns_any_push(struct dns_packet *, union dns_any *, enum dns_type);
+
+DNS_PUBLIC int dns_any_cmp(const union dns_any *, enum dns_type, const union dns_any *, enum dns_type);
+
+DNS_PUBLIC size_t dns_any_print(void *, size_t, union dns_any *, enum dns_type);
+
+DNS_PUBLIC size_t dns_any_cname(void *, size_t, union dns_any *, enum dns_type);
+
+
+/*
+ * H O S T S I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_hosts;
+
+DNS_PUBLIC struct dns_hosts *dns_hosts_open(int *);
+
+DNS_PUBLIC void dns_hosts_close(struct dns_hosts *);
+
+DNS_PUBLIC dns_refcount_t dns_hosts_acquire(struct dns_hosts *);
+
+DNS_PUBLIC dns_refcount_t dns_hosts_release(struct dns_hosts *);
+
+DNS_PUBLIC struct dns_hosts *dns_hosts_mortal(struct dns_hosts *);
+
+DNS_PUBLIC struct dns_hosts *dns_hosts_local(int *);
+
+DNS_PUBLIC int dns_hosts_loadfile(struct dns_hosts *, FILE *);
+
+DNS_PUBLIC int dns_hosts_loadpath(struct dns_hosts *, const char *);
+
+DNS_PUBLIC int dns_hosts_dump(struct dns_hosts *, FILE *);
+
+DNS_PUBLIC int dns_hosts_insert(struct dns_hosts *, int, const void *, const void *, _Bool);
+
+DNS_PUBLIC struct dns_packet *dns_hosts_query(struct dns_hosts *, struct dns_packet *, int *);
+
+
+/*
+ * R E S O L V . C O N F I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_resolv_conf {
+ struct sockaddr_storage nameserver[3];
+
+ char search[4][DNS_D_MAXNAME + 1];
+
+ /* (f)ile, (b)ind, (c)ache */
+ char lookup[4 * (1 + (4 * 2))];
+
+ /* getaddrinfo family by preference order ("inet4", "inet6") */
+ int family[3];
+
+ struct {
+ _Bool edns0;
+
+ unsigned ndots;
+
+ unsigned timeout;
+
+ unsigned attempts;
+
+ _Bool rotate;
+
+ _Bool recurse;
+
+ _Bool smart;
+
+ enum {
+ DNS_RESCONF_TCP_ENABLE,
+ DNS_RESCONF_TCP_ONLY,
+ DNS_RESCONF_TCP_SOCKS,
+ DNS_RESCONF_TCP_DISABLE,
+ } tcp;
+ } options;
+
+ struct sockaddr_storage iface;
+
+ struct { /* PRIVATE */
+ dns_atomic_t refcount;
+ } _;
+}; /* struct dns_resolv_conf */
+
+DNS_PUBLIC struct dns_resolv_conf *dns_resconf_open(int *);
+
+DNS_PUBLIC void dns_resconf_close(struct dns_resolv_conf *);
+
+DNS_PUBLIC dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *);
+
+DNS_PUBLIC dns_refcount_t dns_resconf_release(struct dns_resolv_conf *);
+
+DNS_PUBLIC struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *);
+
+DNS_PUBLIC struct dns_resolv_conf *dns_resconf_local(int *);
+
+DNS_PUBLIC struct dns_resolv_conf *dns_resconf_root(int *);
+
+DNS_PUBLIC int dns_resconf_pton(struct sockaddr_storage *, const char *);
+
+DNS_PUBLIC int dns_resconf_loadfile(struct dns_resolv_conf *, FILE *);
+
+DNS_PUBLIC int dns_resconf_loadpath(struct dns_resolv_conf *, const char *);
+
+DNS_PUBLIC int dns_nssconf_loadfile(struct dns_resolv_conf *, FILE *);
+
+DNS_PUBLIC int dns_nssconf_loadpath(struct dns_resolv_conf *, const char *);
+
+DNS_PUBLIC int dns_resconf_dump(struct dns_resolv_conf *, FILE *);
+
+DNS_PUBLIC int dns_nssconf_dump(struct dns_resolv_conf *, FILE *);
+
+DNS_PUBLIC int dns_resconf_setiface(struct dns_resolv_conf *, const char *, unsigned short);
+
+typedef unsigned long dns_resconf_i_t;
+
+DNS_PUBLIC size_t dns_resconf_search(void *, size_t, const void *, size_t, struct dns_resolv_conf *, dns_resconf_i_t *);
+
+
+/*
+ * H I N T S E R V E R I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_hints;
+
+DNS_PUBLIC struct dns_hints *dns_hints_open(struct dns_resolv_conf *, int *);
+
+DNS_PUBLIC void dns_hints_close(struct dns_hints *);
+
+DNS_PUBLIC dns_refcount_t dns_hints_acquire(struct dns_hints *);
+
+DNS_PUBLIC dns_refcount_t dns_hints_release(struct dns_hints *);
+
+DNS_PUBLIC struct dns_hints *dns_hints_mortal(struct dns_hints *);
+
+DNS_PUBLIC int dns_hints_insert(struct dns_hints *, const char *, const struct sockaddr *, unsigned);
+
+DNS_PUBLIC unsigned dns_hints_insert_resconf(struct dns_hints *, const char *, const struct dns_resolv_conf *, int *);
+
+DNS_PUBLIC struct dns_hints *dns_hints_local(struct dns_resolv_conf *, int *);
+
+DNS_PUBLIC struct dns_hints *dns_hints_root(struct dns_resolv_conf *, int *);
+
+DNS_PUBLIC struct dns_packet *dns_hints_query(struct dns_hints *, struct dns_packet *, int *);
+
+DNS_PUBLIC int dns_hints_dump(struct dns_hints *, FILE *);
+
+
+struct dns_hints_i {
+ const char *zone;
+
+ struct {
+ unsigned next;
+ unsigned seed;
+ } state;
+}; /* struct dns_hints_i */
+
+#define dns_hints_i_new(...) (&(struct dns_hints_i){ __VA_ARGS__ })
+
+DNS_PUBLIC unsigned dns_hints_grep(struct sockaddr **, socklen_t *, unsigned, struct dns_hints_i *, struct dns_hints *);
+
+
+/*
+ * C A C H E I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_cache {
+ void *state;
+
+ dns_refcount_t (*acquire)(struct dns_cache *);
+ dns_refcount_t (*release)(struct dns_cache *);
+
+ struct dns_packet *(*query)(struct dns_packet *, struct dns_cache *, int *);
+
+ int (*submit)(struct dns_packet *, struct dns_cache *);
+ int (*check)(struct dns_cache *);
+ struct dns_packet *(*fetch)(struct dns_cache *, int *);
+
+ int (*pollfd)(struct dns_cache *);
+ short (*events)(struct dns_cache *);
+ void (*clear)(struct dns_cache *);
+
+ union {
+ long i;
+ void *p;
+ } arg[3];
+
+ struct { /* PRIVATE */
+ dns_atomic_t refcount;
+ } _;
+}; /* struct dns_cache */
+
+
+DNS_PUBLIC struct dns_cache *dns_cache_init(struct dns_cache *);
+
+DNS_PUBLIC void dns_cache_close(struct dns_cache *);
+
+
+/*
+ * A P P L I C A T I O N I N T E R F A C E
+ *
+ * Options to change the behavior of the API. Applies across all the
+ * different components.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_OPTS_INITIALIZER_ { 0, 0 }, 0, 0
+#define DNS_OPTS_INITIALIZER { DNS_OPTS_INITIALIZER_ }
+#define DNS_OPTS_INIT(...) { DNS_OPTS_INITIALIZER_, __VA_ARGS__ }
+
+#define dns_opts(...) (&dns_quietinit((struct dns_options)DNS_OPTS_INIT(__VA_ARGS__)))
+
+struct dns_options {
+ /*
+ * If the callback closes *fd, it must set it to -1. Otherwise, the
+ * descriptor is queued and lazily closed at object destruction or
+ * by an explicit call to _clear(). This allows safe use of
+ * kqueue(2), epoll(2), et al -style persistent events.
+ */
+ struct {
+ void *arg;
+ int (*cb)(int *fd, void *arg);
+ } closefd;
+
+ /* bitmask for _events() routines */
+ enum dns_events {
+ DNS_SYSPOLL,
+ DNS_LIBEVENT,
+ } events;
+
+ /* Use this SOCKS server. */
+ const struct sockaddr_storage *socks_host;
+
+ /* Credentials for the SOCKS server (optional). */
+ const char *socks_user;
+ const char *socks_password;
+}; /* struct dns_options */
+
+
+/*
+ * S T A T S I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_stat {
+ size_t queries;
+
+ struct {
+ struct {
+ size_t count, bytes;
+ } sent, rcvd;
+ } udp, tcp;
+}; /* struct dns_stat */
+
+
+/*
+ * S O C K E T I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_socket;
+
+DNS_PUBLIC struct dns_socket *dns_so_open(const struct sockaddr *, int, const struct dns_options *, int *error);
+
+DNS_PUBLIC void dns_so_close(struct dns_socket *);
+
+DNS_PUBLIC void dns_so_reset(struct dns_socket *);
+
+DNS_PUBLIC unsigned short dns_so_mkqid(struct dns_socket *so);
+
+DNS_PUBLIC struct dns_packet *dns_so_query(struct dns_socket *, struct dns_packet *, struct sockaddr *, int *);
+
+DNS_PUBLIC int dns_so_submit(struct dns_socket *, struct dns_packet *, struct sockaddr *);
+
+DNS_PUBLIC int dns_so_check(struct dns_socket *);
+
+DNS_PUBLIC struct dns_packet *dns_so_fetch(struct dns_socket *, int *);
+
+DNS_PUBLIC time_t dns_so_elapsed(struct dns_socket *);
+
+DNS_PUBLIC void dns_so_clear(struct dns_socket *);
+
+DNS_PUBLIC int dns_so_events(struct dns_socket *);
+
+DNS_PUBLIC int dns_so_pollfd(struct dns_socket *);
+
+DNS_PUBLIC int dns_so_poll(struct dns_socket *, int);
+
+DNS_PUBLIC const struct dns_stat *dns_so_stat(struct dns_socket *);
+
+DNS_PUBLIC struct dns_trace *dns_so_trace(struct dns_socket *);
+
+DNS_PUBLIC void dns_so_settrace(struct dns_socket *, struct dns_trace *);
+
+
+/*
+ * R E S O L V E R I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_resolver;
+
+DNS_PUBLIC struct dns_resolver *dns_res_open(struct dns_resolv_conf *, struct dns_hosts *hosts, struct dns_hints *, struct dns_cache *, const struct dns_options *, int *);
+
+DNS_PUBLIC struct dns_resolver *dns_res_stub(const struct dns_options *, int *);
+
+DNS_PUBLIC void dns_res_reset(struct dns_resolver *);
+
+DNS_PUBLIC void dns_res_close(struct dns_resolver *);
+
+DNS_PUBLIC dns_refcount_t dns_res_acquire(struct dns_resolver *);
+
+DNS_PUBLIC dns_refcount_t dns_res_release(struct dns_resolver *);
+
+DNS_PUBLIC struct dns_resolver *dns_res_mortal(struct dns_resolver *);
+
+DNS_PUBLIC int dns_res_submit(struct dns_resolver *, const char *, enum dns_type, enum dns_class);
+
+DNS_PUBLIC int dns_res_submit2(struct dns_resolver *, const char *, size_t, enum dns_type, enum dns_class);
+
+DNS_PUBLIC int dns_res_check(struct dns_resolver *);
+
+DNS_PUBLIC struct dns_packet *dns_res_fetch(struct dns_resolver *, int *);
+
+DNS_PUBLIC time_t dns_res_elapsed(struct dns_resolver *);
+
+DNS_PUBLIC void dns_res_clear(struct dns_resolver *);
+
+DNS_PUBLIC int dns_res_events(struct dns_resolver *);
+
+DNS_PUBLIC int dns_res_pollfd(struct dns_resolver *);
+
+DNS_PUBLIC time_t dns_res_timeout(struct dns_resolver *);
+
+DNS_PUBLIC int dns_res_poll(struct dns_resolver *, int);
+
+DNS_PUBLIC struct dns_packet *dns_res_query(struct dns_resolver *, const char *, enum dns_type, enum dns_class, int, int *);
+
+DNS_PUBLIC const struct dns_stat *dns_res_stat(struct dns_resolver *);
+
+DNS_PUBLIC void dns_res_sethints(struct dns_resolver *, struct dns_hints *);
+
+DNS_PUBLIC struct dns_trace *dns_res_trace(struct dns_resolver *);
+
+DNS_PUBLIC void dns_res_settrace(struct dns_resolver *, struct dns_trace *);
+
+
+/*
+ * A D D R I N F O I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct dns_addrinfo;
+
+DNS_PUBLIC struct dns_addrinfo *dns_ai_open(const char *, const char *, enum dns_type, const struct addrinfo *, struct dns_resolver *, int *);
+
+DNS_PUBLIC void dns_ai_close(struct dns_addrinfo *);
+
+DNS_PUBLIC int dns_ai_nextent(struct addrinfo **, struct dns_addrinfo *);
+
+DNS_PUBLIC size_t dns_ai_print(void *, size_t, struct addrinfo *, struct dns_addrinfo *);
+
+DNS_PUBLIC time_t dns_ai_elapsed(struct dns_addrinfo *);
+
+DNS_PUBLIC void dns_ai_clear(struct dns_addrinfo *);
+
+DNS_PUBLIC int dns_ai_events(struct dns_addrinfo *);
+
+DNS_PUBLIC int dns_ai_pollfd(struct dns_addrinfo *);
+
+DNS_PUBLIC time_t dns_ai_timeout(struct dns_addrinfo *);
+
+DNS_PUBLIC int dns_ai_poll(struct dns_addrinfo *, int);
+
+DNS_PUBLIC const struct dns_stat *dns_ai_stat(struct dns_addrinfo *);
+
+DNS_PUBLIC struct dns_trace *dns_ai_trace(struct dns_addrinfo *);
+
+DNS_PUBLIC void dns_ai_settrace(struct dns_addrinfo *, struct dns_trace *);
+
+
+/*
+ * Q U E R Y T R A C I N G I N T E R F A C E
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_TRACE_ID_C(n) UINT64_C(n)
+typedef uint64_t dns_trace_id_t;
+
+#define DNS_TRACE_ABI 0x20160803
+
+struct dns_trace_event {
+ enum {
+ DNS_TE_RES_SUBMIT = 1,
+ DNS_TE_RES_FETCH = 99,
+
+ DNS_TE_SO_SUBMIT = 100,
+ DNS_TE_SO_VERIFY,
+ DNS_TE_SO_FETCH = 199,
+
+ DNS_TE_SYS_CONNECT = 200,
+ DNS_TE_SYS_SEND,
+ DNS_TE_SYS_RECV,
+ } type;
+
+ size_t size;
+ dns_trace_id_t id;
+ struct timespec ts;
+ int abi;
+
+ union {
+ struct {
+ char qname[DNS_D_MAXNAME + 1];
+ enum dns_type qtype;
+ enum dns_class qclass;
+ int error;
+ } res_submit;
+
+ struct {
+ int error;
+ } res_fetch;
+
+ struct {
+ struct sockaddr_storage haddr;
+ char hname[DNS_D_MAXNAME + 1];
+ int error;
+ } so_submit;
+
+ struct {
+ int error;
+ } so_verify;
+
+ struct {
+ int error;
+ } so_fetch;
+
+ struct {
+ struct sockaddr_storage src, dst;
+ int socktype;
+ dns_error_t error;
+ } sys_connect, sys_send, sys_recv;
+ };
+
+ unsigned char data[];
+};
+
+static inline size_t dns_te_datasize(const struct dns_trace_event *te) {
+ size_t n = offsetof(struct dns_trace_event, data);
+ return (n <= te->size)? te->size - n : 0;
+}
+
+struct dns_trace;
+
+DNS_PUBLIC int dns_trace_abi(void);
+
+DNS_PUBLIC struct dns_trace *dns_trace_open(FILE *, dns_error_t *);
+
+DNS_PUBLIC void dns_trace_close(struct dns_trace *);
+
+DNS_PUBLIC dns_refcount_t dns_trace_acquire(struct dns_trace *);
+
+DNS_PUBLIC dns_refcount_t dns_trace_release(struct dns_trace *);
+
+DNS_PUBLIC dns_trace_id_t dns_trace_id(struct dns_trace *);
+
+DNS_PUBLIC dns_trace_id_t dns_trace_setid(struct dns_trace *, dns_trace_id_t);
+
+DNS_PUBLIC struct dns_trace_event *dns_trace_get(struct dns_trace *, struct dns_trace_event **, dns_error_t *);
+
+DNS_PUBLIC struct dns_trace_event *dns_trace_tag(struct dns_trace *, struct dns_trace_event *);
+
+DNS_PUBLIC dns_error_t dns_trace_put(struct dns_trace *, const struct dns_trace_event *, const void *, size_t);
+
+DNS_PUBLIC dns_error_t dns_trace_dump(struct dns_trace *, FILE *);
+
+DNS_PUBLIC struct dns_trace_event *dns_trace_fget(struct dns_trace_event **, FILE *, dns_error_t *);
+
+DNS_PUBLIC dns_error_t dns_trace_fput(const struct dns_trace_event *, const void *, size_t, FILE *);
+
+
+/*
+ * U T I L I T Y I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+DNS_PUBLIC size_t dns_strlcpy(char *, const char *, size_t);
+
+DNS_PUBLIC size_t dns_strlcat(char *, const char *, size_t);
+
+
+/*
+ * M A C R O M A G I C S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define DNS_PP_MIN(a, b) (((a) < (b))? (a) : (b))
+#define DNS_PP_MAX(a, b) (((a) > (b))? (a) : (b))
+#define DNS_PP_NARG_(a, b, c, d, e, f, g, h, i, j, k, N,...) N
+#define DNS_PP_NARG(...) DNS_PP_NARG_(__VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define DNS_PP_CALL(F, ...) F(__VA_ARGS__)
+#define DNS_PP_PASTE(x, y) x##y
+#define DNS_PP_XPASTE(x, y) DNS_PP_PASTE(x, y)
+#define DNS_PP_STRINGIFY_(s) #s
+#define DNS_PP_STRINGIFY(s) DNS_PP_STRINGIFY_(s)
+#define DNS_PP_D1 0
+#define DNS_PP_D2 1
+#define DNS_PP_D3 2
+#define DNS_PP_D4 3
+#define DNS_PP_D5 4
+#define DNS_PP_D6 5
+#define DNS_PP_D7 6
+#define DNS_PP_D8 7
+#define DNS_PP_D9 8
+#define DNS_PP_D10 9
+#define DNS_PP_D11 10
+#define DNS_PP_DEC(N) DNS_PP_XPASTE(DNS_PP_D, N)
+
+#endif /* DNS_H */
{
if (sess->tls_session)
{
-# ifdef HTTP_USE_GNUTLS
+# if HTTP_USE_NTBTLS
+ /* FIXME!!
+ Possibly, ntbtls_get_transport and close those streams.
+ Somehow get SOCK to call my_socket_unref.
+ */
+ ntbtls_release (sess->tls_session);
+# elif HTTP_USE_GNUTLS
my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session);
my_socket_unref (sock, NULL, NULL);
gnutls_deinit (sess->tls_session);
{
(void)tls_priority;
+ /* ntbtls_set_debug (99, NULL, NULL); */
+
err = ntbtls_new (&sess->tls_session, NTBTLS_CLIENT);
if (err)
{
#if HTTP_USE_NTBTLS
if (hd->uri->use_tls)
{
+ estream_t in, out;
+
my_socket_ref (hd->sock);
+ in = es_fdopen_nc (hd->sock->fd, "rb");
+ if (!in)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (proxy_authstr);
+ return err;
+ }
+
+ out = es_fdopen_nc (hd->sock->fd, "wb");
+ if (!out)
+ {
+ err = gpg_error_from_syserror ();
+ es_fclose (in);
+ xfree (proxy_authstr);
+ return err;
+ }
+
+ err = ntbtls_set_transport (hd->session->tls_session, in, out);
+ if (err)
+ {
+ log_info ("TLS set_transport failed: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
+ xfree (proxy_authstr);
+ return err;
+ }
+
while ((err = ntbtls_handshake (hd->session->tls_session)))
{
switch (err)
{
gpg_error_t err;
assuan_fd_t sock = ASSUAN_INVALID_FD;
- int srvcount = 0;
+ unsigned int srvcount = 0;
int hostfound = 0;
int anyhostaddr = 0;
int srv, connected;
#endif /*!HASSUAN_SOCK_TOR*/
}
-#ifdef USE_DNS_SRV
/* Do the SRV thing */
if (srvtag)
{
{
stpcpy (stpcpy (stpcpy (stpcpy (srvname,"_"), srvtag),
"._tcp."), server);
- srvcount = getsrv (srvname, &serverlist);
+ err = get_dns_srv (srvname, &serverlist, &srvcount);
+ if (err)
+ log_info ("getting SRV '%s' failed: %s\n",
+ srvname, gpg_strerror (err));
xfree (srvname);
+ /* Note that on error SRVCOUNT is zero. */
}
}
}
-#else
- (void)flags;
- (void)srvtag;
-#endif /*USE_DNS_SRV*/
if (!serverlist)
{
size = c->content_length;
}
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+ if (c->use_tls && c->session && c->session->tls_session)
+ {
+ estream_t in, out;
+
+ ntbtls_get_stream (c->session->tls_session, &in, &out);
+ nread = es_fread (buffer, 1, size, in);
+ log_debug ("TLS network read: %d/%u\n", nread, size);
+ }
+ else
+#elif HTTP_USE_GNUTLS
if (c->use_tls && c->session && c->session->tls_session)
{
again:
cookie_t c = cookie;
int nwritten = 0;
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+ if (c->use_tls && c->session && c->session->tls_session)
+ {
+ estream_t in, out;
+
+ ntbtls_get_stream (c->session->tls_session, &in, &out);
+ if (size == 0)
+ es_fflush (out);
+ else
+ nwritten = es_fwrite (buffer, 1, size, out);
+ log_debug ("TLS network write: %d/%u\n", nwritten, size);
+ }
+ else
+#elif HTTP_USE_GNUTLS
if (c->use_tls && c->session && c->session->tls_session)
{
int nleft = size;
if (!c)
return 0;
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+ if (c->use_tls && c->session && c->session->tls_session)
+ {
+ /* FIXME!! Possibly call ntbtls_close_notify for close
+ of write stream. */
+ my_socket_unref (c->sock, NULL, NULL);
+ }
+ else
+#elif HTTP_USE_GNUTLS
if (c->use_tls && c->session && c->session->tls_session)
my_socket_unref (c->sock, send_gnutls_bye, c->session->tls_session);
else
int refidx;
int is_pool = 0;
char *cname;
-#ifdef USE_DNS_SRV
char *srvrecord;
struct srventry *srvs;
- int srvscount;
-#endif /* USE_DNS_SRV */
+ unsigned int srvscount;
reftblsize = 100;
reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
}
hi = hosttable[idx];
-#ifdef USE_DNS_SRV
if (!is_ip_address (name))
{
/* Check for SRV records. */
return err;
}
- srvscount = getsrv (srvrecord, &srvs);
+ err = get_dns_srv (srvrecord, &srvs, &srvscount);
xfree (srvrecord);
- if (srvscount < 0)
+ if (err)
{
- err = gpg_error_from_syserror ();
xfree (reftbl);
return err;
}
xfree (srvs);
}
}
-#endif /* USE_DNS_SRV */
/* Find all A records for this entry and put them into the pool
list - if any. */
only viable solutions are either to have another thread
responsible for logging the messages or to add an option to the
wrapper module to do the logging on its own. Given that we anyway
- need a way to rip the child process and this is best done using a
- general ripping thread, that thread can do the logging too. */
+ need a way to reap the child process and this is best done using a
+ general reaping thread, that thread can do the logging too. */
+ ldap_wrapper_launch_thread ();
*reader = NULL;
/* Create the filename of the file with the keys. */
keyfile_fname = make_filename_try (gnupg_datadir (), "distsigkey.gpg", NULL);
if (!keyfile_fname)
- goto leave;
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
/* Fetch the swdb from the web. */
err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst", &swdb);
}
else if (!strcmp (line, "dnsinfo"))
{
-#if USE_ADNS && HAVE_ADNS_IF_TORMODE
- assuan_set_okay_line (ctx, "- ADNS with Tor support");
-#elif USE_ADNS
- assuan_set_okay_line (ctx, "- ADNS w/o Tor support");
+ if (standard_resolver_p ())
+ assuan_set_okay_line
+ (ctx, "- Forced use of System resolver (w/o Tor support)");
+ else
+ {
+#ifdef USE_LIBDNS
+ assuan_set_okay_line (ctx, (recursive_resolver_p ()
+ ? "- Libdns recursive resolver"
+ : "- Libdns stub resolver"));
#else
- assuan_set_okay_line (ctx, "- System resolver w/o Tor support");
+ assuan_set_okay_line (ctx, "- System resolver (w/o Tor support)");
#endif
+ }
err = 0;
}
else
static int debug;
+static void
+init_sockets (void)
+{
+#ifdef HAVE_W32_SYSTEM
+ WSADATA wsadat;
+
+ WSAStartup (0x202, &wsadat);
+#endif
+}
+
int
main (int argc, char **argv)
{
fputs ("usage: " PGM " [HOST]\n"
"Options:\n"
- " --verbose print timings etc.\n"
- " --debug flyswatter\n"
- " --use-tor use Tor\n"
- " --new-circuit use a new Tor circuit\n"
- " --bracket enclose v6 addresses in brackets\n"
- " --cert lookup a CERT RR\n"
- " --srv lookup a SRV RR\n"
- " --cname lookup a CNAME RR\n"
+ " --verbose print timings etc.\n"
+ " --debug flyswatter\n"
+ " --standard-resolver use the system's resolver\n"
+ " --use-tor use Tor\n"
+ " --new-circuit use a new Tor circuit\n"
+ " --bracket enclose v6 addresses in brackets\n"
+ " --cert lookup a CERT RR\n"
+ " --srv lookup a SRV RR\n"
+ " --cname lookup a CNAME RR\n"
+ " --timeout SECONDS timeout after SECONDS\n"
, stdout);
exit (0);
}
opt_new_circuit = 1;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--standard-resolver"))
+ {
+ enable_standard_resolver (1);
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--recursive-resolver"))
+ {
+ enable_recursive_resolver (1);
+ argc--; argv++;
+ }
else if (!strcmp (*argv, "--bracket"))
{
opt_bracket = 1;
any_options = opt_cname = 1;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--timeout"))
+ {
+ argc--; argv++;
+ if (argc)
+ {
+ set_dns_timeout (atoi (*argv));
+ argc--; argv++;
+ }
+ }
else if (!strncmp (*argv, "--", 2))
{
fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
exit (1);
}
+ set_dns_verbose (verbose, debug);
+ init_sockets ();
+
if (opt_tor)
{
err = enable_dns_tormode (opt_new_circuit);
{
printf ("CNAME found: '%s'\n", cname);
}
-
xfree (cname);
}
else if (opt_srv)
{
struct srventry *srv;
- int rc,i;
+ unsigned int count;
+ int i;
- rc=getsrv (name? name : "_hkp._tcp.wwwkeys.pgp.net", &srv);
- printf("Count=%d\n",rc);
- for(i=0;i<rc;i++)
+ err = get_dns_srv (name? name : "_hkp._tcp.wwwkeys.pgp.net",
+ &srv, &count);
+ if (err)
+ printf ("get_dns_srv failed: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
+ else
{
- printf("priority=%-8hu ",srv[i].priority);
- printf("weight=%-8hu ",srv[i].weight);
- printf("port=%-5hu ",srv[i].port);
- printf("target=%s\n",srv[i].target);
- }
+ printf ("count=%u\n",count);
+ for (i=0; i < count; i++)
+ {
+ printf("priority=%-8hu ",srv[i].priority);
+ printf("weight=%-8hu ",srv[i].weight);
+ printf("port=%-5hu ",srv[i].port);
+ printf("target=%s\n",srv[i].target);
+ }
- xfree(srv);
+ xfree(srv);
+ }
}
else /* Standard lookup. */
{
(opt_bracket? DNS_WITHBRACKET:0),
&host);
if (err)
- printf ("[resolve_dns_addr failed (2): %s]", gpg_strerror (err));
+ printf (" [resolve_dns_addr failed (2): %s]", gpg_strerror (err));
else
{
if (!is_ip_address (host))
free_dns_addrinfo (aibuf);
}
+ reload_dns_stuff (1); /* Release objects. */
return 0;
}
userid encoded in UTF-8 and percent escaped. The fingerprint is
identical for all TOFU_USER lines up to a NEWSIG line.
-*** TOFU_STATS <validity> <sign-count> 0 [<policy> [<tm1> <tm2> <tm3> <tm4>]]
+*** TOFU_STATS <MANY_ARGS>
Statistics for the current user id.
- Values for VALIDITY are:
- - 0 :: conflict
- - 1 :: key without history
- - 2 :: key with too little history
+ The <MANY_ARGS> are the usual space delimited arguments. Here we
+ have too many of them to fit on one printed line and thus they are
+ given on 3 printed lines:
+
+ : <summary> <sign-count> <encryption-count>
+ : [<policy> [<tm1> <tm2> <tm3> <tm4>
+ : [<validity> [<sign-days> <encrypt-days>]]]]
+
+ Values for SUMMARY are:
+ - 0 :: attention, an interaction with the user is required (conflict)
+ - 1 :: key with no verification/encryption history
+ - 2 :: key with little history
- 3 :: key with enough history for basic trust
- 4 :: key with a lot of history
- good :: Policy is "good"
- bad :: Policy is "bad"
- ask :: Policy is "ask"
- - unknown :: Policy is not known.
+ - unknown :: Policy is "unknown" (TOFU information does not
+ contribute to the key's validity)
- TM1 ist the time the first message was verified. TM2 is the time
+ TM1 is the time the first message was verified. TM2 is the time
the most recent message was verified. TM3 is the time the first
message was encrypted. TM4 is the most recent encryption. All may
either be seconds since Epoch or an ISO time string
(yyyymmddThhmmss).
+ VALIDITY is the same as SUMMARY with the exception that VALIDITY
+ doesn't reflect whether the key needs attention. That is it never
+ takes on value 0. Instead, if there is a conflict, VALIDITY still
+ reflects the key's validity (values: 1-4).
+
+ SIGN-COUNT and ENCRYPTION-COUNT are the number of messages that we
+ have seen that have been signed by this key / encryption to this
+ key.
+
+ SIGN-DAYS and ENCRYPTION-DAYS are similar, but the number of days
+ (in UTC) on which we have seen messages signed by this key /
+ encrypted to this key.
+
*** TOFU_STATS_SHORT <long_string>
Information about the TOFU binding for the signature.
This may either be the specified mountpoint or one randomly
chosen by g13.
-*** PINENTRY_LAUNCHED <pid>
+*** PINENTRY_LAUNCHED <pid>[:<extra>]
This status line is emitted by gpg to notify a client that a
Pinentry has been launched. <pid> is the PID of the Pinentry. It
may be used to display a hint to the user but can't be used to
+* Debug flags
+
+This tables gives the flag values for the --debug option along with
+the alternative names used by the components.
+
+| | gpg | gpgsm | agent | scd | dirmngr | g13 | wks |
+|-------+---------+---------+---------+---------+---------+---------+---------|
+| 1 | packet | x509 | | | x509 | mount | mime |
+| 2 | mpi | mpi | mpi | mpi | | | parser |
+| 4 | crypto | crypto | crypto | crypto | crypto | crypto | crypto |
+| 8 | filter | | | | | | |
+| 16 | iobuf | | | | dns | | |
+| 32 | memory | memory | memory | memory | memory | memory | memory |
+| 64 | cache | cache | cache | cache | cache | | |
+| 128 | memstat | memstat | memstat | memstat | memstat | memstat | memstat |
+| 256 | trust | | | | | | |
+| 512 | hashing | hashing | hashing | hashing | hashing | | |
+| 1024 | ipc | ipc | ipc | ipc | ipc | ipc | ipc |
+| 2048 | | | | cardio | network | | |
+| 4096 | clock | | | reader | | | |
+| 8192 | lookup | | | | lookup | | |
+| 16384 | extprog | | | | | | extprog |
+
+Description of some debug flags:
+
+ - cardio :: Used by scdaemon to trace the APDUs exchange with the
+ card.
+ - clock :: Show execution times of certain functions.
+ - crypto :: Trace crypto operations.
+ - hashing :: Create files with the hashed data.
+ - ipc :: Trace the Assuan commands.
+ - mpi :: Show the values of the MPIs.
+ - reader :: Used by scdaemon to trace card reader related code. For
+ example: Open and close reader.
+
+
+
* Miscellaneous notes
** v3 fingerprints
examples/systemd-user/gpg-agent.service \
examples/systemd-user/gpg-agent.socket \
examples/systemd-user/gpg-agent-ssh.socket \
+ examples/systemd-user/gpg-agent-browser.socket \
examples/systemd-user/gpg-agent-extra.socket \
examples/gpgconf.conf examples/pwpattern.list
help.pt_BR.txt help.ro.txt help.ru.txt help.sk.txt \
help.sv.txt help.tr.txt help.zh_CN.txt help.zh_TW.txt
+profiles =
+
EXTRA_DIST = samplekeys.asc mksamplekeys com-certs.pem qualified.txt \
gnupg-logo.eps gnupg-logo.pdf gnupg-logo.png gnupg-logo-tr.png \
gnupg-module-overview.png gnupg-module-overview.pdf \
info_TEXINFOS = gnupg.texi
-dist_pkgdata_DATA = $(helpfiles)
+dist_pkgdata_DATA = $(helpfiles) $(profiles)
nobase_dist_doc_DATA = FAQ DETAILS HACKING DCO TRANSLATE OpenPGP KEYSERVER \
$(examples)
@noindent
The solution is to use the command @command{wineconsole}.
-Some operations like gen-key really want to talk to the console directly
+Some operations like @option{--generate-key} really want to talk to
+the console directly
for increased security (for example to prevent the passphrase from
appearing on the screen). So, you should use @command{wineconsole}
instead of @command{wine}, which will launch a windows console that
@item --homedir @var{dir}
@opindex options
Set the name of the home directory to @var{dir}. This option is only
-effective when used on the command line. The default os
+effective when used on the command line. The default is
the directory named @file{.gnupg} directly below the home directory
of the user unless the environment variable @code{GNUPGHOME} has been set
-in which case its value will be used. All kind of data is stored below
+in which case its value will be used. Many kinds of data are stored within
this directory.
keyserver pool. Certain other features are disabled if this mode is
active.
+@item --standard-resolver
+@opindex standard-resolver
+This option forces the use of the system's standard DNS resolver code.
+This is mainly used for debugging. Note that on Windows a standard
+resolver is not used and all DNS access will return the error ``Not
+Implemented'' if this function is used.
+
+@item --recursive-resolver
+@opindex recursive-resolver
+When possible use a recursive resolver instead of a stub resolver.
+
+@item --resolver-timeout @var{n}
+Set the timeout for the DNS resolver to N seconds. The default are 30
+seconds.
+
@item --allow-version-check
@opindex allow-version-check
Allow Dirmngr to connect to @code{https://versions.gnupg.org} to get
names. If the default public resolver, which is @code{8.8.8.8}, shall
not be used a different one can be given using this option. Note that
a numerical IP address must be given (IPv6 or IPv4) and that no error
-checking is done for @var{ipaddr}. DNS queries in Tor mode do only
-work if GnuPG as been build with ADNS support.
+checking is done for @var{ipaddr}.
@item --disable-ldap
@opindex disable-ldap
# Empty lines and comment lines, indicated by a hash mark as first non
# white space character, are ignored. The line is separated by white
# space into fields. The first field is used to match the user or
-# group and must start at the first column, the file is processes
+# group and must start at the first column, the file is processed
# sequential until a matching rule is found. A rule may contain
# several lines; continuation lines are indicated by a indenting them.
#
# * - Matches any user.
# All other variants are not defined and reserved for future use.
#
-# <component> and <option> are as specified by gpgconf.
+# <component> and <option> are as specified by gpgconf.
# <flag> may be one of:
# default - Delete the option so that the default is used.
# no-change - Mark the field as non changeable by gpgconf.
# gpg-agent min-passphrase-len 6
#
# * gpg-agent min-passphrase-len [no-change] 8
-# gpg-agent min-passphrase-nonalpha [no-change] 1
+# gpg-agent min-passphrase-nonalpha [no-change] 1
# gpg-agent max-passphrase-days [no-change] 700
# gpg-agent enable-passphrase-history [no-change]
# gpg-agent enforce-passphrase-constraints [default]
# gpg-agent max-cache-ttl-ssh [no-change] 10800
# gpg-agent allow-mark-trusted [default]
# gpg-agent allow-mark-trusted [no-change]
-# gpgsm enable-ocsp
+# gpgsm enable-ocsp
#===========
# All users in the group "staff" are allowed to change the value for
# --allow-mark-trusted; gpgconf's default is not to allow a change
To generate a secret/public keypair, run:
- : $ gpg --gen-key
+ : $ gpg --generate-key
and choose the default values.
@command{@gpgname} is the OpenPGP part of the GNU Privacy Guard (GnuPG). It
is a tool to provide digital encryption and signing services using the
OpenPGP standard. @command{@gpgname} features complete key management and
-all bells and whistles you can expect from a decent OpenPGP
+all the bells and whistles you would expect from a full OpenPGP
implementation.
+There are two main versions of GnuPG: GnuPG 1.x and GnuPG 2.x. GnuPG
+2.x supports modern encryption algorithms and thus should be preferred
+over GnuPG 1.x. You only need to use GnuPG 1.x if your platform
+doesn't support GnuPG 2.x, or you need support for some features that
+GnuPG 2.x has deprecated, e.g., decrypting data created with PGP-2
+keys.
+
@ifclear gpgtwohack
-Note that this version of GnuPG features all modern algorithms and
-should thus be preferred over older GnuPG versions. If you are
-looking for version 1 of GnuPG, you may find that version installed
-under the name @command{gpg1}.
+If you are looking for version 1 of GnuPG, you may find that version
+installed under the name @command{gpg1}.
@end ifclear
@ifset gpgtwohack
-In contrast to the standalone command gpg from GnuPG 1.x, which
-might be better suited for server and embedded platforms, the 2.x
-version is commonly installed under the name @command{@gpgname} and
-targeted to the desktop as it requires several other modules to be
-installed.
+In contrast to the standalone command @command{gpg} from GnuPG 1.x,
+the 2.x version is commonly installed under the name
+@command{@gpgname}.
@end ifset
@manpause
@section Commands
Commands are not distinguished from options except for the fact that
-only one command is allowed.
+only one command is allowed. Generally speaking, irrelevant options
+are silently ignored, and may not be checked for correctness.
-@command{@gpgname} may be run with no commands, in which case it will
+@command{@gpgname} may be run with no commands. In this case it will
perform a reasonable action depending on the type of file it is given
as input (an encrypted message is decrypted, a signature is verified,
-a file containing keys is listed).
-
-Please remember that option as well as command parsing stops as soon as
-a non-option is encountered, you can explicitly stop parsing by
-using the special option @option{--}.
+a file containing keys is listed, etc.).
@menu
@item --help
@itemx -h
@opindex help
-Print a usage message summarizing the most useful command line options.
+Print a usage message summarizing the most useful command-line options.
Note that you cannot abbreviate this command.
@item --warranty
@item --sign
@itemx -s
@opindex sign
-Make a signature. This command may be combined with @option{--encrypt}
-(for a signed and encrypted message), @option{--symmetric} (for a
-signed and symmetrically encrypted message), or @option{--encrypt} and
-@option{--symmetric} together (for a signed message that may be
-decrypted via a secret key or a passphrase). The key to be used for
-signing is chosen by default or can be set with the
+Sign a message. This command may be combined with @option{--encrypt}
+(to sign and encrypt a message), @option{--symmetric} (to sign and
+symmetrically encrypt a message), or both @option{--encrypt} and
+@option{--symmetric} (to sign and encrypt a message that can be
+decrypted using a secret key or a passphrase). The signing key is
+chosen by default or can be set explicitly using the
@option{--local-user} and @option{--default-key} options.
-@item --clearsign
+@item --clear-sign
+@opindex clear-sign
+@itemx --clearsign
@opindex clearsign
-Make a clear text signature. The content in a clear text signature is
+Make a cleartext signature. The content in a cleartext signature is
readable without any special software. OpenPGP software is only needed
-to verify the signature. Clear text signatures may modify end-of-line
+to verify the signature. cleartext signatures may modify end-of-line
whitespace for platform independence and are not intended to be
-reversible. The key to be used for signing is chosen by default or
-can be set with the @option{--local-user} and @option{--default-key}
+reversible. The signing key is chosen by default or can be set
+explicitly using the @option{--local-user} and @option{--default-key}
options.
@item --encrypt
@itemx -e
@opindex encrypt
-Encrypt data. This command may be combined with @option{--sign} (for a
-signed and encrypted message), @option{--symmetric} (for a message that
-may be decrypted via a secret key or a passphrase), or @option{--sign}
-and @option{--symmetric} together (for a signed message that may be
-decrypted via a secret key or a passphrase).
+Encrypt data. This command may be combined with @option{--sign} (to
+sign and encrypt a message), @option{--symmetric} (to encrypt a
+message that can decrypted using a secret key or a passphrase), or
+@option{--sign} and @option{--symmetric} together (for a signed
+message that can be decrypted using a secret key or a passphrase).
@item --symmetric
@itemx -c
@option{--output}). If the decrypted file is signed, the signature is also
verified. This command differs from the default operation, as it never
writes to the filename which is included in the file and it rejects
-files which don't begin with an encrypted message.
+files that don't begin with an encrypted message.
@item --verify
@opindex verify
Assume that the first argument is a signed file and verify it without
generating any output. With no arguments, the signature packet is
-read from STDIN. If only a one argument is given, it is expected to
-be a complete signature.
+read from STDIN. If only one argument is given, the specified file is
+expected to include a complete signature.
-With more than 1 argument, the first should be a detached signature
-and the remaining files make up the the signed data. To read the signed
-data from STDIN, use @samp{-} as the second filename. For security
-reasons a detached signature cannot read the signed material from
-STDIN without denoting it in the above way.
+With more than one argument, the first argument should specify a file
+with a detached signature and the remaining files should contain the
+signed data. To read the signed data from STDIN, use @samp{-} as the
+second filename. For security reasons, a detached signature will not
+read the signed material from STDIN if not explicitly specified.
Note: If the option @option{--batch} is not used, @command{@gpgname}
-may assume that a single argument is a file with a detached signature
+may assume that a single argument is a file with a detached signature,
and it will try to find a matching data file by stripping certain
suffixes. Using this historical feature to verify a detached
-signature is strongly discouraged; always specify the data file too.
+signature is strongly discouraged; you should always specify the data file
+explicitly.
-Note: When verifying a cleartext signature, @command{gpg} verifies
+Note: When verifying a cleartext signature, @command{@gpgname} verifies
only what makes up the cleartext signed data and not any extra data
-outside of the cleartext signature or header lines following directly
+outside of the cleartext signature or the header lines directly following
the dash marker line. The option @code{--output} may be used to write
-out the actual signed data; but there are other pitfalls with this
+out the actual signed data, but there are other pitfalls with this
format as well. It is suggested to avoid cleartext signatures in
favor of detached signatures.
@itemx -k
@itemx --list-public-keys
@opindex list-keys
-List all keys from the public keyrings, or just the keys given on the
-command line.
+List the specified keys. If no keys are specified, then all keys from
+the configured public keyrings are listed.
-Avoid using the output of this command in scripts or other programs as
-it is likely to change as GnuPG changes. See @option{--with-colons}
-for a machine-parseable key listing command that is appropriate for
-use in scripts and other programs. Never use the regular output for
-scripts --- it is only for human consumption.
+Never use the output of this command in scripts or other programs.
+The output is intended only for humans and its format is likely to
+change. The @option{--with-colons} option emits the output in a
+stable, machine-parseable format, which is intended for use by scripts
+and other programs.
@item --list-secret-keys
@itemx -K
@opindex list-secret-keys
-List all keys from the secret keyrings, or just the ones given on the
-command line. A @code{#} after the letters @code{sec} means that the
-secret key is not usable (for example, if it was created via
-@option{--export-secret-subkeys}). See also @option{--list-keys}.
-
-@item --list-sigs
+List the specified secret keys. If no keys are specified, then all
+known secret keys are listed. A @code{#} after the letters @code{sec}
+means that the secret key is not usable (for example, if it was
+exported using @option{--export-secret-subkeys}). See also
+@option{--list-keys}.
+
+@item --list-signatures
+@opindex list-signatures
+@itemx --list-sigs
@opindex list-sigs
Same as @option{--list-keys}, but the signatures are listed too.
This command has the same effect as
above to indicate trust signature levels (see the @option{--edit-key}
command "tsign").
-@item --check-sigs
+@item --check-signatures
+@opindex check-signatures
+@itemx --check-sigs
@opindex check-sigs
-Same as @option{--list-sigs}, but the signatures are verified. Note
+Same as @option{--list-signatures}, but the signatures are verified. Note
that for performance reasons the revocation status of a signing key is
not shown.
This command has the same effect as
The status of the verification is indicated by a flag directly following
the "sig" tag (and thus before the flags described above for
-@option{--list-sigs}). A "!" indicates that the signature has been
+@option{--list-signatures}). A "!" indicates that the signature has been
successfully verified, a "-" denotes a bad signature and a "%" is used
if an error occurred while checking the signature (e.g. a non supported
algorithm).
List all keys (or the specified ones) along with their
fingerprints. This is the same output as @option{--list-keys} but with
the additional output of a line with the fingerprint. May also be
-combined with @option{--list-sigs} or @option{--check-sigs}. If this
+combined with @option{--list-signatures} or @option{--check-signatures}. If this
command is given twice, the fingerprints of all secondary keys are
listed too. This command also forces pretty printing of fingerprints
if the keyid format has been set to "none".
this command may change with new releases.
-@item --card-edit
+@item --edit-card
+@opindex edit-card
+@itemx --card-edit
@opindex card-edit
Present a menu to work with a smartcard. The subcommand "help" provides
an overview on available commands. For a detailed description, please
@opindex change-pin
Present a menu to allow changing the PIN of a smartcard. This
functionality is also available as the subcommand "passwd" with the
-@option{--card-edit} command.
+@option{--edit-card} command.
@item --delete-keys @code{name}
@itemx --delete-keys @code{name}
Remove key from the secret keyring. In batch mode the key must be
specified by fingerprint. The option @option{--yes} can be used to
advice gpg-agent not to request a confirmation. This extra
-pre-caution is done because @command{gpg} can't be sure that the
+pre-caution is done because @command{@gpgname} can't be sure that the
secret key (as controlled by gpg-agent) is only used for the given
OpenPGP public key.
Fingerprints may be used instead of key IDs. Option @option{--keyserver}
must be used to give the name of this keyserver. Don't send your
complete keyring to a keyserver --- select only those keys which are new
-or changed by you. If no key IDs are given, @command{gpg} does nothing.
+or changed by you. If no key IDs are given, @command{@gpgname} does nothing.
@item --export-secret-keys
@itemx --export-secret-subkeys
Same as @option{--export}, but exports the secret keys instead. The
exported keys are written to STDOUT or to the file given with option
@option{--output}. This command is often used along with the option
-@option{--armor} to allow easy printing of the key for paper backup;
-however the external tool @command{paperkey} does a better job for
+@option{--armor} to allow for easy printing of the key for paper backup;
+however the external tool @command{paperkey} does a better job of
creating backups on paper. Note that exporting a secret key can be a
security risk if the exported keys are sent over an insecure channel.
The second form of the command has the special property to render the
secret part of the primary key useless; this is a GNU extension to
OpenPGP and other implementations can not be expected to successfully
-import such a key. Its intended use is to generated a full key with
-an additional signing subkey on a dedicated machine and then using
-this command to export the key without the primary key to the main
-machine.
+import such a key. Its intended use is in generating a full key with
+an additional signing subkey on a dedicated machine. This command
+then exports the key without the primary key to the main machine.
GnuPG may ask you to enter the passphrase for the key. This is
-required because the internal protection method of the secret key is
+required, because the internal protection method of the secret key is
different from the one specified by the OpenPGP protocol.
@item --export-ssh-key
which does not insert new keys but does only the merging of new
signatures, user-IDs and subkeys.
-@item --recv-keys @code{key IDs}
+@item --receive-keys @code{key IDs}
+@opindex receive-keys
+@itemx --recv-keys @code{key IDs}
@opindex recv-keys
Import the keys with the given key IDs from a keyserver. Option
@option{--keyserver} must be used to give the name of this keyserver.
@table @gnupgtabopt
-@item --quick-gen-key @code{user-id} [@code{algo} [@code{usage} [@code{expire}]]]
-@opindex quick-gen-key
+@item --quick-generate-key @code{user-id} [@code{algo} [@code{usage} [@code{expire}]]]
+@opindex quick-generate-key
This is a simple command to generate a standard key with one user id.
-In contrast to @option{--gen-key} the key is generated directly
+In contrast to @option{--generate-key} the key is generated directly
without the need to answer a bunch of prompts. Unless the option
@option{--yes} is given, the key creation will be canceled if the
given user id already exists in the keyring.
still create a primary and subkey use ``default'' or
``future-default'' for @code{algo} and ``default'' for @code{usage}.
For a description of these optional arguments see the command
-@code{--quick-addkey}. The @code{usage} accepts also the value
+@code{--quick-add-key}. The @code{usage} accepts also the value
``cert'' which can be used to create a certification only primary key;
the default is to a create certification and signing key.
+The @code{expire} argument can be used to specify an expiration date
+for the key. Several formats are supported; commonly the ISO
+YYYY-MM-DD format is used. The values ``never'', ``none'' can be used
+for no expiration date. Not specifying a value, or using ``-''
+results in a key expiring in a reasonable default interval.
+
If this command is used with @option{--batch},
@option{--pinentry-mode} has been set to @code{loopback}, and one of
the passphrase options (@option{--passphrase},
for it. To create a key without any protection @code{--passphrase ''}
may be used.
-@item --quick-addkey @code{fpr} [@code{algo} [@code{usage} [@code{expire}]]]
-@opindex quick-addkey
+@item --quick-set-expire @code{fpr} @code{expire}
+@opindex quick-set-expire
+Directly set the expiration time of the primary key to @code{expire}.
+To remove the expiration time @code{0} can be used.
+
+
+@item --quick-add-key @code{fpr} [@code{algo} [@code{usage} [@code{expire}]]]
+@opindex quick-add-key
Directly add a subkey to the key identified by the fingerprint
@code{fpr}. Without the optional arguments an encryption subkey is
added. If any of the arguments are given a more specific subkey is
YYYY-MM-DD format is used. The values ``never'', ``none'', or ``-''
can be used for no expiration date.
-@item --gen-key
+@item --generate-key
+@opindex generate-key
+@itemx --gen-key
@opindex gen-key
Generate a new key pair using the current default parameters. This is
the standard command to create a new key. In addition to the key a
revocation certificate is created and stored in the
@file{openpgp-revocs.d} directory below the GnuPG home directory.
-@item --full-gen-key
-@opindex gen-key
+@item --full-generate-key
+@opindex full-generate-key
+@itemx --full-gen-key
+@opindex full-gen-key
Generate a new key pair with dialogs for all options. This is an
-extended version of @option{--gen-key}.
+extended version of @option{--generate-key}.
There is also a feature which allows you to create keys in batch
mode. See the manual section ``Unattended key generation'' on how
to use this.
-@item --gen-revoke @code{name}
+@item --generate-revocation @code{name}
+@opindex generate-revocation
+@itemx --gen-revoke @code{name}
@opindex gen-revoke
Generate a revocation certificate for the complete key. To only revoke
a subkey or a key signature, use the @option{--edit} command.
to a file which is then send to frequent communication partners.
-@item --desig-revoke @code{name}
+@item --generate-designated-revocation @code{name}
+@opindex generate-designated-revocation
+@itemx --desig-revoke @code{name}
@opindex desig-revoke
Generate a designated revocation certificate for a key. This allows a
user (with the permission of the keyholder) to revoke someone else's
@opindex keyedit:delkey
Remove a subkey (secondary key). Note that it is not possible to retract
a subkey, once it has been send to the public (i.e. to a keyserver). In
- that case you better use @code{revkey}.
+ that case you better use @code{revkey}. Also note that this only
+ deletes the public part of a key.
@item revkey
@opindex keyedit:revkey
Its intended use is to help unattended key signing by utilizing a list
of verified fingerprints.
-@item --quick-adduid @var{user-id} @var{new-user-id}
-@opindex quick-adduid
+@item --quick-add-uid @var{user-id} @var{new-user-id}
+@opindex quick-add-uid
This command adds a new user id to an existing key. In contrast to
the interactive sub-command @code{adduid} of @option{--edit-key} the
@var{new-user-id} is added verbatim with only leading and trailing
white space removed, it is expected to be UTF-8 encoded, and no checks
on its form are applied.
-@item --quick-revuid @var{user-id} @var{user-id-to-revoke}
-@opindex quick-revuid
+@item --quick-revoke-uid @var{user-id} @var{user-id-to-revoke}
+@opindex quick-revoke-uid
This command revokes a User ID on an existing key. It cannot be used
to revoke the last User ID on key (some non-revoked User ID must
remain), with revocation reason ``User ID is no longer valid''. If
supplementary revocation text, you should use the interactive
sub-command @code{revuid} of @option{--edit-key}.
-@item --passwd @var{user_id}
+@item --change-passphrase @var{user_id}
+@opindex change-passphrase
+@itemx --passwd @var{user_id}
@opindex passwd
Change the passphrase of the secret key belonging to the certificate
specified as @var{user_id}. This is a shortcut for the sub-command
STDIN (in particular if gpg figures that the input is a
detached signature and no data file has been specified). Thus if you
do not want to feed data via STDIN, you should connect STDIN to
-@file{/dev/null}.
+g@file{/dev/null}.
+
+It is highly recommended to use this option along with the options
+@option{--status-fd} and @option{--with-colons} for any unattended of
+@command{gpg}.
@item --no-tty
@opindex no-tty
@opindex list-options
This is a space or comma delimited string that gives options used when
listing keys and signatures (that is, @option{--list-keys},
-@option{--list-sigs}, @option{--list-public-keys},
+@option{--list-signatures}, @option{--list-public-keys},
@option{--list-secret-keys}, and the @option{--edit-key} functions).
Options can be prepended with a @option{no-} (after the two dashes) to
give the opposite meaning. The options are:
@item show-photos
@opindex list-options:show-photos
- Causes @option{--list-keys}, @option{--list-sigs},
+ Causes @option{--list-keys}, @option{--list-signatures},
@option{--list-public-keys}, and @option{--list-secret-keys} to
display any photo IDs attached to the key. Defaults to no. See also
@option{--photo-viewer}. Does not work with @option{--with-colons}:
@item show-policy-urls
@opindex list-options:show-policy-urls
- Show policy URLs in the @option{--list-sigs} or @option{--check-sigs}
+ Show policy URLs in the @option{--list-signatures} or @option{--check-signatures}
listings. Defaults to no.
@item show-notations
@opindex list-options:show-std-notations
@opindex list-options:show-user-notations
Show all, IETF standard, or user-defined signature notations in the
- @option{--list-sigs} or @option{--check-sigs} listings. Defaults to no.
+ @option{--list-signatures} or @option{--check-signatures} listings. Defaults to no.
@item show-keyserver-urls
@opindex list-options:show-keyserver-urls
- Show any preferred keyserver URL in the @option{--list-sigs} or
- @option{--check-sigs} listings. Defaults to no.
+ Show any preferred keyserver URL in the @option{--list-signatures} or
+ @option{--check-signatures} listings. Defaults to no.
@item show-uid-validity
@opindex list-options:show-uid-validity
@item show-sig-expire
@opindex list-options:show-sig-expire
- Show signature expiration dates (if any) during @option{--list-sigs} or
- @option{--check-sigs} listings. Defaults to no.
+ Show signature expiration dates (if any) during @option{--list-signatures} or
+ @option{--check-signatures} listings. Defaults to no.
@item show-sig-subpackets
@opindex list-options:show-sig-subpackets
optional argument list of the subpackets to list. If no argument is
passed, list all subpackets. Defaults to no. This option is only
meaningful when using @option{--with-colons} along with
- @option{--list-sigs} or @option{--check-sigs}.
+ @option{--list-signatures} or @option{--check-signatures}.
@end table
@itemx --disable-large-rsa
@opindex enable-large-rsa
@opindex disable-large-rsa
-With --gen-key and --batch, enable the creation of RSA secret keys as
+With --generate-key and --batch, enable the creation of RSA secret keys as
large as 8192 bit. Note: 8192 bit is more than is generally
recommended. These large keys don't significantly improve security,
but they are more expensive to use, and their signatures and
@file{dirmngr.conf} instead.
Use @code{name} as your keyserver. This is the server that
-@option{--recv-keys}, @option{--send-keys}, and @option{--search-keys}
+@option{--receive-keys}, @option{--send-keys}, and @option{--search-keys}
will communicate with to receive keys from, send keys to, and search for
keys on. The format of the @code{name} is a URI:
`scheme:[//]keyservername[:port]' The scheme is the type of keyserver:
Tell the keyserver helper program how long (in seconds) to try and
perform a keyserver action before giving up. Note that performing
multiple actions at the same time uses this timeout value per action.
- For example, when retrieving multiple keys via @option{--recv-keys}, the
+ For example, when retrieving multiple keys via @option{--receive-keys}, the
timeout applies separately to each key retrieval, and not to the
- @option{--recv-keys} command as a whole. Defaults to 30 seconds.
+ @option{--receive-keys} command as a whole. Defaults to 30 seconds.
@item http-proxy=@code{value}
This option is deprecated.
@opindex recipient-file
This option is similar to @option{--recipient} except that it
encrypts to a key stored in the given file. @var{file} must be the
-name of a file containing exactly one key. @command{gpg} assumes that
+name of a file containing exactly one key. @command{@gpgname} assumes that
the key in this file is fully valid.
@item --hidden-recipient-file @var{file}
@opindex hidden-recipient-file
This option is similar to @option{--hidden-recipient} except that it
encrypts to a key stored in the given file. @var{file} must be the
-name of a file containing exactly one key. @command{gpg} assumes that
+name of a file containing exactly one key. @command{@gpgname} assumes that
the key in this file is fully valid.
@item --encrypt-to @code{name}
that this cannot completely repair the damaged key as some crucial data
is removed by the keyserver, but it does at least give you back one
subkey. Defaults to no for regular @option{--import} and to yes for
- keyserver @option{--recv-keys}.
+ keyserver @option{--receive-keys}.
@item import-show
Show a listing of the key as imported right before it is stored.
preferences, as GPG will only select an algorithm that is usable by
all recipients. The most highly ranked digest algorithm in this list
is also used when signing without encryption
-(e.g. @option{--clearsign} or @option{--sign}).
+(e.g. @option{--clear-sign} or @option{--sign}).
@item --personal-compress-preferences @code{string}
@opindex personal-compress-preferences
@item --comment @code{string}
@itemx --no-comments
@opindex comment
-Use @code{string} as a comment string in clear text signatures and ASCII
+Use @code{string} as a comment string in cleartext signatures and ASCII
armored messages or keys (see @option{--armor}). The default behavior is
not to use a comment string. @option{--comment} may be repeated multiple
times to get multiple comment strings. @option{--no-comments} removes
(for example "2m" for two months, or "5y" for five years), or an
absolute date in the form YYYY-MM-DD. Defaults to "0".
+@item --default-new-key-algo @var{string}
+@opindex default-new-key-algo @var{string}
+This option can be used to change the default algorithms for key
+generation. Note that the advanced key generation commands can always
+be used to specify a key algorithm directly. Please consult the
+source code to learn the syntax of @var{string}.
+
@item --allow-secret-key-import
@opindex allow-secret-key-import
This is an obsolete option and is not used anywhere.
@opindex gpgconf-test
This is more or less dummy action. However it parses the configuration
file and returns with failure if the configuration file would prevent
-@command{gpg} from startup. Thus it may be used to run a syntax check
+@command{@gpgname} from startup. Thus it may be used to run a syntax check
on the configuration file.
@end table
@item --show-photos
@itemx --no-show-photos
@opindex show-photos
-Causes @option{--list-keys}, @option{--list-sigs},
+Causes @option{--list-keys}, @option{--list-signatures},
@option{--list-public-keys}, @option{--list-secret-keys}, and verifying
a signature to also display the photo ID attached to the key, if
any. See also @option{--photo-viewer}. These options are deprecated. Use
@item --show-notation
@itemx --no-show-notation
@opindex show-notation
-Show signature notations in the @option{--list-sigs} or @option{--check-sigs} listings
+Show signature notations in the @option{--list-signatures} or @option{--check-signatures} listings
as well as when verifying a signature with a notation in it. These
options are deprecated. Use @option{--list-options [no-]show-notation}
and/or @option{--verify-options [no-]show-notation} instead.
@item --show-policy-url
@itemx --no-show-policy-url
@opindex show-policy-url
-Show policy URLs in the @option{--list-sigs} or @option{--check-sigs}
+Show policy URLs in the @option{--list-signatures} or @option{--check-signatures}
listings as well as when verifying a signature with a policy URL in
it. These options are deprecated. Use @option{--list-options
[no-]show-policy-url} and/or @option{--verify-options
@item gpg -se -r @code{Bob} @code{file}
sign and encrypt for user Bob
-@item gpg --clearsign @code{file}
-make a clear text signature
+@item gpg --clear-sign @code{file}
+make a cleartext signature
@item gpg -sb @code{file}
make a detached signature
@node Unattended Usage of GPG
@section Unattended Usage
-@command{gpg} is often used as a backend engine by other software. To help
+@command{@gpgname} is often used as a backend engine by other software. To help
with this a machine interface has been defined to have an unambiguous
way to do this. The options @option{--status-fd} and @option{--batch}
are almost always required for this.
@menu
+* Programmatic use of GnuPG:: Programmatic use of GnuPG
+* Ephemeral home directories:: Ephemeral home directories
+* The quick key manipulation interface:: The quick key manipulation interface
* Unattended GPG key generation:: Unattended key generation
@end menu
+@node Programmatic use of GnuPG
+@subsection Programmatic use of GnuPG
+
+Please consider using GPGME instead of calling @command{@gpgname}
+directly. GPGME offers a stable, backend-independent interface for
+many cryptographic operations. It supports OpenPGP and S/MIME, and
+also allows interaction with various GnuPG components.
+
+GPGME provides a C-API, and comes with bindings for C++, Qt, and
+Python. Bindings for other languages are available.
+
+@node Ephemeral home directories
+@subsection Ephemeral home directories
+
+Sometimes you want to contain effects of some operation, for example
+you want to import a key to inspect it, but you do not want this key
+to be added to your keyring. In earlier versions of GnuPG, it was
+possible to specify alternate keyring files for both public and secret
+keys. In modern GnuPG versions, however, we changed how secret keys
+are stored in order to better protect secret key material, and it was
+not possible to preserve this interface.
+
+The preferred way to do this is to use ephemeral home directories.
+This technique works across all versions of GnuPG.
+
+Create a temporary directory, create (or copy) a configuration that
+meets your needs, make @command{@gpgname} use this directory either
+using the environment variable @var{GNUPGHOME}, or the option
+@option{--homedir}. GPGME supports this too on a per-context basis,
+by modifying the engine info of contexts. Now execute whatever
+operation you like, import and export key material as necessary. Once
+finished, you can delete the directory. All GnuPG backend services
+that were started will detect this and shut down.
+
+@node The quick key manipulation interface
+@subsection The quick key manipulation interface
+
+Recent versions of GnuPG have an interface to manipulate keys without
+using the interactive command @option{--edit-key}. This interface was
+added mainly for the benefit of GPGME (please consider using GPGME,
+see the manual subsection ``Programmatic use of GnuPG''). This
+interface is described in the subsection ``How to manage your keys''.
+
@node Unattended GPG key generation
@subsection Unattended key generation
-The command @option{--gen-key} may be used along with the option
-@option{--batch} for unattended key generation. The parameters are
-either read from stdin or given as a file on the command line.
-The format of the parameter file is as follows:
+The command @option{--generate-key} may be used along with the option
+@option{--batch} for unattended key generation. This is the most
+flexible way of generating keys, but it is also the most complex one.
+Consider using the quick key manipulation interface described in the
+previous subsection ``The quick key manipulation interface''.
+
+The parameters for the key are either read from stdin or given as a
+file on the command line. The format of the parameter file is as
+follows:
@itemize @bullet
@item Text only, line length is limited to about 1000 characters.
the next @asis{Key-Type} parameter.
@item %pubring @var{filename}
-@itemx %secring @var{filename}
Do not write the key to the default or commandline given keyring but
to @var{filename}. This must be given before the first commit to take
place, duplicate specification of the same filename is ignored, the
last filename before a commit is used. The filename is used until a
new filename is used (at commit points) and all keys are written to
that file. If a new filename is given, this file is created (and
-overwrites an existing one). For GnuPG versions prior to 2.1, both
-control statements must be given. For GnuPG 2.1 and later
-@samp{%secring} is a no-op.
+overwrites an existing one).
+
+See the previous subsection ``Ephemeral home directories'' for a more
+robust way to contain side-effects.
+
+@item %secring @var{filename}
+This option is a no-op for GnuPG 2.1 and later.
+
+See the previous subsection ``Ephemeral home directories''.
@item %ask-passphrase
@itemx %no-ask-passphrase
@end table
@noindent
-Here is an example on how to create a key:
+Here is an example on how to create a key in an ephemeral home directory:
@smallexample
+$ export GNUPGHOME="$(mktemp -d)"
$ cat >foo <<EOF
%echo Generating a basic OpenPGP key
Key-Type: DSA
Name-Email: joe@@foo.bar
Expire-Date: 0
Passphrase: abc
- %pubring foo.pub
- %secring foo.sec
# Do a commit here, so that we can later print "done" :-)
%commit
%echo done
EOF
-$ @gpgname --batch --gen-key foo
+$ @gpgname --batch --generate-key foo
[...]
-$ @gpgname --no-default-keyring --secret-keyring ./foo.sec \
- --keyring ./foo.pub --list-secret-keys
-/home/wk/work/gnupg-stable/scratch/foo.sec
-------------------------------------------
-sec 1024D/915A878D 2000-03-09 Joe Tester (with stupid passphrase) <joe@@foo.bar>
-ssb 1024g/8F70E2C0 2000-03-09
+$ @gpgname --list-secret-keys
+/tmp/tmp.0NQxB74PEf/pubring.kbx
+-------------------------------
+sec dsa1024 2016-12-16 [SCA]
+ 768E895903FC1C44045C8CB95EEBDB71E9E849D0
+uid [ultimate] Joe Tester (with stupid passphrase) <joe@@foo.bar>
+ssb elg1024 2016-12-16 [E]
@end smallexample
-
@noindent
If you want to create a key with the default algorithms you would use
these parameters:
Name-Email: joe@@foo.bar
Expire-Date: 0
Passphrase: abc
- %pubring foo.pub
- %secring foo.sec
# Do a commit here, so that we can later print "done" :-)
%commit
%echo done
@subsection How to manage the certificates and keys
@table @gnupgtabopt
-@item --gen-key
+@item --generate-key
+@opindex generate-key
+@itemx --gen-key
@opindex gen-key
This command allows the creation of a certificate signing request or a
self-signed certificate. It is commonly used along with the
the certificates from there. This command utilizes the @command{gpg-agent}
and in turn the @command{scdaemon}.
-@item --passwd @var{user_id}
+@item --change-passphrase @var{user_id}
+@opindex change-passphrase
+@itemx --passwd @var{user_id}
@opindex passwd
Change the passphrase of the private key belonging to the certificate
specified as @var{user_id}. Note, that changing the passphrase/PIN of a
@node CSR and certificate creation
@subsection CSR and certificate creation
-The command @option{--gen-key} may be used along with the option
+The command @option{--generate-key} may be used along with the option
@option{--batch} to either create a certificate signing request (CSR)
or an X.509 certificate. This is controlled by a parameter file; the
format of this file is as follows:
@cartouche
@example
- $ gpgsm --gen-key >example.com.cert-req.pem
+ $ gpgsm --generate-key >example.com.cert-req.pem
Please select what kind of key you want:
(1) RSA
(2) Existing key
@item --check-options @var{component}
Check the options for the component @var{component}.
+@item --apply-profile @var{file}
+Apply the configuration settings listed in @var{file} to the
+configuration files. If @var{file} has no suffix and no slashes the
+command first tries to read a file with the suffix @code{.prf} from
+the the data directory (@code{gpgconf --list-dirs datadir}) before it
+reads the file verbatim. A profile is divided into sections using the
+bracketed component name. Each section then lists the option which
+shall go into the respective configuration file.
+
@item --apply-defaults
Update all configuration files with values taken from the global
configuration file (usually @file{/etc/gnupg/gpgconf.conf}).
if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
n = 0;
if( afx->buffer_len ) {
+ /* Copy the data from AFX->BUFFER to BUF. */
for(; n < size && afx->buffer_pos < afx->buffer_len; n++ )
buf[n++] = afx->buffer[afx->buffer_pos++];
if( afx->buffer_pos >= afx->buffer_len )
afx->buffer_len = 0;
}
+ /* If there is still space in BUF, read directly into it. */
for(; n < size; n++ ) {
if( (c=iobuf_get(a)) == -1 )
break;
buf[n] = c & 0xff;
}
if( !n )
+ /* We didn't get any data. EOF. */
rc = -1;
*ret_len = n;
}
parm->key_attr[keyno].nbits = strtoul (line+n+3, NULL, 10);
else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA)
- parm->key_attr[keyno].curve = openpgp_is_curve_supported (line+n, NULL);
+ parm->key_attr[keyno].curve = openpgp_is_curve_supported (line + n,
+ NULL, NULL);
}
else if (keywordlen == 12 && !memcmp (keyword, "PRIVATE-DO-", 11)
&& strchr("1234", keyword[11]))
care about the data returned. If the card has already been
initialized, this is a very fast command. The main reason we
need to do this here is to handle a card removed case so that an
- "l" command in --card-edit can be used to show ta newly inserted
+ "l" command in --edit-card can be used to show ta newly inserted
card. We request the openpgp card because that is what we
expect. */
rc = assuan_transact (agent_ctx, "SCD SERIALNO openpgp",
/* Request a string from the client over the command-fd. If GETBOOL
is set the function returns a static string (do not free) if the
- netered value was true or NULL if the entered value was false. */
+ entered value was true or NULL if the entered value was false. */
static char *
do_get_from_fd ( const char *keyword, int hidden, int getbool )
{
decrypt_messages (ctrl_t ctrl, int nfiles, char *files[])
{
IOBUF fp;
- armor_filter_context_t *afx = NULL;
progress_filter_context_t *pfx;
char *p, *output = NULL;
int rc=0,use_stdin=0;
{
if (use_armor_filter(fp))
{
- afx = new_armor_context ();
- push_armor_filter ( afx, fp );
+ armor_filter_context_t *afx = new_armor_context ();
+ rc = push_armor_filter (afx, fp);
+ if (rc)
+ log_error("failed to push armor filter");
+ release_armor_context (afx);
}
}
rc = proc_packets (ctrl,NULL, fp);
}
set_next_passphrase(NULL);
- release_armor_context (afx);
release_progress_context (pfx);
}
/*
* Receive a secret key from agent specified by HEXGRIP.
*
- * Since the key data from agant is encrypted, decrypt it by CIPHERHD.
- * Then, parse the decrypted key data in transfer format, and put
- * secret parameters into PK.
+ * Since the key data from the agent is encrypted, decrypt it using
+ * CIPHERHD context. Then, parse the decrypted key data into transfer
+ * format, and put secret parameters into PK.
*
* If CLEARTEXT is 0, store the secret key material
* passphrase-protected. Otherwise, store secret key material in the
new->uid = scopy_user_id (uid);
new->validity =
- get_validity (ctrl, &new->key, uid, NULL, 0) & TRUST_MASK;
+ get_validity (ctrl, new_keyblock, &new->key, uid, NULL, 0) & TRUST_MASK;
new->valid = 1;
if (! old->valid)
if (pk && ret_keyblock == NULL)
ret_keyblock = &keyblock;
- rc = lookup (ctx, ret_keyblock, &found_key, ctx->want_secret);
+ rc = lookup (ctx, ret_keyblock, pk ? &found_key : NULL, ctx->want_secret);
if (!rc && pk)
{
log_assert (found_key);
akl->type = AKL_LDAP;
else if (ascii_strcasecmp (tok, "keyserver") == 0)
akl->type = AKL_KEYSERVER;
-#ifdef USE_DNS_CERT
else if (ascii_strcasecmp (tok, "cert") == 0)
akl->type = AKL_CERT;
-#endif
else if (ascii_strcasecmp (tok, "pka") == 0)
akl->type = AKL_PKA;
else if (ascii_strcasecmp (tok, "dane") == 0)
aQuickAddUid,
aQuickAddKey,
aQuickRevUid,
+ aQuickSetExpire,
aListConfig,
aListGcryptConfig,
aGPGConfList,
oPrintDANERecords,
oTOFUDefaultPolicy,
oTOFUDBFormat,
+ oDefaultNewKeyAlgo,
oWeakDigest,
oUnwrap,
oOnlySignTextIDs,
ARGPARSE_group (300, N_("@Commands:\n ")),
ARGPARSE_c (aSign, "sign", N_("make a signature")),
- ARGPARSE_c (aClearsign, "clearsign", N_("make a clear text signature")),
+ ARGPARSE_c (aClearsign, "clear-sign", N_("make a clear text signature")),
+ ARGPARSE_c (aClearsign, "clearsign", "@"),
ARGPARSE_c (aDetachedSign, "detach-sign", N_("make a detached signature")),
ARGPARSE_c (aEncr, "encrypt", N_("encrypt data")),
ARGPARSE_c (aEncrFiles, "encrypt-files", "@"),
ARGPARSE_c (aVerifyFiles, "verify-files" , "@" ),
ARGPARSE_c (aListKeys, "list-keys", N_("list keys")),
ARGPARSE_c (aListKeys, "list-public-keys", "@" ),
- ARGPARSE_c (aListSigs, "list-sigs", N_("list keys and signatures")),
- ARGPARSE_c (aCheckKeys, "check-sigs",N_("list and check key signatures")),
+ ARGPARSE_c (aListSigs, "list-signatures", N_("list keys and signatures")),
+ ARGPARSE_c (aListSigs, "list-sigs", "@"),
+ ARGPARSE_c (aCheckKeys, "check-signatures",
+ N_("list and check key signatures")),
+ ARGPARSE_c (aCheckKeys, "check-sigs", "@"),
ARGPARSE_c (oFingerprint, "fingerprint", N_("list keys and fingerprints")),
ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")),
- ARGPARSE_c (aKeygen, "gen-key",
+ ARGPARSE_c (aKeygen, "generate-key",
N_("generate a new key pair")),
- ARGPARSE_c (aQuickKeygen, "quick-gen-key" ,
+ ARGPARSE_c (aKeygen, "gen-key", "@"),
+ ARGPARSE_c (aQuickKeygen, "quick-generate-key" ,
N_("quickly generate a new key pair")),
- ARGPARSE_c (aQuickAddUid, "quick-adduid",
+ ARGPARSE_c (aQuickKeygen, "quick-gen-key", "@"),
+ ARGPARSE_c (aQuickAddUid, "quick-add-uid",
N_("quickly add a new user-id")),
+ ARGPARSE_c (aQuickAddUid, "quick-adduid", "@"),
+ ARGPARSE_c (aQuickAddKey, "quick-add-key", "@"),
ARGPARSE_c (aQuickAddKey, "quick-addkey", "@"),
- ARGPARSE_c (aQuickRevUid, "quick-revuid",
+ ARGPARSE_c (aQuickRevUid, "quick-revoke-uid",
N_("quickly revoke a user-id")),
- ARGPARSE_c (aFullKeygen, "full-gen-key" ,
+ ARGPARSE_c (aQuickRevUid, "quick-revuid", "@"),
+ ARGPARSE_c (aQuickSetExpire, "quick-set-expire",
+ N_("quickly set a new expiration date")),
+ ARGPARSE_c (aFullKeygen, "full-generate-key" ,
N_("full featured key pair generation")),
- ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
+ ARGPARSE_c (aFullKeygen, "full-gen-key", "@"),
+ ARGPARSE_c (aGenRevoke, "generate-revocation",
+ N_("generate a revocation certificate")),
+ ARGPARSE_c (aGenRevoke, "gen-revoke", "@"),
ARGPARSE_c (aDeleteKeys,"delete-keys",
N_("remove keys from the public keyring")),
ARGPARSE_c (aDeleteSecretKeys, "delete-secret-keys",
ARGPARSE_c (aLSignKey, "lsign-key" ,N_("sign a key locally")),
ARGPARSE_c (aEditKey, "edit-key" ,N_("sign or edit a key")),
ARGPARSE_c (aEditKey, "key-edit" ,"@"),
- ARGPARSE_c (aPasswd, "passwd", N_("change a passphrase")),
+ ARGPARSE_c (aPasswd, "change-passphrase", N_("change a passphrase")),
+ ARGPARSE_c (aPasswd, "passwd", "@"),
+ ARGPARSE_c (aDesigRevoke, "generate-designated-revocation", "@"),
ARGPARSE_c (aDesigRevoke, "desig-revoke","@" ),
ARGPARSE_c (aExport, "export" , N_("export keys") ),
ARGPARSE_c (aSendKeys, "send-keys" , N_("export keys to a keyserver") ),
- ARGPARSE_c (aRecvKeys, "recv-keys" , N_("import keys from a keyserver") ),
+ ARGPARSE_c (aRecvKeys, "receive-keys" , N_("import keys from a keyserver") ),
+ ARGPARSE_c (aRecvKeys, "recv-keys" , "@"),
ARGPARSE_c (aSearchKeys, "search-keys" ,
N_("search for keys on a keyserver") ),
ARGPARSE_c (aRefreshKeys, "refresh-keys",
ARGPARSE_c (aFastImport, "fast-import", "@"),
#ifdef ENABLE_CARD_SUPPORT
ARGPARSE_c (aCardStatus, "card-status", N_("print the card status")),
- ARGPARSE_c (aCardEdit, "card-edit", N_("change data on a card")),
+ ARGPARSE_c (aCardEdit, "edit-card", N_("change data on a card")),
+ ARGPARSE_c (aCardEdit, "card-edit", "@"),
ARGPARSE_c (aChangePIN, "change-pin", N_("change a card's PIN")),
#endif
ARGPARSE_c (aListConfig, "list-config", "@"),
ARGPARSE_group (303, N_("@\nExamples:\n\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
- " --clearsign [file] make a clear text signature\n"
+ " --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n")),
ARGPARSE_s_n (oNoAllowMultipleMessages, "no-allow-multiple-messages", "@"),
ARGPARSE_s_n (oAllowWeakDigestAlgos, "allow-weak-digest-algos", "@"),
+ ARGPARSE_s_s (oDefaultNewKeyAlgo, "default-new-key-algo", "@"),
+
/* These two are aliases to help users of the PGP command line
product use gpg with minimal pain. Many commands are common
already as they seem to have borrowed commands from us. Now I'm
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_TRUST_VALUE , "trust" },
{ DBG_HASHING_VALUE, "hashing" },
- { DBG_CARD_IO_VALUE, "cardio" },
{ DBG_IPC_VALUE , "ipc" },
{ DBG_CLOCK_VALUE , "clock" },
{ DBG_LOOKUP_VALUE , "lookup" },
any=1;
}
+ if(show_all || ascii_strcasecmp (name, "compressname") == 0)
+ {
+ es_printf ("cfg:compressname:");
+ print_algo_names (check_compress_algo,
+ compress_algo_to_string);
+ es_printf ("\n");
+ any=1;
+ }
+
if (show_all || !ascii_strcasecmp(name,"ccid-reader-id"))
{
/* We ignore this for GnuPG 1.4 backward compatibility. */
es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("group:%lu:\n", GC_OPT_FLAG_NONE);
+ es_printf ("compliance:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "gnupg");
+ es_printf ("default-new-key-algo:%lu:\n", GC_OPT_FLAG_NONE);
/* The next one is an info only item and should match the macros at
the top of keygen.c */
es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT,
- "RSA-2048");
+ get_default_pubkey_algo ());
xfree (configfile_esc);
}
case aQuickAddUid:
case aQuickAddKey:
case aQuickRevUid:
+ case aQuickSetExpire:
case aExportOwnerTrust:
case aImportOwnerTrust:
case aRebuildKeydbCaches:
case oAllowSecretKeyImport: /* obsolete */ break;
case oTryAllSecrets: opt.try_all_secrets = 1; break;
case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break;
+
case oEnableSpecialFilenames:
- iobuf_enable_special_filenames (1);
+ enable_special_filenames ();
break;
+
case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break;
case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break;
case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break;
case oNoAutostart: opt.autostart = 0; break;
+ case oDefaultNewKeyAlgo:
+ opt.def_new_key_algo = pargs.r.ret_str;
+ break;
+
case oNoop: break;
default:
cmdname="--sign --encrypt";
break;
case aClearsign:
- cmdname="--clearsign";
+ cmdname="--clear-sign";
break;
case aDetachedSign:
cmdname="--detach-sign";
case aStore: /* only store the file */
if( argc > 1 )
- wrong_args(_("--store [filename]"));
+ wrong_args("--store [filename]");
if( (rc = encrypt_store(fname)) )
{
write_status_failure ("store", rc);
break;
case aSym: /* encrypt the given file only with the symmetric cipher */
if( argc > 1 )
- wrong_args(_("--symmetric [filename]"));
+ wrong_args("--symmetric [filename]");
if( (rc = encrypt_symmetric(fname)) )
{
write_status_failure ("symencrypt", rc);
else
{
if( argc > 1 )
- wrong_args(_("--encrypt [filename]"));
+ wrong_args("--encrypt [filename]");
if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 0, NULL, -1)) )
{
write_status_failure ("encrypt", rc);
might work with 7, but alas, I don't have a copy to test
with right now. */
if( argc > 1 )
- wrong_args(_("--symmetric --encrypt [filename]"));
+ wrong_args("--symmetric --encrypt [filename]");
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --encrypt"
" with --s2k-mode 0\n"));
}
else {
if( argc > 1 )
- wrong_args(_("--sign [filename]"));
+ wrong_args("--sign [filename]");
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
case aSignEncr: /* sign and encrypt the given file */
if( argc > 1 )
- wrong_args(_("--sign --encrypt [filename]"));
+ wrong_args("--sign --encrypt [filename]");
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
case aSignEncrSym: /* sign and encrypt the given file */
if( argc > 1 )
- wrong_args(_("--symmetric --sign --encrypt [filename]"));
+ wrong_args("--symmetric --sign --encrypt [filename]");
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --sign --encrypt"
" with --s2k-mode 0\n"));
case aSignSym: /* sign and conventionally encrypt the given file */
if (argc > 1)
- wrong_args(_("--sign --symmetric [filename]"));
+ wrong_args("--sign --symmetric [filename]");
rc = sign_symencrypt_file (ctrl, fname, locusr);
if (rc)
{
case aClearsign: /* make a clearsig */
if( argc > 1 )
- wrong_args(_("--clearsign [filename]"));
+ wrong_args("--clear-sign [filename]");
if( (rc = clearsign_file (ctrl, fname, locusr, NULL)) )
{
write_status_failure ("sign", rc);
- log_error("%s: clearsign failed: %s\n",
+ log_error("%s: clear-sign failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
break;
else
{
if( argc > 1 )
- wrong_args(_("--decrypt [filename]"));
+ wrong_args("--decrypt [filename]");
if( (rc = decrypt_message (ctrl, fname) ))
{
write_status_failure ("decrypt", rc);
case aSignKey:
if( argc != 1 )
- wrong_args(_("--sign-key user-id"));
+ wrong_args("--sign-key user-id");
/* fall through */
case aLSignKey:
if( argc != 1 )
- wrong_args(_("--lsign-key user-id"));
+ wrong_args("--lsign-key user-id");
/* fall through */
sl=NULL;
case aEditKey: /* Edit a key signature */
if( !argc )
- wrong_args(_("--edit-key user-id [commands]"));
+ wrong_args("--edit-key user-id [commands]");
username = make_username( fname );
if( argc > 1 ) {
sl = NULL;
case aPasswd:
if (argc != 1)
- wrong_args (_("--passwd <user-id>"));
+ wrong_args("--change-passphrase <user-id>");
else
{
username = make_username (fname);
const char *x_algo, *x_usage, *x_expire;
if (argc < 1 || argc > 4)
- wrong_args("--quick-gen-key USER-ID [ALGO [USAGE [EXPIRE]]]");
+ wrong_args("--quick-generate-key USER-ID [ALGO [USAGE [EXPIRE]]]");
username = make_username (fname);
argv++, argc--;
x_algo = "";
case aKeygen: /* generate a key */
if( opt.batch ) {
if( argc > 1 )
- wrong_args("--gen-key [parameterfile]");
+ wrong_args("--generate-key [parameterfile]");
generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
}
else {
if (opt.command_fd != -1 && argc)
{
if( argc > 1 )
- wrong_args("--gen-key [parameterfile]");
+ wrong_args("--generate-key [parameterfile]");
opt.batch = 1;
generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
}
else if (argc)
- wrong_args ("--gen-key");
+ wrong_args ("--generate-key");
else
generate_keypair (ctrl, 0, NULL, NULL, 0);
}
if (opt.batch)
{
if (argc > 1)
- wrong_args ("--full-gen-key [parameterfile]");
+ wrong_args ("--full-generate-key [parameterfile]");
generate_keypair (ctrl, 1, argc? *argv : NULL, NULL, 0);
}
else
{
if (argc)
- wrong_args("--full-gen-key");
+ wrong_args("--full-generate-key");
generate_keypair (ctrl, 1, NULL, NULL, 0);
}
break;
const char *uid, *newuid;
if (argc != 2)
- wrong_args ("--quick-adduid USER-ID NEW-USER-ID");
+ wrong_args ("--quick-add-uid USER-ID NEW-USER-ID");
uid = *argv++; argc--;
newuid = *argv++; argc--;
keyedit_quick_adduid (ctrl, uid, newuid);
const char *x_fpr, *x_algo, *x_usage, *x_expire;
if (argc < 1 || argc > 4)
- wrong_args ("--quick-addkey FINGERPRINT [ALGO [USAGE [EXPIRE]]]");
+ wrong_args ("--quick-add-key FINGERPRINT [ALGO [USAGE [EXPIRE]]]");
x_fpr = *argv++; argc--;
x_algo = "";
x_usage = "";
const char *uid, *uidtorev;
if (argc != 2)
- wrong_args ("--quick-revuid USER-ID USER-ID-TO-REVOKE");
+ wrong_args ("--quick-revoke-uid USER-ID USER-ID-TO-REVOKE");
uid = *argv++; argc--;
uidtorev = *argv++; argc--;
keyedit_quick_revuid (ctrl, uid, uidtorev);
}
break;
+ case aQuickSetExpire:
+ {
+ const char *x_fpr, *x_expire;
+
+ if (argc != 2)
+ wrong_args ("--quick-set-exipre FINGERPRINT EXPIRE");
+ x_fpr = *argv++; argc--;
+ x_expire = *argv++; argc--;
+ keyedit_quick_set_expire (ctrl, x_fpr, x_expire);
+ }
+ break;
+
case aFastImport:
opt.import_options |= IMPORT_FAST;
case aImport:
case aGenRevoke:
if( argc != 1 )
- wrong_args("--gen-revoke user-id");
+ wrong_args("--generate-revocation user-id");
username = make_username(*argv);
gen_revoke( username );
xfree( username );
case aDesigRevoke:
if (argc != 1)
- wrong_args ("--desig-revoke user-id");
+ wrong_args ("--generate-designated-revocation user-id");
username = make_username (*argv);
gen_desig_revoke (ctrl, username, locusr);
xfree (username);
merge_keys_and_selfsig (kb);
if (tofu_set_policy (ctrl, kb, policy))
g10_exit (1);
+
+ release_kbnode (kb);
}
tofu_end_batch_update (ctrl);
case aListPackets:
default:
if( argc > 1 )
- wrong_args(_("[filename]"));
+ wrong_args("[filename]");
/* Issue some output for the unix newbie */
if (!fname && !opt.outfile
&& gnupg_isatty (fileno (stdin))
break;
case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
case oEnableSpecialFilenames:
- iobuf_enable_special_filenames (1);
+ enable_special_filenames ();
break;
default : pargs.err = ARGPARSE_PRINT_ERROR; break;
}
}
int
-get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid)
+get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk,
+ PKT_user_id *uid)
{
(void)ctrl;
+ (void)kb;
(void)pk;
(void)uid;
return '?';
}
unsigned int
-get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid,
+get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid,
PKT_signature *sig, int may_ask)
{
(void)ctrl;
+ (void)kb;
(void)pk;
(void)uid;
(void)sig;
{
(void)ctrl;
}
+
+gpg_error_t
+tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
+{
+ (void) ctrl;
+ (void) kb;
+
+ return 0;
+}
struct import_stats_s *stats, int batch,
unsigned int options, int for_migration,
import_screener_t screener, void *screener_arg);
-static int import_revoke_cert (kbnode_t node, struct import_stats_s *stats);
+static int import_revoke_cert (ctrl_t ctrl,
+ kbnode_t node, struct import_stats_s *stats);
static int chk_self_sigs (kbnode_t keyblock, u32 *keyid, int *non_self);
static int delete_inv_parts (kbnode_t keyblock,
u32 *keyid, unsigned int options);
screener, screener_arg);
else if (keyblock->pkt->pkttype == PKT_SIGNATURE
&& keyblock->pkt->pkt.signature->sig_class == 0x20 )
- rc = import_revoke_cert (keyblock, stats);
+ rc = import_revoke_cert (ctrl, keyblock, stats);
else
{
log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype);
{
mod_key = 1;
/* KEYBLOCK_ORIG has been updated; write */
- rc = keydb_update_keyblock (hd, keyblock_orig);
+ rc = keydb_update_keyblock (ctrl, hd, keyblock_orig);
if (rc)
log_error (_("error writing keyring '%s': %s\n"),
keydb_get_resource_name (hd), gpg_strerror (rc) );
* Import a revocation certificate; this is a single signature packet.
*/
static int
-import_revoke_cert (kbnode_t node, struct import_stats_s *stats)
+import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
{
PKT_public_key *pk = NULL;
kbnode_t onode;
insert_kbnode( keyblock, clone_kbnode(node), 0 );
/* and write the keyblock back */
- rc = keydb_update_keyblock (hd, keyblock );
+ rc = keydb_update_keyblock (ctrl, hd, keyblock );
if (rc)
log_error (_("error writing keyring '%s': %s\n"),
keydb_get_resource_name (hd), gpg_strerror (rc) );
* you should use keydb_push_found_state and keydb_pop_found_state to
* save and restore it. */
gpg_error_t
-keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
+keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb)
{
gpg_error_t err;
PKT_public_key *pk;
if (err)
return err;
+#ifdef USE_TOFU
+ tofu_notice_key_changed (ctrl, kb);
+#endif
+
memset (&desc, 0, sizeof (desc));
fingerprint_from_pk (pk, desc.u.fpr, &len);
if (len == 20)
gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb);
/* Update the keyblock KB. */
-gpg_error_t keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
+gpg_error_t keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb);
/* Insert a keyblock into one of the underlying keyrings or keyboxes. */
gpg_error_t keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
return 0;
}
+/* Return whether PK is a primary key. */
+static int GPGRT_ATTR_UNUSED
+pk_is_primary (PKT_public_key *pk)
+{
+ return keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0;
+}
+
/* Copy the keyid in SRC to DEST and return DEST. */
u32 *keyid_copy (u32 *dest, const u32 *src);
static int menu_clean (KBNODE keyblock, int self_only);
static void menu_delkey (KBNODE pub_keyblock);
static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive);
-static int menu_expire (KBNODE pub_keyblock);
+static gpg_error_t menu_expire (kbnode_t pub_keyblock,
+ int force_mainkey, u32 newexpiration);
static int menu_changeusage (kbnode_t keyblock);
static int menu_backsign (KBNODE pub_keyblock);
static int menu_set_primary_uid (KBNODE pub_keyblock);
break;
case cmdEXPIRE:
- if (menu_expire (keyblock))
+ if (gpg_err_code (menu_expire (keyblock, 0, 0)) == GPG_ERR_TRUE)
{
merge_keys_and_selfsig (keyblock);
run_subkey_warnings = 1;
case cmdSAVE:
if (modified)
{
- err = keydb_update_keyblock (kdbhd, keyblock);
+ err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
if (err)
{
log_error (_("update failed: %s\n"), gpg_strerror (err));
if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring))
{
- err = keydb_update_keyblock (kdbhd, keyblock);
+ err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
if (err)
{
log_error (_("update failed: %s\n"), gpg_strerror (err));
gpg_strerror (err));
goto leave;
}
- err = keydb_update_keyblock (kdbhd, keyblock);
+ err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
if (err)
{
log_error (_("update failed: %s\n"), gpg_strerror (err));
if (modified)
{
- err = keydb_update_keyblock (kdbhd, keyblock);
+ err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
if (err)
{
log_error (_("update failed: %s\n"), gpg_strerror (err));
/* Store. */
if (modified)
{
- err = keydb_update_keyblock (kdbhd, keyblock);
+ err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
if (err)
{
log_error (_("update failed: %s\n"), gpg_strerror (err));
}
+/* Unattended expiration setting function for the main key.
+ *
+ */
+void
+keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr)
+{
+ gpg_error_t err;
+ kbnode_t keyblock;
+ KEYDB_HANDLE kdbhd;
+ int modified = 0;
+ PKT_public_key *pk;
+ u32 expire;
+
+#ifdef HAVE_W32_SYSTEM
+ /* See keyedit_menu for why we need this. */
+ check_trustdb_stale (ctrl);
+#endif
+
+ /* We require a fingerprint because only this uniquely identifies a
+ * key and may thus be used to select a key for unattended
+ * expiration setting. */
+ err = find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd);
+ if (err)
+ goto leave;
+
+ if (fix_keyblock (&keyblock))
+ modified++;
+
+ pk = keyblock->pkt->pkt.public_key;
+ if (pk->flags.revoked)
+ {
+ if (!opt.verbose)
+ show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+ log_error ("%s%s", _("Key is revoked."), "\n");
+ err = gpg_error (GPG_ERR_CERT_REVOKED);
+ goto leave;
+ }
+
+
+ expire = parse_expire_string (expirestr);
+ if (expire == (u32)-1 )
+ {
+ log_error (_("'%s' is not a valid expiration time\n"), expirestr);
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ if (expire)
+ expire += make_timestamp ();
+
+ /* Set the new expiration date. */
+ err = menu_expire (keyblock, 1, expire);
+ if (gpg_err_code (err) == GPG_ERR_TRUE)
+ modified = 1;
+ else if (err)
+ goto leave;
+ es_fflush (es_stdout);
+
+ /* Store. */
+ if (modified)
+ {
+ err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
+ if (err)
+ {
+ log_error (_("update failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ if (update_trust)
+ revalidation_mark ();
+ }
+ else
+ log_info (_("Key not changed so no update needed.\n"));
+
+ leave:
+ release_kbnode (keyblock);
+ keydb_release (kdbhd);
+ if (err)
+ write_status_error ("set_expire", err);
+}
+
+
\f
static void
tty_print_notations (int indent, PKT_signature * sig)
es_putc ('e', fp);
else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks))
{
- int trust = get_validity_info (ctrl, pk, NULL);
+ int trust = get_validity_info (ctrl, keyblock, pk, NULL);
if (trust == 'u')
ulti_hack = 1;
es_putc (trust, fp);
int uid_validity;
if (primary && !ulti_hack)
- uid_validity = get_validity_info (ctrl, primary, uid);
+ uid_validity = get_validity_info (ctrl, keyblock, primary, uid);
else
uid_validity = 'u';
es_fprintf (fp, "%c::::::::", uid_validity);
/* Show a warning once */
if (!did_warn
- && (get_validity (ctrl, pk, NULL, NULL, 0)
+ && (get_validity (ctrl, keyblock, pk, NULL, NULL, 0)
& TRUST_FLAG_PENDING_CHECK))
{
did_warn = 1;
}
-static int
-menu_expire (KBNODE pub_keyblock)
+/* With FORCE_MAINKEY cleared this function handles the interactive
+ * menu option "expire". With FORCE_MAINKEY set this functions only
+ * sets the expiration date of the primary key to NEWEXPIRATION and
+ * avoid all interactivity. Retirns 0 if nothing was done,
+ * GPG_ERR_TRUE if the key was modified, or any other error code. */
+static gpg_error_t
+menu_expire (kbnode_t pub_keyblock, int force_mainkey, u32 newexpiration)
{
- int n1, signumber, rc;
+ int signumber, rc;
u32 expiredate;
int mainkey = 0;
PKT_public_key *main_pk, *sub_pk;
PKT_user_id *uid;
- KBNODE node;
+ kbnode_t node;
u32 keyid[2];
- n1 = count_selected_keys (pub_keyblock);
- if (n1 > 1)
+ if (force_mainkey)
{
- if (!cpr_get_answer_is_yes
- ("keyedit.expire_multiple_subkeys.okay",
- _("Are you sure you want to change the"
- " expiration time for multiple subkeys? (y/N) ")))
- return 0;
+ mainkey = 1;
+ expiredate = newexpiration;
}
- else if (n1)
- tty_printf (_("Changing expiration time for a subkey.\n"));
else
{
- tty_printf (_("Changing expiration time for the primary key.\n"));
- mainkey = 1;
- no_primary_warning (pub_keyblock);
+ int n1 = count_selected_keys (pub_keyblock);
+ if (n1 > 1)
+ {
+ if (!cpr_get_answer_is_yes
+ ("keyedit.expire_multiple_subkeys.okay",
+ _("Are you sure you want to change the"
+ " expiration time for multiple subkeys? (y/N) ")))
+ return gpg_error (GPG_ERR_CANCELED);;
+ }
+ else if (n1)
+ tty_printf (_("Changing expiration time for a subkey.\n"));
+ else
+ {
+ tty_printf (_("Changing expiration time for the primary key.\n"));
+ mainkey = 1;
+ no_primary_warning (pub_keyblock);
+ }
+
+ expiredate = ask_expiredate ();
}
- expiredate = ask_expiredate ();
/* Now we can actually change the self-signature(s) */
main_pk = sub_pk = NULL;
}
else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
- if (node->flag & NODFLG_SELKEY)
+ if ((node->flag & NODFLG_SELKEY) && !force_mainkey)
{
sub_pk = node->pkt->pkt.public_key;
sub_pk->expiredate = expiredate;
&& (mainkey || sub_pk))
{
PKT_signature *sig = node->pkt->pkt.signature;
+
if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
&& ((mainkey && uid
&& uid->created && (sig->sig_class & ~3) == 0x10)
{
log_info
(_("You can't change the expiration date of a v3 key\n"));
- return 0;
+ return gpg_error (GPG_ERR_LEGACY_KEY);
}
if (mainkey)
{
log_error ("make_keysig_packet failed: %s\n",
gpg_strerror (rc));
- return 0;
+ if (gpg_err_code (rc) == GPG_ERR_TRUE)
+ rc = GPG_ERR_GENERAL;
+ return rc;
}
/* Replace the packet. */
}
update_trust = 1;
- return 1;
+ return gpg_error (GPG_ERR_TRUE);
}
/* If the trustdb has an entry for this key+uid then the
trustdb needs an update. */
if (!update_trust
- && ((get_validity (ctrl, pk, uid, NULL, 0) & TRUST_MASK)
+ && ((get_validity (ctrl, keyblock, pk, uid, NULL, 0)
+ & TRUST_MASK)
>= TRUST_UNDEFINED))
update_trust = 1;
#endif /*!NO_TRUST_MODELS*/
/* The default algorithms. If you change them remember to change them
also in gpg.c:gpgconf_list. You should also check that the value
- is inside the bounds enforced by ask_keysize and gen_xxx. */
-#define DEFAULT_STD_ALGO PUBKEY_ALGO_RSA
-#define DEFAULT_STD_KEYSIZE 2048
-#define DEFAULT_STD_KEYUSE (PUBKEY_USAGE_CERT|PUBKEY_USAGE_SIG)
-#define DEFAULT_STD_CURVE NULL
-#define DEFAULT_STD_SUBALGO PUBKEY_ALGO_RSA
-#define DEFAULT_STD_SUBKEYSIZE 2048
-#define DEFAULT_STD_SUBKEYUSE PUBKEY_USAGE_ENC
-#define DEFAULT_STD_SUBCURVE NULL
-
-#define FUTURE_STD_ALGO PUBKEY_ALGO_EDDSA
-#define FUTURE_STD_KEYSIZE 0
-#define FUTURE_STD_KEYUSE (PUBKEY_USAGE_CERT|PUBKEY_USAGE_SIG)
-#define FUTURE_STD_CURVE "Ed25519"
-#define FUTURE_STD_SUBALGO PUBKEY_ALGO_ECDH
-#define FUTURE_STD_SUBKEYSIZE 0
-#define FUTURE_STD_SUBKEYUSE PUBKEY_USAGE_ENC
-#define FUTURE_STD_SUBCURVE "Curve25519"
+ is inside the bounds enforced by ask_keysize and gen_xxx. See also
+ get_keysize_range which encodes the allowed ranges. */
+#define DEFAULT_STD_KEY_PARAM "rsa2048/cert,sign+rsa2048/encr"
+#define FUTURE_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr"
+
+/* When generating keys using the streamlined key generation dialog,
+ use this as a default expiration interval. */
+const char *default_expiration_interval = "2y";
/* Flag bits used during key generation. */
#define KEYGEN_FLAG_NO_PROTECTION 1
static gpg_error_t gen_card_key (int keyno, int algo, int is_primary,
kbnode_t pub_root, u32 *timestamp,
u32 expireval);
+static unsigned int get_keysize_range (int algo,
+ unsigned int *min, unsigned int *max);
+
+
+\f
+/* Return the algo string for a default new key. */
+const char *
+get_default_pubkey_algo (void)
+{
+ if (opt.def_new_key_algo)
+ {
+ if (*opt.def_new_key_algo && !strchr (opt.def_new_key_algo, ':'))
+ return opt.def_new_key_algo;
+ /* To avoid checking that option every time we delay that until
+ * here. The only thing we really need to make sure is that
+ * there is no colon in the string so that the --gpgconf-list
+ * command won't mess up its output. */
+ log_info (_("invalid value for option '%s'\n"), "--default-new-key-algo");
+ }
+ return DEFAULT_STD_KEY_PARAM;
+}
static void
{
gpg_error_t err;
gcry_sexp_t list, l2;
- char *curve;
+ char *curve = NULL;
int i;
const char *oidstr;
unsigned int nbits;
}
leave:
+ xfree (curve);
if (err)
{
for (i=0; i < 3; i++)
log_assert (is_RSA(algo));
if (!nbits)
- nbits = DEFAULT_STD_KEYSIZE;
+ nbits = get_keysize_range (algo, NULL, NULL);
if (nbits < 1024)
{
}
-static void
-get_keysize_range (int algo,
- unsigned int *min, unsigned int *def, unsigned int *max)
+static unsigned int
+get_keysize_range (int algo, unsigned int *min, unsigned int *max)
{
- *min = opt.compliance == CO_DE_VS ? 2048: 1024;
- *def = DEFAULT_STD_KEYSIZE;
- *max = 4096;
+ unsigned int def;
+ unsigned int dummy1, dummy2;
+
+ if (!min)
+ min = &dummy1;
+ if (!max)
+ max = &dummy2;
- /* Deviations from the standard values. */
switch(algo)
{
case PUBKEY_ALGO_DSA:
*min = opt.expert? 768 : 1024;
- *def=2048;
*max=3072;
+ def=2048;
break;
case PUBKEY_ALGO_ECDSA:
case PUBKEY_ALGO_ECDH:
*min=256;
- *def=256;
*max=521;
+ def=256;
break;
case PUBKEY_ALGO_EDDSA:
*min=255;
- *def=255;
*max=441;
+ def=255;
+ break;
+
+ default:
+ *min = opt.compliance == CO_DE_VS ? 2048: 1024;
+ *max = 4096;
+ def = 2048;
break;
}
+
+ return def;
}
int for_subkey = !!primary_keysize;
int autocomp = 0;
- get_keysize_range (algo, &min, &def, &max);
+ def = get_keysize_range (algo, &min, &max);
if (primary_keysize && !opt.expert)
{
}
+/* Helper for parse_key_parameter_string for one part of the
+ * specification string; i.e. ALGO/FLAGS. If STRING is NULL or empty
+ * success is returned. On error an error code is returned. Note
+ * that STRING may be modified by this function. NULL may be passed
+ * for any parameter. FOR_SUBKEY shall be true if this is used as a
+ * subkey. */
+static gpg_error_t
+parse_key_parameter_part (char *string, int for_subkey,
+ int *r_algo, unsigned int *r_size,
+ unsigned int *r_keyuse,
+ char const **r_curve)
+{
+ char *flags;
+ int algo;
+ char *endp;
+ const char *curve = NULL;
+ int ecdh_or_ecdsa = 0;
+ unsigned int size;
+ int keyuse;
+ int i;
+ const char *s;
+
+ if (!string || !*string)
+ return 0; /* Success. */
+
+ flags = strchr (string, '/');
+ if (flags)
+ *flags++ = 0;
+
+ algo = 0;
+ if (strlen (string) >= 3 && (digitp (string+3) || !string[3]))
+ {
+ if (!ascii_memcasecmp (string, "rsa", 3))
+ algo = PUBKEY_ALGO_RSA;
+ else if (!ascii_memcasecmp (string, "dsa", 3))
+ algo = PUBKEY_ALGO_DSA;
+ else if (!ascii_memcasecmp (string, "elg", 3))
+ algo = PUBKEY_ALGO_ELGAMAL_E;
+ }
+ if (algo)
+ {
+ if (!string[3])
+ size = get_keysize_range (algo, NULL, NULL);
+ else
+ {
+ size = strtoul (string+3, &endp, 10);
+ if (size < 512 || size > 16384 || *endp)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ }
+ else if ((curve = openpgp_is_curve_supported (string, &algo, &size)))
+ {
+ if (!algo)
+ {
+ algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm. */
+ ecdh_or_ecdsa = 1; /* We may need to switch the algo. */
+ }
+ }
+ else
+ return gpg_error (GPG_ERR_UNKNOWN_CURVE);
+
+ /* Parse the flags. */
+ keyuse = 0;
+ if (flags)
+ {
+ char **tokens = NULL;
+
+ tokens = strtokenize (flags, ",");
+ if (!tokens)
+ return gpg_error_from_syserror ();
+
+ for (i=0; (s = tokens[i]); i++)
+ {
+ if (!*s)
+ ;
+ else if (!ascii_strcasecmp (s, "sign"))
+ keyuse |= PUBKEY_USAGE_SIG;
+ else if (!ascii_strcasecmp (s, "encrypt")
+ || !ascii_strcasecmp (s, "encr"))
+ keyuse |= PUBKEY_USAGE_ENC;
+ else if (!ascii_strcasecmp (s, "auth"))
+ keyuse |= PUBKEY_USAGE_AUTH;
+ else if (!ascii_strcasecmp (s, "cert"))
+ keyuse |= PUBKEY_USAGE_CERT;
+ else if (!ascii_strcasecmp (s, "ecdsa"))
+ {
+ if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA)
+ algo = PUBKEY_ALGO_ECDSA;
+ else
+ {
+ xfree (tokens);
+ return gpg_error (GPG_ERR_INV_FLAG);
+ }
+ ecdh_or_ecdsa = 0;
+ }
+ else if (!ascii_strcasecmp (s, "ecdh"))
+ {
+ if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA)
+ algo = PUBKEY_ALGO_ECDH;
+ else
+ {
+ xfree (tokens);
+ return gpg_error (GPG_ERR_INV_FLAG);
+ }
+ ecdh_or_ecdsa = 0;
+ }
+ else if (!ascii_strcasecmp (s, "eddsa"))
+ {
+ /* Not required but we allow it for consistency. */
+ if (algo == PUBKEY_ALGO_EDDSA)
+ ;
+ else
+ {
+ xfree (tokens);
+ return gpg_error (GPG_ERR_INV_FLAG);
+ }
+ }
+ else
+ {
+ xfree (tokens);
+ return gpg_error (GPG_ERR_UNKNOWN_FLAG);
+ }
+ }
+
+ xfree (tokens);
+ }
+
+ /* If not yet decided switch between ecdh and ecdsa. */
+ if (ecdh_or_ecdsa && keyuse)
+ algo = (keyuse & PUBKEY_USAGE_ENC)? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA;
+ else if (ecdh_or_ecdsa)
+ algo = for_subkey? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA;
+
+ /* Set or fix key usage. */
+ if (!keyuse)
+ {
+ if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA
+ || algo == PUBKEY_ALGO_DSA)
+ keyuse = PUBKEY_USAGE_SIG;
+ else if (algo == PUBKEY_ALGO_RSA)
+ keyuse = for_subkey? PUBKEY_USAGE_ENC : PUBKEY_USAGE_SIG;
+ else
+ keyuse = PUBKEY_USAGE_ENC;
+ }
+ else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA
+ || algo == PUBKEY_ALGO_DSA)
+ {
+ keyuse &= ~PUBKEY_USAGE_ENC; /* Forbid encryption. */
+ }
+ else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ELGAMAL_E)
+ {
+ keyuse = PUBKEY_USAGE_ENC; /* Allow only encryption. */
+ }
+
+ /* Make sure a primary key can certify. */
+ if (!for_subkey)
+ keyuse |= PUBKEY_USAGE_CERT;
+
+ /* Check that usage is actually possible. */
+ if (/**/((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT))
+ && !pubkey_get_nsig (algo))
+ || ((keyuse & PUBKEY_USAGE_ENC)
+ && !pubkey_get_nenc (algo))
+ || (for_subkey && (keyuse & PUBKEY_USAGE_CERT)))
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+
+ /* Return values. */
+ if (r_algo)
+ *r_algo = algo;
+ if (r_size)
+ {
+ unsigned int min, def, max;
+
+ /* Make sure the keysize is in the allowed range. */
+ def = get_keysize_range (algo, &min, &max);
+ if (!size)
+ size = def;
+ else if (size < min)
+ size = min;
+ else if (size > max)
+ size = max;
+
+ *r_size = fixup_keysize (size, algo, 1);
+ }
+ if (r_keyuse)
+ *r_keyuse = keyuse;
+ if (r_curve)
+ *r_curve = curve;
+
+ return 0;
+}
+
+/* Parse and return the standard key generation parameter.
+ * The string is expected to be in this format:
+ *
+ * ALGO[/FLAGS][+SUBALGO[/FLAGS]]
+ *
+ * Here ALGO is a string in the same format as printed by the
+ * keylisting. For example:
+ *
+ * rsa3072 := RSA with 3072 bit.
+ * dsa2048 := DSA with 2048 bit.
+ * elg2048 := Elgamal with 2048 bit.
+ * ed25519 := EDDSA using curve Ed25519.
+ * cv25519 := ECDH using curve Curve25519.
+ * nistp256:= ECDSA or ECDH using curve NIST P-256
+ *
+ * All strings with an unknown prefix are considered an elliptic
+ * curve. Curves which have no implicit algorithm require that FLAGS
+ * is given to select whether ECDSA or ECDH is used; this can eoither
+ * be done using an algorithm keyword or usage keywords.
+ *
+ * FLAGS is a comma delimited string of keywords:
+ *
+ * cert := Allow usage Certify
+ * sign := Allow usage Sign
+ * encr := Allow usage Encrypt
+ * auth := Allow usage Authentication
+ * encrypt := Alias for "encr"
+ * ecdsa := Use algorithm ECDSA.
+ * eddsa := Use algorithm EdDSA.
+ * ecdh := Use algorithm ECDH.
+ *
+ * There are several defaults and fallbacks depending on the
+ * algorithm. PART can be used to select which part of STRING is
+ * used:
+ * -1 := Both parts
+ * 0 := Only the part of the primary key
+ * 1 := If there is one part parse that one, if there are
+ * two parts parse the second part. Always return
+ * in the args for the primary key (R_ALGO,....).
+ *
+ */
+gpg_error_t
+parse_key_parameter_string (const char *string, int part,
+ int *r_algo, unsigned int *r_size,
+ unsigned *r_keyuse,
+ char const **r_curve,
+ int *r_subalgo, unsigned int *r_subsize,
+ unsigned *r_subkeyuse,
+ char const **r_subcurve)
+{
+ gpg_error_t err = 0;
+ char *primary, *secondary;
+
+ if (r_algo)
+ *r_algo = 0;
+ if (r_size)
+ *r_size = 0;
+ if (r_keyuse)
+ *r_keyuse = 0;
+ if (r_curve)
+ *r_curve = NULL;
+ if (r_subalgo)
+ *r_subalgo = 0;
+ if (r_subsize)
+ *r_subsize = 0;
+ if (r_subkeyuse)
+ *r_subkeyuse = 0;
+ if (r_subcurve)
+ *r_subcurve = NULL;
+
+ if (!string || !*string
+ || !strcmp (string, "default") || !strcmp (string, "-"))
+ string = get_default_pubkey_algo ();
+ else if (!strcmp (string, "future-default"))
+ string = FUTURE_STD_KEY_PARAM;
+
+ primary = xstrdup (string);
+ secondary = strchr (primary, '+');
+ if (secondary)
+ *secondary++ = 0;
+ if (part == -1 || part == 0)
+ {
+ err = parse_key_parameter_part (primary, 0, r_algo, r_size,
+ r_keyuse, r_curve);
+ if (!err && part == -1)
+ err = parse_key_parameter_part (secondary, 1, r_subalgo, r_subsize,
+ r_subkeyuse, r_subcurve);
+ }
+ else if (part == 1)
+ {
+ /* If we have SECONDARY, use that part. If there is only one
+ * part consider this to be the subkey algo. */
+ err = parse_key_parameter_part (secondary? secondary : primary, 1,
+ r_algo, r_size, r_keyuse, r_curve);
+ }
+
+ xfree (primary);
+
+ return err;
+}
+
+
+
/* Append R to the linked list PARA. */
static void
append_to_parameter (struct para_data_s *para, struct para_data_s *r)
if (!ascii_strcasecmp (r->u.value, "default"))
{
/* Note: If you change this default algo, remember to change it
- also in gpg.c:gpgconf_list. */
- i = DEFAULT_STD_ALGO;
+ * also in gpg.c:gpgconf_list. */
+ /* FIXME: We only allow the algo here and have a separate thing
+ * for the curve etc. That is a ugly but demanded for backward
+ * compatibility with the batch key generation. It would be
+ * better to make full use of parse_key_parameter_string. */
+ parse_key_parameter_string (NULL, 0,
+ &i, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+
if (r_default)
*r_default = 1;
}
/* Parse a usage string. The usage keywords "auth", "sign", "encr"
- * may be elimited by space, tab, or comma. On error -1 is returned
- * instead of the usage flags/ */
+ * may be delimited by space, tab, or comma. On error -1 is returned
+ * instead of the usage flags. */
static int
parse_usagestr (const char *usagestr)
{
}
}
+ if (!*expirestr || strcmp (expirestr, "-") == 0)
+ expirestr = default_expiration_interval;
if ((!*algostr || !strcmp (algostr, "default")
|| !strcmp (algostr, "future-default"))
&& (!*usagestr || !strcmp (usagestr, "default")
|| !strcmp (usagestr, "-")))
{
- if (!strcmp (algostr, "future-default"))
- {
- para = quickgen_set_para (para, 0,
- FUTURE_STD_ALGO, FUTURE_STD_KEYSIZE,
- FUTURE_STD_CURVE, 0);
- para = quickgen_set_para (para, 1,
- FUTURE_STD_SUBALGO, FUTURE_STD_SUBKEYSIZE,
- FUTURE_STD_SUBCURVE, 0);
- }
- else
+ /* Use default key parameters. */
+ int algo, subalgo;
+ unsigned int size, subsize;
+ unsigned int keyuse, subkeyuse;
+ const char *curve, *subcurve;
+
+ err = parse_key_parameter_string (algostr, -1,
+ &algo, &size, &keyuse, &curve,
+ &subalgo, &subsize, &subkeyuse,
+ &subcurve);
+ if (err)
{
- para = quickgen_set_para (para, 0,
- DEFAULT_STD_ALGO, DEFAULT_STD_KEYSIZE,
- DEFAULT_STD_CURVE, 0);
- para = quickgen_set_para (para, 1,
- DEFAULT_STD_SUBALGO, DEFAULT_STD_SUBKEYSIZE,
- DEFAULT_STD_SUBCURVE, 0);
+ log_error (_("Key generation failed: %s\n"), gpg_strerror (err));
+ goto leave;
}
+ para = quickgen_set_para (para, 0, algo, size, curve, keyuse);
+ if (subalgo)
+ para = quickgen_set_para (para, 1,
+ subalgo, subsize, subcurve, subkeyuse);
+
if (*expirestr)
{
u32 expire;
generate_keypair (ctrl_t ctrl, int full, const char *fname,
const char *card_serialno, int card_backup_key)
{
+ gpg_error_t err;
unsigned int nbits;
char *uid = NULL;
int algo;
if (card_serialno)
{
#ifdef ENABLE_CARD_SUPPORT
- gpg_error_t err;
struct agent_card_info_s info;
memset (&info, 0, sizeof (info));
err = agent_scd_getattr ("KEY-ATTR", &info);
if (err)
{
- log_error (_("error getting current key info: %s\n"), gpg_strerror (err));
+ log_error (_("error getting current key info: %s\n"),
+ gpg_strerror (err));
return;
}
}
else /* Default key generation. */
{
+ int subalgo;
+ unsigned int size, subsize;
+ unsigned int keyuse, subkeyuse;
+ const char *curve, *subcurve;
+
tty_printf ( _("Note: Use \"%s %s\""
" for a full featured key generation dialog.\n"),
#if USE_GPG2_HACK
#else
GPG_NAME
#endif
- , "--full-gen-key" );
- para = quickgen_set_para (para, 0,
- DEFAULT_STD_ALGO, DEFAULT_STD_KEYSIZE,
- DEFAULT_STD_CURVE, 0);
- para = quickgen_set_para (para, 1,
- DEFAULT_STD_SUBALGO, DEFAULT_STD_SUBKEYSIZE,
- DEFAULT_STD_SUBCURVE, 0);
+ , "--full-generate-key" );
+
+ err = parse_key_parameter_string (NULL, -1,
+ &algo, &size, &keyuse, &curve,
+ &subalgo, &subsize,
+ &subkeyuse, &subcurve);
+ if (err)
+ {
+ log_error (_("Key generation failed: %s\n"), gpg_strerror (err));
+ return;
+ }
+ para = quickgen_set_para (para, 0, algo, size, curve, keyuse);
+ if (subalgo)
+ para = quickgen_set_para (para, 1,
+ subalgo, subsize, subcurve, subkeyuse);
+
+
}
- expire = full? ask_expire_interval (0, NULL) : 0;
+ expire = full? ask_expire_interval (0, NULL)
+ : parse_expire_string (default_expiration_interval);
r = xcalloc (1, sizeof *r + 20);
r->key = pKEYEXPIRE;
r->u.expire = expire;
int *r_algo, unsigned int *r_usage, u32 *r_expire,
unsigned int *r_nbits, char **r_curve)
{
+ gpg_error_t err;
int algo;
unsigned int use, nbits;
u32 expire;
int wantuse;
- unsigned int min, def, max;
const char *curve = NULL;
- int eccalgo = 0;
*r_curve = NULL;
nbits = 0;
+
/* Parse the algo string. */
- if (!algostr || !*algostr
- || !strcmp (algostr, "default") || !strcmp (algostr, "-"))
- {
- algo = for_subkey? DEFAULT_STD_SUBALGO : DEFAULT_STD_ALGO;
- use = for_subkey? DEFAULT_STD_SUBKEYUSE : DEFAULT_STD_KEYUSE;
- nbits = for_subkey? DEFAULT_STD_SUBKEYSIZE : DEFAULT_STD_KEYSIZE;
- curve = for_subkey? DEFAULT_STD_SUBCURVE : DEFAULT_STD_CURVE;
- }
- else if (!strcmp (algostr, "future-default"))
- {
- algo = for_subkey? FUTURE_STD_SUBALGO : FUTURE_STD_ALGO;
- use = for_subkey? FUTURE_STD_SUBKEYUSE : FUTURE_STD_KEYUSE;
- nbits = for_subkey? FUTURE_STD_SUBKEYSIZE : FUTURE_STD_KEYSIZE;
- curve = for_subkey? FUTURE_STD_SUBCURVE : FUTURE_STD_CURVE;
- }
- else if (*algostr == '&' && strlen (algostr) == 41)
+ if (algostr && *algostr == '&' && strlen (algostr) == 41)
{
/* Take algo from existing key. */
algo = check_keygrip (ctrl, algostr+1);
/* FIXME: We need the curve name as well. */
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
- else if (!strncmp (algostr, "rsa", 3))
- {
- algo = PUBKEY_ALGO_RSA;
- use = for_subkey? DEFAULT_STD_SUBKEYUSE : DEFAULT_STD_KEYUSE;
- if (algostr[3])
- nbits = atoi (algostr + 3);
- }
- else if (!strncmp (algostr, "elg", 3))
- {
- algo = PUBKEY_ALGO_ELGAMAL_E;
- use = PUBKEY_USAGE_ENC;
- if (algostr[3])
- nbits = atoi (algostr + 3);
- }
- else if (!strncmp (algostr, "dsa", 3))
- {
- algo = PUBKEY_ALGO_DSA;
- use = PUBKEY_USAGE_SIG;
- if (algostr[3])
- nbits = atoi (algostr + 3);
- }
- else if ((curve = openpgp_is_curve_supported (algostr, &algo)))
- {
- if (!algo)
- {
- algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm. */
- eccalgo = 1; /* Remember - we may need to fix it up. */
- }
- if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA)
- use = PUBKEY_USAGE_SIG;
- else
- use = PUBKEY_USAGE_ENC;
- }
- else
- return gpg_error (GPG_ERR_UNKNOWN_CURVE);
+ err = parse_key_parameter_string (algostr, for_subkey? 1 : 0,
+ &algo, &nbits, &use, &curve,
+ NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
/* Parse the usage string. */
if (!usagestr || !*usagestr
|| !strcmp (usagestr, "default") || !strcmp (usagestr, "-"))
- ; /* Keep default usage */
+ ; /* Keep usage from parse_key_parameter_string. */
else if ((wantuse = parse_usagestr (usagestr)) != -1)
- {
- use = wantuse;
- if (eccalgo && !(use & PUBKEY_USAGE_ENC))
- algo = PUBKEY_ALGO_ECDSA; /* Switch from ECDH to ECDSA. */
- }
+ use = wantuse;
else
return gpg_error (GPG_ERR_INV_VALUE);
if (!for_subkey)
use |= PUBKEY_USAGE_CERT;
- /* Check that usage is possible. */
+ /* Check that usage is possible. NB: We have the same check in
+ * parse_key_parameter_string but need it here again in case the
+ * separate usage value has been given. */
if (/**/((use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT))
&& !pubkey_get_nsig (algo))
|| ((use & PUBKEY_USAGE_ENC)
if (expire == (u32)-1 )
return gpg_error (GPG_ERR_INV_VALUE);
- /* Make sure the keysize is in the allowed range. */
- get_keysize_range (algo, &min, &def, &max);
- if (!nbits)
- nbits = def;
- else if (nbits < min)
- nbits = min;
- else if (nbits > max)
- nbits = max;
-
- nbits = fixup_keysize (nbits, algo, 1);
-
if (curve)
{
*r_curve = xtrystrdup (curve);
trustletter_print = 0;
else
{
- trustletter = get_validity_info (ctrl, pk, NULL);
+ trustletter = get_validity_info (ctrl, keyblock, pk, NULL);
if (trustletter == 'u')
ulti_hack = 1;
trustletter_print = trustletter;
else if (ulti_hack)
uid_validity = 'u';
else
- uid_validity = get_validity_info (ctrl, pk, uid);
+ uid_validity = get_validity_info (ctrl, keyblock, pk, uid);
es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout);
if (uid_validity)
struct keyserver_spec *keyserver;
strlist_t list=NULL;
int rc,hostlen=1;
-#ifdef USE_DNS_SRV
struct srventry *srvlist=NULL;
int srvcount,i;
char srvname[MAXDNAME];
-#endif
/* Parse out the domain */
domain=strrchr(name,'@');
keyserver->host=xmalloc(1);
keyserver->host[0]='\0';
-#ifdef USE_DNS_SRV
snprintf(srvname,MAXDNAME,"_pgpkey-ldap._tcp.%s",domain);
FIXME("network related - move to dirmngr or drop the code");
}
free(srvlist);
-#endif
/* If all else fails, do the PGP Universal trick of
ldap://keys.(domain) */
const char *uidtorev);
void keyedit_quick_sign (ctrl_t ctrl, const char *fpr,
strlist_t uids, strlist_t locusr, int local);
+void keyedit_quick_set_expire (ctrl_t ctrl,
+ const char *fpr, const char *expirestr);
void show_basic_key_info (KBNODE keyblock);
/*-- keygen.c --*/
+const char *get_default_pubkey_algo (void);
u32 parse_expire_string(const char *string);
u32 ask_expire_interval(int object,const char *def_expire);
u32 ask_expiredate(void);
keyid_from_pk( pk, keyid );
if (pk->flags.primary)
- c->trustletter = (opt.fast_list_mode?
- 0 : get_validity_info (c->ctrl, pk, NULL));
+ c->trustletter = (opt.fast_list_mode
+ ? 0
+ : get_validity_info
+ (c->ctrl,
+ node->pkt->pkttype == PKT_PUBLIC_KEY
+ ? node : NULL,
+ pk, NULL));
es_printf ("%s:", pk->flags.primary? "pub":"sub" );
if (c->trustletter)
es_putc (c->trustletter, es_stdout);
does not print a LF we need to compute the validity
before calling that function. */
if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY))
- valid = get_validity (c->ctrl, mainpk, un->pkt->pkt.user_id,
- NULL, 0);
+ valid = get_validity (c->ctrl, keyblock, mainpk,
+ un->pkt->pkt.user_id, NULL, 0);
else
valid = 0; /* Not used. */
actually ask the user to update any trust
information. */
valid = (trust_value_to_string
- (get_validity (c->ctrl, mainpk,
+ (get_validity (c->ctrl, keyblock, mainpk,
un->pkt->pkt.user_id, NULL, 0)));
log_printf (" [%s]\n",valid);
}
snprintf (buf, sizeof buf, "%08lX%08lX %d %d %02x %lu %d",
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
sig->pubkey_algo, sig->digest_algo,
- sig->sig_class, (ulong)sig->timestamp, rc);
+ sig->sig_class, (ulong)sig->timestamp, gpg_err_code (rc));
write_status_text (STATUS_ERRSIG, buf);
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
{
const char *agent_program;
const char *dirmngr_program;
+ const char *def_new_key_algo;
+
/* Options to be passed to the gpg-agent */
session_env_t session_env;
char *lc_ctype;
#define DBG_TRUST_VALUE 256 /* debug the trustdb */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_IPC_VALUE 1024 /* debug assuan communication */
-#define DBG_CARD_IO_VALUE 2048 /* debug smart card I/O. */
#define DBG_CLOCK_VALUE 4096
#define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */
#define DBG_EXTPROG_VALUE 16384 /* debug external program calls */
#define DBG_TRUST (opt.debug & DBG_TRUST_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
-#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
#define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE)
#define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE)
typedef struct {
/* RFC 4880: this must be 4. */
byte version;
- /* The cipher algorithm used. */
+ /* The cipher algorithm used to encrypt the session key. (This may
+ be different from the algorithm that is used to encrypt the SED
+ packet.) */
byte cipher_algo;
/* The string-to-key specifier. */
STRING2KEY s2k;
struct user_attribute *attribs;
int numattribs;
/* If this is not NULL, the packet is a user attribute rather than a
- user id. (Serialized.) */
+ user id (See RFC 4880 5.12). (Serialized.) */
byte *attrib_data;
/* The length of ATTRIB_DATA. */
unsigned long attrib_len;
decoded values are given as PKGTYPE and PKTLEN.
If the packet is a partial body length packet (RFC 4880, Section
- 4.2.2.4), then iobuf_set_partial_block_mode should already have
- been called on INP and PARTIAL should be set.
+ 4.2.2.4), then iobuf_set_partial_block_modeiobuf_set_partial_block_mode
+ should already have been called on INP and PARTIAL should be set.
If PARTIAL is set or PKTLEN is 0 and PKTTYPE is PKT_COMPRESSED,
copy until the first EOF is encountered on INP.
/*
- * This packet is internally generated by us (ibn armor.c) to transfer
+ * This packet is internally generated by us (in armor.c) to transfer
* some information to the lower layer. To make sure that this packet
* is really a GPG faked one and not one coming from outside, we
* first check that there is a unique tag in it.
log_assert (create && !nocache);
/* This is used for the old rfc1991 mode
* Note: This must match the code in encode.c with opt.rfc1991 set */
+ memset (&help_s2k, 0, sizeof (help_s2k));
s2k = &help_s2k;
- s2k->mode = 0;
s2k->hash_algo = S2K_DIGEST_ALGO;
}
memset (&args, 0, sizeof(args));
args.pk = pk;
- args.validity_info = get_validity_info (ctrl, pk, uid);
+ args.validity_info = get_validity_info (ctrl, NULL, pk, uid);
args.validity_string = get_validity_string (ctrl, pk, uid);
namehash_from_uid (uid);
args.namehash = uid->namehash;
if ((trustlevel & TRUST_MASK) == TRUST_NEVER)
tty_printf(
- _("This key has is bad! It has been marked as untrusted! If you\n"
+ _("This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"));
else
log_info(_("WARNING: this key might be revoked (revocation key"
" not present)\n"));
- trustlevel = get_validity (ctrl, pk, NULL, sig, 1);
+ trustlevel = get_validity (ctrl, NULL, pk, NULL, sig, 1);
if ( (trustlevel & TRUST_FLAG_REVOKED) )
{
{
int trustlevel;
- trustlevel = get_validity (ctrl, pk, pk->user_id, NULL, 1);
+ trustlevel = get_validity (ctrl, NULL, pk, pk->user_id, NULL, 1);
if ( (trustlevel & TRUST_FLAG_DISABLED) )
{
/* Key has been disabled. */
{ /* Check validity of this key. */
int trustlevel;
- trustlevel = get_validity (ctrl, pk, pk->user_id, NULL, 1);
+ trustlevel =
+ get_validity (ctrl, NULL, pk, pk->user_id, NULL, 1);
if ( (trustlevel & TRUST_FLAG_DISABLED) )
{
tty_printf (_("Public key is disabled.\n") );
}
else if (iobuf_is_pipe_filename (fname) || !*fname)
{
- /* No filename or "-" given; write to stdout. */
- fp = es_stdout;
- es_set_binary (fp);
+ /* Special file name, no filename, or "-" given; write to the
+ * file descriptor or to stdout. */
+ int fd;
+ char xname[64];
+
+ fd = check_special_filename (fname, 1, 0);
+ if (fd == -1)
+ {
+ /* Not a special filename, thus we want stdout. */
+ fp = es_stdout;
+ es_set_binary (fp);
+ }
+ else if (!(fp = es_fdopen_nc (fd, "wb")))
+ {
+ err = gpg_error_from_syserror ();
+ snprintf (xname, sizeof xname, "[fd %d]", fd);
+ log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err));
+ goto leave;
+ }
}
else
{
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
- "of the gpg command \"--gen-revoke\" in the GnuPG manual."),
+ "of the gpg command \"--generate-revocation\" in the "
+ "GnuPG manual."),
_("To avoid an accidental use of this file, a colon has been inserted\n"
"before the 5 dashes below. Remove this colon with a text editor\n"
"before importing and publishing this revocation certificate."));
sig->digest_algo = mdalgo;
sig->digest_start[0] = dp[0];
sig->digest_start[1] = dp[1];
+ mpi_release (sig->data[0]);
sig->data[0] = NULL;
+ mpi_release (sig->data[1]);
sig->data[1] = NULL;
/****************
- * Create a signature packet for the given public key certificate and
- * the user id and return it in ret_sig. User signature class SIGCLASS
- * user-id is not used (and may be NULL if sigclass is 0x20) If
- * DIGEST_ALGO is 0 the function selects an appropriate one.
- * SIGVERSION gives the minimal required signature packet version;
- * this is needed so that special properties like local sign are not
- * applied (actually: dropped) when a v3 key is used. TIMESTAMP is
- * the timestamp to use for the signature. 0 means "now" */
+ * Create a v4 signature in *RET_SIG.
+ *
+ * PK is the primary key to sign (required for all sigs)
+ * UID is the user id to sign (required for 0x10..0x13, 0x30)
+ * SUBPK is subkey to sign (required for 0x18, 0x19, 0x28)
+ *
+ * PKSK is the signing key
+ *
+ * SIGCLASS is the type of signature to create.
+ *
+ * DIGEST_ALGO is the digest algorithm. If it is 0 the function
+ * selects an appropriate one.
+ *
+ * TIMESTAMP is the timestamp to use for the signature. 0 means "now"
+ *
+ * DURATION is the amount of time (in seconds) until the signature
+ * expires.
+ *
+ * This function creates the following subpackets: issuer, created,
+ * and expire (if duration is not 0). Additional subpackets can be
+ * added using MKSUBPKT, which is called after these subpackets are
+ * added and before the signature is generated. OPAQUE is passed to
+ * MKSUBPKT.
+ */
int
make_keysig_packet (PKT_signature **ret_sig, PKT_public_key *pk,
PKT_user_id *uid, PKT_public_key *subpk,
}
int
-get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid)
+get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk,
+ PKT_user_id *uid)
{
(void)ctrl;
+ (void)kb;
(void)pk;
(void)uid;
return '?';
}
unsigned int
-get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid,
+get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid,
PKT_signature *sig, int may_ask)
{
(void)ctrl;
+ (void)kb;
(void)pk;
(void)uid;
(void)sig;
{
(void)ctrl;
}
+
+gpg_error_t
+tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
+{
+ (void) ctrl;
+ (void) kb;
+
+ return 0;
+}
#include <stdarg.h>
#include <sqlite3.h>
#include <time.h>
-#include <utime.h>
-#include <fcntl.h>
-#include <unistd.h>
#include "gpg.h"
#include "types.h"
#define CONTROL_L ('L' - 'A' + 1)
-/* Number of signed messages required to indicate that enough history
- * is available for basic trust. */
-#define BASIC_TRUST_THRESHOLD 10
-/* Number of signed messages required to indicate that a lot of
- * history is available. */
-#define FULL_TRUST_THRESHOLD 100
+/* Number of days with signed / ecnrypted messages required to
+ * indicate that enough history is available for basic trust. */
+#define BASIC_TRUST_THRESHOLD 4
+/* Number of days with signed / encrypted messages required to
+ * indicate that a lot of history is available. */
+#define FULL_TRUST_THRESHOLD 21
-/* An struct with data pertaining to the tofu DB.
-
- To initialize this data structure, call opendbs(). Cleanup is done
- when the CTRL object is released. To get a handle to a database,
- use the getdb() function. This will either return an existing
- handle or open a new DB connection, as appropriate. */
+/* A struct with data pertaining to the tofu DB. There is one such
+ struct per session and it is cached in session's ctrl structure.
+ To initialize this or get the current singleton, call opendbs().
+ There is no need to explicitly release it; cleanup is done when the
+ CTRL object is released. */
struct tofu_dbs_s
{
sqlite3 *db;
sqlite3_stmt *record_binding_get_old_policy;
sqlite3_stmt *record_binding_update;
- sqlite3_stmt *record_binding_update2;
sqlite3_stmt *get_policy_select_policy_and_conflict;
sqlite3_stmt *get_trust_bindings_with_this_email;
sqlite3_stmt *get_trust_gather_other_user_ids;
sqlite3_stmt *get_trust_gather_signature_stats;
sqlite3_stmt *get_trust_gather_encryption_stats;
sqlite3_stmt *register_already_seen;
- sqlite3_stmt *register_insert;
+ sqlite3_stmt *register_signature;
+ sqlite3_stmt *register_encryption;
} s;
int in_batch_transaction;
/* Local prototypes. */
static gpg_error_t end_transaction (ctrl_t ctrl, int only_batch);
static char *email_from_user_id (const char *user_id);
-
-
+static int show_statistics (tofu_dbs_t dbs,
+ const char *fingerprint, const char *email,
+ enum tofu_policy policy,
+ estream_t outfp, int only_status_fd, time_t now);
\f
const char *
tofu_policy_str (enum tofu_policy policy)
* than 500 ms), to prevent starving other gpg processes, we drop
* and retake the batch lock.
*
- * Note: if we wanted higher resolution, we could use
- * npth_clock_gettime. */
+ * Note: gnupg_get_time has a one second resolution, if we wanted a
+ * higher resolution, we could use npth_clock_gettime. */
if (/* No real transactions. */
dbs->in_transaction == 0
/* There is an open batch transaction. */
/* Commit a transaction. If ONLY_BATCH is 1, then this only ends the
* batch transaction if we have left batch mode. If ONLY_BATCH is 2,
- * this ends any open batch transaction even if we are still in batch
- * mode. */
+ * this commits any open batch transaction even if we are still in
+ * batch mode. */
static gpg_error_t
end_transaction (ctrl_t ctrl, int only_batch)
{
log_assert (dbs);
log_assert (dbs->in_transaction > 0);
- /* Be careful to not any progress made by closed transactions in
+ /* Be careful to not undo any progress made by closed transactions in
batch mode. */
rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err,
"rollback to inner%d;",
if (errno || !(!strcmp (tail, ".0") || !*tail))
{
err = errno? gpg_error_from_errno (errno) : gpg_error (GPG_ERR_BAD_DATA);
- log_debug ("%s:%d: "
- "strtol failed for DB returned string (tail=%.10s): %s\n",
- __FILE__, line, tail, gpg_strerror (err));
+ log_debug ("%s:%d: strtol failed for TOFU DB data; returned string"
+ " (string='%.10s%s'; tail='%.10s%s'): %s\n",
+ __FILE__, line,
+ string, string && strlen(string) > 10 ? "..." : "",
+ tail, tail && strlen(tail) > 10 ? "..." : "",
+ gpg_strerror (err));
*r_value = fallback;
}
else
if (errno || !(!strcmp (tail, ".0") || !*tail))
{
err = errno? gpg_error_from_errno (errno) : gpg_error (GPG_ERR_BAD_DATA);
- log_debug ("%s:%d: "
- "strtoul failed for DB returned string (tail=%.10s): %s\n",
- __FILE__, line, tail, gpg_strerror (err));
+ log_debug ("%s:%d: strtoul failed for TOFU DB data; returned string"
+ " (string='%.10s%s'; tail='%.10s%s'): %s\n",
+ __FILE__, line,
+ string, string && strlen(string) > 10 ? "..." : "",
+ tail, tail && strlen(tail) > 10 ? "..." : "",
+ gpg_strerror (err));
*r_value = fallback;
}
else
return 1;
}
+static int
+check_utks (sqlite3 *db)
+{
+ int rc;
+ char *err = NULL;
+ struct key_item *utks;
+ struct key_item *ki;
+ int utk_count;
+ char *utks_string = NULL;
+ char keyid_str[16+1];
+ long utks_unchanged = 0;
+
+ /* An early version of the v1 format did not include the list of
+ * known ultimately trusted keys.
+ *
+ * This list is used to detect when the set of ultimately trusted
+ * keys changes. We need to detect this to invalidate the effective
+ * policy, which can change if an ultimately trusted key is added or
+ * removed. */
+ rc = sqlite3_exec (db,
+ "create table if not exists ultimately_trusted_keys"
+ " (keyid);\n",
+ NULL, NULL, &err);
+ if (rc)
+ {
+ log_error (_("error creating 'ultimately_trusted_keys' TOFU table: %s\n"),
+ err);
+ sqlite3_free (err);
+ goto out;
+ }
+
+
+ utks = tdb_utks ();
+ for (ki = utks, utk_count = 0; ki; ki = ki->next, utk_count ++)
+ ;
+
+ if (utk_count)
+ {
+ /* Build a list of keyids of the form "XXX","YYY","ZZZ". */
+ int len = (1 + 16 + 1 + 1) * utk_count;
+ int o = 0;
+
+ utks_string = xmalloc (len);
+ *utks_string = 0;
+ for (ki = utks, utk_count = 0; ki; ki = ki->next, utk_count ++)
+ {
+ utks_string[o ++] = '\'';
+ format_keyid (ki->kid, KF_LONG,
+ keyid_str, sizeof (keyid_str));
+ memcpy (&utks_string[o], keyid_str, 16);
+ o += 16;
+ utks_string[o ++] = '\'';
+ utks_string[o ++] = ',';
+ }
+ utks_string[o - 1] = 0;
+ log_assert (o == len);
+ }
+
+ rc = gpgsql_exec_printf
+ (db, get_single_unsigned_long_cb, &utks_unchanged, &err,
+ "select"
+ /* Removed UTKs? (Known UTKs in current UTKs.) */
+ " ((select count(*) from ultimately_trusted_keys"
+ " where (keyid in (%s))) == %d)"
+ " and"
+ /* New UTKs? */
+ " ((select count(*) from ultimately_trusted_keys"
+ " where keyid not in (%s)) == 0);",
+ utks_string ? utks_string : "",
+ utk_count,
+ utks_string ? utks_string : "");
+ xfree (utks_string);
+ if (rc)
+ {
+ log_error (_("TOFU DB error"));
+ print_further_info ("checking if ultimately trusted keys changed: %s",
+ err);
+ sqlite3_free (err);
+ goto out;
+ }
+
+ if (utks_unchanged)
+ goto out;
+
+ if (DBG_TRUST)
+ log_debug ("TOFU: ultimately trusted keys changed.\n");
+
+ /* Given that the set of ultimately trusted keys
+ * changed, clear any cached policies. */
+ rc = gpgsql_exec_printf
+ (db, NULL, NULL, &err,
+ "update bindings set effective_policy = %d;",
+ TOFU_POLICY_NONE);
+ if (rc)
+ {
+ log_error (_("TOFU DB error"));
+ print_further_info ("clearing cached policies: %s", err);
+ sqlite3_free (err);
+ goto out;
+ }
+
+ /* Now, update the UTK table. */
+ rc = sqlite3_exec (db,
+ "drop table ultimately_trusted_keys;",
+ NULL, NULL, &err);
+ if (rc)
+ {
+ log_error (_("TOFU DB error"));
+ print_further_info ("dropping ultimately_trusted_keys: %s", err);
+ sqlite3_free (err);
+ goto out;
+ }
+
+ rc = sqlite3_exec (db,
+ "create table if not exists"
+ " ultimately_trusted_keys (keyid);\n",
+ NULL, NULL, &err);
+ if (rc)
+ {
+ log_error (_("TOFU DB error"));
+ print_further_info ("creating ultimately_trusted_keys: %s", err);
+ sqlite3_free (err);
+ goto out;
+ }
+
+ for (ki = utks; ki; ki = ki->next)
+ {
+ format_keyid (ki->kid, KF_LONG,
+ keyid_str, sizeof (keyid_str));
+ rc = gpgsql_exec_printf
+ (db, NULL, NULL, &err,
+ "insert into ultimately_trusted_keys values ('%s');",
+ keyid_str);
+ if (rc)
+ {
+ log_error (_("TOFU DB error"));
+ print_further_info ("updating ultimately_trusted_keys: %s",
+ err);
+ sqlite3_free (err);
+ goto out;
+ }
+ }
+
+ out:
+ return rc;
+}
/* If the DB is new, initialize it. Otherwise, check the DB's
version.
{
/* Early version of the v1 format did not include the encryption
table. Add it. */
- sqlite3_exec (db,
- "create table if not exists encryptions"
- " (binding INTEGER NOT NULL,"
- " time INTEGER);"
- "create index if not exists encryptions_binding"
- " on encryptions (binding);\n",
- NULL, NULL, &err);
+ rc = sqlite3_exec (db,
+ "create table if not exists encryptions"
+ " (binding INTEGER NOT NULL,"
+ " time INTEGER);"
+ "create index if not exists encryptions_binding"
+ " on encryptions (binding);\n",
+ NULL, NULL, &err);
+ if (rc)
+ {
+ log_error (_("error creating 'encryptions' TOFU table: %s\n"),
+ err);
+ sqlite3_free (err);
+ }
+ }
+ if (! rc)
+ {
+ /* The effective policy for a binding. If a key is ultimately
+ * trusted, then the effective policy of all of its bindings is
+ * good. Likewise if a key is signed by an ultimately trusted
+ * key, etc. If the effective policy is NONE, then we need to
+ * recompute the effective policy. Otherwise, the effective
+ * policy is considered to be up to date, i.e., effective_policy
+ * is a cache of the computed policy. */
+ rc = gpgsql_exec_printf
+ (db, NULL, NULL, &err,
+ "alter table bindings"
+ " add column effective_policy INTEGER"
+ " DEFAULT %d"
+ " CHECK (effective_policy in (%d, %d, %d, %d, %d, %d));",
+ TOFU_POLICY_NONE,
+ TOFU_POLICY_NONE, TOFU_POLICY_AUTO, TOFU_POLICY_GOOD,
+ TOFU_POLICY_UNKNOWN, TOFU_POLICY_BAD, TOFU_POLICY_ASK);
+ if (rc)
+ {
+ if (rc == SQLITE_ERROR)
+ /* Almost certainly "duplicate column name", which we can
+ * safely ignore. */
+ rc = 0;
+ else
+ log_error (_("adding column effective_policy to bindings DB: %s\n"),
+ err);
+ sqlite3_free (err);
+ }
}
+ if (! rc)
+ rc = check_utks (db);
+
if (rc)
{
rc = sqlite3_exec (db, "rollback;", NULL, NULL, &err);
(void) call_count;
- /* Update the lock file time stamp so that the current owner knows
- that we want the lock. */
+ /* Update the want-lock-file time stamp (specifically, the ctime) so
+ * that the current owner knows that we (well, someone) want the
+ * lock. */
if (dbs)
{
/* Note: we don't fail if we can't create the lock file: this
- process will have to wait a bit longer, but otherwise nothing
- horrible should happen. */
+ * process will have to wait a bit longer, but otherwise nothing
+ * horrible should happen. */
+
+ estream_t fp;
- int fd = open (dbs->want_lock_file, O_CREAT|O_WRONLY|O_TRUNC,
- S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR);
- if (fd == -1)
+ fp = es_fopen (dbs->want_lock_file, "w");
+ if (! fp)
log_debug ("TOFU: Error opening '%s': %s\n",
dbs->want_lock_file, strerror (errno));
else
- close (fd);
+ es_fclose (fp);
}
/* Call again. */
If SHOW_OLD is set, the binding's old policy is displayed. */
static gpg_error_t
record_binding (tofu_dbs_t dbs, const char *fingerprint, const char *email,
- const char *user_id, enum tofu_policy policy,
- const char *conflict,
+ const char *user_id,
+ enum tofu_policy policy, enum tofu_policy effective_policy,
+ const char *conflict, int set_conflict,
int show_old, time_t now)
{
char *fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
rc = gpgsql_stepx
(dbs->db, &dbs->s.record_binding_update, NULL, NULL, &err,
"insert or replace into bindings\n"
- " (oid, fingerprint, email, user_id, time, policy, conflict)\n"
+ " (oid, fingerprint, email, user_id, time,"
+ " policy, conflict, effective_policy)\n"
" values (\n"
/* If we don't explicitly reuse the OID, then SQLite will
- reallocate a new one. We just need to search for the OID
- based on the fingerprint and email since they are unique. */
+ * reallocate a new one. We just need to search for the OID
+ * based on the fingerprint and email since they are unique. */
" (select oid from bindings where fingerprint = ? and email = ?),\n"
- " ?, ?, ?, ?, ?, ?);",
+ " ?, ?, ?, ?, ?,"
+ /* If SET_CONFLICT is 0, then preserve conflict's current value. */
+ " case ?"
+ " when 0 then"
+ " (select conflict from bindings where fingerprint = ? and email = ?)"
+ " else ?"
+ " end,"
+ " ?);",
+ /* oid subquery. */
GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email,
+ /* values 2 through 6. */
GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email,
GPGSQL_ARG_STRING, user_id,
GPGSQL_ARG_LONG_LONG, (long long) now,
GPGSQL_ARG_INT, (int) policy,
+ /* conflict subquery. */
+ GPGSQL_ARG_INT, set_conflict ? 1 : 0,
+ GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email,
GPGSQL_ARG_STRING, conflict ? conflict : "",
+ GPGSQL_ARG_INT, (int) effective_policy,
GPGSQL_ARG_END);
if (rc)
{
}
-/* Collect the strings returned by a query in a simply string list.
+/* Collect the strings returned by a query in a simple string list.
Any NULL values are converted to the empty string.
If a result has 3 rows and each row contains two columns, then the
return 0;
}
-/* Convert from seconds to time units.
-
- Note: T should already be a multiple of TIME_AGO_UNIT_SMALL or
- TIME_AGO_UNIT_MEDIUM or TIME_AGO_UNIT_LARGE. */
-signed long
-time_ago_scale (signed long t)
-{
- if (t < TIME_AGO_UNIT_MEDIUM)
- return t / TIME_AGO_UNIT_SMALL;
- if (t < TIME_AGO_UNIT_LARGE)
- return t / TIME_AGO_UNIT_MEDIUM;
- return t / TIME_AGO_UNIT_LARGE;
-}
-
-
-/* Return the policy for the binding <FINGERPRINT, EMAIL> (email has
- already been normalized) and any conflict information in *CONFLICT
- if CONFLICT is not NULL. Returns _tofu_GET_POLICY_ERROR if an error
- occurs. */
-static enum tofu_policy
-get_policy (tofu_dbs_t dbs, const char *fingerprint, const char *email,
- char **conflict)
-{
- int rc;
- char *err = NULL;
- strlist_t strlist = NULL;
- enum tofu_policy policy = _tofu_GET_POLICY_ERROR;
- long along;
-
- /* Check if the <FINGERPRINT, EMAIL> binding is known
- (TOFU_POLICY_NONE cannot appear in the DB. Thus, if POLICY is
- still TOFU_POLICY_NONE after executing the query, then the
- result set was empty.) */
- rc = gpgsql_stepx (dbs->db, &dbs->s.get_policy_select_policy_and_conflict,
- strings_collect_cb2, &strlist, &err,
- "select policy, conflict from bindings\n"
- " where fingerprint = ? and email = ?",
- GPGSQL_ARG_STRING, fingerprint,
- GPGSQL_ARG_STRING, email,
- GPGSQL_ARG_END);
- if (rc)
- {
- log_error (_("error reading TOFU database: %s\n"), err);
- print_further_info ("checking for existing bad bindings");
- sqlite3_free (err);
- rc = gpg_error (GPG_ERR_GENERAL);
- goto out;
- }
-
- if (strlist_length (strlist) == 0)
- /* No results. */
- {
- policy = TOFU_POLICY_NONE;
- goto out;
- }
- else if (strlist_length (strlist) != 2)
- /* The result has the wrong form. */
- {
- log_error (_("error reading TOFU database: %s\n"),
- gpg_strerror (GPG_ERR_BAD_DATA));
- print_further_info ("checking for existing bad bindings:"
- " expected 2 results, got %d\n",
- strlist_length (strlist));
- goto out;
- }
-
- /* The result has the right form. */
-
- if (string_to_long (&along, strlist->d, 0, __LINE__))
- {
- log_error (_("error reading TOFU database: %s\n"),
- gpg_strerror (GPG_ERR_BAD_DATA));
- print_further_info ("bad value for policy: %s", strlist->d);
- goto out;
- }
- policy = along;
-
- if (! (policy == TOFU_POLICY_AUTO
- || policy == TOFU_POLICY_GOOD
- || policy == TOFU_POLICY_UNKNOWN
- || policy == TOFU_POLICY_BAD
- || policy == TOFU_POLICY_ASK))
- {
- log_error (_("error reading TOFU database: %s\n"),
- gpg_strerror (GPG_ERR_DB_CORRUPTED));
- print_further_info ("invalid value for policy (%d)", policy);
- policy = _tofu_GET_POLICY_ERROR;
- goto out;
- }
-
-
- /* If CONFLICT is set, then policy should be TOFU_POLICY_ASK. But,
- just in case, we do the check again here and ignore the conflict
- if POLICY is not TOFU_POLICY_ASK. */
- if (conflict)
- {
- if (policy == TOFU_POLICY_ASK && *strlist->next->d)
- *conflict = xstrdup (strlist->next->d);
- else
- *conflict = NULL;
- }
-
- out:
- log_assert (policy == _tofu_GET_POLICY_ERROR
- || policy == TOFU_POLICY_NONE
- || policy == TOFU_POLICY_AUTO
- || policy == TOFU_POLICY_GOOD
- || policy == TOFU_POLICY_UNKNOWN
- || policy == TOFU_POLICY_BAD
- || policy == TOFU_POLICY_ASK);
-
- free_strlist (strlist);
-
- return policy;
-}
-
-
/* Format the first part of a conflict message and return that as a
* malloced string. */
static char *
else if (policy == TOFU_POLICY_ASK && conflict_set->next)
{
int conflicts = strlist_length (conflict_set);
- es_fprintf (fp, _("The email address \"%s\" is associated with %d keys!"),
- email, conflicts);
+ es_fprintf
+ (fp, ngettext("The email address \"%s\" is associated with %d key!",
+ "The email address \"%s\" is associated with %d keys!",
+ conflicts),
+ email, conflicts);
if (opt.verbose)
es_fprintf (fp,
_(" Since this binding's policy was 'auto', it has been "
char *key = NULL;
strlist_t binding;
int seen_in_past = 0;
+ int encrypted = 1;
es_fprintf (fp, _("Statistics for keys"
" with the email address \"%s\":\n"),
stats_iter->count);
#endif
+ if (stats_iter->time_ago > 0 && encrypted)
+ {
+ /* We've change from the encrypted stats to the verified
+ * stats. Reset SEEN_IN_PAST. */
+ encrypted = 0;
+ seen_in_past = 0;
+ }
+
if (! key || strcmp (key, stats_iter->fingerprint))
{
int this_key;
xfree (key_pp);
seen_in_past = 0;
+
+ show_statistics (dbs, stats_iter->fingerprint, email,
+ TOFU_POLICY_ASK, NULL, 1, now);
}
if (labs(stats_iter->time_ago) == 1)
seen_in_past += stats_iter->count;
es_fputs (" ", fp);
- /* TANSLATORS: This string is concatenated with one of
- * the day/week/month strings to form one sentence. */
- if (stats_iter->time_ago > 0)
- es_fprintf (fp, ngettext("Verified %d message",
- "Verified %d messages",
- seen_in_past), seen_in_past);
- else
- es_fprintf (fp, ngettext("Encrypted %d message",
- "Encrypted %d messages",
- seen_in_past), seen_in_past);
if (!stats_iter->count)
- es_fputs (".", fp);
+ {
+ if (stats_iter->time_ago > 0)
+ es_fprintf (fp, ngettext("Verified %d message.",
+ "Verified %d messages.",
+ seen_in_past), seen_in_past);
+ else
+ es_fprintf (fp, ngettext("Encrypted %d message.",
+ "Encrypted %d messages.",
+ seen_in_past), seen_in_past);
+ }
else if (labs(stats_iter->time_ago) == 2)
{
- es_fprintf (fp, "in the future.");
+ if (stats_iter->time_ago > 0)
+ es_fprintf (fp, ngettext("Verified %d message in the future.",
+ "Verified %d messages in the future.",
+ seen_in_past), seen_in_past);
+ else
+ es_fprintf (fp, ngettext("Encrypted %d message in the future.",
+ "Encrypted %d messages in the future.",
+ seen_in_past), seen_in_past);
/* Reset it. */
seen_in_past = 0;
}
else
{
if (labs(stats_iter->time_ago) == 3)
- es_fprintf (fp, ngettext(" over the past day.",
- " over the past %d days.",
- seen_in_past),
- TIME_AGO_SMALL_THRESHOLD
- / TIME_AGO_UNIT_SMALL);
+ {
+ int days = 1 + stats_iter->time_ago / TIME_AGO_UNIT_SMALL;
+ if (stats_iter->time_ago > 0)
+ es_fprintf
+ (fp,
+ ngettext("Messages verified over the past %d day: %d.",
+ "Messages verified over the past %d days: %d.",
+ days), days, seen_in_past);
+ else
+ es_fprintf
+ (fp,
+ ngettext("Messages encrypted over the past %d day: %d.",
+ "Messages encrypted over the past %d days: %d.",
+ days), days, seen_in_past);
+ }
else if (labs(stats_iter->time_ago) == 4)
- es_fprintf (fp, ngettext(" over the past month.",
- " over the past %d months.",
- seen_in_past),
- TIME_AGO_MEDIUM_THRESHOLD
- / TIME_AGO_UNIT_MEDIUM);
+ {
+ int months = 1 + stats_iter->time_ago / TIME_AGO_UNIT_MEDIUM;
+ if (stats_iter->time_ago > 0)
+ es_fprintf
+ (fp,
+ ngettext("Messages verified over the past %d month: %d.",
+ "Messages verified over the past %d months: %d.",
+ months), months, seen_in_past);
+ else
+ es_fprintf
+ (fp,
+ ngettext("Messages encrypted over the past %d month: %d.",
+ "Messages encrypted over the past %d months: %d.",
+ months), months, seen_in_past);
+ }
else if (labs(stats_iter->time_ago) == 5)
- es_fprintf (fp, ngettext(" over the past year.",
- " over the past %d years.",
- seen_in_past),
- TIME_AGO_LARGE_THRESHOLD
- / TIME_AGO_UNIT_LARGE);
+ {
+ int years = 1 + stats_iter->time_ago / TIME_AGO_UNIT_LARGE;
+ if (stats_iter->time_ago > 0)
+ es_fprintf
+ (fp,
+ ngettext("Messages verified over the past %d year: %d.",
+ "Messages verified over the past %d years: %d.",
+ years), years, seen_in_past);
+ else
+ es_fprintf
+ (fp,
+ ngettext("Messages encrypted over the past %d year: %d.",
+ "Messages encrypted over the past %d years: %d.",
+ years), years, seen_in_past);
+ }
else if (labs(stats_iter->time_ago) == 6)
- es_fprintf (fp, _(" in the past."));
+ {
+ if (stats_iter->time_ago > 0)
+ es_fprintf
+ (fp, _("Messages verified in the past: %d."),
+ seen_in_past);
+ else
+ es_fprintf
+ (fp, _("Messages encrypted in the past: %d."),
+ seen_in_past);
+ }
else
log_assert (! "Broken SQL.\n");
}
}
if (record_binding (dbs, fingerprint, email, user_id,
- *policy, NULL, 0, now))
+ *policy, TOFU_POLICY_NONE, NULL, 0, 0, now))
{
/* If there's an error registering the
* binding, don't save the signature. */
int j;
strlist_t *prevp;
strlist_t iter_next;
- int die[conflict_set_count];
+ int *die;
- memset (die, 0, sizeof (die));
+ log_assert (conflict_set_count > 0);
+ die = xtrycalloc (conflict_set_count, sizeof *die);
+ if (!die)
+ {
+ /*err = gpg_error_from_syserror ();*/
+ xoutofcore (); /* Fixme: Let the fucntion return an error. */
+ }
for (i = 0; i < conflict_set_count; i ++)
{
/* We shouldn't have removed the head. */
log_assert (conflict_set);
log_assert (conflict_set_count >= 1);
+ xfree (die);
}
xfree (kb_all);
}
-/* Return the trust level (TRUST_NEVER, etc.) for the binding
- * <FINGERPRINT, EMAIL> (email is already normalized). If no policy
- * is registered, returns TOFU_POLICY_NONE. If an error occurs,
- * returns _tofu_GET_TRUST_ERROR.
- *
- * PK is the public key object for FINGERPRINT.
- *
- * USER_ID is the unadulterated user id.
- *
- * If MAY_ASK is set, then we may interact with the user. This is
- * necessary if there is a conflict or the binding's policy is
- * TOFU_POLICY_ASK. In the case of a conflict, we set the new
- * conflicting binding's policy to TOFU_POLICY_ASK. In either case,
- * we return TRUST_UNDEFINED. Note: if MAY_ASK is set, then this
- * function must not be called while in a transaction! */
+/* Return the effective policy for the binding <FINGERPRINT, EMAIL>
+ * (email has already been normalized) and any conflict information in
+ * *CONFLICT_SETP, if CONFLICT_SETP is not NULL. Returns
+ * _tofu_GET_POLICY_ERROR if an error occurs. */
static enum tofu_policy
-get_trust (ctrl_t ctrl, PKT_public_key *pk,
- const char *fingerprint, const char *email,
- const char *user_id, int may_ask, time_t now)
+get_policy (tofu_dbs_t dbs, PKT_public_key *pk,
+ const char *fingerprint, const char *user_id, const char *email,
+ strlist_t *conflict_setp, time_t now)
{
- tofu_dbs_t dbs = ctrl->tofu.dbs;
- int in_transaction = 0;
- enum tofu_policy policy;
int rc;
- char *sqerr = NULL;
- int change_conflicting_to_ask = 0;
+ char *err = NULL;
+ strlist_t results = NULL;
+ enum tofu_policy policy = _tofu_GET_POLICY_ERROR;
+ enum tofu_policy effective_policy_orig = TOFU_POLICY_NONE;
+ enum tofu_policy effective_policy = _tofu_GET_POLICY_ERROR;
+ long along;
+ char *conflict_orig = NULL;
+ char *conflict = NULL;
strlist_t conflict_set = NULL;
int conflict_set_count;
- int trust_level = TRUST_UNKNOWN;
- strlist_t iter;
-
- log_assert (dbs);
- if (may_ask)
- log_assert (dbs->in_transaction == 0);
-
- if (opt.batch)
- may_ask = 0;
+ /* Check if the <FINGERPRINT, EMAIL> binding is known
+ (TOFU_POLICY_NONE cannot appear in the DB. Thus, if POLICY is
+ still TOFU_POLICY_NONE after executing the query, then the
+ result set was empty.) */
+ rc = gpgsql_stepx (dbs->db, &dbs->s.get_policy_select_policy_and_conflict,
+ strings_collect_cb2, &results, &err,
+ "select policy, conflict, effective_policy from bindings\n"
+ " where fingerprint = ? and email = ?",
+ GPGSQL_ARG_STRING, fingerprint,
+ GPGSQL_ARG_STRING, email,
+ GPGSQL_ARG_END);
+ if (rc)
+ {
+ log_error (_("error reading TOFU database: %s\n"), err);
+ print_further_info ("reading the policy");
+ sqlite3_free (err);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto out;
+ }
- log_assert (keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0);
+ if (strlist_length (results) == 0)
+ {
+ /* No results. Use the defaults. */
+ policy = TOFU_POLICY_NONE;
+ effective_policy = TOFU_POLICY_NONE;
+ }
+ else if (strlist_length (results) == 3)
+ {
+ /* Parse and sanity check the results. */
- /* Make sure _tofu_GET_TRUST_ERROR isn't equal to any of the trust
- levels. */
- log_assert (_tofu_GET_TRUST_ERROR != TRUST_UNKNOWN
- && _tofu_GET_TRUST_ERROR != TRUST_EXPIRED
- && _tofu_GET_TRUST_ERROR != TRUST_UNDEFINED
- && _tofu_GET_TRUST_ERROR != TRUST_NEVER
- && _tofu_GET_TRUST_ERROR != TRUST_MARGINAL
- && _tofu_GET_TRUST_ERROR != TRUST_FULLY
- && _tofu_GET_TRUST_ERROR != TRUST_ULTIMATE);
+ if (string_to_long (&along, results->d, 0, __LINE__))
+ {
+ log_error (_("error reading TOFU database: %s\n"),
+ gpg_strerror (GPG_ERR_BAD_DATA));
+ print_further_info ("bad value for policy: %s", results->d);
+ goto out;
+ }
+ policy = along;
- begin_transaction (ctrl, 0);
- in_transaction = 1;
+ if (! (policy == TOFU_POLICY_AUTO
+ || policy == TOFU_POLICY_GOOD
+ || policy == TOFU_POLICY_UNKNOWN
+ || policy == TOFU_POLICY_BAD
+ || policy == TOFU_POLICY_ASK))
+ {
+ log_error (_("error reading TOFU database: %s\n"),
+ gpg_strerror (GPG_ERR_DB_CORRUPTED));
+ print_further_info ("invalid value for policy (%d)", policy);
+ effective_policy = _tofu_GET_POLICY_ERROR;
+ goto out;
+ }
- policy = get_policy (dbs, fingerprint, email, NULL);
- {
- /* See if the key is ultimately trusted. If so, we're done. */
- u32 kid[2];
+ if (*results->next->d)
+ conflict = xstrdup (results->next->d);
- keyid_from_pk (pk, kid);
-
- if (tdb_keyid_is_utk (kid))
- {
- if (policy == TOFU_POLICY_NONE)
- /* New binding. */
- {
- if (record_binding (dbs, fingerprint, email, user_id,
- TOFU_POLICY_GOOD, NULL, 0, now) != 0)
- {
- log_error (_("error setting TOFU binding's trust level"
- " to %s\n"), "good");
- trust_level = _tofu_GET_TRUST_ERROR;
- goto out;
- }
- }
-
- trust_level = TRUST_ULTIMATE;
- goto out;
- }
- }
-
- if (policy == TOFU_POLICY_AUTO)
- {
- policy = opt.tofu_default_policy;
- if (DBG_TRUST)
- log_debug ("TOFU: binding <key: %s, user id: %s>'s policy is"
- " auto (default: %s).\n",
- fingerprint, email,
- tofu_policy_str (opt.tofu_default_policy));
+ if (string_to_long (&along, results->next->next->d, 0, __LINE__))
+ {
+ log_error (_("error reading TOFU database: %s\n"),
+ gpg_strerror (GPG_ERR_BAD_DATA));
+ print_further_info ("bad value for effective policy: %s",
+ results->next->next->d);
+ goto out;
+ }
+ effective_policy = along;
+
+ if (! (effective_policy == TOFU_POLICY_NONE
+ || effective_policy == TOFU_POLICY_AUTO
+ || effective_policy == TOFU_POLICY_GOOD
+ || effective_policy == TOFU_POLICY_UNKNOWN
+ || effective_policy == TOFU_POLICY_BAD
+ || effective_policy == TOFU_POLICY_ASK))
+ {
+ log_error (_("error reading TOFU database: %s\n"),
+ gpg_strerror (GPG_ERR_DB_CORRUPTED));
+ print_further_info ("invalid value for effective_policy (%d)",
+ effective_policy);
+ effective_policy = _tofu_GET_POLICY_ERROR;
+ goto out;
+ }
}
- switch (policy)
+ else
{
- case TOFU_POLICY_AUTO:
- case TOFU_POLICY_GOOD:
- case TOFU_POLICY_UNKNOWN:
- case TOFU_POLICY_BAD:
- /* The saved judgement is auto -> auto, good, unknown or bad.
- * We don't need to ask the user anything. */
- if (DBG_TRUST)
- log_debug ("TOFU: Known binding <key: %s, user id: %s>'s policy: %s\n",
- fingerprint, email, tofu_policy_str (policy));
- trust_level = tofu_policy_to_trust_level (policy);
+ /* The result has the wrong form. */
+
+ log_error (_("error reading TOFU database: %s\n"),
+ gpg_strerror (GPG_ERR_BAD_DATA));
+ print_further_info ("reading policy: expected 3 columns, got %d\n",
+ strlist_length (results));
goto out;
+ }
- case TOFU_POLICY_ASK:
- /* We need to ask the user what to do. Case #1 or #2 below. */
- break;
+ /* Save the effective policy and conflict so we know if we changed
+ * them. */
+ effective_policy_orig = effective_policy;
+ conflict_orig = conflict;
- case TOFU_POLICY_NONE:
- /* The binding is new, we need to check for conflicts. Case #3
- * below. */
- break;
+ /* Unless there is a conflict, if the effective policy is cached,
+ * just return it. The reason we don't do this when there is a
+ * conflict is because of the following scenario: assume A and B
+ * conflict and B has signed A's key. Now, later we import A's
+ * signature on B. We need to recheck A, but the signature was on
+ * B, i.e., when B changes, we invalidate B's effective policy, but
+ * we also need to invalidate A's effective policy. Instead, we
+ * assume that conflicts are rare and don't optimize for them, which
+ * would complicate the code. */
+ if (effective_policy != TOFU_POLICY_NONE && !conflict)
+ goto out;
- case _tofu_GET_POLICY_ERROR:
- trust_level = _tofu_GET_TRUST_ERROR;
+ /* If the user explicitly set the policy, then respect that. */
+ if (policy != TOFU_POLICY_AUTO && policy != TOFU_POLICY_NONE)
+ {
+ effective_policy = policy;
goto out;
-
- default:
- log_bug ("%s: Impossible value for policy (%d)\n", __func__, policy);
}
+ /* Unless proven wrong, assume the effective policy is 'auto'. */
+ effective_policy = TOFU_POLICY_AUTO;
- /* We get here if:
- *
- * 1. The saved policy is auto and the default policy is ask
- * (get_policy() == TOFU_POLICY_AUTO
- * && opt.tofu_default_policy == TOFU_POLICY_ASK)
- *
- * 2. The saved policy is ask (either last time the user selected
- * accept once or reject once or there was a conflict and this
- * binding's policy was changed from auto to ask)
- * (policy == TOFU_POLICY_ASK), or,
- *
- * 3. We don't have a saved policy (policy == TOFU_POLICY_NONE)
- * (need to check for a conflict).
- *
- * In summary: POLICY is ask or none.
- */
+ /* See if the key is ultimately trusted. */
+ {
+ u32 kid[2];
- /* Before continuing, see if the key is signed by an ultimately
- * trusted key. */
+ keyid_from_pk (pk, kid);
+ if (tdb_keyid_is_utk (kid))
+ {
+ effective_policy = TOFU_POLICY_GOOD;
+ goto out;
+ }
+ }
+
+ /* See if the key is signed by an ultimately trusted key. */
{
int fingerprint_raw_len = strlen (fingerprint) / 2;
char fingerprint_raw[fingerprint_raw_len];
int len = 0;
- int is_signed_by_utk = 0;
if (fingerprint_raw_len != 20
|| ((len = hex2bin (fingerprint,
}
else
{
- is_signed_by_utk = signed_by_utk (email, kb);
+ int is_signed_by_utk = signed_by_utk (email, kb);
release_kbnode (kb);
+ if (is_signed_by_utk)
+ {
+ effective_policy = TOFU_POLICY_GOOD;
+ goto out;
+ }
}
}
-
- if (is_signed_by_utk)
- {
- if (record_binding (dbs, fingerprint, email, user_id,
- TOFU_POLICY_GOOD, NULL, 0, now) != 0)
- {
- log_error (_("error setting TOFU binding's trust level"
- " to %s\n"), "good");
- trust_level = _tofu_GET_TRUST_ERROR;
- }
- else
- trust_level = TRUST_FULLY;
-
- goto out;
- }
}
+ /* Check for any conflicts / see if a previously discovered conflict
+ * disappeared. The latter can happen if the conflicting bindings
+ * are now cross signed, for instance. */
- /* Look for conflicts. This is needed in all 3 cases. */
conflict_set = build_conflict_set (dbs, pk, fingerprint, email);
conflict_set_count = strlist_length (conflict_set);
if (conflict_set_count == 0)
{
- /* We should always at least have the current binding. */
- trust_level = _tofu_GET_TRUST_ERROR;
+ /* build_conflict_set should always at least return the current
+ binding. Something went wrong. */
+ effective_policy = _tofu_GET_POLICY_ERROR;
goto out;
}
if (conflict_set_count == 1
- && (conflict_set->flags & BINDING_NEW)
- && opt.tofu_default_policy != TOFU_POLICY_ASK)
+ && (conflict_set->flags & BINDING_NEW))
{
/* We've never observed a binding with this email address and we
* have a default policy, which is not to ask the user. */
log_debug ("TOFU: New binding <key: %s, user id: %s>, no conflict.\n",
fingerprint, email);
- if (record_binding (dbs, fingerprint, email, user_id,
- TOFU_POLICY_AUTO, NULL, 0, now) != 0)
- {
- log_error (_("error setting TOFU binding's trust level to %s\n"),
- "auto");
- trust_level = _tofu_GET_TRUST_ERROR;
- goto out;
- }
-
- trust_level = tofu_policy_to_trust_level (TOFU_POLICY_AUTO);
+ effective_policy = TOFU_POLICY_AUTO;
goto out;
}
&& (conflict_set->flags & BINDING_CONFLICT))
{
/* No known conflicts now, but there was a conflict. This means
- * at somepoint, there was a conflict and we changed this
+ * at some point, there was a conflict and we changed this
* binding's policy to ask and set the conflicting key. The
* conflict can go away if there is not a cross sig between the
* two keys. In this case, just silently clear the conflict and
* reset the policy to auto. */
- log_assert (policy == TOFU_POLICY_ASK);
-
if (DBG_TRUST)
log_debug ("TOFU: binding <key: %s, user id: %s> had a conflict, but it's been resolved (probably via cross sig).\n",
fingerprint, email);
- if (record_binding (dbs, fingerprint, email, user_id,
- TOFU_POLICY_AUTO, NULL, 0, now) != 0)
- log_error (_("error setting TOFU binding's trust level to %s\n"),
- "auto");
+ effective_policy = TOFU_POLICY_AUTO;
+ conflict = NULL;
- trust_level = tofu_policy_to_trust_level (TOFU_POLICY_AUTO);
goto out;
}
- /* We have a conflict. Mark any conflicting bindings that have an
- * automatic policy as now requiring confirmation. Note: we delay
- * this until after we ask for confirmation so that when the current
- * policy is printed, it is correct. */
- change_conflicting_to_ask = 1;
-
- if (! may_ask)
+ if (conflict_set_count == 1)
{
- log_assert (policy == TOFU_POLICY_NONE || policy == TOFU_POLICY_ASK);
- if (policy == TOFU_POLICY_NONE)
- {
- /* We get here in the third case (no saved policy) and if
- * there is a conflict. */
- if (record_binding (dbs, fingerprint, email, user_id,
- TOFU_POLICY_ASK,
- conflict_set && conflict_set->next
- ? conflict_set->next->d : NULL,
- 0, now) != 0)
- log_error (_("error setting TOFU binding's trust level to %s\n"),
- "ask");
- }
+ /* No conflicts and never marked as conflicting. */
+
+ log_assert (!conflict);
+
+ effective_policy = TOFU_POLICY_AUTO;
- trust_level = TRUST_UNDEFINED;
goto out;
}
- /* We can't be in a normal transaction in ask_about_binding. */
- end_transaction (ctrl, 0);
- in_transaction = 0;
-
- /* If we get here, we need to ask the user about the binding. */
- ask_about_binding (ctrl,
- &policy,
- &trust_level,
- conflict_set,
- fingerprint,
- email,
- user_id,
- now);
+ /* There is a conflicting key. */
+ log_assert (conflict_set_count > 1);
+ effective_policy = TOFU_POLICY_ASK;
+ conflict = xstrdup (conflict_set->next->d);
out:
+ log_assert (policy == _tofu_GET_POLICY_ERROR
+ || policy == TOFU_POLICY_NONE
+ || policy == TOFU_POLICY_AUTO
+ || policy == TOFU_POLICY_GOOD
+ || policy == TOFU_POLICY_UNKNOWN
+ || policy == TOFU_POLICY_BAD
+ || policy == TOFU_POLICY_ASK);
+ /* Everything but NONE. */
+ log_assert (effective_policy == _tofu_GET_POLICY_ERROR
+ || effective_policy == TOFU_POLICY_AUTO
+ || effective_policy == TOFU_POLICY_GOOD
+ || effective_policy == TOFU_POLICY_UNKNOWN
+ || effective_policy == TOFU_POLICY_BAD
+ || effective_policy == TOFU_POLICY_ASK);
+
+ if (effective_policy != TOFU_POLICY_ASK && conflict)
+ conflict = NULL;
+
+ /* If we don't have a record of this binding, its effective policy
+ * changed, or conflict changed, update the DB. */
+ if (effective_policy != _tofu_GET_POLICY_ERROR
+ && (/* New binding. */
+ policy == TOFU_POLICY_NONE
+ /* effective_policy changed. */
+ || effective_policy != effective_policy_orig
+ /* conflict changed. */
+ || (conflict != conflict_orig
+ && (!conflict || !conflict_orig
+ || strcmp (conflict, conflict_orig) != 0))))
+ {
+ if (record_binding (dbs, fingerprint, email, user_id,
+ policy == TOFU_POLICY_NONE ? TOFU_POLICY_AUTO : policy,
+ effective_policy, conflict, 1, 0, now) != 0)
+ log_error (_("error setting TOFU binding's policy"
+ " to %s\n"), tofu_policy_str (policy));
+ }
- if (change_conflicting_to_ask)
+ /* If the caller wants the set of conflicts, return it. */
+ if (effective_policy == TOFU_POLICY_ASK && conflict_setp)
+ {
+ if (! conflict_set)
+ conflict_set = build_conflict_set (dbs, pk, fingerprint, email);
+ *conflict_setp = conflict_set;
+ }
+ else
{
- /* Mark any conflicting bindings that have an automatic policy as
- * now requiring confirmation. */
+ free_strlist (conflict_set);
- if (! in_transaction)
- {
- begin_transaction (ctrl, 0);
- in_transaction = 1;
- }
+ if (conflict_setp)
+ *conflict_setp = NULL;
+ }
+
+ xfree (conflict_orig);
+ if (conflict != conflict_orig)
+ xfree (conflict);
+ free_strlist (results);
+
+ return effective_policy;
+}
+
+
+/* Return the trust level (TRUST_NEVER, etc.) for the binding
+ * <FINGERPRINT, EMAIL> (email is already normalized). If no policy
+ * is registered, returns TOFU_POLICY_NONE. If an error occurs,
+ * returns _tofu_GET_TRUST_ERROR.
+ *
+ * PK is the public key object for FINGERPRINT.
+ *
+ * USER_ID is the unadulterated user id.
+ *
+ * If MAY_ASK is set, then we may interact with the user. This is
+ * necessary if there is a conflict or the binding's policy is
+ * TOFU_POLICY_ASK. In the case of a conflict, we set the new
+ * conflicting binding's policy to TOFU_POLICY_ASK. In either case,
+ * we return TRUST_UNDEFINED. Note: if MAY_ASK is set, then this
+ * function must not be called while in a transaction! */
+static enum tofu_policy
+get_trust (ctrl_t ctrl, PKT_public_key *pk,
+ const char *fingerprint, const char *email,
+ const char *user_id, int may_ask, time_t now)
+{
+ tofu_dbs_t dbs = ctrl->tofu.dbs;
+ int in_transaction = 0;
+ enum tofu_policy policy;
+ int rc;
+ char *sqerr = NULL;
+ strlist_t conflict_set = NULL;
+ int trust_level = TRUST_UNKNOWN;
+ strlist_t iter;
+
+ log_assert (dbs);
+
+ if (may_ask)
+ log_assert (dbs->in_transaction == 0);
+
+ if (opt.batch)
+ may_ask = 0;
+
+ log_assert (pk_is_primary (pk));
- /* If we weren't allowed to ask, also update this key as
- * conflicting with itself. */
- for (iter = may_ask ? conflict_set->next : conflict_set;
- iter; iter = iter->next)
+ /* Make sure _tofu_GET_TRUST_ERROR isn't equal to any of the trust
+ levels. */
+ log_assert (_tofu_GET_TRUST_ERROR != TRUST_UNKNOWN
+ && _tofu_GET_TRUST_ERROR != TRUST_EXPIRED
+ && _tofu_GET_TRUST_ERROR != TRUST_UNDEFINED
+ && _tofu_GET_TRUST_ERROR != TRUST_NEVER
+ && _tofu_GET_TRUST_ERROR != TRUST_MARGINAL
+ && _tofu_GET_TRUST_ERROR != TRUST_FULLY
+ && _tofu_GET_TRUST_ERROR != TRUST_ULTIMATE);
+
+ /* If the key is ultimately trusted, there is nothing to do. */
+ {
+ u32 kid[2];
+
+ keyid_from_pk (pk, kid);
+ if (tdb_keyid_is_utk (kid))
+ {
+ trust_level = TRUST_ULTIMATE;
+ goto out;
+ }
+ }
+
+ begin_transaction (ctrl, 0);
+ in_transaction = 1;
+
+ policy = get_policy (dbs, pk, fingerprint, user_id, email, &conflict_set, now);
+ if (policy == TOFU_POLICY_AUTO)
+ {
+ policy = opt.tofu_default_policy;
+ if (DBG_TRUST)
+ log_debug ("TOFU: binding <key: %s, user id: %s>'s policy is"
+ " auto (default: %s).\n",
+ fingerprint, email,
+ tofu_policy_str (opt.tofu_default_policy));
+ }
+ switch (policy)
+ {
+ case TOFU_POLICY_AUTO:
+ case TOFU_POLICY_GOOD:
+ case TOFU_POLICY_UNKNOWN:
+ case TOFU_POLICY_BAD:
+ /* The saved judgement is auto -> auto, good, unknown or bad.
+ * We don't need to ask the user anything. */
+ if (DBG_TRUST)
+ log_debug ("TOFU: Known binding <key: %s, user id: %s>'s policy: %s\n",
+ fingerprint, email, tofu_policy_str (policy));
+ trust_level = tofu_policy_to_trust_level (policy);
+ goto out;
+
+ case TOFU_POLICY_ASK:
+ /* We need to ask the user what to do. */
+ break;
+
+ case _tofu_GET_POLICY_ERROR:
+ trust_level = _tofu_GET_TRUST_ERROR;
+ goto out;
+
+ default:
+ log_bug ("%s: Impossible value for policy (%d)\n", __func__, policy);
+ }
+
+
+ /* We get here if:
+ *
+ * 1. The saved policy is auto and the default policy is ask
+ * (get_policy() == TOFU_POLICY_AUTO
+ * && opt.tofu_default_policy == TOFU_POLICY_ASK)
+ *
+ * 2. The saved policy is ask (either last time the user selected
+ * accept once or reject once or there was a conflict and this
+ * binding's policy was changed from auto to ask)
+ * (policy == TOFU_POLICY_ASK).
+ */
+ log_assert (policy == TOFU_POLICY_ASK);
+
+ if (may_ask)
+ {
+ /* We can't be in a normal transaction in ask_about_binding. */
+ end_transaction (ctrl, 0);
+ in_transaction = 0;
+
+ /* If we get here, we need to ask the user about the binding. */
+ ask_about_binding (ctrl,
+ &policy,
+ &trust_level,
+ conflict_set,
+ fingerprint,
+ email,
+ user_id,
+ now);
+ }
+ else
+ {
+ for (iter = conflict_set; iter; iter = iter->next)
+ show_statistics (dbs, iter->d, email,
+ TOFU_POLICY_ASK, NULL, 1, now);
+
+ trust_level = TRUST_UNDEFINED;
+ }
+
+ /* Mark any conflicting bindings that have an automatic policy as
+ * now requiring confirmation. Note: we do this after we ask for
+ * confirmation so that when the current policy is printed, it is
+ * correct. */
+ if (! in_transaction)
+ {
+ begin_transaction (ctrl, 0);
+ in_transaction = 1;
+ }
+
+ /* The conflict set should always contain at least one element:
+ * the current key. */
+ log_assert (conflict_set);
+
+ for (iter = conflict_set->next; iter; iter = iter->next)
+ {
+ /* We don't immediately set the effective policy to 'ask,
+ because */
+ rc = gpgsql_exec_printf
+ (dbs->db, NULL, NULL, &sqerr,
+ "update bindings set effective_policy = %d, conflict = %Q"
+ " where email = %Q and fingerprint = %Q and effective_policy != %d;",
+ TOFU_POLICY_NONE, fingerprint,
+ email, iter->d, TOFU_POLICY_ASK);
+ if (rc)
{
- rc = gpgsql_exec_printf
- (dbs->db, NULL, NULL, &sqerr,
- "update bindings set policy = %d, conflict = %Q"
- " where email = %Q and fingerprint = %Q and policy = %d;",
- TOFU_POLICY_ASK, fingerprint,
- email, iter->d, TOFU_POLICY_AUTO);
- if (rc)
- {
- log_error (_("error changing TOFU policy: %s\n"), sqerr);
- print_further_info ("binding: <key: %s, user id: %s>",
- fingerprint, user_id);
- sqlite3_free (sqerr);
- sqerr = NULL;
- rc = gpg_error (GPG_ERR_GENERAL);
- }
- else if (DBG_TRUST)
- log_debug ("Set %s to conflict with %s\n",
- iter->d, fingerprint);
+ log_error (_("error changing TOFU policy: %s\n"), sqerr);
+ print_further_info ("binding: <key: %s, user id: %s>",
+ fingerprint, user_id);
+ sqlite3_free (sqerr);
+ sqerr = NULL;
+ rc = gpg_error (GPG_ERR_GENERAL);
}
+ else if (DBG_TRUST)
+ log_debug ("Set %s to conflict with %s\n",
+ iter->d, fingerprint);
}
+ out:
if (in_transaction)
end_transaction (ctrl, 0);
/* Return a malloced string of the form
- * "7 months, 1 day, 5 minutes, 0 seconds"
+ * "7~months"
* The caller should replace all '~' in the returned string by a space
* and also free the returned string.
*
static char *
time_ago_str (long long int t)
{
- estream_t fp;
- int years = 0;
- int months = 0;
- int days = 0;
- int hours = 0;
- int minutes = 0;
- int seconds = 0;
-
- /* The number of units that we've printed so far. */
- int count = 0;
- /* The first unit that we printed (year = 0, month = 1,
- etc.). */
- int first = -1;
- /* The current unit. */
- int i = 0;
-
- char *str;
-
/* It would be nice to use a macro to do this, but gettext
works on the unpreprocessed code. */
#define MIN_SECS (60)
#define HOUR_SECS (60 * MIN_SECS)
#define DAY_SECS (24 * HOUR_SECS)
+#define WEEK_SECS (7 * DAY_SECS)
#define MONTH_SECS (30 * DAY_SECS)
#define YEAR_SECS (365 * DAY_SECS)
- if (t > YEAR_SECS)
+ if (t > 2 * YEAR_SECS)
{
- years = t / YEAR_SECS;
- t -= years * YEAR_SECS;
+ long long int c = t / YEAR_SECS;
+ return xtryasprintf (ngettext("%lld~year", "%lld~years", c), c);
}
- if (t > MONTH_SECS)
+ if (t > 2 * MONTH_SECS)
{
- months = t / MONTH_SECS;
- t -= months * MONTH_SECS;
+ long long int c = t / MONTH_SECS;
+ return xtryasprintf (ngettext("%lld~month", "%lld~months", c), c);
}
- if (t > DAY_SECS)
+ if (t > 2 * WEEK_SECS)
{
- days = t / DAY_SECS;
- t -= days * DAY_SECS;
+ long long int c = t / WEEK_SECS;
+ return xtryasprintf (ngettext("%lld~week", "%lld~weeks", c), c);
}
- if (t > HOUR_SECS)
+ if (t > 2 * DAY_SECS)
{
- hours = t / HOUR_SECS;
- t -= hours * HOUR_SECS;
+ long long int c = t / DAY_SECS;
+ return xtryasprintf (ngettext("%lld~day", "%lld~days", c), c);
}
- if (t > MIN_SECS)
+ if (t > 2 * HOUR_SECS)
{
- minutes = t / MIN_SECS;
- t -= minutes * MIN_SECS;
+ long long int c = t / HOUR_SECS;
+ return xtryasprintf (ngettext("%lld~hour", "%lld~hours", c), c);
}
- seconds = t;
-
-#undef MIN_SECS
-#undef HOUR_SECS
-#undef DAY_SECS
-#undef MONTH_SECS
-#undef YEAR_SECS
-
- fp = es_fopenmem (0, "rw,samethread");
- if (! fp)
- log_fatal ("error creating memory stream: %s\n",
- gpg_strerror (gpg_error_from_syserror()));
-
- if (years)
- {
- /* TRANSLATORS: The tilde ('~') is used here to indicate a
- * non-breakable space */
- es_fprintf (fp, ngettext("%d~year", "%d~years", years), years);
- count ++;
- first = i;
- }
- i ++;
- if ((first == -1 || i - first <= 3) && count <= 0 && months)
- {
- if (count)
- es_fprintf (fp, ", ");
- es_fprintf (fp, ngettext("%d~month", "%d~months", months), months);
- count ++;
- first = i;
- }
- i ++;
- if ((first == -1 || i - first <= 3) && count <= 0 && days)
+ if (t > 2 * MIN_SECS)
{
- if (count)
- es_fprintf (fp, ", ");
- es_fprintf (fp, ngettext("%d~day", "%d~days", days), days);
- count ++;
- first = i;
+ long long int c = t / MIN_SECS;
+ return xtryasprintf (ngettext("%lld~minute", "%lld~minutes", c), c);
}
- i ++;
- if ((first == -1 || i - first <= 3) && count <= 0 && hours)
- {
- if (count)
- es_fprintf (fp, ", ");
- es_fprintf (fp, ngettext("%d~hour", "%d~hours", hours), hours);
- count ++;
- first = i;
- }
- i ++;
- if ((first == -1 || i - first <= 3) && count <= 0 && minutes)
- {
- if (count)
- es_fprintf (fp, ", ");
- es_fprintf (fp, ngettext("%d~minute", "%d~minutes", minutes), minutes);
- count ++;
- first = i;
- }
- i ++;
- if ((first == -1 || i - first <= 3) && count <= 0)
- {
- if (count)
- es_fprintf (fp, ", ");
- es_fprintf (fp, ngettext("%d~second", "%d~seconds", seconds), seconds);
- }
-
- es_fputc (0, fp);
- if (es_fclose_snatch (fp, (void **) &str, NULL))
- log_fatal ("error snatching memory stream\n");
-
- return str;
+ return xtryasprintf (ngettext("%lld~second", "%lld~seconds", t), t);
}
unsigned long signature_count,
unsigned long signature_first_seen,
unsigned long signature_most_recent,
+ unsigned long signature_days,
unsigned long encryption_count,
unsigned long encryption_first_done,
- unsigned long encryption_most_recent)
+ unsigned long encryption_most_recent,
+ unsigned long encryption_days)
{
- const char *validity;
- unsigned long messages;
+ int summary;
+ int validity;
+ unsigned long days;
/* Use the euclidean distance (m = sqrt(a^2 + b^2)) rather then the
sum of the magnitudes (m = a + b) to ensure a balance between
verified signatures and encrypted messages. */
- messages = sqrtu32 (signature_count * signature_count
- + encryption_count * encryption_count);
-
- if (messages < 1)
- validity = "1"; /* Key without history. */
- else if (messages < 2 * BASIC_TRUST_THRESHOLD)
- validity = "2"; /* Key with too little history. */
- else if (messages < 2 * FULL_TRUST_THRESHOLD)
- validity = "3"; /* Key with enough history for basic trust. */
+ days = sqrtu32 (signature_days * signature_days
+ + encryption_days * encryption_days);
+
+ if (days < 1)
+ validity = 1; /* Key without history. */
+ else if (days < 2 * BASIC_TRUST_THRESHOLD)
+ validity = 2; /* Key with too little history. */
+ else if (days < 2 * FULL_TRUST_THRESHOLD)
+ validity = 3; /* Key with enough history for basic trust. */
else
- validity = "4"; /* Key with a lot of history. */
+ validity = 4; /* Key with a lot of history. */
+
+ if (policy == TOFU_POLICY_ASK)
+ summary = 0; /* Key requires attention. */
+ else
+ summary = validity;
if (fp)
{
- es_fprintf (fp, "tfs:1:%s:%lu:%lu:%s:%lu:%lu:%lu:%lu:\n",
- validity, signature_count, encryption_count,
+ es_fprintf (fp, "tfs:1:%d:%lu:%lu:%s:%lu:%lu:%lu:%lu:%d:%lu:%lu:\n",
+ summary, signature_count, encryption_count,
tofu_policy_str (policy),
signature_first_seen, signature_most_recent,
- encryption_first_done, encryption_most_recent);
+ encryption_first_done, encryption_most_recent,
+ validity, signature_days, encryption_days);
}
else
{
write_status_printf (STATUS_TOFU_STATS,
- "%s %lu %lu %s %lu %lu %lu %lu",
- validity,
+ "%d %lu %lu %s %lu %lu %lu %lu %d %lu %lu",
+ summary,
signature_count,
encryption_count,
tofu_policy_str (policy),
signature_first_seen,
signature_most_recent,
encryption_first_done,
- encryption_most_recent);
+ encryption_most_recent,
+ validity,
+ signature_days, encryption_days);
}
}
/* Note: If OUTFP is not NULL, this function merely prints a "tfs" record
- * to OUTFP. In this case USER_ID is not required.
+ * to OUTFP.
+ *
+ * POLICY is the key's policy (as returned by get_policy).
*
- * Returns whether the caller should call show_warning after iterating
- * over all user ids.
+ * Returns 0 if if ONLY_STATUS_FD is set. Otherwise, returns whether
+ * the caller should call show_warning after iterating over all user
+ * ids.
*/
static int
-show_statistics (tofu_dbs_t dbs, const char *fingerprint,
- const char *email, const char *user_id,
- estream_t outfp, time_t now)
+show_statistics (tofu_dbs_t dbs,
+ const char *fingerprint, const char *email,
+ enum tofu_policy policy,
+ estream_t outfp, int only_status_fd, time_t now)
{
- enum tofu_policy policy = get_policy (dbs, fingerprint, email, NULL);
-
char *fingerprint_pp;
int rc;
strlist_t strlist = NULL;
unsigned long signature_first_seen = 0;
unsigned long signature_most_recent = 0;
unsigned long signature_count = 0;
+ unsigned long signature_days = 0;
unsigned long encryption_first_done = 0;
unsigned long encryption_most_recent = 0;
unsigned long encryption_count = 0;
+ unsigned long encryption_days = 0;
int show_warning = 0;
- (void) user_id;
+ if (only_status_fd && ! is_status_enabled ())
+ return 0;
fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
rc = gpg_error (GPG_ERR_GENERAL);
goto out;
}
+ rc = gpgsql_exec_printf
+ (dbs->db, strings_collect_cb, &strlist, &err,
+ "select count (*) from\n"
+ " (select round(signatures.time / (24 * 60 * 60)) day\n"
+ " from signatures\n"
+ " left join bindings on signatures.binding = bindings.oid\n"
+ " where fingerprint = %Q and email = %Q\n"
+ " group by day);",
+ fingerprint, email);
+ if (rc)
+ {
+ log_error (_("error reading TOFU database: %s\n"), err);
+ print_further_info ("getting signature statistics (by day)");
+ sqlite3_free (err);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto out;
+ }
if (strlist)
{
- /* We expect exactly 3 elements. */
+ /* We expect exactly 4 elements. */
log_assert (strlist->next);
log_assert (strlist->next->next);
- log_assert (! strlist->next->next->next);
+ log_assert (strlist->next->next->next);
+ log_assert (! strlist->next->next->next->next);
- string_to_ulong (&signature_count, strlist->d, -1, __LINE__);
- string_to_ulong (&signature_first_seen, strlist->next->d, -1, __LINE__);
- string_to_ulong (&signature_most_recent,
+ string_to_ulong (&signature_days, strlist->d, -1, __LINE__);
+ string_to_ulong (&signature_count, strlist->next->d, -1, __LINE__);
+ string_to_ulong (&signature_first_seen,
strlist->next->next->d, -1, __LINE__);
+ string_to_ulong (&signature_most_recent,
+ strlist->next->next->next->d, -1, __LINE__);
free_strlist (strlist);
strlist = NULL;
rc = gpg_error (GPG_ERR_GENERAL);
goto out;
}
+ rc = gpgsql_exec_printf
+ (dbs->db, strings_collect_cb, &strlist, &err,
+ "select count (*) from\n"
+ " (select round(encryptions.time / (24 * 60 * 60)) day\n"
+ " from encryptions\n"
+ " left join bindings on encryptions.binding = bindings.oid\n"
+ " where fingerprint = %Q and email = %Q\n"
+ " group by day);",
+ fingerprint, email);
+ if (rc)
+ {
+ log_error (_("error reading TOFU database: %s\n"), err);
+ print_further_info ("getting encryption statistics (by day)");
+ sqlite3_free (err);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto out;
+ }
if (strlist)
{
- /* We expect exactly 3 elements. */
+ /* We expect exactly 4 elements. */
log_assert (strlist->next);
log_assert (strlist->next->next);
- log_assert (! strlist->next->next->next);
+ log_assert (strlist->next->next->next);
+ log_assert (! strlist->next->next->next->next);
- string_to_ulong (&encryption_count, strlist->d, -1, __LINE__);
- string_to_ulong (&encryption_first_done, strlist->next->d, -1, __LINE__);
- string_to_ulong (&encryption_most_recent,
+ string_to_ulong (&encryption_days, strlist->d, -1, __LINE__);
+ string_to_ulong (&encryption_count, strlist->next->d, -1, __LINE__);
+ string_to_ulong (&encryption_first_done,
strlist->next->next->d, -1, __LINE__);
+ string_to_ulong (&encryption_most_recent,
+ strlist->next->next->next->d, -1, __LINE__);
free_strlist (strlist);
strlist = NULL;
signature_count,
signature_first_seen,
signature_most_recent,
+ signature_days,
encryption_count,
encryption_first_done,
- encryption_most_recent);
+ encryption_most_recent,
+ encryption_days);
- if (!outfp)
+ if (!outfp && !only_status_fd)
{
estream_t fp;
char *msg;
log_fatal ("error creating memory stream: %s\n",
gpg_strerror (gpg_error_from_syserror()));
- es_fprintf (fp, _("%s: "), email);
-
- if (signature_count == 0)
- {
- es_fprintf (fp, _("Verified %ld signatures"), 0L);
- es_fputc ('\n', fp);
- }
- else
+ if (signature_count == 0 && encryption_count == 0)
{
- char *first_seen_ago_str = time_ago_str (now - signature_first_seen);
-
- /* TRANSLATORS: The final %s is replaced by a string like
- "7 months, 1 day, 5 minutes, 0 seconds". */
es_fprintf (fp,
- ngettext("Verified %ld signature in the past %s",
- "Verified %ld signatures in the past %s",
- signature_count),
- signature_count, first_seen_ago_str);
-
- xfree (first_seen_ago_str);
- }
-
- if (encryption_count == 0)
- {
- es_fprintf (fp, _(", and encrypted %ld messages"), 0L);
+ _("%s: Verified 0~signatures and encrypted 0~messages."),
+ email);
}
else
{
- char *first_done_ago_str = time_ago_str (now - encryption_first_done);
+ if (signature_count == 0)
+ es_fprintf (fp, _("%s: Verified 0 signatures."), email);
+ else
+ {
+ /* TRANSLATORS: The final %s is replaced by a string like
+ "7~months". */
+ char *ago_str = time_ago_str (now - signature_first_seen);
+ es_fprintf
+ (fp,
+ ngettext("%s: Verified %ld~signature in the past %s.",
+ "%s: Verified %ld~signatures in the past %s.",
+ signature_count),
+ email, signature_count, ago_str);
+ xfree (ago_str);
+ }
- /* TRANSLATORS: The final %s is replaced by a string like
- "7 months, 1 day, 5 minutes, 0 seconds". */
- es_fprintf (fp,
- ngettext(", and encrypted %ld message in the past %s",
- ", and encrypted %ld messages in the past %s",
- encryption_count),
- encryption_count, first_done_ago_str);
+ es_fputs (" ", fp);
- xfree (first_done_ago_str);
+ if (encryption_count == 0)
+ es_fprintf (fp, _("Encrypted 0 messages."));
+ else
+ {
+ char *ago_str = time_ago_str (now - encryption_first_done);
+
+ /* TRANSLATORS: The final %s is replaced by a string like
+ "7~months". */
+ es_fprintf (fp,
+ ngettext("Encrypted %ld~message in the past %s.",
+ "Encrypted %ld~messages in the past %s.",
+ encryption_count),
+ encryption_count, ago_str);
+ xfree (ago_str);
+ }
}
if (opt.verbose)
{
es_fputs (" ", fp);
- es_fputc ('(', fp);
- es_fprintf (fp, _("policy: %s"), tofu_policy_str (policy));
- es_fputs (").\n", fp);
+ es_fprintf (fp, _("(policy: %s)"), tofu_policy_str (policy));
}
- else
- es_fputs (".\n", fp);
+ es_fputs ("\n", fp);
{
if (rc)
return rc;
- log_assert (keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0);
+ log_assert (pk_is_primary (pk));
sig_digest = make_radix64_string (sig_digest_bin, sig_digest_bin_len);
fingerprint = hexfingerprint (pk, NULL, 0);
log_assert (c == 0);
rc = gpgsql_stepx
- (dbs->db, &dbs->s.register_insert, NULL, NULL, &err,
+ (dbs->db, &dbs->s.register_signature, NULL, NULL, &err,
"insert into signatures\n"
" (binding, sig_digest, origin, sig_time, time)\n"
" values\n"
}
if (/* We need the key block to find the primary key. */
- keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) != 0
+ ! pk_is_primary (pk)
/* We need the key block to find all user ids. */
|| ! user_id_list)
kb = get_pubkeyblock (pk->keyid);
/* Make sure PK is a primary key. */
- if (keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) != 0)
+ if (! pk_is_primary (pk))
pk = kb->pkt->pkt.public_key;
if (! user_id_list)
free_user_id_list = 1;
if (! user_id_list)
- log_info (_("WARNING: Encrypting to %s, which has no"
+ log_info (_("WARNING: Encrypting to %s, which has no "
"non-revoked user ids.\n"),
keystr (pk->keyid));
}
}
rc = gpgsql_stepx
- (dbs->db, &dbs->s.register_insert, NULL, NULL, &err,
+ (dbs->db, &dbs->s.register_encryption, NULL, NULL, &err,
"insert into encryptions\n"
" (binding, time)\n"
" values\n"
tofu_dbs_t dbs;
char *fingerprint;
char *email;
+ enum tofu_policy policy;
if (!*user_id)
return 0; /* No TOFU stats possible for an empty ID. */
fingerprint = hexfingerprint (pk, NULL, 0);
email = email_from_user_id (user_id);
+ policy = get_policy (dbs, pk, fingerprint, user_id, email, NULL, now);
- show_statistics (dbs, fingerprint, email, user_id, fp, now);
+ show_statistics (dbs, fingerprint, email, policy, fp, 0, now);
xfree (email);
xfree (fingerprint);
bindings_valid ++;
if (may_ask && tl != TRUST_ULTIMATE && tl != TRUST_EXPIRED)
- need_warning |=
- show_statistics (dbs, fingerprint, email, user_id->d, NULL, now);
+ {
+ enum tofu_policy policy =
+ get_policy (dbs, pk, fingerprint, user_id->d, email, NULL, now);
+
+ need_warning |=
+ show_statistics (dbs, fingerprint, email, policy, NULL, 0, now);
+ }
if (tl == TRUST_NEVER)
trust_level = TRUST_NEVER;
if (DBG_TRUST)
log_debug ("Setting TOFU policy for %s to %s\n",
keystr (pk->keyid), tofu_policy_str (policy));
- if (keyid_cmp (pk_main_keyid (pk), pk_keyid (pk)) != 0)
+ if (! pk_is_primary (pk))
log_bug ("%s: Passed a subkey, but expecting a primary key.\n", __func__);
fingerprint = hexfingerprint (pk, NULL, 0);
email = email_from_user_id (user_id->name);
err = record_binding (dbs, fingerprint, email, user_id->name,
- policy, NULL, 1, now);
+ policy, TOFU_POLICY_NONE, NULL, 0, 1, now);
if (err)
{
log_error (_("error setting policy for key %s, user id \"%s\": %s"),
return err;
}
-/* Set the TOFU policy for all non-revoked user ids in the KEY with
- the key id KEYID to POLICY.
-
- If no key is available with the specified key id, then this
- function returns GPG_ERR_NO_PUBKEY.
-
- Returns 0 on success and an error code otherwise. */
-gpg_error_t
-tofu_set_policy_by_keyid (ctrl_t ctrl, u32 *keyid, enum tofu_policy policy)
-{
- kbnode_t keyblock = get_pubkeyblock (keyid);
- if (! keyblock)
- return gpg_error (GPG_ERR_NO_PUBKEY);
-
- return tofu_set_policy (ctrl, keyblock, policy);
-}
-
/* Return the TOFU policy for the specified binding in *POLICY. If no
policy has been set for the binding, sets *POLICY to
TOFU_POLICY_NONE.
tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id,
enum tofu_policy *policy)
{
+ time_t now = gnupg_get_time ();
tofu_dbs_t dbs;
char *fingerprint;
char *email;
/* Make sure PK is a primary key. */
- log_assert (pk->main_keyid[0] == pk->keyid[0]
- && pk->main_keyid[1] == pk->keyid[1]);
+ log_assert (pk_is_primary (pk));
dbs = opendbs (ctrl);
if (! dbs)
email = email_from_user_id (user_id->name);
- *policy = get_policy (dbs, fingerprint, email, NULL);
+ *policy = get_policy (dbs, pk, fingerprint, user_id->name, email, NULL, now);
xfree (email);
xfree (fingerprint);
return gpg_error (GPG_ERR_GENERAL);
return 0;
}
+
+gpg_error_t
+tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
+{
+ tofu_dbs_t dbs;
+ PKT_public_key *pk;
+ char *fingerprint;
+ char *sqlerr = NULL;
+ int rc;
+
+ /* Make sure PK is a primary key. */
+ setup_main_keyids (kb);
+ pk = kb->pkt->pkt.public_key;
+ log_assert (pk_is_primary (pk));
+
+ dbs = opendbs (ctrl);
+ if (! dbs)
+ {
+ log_error (_("error opening TOFU database: %s\n"),
+ gpg_strerror (GPG_ERR_GENERAL));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ fingerprint = hexfingerprint (pk, NULL, 0);
+
+ rc = gpgsql_stepx (dbs->db, NULL, NULL, NULL, &sqlerr,
+ "update bindings set effective_policy = ?"
+ " where fingerprint = ?;",
+ GPGSQL_ARG_INT, (int) TOFU_POLICY_NONE,
+ GPGSQL_ARG_STRING, fingerprint,
+ GPGSQL_ARG_END);
+ xfree (fingerprint);
+
+ if (rc == _tofu_GET_POLICY_ERROR)
+ return gpg_error (GPG_ERR_GENERAL);
+ return 0;
+}
POLICY. */
gpg_error_t tofu_set_policy (ctrl_t ctrl, kbnode_t kb, enum tofu_policy policy);
-/* Set the TOFU policy for all non-revoked users in the key with the
- key id KEYID to POLICY. */
-gpg_error_t tofu_set_policy_by_keyid (ctrl_t ctrl,
- u32 *keyid, enum tofu_policy policy);
-
/* Return the TOFU policy for the specified binding in *POLICY. */
gpg_error_t tofu_get_policy (ctrl_t ctrl,
PKT_public_key *pk, PKT_user_id *user_id,
/* Release all of the resources associated with a DB meta-handle. */
void tofu_closedbs (ctrl_t ctrl);
+/* Whenever a key is modified (e.g., a user id is added or revoked, a
+ * new signature, etc.), this function should be called to cause TOFU
+ * to update its world view. */
+gpg_error_t tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb);
+
#endif /*G10_TOFU_H*/
return _("[ expired]");
else if(key)
{
- switch (get_validity (ctrl, key, uid, NULL, 0) & TRUST_MASK)
+ switch (get_validity (ctrl, NULL, key, uid, NULL, 0) & TRUST_MASK)
{
case TRUST_UNKNOWN: return _("[ unknown]");
case TRUST_EXPIRED: return _("[ expired]");
/*
- * Return the validity information for PK. If the namehash is not
- * NULL, the validity of the corresponding user ID is returned,
- * otherwise, a reasonable value for the entire key is returned.
+ * Return the validity information for KB/PK (at least one must be
+ * non-NULL). If the namehash is not NULL, the validity of the
+ * corresponding user ID is returned, otherwise, a reasonable value
+ * for the entire key is returned.
*/
unsigned int
-get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid,
+get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid,
PKT_signature *sig, int may_ask)
{
int rc;
u32 kid[2];
PKT_public_key *main_pk;
+ if (kb && pk)
+ log_assert (keyid_cmp (pk_main_keyid (pk),
+ pk_main_keyid (kb->pkt->pkt.public_key)) == 0);
+
+ if (! pk)
+ {
+ log_assert (kb);
+ pk = kb->pkt->pkt.public_key;
+ }
+
if (uid)
namehash_from_uid (uid);
if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1])
{
/* This is a subkey - get the mainkey. */
- main_pk = xmalloc_clear (sizeof *main_pk);
- rc = get_pubkey (main_pk, pk->main_keyid);
- if (rc)
+ if (kb)
+ main_pk = kb->pkt->pkt.public_key;
+ else
{
- char *tempkeystr = xstrdup (keystr (pk->main_keyid));
- log_error ("error getting main key %s of subkey %s: %s\n",
- tempkeystr, keystr (kid), gpg_strerror (rc));
- xfree (tempkeystr);
- validity = TRUST_UNKNOWN;
- goto leave;
- }
+ main_pk = xmalloc_clear (sizeof *main_pk);
+ rc = get_pubkey (main_pk, pk->main_keyid);
+ if (rc)
+ {
+ char *tempkeystr = xstrdup (keystr (pk->main_keyid));
+ log_error ("error getting main key %s of subkey %s: %s\n",
+ tempkeystr, keystr (kid), gpg_strerror (rc));
+ xfree (tempkeystr);
+ validity = TRUST_UNKNOWN;
+ goto leave;
+ }
+ }
}
else
main_pk = pk;
#ifdef NO_TRUST_MODELS
validity = TRUST_UNKNOWN;
#else
- validity = tdb_get_validity_core (ctrl, pk, uid, main_pk, sig, may_ask);
+ validity = tdb_get_validity_core (ctrl, kb, pk, uid, main_pk, sig, may_ask);
#endif
leave:
validity = ((validity & (~TRUST_MASK | TRUST_FLAG_PENDING_CHECK))
| TRUST_EXPIRED);
- if (main_pk != pk)
+ if (main_pk != pk && !kb)
free_public_key (main_pk);
return validity;
}
int
-get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid)
+get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk,
+ PKT_user_id *uid)
{
int trustlevel;
+ if (kb && pk)
+ log_assert (keyid_cmp (pk_main_keyid (pk),
+ pk_main_keyid (kb->pkt->pkt.public_key)) == 0);
+
+ if (! pk && kb)
+ pk = kb->pkt->pkt.public_key;
if (!pk)
return '?'; /* Just in case a NULL PK is passed. */
- trustlevel = get_validity (ctrl, pk, uid, NULL, 0);
+ trustlevel = get_validity (ctrl, kb, pk, uid, NULL, 0);
if ((trustlevel & TRUST_FLAG_REVOKED))
return 'r';
return trust_letter (trustlevel);
if (!pk)
return "err"; /* Just in case a NULL PK is passed. */
- trustlevel = get_validity (ctrl, pk, uid, NULL, 0);
+ trustlevel = get_validity (ctrl, NULL, pk, uid, NULL, 0);
if ((trustlevel & TRUST_FLAG_REVOKED))
return _("revoked");
return trust_value_to_string (trustlevel);
return 0;
}
+
+/* Return the list of ultimately trusted keys. */
+struct key_item *
+tdb_utks (void)
+{
+ return utk_list;
+}
\f
/*********************************************
*********** TrustDB stuff *******************
}
/*
- * Return the validity information for PK. This is the core of
- * get_validity. If SIG is not NULL, then the trust is being
- * evaluated in the context of the provided signature. This is used
- * by the TOFU code to record statistics.
+ * Return the validity information for KB/PK (at least one of them
+ * must be non-NULL). This is the core of get_validity. If SIG is
+ * not NULL, then the trust is being evaluated in the context of the
+ * provided signature. This is used by the TOFU code to record
+ * statistics.
*/
unsigned int
tdb_get_validity_core (ctrl_t ctrl,
+ kbnode_t kb,
PKT_public_key *pk, PKT_user_id *uid,
PKT_public_key *main_pk,
PKT_signature *sig,
unsigned int tofu_validity = TRUST_UNKNOWN;
#endif
unsigned int validity = TRUST_UNKNOWN;
+ int free_kb = 0;
+
+ if (kb && pk)
+ log_assert (keyid_cmp (pk_main_keyid (pk),
+ pk_main_keyid (kb->pkt->pkt.public_key)) == 0);
+
+ if (! pk)
+ {
+ log_assert (kb);
+ pk = kb->pkt->pkt.public_key;
+ }
#ifndef USE_TOFU
(void)sig;
#ifdef USE_TOFU
if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
{
- kbnode_t kb = NULL;
kbnode_t n = NULL;
strlist_t user_id_list = NULL;
int done = 0;
/* If the caller didn't supply a user id then use all uids. */
if (! uid)
- kb = n = get_pubkeyblock (main_pk->keyid);
+ {
+ if (! kb)
+ {
+ kb = get_pubkeyblock (main_pk->keyid);
+ free_kb = 1;
+ }
+ n = kb;
+ }
if (DBG_TRUST && sig && sig->signers_uid)
log_debug ("TOFU: only considering user id: '%s'\n",
may_ask);
free_strlist (user_id_list);
- release_kbnode (kb);
+ if (free_kb)
+ release_kbnode (kb);
}
#endif /*USE_TOFU*/
void check_trustdb_stale (ctrl_t ctrl);
void check_or_update_trustdb (ctrl_t ctrl);
-unsigned int get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid,
+unsigned int get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk,
+ PKT_user_id *uid,
PKT_signature *sig, int may_ask);
-int get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid);
+int get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk,
+ PKT_user_id *uid);
const char *get_validity_string (ctrl_t ctrl,
PKT_public_key *pk, PKT_user_id *uid);
void tdb_register_trusted_key (const char *string);
/* Returns whether KID is on the list of ultimately trusted keys. */
int tdb_keyid_is_utk (u32 *kid);
+/* Return the list of ultimately trusted keys. The caller must not
+ * modify this list nor must it free the list. */
+struct key_item *tdb_utks (void);
void check_trustdb (ctrl_t ctrl);
void update_trustdb (ctrl_t ctrl);
int setup_trustdb( int level, const char *dbname );
int tdb_cache_disabled_value (PKT_public_key *pk);
-unsigned int tdb_get_validity_core (ctrl_t ctrl,
+unsigned int tdb_get_validity_core (ctrl_t ctrl, kbnode_t kb,
PKT_public_key *pk, PKT_user_id *uid,
PKT_public_key *main_pk,
PKT_signature *sig, int may_ask);
msgid "quickly revoke a user-id"
msgstr "genera un nou parell de claus"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "genera un nou parell de claus"
+
msgid "full featured key pair generation"
msgstr ""
# Crec q (A)lice (orig.), (B)ob (dest.), etc. són noms usats pel Zimmerman
# en el manual original de PGP. A, B, C... ivb
# En efecte. Idem per a Mallory més endavant. Els deixe com a l'original. jm
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Exemples:\n"
"\n"
" -se -r Bob [fitxer] signa i xifra per a l'usuari Bob\n"
-" --clearsign [fitxer] crea una signatura en text clar\n"
+" --clear-sign [fitxer] crea una signatura en text clar\n"
" --detach-sign [fitxer] crea una signatura separada\n"
" --list-keys [noms] mostra claus\n"
" --fingerprint [noms] mostra empremtes digitals\n"
msgstr ""
"AVÍS: s'han donat destinataris (-r) sense usar xifratge de clau pública\n"
-msgid "--store [filename]"
-msgstr "--store [nom_del_fitxer]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [nom_del_fitxer]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "ha fallat el desxifratge: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [nom_del_fitxer]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [nom_del_fitxer]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "no podeu usar %s mentre esteu en mode %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [nom_del_fitxer]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [nom_del_fitxer]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [nom_del_fitxer]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "no podeu usar %s mentre esteu en mode %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [nom_del_fitxer]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [nom_del_fitxer]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [nom_del_fitxer]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key user-id"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key user-id"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key user-id [ordres]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key user-id"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "l'enviament al servidor de claus ha fallat: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[nom_del_fitxer]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Endavant, escriviu el missatge...\n"
msgid "Nothing to sign.\n"
msgstr "No hi ha res que signar amb la clau %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s no és un joc de caràcters vàlid\n"
+
msgid "Digest: "
msgstr "Resum: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Algoritmes suportats:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "error en la creació de la contrasenya: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "error mentre s'enviava a «%s»: %s\n"
msgstr "%s: error en escriure el registre de directoris: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "error mentre s'enviava a «%s»: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "error en la lectura de «%s»: %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "llista claus"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [fitxers]|imprimeix resums de missatges"
+msgstr[1] "|algo [fitxers]|imprimeix resums de missatges"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "xifrat amb %lu contrasenyes\n"
+msgstr[1] "xifrat amb %lu contrasenyes\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [fitxers]|imprimeix resums de missatges"
+msgstr[1] "|algo [fitxers]|imprimeix resums de missatges"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [fitxers]|imprimeix resums de missatges"
msgstr[1] "|algo [fitxers]|imprimeix resums de missatges"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [fitxers]|imprimeix resums de missatges"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "error mentre s'escrivia l'anell «%s»: %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "error en la lectura de «%s»: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "error en la creació de la contrasenya: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
# Werner FIXME: use ngettext. jm
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "S'han esborrat %d signatures.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [fitxers]|imprimeix resums de missatges"
msgstr[1] "|algo [fitxers]|imprimeix resums de missatges"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "xifrat amb %lu contrasenyes\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [fitxers]|imprimeix resums de missatges"
msgstr[1] "|algo [fitxers]|imprimeix resums de missatges"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Política: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "error mentre s'enviava a «%s»: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "error en la lectura de «%s»: %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "error en la lectura de «%s»: %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [nom_del_fitxer]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [nom_del_fitxer]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [nom_del_fitxer]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nom_del_fitxer]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [nom_del_fitxer]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nom_del_fitxer]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nom_del_fitxer]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [nom_del_fitxer]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [nom_del_fitxer]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [nom_del_fitxer]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key user-id"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key user-id"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key user-id [ordres]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key user-id"
+
+#~ msgid "[filename]"
+#~ msgstr "[nom_del_fitxer]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "no s'ha pogut eliminar el bloc de claus: %s\n"
msgid "quickly revoke a user-id"
msgstr "rychle přidat novou identitu uživatele"
+#, fuzzy
+#| msgid "quickly generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "rychle vytvořit nový pár klíčů"
+
msgid "full featured key pair generation"
msgstr "komplexní vytvoření páru klíčů"
"@\n"
"(Pro kompletní seznam všech příkazů a možností použijte manuálové stránky.)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
msgstr ""
"@\n"
" -se -r Bob [soubor] podepsat a zašifrovat pro uživatele Bob\n"
-" --clearsign [soubor] vytvořit podpis čitelného dokumentu\n"
+" --clear-sign [soubor] vytvořit podpis čitelného dokumentu\n"
" --detach-sign [soubor] vytvořit podpis oddělený od dokumentu\n"
" --list-keys [jména] vypsat klíče\n"
" --fingerprint [jména] vypsat otisky\n"
msgstr ""
"VAROVÁNÍ: specifikován adresát (-r) bez použití šifrování s veřejným klíčem\n"
-msgid "--store [filename]"
-msgstr "--store [jméno souboru]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [jméno souboru]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "symetrické šifrování „%s“ se nepovedlo: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [jméno souboru]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [jméno souboru]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "nelze použít --symmetric --encrypt s příkazem --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "nelze použít --symmetric --encrypt v módu %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [jméno souboru]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [jméno souboru]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [jméno souboru]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "nelze použít --symmetric --sign --encrypt s příkazem --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "nelze použít --symmetric --sign --encrypt v módu %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [jméno souboru]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [jméno souboru]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [jméno souboru]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key id_uživatele"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key id_uživatele"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key id_uživatele [příkazy]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <id-uživatele>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "odeslání na keyserver se nezdařilo: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr "„%s“ nevypadá jako platné ID klíče, otisk klíče nebo keygrip\n"
-msgid "[filename]"
-msgstr "[jméno souboru]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Začněte psát svou zprávu…\n"
msgid "Nothing to sign.\n"
msgstr "Nic na podepsání.\n"
+#, fuzzy, c-format
+#| msgid "'%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "„%s“ není platná doba expirace podpisu\n"
+
msgid "Digest: "
msgstr "Hash: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"Použijte jej pro odvolání tohoto klíče v případě zneužití nebo ztráty\n"
"soukromého klíče. Avšak bude-li soukromý klíč stále přístupný, bude\n"
msgstr "nepodporovaný algoritmus: %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "chyba při vytváření dočasného souboru: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending data: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "chyba při odesílání dat: %s\n"
msgstr "chyba při inicializaci čtecího objektu: %s\n"
#, fuzzy, c-format
+#| msgid "error sending data: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "chyba při odesílání dat: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening '%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "chyba při otevírání „%s“: %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "vypsat hash zprávy"
+msgstr[1] "vypsat hash zprávy"
+msgstr[2] "vypsat hash zprávy"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "zašifrováno s heslem %lu\n"
+msgstr[1] "zašifrováno s heslem %lu\n"
+msgstr[2] "zašifrováno s heslem %lu\n"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "vypsat hash zprávy"
+msgstr[1] "vypsat hash zprávy"
+msgstr[2] "vypsat hash zprávy"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "vypsat hash zprávy"
msgstr[1] "vypsat hash zprávy"
msgstr[2] "vypsat hash zprávy"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Messages encrypted in the past: %d."
+msgstr "vypsat hash zprávy"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error setting OCSP target: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "chyba při nastavování cíle OCSP: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "chyba při vytváření roury: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Smazáno %d podpisů.\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "vypsat hash zprávy"
msgstr[1] "vypsat hash zprávy"
msgstr[2] "vypsat hash zprávy"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "zašifrováno s heslem %lu\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "vypsat hash zprávy"
msgstr[1] "vypsat hash zprávy"
msgstr[2] "vypsat hash zprávy"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "platnost: %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "chyba při odesílání dat: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Vezměte na vědomí, že určení skupiny se ignoruje\n"
+#, fuzzy, c-format
+#| msgid "error closing '%s': %s\n"
+msgid "error closing '%s'\n"
+msgstr "chyba při uzavírání „%s“: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error hashing '%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "chyba při výpočtu haše „%s“: %s\n"
+
msgid "list all components"
msgstr "vypsat všechny komponenty"
msgid "apply global default values"
msgstr "aplikovat globální implicitní hodnoty"
+#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|SOUBOR|vzít politiky ze SOUBORU"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "získat adresáře s nastavením @GPGCONF@"
"Syntaxe: gpg-check-pattern [volby] soubor_se_vzorem\n"
"Prověří heslo zadané na vstupu proti souboru se vzory\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [jméno souboru]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [jméno souboru]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [jméno souboru]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [jméno souboru]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [jméno souboru]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [jméno souboru]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [jméno souboru]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [jméno souboru]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [jméno souboru]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [jméno souboru]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key id_uživatele"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key id_uživatele"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key id_uživatele [příkazy]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <id-uživatele>"
+
+#~ msgid "[filename]"
+#~ msgstr "[jméno souboru]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "výroba stínového klíče se nezdařila: %s\n"
msgid "quickly revoke a user-id"
msgstr "opret et nyt nøglepar"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "opret et nyt nøglepar"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Se manualsiden for en fuldstændig liste over alle kommandoer og tilvalg)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Eksempler:\n"
"\n"
" -se -r Mikael [fil] underskriv og krypter for bruger Mikael\n"
-" --clearsign [fil] lav en ren tekstunderskrift\n"
+" --clear-sign [fil] lav en ren tekstunderskrift\n"
" --detach-sign [fil] lav en separat underskrift\n"
" --list-keys [navne] vis nøgler\n"
" --fingerprint [navne] vis fingeraftryk\n"
msgstr ""
"ADVARSEL: modtagere (-r) angivet uden brug af offentlig nøglekryptering\n"
-msgid "--store [filename]"
-msgstr "--store [filnavn]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [filnavn]"
-
#, fuzzy, c-format
#| msgid "symmetric encryption of `%s' failed: %s\n"
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "symmetrisk kryptering af »%s« mislykkedes: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [filnavn]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [filnavn]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "du kan ikke bruge --symmetric --encrypt med --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "du kan ikke bruge --symmetric --encrypt i tilstanden %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [filnavn]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [filnavn]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [filnavn]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "du kan ikke bruge --symmetric --sign --encrypt med --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "du kan ikke bruge --symmetric --sign --encrypt i tilstanden %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [filnavn]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [filnavn]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [filnavn]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key bruger-id"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key bruger-id"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key bruger-id [kommandoer]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <bruger-id>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "nøgleserver send mislykkedes: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[filnavn]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Gå til sagen og skriv meddelelsen ...\n"
msgid "Nothing to sign.\n"
msgstr "Intet at underskrive med nøgle %s\n"
+#, fuzzy, c-format
+#| msgid "`%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "»%s« er ikke et gyldigt underskriftudløb\n"
+
msgid "Digest: "
msgstr "Sammendrag: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
msgstr "ikke understøttet algoritme: %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "fejl ved oprettelse af midlertidig fil: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending %s command: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "fejl under afsendelse af %s-kommando: %s\n"
msgstr "%s: fejl ved skrivning af mappepost: %s\n"
#, fuzzy, c-format
+#| msgid "error sending %s command: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "fejl under afsendelse af %s-kommando: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening `%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "fejl ved åbning af »%s«: %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "vis beskedsammendrag"
+msgstr[1] "vis beskedsammendrag"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "krypteret med %lu adgangsfraser\n"
+msgstr[1] "krypteret med %lu adgangsfraser\n"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "vis beskedsammendrag"
+msgstr[1] "vis beskedsammendrag"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "vis beskedsammendrag"
msgstr[1] "vis beskedsammendrag"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Messages encrypted in the past: %d."
+msgstr "vis beskedsammendrag"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error storing flags: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "fejl ved lagring af flag: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "fejl ved oprettelse af datakanal: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Slettede %d underskrifter.\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "vis beskedsammendrag"
msgstr[1] "vis beskedsammendrag"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "krypteret med %lu adgangsfraser\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "vis beskedsammendrag"
msgstr[1] "vis beskedsammendrag"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "validitet: %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "fejl under afsendelse af %s-kommando: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Bemærk at gruppespecifikationer ignoreres\n"
+#, fuzzy, c-format
+#| msgid "error closing %s: %s\n"
+msgid "error closing '%s'\n"
+msgstr "fejl ved lukning af %s: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error in `%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "fejl i »%s«: %s\n"
+
msgid "list all components"
msgstr "vis alle komponenter"
msgstr "anvend globale standardværdier"
#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FILE|tag politikinformation fra FIL"
+
+#, fuzzy
#| msgid "get the configuration directories for gpgconf"
msgid "get the configuration directories for @GPGCONF@"
msgstr "hent konfigurationsmapperne for gpgconf"
"Syntaks: gpg-check-pattern [tilvalg] mønsterfil\n"
"Kontroller en adgangsfrase angivet på stdin mod mønsterfilen\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [filnavn]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [filnavn]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [filnavn]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [filnavn]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [filnavn]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [filnavn]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [filnavn]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [filnavn]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [filnavn]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [filnavn]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key bruger-id"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key bruger-id"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key bruger-id [kommandoer]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <bruger-id>"
+
+#~ msgid "[filename]"
+#~ msgstr "[filnavn]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "skygge for nøgle mislykkedes: %s\n"
msgstr ""
"Project-Id-Version: gnupg-2.1.0\n"
"Report-Msgid-Bugs-To: translations@gnupg.org\n"
-"PO-Revision-Date: 2016-11-18 15:40+0100\n"
+"PO-Revision-Date: 2016-12-20 11:14+0100\n"
"Last-Translator: Werner Koch <wk@gnupg.org>\n"
"Language-Team: German <de@li.org>\n"
"Language: de\n"
msgid "quickly revoke a user-id"
msgstr "Schnell eine User-ID widerrufen"
+msgid "quickly set a new expiration date"
+msgstr "Schnell ein neues Ablaufdatum setzen"
+
msgid "full featured key pair generation"
msgstr "Ein neues Schlüsselpaar erzeugen (alle Optionen)"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Beispiele:\n"
"\n"
" -se -r Bob [Datei] Signieren und verschlüsseln für Benutzer Bob\n"
-" --clearsign [Datei] Eine Klartextsignatur erzeugen\n"
+" --clear-sign [Datei] Eine Klartextsignatur erzeugen\n"
" --detach-sign [Datei] Eine abgetrennte Signatur erzeugen\n"
" --list-keys [Namen] Schlüssel anzeigen\n"
" --fingerprint [Namen] \"Fingerabdrücke\" anzeigen\n"
msgstr ""
"WARNUNG: Empfänger (-r) angegeben ohne Verwendung von Public-Key-Verfahren\n"
-msgid "--store [filename]"
-msgstr "--store [Dateiname]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [Dateiname]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "Symmetrische Entschlüsselung von `%s' fehlgeschlagen: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [Dateiname]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [Dateiname]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
"--symmetric --encrypt kann nicht zusammen mit --s2k-mode 0 verwendet werden\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "Im %s-Modus kann --symmetric --encrypt nicht verwendet werden.\n"
-msgid "--sign [filename]"
-msgstr "--sign [Dateiname]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [Dateiname]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [Dateiname]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
"--symmetric --sign --encrypt kann nicht zusammen mit --s2k-mode 0 verwendet "
msgstr ""
"Im %s-Modus kann --symmetric --sign --encrypt nicht verwendet werden.\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [Dateiname]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [Dateiname]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [Dateiname]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key User-ID"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key User-ID"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key User-ID [Befehle]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd User-ID"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "Senden an Schlüsselserver fehlgeschlagen: %s\n"
"'%s\" sieht nicht nach einer gültigen Schlüssel-ID, einem Fingerabdruck oder "
"einem \"Keygrip\" aus\n"
-msgid "[filename]"
-msgstr "[Dateiname]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Auf geht's - Botschaft eintippen ...\n"
msgid "Nothing to sign.\n"
msgstr "Nichts zu beglaubigen\n"
+#, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "'%s' ist kein gültiges Ablaufdatum\n"
+
msgid "Digest: "
msgstr "Digest: "
"Dieser Schlüssel gehört uns (da wir nämlich den geheimen Schlüssel dazu "
"haben)\n"
-#, fuzzy, c-format
-#| msgid "root certificate has now been marked as trusted\n"
+#, c-format
msgid "%s: This key is bad! It has been marked as untrusted!\n"
-msgstr "Das Wurzelzertifikat wurde nun als vertrauenswürdig markiert\n"
+msgstr ""
+"%s: Der Schlüssel ist gefälscht! Er wurde als nicht vertrauenswürdig "
+"markiert.\n"
-#, fuzzy
-#| msgid ""
-#| "It is NOT certain that the key belongs to the person named\n"
-#| "in the user ID. If you *really* know what you are doing,\n"
-#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
-"Es ist NICHT sicher, daß der Schlüssel zu dem in der User-ID\n"
-"Genannten gehört. Wenn Sie *wirklich* wissen, was Sie tun,\n"
-"können Sie die nächste Frage mit ja beantworten\n"
+"Der Schlüssel ist gefälscht! Er wurde als als nicht vertrauenswürdig\n"
+"markiert. Wenn Sie *wirklich* wissen, was Sie tun, können Sie die\n"
+"nächste Frage mit ja beantworten.\n"
msgid ""
"It is NOT certain that the key belongs to the person named\n"
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"Benutzen Sie es, um einen Schlüssel zu widerrufen, falls der private\n"
"Schlüssel verloren wurde oder kompromittiert ist. Falls jedoch auf\n"
"den privaten Schlüssel noch zugegriffen werden kann, so ist es besser,\n"
"ein neues Widerrufszertifikat zu erzeugen, um den Grund des Widerrufs\n"
"mit angeben zu können. Weitere Informationen finden Sie im GnuPG\n"
-"Handbuch unter der Beschreibung des gpg Kommandos \"--gen-revoke\"."
+"Handbuch unter der Beschreibung des gpg Kommandos \"--generate-revocation\"."
msgid ""
"To avoid an accidental use of this file, a colon has been inserted\n"
msgid "unsupported TOFU database version: %s\n"
msgstr "Nicht unterstützte TOFU Datenbank Version: %s\n"
+#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "Fehler beim Erstellen einer temporären Datei: %s\n"
+
+msgid "TOFU DB error"
+msgstr "TOFU (Trust on First Use) database error"
+
#, c-format
msgid "error reading TOFU database: %s\n"
msgstr "Fehler beim Lesen der TOFU Datenbank: %s\n"
msgid "error initializing TOFU database: %s\n"
msgstr "Fehler beim Initialisieren der TOFU Datenbank: %s\n"
+#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "Fehler beim Erstellen einer temporären Datei: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
#, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "Fehler beim Öffner der TOFU Datenbank '%s': %s\n"
#, fuzzy, c-format
#| msgid "The email address \"%s\" is associated with %d key:\n"
#| msgid_plural "The email address \"%s\" is associated with %d keys:\n"
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr "Die Email-Adresse \"%s\" ist mit einem Schlüssel assoziert:\n"
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] "Die Email-Adresse \"%s\" ist mit einem Schlüssel assoziert:\n"
+msgstr[1] "Die Email-Adresse \"%s\" ist mit einem Schlüssel assoziert:\n"
#, fuzzy
#| msgid ""
#, fuzzy, c-format
#| msgid "Verified %ld messages signed by \"%s\"."
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
msgstr[0] "%ld überprüfte Nachrichten von \"%s\"."
msgstr[1] "%ld überprüfte Nachrichten von \"%s\"."
-#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
-msgstr[0] ""
-msgstr[1] ""
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "Verschlüsselt mit %lu Passphrases\n"
+msgstr[1] "Verschlüsselt mit %lu Passphrases\n"
+
+#, fuzzy, c-format
+#| msgid "%ld message signed in the future."
+#| msgid_plural "%ld messages signed in the future."
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "%ld Nachricht in der Zukunft signiert."
+msgstr[1] "%ld Nachrichten in der Zukunf signiert."
+
+#, fuzzy, c-format
+#| msgid "%ld message signed in the future."
+#| msgid_plural "%ld messages signed in the future."
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
+msgstr[0] "%ld Nachricht in der Zukunft signiert."
+msgstr[1] "%ld Nachrichten in der Zukunf signiert."
#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
msgstr[0] " innerhalb des letzten Tages."
msgstr[1] " innerhalb der letzten %ld Tage."
#, fuzzy, c-format
+#| msgid " over the past %ld day."
+#| msgid_plural " over the past %ld days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
+msgstr[0] " innerhalb des letzten Tages."
+msgstr[1] " innerhalb der letzten %ld Tage."
+
+#, fuzzy, c-format
+#| msgid " over the past %ld month."
+#| msgid_plural " over the past %ld months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
+msgstr[0] " innerhalb des letzten Monats."
+msgstr[1] " innerhalb der letzten %ld Monate."
+
+#, fuzzy, c-format
#| msgid " over the past %ld month."
#| msgid_plural " over the past %ld months."
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] " innerhalb des letzten Monats."
msgstr[1] " innerhalb der letzten %ld Monate."
#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] " innerhalb des letzten Tages."
msgstr[1] " innerhalb der letzten %ld Tage."
-#, fuzzy
+#, fuzzy, c-format
+#| msgid " over the past %ld day."
+#| msgid_plural " over the past %ld days."
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] " innerhalb des letzten Tages."
+msgstr[1] " innerhalb der letzten %ld Tage."
+
+#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " in the past."
+msgid "Messages verified in the past: %d."
msgstr " innerhalb des letzten Tages."
+#, fuzzy, c-format
+#| msgid ""
+#| "Verified %ld message signed by \"%s\"\n"
+#| "in the past %s."
+#| msgid_plural ""
+#| "Verified %ld messages signed by \"%s\"\n"
+#| "in the past %s."
+msgid "Messages encrypted in the past: %d."
+msgstr ""
+"%ld überprüfte Nachricht von \"%s\"\n"
+"in den letzten %s."
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgid "resetting keydb: %s\n"
msgstr "Fehler beim Schreiben des Schlüssels: %s\n"
-#, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+#, fuzzy, c-format
+#| msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "Fehler beim Setzen der TOFU Binding Vertrauensstufe auf %s\n"
#, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "Fehler beim Ändern der TOFU Richtlinie: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
-#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+#, fuzzy, c-format
+#| msgid "%d~year"
+#| msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] "%d~Jahr"
msgstr[1] "%d~Jahre"
-#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+#, fuzzy, c-format
+#| msgid "%d~month"
+#| msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] "%d~Monat"
msgstr[1] "%d~Monate"
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
+msgstr[0] ""
+msgstr[1] ""
+
+#, fuzzy, c-format
+#| msgid "%d~day"
+#| msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] "%d~Tag"
msgstr[1] "%d~Tage"
-#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+#, fuzzy, c-format
+#| msgid "%d~hour"
+#| msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] "%d~Stunde"
msgstr[1] "%d~Stunden"
-#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+#, fuzzy, c-format
+#| msgid "%d~minute"
+#| msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] "%d~Minute"
msgstr[1] "%d~Minuten"
-#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+#, fuzzy, c-format
+#| msgid "%d~second"
+#| msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] "%d~Sekunde"
msgstr[1] "%d~Sekunden"
-#, c-format
-msgid "%s: "
+#, fuzzy, c-format
+#| msgid "TOFU: few signatures %d message %s"
+#| msgid_plural "TOFU: few signatures %d messages %s"
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
+"WARNUNG: Falls sie glauben, mehr als %d mit diesem Schlüssel signierte\n"
+"Nachricht erhalten zu haben, so kann es sich bei diesem Schlüssel um\n"
+"eine Fälschung handeln! Prüfen Sie die Email-Adresse genau auf kleine\n"
+"Variationen (z.B. zusätzliche Leerzeichen). Falls Ihnen der Schlüssel\n"
+"suspekt erscheint, so benutzen Sie '%s' um den Schlüssel als Fälschung\n"
+"zu markieren."
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "%d Beglaubigungen entfernt.\n"
#, fuzzy, c-format
#| msgid_plural ""
#| "Verified %ld messages signed by \"%s\"\n"
#| "in the past %s."
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] ""
"%ld überprüfte Nachricht von \"%s\"\n"
"in den letzten %s."
"%ld überprüfte Nachrichten von \"%s\"\n"
"in den letzten %s."
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "Verschlüsselt mit %lu Passphrases\n"
#, fuzzy, c-format
#| msgid_plural ""
#| "Verified %ld messages signed by \"%s\"\n"
#| "in the past %s."
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] ""
"%ld überprüfte Nachricht von \"%s\"\n"
"in den letzten %s."
"%ld überprüfte Nachrichten von \"%s\"\n"
"in den letzten %s."
+#, fuzzy, c-format
+#| msgid "policy: %s"
+msgid "(policy: %s)"
+msgstr "Richtlinie: %s"
+
#, fuzzy
#| msgid "Warning: we've have yet to see a message signed by this key!\n"
msgid ""
msgstr "Fehler beim Öffnen der TOFU Datenbank: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Beachten Sie, daß Gruppenspezifiaktionen ignoriert werden\n"
+#, c-format
+msgid "error closing '%s'\n"
+msgstr "Fehler beim Schließen von '%s'\n"
+
+#, c-format
+msgid "error parsing '%s'\n"
+msgstr "Fehler beim Hashen von '%s'\n"
+
msgid "list all components"
msgstr "Liste aller Komponenten"
msgid "apply global default values"
msgstr "Wende die gobalen Voreinstellungen an"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|DATEI|Konfigurationsdateien mittels DATEI ändern"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "Hole die Einstellungsverzeichnisse von @GPGCONF@"
"Syntax: gpg-check-pattern [optionen] Musterdatei\n"
"Die von stdin gelesene Passphrase gegen die Musterdatei prüfen\n"
+#, fuzzy
+#~| msgid ""
+#~| "@\n"
+#~| "Examples:\n"
+#~| "\n"
+#~| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#~| " --clear-sign [file] make a clear text signature\n"
+#~| " --detach-sign [file] make a detached signature\n"
+#~| " --list-keys [names] show keys\n"
+#~| " --fingerprint [names] show fingerprints\n"
+#~ msgid ""
+#~ "@\n"
+#~ "Examples:\n"
+#~ "\n"
+#~ " -se -r Bob [file] sign and encrypt for user Bob\n"
+#~ " --clearsign [file] make a clear text signature\n"
+#~ " --detach-sign [file] make a detached signature\n"
+#~ " --list-keys [names] show keys\n"
+#~ " --fingerprint [names] show fingerprints\n"
+#~ msgstr ""
+#~ "@\n"
+#~ "Beispiele:\n"
+#~ "\n"
+#~ " -se -r Bob [Datei] Signieren und verschlüsseln für Benutzer Bob\n"
+#~ " --clear-sign [Datei] Eine Klartextsignatur erzeugen\n"
+#~ " --detach-sign [Datei] Eine abgetrennte Signatur erzeugen\n"
+#~ " --list-keys [Namen] Schlüssel anzeigen\n"
+#~ " --fingerprint [Namen] \"Fingerabdrücke\" anzeigen\n"
+
+#~ msgid "--store [filename]"
+#~ msgstr "--store [Dateiname]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [Dateiname]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [Dateiname]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [Dateiname]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [Dateiname]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [Dateiname]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [Dateiname]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [Dateiname]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [Dateiname]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [Dateiname]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key User-ID"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key User-ID"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key User-ID [Befehle]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd User-ID"
+
+#~ msgid "[filename]"
+#~ msgstr "[Dateiname]"
+
+#, fuzzy
+#~| msgid " over the past %ld day."
+#~| msgid_plural " over the past %ld days."
+#~ msgid " in the past."
+#~ msgstr " innerhalb des letzten Tages."
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "\"Shadowing\" des Schlüssels schlug fehl: %s\n"
#~ msgid "Known user IDs associated with this key:\n"
#~ msgstr "Bekannte, mit diesem Schlüssel assozierte, User-IDs:\n"
-#~ msgid "%ld message signed in the future."
-#~ msgid_plural "%ld messages signed in the future."
-#~ msgstr[0] "%ld Nachricht in der Zukunft signiert."
-#~ msgstr[1] "%ld Nachrichten in der Zukunf signiert."
-
#~ msgid "%ld message signed"
#~ msgid_plural "%ld messages signed"
#~ msgstr[0] "%ld Nachricht signiert"
#~ msgstr[0] "%d Tag"
#~ msgstr[1] "%d Tage"
-#~ msgid "TOFU: few signatures %d message %s"
-#~ msgid_plural "TOFU: few signatures %d messages %s"
-#~ msgstr[0] ""
-#~ "WARNUNG: Falls sie glauben, mehr als %d mit diesem Schlüssel signierte\n"
-#~ "Nachricht erhalten zu haben, so kann es sich bei diesem Schlüssel um\n"
-#~ "eine Fälschung handeln! Prüfen Sie die Email-Adresse genau auf kleine\n"
-#~ "Variationen (z.B. zusätzliche Leerzeichen). Falls Ihnen der Schlüssel\n"
-#~ "suspekt erscheint, so benutzen Sie '%s' um den Schlüssel als Fälschung\n"
-#~ "zu markieren."
-#~ msgstr[1] ""
-#~ "WARNUNG: Falls sie glauben, mehr als %d mit diesem Schlüssel signierte\n"
-#~ "Nachrichten erhalten zu haben, so kann es sich bei diesem Schlüssel um\n"
-#~ "eine Fälschung handeln! Prüfen Sie die Email-Adresse genau auf kleine\n"
-#~ "Variationen (z.B. zusätzliche Leerzeichen). Falls Ihnen der Schlüssel\n"
-#~ "suspekt erscheint, so benutzen Sie '%s' um den Schlüssel als Fälschung\n"
-#~ "zu markieren."
-
#~ msgid "sending key %s to %s server %s\n"
#~ msgstr "sende Schlüssel %s auf den %s-Server %s\n"
msgid "quickly revoke a user-id"
msgstr "δημιουργία ενός νέου ζεύγους κλειδιών"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "δημιουργία ενός νέου ζεύγους κλειδιών"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(δείτε τη σελίδα man για μια πλήρη λίστα εντολών και επιλογών)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Παραδείγματα:\n"
"\n"
" -se -r Bob [αρχείο] υπογραφή και κρυπτογράφηση για το Bob\n"
-" --clearsign [αρχείο] δημιουργία μη κρυπτογραφημένης υπογραφής\n"
+" --clear-sign [αρχείο] δημιουργία μη κρυπτογραφημένης υπογραφής\n"
" --detach-sign [αρχείο] δημιουργία αποκομμένης υπογραφής\n"
" --list-keys [ονόματα] απεικόνιση κλειδιών\n"
" --fingerprint [ονόματα] απεικόνιση αποτυπωμάτων (fingerprints)\n"
"ΠΡΟΕΙΔΟΠΟΙΗΣΗ: δώθηκαν παραλήπτες (-r) χώρις χρήση κρυπτογράφησης\n"
"δημοσίου κλειδιού\n"
-msgid "--store [filename]"
-msgstr "--store [όνομα αρχείου]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [όνομα αρχείου]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "αποκρυπτογράφηση απέτυχε: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [όνομα αρχείου]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [όνομα αρχείου]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "απαγορεύετε η χρήση του %s στην κατάσταση %s.\n"
-msgid "--sign [filename]"
-msgstr "--sign [όνομα αρχείου]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [όνομα αρχείου]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [όνομα αρχείου]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "απαγορεύετε η χρήση του %s στην κατάσταση %s.\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [όνομα αρχείου]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [όνομα αρχείου]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [όνομα αρχείου]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key user-id"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key user-id"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key user-id [εντολές]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key user-id"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "keyserver αποστολή απέτυχε: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[όνομα αρχείου]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Μπορείτε τώρα να εισαγάγετε το μήνυμα σας ...\n"
msgid "Nothing to sign.\n"
msgstr "Τίποτα για να υπογραφεί με το κλειδί %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "το %s δεν είναι έγκυρο σετ χαρακτήρων\n"
+
msgid "Digest: "
msgstr "Περίληψη: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Υποστηριζόμενοι αλγόριθμοι:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "σφάλμα στη δημιουργία της φράσης κλειδί: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "σφάλμα στη αποστολή προς το `%s': %s\n"
msgstr "%s: σφάλμα στην εγγραφή της εγγραφής dir : %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "σφάλμα στη αποστολή προς το `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "σφάλμα κατά την ανάγνωση του `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "απεικόνιση της λίστας κλειδιών"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
+msgstr[1] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "κρυπτογραφημένο με %lu φράσεις κλειδιά\n"
+msgstr[1] "κρυπτογραφημένο με %lu φράσεις κλειδιά\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
+msgstr[1] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
msgstr[1] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "αδυναμία εγγραφής της κλειδοθήκης `%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "σφάλμα κατά την ανάγνωση του `%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "σφάλμα στη δημιουργία της φράσης κλειδί: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Διαγράφηκαν %d υπογραφές.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
msgstr[1] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "κρυπτογραφημένο με %lu φράσεις κλειδιά\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
msgstr[1] "|αλγόρ [αρχεία]| απεικόνιση περιλήψεων των μηνυμάτων"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Πολιτική: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "σφάλμα στη αποστολή προς το `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "σφάλμα κατά την ανάγνωση του `%s': %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "σφάλμα κατά την ανάγνωση του `%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [όνομα αρχείου]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [όνομα αρχείου]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [όνομα αρχείου]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [όνομα αρχείου]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [όνομα αρχείου]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [όνομα αρχείου]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [όνομα αρχείου]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [όνομα αρχείου]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [όνομα αρχείου]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [όνομα αρχείου]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key user-id"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key user-id"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key user-id [εντολές]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key user-id"
+
+#~ msgid "[filename]"
+#~ msgstr "[όνομα αρχείου]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "διαγραφή block κλειδιών απέτυχε: %s\n"
msgid "quickly revoke a user-id"
msgstr "krei novan ŝlosilparon"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "krei novan ŝlosilparon"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Vidu la manpaĝon por kompleta listo de ĉiuj komandoj kaj opcioj)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Ekzemploj:\n"
"\n"
" -se -r Bob [dosiero] subskribi kaj ĉifri por uzanto Bob\n"
-" --clearsign [dosiero] fari klartekstan subskribon\n"
+" --clear-sign [dosiero] fari klartekstan subskribon\n"
" --detach-sign [dosiero] fari apartan subskribon\n"
" --list-keys [nomoj] montri ŝlosilojn\n"
" --fingerprint [nomoj] montri fingroŝpurojn\n"
msgid "WARNING: recipients (-r) given without using public key encryption\n"
msgstr ""
-msgid "--store [filename]"
-msgstr "--store [dosiero]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [dosiero]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "malĉifrado malsukcesis: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [dosiero]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [dosiero]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "Tiu komando ne eblas en la reĝimo %s.\n"
-msgid "--sign [filename]"
-msgstr "--sign [dosiero]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [dosiero]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [dosiero]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "Tiu komando ne eblas en la reĝimo %s.\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [dosiero]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [dosiero]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [dosiero]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key uzantidentigilo"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key uzantidentigilo"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key uzantidentigilo [komandoj]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key uzantidentigilo"
-
#, fuzzy, c-format
msgid "keyserver send failed: %s\n"
msgstr "Kreado de ŝlosiloj malsukcesis: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[dosiero]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Ektajpu vian mesaĝon ...\n"
msgid "Nothing to sign.\n"
msgstr "Nenio por subskribi per ŝlosilo %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s ne estas valida signaro\n"
+
msgid "Digest: "
msgstr ""
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Realigitaj metodoj:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "eraro dum kreado de pasfrazo: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "eraro dum sendo al '%s': %s\n"
msgstr "%s: eraro dum skribo de dosieruja registro: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "eraro dum sendo al '%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "eraro dum legado de '%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "listigi ŝlosilojn"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
+msgstr[1] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "Ripetu pasfrazon\n"
+msgstr[1] "Ripetu pasfrazon\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
+msgstr[1] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
msgstr[1] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "eraro dum skribado de ŝlosilaro '%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "eraro dum legado de '%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "eraro dum kreado de pasfrazo: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Forviŝis %d subskribojn.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
msgstr[1] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
-#, fuzzy, c-format
-msgid ", and encrypted %ld messages"
+#, fuzzy
+msgid "Encrypted 0 messages."
msgstr "Ripetu pasfrazon\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
msgstr[1] "|metodo [dosieroj]|presi mesaĝo-kompendiojn"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Gvidlinio: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "eraro dum sendo al '%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "eraro dum legado de '%s': %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "eraro dum legado de '%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [dosiero]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [dosiero]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [dosiero]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [dosiero]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [dosiero]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [dosiero]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [dosiero]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [dosiero]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [dosiero]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [dosiero]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key uzantidentigilo"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key uzantidentigilo"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key uzantidentigilo [komandoj]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key uzantidentigilo"
+
+#~ msgid "[filename]"
+#~ msgstr "[dosiero]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "forviŝo de ŝlosilbloko malsukcesis: %s\n"
msgid "quickly revoke a user-id"
msgstr "genera un nuevo par de claves"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "genera un nuevo par de claves"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Véase en la página del manual la lista completo de órdenes y opciones)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Ejemplos:\n"
"\n"
" -se -r Bob [fichero] firma y cifra para el usuario Bob\n"
-" --clearsign [fichero] hace una firma manteniendo el texto sin cifrar\n"
+" --clear-sign [fichero] hace una firma manteniendo el texto sin cifrar\n"
" --detach-sign [fichero] hace una firma separada\n"
" --list-keys [nombres] muestra las claves\n"
" --fingerprint [nombres] muestra las huellas dactilares\n"
msgid "WARNING: recipients (-r) given without using public key encryption\n"
msgstr "AVISO: se indicaron receptores (-r) sin clave pública de cifrado\n"
-msgid "--store [filename]"
-msgstr "--store [nombre_fichero]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [nombre_fichero]"
-
#, fuzzy, c-format
#| msgid "symmetric encryption of `%s' failed: %s\n"
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "el cifrado simétrico de `%s' falló: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [nombre_fichero]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [nombre_fichero]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "no puede usar --symetric --encrypt con --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "no puede usar --symetric --encrypt en modo %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [nombre_fichero]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [nombre_fichero]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [nombre_fichero]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "no puede usar --symetric --sign --encrypt con --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "no puede usar --symmetric --sign --encrypt en modo %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [nombre_fichero]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [nombre_fichero]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [nombre_fichero]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key id-usuario"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key id-usuario"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key id-usuario [órdenes]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <id-usuario>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "envío al servidor de claves fallido: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[nombre_fichero]"
-
# Falta un espacio.
# En español no se deja espacio antes de los puntos suspensivos
# (Real Academia dixit) :)
msgid "Nothing to sign.\n"
msgstr "Nada que firmar con la clave %s\n"
+#, fuzzy, c-format
+#| msgid "`%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "`%s' no es una fecha de caducidad válida\n"
+
msgid "Digest: "
msgstr "Resumen: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
msgstr "algoritmo no disponible: %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "error creando fichero temporal: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending %s command: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "error enviando orden %s: %s\n"
msgstr "%s: error escribiendo registro de directorio: %s\n"
#, fuzzy, c-format
+#| msgid "error sending %s command: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "error enviando orden %s: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening `%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "error abriendo `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
msgstr[0] "imprime resúmenes de mensaje"
msgstr[1] "imprime resúmenes de mensaje"
#, fuzzy, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
msgstr[0] "el algoritmo de resumen seleccionado no es válido\n"
msgstr[1] "el algoritmo de resumen seleccionado no es válido\n"
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "imprime resúmenes de mensaje"
+msgstr[1] "imprime resúmenes de mensaje"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
+msgstr[0] "el algoritmo de resumen seleccionado no es válido\n"
+msgstr[1] "el algoritmo de resumen seleccionado no es válido\n"
+
+#, c-format
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "el algoritmo de resumen seleccionado no es válido\n"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error storing flags: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "error almacenando parámetros: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "error creando tubería: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, fuzzy, c-format
#| msgid "second"
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] "segundo"
msgstr[1] "segundo"
-#, fuzzy, c-format
-#| msgid "%s: okay\n"
-msgid "%s: "
-msgstr "%s: bien\n"
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
+msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "%d firmas borradas\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "imprime resúmenes de mensaje"
msgstr[1] "imprime resúmenes de mensaje"
-#, fuzzy, c-format
-msgid ", and encrypted %ld messages"
+#, fuzzy
+msgid "Encrypted 0 messages."
msgstr "el algoritmo de resumen seleccionado no es válido\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "el algoritmo de resumen seleccionado no es válido\n"
msgstr[1] "el algoritmo de resumen seleccionado no es válido\n"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Política: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "error enviando orden %s: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Note que las especificación de grupo se ignoran\n"
+#, fuzzy, c-format
+#| msgid "error closing %s: %s\n"
+msgid "error closing '%s'\n"
+msgstr "error cerrando %s: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error in `%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "error en `%s': %s\n"
+
msgid "list all components"
msgstr "listar todos los componentes"
msgstr "aplicar valores globales por defecto"
#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FICHERO|tomar política de información de FICHERO"
+
+#, fuzzy
#| msgid "get the configuration directories for gpgconf"
msgid "get the configuration directories for @GPGCONF@"
msgstr "obtener directorios de configuración para gpgconf"
"Compara frase contraseña dada en entrada estándar con un fichero de "
"patrones\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [nombre_fichero]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [nombre_fichero]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [nombre_fichero]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [nombre_fichero]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [nombre_fichero]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nombre_fichero]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [nombre_fichero]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [nombre_fichero]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [nombre_fichero]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [nombre_fichero]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key id-usuario"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key id-usuario"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key id-usuario [órdenes]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <id-usuario>"
+
+#~ msgid "[filename]"
+#~ msgstr "[nombre_fichero]"
+
+#, fuzzy
+#~| msgid "%s: okay\n"
+#~ msgid "%s: "
+#~ msgstr "%s: bien\n"
+
+#, fuzzy
+#~ msgid ", and encrypted %ld messages"
+#~ msgstr "el algoritmo de resumen seleccionado no es válido\n"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "el sombreado de la clave falló: %s\n"
msgid "quickly revoke a user-id"
msgstr "genereeri uus võtmepaar"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "genereeri uus võtmepaar"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Kõikide käskude ja võtmete täieliku kirjelduse leiate manualist)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Näited:\n"
"\n"
" -se -r Bob [fail] allkirjasta ja krüpti kasutajale Bob\n"
-" --clearsign [fail] loo avateksti allkiri\n"
+" --clear-sign [fail] loo avateksti allkiri\n"
" --detach-sign [fail] loo eraldiseisev allkiri\n"
" --list-keys [nimed] näita võtmeid\n"
" --fingerprint [nimed] näita sõrmejälgi\n"
msgstr ""
"HOIATUS: määrati saajad (-r) aga ei kasutata avaliku võtme krüptograafiat\n"
-msgid "--store [filename]"
-msgstr "--store [failinimi]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [failinimi]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "lahtikrüpteerimine ebaõnnestus: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [failinimi]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [failinimi]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "%s ei ole moodis %s lubatud.\n"
-msgid "--sign [filename]"
-msgstr "--sign [failinimi]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [failinimi]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [failinimi]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "%s ei ole moodis %s lubatud.\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [failinimi]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [failinimi]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [failinimi]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key kasutaja-id"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key kasutaja-id"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key kasutaja-id [käsud]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key kasutaja-id"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "võtmeserverile saatmine ebaõnnestus: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[failinimi]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Kirjutage nüüd oma teade ...\n"
msgid "Nothing to sign.\n"
msgstr "Võtmega %08lX pole midagi allkirjastada\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s ei ole lubatud kooditabel\n"
+
msgid "Digest: "
msgstr "Teatelühend: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Toetatud algoritmid:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "viga parooli loomisel: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "viga teate saatmisel serverile `%s': %s\n"
msgstr "%s: viga kataloogikirje kirjutamisel: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "viga teate saatmisel serverile `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "viga `%s' lugemisel: %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "näita võtmeid"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [failid]|trüki teatelühendid"
+msgstr[1] "|algo [failid]|trüki teatelühendid"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "krüpteeritud kasutades %lu parooli\n"
+msgstr[1] "krüpteeritud kasutades %lu parooli\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [failid]|trüki teatelühendid"
+msgstr[1] "|algo [failid]|trüki teatelühendid"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [failid]|trüki teatelühendid"
msgstr[1] "|algo [failid]|trüki teatelühendid"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [failid]|trüki teatelühendid"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "viga võtmehoidlasse `%s' kirjutamisel: %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "viga `%s' lugemisel: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "viga parooli loomisel: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Kustutatud %d allkirja.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [failid]|trüki teatelühendid"
msgstr[1] "|algo [failid]|trüki teatelühendid"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "krüpteeritud kasutades %lu parooli\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [failid]|trüki teatelühendid"
msgstr[1] "|algo [failid]|trüki teatelühendid"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Poliis: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "viga teate saatmisel serverile `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "viga `%s' lugemisel: %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "viga `%s' lugemisel: %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [failinimi]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [failinimi]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [failinimi]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [failinimi]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [failinimi]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [failinimi]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [failinimi]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [failinimi]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [failinimi]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [failinimi]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key kasutaja-id"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key kasutaja-id"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key kasutaja-id [käsud]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key kasutaja-id"
+
+#~ msgid "[filename]"
+#~ msgstr "[failinimi]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "võtmebloki kustutamine ebaõnnestus: %s\n"
msgid "quickly revoke a user-id"
msgstr "luo uusi avainpari"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "luo uusi avainpari"
+
msgid "full featured key pair generation"
msgstr ""
"(Katso täydellinen luettelo kaikista komennoista ja valitsimista man-"
"sivuilta)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"@\n"
"Esim:\n"
" -se -r Pekka [tiedosto] allekirjoita ja salaa Pekalle\n"
-" --clearsign [tiedosto] tee tekstimuotoinen allekirjoitus\n"
+" --clear-sign [tiedosto] tee tekstimuotoinen allekirjoitus\n"
" --detach-sign [tiedosto] tee erillinen allekirjoitus\n"
" --list-keys [nimet] näytä avaimet\n"
" --fingerprint [nimet] näytä sormenjäljet\n"
msgstr ""
"VAROITUS: vastaanottajia (-r) annettu käyttämättä julkisen avaimen salausta\n"
-msgid "--store [filename]"
-msgstr "--store [tiedostonimi]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [tiedostonimi]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "avaus epäonnistui: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [tiedostonimi]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [tiedostonimi]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "valitsinta %s ei voi käyttää %s-tilassa\n"
-msgid "--sign [filename]"
-msgstr "--allekirjoita [tiedostonimi]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [tiedostonimi]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [tiedostonimi]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "valitsinta %s ei voi käyttää %s-tilassa\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [tiedostonimi]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [tiedostonimi]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [tiedostonimi]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key käyttäjätunnus"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key käyttäjätunnus"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key käyttäjätunnus [komennot]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key käyttäjätunnus"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "avainpalvelimelle lähettäminen epäonnistui: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[tiedostonimi]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Kirjoita viestisi...\n"
msgid "Nothing to sign.\n"
msgstr "Avaimelle %08lX ei löydy mitään mitä allekirjoittaa\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s ei kelpaa merkistöksi\n"
+
msgid "Digest: "
msgstr "Tiiviste: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Tuetut algoritmit:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "virhe luotaessa salasanaa: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "virhe lähettäessä kohteeseen \"%s\": %s\n"
msgstr "%s: virhe kirjoitettaessa hakemistotietuetta: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "virhe lähettäessä kohteeseen \"%s\": %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "virhe luettaessa tiedostoa \"%s\": %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "näytä avaimet"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [tiedostot]|tulosta viestien tiivisteet"
+msgstr[1] "|algo [tiedostot]|tulosta viestien tiivisteet"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "salattu %lu salasanalla\n"
+msgstr[1] "salattu %lu salasanalla\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [tiedostot]|tulosta viestien tiivisteet"
+msgstr[1] "|algo [tiedostot]|tulosta viestien tiivisteet"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [tiedostot]|tulosta viestien tiivisteet"
msgstr[1] "|algo [tiedostot]|tulosta viestien tiivisteet"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [tiedostot]|tulosta viestien tiivisteet"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "virhe kirjoitettaessa avainrenkaaseen \"%s\": %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "virhe luettaessa tiedostoa \"%s\": %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "virhe luotaessa salasanaa: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "%d allekirjoitusta poistettu.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [tiedostot]|tulosta viestien tiivisteet"
msgstr[1] "|algo [tiedostot]|tulosta viestien tiivisteet"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "salattu %lu salasanalla\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [tiedostot]|tulosta viestien tiivisteet"
msgstr[1] "|algo [tiedostot]|tulosta viestien tiivisteet"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Käytäntö: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "virhe lähettäessä kohteeseen \"%s\": %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "virhe luettaessa tiedostoa \"%s\": %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "virhe luettaessa tiedostoa \"%s\": %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [tiedostonimi]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [tiedostonimi]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [tiedostonimi]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [tiedostonimi]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--allekirjoita [tiedostonimi]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [tiedostonimi]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [tiedostonimi]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [tiedostonimi]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [tiedostonimi]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [tiedostonimi]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key käyttäjätunnus"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key käyttäjätunnus"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key käyttäjätunnus [komennot]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key käyttäjätunnus"
+
+#~ msgid "[filename]"
+#~ msgstr "[tiedostonimi]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "avainlohkojen poisto epäonnistui: %s\n"
msgid "quickly revoke a user-id"
msgstr "générer rapidement une nouvelle paire de clefs"
+#, fuzzy
+#| msgid "quickly generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "générer rapidement une nouvelle paire de clefs"
+
msgid "full featured key pair generation"
msgstr "générer une paire de clefs complètes"
"(Consultez la page de manuel pour obtenir une liste complète des commandes\n"
"et options)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Exemples :\n"
"\n"
" -se -r Alice [fichier] signer et chiffrer pour l'utilisateur Alice\n"
-" --clearsign [fichier] faire une signature en texte clair\n"
+" --clear-sign [fichier] faire une signature en texte clair\n"
" --detach-sign [fichier] faire une signature détachée\n"
" --list-keys [noms] montrer les clefs\n"
" --fingerprint [noms] montrer les empreintes\n"
"Attention : les destinataires (-r) indiqués n'utilisent pas\n"
" de clef publique pour le chiffrement\n"
-msgid "--store [filename]"
-msgstr "--store [fichier]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [fichier]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "échec du chiffrement symétrique de « %s » : %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [fichier]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [fichier]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "impossible d'utiliser --symmetric --encrypt avec --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "impossible d'utiliser --symmetric --encrypt en mode %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [fichier]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [fichier]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [fichier]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "impossible d'utiliser --symmetric --sign --encrypt avec --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "impossible d'utiliser --symmetric --sign --encrypt en mode %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [fichier]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [fichier]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [fichier]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key identité"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key identité"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key identité [commandes]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <identité>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "échec d'envoi vers le serveur de clefs : %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[fichier]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Vous pouvez taper votre message…\n"
msgid "Nothing to sign.\n"
msgstr "Rien à signer.\n"
+#, fuzzy, c-format
+#| msgid "'%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "« %s » n'est pas une date d'expiration de signature valable\n"
+
msgid "Digest: "
msgstr "Hachage : "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"Utilisez-le pour révoquer cette clef en cas de compromis ou de\n"
"perte de la clef secrète. Cependant, si la clef secrète est\n"
msgstr "algorithme non pris en charge : %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "erreur de création du fichier temporaire : %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending data: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "erreur d'envoi de données : %s\n"
msgstr "erreur d'initialisation de l'objet lecteur : %s\n"
#, fuzzy, c-format
+#| msgid "error sending data: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "erreur d'envoi de données : %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening '%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "erreur d'ouverture de « %s » : %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "indiquer les fonctions de hachage"
+msgstr[1] "indiquer les fonctions de hachage"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "chiffré avec %lu phrases secrètes\n"
+msgstr[1] "chiffré avec %lu phrases secrètes\n"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "indiquer les fonctions de hachage"
+msgstr[1] "indiquer les fonctions de hachage"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "indiquer les fonctions de hachage"
msgstr[1] "indiquer les fonctions de hachage"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Messages encrypted in the past: %d."
+msgstr "indiquer les fonctions de hachage"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error setting OCSP target: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "erreur de configuration de la cible OCSP : %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "erreur de création d'un tube : %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "%d signatures supprimées\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "indiquer les fonctions de hachage"
msgstr[1] "indiquer les fonctions de hachage"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "chiffré avec %lu phrases secrètes\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "indiquer les fonctions de hachage"
msgstr[1] "indiquer les fonctions de hachage"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "validité : %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "erreur d'envoi de données : %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Remarquez que les spécifications de groupe sont ignorées\n"
+#, fuzzy, c-format
+#| msgid "error closing '%s': %s\n"
+msgid "error closing '%s'\n"
+msgstr "erreur de fermeture de « %s » : %s\n"
+
+#, fuzzy, c-format
+#| msgid "error hashing '%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "erreur du hachage de « %s » : %s\n"
+
msgid "list all components"
msgstr "afficher tous les composants"
msgid "apply global default values"
msgstr "appliquer les valeurs par défaut globales"
+#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FICHIER|prendre renseignements de politique du FICHIER"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "aff. répertoires de configuration pour @GPGCONF@"
"Vérifier une phrase secrète donnée sur l'entrée standard par rapport à "
"ficmotif\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [fichier]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [fichier]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [fichier]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [fichier]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [fichier]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [fichier]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [fichier]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [fichier]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [fichier]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [fichier]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key identité"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key identité"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key identité [commandes]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <identité>"
+
+#~ msgid "[filename]"
+#~ msgstr "[fichier]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "échec de dissimulation de la clef : %s\n"
msgid "quickly revoke a user-id"
msgstr "xerar un novo par de chaves"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "xerar un novo par de chaves"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Vexa a páxina man para un listado completo de comandos e opcións)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
msgstr ""
"AVISO: deronse destinatarios (-r) sen empregar cifrado de chave pública\n"
-msgid "--store [filename]"
-msgstr "--store [ficheiro]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [ficheiro]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "o descifrado fallou: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [ficheiro]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [ficheiro]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "non se pode empregar %s no modo %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [ficheiro]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [ficheiro]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [ficheiro]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "non se pode empregar %s no modo %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [ficheiro]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [ficheiro]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [ficheiro]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key id-de-usuario"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key id-de-usuario"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key id-de-usuario [comandos]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key id-de-usuario"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "o envío ao servidor de chaves fallou: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[ficheiro]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Escriba a súa mensaxe ...\n"
msgid "Nothing to sign.\n"
msgstr "Nada que asinar coa chave %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s non é un xogo de caracteres válido\n"
+
msgid "Digest: "
msgstr "Resumo: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Algoritmos soportados:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "erro ao crea-lo contrasinal: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "erro ao enviar a `%s': %s\n"
msgstr "%s: erro ao escribi-lo rexistro de directorios: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "erro ao enviar a `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "erro lendo `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "ve-la lista de chaves"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [ficheiros]|visualizar resumos de mensaxes"
+msgstr[1] "|algo [ficheiros]|visualizar resumos de mensaxes"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "cifrado con %lu contrasinais\n"
+msgstr[1] "cifrado con %lu contrasinais\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [ficheiros]|visualizar resumos de mensaxes"
+msgstr[1] "|algo [ficheiros]|visualizar resumos de mensaxes"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [ficheiros]|visualizar resumos de mensaxes"
msgstr[1] "|algo [ficheiros]|visualizar resumos de mensaxes"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [ficheiros]|visualizar resumos de mensaxes"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "erro escribindo no chaveiro `%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "erro lendo `%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "erro ao crea-lo contrasinal: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Borradas %d sinaturas.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [ficheiros]|visualizar resumos de mensaxes"
msgstr[1] "|algo [ficheiros]|visualizar resumos de mensaxes"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "cifrado con %lu contrasinais\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [ficheiros]|visualizar resumos de mensaxes"
msgstr[1] "|algo [ficheiros]|visualizar resumos de mensaxes"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Normativa: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "erro ao enviar a `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "erro lendo `%s': %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "erro lendo `%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [ficheiro]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [ficheiro]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [ficheiro]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [ficheiro]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [ficheiro]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [ficheiro]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [ficheiro]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [ficheiro]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [ficheiro]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [ficheiro]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key id-de-usuario"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key id-de-usuario"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key id-de-usuario [comandos]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key id-de-usuario"
+
+#~ msgid "[filename]"
+#~ msgstr "[ficheiro]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "fallou o borrado do bloque de chaves: %s\n"
msgid "quickly revoke a user-id"
msgstr "új kulcspár létrehozása"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "új kulcspár létrehozása"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(A parancsok és opciók teljes listáját a man oldalon tekintheti meg.)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Példák:\n"
"\n"
" -se -r Bob [fájl] titkosítás és aláírás Bob részére\n"
-" --clearsign [fájl] olvasható szöveg aláírása\n"
+" --clear-sign [fájl] olvasható szöveg aláírása\n"
" --detach-sign [fájl] különálló aláírás készítése\n"
" --list-keys [nevek] kulcsok kiíratása\n"
" --fingerprint [nevek] ujjlenyomatok kiíratása\n"
msgstr ""
"FIGYELEM: Címzett megadva (-r), de nincs nyilvános kulcsú titkosítás!\n"
-msgid "--store [filename]"
-msgstr "--store [fájlnév]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [fájlnév]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "Visszafejtés sikertelen: %s.\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [fájlnév]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [fájlnév]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "Lehet, hogy nem használhatja %s-t %s módban!\n"
-msgid "--sign [filename]"
-msgstr "--sign [fájlnév]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [fájlnév]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [fájlnév]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "Lehet, hogy nem használhatja %s-t %s módban!\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [fájlnév]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [fájlnév]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [fájlnév]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key felh-azonosító"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key felh-azonosító"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key felh-azonosító [parancsok]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key felh-azonosító"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "Küldés a kulcsszerverre sikertelen: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[fájlnév]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Kezdheti gépelni az üzenetet...\n"
msgid "Nothing to sign.\n"
msgstr "Nincs mit aláírni a %08lX kulccsal!\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s nem érvényes karakterkiosztás!\n"
+
msgid "Digest: "
msgstr "Kivonat: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Támogatott algoritmusok:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "Hiba a jelszó létrehozásakor: %s.\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "Hiba %s-ra/-re küldéskor: %s\n"
msgstr "%s: Hiba könyvtárrekord írásakor: %s.\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "Hiba %s-ra/-re küldéskor: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "Hiba \"%s\" olvasásakor: %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "kulcsok listázása"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [fájlok]|üzenet kivonatának kiírása"
+msgstr[1] "|algo [fájlok]|üzenet kivonatának kiírása"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "%lu jelszóval rejtjelezve\n"
+msgstr[1] "%lu jelszóval rejtjelezve\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [fájlok]|üzenet kivonatának kiírása"
+msgstr[1] "|algo [fájlok]|üzenet kivonatának kiírása"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [fájlok]|üzenet kivonatának kiírása"
msgstr[1] "|algo [fájlok]|üzenet kivonatának kiírása"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [fájlok]|üzenet kivonatának kiírása"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "Hiba a \"%s\" kulcskarika írásakor: %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "Hiba \"%s\" olvasásakor: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "Hiba a jelszó létrehozásakor: %s.\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Töröltem %d aláírást.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [fájlok]|üzenet kivonatának kiírása"
msgstr[1] "|algo [fájlok]|üzenet kivonatának kiírása"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "%lu jelszóval rejtjelezve\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [fájlok]|üzenet kivonatának kiírása"
msgstr[1] "|algo [fájlok]|üzenet kivonatának kiírása"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Eljárásmód: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "Hiba %s-ra/-re küldéskor: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "Hiba \"%s\" olvasásakor: %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "Hiba \"%s\" olvasásakor: %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [fájlnév]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [fájlnév]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [fájlnév]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [fájlnév]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [fájlnév]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [fájlnév]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [fájlnév]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [fájlnév]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [fájlnév]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [fájlnév]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key felh-azonosító"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key felh-azonosító"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key felh-azonosító [parancsok]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key felh-azonosító"
+
+#~ msgid "[filename]"
+#~ msgstr "[fájlnév]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "A kulcsblokk törlése sikertelen: %s.\n"
msgid "quickly revoke a user-id"
msgstr "buat sepasang kunci baru"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "buat sepasang kunci baru"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Lihat man page untuk daftar lengkap semua perintah dan option)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Contoh:\n"
"\n"
" -se -r Bob [file] tandai dan enkripsi untuk user Bob\n"
-" --clearsign [file] buat signature berbentuk teks\n"
+" --clear-sign [file] buat signature berbentuk teks\n"
" --detach-sign [file] buat signature detached\n"
" --list-keys [nama] tampilkan kunci\n"
" --fingerprint [nama] tampilkan fingerprint\n"
"Peringatan: penerima yang disebutkan (-r) tanpa menggunakan enkripsi public "
"key \n"
-msgid "--store [filename]"
-msgstr "--store [namafile]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [namafile]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "dekripsi gagal: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [namafile]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [namafile]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "anda tidak boleh menggunakan %s saat dalam mode %s.\n"
-msgid "--sign [filename]"
-msgstr "--sign [namafile]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [namafile]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [namafile]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "anda tidak boleh menggunakan %s saat dalam mode %s.\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [namafile]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [namafile]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [namafile]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key id-user"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key id-user"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key id-user [perintah]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key id-user"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "Pengiriman keyserver gagal: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[namafile]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Teruskan dan ketikkan pesan anda ....\n"
msgid "Nothing to sign.\n"
msgstr "Tidak ada yang ditandai dengan kunci %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s bukanlah set karakter yang valid\n"
+
msgid "Digest: "
msgstr "Digest: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Algoritma yang didukung:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "kesalahan penciptaan passphrase: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "kesalahan mengirim ke `%s': %s\n"
msgstr "%s: kesalahan menulis dir record: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "kesalahan mengirim ke `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "kesalahan membaca `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "tampilkan kunci"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [file]|cetak digest pesan"
+msgstr[1] "|algo [file]|cetak digest pesan"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "dienkripsi dengan passphrase %lu\n"
+msgstr[1] "dienkripsi dengan passphrase %lu\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [file]|cetak digest pesan"
+msgstr[1] "|algo [file]|cetak digest pesan"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [file]|cetak digest pesan"
msgstr[1] "|algo [file]|cetak digest pesan"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [file]|cetak digest pesan"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "kesalahan menulis keyring `%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "kesalahan membaca `%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "kesalahan penciptaan passphrase: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Menghapus %d signature.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [file]|cetak digest pesan"
msgstr[1] "|algo [file]|cetak digest pesan"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "dienkripsi dengan passphrase %lu\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [file]|cetak digest pesan"
msgstr[1] "|algo [file]|cetak digest pesan"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Kebijakan: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "kesalahan mengirim ke `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "kesalahan membaca `%s': %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "kesalahan membaca `%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [namafile]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [namafile]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [namafile]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [namafile]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [namafile]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [namafile]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [namafile]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [namafile]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [namafile]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [namafile]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key id-user"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key id-user"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key id-user [perintah]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key id-user"
+
+#~ msgid "[filename]"
+#~ msgstr "[namafile]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "gagal menghapus keyblok: %s\n"
msgid "quickly revoke a user-id"
msgstr "genera una nuova coppia di chiavi"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "genera una nuova coppia di chiavi"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Vedi la man page per una lista completa di tutti i comandi e opzioni)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Esempi:\n"
"\n"
" -se -r Bob [file] firma e cifra per l'utente Bob\n"
-" --clearsign [file] fai una firma mantenendo il testo in chiaro\n"
+" --clear-sign [file] fai una firma mantenendo il testo in chiaro\n"
" --detach-sign [file] fai una firma separata\n"
" --list-keys [nomi] mostra le chiavi\n"
" --fingerprint [nomi] mostra le impronte digitali\n"
"ATTENZIONE: sono stati indicati dei destinatari (-r) senza usare la\n"
"crittografia a chiave pubblica\n"
-msgid "--store [filename]"
-msgstr "--store [nomefile]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [nomefile]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "decifratura fallita: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [nomefile]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [nomefile]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "non è possibile usare %s in modalità %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [nomefile]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [nomefile]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [nomefile]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "non è possibile usare %s in modalità %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [nomefile]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [nomefile]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [nomefile]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key user-id"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key user-id"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key user-id [comandi]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key user-id"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "invio al keyserver fallito: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[nomefile]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Vai avanti e scrivi il messaggio...\n"
msgid "Nothing to sign.\n"
msgstr "Niente da firmare con la chiave %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s non è un set di caratteri valido\n"
+
msgid "Digest: "
msgstr "Digest: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Algoritmi gestiti:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "errore nella creazione della passhprase: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "errore leggendo `%s': %s\n"
msgstr "%s: errore durante la scrittura del dir record: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "errore leggendo `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "errore leggendo `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "elenca le chiavi"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [files]|stampa tutti i message digests"
+msgstr[1] "|algo [files]|stampa tutti i message digests"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "cifratto con %lu passphrase\n"
+msgstr[1] "cifratto con %lu passphrase\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [files]|stampa tutti i message digests"
+msgstr[1] "|algo [files]|stampa tutti i message digests"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [files]|stampa tutti i message digests"
msgstr[1] "|algo [files]|stampa tutti i message digests"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [files]|stampa tutti i message digests"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "errore scrivendo il portachiavi `%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "errore leggendo `%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "errore nella creazione della passhprase: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Cancellate %d firme.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [files]|stampa tutti i message digests"
msgstr[1] "|algo [files]|stampa tutti i message digests"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "cifratto con %lu passphrase\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [files]|stampa tutti i message digests"
msgstr[1] "|algo [files]|stampa tutti i message digests"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Policy: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "errore leggendo `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "errore leggendo `%s': %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "errore leggendo `%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [nomefile]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [nomefile]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [nomefile]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nomefile]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [nomefile]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nomefile]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nomefile]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [nomefile]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [nomefile]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [nomefile]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key user-id"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key user-id"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key user-id [comandi]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key user-id"
+
+#~ msgid "[filename]"
+#~ msgstr "[nomefile]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "cancellazione del keyblock fallita: %s\n"
#
msgid ""
msgstr ""
-"Project-Id-Version: gnupg 2.1.15\n"
+"Project-Id-Version: gnupg 2.1.16\n"
"Report-Msgid-Bugs-To: translations@gnupg.org\n"
-"PO-Revision-Date: 2016-09-02 16:58+0200\n"
+"PO-Revision-Date: 2016-12-17 16:29+0900\n"
"Last-Translator: NIIBE Yutaka <gniibe@fsij.org>\n"
"Language-Team: none\n"
"Language: ja\n"
msgid "run in server mode (foreground)"
msgstr "サーバ・モードで実行 (フォアグラウンド)"
-#, fuzzy
-#| msgid "run in server mode"
msgid "run in supervised mode"
-msgstr "ã\82µã\83¼ã\83\90・モードで実行"
+msgstr "ã\82¹ã\83¼ã\83\91ã\83¼ã\83\90ã\82¤ã\82ºã\83\89・モードで実行"
msgid "verbose"
msgstr "冗長"
msgid "Warning: unsafe permissions on %s \"%s\"\n"
msgstr "警告: '%s'の安全でない許可 \"%s\"\n"
-#, fuzzy, c-format
-#| msgid "waiting for the agent to come up ... (%ds)\n"
+#, c-format
msgid "waiting for file '%s' to become accessible ...\n"
-msgstr "agentの起動のため、%d秒待ちます\n"
+msgstr "ファイル'%s'がアクセスできるのを待ちます...\n"
#, c-format
msgid "renaming '%s' to '%s' failed: %s\n"
msgid "quickly revoke a user-id"
msgstr "すばやくユーザIDを失効"
+msgid "quickly set a new expiration date"
+msgstr "すばやく新しい有効期限を設定"
+
msgid "full featured key pair generation"
msgstr "全機能の鍵ペアを生成"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"例:\n"
"\n"
" -se -r Bob [ファイル] ユーザBobへ署名と暗号化\n"
-" --clearsign [ファイル] クリア・テクスト署名を作成\n"
+" --clear-sign [ファイル] クリア・テクスト署名を作成\n"
" --detach-sign [ファイル] 分遣署名を作成\n"
" --list-keys [名前] 鍵を表示\n"
" --fingerprint [名前] フィンガープリントを表示\n"
msgid "show expiration dates during signature listings"
msgstr "署名の一覧時に有効期限の日付を表示する"
-#, fuzzy, c-format
-#| msgid "invalid argument for option \"%.50s\"\n"
+#, c-format
msgid "valid values for option '%s':\n"
-msgstr "オプション\"%.50s\"には無効な引数です\n"
+msgstr "オプション'%s'に有効な値:\n"
#, c-format
msgid "unknown TOFU policy '%s'\n"
msgid "(use \"help\" to list choices)\n"
msgstr "(選択肢の一覧には\"help\"を使ってください)\n"
-#, fuzzy, c-format
-#| msgid "invalid argument for option \"%.50s\"\n"
+#, c-format
msgid "invalid value for option '%s'\n"
-msgstr "オプション\"%.50s\"には無効な引数です\n"
+msgstr "オプション'%s'に無効な値です\n"
#, c-format
msgid "Note: old default options file '%s' ignored\n"
msgid "WARNING: recipients (-r) given without using public key encryption\n"
msgstr "*警告*: 公開鍵暗号を使わずに、受取人 (-r) を指定しています\n"
-msgid "--store [filename]"
-msgstr "--store [ファイル名]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [ファイル名]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "'%s'の共通鍵暗号に失敗しました: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [ファイル名]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [ファイル名]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "--symmetric --encryptを--s2k-mode 0で使うことはできません\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "--symmetric --encryptを%sモードで使うことはできません\n"
-msgid "--sign [filename]"
-msgstr "--sign [ファイル名]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [ファイル名]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [ファイル名]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "--symmetric --sign --encryptを--s2k-mode 0で使うことはできません\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "--symmetric --sign --encryptを%sモードで使うことはできません\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [ファイル名]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [ファイル名]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [ファイル名]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key ユーザid"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key ユーザid"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key ユーザid [コマンド]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <ユーザid>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "鍵サーバへの送信に失敗しました: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr "'%s'は有効な鍵ID, フィンガープリント、keygripではないようです。\n"
-msgid "[filename]"
-msgstr "[ファイル名]"
-
msgid "Go ahead and type your message ...\n"
msgstr "開始します。メッセージを打ってください ...\n"
msgid "Nothing to sign.\n"
msgstr "署名するものがありません。\n"
+#, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "'%s'は、有効な有効期限ではありません\n"
+
msgid "Digest: "
msgstr "ダイジェスト: "
msgid "This key belongs to us\n"
msgstr "この鍵は自分のものです\n"
-#, fuzzy, c-format
-#| msgid "root certificate has now been marked as trusted\n"
+#, c-format
msgid "%s: This key is bad! It has been marked as untrusted!\n"
-msgstr "ルート証明書は信用すると今、マークされました\n"
+msgstr "%s: この鍵はダメです! 信用できないとマークされています!\n"
-#, fuzzy
-#| msgid ""
-#| "It is NOT certain that the key belongs to the person named\n"
-#| "in the user ID. If you *really* know what you are doing,\n"
-#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
-"ã\81\93ã\81®é\8dµã\81¯ã\80\81ã\81\93ã\81®ã\83¦ã\83¼ã\82¶IDã\82\92ã\81ªã\81®ã\82\8bæ\9c¬äººã\81®ã\82\82ã\81®ã\81\8bã\81©ã\81\86ã\81\8b確信ã\81§ã\81\8d\n"
-"ã\81¾ã\81\9bã\82\93ã\80\82ä»\8aã\81\8bã\82\89è¡\8cã\81\86ã\81\93ã\81¨ã\82\92ï¼\8aæ\9c¬å½\93ã\81«ï¼\8aç\90\86解ã\81\97ã\81¦ã\81\84ã\81ªã\81\84å ´å\90\88ã\81«ã\81¯ã\80\81\n"
-"次の質問にはnoと答えてください。\n"
+"ã\81\93ã\81®é\8dµã\81¯ã\80\81ã\83\80ã\83¡ã\81§ã\81\99! ä¿¡ç\94¨ã\81§ã\81\8dã\81ªã\81\84ã\81¨ã\83\9eã\83¼ã\82¯ã\81\95ã\82\8cã\81¦ã\81\84ã\81¾ã\81\99! *æ\9c¬å½\93ã\81«*\n"
+"ã\81ªã\81«ã\82\92ã\81\97ã\81¦ã\81\84ã\82\8bã\81®ã\81\8bå\88\86ã\81\8bã\81£ã\81¦ã\81\84ã\82\8bå ´å\90\88ã\81«ã\81¯ã\80\81次ã\81®è³ªå\95\8fã\81«ã\81¯ yes ã\81¨\n"
+"答えてください。\n"
msgid ""
"It is NOT certain that the key belongs to the person named\n"
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"秘密鍵のコンプロマイズや紛失の場合、これを使ってこの鍵を失効させます。\n"
"しかし、秘密鍵がまだアクセス可能である場合、新しい失効証明書を生成し、\n"
"失効の理由をつける方がよいでしょう。詳細は、GnuPGマニュアルのgpgコマンド \"--"
-"gen-revoke\"の記述をご覧ください。"
+"generate-revocation\"の記述をご覧ください。"
msgid ""
"To avoid an accidental use of this file, a colon has been inserted\n"
msgstr "サポートされていないTOFUデータベースバージョン: %s\n"
#, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "'ultimately_trusted_keys' TOFUテーブル作成エラー: %s\n"
+
+msgid "TOFU DB error"
+msgstr "TOFU DBエラー"
+
+#, c-format
msgid "error reading TOFU database: %s\n"
msgstr "TOFUデータベースの読み込みエラー: %s\n"
msgstr "TOFUデータベースの初期化エラー: %s\n"
#, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "'encryptions' TOFUデータベースの作成エラー: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr "バインディングDBにカラムeffective_policyを追加: %s\n"
+
+#, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "TOFUデータベースのオープンでエラー '%s': %s\n"
msgid "error updating TOFU database: %s\n"
msgstr "TOFUデータベースの更新エラー: %s\n"
-#, fuzzy, c-format
-#| msgid "The email address \"%s\" is associated with %d key:\n"
-#| msgid_plural "The email address \"%s\" is associated with %d keys:\n"
+#, c-format
msgid ""
"This is the first time the email address \"%s\" is being used with key %s."
-msgstr "電子メールアドレス\"%s\"は%d個の鍵に結びつけられます:\n"
-
-#, fuzzy, c-format
-#| msgid "The email address \"%s\" is associated with %d key:\n"
-#| msgid_plural "The email address \"%s\" is associated with %d keys:\n"
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr "電子メールアドレス\"%s\"は%d個の鍵に結びつけられます:\n"
-
-#, fuzzy
-#| msgid ""
-#| "The key with fingerprint %s raised a conflict with the binding %s. Since "
-#| "this binding's policy was 'auto', it was changed to 'ask'."
+msgstr "電子メールアドレス\"%s\"が鍵%sに使われたのはこれが最初です。"
+
+#, c-format
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] "電子メールアドレス\"%s\"は%d個の鍵に結びつけられます!"
+
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
-"鍵のフィンガープリント%sがバインディング%sと衝突しました。このバインディング"
-"ポリシーは'auto'だったので、'ask'に変更されました。"
+" このバインディングポリシーは'auto'だったので、'ask'に変更されました。"
#, c-format
msgid ""
"Please indicate whether this email address should be associated with key %s "
"or whether you think someone is impersonating \"%s\"."
msgstr ""
+"この電子メールアドレスが鍵 %s に結びつけられるべきか、あるいは誰かが\"%s\" に"
+"なりすましていると思うか指定してください。"
#, c-format
msgid "error gathering other user IDs: %s\n"
msgstr "ほかのユーザIDの収集エラー: %s\n"
-#, fuzzy
-#| msgid "list key and user IDs"
msgid "This key's user IDs:\n"
-msgstr "鍵とユーザIDの一覧"
+msgstr "この鍵のユーザID:\n"
#, c-format
msgid "policy: %s"
msgstr "この電子メールアドレス\"%s\"の鍵の統計:\n"
msgid ", "
-msgstr ""
+msgstr ", "
msgid "this key"
msgstr "この鍵"
-#, fuzzy, c-format
-#| msgid "Verified %ld messages signed by \"%s\"."
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
-msgstr[0] "%ld個のメッセージを検証しました(\"%s\"で署名されたもの)。"
+#, c-format
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "%d個のメッセージを検証しました。"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
-msgstr[0] ""
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "%d個のメッセージを暗号化しました。"
+
+#, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "%d個のメッセージが未来に検証されました。"
+
+#, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
+msgstr[0] "%d個のメッセージが未来に暗号化されました。"
+
+#, c-format
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] "過去%d日間に検証されたメッセージの数: %d."
-#, fuzzy, c-format
-#| msgid " over the past %ld day."
-#| msgid_plural " over the past %ld days."
-msgid " over the past day."
-msgid_plural " over the past %d days."
-msgstr[0] "過去%ld日に。"
-
-#, fuzzy, c-format
-#| msgid " over the past %ld month."
-#| msgid_plural " over the past %ld months."
-msgid " over the past month."
-msgid_plural " over the past %d months."
-msgstr[0] "過去%ld月に。"
-
-#, fuzzy, c-format
-#| msgid " over the past %ld day."
-#| msgid_plural " over the past %ld days."
-msgid " over the past year."
-msgid_plural " over the past %d years."
-msgstr[0] "過去%ld日に。"
-
-#, fuzzy
-#| msgid " over the past %ld day."
-#| msgid_plural " over the past %ld days."
-msgid " in the past."
-msgstr "過去%ld日に。"
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
+msgstr[0] "過去%d日間に暗号化されたメッセージの数: %d."
+
+#, c-format
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
+msgstr[0] "過去%d月の間に検証されたメッセージの数: %d."
+
+#, c-format
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
+msgstr[0] "過去%d月の間に暗号化されたメッセージの数: %d."
+
+#, c-format
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
+msgstr[0] "過去%d年間に検証されたメッセージの数: %d."
+
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] "過去%d年間に暗号化されたメッセージの数: %d."
+
+#, c-format
+msgid "Messages verified in the past: %d."
+msgstr "これまでに検証されたメッセージの数: %d."
+
+#, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "これまでに暗号化されたメッセージの数: %d."
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
msgid "(G)ood, (A)ccept once, (U)nknown, (R)eject once, (B)ad? "
msgstr ""
-"(G)ood-è\89¯, (A)ccept once-ä¸\80度ã\81 ã\81\91è\89¯, (U)nknown-ä¸\8dæ\98\8e, (R)eject once-ä¸\80度ã\81 ã\81\91"
-"否, (B)ad-ダメ? "
+"(G)ood-è\89¯, (A)ccept once-ä¸\80度ã\81 ã\81\91èª\8dã\82\81ã\82\8b, (U)nknown-ä¸\8dæ\98\8e, (R)eject once-ä¸\80度ã\81 "
+"け否, (B)ad-ダメ? "
msgid "Defaulting to unknown."
-msgstr ""
+msgstr "不明がデフォルトです。"
msgid "TOFU db corruption detected.\n"
-msgstr ""
+msgstr "TOFU dbが壊れていることが検出されました。\n"
-#, fuzzy, c-format
-#| msgid "error writing key: %s\n"
+#, c-format
msgid "resetting keydb: %s\n"
-msgstr "鍵の書き込みエラー: %s\n"
+msgstr "keydbをリセット: %s\n"
#, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
-msgstr "TOFUã\83\87ã\83¼ã\82¿ã\83\99ã\83¼ã\82¹ã\81®ä¿¡ç\94¨ã\83¬ã\83\99ã\83«ã\81®è¨å®\9aã\82¨ã\83©ã\83¼: %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
+msgstr "TOFUã\83\90ã\82¤ã\83³ã\83\87ã\82£ã\83³ã\82°ã\81®ã\83\9dã\83ªã\82·ã\83¼ã\82\92 %s ã\81«è¨å®\9aã\82¨ã\83©ã\83¼\n"
#, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "TOFUポリシーの作成エラー: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
-msgstr[0] "%d~年"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] "%lld年"
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
-msgstr[0] "%d~月"
+msgid "%lld~month"
+msgid_plural "%lld~months"
+msgstr[0] "%lld月"
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
-msgstr[0] "%d~月"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
+msgstr[0] "%lld週"
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
-msgstr[0] "%d~時間"
+msgid "%lld~day"
+msgid_plural "%lld~days"
+msgstr[0] "%lld日"
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
-msgstr[0] "%d~分"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
+msgstr[0] "%lld時間"
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
-msgstr[0] "%d~秒"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
+msgstr[0] "%lld分"
#, c-format
-msgid "%s: "
-msgstr ""
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] "%lld秒"
-#, fuzzy, c-format
-#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
-msgstr "%d個の署名を削除しました。\n"
-
-#, fuzzy, c-format
-#| msgid ""
-#| "Verified %ld message signed by \"%s\"\n"
-#| "in the past %s."
-#| msgid_plural ""
-#| "Verified %ld messages signed by \"%s\"\n"
-#| "in the past %s."
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
-msgstr[0] "メッセージを%ld回検証しました(\"%s\"で署名されたもの。かつて %s)。"
-
-#, fuzzy, c-format
-#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
-msgstr "%lu 個のパスフレーズで暗号化\n"
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
+msgstr "%s: 0個の署名を検証、0個のメッセージを暗号化しました。"
+
+#, c-format
+msgid "%s: Verified 0 signatures."
+msgstr "%s: 0個の署名を検証しました。"
+
+#, c-format
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
+msgstr[0] "%s: 署名を%ld個検証しました(これまで %s に)。"
+
+msgid "Encrypted 0 messages."
+msgstr "0 個のメッセージを暗号化しました。"
+
+#, c-format
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
+msgstr[0] "メッセージを%ld個暗号化しました(これまで %s に)。"
+
+#, c-format
+msgid "(policy: %s)"
+msgstr "(ポリシー: %s)"
-#, fuzzy, c-format
-#| msgid ""
-#| "Verified %ld message signed by \"%s\"\n"
-#| "in the past %s."
-#| msgid_plural ""
-#| "Verified %ld messages signed by \"%s\"\n"
-#| "in the past %s."
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
-msgstr[0] "メッセージを%ld回検証しました(\"%s\"で署名されたもの。かつて %s)。"
-
-#, fuzzy
-#| msgid "Warning: we've have yet to see a message signed by this key!\n"
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
-msgstr "è¦å\91\8a: ã\81\93ã\81®é\8dµã\81§ç½²å\90\8dã\81\95ã\82\8cã\81\9fã\83¡ã\83\83ã\82»ã\83¼ã\82¸ã\82\92æ¤\9c証ã\81\97ã\81\9fã\81\93ã\81¨ã\81¯ã\81\82ã\82\8aません!\n"
+msgstr "è¦å\91\8a: ã\81\93ã\81®é\8dµã\81¨ã\83¦ã\83¼ã\82¶IDã\81§ç½²å\90\8dã\81\95ã\82\8cã\81\9fã\83¡ã\83\83ã\82»ã\83¼ã\82¸ã\81¯ä¸\80度ã\82\82è¦\8bã\81¦ません!\n"
-#, fuzzy
-#| msgid "Warning: we've only seen a single message signed by this key!\n"
msgid ""
"Warning: we've only seen one message signed using this key and user id!\n"
-msgstr ""
-"警告: この鍵で署名されたメッセージを検証したことは一度しかありません!\n"
+msgstr "警告: この鍵とユーザIDで署名されたメッセージは一度しか見てません!\n"
-#, fuzzy
-#| msgid "Warning: we've have yet to see a message signed by this key!\n"
msgid "Warning: you have yet to encrypt a message to this key!\n"
-msgstr "è¦å\91\8a: ã\81\93ã\81®é\8dµã\81§ç½²å\90\8dã\81\95ã\82\8cã\81\9fã\83¡ã\83\83ã\82»ã\83¼ã\82¸ã\82\92æ¤\9c証ã\81\97ã\81\9fã\81\93ã\81¨ã\81¯ありません!\n"
+msgstr "è¦å\91\8a: ã\81\93ã\81®é\8dµã\81¸ã\81¯ä¸\80ã\81¤ã\82\82ã\83¡ã\83\83ã\82»ã\83¼ã\82¸ã\82\92æ\9a\97å\8f·å\8c\96ã\81\97ã\81\9fã\81\93ã\81¨ã\81\8cありません!\n"
-#, fuzzy
-#| msgid "Warning: we've only seen a single message signed by this key!\n"
msgid "Warning: you have only encrypted one message to this key!\n"
-msgstr ""
-"警告: この鍵で署名されたメッセージを検証したことは一度しかありません!\n"
-
-#, fuzzy, c-format
-#| msgid ""
-#| "Warning: if you think you've seen more than %ld message signed by this "
-#| "key, then this key might be a forgery! Carefully examine the email "
-#| "address for small variations. If the key is suspect, then use\n"
-#| " %s\n"
-#| "to mark it as being bad.\n"
-#| msgid_plural ""
-#| "Warning: if you think you've seen more than %ld messages signed by this "
-#| "key, then this key might be a forgery! Carefully examine the email "
-#| "address for small variations. If the key is suspect, then use\n"
-#| " %s\n"
-#| "to mark it as being bad.\n"
+msgstr "警告: この鍵へは一つのメッセージしか暗号化したことがありません!\n"
+
+#, c-format
msgid ""
"Warning: if you think you've seen more signatures by this key and user id, "
"then this key might be a forgery! Carefully examine the email address for "
" %s\n"
"to mark it as being bad.\n"
msgstr[0] ""
-"è¦å\91\8a: ã\81\93ã\81®é\8dµã\81§ç½²å\90\8dã\81\95ã\82\8cã\81\9fã\83¡ã\83\83ã\82»ã\83¼ã\82¸ã\82\92%ldå\9b\9e以ä¸\8aè¦\8bã\81\9fã\81¨æ\80\9dã\81\86å ´å\90\88ã\80\81å\81½è\80\85ã\81®å\8f¯è\83½æ\80§ã\81\8cã\81\82"
-"ã\82\8aã\81¾ã\81\99! å°\91æ\95°ã\81®ã\83\90ã\83ªã\82¨ã\83¼ã\82·ã\83§ã\83³ã\81§ã\81\93ã\81®é\9b»å\90ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\82\92注æ\84\8fæ·±ã\81\8fæ¤\9cæ\9f»ã\81\97ã\81¦ã\81\8fã\81 ã\81\95"
-"い。この鍵が疑われる場合、\n"
+"è¦å\91\8a: ã\81\93ã\81®é\8dµã\81¨ã\83¦ã\83¼ã\82¶IDã\81«ã\82\88ã\82\8bç½²å\90\8dã\82\92ã\82\82ã\81£ã\81¨è¦\8bã\81\9fã\81¨æ\80\9dã\81\86å ´å\90\88ã\80\81ã\81\93ã\81®é\8dµã\81¯å\81½ç\89©ã\81®å\8f¯è\83½æ\80§"
+"ã\81\8cã\81\82ã\82\8aã\81¾ã\81\99! å°\91æ\95°ã\81®ã\83\90ã\83ªã\82¨ã\83¼ã\82·ã\83§ã\83³ã\81§ã\81\93ã\81®é\9b»å\90ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\82\92注æ\84\8fæ·±ã\81\8fæ¤\9cæ\9f»ã\81\97ã\81¦ã\81\8f"
+"ã\81 ã\81\95ã\81\84ã\80\82ã\81\93ã\81®é\8dµã\81\8cç\96\91ã\82\8fã\82\8cã\82\8bå ´å\90\88ã\80\81\n"
" %s\n"
"でダメとマークしてください。\n"
msgstr "TOFUデータベースのオープンでエラー: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
+"*警告*: %s に暗号化します。失効していないユーザIDが一つもないものです。\n"
-#, fuzzy, c-format
-#| msgid "error writing public keyring '%s': %s\n"
+#, c-format
msgid "error setting policy for key %s, user id \"%s\": %s"
-msgstr "公開鍵リング'%s'の書き込みエラー: %s\n"
+msgstr "鍵%s, ユーザID \"%s\"のポリシーの設定エラー: %s"
#, c-format
msgid "'%s' is not a valid long keyID\n"
msgid "response does not contain the RSA public exponent\n"
msgstr "応答にRSA公開指数が含まれていません\n"
-#, fuzzy
-#| msgid "response does not contain the EC public point\n"
msgid "response does not contain the EC public key\n"
-msgstr "応答に楕円曲線の公開点が含まれていません\n"
+msgstr "応答に楕円曲線の公開鍵が含まれていません\n"
msgid "response does not contain the public key data\n"
msgstr "応答に公開鍵データが含まれていません\n"
msgstr "OCSP要求の送信を認める"
msgid "allow online software version check"
-msgstr ""
+msgstr "オンラインのソフトウェア・バージョン・チェックを許す"
msgid "inhibit the use of HTTP"
msgstr "HTTPの使用を禁止する"
msgid "Configuration for OCSP"
msgstr "OCSPのコンフィグレーション"
-#, fuzzy
-#| msgid "GPG for OpenPGP"
msgid "OpenPGP"
-msgstr "OpenPGPのためのGPG"
+msgstr "OpenPGP"
msgid "Private Keys"
-msgstr ""
+msgstr "プライベート鍵"
-#, fuzzy
-#| msgid "Smartcard Daemon"
msgid "Smartcards"
-msgstr "スマートカード・デーモン"
+msgstr "スマートカード"
-#, fuzzy
-#| msgid "GPG for S/MIME"
msgid "S/MIME"
-msgstr "S/MIME のためのGPG"
+msgstr "S/MIME"
msgid "Network"
-msgstr ""
+msgstr "ネットワーク"
-#, fuzzy
-#| msgid "PIN and Passphrase Entry"
msgid "Passphrase Entry"
-msgstr "PINとパスフレーズの入力"
+msgstr "パスフレーズ入力"
msgid "Component not suitable for launching"
msgstr "コンポーネントが起動するために適切ではありません"
msgid "Note that group specifications are ignored\n"
msgstr "グループ仕様は無視されていることに注意してください\n"
+#, c-format
+msgid "error closing '%s'\n"
+msgstr "'%s'でクローズのエラー\n"
+
+#, c-format
+msgid "error parsing '%s'\n"
+msgstr "'%s'でパーズのエラー\n"
+
msgid "list all components"
msgstr "すべてのコンポーネントをリストする"
msgid "apply global default values"
msgstr "グローバル・デフォルト値を適用する"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FILE|FILEを使ってコンフィグレーション・ファイルを更新する"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "@GPGCONF@のためにコンフィグレーション・ディレクトリを取得する"
msgid "check global configuration file"
msgstr "グローバルのコンフィグレーション・ファイルをチェックする"
-#, fuzzy
-#| msgid "update the trust database"
msgid "query the software version database"
-msgstr "信用データベースを更新"
+msgstr "ソフトウェア・バージョン・データベースに問い合わせる"
msgid "reload all or a given component"
msgstr "すべて、あるいは指定されたコンポーネントをリロードする"
"形式: gpg-check-pattern [オプション] パターンファイル\n"
"パターンファイルに対して標準入力のパスフレーズを確認する\n"
+#~ msgid ""
+#~ "@\n"
+#~ "Examples:\n"
+#~ "\n"
+#~ " -se -r Bob [file] sign and encrypt for user Bob\n"
+#~ " --clearsign [file] make a clear text signature\n"
+#~ " --detach-sign [file] make a detached signature\n"
+#~ " --list-keys [names] show keys\n"
+#~ " --fingerprint [names] show fingerprints\n"
+#~ msgstr ""
+#~ "@\n"
+#~ "例:\n"
+#~ "\n"
+#~ " -se -r Bob [ファイル] ユーザBobへ署名と暗号化\n"
+#~ " --clearsign [ファイル] クリア・テクスト署名を作成\n"
+#~ " --detach-sign [ファイル] 分遣署名を作成\n"
+#~ " --list-keys [名前] 鍵を表示\n"
+#~ " --fingerprint [名前] フィンガープリントを表示\n"
+
+#~ msgid "--store [filename]"
+#~ msgstr "--store [ファイル名]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [ファイル名]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [ファイル名]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [ファイル名]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [ファイル名]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [ファイル名]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [ファイル名]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [ファイル名]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [ファイル名]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [ファイル名]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key ユーザid"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key ユーザid"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key ユーザid [コマンド]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <ユーザid>"
+
+#~ msgid "[filename]"
+#~ msgstr "[ファイル名]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "鍵のシャドウ化に失敗しました: %s\n"
#~ msgid "Known user IDs associated with this key:\n"
#~ msgstr "この鍵に結びつけられた知られているユーザID:\n"
-#~ msgid "%ld message signed in the future."
-#~ msgid_plural "%ld messages signed in the future."
-#~ msgstr[0] "%ld個のメッセージが未来に署名されました。"
-
#~ msgid "%ld message signed"
#~ msgid_plural "%ld messages signed"
#~ msgstr[0] "%ld個のメッセージに署名しました"
msgid "quickly revoke a user-id"
msgstr "opphev bruker-ID raskt"
+#, fuzzy
+#| msgid "quickly generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "lag nytt nøkkelpar raskt"
+
msgid "full featured key pair generation"
msgstr "fullverdig generering av nøkkelpar"
"@\n"
"(Se bruksanvisning for en fullstendig liste over alle kommandoer og valg)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Eksempler:\n"
"\n"
" -se -r Bob [fil] signer og krypter for brukeren Bob\n"
-" --clearsign [fil] lag klartekst-signatur\n"
+" --clear-sign [fil] lag klartekst-signatur\n"
" --detach-sign [fil] lag adskilt signatur\n"
" --list-keys [navn] vis nøkler\n"
" --fingerprint [navn] vis fingeravtrykk\n"
msgid "WARNING: recipients (-r) given without using public key encryption\n"
msgstr "ADVARSEL: mottakere (-r) valgt uten offentlig nøkkelkryptering\n"
-msgid "--store [filename]"
-msgstr "--store [filnavn]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [filnavn]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "symmetrisk kryptering av «%s» mislyktes: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [filnavn]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [filnavn]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "du kan ikke bruke «--symmetric --encrypt» og «--s2k-mode 0» samtidig\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "du kan ikke velge «--symmtric» og «--encrypt» i «%s»-modus\n"
-msgid "--sign [filename]"
-msgstr "--sign [filnavn]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [filnavn]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [filnavn]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
"du kan ikke bruke «--symmetric --sign --encrypt» og «--s2k-mode 0» samtidig\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "du kan ikke velge «--symmtric», «--sign» og «--encrypt» i «%s»-modus\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [filnavn]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [filnavn]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [filnavn]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key bruker-ID"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key bruker-ID"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key bruker-ID [kommandoer]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <bruker-id>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "sending til nøkkeltjener mislyktes: %s\n"
"«%s» ser hverken ut til å være en gyldig nøkkel-ID, fingeravtrykk eller "
"nøkkelgrep\n"
-msgid "[filename]"
-msgstr "[filnavn]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Skriv inn melding …\n"
msgid "Nothing to sign.\n"
msgstr "Ingenting å signere.\n"
+#, fuzzy, c-format
+#| msgid "'%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "«%s» er en ugyldig signatur-utløpstid\n"
+
msgid "Digest: "
msgstr "Kontrollsum: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"Bruk det for å oppheve denne nøkkelen hvis den blir kompromittert\n"
"eller hvis du mister den hemmelige nøkkelen. Hvis du fremdeles har\n"
"tilgang til den hemmelige nøkkelen, er det lurt å heller lage et nytt\n"
"opphevelsessertifikat og oppgi en grunn til opphevelsen.\n"
-"Se beskrivelse av kommandoen «--gen-revoke» i GnuPG-\n"
+"Se beskrivelse av kommandoen «--generate-revocation» i GnuPG-\n"
"bruksanvisninga for detaljer."
msgid ""
msgid "unsupported TOFU database version: %s\n"
msgstr "TOFU-databaseversjon støttes ikke: %s\n"
+#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "feil under oppretting av midlertidig fil: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
#, c-format
msgid "error reading TOFU database: %s\n"
msgstr "feil under lesing av TOFU-database: %s\n"
msgid "error initializing TOFU database: %s\n"
msgstr "feil under oppstart av TOFU-database: %s\n"
+#, fuzzy, c-format
+#| msgid "error reading TOFU database: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "feil under lesing av TOFU-database: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
#, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "feil under åpning av TOFU-database «%s»: %s\n"
#, fuzzy, c-format
#| msgid "The email address \"%s\" is associated with %d key:\n"
#| msgid_plural "The email address \"%s\" is associated with %d keys:\n"
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr "E-postadressen «%s» er tilknyttet %d nøkkel:\n"
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] "E-postadressen «%s» er tilknyttet %d nøkkel:\n"
+msgstr[1] "E-postadressen «%s» er tilknyttet %d nøkkel:\n"
#, fuzzy
#| msgid ""
#, fuzzy, c-format
#| msgid "Verified %ld messages signed by \"%s\"."
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
msgstr[0] "Bekreftet %ld meldinger signert av «%s»."
msgstr[1] "Bekreftet %ld meldinger signert av «%s»."
-#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
-msgstr[0] ""
-msgstr[1] ""
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "kryptert med %lu passordfraser\n"
+msgstr[1] "kryptert med %lu passordfraser\n"
+
+#, fuzzy, c-format
+#| msgid "%ld message signed in the future."
+#| msgid_plural "%ld messages signed in the future."
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "%ld melding signert i fremtid."
+msgstr[1] "%ld meldinger signert i fremtid."
+
+#, fuzzy, c-format
+#| msgid "%ld message signed in the future."
+#| msgid_plural "%ld messages signed in the future."
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
+msgstr[0] "%ld melding signert i fremtid."
+msgstr[1] "%ld meldinger signert i fremtid."
+
+#, fuzzy, c-format
+#| msgid " over the past %ld day."
+#| msgid_plural " over the past %ld days."
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] " i løpet av gårsdagen (%ld)."
+msgstr[1] " i løpet av de %ld siste dagene."
#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] " i løpet av gårsdagen (%ld)."
msgstr[1] " i løpet av de %ld siste dagene."
#, fuzzy, c-format
#| msgid " over the past %ld month."
#| msgid_plural " over the past %ld months."
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
+msgstr[0] " i løpet av den siste måneden (%ld)."
+msgstr[1] " i løpet av de siste %ld månedene."
+
+#, fuzzy, c-format
+#| msgid " over the past %ld month."
+#| msgid_plural " over the past %ld months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] " i løpet av den siste måneden (%ld)."
msgstr[1] " i løpet av de siste %ld månedene."
#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] " i løpet av gårsdagen (%ld)."
msgstr[1] " i løpet av de %ld siste dagene."
-#, fuzzy
+#, fuzzy, c-format
+#| msgid " over the past %ld day."
+#| msgid_plural " over the past %ld days."
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] " i løpet av gårsdagen (%ld)."
+msgstr[1] " i løpet av de %ld siste dagene."
+
+#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " in the past."
+msgid "Messages verified in the past: %d."
msgstr " i løpet av gårsdagen (%ld)."
+#, fuzzy, c-format
+#| msgid ""
+#| "Verified %ld message signed by \"%s\"\n"
+#| "in the past %s."
+#| msgid_plural ""
+#| "Verified %ld messages signed by \"%s\"\n"
+#| "in the past %s."
+msgid "Messages encrypted in the past: %d."
+msgstr ""
+"Bekreftet %ld melding signert av «%s»\n"
+"siste %s."
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgid "resetting keydb: %s\n"
msgstr "feil under skriving av nøkkel: %s\n"
-#, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+#, fuzzy, c-format
+#| msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "feil under endring av tillitsnivå for TOFU-tilknytning til %s\n"
#, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "feil under endring av TOFU-regler: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
-#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+#, fuzzy, c-format
+#| msgid "%d~year"
+#| msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] "%d~år"
msgstr[1] "%d~år"
-#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+#, fuzzy, c-format
+#| msgid "%d~month"
+#| msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] "%d~måned"
msgstr[1] "%d~måneder"
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
+msgstr[0] ""
+msgstr[1] ""
+
+#, fuzzy, c-format
+#| msgid "%d~day"
+#| msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] "%d~dag"
msgstr[1] "%d~dager"
-#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+#, fuzzy, c-format
+#| msgid "%d~hour"
+#| msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] "%d~time"
msgstr[1] "%d~timer"
-#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+#, fuzzy, c-format
+#| msgid "%d~minute"
+#| msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] "%d~minutt"
msgstr[1] "%d~minutter"
-#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+#, fuzzy, c-format
+#| msgid "%d~second"
+#| msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] "%d~sekund"
msgstr[1] "%d~sekunder"
-#, c-format
-msgid "%s: "
-msgstr ""
+#, fuzzy, c-format
+#| msgid "TOFU: few signatures %d message %s"
+#| msgid_plural "TOFU: few signatures %d messages %s"
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
+msgstr "TOFU: få signaturer %d melding %s"
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Slettet %d signaturer.\n"
#, fuzzy, c-format
#| msgid_plural ""
#| "Verified %ld messages signed by \"%s\"\n"
#| "in the past %s."
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] ""
"Bekreftet %ld melding signert av «%s»\n"
"siste %s."
"Bekreftet %ld meldinger signert av «%s»\n"
"siste %s."
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "kryptert med %lu passordfraser\n"
#, fuzzy, c-format
#| msgid_plural ""
#| "Verified %ld messages signed by \"%s\"\n"
#| "in the past %s."
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] ""
"Bekreftet %ld melding signert av «%s»\n"
"siste %s."
"Bekreftet %ld meldinger signert av «%s»\n"
"siste %s."
+#, fuzzy, c-format
+#| msgid "policy: %s"
+msgid "(policy: %s)"
+msgstr "regelverk: %s"
+
#, fuzzy
#| msgid "Warning: we've have yet to see a message signed by this key!\n"
msgid ""
msgstr "feil under åpning av TOFU-database: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Merk at gruppespesifikasjoner blir ignorert\n"
+#, fuzzy, c-format
+#| msgid "error closing '%s': %s\n"
+msgid "error closing '%s'\n"
+msgstr "feil under lukking av «%s»: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error hashing '%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "feil under summering av «%s»: %s\n"
+
msgid "list all components"
msgstr "vis alle komponenter"
msgid "apply global default values"
msgstr "bruk globale standardverdier"
+#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FIL|hent regler fra valgt FIL"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "hent oppsettsmapper for @GPGCONF@"
"Syntaks: gpg-check-pattern [valg] mønsterfil\n"
"Kontroller passordfrase oppgitt på standard innkanal mot valgt mønsterfil\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [filnavn]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [filnavn]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [filnavn]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [filnavn]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [filnavn]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [filnavn]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [filnavn]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [filnavn]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [filnavn]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [filnavn]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key bruker-ID"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key bruker-ID"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key bruker-ID [kommandoer]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <bruker-id>"
+
+#~ msgid "[filename]"
+#~ msgstr "[filnavn]"
+
+#, fuzzy
+#~| msgid " over the past %ld day."
+#~| msgid_plural " over the past %ld days."
+#~ msgid " in the past."
+#~ msgstr " i løpet av gårsdagen (%ld)."
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "skyggelegging av nøkkel mislyktes: %s\n"
#~ msgid "Known user IDs associated with this key:\n"
#~ msgstr "Kjente bruker-id-er tilknyttet denne nøkkelen:\n"
-#~ msgid "%ld message signed in the future."
-#~ msgid_plural "%ld messages signed in the future."
-#~ msgstr[0] "%ld melding signert i fremtid."
-#~ msgstr[1] "%ld meldinger signert i fremtid."
-
#~ msgid "%ld message signed"
#~ msgid_plural "%ld messages signed"
#~ msgstr[0] "%ld melding signert"
#~ msgstr[0] "%d dag"
#~ msgstr[1] "%d dager"
-#~ msgid "TOFU: few signatures %d message %s"
-#~ msgid_plural "TOFU: few signatures %d messages %s"
-#~ msgstr[0] "TOFU: få signaturer %d melding %s"
-#~ msgstr[1] "TOFU: få signaturer %d meldinger %s"
-
#~ msgid "can't gen prime with pbits=%u qbits=%u\n"
#~ msgstr "du kan ikke lage primtall med pbits=%u qbits=%u\n"
msgid "quickly revoke a user-id"
msgstr "generacja nowej pary kluczy"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "generacja nowej pary kluczy"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Pełną listę poleceń i opcji można znaleźć w podręczniku systemowym.)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Przykłady:\n"
"\n"
" -se -r Bob [plik] podpisanie i zaszyfrowanie kluczem Boba\n"
-" --clearsign [plik] podpisanie z pozostawieniem czytelności "
+" --clear-sign [plik] podpisanie z pozostawieniem czytelności "
"dokumentu\n"
" --detach-sign [plik] podpisanie z umieszczeniem podpisu w osobnym "
"pliku\n"
msgid "WARNING: recipients (-r) given without using public key encryption\n"
msgstr "OSTRZEŻENIE: podano adresatów (-r) w działaniu które ich nie dotyczy\n"
-msgid "--store [filename]"
-msgstr "--store [plik]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [plik]"
-
#, fuzzy, c-format
#| msgid "symmetric encryption of `%s' failed: %s\n"
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "szyfrowanie symetryczne ,,%s'' nie powiodło się: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [plik]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [plik]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "nie można użyć --symmetric --encrypt wraz z --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "nie można użyć --symmetric --encrypt w trybie %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [plik]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [plik]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [plik]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "nie można użyć --symmetric --sign --encrypt wraz z --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "nie można użyć --symmetric --sign --encrypt w trybie %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [plik]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [plik]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [plik]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key nazwa użytkownika"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key nazwa użytkownika"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key nazwa użytkownika [polecenia]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <id-użytkownika>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "wysyłka do serwera kluczy nie powiodła się: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[nazwa pliku]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Wpisz tutaj swoją wiadomość ...\n"
msgid "Nothing to sign.\n"
msgstr "Nie ma nic do podpisania kluczem %s\n"
+#, fuzzy, c-format
+#| msgid "`%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr ",,%s'' nie jest poprawnym czasem wygaśnięcia podpisu\n"
+
msgid "Digest: "
msgstr "Skrót: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
msgstr "nieobsługiwany algorytm: %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "błąd tworzenia pliku tymczasowego: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending %s command: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "błąd wysyłania polecenia %s: %s\n"
msgstr "%s: błąd zapisu wpisu katalogowego: %s\n"
#, fuzzy, c-format
+#| msgid "error sending %s command: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "błąd wysyłania polecenia %s: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening `%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "błąd podczas otwierania ,,%s'': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "wypisanie skrótów wiadomości"
+msgstr[1] "wypisanie skrótów wiadomości"
+msgstr[2] "wypisanie skrótów wiadomości"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "zaszyfrowane za pomocą %lu haseł\n"
+msgstr[1] "zaszyfrowane za pomocą %lu haseł\n"
+msgstr[2] "zaszyfrowane za pomocą %lu haseł\n"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "wypisanie skrótów wiadomości"
+msgstr[1] "wypisanie skrótów wiadomości"
+msgstr[2] "wypisanie skrótów wiadomości"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "wypisanie skrótów wiadomości"
msgstr[1] "wypisanie skrótów wiadomości"
msgstr[2] "wypisanie skrótów wiadomości"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Messages encrypted in the past: %d."
+msgstr "wypisanie skrótów wiadomości"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error storing flags: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "błąd zapisywania flag: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "błąd tworzenia potoku: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "%d podpisów usuniętych.\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "wypisanie skrótów wiadomości"
msgstr[1] "wypisanie skrótów wiadomości"
msgstr[2] "wypisanie skrótów wiadomości"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "zaszyfrowane za pomocą %lu haseł\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "wypisanie skrótów wiadomości"
msgstr[1] "wypisanie skrótów wiadomości"
msgstr[2] "wypisanie skrótów wiadomości"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "poprawność: %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "błąd wysyłania polecenia %s: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Uwaga, określenia grup są ignorowane\n"
+#, fuzzy, c-format
+#| msgid "error closing %s: %s\n"
+msgid "error closing '%s'\n"
+msgstr "błąd zamykania %s: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error in `%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "błąd w ,,%s'': %s\n"
+
msgid "list all components"
msgstr "lista wszystkich komponentów"
msgstr "zastosowanie globalnych wartości domyślnych"
#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|PLIK|pobranie informacji o polityce z PLIKU"
+
+#, fuzzy
#| msgid "get the configuration directories for gpgconf"
msgid "get the configuration directories for @GPGCONF@"
msgstr "katalogi konfiguracyjne programu gpgconf"
"Składnia: gpg-check-pattern [opcje] plik-wzorców\n"
"Sprawdzanie hasła ze standardowego wejścia względem pliku wzorców\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [plik]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [plik]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [plik]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [plik]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [plik]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [plik]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [plik]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [plik]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [plik]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [plik]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key nazwa użytkownika"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key nazwa użytkownika"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key nazwa użytkownika [polecenia]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <id-użytkownika>"
+
+#~ msgid "[filename]"
+#~ msgstr "[nazwa pliku]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "zaciemnienie klucza nie powiodło się: %s\n"
msgid "quickly revoke a user-id"
msgstr "gerar um novo par de chaves"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "gerar um novo par de chaves"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Veja a página man para uma lista completa de comandos e opções)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Exemplos:\n"
"\n"
" -se -r Bob [ficheiro] assinar e cifrar para o utilizador Bob\n"
-" --clearsign [ficheiro] criar uma assinatura em texto puro\n"
+" --clear-sign [ficheiro] criar uma assinatura em texto puro\n"
" --detach-sign [ficheiro] criar uma assinatura separada\n"
" --list-keys [nomes] mostrar chaves\n"
" --fingerprint [nomes] mostrar impressões digitais\n"
msgstr ""
"AVISO: destinatários (-r) dados sem utilizar uma cifra de chave pública\n"
-msgid "--store [filename]"
-msgstr "--store [nome_do_ficheiro]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [nome_do_ficheiro]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "decifragem falhou: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [nome_do_ficheiro]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [nome_do_ficheiro]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "não pode utilizar %s enquanto estiver no modo %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [nome_do_ficheiro]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [nome_do_ficheiro]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [nome_do_ficheiro]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "não pode utilizar %s enquanto estiver no modo %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [nome_do_ficheiro]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [nome_do_ficheiro]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [nome_do_ficheiro]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key id-utilizador"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key id-utilizador"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key id-utilizador [comandos]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key id-utilizador"
-
#, fuzzy, c-format
msgid "keyserver send failed: %s\n"
msgstr "A geração de chaves falhou: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[nome_do_ficheiro]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Digite a sua mensagem ...\n"
msgid "Nothing to sign.\n"
msgstr "Nada para assinar com a chave %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s não é um conjunto de caracteres válido\n"
+
msgid "Digest: "
msgstr "'Digest': "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Algoritmos suportados:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "erro na criação da frase secreta: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "erro ao enviar para `%s': %s\n"
msgstr "%s: erro ao escrever registo de diretório: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "erro ao enviar para `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "erro na leitura de `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "listar as chaves"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
+msgstr[1] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "Repita a frase secreta\n"
+msgstr[1] "Repita a frase secreta\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
+msgstr[1] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
msgstr[1] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [ficheiros]|imprimir \"digests\" de mensagens"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "erro na escrita do porta-chaves `%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "erro na leitura de `%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "erro na criação da frase secreta: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "%d assinaturas removidas.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
msgstr[1] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
-#, fuzzy, c-format
-msgid ", and encrypted %ld messages"
+#, fuzzy
+msgid "Encrypted 0 messages."
msgstr "Repita a frase secreta\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
msgstr[1] "|algo [ficheiros]|imprimir \"digests\" de mensagens"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Política: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "erro ao enviar para `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "erro na leitura de `%s': %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "erro na leitura de `%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [nome_do_ficheiro]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [nome_do_ficheiro]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [nome_do_ficheiro]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nome_do_ficheiro]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [nome_do_ficheiro]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nome_do_ficheiro]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nome_do_ficheiro]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [nome_do_ficheiro]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [nome_do_ficheiro]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [nome_do_ficheiro]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key id-utilizador"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key id-utilizador"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key id-utilizador [comandos]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key id-utilizador"
+
+#~ msgid "[filename]"
+#~ msgstr "[nome_do_ficheiro]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "remoção do bloco de chave falhou: %s\n"
msgid "quickly revoke a user-id"
msgstr "generează o nouă perechi de chei"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "generează o nouă perechi de chei"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Arată pagina man pentru o listă completă a comenzilor şi opţiunilor)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Exemple:\n"
"\n"
" -se -r Dan [fişier] semnează şi cifrează pentru utilizatorul Dan\n"
-" --clearsign [fişier] crează o semnătură text în clar\n"
+" --clear-sign [fişier] crează o semnătură text în clar\n"
" --detach-sign [fişier] crează o semnătură detaşată\n"
" --list-keys [nume] arată chei\n"
" --fingerprint [nume] arată amprente\n"
"AVERTISMENT: destinatari (-r) furnizaţi fără a folosi cifrare cu cheie "
"publică\n"
-msgid "--store [filename]"
-msgstr "--store [nume_fişier]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [nume_fişier]"
-
#, fuzzy, c-format
#| msgid "symmetric encryption of `%s' failed: %s\n"
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "cifrarea simetrică a lui `%s' a eşuat: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [nume_fişier]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [nume_fişier]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "nu puteţi folosi --symmetric --encrypt cu --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "nu puteţi folosi --symmetric --encrypt câtă vreme în modul %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [nume_fişier]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [nume_fişier]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [nume_fişier]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "nu puteţi folosi --symmetric --sign --encrypt cu --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "nu puteţi folosi --symmetric --sign --encrypt câtă vreme în modul %s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [nume_fişier]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [nume_fişier]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [nume_fişier]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key id-utilizator"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key id-utilizator"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key id-utilizator [comenzi]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key id-utilizator"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "trimitere server de chei eşuată: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[nume_fişier]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Daţi-i drumul şi scrieţi mesajul ...\n"
msgid "Nothing to sign.\n"
msgstr "Nimic de semnat cu cheia %s\n"
+#, fuzzy, c-format
+#| msgid "`%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "`%s' nu este expirare de semnătură validă\n"
+
msgid "Digest: "
msgstr "Rezumat: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Algoritmuri suportate:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "eroare la crearea frazei-parolă: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "eroare trimitere la `%s': %s\n"
msgstr "%s: eroare scriere înregistrare dir: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "eroare trimitere la `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "eroare în `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "enumeră chei"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [fişiere]|afişează rezumate mesaje"
+msgstr[1] "|algo [fişiere]|afişează rezumate mesaje"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "cifrată cu %lu fraze-parolă\n"
+msgstr[1] "cifrată cu %lu fraze-parolă\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [fişiere]|afişează rezumate mesaje"
+msgstr[1] "|algo [fişiere]|afişează rezumate mesaje"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [fişiere]|afişează rezumate mesaje"
msgstr[1] "|algo [fişiere]|afişează rezumate mesaje"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [fişiere]|afişează rezumate mesaje"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "eroare la scrierea inelului de chei `%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "eroare la citire `%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "eroare la crearea frazei-parolă: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Am şters %d semnături.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [fişiere]|afişează rezumate mesaje"
msgstr[1] "|algo [fişiere]|afişează rezumate mesaje"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "cifrată cu %lu fraze-parolă\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [fişiere]|afişează rezumate mesaje"
msgstr[1] "|algo [fişiere]|afişează rezumate mesaje"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Politica: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "eroare trimitere la `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "eroare în `%s': %s\n"
+
+#, fuzzy, c-format
+#| msgid "error in `%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "eroare în `%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [nume_fişier]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [nume_fişier]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [nume_fişier]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [nume_fişier]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [nume_fişier]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [nume_fişier]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [nume_fişier]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [nume_fişier]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [nume_fişier]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [nume_fişier]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key id-utilizator"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key id-utilizator"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key id-utilizator [comenzi]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key id-utilizator"
+
+#~ msgid "[filename]"
+#~ msgstr "[nume_fişier]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "citirea cheii publice a eşuat: %s\n"
msgid "quickly revoke a user-id"
msgstr "быстро отозвать ID пользователя"
+#, fuzzy
+#| msgid "quickly generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "быстро создать новую пару ключей"
+
msgid "full featured key pair generation"
msgstr "создание полноценной пары ключей"
"@\n"
"(Полный список команд и параметров см. на странице man)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Примеры:\n"
"\n"
" -se -r Вова [файл] подписать и зашифровать для получателя Вова\n"
-" --clearsign [файл] создать текстовую подпись\n"
+" --clear-sign [файл] создать текстовую подпись\n"
" --detach-sign [файл] создать отделенную подпись\n"
" --list-keys [имена] показать ключи\n"
" --fingerprint [имена] показать отпечатки\n"
"ВНИМАНИЕ: получатели (-r) заданы без использования шифрования с открытым "
"ключом\n"
-msgid "--store [filename]"
-msgstr "--store [файл]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [файл]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "сбой симметричного шифрования '%s': %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [файл]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [файл]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "--symmetric --encrypt нельзя использовать совместно с --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "в режиме %s нельзя использовать --symmetric --encrypt\n"
-msgid "--sign [filename]"
-msgstr "--sign [файл]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [файл]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [файл]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
"нельзя использовать --symmetric --sign --encrypt совместно с --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "в режиме %s нельзя использовать --symmetric --sign --encrypt\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [файл]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [файл]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [файл]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key <ID пользователя>"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key <ID пользователя>"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key <ID пользователя> [команды]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <ID пользователя>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "сбой при отправке на сервер ключей: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr "'%s' не является идентификатором, отпечатком или кодом ключа\n"
-msgid "[filename]"
-msgstr "[файл]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Пишите сообщение ...\n"
msgid "Nothing to sign.\n"
msgstr "Подписывать нечего.\n"
+#, fuzzy, c-format
+#| msgid "'%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "'%s' - не допустимый срок действия подписи\n"
+
msgid "Digest: "
msgstr "Хеш: "
msgid "%s: This key is bad! It has been marked as untrusted!\n"
msgstr "%s: Некачественный ключ! Он помечен как недоверенный!\n"
+#, fuzzy
+#| msgid ""
+#| "This key has is bad! It has been marked as untrusted! If you\n"
+#| "*really* know what you are doing, you may answer the next\n"
+#| "question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"Пользуйтесь им для отзыва этого ключа в случае раскрытия или потери\n"
"секретного ключа. Однако, если секретный ключ доступен, лучше создать\n"
"новый сертификат с указанием причины отзыва. Подробности см. в описании\n"
-"команды gpg \"--gen-revoke\" в руководстве по GnuPG."
+"команды gpg \"--generate-revocation\" в руководстве по GnuPG."
msgid ""
"To avoid an accidental use of this file, a colon has been inserted\n"
msgid "unsupported TOFU database version: %s\n"
msgstr "версия базы данных TOFU (не поддерживается): %s\n"
+#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "ошибка создания временного файла: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
#, c-format
msgid "error reading TOFU database: %s\n"
msgstr "ошибка чтения базы данных TOFU: %s\n"
msgid "error initializing TOFU database: %s\n"
msgstr "ошибка инициализации базы данных TOFU: %s\n"
+#, fuzzy, c-format
+#| msgid "error reading TOFU database: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "ошибка чтения базы данных TOFU: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
#, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "ошибка открытия базы данных TOFU '%s': %s\n"
"This is the first time the email address \"%s\" is being used with key %s."
msgstr "Адрес электронной почты \"%s\" используется с ключом %s впервые."
-#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr "Адрес электронной почты \"%s\" связан с %d ключами!"
+#, fuzzy, c-format
+#| msgid "The email address \"%s\" is associated with %d keys!"
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] "Адрес электронной почты \"%s\" связан с %d ключами!"
+msgstr[1] "Адрес электронной почты \"%s\" связан с %d ключами!"
+msgstr[2] "Адрес электронной почты \"%s\" связан с %d ключами!"
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgid "this key"
msgstr "этот ключ"
-#, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+#, fuzzy, c-format
+#| msgid "Verified %d message"
+#| msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
msgstr[0] "Проверено %d сообщение"
msgstr[1] "Проверены %d сообщения"
msgstr[2] "Проверено %d сообщений"
-#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+#, fuzzy, c-format
+#| msgid "Encrypted %d message"
+#| msgid_plural "Encrypted %d messages"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
msgstr[0] "Зашифровано %d сообщение"
msgstr[1] "Зашифрованы %d сообщения"
msgstr[2] "Зашифровано %d сообщений"
-#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+#, fuzzy, c-format
+#| msgid "Verified %d message"
+#| msgid_plural "Verified %d messages"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "Проверено %d сообщение"
+msgstr[1] "Проверены %d сообщения"
+msgstr[2] "Проверено %d сообщений"
+
+#, fuzzy, c-format
+#| msgid ", and encrypted %ld message in the past %s"
+#| msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
+msgstr[0] ", зашифровано %ld сообщение за %s."
+msgstr[1] ", зашифрованы %ld сообщения за %s."
+msgstr[2] ", зашифровано %ld сообщений за %s."
+
+#, fuzzy, c-format
+#| msgid " over the past day."
+#| msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
msgstr[0] " за %d прошедший день."
msgstr[1] " за %d прошедших дня."
msgstr[2] " за %d прошедших дней."
-#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+#, fuzzy, c-format
+#| msgid " over the past day."
+#| msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
+msgstr[0] " за %d прошедший день."
+msgstr[1] " за %d прошедших дня."
+msgstr[2] " за %d прошедших дней."
+
+#, fuzzy, c-format
+#| msgid " over the past month."
+#| msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] " за %d прошедший месяц."
msgstr[1] " за %d прошедших месяца."
msgstr[2] " за %d прошедших месяцев."
-#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+#, fuzzy, c-format
+#| msgid " over the past month."
+#| msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
+msgstr[0] " за %d прошедший месяц."
+msgstr[1] " за %d прошедших месяца."
+msgstr[2] " за %d прошедших месяцев."
+
+#, fuzzy, c-format
+#| msgid " over the past year."
+#| msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] " за %d прошедший год."
msgstr[1] " за %d прошедших дня."
msgstr[2] " за %d прошедших дней."
-msgid " in the past."
-msgstr " в прошлом."
+#, fuzzy, c-format
+#| msgid " over the past year."
+#| msgid_plural " over the past %d years."
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] " за %d прошедший год."
+msgstr[1] " за %d прошедших дня."
+msgstr[2] " за %d прошедших дней."
+
+#, fuzzy, c-format
+#| msgid " over the past day."
+#| msgid_plural " over the past %d days."
+msgid "Messages verified in the past: %d."
+msgstr " за %d прошедший день."
+
+#, fuzzy, c-format
+#| msgid ", and encrypted %ld message in the past %s"
+#| msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Messages encrypted in the past: %d."
+msgstr ", зашифровано %ld сообщение за %s."
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
msgid "resetting keydb: %s\n"
msgstr "сброс базы данных ключей: %s\n"
-#, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+#, fuzzy, c-format
+#| msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "ошибка установки уровня доверия привязки TOFU в %s\n"
#, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "ошибка при смене правила TOFU: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
-#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+#, fuzzy, c-format
+#| msgid "%d~year"
+#| msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] "%d~год"
msgstr[1] "%d~года"
msgstr[2] "%d~лет"
-#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+#, fuzzy, c-format
+#| msgid "%d~month"
+#| msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] "%d~месяц"
msgstr[1] "%d~месяца"
msgstr[2] "%d~месяцев"
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, fuzzy, c-format
+#| msgid "%d~day"
+#| msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] "%d~день"
msgstr[1] "%d~дня"
msgstr[2] "%d~дней"
-#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+#, fuzzy, c-format
+#| msgid "%d~hour"
+#| msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] "%d~час"
msgstr[1] "%d~часа"
msgstr[2] "%d~часов"
-#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+#, fuzzy, c-format
+#| msgid "%d~minute"
+#| msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] "%d~минута"
msgstr[1] "%d~минуты"
msgstr[2] "%d~минут"
-#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+#, fuzzy, c-format
+#| msgid "%d~second"
+#| msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] "%d~секунда"
msgstr[1] "%d~секунды"
msgstr[2] "%d~секунд"
#, c-format
-msgid "%s: "
-msgstr "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
+msgstr ""
-#, c-format
-msgid "Verified %ld signatures"
+#, fuzzy, c-format
+#| msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Проверено %ld подписей"
-#, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+#, fuzzy, c-format
+#| msgid "Verified %ld signature in the past %s"
+#| msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "Проверена %ld подпись за %s."
msgstr[1] "Проверены %ld прописи за %s."
msgstr[2] "Проверено %ld подписей за %s."
-#, c-format
-msgid ", and encrypted %ld messages"
-msgstr ", зашифровано %ld сообщений"
+#, fuzzy
+#| msgid "Encrypted %d message"
+#| msgid_plural "Encrypted %d messages"
+msgid "Encrypted 0 messages."
+msgstr "Зашифровано %d сообщение"
-#, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+#, fuzzy, c-format
+#| msgid ", and encrypted %ld message in the past %s"
+#| msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] ", зашифровано %ld сообщение за %s."
msgstr[1] ", зашифрованы %ld сообщения за %s."
msgstr[2] ", зашифровано %ld сообщений за %s."
+#, fuzzy, c-format
+#| msgid "policy: %s"
+msgid "(policy: %s)"
+msgstr "правило: %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgid "error opening TOFU database: %s\n"
msgstr "ошибка при открытии базы данных TOFU: %s\n"
-#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+#, fuzzy, c-format
+#| msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
"ВНИМАНИЕ: Шифрование для ключа %s, у которого нет неотозванных "
"идентификаторов пользователя.\n"
msgid "Note that group specifications are ignored\n"
msgstr "Обратите внимание, что спецификации групп игнорируются\n"
+#, fuzzy, c-format
+#| msgid "error closing '%s': %s\n"
+msgid "error closing '%s'\n"
+msgstr "ошибка закрытия '%s': %s\n"
+
+#, fuzzy, c-format
+#| msgid "error hashing '%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "ошибка при получении хеша '%s': %s\n"
+
msgid "list all components"
msgstr "вывод списка всех компонентов"
msgid "apply global default values"
msgstr "применить глобальные значения по умолчанию"
+#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FILE|взять информацию о правилах из файла FILE"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "получить каталоги настроек для @GPGCONF@"
"Синтаксис: gpg-check-pattern [параметры] файл_образцов\n"
"Проверить фразу-пароль, поступающую из stdin, по файлу образцов\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [файл]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [файл]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [файл]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [файл]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [файл]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [файл]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [файл]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [файл]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [файл]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [файл]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key <ID пользователя>"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key <ID пользователя>"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key <ID пользователя> [команды]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <ID пользователя>"
+
+#~ msgid "[filename]"
+#~ msgstr "[файл]"
+
+#~ msgid " in the past."
+#~ msgstr " в прошлом."
+
+#~ msgid "%s: "
+#~ msgstr "%s: "
+
+#~ msgid ", and encrypted %ld messages"
+#~ msgstr ", зашифровано %ld сообщений"
+
#~ msgid "GPG Agent"
#~ msgstr "Агент GPG"
msgid "quickly revoke a user-id"
msgstr "vytvoriť nový pár kľúčov"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "vytvoriť nový pár kľúčov"
+
msgid "full featured key pair generation"
msgstr ""
"(Použite manuálové stránky pre kompletný zoznam všetkých príkazov a "
"možností)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Príklady:\n"
"\n"
" -se -r Bob [súbor] podpísať a zašifrovať pre užívateľa Bob\n"
-" --clearsign [súbor] vytvoriť podpis čitateľného dokumentu\n"
+" --clear-sign [súbor] vytvoriť podpis čitateľného dokumentu\n"
" --detach-sign [súbor] vytvoriť podpis oddelený od dokumentu\n"
" --list-keys [mená] vypísať kľúče\n"
" --fingerprint [mená] vypísať fingerprinty\n"
msgstr ""
"VAROVANIE: daný adresát (-r) bez použitia šifrovania s verejným kľúčom\n"
-msgid "--store [filename]"
-msgstr "--store [meno súboru]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [meno súboru]"
-
#, fuzzy, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "dešifrovanie zlyhalo: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [meno súboru]"
-
-#, fuzzy
-msgid "--symmetric --encrypt [filename]"
-msgstr "--sign --encrypt [meno súboru]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
"použitie %s nie je v móde %s dovolené\n"
"\n"
-msgid "--sign [filename]"
-msgstr "--sign [meno súboru]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [meno súboru]"
-
-#, fuzzy
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--sign --encrypt [meno súboru]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
"použitie %s nie je v móde %s dovolené\n"
"\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [meno súboru]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [meno súboru]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [meno súboru]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key id užívateľa"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key id užívateľa"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key id užívateľa [príkazy]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key id užívateľa"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "nepodarilo poslať kľúč na server: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[meno súboru]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Začnite písať svoju správu ...\n"
msgid "Nothing to sign.\n"
msgstr "Nič na podpísanie kľúčom %08lX\n"
+#, fuzzy, c-format
+msgid "'%s' is not a valid expiration time\n"
+msgstr "%s nie je platná znaková sada\n"
+
msgid "Digest: "
msgstr "Digest: "
#, fuzzy
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"Podporované algoritmy:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "chyba pri vytváraní hesla: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "chyba pri posielaní na `%s': %s\n"
msgstr "%s: chyba pri zápise adresárového záznamu: %s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "chyba pri posielaní na `%s': %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "chyba pri čítaní `%s': %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "vypísať zoznam kľúčov"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|algo [súbory]|vypíš hash"
+msgstr[1] "|algo [súbory]|vypíš hash"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "zašifrované s %lu heslami\n"
+msgstr[1] "zašifrované s %lu heslami\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|algo [súbory]|vypíš hash"
+msgstr[1] "|algo [súbory]|vypíš hash"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|algo [súbory]|vypíš hash"
msgstr[1] "|algo [súbory]|vypíš hash"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|algo [súbory]|vypíš hash"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "chyba pri zápise súboru kľúčov (keyring) `%s': %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "chyba pri čítaní `%s': %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "chyba pri vytváraní hesla: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Zmazaných %d podpisov.\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|algo [súbory]|vypíš hash"
msgstr[1] "|algo [súbory]|vypíš hash"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "zašifrované s %lu heslami\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|algo [súbory]|vypíš hash"
msgstr[1] "|algo [súbory]|vypíš hash"
+#, fuzzy, c-format
+#| msgid "Policy: "
+msgid "(policy: %s)"
+msgstr "Politika: "
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "chyba pri posielaní na `%s': %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "chyba pri čítaní `%s': %s\n"
+
+#, fuzzy, c-format
+msgid "error parsing '%s'\n"
+msgstr "chyba pri čítaní `%s': %s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [meno súboru]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [meno súboru]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [meno súboru]"
+
+#, fuzzy
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--sign --encrypt [meno súboru]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [meno súboru]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [meno súboru]"
+
+#, fuzzy
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [meno súboru]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [meno súboru]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [meno súboru]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [meno súboru]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key id užívateľa"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key id užívateľa"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key id užívateľa [príkazy]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key id užívateľa"
+
+#~ msgid "[filename]"
+#~ msgstr "[meno súboru]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "zmazanie bloku kľúča sa nepodarilo: %s\n"
msgid "quickly revoke a user-id"
msgstr "generera ett nytt nyckelpar"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "generera ett nytt nyckelpar"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Se manualsidan för en fullständig lista över alla kommandon och flaggor)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Exempel:\n"
"\n"
"-se -r Bosse [fil] signera och kryptera för användaren Bosse\n"
-"--clearsign [fil] skapa en klartextsignatur\n"
+"--clear-sign [fil] skapa en klartextsignatur\n"
"--detach-sign [fil] skapa signatur i en separat fil\n"
"--list-keys [namn] visa nycklar\n"
"--fingerprint [namn] visa fingeravtryck\n"
msgstr ""
"VARNING: mottagare (-r) angivna utan att använda publik nyckel-kryptering\n"
-msgid "--store [filename]"
-msgstr "--store [filnamn]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [filnamn]"
-
#, fuzzy, c-format
#| msgid "symmetric encryption of `%s' failed: %s\n"
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "symmetrisk kryptering av \"%s\" misslyckades: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [filnamn]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [filnamn]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "du kan inte använda --symmetric --encrypt med --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "du kan inte använda --symmetric --encrypt i %s-läget\n"
-msgid "--sign [filename]"
-msgstr "--sign [filnamn]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [filnamn]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [filnamn]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "du kan inte använda --symmetric --sign --encrypt med --s2k-mode 0\n"
msgstr ""
"du kan inte använda --symmetric --sign --encrypt när du är i %s-läget\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [filnamn]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [filnamn]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [filnamn]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key användaridentitet"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key användaridentitet"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key användaridentitet [kommandon]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <användaridentitet>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "sändning till nyckelservern misslyckades: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[filnamn]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Skriv ditt meddelande här ...\n"
msgid "Nothing to sign.\n"
msgstr "Det finns inget att signera med nyckeln %s\n"
+#, fuzzy, c-format
+#| msgid "`%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "\"%s\" är inte ett giltigt utgångsdatum för en signatur\n"
+
msgid "Digest: "
msgstr "Sammandrag: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
msgstr "algoritmen stöds inte: %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "fel när temporärfil skapades: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending %s command: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "fel vid sändning av %s-kommando: %s\n"
msgstr "%s: fel vid läsning av katalogpost: %s\n"
#, fuzzy, c-format
+#| msgid "error sending %s command: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "fel vid sändning av %s-kommando: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening `%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "fel vid öppnandet av \"%s\": %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "skriv ut kontrollsummor"
+msgstr[1] "skriv ut kontrollsummor"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "krypterad med %lu lösenfraser\n"
+msgstr[1] "krypterad med %lu lösenfraser\n"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "skriv ut kontrollsummor"
+msgstr[1] "skriv ut kontrollsummor"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "skriv ut kontrollsummor"
msgstr[1] "skriv ut kontrollsummor"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Messages encrypted in the past: %d."
+msgstr "skriv ut kontrollsummor"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error storing flags: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "fel vid lagring av flaggor: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "fel när ett rör skapades: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Raderade %d signaturer.\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "skriv ut kontrollsummor"
msgstr[1] "skriv ut kontrollsummor"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "krypterad med %lu lösenfraser\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "skriv ut kontrollsummor"
msgstr[1] "skriv ut kontrollsummor"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "giltighet: %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "fel vid sändning av %s-kommando: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Observera att gruppspecifikationer ignoreras\n"
+#, fuzzy, c-format
+#| msgid "error closing %s: %s\n"
+msgid "error closing '%s'\n"
+msgstr "fel vid stängning av %s: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error in `%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "fel i \"%s\": %s\n"
+
msgid "list all components"
msgstr "lista alla komponenter"
msgstr "tillämpa globala standardvärden"
#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FIL|hämta policyinformation från FIL"
+
+#, fuzzy
#| msgid "get the configuration directories for gpgconf"
msgid "get the configuration directories for @GPGCONF@"
msgstr "hämta konfigurationskatalogerna för gpgconf"
"Syntax: gpg-check-pattern [flaggor] mönsterfil\n"
"Kontrollera en lösenfras angiven på standard in mot mönsterfilen\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [filnamn]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [filnamn]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [filnamn]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [filnamn]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [filnamn]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [filnamn]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [filnamn]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [filnamn]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [filnamn]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [filnamn]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key användaridentitet"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key användaridentitet"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key användaridentitet [kommandon]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <användaridentitet>"
+
+#~ msgid "[filename]"
+#~ msgstr "[filnamn]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "skuggning av nyckeln misslyckades: %s\n"
msgid "quickly revoke a user-id"
msgstr "yeni bir anahtar çifti üretir"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "yeni bir anahtar çifti üretir"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(Tüm komut ve seçeneklerin komple listesi için man sayfalarına bakın)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"Örnekler:\n"
"\n"
" -se -r Ali [dosya] kullanıcı Ali için imzalar ve şifreler\n"
-" --clearsign [dosya] açıkça okunabilir bir imza yapar\n"
+" --clear-sign [dosya] açıkça okunabilir bir imza yapar\n"
" --detach-sign [dosya] bağımsız bir imza yapar\n"
" --list-keys [isimler] anahtarları listeler\n"
" --fingerprint [isimler] parmak izlerini gösterir\n"
msgstr ""
"UYARI: alıcılar (-r) genel anahtar şifrelemesi kullanılmadan belirtilmiş\n"
-msgid "--store [filename]"
-msgstr "--store [dosyaismi]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [dosyaismi]"
-
#, fuzzy, c-format
#| msgid "symmetric encryption of `%s' failed: %s\n"
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "`%s' için simetrik şifreleme başarısız: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [dosyaismi]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [dosyaismi]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "--s2k-mode 0 ile --symmetric --encrypt kullanamazsınız\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "%s kipindeyken --symmetric --encrypt kullanamazsınız\n"
-msgid "--sign [filename]"
-msgstr "--sign [dosyaismi]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [dosyaismi]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [dosyaismi]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "--s2k-mode 0 ile --symmetric --sign --encrypt kullanamazsınız\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "%s kipindeyken --symmetric --sign --encrypt kullanamazsınız.\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [DOSYA]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [dosyaismi]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [dosyaismi]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key kullanıcı-kimliği"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key kullanıcı-kimliği"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key kullanıcı-kimliği [komutlar]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key kullanıcı-kimliği"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "anahtar sunucusuna gönderim başarısızlığa uğradı: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[dosyaismi]"
-
msgid "Go ahead and type your message ...\n"
msgstr "İletinizi yazın ...\n"
msgid "Nothing to sign.\n"
msgstr "%s anahtarı ile imzalanacak hiçbir şey yok\n"
+#, fuzzy, c-format
+#| msgid "`%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "'%s' geçerli bir imza zamanaşımı değil\n"
+
msgid "Digest: "
msgstr "Özet: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
msgstr "desteklenmeyen algoritma: %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "geçici dosya oluşturulurken hata: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending %s command: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "%s komutu gönderilirken hata: %s\n"
msgstr "%s: dizin kaydını yazma hatası: %s\n"
#, fuzzy, c-format
+#| msgid "error sending %s command: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "%s komutu gönderilirken hata: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening `%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "'%s' açılırken hata: %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "ileti özetlerini gösterir"
+msgstr[1] "ileti özetlerini gösterir"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "%lu anahtar parolası ile şifrelenmiş\n"
+msgstr[1] "%lu anahtar parolası ile şifrelenmiş\n"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "ileti özetlerini gösterir"
+msgstr[1] "ileti özetlerini gösterir"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "ileti özetlerini gösterir"
msgstr[1] "ileti özetlerini gösterir"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Messages encrypted in the past: %d."
+msgstr "ileti özetlerini gösterir"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error storing flags: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "bayraklar saklanırken hata: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "boru oluşturulurken hata: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "%d imza silindi.\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "ileti özetlerini gösterir"
msgstr[1] "ileti özetlerini gösterir"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "%lu anahtar parolası ile şifrelenmiş\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "ileti özetlerini gösterir"
msgstr[1] "ileti özetlerini gösterir"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "geçerliliği: %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "%s komutu gönderilirken hata: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Grup belirtimlerinin yoksayıldığına dikkat edin\n"
+#, fuzzy, c-format
+#| msgid "error closing %s: %s\n"
+msgid "error closing '%s'\n"
+msgstr "%s kapanırken hata: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error in `%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "'%s' de hata: %s\n"
+
msgid "list all components"
msgstr "tüm bileşenleri listeler"
msgstr "öntanımlı küresel değerleri uygular"
#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|DOSYA|poliçe bilgisi DOSYAdan alınır"
+
+#, fuzzy
#| msgid "get the configuration directories for gpgconf"
msgid "get the configuration directories for @GPGCONF@"
msgstr "gpgconf için yapılandırma dizinlerini getirir"
"Standart girdiden verilen anahtar parolasını örüntü dosyasıyla "
"karşılaştırır\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [dosyaismi]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [dosyaismi]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [dosyaismi]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [dosyaismi]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [dosyaismi]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [dosyaismi]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [dosyaismi]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [DOSYA]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [dosyaismi]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [dosyaismi]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key kullanıcı-kimliği"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key kullanıcı-kimliği"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key kullanıcı-kimliği [komutlar]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key kullanıcı-kimliği"
+
+#~ msgid "[filename]"
+#~ msgstr "[dosyaismi]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "anahtar gölgelenemedi: %s\n"
msgid "quickly revoke a user-id"
msgstr "швидке відкликання ідентифікатора користувача"
+#, fuzzy
+#| msgid "quickly generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "швидке створення пари ключів"
+
msgid "full featured key pair generation"
msgstr "повноцінне створення пари ключів"
"(Щоб ознайомитися зі списком команд і параметрів, скористайтеся сторінкою "
"довідника (man))\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"\n"
" -se -r Bob [файл] підписати і зашифрувати дані для користувача "
"Bob\n"
-" --clearsign [файл] створити текстовий підпис\n"
+" --clear-sign [файл] створити текстовий підпис\n"
" --detach-sign [файл] створити від’єднаний підпис\n"
" --list-keys [назви] показати ключі\n"
" --fingerprint [назви] показати відбитки\n"
"УВАГА: отримувачів (-r) вказано без використання шифрування відкритим "
"ключем\n"
-msgid "--store [filename]"
-msgstr "--store [назва файла]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [назва файла]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "помилка під час спроби симетричного шифрування «%s»: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [назва файла]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [назва файла]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr ""
"не можна використовувати комбінацію --symmetric --encrypt у режимі --s2k-"
msgstr ""
"не можна використовувати комбінацію --symmetric --encrypt у режимі %s\n"
-msgid "--sign [filename]"
-msgstr "--sign [назва файла]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [назва файла]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [назва файла]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr ""
"не можна використовувати комбінацію --symmetric --sign --encrypt у режимі --"
"не можна використовувати комбінацію --symmetric --sign --encrypt у режимі "
"%s\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [назва файла]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [назва файла]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [назва файла]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key user-id"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key user-id"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key user-id [команди]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd <ідентифікатор-користувача>"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "помилка під час надсилання даних на сервер ключів: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr "«%s» не є коректним ідентифікатором ключа, відбитком або кодом\n"
-msgid "[filename]"
-msgstr "[назва файла]"
-
msgid "Go ahead and type your message ...\n"
msgstr "Почніть вводити ваше повідомлення...\n"
msgid "Nothing to sign.\n"
msgstr "Нічого підписувати.\n"
+#, fuzzy, c-format
+#| msgid "'%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "«%s» не є коректним записом завершення строку дії підпису\n"
+
msgid "Digest: "
msgstr "Контрольна сума: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"Скористайтеся цим для відкликання цього ключа у випадку його\n"
"компрометації або втрати закритої частини. Втім, якщо доступ до\n"
"закритого ключа не втрачено, краще створити новий сертифікат\n"
"відкликання і вказати причину відкликання. Докладніший опис\n"
"можна знайти у розділах підручника з GnuPG щодо команди\n"
-"gpg «--gen-revoke»."
+"gpg «--generate-revocation»."
msgid ""
"To avoid an accidental use of this file, a colon has been inserted\n"
msgid "unsupported TOFU database version: %s\n"
msgstr "непідтримувана версія бази даних TOFU: %s\n"
+#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "помилка створення тимчасового файла: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
#, c-format
msgid "error reading TOFU database: %s\n"
msgstr "помилка під час спроби читання бази даних TOFU: %s\n"
msgid "error initializing TOFU database: %s\n"
msgstr "помилка під час спроби ініціалізації бази даних TOFU: %s\n"
+#, fuzzy, c-format
+#| msgid "error reading TOFU database: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "помилка під час спроби читання бази даних TOFU: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
#, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "помилка під час спроби відкрити бази даних TOFU «%s»: %s\n"
#, fuzzy, c-format
#| msgid "The email address \"%s\" is associated with %d key:\n"
#| msgid_plural "The email address \"%s\" is associated with %d keys:\n"
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr "Адресу електронної пошти «%s» пов’язано із %d ключем:\n"
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] "Адресу електронної пошти «%s» пов’язано із %d ключем:\n"
+msgstr[1] "Адресу електронної пошти «%s» пов’язано із %d ключем:\n"
+msgstr[2] "Адресу електронної пошти «%s» пов’язано із %d ключем:\n"
#, fuzzy
#| msgid ""
#, fuzzy, c-format
#| msgid "Verified %ld messages signed by \"%s\"."
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
msgstr[0] "Перевірено %ld повідомлень, підписаних «%s»."
msgstr[1] "Перевірено %ld повідомлень, підписаних «%s»."
msgstr[2] "Перевірено %ld повідомлень, підписаних «%s»."
-#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "зашифровано за допомогою %lu паролів\n"
+msgstr[1] "зашифровано за допомогою %lu паролів\n"
+msgstr[2] "зашифровано за допомогою %lu паролів\n"
+
+#, fuzzy, c-format
+#| msgid "%ld message signed in the future."
+#| msgid_plural "%ld messages signed in the future."
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "%ld повідомлення підписано у майбутньому."
+msgstr[1] "%ld повідомлення підписано у майбутньому."
+msgstr[2] "%ld повідомлень підписано у майбутньому."
+
+#, fuzzy, c-format
+#| msgid "%ld message signed in the future."
+#| msgid_plural "%ld messages signed in the future."
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
+msgstr[0] "%ld повідомлення підписано у майбутньому."
+msgstr[1] "%ld повідомлення підписано у майбутньому."
+msgstr[2] "%ld повідомлень підписано у майбутньому."
+
+#, fuzzy, c-format
+#| msgid " over the past %ld day."
+#| msgid_plural " over the past %ld days."
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] " протягом %ld попереднього дня."
+msgstr[1] " протягом %ld попередніх днів."
+msgstr[2] " протягом %ld попередніх днів."
#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] " протягом %ld попереднього дня."
msgstr[1] " протягом %ld попередніх днів."
msgstr[2] " протягом %ld попередніх днів."
#, fuzzy, c-format
#| msgid " over the past %ld month."
#| msgid_plural " over the past %ld months."
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
+msgstr[0] " протягом %ld попереднього місяця."
+msgstr[1] " протягом %ld попередніх місяців."
+msgstr[2] " протягом %ld попередніх місяців."
+
+#, fuzzy, c-format
+#| msgid " over the past %ld month."
+#| msgid_plural " over the past %ld months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] " протягом %ld попереднього місяця."
msgstr[1] " протягом %ld попередніх місяців."
msgstr[2] " протягом %ld попередніх місяців."
#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] " протягом %ld попереднього дня."
msgstr[1] " протягом %ld попередніх днів."
msgstr[2] " протягом %ld попередніх днів."
-#, fuzzy
+#, fuzzy, c-format
#| msgid " over the past %ld day."
#| msgid_plural " over the past %ld days."
-msgid " in the past."
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] " протягом %ld попереднього дня."
+msgstr[1] " протягом %ld попередніх днів."
+msgstr[2] " протягом %ld попередніх днів."
+
+#, fuzzy, c-format
+#| msgid " over the past %ld day."
+#| msgid_plural " over the past %ld days."
+msgid "Messages verified in the past: %d."
msgstr " протягом %ld попереднього дня."
+#, fuzzy, c-format
+#| msgid ""
+#| "Verified %ld message signed by \"%s\"\n"
+#| "in the past %s."
+#| msgid_plural ""
+#| "Verified %ld messages signed by \"%s\"\n"
+#| "in the past %s."
+msgid "Messages encrypted in the past: %d."
+msgstr ""
+"Перевірено %ld повідомлення, підписане «%s»,\n"
+"протягом такого строку: %s."
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgid "resetting keydb: %s\n"
msgstr "помилка під час спроби запису ключа: %s\n"
-#, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+#, fuzzy, c-format
+#| msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr ""
"помилка під час спроби встановлення рівня довіри до прив’язки TOFU до %s\n"
msgid "error changing TOFU policy: %s\n"
msgstr "помилка під час спроби змінити правила TOFU: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
-#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+#, fuzzy, c-format
+#| msgid "%d~year"
+#| msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
msgstr[0] "%d~рік"
msgstr[1] "%d~роки"
msgstr[2] "%d~років"
-#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+#, fuzzy, c-format
+#| msgid "%d~month"
+#| msgid_plural "%d~months"
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] "%d~місяць"
msgstr[1] "%d~місяці"
msgstr[2] "%d~місяців"
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, fuzzy, c-format
+#| msgid "%d~day"
+#| msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] "%d~день"
msgstr[1] "%d~дні"
msgstr[2] "%d~днів"
-#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+#, fuzzy, c-format
+#| msgid "%d~hour"
+#| msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] "%d~година"
msgstr[1] "%d~години"
msgstr[2] "%d~годин"
-#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+#, fuzzy, c-format
+#| msgid "%d~minute"
+#| msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] "%d~хвилина"
msgstr[1] "%d~хвилини"
msgstr[2] "%d~хвилин"
-#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+#, fuzzy, c-format
+#| msgid "%d~second"
+#| msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] "%d~секунда"
msgstr[1] "%d~секунди"
msgstr[2] "%d~секунд"
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "Вилучено %d підписів.\n"
#, fuzzy, c-format
#| msgid_plural ""
#| "Verified %ld messages signed by \"%s\"\n"
#| "in the past %s."
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] ""
"Перевірено %ld повідомлення, підписане «%s»,\n"
"протягом такого строку: %s."
"Перевірено %ld повідомлень, підписаних «%s»,\n"
"протягом такого строку: %s."
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "зашифровано за допомогою %lu паролів\n"
#, fuzzy, c-format
#| msgid_plural ""
#| "Verified %ld messages signed by \"%s\"\n"
#| "in the past %s."
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] ""
"Перевірено %ld повідомлення, підписане «%s»,\n"
"протягом такого строку: %s."
"Перевірено %ld повідомлень, підписаних «%s»,\n"
"протягом такого строку: %s."
+#, fuzzy, c-format
+#| msgid "policy: %s"
+msgid "(policy: %s)"
+msgstr "правило: %s"
+
#, fuzzy
#| msgid "Warning: we've have yet to see a message signed by this key!\n"
msgid ""
msgstr "помилка під час спроби відкрити бази даних TOFU: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "Зауважте, що специфікації груп буде проігноровано\n"
+#, fuzzy, c-format
+#| msgid "error closing '%s': %s\n"
+msgid "error closing '%s'\n"
+msgstr "помилка під час спроби закрити «%s»: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error hashing '%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "помилка під час спроби хешування «%s»: %s\n"
+
msgid "list all components"
msgstr "показати список всіх компонентів"
msgid "apply global default values"
msgstr "застосувати загальні типові значення"
+#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|FILE|взяти дані щодо правил з вказаного файла"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "отримати назви каталогів налаштувань для @GPGCONF@"
"Синтаксис: gpg-check-pattern [параметри] файл_шаблонів\n"
"Перевірити пароль, вказаний у stdin, за допомогою файла_шаблонів\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [назва файла]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [назва файла]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [назва файла]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [назва файла]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [назва файла]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [назва файла]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [назва файла]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [назва файла]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [назва файла]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [назва файла]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key user-id"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key user-id"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key user-id [команди]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd <ідентифікатор-користувача>"
+
+#~ msgid "[filename]"
+#~ msgstr "[назва файла]"
+
+#, fuzzy
+#~| msgid " over the past %ld day."
+#~| msgid_plural " over the past %ld days."
+#~ msgid " in the past."
+#~ msgstr " протягом %ld попереднього дня."
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "помилка під час спроби хешування ключа: %s\n"
#~ msgid "Known user IDs associated with this key:\n"
#~ msgstr "Відомі ідентифікатори користувачів, пов’язані із цим ключем:\n"
-#~ msgid "%ld message signed in the future."
-#~ msgid_plural "%ld messages signed in the future."
-#~ msgstr[0] "%ld повідомлення підписано у майбутньому."
-#~ msgstr[1] "%ld повідомлення підписано у майбутньому."
-#~ msgstr[2] "%ld повідомлень підписано у майбутньому."
-
#~ msgid "%ld message signed"
#~ msgid_plural "%ld messages signed"
#~ msgstr[0] "підписано %ld повідомлення"
msgid "quickly revoke a user-id"
msgstr "生成一副新的密钥对"
+#, fuzzy
+#| msgid "generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "生成一副新的密钥对"
+
msgid "full featured key pair generation"
msgstr ""
"@\n"
"(请参考在线说明以获得所有命令和选项的完整清单)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"范例:\n"
"\n"
" -se -r Bob [文件名] 为 Bob 这个收件人签名及加密\n"
-" --clearsign [文件名] 做出明文签名\n"
+" --clear-sign [文件名] 做出明文签名\n"
" --detach-sign [文件名] 做出分离式签名\n"
" --list-keys [某甲] 显示密钥\n"
" --fingerprint [某甲] 显示指纹\n"
msgid "WARNING: recipients (-r) given without using public key encryption\n"
msgstr "警告:给定了收件人(-r)但并未使用公钥加密\n"
-msgid "--store [filename]"
-msgstr "--store [文件名]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [文件名]"
-
#, fuzzy, c-format
#| msgid "symmetric encryption of `%s' failed: %s\n"
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "对称加密‘%s’失败:%s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [文件名]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [文件名]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "使用 --symmetric --encrypt 时不能使用 --s2k-mode 0\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "您不能在 %s 模式下使用 --symmetric -encrypt\n"
-msgid "--sign [filename]"
-msgstr "--sign [文件名]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [文件名]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [文件名]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "使用 --symmetric --sign --encrypt 时不能使用 --s2k-mode 0\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "您不能在 %s 模式下使用 --symmetric --sign -encrypt\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [文件名]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [文件名]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [文件名]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key 用户标识"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key 用户标识"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key 用户标识 [指令]"
-
-#, fuzzy
-msgid "--passwd <user-id>"
-msgstr "--sign-key 用户标识"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "上传至公钥服务器失败:%s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[文件名]"
-
msgid "Go ahead and type your message ...\n"
msgstr "请开始键入您的报文……\n"
msgid "Nothing to sign.\n"
msgstr "没有东西可以让密钥 %s 签名\n"
+#, fuzzy, c-format
+#| msgid "`%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "‘%s’不是一个有效的签名过期日期\n"
+
msgid "Digest: "
msgstr "散列:"
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
msgid ""
"支持的算法:\n"
#, fuzzy, c-format
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "生成密码的时候发生错误:%s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error reading TOFU database: %s\n"
msgstr "读取‘%s’时出错:%s\n"
msgstr "%s:写入目录记录时出错:%s\n"
#, fuzzy, c-format
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "读取‘%s’时出错:%s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
msgid "error opening TOFU database '%s': %s\n"
msgstr "‘%s’中出错:%s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
+msgstr[1] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
msgstr "列出密钥"
#, fuzzy, c-format
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
+msgstr[0] "|算法 [文件]|使用指定的散列算法打印报文散列值"
+msgstr[1] "|算法 [文件]|使用指定的散列算法打印报文散列值"
+
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "以 %lu 个密码加密\n"
+msgstr[1] "以 %lu 个密码加密\n"
+
+#, fuzzy, c-format
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "|算法 [文件]|使用指定的散列算法打印报文散列值"
+msgstr[1] "|算法 [文件]|使用指定的散列算法打印报文散列值"
+
+#, fuzzy, c-format
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
msgstr[0] "|算法 [文件]|使用指定的散列算法打印报文散列值"
msgstr[1] "|算法 [文件]|使用指定的散列算法打印报文散列值"
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
msgstr[1] ""
-msgid " in the past."
+#, c-format
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+msgid "Messages encrypted in the past: %d."
+msgstr "|算法 [文件]|使用指定的散列算法打印报文散列值"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
msgstr "写入钥匙环‘%s’时出错: %s\n"
#, fuzzy, c-format
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "读取‘%s’时出错:%s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "生成密码的时候发生错误:%s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+msgstr[1] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
msgstr[1] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "已经删除了 %d 个签名。\n"
#, fuzzy, c-format
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "|算法 [文件]|使用指定的散列算法打印报文散列值"
msgstr[1] "|算法 [文件]|使用指定的散列算法打印报文散列值"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "以 %lu 个密码加密\n"
#, fuzzy, c-format
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "|算法 [文件]|使用指定的散列算法打印报文散列值"
msgstr[1] "|算法 [文件]|使用指定的散列算法打印报文散列值"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "有效性:%s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "读取‘%s’时出错:%s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr ""
+#, fuzzy, c-format
+msgid "error closing '%s'\n"
+msgstr "‘%s’中出错:%s\n"
+
+#, fuzzy, c-format
+#| msgid "error in `%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "‘%s’中出错:%s\n"
+
msgid "list all components"
msgstr ""
msgid "apply global default values"
msgstr ""
+msgid "|FILE|update configuration files using FILE"
+msgstr ""
+
msgid "get the configuration directories for @GPGCONF@"
msgstr ""
"Check a passphrase given on stdin against the patternfile\n"
msgstr ""
+#~ msgid "--store [filename]"
+#~ msgstr "--store [文件名]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [文件名]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [文件名]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [文件名]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [文件名]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [文件名]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [文件名]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [文件名]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [文件名]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [文件名]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key 用户标识"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key 用户标识"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key 用户标识 [指令]"
+
+#, fuzzy
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--sign-key 用户标识"
+
+#~ msgid "[filename]"
+#~ msgstr "[文件名]"
+
#, fuzzy
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "无法读出公钥:%s\n"
msgid "quickly revoke a user-id"
msgstr "快速產生新的金鑰對"
+#, fuzzy
+#| msgid "quickly generate a new key pair"
+msgid "quickly set a new expiration date"
+msgstr "快速產生新的金鑰對"
+
msgid "full featured key pair generation"
msgstr "全能金鑰對生成"
"@\n"
"(請參照線上說明頁面來取得所有命令和選項的完整清單)\n"
+#, fuzzy
+#| msgid ""
+#| "@\n"
+#| "Examples:\n"
+#| "\n"
+#| " -se -r Bob [file] sign and encrypt for user Bob\n"
+#| " --clear-sign [file] make a clear text signature\n"
+#| " --detach-sign [file] make a detached signature\n"
+#| " --list-keys [names] show keys\n"
+#| " --fingerprint [names] show fingerprints\n"
msgid ""
"@\n"
"Examples:\n"
"\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
-" --clearsign [file] make a clear text signature\n"
+" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n"
"範例:\n"
"\n"
" -se -r Bob [檔案] 對 Bob 這個使用者簽署及加密\n"
-" --clearsign [檔案] 做出明文簽章\n"
+" --clear-sign [檔案] 做出明文簽章\n"
" --detach-sign [檔案] 做出分離式簽章\n"
" --list-keys [名字] 顯示金鑰\n"
" --fingerprint [名字] 顯示指紋\n"
msgid "WARNING: recipients (-r) given without using public key encryption\n"
msgstr "警告: 給定的收件者 (-r) 未使用公鑰加密\n"
-msgid "--store [filename]"
-msgstr "--store [檔名]"
-
-msgid "--symmetric [filename]"
-msgstr "--symmetric [檔名]"
-
#, c-format
msgid "symmetric encryption of '%s' failed: %s\n"
msgstr "'%s' 對稱式加密失敗: %s\n"
-msgid "--encrypt [filename]"
-msgstr "--encrypt [檔名]"
-
-msgid "--symmetric --encrypt [filename]"
-msgstr "--symmetric --encrypt [檔名]"
-
msgid "you cannot use --symmetric --encrypt with --s2k-mode 0\n"
msgstr "你不能在 --s2k-mode 0 中使用 --symmetric --encrypt\n"
msgid "you cannot use --symmetric --encrypt while in %s mode\n"
msgstr "你不能在 %s 模式中使用 --symmetric --encrypt\n"
-msgid "--sign [filename]"
-msgstr "--sign [檔名]"
-
-msgid "--sign --encrypt [filename]"
-msgstr "--sign --encrypt [檔名]"
-
-msgid "--symmetric --sign --encrypt [filename]"
-msgstr "--symmetric --sign --encrypt [檔名]"
-
msgid "you cannot use --symmetric --sign --encrypt with --s2k-mode 0\n"
msgstr "你不能在 --s2k-mode 0 中使用 --symmetric --sign --encrypt\n"
msgid "you cannot use --symmetric --sign --encrypt while in %s mode\n"
msgstr "你不能在 %s 模式中使用 --symmetric --sign --encrypt\n"
-msgid "--sign --symmetric [filename]"
-msgstr "--sign --symmetric [檔名]"
-
-msgid "--clearsign [filename]"
-msgstr "--clearsign [檔名]"
-
-msgid "--decrypt [filename]"
-msgstr "--decrypt [檔名]"
-
-msgid "--sign-key user-id"
-msgstr "--sign-key 使用者ID"
-
-msgid "--lsign-key user-id"
-msgstr "--lsign-key 使用者ID"
-
-msgid "--edit-key user-id [commands]"
-msgstr "--edit-key 使用者ID [指令]"
-
-msgid "--passwd <user-id>"
-msgstr "--passwd 使用者ID"
-
#, c-format
msgid "keyserver send failed: %s\n"
msgstr "送至金鑰伺服器失敗: %s\n"
msgid "'%s' does not appear to be a valid key ID, fingerprint or keygrip\n"
msgstr ""
-msgid "[filename]"
-msgstr "[檔名]"
-
msgid "Go ahead and type your message ...\n"
msgstr "請開始輸入你的訊息 ...\n"
msgid "Nothing to sign.\n"
msgstr "沒有東西可以簽署.\n"
+#, fuzzy, c-format
+#| msgid "'%s' is not a valid signature expiration\n"
+msgid "'%s' is not a valid expiration time\n"
+msgstr "'%s' 不是有效的簽章使用期限\n"
+
msgid "Digest: "
msgstr "摘要: "
#| "in the user ID. If you *really* know what you are doing,\n"
#| "you may answer the next question with yes.\n"
msgid ""
-"This key has is bad! It has been marked as untrusted! If you\n"
+"This key is bad! It has been marked as untrusted! If you\n"
"*really* know what you are doing, you may answer the next\n"
"question with yes.\n"
msgstr ""
"the secret key. However, if the secret key is still accessible,\n"
"it is better to generate a new revocation certificate and give\n"
"a reason for the revocation. For details see the description of\n"
-"of the gpg command \"--gen-revoke\" in the GnuPG manual."
+"of the gpg command \"--generate-revocation\" in the GnuPG manual."
msgstr ""
"萬一私鑰外流或遺失時, 可以用來撤銷金鑰.\n"
"然而, 如果其實還可以存取私鑰, 建議更好的\n"
msgstr "未支援的演算法: %s"
#, fuzzy, c-format
+#| msgid "error creating temporary file: %s\n"
+msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n"
+msgstr "建立暫存檔時出錯: %s\n"
+
+msgid "TOFU DB error"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error sending data: %s\n"
msgid "error reading TOFU database: %s\n"
msgstr "送出資料時出錯: %s\n"
msgstr "啟始讀取程式物件時出錯: %s\n"
#, fuzzy, c-format
+#| msgid "error sending data: %s\n"
+msgid "error creating 'encryptions' TOFU table: %s\n"
+msgstr "送出資料時出錯: %s\n"
+
+#, c-format
+msgid "adding column effective_policy to bindings DB: %s\n"
+msgstr ""
+
+#, fuzzy, c-format
#| msgid "error opening '%s': %s\n"
msgid "error opening TOFU database '%s': %s\n"
msgstr "開啟 '%s' 時出錯: %s\n"
msgstr ""
#, c-format
-msgid "The email address \"%s\" is associated with %d keys!"
-msgstr ""
+msgid "The email address \"%s\" is associated with %d key!"
+msgid_plural "The email address \"%s\" is associated with %d keys!"
+msgstr[0] ""
msgid " Since this binding's policy was 'auto', it has been changed to 'ask'."
msgstr ""
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %d message"
-msgid_plural "Verified %d messages"
+msgid "Verified %d message."
+msgid_plural "Verified %d messages."
msgstr[0] "印出訊息摘要"
+#, fuzzy, c-format
+#| msgid "encrypted with %lu passphrases\n"
+msgid "Encrypted %d message."
+msgid_plural "Encrypted %d messages."
+msgstr[0] "已用 %lu 個密語加密了\n"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Verified %d message in the future."
+msgid_plural "Verified %d messages in the future."
+msgstr[0] "印出訊息摘要"
+
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Encrypted %d message in the future."
+msgid_plural "Encrypted %d messages in the future."
+msgstr[0] "印出訊息摘要"
+
+#, c-format
+msgid "Messages verified over the past %d day: %d."
+msgid_plural "Messages verified over the past %d days: %d."
+msgstr[0] ""
+
+#, c-format
+msgid "Messages encrypted over the past %d day: %d."
+msgid_plural "Messages encrypted over the past %d days: %d."
+msgstr[0] ""
+
#, c-format
-msgid "Encrypted %d message"
-msgid_plural "Encrypted %d messages"
+msgid "Messages verified over the past %d month: %d."
+msgid_plural "Messages verified over the past %d months: %d."
msgstr[0] ""
#, c-format
-msgid " over the past day."
-msgid_plural " over the past %d days."
+msgid "Messages encrypted over the past %d month: %d."
+msgid_plural "Messages encrypted over the past %d months: %d."
msgstr[0] ""
#, c-format
-msgid " over the past month."
-msgid_plural " over the past %d months."
+msgid "Messages verified over the past %d year: %d."
+msgid_plural "Messages verified over the past %d years: %d."
msgstr[0] ""
#, c-format
-msgid " over the past year."
-msgid_plural " over the past %d years."
+msgid "Messages encrypted over the past %d year: %d."
+msgid_plural "Messages encrypted over the past %d years: %d."
msgstr[0] ""
-msgid " in the past."
+#, c-format
+msgid "Messages verified in the past: %d."
msgstr ""
+#, fuzzy, c-format
+#| msgid "print message digests"
+msgid "Messages encrypted in the past: %d."
+msgstr "印出訊息摘要"
+
#. TRANSLATORS: Please translate the text found in the source
#. * file below. We don't directly internationalize that text so
#. * that we can tweak it without breaking translations.
#, fuzzy, c-format
#| msgid "error setting OCSP target: %s\n"
-msgid "error setting TOFU binding's trust level to %s\n"
+msgid "error setting TOFU binding's policy to %s\n"
msgstr "設定 OCSP 目標時出錯: %s\n"
#, fuzzy, c-format
msgid "error changing TOFU policy: %s\n"
msgstr "建立管道時出錯: %s\n"
-#. TRANSLATORS: The tilde ('~') is used here to indicate a
-#. * non-breakable space
#, c-format
-msgid "%d~year"
-msgid_plural "%d~years"
+msgid "%lld~year"
+msgid_plural "%lld~years"
+msgstr[0] ""
+
+#, c-format
+msgid "%lld~month"
+msgid_plural "%lld~months"
msgstr[0] ""
#, c-format
-msgid "%d~month"
-msgid_plural "%d~months"
+msgid "%lld~week"
+msgid_plural "%lld~weeks"
msgstr[0] ""
#, c-format
-msgid "%d~day"
-msgid_plural "%d~days"
+msgid "%lld~day"
+msgid_plural "%lld~days"
msgstr[0] ""
#, c-format
-msgid "%d~hour"
-msgid_plural "%d~hours"
+msgid "%lld~hour"
+msgid_plural "%lld~hours"
msgstr[0] ""
#, c-format
-msgid "%d~minute"
-msgid_plural "%d~minutes"
+msgid "%lld~minute"
+msgid_plural "%lld~minutes"
msgstr[0] ""
#, c-format
-msgid "%d~second"
-msgid_plural "%d~seconds"
+msgid "%lld~second"
+msgid_plural "%lld~seconds"
msgstr[0] ""
#, c-format
-msgid "%s: "
+msgid "%s: Verified 0~signatures and encrypted 0~messages."
msgstr ""
#, fuzzy, c-format
#| msgid "Deleted %d signatures.\n"
-msgid "Verified %ld signatures"
+msgid "%s: Verified 0 signatures."
msgstr "已經刪除了 %d 份簽章.\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid "Verified %ld signature in the past %s"
-msgid_plural "Verified %ld signatures in the past %s"
+msgid "%s: Verified %ld~signature in the past %s."
+msgid_plural "%s: Verified %ld~signatures in the past %s."
msgstr[0] "印出訊息摘要"
-#, fuzzy, c-format
+#, fuzzy
#| msgid "encrypted with %lu passphrases\n"
-msgid ", and encrypted %ld messages"
+msgid "Encrypted 0 messages."
msgstr "已用 %lu 個密語加密了\n"
#, fuzzy, c-format
#| msgid "print message digests"
-msgid ", and encrypted %ld message in the past %s"
-msgid_plural ", and encrypted %ld messages in the past %s"
+msgid "Encrypted %ld~message in the past %s."
+msgid_plural "Encrypted %ld~messages in the past %s."
msgstr[0] "印出訊息摘要"
+#, fuzzy, c-format
+#| msgid "validity: %s"
+msgid "(policy: %s)"
+msgstr "有效性: %s"
+
msgid ""
"Warning: we have yet to see a message signed using this key and user id!\n"
msgstr ""
msgstr "送出資料時出錯: %s\n"
#, c-format
-msgid "WARNING: Encrypting to %s, which has nonon-revoked user ids.\n"
+msgid "WARNING: Encrypting to %s, which has no non-revoked user ids.\n"
msgstr ""
#, fuzzy, c-format
msgid "Note that group specifications are ignored\n"
msgstr "請注意群組規格已忽略\n"
+#, fuzzy, c-format
+#| msgid "error closing '%s': %s\n"
+msgid "error closing '%s'\n"
+msgstr "關閉 '%s' 時出錯: %s\n"
+
+#, fuzzy, c-format
+#| msgid "error hashing '%s': %s\n"
+msgid "error parsing '%s'\n"
+msgstr "計算 '%s' 的雜湊時出錯: %s\n"
+
msgid "list all components"
msgstr "列出所有的元件"
msgid "apply global default values"
msgstr "套用全域預設值"
+#, fuzzy
+#| msgid "|FILE|take policy information from FILE"
+msgid "|FILE|update configuration files using FILE"
+msgstr "|檔案|從指定檔案中取得原則資訊"
+
msgid "get the configuration directories for @GPGCONF@"
msgstr "取得 @GPGCONF@ 的組態目錄"
"語法: gpg-check-pattern [選項] 樣式檔案\n"
"用樣式檔案來檢查由標準輸入給定的密語\n"
+#~ msgid "--store [filename]"
+#~ msgstr "--store [檔名]"
+
+#~ msgid "--symmetric [filename]"
+#~ msgstr "--symmetric [檔名]"
+
+#~ msgid "--encrypt [filename]"
+#~ msgstr "--encrypt [檔名]"
+
+#~ msgid "--symmetric --encrypt [filename]"
+#~ msgstr "--symmetric --encrypt [檔名]"
+
+#~ msgid "--sign [filename]"
+#~ msgstr "--sign [檔名]"
+
+#~ msgid "--sign --encrypt [filename]"
+#~ msgstr "--sign --encrypt [檔名]"
+
+#~ msgid "--symmetric --sign --encrypt [filename]"
+#~ msgstr "--symmetric --sign --encrypt [檔名]"
+
+#~ msgid "--sign --symmetric [filename]"
+#~ msgstr "--sign --symmetric [檔名]"
+
+#~ msgid "--clear-sign [filename]"
+#~ msgstr "--clear-sign [檔名]"
+
+#~ msgid "--decrypt [filename]"
+#~ msgstr "--decrypt [檔名]"
+
+#~ msgid "--sign-key user-id"
+#~ msgstr "--sign-key 使用者ID"
+
+#~ msgid "--lsign-key user-id"
+#~ msgstr "--lsign-key 使用者ID"
+
+#~ msgid "--edit-key user-id [commands]"
+#~ msgstr "--edit-key 使用者ID [指令]"
+
+#~ msgid "--passwd <user-id>"
+#~ msgstr "--passwd 使用者ID"
+
+#~ msgid "[filename]"
+#~ msgstr "[檔名]"
+
#~ msgid "shadowing the key failed: %s\n"
#~ msgstr "遮蔽金鑰時失敗: %s\n"
if (use_extended_length && (le > 256 || le < 0))
{
- result_buffer_size = le < 0? 4096 : le;
- result_buffer = xtrymalloc (result_buffer_size + 10);
+ /* Two more bytes are needed for status bytes. */
+ result_buffer_size = le < 0? 4096 : (le + 2);
+ result_buffer = xtrymalloc (result_buffer_size);
if (!result_buffer)
{
xfree (apdu_buffer);
unsigned int sm_algo:2; /* Symmetric crypto algo for SM. */
unsigned int max_certlen_3:16;
unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */
- unsigned int max_cmd_data:16; /* Maximum data size for a command. */
- unsigned int max_rsp_data:16; /* Maximum size of a response. */
} extcap;
/* Flags used to control the application. */
}
if (try_extlen && app->app_local->cardcap.ext_lc_le)
- exmode = app->app_local->extcap.max_rsp_data;
+ exmode = app->app_local->extcap.max_certlen_3;
else
exmode = 0;
if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
{
- if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le)
- exmode = app->app_local->extcap.max_rsp_data;
- else
- exmode = 0;
+ exmode = 0;
rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
if (rc)
{
}
+#define RSA_SMALL_SIZE_KEY 1952
+#define RSA_SMALL_SIZE_OP 2048
+
+static int
+determine_rsa_response (app_t app, int keyno)
+{
+ int size;
+
+ size = 2 + 3 /* header */
+ + 4 /* tag+len */ + app->app_local->keyattr[keyno].rsa.n_bits/8
+ + 2 /* tag+len */ + app->app_local->keyattr[keyno].rsa.e_bits/8;
+
+ return size;
+}
+
+
/* Implement the GETATTR command. This is similar to the LEARN
command but returns just one value via the status interface. */
static gpg_error_t
int exmode, le_value;
/* We may simply read the public key out of these cards. */
- if (app->app_local->cardcap.ext_lc_le)
+ if (app->app_local->cardcap.ext_lc_le
+ && app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[keyno].rsa.n_bits > RSA_SMALL_SIZE_KEY)
{
exmode = 1; /* Use extended length. */
- le_value = app->app_local->extcap.max_rsp_data;
+ le_value = determine_rsa_response (app, keyno);
}
else
{
memcpy (curve_name, tok, toklen);
curve_name[toklen] = 0;
- curve = openpgp_is_curve_supported (curve_name, NULL);
+ curve = openpgp_is_curve_supported (curve_name, NULL, NULL);
xfree (curve_name);
}
else if (tok && toklen == 5 && !memcmp (tok, "flags", 5))
if (keybits > 4096)
return gpg_error (GPG_ERR_TOO_LARGE);
- /* Test whether we will need extended length mode. (1900 is an
- arbitrary length which for sure fits into a short apdu.) */
- if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
+ if (app->app_local->cardcap.ext_lc_le && keybits > RSA_SMALL_SIZE_KEY
+ && app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
{
exmode = 1; /* Use extended length w/o a limit. */
- le_value = app->app_local->extcap.max_rsp_data;
+ le_value = determine_rsa_response (app, keyno);
/* No need to check le_value because it comes from a 16 bit
value and thus can't create an overflow on a 32 bit
system. */
}
- if (app->app_local->cardcap.ext_lc_le)
+ if (app->app_local->cardcap.ext_lc_le
+ && app->app_local->keyattr[0].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[0].rsa.n_bits > RSA_SMALL_SIZE_OP)
{
exmode = 1; /* Use extended length. */
- le_value = app->app_local->extcap.max_rsp_data;
+ le_value = app->app_local->keyattr[0].rsa.n_bits / 8;
}
else
{
{
int exmode, le_value;
- if (app->app_local->cardcap.ext_lc_le)
+ if (app->app_local->cardcap.ext_lc_le
+ && app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[2].rsa.n_bits > RSA_SMALL_SIZE_OP)
{
exmode = 1; /* Use extended length. */
- le_value = app->app_local->extcap.max_rsp_data;
+ le_value = app->app_local->keyattr[2].rsa.n_bits / 8;
}
else
{
else
return gpg_error (GPG_ERR_INV_VALUE);
- if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
+ if (app->app_local->cardcap.ext_lc_le
+ && (indatalen > 254
+ || (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[1].rsa.n_bits > RSA_SMALL_SIZE_OP)))
{
exmode = 1; /* Extended length w/o a limit. */
- le_value = app->app_local->extcap.max_rsp_data;
+ le_value = app->app_local->keyattr[1].rsa.n_bits / 8;
}
else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
{
log_printf (" (%s)", s->extcap.sm_algo==2? "3DES":
(s->extcap.sm_algo==2? "AES-128" : "AES-256"));
log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3);
- log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data);
- log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data);
log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no");
log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no");
log_info ("Status Indicator: %02X\n", s->status_indicator);
app->app_local->extcap.max_get_challenge
= (buffer[2] << 8 | buffer[3]);
app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
- app->app_local->extcap.max_cmd_data = (buffer[6] << 8 | buffer[7]);
- app->app_local->extcap.max_rsp_data = (buffer[8] << 8 | buffer[9]);
}
xfree (relptr);
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
- { DBG_COMMAND_VALUE, "command" },
{ DBG_MPI_VALUE , "mpi" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_MEMORY_VALUE , "memory" },
else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_IPC_VALUE;
else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
- opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
+ opt.debug = DBG_IPC_VALUE;
else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
- opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
- |DBG_CACHE_VALUE|DBG_CARD_IO_VALUE);
+ opt.debug = (DBG_IPC_VALUE|DBG_CACHE_VALUE|DBG_CARD_IO_VALUE);
else if (!strcmp (level, "guru") || numok)
{
opt.debug = ~0;
} opt;
-#define DBG_COMMAND_VALUE 1 /* debug commands i/o */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_CARD_IO_VALUE 2048
#define DBG_READER_VALUE 4096 /* Trace reader related functions. */
-#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
rc = start_agent (ctrl);
if (rc)
return rc;
+ inq_parm.ctrl = ctrl;
inq_parm.ctx = agent_ctx;
if (digestlen*2 + 50 > DIM(line))
rc = start_agent (ctrl);
if (rc)
return rc;
+ inq_parm.ctrl = ctrl;
inq_parm.ctx = agent_ctx;
if (digestlen*2 + 50 > DIM(line))
rc = start_agent (ctrl);
if (rc)
return rc;
+ inq_parm.ctrl = ctrl;
inq_parm.ctx = agent_ctx;
rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
n=0;
- if (seskeylen == 24)
+ if (seskeylen == 24 || seskeylen == 16)
{
- /* Smells like a 3-des key. This might happen because a SC has
- already done the unpacking. */
+ /* Smells like a 3-DES or AES-128 key. This might happen
+ * because a SC has already done the unpacking. A better
+ * solution would be to test for this only after we triggered
+ * the GPG_ERR_INV_SESSION_KEY. */
}
else
{
ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")),
ARGPARSE_c (aListChain, "list-chain", N_("list certificate chain")),
ARGPARSE_c (aFingerprint, "fingerprint", N_("list keys and fingerprints")),
- ARGPARSE_c (aKeygen, "gen-key", N_("generate a new key pair")),
+ ARGPARSE_c (aKeygen, "generate-key", N_("generate a new key pair")),
+ ARGPARSE_c (aKeygen, "gen-key", "@"),
ARGPARSE_c (aDeleteKey, "delete-keys",
N_("remove keys from the public keyring")),
/*ARGPARSE_c (aSendKeys, "send-keys", N_("export keys to a keyserver")),*/
N_("pass a command to the dirmngr")),
ARGPARSE_c (aCallProtectTool, "call-protect-tool",
N_("invoke gpg-protect-tool")),
- ARGPARSE_c (aPasswd, "passwd", N_("change a passphrase")),
+ ARGPARSE_c (aPasswd, "change-passphrase", N_("change a passphrase")),
+ ARGPARSE_c (aPasswd, "passwd", "@"),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
"@\n(See the man page for a complete listing of all commands and options)\n"
)),
- ARGPARSE_group (303, N_("@\nExamples:\n\n"
- " -se -r Bob [file] sign and encrypt for user Bob\n"
- " --clearsign [file] make a clear text signature\n"
- " --detach-sign [file] make a detached signature\n"
- " --list-keys [names] show keys\n"
- " --fingerprint [names] show fingerprints\n" )),
/* Hidden options. */
ARGPARSE_s_s (oCompliance, "compliance", "@"),
/* Command aliases. */
ARGPARSE_c (aListKeys, "list-key", "@"),
- ARGPARSE_c (aListChain, "list-sig", "@"),
+ ARGPARSE_c (aListChain, "list-signatures", "@"),
ARGPARSE_c (aListChain, "list-sigs", "@"),
- ARGPARSE_c (aListChain, "check-sig", "@"),
+ ARGPARSE_c (aListChain, "check-signatures", "@"),
ARGPARSE_c (aListChain, "check-sigs", "@"),
ARGPARSE_c (aDeleteKey, "delete-key", "@"),
static const char *debug_level;
static unsigned int debug_value;
-/* Option --enable-special-filenames */
-static int allow_special_filenames;
-
/* Default value for include-certs. We need an extra macro for
gpgconf-list because the variable will be changed by the command
line option.
enum cmd_and_opt_values new_cmd );
static void emergency_cleanup (void);
-static int check_special_filename (const char *fname, int for_write);
static int open_read (const char *filename);
static estream_t open_es_fread (const char *filename, const char *mode);
static estream_t open_es_fwrite (const char *filename);
case oNoRandomSeedFile: use_random_seed = 0; break;
case oNoCommonCertsImport: no_common_certs_import = 1; break;
- case oEnableSpecialFilenames: allow_special_filenames =1; break;
+ case oEnableSpecialFilenames:
+ enable_special_filenames ();
+ break;
case oValidationModel: parse_validation_model (pargs.r.ret_str); break;
es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("disable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE);
+ es_printf ("enable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("disable-trusted-cert-crl-check:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("enable-ocsp:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("include-certs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT,
else if (argc == 1) /* From file. */
fpin = open_es_fread (*argv, "r");
else
- wrong_args ("--gen-key --batch [parmfile]");
+ wrong_args ("--generate-key --batch [parmfile]");
}
fpout = open_es_fwrite (opt.outfile?opt.outfile:"-");
case aPasswd:
if (argc != 1)
- wrong_args ("--passwd <key-Id>");
+ wrong_args ("--change-passphrase <key-Id>");
else
{
int rc;
}
-/* Check whether the filename has the form "-&nnnn", where n is a
- non-zero number. Returns this number or -1 if it is not the case. */
-static int
-check_special_filename (const char *fname, int for_write)
-{
- if (allow_special_filenames
- && fname && *fname == '-' && fname[1] == '&' ) {
- int i;
-
- fname += 2;
- for (i=0; isdigit (fname[i]); i++ )
- ;
- if ( !fname[i] )
- return translate_sys2libc_fd_int (atoi (fname), for_write);
- }
- return -1;
-}
-
-
/* Open the FILENAME for read and return the file descriptor. Stop
with an error message in case of problems. "-" denotes stdin and
set_binary (stdin);
return 0; /* stdin */
}
- fd = check_special_filename (filename, 0);
+ fd = check_special_filename (filename, 0, 0);
if (fd != -1)
return fd;
fd = open (filename, O_RDONLY | O_BINARY);
if (filename[0] == '-' && !filename[1])
fd = fileno (stdin);
else
- fd = check_special_filename (filename, 0);
+ fd = check_special_filename (filename, 0, 0);
if (fd != -1)
{
fp = es_fdopen_nc (fd, mode);
return fp;
}
- fd = check_special_filename (filename, 1);
+ fd = check_special_filename (filename, 1, 0);
if (fd != -1)
{
fp = es_fdopen_nc (fd, "wb");
## Process this file with automake to produce Makefile.in
-SUBDIRS = gpgscm openpgp migrations pkits .
+SUBDIRS = gpgscm openpgp migrations gpgme pkits .
GPGSM = ../sm/gpgsm
--- /dev/null
+# Makefile.am - For tests/gpgme
+# Copyright (C) 2016 g10 Code GmbH
+#
+# 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/>.
+# Process this file with automake to create Makefile.in
+
+
+# Programs required before we can run these tests.
+required_pgms = ../../g10/gpg$(EXEEXT) ../../agent/gpg-agent$(EXEEXT) \
+ ../../tools/gpg-connect-agent$(EXEEXT) \
+ ../gpgscm/gpgscm$(EXEEXT)
+
+AM_CPPFLAGS = -I$(top_srcdir)/common
+include $(top_srcdir)/am/cmacros.am
+
+AM_CFLAGS =
+
+TMP ?= /tmp
+
+TESTS_ENVIRONMENT = LC_ALL=C \
+ EXEEXT=$(EXEEXT) \
+ PATH=../gpgscm:$(PATH) \
+ TMP=$(TMP) \
+ srcdir=$(abs_srcdir) \
+ objdir=$(abs_top_builddir) \
+ GPGSCM_PATH=$(abs_top_srcdir)/tests/gpgscm:$(abs_top_srcdir)/tests/openpgp:$(abs_top_srcdir)/tests/gpgme
+
+# XXX: Currently, one cannot override automake's 'check' target. As a
+# workaround, we avoid defining 'TESTS', thus automake will not emit
+# the 'check' target. For extra robustness, we merely define a
+# dependency on 'xcheck', so this hack should also work even if
+# automake would emit the 'check' target, as adding dependencies to
+# targets is okay.
+check: xcheck
+
+.PHONY: xcheck
+xcheck:
+ $(TESTS_ENVIRONMENT) $(abs_top_builddir)/tests/gpgscm/gpgscm \
+ $(abs_srcdir)/run-tests.scm $(TESTFLAGS) $(XTESTS)
+
+EXTRA_DIST = gpgme-defs.scm run-tests.scm setup.scm wrap.scm
+
+CLEANFILES = *.log
+
+# We need to depend on a couple of programs so that the tests don't
+# start before all programs are built.
+all-local: $(required_pgms)
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+
+(define gpgme-srcdir (getenv "XTEST_GPGME_SRCDIR"))
+(when (string=? "" gpgme-srcdir)
+ (info
+ "SKIP: Environment variable 'XTEST_GPGME_SRCDIR' not set. Please"
+ "point it to a recent GPGME source tree to run the GPGME test suite.")
+ (exit 0))
+
+(define (in-gpgme-srcdir . names)
+ (canonical-path (apply path-join (cons gpgme-srcdir names))))
+
+(define gpgme-builddir (getenv "XTEST_GPGME_BUILDDIR"))
+(when (string=? "" gpgme-builddir)
+ (info
+ "SKIP: Environment variable 'XTEST_GPGME_BUILDDIR' not set. Please"
+ "point it to a recent GPGME build tree to run the GPGME test suite.")
+ (exit 0))
+
+;; Make sure that GPGME picks up our gpgconf. This makes GPGME use
+;; and thus executes the tests with GnuPG components from the build
+;; tree.
+(setenv "PATH" (string-append (path-join (getenv "GNUPG_BUILDDIR") "tools")
+ (string *pathsep*) (getenv "PATH")) #t)
+
+(define (create-file name content)
+ (letfd ((fd (open name (logior O_WRONLY O_CREAT O_BINARY) #o600)))
+ (display content (fdopen fd "wb"))))
+
+(define (create-gpgmehome . path)
+ (create-file "gpg.conf" "no-force-v3-sigs\n")
+ (create-file
+ "gpg-agent.conf"
+ (string-append "pinentry-program "
+ (in-gpgme-srcdir "tests" "gpg" "pinentry") "\n"))
+ (mkdir "private-keys-v1.d" "-rwx")
+
+ (log "Storing private keys")
+ (for-each
+ (lambda (name)
+ (file-copy (apply in-gpgme-srcdir `(,@path ,name))
+ (path-join "private-keys-v1.d"
+ (string-append name ".key"))))
+ '("13CD0F3BDF24BE53FE192D62F18737256FF6E4FD"
+ "76F7E2B35832976B50A27A282D9B87E44577EB66"
+ "A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD"
+ "13CBE3758AFE42B5E5E2AE4CED27AFA455E3F87F"
+ "7A030357C0F253A5BBCD282FFC4E521B37558F5C"))
+
+ (log "Importing public demo and test keys")
+ (for-each
+ (lambda (file)
+ (call-check `(,@GPG --yes --import ,(apply in-gpgme-srcdir
+ `(,@path ,file)))))
+ (list "pubdemo.asc" "secdemo.asc"))
+ (stop-agent))
+
+;; Initialize the test environment, install appropriate configuration
+;; and start the agent, with the keys from the legacy test suite.
+(define (setup-gpgme-environment . path)
+ (if (member "--unpack-tarball" *args*)
+ (begin
+ (call-check `(,(tool 'gpgtar) --extract --directory=. ,(cadr *args*)))
+ (start-agent))
+ (apply create-gpgme-gpghome path)))
+
+;; Command line flag handling. Returns the elements following KEY in
+;; ARGUMENTS up to the next argument, or #f if KEY is not in
+;; ARGUMENTS.
+(define (flag key arguments)
+ (cond
+ ((null? arguments)
+ #f)
+ ((string=? key (car arguments))
+ (let loop ((acc '())
+ (args (cdr arguments)))
+ (if (or (null? args) (string-prefix? (car args) "--"))
+ (reverse acc)
+ (loop (cons (car args) acc) (cdr args)))))
+ ((string=? "--" (car arguments))
+ #f)
+ (else
+ (flag key (cdr arguments)))))
+(assert (equal? (flag "--xxx" '("--yyy")) #f))
+(assert (equal? (flag "--xxx" '("--xxx")) '()))
+(assert (equal? (flag "--xxx" '("--xxx" "yyy")) '("yyy")))
+(assert (equal? (flag "--xxx" '("--xxx" "yyy" "zzz")) '("yyy" "zzz")))
+(assert (equal? (flag "--xxx" '("--xxx" "yyy" "zzz" "--")) '("yyy" "zzz")))
+(assert (equal? (flag "--xxx" '("--xxx" "yyy" "--" "zzz")) '("yyy")))
+(assert (equal? (flag "--" '("--" "xxx" "yyy" "--" "zzz")) '("xxx" "yyy")))
+
+(define (parse-makefile port key)
+ (define (is-continuation? tokens)
+ (string=? (last tokens) "\\"))
+ (define (valid-token? s)
+ (< 0 (string-length s)))
+ (define (drop-continuations tokens)
+ (let loop ((acc '()) (tks tokens))
+ (if (null? tks)
+ (reverse acc)
+ (loop (if (string=? "\\" (car tks))
+ acc
+ (cons (car tks) acc)) (cdr tks)))))
+ (let next ((acc '()) (found #f))
+ (let ((line (read-line port)))
+ (if (eof-object? line)
+ acc
+ (let ((tokens (filter valid-token?
+ (string-splitp (string-trim char-whitespace?
+ line)
+ char-whitespace? -1))))
+ (cond
+ ((or (null? tokens)
+ (string-prefix? (car tokens) "#")
+ (and (not found) (not (and (string=? key (car tokens))
+ (string=? "=" (cadr tokens))))))
+ (next acc found))
+ ((not found)
+ (assert (and (string=? key (car tokens))
+ (string=? "=" (cadr tokens))))
+ (if (is-continuation? tokens)
+ (next (drop-continuations (cddr tokens)) #t)
+ (drop-continuations (cddr tokens))))
+ (else
+ (assert found)
+ (if (is-continuation? tokens)
+ (next (append acc (drop-continuations tokens)) found)
+ (append acc (drop-continuations tokens))))))))))
+
+(define (parse-makefile-expand filename expand key)
+ (define (variable? v)
+ (and (string-prefix? v "$(") (string-suffix? v ")")))
+
+ (let expand-all ((values (parse-makefile (open-input-file filename) key)))
+ (if (any variable? values)
+ (expand-all
+ (let expand-one ((acc '()) (v values))
+ (cond
+ ((null? v)
+ acc)
+ ((variable? (car v))
+ (let ((makefile (open-input-file filename))
+ (key (substring (car v) 2 (- (string-length (car v)) 1))))
+ (expand-one (append acc (expand filename makefile key))
+ (cdr v))))
+ (else
+ (expand-one (append acc (list (car v))) (cdr v))))))
+ values)))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "gpgme-defs.scm"))
+
+(info "Running GPGME's test suite...")
+
+(define (gpgme-makefile-expand filename port key)
+ ;;(interactive-repl (current-environment))
+ (cond
+ ((string=? key "tests_unix")
+ (if *win32*
+ (parse-makefile port key) ;; Use win32 definition.
+ (begin
+ (parse-makefile port key) ;; Skip win32 definition.
+ (parse-makefile port key))))
+ (else
+ (parse-makefile port key))))
+
+(define (all-tests filename key)
+ (parse-makefile-expand filename gpgme-makefile-expand key))
+
+(let* ((runner (if (member "--parallel" *args*)
+ run-tests-parallel
+ run-tests-sequential))
+ (tests (filter (lambda (arg) (not (string-prefix? arg "--"))) *args*)))
+ (runner
+ (test::scm "setup.scm" (in-srcdir "setup.scm") "--" "tests" "gpg")
+ (apply
+ append
+ (map (lambda (cmpnts)
+ (define (compiled? name)
+ (not (or (string-suffix? name ".py")
+ (string-suffix? name ".test"))))
+ (define :path car)
+ (define :key cadr)
+ (define (find-test name)
+ (apply path-join
+ `(,(if (compiled? name)
+ gpgme-builddir
+ gpgme-srcdir) ,@(:path cmpnts),name)))
+ (let ((makefile (apply path-join `(,gpgme-srcdir ,@(:path cmpnts)
+ "Makefile.am"))))
+ (map (lambda (name)
+ (apply test::scm
+ `(,name ,(in-srcdir "wrap.scm") --executable
+ ,(find-test name)
+ -- ,@(:path cmpnts))))
+ (if (null? tests) (all-tests makefile (:key cmpnts)) tests))))
+ '((("tests" "gpg") "c_tests")
+ ;; XXX: Not yet.
+ ;; (("lang" "python" "tests") "py_tests")
+ (("lang" "qt" "tests") "TESTS"))))))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "gpgme-defs.scm"))
+
+(define tarball (flag "--create-tarball" *args*))
+(unless (and tarball (not (null? tarball)))
+ (error "Usage: setup.scm --create-tarball <file> ..."))
+
+(define components (flag "--" *args*))
+(unless (and components (not (null? components)))
+ (error "Usage: setup.scm --create-tarball " (cadr tarball)
+ " -- component [component ...]"))
+
+(with-temporary-working-directory
+ (setenv "GNUPGHOME" (getcwd) #t)
+ (apply create-gpgmehome components)
+ (stop-agent)
+ (call-check `(,(tool 'gpgtar) --create --output ,(car tarball) ".")))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "gpgme-defs.scm"))
+
+(define executable (flag "--executable" *args*))
+(unless (and executable (not (null? executable)))
+ (error "Usage: wrap.scm --executable <file> [args...]"))
+
+(setup-gpgme-environment "tests" "gpg")
+
+(setenv "abs_builddir" (getcwd) #t)
+(setenv "top_srcdir" gpgme-srcdir #t)
+(setenv "srcdir" (path-join gpgme-srcdir "tests" "gpg") #t)
+
+(define (run what)
+ (if (string-suffix? (car what) ".py")
+ (begin
+ (setenv "LD_LIBRARY_PATH"
+ (if (< 0 (string-length (getenv "LD_LIBRARY_PATH")))
+ (string-append (path-join gpgme-builddir "src/.libs")
+ (string *pathsep*)
+ (getenv "LD_LIBRARY_PATH"))
+ (path-join gpgme-builddir "src/.libs"))
+ #t)
+ (call-with-fds
+ `("/usr/bin/python"
+ ,(in-gpgme-srcdir "lang" "python" "tests" "run-tests.py")
+ --quiet
+ --interpreters=/usr/bin/python
+ --builddir ,(path-join gpgme-builddir "lang" "python" "tests")
+ ,@what)
+ STDIN_FILENO STDOUT_FILENO STDERR_FILENO))
+ (if #f 77 (call-with-fds what STDIN_FILENO STDOUT_FILENO STDERR_FILENO))))
+
+(let ((name (basename (car executable))))
+ (cond
+ ((string=? "t-keylist" name)
+ ;; This test assumes that 't-import' imported a key.
+ (log "Importing extra key...")
+ (call-check `(,@GPG --yes --import ,(in-srcdir "pubkey-1.asc"))))))
+
+(log "Running" (car executable))
+(exit (run executable))
;; Low-level mechanism to terminate the process.
(ffi-define (_exit status))
-
-;; High-level mechanism to terminate the process is to throw an error
-;; of the form (*interpreter-exit* status). This gives automatic
-;; resource management a chance to clean up.
-(define *interpreter-exit* (gensym))
-(define (throw . x)
- (cond
- ((more-handlers?)
- (apply (pop-handler) x))
- ((and (= 2 (length x)) (equal? *interpreter-exit* (car x)))
- (*run-atexit-handlers*)
- (_exit (cadr x)))
- (else
- (apply error x))))
-
-;; Terminate the process returning STATUS to the parent.
-(define (exit status)
- (throw *interpreter-exit* status))
-
-;; A list of functions run at interpreter shutdown.
-(define *atexit-handlers* (list))
-
-;; Execute all these functions.
-(define (*run-atexit-handlers*)
- (unless (null? *atexit-handlers*)
- (let ((proc (car *atexit-handlers*)))
- ;; Drop proc from the list so that it will not get
- ;; executed again even if it raises an exception.
- (set! *atexit-handlers* (cdr *atexit-handlers*))
- (proc)
- (*run-atexit-handlers*))))
-
-;; Register a function to be run at interpreter shutdown.
-(define (atexit proc)
- (set! *atexit-handlers* (cons proc *atexit-handlers*)))
`(define ,(cadr form)
(call/cc (lambda (return) ,@(cddr form)))))
+;; Print the given history.
+(define (vm-history-print history)
+ (let loop ((n 0) (skip 0) (frames history))
+ (cond
+ ((null? frames)
+ #t)
+ ((> skip 0)
+ (loop 0 (- skip 1) (cdr frames)))
+ (else
+ (let ((f (car frames)))
+ (display n)
+ (display ": ")
+ (let ((tag (get-tag f)))
+ (unless (null? tag)
+ (display (basename (car tag)))
+ (display ":")
+ (display (+ 1 (cdr tag)))
+ (display ": ")))
+ (write f))
+ (newline)
+ (loop (+ n 1) skip (cdr frames))))))
+
;;;; Simple exception handling
;
; Exceptions are caught as follows:
; "Catch" establishes a scope spanning multiple call-frames until
; another "catch" is encountered. Within the recovery expression
; the thrown exception is bound to *error*. Errors can be rethrown
-; using (apply throw *error*).
+; using (rethrow *error*).
;
; Exceptions are thrown with:
;
(define (more-handlers?)
(pair? *handlers*))
-(define (throw . x)
- (if (more-handlers?)
- (apply (pop-handler) x)
- (apply error x)))
+;; This throws an exception.
+(define (throw message . args)
+ (throw' message args (cdr (*vm-history*))))
+
+;; This is used by the vm to throw exceptions.
+(define (throw' message args history)
+ (cond
+ ((more-handlers?)
+ ((pop-handler) message args history))
+ ((and args (list? args) (= 2 (length args))
+ (equal? *interpreter-exit* (car args)))
+ (*run-atexit-handlers*)
+ (quit (cadr args)))
+ (else
+ (display message)
+ (if args (begin
+ (display ": ")
+ (write args)))
+ (newline)
+ (vm-history-print history)
+ (quit 1))))
+
+;; Convenience function to rethrow the error.
+(define (rethrow e)
+ (apply throw' e))
(macro (catch form)
(let ((label (gensym)))
(pop-handler)
,label)))))
-(define *error-hook* throw)
+;; Make the vm use throw'.
+(define *error-hook* throw')
+
+\f
+
+;; High-level mechanism to terminate the process is to throw an error
+;; of the form (*interpreter-exit* status). This gives automatic
+;; resource management a chance to clean up.
+(define *interpreter-exit* (gensym))
+
+;; Terminate the process returning STATUS to the parent.
+(define (exit status)
+ (throw "interpreter exit" *interpreter-exit* status))
+
+;; A list of functions run at interpreter shutdown.
+(define *atexit-handlers* (list))
+
+;; Execute all these functions.
+(define (*run-atexit-handlers*)
+ (unless (null? *atexit-handlers*)
+ (let ((proc (car *atexit-handlers*)))
+ ;; Drop proc from the list so that it will not get
+ ;; executed again even if it raises an exception.
+ (set! *atexit-handlers* (cdr *atexit-handlers*))
+ (proc)
+ (*run-atexit-handlers*))))
+
+;; Register a function to be run at interpreter shutdown.
+(define (atexit proc)
+ (set! *atexit-handlers* (cons proc *atexit-handlers*)))
+\f
;;;;; Definition of MAKE-ENVIRONMENT, to be used with two-argument EVAL
;; along with this program; if not, see <http://www.gnu.org/licenses/>.
(macro (assert form)
- `(if (not ,(cadr form))
- (begin
- (display "Assertion failed: ")
- (write (quote ,(cadr form)))
- (newline)
- (exit 1))))
+ (let ((tag (get-tag form)))
+ `(if (not ,(cadr form))
+ (throw ,(if (pair? tag)
+ `(string-append ,(car tag) ":"
+ ,(number->string (+ 1 (cdr tag)))
+ ": Assertion failed: ")
+ "Assertion failed: ")
+ (quote ,(cadr form))))))
(assert #t)
+(assert (not #f))
(define (filter pred lst)
(cond ((null? lst) '())
(assert (equal? #f (string-rindex "Hallo" #\a 2)))
(assert (equal? #f (string-rindex "Hallo" #\.)))
-;; Split haystack at delimiter at most n times.
-(define (string-splitn haystack delimiter n)
+;; Split HAYSTACK at each character that makes PREDICATE true at most
+;; N times.
+(define (string-split-pln haystack predicate lookahead n)
(let ((length (string-length haystack)))
- (define (split acc delimiter offset n)
+ (define (split acc offset n)
(if (>= offset length)
(reverse acc)
- (let ((i (string-index haystack delimiter offset)))
+ (let ((i (lookahead haystack offset)))
(if (or (eq? i #f) (= 0 n))
(reverse (cons (substring haystack offset length) acc))
(split (cons (substring haystack offset i) acc)
- delimiter (+ i 1) (- n 1))))))
- (split '() delimiter 0 n)))
+ (+ i 1) (- n 1))))))
+ (split '() 0 n)))
+
+(define (string-indexp haystack offset predicate)
+ (cond
+ ((= (string-length haystack) offset)
+ #f)
+ ((predicate (string-ref haystack offset))
+ offset)
+ (else
+ (string-indexp haystack (+ 1 offset) predicate))))
+
+;; Split HAYSTACK at each character that makes PREDICATE true at most
+;; N times.
+(define (string-splitp haystack predicate n)
+ (string-split-pln haystack predicate
+ (lambda (haystack offset)
+ (string-indexp haystack offset predicate))
+ n))
+(assert (equal? '("a" "b") (string-splitp "a b" char-whitespace? -1)))
+(assert (equal? '("a" "b") (string-splitp "a\tb" char-whitespace? -1)))
+(assert (equal? '("a" "" "b") (string-splitp "a \tb" char-whitespace? -1)))
+
+;; Split haystack at delimiter at most n times.
+(define (string-splitn haystack delimiter n)
+ (string-split-pln haystack
+ (lambda (c) (char=? c delimiter))
+ (lambda (haystack offset)
+ (string-index haystack delimiter offset))
+ n))
(assert (= 2 (length (string-splitn "foo:bar:baz" #\: 1))))
(assert (string=? "foo" (car (string-splitn "foo:bar:baz" #\: 1))))
(assert (string=? "bar:baz" (cadr (string-splitn "foo:bar:baz" #\: 1))))
;; Trim the prefix of S containing only characters that make PREDICATE
;; true.
(define (string-ltrim predicate s)
- (let loop ((s' (string->list s)))
- (if (predicate (car s'))
- (loop (cdr s'))
- (list->string s'))))
+ (if (string=? s "")
+ ""
+ (let loop ((s' (string->list s)))
+ (if (predicate (car s'))
+ (loop (cdr s'))
+ (list->string s')))))
+(assert (string=? "" (string-ltrim char-whitespace? "")))
(assert (string=? "foo" (string-ltrim char-whitespace? " foo")))
;; Trim the suffix of S containing only characters that make PREDICATE
;; true.
(define (string-rtrim predicate s)
- (let loop ((s' (reverse (string->list s))))
- (if (predicate (car s'))
- (loop (cdr s'))
- (list->string (reverse s')))))
+ (if (string=? s "")
+ ""
+ (let loop ((s' (reverse (string->list s))))
+ (if (predicate (car s'))
+ (loop (cdr s'))
+ (list->string (reverse s'))))))
+(assert (string=? "" (string-rtrim char-whitespace? "")))
(assert (string=? "foo" (string-rtrim char-whitespace? "foo ")))
;; Trim both the prefix and suffix of S containing only characters
;; that make PREDICATE true.
(define (string-trim predicate s)
(string-ltrim predicate (string-rtrim predicate s)))
+(assert (string=? "" (string-trim char-whitespace? "")))
(assert (string=? "foo" (string-trim char-whitespace? " foo ")))
;; Check if needle is contained in haystack.
(apply read-char p)
'()))))))
+(define (list->string-reversed lst)
+ (let* ((len (length lst))
+ (str (make-string len)))
+ (let loop ((i (- len 1))
+ (l lst))
+ (if (< i 0)
+ (begin
+ (assert (null? l))
+ str)
+ (begin
+ (string-set! str i (car l))
+ (loop (- i 1) (cdr l)))))))
+
;; Read a line from port P.
(define (read-line . p)
- (list->string
- (let f ()
- (let ((c (apply peek-char p)))
- (cond
- ((eof-object? c) '())
- ((char=? c #\newline)
- (apply read-char p)
- '())
- (else
- (apply read-char p)
- (cons c (f))))))))
+ (let loop ((acc '()))
+ (let ((c (apply peek-char p)))
+ (cond
+ ((eof-object? c)
+ (if (null? acc)
+ c ;; #eof
+ (list->string-reversed acc)))
+ ((char=? c #\newline)
+ (apply read-char p)
+ (list->string-reversed acc))
+ (else
+ (apply read-char p)
+ (loop (cons c acc)))))))
;; Read everything from port P.
(define (read-all . p)
h = fopen (qualified_name, "r");
if (h)
- break;
+ {
+ err = 0;
+ break;
+ }
if (n > 1)
{
fprintf (stderr,
"Consider using GPGSCM_PATH to specify the location "
"of the Scheme library.\n");
- return err;
+ goto leave;
}
if (verbose > 1)
fprintf (stderr, "Loading %s...\n", qualified_name);
scheme_load_named_file (sc, h, qualified_name);
fclose (h);
- if (sc->retcode)
+ if (sc->retcode && sc->nesting)
{
- if (sc->nesting)
- fprintf (stderr, "%s: Unbalanced parenthesis\n", qualified_name);
- return gpg_error (GPG_ERR_GENERAL);
+ fprintf (stderr, "%s: Unbalanced parenthesis\n", qualified_name);
+ err = gpg_error (GPG_ERR_GENERAL);
}
+ leave:
if (file_name != qualified_name)
free (qualified_name);
- return 0;
+ return err;
}
\f
int
main (int argc, char **argv)
{
+ int retcode;
gpg_error_t err;
char *argv0;
ARGPARSE_ARGS pargs;
log_fatal ("%s: %s", script, gpg_strerror (err));
}
+ retcode = sc->retcode;
scheme_load_string (sc, "(*run-atexit-handlers*)");
scheme_deinit (sc);
xfree (sc);
- return EXIT_SUCCESS;
+ return retcode;
}
#endif
_OP_DEF(opexe_0, 0, 0, 0, 0, OP_E0ARGS )
_OP_DEF(opexe_0, 0, 0, 0, 0, OP_E1ARGS )
+#if USE_HISTORY
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_CALLSTACK_POP )
+#endif
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_APPLY_CODE )
_OP_DEF(opexe_0, 0, 0, 0, 0, OP_APPLY )
#if USE_TRACING
_OP_DEF(opexe_0, 0, 0, 0, 0, OP_REAL_APPLY )
_OP_DEF(opexe_4, "list*", 1, INF_ARG, TST_NONE, OP_LIST_STAR )
_OP_DEF(opexe_4, "append", 0, INF_ARG, TST_NONE, OP_APPEND )
#if USE_PLIST
- _OP_DEF(opexe_4, "put", 3, 3, TST_NONE, OP_PUT )
- _OP_DEF(opexe_4, "get", 2, 2, TST_NONE, OP_GET )
+ _OP_DEF(opexe_4, "set-symbol-property!", 3, 3, TST_SYMBOL TST_SYMBOL TST_ANY, OP_SET_SYMBOL_PROPERTY )
+ _OP_DEF(opexe_4, "symbol-property", 2, 2, TST_SYMBOL TST_SYMBOL, OP_SYMBOL_PROPERTY )
+#endif
+#if USE_TAGS
+ _OP_DEF(opexe_4, NULL, 0, 0, TST_NONE, OP_TAG_VALUE )
+ _OP_DEF(opexe_4, "make-tagged-value", 2, 2, TST_ANY TST_PAIR, OP_MK_TAGGED )
+ _OP_DEF(opexe_4, "get-tag", 1, 1, TST_ANY, OP_GET_TAG )
#endif
_OP_DEF(opexe_4, "quit", 0, 1, TST_NUMBER, OP_QUIT )
_OP_DEF(opexe_4, "gc", 0, 0, 0, OP_GC )
_OP_DEF(opexe_6, "get-closure-code", 1, 1, TST_NONE, OP_GET_CLOSURE )
_OP_DEF(opexe_6, "closure?", 1, 1, TST_NONE, OP_CLOSUREP )
_OP_DEF(opexe_6, "macro?", 1, 1, TST_NONE, OP_MACROP )
+ _OP_DEF(opexe_6, "*vm-history*", 0, 0, TST_NONE, OP_VM_HISTORY )
+
#undef _OP_DEF
(read (open-input-string next)))))
(if (not (eof-object? c))
(begin
- (catch (echo "Error:" *error*)
+ (catch (begin
+ (display (car *error*))
+ (when (and (cadr *error*)
+ (not (null? (cadr *error*))))
+ (display ": ")
+ (write (cadr *error*)))
+ (newline)
+ (vm-history-print (caddr *error*)))
(echo " ===>" (eval c environment)))
(exit (loop ""))))
(exit (loop next)))))))))
} _object;
};
+#if USE_HISTORY
+/* The history is a two-dimensional ring buffer. A donut-shaped data
+ * structure. This data structure is inspired by MIT/GNU Scheme. */
+struct history {
+ /* Number of calls to store. Must be a power of two. */
+ size_t N;
+
+ /* Number of tail-calls to store in each call frame. Must be a
+ * power of two. */
+ size_t M;
+
+ /* Masks for fast index calculations. */
+ size_t mask_N;
+ size_t mask_M;
+
+ /* A vector of size N containing calls. */
+ pointer callstack;
+
+ /* A vector of size N containing vectors of size M containing tail
+ * calls. */
+ pointer tailstacks;
+
+ /* Our current position. */
+ size_t n;
+ size_t *m;
+};
+#endif
+
struct scheme {
/* arrays for segments */
func_alloc malloc;
pointer code; /* register for current code */
pointer dump; /* stack register for next evaluation */
+#if USE_HISTORY
+struct history history; /* we keep track of the call history for
+ * error messages */
+#endif
+
int interactive_repl; /* are we in an interactive REPL? */
struct cell _sink;
pointer COMPILE_HOOK; /* *compile-hook* */
#endif
+#if USE_SMALL_INTEGERS
+/* A fixed allocation of small integers. */
+void *integer_alloc;
+pointer integer_cells;
+#endif
+
pointer free_cell; /* pointer to top of free cells */
long fcells; /* # of free cells */
size_t inhibit_gc; /* nesting of gc_disable */
int print_flag;
pointer value;
int op;
+unsigned int flags;
void *ext_data; /* For the benefit of foreign functions */
long gensym_cnt;
#include <assert.h>
#include <limits.h>
+#include <stdint.h>
#include <float.h>
#include <ctype.h>
#define ADJ 32
#define TYPE_BITS 5
#define T_MASKTYPE 31 /* 0000000000011111 */
+#define T_TAGGED 1024 /* 0000010000000000 */
#define T_FINALIZE 2048 /* 0000100000000000 */
#define T_SYNTAX 4096 /* 0001000000000000 */
#define T_IMMUTABLE 8192 /* 0010000000000000 */
INTERFACE INLINE int is_symbol(pointer p) { return (type(p)==T_SYMBOL); }
INTERFACE INLINE char *symname(pointer p) { return strvalue(car(p)); }
#if USE_PLIST
-SCHEME_EXPORT INLINE int hasprop(pointer p) { return (typeflag(p)&T_SYMBOL); }
+SCHEME_EXPORT INLINE int hasprop(pointer p) { return (is_symbol(p)); }
#define symprop(p) cdr(p)
#endif
#define cadddr(p) car(cdr(cdr(cdr(p))))
#define cddddr(p) cdr(cdr(cdr(cdr(p))))
+#if USE_HISTORY
+static pointer history_flatten(scheme *sc);
+static void history_mark(scheme *sc);
+#else
+# define history_mark(SC) (void) 0
+# define history_flatten(SC) (SC)->NIL
+#endif
+
#if USE_CHAR_CLASSIFIERS
static INLINE int Cisalpha(int c) { return isascii(c) && isalpha(c); }
static INLINE int Cisdigit(int c) { return isascii(c) && isdigit(c); }
static pointer mk_proc(scheme *sc, enum scheme_opcodes op);
static pointer mk_closure(scheme *sc, pointer c, pointer e);
static pointer mk_continuation(scheme *sc, pointer d);
-static pointer reverse(scheme *sc, pointer a);
+static pointer reverse(scheme *sc, pointer term, pointer list);
static pointer reverse_in_place(scheme *sc, pointer term, pointer list);
static pointer revappend(scheme *sc, pointer a, pointer b);
static void dump_stack_mark(scheme *);
return x;
}
+\f
+
+/* Tags are like property lists, but can be attached to arbitrary
+ * values. */
+
+#if USE_TAGS
+
+static pointer
+mk_tagged_value(scheme *sc, pointer v, pointer tag_car, pointer tag_cdr)
+{
+ pointer r, t;
+
+ assert(! is_vector(v));
+
+ r = get_consecutive_cells(sc, 2);
+ if (r == sc->sink)
+ return sc->sink;
+
+ memcpy(r, v, sizeof *v);
+ typeflag(r) |= T_TAGGED;
+
+ t = r + 1;
+ typeflag(t) = T_PAIR;
+ car(t) = tag_car;
+ cdr(t) = tag_cdr;
+
+ return r;
+}
+
+static INLINE int
+has_tag(pointer v)
+{
+ return !! (typeflag(v) & T_TAGGED);
+}
+
+static INLINE pointer
+get_tag(scheme *sc, pointer v)
+{
+ if (has_tag(v))
+ return v + 1;
+ return sc->NIL;
+}
+
+#else
+
+#define mk_tagged_value(SC, X, A, B) (X)
+#define has_tag(V) 0
+#define get_tag(SC, V) (SC)->NIL
+
+#endif
+
+\f
+
+/* Allocate a new cell segment but do not make it available yet. */
+static int
+_alloc_cellseg(scheme *sc, size_t len, void **alloc, pointer *cells)
+{
+ int adj = ADJ;
+ void *cp;
+
+ if (adj < sizeof(struct cell))
+ adj = sizeof(struct cell);
+
+ cp = sc->malloc(len * sizeof(struct cell) + adj);
+ if (cp == NULL)
+ return 1;
+
+ *alloc = cp;
+
+ /* adjust in TYPE_BITS-bit boundary */
+ if (((uintptr_t) cp) % adj != 0)
+ cp = (void *) (adj * ((uintptr_t) cp / adj + 1));
+
+ *cells = cp;
+ return 0;
+}
+
/* allocate new cell segment */
static int alloc_cellseg(scheme *sc, int n) {
pointer newp;
pointer last;
pointer p;
- void *cp;
long i;
int k;
- int adj=ADJ;
-
- if(adj<sizeof(struct cell)) {
- adj=sizeof(struct cell);
- }
for (k = 0; k < n; k++) {
if (sc->last_cell_seg >= CELL_NSEGMENT - 1)
return k;
- cp = sc->malloc(CELL_SEGSIZE * sizeof(struct cell)+adj);
- if (cp == 0)
- return k;
- i = ++sc->last_cell_seg ;
- sc->alloc_seg[i] = cp;
- /* adjust in TYPE_BITS-bit boundary */
- if(((unsigned long)cp)%adj!=0) {
- cp=(void *)(adj*((unsigned long)cp/adj+1));
- }
+ i = ++sc->last_cell_seg;
+ if (_alloc_cellseg(sc, CELL_SEGSIZE, &sc->alloc_seg[i], &newp)) {
+ sc->last_cell_seg--;
+ return k;
+ }
/* insert new segment in address order */
- newp=(pointer)cp;
sc->cell_seg[i] = newp;
while (i > 0 && sc->cell_seg[i - 1] > sc->cell_seg[i]) {
p = sc->cell_seg[i];
return (x);
}
+\f
+
+#if USE_SMALL_INTEGERS
+
+/* s_save assumes that all opcodes can be expressed as a small
+ * integer. */
+#define MAX_SMALL_INTEGER OP_MAXDEFINED
+
+static int
+initialize_small_integers(scheme *sc)
+{
+ int i;
+ if (_alloc_cellseg(sc, MAX_SMALL_INTEGER, &sc->integer_alloc,
+ &sc->integer_cells))
+ return 1;
+
+ for (i = 0; i < MAX_SMALL_INTEGER; i++) {
+ pointer x = &sc->integer_cells[i];
+ typeflag(x) = T_NUMBER | T_ATOM | MARK;
+ ivalue_unchecked(x) = i;
+ set_num_integer(x);
+ }
+
+ return 0;
+}
+
+static INLINE pointer
+mk_small_integer(scheme *sc, long n)
+{
+#define mk_small_integer_allocates 0
+ assert(0 <= n && n < MAX_SMALL_INTEGER);
+ return &sc->integer_cells[n];
+}
+#else
+
+#define mk_small_integer_allocates 1
+#define mk_small_integer mk_integer
+
+#endif
+
/* get number atom (integer) */
INTERFACE pointer mk_integer(scheme *sc, long n) {
- pointer x = get_cell(sc,sc->NIL, sc->NIL);
+ pointer x;
+
+#if USE_SMALL_INTEGERS
+ if (0 <= n && n < MAX_SMALL_INTEGER)
+ return mk_small_integer(sc, n);
+#endif
+ x = get_cell(sc,sc->NIL, sc->NIL);
typeflag(x) = (T_NUMBER | T_ATOM);
ivalue_unchecked(x)= n;
set_num_integer(x);
return (x);
}
+\f
+
INTERFACE pointer mk_real(scheme *sc, double n) {
pointer x = get_cell(sc,sc->NIL, sc->NIL);
int has_fp_exp = 0;
#if USE_COLON_HOOK
- if((p=strstr(q,"::"))!=0) {
+ char *next;
+ next = p = q;
+ while ((next = strstr(next, "::")) != 0) {
+ /* Keep looking for the last occurrence. */
+ p = next;
+ next = next + 2;
+ }
+
+ if (p != q) {
*p=0;
return cons(sc, sc->COLON_HOOK,
cons(sc,
cons(sc,
sc->QUOTE,
- cons(sc, mk_atom(sc,p+2), sc->NIL)),
- cons(sc, mk_symbol(sc,strlwr(q)), sc->NIL)));
+ cons(sc, mk_symbol(sc, strlwr(p + 2)),
+ sc->NIL)),
+ cons(sc, mk_atom(sc, q), sc->NIL)));
}
#endif
mark(p+1+i);
}
}
+ /* Mark tag if p has one. */
+ if (has_tag(p))
+ mark(p + 1);
if (is_atom(p))
goto E6;
/* E4: down car */
mark(sc->args);
mark(sc->envir);
mark(sc->code);
+ history_mark(sc);
dump_stack_mark(sc);
mark(sc->value);
mark(sc->inport);
/* reclaim cell */
if (typeflag(p) & T_FINALIZE) {
finalize_cell(sc, p);
- typeflag(p) = 0;
- car(p) = sc->NIL;
}
++sc->fcells;
+ typeflag(p) = 0;
+ car(p) = sc->NIL;
cdr(p) = sc->free_cell;
sc->free_cell = p;
}
}
/* reverse list -- produce new list */
-static pointer reverse(scheme *sc, pointer a) {
+static pointer reverse(scheme *sc, pointer term, pointer list) {
/* a must be checked by gc */
- pointer p = sc->NIL;
+ pointer a = list, p = term;
for ( ; is_pair(a); a = cdr(a)) {
p = cons(sc, car(a), p);
static pointer _Error_1(scheme *sc, const char *s, pointer a) {
const char *str = s;
+ pointer history;
#if USE_ERROR_HOOK
pointer x;
pointer hdl=sc->ERROR_HOOK;
#if SHOW_ERROR_LINE
char sbuf[STRBUFFSIZE];
+#endif
+
+ history = history_flatten(sc);
+#if SHOW_ERROR_LINE
/* make sure error is not in REPL */
if (sc->load_stack[sc->file_i].kind & port_file &&
sc->load_stack[sc->file_i].rep.stdio.file != stdin) {
- int ln = sc->load_stack[sc->file_i].rep.stdio.curr_line;
- const char *fname = sc->load_stack[sc->file_i].rep.stdio.filename;
+ pointer tag;
+ const char *fname;
+ int ln;
+
+ if (history != sc->NIL && has_tag(car(history))
+ && (tag = get_tag(sc, car(history)))
+ && is_string(car(tag)) && is_integer(cdr(tag))) {
+ fname = string_value(car(tag));
+ ln = ivalue_unchecked(cdr(tag));
+ } else {
+ fname = sc->load_stack[sc->file_i].rep.stdio.filename;
+ ln = sc->load_stack[sc->file_i].rep.stdio.curr_line;
+ }
/* should never happen */
if(!fname) fname = "<unknown>";
/* we started from 0 */
ln++;
- snprintf(sbuf, STRBUFFSIZE, "(%s : %i) %s", fname, ln, s);
+ snprintf(sbuf, STRBUFFSIZE, "%s:%i: %s", fname, ln, s);
str = (const char*)sbuf;
}
#if USE_ERROR_HOOK
x=find_slot_in_env(sc,sc->envir,hdl,1);
if (x != sc->NIL) {
+ sc->code = cons(sc, cons(sc, sc->QUOTE,
+ cons(sc, history, sc->NIL)),
+ sc->NIL);
if(a!=0) {
- sc->code = cons(sc, cons(sc, sc->QUOTE, cons(sc,(a), sc->NIL)), sc->NIL);
+ sc->code = cons(sc, cons(sc, sc->QUOTE, cons(sc, a, sc->NIL)),
+ sc->code);
} else {
- sc->code = sc->NIL;
- }
+ sc->code = cons(sc, sc->F, sc->code);
+ }
sc->code = cons(sc, mk_string(sc, str), sc->code);
setimmutable(car(sc->code));
sc->code = cons(sc, slot_value_in_env(x), sc->code);
# define BEGIN do {
# define END } while (0)
+\f
+
+/* Flags. The interpreter has a flags field. When the interpreter
+ * pushes a frame to the dump stack, it is encoded with the opcode.
+ * Therefore, we do not use the least significant byte. */
+
+/* Masks used to encode and decode opcode and flags. */
+#define S_OP_MASK 0x000000ff
+#define S_FLAG_MASK 0xffffff00
+
+/* Set if the interpreter evaluates an expression in a tail context
+ * (see R5RS, section 3.5). If a function, procedure, or continuation
+ * is invoked while this flag is set, the call is recorded as tail
+ * call in the history buffer. */
+#define S_FLAG_TAIL_CONTEXT 0x00000100
+
+/* Set flag F. */
+#define s_set_flag(sc, f) \
+ BEGIN \
+ (sc)->flags |= S_FLAG_ ## f; \
+ END
+
+/* Clear flag F. */
+#define s_clear_flag(sc, f) \
+ BEGIN \
+ (sc)->flags &= ~ S_FLAG_ ## f; \
+ END
+
+/* Check if flag F is set. */
+#define s_get_flag(sc, f) \
+ !!((sc)->flags & S_FLAG_ ## f)
+
+\f
+
/* Bounce back to Eval_Cycle and execute A. */
#define s_goto(sc,a) BEGIN \
sc->op = (int)(a); \
static pointer _s_return(scheme *sc, pointer a, int enable_gc) {
pointer dump = sc->dump;
pointer op;
+ unsigned long v;
sc->value = (a);
if (enable_gc)
gc_enable(sc);
if (dump == sc->NIL)
return sc->NIL;
free_cons(sc, dump, &op, &dump);
- sc->op = ivalue(op);
- free_cell(sc, op);
+ v = (unsigned long) ivalue_unchecked(op);
+ sc->op = (int) (v & S_OP_MASK);
+ sc->flags = v & S_FLAG_MASK;
+#ifdef USE_SMALL_INTEGERS
+ if (v < MAX_SMALL_INTEGER) {
+ /* This is a small integer, we must not free it. */
+ } else
+ /* Normal integer. Recover the cell. */
+#endif
+ free_cell(sc, op);
free_cons(sc, dump, &sc->args, &dump);
free_cons(sc, dump, &sc->envir, &dump);
free_cons(sc, dump, &sc->code, &sc->dump);
static void s_save(scheme *sc, enum scheme_opcodes op, pointer args, pointer code) {
#define s_save_allocates 5
pointer dump;
+ unsigned long v = sc->flags | ((unsigned long) op);
gc_disable(sc, gc_reservations (s_save));
dump = cons(sc, sc->envir, cons(sc, (code), sc->dump));
dump = cons(sc, (args), dump);
- sc->dump = cons(sc, mk_integer(sc, (long)(op)), dump);
+ sc->dump = cons(sc, mk_integer(sc, (long) v), dump);
gc_enable(sc);
}
mark(sc->dump);
}
+\f
+
+#if USE_HISTORY
+
+static void
+history_free(scheme *sc)
+{
+ sc->free(sc->history.m);
+ sc->history.tailstacks = sc->NIL;
+ sc->history.callstack = sc->NIL;
+}
+
+static pointer
+history_init(scheme *sc, size_t N, size_t M)
+{
+ size_t i;
+ struct history *h = &sc->history;
+
+ h->N = N;
+ h->mask_N = N - 1;
+ h->n = N - 1;
+ assert ((N & h->mask_N) == 0);
+
+ h->M = M;
+ h->mask_M = M - 1;
+ assert ((M & h->mask_M) == 0);
+
+ h->callstack = mk_vector(sc, N);
+ if (h->callstack == sc->sink)
+ goto fail;
+
+ h->tailstacks = mk_vector(sc, N);
+ for (i = 0; i < N; i++) {
+ pointer tailstack = mk_vector(sc, M);
+ if (tailstack == sc->sink)
+ goto fail;
+ set_vector_elem(h->tailstacks, i, tailstack);
+ }
+
+ h->m = sc->malloc(N * sizeof *h->m);
+ if (h->m == NULL)
+ goto fail;
+
+ for (i = 0; i < N; i++)
+ h->m[i] = 0;
+
+ return sc->T;
+
+fail:
+ history_free(sc);
+ return sc->F;
+}
+
+static void
+history_mark(scheme *sc)
+{
+ struct history *h = &sc->history;
+ mark(h->callstack);
+ mark(h->tailstacks);
+}
+
+#define add_mod(a, b, mask) (((a) + (b)) & (mask))
+#define sub_mod(a, b, mask) add_mod(a, (mask) + 1 - (b), mask)
+
+static INLINE void
+tailstack_clear(scheme *sc, pointer v)
+{
+ assert(is_vector(v));
+ /* XXX optimize */
+ fill_vector(v, sc->NIL);
+}
+
+static pointer
+callstack_pop(scheme *sc)
+{
+ struct history *h = &sc->history;
+ size_t n = h->n;
+ pointer item;
+
+ if (h->callstack == sc->NIL)
+ return sc->NIL;
+
+ item = vector_elem(h->callstack, n);
+ /* Clear our frame so that it can be gc'ed and we don't run into it
+ * when walking the history. */
+ set_vector_elem(h->callstack, n, sc->NIL);
+ tailstack_clear(sc, vector_elem(h->tailstacks, n));
+
+ /* Exit from the frame. */
+ h->n = sub_mod(h->n, 1, h->mask_N);
+
+ return item;
+}
+
+static void
+callstack_push(scheme *sc, pointer item)
+{
+ struct history *h = &sc->history;
+ size_t n = h->n;
+
+ if (h->callstack == sc->NIL)
+ return;
+
+ /* Enter a new frame. */
+ n = h->n = add_mod(n, 1, h->mask_N);
+
+ /* Initialize tail stack. */
+ tailstack_clear(sc, vector_elem(h->tailstacks, n));
+ h->m[n] = h->mask_M;
+
+ set_vector_elem(h->callstack, n, item);
+}
+
+static void
+tailstack_push(scheme *sc, pointer item)
+{
+ struct history *h = &sc->history;
+ size_t n = h->n;
+ size_t m = h->m[n];
+
+ if (h->callstack == sc->NIL)
+ return;
+
+ /* Enter a new tail frame. */
+ m = h->m[n] = add_mod(m, 1, h->mask_M);
+ set_vector_elem(vector_elem(h->tailstacks, n), m, item);
+}
+
+static pointer
+tailstack_flatten(scheme *sc, pointer tailstack, size_t i, size_t n,
+ pointer acc)
+{
+ struct history *h = &sc->history;
+ pointer frame;
+
+ assert(i <= h->M);
+ assert(n < h->M);
+
+ if (acc == sc->sink)
+ return sc->sink;
+
+ if (i == 0) {
+ /* We reached the end, but we did not see a unused frame. Signal
+ this using '... . */
+ return cons(sc, mk_symbol(sc, "..."), acc);
+ }
+
+ frame = vector_elem(tailstack, n);
+ if (frame == sc->NIL) {
+ /* A unused frame. We reached the end of the history. */
+ return acc;
+ }
+
+ /* Add us. */
+ acc = cons(sc, frame, acc);
+
+ return tailstack_flatten(sc, tailstack, i - 1, sub_mod(n, 1, h->mask_M),
+ acc);
+}
+
+static pointer
+callstack_flatten(scheme *sc, size_t i, size_t n, pointer acc)
+{
+ struct history *h = &sc->history;
+ pointer frame;
+
+ assert(i <= h->N);
+ assert(n < h->N);
+
+ if (acc == sc->sink)
+ return sc->sink;
+
+ if (i == 0) {
+ /* We reached the end, but we did not see a unused frame. Signal
+ this using '... . */
+ return cons(sc, mk_symbol(sc, "..."), acc);
+ }
+
+ frame = vector_elem(h->callstack, n);
+ if (frame == sc->NIL) {
+ /* A unused frame. We reached the end of the history. */
+ return acc;
+ }
+
+ /* First, emit the tail calls. */
+ acc = tailstack_flatten(sc, vector_elem(h->tailstacks, n), h->M, h->m[n],
+ acc);
+
+ /* Then us. */
+ acc = cons(sc, frame, acc);
+
+ return callstack_flatten(sc, i - 1, sub_mod(n, 1, h->mask_N), acc);
+}
+
+static pointer
+history_flatten(scheme *sc)
+{
+ struct history *h = &sc->history;
+ pointer history;
+
+ if (h->callstack == sc->NIL)
+ return sc->NIL;
+
+ history = callstack_flatten(sc, h->N, h->n, sc->NIL);
+ if (history == sc->sink)
+ return sc->sink;
+
+ return reverse_in_place(sc, sc->NIL, history);
+}
+
+#undef add_mod
+#undef sub_mod
+
+#else /* USE_HISTORY */
+
+#define history_init(SC, A, B) (void) 0
+#define history_free(SC) (void) 0
+#define callstack_pop(SC) (void) 0
+#define callstack_push(SC, X) (void) 0
+#define tailstack_push(SC, X) (void) 0
+
+#endif /* USE_HISTORY */
+
+\f
+
#define s_retbool(tf) s_return(sc,(tf) ? sc->T : sc->F)
static pointer opexe_0(scheme *sc, enum scheme_opcodes op) {
pointer x, y;
+ pointer callsite;
switch (op) {
CASE(OP_LOAD): /* load */
s_save(sc,OP_E0ARGS, sc->NIL, sc->code);
/* If no macros => s_save(sc,OP_E1ARGS, sc->NIL, cdr(sc->code));*/
sc->code = car(sc->code);
+ s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
}
} else {
sc->args = cons(sc,sc->code, sc->NIL);
gc_enable(sc);
sc->code = sc->value;
+ s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_APPLY);
} else {
- sc->code = cdr(sc->code);
+ gc_disable(sc, 1);
+ sc->args = cons(sc, sc->code, sc->NIL);
+ gc_enable(sc);
+ sc->code = cdr(sc->code);
s_thread_to(sc,OP_E1ARGS);
}
s_save(sc,OP_E1ARGS, sc->args, cdr(sc->code));
sc->code = car(sc->code);
sc->args = sc->NIL;
+ s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
} else { /* end */
sc->args = reverse_in_place(sc, sc->NIL, sc->args);
- sc->code = car(sc->args);
- sc->args = cdr(sc->args);
- s_thread_to(sc,OP_APPLY);
+ s_thread_to(sc,OP_APPLY_CODE);
}
#if USE_TRACING
}
#endif
+#if USE_HISTORY
+ CASE(OP_CALLSTACK_POP): /* pop the call stack */
+ callstack_pop(sc);
+ s_return(sc, sc->value);
+#endif
+
+ CASE(OP_APPLY_CODE): /* apply 'cadr(args)' to 'cddr(args)',
+ * record in the history as invoked from
+ * 'car(args)' */
+ free_cons(sc, sc->args, &callsite, &sc->args);
+ sc->code = car(sc->args);
+ sc->args = cdr(sc->args);
+ /* Fallthrough. */
+
CASE(OP_APPLY): /* apply 'code' to 'args' */
#if USE_TRACING
if(sc->tracing) {
/* fall through */
CASE(OP_REAL_APPLY):
#endif
+#if USE_HISTORY
+ if (op != OP_APPLY_CODE)
+ callsite = sc->code;
+ if (s_get_flag(sc, TAIL_CONTEXT)) {
+ /* We are evaluating a tail call. */
+ tailstack_push(sc, callsite);
+ } else {
+ callstack_push(sc, callsite);
+ s_save(sc, OP_CALLSTACK_POP, sc->NIL, sc->NIL);
+ }
+#endif
+
if (is_proc(sc->code)) {
s_goto(sc,procnum(sc->code)); /* PROCEDURE */
} else if (is_foreign(sc->code))
}
sc->code = cdr(closure_code(sc->code));
sc->args = sc->NIL;
+ s_set_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_BEGIN);
} else if (is_continuation(sc->code)) { /* CONTINUATION */
sc->dump = cont_dump(sc->code);
}
}
- CASE(OP_LAMBDA1):
- gc_disable(sc, 1);
- s_return_enable_gc(sc, mk_closure(sc, sc->value, sc->envir));
-
#else
CASE(OP_LAMBDA): /* lambda */
+ sc->value = sc->code;
+ /* Fallthrough. */
+#endif
+
+ CASE(OP_LAMBDA1):
gc_disable(sc, 1);
- s_return_enable_gc(sc, mk_closure(sc, sc->code, sc->envir));
+ s_return_enable_gc(sc, mk_closure(sc, sc->value, sc->envir));
-#endif
CASE(OP_MKCLOSURE): /* make-closure */
x=car(sc->args);
CASE(OP_BEGIN): /* begin */
- if (!is_pair(sc->code)) {
- s_return(sc,sc->code);
- }
- if (cdr(sc->code) != sc->NIL) {
- s_save(sc,OP_BEGIN, sc->NIL, cdr(sc->code));
- }
- sc->code = car(sc->code);
- s_thread_to(sc,OP_EVAL);
+ {
+ int last;
+
+ if (!is_pair(sc->code)) {
+ s_return(sc,sc->code);
+ }
+
+ last = cdr(sc->code) == sc->NIL;
+ if (!last) {
+ s_save(sc,OP_BEGIN, sc->NIL, cdr(sc->code));
+ }
+ sc->code = car(sc->code);
+ if (! last)
+ /* This is not the end of the list. This is not a tail
+ * position. */
+ s_clear_flag(sc, TAIL_CONTEXT);
+ s_thread_to(sc,OP_EVAL);
+ }
CASE(OP_IF0): /* if */
s_save(sc,OP_IF1, sc->NIL, cdr(sc->code));
sc->code = car(sc->code);
+ s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
CASE(OP_IF1): /* if */
gc_enable(sc);
sc->code = cadar(sc->code);
sc->args = sc->NIL;
+ s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
} else { /* end */
gc_enable(sc);
}
s_save(sc,OP_LET1AST, cdr(sc->code), car(sc->code));
sc->code = cadaar(sc->code);
+ s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
CASE(OP_LET1AST): /* let* (make new frame) */
s_save(sc,OP_LET2AST, sc->args, sc->code);
sc->code = cadar(sc->code);
sc->args = sc->NIL;
+ s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
} else { /* end */
sc->code = sc->args;
s_save(sc,OP_LET1REC, sc->args, cdr(sc->code));
sc->code = cadar(sc->code);
sc->args = sc->NIL;
+ s_clear_flag(sc, TAIL_CONTEXT);
s_goto(sc,OP_EVAL);
} else { /* end */
sc->args = reverse_in_place(sc, sc->NIL, sc->args);
}
s_save(sc,OP_COND1, sc->NIL, sc->code);
sc->code = caar(sc->code);
+ s_clear_flag(sc, TAIL_CONTEXT);
s_goto(sc,OP_EVAL);
CASE(OP_COND1): /* cond */
} else {
s_save(sc,OP_COND1, sc->NIL, sc->code);
sc->code = caar(sc->code);
+ s_clear_flag(sc, TAIL_CONTEXT);
s_goto(sc,OP_EVAL);
}
}
s_return(sc,sc->T);
}
s_save(sc,OP_AND1, sc->NIL, cdr(sc->code));
+ if (cdr(sc->code) != sc->NIL)
+ s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_goto(sc,OP_EVAL);
s_return(sc,sc->value);
} else {
s_save(sc,OP_AND1, sc->NIL, cdr(sc->code));
+ if (cdr(sc->code) != sc->NIL)
+ s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_goto(sc,OP_EVAL);
}
s_return(sc,sc->F);
}
s_save(sc,OP_OR1, sc->NIL, cdr(sc->code));
+ if (cdr(sc->code) != sc->NIL)
+ s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_goto(sc,OP_EVAL);
s_return(sc,sc->value);
} else {
s_save(sc,OP_OR1, sc->NIL, cdr(sc->code));
+ if (cdr(sc->code) != sc->NIL)
+ s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_goto(sc,OP_EVAL);
}
CASE(OP_CASE0): /* case */
s_save(sc,OP_CASE1, sc->NIL, cdr(sc->code));
sc->code = car(sc->code);
+ s_clear_flag(sc, TAIL_CONTEXT);
s_goto(sc,OP_EVAL);
CASE(OP_CASE1): /* case */
return sc->T;
}
+#if USE_PLIST
+static pointer
+get_property(scheme *sc, pointer obj, pointer key)
+{
+ pointer x;
+
+ assert (is_symbol(obj));
+ assert (is_symbol(key));
+
+ for (x = symprop(obj); x != sc->NIL; x = cdr(x)) {
+ if (caar(x) == key)
+ break;
+ }
+
+ if (x != sc->NIL)
+ return cdar(x);
+
+ return sc->NIL;
+}
+
+static pointer
+set_property(scheme *sc, pointer obj, pointer key, pointer value)
+{
+#define set_property_allocates 2
+ pointer x;
+
+ assert (is_symbol(obj));
+ assert (is_symbol(key));
+
+ for (x = symprop(obj); x != sc->NIL; x = cdr(x)) {
+ if (caar(x) == key)
+ break;
+ }
+
+ if (x != sc->NIL)
+ cdar(x) = value;
+ else {
+ gc_disable(sc, gc_reservations(set_property));
+ symprop(obj) = cons(sc, cons(sc, key, value), symprop(obj));
+ gc_enable(sc);
+ }
+
+ return sc->T;
+}
+#endif
+
static pointer opexe_2(scheme *sc, enum scheme_opcodes op) {
pointer x;
num v;
}
CASE(OP_REVERSE): /* reverse */
- s_return(sc,reverse(sc, car(sc->args)));
+ s_return(sc,reverse(sc, sc->NIL, car(sc->args)));
CASE(OP_LIST_STAR): /* list* */
s_return(sc,list_star(sc,sc->args));
s_return(sc, reverse_in_place(sc, car(y), x));
#if USE_PLIST
- CASE(OP_PUT): /* put */
- if (!hasprop(car(sc->args)) || !hasprop(cadr(sc->args))) {
- Error_0(sc,"illegal use of put");
- }
- for (x = symprop(car(sc->args)), y = cadr(sc->args); x != sc->NIL; x = cdr(x)) {
- if (caar(x) == y) {
- break;
- }
- }
- if (x != sc->NIL)
- cdar(x) = caddr(sc->args);
- else
- symprop(car(sc->args)) = cons(sc, cons(sc, y, caddr(sc->args)),
- symprop(car(sc->args)));
- s_return(sc,sc->T);
+ CASE(OP_SET_SYMBOL_PROPERTY): /* set-symbol-property! */
+ gc_disable(sc, gc_reservations(set_property));
+ s_return_enable_gc(sc,
+ set_property(sc, car(sc->args),
+ cadr(sc->args), caddr(sc->args)));
- CASE(OP_GET): /* get */
- if (!hasprop(car(sc->args)) || !hasprop(cadr(sc->args))) {
- Error_0(sc,"illegal use of get");
- }
- for (x = symprop(car(sc->args)), y = cadr(sc->args); x != sc->NIL; x = cdr(x)) {
- if (caar(x) == y) {
- break;
- }
- }
- if (x != sc->NIL) {
- s_return(sc,cdar(x));
- } else {
- s_return(sc,sc->NIL);
- }
+ CASE(OP_SYMBOL_PROPERTY): /* symbol-property */
+ s_return(sc, get_property(sc, car(sc->args), cadr(sc->args)));
#endif /* USE_PLIST */
+
+#if USE_TAGS
+ CASE(OP_TAG_VALUE): { /* not exposed */
+ /* This tags sc->value with car(sc->args). Useful to tag
+ * results of opcode evaluations. */
+ pointer a, b, c;
+ free_cons(sc, sc->args, &a, &b);
+ free_cons(sc, b, &b, &c);
+ assert(c == sc->NIL);
+ s_return(sc, mk_tagged_value(sc, sc->value, a, b));
+ }
+
+ CASE(OP_MK_TAGGED): /* make-tagged-value */
+ if (is_vector(car(sc->args)))
+ Error_0(sc, "cannot tag vector");
+ s_return(sc, mk_tagged_value(sc, car(sc->args),
+ car(cadr(sc->args)),
+ cdr(cadr(sc->args))));
+
+ CASE(OP_GET_TAG): /* get-tag */
+ s_return(sc, get_tag(sc, car(sc->args)));
+#endif /* USE_TAGS */
+
CASE(OP_QUIT): /* quit */
if(is_pair(sc->args)) {
sc->retcode=ivalue(car(sc->args));
Error_0(sc,"syntax error: illegal dot expression");
} else {
sc->nesting_stack[sc->file_i]++;
+#if USE_TAGS && SHOW_ERROR_LINE
+ {
+ const char *filename =
+ sc->load_stack[sc->file_i].rep.stdio.filename;
+ int lineno =
+ sc->load_stack[sc->file_i].rep.stdio.curr_line;
+
+ s_save(sc, OP_TAG_VALUE,
+ cons(sc, mk_string(sc, filename),
+ cons(sc, mk_integer(sc, lineno), sc->NIL)),
+ sc->NIL);
+ }
+#endif
s_save(sc,OP_RDLIST, sc->NIL, sc->NIL);
s_thread_to(sc,OP_RDSEXPR);
}
s_retbool(is_closure(car(sc->args)));
CASE(OP_MACROP): /* macro? */
s_retbool(is_macro(car(sc->args)));
+ CASE(OP_VM_HISTORY): /* *vm-history* */
+ s_return(sc, history_flatten(sc));
default:
snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", sc->op);
Error_0(sc,sc->strbuff);
sc->T = &sc->_HASHT;
sc->F = &sc->_HASHF;
sc->EOF_OBJ=&sc->_EOF_OBJ;
+
+#if USE_SMALL_INTEGERS
+ if (initialize_small_integers(sc)) {
+ sc->no_memory=1;
+ return 0;
+ }
+#endif
+
sc->free_cell = &sc->_NIL;
sc->fcells = 0;
sc->inhibit_gc = GC_ENABLED;
dump_stack_initialize(sc);
sc->code = sc->NIL;
sc->tracing=0;
+ sc->op = -1;
+ sc->flags = 0;
/* init sc->NIL */
typeflag(sc->NIL) = (T_NIL | T_ATOM | MARK);
car(sc->EOF_OBJ) = cdr(sc->EOF_OBJ) = sc->EOF_OBJ;
/* init sink */
typeflag(sc->sink) = (T_SINK | T_PAIR | MARK);
- car(sc->sink) = sc->NIL;
+ car(sc->sink) = cdr(sc->sink) = sc->NIL;
/* init c_nest */
sc->c_nest = sc->NIL;
}
}
+ history_init(sc, 8, 8);
+
/* initialization of global pointers to special symbols */
sc->LAMBDA = mk_symbol(sc, "lambda");
sc->QUOTE = mk_symbol(sc, "quote");
dump_stack_free(sc);
sc->envir=sc->NIL;
sc->code=sc->NIL;
+ history_free(sc);
sc->args=sc->NIL;
sc->value=sc->NIL;
if(is_port(sc->inport)) {
sc->gc_verbose=0;
gc(sc,sc->NIL,sc->NIL);
+#if USE_SMALL_INTEGERS
+ sc->free(sc->integer_alloc);
+#endif
+
for(i=0; i<=sc->last_cell_seg; i++) {
sc->free(sc->alloc_seg[i]);
}
# define USE_COMPILE_HOOK 0
# define USE_DL 0
# define USE_PLIST 0
+# define USE_SMALL_INTEGERS 0
+# define USE_TAGS 0
+# define USE_HISTORY 0
#endif
# define USE_PLIST 0
#endif
+/* If set, then every object can be tagged. */
+#ifndef USE_TAGS
+# define USE_TAGS 1
+#endif
+
+/* Keep a history of function calls. This enables a feature similar
+ * to stack traces. */
+#ifndef USE_HISTORY
+# define USE_HISTORY 1
+#endif
+
/* To force system errors through user-defined error handling (see *error-hook*) */
#ifndef USE_ERROR_HOOK
# define USE_ERROR_HOOK 1
# define USE_THREADED_CODE 1
#endif
+/* Use a static set of cells to represent small numbers. This set
+ * notably includes all opcodes, and hence saves a cell reservation
+ * during 's_save'. */
+#ifndef USE_SMALL_INTEGERS
+# define USE_SMALL_INTEGERS 1
+#endif
+
#ifndef USE_STRCASECMP /* stricmp for Unix */
# define USE_STRCASECMP 0
#endif
(if (> (*verbose*) 0)
(apply info msg)))
-(define (error . msg)
+(define (fail . msg)
(apply info msg)
(exit 1))
(flush-stdio)))
(set! *progress-nesting* (- *progress-nesting* 1)))
-(define (for-each-p msg proc lst)
- (for-each-p' msg proc (lambda (x) x) lst))
+(define (for-each-p msg proc lst . lsts)
+ (apply for-each-p' `(,msg ,proc ,(lambda (x . xs) x) ,lst ,@lsts)))
-(define (for-each-p' msg proc fmt lst)
+(define (for-each-p' msg proc fmt lst . lsts)
(call-with-progress
msg
(lambda (progress)
- (for-each (lambda (a)
- (progress (fmt a))
- (proc a))
- lst))))
+ (apply for-each
+ `(,(lambda args
+ (progress (apply fmt args))
+ (apply proc args))
+ ,lst ,@lsts)))))
;; Process management.
(define CLOSED_FD -1)
(let ((result (call-with-io what "")))
(if (= 0 (:retcode result))
(:stdout result)
- (throw (list what "failed:" (:stderr result))))))
+ (throw (string-append (stringify what) " failed")
+ (:stderr result)))))
(define (call-popen command input-string)
(let ((result (call-with-io command input-string)))
(assert (string=? (path-join "foo" "bar" "baz") "foo/bar/baz"))
(assert (string=? (path-join "" "bar" "baz") "bar/baz"))
+;; Is PATH an absolute path?
+(define (absolute-path? path)
+ (or (char=? #\/ (string-ref path 0))
+ (and *win32* (char=? #\\ (string-ref path 0)))
+ (and *win32*
+ (char-alphabetic? (string-ref path 0))
+ (char=? #\: (string-ref path 1))
+ (or (char=? #\/ (string-ref path 2))
+ (char=? #\\ (string-ref path 2))))))
+
+;; Make PATH absolute.
(define (canonical-path path)
- (if (or (char=? #\/ (string-ref path 0))
- (and *win32* (char=? #\\ (string-ref path 0)))
- (and *win32*
- (char-alphabetic? (string-ref path 0))
- (char=? #\: (string-ref path 1))
- (or (char=? #\/ (string-ref path 2))
- (char=? #\\ (string-ref path 2)))))
- path
- (path-join (getcwd) path)))
+ (if (absolute-path? path) path (path-join (getcwd) path)))
(define (in-srcdir . names)
(canonical-path (apply path-join (cons (getenv "srcdir") names))))
(let ((,result-sym
,(if (= 1 (length (cadr form)))
`(catch (begin (close ,(caaadr form))
- (apply throw *error*))
+ (rethrow *error*))
,@(cddr form))
`(letfd ,(cdadr form) ,@(cddr form)))))
(close ,(caaadr form))
`(let* ((,cwd-sym (getcwd))
(_ (if ,(cadr form) (chdir ,(cadr form))))
(,result-sym (catch (begin (chdir ,cwd-sym)
- (apply throw *error*))
+ (rethrow *error*))
,@(cddr form))))
(chdir ,cwd-sym)
,result-sym)))
(_ (chdir ,tmp-sym))
(,result-sym (catch (begin (chdir ,cwd-sym)
(unlink-recursively ,tmp-sym)
- (apply throw *error*))
+ (rethrow *error*))
,@(cdr form))))
(chdir ,cwd-sym)
(unlink-recursively ,tmp-sym)
(let ((,result-sym
,(if (= 1 (length (cadr form)))
`(catch (begin (remove-temporary-file ,(caadr form))
- (apply throw *error*))
+ (rethrow *error*))
,@(cddr form))
`(lettmp ,(cdadr form) ,@(cddr form)))))
(remove-temporary-file ,(caadr form))
(lettmp (sink)
(transformer source sink)
(if (not (file=? source sink))
- (error "mismatch"))))
+ (fail "mismatch"))))
;;
;; Monadic pipe support.
(define (tr:spawn input command)
(lambda (tmpfiles source)
(if (and (member '**in** command) (not source))
- (error (string-append (stringify cmd) " needs an input")))
+ (fail (string-append (stringify cmd) " needs an input")))
(let* ((t (make-temporary-file))
(cmd (map (lambda (x)
(cond
(catch (list (cons t tmpfiles) t *error*)
(call-popen cmd input)
(if (and (member '**out** command) (not (file-exists? t)))
- (error (string-append (stringify cmd)
+ (fail (string-append (stringify cmd)
" did not produce '" t "'.")))
(list (cons t tmpfiles) t #f)))))
(define (tr:assert-identity reference)
(lambda (tmpfiles source)
(if (not (file=? source reference))
- (error "mismatch"))
+ (fail "mismatch"))
(list tmpfiles source #f)))
(define (tr:assert-weak-identity reference)
(lambda (tmpfiles source)
(if (not (text-file=? source reference))
- (error "mismatch"))
+ (fail "mismatch"))
(list tmpfiles source #f)))
(define (tr:call-with-content function . args)
;; Spawn an os shell.
(define (interactive-shell)
- (call-with-fds `(,(getenv "SHELL")) 0 1 2))
+ (call-with-fds `(,(getenv "SHELL") -i) 0 1 2))
+
+;;
+;; The main test framework.
+;;
+
+;; A pool of tests.
+(define test-pool
+ (package
+ (define (new procs)
+ (package
+ (define (add test)
+ (new (cons test procs)))
+ (define (wait)
+ (let ((unfinished (filter (lambda (t) (not t::retcode)) procs)))
+ (if (null? unfinished)
+ (package)
+ (let* ((names (map (lambda (t) t::name) unfinished))
+ (pids (map (lambda (t) t::pid) unfinished))
+ (results
+ (map (lambda (pid retcode) (list pid retcode))
+ pids
+ (wait-processes (map stringify names) pids #t))))
+ (new
+ (map (lambda (t)
+ (if t::retcode
+ t
+ (t::set-retcode (cadr (assoc t::pid results)))))
+ procs))))))
+ (define (passed)
+ (filter (lambda (p) (= 0 p::retcode)) procs))
+ (define (skipped)
+ (filter (lambda (p) (= 77 p::retcode)) procs))
+ (define (hard-errored)
+ (filter (lambda (p) (= 99 p::retcode)) procs))
+ (define (failed)
+ (filter (lambda (p)
+ (not (or (= 0 p::retcode) (= 77 p::retcode)
+ (= 99 p::retcode))))
+ procs))
+ (define (report)
+ (define (print-tests tests message)
+ (unless (null? tests)
+ (apply echo (cons message
+ (map (lambda (t) t::name) tests)))))
+
+ (let ((failed' (failed)) (skipped' (skipped)))
+ (echo (length procs) "tests run,"
+ (length (passed)) "succeeded,"
+ (length failed') "failed,"
+ (length skipped') "skipped.")
+ (print-tests failed' "Failed tests:")
+ (print-tests skipped' "Skipped tests:")
+ (length failed')))))))
+
+(define (verbosity n)
+ (if (= 0 n) '() (cons '--verbose (verbosity (- n 1)))))
+
+(define (locate-test path)
+ (if (absolute-path? path) path (in-srcdir path)))
+
+;; A single test.
+(define test
+ (package
+ (define (scm name path . args)
+ ;; Start the process.
+ (define (spawn-scm args' in out err)
+ (spawn-process-fd `(,*argv0* ,@(verbosity (*verbose*))
+ ,(locate-test path)
+ ,@args' ,@args) in out err))
+ (new name #f spawn-scm #f #f CLOSED_FD))
+
+ (define (binary name path . args)
+ ;; Start the process.
+ (define (spawn-binary args' in out err)
+ (spawn-process-fd `(,path ,@args' ,@args) in out err))
+ (new name #f spawn-binary #f #f CLOSED_FD))
+
+ (define (new name directory spawn pid retcode logfd)
+ (package
+ (define (set-directory x)
+ (new name x spawn pid retcode logfd))
+ (define (set-retcode x)
+ (new name directory spawn pid x logfd))
+ (define (set-pid x)
+ (new name directory spawn x retcode logfd))
+ (define (set-logfd x)
+ (new name directory spawn pid retcode x))
+ (define (open-log-file)
+ (let ((filename (string-append (basename name) ".log")))
+ (catch '() (unlink filename))
+ (open filename (logior O_RDWR O_BINARY O_CREAT) #o600)))
+ (define (run-sync . args)
+ (letfd ((log (open-log-file)))
+ (with-working-directory directory
+ (let* ((p (inbound-pipe))
+ (pid (spawn args 0 (:write-end p) (:write-end p))))
+ (close (:write-end p))
+ (splice (:read-end p) STDERR_FILENO log)
+ (close (:read-end p))
+ (let ((t' (set-retcode (wait-process name pid #t))))
+ (t'::report)
+ t')))))
+ (define (run-sync-quiet . args)
+ (with-working-directory directory
+ (set-retcode
+ (wait-process
+ name (spawn args CLOSED_FD CLOSED_FD CLOSED_FD) #t))))
+ (define (run-async . args)
+ (let ((log (open-log-file)))
+ (with-working-directory directory
+ (new name directory spawn
+ (spawn args CLOSED_FD log log)
+ retcode log))))
+ (define (status)
+ (let ((t (assoc retcode '((0 "PASS") (77 "SKIP") (99 "ERROR")))))
+ (if (not t) "FAIL" (cadr t))))
+ (define (report)
+ (unless (= logfd CLOSED_FD)
+ (seek logfd 0 SEEK_SET)
+ (splice logfd STDERR_FILENO)
+ (close logfd))
+ (echo (string-append (status retcode) ":") name))))))
+
+;; Run the setup target to create an environment, then run all given
+;; tests in parallel.
+(define (run-tests-parallel setup tests)
+ (lettmp (gpghome-tar)
+ (setup::run-sync '--create-tarball gpghome-tar)
+ (let loop ((pool (test-pool::new '())) (tests' tests))
+ (if (null? tests')
+ (let ((results (pool::wait)))
+ (for-each (lambda (t)
+ (catch (echo "Removing" t::directory "failed:" *error*)
+ (unlink-recursively t::directory))
+ (t::report)) (reverse results::procs))
+ (exit (results::report)))
+ (let* ((wd (mkdtemp))
+ (test (car tests'))
+ (test' (test::set-directory wd)))
+ (loop (pool::add (test'::run-async '--unpack-tarball gpghome-tar))
+ (cdr tests')))))))
+
+;; Run the setup target to create an environment, then run all given
+;; tests in sequence.
+(define (run-tests-sequential setup tests)
+ (lettmp (gpghome-tar)
+ (setup::run-sync '--create-tarball gpghome-tar)
+ (let loop ((pool (test-pool::new '())) (tests' tests))
+ (if (null? tests')
+ (let ((results (pool::wait)))
+ (for-each (lambda (t)
+ (catch (echo "Removing" t::directory "failed:" *error*)
+ (unlink-recursively t::directory)))
+ results::procs)
+ (exit (results::report)))
+ (let* ((wd (mkdtemp))
+ (test (car tests'))
+ (test' (test::set-directory wd)))
+ (loop (pool::add (test'::run-sync '--unpack-tarball gpghome-tar))
+ (cdr tests')))))))
# Programs required before we can run these tests.
required_pgms = ../../g10/gpg$(EXEEXT) ../../agent/gpg-agent$(EXEEXT) \
- ../../tools/gpgtar$(EXEEXT)
+ ../../tools/gpgtar$(EXEEXT) \
+ ../gpgscm/gpgscm$(EXEEXT)
AM_CPPFLAGS = -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
TMP ?= /tmp
TESTS_ENVIRONMENT = GPG_AGENT_INFO= LC_ALL=C \
+ EXEEXT=$(EXEEXT) \
PATH=../gpgscm:$(PATH) \
TMP=$(TMP) \
- GPGSCM_PATH=$(top_srcdir)/tests/gpgscm:$(top_srcdir)/tests/migrations
+ srcdir=$(abs_srcdir) \
+ objdir=$(abs_top_builddir) \
+ GPGSCM_PATH=$(abs_top_srcdir)/tests/gpgscm:$(abs_top_srcdir)/tests/migrations
-TESTS = from-classic.scm \
+XTESTS = from-classic.scm \
extended-pkf.scm \
issue2276.scm
extended-pkf.tar.asc \
issue2276.tar.asc
-EXTRA_DIST = common.scm $(TESTS) $(TEST_FILES)
+# XXX: Currently, one cannot override automake's 'check' target. As a
+# workaround, we avoid defining 'TESTS', thus automake will not emit
+# the 'check' target. For extra robustness, we merely define a
+# dependency on 'xcheck', so this hack should also work even if
+# automake would emit the 'check' target, as adding dependencies to
+# targets is okay.
+check: xcheck
-CLEANFILES = prepared.stamp x y yy z out err $(data_files) \
- plain-1 plain-2 plain-3 trustdb.gpg *.lock .\#lk* \
- *.test.log gpg_dearmor gpg.conf gpg-agent.conf S.gpg-agent \
- pubring.gpg pubring.gpg~ pubring.kbx pubring.kbx~ \
- secring.gpg pubring.pkr secring.skr \
- gnupg-test.stop random_seed gpg-agent.log tofu.db
+.PHONY: xcheck
+xcheck:
+ $(TESTS_ENVIRONMENT) $(abs_top_builddir)/tests/gpgscm/gpgscm \
+ run-tests.scm $(TESTFLAGS) $(XTESTS)
-clean-local:
- -rm -rf from-classic.gpghome/*.gpg
+EXTRA_DIST = common.scm run-tests.scm setup.scm $(XTESTS) $(TEST_FILES)
+
+CLEANFILES = *.log
# We need to depend on a couple of programs so that the tests don't
# start before all programs are built.
(if (string=? "" (getenv "srcdir"))
(error "not called from make"))
-(setenv "GNUPGHOME" "" #t)
+(let ((verbose (string->number (getenv "verbose"))))
+ (if (number? verbose)
+ (*set-verbose!* verbose)))
(define (qualify executable)
(string-append executable (getenv "EXEEXT")))
;; We may not use a relative name for gpg-agent.
-(define GPG-AGENT (qualify (string-append (getcwd) "/../../agent/gpg-agent")))
-(define GPG `(,(qualify (string-append (getcwd) "/../../g10/gpg"))
+(define GPG-AGENT (path-join (getenv "objdir") "agent" (qualify "gpg-agent")))
+(define GPG `(,(path-join (getenv "objdir") "g10" (qualify "gpg"))
--no-permission-warning --no-greeting
--no-secmem-warning --batch
,(string-append "--agent-program=" GPG-AGENT
(define GPG-no-batch
(filter (lambda (arg) (not (equal? arg '--batch))) GPG))
-(define GPGTAR (qualify (string-append (getcwd) "/../../tools/gpgtar")))
+(define GPGTAR (path-join (getenv "objdir") "tools" (qualify "gpgtar")))
(define (untar-armored source-name)
(pipe:do
--- /dev/null
+;; Test-suite runner.
+;;
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(let* ((tests (filter (lambda (arg) (not (string-prefix? arg "--"))) *args*))
+ (runner (if (and (member "--parallel" *args*)
+ (> (length tests) 1))
+ run-tests-parallel
+ run-tests-sequential)))
+ (runner (test::scm "setup.scm" "setup.scm")
+ (map (lambda (t) (test::scm t t)) tests)))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+;; Nothing to do for now.
(if (= 0 (call `(,@GPG --list-packets ,(in-srcdir "4gb-packet.asc"))))
(info "Can parse 4GB packets.")
- (error "Failed to parse 4GB packet."))
+ (fail "Failed to parse 4GB packet."))
XTESTS = \
version.scm \
+ enarmor.scm \
mds.scm \
decrypt.scm \
+ decrypt-multifile.scm \
decrypt-dsa.scm \
sigs.scm \
sigs-dsa.scm \
encrypt.scm \
+ encrypt-multifile.scm \
encrypt-dsa.scm \
+ compression.scm \
seat.scm \
clearsig.scm \
encryptp.scm \
conventional-mdc.scm \
multisig.scm \
verify.scm \
+ verify-multifile.scm \
gpgv-forged-keyring.scm \
armor.scm \
import.scm \
+ import-revocation-certificate.scm \
ecc.scm \
4gb-packet.scm \
tofu.scm \
use-exact-key.scm \
default-key.scm \
export.scm \
- ssh.scm \
+ ssh-import.scm \
+ ssh-export.scm \
quick-key-manipulation.scm \
key-selection.scm \
+ delete-keys.scm \
issue2015.scm \
issue2346.scm \
issue2417.scm \
gpg.conf.tmpl gpg-agent.conf.tmpl \
bug537-test.data.asc bug894-test.asc \
bug1223-good.asc bug1223-bogus.asc 4gb-packet.asc \
- tofu-keys.asc tofu-keys-secret.asc \
- tofu-2183839A-1.txt tofu-BC15C85A-1.txt tofu-EE37CF96-1.txt \
+ tofu/conflicting/1C005AF3.gpg \
+ tofu/conflicting/1C005AF3-secret.gpg \
+ tofu/conflicting/1C005AF3-1.txt \
+ tofu/conflicting/1C005AF3-2.txt \
+ tofu/conflicting/1C005AF3-3.txt \
+ tofu/conflicting/1C005AF3-4.txt \
+ tofu/conflicting/1C005AF3-5.txt \
+ tofu/conflicting/B662E42F.gpg \
+ tofu/conflicting/B662E42F-secret.gpg \
+ tofu/conflicting/B662E42F-1.txt \
+ tofu/conflicting/B662E42F-2.txt \
+ tofu/conflicting/B662E42F-3.txt \
+ tofu/conflicting/B662E42F-4.txt \
+ tofu/conflicting/B662E42F-5.txt \
+ tofu/conflicting/BE04EB2B.gpg \
+ tofu/conflicting/BE04EB2B-secret.gpg \
+ tofu/conflicting/BE04EB2B-1.txt \
+ tofu/conflicting/BE04EB2B-2.txt \
+ tofu/conflicting/BE04EB2B-3.txt \
+ tofu/conflicting/BE04EB2B-4.txt \
+ tofu/conflicting/BE04EB2B-5.txt \
tofu/cross-sigs/EC38277E-secret.gpg \
tofu/cross-sigs/EC38277E-1.gpg \
tofu/cross-sigs/EC38277E-1.txt \
samplekeys/ssh-ecdsa.key \
samplekeys/ssh-ed25519.key \
samplekeys/ssh-rsa.key \
- samplekeys/issue2346.gpg
-
-sample_msgs = samplemsgs/issue2419.asc
+ samplekeys/issue2346.gpg \
+ samplekeys/authenticate-only.pub.asc \
+ samplekeys/authenticate-only.sec.asc
+
+sample_msgs = samplemsgs/issue2419.asc \
+ samplemsgs/clearsig-1-key-1.asc \
+ samplemsgs/signed-1-key-1.asc \
+ samplemsgs/revoke-2D727CC768697734.asc
EXTRA_DIST = defs.scm $(XTESTS) $(TEST_FILES) \
mkdemodirs signdemokey $(priv_keys) $(sample_keys) \
;; Otherwise, we do check that we recover the original file.
check-identity)
source
- (check-signing '(--passphrase-fd "0" --clearsign) usrpass1)))
+ (check-signing '(--passphrase-fd "0" --clear-sign) usrpass1)))
(append plain-files '("plain-large")))
;; The test vectors are lists of length three, containing
(with-output-to-file tmp (lambda () (display (:string vec))))
((if (:check-equality vec) check-identity check-execution)
tmp
- (check-signing `(--passphrase-fd "0" --clearsign ,@(:options vec))
+ (check-signing `(--passphrase-fd "0" --clear-sign ,@(:options vec))
usrpass1))))
(lambda (vec) (counter))
vectors))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-legacy-environment)
+
+(for-each-p
+ "Checking encryption using a specific compression algorithm"
+ (lambda (compression)
+ (for-each-p
+ ""
+ (lambda (source)
+ (tr:do
+ (tr:open source)
+ (tr:gpg "" `(--yes --encrypt --recipient ,usrname2
+ --compress-algo ,compression))
+ (tr:gpg "" '(--yes))
+ (tr:assert-identity source)))
+ (append plain-files data-files)))
+ (force all-compression-algos))
(tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k))
(tr:assert-identity source)))
'("plain-1" "data-80000")))
- all-cipher-algos)
+ (force all-cipher-algos))
(for-each-p
"Checking sign+symencrypt"
(tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k))
(tr:assert-identity source)))
'("plain-1" "data-80000")))
- all-cipher-algos)
+ (force all-cipher-algos))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-legacy-environment)
+
+(info "Checking decryption of supplied files using --multifile.")
+
+(define my-wd (getcwd))
+(define encrypted-files (map (lambda (name)
+ (string-append name ".asc"))
+ plain-files))
+
+(with-temporary-working-directory
+ ;; First, copy the files so that GnuPG writes the decrypted files here
+ ;; and not into the source directory.
+ (for-each (lambda (name)
+ (file-copy (in-srcdir name) name))
+ encrypted-files)
+
+ ;; Now decrypt all files.
+ (call-check `(,@gpg --decrypt --multifile ,@encrypted-files))
+
+ ;; And verify the result. Reference files are in our original
+ ;; working directory courtesy of setup-legacy-environment.
+ (for-each-p
+ "Verifying files:"
+ (lambda (name)
+ (unless (file=? (path-join my-wd name) name)
+ (fail "decrypted file differs")))
+ plain-files))
;; first and then search for the encryption subkey.)
(define dsa-usrname2 "0xCB879DE9")
+(define keys
+ (package
+ (define (new fpr grip uids subkeys)
+ (package))
+ (define (subkey fpr grip)
+ (package))
+ (define alfa (new "A0FF4590BB6122EDEF6E3C542D727CC768697734"
+ "76F7E2B35832976B50A27A282D9B87E44577EB66"
+ '("alfa@example.net" "alpha@example.net")
+ (list
+ (subkey "3B3FBC948FE59301ED629EFB6AE6D7EE46A871F8"
+ "A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD"))))
+ (define one (new "289B0EF1D105E124B6F626020EF77096D74C5F22"
+ "50B2D4FA4122C212611048BC5FC31BD44393626E"
+ '("one@example.com")
+ (list
+ (subkey "EB467DCA4AD7676A6A62B2ABABAB28A247BE2775"
+ "7E201E28B6FEB2927B321F443205F4724EBE637E"))))
+ (define two (new "C1DEBB34EA8B71009EAFA474973D50E1C40FDECF"
+ "343D8AF79796EE107D645A2787A9D9252F924E6F"
+ '("two@example.com")
+ (list
+ (subkey "CD3D0F5701CBFCACB2A4907305A37887B27907AA"
+ "8B5ABF3EF9EB8D96B91A0B8C2C4401C91C834C34"))))))
+
(define key-file1 "samplekeys/rsa-rsa-sample-1.asc")
(define key-file2 "samplekeys/ed25519-cv25519-sample-1.asc")
(define data-files '("data-500" "data-9000" "data-32000" "data-80000"))
(define exp-files '())
+(let ((verbose (string->number (getenv "verbose"))))
+ (if (number? verbose)
+ (*set-verbose!* verbose)))
+
(define (qualify executable)
(string-append executable (getenv "EXEEXT")))
value)))
(define tools
- '((gpg "GPG" "g10/gpg")
- (gpgv "GPGV" "g10/gpgv")
- (gpg-agent "GPG_AGENT" "agent/gpg-agent")
+ '((gpgv "GPGV" "g10/gpgv")
(gpg-connect-agent "GPG_CONNECT_AGENT" "tools/gpg-connect-agent")
(gpgconf "GPGCONF" "tools/gpgconf")
(gpg-preset-passphrase "GPG_PRESET_PASSPHRASE"
(gpg-zip "GPGZIP" "tools/gpg-zip")
(pinentry "PINENTRY" "tests/openpgp/fake-pinentry")))
-(define (tool which)
+(define (tool-hardcoded which)
(let ((t (assoc which tools))
(prefix (getenv "BIN_PREFIX")))
(getenv' (cadr t)
(string-append (getenv "objdir") "/" (caddr t))
(string-append prefix "/" (basename (caddr t))))))))
+(define (gpg-conf . args)
+ (let ((s (call-popen `(,(tool-hardcoded 'gpgconf) ,@args) "")))
+ (map (lambda (line) (string-split line #\:))
+ (string-split-newlines s))))
+(define :gc:c:name car)
+(define :gc:c:description cadr)
+(define :gc:c:pgmname caddr)
-(define have-opt-always-trust
+(setenv "GNUPG_BUILDDIR" (getenv "objdir") #t)
+(define gpg-components (gpg-conf '--build-prefix (getenv "objdir")
+ '--list-components))
+
+(define (tool which)
+ (case which
+ ((gpg gpg-agent scdaemon gpgsm dirmngr)
+ (:gc:c:pgmname (assoc (symbol->string which) gpg-components)))
+ (else
+ (tool-hardcoded which))))
+
+(define (gpg-has-option? option)
(string-contains? (call-popen `(,(tool 'gpg) --dump-options) "")
- "--always-trust"))
+ option))
+
+(define have-opt-always-trust
+ (catch #f
+ (call-check `(,(tool 'gpg) --gpgconf-test --always-trust))
+ #t))
(define GPG `(,(tool 'gpg) --no-permission-warning
,@(if have-opt-always-trust '(--always-trust) '())))
(map (lambda (line) (string-split line #\:))
(string-split-newlines s))))
+;; Convenient accessors for the colon output.
+(define (:type x) (string->symbol (list-ref x 0)))
+(define (:length x) (string->number (list-ref x 2)))
+(define (:alg x) (string->number (list-ref x 3)))
+(define (:expire x) (list-ref x 6))
+(define (:fpr x) (list-ref x 9))
+(define (:cap x) (list-ref x 11))
+
+(define (have-public-key? key)
+ (catch #f
+ (pair? (filter (lambda (l) (and (equal? 'fpr (:type l))
+ (equal? key::fpr (:fpr l))))
+ (gpg-with-colons `(--list-keys ,key::fpr))))))
+
+(define (have-secret-key? key)
+ (catch #f
+ (pair? (filter (lambda (l) (and (equal? 'fpr (:type l))
+ (equal? key::fpr (:fpr l))))
+ (gpg-with-colons `(--list-secret-keys ,key::fpr))))))
+
+(define (have-secret-key-file? key)
+ (file-exists? (path-join (getenv "GNUPGHOME") "private-keys-v1.d"
+ (string-append key::grip ".key"))))
+
(define (get-config what)
(string-split (caddar (gpg-with-colons `(--list-config ,what))) #\;))
-(define all-pubkey-algos (get-config "pubkeyname"))
-(define all-hash-algos (get-config "digestname"))
-(define all-cipher-algos (get-config "ciphername"))
+(define all-pubkey-algos (delay (get-config "pubkeyname")))
+(define all-hash-algos (delay (get-config "digestname")))
+(define all-cipher-algos (delay (get-config "ciphername")))
+(define all-compression-algos (delay (get-config "compressname")))
(define (have-pubkey-algo? x)
- (not (not (member x all-pubkey-algos))))
+ (not (not (member x (force all-pubkey-algos)))))
(define (have-hash-algo? x)
- (not (not (member x all-hash-algos))))
+ (not (not (member x (force all-hash-algos)))))
(define (have-cipher-algo? x)
- (not (not (member x all-cipher-algos))))
+ (not (not (member x (force all-cipher-algos)))))
(define (gpg-pipe args0 args1 errfd)
(lambda (source sink)
(pipe:spawn `(,@GPG --dearmor))
(pipe:write-to sink-name (logior O_WRONLY O_CREAT O_BINARY) #o600)))
-(let ((verbose (string->number (getenv "verbose"))))
- (if (number? verbose)
- (*set-verbose!* verbose)))
-
;;
;; Support for test environment creation and teardown.
;;
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-legacy-environment)
+
+(let* ((key keys::alfa)
+ (subkey (car key::subkeys)))
+ (assert (have-public-key? key))
+ (assert (have-public-key? subkey))
+ (assert (have-secret-key? key))
+ (assert (have-secret-key-file? key))
+ (assert (have-secret-key? subkey))
+ (assert (have-secret-key-file? subkey))
+
+ ;; Firstly, delete the secret key.
+ (call-check `(,@gpg --delete-secret-keys ,key::fpr))
+ (assert (have-public-key? key))
+ (assert (have-public-key? subkey))
+ (assert (not (have-secret-key? key)))
+ (assert (not (have-secret-key-file? key)))
+ (assert (not (have-secret-key? subkey)))
+ (assert (not (have-secret-key-file? subkey)))
+
+ ;; Now, delete the public key.
+ (call-check `(,@gpg --delete-keys ,key::fpr))
+ (assert (not (have-public-key? key)))
+ (assert (not (have-public-key? subkey))))
+
+;; Do the same for key one, but do the subkeys separately.
+(let* ((key keys::one)
+ (subkey (car key::subkeys)))
+ (assert (have-public-key? key))
+ (assert (have-public-key? subkey))
+ (assert (have-secret-key? key))
+ (assert (have-secret-key-file? key))
+ (assert (have-secret-key-file? key))
+ (assert (have-secret-key? subkey))
+ (assert (have-secret-key-file? subkey))
+
+ ;; Firstly, delete the secret subkey.
+ (call-check `(,@gpg --delete-secret-keys ,subkey::fpr))
+ (assert (have-public-key? key))
+ (assert (have-public-key? subkey))
+ ;; JW: Deleting the secret subkey also deletes the secret key.
+ ;; XXX (assert (have-secret-key? key))
+ ;; XXX (assert (have-secret-key-file? key))
+ (assert (not (have-secret-key? subkey)))
+ (assert (not (have-secret-key-file? subkey)))
+
+ ;; Then, delete the secret key.
+ ;; XXX (call-check `(,@gpg --delete-secret-keys ,key::fpr))
+ (assert (have-public-key? key))
+ (assert (have-public-key? subkey))
+ (assert (not (have-secret-key? key)))
+ (assert (not (have-secret-key-file? key)))
+ (assert (not (have-secret-key? subkey)))
+ (assert (not (have-secret-key-file? subkey)))
+
+ ;; Now, delete the public subkey.
+ (call-check `(,@gpg --delete-keys ,subkey::fpr))
+ ;; JW: Deleting the subkey also deletes the key.
+ ;; XXX (assert (have-public-key? key))
+ (assert (not (have-public-key? subkey)))
+
+ ;; Now, delete the public key.
+ ;; XXX (call-check `(,@gpg --delete-keys ,key::fpr))
+ (assert (not (have-public-key? key)))
+ (assert (not (have-public-key? subkey))))
+
+(let* ((key keys::two)
+ (subkey (car key::subkeys)))
+ (assert (have-public-key? key))
+ (assert (have-public-key? subkey))
+ (assert (have-secret-key? key))
+ (assert (have-secret-key-file? key))
+ (assert (have-secret-key? subkey))
+ (assert (have-secret-key-file? subkey))
+
+ ;; Delete everything at once.
+ (call-check `(,@gpg --delete-secret-and-public-key ,key::fpr))
+ (assert (not (have-public-key? key)))
+ (assert (not (have-public-key? subkey)))
+ (assert (not (have-secret-key? key)))
+ (assert (not (have-secret-key-file? key)))
+ (assert (not (have-secret-key? subkey)))
+ (assert (not (have-secret-key-file? subkey))))
x (lambda (p) (display (eval test (current-environment)) p)))
(call-check `(,(tool 'gpg) --verify ,x))
(call-check `(,(tool 'gpg) --output ,y ,x))
- (unless (file=? y z) (error "mismatch"))))
+ (unless (file=? y z) (fail "mismatch"))))
'(msg_opaque_signed_256 msg_opaque_signed_384 msg_opaque_signed_521)))
;;
(call-with-output-file
x (lambda (p) (display (eval test (current-environment)) p)))
(call-check `(,@GPG --yes --output ,y ,x))
- (unless (file=? y z) (error "mismatch"))))
+ (unless (file=? y z) (fail "mismatch"))))
'(msg_encrypted_256 msg_encrypted_384 msg_encrypted_521)))
;;
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-legacy-environment)
+
+(for-each-p
+ "Checking armor encoding and decoding"
+ (lambda (source)
+ (tr:do
+ (tr:open source)
+ (tr:gpg "" `(--enarmor))
+ (tr:gpg "" '(--dearmor))
+ (tr:assert-identity source)))
+ (append plain-files data-files))
(tr:gpg "" '(--yes))
(tr:assert-identity source)))
(append plain-files data-files)))
- all-cipher-algos)
+ (force all-cipher-algos))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-legacy-environment)
+
+(define files (append plain-files data-files))
+
+(info "Checking encryption of supplied files using --multifile.")
+
+;; Now encrypt all files.
+(call-check `(,@gpg --encrypt --recipient ,usrname2
+ --multifile ,@files))
+
+;; And check if we can decrypt them.
+(for-each-p
+ "Verifying files:"
+ (lambda (source)
+ (tr:do
+ (tr:open (string-append source ".gpg"))
+ (tr:gpg "" '(--yes))
+ (tr:assert-identity source)))
+ files)
(tr:gpg "" '(--yes))
(tr:assert-identity source)))
(append plain-files data-files)))
- all-cipher-algos)
+ (force all-cipher-algos))
;; We encrypt to two keys and we have also put the first key into our
(define (check-for predicate lines message)
(unless (any predicate lines)
- (error message)))
+ (fail message)))
(define (check-exported-key dump keyid)
(check-for (lambda (l)
(lambda (port)
(unless
(eof-object? (peek-char port))
- (error (string-append
+ (fail (string-append
"Expected all passphrases to be consumed, but found: "
(read-all port)))))))
(define (genkey config)
(pipe:do
(pipe:echo config)
- (pipe:spawn `(,(tool 'gpg) --quiet --batch --gen-key))))
+ (pipe:spawn `(,(tool 'gpg) --quiet --batch --generate-key))))
(info "Checking batch key generation")
(genkey "Key-Type: DSA
(tr:call-with-content
(lambda (c)
(unless (all (lambda (f) (string-contains? c f)) testfiles)
- (error "some file(s) are missing from archive")))))
+ (fail "some file(s) are missing from archive")))))
(with-temporary-working-directory
(call-check `(,(tool 'gpgtar) --gpg ,(tool 'gpg) --gpg-args ,gpgargs
(for-each
(lambda (f) (unless (call-with-input-file f (lambda (x) #t))
- (error (string-append "missing file: " f))))
+ (fail (string-append "missing file: " f))))
testfiles))))
(info "Checking gpgtar without encryption")
(info "Checking gpgtar with symmetric encryption and chosen cipher")
(do-test `(,@ppflags --symmetric --gpg-args
- ,(string-append "--cipher=" (car all-cipher-algos)))
+ ,(string-append "--cipher="
+ (car (force all-cipher-algos))))
ppflags (cons '--decrypt ppflags))
(info "Checking gpgtar with both symmetric and asymmetric encryption")
(pipe:do
(pipe:echo (eval armored-file (current-environment)))
(pipe:spawn `(,@GPGV --keyring ,(in-srcdir "forged-keyring.gpg"))))
- (error "verification succeeded but should not")))
+ (fail "verification succeeded but should not")))
'(msg_signed_asc))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-legacy-environment)
+
+;; XXX because of --always-trust, the trustdb is not created.
+;; Therefore, we redefine GPG without --always-trust.
+(define gpg `(,(tool 'gpg) --no-permission-warning))
+
+(info "Checking key revocation.")
+(call-check `(,@gpg --import ,(in-srcdir "samplemsgs"
+ "revoke-2D727CC768697734.asc")))
+(let loop ((output (gpg-with-colons '(--list-secret-keys "2D727CC768697734"))))
+ (unless (null? output)
+ (let ((line (car output))
+ (rest (cdr output)))
+ (when (member (car line) '("sec" "uid" "ssb"))
+ (unless (equal? (cadr line) "r")
+ (fail (car line) "not revoked.")))
+ (loop rest))))
(string-contains? line ":4096:1:DDA252EBB8EBE1AF:")))
(string-split-newlines c))))
(unless (= 2 (length keys))
- (error "Importing keys with long id collision failed"))))))
+ (fail "Importing keys with long id collision failed"))))))
"GET_PASSPHRASE --no-ask some_id X X X")))
(unless (string=? (string-rtrim char-whitespace? response)
"OK 736F6D655F70617373706872617365")
- (error "Could not retrieve passphrase from cache:" response)))
+ (fail "Could not retrieve passphrase from cache:" response)))
(info "Checking import statistics (issue2346)...")
(let ((status (call-popen `(,@GPG --status-fd=1 --import ,key) "")))
(unless (string-contains? status "IMPORT_RES 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0")
- (error "Unexpected number of keys imported" status)))
+ (fail "Unexpected number of keys imported" status)))
(dearmor (in-srcdir "samplemsgs/issue2419.asc") onebyte)
(catch (assert (string-contains? (car *error*) "invalid packet"))
(call-popen `(,@GPG --list-packets ,onebyte) "")
- (error "Expected an error but got none")))
+ (fail "Expected an error but got none")))
(display (call-popen `(,@gpg --locate-key ,mailbox) ""))
(echo "This is the key we expected:")
(display (call-popen `(,@gpg --list-keys ,expected) ""))
- (error "Expected" expected "but got" fpr)))
+ (fail "Expected" expected "but got" fpr)))
(delete-keys set))
(lambda (set)
(length set))
(define (test-hash hash ref)
(unless (eq? #f ref)
(if (not (string=? (:value hash) (:value ref)))
- (error "failed"))))
+ (fail "failed"))))
;; Test whether the hashes computed over S match the REFERENCE set.
(define (test-hashes msg s reference)
(pipe:write-to file (logior O_WRONLY O_CREAT O_BINARY) #o600))
(if (= 0 (call `(,@GPG --verify ,file)))
- (error "Bad signature verified ok"))))
+ (fail "Bad signature verified ok"))))
'(sig-1ls1ls-valid sig-ls-valid sig-1lsls-invalid
sig-lsls-invalid sig-lss-invalid sig-slsl-invalid))
(define (count-uids-of-secret-key id)
(length (filter (lambda (x) (and (string=? "uid" (car x))
- (string=? "u" (cadr x))))
+ (not (string=? "r" (cadr x)))))
(gpg-with-colons
`(--with-fingerprint
--list-secret-keys ,(exact id))))))
(setenv "PINENTRY_USER_DATA" "test" #t)
(info "Checking quick key generation...")
-(call-check `(,@GPG --quick-gen-key ,alpha))
+(call-check `(,@GPG --quick-generate-key ,alpha))
-(call-check `(,@GPG --check-trustdb)) ; XXX why?
+(define keyinfo (gpg-with-colons `(-k ,(exact alpha))))
+(define fpr (:fpr (assoc "fpr" keyinfo)))
(assert (= 1 (count-uids-of-secret-key alpha)))
+(assert (not (equal? "" (:expire (assoc "pub" keyinfo)))))
(info "Checking that we can add a user ID...")
;; Make sure the key capabilities don't change when we add a user id.
;; (See bug #2697.)
(let ((pre (key-data (exact alpha)))
- (result (call-check `(,@GPG --quick-adduid ,(exact alpha) ,bravo)))
+ (result (call-check `(,@GPG --quick-add-uid ,(exact alpha) ,bravo)))
(post (key-data (exact alpha))))
(if (not (equal? pre post))
(begin
(newline)
(exit 1))))
-(call-check `(,@GPG --check-trustdb)) ; XXX why?
-
(assert (= 2 (count-uids-of-secret-key alpha)))
(assert (= 2 (count-uids-of-secret-key bravo)))
(info "Checking that we can revoke a user ID...")
-(call-check `(,@GPG --quick-revuid ,(exact bravo) ,alpha))
-
-(call-check `(,@GPG --check-trustdb)) ; XXX why?
+(call-check `(,@GPG --quick-revoke-uid ,(exact bravo) ,alpha))
(assert (= 1 (count-uids-of-secret-key bravo)))
+
+(info "Checking that we can change the expiration time.")
+
+(define (expiration-time id)
+ (:expire (assoc "pub" (gpg-with-colons `(-k ,id)))))
+
+;; Remove the expiration date.
+(call-check `(,@gpg --quick-set-expire ,fpr "0"))
+(assert (equal? "" (expiration-time fpr)))
+
+;; Make the key expire in one year.
+(call-check `(,@gpg --quick-set-expire ,fpr "1y"))
+;; XXX It'd be nice to check that the value is right.
+(assert (not (equal? "" (expiration-time fpr))))
+
+
+;;
+;; Check --quick-addkey
+;;
+
+;; Get the subkeys.
+(define (get-subkeys)
+ (filter (lambda (x) (equal? "sub" (car x)))
+ (gpg-with-colons `(-k ,fpr))))
+
+;; This keeps track of the number of subkeys.
+(define count (length (get-subkeys)))
+
+(for-each-p
+ "Checking that we can add subkeys..."
+ (lambda (args check)
+ (set! count (+ 1 count))
+ (call-check `(,@gpg --quick-add-key ,fpr ,@args))
+ (let ((subkeys (get-subkeys)))
+ (assert (= count (length subkeys)))
+ (if check (check (last subkeys)))))
+ ;; A bunch of arguments...
+ '(()
+ (- - -)
+ (default default never)
+ (rsa sign "2d")
+ (rsa1024 sign "2w")
+ (rsa2048 encr "2m")
+ (rsa4096 sign,auth "2y")
+ (future-default))
+ ;; ... with functions to check that the created key matches the
+ ;; expectations (or #f for no tests).
+ (list
+ #f
+ #f
+ (lambda (subkey)
+ (assert (equal? "" (:expire subkey))))
+ (lambda (subkey)
+ (assert (= 1 (:alg subkey)))
+ (assert (string-contains? (:cap subkey) "s"))
+ (assert (not (equal? "" (:expire subkey)))))
+ (lambda (subkey)
+ (assert (= 1 (:alg subkey)))
+ (assert (= 1024 (:length subkey)))
+ (assert (string-contains? (:cap subkey) "s"))
+ (assert (not (equal? "" (:expire subkey)))))
+ (lambda (subkey)
+ (assert (= 1 (:alg subkey)))
+ (assert (= 2048 (:length subkey)))
+ (assert (string-contains? (:cap subkey) "e"))
+ (assert (not (equal? "" (:expire subkey)))))
+ (lambda (subkey)
+ (assert (= 1 (:alg subkey)))
+ (assert (= 4096 (:length subkey)))
+ (assert (string-contains? (:cap subkey) "s"))
+ (assert (string-contains? (:cap subkey) "a"))
+ (assert (not (equal? "" (:expire subkey)))))
+ #f))
;; Set objdir so that the tests can locate built programs.
(setenv "objdir" (getcwd) #f)
-(define test-pool
- (package
- (define (new procs)
- (package
- (define (add test)
- (new (cons test procs)))
- (define (wait)
- (let ((unfinished (filter (lambda (t) (not t::retcode)) procs)))
- (if (null? unfinished)
- (package)
- (let* ((commands (map (lambda (t) t::command) unfinished))
- (pids (map (lambda (t) t::pid) unfinished))
- (results
- (map (lambda (pid retcode) (list pid retcode))
- pids
- (wait-processes (map stringify commands) pids #t))))
- (new
- (map (lambda (t)
- (if t::retcode
- t
- (t::set-retcode (cadr (assoc t::pid results)))))
- procs))))))
- (define (passed)
- (filter (lambda (p) (= 0 p::retcode)) procs))
- (define (skipped)
- (filter (lambda (p) (= 77 p::retcode)) procs))
- (define (hard-errored)
- (filter (lambda (p) (= 99 p::retcode)) procs))
- (define (failed)
- (filter (lambda (p)
- (not (or (= 0 p::retcode) (= 77 p::retcode)
- (= 99 p::retcode))))
- procs))
- (define (report)
- (echo (length procs) "tests run,"
- (length (passed)) "succeeded,"
- (length (failed)) "failed,"
- (length (skipped)) "skipped.")
- (length (failed)))))))
-
-(define (verbosity n)
- (if (= 0 n) '() (cons '--verbose (verbosity (- n 1)))))
-
-(define test
- (package
- (define (scm name . args)
- (new name #f `(,*argv0* ,@(verbosity (*verbose*)) ,@args
- ,(in-srcdir name)) #f #f CLOSED_FD))
- (define (new name directory command pid retcode logfd)
- (package
- (define (set-directory x)
- (new name x command pid retcode logfd))
- (define (set-retcode x)
- (new name directory command pid x logfd))
- (define (set-pid x)
- (new name directory command x retcode logfd))
- (define (set-logfd x)
- (new name directory command pid retcode x))
- (define (open-log-file)
- (let ((filename (string-append name ".log")))
- (catch '() (unlink filename))
- (open filename (logior O_RDWR O_BINARY O_CREAT) #o600)))
- (define (run-sync . args)
- (letfd ((log (open-log-file)))
- (with-working-directory directory
- (let* ((p (inbound-pipe))
- (pid (spawn-process-fd (append command args) 0
- (:write-end p) (:write-end p))))
- (close (:write-end p))
- (splice (:read-end p) STDERR_FILENO log)
- (close (:read-end p))
- (let ((t' (set-retcode (wait-process name pid #t))))
- (t'::report)
- t')))))
- (define (run-sync-quiet . args)
- (with-working-directory directory
- (set-retcode
- (wait-process
- name (spawn-process-fd (append command args)
- CLOSED_FD CLOSED_FD CLOSED_FD) #t))))
- (define (run-async . args)
- (let ((log (open-log-file)))
- (with-working-directory directory
- (new name directory command
- (spawn-process-fd (append command args) CLOSED_FD log log)
- retcode log))))
- (define (status)
- (let ((t (assoc retcode '((0 "PASS") (77 "SKIP") (99 "ERROR")))))
- (if (not t) "FAIL" (cadr t))))
- (define (report)
- (unless (= logfd CLOSED_FD)
- (seek logfd 0 SEEK_SET)
- (splice logfd STDERR_FILENO)
- (close logfd))
- (echo (string-append (status retcode) ":") name))))))
-
-(define (run-tests-parallel setup tests)
- (lettmp (gpghome-tar)
- (setup::run-sync '--create-tarball gpghome-tar)
- (let loop ((pool (test-pool::new '())) (tests' tests))
- (if (null? tests')
- (let ((results (pool::wait)))
- (for-each (lambda (t)
- (catch (echo "Removing" t::directory "failed:" *error*)
- (unlink-recursively t::directory))
- (t::report)) (reverse results::procs))
- (exit (results::report)))
- (let* ((wd (mkdtemp))
- (test (car tests'))
- (test' (test::set-directory wd)))
- (loop (pool::add (test'::run-async '--unpack-tarball gpghome-tar))
- (cdr tests')))))))
-
-(define (run-tests-sequential setup tests)
- (lettmp (gpghome-tar)
- (setup::run-sync '--create-tarball gpghome-tar)
- (let loop ((pool (test-pool::new '())) (tests' tests))
- (if (null? tests')
- (let ((results (pool::wait)))
- (for-each (lambda (t)
- (catch (echo "Removing" t::directory "failed:" *error*)
- (unlink-recursively t::directory)))
- results::procs)
- (exit (results::report)))
- (let* ((wd (mkdtemp))
- (test (car tests'))
- (test' (test::set-directory wd)))
- (loop (pool::add (test'::run-sync '--unpack-tarball gpghome-tar))
- (cdr tests')))))))
-
-(let* ((runner (if (member "--parallel" *args*)
+(let* ((tests (filter (lambda (arg) (not (string-prefix? arg "--"))) *args*))
+ (runner (if (and (member "--parallel" *args*)
+ (> (length tests) 1))
run-tests-parallel
- run-tests-sequential))
- (tests (filter (lambda (arg) (not (string-prefix? arg "--"))) *args*)))
- (runner (test::scm "setup.scm") (map test::scm tests)))
+ run-tests-sequential)))
+ (runner (test::scm "setup.scm" "setup.scm")
+ (map (lambda (t) (test::scm t t)) tests)))
--- /dev/null
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFg8F68BCAC5VS/59Nrg5mw+wEAnk/vi0CGI+K5rGTmyAruy/aiS4kYfUJiE
+Pq/JZYCeNQ9GTbJN5M+Fiv1bhw6xeEfl4FAC/A0Uo39fvUSLJH2V+8lT3GxXt8KR
+Yx8MvotvoKiG1DRvgnwRh1qibX6+htJahPwHDViUuFz0ZeEPWiCwuoMOELmiTsuE
+VZbQsv08BwCOzIc3M8s63TtwMCUe+MBhqfRjEu6/Gt7miuGGVFXB96ao2O9u1HsR
+7cUjgVQp6JwGLgIIijjy167Ms+fKZetqA9tzu1C1SAtNYiqmAUoqGG1YEjS7oApo
+AKmHD+LGCMUPg5d6bhw+NyHjDAFWJEYv3kG3ABEBAAG0NVRlc3QgS2V5IChkbyBu
+b3QgdXNlKSA8YXV0aGVudGljYXRlLW9ubHlAZXhhbXBsZS5vcmc+iQFOBBMBCAA4
+FiEEkn7zd/0aG295XkDAKoeRfY/7pJ8FAlg8F68CGwEFCwkIBwIGFQgJCgsCBBYC
+AwECHgECF4AACgkQKoeRfY/7pJ9+zAf9FVZknDCWCNY42+RIR36fq2l1Gs8KfMsX
+rNXtfYPtLqioP8fz2LE0LoudSsZMLVygonPG0ZAxdIUHXcFdUqPrEzs4fEyR4xae
+OSxui2Q/u6+9bi7eilYYzVNRWkuyx2TrRQZEjMXMnuJcWptGbRWs/ipRUZBHWfLl
+2udLl+0TRIL7hni06xVCHbwE5szcLoyzzvIowGVADXFqeG7nty7uRNZNAD+ZWMH2
+J0vspZlUSVi7z1VygzDI3U42SMJmVqnRmICsB3QLI8Ns4nxWXO2z8fheSFcrP+LA
+cY3W6JgnLCzvyuogxnWmd4fzr1iB3E2Hcy/sr0cgQ5wtuseQRhmTlrkBDQRYPBfZ
+AQgAtKcbBYrIqh3cRTqyZjMxw492RVQoFawYmpr1bQ4HphVGnT4IhJZQ4DAm1JJZ
+QjzwDQiZMc1wD9Om6UC/g8gUBuFCpLCobwBzjH4an/G3Cfa8zGz5ANAROo5i5T1j
+vgoFEFdVue/GpAmMNixz+0ItQBh9jiOC6IYk1yyv/OsTzsj6AHnH0XiDpGoTNkca
+tb6Mu1VcXTMNf620Mxj6c7WE7awxL6MwKIa7XQSgXaO+JnbB+5Szz1wZ1ZhlnDp3
+KOurlfcXIbZirEaqmRByb+V21Dj3icXOJEj1RUbyVNqBS8rhw17kSxcehw/6ZAMW
+ehDmhXO98VVUknS0Mf+OJBi2JwARAQABiQE2BBgBCAAgFiEEkn7zd/0aG295XkDA
+KoeRfY/7pJ8FAlg8F9kCGyAACgkQKoeRfY/7pJ+sawf/eSjxxAglAdolF9lK070u
+VmMgq4GFPqJ3RqJPUFjwEFFSYLXkiALnMGXDSmOfPqCQ4c+PWwumFhKCz4MXVDD8
+x6mi9Z+HlEwIMaCnckrSTuQ5OgwO/6vkhz42OcgMZ3WQnWfNVM8jbNP9vX1vroPe
+HLFaPGy9KJMM0Z/hlCIIeyK/a90zWlT5UMfRoqNQRbY/iiYdmpvf69I9PobGVbo/
+7ahZTumPWwjiGOztNXeuo5UUaAVVxMQBYKp2w3wil2sHzYfTfYUSMyh+oUFx4Xlz
+WF3bLzsafRaeuK1h5+JuvIcimvU5zWZtn0hOpiIXpZOoJvvM9r5D4ZRT5UX2blQ8
+Pw==
+=vt5C
+-----END PGP PUBLIC KEY BLOCK-----
--- /dev/null
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lQPGBFg8F68BCAC5VS/59Nrg5mw+wEAnk/vi0CGI+K5rGTmyAruy/aiS4kYfUJiE
+Pq/JZYCeNQ9GTbJN5M+Fiv1bhw6xeEfl4FAC/A0Uo39fvUSLJH2V+8lT3GxXt8KR
+Yx8MvotvoKiG1DRvgnwRh1qibX6+htJahPwHDViUuFz0ZeEPWiCwuoMOELmiTsuE
+VZbQsv08BwCOzIc3M8s63TtwMCUe+MBhqfRjEu6/Gt7miuGGVFXB96ao2O9u1HsR
+7cUjgVQp6JwGLgIIijjy167Ms+fKZetqA9tzu1C1SAtNYiqmAUoqGG1YEjS7oApo
+AKmHD+LGCMUPg5d6bhw+NyHjDAFWJEYv3kG3ABEBAAH+BwMCzWzbxiWo+krZWBSs
+jFH7f5mn3T/L0PBesTdqiwKwUOj8Fltri+6OY9G028CJB4UlTrEmvD4zdu7HFlvo
+XgrNhZiE8INjEY6QDrNTKNX474dWcCQDDinz6NIwitYNMkv//Ct19qhrqd9WpGVH
+QtKVe0BOHimiokpqu3YBMvV8tZmM2Am8vN32nn/lHo8G/UhSRVnYewazSvAjCSPG
+OqJAPuD3vldssTgpPA2BhBeh6Q7jDkghGfd97a1LkB+JpuDF6UNXs1OxVthONb7q
+ScEsxUhiYne7LrglzZhfDdqsRWcyQyaTzIg/3tOh69hRI2I+8EoJJ9PeLx4rMGFJ
+Z0DkgSbUwXDYYa8GgewT5yZSkfJyhcx/q8eI3PW39TB0YI48TaN3KufzqS+U8ITz
+bQygpzX2WGBXKcwXV0zDXjpapqd/77m600FhvrrIJuJNGmNNSQ1eWAYmY13rmR1R
+7aXRrocVlF3ZPfWzKLi6DophmW0vfcwlt9XxgQ2VxF0rk2f6OF4WcTYbaOCwh8M3
+Ffo98i3OO+upHsJLKyvQRrM1/LVeecGTxV9cXOnE+YB+aU36AS+AjeLRTqM2ZbzU
+UgNTdkcs6+8B4q3XMWtTojXXCYfHMpFYu2FuKT+cXi1UEKWF7/Rhu2i3aeRahRLF
+zdtSwWhbx0WNqH4BL4hhGZ85BUXKrOwdDiRyzgzKQilwWXu1ZEJuaWrY+vxdqdxi
+PyikwO7/JXlBgcICO7v5hKDVuHGll8jP1CQU3hvfcSKuzny++ComI3ND2H2Wj3Jh
+ocz9/7Z+hAp/RMeE2MbptvnJTzeTEg/Wk2aMt6ArxEKCpcpvXlvm5TENl20/oejo
+H/o6DNJhtU75AI5tQZ315cXfK56ae0Swc90UGPImTDl3LiG+qTgQbcHdNV+I0zL9
+FeqvLZg5ZgpBtDVUZXN0IEtleSAoZG8gbm90IHVzZSkgPGF1dGhlbnRpY2F0ZS1v
+bmx5QGV4YW1wbGUub3JnPokBTgQTAQgAOBYhBJJ+83f9GhtveV5AwCqHkX2P+6Sf
+BQJYPBevAhsBBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJECqHkX2P+6SffswH
+/RVWZJwwlgjWONvkSEd+n6tpdRrPCnzLF6zV7X2D7S6oqD/H89ixNC6LnUrGTC1c
+oKJzxtGQMXSFB13BXVKj6xM7OHxMkeMWnjksbotkP7uvvW4u3opWGM1TUVpLssdk
+60UGRIzFzJ7iXFqbRm0VrP4qUVGQR1ny5drnS5ftE0SC+4Z4tOsVQh28BObM3C6M
+s87yKMBlQA1xanhu57cu7kTWTQA/mVjB9idL7KWZVElYu89VcoMwyN1ONkjCZlap
+0ZiArAd0CyPDbOJ8Vlzts/H4XkhXKz/iwHGN1uiYJyws78rqIMZ1pneH869YgdxN
+h3Mv7K9HIEOcLbrHkEYZk5adA8YEWDwX2QEIALSnGwWKyKod3EU6smYzMcOPdkVU
+KBWsGJqa9W0OB6YVRp0+CISWUOAwJtSSWUI88A0ImTHNcA/TpulAv4PIFAbhQqSw
+qG8Ac4x+Gp/xtwn2vMxs+QDQETqOYuU9Y74KBRBXVbnvxqQJjDYsc/tCLUAYfY4j
+guiGJNcsr/zrE87I+gB5x9F4g6RqEzZHGrW+jLtVXF0zDX+ttDMY+nO1hO2sMS+j
+MCiGu10EoF2jviZ2wfuUs89cGdWYZZw6dyjrq5X3FyG2YqxGqpkQcm/ldtQ494nF
+ziRI9UVG8lTagUvK4cNe5EsXHocP+mQDFnoQ5oVzvfFVVJJ0tDH/jiQYticAEQEA
+Af4HAwLbL5r0DsoKEtmeahTDsYhsRTH/hmOPCGKyjhblVfWoNVQjmrDNsRVrPx+2
+CVj304Npu9XpkKsgp/U20pv6y00xET/l+FfftyN5zUTdGC4qi0gN3AXE5amQkbnJ
+/ItzUTnf8YPYenSKioiQSrk39Q+ikRZ3WBIPVQcMR2bPnzAobNupit5fgS7e/aRA
+0vswD12SeqqNM82JWAuSFJc51F+Gn8xTkWnnK2AdA8Rz351qqUHpmFwg8kaRVhVe
+TwSP7nAN5JPS7lrr91vvp3jRlH84XgaUxFk/8Gm8pSNN4tyxNjo9Cv6YaG0PR/rO
+skg6G/9AfqVtAuRyaRNu1HP/JPDyuGGEqOt7mfMr2vd43a5LTVO3EkO5OfQkXg1/
+t2HXn6dzc0hBolq0pWAUU+sVwZD9h2F8pEWPPyeInv5H4c95J9HU466tro9VJpvm
+p/iw55evDiBRAu+nNkCI8eyFEpB06d3ikjxLAJ5xS3Fb6Vj3PX0TdZMieRNHQXlt
+Gpi8UQOpOLhj+lytW9NYCGG3SYcy2ytYVgs2IoIycx37c4tz5+v1yi8/FvnQPPTf
+8LjLZUrrFL6GRJm0fNtp0I3jm5Ic/oESQ4UDrVmQO1+G6/+9iOalgA7x67+X13rD
+6nz0Et0WBH0HpZrOEFe2b07livdfs5gS3xtyqyMBRLu4lKYDWYJ55R0nbMiwQ1dQ
+EG5U/ur0E1ENJeFDE2MmL06RIXtizVcGmoU23m/tAEShNJBaHGe1qKKDGk8lutmF
+9S4UN5eXE3qzBJmqWQTG4/z2N+QRB/Nj6h1IbKPvAMML2c5vdI1V1LniCa55vytJ
+F6Ngtyw6f7M2UVqyPr6FZmzZyw6SPxjGqFUcp/o30Pqm6k0AYr3HVQLLnCM9vpCY
+Y73QV1EOpBNPNIPTyiWWFSnrXID6xoObHg2JATYEGAEIACAWIQSSfvN3/Robb3le
+QMAqh5F9j/uknwUCWDwX2QIbIAAKCRAqh5F9j/ukn6xrB/95KPHECCUB2iUX2UrT
+vS5WYyCrgYU+ondGok9QWPAQUVJgteSIAucwZcNKY58+oJDhz49bC6YWEoLPgxdU
+MPzHqaL1n4eUTAgxoKdyStJO5Dk6DA7/q+SHPjY5yAxndZCdZ81UzyNs0/29fW+u
+g94csVo8bL0okwzRn+GUIgh7Ir9r3TNaVPlQx9Gio1BFtj+KJh2am9/r0j0+hsZV
+uj/tqFlO6Y9bCOIY7O01d66jlRRoBVXExAFgqnbDfCKXawfNh9N9hRIzKH6hQXHh
+eXNYXdsvOxp9Fp64rWHn4m68hyKa9TnNZm2fSE6mIhelk6gm+8z2vkPhlFPlRfZu
+VDw/
+=vLFX
+-----END PGP PRIVATE KEY BLOCK-----
--- /dev/null
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Comment: This is a revocation certificate
+
+iGAEIBECACAWIQSg/0WQu2Ei7e9uPFQtcnzHaGl3NAUCWEaoMAIdAAAKCRAtcnzH
+aGl3NISuAJ9rsxoazHvPs89Ki33o/SgKMjOg/wCggbFG8V5wXU1njwuiviPUKap3
+uqA=
+=79yW
+-----END PGP PUBLIC KEY BLOCK-----
(load (with-path "defs.scm"))
(unless (member "--create-tarball" *args*)
- (error "Usage: setup.scm --create-tarball <file>"))
+ (fail "Usage: setup.scm --create-tarball <file>"))
(with-temporary-working-directory
(setenv "GNUPGHOME" (getcwd) #t)
usrpass1)
(if (not (string=? "4336AE2A528FAE091E73E59E325B588FEE795F9B"
(cadar (gpg-hash-string `(--print-md SHA1 ,tmp) ""))))
- (error "bug537-test.data.asc: mismatch (bug 537)")))
+ (fail "bug537-test.data.asc: mismatch (bug 537)")))
`(--yes --sign --passphrase-fd "0" --digest-algo ,hash))
(tr:gpg "" '(--yes))
(tr:assert-identity (car plain-files)))))
- all-hash-algos)
+ (force all-hash-algos))
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-environment)
+
+(define key
+ `(,(in-srcdir "samplekeys" "authenticate-only.sec.asc")
+ "927EF377FD1A1B6F795E40C02A87917D8FFBA49F"
+ "72360FDB6380212D5DAF2FA9E51185A9253C496D"
+ "ssh-rsa"))
+
+(define :file car)
+(define :fpr cadr)
+(define :subkey-fpr caddr)
+(define :kind cadddr)
+
+;; Return true if a-str and b-str share a suffix of length n.
+(define (string-common-suffix? n a-str b-str)
+ (let ((a-len (string-length a-str))
+ (b-len (string-length b-str)))
+ (if (> n (min a-len b-len))
+ #f
+ (string=? (substring a-str (- a-len n) a-len)
+ (substring b-str (- b-len n) b-len)))))
+
+(info "Checking ssh export...")
+(call-check `(,@GPG --yes --import ,(:file key)))
+
+(let* ((result (call-check `(,@GPG --export-ssh-key ,(:fpr key))))
+ ;; XXX: We should split at any whitespace here.
+ (parts (string-split (string-trim char-whitespace? result) #\space)))
+ (assert (string=? (car parts) (:kind key)))
+ ;; XXX: We should not use a short keyid as the comment when
+ ;; exporting an ssh key.
+ (assert (string-common-suffix? 8 (caddr parts) (:subkey-fpr key))))
(define GNUPGHOME (getenv "GNUPGHOME"))
(if (string=? "" GNUPGHOME)
- (error "GNUPGHOME not set"))
+ (fail "GNUPGHOME not set"))
(setenv "SSH_AUTH_SOCK"
(call-check `(,(tool 'gpgconf) --null --list-dirs agent-ssh-socket))
(pipe:open file (logior O_RDONLY O_BINARY))
(pipe:spawn `(,SSH-ADD -)))
(unless (string-contains? (call-popen `(,SSH-ADD -l "-E" md5) "") hash)
- (error "key not added"))))
+ (fail "key not added"))))
car keys)
(info "Checking for issue2316...")
(unless
(string-contains? (call-popen `(,SSH-ADD -l "-E" md5) "")
"c9:85:b5:55:00:84:a9:82:5a:df:d6:62:1b:5a:28:22")
- (error "known private key not (re-)added to sshcontrol"))
+ (fail "known private key not (re-)added to sshcontrol"))
+++ /dev/null
------BEGIN PGP MESSAGE-----
-Version: GnuPG v2
-
-owGbwMvMwMF46tzNaXtET0QxnmZPYgj9/c+Sq2MOCwMjBwMbKxOIy8DFKQBTo/SK
-hWFThVuj19r3R/6VzQkpaZuQx7s3r9BQ46v8KXkjb58dSjmXyr7enlCzb7dg1zE7
-aynbc6YTF+wXZI4IlAgPuLJhUeSXo0+WllxbFXUz39407cv15TcXThLj+3tFkSnZ
-YFXwM9+nfAoHpt6I/ZY96SJT3XFZKzO1jeZNJhZsV4Vfrjp0UmnH3E4A
-=X9WM
------END PGP MESSAGE-----
+++ /dev/null
------BEGIN PGP MESSAGE-----
-Version: GnuPG v2
-
-owGbwMvMwMEY0Tqz9J35+WmMp9mTGEJ//xPk6pjDwsDIwcDGygTiMnBxCsDULFZm
-/sk4S36iQ6FuZZPMPdOSe/rZOxNThTmzvJN4l1qe9XGdlLhtpumfzh0uhRnzT2Xc
-jmra+ZdN9+XBhml//i7v6XrfuWu56OuEI/fXH0i3P5HELb+j++6SO85VemLq/tvO
-hNvWtddvuZ7+z2JJaqnP4wiu2t+sEze/MWKZ9zz+u2FV6a3OIyJxjwA=
-=JMtb
------END PGP MESSAGE-----
+++ /dev/null
------BEGIN PGP PRIVATE KEY BLOCK-----
-Version: GnuPG v2
-
-lgAAAgYEVfv86AEEAN20yizZgtnQaJPUV++9Z+rRg4XzjWpLvmiWMpTsn8qhjpyS
-kAa4/4P4/MRWVvSXiRC1uJ7T59Sbm/KFs8TdKaqIMuON3QYjztxm2NmDMA/f5FTv
-RuLkgKAEpwGOqI1Zvm3uleH8hkx0n45tHxCI3bLCfW+12lZxJCGNDBnhvj+5ABEB
-AAH+BwMCeYHLsHWjaoTufvOw6/xINpFQV8JcwSc+RaEIfmIwEwO242+vUEZefkia
-yMMJTd20C144zMr/3Tsx/+c8ULAbR/NBtuG49jsGWFJH2uN/5pi40x2S/afJuwru
-0co5xQSnpZtM4v9mvFM517IROhHY1pl6KpK87pZm5JHGB4525DpAYJ7vTTmHE2NW
-e5jr7a7SpXwTU7dKHbLxY+kofH7DLvMX6KjOJ/kDLIqnK3AeCwfhXkkRRP8UI/0J
-pZEPUyImag6FryRdoZJPTPX7TMWM4zrdnT6xOffIe1REpo59LVkvg6TiPtnlnuY8
-Y9NVZ+mWz0RHtxFh1b70G6D5C5Mdi/iGUAAfTwNhjdnmYsN1qKxcO533qlj/rXHn
-6uxauiR4d+7Ioy2RsPpY2FqTkgymhBLn6ZcYvzwEXaAygLUs8HmzPuiVm5Ls5UXn
-VKaRMc+DBQPz3W3CuMWsHAyKsg4ibp/6MSf0klYHUG8WVXI4tLGOkbg5HbQTVGVz
-dGluZyAoaW5zZWN1cmUhKYi9BBMBCAAnBQJV+/zoAhsDBQkB4TOABQsJCAcCBhUI
-CQoLAgQWAgMBAh4BAheAAAoJEFiFmXXuN8+WqPYEAIW+qAoFnc2emFnx/b+vKW9X
-1g3NLmsLyUUBI34GCh+sGa6C0SptdKc68uvKUc6daBiHuoukN4F+1rYUuNG8WNMs
-V/JwGPKVADPIFrgGiotMW770ZnzZsoqGWvwUnyrlaUI6AYHe4Uj9YAmnmi647A/u
-UxcI1H20M3dENSUyiS1zngAAAgUEVfv86AEEAMgaJrwhFOhEmHHgqyzx2KFzG4SD
-F6jyAg1CIVKmiLSBfNXWa43vJwfxLo7vbT1wy0iiJF8+ALD/ghppmZb9NpsiUC+X
-xT4ublOSvRgN+527WdUX8ym0EXxjpuSSW+hVZZwUP0K0fBdIVaVCawJGEp5Lc/mX
-KnjmXvLQxWSQYgB9ABEBAAH+BwMCtE0VqaVadDju5hPxFcvSTjNkKwGVZZgQBWVZ
-sYj/Sd/Pbc90xb3TSf/VQGVQhKei+GBmUPYOPqStOP30pJvK0SBxkJ2BYb876RJC
-lj48lkTGFPZwhw69BZq6QA5nfBm41V+W6iakdyEww6g1Q93AyzuAirBJraR+oQ6Q
-beqo52TtYAhpAQbUBsQ/1VO/1zx8eHOG298kYpU2Jo7Te81d03rWcSaDbJqcEmsI
-jJe1ccvQ8oU+k6ttbY3xTiKYWfJCxEaOcYpO4z1/94CPFYv1D5rJqJ/C0/SPmS4t
-4ZMqenEhsAGhMgPLKXNmQadQA2WBOATsSxmKCcC9LNjw1YudXPiLfHEnBKGQSbRF
-sZ2xZqRm7wRTQ/eXAJGGiQ41owstwSUAcFTGIhHunw9dy41CdgnZIEQCxb7R8tBv
-isRlG0cIpO5159LB3NECR4++xBB02nq6lOjysKDmYuWYuQakD1u9L6R+LQBVTxYL
-/iEK8wyf18n/iKUEGAEIAA8FAlX7/OgCGwwFCQHhM4AACgkQWIWZde43z5ZTvAP9
-EWGZu97aZhjIbD18Y2HjbXQn4L6iyeDMuM++Tsnnn57li+HLUAX8ieRHy1l/VE3t
-HhdcqRqAsrxnkGAWKMlYYZS9WHDzrffxtQlszOwpAOWdNDsWsPdbko95XvLatoqk
-t9KxB19sLao6eCBKwB9muMs10i86P+Cehwh97n/UNGOWAAACBgRV+/07AQQAxCWd
-rsUW2IhexMxOvMi32Z63bOEC5JkEy8tntGYwk54I2XGXRebdutMrXqh0nKO7p23k
-gfWjRp1dpbSp20AzdIkwsRlAjOuqhZ3Q6t+kP6xWtxAQI8YZ6lQ0VeZC0dTBllr3
-UlY4tw0emLcScNsGuDVUPYhQoJBMkk4oNw+wWfUAEQEAAf4HAwJNRwdntiqzHO76
-GxxlNilWuwitCGbGwZfmo8K8m2uAMzSKsxUp16rcLVvfQsEzS6rDhF4VbJQyLvZJ
-LDkXB0/DFbPVrxG8byJ2i6WKUzsqcevM29OXOmFfH1NVuVi5oUWbwCR6ctsNQSL7
-Bje0E6+6pme9YQtKgUIBzc2Dw+nq6WjfLc0aEc+rrXzWsJKEUKkjnaUa/AeAVYyO
-rTOk5fLrw6vy/sKsuScvLNvQUrr7U+g69gpk53Cyw2WILlADxbysg2CDMDsDmXk/
-sK6zikAgDjQTRaOJkX4BzCBoqZRaDbLMfze6kA6cwQqDTsUELy1ziH56FjRXuBqj
-D4IziA0/XE8gyMRtoMYXmF0pKBQh0RLoudorcPQE9PCFvKaXmASA80nMeBoYxlIm
-kPMBkkkwiXU4irc1m8phlcrZjYE12pxzWgSYBEwTbbzNe2EcFKf+H1vp9DXqZSua
-wLdiUx6JrSHGzoPl3XFAQXNFoOEGvlFN9nH+tBNUZXN0aW5nIChpbnNlY3VyZSEp
-iL0EEwEIACcFAlX7/TsCGwMFCQHhM4AFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AA
-CgkQys7ZlrwVyFq0NgP/cazey0+qJrTaQ0Z6eab1p8PMFE8BpcegrokxfJn61zo7
-JECjQW+htoOBBIQH32mtqjO/J/SbiBDp3xNcdabCnkphW4jkcgn+FoUbLA3GFk9f
-xtElNDGXHcQNimvhhxfrEr2Mi1yo2rKShiIO0N2yySXCJJIC9CXpDCAIhNdEYeCe
-AAACBQRV+/07AQQA3BJN5N1RI6uesA03xwTW1ABTV4tbjLROKLlTPbxb+TjWQAfQ
-lztbSavzjTO6wPPmHnGv2sXPiH2guET+thKAw1WchItKx+MiT8nnsBJHl950mqI8
-uTHGljkQBuKARVl1ELS3do6CQvGyG+5qHyl3crpED152Q5C/F53b4EfgNXEAEQEA
-Af4HAwL449o07unvl+6XONg4R9pVE0Qp0xCL5CmjhwlL8lUuGTvjciN+lXD6k7VH
-Xj9Wu86alkKZQKyZxESPtsRR5dGWgrvhmUrvPftRmO4PV7A5AS0yi54CQGaWSnOL
-nqVkENUs85Pq1LLfnM8MRIdGpS9225bwsAoB/eJk7zKNRGOUlzCDGW3f12aemyrR
-2RHGVPOvn6SVb8r8RkqCDMApR0j76cTMDiMyaGByi93y8qhXiu88Y+J/+fK5wQis
-FwPJGZVCqNTiglclgrNG4+z8G4SUvkA6W5yDiZyftN67TXqxJKKBXFS5gzWujPti
-boDzivsY9sP4Mkoc94TAmJeaLtNrqHy4UMo/m9YBmuP4hRJ7TCKmvVN4hZCN2mvJ
-4S1vi4Z9GnyxJAbxq9Gb1UA9glVAVt6bQVYO6ySIp4W29xFnoRUm4i0tCovWBn9x
-MWSkG5SLznbh2tKLN0uJGzh4G8xo2fdfx6tWy2x0gw95T5WDg7S2oe6IpQQYAQgA
-DwUCVfv9OwIbDAUJAeEzgAAKCRDKztmWvBXIWqexA/9nZUXs9BGcwpodhqjGY+H9
-/IUJua95jti9t0BleEu+h0R9O+XDEE/77IK9ET4f0t9WMfMhPO7ZIgUxFutB/Z7U
-MuyVteIvGxF/TTbQAKuCrnLYuPWkGiYjR9e0ZDbgmKrRZ/jwhdaxF0IHrR1PJLUn
-vO97qfZC7097/urCsWDMo5YAAAIGBFX8ElYBBACfcdcAcR6BJ2Ba3/HnQR1S0rG3
-8bWq8Rdtt072hDd16oQCNFpQs5WQNruCCpobmB6yOmjKJv8Cf9mxBdcQDxobcw6M
-lHPWZl04SoQKQOa5h6ptITxr+UFFFqfh7AZ7ZtDYaFfBqQX9fvdOX99C18SIcCcN
-0rHoxXfG7D/AaHEysQARAQAB/gcDAj0P/+idN7Q87sZYs1aBo3OqKKdl+a51tcgd
-80HdoEQWyIwOStl9+XleUHyrU5f9kni1I2NCrl+hLyPGaT8dGJinH103fgsGvY/L
-Z2lg5gsPdfb5U5Kyn8MfgAuAEVh0XiLOAVZf4tVjcn3jGW9VM/cDHQI9uwz0MtN0
-xxj1iw151/ydtFt4Qw+Ljh0cwBauiHSaG8rhfObJGbKpXNBJG6QfaGBlOAErO1my
-fr7UgWbul6xCZe/t7Um2rp5GxTJsN+AwDDLqSbwCzmArXRJiEnL5qaw891HuXTIC
-+lxtGNxP6bqe+4Bg/T+MIjJVWzx9avGR2WweSKBqbsyRkmZQCIkWDmp/g9t17ujo
-RrzNUT60Y0gMhJOQxZcgdXJtlT/X0RvP+tGAiVEAlvpQ+9RTzqvf4sZAPndpE4PY
-dKXJF5Pua9cWU+UceQV/Nr+JAlLzNWOlwSOJUVGsQ+RzeFJyB2D5xoG6tRI9idYU
-V+vcNGRpJzsXO6S0E1Rlc3RpbmcgKGluc2VjdXJlISmIvQQTAQgAJwUCVfwSVgIb
-AwUJAeEzgAULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRA8WpFfIYODmknrA/96
-90yhjN3ELmWSJetKzvt7MlUS0j6UkA5VvDObCmAm+bDrQSGdwDJj6gu88b4biNEx
-Cz/Dmo67R9Z+gLE6LGvzYCPZ+GE/ZQ9VMo/AeUEZO44Aa7vRwnYFU0VmMJUeGQbC
-Je4JnLjF/+0yIgh/CtwFL3J/+9eayf6e6L/9WhUZ5J4AAAIGBFX8ElYBBADXznv8
-7J5i/EN8dMtjzx99LXtJdSJ3iJfp69d5V1FygvsDSlMZVekflWKF2ipHRulxLXea
-8mH0salQviQ32qPAyfCWpELLL2srTVezj6ntKVF9hZruQ2d1KBVV+syq6nSY9Eg8
-0mHizvIV5cR2b2X/X6qybJrwhW10oWh+cuLg6QARAQAB/gcDAkwZfkpx6rGW7qkb
-iuwl3c6d1o2x9HeiZG8fZ8UGU5n0Nx4bp4a60j/d+bJowww8sPRcJ+8mi/dNi9dC
-1Dls2CmmOP8U2DsPT189d+JiqlXUumhRyTo5ptglMrHkrMp489QpyCIUhW6HVopI
-ppdOJGE0kTJ7pRx0fevz3la5553IyglJ9iUqgxz2+9XlvDhSplz8zVhyZd5UPW94
-hi+vHCDf3TSakMFFZEVPCQaMunB7urI1wXx/mOT5BTSOp1PVq4SE5TtC2/GrHBU6
-/5wuqyhlT3oH+jF/GfvZQgattnkaFn/JY77/mfTCzyQb1/2iQMO8uTe8KjWAKd5h
-AoCcgxoX0rqSxe7YS2Obl1v0icWbg4wvI8WUAv5pRL7EMVcuUugrb40rWzOiJzYY
-IwEmO+tp08Ev+arbjEMzk+IXLTr3wDip/2oHHU3P2OSi46iLdueUvVnnNXff0H4e
-mqT2zlJQoPCbYMaKxL0yxvFnZLfCWolLOJaIpQQYAQgADwUCVfwSVgIbDAUJAeEz
-gAAKCRA8WpFfIYODmqzxBACNLC9j2EJvoiKhRMAUJTGCQvDWNWAI/2Ln/61Ftqu5
-+OoOI0N7uL1LjWNHrhS/PMKwcIu9iZn/uQV/OGj9YuKw58WeyKkTIEnD7bU5aUQk
-8jdRITPnr/InyHvs21P9hh18MZvDk9L9rL+uwK+9BkeL0MDL3wlAG57Fay9OXgY1
-CQ==
-=2SlE
------END PGP PRIVATE KEY BLOCK-----
+++ /dev/null
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v2
-
-mI0EVfv86AEEAN20yizZgtnQaJPUV++9Z+rRg4XzjWpLvmiWMpTsn8qhjpySkAa4
-/4P4/MRWVvSXiRC1uJ7T59Sbm/KFs8TdKaqIMuON3QYjztxm2NmDMA/f5FTvRuLk
-gKAEpwGOqI1Zvm3uleH8hkx0n45tHxCI3bLCfW+12lZxJCGNDBnhvj+5ABEBAAG0
-E1Rlc3RpbmcgKGluc2VjdXJlISmIvQQTAQgAJwUCVfv86AIbAwUJAeEzgAULCQgH
-AgYVCAkKCwIEFgIDAQIeAQIXgAAKCRBYhZl17jfPlqj2BACFvqgKBZ3NnphZ8f2/
-rylvV9YNzS5rC8lFASN+BgofrBmugtEqbXSnOvLrylHOnWgYh7qLpDeBfta2FLjR
-vFjTLFfycBjylQAzyBa4BoqLTFu+9GZ82bKKhlr8FJ8q5WlCOgGB3uFI/WAJp5ou
-uOwP7lMXCNR9tDN3RDUlMoktc7iNBFX7/OgBBADIGia8IRToRJhx4Kss8dihcxuE
-gxeo8gINQiFSpoi0gXzV1muN7ycH8S6O7209cMtIoiRfPgCw/4IaaZmW/TabIlAv
-l8U+Lm5Tkr0YDfudu1nVF/MptBF8Y6bkklvoVWWcFD9CtHwXSFWlQmsCRhKeS3P5
-lyp45l7y0MVkkGIAfQARAQABiKUEGAEIAA8FAlX7/OgCGwwFCQHhM4AACgkQWIWZ
-de43z5ZTvAP9EWGZu97aZhjIbD18Y2HjbXQn4L6iyeDMuM++Tsnnn57li+HLUAX8
-ieRHy1l/VE3tHhdcqRqAsrxnkGAWKMlYYZS9WHDzrffxtQlszOwpAOWdNDsWsPdb
-ko95XvLatoqkt9KxB19sLao6eCBKwB9muMs10i86P+Cehwh97n/UNGOYjQRV+/07
-AQQAxCWdrsUW2IhexMxOvMi32Z63bOEC5JkEy8tntGYwk54I2XGXRebdutMrXqh0
-nKO7p23kgfWjRp1dpbSp20AzdIkwsRlAjOuqhZ3Q6t+kP6xWtxAQI8YZ6lQ0VeZC
-0dTBllr3UlY4tw0emLcScNsGuDVUPYhQoJBMkk4oNw+wWfUAEQEAAbQTVGVzdGlu
-ZyAoaW5zZWN1cmUhKYi9BBMBCAAnBQJV+/07AhsDBQkB4TOABQsJCAcCBhUICQoL
-AgQWAgMBAh4BAheAAAoJEMrO2Za8FchatDYD/3Gs3stPqia02kNGenmm9afDzBRP
-AaXHoK6JMXyZ+tc6OyRAo0FvobaDgQSEB99praozvyf0m4gQ6d8TXHWmwp5KYVuI
-5HIJ/haFGywNxhZPX8bRJTQxlx3EDYpr4YcX6xK9jItcqNqykoYiDtDdssklwiSS
-AvQl6QwgCITXRGHguI0EVfv9OwEEANwSTeTdUSOrnrANN8cE1tQAU1eLW4y0Tii5
-Uz28W/k41kAH0Jc7W0mr840zusDz5h5xr9rFz4h9oLhE/rYSgMNVnISLSsfjIk/J
-57ASR5fedJqiPLkxxpY5EAbigEVZdRC0t3aOgkLxshvuah8pd3K6RA9edkOQvxed
-2+BH4DVxABEBAAGIpQQYAQgADwUCVfv9OwIbDAUJAeEzgAAKCRDKztmWvBXIWqex
-A/9nZUXs9BGcwpodhqjGY+H9/IUJua95jti9t0BleEu+h0R9O+XDEE/77IK9ET4f
-0t9WMfMhPO7ZIgUxFutB/Z7UMuyVteIvGxF/TTbQAKuCrnLYuPWkGiYjR9e0ZDbg
-mKrRZ/jwhdaxF0IHrR1PJLUnvO97qfZC7097/urCsWDMo5iNBFX8ElYBBACfcdcA
-cR6BJ2Ba3/HnQR1S0rG38bWq8Rdtt072hDd16oQCNFpQs5WQNruCCpobmB6yOmjK
-Jv8Cf9mxBdcQDxobcw6MlHPWZl04SoQKQOa5h6ptITxr+UFFFqfh7AZ7ZtDYaFfB
-qQX9fvdOX99C18SIcCcN0rHoxXfG7D/AaHEysQARAQABtBNUZXN0aW5nIChpbnNl
-Y3VyZSEpiL0EEwEIACcFAlX8ElYCGwMFCQHhM4AFCwkIBwIGFQgJCgsCBBYCAwEC
-HgECF4AACgkQPFqRXyGDg5pJ6wP/evdMoYzdxC5lkiXrSs77ezJVEtI+lJAOVbwz
-mwpgJvmw60EhncAyY+oLvPG+G4jRMQs/w5qOu0fWfoCxOixr82Aj2fhhP2UPVTKP
-wHlBGTuOAGu70cJ2BVNFZjCVHhkGwiXuCZy4xf/tMiIIfwrcBS9yf/vXmsn+nui/
-/VoVGeS4jQRV/BJWAQQA1857/OyeYvxDfHTLY88ffS17SXUid4iX6evXeVdRcoL7
-A0pTGVXpH5VihdoqR0bpcS13mvJh9LGpUL4kN9qjwMnwlqRCyy9rK01Xs4+p7SlR
-fYWa7kNndSgVVfrMqup0mPRIPNJh4s7yFeXEdm9l/1+qsmya8IVtdKFofnLi4OkA
-EQEAAYilBBgBCAAPBQJV/BJWAhsMBQkB4TOAAAoJEDxakV8hg4OarPEEAI0sL2PY
-Qm+iIqFEwBQlMYJC8NY1YAj/Yuf/rUW2q7n46g4jQ3u4vUuNY0euFL88wrBwi72J
-mf+5BX84aP1i4rDnxZ7IqRMgScPttTlpRCTyN1EhM+ev8ifIe+zbU/2GHXwxm8OT
-0v2sv67Ar70GR4vQwMvfCUAbnsVrL05eBjUJ
-=Btw1
------END PGP PUBLIC KEY BLOCK-----
(load (with-path "defs.scm"))
(setup-environment)
- ;; Redefine GPG without --always-trust and a fixed time.
-(define GPG `(,(tool 'gpg) --no-permission-warning
- --faked-system-time=1466684990))
+(define GPGTIME 1480943782)
+
+;; Generate a --faked-system-time parameter for a particular offset.
+(define (faketime delta)
+ (string-append "--faked-system-time=" (number->string (+ GPGTIME delta))))
+;; A convenience function for the above.
+(define (days->seconds days) (* days 24 60 60))
+
+;; Redefine GPG without --always-trust and a fixed time.
+(define GPG `(,(tool 'gpg) --no-permission-warning ,(faketime GPGTIME)))
(define GNUPGHOME (getenv "GNUPGHOME"))
(if (string=? "" GNUPGHOME)
- (error "GNUPGHOME not set"))
+ (fail "GNUPGHOME not set"))
(catch (skip "Tofu not supported")
(call-check `(,@GPG --trust-model=tofu --list-config)))
-(define KEYS '("2183839A" "BC15C85A" "EE37CF96"))
+(define KEYS '("1C005AF3" "BE04EB2B" "B662E42F"))
;; Import the test keys.
-(call-check `(,@GPG --import ,(in-srcdir "tofu-keys.asc")))
-
-;; Make sure the keys are imported.
(for-each (lambda (keyid)
- (catch (error "Missing key" keyid)
+ (call-check `(,@GPG --import
+ ,(in-srcdir "tofu/conflicting/"
+ (string-append keyid ".gpg"))))
+ (catch (fail "Missing key" keyid)
(call-check `(,@GPG --list-keys ,keyid))))
KEYS)
,@args
--list-keys ,keyid))) 5)))
(unless (member policy '("auto" "good" "unknown" "bad" "ask"))
- (error "Bad policy:" policy))
+ (fail "Bad policy:" policy))
policy))
;; Check that KEYID's tofu policy matches EXPECTED-POLICY. Any
(define (checkpolicy keyid expected-policy . args)
(let ((policy (apply getpolicy `(,keyid ,@args))))
(unless (string=? policy expected-policy)
- (error keyid ": Expected policy to be" expected-policy
+ (fail keyid ": Expected policy to be" expected-policy
"but got" policy))))
;; Get the trust level for KEYID. Any remaining arguments are simply
--list-keys ,keyid))) 1)))
(unless (and (= 1 (string-length trust))
(member (string-ref trust 0) (string->list "oidreqnmfuws-")))
- (error "Bad trust value:" trust))
+ (fail "Bad trust value:" trust))
trust))
;; Check that KEYID's trust level matches EXPECTED-TRUST. Any
(define (checktrust keyid expected-trust . args)
(let ((trust (apply gettrust `(,keyid ,@args))))
(unless (string=? trust expected-trust)
- (error keyid ": Expected trust to be" expected-trust
+ (fail keyid ": Expected trust to be" expected-trust
"but got" trust))))
;; Set key KEYID's policy to POLICY. Any remaining arguments are
;; Verify a message. There should be no conflict and the trust
;; policy should be set to auto.
(call-check `(,@GPG --trust-model=tofu
- --verify ,(in-srcdir "tofu-2183839A-1.txt")))
+ --verify ,(in-srcdir "tofu/conflicting/1C005AF3-1.txt")))
-(checkpolicy "2183839A" "auto")
+(checkpolicy "1C005AF3" "auto")
;; Check default trust.
-(checktrust "2183839A" "m")
+(checktrust "1C005AF3" "m")
;; Trust should be derived lazily. Thus, if the policy is set to
;; auto and we change --tofu-default-policy, then the trust should
;; change as well. Try it.
-(checktrust "2183839A" "f" '--tofu-default-policy=good)
-(checktrust "2183839A" "-" '--tofu-default-policy=unknown)
-(checktrust "2183839A" "n" '--tofu-default-policy=bad)
+(checktrust "1C005AF3" "f" '--tofu-default-policy=good)
+(checktrust "1C005AF3" "-" '--tofu-default-policy=unknown)
+(checktrust "1C005AF3" "n" '--tofu-default-policy=bad)
;; Change the policy to something other than auto and make sure the
;; policy and the trust are correct.
((string=? "good" policy) "f")
((string=? "unknown" policy) "-")
(else "n"))))
- (setpolicy "2183839A" policy)
+ (setpolicy "1C005AF3" policy)
;; Since we have a fixed policy, the trust level shouldn't
;; change if we change the default policy.
(for-each-p
""
(lambda (default-policy)
- (checkpolicy "2183839A" policy
+ (checkpolicy "1C005AF3" policy
'--tofu-default-policy default-policy)
- (checktrust "2183839A" expected-trust
+ (checktrust "1C005AF3" expected-trust
'--tofu-default-policy default-policy))
'("auto" "good" "unknown" "bad" "ask"))))
'("good" "unknown" "bad"))
-;; BC15C85A conflicts with 2183839A. On conflict, this will set
-;; BC15C85A to ask. If 2183839A is auto (it's not, it's bad), then
-;; it will be set to ask.
+;; At the end, 1C005AF3's policy should be bad.
+(checkpolicy "1C005AF3" "bad")
+
+;; 1C005AF3 and BE04EB2B conflict. A policy setting of "auto"
+;; (BE04EB2B's state) will result in an effective policy of ask. But,
+;; a policy setting of "bad" will result in an effective policy of
+;; bad.
+(setpolicy "BE04EB2B" "auto")
+(checkpolicy "BE04EB2B" "ask")
+(checkpolicy "1C005AF3" "bad")
+
+;; 1C005AF3, B662E42F, and BE04EB2B conflict. We change BE04EB2B's
+;; policy to auto and leave 1C005AF3's policy at bad. This conflict
+;; should cause BE04EB2B's effective policy to be ask (since it is
+;; auto), but not affect 1C005AF3's policy.
+(setpolicy "BE04EB2B" "auto")
+(checkpolicy "BE04EB2B" "ask")
+(call-check `(,@GPG --trust-model=tofu
+ --verify ,(in-srcdir "tofu/conflicting/B662E42F-1.txt")))
+(checkpolicy "BE04EB2B" "ask")
+(checkpolicy "1C005AF3" "bad")
+(checkpolicy "B662E42F" "ask")
+
+;; Check that the stats are emitted correctly.
+
+(display "Checking TOFU stats...\n")
+
+(define (check-counts keyid expected-sigs expected-sig-days
+ expected-encs expected-enc-days . args)
+ (let*
+ ((tfs (assoc "tfs"
+ (gpg-with-colons
+ `(--trust-model=tofu --with-tofu-info
+ ,@args --list-keys ,keyid))))
+ (sigs (string->number (list-ref tfs 3)))
+ (sig-days (string->number (list-ref tfs 11)))
+ (encs (string->number (list-ref tfs 4)))
+ (enc-days (string->number (list-ref tfs 12)))
+ )
+ ; (display keyid) (display ": ") (display tfs) (display "\n")
+ (unless (= sigs expected-sigs)
+ (fail keyid ": # signatures (" sigs ") does not match expected"
+ "# signatures (" expected-sigs ").\n"))
+ (unless (= sig-days expected-sig-days)
+ (fail keyid ": # signature days (" sig-days ")"
+ "does not match expected"
+ "# signature days (" expected-sig-days ").\n"))
+ (unless (= encs expected-encs)
+ (fail keyid ": # encryptions (" encs ") does not match expected"
+ "# encryptions (" expected-encs ").\n"))
+ (unless (= enc-days expected-enc-days)
+ (fail keyid ": # encryption days (" encs ")"
+ "does not match expected"
+ "# encryption days (" expected-enc-days ").\n"))
+ ))
+
+;; Carefully remove the TOFU db.
+(catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
+
+(check-counts "1C005AF3" 0 0 0 0)
+(check-counts "BE04EB2B" 0 0 0 0)
+(check-counts "B662E42F" 0 0 0 0)
+
+;; Verify a message. The signature count should increase by 1.
(call-check `(,@GPG --trust-model=tofu
- --verify ,(in-srcdir "tofu-BC15C85A-1.txt")))
-(checkpolicy "BC15C85A" "ask")
-(checkpolicy "2183839A" "bad")
-
-;; EE37CF96 conflicts with 2183839A and BC15C85A. We change
-;; BC15C85A's policy to auto and leave 2183839A's policy at bad.
-;; This conflict should cause BC15C85A's policy to be changed to
-;; ask (since it is auto), but not affect 2183839A's policy.
-(setpolicy "BC15C85A" "auto")
-(checkpolicy "BC15C85A" "auto")
+ --verify ,(in-srcdir "tofu/conflicting/1C005AF3-1.txt")))
+
+(check-counts "1C005AF3" 1 1 0 0)
+
+;; Verify the same message. The signature count should remain the
+;; same.
(call-check `(,@GPG --trust-model=tofu
- --verify ,(in-srcdir "tofu-EE37CF96-1.txt")))
-(checkpolicy "BC15C85A" "ask")
-(checkpolicy "2183839A" "bad")
-(checkpolicy "EE37CF96" "ask")
+ --verify ,(in-srcdir "tofu/conflicting/1C005AF3-1.txt")))
+(check-counts "1C005AF3" 1 1 0 0)
+;; Verify another message.
+(call-check `(,@GPG --trust-model=tofu
+ --verify ,(in-srcdir "tofu/conflicting/1C005AF3-2.txt")))
+(check-counts "1C005AF3" 2 1 0 0)
+;; Verify another message.
+(call-check `(,@GPG --trust-model=tofu
+ --verify ,(in-srcdir "tofu/conflicting/1C005AF3-3.txt")))
+(check-counts "1C005AF3" 3 1 0 0)
+
+;; Verify a message from a different sender. The signature count
+;; should increase by 1 for that key.
+(call-check `(,@GPG --trust-model=tofu
+ --verify ,(in-srcdir "tofu/conflicting/BE04EB2B-1.txt")))
+(check-counts "1C005AF3" 3 1 0 0)
+(check-counts "BE04EB2B" 1 1 0 0)
+(check-counts "B662E42F" 0 0 0 0)
+
+;; Verify another message on a new day. (Recall: we are interested in
+;; when the message was first verified, not when the signer claimed
+;; that it was signed.)
+(call-check `(,@GPG --trust-model=tofu ,(faketime (days->seconds 2))
+ --verify ,(in-srcdir "tofu/conflicting/1C005AF3-4.txt")))
+(check-counts "1C005AF3" 4 2 0 0)
+(check-counts "BE04EB2B" 1 1 0 0)
+(check-counts "B662E42F" 0 0 0 0)
+
+;; And another.
+(call-check `(,@GPG --trust-model=tofu ,(faketime (days->seconds 2))
+ --verify ,(in-srcdir "tofu/conflicting/1C005AF3-5.txt")))
+(check-counts "1C005AF3" 5 2 0 0)
+(check-counts "BE04EB2B" 1 1 0 0)
+(check-counts "B662E42F" 0 0 0 0)
+
+;; Another, but for a different key.
+(call-check `(,@GPG --trust-model=tofu ,(faketime (days->seconds 2))
+ --verify ,(in-srcdir "tofu/conflicting/BE04EB2B-2.txt")))
+(check-counts "1C005AF3" 5 2 0 0)
+(check-counts "BE04EB2B" 2 2 0 0)
+(check-counts "B662E42F" 0 0 0 0)
+
+;; And add a third day.
+(call-check `(,@GPG --trust-model=tofu ,(faketime (days->seconds 4))
+ --verify ,(in-srcdir "tofu/conflicting/BE04EB2B-3.txt")))
+(check-counts "1C005AF3" 5 2 0 0)
+(check-counts "BE04EB2B" 3 3 0 0)
+(check-counts "B662E42F" 0 0 0 0)
+
+(call-check `(,@GPG --trust-model=tofu ,(faketime (days->seconds 4))
+ --verify ,(in-srcdir "tofu/conflicting/BE04EB2B-4.txt")))
+(check-counts "1C005AF3" 5 2 0 0)
+(check-counts "BE04EB2B" 4 3 0 0)
+(check-counts "B662E42F" 0 0 0 0)
;; Check that we detect the following attack:
;;
(checkpolicy KEYA "ask")
(checkpolicy KEYB "ask")
-;; Import Alice's signature on the conflicting user id.
+;; Import Alice's signature on the conflicting user id. Since there
+;; is now a cross signature, we should revert to the default policy.
(display " > Adding cross signature on user id. ")
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-4.gpg"))))
(verify-messages)
;; Alice has an ultimately trusted key and she signs Bob's key. Then
;; Bob adds a new user id, "Alice". TOFU should now detect a
;; conflict, because Alice only signed Bob's "Bob" user id.
+;;
+;;
+;; The Alice key:
+;; pub rsa2048 2016-10-11 [SC]
+;; 1938C3A0E4674B6C217AC0B987DB2814EC38277E
+;; uid [ultimate] Spy Cow <spy@cow.com>
+;; sub rsa2048 2016-10-11 [E]
+;;
+;; The Bob key:
+;;
+;; pub rsa2048 2016-10-11 [SC]
+;; DC463A16E42F03240D76E8BA8B48C6BD871C2247
+;; uid [ full ] Spy R. Cow <spy@cow.com>
+;; uid [ full ] Spy R. Cow <spy@cow.de>
+;; sub rsa2048 2016-10-11 [E]
(display "Checking UTK sigs...\n")
(define GPG `(,(tool 'gpg) --no-permission-warning
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-1.gpg"))))
(display "<\n")
+(checkpolicy KEYA "auto")
+(checkpolicy KEYB "auto")
+
;; Import the cross sigs.
(display " > Adding cross signatures. ")
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-2.gpg"))))
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-2.gpg"))))
(display "<\n")
+(checkpolicy KEYA "auto")
+(checkpolicy KEYB "auto")
+
;; Make KEYA ultimately trusted.
(display (string-append " > Marking " KEYA " as ultimately trusted. "))
(pipe:do
--- /dev/null
+£\ 1\9bÀËÌÀÁ¨%¶¼\7f[Ò\13}ÆÓ\1cI\f\11®i\92¦\\9d\8cÆ,\f\8c\1c\f²b\8a,ù&ÖkÏhí³\89ÒÓ\8c\86)ee\ 2©càâ\14\80\89h&q0lÊ;S\93ɵ´`ÅâKË\14¾>\9e¾}iÚ\1fñªwò²ö5?VÙðF+\ 6' |]ó$í/\9fôj×m\95³ÓJÄ\1fß\96TjY\19\91À¤Á£<ÿø\99i»½6%Æ|ªÿ÷¢aûÿGVkgË4e\9dµ<akï\11¿õ\14ÓB\eî U£o¿ffø\17öZ<Úñ\8d\8dU[ÕÔhþÞJe\81\15Ò\1fÂníóÜýÞë¤\99þ\9c¶\9aâ\9c\rf~÷[\94dm"v²\95?P\7f3}=×ü\8c\8c}Ó#"ò^µ±¬\9dj-µ\91×ùTÒ\81²Õ\9c+/q=±&Ü>̬¸²å\81üëÏÕÕ%îíkºzù\8aNëìýlʳê\14×\9e\9f\9a\9e©:æü\91\9d\8aÏ7\16zü\9b\9eÎÐ\\eoÞµs\99úJÅs1éÚñû\ 1
\ No newline at end of file
--- /dev/null
+This directory contains three keys (1C005AF3, B662E42F, and BE04EB2B),
+which all have the same user id, namely "Joke Factory
+<joke.factory@example.com>".
+
+The keys are stored in KEYID.gpg. The secret key material is stored
+in KEYID-secret.gpg (the secret key material is not password
+protected). The files KEYID-N.txt contain messages, which are signed
+by KEYID. The message is "N\n".
--- /dev/null
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (with-path "defs.scm"))
+(setup-legacy-environment)
+
+(define files '("clearsig-1-key-1.asc" "signed-1-key-1.asc"))
+
+(info "Checking verification of supplied files using --multifile.")
+
+(let* ((status
+ (call-popen
+ `(,@gpg --verify --multifile --status-fd=1
+ ,@(map (lambda (name) (in-srcdir "samplemsgs" name)) files))
+ ""))
+ (lines (map (lambda (l)
+ (assert (string-prefix? l "[GNUPG:] "))
+ ;; Split, and strip the prefix.
+ (cdr (string-split l #\space)))
+ (string-split-newlines status))))
+ (assert
+ (= 2 (length (filter (lambda (l)
+ (and (equal? (car l) "GOODSIG")
+ (equal? (caddr l) "steve.biko@example.net")))
+ lines)))))
(display (make-string 64 (integer->char (string->number char)))
port)))
(if (= 0 (call `(,@GPG --verify ,x data-500)))
- (error "no error code from verify"))))
+ (fail "no error code from verify"))))
'("#x2d" "#xca"))
;; A plain signed message created using
(pipe:defer (lambda (sink)
(display armored-file (fdopen sink "w"))))
(pipe:spawn `(,@GPG --verify)))
- (error "verification succeeded but should not")))
+ (fail "verification succeeded but should not")))
'(msg_olsols_asc_multiple msg_clsclss_asc_multiple))
(for-each-p
(pipe:do
(pipe:echo (eval armored-file (current-environment)))
(pipe:spawn `(,@GPG --verify)))
- (error "verification succeeded but should not")))
+ (fail "verification succeeded but should not")))
'(bad_ls_asc bad_fols_asc bad_olsf_asc bad_ools_asc))
gpg_wks_server_CFLAGS = $(GPG_ERROR_CFLAGS) $(INCICONV)
gpg_wks_server_LDADD = $(libcommon) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
- $(LIBICONV)
+ $(LIBINTL) $(LIBICONV)
gpg_wks_client_SOURCES = \
gpg-wks-client.c \
gpg_wks_client_CFLAGS = $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV)
gpg_wks_client_LDADD = $(libcommon) \
$(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
- $(LIBICONV)
+ $(LIBINTL) $(LIBICONV)
# Make sure that all libs are build before we use them. This is
assuan_release (ctx);
return err;
}
+
+
+/* Ask the dirmngr for the key for ADDRSPEC. On success a stream with
+ * the key is stored at R_KEY. */
+gpg_error_t
+wkd_get_key (const char *addrspec, estream_t *r_key)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct wkd_get_parm_s parm;
+ char *line = NULL;
+
+ memset (&parm, 0, sizeof parm);
+ *r_key = NULL;
+
+ err = connect_dirmngr (&ctx);
+ if (err)
+ return err;
+
+ line = es_bsprintf ("WKD_GET -- %s", addrspec);
+ if (!line)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (strlen (line) + 2 >= ASSUAN_LINELENGTH)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ goto leave;
+ }
+
+ parm.memfp = es_fopenmem (0, "rwb");
+ if (!parm.memfp)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = assuan_transact (ctx, line, wkd_get_data_cb, &parm,
+ NULL, NULL, wkd_get_status_cb, &parm);
+ if (err)
+ goto leave;
+
+ es_rewind (parm.memfp);
+ *r_key = parm.memfp;
+ parm.memfp = NULL;
+
+ leave:
+ es_fclose (parm.memfp);
+ xfree (line);
+ assuan_release (ctx);
+ return err;
+}
char **r_addrspec);
gpg_error_t wkd_get_policy_flags (const char *addrspec, estream_t *r_buffer);
+gpg_error_t wkd_get_key (const char *addrspec, estream_t *r_key);
+
#endif /*GNUPG_TOOLS_CALL_DIRMNGR_H*/
#include <string.h>
#include "util.h"
+#include "status.h"
#include "i18n.h"
#include "sysutils.h"
#include "init.h"
oDebug = 500,
aSupported,
+ aCheck,
aCreate,
aReceive,
aRead,
oGpgProgram,
oSend,
oFakeSubmissionAddr,
+ oStatusFD,
oDummy
};
ARGPARSE_c (aSupported, "supported",
("check whether provider supports WKS")),
+ ARGPARSE_c (aCheck, "check",
+ ("check whether a key is available")),
ARGPARSE_c (aCreate, "create",
("create a publication request")),
ARGPARSE_c (aReceive, "receive",
ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
ARGPARSE_s_n (oSend, "send", "send the mail using sendmail"),
ARGPARSE_s_s (oOutput, "output", "|FILE|write the mail to FILE"),
+ ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")),
ARGPARSE_s_s (oFakeSubmissionAddr, "fake-submission-addr", "@"),
static void wrong_args (const char *text) GPGRT_ATTR_NORETURN;
static gpg_error_t command_supported (char *userid);
+static gpg_error_t command_check (char *userid);
static gpg_error_t command_send (const char *fingerprint, char *userid);
static gpg_error_t encrypt_response (estream_t *r_output, estream_t input,
const char *addrspec,
case oFakeSubmissionAddr:
fake_submission_addr = pargs->r.ret_str;
break;
+ case oStatusFD:
+ wks_set_status_fd (translate_sys2libc_fd_int (pargs->r.ret_int, 1));
+ break;
case aSupported:
case aCreate:
case aReceive:
case aRead:
+ case aCheck:
cmd = pargs->r_opt;
break;
log_error ("processing mail failed: %s\n", gpg_strerror (err));
break;
+ case aCheck:
+ if (argc != 1)
+ wrong_args ("--check USER-ID");
+ err = command_check (argv[0]);
+ break;
+
default:
usage (1);
+ err = 0;
break;
}
+ if (err)
+ wks_write_status (STATUS_FAILURE, "- %u", err);
+ else if (log_get_errorcount (0))
+ wks_write_status (STATUS_FAILURE, "- %u", GPG_ERR_GENERAL);
+ else
+ wks_write_status (STATUS_SUCCESS, NULL);
return log_get_errorcount (0)? 1:0;
}
\f
+/* Check whether the key for USERID is available in the WKD. */
+static gpg_error_t
+command_check (char *userid)
+{
+ gpg_error_t err;
+ char *addrspec = NULL;
+ estream_t key = NULL;
+ char *fpr = NULL;
+ strlist_t mboxes = NULL;
+ strlist_t sl;
+ int found = 0;
+
+ addrspec = mailbox_from_userid (userid);
+ if (!addrspec)
+ {
+ log_error (_("\"%s\" is not a proper mail address\n"), userid);
+ err = gpg_error (GPG_ERR_INV_USER_ID);
+ goto leave;
+ }
+
+ /* Get the submission address. */
+ err = wkd_get_key (addrspec, &key);
+ switch (gpg_err_code (err))
+ {
+ case 0:
+ if (opt.verbose)
+ log_info ("public key for '%s' found via WKD\n", addrspec);
+ /* Fixme: Check that the key contains the user id. */
+ break;
+
+ case GPG_ERR_NO_DATA: /* No such key. */
+ if (opt.verbose)
+ log_info ("public key for '%s' NOT found via WKD\n", addrspec);
+ err = gpg_error (GPG_ERR_NO_PUBKEY);
+ log_inc_errorcount ();
+ break;
+
+ case GPG_ERR_UNKNOWN_HOST:
+ if (opt.verbose)
+ log_info ("error looking up '%s' via WKD: %s\n",
+ addrspec, gpg_strerror (err));
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ break;
+
+ default:
+ log_error ("error looking up '%s' via WKD: %s\n",
+ addrspec, gpg_strerror (err));
+ break;
+ }
+
+ if (err)
+ goto leave;
+
+ /* Look closer at the key. */
+ err = wks_list_key (key, &fpr, &mboxes);
+ if (err || !fpr)
+ {
+ log_error ("error parsing key: %s\n",
+ err? gpg_strerror (err) : "no fingerprint found");
+ err = gpg_error (GPG_ERR_NO_PUBKEY);
+ goto leave;
+ }
+
+ if (opt.verbose)
+ log_info ("fingerprint: %s\n", fpr);
+
+ for (sl = mboxes; sl; sl = sl->next)
+ {
+ if (!strcmp (sl->d, addrspec))
+ found = 1;
+ if (opt.verbose)
+ log_info (" addr-spec: %s\n", sl->d);
+ }
+ if (!found)
+ {
+ log_error ("public key for '%s' has no user id with the mail address\n",
+ addrspec);
+ err = gpg_error (GPG_ERR_CERT_REVOKED);
+ }
+
+ leave:
+ xfree (fpr);
+ free_strlist (mboxes);
+ es_fclose (key);
+ xfree (addrspec);
+ return err;
+}
+
+
+\f
/* Locate the key by fingerprint and userid and send a publication
* request. */
static gpg_error_t
}
-\f
-static void
-list_key_status_cb (void *opaque, const char *keyword, char *args)
-{
- server_ctx_t ctx = opaque;
- (void)ctx;
- if (DBG_CRYPTO)
- log_debug ("gpg status: %s %s\n", keyword, args);
-}
-
-
-static gpg_error_t
-list_key (server_ctx_t ctx, estream_t key)
-{
- gpg_error_t err;
- ccparray_t ccp;
- const char **argv;
- estream_t listing;
- char *line = NULL;
- size_t length_of_line = 0;
- size_t maxlen;
- ssize_t len;
- char **fields = NULL;
- int nfields;
- int lnr;
- char *mbox = NULL;
-
- /* We store our results in the context - clear it first. */
- xfree (ctx->fpr);
- ctx->fpr = NULL;
- free_strlist (ctx->mboxes);
- ctx->mboxes = NULL;
-
- /* Open a memory stream. */
- listing = es_fopenmem (0, "w+b");
- if (!listing)
- {
- err = gpg_error_from_syserror ();
- log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
- return err;
- }
-
- ccparray_init (&ccp, 0);
-
- ccparray_put (&ccp, "--no-options");
- if (!opt.verbose)
- ccparray_put (&ccp, "--quiet");
- else if (opt.verbose > 1)
- ccparray_put (&ccp, "--verbose");
- ccparray_put (&ccp, "--batch");
- ccparray_put (&ccp, "--status-fd=2");
- ccparray_put (&ccp, "--always-trust");
- ccparray_put (&ccp, "--with-colons");
- ccparray_put (&ccp, "--dry-run");
- ccparray_put (&ccp, "--import-options=import-minimal,import-show");
- ccparray_put (&ccp, "--import");
-
- ccparray_put (&ccp, NULL);
- argv = ccparray_get (&ccp, NULL);
- if (!argv)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
- NULL, listing,
- list_key_status_cb, ctx);
- if (err)
- {
- log_error ("import failed: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- es_rewind (listing);
- lnr = 0;
- maxlen = 2048; /* Set limit. */
- while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0)
- {
- lnr++;
- if (!maxlen)
- {
- log_error ("received line too long\n");
- err = gpg_error (GPG_ERR_LINE_TOO_LONG);
- goto leave;
- }
- /* Strip newline and carriage return, if present. */
- while (len > 0
- && (line[len - 1] == '\n' || line[len - 1] == '\r'))
- line[--len] = '\0';
- /* log_debug ("line '%s'\n", line); */
-
- xfree (fields);
- fields = strtokenize (line, ":");
- if (!fields)
- {
- err = gpg_error_from_syserror ();
- log_error ("strtokenize failed: %s\n", gpg_strerror (err));
- goto leave;
- }
- for (nfields = 0; fields[nfields]; nfields++)
- ;
- if (!nfields)
- {
- err = gpg_error (GPG_ERR_INV_ENGINE);
- goto leave;
- }
- if (!strcmp (fields[0], "sec"))
- {
- /* gpg may return "sec" as the first record - but we do not
- * accept secret keys. */
- err = gpg_error (GPG_ERR_NO_PUBKEY);
- goto leave;
- }
- if (lnr == 1 && strcmp (fields[0], "pub"))
- {
- /* First record is not a public key. */
- err = gpg_error (GPG_ERR_INV_ENGINE);
- goto leave;
- }
- if (lnr > 1 && !strcmp (fields[0], "pub"))
- {
- /* More than one public key. */
- err = gpg_error (GPG_ERR_TOO_MANY);
- goto leave;
- }
- if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb"))
- break; /* We can stop parsing here. */
-
- if (!strcmp (fields[0], "fpr") && nfields > 9 && !ctx->fpr)
- {
- ctx->fpr = xtrystrdup (fields[9]);
- if (!ctx->fpr)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- }
- else if (!strcmp (fields[0], "uid") && nfields > 9)
- {
- /* Fixme: Unescape fields[9] */
- xfree (mbox);
- mbox = mailbox_from_userid (fields[9]);
- if (mbox && !append_to_strlist_try (&ctx->mboxes, mbox))
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- }
- }
- if (len < 0 || es_ferror (listing))
- log_error ("error reading memory stream\n");
-
- leave:
- xfree (mbox);
- xfree (fields);
- es_free (line);
- xfree (argv);
- es_fclose (listing);
- return err;
-}
-
-
/* Take the key in KEYFILE and write it to OUTFILE in binary encoding.
* If ADDRSPEC is given only matching user IDs are included in the
* output. */
struct policy_flags_s policybuf;
/* First figure out the user id from the key. */
- err = list_key (ctx, key);
+ xfree (ctx->fpr);
+ free_strlist (ctx->mboxes);
+ err = wks_list_key (key, &ctx->fpr, &ctx->mboxes);
if (err)
goto leave;
if (!ctx->fpr)
}
/* We need to get the fingerprint from the key. */
- err = list_key (ctx, key);
+ xfree (ctx->fpr);
+ free_strlist (ctx->mboxes);
+ err = wks_list_key (key, &ctx->fpr, &ctx->mboxes);
if (err)
goto leave;
if (!ctx->fpr)
/*-- wks-util.c --*/
+void wks_set_status_fd (int fd);
+void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
+gpg_error_t wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes);
gpg_error_t wks_send_mime (mime_maker_t mime);
gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream,
int ignore_unknown);
/* gpgconf-comp.c - Configuration utility for GnuPG.
- * Copyright (C) 2004, 2007, 2008, 2009, 2010,
- * 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2007-2011 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Werner Koch
*
* This file is part of GnuPG.
*
{ "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED,
"gnupg", N_("|SPEC|set up email aliases"),
GC_ARG_TYPE_ALIAS_LIST, GC_BACKEND_GPG },
- { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
- "gnupg", "|FILE|read options from FILE",
+ { "options", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG },
+ { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+ { "default-new-key-algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
{ "default_pubkey_algo",
(GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE,
NULL, NULL,
{ "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"gnupg", "never consult a CRL",
GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "enable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
{ "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
"gnupg", N_("do not check CRLs for root certificates"),
GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
{ "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
"dirmngr", "|FILE|read options from FILE",
GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR },
+ { "resolver-timeout", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_INT32, GC_BACKEND_DIRMNGR },
+ { "nameserver", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
{ "Debug",
GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
gc_backend_t backend;
gc_option_t *option;
- if (component == GC_COMPONENT_PINENTRY)
- return; /* Dummy module for now. */
-
for (backend = 0; backend < GC_BACKEND_NR; backend++)
backend_seen[backend] = 0;
do
{
+ if (component == GC_COMPONENT_PINENTRY)
+ continue; /* Skip this dummy component. */
+
option = gc_component[component].options;
while (option && option->name)
\f
/* Perform a simple validity check based on the type. Return in
- NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
- type GC_ARG_TYPE_NONE. */
+ * NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
+ * type GC_ARG_TYPE_NONE. If VERBATIM is set the profile parsing mode
+ * is used. */
static void
option_check_validity (gc_option_t *option, unsigned long flags,
- char *new_value, unsigned long *new_value_nr)
+ char *new_value, unsigned long *new_value_nr,
+ int verbatim)
{
char *arg;
arg = new_value;
do
{
- if (*arg == '\0' || *arg == ',')
+ if (*arg == '\0' || (*arg == ',' && !verbatim))
{
if (!(option->flags & GC_OPT_FLAG_ARG_OPT))
gc_error (1, 0, "argument required for option %s", option->name);
- if (*arg == ',' && !(option->flags & GC_OPT_FLAG_LIST))
+ if (*arg == ',' && !verbatim && !(option->flags & GC_OPT_FLAG_LIST))
gc_error (1, 0, "list found for non-list option %s", option->name);
}
else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_STRING)
{
- if (*arg != '"')
+ if (*arg != '"' && !verbatim)
gc_error (1, 0, "string argument for option %s must begin "
"with a quote (\") character", option->name);
we do not quote arguments in configuration files, and
thus no argument is indistinguishable from the empty
string. */
- if (arg[1] == '\0' || arg[1] == ',')
+ if (arg[1] == '\0' || (arg[1] == ',' && !verbatim))
gc_error (1, 0, "empty string argument for option %s is "
"currently not allowed. Please report this!",
option->name);
gc_error (1, errno, "invalid argument for option %s",
option->name);
- if (*arg != '\0' && *arg != ',')
+ if (*arg != '\0' && (*arg != ',' || verbatim))
gc_error (1, 0, "garbage after argument for option %s",
option->name);
}
gc_error (1, errno, "invalid argument for option %s",
option->name);
- if (*arg != '\0' && *arg != ',')
+ if (*arg != '\0' && (*arg != ',' || verbatim))
gc_error (1, 0, "garbage after argument for option %s",
option->name);
}
- arg = strchr (arg, ',');
+ arg = verbatim? strchr (arg, ',') : NULL;
if (arg)
arg++;
}
while (arg && *arg);
}
+
#ifdef HAVE_W32_SYSTEM
int
copy_file (const char *src_name, const char *dst_name)
/* Create and verify the new configuration file for the specified
- backend and component. Returns 0 on success and -1 on error. */
+ * backend and component. Returns 0 on success and -1 on error. If
+ * VERBATIM is set the profile mode is used. */
static int
change_options_program (gc_component_t component, gc_backend_t backend,
char **src_filenamep, char **dest_filenamep,
- char **orig_filenamep)
+ char **orig_filenamep,
+ int verbatim)
{
static const char marker[] = "###+++--- " GPGCONF_DISP_NAME " ---+++###";
/* True if we are within the marker in the config file. */
{
char *end;
- assert (*arg == '"');
- arg++;
+ if (!verbatim)
+ {
+ log_assert (*arg == '"');
+ arg++;
- end = strchr (arg, ',');
- if (end)
- *end = '\0';
+ end = strchr (arg, ',');
+ if (end)
+ *end = '\0';
+ }
+ else
+ end = NULL;
fprintf (src_file, "%s %s\n", option->name,
- percent_deescape (arg));
+ verbatim? arg : percent_deescape (arg));
if (ferror (src_file))
goto change_one_err;
/* Common code for gc_component_change_options and
- gc_process_gpgconf_conf. */
+ * gc_process_gpgconf_conf. If VERBATIM is set the profile parsing
+ * mode is used. */
static void
change_one_value (gc_option_t *option, int *runtime,
- unsigned long flags, char *new_value)
+ unsigned long flags, char *new_value, int verbatim)
{
unsigned long new_value_nr = 0;
- option_check_validity (option, flags, new_value, &new_value_nr);
+ option_check_validity (option, flags, new_value, &new_value_nr, verbatim);
if (option->flags & GC_OPT_FLAG_RUNTIME)
runtime[option->backend] = 1;
/* Read the modifications from IN and apply them. If IN is NULL the
modifications are expected to already have been set to the global
- table. */
+ table. If VERBATIM is set the profile mode is used. */
void
-gc_component_change_options (int component, estream_t in, estream_t out)
+gc_component_change_options (int component, estream_t in, estream_t out,
+ int verbatim)
{
int err = 0;
int runtime[GC_BACKEND_NR];
continue;
}
- change_one_value (option, runtime, flags, new_value);
+ change_one_value (option, runtime, flags, new_value, 0);
}
}
err = change_options_program (component, option->backend,
&src_filename[option->backend],
&dest_filename[option->backend],
- &orig_filename[option->backend]);
+ &orig_filename[option->backend],
+ verbatim);
if (! err)
{
/* External verification. */
xfree (option_info->new_value);
option_info->new_value = NULL;
}
- change_one_value (option_info, runtime, newflags, value);
+ change_one_value (option_info, runtime, newflags, value, 0);
}
}
}
for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
{
- gc_component_change_options (component_id, NULL, NULL);
+ gc_component_change_options (component_id, NULL, NULL, 0);
}
opt.runtime = save_opt_runtime;
xfree (fname);
return result;
}
+
+
+/*
+ * Apply the profile FNAME to all known configure files.
+ */
+gpg_error_t
+gc_apply_profile (const char *fname)
+{
+ gpg_error_t err;
+ char *fname_buffer = NULL;
+ char *line = NULL;
+ size_t line_len = 0;
+ ssize_t length;
+ estream_t fp;
+ int lineno = 0;
+ int runtime[GC_BACKEND_NR];
+ int backend_id;
+ int component_id = -1;
+ int skip_section = 0;
+ int error_count = 0;
+ int newflags;
+
+ if (!fname)
+ fname = "-";
+
+ for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+ runtime[backend_id] = 0;
+
+
+ if (!(!strcmp (fname, "-")
+ || strchr (fname, '/')
+#ifdef HAVE_W32_SYSTEM
+ || strchr (fname, '\\')
+#endif
+ || strchr (fname, '.')))
+ {
+ /* FNAME looks like a standard profile name. Check whether one
+ * is installed and use that instead of the given file name. */
+ fname_buffer = xstrconcat (gnupg_datadir (), DIRSEP_S,
+ fname, ".prf", NULL);
+ if (!access (fname_buffer, F_OK))
+ fname = fname_buffer;
+ }
+
+ fp = !strcmp (fname, "-")? es_stdin : es_fopen (fname, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
+ return err;
+ }
+
+ if (opt.verbose)
+ log_info ("applying profile '%s'\n", fname);
+
+ err = 0;
+ while ((length = es_read_line (fp, &line, &line_len, NULL)) > 0)
+ {
+ char *name, *flags, *value;
+ gc_option_t *option_info = NULL;
+ char *p;
+
+ lineno++;
+ name = line;
+ while (*name == ' ' || *name == '\t')
+ name++;
+ if (!*name || *name == '#' || *name == '\r' || *name == '\n')
+ continue;
+ trim_trailing_spaces (name);
+
+ /* Check whether this is a new section. */
+ if (*name == '[')
+ {
+ name++;
+ skip_section = 0;
+ /* New section: Get the name of the component. */
+ p = strchr (name, ']');
+ if (!p)
+ {
+ error_count++;
+ log_info ("%s:%d:%d: error: syntax error in section tag\n",
+ fname, lineno, (int)(name - line));
+ skip_section = 1;
+ continue;
+ }
+ *p++ = 0;
+ if (*p)
+ log_info ("%s:%d:%d: warning: garbage after section tag\n",
+ fname, lineno, (int)(p - line));
+
+ trim_spaces (name);
+ component_id = gc_component_find (name);
+ if (component_id < 0)
+ {
+ log_info ("%s:%d:%d: warning: skipping unknown section '%s'\n",
+ fname, lineno, (int)(name - line), name );
+ skip_section = 1;
+ }
+ continue;
+ }
+
+ if (skip_section)
+ continue;
+ if (component_id < 0)
+ {
+ error_count++;
+ log_info ("%s:%d:%d: error: not in a valid section\n",
+ fname, lineno, (int)(name - line));
+ skip_section = 1;
+ continue;
+ }
+
+ /* Parse the option name. */
+ for (p = name; *p && !spacep (p); p++)
+ ;
+ *p++ = 0;
+ value = p;
+
+ option_info = find_option (component_id, name, GC_BACKEND_ANY);
+ if (!option_info)
+ {
+ error_count++;
+ log_info ("%s:%d:%d: error: unknown option '%s' in section '%s'\n",
+ fname, lineno, (int)(name - line),
+ name, gc_component[component_id].name);
+ continue;
+ }
+
+ /* Parse the optional flags. */
+ trim_spaces (value);
+ flags = value;
+ if (*flags == '[')
+ {
+ flags++;
+ p = strchr (flags, ']');
+ if (!p)
+ {
+ log_info ("%s:%d:%d: warning: invalid flag specification\n",
+ fname, lineno, (int)(p - line));
+ continue;
+ }
+ *p++ = 0;
+ value = p;
+ trim_spaces (value);
+ }
+ else /* No flags given. */
+ flags = NULL;
+
+ /* Set required defaults. */
+ if (gc_arg_type[option_info->arg_type].fallback == GC_ARG_TYPE_NONE
+ && !*value)
+ value = "1";
+
+ /* Check and save this option. */
+ newflags = 0;
+ if (flags && !strcmp (flags, "default"))
+ newflags |= GC_OPT_FLAG_DEFAULT;
+
+ if (newflags)
+ option_info->new_flags = 0;
+ if (*value)
+ {
+ xfree (option_info->new_value);
+ option_info->new_value = NULL;
+ }
+ change_one_value (option_info, runtime, newflags, value, 1);
+ }
+
+ if (length < 0 || es_ferror (fp))
+ {
+ err = gpg_error_from_syserror ();
+ error_count++;
+ log_error (_("%s:%u: read error: %s\n"),
+ fname, lineno, gpg_strerror (err));
+ }
+ if (es_fclose (fp))
+ log_error (_("error closing '%s'\n"), fname);
+ if (error_count)
+ log_error (_("error parsing '%s'\n"), fname);
+
+ xfree (line);
+
+ /* If it all worked, process the options. */
+ if (!err)
+ {
+ /* We need to switch off the runtime update, so that we can do
+ it later all at once. */
+ int save_opt_runtime = opt.runtime;
+ opt.runtime = 0;
+
+ for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
+ {
+ gc_component_change_options (component_id, NULL, NULL, 1);
+ }
+ opt.runtime = save_opt_runtime;
+
+ if (opt.runtime)
+ {
+ for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+ if (runtime[backend_id] && gc_backend[backend_id].runtime_change)
+ (*gc_backend[backend_id].runtime_change) (0);
+ }
+ }
+
+ xfree (fname_buffer);
+ return err;
+}
oNull = '0',
oNoVerbose = 500,
oHomedir,
+ oBuilddir,
aListComponents,
aCheckPrograms,
aKill,
aCreateSocketDir,
aRemoveSocketDir,
+ aApplyProfile,
aReload
};
{ aCheckOptions, "check-options", 256, N_("|COMPONENT|check options") },
{ aApplyDefaults, "apply-defaults", 256,
N_("apply global default values") },
+ { aApplyProfile, "apply-profile", 256,
+ N_("|FILE|update configuration files using FILE") },
{ aListDirs, "list-dirs", 256,
N_("get the configuration directories for @GPGCONF@") },
{ aListConfig, "list-config", 256,
{ oRuntime, "runtime", 0, N_("activate changes at runtime, if possible") },
/* hidden options */
{ oHomedir, "homedir", 2, "@" },
+ { oBuilddir, "build-prefix", 2, "@" },
{ oNull, "null", 0, "@" },
{ oNoVerbose, "no-verbose", 0, "@"},
{0}
case oVerbose: opt.verbose++; break;
case oNoVerbose: opt.verbose = 0; break;
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
+ case oBuilddir: gnupg_set_builddir (pargs.r.ret_str); break;
case oNull: opt.null = 1; break;
case aListDirs:
case aChangeOptions:
case aCheckOptions:
case aApplyDefaults:
+ case aApplyProfile:
case aListConfig:
case aCheckConfig:
case aQuerySWDB:
if (cmd == aListOptions)
gc_component_list_options (idx, get_outfp (&outfp));
else if (cmd == aChangeOptions)
- gc_component_change_options (idx, es_stdin, get_outfp (&outfp));
+ gc_component_change_options (idx, es_stdin,
+ get_outfp (&outfp), 0);
}
}
break;
exit (1);
break;
+ case aApplyProfile:
+ gc_component_retrieve_options (-1);
+ if (gc_apply_profile (fname))
+ exit (1);
+ break;
+
case aListDirs:
/* Show the system configuration directories for gpgconf. */
get_outfp (&outfp);
if (gnupg_mkdir (socketdir, "-rwx"))
gc_error (1, errno, "error creating '%s'", socketdir);
/* Try again. */
+ xfree (socketdir);
socketdir = _gnupg_socketdir_internal (1, &flags);
}
void gc_component_list_options (int component, estream_t out);
/* Read the modifications from IN and apply them. */
-void gc_component_change_options (int component, estream_t in, estream_t out);
+void gc_component_change_options (int component, estream_t in, estream_t out,
+ int verbatim);
/* Check the options of a single component. Returns 0 if everything
is OK. */
int gc_process_gpgconf_conf (const char *fname, int update, int defaults,
estream_t listfp);
+/* Apply a profile. */
+gpg_error_t gc_apply_profile (const char *fname);
+
#endif /*GPGCONF_H*/
{
ctx->mail = xtrycalloc (1, sizeof *ctx->mail);
if (!ctx->mail)
- return gpg_error_from_syserror ();
+ {
+ if (r_parent)
+ *r_parent = NULL;
+ return gpg_error_from_syserror ();
+ }
log_assert (!ctx->current_part);
ctx->current_part = ctx->mail;
ctx->current_part->headers_tail = &ctx->current_part->headers;
goto leave;
}
+ err = 0;
leave:
return err;
#include <string.h>
#include "util.h"
+#include "status.h"
+#include "ccparray.h"
+#include "exectool.h"
+#include "mbox-util.h"
#include "mime-maker.h"
#include "send-mail.h"
#include "gpg-wks.h"
+/* The stream to output the status information. Output is disabled if
+ this is NULL. */
+static estream_t statusfp;
+
+
+\f
+/* Set the status FD. */
+void
+wks_set_status_fd (int fd)
+{
+ static int last_fd = -1;
+
+ if (fd != -1 && last_fd == fd)
+ return;
+
+ if (statusfp && statusfp != es_stdout && statusfp != es_stderr)
+ es_fclose (statusfp);
+ statusfp = NULL;
+ if (fd == -1)
+ return;
+
+ if (fd == 1)
+ statusfp = es_stdout;
+ else if (fd == 2)
+ statusfp = es_stderr;
+ else
+ statusfp = es_fdopen (fd, "w");
+ if (!statusfp)
+ {
+ log_fatal ("can't open fd %d for status output: %s\n",
+ fd, gpg_strerror (gpg_error_from_syserror ()));
+ }
+ last_fd = fd;
+}
+
+
+/* Write a status line with code NO followed by the outout of the
+ * printf style FORMAT. The caller needs to make sure that LFs and
+ * CRs are not printed. */
+void
+wks_write_status (int no, const char *format, ...)
+{
+ va_list arg_ptr;
+
+ if (!statusfp)
+ return; /* Not enabled. */
+
+ es_fputs ("[GNUPG:] ", statusfp);
+ es_fputs (get_status_string (no), statusfp);
+ if (format)
+ {
+ es_putc (' ', statusfp);
+ va_start (arg_ptr, format);
+ es_vfprintf (statusfp, format, arg_ptr);
+ va_end (arg_ptr);
+ }
+ es_putc ('\n', statusfp);
+}
+
+
+\f
+/* Helper for wks_list_key. */
+static void
+list_key_status_cb (void *opaque, const char *keyword, char *args)
+{
+ (void)opaque;
+
+ if (DBG_CRYPTO)
+ log_debug ("gpg status: %s %s\n", keyword, args);
+}
+
+
+/* Run gpg on KEY and store the primary fingerprint at R_FPR and the
+ * list of mailboxes at R_MBOXES. Returns 0 on success; on error NULL
+ * is stored at R_FPR and R_MBOXES and an error code is returned. */
+gpg_error_t
+wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
+{
+ gpg_error_t err;
+ ccparray_t ccp;
+ const char **argv;
+ estream_t listing;
+ char *line = NULL;
+ size_t length_of_line = 0;
+ size_t maxlen;
+ ssize_t len;
+ char **fields = NULL;
+ int nfields;
+ int lnr;
+ char *mbox = NULL;
+ char *fpr = NULL;
+ strlist_t mboxes = NULL;
+
+ *r_fpr = NULL;
+ *r_mboxes = NULL;
+
+ /* Open a memory stream. */
+ listing = es_fopenmem (0, "w+b");
+ if (!listing)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ ccparray_init (&ccp, 0);
+
+ ccparray_put (&ccp, "--no-options");
+ if (!opt.verbose)
+ ccparray_put (&ccp, "--quiet");
+ else if (opt.verbose > 1)
+ ccparray_put (&ccp, "--verbose");
+ ccparray_put (&ccp, "--batch");
+ ccparray_put (&ccp, "--status-fd=2");
+ ccparray_put (&ccp, "--always-trust");
+ ccparray_put (&ccp, "--with-colons");
+ ccparray_put (&ccp, "--dry-run");
+ ccparray_put (&ccp, "--import-options=import-minimal,import-show");
+ ccparray_put (&ccp, "--import");
+
+ ccparray_put (&ccp, NULL);
+ argv = ccparray_get (&ccp, NULL);
+ if (!argv)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
+ NULL, listing,
+ list_key_status_cb, NULL);
+ if (err)
+ {
+ log_error ("import failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ es_rewind (listing);
+ lnr = 0;
+ maxlen = 2048; /* Set limit. */
+ while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0)
+ {
+ lnr++;
+ if (!maxlen)
+ {
+ log_error ("received line too long\n");
+ err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+ goto leave;
+ }
+ /* Strip newline and carriage return, if present. */
+ while (len > 0
+ && (line[len - 1] == '\n' || line[len - 1] == '\r'))
+ line[--len] = '\0';
+ /* log_debug ("line '%s'\n", line); */
+
+ xfree (fields);
+ fields = strtokenize (line, ":");
+ if (!fields)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("strtokenize failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ for (nfields = 0; fields[nfields]; nfields++)
+ ;
+ if (!nfields)
+ {
+ err = gpg_error (GPG_ERR_INV_ENGINE);
+ goto leave;
+ }
+ if (!strcmp (fields[0], "sec"))
+ {
+ /* gpg may return "sec" as the first record - but we do not
+ * accept secret keys. */
+ err = gpg_error (GPG_ERR_NO_PUBKEY);
+ goto leave;
+ }
+ if (lnr == 1 && strcmp (fields[0], "pub"))
+ {
+ /* First record is not a public key. */
+ err = gpg_error (GPG_ERR_INV_ENGINE);
+ goto leave;
+ }
+ if (lnr > 1 && !strcmp (fields[0], "pub"))
+ {
+ /* More than one public key. */
+ err = gpg_error (GPG_ERR_TOO_MANY);
+ goto leave;
+ }
+ if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb"))
+ break; /* We can stop parsing here. */
+
+ if (!strcmp (fields[0], "fpr") && nfields > 9 && !fpr)
+ {
+ fpr = xtrystrdup (fields[9]);
+ if (!fpr)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+ else if (!strcmp (fields[0], "uid") && nfields > 9)
+ {
+ /* Fixme: Unescape fields[9] */
+ xfree (mbox);
+ mbox = mailbox_from_userid (fields[9]);
+ if (mbox && !append_to_strlist_try (&mboxes, mbox))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+ }
+ if (len < 0 || es_ferror (listing))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading memory stream\n");
+ goto leave;
+ }
+
+ *r_fpr = fpr;
+ fpr = NULL;
+ *r_mboxes = mboxes;
+ mboxes = NULL;
+
+ leave:
+ xfree (fpr);
+ xfree (mboxes);
+ xfree (mbox);
+ xfree (fields);
+ es_free (line);
+ xfree (argv);
+ es_fclose (listing);
+ return err;
+}
+
/* Helper to write mail to the output(s). */
gpg_error_t
if (!err && !es_feof (stream))
err = gpg_error_from_syserror ();
- leave:
+
+ leave:
if (err)
log_error ("error reading '%s', line %d: %s\n",
es_fname_get (stream), lnr, gpg_strerror (err));