move libunbound to its own repo

You can now find it over at https://github.com/KumoCorp/libunbound-rs
This commit is contained in:
Wez Furlong
2023-10-30 11:19:17 -07:00
parent e731d47c3e
commit 2b0ef4820f
311 changed files with 5 additions and 186763 deletions
Generated
+2
View File
@@ -2600,6 +2600,7 @@ dependencies = [
[[package]]
name = "libunbound"
version = "0.1.0"
source = "git+https://github.com/KumoCorp/libunbound-rs.git?rev=5913110316a5c7f4a5fc928b542b2ba053b5cc2a#5913110316a5c7f4a5fc928b542b2ba053b5cc2a"
dependencies = [
"libc",
"libunbound-sys",
@@ -2612,6 +2613,7 @@ dependencies = [
[[package]]
name = "libunbound-sys"
version = "0.1.0"
source = "git+https://github.com/KumoCorp/libunbound-rs.git?rev=5913110316a5c7f4a5fc928b542b2ba053b5cc2a#5913110316a5c7f4a5fc928b542b2ba053b5cc2a"
dependencies = [
"cc",
"openssl-sys",
+1 -1
View File
@@ -16,7 +16,6 @@ members = [
"crates/throttle",
"crates/traffic-gen",
"crates/tsa-daemon",
"crates/libunbound-sys",
"crates/validate-shaping",
"crates/version-info",
]
@@ -32,6 +31,7 @@ axum-server = "0.5"
data-encoding = "2.4"
ed25519-dalek = "2.0"
futures = "0.3.28"
libunbound = {git="https://github.com/KumoCorp/libunbound-rs.git", rev="5913110316a5c7f4a5fc928b542b2ba053b5cc2a"}
mlua = "0.8"
once_cell = "1.17"
openssl = { version="0.10.57" }
+1 -1
View File
@@ -19,7 +19,7 @@ anyhow = "1.0"
arc-swap = "1.6"
kumo-log-types = {path="../kumo-log-types"}
lazy_static = "1.4"
libunbound = {path="../libunbound"}
libunbound = {workspace=true}
lruttl = {path="../lruttl"}
serde = {version="1.0", features=["derive"]}
tokio = {workspace=true}
-18
View File
@@ -1,18 +0,0 @@
[package]
name = "libunbound-sys"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[build-dependencies]
cc = {version="1.0", features = ["parallel"]}
tempfile = {workspace=true}
[target.'cfg(not(any(windows, target_os="macos")))'.dependencies]
openssl-sys = {workspace=true}
[target.'cfg(any(windows, target_os="macos"))'.dependencies]
openssl-sys = { workspace=true, features=["vendored"] }
-482
View File
@@ -1,482 +0,0 @@
use std::collections::HashSet;
use std::path::PathBuf;
fn new_build() -> cc::Build {
let mut cfg = cc::Build::new();
cfg.warnings(false);
cfg.extra_warnings(false);
cfg.flag_if_supported("-Wno-deprecated-declarations");
println!("cargo:rerun-if-env-changed=DEP_OPENSSL_INCLUDE");
if let Some(path) = std::env::var_os("DEP_OPENSSL_INCLUDE") {
if let Some(path) = std::env::split_paths(&path).next() {
if let Some(path) = path.to_str() {
if !path.is_empty() {
cfg.include(path);
}
}
}
}
if let Some(lib_root) = std::env::var_os("DEP_OPENSSL_ROOT") {
println!(
"cargo:rustc-link-search=native={}",
PathBuf::from(lib_root).join("lib").to_str().unwrap()
);
}
cfg
}
struct Probed {
defined: HashSet<String>,
}
impl Probed {
fn new() -> Self {
Self {
defined: HashSet::new(),
}
}
fn try_compile(&self, code: &str) -> std::io::Result<bool> {
let temp = tempfile::TempDir::new()?;
let main_c = temp.path().join("main.c");
std::fs::write(&main_c, format!("{code}"))?;
let mut cfg = cc::Build::new();
cfg.cargo_metadata(false);
if let Some(path) = std::env::var_os("DEP_OPENSSL_INCLUDE") {
eprintln!("DEP_OPENSSL_INCLUDE is {path:?}");
if let Some(path) = std::env::split_paths(&path).next() {
if let Some(path) = path.to_str() {
if !path.is_empty() {
eprintln!("adding {path} to includes");
cfg.include(path);
}
}
}
}
let mut cmd = cfg.get_compiler().to_command();
cmd.current_dir(temp.path());
// Source file first
cmd.arg(&main_c);
// then libraries
if let Ok(lib_root) = std::env::var("DEP_OPENSSL_ROOT") {
eprintln!("DEP_OPENSSL_ROOT is {lib_root:?}");
cmd.arg(&format!("-L{lib_root}/lib"));
}
cmd.arg("-lssl");
cmd.arg("-lcrypto");
cmd.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.output()
.map(|o| o.status.success())
}
fn check_header(&mut self, cfg: &mut cc::Build, name: &str) -> std::io::Result<()> {
let def = name.to_uppercase().replace(".", "_").replace("/", "_");
let def = format!("HAVE_{def}");
eprintln!("checking for <{name}>");
if self.try_compile(&format!(
r#"
#include <{name}>
int main(void) {{
return 0;
}}
"#
))? {
eprintln!("defining {def}");
cfg.define(&def, Some("1"));
self.defined.insert(def);
}
Ok(())
}
fn check_type(&mut self, cfg: &mut cc::Build, name: &str, header: &str) -> std::io::Result<()> {
let def = name.to_uppercase().replace(".", "_").replace("/", "_");
let def = format!("HAVE_{def}");
eprintln!("checking for <{name}>");
if self.try_compile(&format!(
r#"
#include <{header}>
int main(void) {{
return sizeof({name});
}}
"#
))? {
eprintln!("defining {def}");
cfg.define(&def, Some("1"));
self.defined.insert(def);
}
Ok(())
}
fn check_func(&mut self, cfg: &mut cc::Build, func: &str) -> std::io::Result<bool> {
let def = func.to_uppercase();
let def = format!("HAVE_{def}");
eprintln!("checking for function {func}");
if self.try_compile(&format!(
r#"
extern int {func}();
int main(void) {{
return {func}();
}}
"#
))? {
eprintln!("defining {def}");
cfg.define(&def, Some("1"));
self.defined.insert(def);
Ok(true)
} else {
Ok(false)
}
}
}
fn unbound() {
let mut cfg = new_build();
for f in [
"services/cache/dns.c",
"services/cache/infra.c",
"services/cache/rrset.c",
"util/as112.c",
"util/data/dname.c",
"util/data/msgencode.c",
"util/data/msgparse.c",
"util/data/msgreply.c",
"util/data/packed_rrset.c",
"iterator/iterator.c",
"iterator/iter_delegpt.c",
"iterator/iter_donotq.c",
"iterator/iter_fwd.c",
"iterator/iter_hints.c",
"iterator/iter_priv.c",
"iterator/iter_resptype.c",
"iterator/iter_scrub.c",
"iterator/iter_utils.c",
"services/listen_dnsport.c",
"services/localzone.c",
"services/mesh.c",
"services/modstack.c",
"services/view.c",
"services/rpz.c",
"util/rfc_1982.c",
"services/outbound_list.c",
"services/outside_network.c",
"util/alloc.c",
"util/config_file.c",
"util/configlexer.c",
"util/configparser.c",
// "util/shm_side/shm_main.c",
"services/authzone.c",
"util/fptr_wlist.c",
"util/locks.c",
"util/log.c",
"util/mini_event.c",
"util/module.c",
"util/netevent.c",
"util/net_help.c",
"util/random.c",
"util/rbtree.c",
"util/regional.c",
"util/rtt.c",
"util/siphash.c",
"util/edns.c",
"util/storage/dnstree.c",
"util/storage/lookup3.c",
"util/storage/lruhash.c",
"util/storage/slabhash.c",
"util/tcp_conn_limit.c",
"util/timehist.c",
"util/tube.c",
"util/proxy_protocol.c",
"util/timeval_func.c",
"util/winsock_event.c",
"validator/autotrust.c",
"validator/val_anchor.c",
"validator/validator.c",
"validator/val_kcache.c",
"validator/val_kentry.c",
"validator/val_neg.c",
"validator/val_nsec3.c",
"validator/val_nsec.c",
"validator/val_secalgo.c",
"validator/val_sigcrypt.c",
"validator/val_utils.c",
"dns64/dns64.c",
/* subnet option
"edns-subnet/edns-subnet.c",
"edns-subnet/subnetmod.c",
"edns-subnet/addrtree.c",
"edns-subnet/subnet-whitelist.c",
*/
"respip/respip.c",
"libunbound/context.c",
"libunbound/libunbound.c",
"libunbound/libworker.c",
"util/ub_event_pluggable.c",
"sldns/keyraw.c",
"sldns/sbuffer.c",
"sldns/wire2str.c",
"sldns/parse.c",
"sldns/parseutil.c",
"sldns/rrdef.c",
"sldns/str2wire.c",
"compat/arc4_lock.c",
"compat/arc4random.c",
"compat/arc4random_uniform.c",
// "compat/fake-rfc2553.c",
//"compat/malloc.c",
//"compat/memcmp.c",
//"compat/memmove.c",
//"compat/reallocarray.c",
"compat/sha512.c",
] {
cfg.file(&format!("unbound/{f}"));
}
let mut probe = Probed::new();
for hdr in &[
"TargetConditionals.h",
"arpa/inet.h",
"bsd/stdlib.h",
"bsd/string.h",
"dlfcn.h",
"endian.h",
"event.h",
"expat.h",
"getopt.h",
"glob.h",
"grp.h",
"hiredis/hiredis.h",
"ifaddrs.h",
"inttypes.h",
"iphlpapi.h",
"libkern/OSByteOrder.h",
"linux/net_tstamp.h",
"login_cap.h",
"memory.h",
"net/if.h",
"netdb.h",
"netinet/in.h",
"netinet/tcp.h",
"netioapi.h",
"nettle/dsa-compat.h",
"nettle/eddsa.h",
"nghttp2/nghttp2.h",
"openssl/bn.h",
"openssl/conf.h",
"openssl/core_names.h",
"openssl/dh.h",
"openssl/dsa.h",
"openssl/engine.h",
"openssl/err.h",
"openssl/param_build.h",
"openssl/rand.h",
"openssl/rsa.h",
"openssl/ssl.h",
"poll.h",
"pwd.h",
"stdarg.h",
"stdbool.h",
"stdint.h",
"stdlib.h",
"string.h",
"strings.h",
"sys/endian.h",
"sys/ipc.h",
"sys/param.h",
"sys/resource.h",
"sys/select.h",
"sys/sha2.h",
"sys/shm.h",
"sys/socket.h",
"sys/stat.h",
"sys/sysctl.h",
"sys/types.h",
"sys/uio.h",
"sys/un.h",
"sys/wait.h",
"syslog.h",
"time.h",
"unistd.h",
"vfork.h",
"windows.h",
"winsock2.h",
"ws2tcpip.h",
] {
probe.check_header(&mut cfg, hdr).unwrap();
}
for func in &[
"BIO_set_callback_ex",
"CRYPTO_THREADID_set_callback",
"CRYPTO_cleanup_all_ex_data",
"DSA_SIG_set0",
"ENGINE_cleanup",
"ERR_free_strings",
"ERR_load_crypto_strings",
"EVP_DigestVerify",
"EVP_EncryptInit_ex",
"EVP_MAC_CTX_set_params",
"EVP_MD_CTX_new",
"EVP_aes_256_cbc",
"EVP_cleanup",
"EVP_default_properties_is_fips_enabled",
"EVP_dss1",
"EVP_sha1",
"EVP_sha256",
"EVP_sha512",
"FIPS_mode",
"HMAC_Init_ex",
"OPENSSL_config",
"OPENSSL_init_crypto",
"OPENSSL_init_ssl",
"OSSL_PARAM_BLD_new",
"OpenSSL_add_all_digests",
"RAND_cleanup",
"SHA512_Update",
"SSL_CTX_set_alpn_protos",
"SSL_CTX_set_alpn_select_cb",
"SSL_CTX_set_ciphersuites",
"SSL_CTX_set_security_level",
"SSL_CTX_set_tlsext_ticket_key_evp_cb",
"SSL_get0_alpn_selected",
"SSL_get0_peername",
"SSL_get1_peer_certificate",
"SSL_set1_host",
"X509_VERIFY_PARAM_set1_host",
"_beginthreadex",
"accept4",
"be64toh",
"chown",
"chroot",
"daemon",
"endprotoent",
"endpwent",
"endservent",
"ev_default_loop",
"ev_loop",
"event_assign",
"event_base_free",
"event_base_get_method",
"event_base_new",
"event_base_once",
"fcntl",
"fork",
"fsync",
"getaddrinfo",
"getauxval",
"getentropy",
"getifaddrs",
"getpwnam",
"getrlimit",
"gettid",
"glob",
"htobe64",
"if_nametoindex",
"ioctlsocket",
"initgroups",
"kill",
"localtime_r",
"memmove",
"poll",
"reallocarray",
"random",
"recvmsg",
"sendmsg",
"setregid",
"setresgid",
"setresuid",
"malloc",
"setreuid",
"setrlimit",
"setsid",
"setusercontext",
"shmget",
"sigprocmask",
"sleep",
"socketpair",
"srandom",
"strftime",
"strptime",
"tzset",
"usleep",
"vfork",
"writev",
] {
probe.check_func(&mut cfg, func).unwrap();
}
if cfg!(target_os = "macos") {
cfg.define("HAVE_ISBLANK", Some("1"));
} else {
if !probe.check_func(&mut cfg, "isblank").unwrap() {
cfg.file(&format!("unbound/compat/isblank.c"));
}
}
for func in &[
//"arc4random",
//"arc4random_uniform",
"ctime_r",
"gmtime_r",
"explicit_bzero",
"strlcat",
"strlcpy",
"inet_ntop",
"inet_pton",
"inet_aton",
"malloc",
"memcmp",
"memmove",
"reallocarray",
"strsep",
"snprintf",
"strptime",
] {
if !probe.check_func(&mut cfg, func).unwrap() {
cfg.file(&format!("unbound/compat/{func}.c"));
}
}
for t in &["pthread_rwlock_t", "pthread_spinlock_t"] {
probe.check_type(&mut cfg, t, "pthread.h").unwrap();
}
if !probe.defined.contains("HAVE_GETENTROPY") {
let name = if cfg!(target_os = "macos") {
"compat/getentropy_osx.c"
} else {
"compat/getentropy_linux.c"
};
//"compat/getentropy_freebsd.c",
//"compat/getentropy_solaris.c",
//"compat/getentropy_win.c",
cfg.file(&format!("unbound/{name}"));
}
let ptr_width_bits: usize = std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
.unwrap()
.parse()
.unwrap();
let ptr_width_bytes = format!("{}", ptr_width_bits / 8);
cfg.define("SIZEOF_SIZE_T", Some(ptr_width_bytes.as_str()));
cfg.define("SIZEOF_TIME_T", Some(ptr_width_bytes.as_str()));
cfg.include(".");
cfg.include("unbound");
cfg.compile("unbound");
}
fn main() {
unbound();
}
-929
View File
@@ -1,929 +0,0 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* apply the noreturn attribute to a function that exits the program */
#define ATTR_NORETURN __attribute__((__noreturn__))
/* apply the weak attribute to a symbol */
#define ATTR_WEAK __attribute__((weak))
/* Directory to chroot to */
#define CHROOT_DIR "/usr/local/etc/unbound"
/* Define this to enable client subnet option. */
/* #undef CLIENT_SUBNET */
/* Do sha512 definitions in config.h */
/* #undef COMPAT_SHA512 */
/* Command line arguments used with configure */
#define CONFCMDLINE ""
/* Pathname to the Unbound configuration file */
#define CONFIGFILE "/usr/local/etc/unbound/unbound.conf"
/* Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work
*/
/* #undef DARWIN_BROKEN_SETREUID */
/* Whether daemon is deprecated */
/* #undef DEPRECATED_DAEMON */
/* Deprecate RSA 1024 bit length, makes that an unsupported key */
/* #undef DEPRECATE_RSA_1024 */
/* Define this to enable kernel based UDP source port randomization. */
/* #undef DISABLE_EXPLICIT_PORT_RANDOMISATION */
/* default dnstap socket path */
/* #undef DNSTAP_SOCKET_PATH */
/* Define if you want to use debug lock checking (slow). */
/* #undef ENABLE_LOCK_CHECKS */
/* Define this if you enabled-allsymbols from libunbound to link binaries to
it for smaller install size, but the libunbound export table is polluted by
internal symbols */
/* #undef EXPORT_ALL_SYMBOLS */
/* Whether the C compiler accepts the "format" attribute */
#define HAVE_ATTR_FORMAT 1
/* Whether the C compiler accepts the "noreturn" attribute */
#define HAVE_ATTR_NORETURN 1
/* Whether the C compiler accepts the "unused" attribute */
#define HAVE_ATTR_UNUSED 1
/* Whether the C compiler accepts the "weak" attribute */
#define HAVE_ATTR_WEAK 1
/* Define to 1 if you have the declaration of `arc4random', and to 0 if you
don't. */
/* #undef HAVE_DECL_ARC4RANDOM */
/* Define to 1 if you have the declaration of `arc4random_uniform', and to 0
if you don't. */
/* #undef HAVE_DECL_ARC4RANDOM_UNIFORM */
/* Define to 1 if you have the declaration of `inet_ntop', and to 0 if you
don't. */
#define HAVE_DECL_INET_NTOP 1
/* Define to 1 if you have the declaration of `inet_pton', and to 0 if you
don't. */
#define HAVE_DECL_INET_PTON 1
/* Define to 1 if you have the declaration of `NID_ED25519', and to 0 if you
don't. */
#define HAVE_DECL_NID_ED25519 1
/* Define to 1 if you have the declaration of `NID_ED448', and to 0 if you
don't. */
#define HAVE_DECL_NID_ED448 1
/* Define to 1 if you have the declaration of `NID_secp384r1', and to 0 if you
don't. */
#define HAVE_DECL_NID_SECP384R1 1
/* Define to 1 if you have the declaration of `NID_X9_62_prime256v1', and to 0
if you don't. */
#define HAVE_DECL_NID_X9_62_PRIME256V1 1
/* Define to 1 if you have the declaration of `reallocarray', and to 0 if you
don't. */
#define HAVE_DECL_REALLOCARRAY 1
/* Define to 1 if you have the declaration of `redisConnect', and to 0 if you
don't. */
/* #undef HAVE_DECL_REDISCONNECT */
/* Define to 1 if you have the declaration of `sk_SSL_COMP_pop_free', and to 0
if you don't. */
#define HAVE_DECL_SK_SSL_COMP_POP_FREE 1
/* Define to 1 if you have the declaration of
`SSL_COMP_get_compression_methods', and to 0 if you don't. */
#define HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS 1
/* Define to 1 if you have the declaration of `SSL_CTX_set_ecdh_auto', and to
0 if you don't. */
#define HAVE_DECL_SSL_CTX_SET_ECDH_AUTO 1
/* Define to 1 if you have the declaration of `strlcat', and to 0 if you
don't. */
/* #undef HAVE_DECL_STRLCAT */
/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you
don't. */
/* #undef HAVE_DECL_STRLCPY */
/* Define to 1 if you have the declaration of `XML_StopParser', and to 0 if
you don't. */
#define HAVE_DECL_XML_STOPPARSER 1
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
#define HAVE_FSEEKO 1
/* Use portable libbsd functions */
/* #undef HAVE_LIBBSD */
/* Define if we have LibreSSL */
/* #undef HAVE_LIBRESSL */
/* Use libnettle for crypto */
/* #undef HAVE_NETTLE */
/* Define to 1 if you have the <nettle/dsa-compat.h> header file. */
/* #undef HAVE_NETTLE_DSA_COMPAT_H */
/* Define to 1 if you have the <nettle/eddsa.h> header file. */
/* #undef HAVE_NETTLE_EDDSA_H */
/* Define this to use nghttp2 client. */
/* #undef HAVE_NGHTTP2 */
/* Define to 1 if you have the <nghttp2/nghttp2.h> header file. */
/* #undef HAVE_NGHTTP2_NGHTTP2_H */
/* Use libnss for crypto */
/* #undef HAVE_NSS */
/* Define to 1 if you have the `OpenSSL_add_all_digests' function. */
/* #undef HAVE_OPENSSL_ADD_ALL_DIGESTS */
/* Define if you have POSIX threads libraries and header files. */
#define HAVE_PTHREAD 1
/* Have PTHREAD_PRIO_INHERIT. */
//#define HAVE_PTHREAD_PRIO_INHERIT 1
/* Define to 1 if the system has the type `pthread_rwlock_t'. */
//#define HAVE_PTHREAD_RWLOCK_T 1
/* Define to 1 if the system has the type `pthread_spinlock_t'. */
//#define HAVE_PTHREAD_SPINLOCK_T 1
/* Define if you have Python libraries and header files. */
/* #undef HAVE_PYTHON */
/* Define to 1 if you have the `RAND_cleanup' function. */
/* #undef HAVE_RAND_CLEANUP */
/* Using Solaris threads */
/* #undef HAVE_SOLARIS_THREADS */
/* Define if you have the SSL libraries installed. */
#define HAVE_SSL /**/
/* Define to 1 if `ipi_spec_dst' is a member of `struct in_pktinfo'. */
#define HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST 1
/* Define to 1 if `sun_len' is a member of `struct sockaddr_un'. */
/* #undef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
/* Define to 1 if `fork' works. */
#define HAVE_WORKING_FORK 1
/* Define to 1 if `vfork' works. */
#define HAVE_WORKING_VFORK 1
/* Define to 1 if you have the `writev' function. */
#define HAVE_WRITEV 1
/* If HMAC_Init_ex() returns void */
/* #undef HMAC_INIT_EX_RETURNS_VOID */
/* if lex has yylex_destroy */
#define LEX_HAS_YYLEX_DESTROY 1
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"
/* Define to the maximum message length to pass to syslog. */
#define MAXSYSLOGMSGLEN 10240
/* Define if memcmp() does not compare unsigned bytes */
/* #undef MEMCMP_IS_BROKEN */
/* Define if mkdir has one argument. */
/* #undef MKDIR_HAS_ONE_ARG */
/* Define if the network stack does not fully support nonblocking io (causes
lower performance). */
/* #undef NONBLOCKING_IS_BROKEN */
/* Put -D_ALL_SOURCE define in config.h */
/* #undef OMITTED__D_ALL_SOURCE */
/* Put -D_BSD_SOURCE define in config.h */
/* #undef OMITTED__D_BSD_SOURCE */
/* Put -D_DEFAULT_SOURCE define in config.h */
/* #undef OMITTED__D_DEFAULT_SOURCE */
/* Put -D_GNU_SOURCE define in config.h */
#define OMITTED__D_GNU_SOURCE 1
/* Put -D_LARGEFILE_SOURCE=1 define in config.h */
/* #undef OMITTED__D_LARGEFILE_SOURCE_1 */
/* Put -D_POSIX_C_SOURCE=200112 define in config.h */
/* #undef OMITTED__D_POSIX_C_SOURCE_200112 */
/* Put -D_XOPEN_SOURCE=600 define in config.h */
/* #undef OMITTED__D_XOPEN_SOURCE_600 */
/* Put -D_XOPEN_SOURCE_EXTENDED=1 define in config.h */
/* #undef OMITTED__D_XOPEN_SOURCE_EXTENDED_1 */
/* Put -D__EXTENSIONS__ define in config.h */
/* #undef OMITTED__D__EXTENSIONS__ */
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues"
/* Define to the full name of this package. */
#define PACKAGE_NAME "unbound"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "unbound 1.18.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "unbound"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.18.1"
/* default pidfile location */
#define PIDFILE "/usr/local/etc/unbound/unbound.pid"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* Return type of signal handlers, but autoconf 2.70 says 'your code may
safely assume C89 semantics that RETSIGTYPE is void.' */
#define RETSIGTYPE void
/* if REUSEPORT is enabled by default */
#define REUSEPORT_DEFAULT 1
/* default rootkey location */
#define ROOT_ANCHOR_FILE "/usr/local/etc/unbound/root.key"
/* default rootcert location */
#define ROOT_CERT_FILE "/usr/local/etc/unbound/icannbundle.pem"
/* version number for resource files */
#define RSRC_PACKAGE_VERSION 1,18,1,0
/* Directory to chdir to */
#define RUN_DIR "/usr/local/etc/unbound"
/* Shared data */
#define SHARE_DIR "/usr/local/etc/unbound"
/* The size of `pthread_t', as computed by sizeof. */
//#define SIZEOF_PTHREAD_T 8
/* The size of `size_t', as computed by sizeof. */
//#define SIZEOF_SIZE_T 8
/* The size of `time_t', as computed by sizeof. */
//#define SIZEOF_TIME_T 8
/* The size of `unsigned long', as computed by sizeof. */
//#define SIZEOF_UNSIGNED_LONG 8
/* define if (v)snprintf does not return length needed, (but length used) */
/* #undef SNPRINTF_RET_BROKEN */
/* Define to 1 if libsodium supports sodium_set_misuse_handler */
/* #undef SODIUM_MISUSE_HANDLER */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* use default strptime. */
#define STRPTIME_WORKS 1
/* Use win32 resources and API */
/* #undef UB_ON_WINDOWS */
/* the SYSLOG_FACILITY to use, default LOG_DAEMON */
#define UB_SYSLOG_FACILITY LOG_DAEMON
/* default username */
#define UB_USERNAME "unbound"
/* use to enable lightweight alloc assertions, for debug use */
/* #undef UNBOUND_ALLOC_LITE */
/* use malloc not regions, for debug use */
/* #undef UNBOUND_ALLOC_NONREGIONAL */
/* use statistics for allocs and frees, for debug use */
/* #undef UNBOUND_ALLOC_STATS */
/* define this to enable debug checks. */
/* #undef UNBOUND_DEBUG */
/* Define to 1 to use cachedb support */
/* #undef USE_CACHEDB */
/* Define to 1 to enable dnscrypt support */
/* #undef USE_DNSCRYPT */
/* Define to 1 to enable dnscrypt with xchacha20 support */
/* #undef USE_DNSCRYPT_XCHACHA20 */
/* Define to 1 to enable dnstap support */
/* #undef USE_DNSTAP */
/* Define this to enable DSA support. */
/* #undef USE_DSA */
/* Define this to enable ECDSA support. */
#define USE_ECDSA 1
/* Define this to enable an EVP workaround for older openssl */
/* #undef USE_ECDSA_EVP_WORKAROUND */
/* Define this to enable ED25519 support. */
#define USE_ED25519 1
/* Define this to enable ED448 support. */
#define USE_ED448 1
/* Define this to enable GOST support. */
/* #undef USE_GOST */
/* Define to 1 to use ipsecmod support. */
/* #undef USE_IPSECMOD */
/* Define to 1 to use ipset support */
/* #undef USE_IPSET */
/* Define if you enable libevent */
/* #undef USE_LIBEVENT */
/* Define this to enable use of /proc/sys/net/ipv4/ip_local_port_range as a
default outgoing port range. This is only for the libunbound on Linux and
does not affect unbound resolving daemon itself. This may severely limit
the number of available outgoing ports and thus decrease randomness. Define
this only when the target system restricts (e.g. some of SELinux enabled
distributions) the use of non-ephemeral ports. */
/* #undef USE_LINUX_IP_LOCAL_PORT_RANGE */
/* Define if you want to use internal select based events */
#define USE_MINI_EVENT 1
/* Define this to enable client TCP Fast Open. */
/* #undef USE_MSG_FASTOPEN */
/* Define this to enable client TCP Fast Open. */
/* #undef USE_OSX_MSG_FASTOPEN */
/* Define this to use hiredis client. */
/* #undef USE_REDIS */
/* Define this to enable SHA1 support. */
#define USE_SHA1 1
/* Define this to enable SHA256 and SHA512 support. */
#define USE_SHA2 1
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# define _ALL_SOURCE 1
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif
/* Define this to enable server TCP Fast Open. */
/* #undef USE_TCP_FASTOPEN */
/* Whether the windows socket API is used */
/* #undef USE_WINSOCK */
/* the version of the windows API enabled */
#define WINVER 0x0502
/* Define if you want dynlib module. */
/* #undef WITH_DYNLIBMODULE */
/* Define if you want Python module. */
/* #undef WITH_PYTHONMODULE */
/* Define if you want PyUnbound. */
/* #undef WITH_PYUNBOUND */
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
`char[]'. */
/* #undef YYTEXT_POINTER */
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
/* #undef _LARGEFILE_SOURCE */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Enable for compile on Minix */
/* #undef _NETBSD_SOURCE */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
/* defined to use gcc ansi snprintf and sscanf that understands %lld when
compiled for windows. */
/* #undef __USE_MINGW_ANSI_STDIO */
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef gid_t */
/* in_addr_t */
/* #undef in_addr_t */
/* in_port_t */
/* #undef in_port_t */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
/* #undef inline */
#endif
/* Define to `short' if <sys/types.h> does not define. */
/* #undef int16_t */
/* Define to `int' if <sys/types.h> does not define. */
/* #undef int32_t */
/* Define to `long long' if <sys/types.h> does not define. */
/* #undef int64_t */
/* Define to `signed char' if <sys/types.h> does not define. */
/* #undef int8_t */
/* Define if replacement function should be used. */
/* #undef malloc */
/* Define to `long int' if <sys/types.h> does not define. */
/* #undef off_t */
/* Define to `int' if <sys/types.h> does not define. */
/* #undef pid_t */
/* Define to 'int' if not defined */
/* #undef rlim_t */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
/* Define to 'int' if not defined */
/* #undef socklen_t */
/* Define to `int' if <sys/types.h> does not define. */
/* #undef ssize_t */
/* Define to 'unsigned char if not defined */
/* #undef u_char */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef uid_t */
/* Define to `unsigned short' if <sys/types.h> does not define. */
/* #undef uint16_t */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef uint32_t */
/* Define to `unsigned long long' if <sys/types.h> does not define. */
/* #undef uint64_t */
/* Define to `unsigned char' if <sys/types.h> does not define. */
/* #undef uint8_t */
/* Define as `fork' if `vfork' does not work. */
/* #undef vfork */
#if defined(OMITTED__D_GNU_SOURCE) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE 1
#endif
#if defined(OMITTED__D_BSD_SOURCE) && !defined(_BSD_SOURCE)
#define _BSD_SOURCE 1
#endif
#if defined(OMITTED__D_DEFAULT_SOURCE) && !defined(_DEFAULT_SOURCE)
#define _DEFAULT_SOURCE 1
#endif
#if defined(OMITTED__D__EXTENSIONS__) && !defined(__EXTENSIONS__)
#define __EXTENSIONS__ 1
#endif
#if defined(OMITTED__D_POSIX_C_SOURCE_200112) && !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE 200112
#endif
#if defined(OMITTED__D_XOPEN_SOURCE_600) && !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#if defined(OMITTED__D_XOPEN_SOURCE_EXTENDED_1) && !defined(_XOPEN_SOURCE_EXTENDED)
#define _XOPEN_SOURCE_EXTENDED 1
#endif
#if defined(OMITTED__D_ALL_SOURCE) && !defined(_ALL_SOURCE)
#define _ALL_SOURCE 1
#endif
#if defined(OMITTED__D_LARGEFILE_SOURCE_1) && !defined(_LARGEFILE_SOURCE)
#define _LARGEFILE_SOURCE 1
#endif
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
#ifndef UNBOUND_DEBUG
# ifndef NDEBUG
# define NDEBUG
# endif
#endif
/** Use small-ldns codebase */
#define USE_SLDNS 1
#ifdef HAVE_SSL
# define LDNS_BUILD_CONFIG_HAVE_SSL 1
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#if STDC_HEADERS
#include <stdlib.h>
#include <stddef.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <errno.h>
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
#if !defined(USE_WINSOCK) || !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN) || defined(__USE_MINGW_ANSI_STDIO)
#define ARG_LL "%ll"
#else
#define ARG_LL "%I64"
#endif
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
#ifdef HAVE_ATTR_FORMAT
# define ATTR_FORMAT(archetype, string_index, first_to_check) \
__attribute__ ((format (archetype, string_index, first_to_check)))
#else /* !HAVE_ATTR_FORMAT */
# define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
#endif /* !HAVE_ATTR_FORMAT */
#if defined(DOXYGEN)
# define ATTR_UNUSED(x) x
#elif defined(__cplusplus)
# define ATTR_UNUSED(x)
#elif defined(HAVE_ATTR_UNUSED)
# define ATTR_UNUSED(x) x __attribute__((unused))
#else /* !HAVE_ATTR_UNUSED */
# define ATTR_UNUSED(x) x
#endif /* !HAVE_ATTR_UNUSED */
#ifndef HAVE_FSEEKO
#define fseeko fseek
#define ftello ftell
#endif /* HAVE_FSEEKO */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif
#if !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN)
#define snprintf snprintf_unbound
#define vsnprintf vsnprintf_unbound
#include <stdarg.h>
int snprintf (char *str, size_t count, const char *fmt, ...);
int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
#endif /* HAVE_SNPRINTF or SNPRINTF_RET_BROKEN */
#ifndef HAVE_INET_PTON
#define inet_pton inet_pton_unbound
int inet_pton(int af, const char* src, void* dst);
#endif /* HAVE_INET_PTON */
#ifndef HAVE_INET_NTOP
#define inet_ntop inet_ntop_unbound
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
#endif
#ifndef HAVE_INET_ATON
#define inet_aton inet_aton_unbound
int inet_aton(const char *cp, struct in_addr *addr);
#endif
#ifndef HAVE_MEMMOVE
#define memmove memmove_unbound
void *memmove(void *dest, const void *src, size_t n);
#endif
#ifndef HAVE_STRLCAT
#define strlcat strlcat_unbound
size_t strlcat(char *dst, const char *src, size_t siz);
#endif
#ifndef HAVE_STRLCPY
#define strlcpy strlcpy_unbound
size_t strlcpy(char *dst, const char *src, size_t siz);
#endif
#ifndef HAVE_GMTIME_R
#define gmtime_r gmtime_r_unbound
struct tm *gmtime_r(const time_t *timep, struct tm *result);
#endif
#ifndef HAVE_REALLOCARRAY
#define reallocarray reallocarrayunbound
void* reallocarray(void *ptr, size_t nmemb, size_t size);
#endif
#if !defined(HAVE_SLEEP) || defined(HAVE_WINDOWS_H)
#define sleep(x) Sleep((x)*1000) /* on win32 */
#endif /* HAVE_SLEEP */
#ifndef HAVE_USLEEP
#define usleep(x) Sleep((x)/1000 + 1) /* on win32 */
#endif /* HAVE_USLEEP */
#ifndef HAVE_RANDOM
#define random rand /* on win32, for tests only (bad random) */
#endif /* HAVE_RANDOM */
#ifndef HAVE_SRANDOM
#define srandom(x) srand(x) /* on win32, for tests only (bad random) */
#endif /* HAVE_SRANDOM */
/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
#ifdef HAVE_WINSOCK2_H
#define FD_SET_T (u_int)
#else
#define FD_SET_T
#endif
#ifndef IPV6_MIN_MTU
#define IPV6_MIN_MTU 1280
#endif /* IPV6_MIN_MTU */
#ifdef MEMCMP_IS_BROKEN
#include "compat/memcmp.h"
#define memcmp memcmp_unbound
int memcmp(const void *x, const void *y, size_t n);
#endif
#ifndef HAVE_CTIME_R
#define ctime_r unbound_ctime_r
char *ctime_r(const time_t *timep, char *buf);
#endif
#ifndef HAVE_STRSEP
#define strsep unbound_strsep
char *strsep(char **stringp, const char *delim);
#endif
#ifndef HAVE_ISBLANK
#define isblank unbound_isblank
int isblank(int c);
#endif
#ifndef HAVE_EXPLICIT_BZERO
#define explicit_bzero unbound_explicit_bzero
void explicit_bzero(void* buf, size_t len);
#endif
#if defined(HAVE_INET_NTOP) && !HAVE_DECL_INET_NTOP
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
#endif
#if defined(HAVE_INET_PTON) && !HAVE_DECL_INET_PTON
int inet_pton(int af, const char* src, void* dst);
#endif
#if !defined(HAVE_STRPTIME) || !defined(STRPTIME_WORKS)
#define strptime unbound_strptime
struct tm;
char *strptime(const char *s, const char *format, struct tm *tm);
#endif
#if !HAVE_DECL_REALLOCARRAY
void *reallocarray(void *ptr, size_t nmemb, size_t size);
#endif
#ifdef HAVE_LIBBSD
#include <bsd/string.h>
#include <bsd/stdlib.h>
#endif
#ifdef HAVE_LIBRESSL
# if !HAVE_DECL_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t siz);
# endif
# if !HAVE_DECL_STRLCAT
size_t strlcat(char *dst, const char *src, size_t siz);
# endif
# if !HAVE_DECL_ARC4RANDOM && defined(HAVE_ARC4RANDOM)
uint32_t arc4random(void);
# endif
# if !HAVE_DECL_ARC4RANDOM_UNIFORM && defined(HAVE_ARC4RANDOM_UNIFORM)
uint32_t arc4random_uniform(uint32_t upper_bound);
# endif
#endif /* HAVE_LIBRESSL */
#ifndef HAVE_ARC4RANDOM
int getentropy(void* buf, size_t len);
uint32_t arc4random(void);
void arc4random_buf(void* buf, size_t n);
void _ARC4_LOCK(void);
void _ARC4_UNLOCK(void);
void _ARC4_LOCK_DESTROY(void);
#endif
#ifndef HAVE_ARC4RANDOM_UNIFORM
uint32_t arc4random_uniform(uint32_t upper_bound);
#endif
#ifdef COMPAT_SHA512
#ifndef SHA512_DIGEST_LENGTH
#define SHA512_BLOCK_LENGTH 128
#define SHA512_DIGEST_LENGTH 64
#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
typedef struct _SHA512_CTX {
uint64_t state[8];
uint64_t bitcount[2];
uint8_t buffer[SHA512_BLOCK_LENGTH];
} SHA512_CTX;
#endif /* SHA512_DIGEST_LENGTH */
void SHA512_Init(SHA512_CTX*);
void SHA512_Update(SHA512_CTX*, void*, size_t);
void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest);
#endif /* COMPAT_SHA512 */
#if defined(HAVE_EVENT_H) && !defined(HAVE_EVENT_BASE_ONCE) && !(defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)) && (defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS))
/* using version of libevent that is not threadsafe. */
# define LIBEVENT_SIGNAL_PROBLEM 1
#endif
#ifndef CHECKED_INET6
# define CHECKED_INET6
# ifdef AF_INET6
# define INET6
# else
# define AF_INET6 28
# endif
#endif /* CHECKED_INET6 */
#ifndef HAVE_GETADDRINFO
struct sockaddr_storage;
#include "compat/fake-rfc2553.h"
#endif
#ifdef UNBOUND_ALLOC_STATS
# define malloc(s) unbound_stat_malloc_log(s, __FILE__, __LINE__, __func__)
# define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
# define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
# define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
void *unbound_stat_malloc(size_t size);
void *unbound_stat_calloc(size_t nmemb, size_t size);
void unbound_stat_free(void *ptr);
void *unbound_stat_realloc(void *ptr, size_t size);
void *unbound_stat_malloc_log(size_t size, const char* file, int line,
const char* func);
void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
int line, const char* func);
void unbound_stat_free_log(void *ptr, const char* file, int line,
const char* func);
void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
int line, const char* func);
#elif defined(UNBOUND_ALLOC_LITE)
# include "util/alloc.h"
#endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */
/** default port for DNS traffic. */
#define UNBOUND_DNS_PORT 53
/** default port for DNS over TLS traffic. */
#define UNBOUND_DNS_OVER_TLS_PORT 853
/** default port for DNS over HTTPS traffic. */
#define UNBOUND_DNS_OVER_HTTPS_PORT 443
/** default port for unbound control traffic, registered port with IANA,
ub-dns-control 8953/tcp unbound dns nameserver control */
#define UNBOUND_CONTROL_PORT 8953
/** the version of unbound-control that this software implements */
#define UNBOUND_CONTROL_VERSION 1
-15
View File
@@ -1,15 +0,0 @@
#!/bin/bash
# Sync the vendored sources from a URL
# eg:
# wget 'https://nlnetlabs.nl/downloads/unbound/unbound-1.18.0.tar.gz'
# import-unbound.sh path/to/unbound-1.18.0.tar.gz
set -x
TARBALL=$1
rm -rf unbound
tar xf $TARBALL
mv unbound-* unbound
rm -rf unbound/{testdata,pythonmod,testcode,winrc,contrib,configure,aclocal.m4,ltmain.sh,install-sh,config.guess,smallapp,doc,.git*,*.m4}
echo > unbound/dnscrypt/dnscrypt_config.h
echo > unbound/dnstap/dnstap_config.h
-11
View File
@@ -1,11 +0,0 @@
#!/bin/bash
bindgen unbound/libunbound/unbound.h -o src/lib.rs \
--no-layout-tests \
--raw-line "#![allow(non_snake_case)]" \
--raw-line "#![allow(non_camel_case_types)]" \
--raw-line "#![allow(non_upper_case_globals)]" \
--raw-line "#![allow(clippy::unreadable_literal)]" \
--raw-line "#![allow(clippy::upper_case_acronyms)]" \
--raw-line "#![allow(rustdoc::broken_intra_doc_links)]" \
--generate=functions,types,vars \
-486
View File
@@ -1,486 +0,0 @@
/* automatically generated by rust-bindgen 0.66.1 */
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(clippy::unreadable_literal)]
#![allow(clippy::upper_case_acronyms)]
#![allow(rustdoc::broken_intra_doc_links)]
pub const UB_STATS_QTYPE_NUM: u32 = 256;
pub const UB_STATS_QCLASS_NUM: u32 = 256;
pub const UB_STATS_RCODE_NUM: u32 = 16;
pub const UB_STATS_OPCODE_NUM: u32 = 16;
pub const UB_STATS_BUCKET_NUM: u32 = 40;
pub const UB_STATS_RPZ_ACTION_NUM: u32 = 10;
#[doc = " The validation context is created to hold the resolver status,\n validation keys and a small cache (containing messages, rrsets,\n roundtrip times, trusted keys, lameness information).\n\n Its contents are internally defined."]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ub_ctx {
_unused: [u8; 0],
}
#[doc = " The validation and resolution results.\n Allocated by the resolver, and need to be freed by the application\n with ub_resolve_free()."]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ub_result {
#[doc = " The original question, name text string."]
pub qname: *mut ::std::os::raw::c_char,
#[doc = " the type asked for"]
pub qtype: ::std::os::raw::c_int,
#[doc = " the class asked for"]
pub qclass: ::std::os::raw::c_int,
#[doc = " a list of network order DNS rdata items, terminated with a\n NULL pointer, so that data[0] is the first result entry,\n data[1] the second, and the last entry is NULL.\n If there was no data, data[0] is NULL."]
pub data: *mut *mut ::std::os::raw::c_char,
#[doc = " the length in bytes of the data items, len[i] for data[i]"]
pub len: *mut ::std::os::raw::c_int,
#[doc = " canonical name for the result (the final cname).\n zero terminated string.\n May be NULL if no canonical name exists."]
pub canonname: *mut ::std::os::raw::c_char,
#[doc = " DNS RCODE for the result. May contain additional error code if\n there was no data due to an error. 0 (NOERROR) if okay."]
pub rcode: ::std::os::raw::c_int,
#[doc = " The DNS answer packet. Network formatted. Can contain DNSSEC types."]
pub answer_packet: *mut ::std::os::raw::c_void,
#[doc = " length of the answer packet in octets."]
pub answer_len: ::std::os::raw::c_int,
#[doc = " If there is any data, this is true.\n If false, there was no data (nxdomain may be true, rcode can be set)."]
pub havedata: ::std::os::raw::c_int,
#[doc = " If there was no data, and the domain did not exist, this is true.\n If it is false, and there was no data, then the domain name\n is purported to exist, but the requested data type is not available."]
pub nxdomain: ::std::os::raw::c_int,
#[doc = " True, if the result is validated securely.\n False, if validation failed or domain queried has no security info.\n\n It is possible to get a result with no data (havedata is false),\n and secure is true. This means that the non-existence of the data\n was cryptographically proven (with signatures)."]
pub secure: ::std::os::raw::c_int,
#[doc = " If the result was not secure (secure==0), and this result is due\n to a security failure, bogus is true.\n This means the data has been actively tampered with, signatures\n failed, expected signatures were not present, timestamps on\n signatures were out of date and so on.\n\n If !secure and !bogus, this can happen if the data is not secure\n because security is disabled for that domain name.\n This means the data is from a domain where data is not signed."]
pub bogus: ::std::os::raw::c_int,
#[doc = " If the result is bogus this contains a string (zero terminated)\n that describes the failure. There may be other errors as well\n as the one described, the description may not be perfectly accurate.\n Is NULL if the result is not bogus."]
pub why_bogus: *mut ::std::os::raw::c_char,
#[doc = " If the query or one of its subqueries was ratelimited. Useful if\n ratelimiting is enabled and answer to the client is SERVFAIL as a\n result."]
pub was_ratelimited: ::std::os::raw::c_int,
#[doc = " TTL for the result, in seconds. If the security is bogus, then\n you also cannot trust this value."]
pub ttl: ::std::os::raw::c_int,
}
#[doc = " Callback for results of async queries.\n The readable function definition looks like:\n void my_callback(void* my_arg, int err, struct ub_result* result);\n It is called with\n\tvoid* my_arg: your pointer to a (struct of) data of your choice,\n\t\tor NULL.\n\tint err: if 0 all is OK, otherwise an error occurred and no results\n\t are forthcoming.\n\tstruct result: pointer to more detailed result structure.\n\t\tThis structure is allocated on the heap and needs to be\n\t\tfreed with ub_resolve_free(result);"]
pub type ub_callback_type = ::std::option::Option<
unsafe extern "C" fn(
arg1: *mut ::std::os::raw::c_void,
arg2: ::std::os::raw::c_int,
arg3: *mut ub_result,
),
>;
#[doc = " no error"]
pub const ub_ctx_err_UB_NOERROR: ub_ctx_err = 0;
#[doc = " socket operation. Set to -1, so that if an error from _fd() is\n passed (-1) it gives a socket error."]
pub const ub_ctx_err_UB_SOCKET: ub_ctx_err = -1;
#[doc = " alloc failure"]
pub const ub_ctx_err_UB_NOMEM: ub_ctx_err = -2;
#[doc = " syntax error"]
pub const ub_ctx_err_UB_SYNTAX: ub_ctx_err = -3;
#[doc = " DNS service failed"]
pub const ub_ctx_err_UB_SERVFAIL: ub_ctx_err = -4;
#[doc = " fork() failed"]
pub const ub_ctx_err_UB_FORKFAIL: ub_ctx_err = -5;
#[doc = " cfg change after finalize()"]
pub const ub_ctx_err_UB_AFTERFINAL: ub_ctx_err = -6;
#[doc = " initialization failed (bad settings)"]
pub const ub_ctx_err_UB_INITFAIL: ub_ctx_err = -7;
#[doc = " error in pipe communication with async bg worker"]
pub const ub_ctx_err_UB_PIPE: ub_ctx_err = -8;
#[doc = " error reading from file (resolv.conf)"]
pub const ub_ctx_err_UB_READFILE: ub_ctx_err = -9;
#[doc = " error async_id does not exist or result already been delivered"]
pub const ub_ctx_err_UB_NOID: ub_ctx_err = -10;
#[doc = " The error constants"]
pub type ub_ctx_err = ::std::os::raw::c_int;
extern "C" {
#[doc = " Create a resolving and validation context.\n The information from /etc/resolv.conf and /etc/hosts is not utilised by\n default. Use ub_ctx_resolvconf and ub_ctx_hosts to read them.\n @return a new context. default initialisation.\n \treturns NULL on error."]
pub fn ub_ctx_create() -> *mut ub_ctx;
}
extern "C" {
#[doc = " Destroy a validation context and free all its resources.\n Outstanding async queries are killed and callbacks are not called for them.\n @param ctx: context to delete."]
pub fn ub_ctx_delete(ctx: *mut ub_ctx);
}
extern "C" {
#[doc = " Set an option for the context.\n @param ctx: context.\n @param opt: option name from the unbound.conf config file format.\n\t(not all settings applicable). The name includes the trailing ':'\n\tfor example ub_ctx_set_option(ctx, \"logfile:\", \"mylog.txt\");\n \tThis is a power-users interface that lets you specify all sorts\n \tof options.\n \tFor some specific options, such as adding trust anchors, special\n \troutines exist.\n @param val: value of the option.\n @return: 0 if OK, else error."]
pub fn ub_ctx_set_option(
ctx: *mut ub_ctx,
opt: *const ::std::os::raw::c_char,
val: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Get an option from the context.\n @param ctx: context.\n @param opt: option name from the unbound.conf config file format.\n\t(not all settings applicable). The name excludes the trailing ':'\n\tfor example ub_ctx_get_option(ctx, \"logfile\", &result);\n \tThis is a power-users interface that lets you specify all sorts\n \tof options.\n @param str: the string is malloced and returned here. NULL on error.\n \tThe caller must free() the string. In cases with multiple\n \tentries (auto-trust-anchor-file), a newline delimited list is\n \treturned in the string.\n @return 0 if OK else an error code (malloc failure, syntax error)."]
pub fn ub_ctx_get_option(
ctx: *mut ub_ctx,
opt: *const ::std::os::raw::c_char,
str_: *mut *mut ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " setup configuration for the given context.\n @param ctx: context.\n @param fname: unbound config file (not all settings applicable).\n \tThis is a power-users interface that lets you specify all sorts\n \tof options.\n \tFor some specific options, such as adding trust anchors, special\n \troutines exist.\n @return: 0 if OK, else error."]
pub fn ub_ctx_config(
ctx: *mut ub_ctx,
fname: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Set machine to forward DNS queries to, the caching resolver to use.\n IP4 or IP6 address. Forwards all DNS requests to that machine, which\n is expected to run a recursive resolver. If the proxy is not\n DNSSEC-capable, validation may fail. Can be called several times, in\n that case the addresses are used as backup servers.\n\n To read the list of nameservers from /etc/resolv.conf (from DHCP or so),\n use the call ub_ctx_resolvconf.\n\n @param ctx: context.\n\tAt this time it is only possible to set configuration before the\n\tfirst resolve is done.\n @param addr: address, IP4 or IP6 in string format.\n \tIf the addr is NULL, forwarding is disabled.\n @return 0 if OK, else error."]
pub fn ub_ctx_set_fwd(
ctx: *mut ub_ctx,
addr: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Use DNS over TLS to send queries to machines set with ub_ctx_set_fwd().\n\n @param ctx: context.\n\tAt this time it is only possible to set configuration before the\n\tfirst resolve is done.\n @param tls: enable or disable DNS over TLS\n @return 0 if OK, else error."]
pub fn ub_ctx_set_tls(ctx: *mut ub_ctx, tls: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Add a stub zone, with given address to send to. This is for custom\n root hints or pointing to a local authoritative dns server.\n For dns resolvers and the 'DHCP DNS' ip address, use ub_ctx_set_fwd.\n This is similar to a stub-zone entry in unbound.conf.\n\n @param ctx: context.\n\tIt is only possible to set configuration before the\n\tfirst resolve is done.\n @param zone: name of the zone, string.\n @param addr: address, IP4 or IP6 in string format.\n \tThe addr is added to the list of stub-addresses if the entry exists.\n \tIf the addr is NULL the stub entry is removed.\n @param isprime: set to true to set stub-prime to yes for the stub.\n \tFor local authoritative servers, people usually set it to false,\n \tFor root hints it should be set to true.\n @return 0 if OK, else error."]
pub fn ub_ctx_set_stub(
ctx: *mut ub_ctx,
zone: *const ::std::os::raw::c_char,
addr: *const ::std::os::raw::c_char,
isprime: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Read list of nameservers to use from the filename given.\n Usually \"/etc/resolv.conf\". Uses those nameservers as caching proxies.\n If they do not support DNSSEC, validation may fail.\n\n Only nameservers are picked up, the searchdomain, ndots and other\n settings from resolv.conf(5) are ignored.\n\n @param ctx: context.\n\tAt this time it is only possible to set configuration before the\n\tfirst resolve is done.\n @param fname: file name string. If NULL \"/etc/resolv.conf\" is used.\n @return 0 if OK, else error."]
pub fn ub_ctx_resolvconf(
ctx: *mut ub_ctx,
fname: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Read list of hosts from the filename given.\n Usually \"/etc/hosts\".\n These addresses are not flagged as DNSSEC secure when queried for.\n\n @param ctx: context.\n\tAt this time it is only possible to set configuration before the\n\tfirst resolve is done.\n @param fname: file name string. If NULL \"/etc/hosts\" is used.\n @return 0 if OK, else error."]
pub fn ub_ctx_hosts(
ctx: *mut ub_ctx,
fname: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Add a trust anchor to the given context.\n The trust anchor is a string, on one line, that holds a valid DNSKEY or\n DS RR.\n @param ctx: context.\n\tAt this time it is only possible to add trusted keys before the\n\tfirst resolve is done.\n @param ta: string, with zone-format RR on one line.\n \t[domainname] [TTL optional] [type] [class optional] [rdata contents]\n @return 0 if OK, else error."]
pub fn ub_ctx_add_ta(
ctx: *mut ub_ctx,
ta: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Add trust anchors to the given context.\n Pass name of a file with DS and DNSKEY records (like from dig or drill).\n @param ctx: context.\n\tAt this time it is only possible to add trusted keys before the\n\tfirst resolve is done.\n @param fname: filename of file with keyfile with trust anchors.\n @return 0 if OK, else error."]
pub fn ub_ctx_add_ta_file(
ctx: *mut ub_ctx,
fname: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Add trust anchor to the given context that is tracked with RFC5011\n automated trust anchor maintenance. The file is written to when the\n trust anchor is changed.\n Pass the name of a file that was output from eg. unbound-anchor,\n or you can start it by providing a trusted DNSKEY or DS record on one\n line in the file.\n @param ctx: context.\n\tAt this time it is only possible to add trusted keys before the\n\tfirst resolve is done.\n @param fname: filename of file with trust anchor.\n @return 0 if OK, else error."]
pub fn ub_ctx_add_ta_autr(
ctx: *mut ub_ctx,
fname: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Add trust anchors to the given context.\n Pass the name of a bind-style config file with trusted-keys{}.\n @param ctx: context.\n\tAt this time it is only possible to add trusted keys before the\n\tfirst resolve is done.\n @param fname: filename of file with bind-style config entries with trust\n \tanchors.\n @return 0 if OK, else error."]
pub fn ub_ctx_trustedkeys(
ctx: *mut ub_ctx,
fname: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Set debug output (and error output) to the specified stream.\n Pass NULL to disable. Default is stderr.\n @param ctx: context.\n @param out: FILE* out file stream to log to.\n \tType void* to avoid stdio dependency of this header file.\n @return 0 if OK, else error."]
pub fn ub_ctx_debugout(
ctx: *mut ub_ctx,
out: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Set debug verbosity for the context\n Output is directed to stderr.\n @param ctx: context.\n @param d: debug level, 0 is off, 1 is very minimal, 2 is detailed,\n\tand 3 is lots.\n @return 0 if OK, else error."]
pub fn ub_ctx_debuglevel(ctx: *mut ub_ctx, d: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Set a context behaviour for asynchronous action.\n @param ctx: context.\n @param dothread: if true, enables threading and a call to resolve_async()\n\tcreates a thread to handle work in the background.\n\tIf false, a process is forked to handle work in the background.\n\tChanges to this setting after async() calls have been made have\n\tno effect (delete and re-create the context to change).\n @return 0 if OK, else error."]
pub fn ub_ctx_async(ctx: *mut ub_ctx, dothread: ::std::os::raw::c_int)
-> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Poll a context to see if it has any new results\n Do not poll in a loop, instead extract the fd below to poll for readiness,\n and then check, or wait using the wait routine.\n @param ctx: context.\n @return: 0 if nothing to read, or nonzero if a result is available.\n \tIf nonzero, call ctx_process() to do callbacks."]
pub fn ub_poll(ctx: *mut ub_ctx) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Wait for a context to finish with results. Calls ub_process() after\n the wait for you. After the wait, there are no more outstanding\n asynchronous queries.\n @param ctx: context.\n @return: 0 if OK, else error."]
pub fn ub_wait(ctx: *mut ub_ctx) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Get file descriptor. Wait for it to become readable, at this point\n answers are returned from the asynchronous validating resolver.\n Then call the ub_process to continue processing.\n This routine works immediately after context creation, the fd\n does not change.\n @param ctx: context.\n @return: -1 on error, or file descriptor to use select(2) with."]
pub fn ub_fd(ctx: *mut ub_ctx) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Call this routine to continue processing results from the validating\n resolver (when the fd becomes readable).\n Will perform necessary callbacks.\n @param ctx: context\n @return: 0 if OK, else error."]
pub fn ub_process(ctx: *mut ub_ctx) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Perform resolution and validation of the target name.\n @param ctx: context.\n\tThe context is finalized, and can no longer accept config changes.\n @param name: domain name in text format (a zero terminated text string).\n @param rrtype: type of RR in host order, 1 is A (address).\n @param rrclass: class of RR in host order, 1 is IN (for internet).\n @param result: the result data is returned in a newly allocated result\n \tstructure. May be NULL on return, return value is set to an error\n \tin that case (out of memory).\n @return 0 if OK, else error."]
pub fn ub_resolve(
ctx: *mut ub_ctx,
name: *const ::std::os::raw::c_char,
rrtype: ::std::os::raw::c_int,
rrclass: ::std::os::raw::c_int,
result: *mut *mut ub_result,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Perform resolution and validation of the target name.\n Asynchronous, after a while, the callback will be called with your\n data and the result.\n @param ctx: context.\n\tIf no thread or process has been created yet to perform the\n\twork in the background, it is created now.\n\tThe context is finalized, and can no longer accept config changes.\n @param name: domain name in text format (a string).\n @param rrtype: type of RR in host order, 1 is A.\n @param rrclass: class of RR in host order, 1 is IN (for internet).\n @param mydata: this data is your own data (you can pass NULL),\n \tand is passed on to the callback function.\n @param callback: this is called on completion of the resolution.\n \tIt is called as:\n \tvoid callback(void* mydata, int err, struct ub_result* result)\n \twith mydata: the same as passed here, you may pass NULL,\n \twith err: is 0 when a result has been found.\n \twith result: a newly allocated result structure.\n\t\tThe result may be NULL, in that case err is set.\n\n \tIf an error happens during processing, your callback will be called\n \twith error set to a nonzero value (and result==NULL).\n @param async_id: if you pass a non-NULL value, an identifier number is\n\treturned for the query as it is in progress. It can be used to\n\tcancel the query.\n @return 0 if OK, else error."]
pub fn ub_resolve_async(
ctx: *mut ub_ctx,
name: *const ::std::os::raw::c_char,
rrtype: ::std::os::raw::c_int,
rrclass: ::std::os::raw::c_int,
mydata: *mut ::std::os::raw::c_void,
callback: ub_callback_type,
async_id: *mut ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Cancel an async query in progress.\n Its callback will not be called.\n\n @param ctx: context.\n @param async_id: which query to cancel.\n @return 0 if OK, else error.\n This routine can return an error if the async_id passed does not exist\n or has already been delivered. If another thread is processing results\n at the same time, the result may be delivered at the same time and the\n cancel fails with an error. Also the cancel can fail due to a system\n error, no memory or socket failures."]
pub fn ub_cancel(ctx: *mut ub_ctx, async_id: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Free storage associated with a result structure.\n @param result: to free"]
pub fn ub_resolve_free(result: *mut ub_result);
}
extern "C" {
#[doc = " Convert error value to a human readable string.\n @param err: error code from one of the libunbound functions.\n \tThe error codes are from the type enum ub_ctx_err.\n @return pointer to constant text string, zero terminated."]
pub fn ub_strerror(err: ::std::os::raw::c_int) -> *const ::std::os::raw::c_char;
}
extern "C" {
#[doc = " Debug routine. Print the local zone information to debug output.\n @param ctx: context. Is finalized by the routine.\n @return 0 if OK, else error."]
pub fn ub_ctx_print_local_zones(ctx: *mut ub_ctx) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Add a new zone with the zonetype to the local authority info of the\n library.\n @param ctx: context. Is finalized by the routine.\n @param zone_name: name of the zone in text, \"example.com\"\n\tIf it already exists, the type is updated.\n @param zone_type: type of the zone (like for unbound.conf) in text.\n @return 0 if OK, else error."]
pub fn ub_ctx_zone_add(
ctx: *mut ub_ctx,
zone_name: *const ::std::os::raw::c_char,
zone_type: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Remove zone from local authority info of the library.\n @param ctx: context. Is finalized by the routine.\n @param zone_name: name of the zone in text, \"example.com\"\n\tIf it does not exist, nothing happens.\n @return 0 if OK, else error."]
pub fn ub_ctx_zone_remove(
ctx: *mut ub_ctx,
zone_name: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Add localdata to the library local authority info.\n Similar to local-data config statement.\n @param ctx: context. Is finalized by the routine.\n @param data: the resource record in text format, for example\n\t\"www.example.com IN A 127.0.0.1\"\n @return 0 if OK, else error."]
pub fn ub_ctx_data_add(
ctx: *mut ub_ctx,
data: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Remove localdata from the library local authority info.\n @param ctx: context. Is finalized by the routine.\n @param data: the name to delete all data from, like \"www.example.com\".\n @return 0 if OK, else error."]
pub fn ub_ctx_data_remove(
ctx: *mut ub_ctx,
data: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
extern "C" {
#[doc = " Get a version string from the libunbound implementation.\n @return a static constant string with the version number."]
pub fn ub_version() -> *const ::std::os::raw::c_char;
}
#[doc = " Some global statistics that are not in struct stats_info,\n this struct is shared on a shm segment (shm-key in unbound.conf)"]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ub_shm_stat_info {
pub num_threads: ::std::os::raw::c_int,
pub time: ub_shm_stat_info__bindgen_ty_1,
pub mem: ub_shm_stat_info__bindgen_ty_2,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ub_shm_stat_info__bindgen_ty_1 {
pub now_sec: ::std::os::raw::c_longlong,
pub now_usec: ::std::os::raw::c_longlong,
pub up_sec: ::std::os::raw::c_longlong,
pub up_usec: ::std::os::raw::c_longlong,
pub elapsed_sec: ::std::os::raw::c_longlong,
pub elapsed_usec: ::std::os::raw::c_longlong,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ub_shm_stat_info__bindgen_ty_2 {
pub msg: ::std::os::raw::c_longlong,
pub rrset: ::std::os::raw::c_longlong,
pub val: ::std::os::raw::c_longlong,
pub iter: ::std::os::raw::c_longlong,
pub subnet: ::std::os::raw::c_longlong,
pub ipsecmod: ::std::os::raw::c_longlong,
pub respip: ::std::os::raw::c_longlong,
pub dnscrypt_shared_secret: ::std::os::raw::c_longlong,
pub dnscrypt_nonce: ::std::os::raw::c_longlong,
pub dynlib: ::std::os::raw::c_longlong,
}
#[doc = " per worker statistics."]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ub_server_stats {
#[doc = " number of queries from clients received."]
pub num_queries: ::std::os::raw::c_longlong,
#[doc = " number of queries that have been dropped/ratelimited by ip."]
pub num_queries_ip_ratelimited: ::std::os::raw::c_longlong,
#[doc = " number of queries with a valid DNS Cookie."]
pub num_queries_cookie_valid: ::std::os::raw::c_longlong,
#[doc = " number of queries with only the client part of the DNS Cookie."]
pub num_queries_cookie_client: ::std::os::raw::c_longlong,
#[doc = " number of queries with invalid DNS Cookie."]
pub num_queries_cookie_invalid: ::std::os::raw::c_longlong,
#[doc = " number of queries that had a cache-miss."]
pub num_queries_missed_cache: ::std::os::raw::c_longlong,
#[doc = " number of prefetch queries - cachehits with prefetch"]
pub num_queries_prefetch: ::std::os::raw::c_longlong,
#[doc = " number of queries which are too late to process"]
pub num_queries_timed_out: ::std::os::raw::c_longlong,
#[doc = " the longest wait time in the queue"]
pub max_query_time_us: ::std::os::raw::c_longlong,
#[doc = " Sum of the querylistsize of the worker for\n every query that missed cache. To calculate average."]
pub sum_query_list_size: ::std::os::raw::c_longlong,
#[doc = " max value of query list size reached."]
pub max_query_list_size: ::std::os::raw::c_longlong,
#[doc = " Extended stats below (bool)"]
pub extended: ::std::os::raw::c_int,
#[doc = " qtype stats"]
pub qtype: [::std::os::raw::c_longlong; 256usize],
#[doc = " bigger qtype values not in array"]
pub qtype_big: ::std::os::raw::c_longlong,
#[doc = " qclass stats"]
pub qclass: [::std::os::raw::c_longlong; 256usize],
#[doc = " bigger qclass values not in array"]
pub qclass_big: ::std::os::raw::c_longlong,
#[doc = " query opcodes"]
pub qopcode: [::std::os::raw::c_longlong; 16usize],
#[doc = " number of queries over TCP"]
pub qtcp: ::std::os::raw::c_longlong,
#[doc = " number of outgoing queries over TCP"]
pub qtcp_outgoing: ::std::os::raw::c_longlong,
#[doc = " number of outgoing queries over UDP"]
pub qudp_outgoing: ::std::os::raw::c_longlong,
#[doc = " number of queries over (DNS over) TLS"]
pub qtls: ::std::os::raw::c_longlong,
#[doc = " number of queries over (DNS over) HTTPS"]
pub qhttps: ::std::os::raw::c_longlong,
#[doc = " number of queries over IPv6"]
pub qipv6: ::std::os::raw::c_longlong,
#[doc = " number of queries with QR bit"]
pub qbit_QR: ::std::os::raw::c_longlong,
#[doc = " number of queries with AA bit"]
pub qbit_AA: ::std::os::raw::c_longlong,
#[doc = " number of queries with TC bit"]
pub qbit_TC: ::std::os::raw::c_longlong,
#[doc = " number of queries with RD bit"]
pub qbit_RD: ::std::os::raw::c_longlong,
#[doc = " number of queries with RA bit"]
pub qbit_RA: ::std::os::raw::c_longlong,
#[doc = " number of queries with Z bit"]
pub qbit_Z: ::std::os::raw::c_longlong,
#[doc = " number of queries with AD bit"]
pub qbit_AD: ::std::os::raw::c_longlong,
#[doc = " number of queries with CD bit"]
pub qbit_CD: ::std::os::raw::c_longlong,
#[doc = " number of queries with EDNS OPT record"]
pub qEDNS: ::std::os::raw::c_longlong,
#[doc = " number of queries with EDNS with DO flag"]
pub qEDNS_DO: ::std::os::raw::c_longlong,
#[doc = " answer rcodes"]
pub ans_rcode: [::std::os::raw::c_longlong; 16usize],
#[doc = " answers with pseudo rcode 'nodata'"]
pub ans_rcode_nodata: ::std::os::raw::c_longlong,
#[doc = " answers that were secure (AD)"]
pub ans_secure: ::std::os::raw::c_longlong,
#[doc = " answers that were bogus (withheld as SERVFAIL)"]
pub ans_bogus: ::std::os::raw::c_longlong,
#[doc = " rrsets marked bogus by validator"]
pub rrset_bogus: ::std::os::raw::c_longlong,
#[doc = " number of queries that have been ratelimited by domain recursion."]
pub queries_ratelimited: ::std::os::raw::c_longlong,
#[doc = " unwanted traffic received on server-facing ports"]
pub unwanted_replies: ::std::os::raw::c_longlong,
#[doc = " unwanted traffic received on client-facing ports"]
pub unwanted_queries: ::std::os::raw::c_longlong,
#[doc = " usage of tcp accept list"]
pub tcp_accept_usage: ::std::os::raw::c_longlong,
#[doc = " expired answers served from cache"]
pub ans_expired: ::std::os::raw::c_longlong,
#[doc = " histogram data exported to array\n if the array is the same size, no data is lost, and\n if all histograms are same size (is so by default) then\n adding up works well."]
pub hist: [::std::os::raw::c_longlong; 40usize],
#[doc = " number of message cache entries"]
pub msg_cache_count: ::std::os::raw::c_longlong,
#[doc = " number of rrset cache entries"]
pub rrset_cache_count: ::std::os::raw::c_longlong,
#[doc = " number of infra cache entries"]
pub infra_cache_count: ::std::os::raw::c_longlong,
#[doc = " number of key cache entries"]
pub key_cache_count: ::std::os::raw::c_longlong,
#[doc = " maximum number of collisions in the msg cache"]
pub msg_cache_max_collisions: ::std::os::raw::c_longlong,
#[doc = " maximum number of collisions in the rrset cache"]
pub rrset_cache_max_collisions: ::std::os::raw::c_longlong,
#[doc = " number of queries that used dnscrypt"]
pub num_query_dnscrypt_crypted: ::std::os::raw::c_longlong,
#[doc = " number of queries that queried dnscrypt certificates"]
pub num_query_dnscrypt_cert: ::std::os::raw::c_longlong,
#[doc = " number of queries in clear text and not asking for the certificates"]
pub num_query_dnscrypt_cleartext: ::std::os::raw::c_longlong,
#[doc = " number of malformed encrypted queries"]
pub num_query_dnscrypt_crypted_malformed: ::std::os::raw::c_longlong,
#[doc = " number of queries which did not have a shared secret in cache"]
pub num_query_dnscrypt_secret_missed_cache: ::std::os::raw::c_longlong,
#[doc = " number of dnscrypt shared secret cache entries"]
pub shared_secret_cache_count: ::std::os::raw::c_longlong,
#[doc = " number of queries which are replays"]
pub num_query_dnscrypt_replay: ::std::os::raw::c_longlong,
#[doc = " number of dnscrypt nonces cache entries"]
pub nonce_cache_count: ::std::os::raw::c_longlong,
#[doc = " number of queries for unbound's auth_zones, upstream query"]
pub num_query_authzone_up: ::std::os::raw::c_longlong,
#[doc = " number of queries for unbound's auth_zones, downstream answers"]
pub num_query_authzone_down: ::std::os::raw::c_longlong,
#[doc = " number of times neg cache records were used to generate NOERROR\n responses."]
pub num_neg_cache_noerror: ::std::os::raw::c_longlong,
#[doc = " number of times neg cache records were used to generate NXDOMAIN\n responses."]
pub num_neg_cache_nxdomain: ::std::os::raw::c_longlong,
#[doc = " number of queries answered from edns-subnet specific data"]
pub num_query_subnet: ::std::os::raw::c_longlong,
#[doc = " number of queries answered from edns-subnet specific data, and\n the answer was from the edns-subnet cache."]
pub num_query_subnet_cache: ::std::os::raw::c_longlong,
#[doc = " number of queries served from cachedb"]
pub num_query_cachedb: ::std::os::raw::c_longlong,
#[doc = " number of bytes in the stream wait buffers"]
pub mem_stream_wait: ::std::os::raw::c_longlong,
#[doc = " number of bytes in the HTTP2 query buffers"]
pub mem_http2_query_buffer: ::std::os::raw::c_longlong,
#[doc = " number of bytes in the HTTP2 response buffers"]
pub mem_http2_response_buffer: ::std::os::raw::c_longlong,
#[doc = " number of TLS connection resume"]
pub qtls_resume: ::std::os::raw::c_longlong,
#[doc = " RPZ action stats"]
pub rpz_action: [::std::os::raw::c_longlong; 10usize],
}
#[doc = " Statistics to send over the control pipe when asked\n This struct is made to be memcopied, sent in binary.\n shm mapped with (number+1) at num_threads+1, with first as total"]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ub_stats_info {
#[doc = " the thread stats"]
pub svr: ub_server_stats,
#[doc = " mesh stats: current number of states"]
pub mesh_num_states: ::std::os::raw::c_longlong,
#[doc = " mesh stats: current number of reply (user) states"]
pub mesh_num_reply_states: ::std::os::raw::c_longlong,
#[doc = " mesh stats: number of reply states overwritten with a new one"]
pub mesh_jostled: ::std::os::raw::c_longlong,
#[doc = " mesh stats: number of incoming queries dropped"]
pub mesh_dropped: ::std::os::raw::c_longlong,
#[doc = " mesh stats: replies sent"]
pub mesh_replies_sent: ::std::os::raw::c_longlong,
#[doc = " mesh stats: sum of waiting times for the replies"]
pub mesh_replies_sum_wait_sec: ::std::os::raw::c_longlong,
#[doc = " mesh stats: sum of waiting times for the replies"]
pub mesh_replies_sum_wait_usec: ::std::os::raw::c_longlong,
#[doc = " mesh stats: median of waiting times for replies (in sec)"]
pub mesh_time_median: f64,
}
-30
View File
@@ -1,30 +0,0 @@
Copyright (c) 2007, NLnet Labs. All rights reserved.
This software is open source.
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 the NLNET LABS nor the names of its 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 COPYRIGHT
HOLDER 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.
File diff suppressed because it is too large Load Diff
-10
View File
@@ -1,10 +0,0 @@
Unbound README
* ./configure && make && make install
* You can use libevent if you want. libevent is useful when using
many (10000) outgoing ports. By default max 256 ports are opened at
the same time and the builtin alternative is equally capable and a
little faster.
* More detailed README, README.svn, README.tests in doc directory
* manual pages can be found in doc directory, and are installed, unbound(8).
* example configuration file doc/example.conf
@@ -1,278 +0,0 @@
# Travis Testing
Unbound 1.10 and above leverage Travis CI to increase coverage of compilers and platforms. Compilers include Clang and GCC; while platforms include Android, iOS, Linux, and OS X on AMD64, Aarch64, PowerPC and s390x hardware.
Android is tested on armv7a, aarch64, x86 and x86_64. The Android recipes build and install OpenSSL and Expat, and then builds Unbound. The testing is tailored for Android NDK-r19 and above, and includes NDK-r20 and NDK-r21. Mips and Mips64 are not tested because they are no longer supported under current NDKs.
iOS is tested for iPhoneOS, WatchOS, AppleTVOS, iPhoneSimulator, AppleTVSimulator and WatchSimulator. The testing uses Xcode 10 on OS X 10.13.
The Unbound Travis configuration file `.travis.yml` does not use top-level keys like `os:` and `compiler:` so there is no matrix expansion. Instead Unbound specifies the exact job to run under the `jobs:` and `include:` keys.
## Typical recipe
A typical recipe tests Clang and GCC on various hardware. The hardware includes AMD64, Aarch64, PowerPC and s390x. PowerPC is a little-endian platform, and s390x is a big-endian platform. There are pairs of recipes that are similar to the following.
```
- os: linux
name: GCC on Linux, Aarch64
compiler: gcc
arch: arm64
dist: bionic
- os: linux
name: Clang on Linux, Aarch64
compiler: clang
arch: arm64
dist: bionic
```
OS X provides a single recipe to test Clang. GCC is not tested because GCC is an alias for Clang.
## Sanitizer builds
Two sanitizer builds are tested using Clang and GCC, for a total of four builds. The first sanitizer is Undefined Behavior sanitizer (UBsan), and the second is Address sanitizer (Asan). The sanitizers are only run on AMD64 hardware. Note the environment includes `TEST_UBSAN=yes` or `TEST_ASAN=yes` for the sanitizer builds.
The recipes are similar to the following.
```
- os: linux
name: UBsan, GCC on Linux, Amd64
compiler: gcc
arch: amd64
dist: bionic
env: TEST_UBSAN=yes
- os: linux
name: UBsan, Clang on Linux, Amd64
compiler: clang
arch: amd64
dist: bionic
env: TEST_UBSAN=yes
```
When the Travis script encounters a sanitizer it uses different `CFLAGS` and configuration string.
```
if [ "$TEST_UBSAN" = "yes" ]; then
export CFLAGS="-DNDEBUG -g2 -O3 -fsanitize=undefined -fno-sanitize-recover"
./configure
make -j 2
make test
elif [ "$TEST_ASAN" = "yes" ]; then
export CFLAGS="-DNDEBUG -g2 -O3 -fsanitize=address"
./configure
make -j 2
make test
...
```
## Android builds
Travis tests Android builds for the armv7a, aarch64, x86 and x86_64 architectures. The builds are trickier than other builds for several reasons. The testing requires installation of the Android NDK and SDK, it requires a cross-compile, and requires OpenSSL and Expat prerequisites. The Android cross-compiles also require care to set the Autotools triplet, the OpenSSL triplet, the toolchain path, the tool variables, and the sysroot. The discussion below detail the steps of the Android recipes.
### Android job
The first step sets environmental variables for the cross-compile using the Travis job. A typical job with variables is shown below.
```
- os: linux
name: Android armv7a, Linux, Amd64
compiler: clang
arch: amd64
dist: bionic
env:
- TEST_ANDROID=yes
- AUTOTOOLS_HOST=armv7a-linux-androideabi
- OPENSSL_HOST=android-arm
- ANDROID_CPU=armv7a
- ANDROID_API=23
- ANDROID_PREFIX="$HOME/android$ANDROID_API-$ANDROID_CPU"
- ANDROID_SDK_ROOT="$HOME/android-sdk"
- ANDROID_NDK_ROOT="$HOME/android-ndk"
```
### ANDROID_NDK_ROOT
The second step for Android is to set the environmental variables `ANDROID_NDK_ROOT` and `ANDROID_SDK_ROOT`. This is an important step because the NDK and SDK use the variables internally to locate their own tools. Also see [Recommended NDK Directory?](https://groups.google.com/forum/#!topic/android-ndk/qZjhOaynHXc) on the android-ndk mailing list. (Many folks miss this step, or use incorrect variables like `ANDROID_NDK_HOME` or `ANDROID_SDK_HOME`).
If you are working from a developer machine you probably already have the necessary tools installed. You should ensure `ANDROID_NDK_ROOT` and `ANDROID_SDK_ROOT` are set properly.
### Tool installation
The second step installs tools needed for OpenSSL, Expat and Unbound. This step is handled in by the script `contrib/android/install_tools.sh`. The tools include curl, tar, zip, unzip and java.
```
before_script:
- |
if [ "$TEST_ANDROID" = "yes" ]; then
./contrib/android/install_tools.sh
elif [ "$TEST_IOS" = "yes" ]; then
./contrib/ios/install_tools.sh
fi
```
### NDK installation
The third step installs the NDK and SDK. This step is handled in by the script `contrib/android/install_ndk.sh`. The script uses `ANDROID_NDK_ROOT` and `ANDROID_SDK_ROOT` to place the NDK and SDK in the `$HOME` directory.
If you are working from a developer machine you probably already have a NDK and SDK installed.
### Android environment
The fourth step sets the Android cross-compile environment using the script `contrib/android/setenv_android.sh`. The script is `sourced` so the variables in the script are available to the calling shell. The script sets variables like `CC`, `CXX`, `AS` and `AR`; sets `CFLAGS` and `CXXFLAGS`; sets a `sysroot` so Android headers and libraries are found; and adds the path to the toolchain to `PATH`.
`contrib/android/setenv_android.sh` knows which toolchain and architecture to select by inspecting environmental variables set by Travis for the job. In particular, the variables `ANDROID_CPU` and `ANDROID_API` tell `contrib/android/setenv_android.sh` which tools and libraries to select.
The `contrib/android/setenv_android.sh` script specifies the tools in a `case` statement like the following. There is a case for each of the architectures armv7a, aarch64, x86 and x86_64.
```
armv8a|aarch64|arm64|arm64-v8a)
CC="aarch64-linux-android$ANDROID_API-clang"
CXX="aarch64-linux-android$ANDROID_API-clang++"
LD="aarch64-linux-android-ld"
AS="aarch64-linux-android-as"
AR="aarch64-linux-android-ar"
RANLIB="aarch64-linux-android-ranlib"
STRIP="aarch64-linux-android-strip"
CFLAGS="-funwind-tables -fexceptions"
CXXFLAGS="-funwind-tables -fexceptions -frtti"
```
### OpenSSL and Expat
The fifth step builds OpenSSL and Expat. OpenSSL and Expat are built for Android using the scripts `contrib/android/install_openssl.sh` and `contrib/android/install_expat.sh`. The scripts download, configure and install the latest release version of the libraries. The libraries are configured with `--prefix="$ANDROID_PREFIX"` so the headers are placed in `$ANDROID_PREFIX/include` directory, and the libraries are placed in the `$ANDROID_PREFIX/lib` directory.
`ANDROID_PREFIX` is the value `$HOME/android$ANDROID_API-$ANDROID_CPU`. The libraries will be installed in `$HOME/android23-armv7a`, `$HOME/android23-aarch64`, etc. For Autotools projects, the appropriate `PKG_CONFIG_PATH` is exported. `PKG_CONFIG_PATH` is the userland equivalent to sysroot, and allows Autotools to find non-system headers and libraries for an architecture. Typical `PKG_CONFIG_PATH` are `$HOME/android23-armv7a/lib/pkgconfig` and `$HOME/android23-aarch64/lib/pkgconfig`.
OpenSSL also uses a custom configuration file called `15-android.conf`. It is a copy of the OpenSSL's project file and located at `contrib/android/15-android.conf`. The Unbound version is copied to the OpenSSL source files after unpacking the OpenSSL distribution. The Unbound version has legacy NDK support removed and some other fixes, like `ANDROID_NDK_ROOT` awareness. The changes mean Unbound's `15-android.conf` will only work with Unbound, with NDK-r19 and above, and a properly set environment.
OpenSSL is configured with `no-engine`. If you want to include OpenSSL engines then edit `contrib/android/install_openssl.sh` and remove the config option.
### Android build
Finally, once OpenSSL and Expat are built, then the Travis script configures and builds Unbound. The recipe looks as follows.
```
elif [ "$TEST_ANDROID" = "yes" ]; then
export AUTOTOOLS_BUILD="$(./config.guess)"
export PKG_CONFIG_PATH="$ANDROID_PREFIX/lib/pkgconfig"
./contrib/android/install_ndk.sh
source ./contrib/android/setenv_android.sh
./contrib/android/install_openssl.sh
./contrib/android/install_expat.sh
./configure \
--build="$AUTOTOOLS_BUILD" \
--host="$AUTOTOOLS_HOST" \
--prefix="$ANDROID_PREFIX" \
--with-ssl="$ANDROID_PREFIX" \
--with-libexpat="$ANDROID_PREFIX" \
--disable-gost;
make -j 2
make install
```
Travis only smoke tests an Android build using a compile, link and install. The self tests are not run. TODO: figure out how to fire up an emulator, push the tests to the device and run them.
### Android flags
`contrib/android/setenv_android.sh` uses specific flags for `CFLAGS` and `CXXFLAGS`. They are taken from `ndk-build`, so we consider them the official flag set. It is important to use the same flags across projects to avoid subtle problems due to mixing and matching different flags.
`CXXFLAGS` includes `-fexceptions` and `-frtti` because exceptions and runtime type info are disabled by default. `CFLAGS` include `-funwind-tables` and `-fexceptions` to ensure C++ exceptions pass through C code, if needed. Also see `docs/CPLUSPLUS-SUPPORT.html` in the NDK docs.
To inspect the flags used by `ndk-build` for a platform clone ASOP's [ndk-samples](https://github.com/android/ndk-samples/tree/master/hello-jni) and build the `hello-jni` project. Use the `V=1` flag to see the full compiler output from `ndk-build`.
## iOS builds
Travis tests iOS builds for the armv7a, armv7s and aarch64 architectures for iPhoneOS, AppleTVOS and WatchOS. iPhoneOS is tested using both 32-bit builds (iPhones) and 64-bit builds (iPads). Travis also tests compiles against the simulators. The builds are trickier than other builds for several reasons. The testing requires a cross-compile, and requires OpenSSL and Expat prerequisites. The iOS cross-compiles also require care to set the Autotools triplet, the OpenSSL triplet, the toolchain path, the tool variables, and the sysroot. The discussion below detail the steps of the iOS recipes.
### iOS job
The first step sets environmental variables for the cross-compile using the Travis job. A typical job with variables is shown below.
```
- os: osx
osx_image: xcode10
name: Apple iPhone on iOS, armv7
compiler: clang
env:
- TEST_IOS=yes
- AUTOTOOLS_HOST=armv7-apple-ios
- OPENSSL_HOST=ios-cross
- IOS_SDK=iPhoneOS
- IOS_CPU=armv7s
- IOS_PREFIX="$HOME/$IOS_SDK-$IOS_CPU"
```
### Tool installation
The second step installs tools needed for OpenSSL, Expat and Unbound. This step is handled in by the script `contrib/ios/install_tools.sh`. The tools include autotools, curl and perl. The installation happens at the `before_script:` stage of Travis.
```
before_script:
- |
if [ "$TEST_ANDROID" = "yes" ]; then
./contrib/android/install_tools.sh
elif [ "$TEST_IOS" = "yes" ]; then
./contrib/ios/install_tools.sh
fi
```
### iOS environment
The third step sets the iOS cross-compile environment using the script `contrib/ios/setenv_ios.sh`. The script is `sourced` so the variables in the script are available to the calling shell. The script sets variables like `CC`, `CXX`, `AS` and `AR`; sets `CFLAGS` and `CXXFLAGS`; sets a `sysroot` so iOS headers and libraries are found; and adds the path to the toolchain to `PATH`.
`contrib/ios/setenv_ios.sh` knows which toolchain and architecture to select by inspecting environmental variables set by Travis for the job. In particular, the variables `IOS_SDK` and `IOS_CPU` tell `contrib/ios/setenv_ios.sh` which tools and libraries to select.
The `contrib/ios/setenv_ios.sh` script specifies the tools to use during the cross-compile. For Apple SDKs, the tool names are the same as a desktop. There are no special prefixes for the mobile tools.
```
CPP=cpp
CC=clang
CXX=clang++
LD=ld
AS=as
AR=ar
RANLIB=ranlib
STRIP=strip
```
If you are working from a developer machine you probably already have the necessary tools installed.
### OpenSSL and Expat
The fourth step builds OpenSSL and Expat. OpenSSL and Expat are built for iOS using the scripts `contrib/ios/install_openssl.sh` and `contrib/ios/install_expat.sh`. The scripts download, configure and install the latest release version of the libraries. The libraries are configured with `--prefix="$IOS_PREFIX"` so the headers are placed in `$IOS_PREFIX/include` directory, and the libraries are placed in the `$IOS_PREFIX/lib` directory.
`IOS_PREFIX` is the value `$HOME/$IOS_SDK-$IOS_CPU`. The scheme handles both iOS SDKs and cpu architectures so the pair receives a unique installation directory. The libraries will be installed in `$HOME/iPhoneOS-armv7s`, `$HOME/iPhoneOS-arm64`, `$HOME/iPhoneSimulator-i386`, etc. For Autotools projects, the appropriate `PKG_CONFIG_PATH` is exported.
`PKG_CONFIG_PATH` is an important variable. It is the userland equivalent to sysroot, and allows Autotools to find non-system headers and libraries for an architecture. Typical `PKG_CONFIG_PATH` are `$HOME/iPhoneOS-armv7s/lib/pkgconfig` and `$HOME/iPhoneOS-arm64/lib/pkgconfig`.
OpenSSL also uses a custom configuration file called `15-ios.conf`. It is a copy of the OpenSSL's project file and located at `contrib/ios/15-ios.conf`. The Unbound version is copied to the OpenSSL source files after unpacking the OpenSSL distribution. The changes mean Unbound's `15-ios.conf` will only work with Unbound and a properly set environment.
OpenSSL is configured with `no-engine`. Engines require dynamic loading so engines are disabled permanently in `15-ios.conf`.
### iOS build
Finally, once OpenSSL and Expat are built, then the Travis script configures and builds Unbound. The full recipe looks as follows.
```
elif [ "$TEST_IOS" = "yes" ]; then
export AUTOTOOLS_BUILD="$(./config.guess)"
export PKG_CONFIG_PATH="$IOS_PREFIX/lib/pkgconfig"
source ./contrib/ios/setenv_ios.sh
./contrib/ios/install_openssl.sh
./contrib/ios/install_expat.sh
./configure \
--build="$AUTOTOOLS_BUILD" \
--host="$AUTOTOOLS_HOST" \
--prefix="$IOS_PREFIX" \
--with-ssl="$IOS_PREFIX" \
--with-libexpat="$IOS_PREFIX" \
--disable-gost;
make -j 2
make install
```
Travis only smoke tests an iOS build using a compile, link and install. The self tests are not run. TODO: figure out how to fire up an simulator, push the tests to the device and run them.
### iOS flags
`contrib/ios/setenv_ios.sh` uses specific flags for `CFLAGS` and `CXXFLAGS`. They are taken from Xcode, so we consider them the official flag set. It is important to use the same flags across projects to avoid subtle problems due to mixing and matching different flags.
-40
View File
@@ -1,40 +0,0 @@
# Unbound
[![Github Build Status](https://github.com/NLnetLabs/unbound/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/NLnetLabs/unbound/actions)
[![Packaging status](https://repology.org/badge/tiny-repos/unbound.svg)](https://repology.org/project/unbound/versions)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/unbound.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:unbound)
[![Documentation Status](https://readthedocs.org/projects/unbound/badge/?version=latest)](https://unbound.readthedocs.io/en/latest/?badge=latest)
[![Mastodon Follow](https://img.shields.io/mastodon/follow/109262826617293067?domain=https%3A%2F%2Ffosstodon.org&style=social)](https://fosstodon.org/@nlnetlabs)
Unbound is a validating, recursive, caching DNS resolver. It is designed to be
fast and lean and incorporates modern features based on open standards. If you
have any feedback, we would love to hear from you. Don’t hesitate to
[create an issue on Github](https://github.com/NLnetLabs/unbound/issues/new)
or post a message on the [Unbound mailing list](https://lists.nlnetlabs.nl/mailman/listinfo/unbound-users).
You can learn more about Unbound by reading our
[documentation](https://unbound.docs.nlnetlabs.nl/).
## Compiling
Make sure you have the C toolchain, OpenSSL and its include files, and libexpat
installed. Unbound can be compiled and installed using:
```
./configure && make && make install
```
You can use libevent if you want. libevent is useful when using many (10000)
outgoing ports. By default max 256 ports are opened at the same time and the
builtin alternative is equally capable and a little faster.
Use the `--with-libevent=dir` configure option to compile Unbound with libevent
support.
## Unbound configuration
All of Unbound's configuration options are described in the man pages, which
will be installed and are available on the Unbound
[documentation page](https://unbound.docs.nlnetlabs.nl/).
An example configuration file is located in
[doc/example.conf](https://github.com/NLnetLabs/unbound/blob/master/doc/example.conf.in).
-31
View File
@@ -1,31 +0,0 @@
# Security Policy
## Supported Versions
NLnet Labs adheres to the straightforward, semantic versioning scheme that is
commonly used in the software industry.
Support is provided in respect of the latest release, i.e. releases with the
highest minor and patch version level. We do not backport security fixes to
older (minor) versions. In the event a new major version is released (e.g. from
3.2.18 to 4.0.0), support will also be provided on the latest minor version of
the previous major version (3.2.18) for a period of one year from the release of
the new major version (4.0.0).
In the event that, during this period, a new patch or minor version of the
previous major version is released, then support on these versions will only be
provided for the remainder of the one-year-period.
You can find detailed information on our software support policy here:
https://www.nlnetlabs.nl/support/software-support-policy/
## Reporting a Vulnerability
We take security very seriously. If you have discovered a security vulnerability
in one of our projects and you would like to report it to us, you can send an
encrypted message to our Security Entry Point.
Details are described here:
https://www.nlnetlabs.nl/security-report/
@@ -1,921 +0,0 @@
/*
* cachedb/cachedb.c - cache from a database external to the program module
*
* Copyright (c) 2016, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains a module that uses an external database to cache
* dns responses.
*/
#include "config.h"
#ifdef USE_CACHEDB
#include "cachedb/cachedb.h"
#include "cachedb/redis.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "services/cache/dns.h"
#include "validator/val_neg.h"
#include "validator/val_secalgo.h"
#include "iterator/iter_utils.h"
#include "sldns/parseutil.h"
#include "sldns/wire2str.h"
#include "sldns/sbuffer.h"
/* header file for htobe64 */
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
#ifndef HAVE_HTOBE64
# ifdef HAVE_LIBKERN_OSBYTEORDER_H
/* In practice this is specific to MacOS X. We assume it doesn't have
* htobe64/be64toh but has alternatives with a different name. */
# include <libkern/OSByteOrder.h>
# define htobe64(x) OSSwapHostToBigInt64(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
# else
/* not OSX */
/* Some compilers do not define __BYTE_ORDER__, like IBM XLC on AIX */
# if __BIG_ENDIAN__
# define be64toh(n) (n)
# define htobe64(n) (n)
# else
# define be64toh(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
# define htobe64(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
# endif /* _ENDIAN */
# endif /* HAVE_LIBKERN_OSBYTEORDER_H */
#endif /* HAVE_BE64TOH */
/** the unit test testframe for cachedb, its module state contains
* a cache for a couple queries (in memory). */
struct testframe_moddata {
/** lock for mutex */
lock_basic_type lock;
/** key for single stored data element, NULL if none */
char* stored_key;
/** data for single stored data element, NULL if none */
uint8_t* stored_data;
/** length of stored data */
size_t stored_datalen;
};
static int
testframe_init(struct module_env* env, struct cachedb_env* cachedb_env)
{
struct testframe_moddata* d;
verbose(VERB_ALGO, "testframe_init");
d = (struct testframe_moddata*)calloc(1,
sizeof(struct testframe_moddata));
cachedb_env->backend_data = (void*)d;
if(!cachedb_env->backend_data) {
log_err("out of memory");
return 0;
}
/* Register an EDNS option (65534) to bypass the worker cache lookup
* for testing */
if(!edns_register_option(LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST,
1 /* bypass cache */,
0 /* no aggregation */, env)) {
log_err("testframe_init, could not register test opcode");
free(d);
return 0;
}
lock_basic_init(&d->lock);
lock_protect(&d->lock, d, sizeof(*d));
return 1;
}
static void
testframe_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
{
struct testframe_moddata* d = (struct testframe_moddata*)
cachedb_env->backend_data;
(void)env;
verbose(VERB_ALGO, "testframe_deinit");
if(!d)
return;
lock_basic_destroy(&d->lock);
free(d->stored_key);
free(d->stored_data);
free(d);
}
static int
testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, struct sldns_buffer* result_buffer)
{
struct testframe_moddata* d = (struct testframe_moddata*)
cachedb_env->backend_data;
(void)env;
verbose(VERB_ALGO, "testframe_lookup of %s", key);
lock_basic_lock(&d->lock);
if(d->stored_key && strcmp(d->stored_key, key) == 0) {
if(d->stored_datalen > sldns_buffer_capacity(result_buffer)) {
lock_basic_unlock(&d->lock);
return 0; /* too large */
}
verbose(VERB_ALGO, "testframe_lookup found %d bytes",
(int)d->stored_datalen);
sldns_buffer_clear(result_buffer);
sldns_buffer_write(result_buffer, d->stored_data,
d->stored_datalen);
sldns_buffer_flip(result_buffer);
lock_basic_unlock(&d->lock);
return 1;
}
lock_basic_unlock(&d->lock);
return 0;
}
static void
testframe_store(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, uint8_t* data, size_t data_len, time_t ATTR_UNUSED(ttl))
{
struct testframe_moddata* d = (struct testframe_moddata*)
cachedb_env->backend_data;
(void)env;
lock_basic_lock(&d->lock);
verbose(VERB_ALGO, "testframe_store %s (%d bytes)", key, (int)data_len);
/* free old data element (if any) */
free(d->stored_key);
d->stored_key = NULL;
free(d->stored_data);
d->stored_data = NULL;
d->stored_datalen = 0;
d->stored_data = memdup(data, data_len);
if(!d->stored_data) {
lock_basic_unlock(&d->lock);
log_err("out of memory");
return;
}
d->stored_datalen = data_len;
d->stored_key = strdup(key);
if(!d->stored_key) {
free(d->stored_data);
d->stored_data = NULL;
d->stored_datalen = 0;
lock_basic_unlock(&d->lock);
return;
}
lock_basic_unlock(&d->lock);
/* (key,data) successfully stored */
}
/** The testframe backend is for unit tests */
static struct cachedb_backend testframe_backend = { "testframe",
testframe_init, testframe_deinit, testframe_lookup, testframe_store
};
/** find a particular backend from possible backends */
static struct cachedb_backend*
cachedb_find_backend(const char* str)
{
#ifdef USE_REDIS
if(strcmp(str, redis_backend.name) == 0)
return &redis_backend;
#endif
if(strcmp(str, testframe_backend.name) == 0)
return &testframe_backend;
/* TODO add more backends here */
return NULL;
}
/** apply configuration to cachedb module 'global' state */
static int
cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg)
{
const char* backend_str = cfg->cachedb_backend;
if(!backend_str || *backend_str==0)
return 1;
cachedb_env->backend = cachedb_find_backend(backend_str);
if(!cachedb_env->backend) {
log_err("cachedb: cannot find backend name '%s'", backend_str);
return 0;
}
/* TODO see if more configuration needs to be applied or not */
return 1;
}
int
cachedb_init(struct module_env* env, int id)
{
struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1,
sizeof(struct cachedb_env));
if(!cachedb_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)cachedb_env;
if(!cachedb_apply_cfg(cachedb_env, env->cfg)) {
log_err("cachedb: could not apply configuration settings.");
free(cachedb_env);
env->modinfo[id] = NULL;
return 0;
}
/* see if a backend is selected */
if(!cachedb_env->backend || !cachedb_env->backend->name)
return 1;
if(!(*cachedb_env->backend->init)(env, cachedb_env)) {
log_err("cachedb: could not init %s backend",
cachedb_env->backend->name);
free(cachedb_env);
env->modinfo[id] = NULL;
return 0;
}
cachedb_env->enabled = 1;
if(env->cfg->serve_expired_reply_ttl)
log_warn(
"cachedb: serve-expired-reply-ttl is set but not working for data "
"originating from the external cache; 0 TLL is used for those.");
if(env->cfg->serve_expired_client_timeout)
log_warn(
"cachedb: serve-expired-client-timeout is set but not working for "
"data originating from the external cache; expired data are used "
"in the reply without first trying to refresh the data.");
return 1;
}
void
cachedb_deinit(struct module_env* env, int id)
{
struct cachedb_env* cachedb_env;
if(!env || !env->modinfo[id])
return;
cachedb_env = (struct cachedb_env*)env->modinfo[id];
if(cachedb_env->enabled) {
(*cachedb_env->backend->deinit)(env, cachedb_env);
}
free(cachedb_env);
env->modinfo[id] = NULL;
}
/** new query for cachedb */
static int
cachedb_new(struct module_qstate* qstate, int id)
{
struct cachedb_qstate* iq = (struct cachedb_qstate*)regional_alloc(
qstate->region, sizeof(struct cachedb_qstate));
qstate->minfo[id] = iq;
if(!iq)
return 0;
memset(iq, 0, sizeof(*iq));
/* initialise it */
/* TODO */
return 1;
}
/**
* Return an error
* @param qstate: our query state
* @param id: module id
* @param rcode: error code (DNS errcode).
* @return: 0 for use by caller, to make notation easy, like:
* return error_response(..).
*/
static int
error_response(struct module_qstate* qstate, int id, int rcode)
{
verbose(VERB_QUERY, "return error response %s",
sldns_lookup_by_id(sldns_rcodes, rcode)?
sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
qstate->return_rcode = rcode;
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return 0;
}
/**
* Hash the query name, type, class and dbacess-secret into lookup buffer.
* @param qstate: query state with query info
* and env->cfg with secret.
* @param buf: returned buffer with hash to lookup
* @param len: length of the buffer.
*/
static void
calc_hash(struct module_qstate* qstate, char* buf, size_t len)
{
uint8_t clear[1024];
size_t clen = 0;
uint8_t hash[CACHEDB_HASHSIZE/8];
const char* hex = "0123456789ABCDEF";
const char* secret = qstate->env->cfg->cachedb_secret;
size_t i;
/* copy the hash info into the clear buffer */
if(clen + qstate->qinfo.qname_len < sizeof(clear)) {
memmove(clear+clen, qstate->qinfo.qname,
qstate->qinfo.qname_len);
clen += qstate->qinfo.qname_len;
}
if(clen + 4 < sizeof(clear)) {
uint16_t t = htons(qstate->qinfo.qtype);
uint16_t c = htons(qstate->qinfo.qclass);
memmove(clear+clen, &t, 2);
memmove(clear+clen+2, &c, 2);
clen += 4;
}
if(secret && secret[0] && clen + strlen(secret) < sizeof(clear)) {
memmove(clear+clen, secret, strlen(secret));
clen += strlen(secret);
}
/* hash the buffer */
secalgo_hash_sha256(clear, clen, hash);
#ifdef HAVE_EXPLICIT_BZERO
explicit_bzero(clear, clen);
#else
memset(clear, 0, clen);
#endif
/* hex encode output for portability (some online dbs need
* no nulls, no control characters, and so on) */
log_assert(len >= sizeof(hash)*2 + 1);
(void)len;
for(i=0; i<sizeof(hash); i++) {
buf[i*2] = hex[(hash[i]&0xf0)>>4];
buf[i*2+1] = hex[hash[i]&0x0f];
}
buf[sizeof(hash)*2] = 0;
}
/** convert data from return_msg into the data buffer */
static int
prep_data(struct module_qstate* qstate, struct sldns_buffer* buf)
{
uint64_t timestamp, expiry;
size_t oldlim;
struct edns_data edns;
memset(&edns, 0, sizeof(edns));
edns.edns_present = 1;
edns.bits = EDNS_DO;
edns.ext_rcode = 0;
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
if(!qstate->return_msg || !qstate->return_msg->rep)
return 0;
/* do not store failures like SERVFAIL in the cachedb, this avoids
* overwriting expired, valid, content with broken content. */
if(FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
LDNS_RCODE_NXDOMAIN &&
FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
LDNS_RCODE_YXDOMAIN)
return 0;
/* We don't store the reply if its TTL is 0 unless serve-expired is
* enabled. Such a reply won't be reusable and simply be a waste for
* the backend. It's also compatible with the default behavior of
* dns_cache_store_msg(). */
if(qstate->return_msg->rep->ttl == 0 &&
!qstate->env->cfg->serve_expired)
return 0;
/* The EDE is added to the out-list so it is encoded in the cached message */
if (qstate->env->cfg->ede && qstate->return_msg->rep->reason_bogus != LDNS_EDE_NONE) {
edns_opt_list_append_ede(&edns.opt_list_out, qstate->env->scratch,
qstate->return_msg->rep->reason_bogus,
qstate->return_msg->rep->reason_bogus_str);
}
if(verbosity >= VERB_ALGO)
log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo,
qstate->return_msg->rep);
if(!reply_info_answer_encode(&qstate->return_msg->qinfo,
qstate->return_msg->rep, 0, qstate->query_flags,
buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0))
return 0;
/* TTLs in the return_msg are relative to time(0) so we have to
* store that, we also store the smallest ttl in the packet+time(0)
* as the packet expiry time */
/* qstate->return_msg->rep->ttl contains that relative shortest ttl */
timestamp = (uint64_t)*qstate->env->now;
expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl;
timestamp = htobe64(timestamp);
expiry = htobe64(expiry);
oldlim = sldns_buffer_limit(buf);
if(oldlim + sizeof(timestamp)+sizeof(expiry) >=
sldns_buffer_capacity(buf))
return 0; /* doesn't fit. */
sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry));
sldns_buffer_write_at(buf, oldlim, &timestamp, sizeof(timestamp));
sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry,
sizeof(expiry));
return 1;
}
/** check expiry, return true if matches OK */
static int
good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
{
uint64_t expiry;
/* the expiry time is the last bytes of the buffer */
if(sldns_buffer_limit(buf) < sizeof(expiry))
return 0;
sldns_buffer_read_at(buf, sldns_buffer_limit(buf)-sizeof(expiry),
&expiry, sizeof(expiry));
expiry = be64toh(expiry);
/* Check if we are allowed to return expired entries:
* - serve_expired needs to be set
* - if SERVE_EXPIRED_TTL is set make sure that the record is not older
* than that. */
if((time_t)expiry < *qstate->env->now &&
(!qstate->env->cfg->serve_expired ||
(SERVE_EXPIRED_TTL &&
*qstate->env->now - (time_t)expiry > SERVE_EXPIRED_TTL)))
return 0;
return 1;
}
/* Adjust the TTL of the given RRset by 'subtract'. If 'subtract' is
* negative, set the TTL to 0. */
static void
packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract)
{
size_t i;
size_t total = data->count + data->rrsig_count;
if(subtract >= 0 && data->ttl > subtract)
data->ttl -= subtract;
else data->ttl = 0;
for(i=0; i<total; i++) {
if(subtract >= 0 && data->rr_ttl[i] > subtract)
data->rr_ttl[i] -= subtract;
else data->rr_ttl[i] = 0;
}
data->ttl_add = (subtract < data->ttl_add) ? (data->ttl_add - subtract) : 0;
}
/* Adjust the TTL of a DNS message and its RRs by 'adjust'. If 'adjust' is
* negative, set the TTLs to 0. */
static void
adjust_msg_ttl(struct dns_msg* msg, time_t adjust)
{
size_t i;
if(adjust >= 0 && msg->rep->ttl > adjust)
msg->rep->ttl -= adjust;
else
msg->rep->ttl = 0;
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
for(i=0; i<msg->rep->rrset_count; i++) {
packed_rrset_ttl_subtract((struct packed_rrset_data*)msg->
rep->rrsets[i]->entry.data, adjust);
}
}
/** convert dns message in buffer to return_msg */
static int
parse_data(struct module_qstate* qstate, struct sldns_buffer* buf)
{
struct msg_parse* prs;
struct edns_data edns;
struct edns_option* ede;
uint64_t timestamp, expiry;
time_t adjust;
size_t lim = sldns_buffer_limit(buf);
if(lim < LDNS_HEADER_SIZE+sizeof(timestamp)+sizeof(expiry))
return 0; /* too short */
/* remove timestamp and expiry from end */
sldns_buffer_read_at(buf, lim-sizeof(expiry), &expiry, sizeof(expiry));
sldns_buffer_read_at(buf, lim-sizeof(expiry)-sizeof(timestamp),
&timestamp, sizeof(timestamp));
expiry = be64toh(expiry);
timestamp = be64toh(timestamp);
/* parse DNS packet */
regional_free_all(qstate->env->scratch);
prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
sizeof(struct msg_parse));
if(!prs)
return 0; /* out of memory */
memset(prs, 0, sizeof(*prs));
memset(&edns, 0, sizeof(edns));
sldns_buffer_set_limit(buf, lim - sizeof(expiry)-sizeof(timestamp));
if(parse_packet(buf, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
sldns_buffer_set_limit(buf, lim);
return 0;
}
if(parse_extract_edns_from_response_msg(prs, &edns, qstate->env->scratch) !=
LDNS_RCODE_NOERROR) {
sldns_buffer_set_limit(buf, lim);
return 0;
}
qstate->return_msg = dns_alloc_msg(buf, prs, qstate->region);
sldns_buffer_set_limit(buf, lim);
if(!qstate->return_msg)
return 0;
/* We find the EDE in the in-list after parsing */
if(qstate->env->cfg->ede &&
(ede = edns_opt_list_find(edns.opt_list_in, LDNS_EDNS_EDE))) {
if(ede->opt_len >= 2) {
qstate->return_msg->rep->reason_bogus =
sldns_read_uint16(ede->opt_data);
}
/* allocate space and store the error string and it's size */
if(ede->opt_len > 2) {
size_t ede_len = ede->opt_len - 2;
qstate->return_msg->rep->reason_bogus_str = regional_alloc(
qstate->region, sizeof(char) * (ede_len+1));
memcpy(qstate->return_msg->rep->reason_bogus_str,
ede->opt_data+2, ede_len);
qstate->return_msg->rep->reason_bogus_str[ede_len] = 0;
}
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
/* see how much of the TTL expired, and remove it */
if(*qstate->env->now <= (time_t)timestamp) {
verbose(VERB_ALGO, "cachedb msg adjust by zero");
return 1; /* message from the future (clock skew?) */
}
adjust = *qstate->env->now - (time_t)timestamp;
if(qstate->return_msg->rep->ttl < adjust) {
verbose(VERB_ALGO, "cachedb msg expired");
/* If serve-expired is enabled, we still use an expired message
* setting the TTL to 0. */
if(!qstate->env->cfg->serve_expired ||
(FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
!= LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
!= LDNS_RCODE_NXDOMAIN &&
FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
!= LDNS_RCODE_YXDOMAIN))
return 0; /* message expired */
else
adjust = -1;
}
verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust);
adjust_msg_ttl(qstate->return_msg, adjust);
/* Similar to the unbound worker, if serve-expired is enabled and
* the msg would be considered to be expired, mark the state so a
* refetch will be scheduled. The comparison between 'expiry' and
* 'now' should be redundant given how these values were calculated,
* but we check it just in case as does good_expiry_and_qinfo(). */
if(qstate->env->cfg->serve_expired &&
(adjust == -1 || (time_t)expiry < *qstate->env->now)) {
qstate->need_refetch = 1;
}
return 1;
}
/**
* Lookup the qstate.qinfo in extcache, store in qstate.return_msg.
* return true if lookup was successful.
*/
static int
cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie)
{
char key[(CACHEDB_HASHSIZE/8)*2+1];
calc_hash(qstate, key, sizeof(key));
/* call backend to fetch data for key into scratch buffer */
if( !(*ie->backend->lookup)(qstate->env, ie, key,
qstate->env->scratch_buffer)) {
return 0;
}
/* check expiry date and check if query-data matches */
if( !good_expiry_and_qinfo(qstate, qstate->env->scratch_buffer) ) {
return 0;
}
/* parse dns message into return_msg */
if( !parse_data(qstate, qstate->env->scratch_buffer) ) {
return 0;
}
return 1;
}
/**
* Store the qstate.return_msg in extcache for key qstate.info
*/
static void
cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie)
{
char key[(CACHEDB_HASHSIZE/8)*2+1];
calc_hash(qstate, key, sizeof(key));
/* prepare data in scratch buffer */
if(!prep_data(qstate, qstate->env->scratch_buffer))
return;
/* call backend */
(*ie->backend->store)(qstate->env, ie, key,
sldns_buffer_begin(qstate->env->scratch_buffer),
sldns_buffer_limit(qstate->env->scratch_buffer),
qstate->return_msg->rep->ttl);
}
/**
* See if unbound's internal cache can answer the query
*/
static int
cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde)
{
uint8_t* dpname=NULL;
size_t dpnamelen=0;
struct dns_msg* msg;
/* for testframe bypass this lookup */
if(cde->backend == &testframe_backend) {
return 0;
}
if(iter_stub_fwd_no_cache(qstate, &qstate->qinfo,
&dpname, &dpnamelen))
return 0; /* no cache for these queries */
msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
qstate->qinfo.qclass, qstate->query_flags,
qstate->region, qstate->env->scratch,
1, /* no partial messages with only a CNAME */
dpname, dpnamelen
);
if(!msg && qstate->env->neg_cache &&
iter_qname_indicates_dnssec(qstate->env, &qstate->qinfo)) {
/* lookup in negative cache; may result in
* NOERROR/NODATA or NXDOMAIN answers that need validation */
msg = val_neg_getmsg(qstate->env->neg_cache, &qstate->qinfo,
qstate->region, qstate->env->rrset_cache,
qstate->env->scratch_buffer,
*qstate->env->now, 1/*add SOA*/, NULL,
qstate->env->cfg);
}
if(!msg)
return 0;
/* this is the returned msg */
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = msg;
return 1;
}
/**
* Store query into the internal cache of unbound.
*/
static void
cachedb_intcache_store(struct module_qstate* qstate)
{
uint32_t store_flags = qstate->query_flags;
if(qstate->env->cfg->serve_expired)
store_flags |= DNSCACHE_STORE_ZEROTTL;
if(!qstate->return_msg)
return;
(void)dns_cache_store(qstate->env, &qstate->qinfo,
qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
qstate->region, store_flags, qstate->qstarttime);
}
/**
* Handle a cachedb module event with a query
* @param qstate: query state (from the mesh), passed between modules.
* contains qstate->env module environment with global caches and so on.
* @param iq: query state specific for this module. per-query.
* @param ie: environment specific for this module. global.
* @param id: module id.
*/
static void
cachedb_handle_query(struct module_qstate* qstate,
struct cachedb_qstate* ATTR_UNUSED(iq),
struct cachedb_env* ie, int id)
{
qstate->is_cachedb_answer = 0;
/* check if we are enabled, and skip if so */
if(!ie->enabled) {
/* pass request to next module */
qstate->ext_state[id] = module_wait_module;
return;
}
if(qstate->blacklist || qstate->no_cache_lookup) {
/* cache is blacklisted or we are instructed from edns to not look */
/* pass request to next module */
qstate->ext_state[id] = module_wait_module;
return;
}
/* lookup inside unbound's internal cache.
* This does not look for expired entries. */
if(cachedb_intcache_lookup(qstate, ie)) {
if(verbosity >= VERB_ALGO) {
if(qstate->return_msg->rep)
log_dns_msg("cachedb internal cache lookup",
&qstate->return_msg->qinfo,
qstate->return_msg->rep);
else log_info("cachedb internal cache lookup: rcode %s",
sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)
?sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name
:"??");
}
/* we are done with the query */
qstate->ext_state[id] = module_finished;
return;
}
/* ask backend cache to see if we have data */
if(cachedb_extcache_lookup(qstate, ie)) {
if(verbosity >= VERB_ALGO)
log_dns_msg(ie->backend->name,
&qstate->return_msg->qinfo,
qstate->return_msg->rep);
/* store this result in internal cache */
cachedb_intcache_store(qstate);
/* In case we have expired data but there is a client timer for expired
* answers, pass execution to next module in order to try updating the
* data first.
* TODO: this needs revisit. The expired data stored from cachedb has
* 0 TTL which is picked up by iterator later when looking in the cache.
* Document that ext cachedb does not work properly with
* serve_stale_reply_ttl yet. */
if(qstate->need_refetch && qstate->serve_expired_data &&
qstate->serve_expired_data->timer) {
qstate->return_msg = NULL;
qstate->ext_state[id] = module_wait_module;
return;
}
qstate->is_cachedb_answer = 1;
/* we are done with the query */
qstate->ext_state[id] = module_finished;
return;
}
/* no cache fetches */
/* pass request to next module */
qstate->ext_state[id] = module_wait_module;
}
/**
* Handle a cachedb module event with a response from the iterator.
* @param qstate: query state (from the mesh), passed between modules.
* contains qstate->env module environment with global caches and so on.
* @param iq: query state specific for this module. per-query.
* @param ie: environment specific for this module. global.
* @param id: module id.
*/
static void
cachedb_handle_response(struct module_qstate* qstate,
struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id)
{
qstate->is_cachedb_answer = 0;
/* check if we are not enabled or instructed to not cache, and skip */
if(!ie->enabled || qstate->no_cache_store) {
/* we are done with the query */
qstate->ext_state[id] = module_finished;
return;
}
/* store the item into the backend cache */
cachedb_extcache_store(qstate, ie);
/* we are done with the query */
qstate->ext_state[id] = module_finished;
}
void
cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct cachedb_env* ie = (struct cachedb_env*)qstate->env->modinfo[id];
struct cachedb_qstate* iq = (struct cachedb_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "cachedb[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
if(iq) log_query_info(VERB_QUERY, "cachedb operate: query",
&qstate->qinfo);
/* perform cachedb state machine */
if((event == module_event_new || event == module_event_pass) &&
iq == NULL) {
if(!cachedb_new(qstate, id)) {
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
iq = (struct cachedb_qstate*)qstate->minfo[id];
}
if(iq && (event == module_event_pass || event == module_event_new)) {
cachedb_handle_query(qstate, iq, ie, id);
return;
}
if(iq && (event == module_event_moddone)) {
cachedb_handle_response(qstate, iq, ie, id);
return;
}
if(iq && outbound) {
/* cachedb does not need to process responses at this time
* ignore it.
cachedb_process_response(qstate, iq, ie, id, outbound, event);
*/
return;
}
if(event == module_event_error) {
verbose(VERB_ALGO, "got called with event error, giving up");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
if(!iq && (event == module_event_moddone)) {
/* during priming, module done but we never started */
qstate->ext_state[id] = module_finished;
return;
}
log_err("bad event for cachedb");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
void
cachedb_inform_super(struct module_qstate* ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate* ATTR_UNUSED(super))
{
/* cachedb does not use subordinate requests at this time */
verbose(VERB_ALGO, "cachedb inform_super was called");
}
void
cachedb_clear(struct module_qstate* qstate, int id)
{
struct cachedb_qstate* iq;
if(!qstate)
return;
iq = (struct cachedb_qstate*)qstate->minfo[id];
if(iq) {
/* free contents of iq */
/* TODO */
}
qstate->minfo[id] = NULL;
}
size_t
cachedb_get_mem(struct module_env* env, int id)
{
struct cachedb_env* ie = (struct cachedb_env*)env->modinfo[id];
if(!ie)
return 0;
return sizeof(*ie); /* TODO - more mem */
}
/**
* The cachedb function block
*/
static struct module_func_block cachedb_block = {
"cachedb",
&cachedb_init, &cachedb_deinit, &cachedb_operate,
&cachedb_inform_super, &cachedb_clear, &cachedb_get_mem
};
struct module_func_block*
cachedb_get_funcblock(void)
{
return &cachedb_block;
}
#endif /* USE_CACHEDB */
@@ -1,112 +0,0 @@
/*
* cachedb/cachedb.h - cache from a database external to the program module
*
* Copyright (c) 2016, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains a module that uses an external database to cache
* dns responses.
*/
#include "util/module.h"
struct cachedb_backend;
/**
* The global variable environment contents for the cachedb
* Shared between threads, this represents long term information.
* Like database connections.
*/
struct cachedb_env {
/** true is cachedb is enabled, the backend is turned on */
int enabled;
/** the backend routines */
struct cachedb_backend* backend;
/** backend specific data here */
void* backend_data;
};
/**
* Per query state for the cachedb module.
*/
struct cachedb_qstate {
int todo;
};
/**
* Backend call routines
*/
struct cachedb_backend {
/** backend name */
const char* name;
/** Init(env, cachedb_env): false on setup failure */
int (*init)(struct module_env*, struct cachedb_env*);
/** Deinit - close db for program exit */
void (*deinit)(struct module_env*, struct cachedb_env*);
/** Lookup (env, cachedb_env, key, result_buffer): true if found */
int (*lookup)(struct module_env*, struct cachedb_env*, char*,
struct sldns_buffer*);
/** Store (env, cachedb_env, key, data, data_len) */
void (*store)(struct module_env*, struct cachedb_env*, char*,
uint8_t*, size_t, time_t);
};
#define CACHEDB_HASHSIZE 256 /* bit hash */
/** Init the cachedb module */
int cachedb_init(struct module_env* env, int id);
/** Deinit the cachedb module */
void cachedb_deinit(struct module_env* env, int id);
/** Operate on an event on a query (in qstate). */
void cachedb_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound);
/** Subordinate query done, inform this super request of its conclusion */
void cachedb_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** clear the cachedb query-specific contents out of qstate */
void cachedb_clear(struct module_qstate* qstate, int id);
/** return memory estimate for cachedb module */
size_t cachedb_get_mem(struct module_env* env, int id);
/**
* Get the function block with pointers to the cachedb functions
* @return the function block for "cachedb".
*/
struct module_func_block* cachedb_get_funcblock(void);
@@ -1,350 +0,0 @@
/*
* cachedb/redis.c - cachedb redis module
*
* Copyright (c) 2018, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains a module that uses the redis database to cache
* dns responses.
*/
#include "config.h"
#ifdef USE_CACHEDB
#include "cachedb/redis.h"
#include "cachedb/cachedb.h"
#include "util/alloc.h"
#include "util/config_file.h"
#include "sldns/sbuffer.h"
#ifdef USE_REDIS
#include "hiredis/hiredis.h"
struct redis_moddata {
redisContext** ctxs; /* thread-specific redis contexts */
int numctxs; /* number of ctx entries */
const char* server_host; /* server's IP address or host name */
int server_port; /* server's TCP port */
const char* server_path; /* server's unix path, or "", NULL if unused */
const char* server_password; /* server's AUTH password, or "", NULL if unused */
struct timeval timeout; /* timeout for connection setup and commands */
};
static redisReply* redis_command(struct module_env*, struct cachedb_env*,
const char*, const uint8_t*, size_t);
static redisContext*
redis_connect(const struct redis_moddata* moddata)
{
redisContext* ctx;
if(moddata->server_path && moddata->server_path[0]!=0) {
ctx = redisConnectUnixWithTimeout(moddata->server_path,
moddata->timeout);
} else {
ctx = redisConnectWithTimeout(moddata->server_host,
moddata->server_port, moddata->timeout);
}
if(!ctx || ctx->err) {
const char *errstr = "out of memory";
if(ctx)
errstr = ctx->errstr;
log_err("failed to connect to redis server: %s", errstr);
goto fail;
}
if(redisSetTimeout(ctx, moddata->timeout) != REDIS_OK) {
log_err("failed to set redis timeout");
goto fail;
}
if(moddata->server_password && moddata->server_password[0]!=0) {
redisReply* rep;
rep = redisCommand(ctx, "AUTH %s", moddata->server_password);
if(!rep || rep->type == REDIS_REPLY_ERROR) {
log_err("failed to authenticate with password");
freeReplyObject(rep);
goto fail;
}
freeReplyObject(rep);
}
verbose(VERB_OPS, "Connection to Redis established");
return ctx;
fail:
if(ctx)
redisFree(ctx);
return NULL;
}
static int
redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
{
int i;
struct redis_moddata* moddata = NULL;
verbose(VERB_OPS, "Redis initialization");
moddata = calloc(1, sizeof(struct redis_moddata));
if(!moddata) {
log_err("out of memory");
return 0;
}
moddata->numctxs = env->cfg->num_threads;
moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->ctxs) {
log_err("out of memory");
free(moddata);
return 0;
}
/* note: server_host is a shallow reference to configured string.
* we don't have to free it in this module. */
moddata->server_host = env->cfg->redis_server_host;
moddata->server_port = env->cfg->redis_server_port;
moddata->server_path = env->cfg->redis_server_path;
moddata->server_password = env->cfg->redis_server_password;
moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000;
for(i = 0; i < moddata->numctxs; i++)
moddata->ctxs[i] = redis_connect(moddata);
cachedb_env->backend_data = moddata;
if(env->cfg->redis_expire_records) {
redisReply* rep = NULL;
int redis_reply_type = 0;
/** check if setex command is supported */
rep = redis_command(env, cachedb_env,
"SETEX __UNBOUND_REDIS_CHECK__ 1 none", NULL, 0);
if(!rep) {
/** init failed, no response from redis server*/
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
return 0;
}
redis_reply_type = rep->type;
freeReplyObject(rep);
switch(redis_reply_type) {
case REDIS_REPLY_STATUS:
break;
default:
/** init failed, setex command not supported */
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
return 0;
}
}
return 1;
}
static void
redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
{
struct redis_moddata* moddata = (struct redis_moddata*)
cachedb_env->backend_data;
(void)env;
verbose(VERB_OPS, "Redis deinitialization");
if(!moddata)
return;
if(moddata->ctxs) {
int i;
for(i = 0; i < moddata->numctxs; i++) {
if(moddata->ctxs[i])
redisFree(moddata->ctxs[i]);
}
free(moddata->ctxs);
}
free(moddata);
}
/*
* Send a redis command and get a reply. Unified so that it can be used for
* both SET and GET. If 'data' is non-NULL the command is supposed to be
* SET and GET otherwise, but the implementation of this function is agnostic
* about the semantics (except for logging): 'command', 'data', and 'data_len'
* are opaquely passed to redisCommand().
* This function first checks whether a connection with a redis server has
* been established; if not it tries to set up a new one.
* It returns redisReply returned from redisCommand() or NULL if some low
* level error happens. The caller is responsible to check the return value,
* if it's non-NULL, it has to free it with freeReplyObject().
*/
static redisReply*
redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
const char* command, const uint8_t* data, size_t data_len)
{
redisContext* ctx;
redisReply* rep;
struct redis_moddata* d = (struct redis_moddata*)
cachedb_env->backend_data;
/* We assume env->alloc->thread_num is a unique ID for each thread
* in [0, num-of-threads). We could treat it as an error condition
* if the assumption didn't hold, but it seems to be a fundamental
* assumption throughout the unbound architecture, so we simply assert
* it. */
log_assert(env->alloc->thread_num < d->numctxs);
ctx = d->ctxs[env->alloc->thread_num];
/* If we've not established a connection to the server or we've closed
* it on a failure, try to re-establish a new one. Failures will be
* logged in redis_connect(). */
if(!ctx) {
ctx = redis_connect(d);
d->ctxs[env->alloc->thread_num] = ctx;
}
if(!ctx)
return NULL;
/* Send the command and get a reply, synchronously. */
rep = (redisReply*)redisCommand(ctx, command, data, data_len);
if(!rep) {
/* Once an error as a NULL-reply is returned the context cannot
* be reused and we'll need to set up a new connection. */
log_err("redis_command: failed to receive a reply, "
"closing connection: %s", ctx->errstr);
redisFree(ctx);
d->ctxs[env->alloc->thread_num] = NULL;
return NULL;
}
/* Check error in reply to unify logging in that case.
* The caller may perform context-dependent checks and logging. */
if(rep->type == REDIS_REPLY_ERROR)
log_err("redis: %s resulted in an error: %s",
data ? "set" : "get", rep->str);
return rep;
}
static int
redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, struct sldns_buffer* result_buffer)
{
redisReply* rep;
char cmdbuf[4+(CACHEDB_HASHSIZE/8)*2+1]; /* "GET " + key */
int n;
int ret = 0;
verbose(VERB_ALGO, "redis_lookup of %s", key);
n = snprintf(cmdbuf, sizeof(cmdbuf), "GET %s", key);
if(n < 0 || n >= (int)sizeof(cmdbuf)) {
log_err("redis_lookup: unexpected failure to build command");
return 0;
}
rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0);
if(!rep)
return 0;
switch(rep->type) {
case REDIS_REPLY_NIL:
verbose(VERB_ALGO, "redis_lookup: no data cached");
break;
case REDIS_REPLY_STRING:
verbose(VERB_ALGO, "redis_lookup found %d bytes",
(int)rep->len);
if((size_t)rep->len > sldns_buffer_capacity(result_buffer)) {
log_err("redis_lookup: replied data too long: %lu",
(size_t)rep->len);
break;
}
sldns_buffer_clear(result_buffer);
sldns_buffer_write(result_buffer, rep->str, rep->len);
sldns_buffer_flip(result_buffer);
ret = 1;
break;
case REDIS_REPLY_ERROR:
break; /* already logged */
default:
log_err("redis_lookup: unexpected type of reply for (%d)",
rep->type);
break;
}
freeReplyObject(rep);
return ret;
}
static void
redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, uint8_t* data, size_t data_len, time_t ttl)
{
redisReply* rep;
int n;
int set_ttl = (env->cfg->redis_expire_records &&
(!env->cfg->serve_expired || env->cfg->serve_expired_ttl > 0));
/* Supported commands:
* - "SET " + key + " %b"
* - "SETEX " + key + " " + ttl + " %b"
*/
char cmdbuf[6+(CACHEDB_HASHSIZE/8)*2+11+3+1];
if (!set_ttl) {
verbose(VERB_ALGO, "redis_store %s (%d bytes)", key, (int)data_len);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SET %s %%b", key);
} else {
/* add expired ttl time to redis ttl to avoid premature eviction of key */
ttl += env->cfg->serve_expired_ttl;
verbose(VERB_ALGO, "redis_store %s (%d bytes) with ttl %u",
key, (int)data_len, (uint32_t)ttl);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SETEX %s %u %%b", key,
(uint32_t)ttl);
}
if(n < 0 || n >= (int)sizeof(cmdbuf)) {
log_err("redis_store: unexpected failure to build command");
return;
}
rep = redis_command(env, cachedb_env, cmdbuf, data, data_len);
if(rep) {
verbose(VERB_ALGO, "redis_store set completed");
if(rep->type != REDIS_REPLY_STATUS &&
rep->type != REDIS_REPLY_ERROR) {
log_err("redis_store: unexpected type of reply (%d)",
rep->type);
}
freeReplyObject(rep);
}
}
struct cachedb_backend redis_backend = { "redis",
redis_init, redis_deinit, redis_lookup, redis_store
};
#endif /* USE_REDIS */
#endif /* USE_CACHEDB */
@@ -1,45 +0,0 @@
/*
* cachedb/redis.h - cachedb redis module
*
* Copyright (c) 2018, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains a module that uses the redis database to cache
* dns responses.
*/
/** the redis backend definition, contains callable functions
* and name string */
extern struct cachedb_backend redis_backend;
@@ -1,82 +0,0 @@
/* arc4_lock.c - global lock for arc4random
*
* Copyright (c) 2014, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
#include "config.h"
#define LOCKRET(func) func
#ifdef ENABLE_LOCK_CHECKS
#undef ENABLE_LOCK_CHECKS
#endif
#include "util/locks.h"
void _ARC4_LOCK(void);
void _ARC4_UNLOCK(void);
#ifdef THREADS_DISABLED
void _ARC4_LOCK(void)
{
}
void _ARC4_UNLOCK(void)
{
}
void _ARC4_LOCK_DESTROY(void)
{
}
#else /* !THREADS_DISABLED */
static lock_quick_type arc4lock;
static int arc4lockinit = 0;
void _ARC4_LOCK(void)
{
if(!arc4lockinit) {
arc4lockinit = 1;
lock_quick_init(&arc4lock);
}
lock_quick_lock(&arc4lock);
}
void _ARC4_UNLOCK(void)
{
lock_quick_unlock(&arc4lock);
}
void _ARC4_LOCK_DESTROY(void)
{
if(arc4lockinit) {
arc4lockinit = 0;
lock_quick_destroy(&arc4lock);
}
}
#endif /* THREADS_DISABLED */
@@ -1,312 +0,0 @@
/* $OpenBSD: arc4random.c,v 1.41 2014/07/12 13:24:54 deraadt Exp $ */
/*
* Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
/*
* ChaCha based random number generator for OpenBSD.
*/
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#ifndef UB_ON_WINDOWS
#include <sys/mman.h>
#endif
#define KEYSTREAM_ONLY
#include "chacha_private.h"
#define arc4_min(a, b) ((a) < (b) ? (a) : (b))
#ifdef __GNUC__
#define inline __inline
#else /* !__GNUC__ */
#define inline
#endif /* !__GNUC__ */
#ifndef MAP_ANON
#define MAP_ANON MAP_ANONYMOUS
#endif
#define KEYSZ 32
#define IVSZ 8
#define BLOCKSZ 64
#define RSBUFSZ (16*BLOCKSZ)
#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
static struct {
size_t rs_have; /* valid bytes at end of rs_buf */
size_t rs_count; /* bytes till reseed */
} *rs;
/* Preserved in fork children. */
static struct {
chacha_ctx rs_chacha; /* chacha context for random keystream */
u_char rs_buf[RSBUFSZ]; /* keystream blocks */
} *rsx;
static inline void _rs_rekey(u_char *dat, size_t datlen);
/*
* Basic sanity checking; wish we could do better.
*/
static int
fallback_gotdata(char *buf, size_t len)
{
char any_set = 0;
size_t i;
for (i = 0; i < len; ++i)
any_set |= buf[i];
if (any_set == 0)
return -1;
return 0;
}
/* fallback for getentropy in case libc returns failure */
static int
fallback_getentropy_urandom(void *buf, size_t len)
{
size_t i;
int fd, flags;
int save_errno = errno;
start:
flags = O_RDONLY;
#ifdef O_NOFOLLOW
flags |= O_NOFOLLOW;
#endif
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif
fd = open("/dev/urandom", flags, 0);
if (fd == -1) {
if (errno == EINTR)
goto start;
goto nodevrandom;
}
#ifndef O_CLOEXEC
# ifdef HAVE_FCNTL
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
# endif
#endif
for (i = 0; i < len; ) {
size_t wanted = len - i;
ssize_t ret = read(fd, (char*)buf + i, wanted);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
close(fd);
goto nodevrandom;
}
i += ret;
}
close(fd);
if (fallback_gotdata(buf, len) == 0) {
errno = save_errno;
return 0; /* satisfied */
}
nodevrandom:
errno = EIO;
return -1;
}
static inline void
_rs_init(u_char *buf, size_t n)
{
assert(buf);
if (n < KEYSZ + IVSZ)
return;
if (rs == NULL) {
#ifndef UB_ON_WINDOWS
if ((rs = mmap(NULL, sizeof(*rs), PROT_READ|PROT_WRITE,
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
abort();
#ifdef MAP_INHERIT_ZERO
if (minherit(rs, sizeof(*rs), MAP_INHERIT_ZERO) == -1)
abort();
#endif
#else /* WINDOWS */
rs = malloc(sizeof(*rs));
if(!rs)
abort();
#endif
}
if (rsx == NULL) {
#ifndef UB_ON_WINDOWS
if ((rsx = mmap(NULL, sizeof(*rsx), PROT_READ|PROT_WRITE,
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
abort();
#else /* WINDOWS */
rsx = malloc(sizeof(*rsx));
if(!rsx)
abort();
#endif
}
chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
}
static void
_rs_stir(void)
{
u_char rnd[KEYSZ + IVSZ];
uint32_t rekey_fuzz = 0;
if (getentropy(rnd, sizeof rnd) == -1) {
if(errno != ENOSYS ||
fallback_getentropy_urandom(rnd, sizeof rnd) == -1) {
#ifdef SIGKILL
raise(SIGKILL);
#else
exit(9); /* windows */
#endif
}
}
if (!rs)
_rs_init(rnd, sizeof(rnd));
else
_rs_rekey(rnd, sizeof(rnd));
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
/* invalidate rs_buf */
rs->rs_have = 0;
memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
/* rekey interval should not be predictable */
chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
(uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
}
static inline void
_rs_stir_if_needed(size_t len)
{
#ifndef MAP_INHERIT_ZERO
static pid_t _rs_pid = 0;
pid_t pid = getpid();
/* If a system lacks MAP_INHERIT_ZERO, resort to getpid() */
if (_rs_pid == 0 || _rs_pid != pid) {
_rs_pid = pid;
if (rs)
rs->rs_count = 0;
}
#endif
if (!rs || rs->rs_count <= len)
_rs_stir();
if (rs->rs_count <= len)
rs->rs_count = 0;
else
rs->rs_count -= len;
}
static inline void
_rs_rekey(u_char *dat, size_t datlen)
{
#ifndef KEYSTREAM_ONLY
memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
#endif
/* fill rs_buf with the keystream */
chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
rsx->rs_buf, sizeof(rsx->rs_buf));
/* mix in optional user provided data */
if (dat) {
size_t i, m;
m = arc4_min(datlen, KEYSZ + IVSZ);
for (i = 0; i < m; i++)
rsx->rs_buf[i] ^= dat[i];
}
/* immediately reinit for backtracking resistance */
_rs_init(rsx->rs_buf, KEYSZ + IVSZ);
memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
}
static inline void
_rs_random_buf(void *_buf, size_t n)
{
u_char *buf = (u_char *)_buf;
u_char *keystream;
size_t m;
_rs_stir_if_needed(n);
while (n > 0) {
if (rs->rs_have > 0) {
m = arc4_min(n, rs->rs_have);
keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
- rs->rs_have;
memcpy(buf, keystream, m);
memset(keystream, 0, m);
buf += m;
n -= m;
rs->rs_have -= m;
}
if (rs->rs_have == 0)
_rs_rekey(NULL, 0);
}
}
static inline void
_rs_random_u32(uint32_t *val)
{
u_char *keystream;
_rs_stir_if_needed(sizeof(*val));
if (rs->rs_have < sizeof(*val))
_rs_rekey(NULL, 0);
keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
memcpy(val, keystream, sizeof(*val));
memset(keystream, 0, sizeof(*val));
rs->rs_have -= sizeof(*val);
}
uint32_t
arc4random(void)
{
uint32_t val;
_ARC4_LOCK();
_rs_random_u32(&val);
_ARC4_UNLOCK();
return val;
}
void
arc4random_buf(void *buf, size_t n)
{
_ARC4_LOCK();
_rs_random_buf(buf, n);
_ARC4_UNLOCK();
}
@@ -1,57 +0,0 @@
/* $OpenBSD: arc4random_uniform.c,v 1.1 2014/07/12 13:24:54 deraadt Exp $ */
/*
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/types.h>
#include <stdlib.h>
/*
* Calculate a uniformly distributed random number less than upper_bound
* avoiding "modulo bias".
*
* Uniformity is achieved by generating new random numbers until the one
* returned is outside the range [0, 2**32 % upper_bound). This
* guarantees the selected random number will be inside
* [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
* after reduction modulo upper_bound.
*/
uint32_t
arc4random_uniform(uint32_t upper_bound)
{
uint32_t r, min;
if (upper_bound < 2)
return 0;
/* 2**32 % x == (2**32 - x) % x */
min = -upper_bound % upper_bound;
/*
* This could theoretically loop forever but each retry has
* p > 0.5 (worst case, usually far better) of selecting a
* number inside the range we need, so it should rarely need
* to re-roll.
*/
for (;;) {
r = arc4random();
if (r >= min)
break;
}
return r % upper_bound;
}
@@ -1,222 +0,0 @@
/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */
typedef unsigned char u8;
typedef unsigned int u32;
typedef struct
{
u32 input[16]; /* could be compressed */
} chacha_ctx;
#define U8C(v) (v##U)
#define U32C(v) (v##U)
#define U8V(v) ((u8)(v) & U8C(0xFF))
#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
#define ROTL32(v, n) \
(U32V((v) << (n)) | ((v) >> (32 - (n))))
#define U8TO32_LITTLE(p) \
(((u32)((p)[0]) ) | \
((u32)((p)[1]) << 8) | \
((u32)((p)[2]) << 16) | \
((u32)((p)[3]) << 24))
#define U32TO8_LITTLE(p, v) \
do { \
(p)[0] = U8V((v) ); \
(p)[1] = U8V((v) >> 8); \
(p)[2] = U8V((v) >> 16); \
(p)[3] = U8V((v) >> 24); \
} while (0)
#define ROTATE(v,c) (ROTL32(v,c))
#define XOR(v,w) ((v) ^ (w))
#define PLUS(v,w) (U32V((v) + (w)))
#define PLUSONE(v) (PLUS((v),1))
#define QUARTERROUND(a,b,c,d) \
a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
static const char sigma[16] = "expand 32-byte k";
static const char tau[16] = "expand 16-byte k";
static void
chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ATTR_UNUSED(ivbits))
{
const char *constants;
x->input[4] = U8TO32_LITTLE(k + 0);
x->input[5] = U8TO32_LITTLE(k + 4);
x->input[6] = U8TO32_LITTLE(k + 8);
x->input[7] = U8TO32_LITTLE(k + 12);
if (kbits == 256) { /* recommended */
k += 16;
constants = sigma;
} else { /* kbits == 128 */
constants = tau;
}
x->input[8] = U8TO32_LITTLE(k + 0);
x->input[9] = U8TO32_LITTLE(k + 4);
x->input[10] = U8TO32_LITTLE(k + 8);
x->input[11] = U8TO32_LITTLE(k + 12);
x->input[0] = U8TO32_LITTLE(constants + 0);
x->input[1] = U8TO32_LITTLE(constants + 4);
x->input[2] = U8TO32_LITTLE(constants + 8);
x->input[3] = U8TO32_LITTLE(constants + 12);
}
static void
chacha_ivsetup(chacha_ctx *x,const u8 *iv)
{
x->input[12] = 0;
x->input[13] = 0;
x->input[14] = U8TO32_LITTLE(iv + 0);
x->input[15] = U8TO32_LITTLE(iv + 4);
}
static void
chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)
{
u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
u8 *ctarget = NULL;
u8 tmp[64];
u_int i;
if (!bytes) return;
j0 = x->input[0];
j1 = x->input[1];
j2 = x->input[2];
j3 = x->input[3];
j4 = x->input[4];
j5 = x->input[5];
j6 = x->input[6];
j7 = x->input[7];
j8 = x->input[8];
j9 = x->input[9];
j10 = x->input[10];
j11 = x->input[11];
j12 = x->input[12];
j13 = x->input[13];
j14 = x->input[14];
j15 = x->input[15];
for (;;) {
if (bytes < 64) {
for (i = 0;i < bytes;++i) tmp[i] = m[i];
m = tmp;
ctarget = c;
c = tmp;
}
x0 = j0;
x1 = j1;
x2 = j2;
x3 = j3;
x4 = j4;
x5 = j5;
x6 = j6;
x7 = j7;
x8 = j8;
x9 = j9;
x10 = j10;
x11 = j11;
x12 = j12;
x13 = j13;
x14 = j14;
x15 = j15;
for (i = 20;i > 0;i -= 2) {
QUARTERROUND( x0, x4, x8,x12)
QUARTERROUND( x1, x5, x9,x13)
QUARTERROUND( x2, x6,x10,x14)
QUARTERROUND( x3, x7,x11,x15)
QUARTERROUND( x0, x5,x10,x15)
QUARTERROUND( x1, x6,x11,x12)
QUARTERROUND( x2, x7, x8,x13)
QUARTERROUND( x3, x4, x9,x14)
}
x0 = PLUS(x0,j0);
x1 = PLUS(x1,j1);
x2 = PLUS(x2,j2);
x3 = PLUS(x3,j3);
x4 = PLUS(x4,j4);
x5 = PLUS(x5,j5);
x6 = PLUS(x6,j6);
x7 = PLUS(x7,j7);
x8 = PLUS(x8,j8);
x9 = PLUS(x9,j9);
x10 = PLUS(x10,j10);
x11 = PLUS(x11,j11);
x12 = PLUS(x12,j12);
x13 = PLUS(x13,j13);
x14 = PLUS(x14,j14);
x15 = PLUS(x15,j15);
#ifndef KEYSTREAM_ONLY
x0 = XOR(x0,U8TO32_LITTLE(m + 0));
x1 = XOR(x1,U8TO32_LITTLE(m + 4));
x2 = XOR(x2,U8TO32_LITTLE(m + 8));
x3 = XOR(x3,U8TO32_LITTLE(m + 12));
x4 = XOR(x4,U8TO32_LITTLE(m + 16));
x5 = XOR(x5,U8TO32_LITTLE(m + 20));
x6 = XOR(x6,U8TO32_LITTLE(m + 24));
x7 = XOR(x7,U8TO32_LITTLE(m + 28));
x8 = XOR(x8,U8TO32_LITTLE(m + 32));
x9 = XOR(x9,U8TO32_LITTLE(m + 36));
x10 = XOR(x10,U8TO32_LITTLE(m + 40));
x11 = XOR(x11,U8TO32_LITTLE(m + 44));
x12 = XOR(x12,U8TO32_LITTLE(m + 48));
x13 = XOR(x13,U8TO32_LITTLE(m + 52));
x14 = XOR(x14,U8TO32_LITTLE(m + 56));
x15 = XOR(x15,U8TO32_LITTLE(m + 60));
#endif
j12 = PLUSONE(j12);
if (!j12) {
j13 = PLUSONE(j13);
/* stopping at 2^70 bytes per nonce is user's responsibility */
}
U32TO8_LITTLE(c + 0,x0);
U32TO8_LITTLE(c + 4,x1);
U32TO8_LITTLE(c + 8,x2);
U32TO8_LITTLE(c + 12,x3);
U32TO8_LITTLE(c + 16,x4);
U32TO8_LITTLE(c + 20,x5);
U32TO8_LITTLE(c + 24,x6);
U32TO8_LITTLE(c + 28,x7);
U32TO8_LITTLE(c + 32,x8);
U32TO8_LITTLE(c + 36,x9);
U32TO8_LITTLE(c + 40,x10);
U32TO8_LITTLE(c + 44,x11);
U32TO8_LITTLE(c + 48,x12);
U32TO8_LITTLE(c + 52,x13);
U32TO8_LITTLE(c + 56,x14);
U32TO8_LITTLE(c + 60,x15);
if (bytes <= 64) {
if (bytes < 64) {
for (i = 0;i < bytes;++i) ctarget[i] = c[i];
}
x->input[12] = j12;
x->input[13] = j13;
return;
}
bytes -= 64;
c += 64;
#ifndef KEYSTREAM_ONLY
m += 64;
#endif
}
}
@@ -1,42 +0,0 @@
/* taken from ldns 1.6.1 */
#include "config.h"
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include "util/locks.h"
/** the lock for ctime buffer */
static lock_basic_type ctime_lock;
/** has it been inited */
static int ctime_r_init = 0;
/** cleanup ctime_r on exit */
static void
ctime_r_cleanup(void)
{
if(ctime_r_init) {
ctime_r_init = 0;
lock_basic_destroy(&ctime_lock);
}
}
char *ctime_r(const time_t *timep, char *buf)
{
char* result;
if(!ctime_r_init) {
/* still small race where this init can be done twice,
* which is mostly harmless */
ctime_r_init = 1;
lock_basic_init(&ctime_lock);
atexit(&ctime_r_cleanup);
}
lock_basic_lock(&ctime_lock);
result = ctime(timep);
if(buf && result) {
if(strlen(result) > 10 && result[7]==' ' && result[8]=='0')
result[8]=' '; /* fix error in windows ctime */
strcpy(buf, result);
}
lock_basic_unlock(&ctime_lock);
return buf;
}
@@ -1,26 +0,0 @@
/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */
/*
* Public domain.
* Written by Matthew Dempsky.
*/
#include "config.h"
#include <string.h>
#ifdef HAVE_ATTR_WEAK
__attribute__((weak)) void
#else
void
#endif
__explicit_bzero_hook(void *ATTR_UNUSED(buf), size_t ATTR_UNUSED(len))
{
}
void
explicit_bzero(void *buf, size_t len)
{
#ifdef UB_ON_WINDOWS
SecureZeroMemory(buf, len);
#endif
memset(buf, 0, len);
__explicit_bzero_hook(buf, len);
}
@@ -1,225 +0,0 @@
/* From openssh 4.3p2 filename openbsd-compat/fake-rfc2553.h */
/*
* Copyright (C) 2000-2003 Damien Miller. All rights reserved.
* Copyright (C) 1999 WIDE Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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.
*/
/*
* Pseudo-implementation of RFC2553 name / address resolution functions
*
* But these functions are not implemented correctly. The minimum subset
* is implemented for ssh use only. For example, this routine assumes
* that ai_family is AF_INET. Don't use it for another purpose.
*/
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "compat/fake-rfc2553.h"
#ifndef HAVE_GETNAMEINFO
int getnameinfo(const struct sockaddr *sa, size_t ATTR_UNUSED(salen), char *host,
size_t hostlen, char *serv, size_t servlen, int flags)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
struct hostent *hp;
char tmpserv[16];
if (serv != NULL) {
snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
if (strlcpy(serv, tmpserv, servlen) >= servlen)
return (EAI_MEMORY);
}
if (host != NULL) {
if (flags & NI_NUMERICHOST) {
if (strlcpy(host, inet_ntoa(sin->sin_addr),
hostlen) >= hostlen)
return (EAI_MEMORY);
else
return (0);
} else {
hp = gethostbyaddr((char *)&sin->sin_addr,
sizeof(struct in_addr), AF_INET);
if (hp == NULL)
return (EAI_NODATA);
if (strlcpy(host, hp->h_name, hostlen) >= hostlen)
return (EAI_MEMORY);
else
return (0);
}
}
return (0);
}
#endif /* !HAVE_GETNAMEINFO */
#ifndef HAVE_GAI_STRERROR
#ifdef HAVE_CONST_GAI_STRERROR_PROTO
const char *
#else
char *
#endif
gai_strerror(int err)
{
switch (err) {
case EAI_NODATA:
return ("no address associated with name");
case EAI_MEMORY:
return ("memory allocation failure.");
case EAI_NONAME:
return ("nodename nor servname provided, or not known");
default:
return ("unknown/invalid error.");
}
}
#endif /* !HAVE_GAI_STRERROR */
#ifndef HAVE_FREEADDRINFO
void
freeaddrinfo(struct addrinfo *ai)
{
struct addrinfo *next;
for(; ai != NULL;) {
next = ai->ai_next;
free(ai);
ai = next;
}
}
#endif /* !HAVE_FREEADDRINFO */
#ifndef HAVE_GETADDRINFO
static struct
addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints)
{
struct addrinfo *ai;
ai = calloc(1, sizeof(*ai) + sizeof(struct sockaddr_in));
if (ai == NULL)
return (NULL);
ai->ai_addr = (struct sockaddr *)(ai + 1);
/* XXX -- ssh doesn't use sa_len */
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_addr->sa_family = ai->ai_family = AF_INET;
((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
/* XXX: the following is not generally correct, but does what we want */
if (hints->ai_socktype)
ai->ai_socktype = hints->ai_socktype;
else
ai->ai_socktype = SOCK_STREAM;
if (hints->ai_protocol)
ai->ai_protocol = hints->ai_protocol;
return (ai);
}
int
getaddrinfo(const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res)
{
struct hostent *hp;
struct servent *sp;
struct in_addr in;
int i;
long int port;
u_long addr;
port = 0;
if (servname != NULL) {
char *cp;
port = strtol(servname, &cp, 10);
if (port > 0 && port <= 65535 && *cp == '\0')
port = htons(port);
else if ((sp = getservbyname(servname, NULL)) != NULL)
port = sp->s_port;
else
port = 0;
}
if (hints && hints->ai_flags & AI_PASSIVE) {
addr = htonl(0x00000000);
if (hostname && inet_aton(hostname, &in) != 0)
addr = in.s_addr;
*res = malloc_ai(port, addr, hints);
if (*res == NULL)
return (EAI_MEMORY);
return (0);
}
if (!hostname) {
*res = malloc_ai(port, htonl(0x7f000001), hints);
if (*res == NULL)
return (EAI_MEMORY);
return (0);
}
if (inet_aton(hostname, &in)) {
*res = malloc_ai(port, in.s_addr, hints);
if (*res == NULL)
return (EAI_MEMORY);
return (0);
}
/* Don't try DNS if AI_NUMERICHOST is set */
if (hints && hints->ai_flags & AI_NUMERICHOST)
return (EAI_NONAME);
hp = gethostbyname(hostname);
if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
struct addrinfo *cur, *prev;
cur = prev = *res = NULL;
for (i = 0; hp->h_addr_list[i]; i++) {
struct in_addr *in = (struct in_addr *)hp->h_addr_list[i];
cur = malloc_ai(port, in->s_addr, hints);
if (cur == NULL) {
if (*res != NULL)
freeaddrinfo(*res);
return (EAI_MEMORY);
}
if (prev)
prev->ai_next = cur;
else
*res = cur;
prev = cur;
}
return (0);
}
return (EAI_NODATA);
}
#endif /* !HAVE_GETADDRINFO */
@@ -1,174 +0,0 @@
/* From openssh 4.3p2 filename openbsd-compat/fake-rfc2553.h */
/*
* Copyright (C) 2000-2003 Damien Miller. All rights reserved.
* Copyright (C) 1999 WIDE Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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.
*/
/*
* Pseudo-implementation of RFC2553 name / address resolution functions
*
* But these functions are not implemented correctly. The minimum subset
* is implemented for ssh use only. For example, this routine assumes
* that ai_family is AF_INET. Don't use it for another purpose.
*/
#ifndef _FAKE_RFC2553_H
#define _FAKE_RFC2553_H
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <limits.h>
/*
* First, socket and INET6 related definitions
*/
#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
# define _SS_MAXSIZE 128 /* Implementation specific max size */
# define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr))
struct sockaddr_storage {
struct sockaddr ss_sa;
char __ss_pad2[_SS_PADSIZE];
};
# define ss_family ss_sa.sa_family
#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
#ifndef IN6_IS_ADDR_LOOPBACK
# define IN6_IS_ADDR_LOOPBACK(a) \
(((uint32_t *)(a))[0] == 0 && ((uint32_t *)(a))[1] == 0 && \
((uint32_t *)(a))[2] == 0 && ((uint32_t *)(a))[3] == htonl(1))
#endif /* !IN6_IS_ADDR_LOOPBACK */
#ifndef HAVE_STRUCT_IN6_ADDR
struct in6_addr {
uint8_t s6_addr[16];
};
#endif /* !HAVE_STRUCT_IN6_ADDR */
#ifndef HAVE_STRUCT_SOCKADDR_IN6
struct sockaddr_in6 {
unsigned short sin6_family;
uint16_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
};
#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */
#ifndef AF_INET6
/* Define it to something that should never appear */
#define AF_INET6 AF_MAX
#endif
/*
* Next, RFC2553 name / address resolution API
*/
#ifndef NI_NUMERICHOST
# define NI_NUMERICHOST (1)
#endif
#ifndef NI_NAMEREQD
# define NI_NAMEREQD (1<<1)
#endif
#ifndef NI_NUMERICSERV
# define NI_NUMERICSERV (1<<2)
#endif
#ifndef AI_PASSIVE
# define AI_PASSIVE (1)
#endif
#ifndef AI_CANONNAME
# define AI_CANONNAME (1<<1)
#endif
#ifndef AI_NUMERICHOST
# define AI_NUMERICHOST (1<<2)
#endif
#ifndef NI_MAXSERV
# define NI_MAXSERV 32
#endif /* !NI_MAXSERV */
#ifndef NI_MAXHOST
# define NI_MAXHOST 1025
#endif /* !NI_MAXHOST */
#ifndef INT_MAX
#define INT_MAX 0xffffffff
#endif
#ifndef EAI_NODATA
# define EAI_NODATA (INT_MAX - 1)
#endif
#ifndef EAI_MEMORY
# define EAI_MEMORY (INT_MAX - 2)
#endif
#ifndef EAI_NONAME
# define EAI_NONAME (INT_MAX - 3)
#endif
#ifndef EAI_SYSTEM
# define EAI_SYSTEM (INT_MAX - 4)
#endif
#ifndef HAVE_STRUCT_ADDRINFO
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
#endif /* !HAVE_STRUCT_ADDRINFO */
#ifndef HAVE_GETADDRINFO
#ifdef getaddrinfo
# undef getaddrinfo
#endif
#define getaddrinfo(a,b,c,d) (getaddrinfo_unbound(a,b,c,d))
int getaddrinfo(const char *, const char *,
const struct addrinfo *, struct addrinfo **);
#endif /* !HAVE_GETADDRINFO */
#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO)
#define gai_strerror(a) (gai_strerror_unbound(a))
char *gai_strerror(int);
#endif /* !HAVE_GAI_STRERROR */
#ifndef HAVE_FREEADDRINFO
#define freeaddrinfo(a) (freeaddrinfo_unbound(a))
void freeaddrinfo(struct addrinfo *);
#endif /* !HAVE_FREEADDRINFO */
#ifndef HAVE_GETNAMEINFO
#define getnameinfo(a,b,c,d,e,f,g) (getnameinfo_unbound(a,b,c,d,e,f,g))
int getnameinfo(const struct sockaddr *, size_t, char *, size_t,
char *, size_t, int);
#endif /* !HAVE_GETNAMEINFO */
#endif /* !_FAKE_RFC2553_H */
@@ -1,62 +0,0 @@
/* $OpenBSD: getentropy_freebsd.c,v 1.3 2016/08/07 03:27:21 tb Exp $ */
/*
* Copyright (c) 2014 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* Copyright (c) 2014 Brent Cook <bcook@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include <sys/types.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stddef.h>
/*
* Derived from lib/libc/gen/arc4random.c from FreeBSD.
*/
static size_t
getentropy_sysctl(u_char *buf, size_t size)
{
int mib[2];
size_t len, done;
mib[0] = CTL_KERN;
mib[1] = KERN_ARND;
done = 0;
do {
len = size;
if (sysctl(mib, 2, buf, &len, NULL, 0) == -1)
return (done);
done += len;
buf += len;
size -= len;
} while (size > 0);
return (done);
}
int
getentropy(void *buf, size_t len)
{
if (len <= 256 && getentropy_sysctl(buf, len) == len)
return (0);
errno = EIO;
return (-1);
}
@@ -1,539 +0,0 @@
/* $OpenBSD: getentropy_linux.c,v 1.46 2018/11/20 08:04:28 deraadt Exp $ */
/*
* Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
* Copyright (c) 2014 Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include "config.h"
/*
#define _POSIX_C_SOURCE 199309L
#define _GNU_SOURCE 1
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#ifdef SYS__sysctl
#include <linux/sysctl.h>
#endif
#include <sys/statvfs.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <link.h>
#include <termios.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#ifndef HAVE_NETTLE
#include <openssl/sha.h>
#else
#include <nettle/sha.h>
#define SHA512_CTX struct sha512_ctx
#define SHA512_Init(x) sha512_init(x)
#define SHA512_Update(x, b, s) sha512_update(x, s, b)
#define SHA512_Final(r, c) sha512_digest(c, SHA512_DIGEST_SIZE, r)
#endif
#include <linux/types.h>
#include <linux/random.h>
#ifdef HAVE_GETAUXVAL
#include <sys/auxv.h>
#endif
#include <sys/vfs.h>
#ifndef MAP_ANON
#define MAP_ANON MAP_ANONYMOUS
#endif
#define REPEAT 5
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define HX(a, b) \
do { \
if ((a)) \
HD(errno); \
else \
HD(b); \
} while (0)
#define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l)))
#define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x)))
#define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*)))
int getentropy(void *buf, size_t len);
#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
static int getentropy_getrandom(void *buf, size_t len);
#endif
static int getentropy_urandom(void *buf, size_t len);
#ifdef SYS__sysctl
static int getentropy_sysctl(void *buf, size_t len);
#endif
static int getentropy_fallback(void *buf, size_t len);
static int getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data);
int
getentropy(void *buf, size_t len)
{
int ret = -1;
if (len > 256) {
errno = EIO;
return (-1);
}
#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
/*
* Try descriptor-less getrandom(), in non-blocking mode.
*
* The design of Linux getrandom is broken. It has an
* uninitialized phase coupled with blocking behaviour, which
* is unacceptable from within a library at boot time without
* possible recovery. See http://bugs.python.org/issue26839#msg267745
*/
ret = getentropy_getrandom(buf, len);
if (ret != -1)
return (ret);
#endif
/*
* Try to get entropy with /dev/urandom
*
* This can fail if the process is inside a chroot or if file
* descriptors are exhausted.
*/
ret = getentropy_urandom(buf, len);
if (ret != -1)
return (ret);
#ifdef SYS__sysctl
/*
* Try to use sysctl CTL_KERN, KERN_RANDOM, RANDOM_UUID.
* sysctl is a failsafe API, so it guarantees a result. This
* should work inside a chroot, or when file descriptors are
* exhausted.
*
* However this can fail if the Linux kernel removes support
* for sysctl. Starting in 2007, there have been efforts to
* deprecate the sysctl API/ABI, and push callers towards use
* of the chroot-unavailable fd-using /proc mechanism --
* essentially the same problems as /dev/urandom.
*
* Numerous setbacks have been encountered in their deprecation
* schedule, so as of June 2014 the kernel ABI still exists on
* most Linux architectures. The sysctl() stub in libc is missing
* on some systems. There are also reports that some kernels
* spew messages to the console.
*/
ret = getentropy_sysctl(buf, len);
if (ret != -1)
return (ret);
#endif /* SYS__sysctl */
/*
* Entropy collection via /dev/urandom and sysctl have failed.
*
* No other API exists for collecting entropy. See the large
* comment block above.
*
* We have very few options:
* - Even syslog_r is unsafe to call at this low level, so
* there is no way to alert the user or program.
* - Cannot call abort() because some systems have unsafe
* corefiles.
* - Could raise(SIGKILL) resulting in silent program termination.
* - Return EIO, to hint that arc4random's stir function
* should raise(SIGKILL)
* - Do the best under the circumstances....
*
* This code path exists to bring light to the issue that Linux
* still does not provide a failsafe API for entropy collection.
*
* We hope this demonstrates that Linux should either retain their
* sysctl ABI, or consider providing a new failsafe API which
* works in a chroot or when file descriptors are exhausted.
*/
#undef FAIL_INSTEAD_OF_TRYING_FALLBACK
#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK
raise(SIGKILL);
#endif
ret = getentropy_fallback(buf, len);
if (ret != -1)
return (ret);
errno = EIO;
return (ret);
}
#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
static int
getentropy_getrandom(void *buf, size_t len)
{
int pre_errno = errno;
int ret;
if (len > 256)
return (-1);
do {
ret = syscall(SYS_getrandom, buf, len, GRND_NONBLOCK);
} while (ret == -1 && errno == EINTR);
if (ret != (int)len)
return (-1);
errno = pre_errno;
return (0);
}
#endif
static int
getentropy_urandom(void *buf, size_t len)
{
struct stat st;
size_t i;
int fd, cnt, flags;
int save_errno = errno;
start:
flags = O_RDONLY;
#ifdef O_NOFOLLOW
flags |= O_NOFOLLOW;
#endif
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif
fd = open("/dev/urandom", flags, 0);
if (fd == -1) {
if (errno == EINTR)
goto start;
goto nodevrandom;
}
#ifndef O_CLOEXEC
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
#endif
/* Lightly verify that the device node looks sane */
if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) {
close(fd);
goto nodevrandom;
}
if (ioctl(fd, RNDGETENTCNT, &cnt) == -1) {
close(fd);
goto nodevrandom;
}
for (i = 0; i < len; ) {
size_t wanted = len - i;
ssize_t ret = read(fd, (char *)buf + i, wanted);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
close(fd);
goto nodevrandom;
}
i += ret;
}
close(fd);
errno = save_errno;
return (0); /* satisfied */
nodevrandom:
errno = EIO;
return (-1);
}
#ifdef SYS__sysctl
static int
getentropy_sysctl(void *buf, size_t len)
{
static int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID };
size_t i;
int save_errno = errno;
for (i = 0; i < len; ) {
size_t chunk = min(len - i, 16);
/* SYS__sysctl because some systems already removed sysctl() */
struct __sysctl_args args = {
.name = mib,
.nlen = 3,
.oldval = (char *)buf + i,
.oldlenp = &chunk,
};
if (syscall(SYS__sysctl, &args) != 0)
goto sysctlfailed;
i += chunk;
}
errno = save_errno;
return (0); /* satisfied */
sysctlfailed:
errno = EIO;
return (-1);
}
#endif /* SYS__sysctl */
static const int cl[] = {
CLOCK_REALTIME,
#ifdef CLOCK_MONOTONIC
CLOCK_MONOTONIC,
#endif
#ifdef CLOCK_MONOTONIC_RAW
CLOCK_MONOTONIC_RAW,
#endif
#ifdef CLOCK_TAI
CLOCK_TAI,
#endif
#ifdef CLOCK_VIRTUAL
CLOCK_VIRTUAL,
#endif
#ifdef CLOCK_UPTIME
CLOCK_UPTIME,
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
CLOCK_PROCESS_CPUTIME_ID,
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
CLOCK_THREAD_CPUTIME_ID,
#endif
};
static int
getentropy_phdr(struct dl_phdr_info *info, size_t ATTR_UNUSED(size), void *data)
{
SHA512_CTX *ctx = data;
SHA512_Update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr));
return (0);
}
static int
getentropy_fallback(void *buf, size_t len)
{
uint8_t results[SHA512_DIGEST_LENGTH];
int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat;
static int cnt;
struct timespec ts;
struct timeval tv;
struct rusage ru;
sigset_t sigset;
struct stat st;
SHA512_CTX ctx;
static pid_t lastpid;
pid_t pid;
size_t i, ii, m;
char *p;
pid = getpid();
if (lastpid == pid) {
faster = 1;
repeat = 2;
} else {
faster = 0;
lastpid = pid;
repeat = REPEAT;
}
for (i = 0; i < len; ) {
int j;
SHA512_Init(&ctx);
for (j = 0; j < repeat; j++) {
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
if (e != -1) {
cnt += (int)tv.tv_sec;
cnt += (int)tv.tv_usec;
}
dl_iterate_phdr(getentropy_phdr, &ctx);
for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++)
HX(clock_gettime(cl[ii], &ts) == -1, ts);
HX((pid = getpid()) == -1, pid);
HX((pid = getsid(pid)) == -1, pid);
HX((pid = getppid()) == -1, pid);
HX((pid = getpgid(0)) == -1, pid);
HX((e = getpriority(0, 0)) == -1, e);
if (!faster) {
ts.tv_sec = 0;
ts.tv_nsec = 1;
(void) nanosleep(&ts, NULL);
}
HX(sigpending(&sigset) == -1, sigset);
HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
sigset);
HF(getentropy); /* an addr in this library */
HF(printf); /* an addr in libc */
p = (char *)&p;
HD(p); /* an addr on stack */
p = (char *)&errno;
HD(p); /* the addr of errno */
if (i == 0) {
struct sockaddr_storage ss;
struct statvfs stvfs;
struct termios tios;
struct statfs stfs;
socklen_t ssl;
off_t off;
/*
* Prime-sized mappings encourage fragmentation;
* thus exposing some address entropy.
*/
struct mm {
size_t npg;
void *p;
} mm[] = {
{ 17, MAP_FAILED }, { 3, MAP_FAILED },
{ 11, MAP_FAILED }, { 2, MAP_FAILED },
{ 5, MAP_FAILED }, { 3, MAP_FAILED },
{ 7, MAP_FAILED }, { 1, MAP_FAILED },
{ 57, MAP_FAILED }, { 3, MAP_FAILED },
{ 131, MAP_FAILED }, { 1, MAP_FAILED },
};
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
HX(mm[m].p = mmap(NULL,
mm[m].npg * pgs,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANON, -1,
(off_t)0), mm[m].p);
if (mm[m].p != MAP_FAILED) {
size_t mo;
/* Touch some memory... */
p = mm[m].p;
mo = cnt %
(mm[m].npg * pgs - 1);
p[mo] = 1;
cnt += (int)((long)(mm[m].p)
/ pgs);
}
/* Check cnts and times... */
for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]);
ii++) {
HX((e = clock_gettime(cl[ii],
&ts)) == -1, ts);
if (e != -1)
cnt += (int)ts.tv_nsec;
}
HX((e = getrusage(RUSAGE_SELF,
&ru)) == -1, ru);
if (e != -1) {
cnt += (int)ru.ru_utime.tv_sec;
cnt += (int)ru.ru_utime.tv_usec;
}
}
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
if (mm[m].p != MAP_FAILED)
munmap(mm[m].p, mm[m].npg * pgs);
mm[m].p = MAP_FAILED;
}
HX(stat(".", &st) == -1, st);
HX(statvfs(".", &stvfs) == -1, stvfs);
HX(statfs(".", &stfs) == -1, stfs);
HX(stat("/", &st) == -1, st);
HX(statvfs("/", &stvfs) == -1, stvfs);
HX(statfs("/", &stfs) == -1, stfs);
HX((e = fstat(0, &st)) == -1, st);
if (e == -1) {
if (S_ISREG(st.st_mode) ||
S_ISFIFO(st.st_mode) ||
S_ISSOCK(st.st_mode)) {
HX(fstatvfs(0, &stvfs) == -1,
stvfs);
HX(fstatfs(0, &stfs) == -1,
stfs);
HX((off = lseek(0, (off_t)0,
SEEK_CUR)) < 0, off);
}
if (S_ISCHR(st.st_mode)) {
HX(tcgetattr(0, &tios) == -1,
tios);
} else if (S_ISSOCK(st.st_mode)) {
memset(&ss, 0, sizeof ss);
ssl = sizeof(ss);
HX(getpeername(0,
(void *)&ss, &ssl) == -1,
ss);
}
}
HX((e = getrusage(RUSAGE_CHILDREN,
&ru)) == -1, ru);
if (e != -1) {
cnt += (int)ru.ru_utime.tv_sec;
cnt += (int)ru.ru_utime.tv_usec;
}
} else {
/* Subsequent hashes absorb previous result */
HD(results);
}
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
if (e != -1) {
cnt += (int)tv.tv_sec;
cnt += (int)tv.tv_usec;
}
HD(cnt);
}
#ifdef HAVE_GETAUXVAL
#ifdef AT_RANDOM
/* Not as random as you think but we take what we are given */
p = (char *) getauxval(AT_RANDOM);
if (p)
HR(p, 16);
#endif
#ifdef AT_SYSINFO_EHDR
p = (char *) getauxval(AT_SYSINFO_EHDR);
if (p)
HR(p, pgs);
#endif
#ifdef AT_BASE
p = (char *) getauxval(AT_BASE);
if (p)
HD(p);
#endif
#endif
SHA512_Final(results, &ctx);
memcpy((char *)buf + i, results, min(sizeof(results), len - i));
i += min(sizeof(results), len - i);
}
explicit_bzero(&ctx, sizeof ctx);
explicit_bzero(results, sizeof results);
errno = save_errno;
return (0); /* satisfied */
}
@@ -1,417 +0,0 @@
/* $OpenBSD: getentropy_osx.c,v 1.12 2018/11/20 08:04:28 deraadt Exp $ */
/*
* Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
* Copyright (c) 2014 Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include <TargetConditionals.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
#include <sys/statvfs.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <mach/mach_time.h>
#include <mach/mach_host.h>
#include <mach/host_info.h>
#if TARGET_OS_OSX
#include <sys/socketvar.h>
#include <sys/vmmeter.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#if TARGET_OS_OSX
#include <netinet/udp.h>
#include <netinet/ip_var.h>
#include <netinet/tcp_var.h>
#include <netinet/udp_var.h>
#endif
#include <CommonCrypto/CommonDigest.h>
#define SHA512_Update(a, b, c) (CC_SHA512_Update((a), (b), (c)))
#define SHA512_Init(xxx) (CC_SHA512_Init((xxx)))
#define SHA512_Final(xxx, yyy) (CC_SHA512_Final((xxx), (yyy)))
#define SHA512_CTX CC_SHA512_CTX
#define SHA512_DIGEST_LENGTH CC_SHA512_DIGEST_LENGTH
#define REPEAT 5
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define HX(a, b) \
do { \
if ((a)) \
HD(errno); \
else \
HD(b); \
} while (0)
#define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l)))
#define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x)))
#define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*)))
int getentropy(void *buf, size_t len);
static int getentropy_urandom(void *buf, size_t len);
static int getentropy_fallback(void *buf, size_t len);
int
getentropy(void *buf, size_t len)
{
int ret = -1;
if (len > 256) {
errno = EIO;
return (-1);
}
/*
* Try to get entropy with /dev/urandom
*
* This can fail if the process is inside a chroot or if file
* descriptors are exhausted.
*/
ret = getentropy_urandom(buf, len);
if (ret != -1)
return (ret);
/*
* Entropy collection via /dev/urandom and sysctl have failed.
*
* No other API exists for collecting entropy, and we have
* no failsafe way to get it on OSX that is not sensitive
* to resource exhaustion.
*
* We have very few options:
* - Even syslog_r is unsafe to call at this low level, so
* there is no way to alert the user or program.
* - Cannot call abort() because some systems have unsafe
* corefiles.
* - Could raise(SIGKILL) resulting in silent program termination.
* - Return EIO, to hint that arc4random's stir function
* should raise(SIGKILL)
* - Do the best under the circumstances....
*
* This code path exists to bring light to the issue that OSX
* does not provide a failsafe API for entropy collection.
*
* We hope this demonstrates that OSX should consider
* providing a new failsafe API which works in a chroot or
* when file descriptors are exhausted.
*/
#undef FAIL_INSTEAD_OF_TRYING_FALLBACK
#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK
raise(SIGKILL);
#endif
ret = getentropy_fallback(buf, len);
if (ret != -1)
return (ret);
errno = EIO;
return (ret);
}
static int
getentropy_urandom(void *buf, size_t len)
{
struct stat st;
size_t i;
int fd, flags;
int save_errno = errno;
start:
flags = O_RDONLY;
#ifdef O_NOFOLLOW
flags |= O_NOFOLLOW;
#endif
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif
fd = open("/dev/urandom", flags, 0);
if (fd == -1) {
if (errno == EINTR)
goto start;
goto nodevrandom;
}
#ifndef O_CLOEXEC
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
#endif
/* Lightly verify that the device node looks sane */
if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) {
close(fd);
goto nodevrandom;
}
for (i = 0; i < len; ) {
size_t wanted = len - i;
ssize_t ret = read(fd, (char *)buf + i, wanted);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
close(fd);
goto nodevrandom;
}
i += ret;
}
close(fd);
errno = save_errno;
return (0); /* satisfied */
nodevrandom:
errno = EIO;
return (-1);
}
#if TARGET_OS_OSX
static int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS };
static int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS };
static int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS };
#endif
static int kmib[] = { CTL_KERN, KERN_USRSTACK };
static int hwmib[] = { CTL_HW, HW_USERMEM };
static int
getentropy_fallback(void *buf, size_t len)
{
uint8_t results[SHA512_DIGEST_LENGTH];
int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat;
static int cnt;
struct timespec ts;
struct timeval tv;
struct rusage ru;
sigset_t sigset;
struct stat st;
SHA512_CTX ctx;
static pid_t lastpid;
pid_t pid;
size_t i, ii, m;
char *p;
#if TARGET_OS_OSX
struct tcpstat tcpstat;
struct udpstat udpstat;
struct ipstat ipstat;
#endif
u_int64_t mach_time;
unsigned int idata;
void *addr;
pid = getpid();
if (lastpid == pid) {
faster = 1;
repeat = 2;
} else {
faster = 0;
lastpid = pid;
repeat = REPEAT;
}
for (i = 0; i < len; ) {
int j;
SHA512_Init(&ctx);
for (j = 0; j < repeat; j++) {
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
if (e != -1) {
cnt += (int)tv.tv_sec;
cnt += (int)tv.tv_usec;
}
mach_time = mach_absolute_time();
HD(mach_time);
ii = sizeof(addr);
HX(sysctl(kmib, sizeof(kmib) / sizeof(kmib[0]),
&addr, &ii, NULL, 0) == -1, addr);
ii = sizeof(idata);
HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]),
&idata, &ii, NULL, 0) == -1, idata);
#if TARGET_OS_OSX
ii = sizeof(tcpstat);
HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]),
&tcpstat, &ii, NULL, 0) == -1, tcpstat);
ii = sizeof(udpstat);
HX(sysctl(udpmib, sizeof(udpmib) / sizeof(udpmib[0]),
&udpstat, &ii, NULL, 0) == -1, udpstat);
ii = sizeof(ipstat);
HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]),
&ipstat, &ii, NULL, 0) == -1, ipstat);
#endif
HX((pid = getpid()) == -1, pid);
HX((pid = getsid(pid)) == -1, pid);
HX((pid = getppid()) == -1, pid);
HX((pid = getpgid(0)) == -1, pid);
HX((e = getpriority(0, 0)) == -1, e);
if (!faster) {
ts.tv_sec = 0;
ts.tv_nsec = 1;
(void) nanosleep(&ts, NULL);
}
HX(sigpending(&sigset) == -1, sigset);
HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
sigset);
HF(getentropy); /* an addr in this library */
HF(printf); /* an addr in libc */
p = (char *)&p;
HD(p); /* an addr on stack */
p = (char *)&errno;
HD(p); /* the addr of errno */
if (i == 0) {
struct sockaddr_storage ss;
struct statvfs stvfs;
struct termios tios;
struct statfs stfs;
socklen_t ssl;
off_t off;
/*
* Prime-sized mappings encourage fragmentation;
* thus exposing some address entropy.
*/
struct mm {
size_t npg;
void *p;
} mm[] = {
{ 17, MAP_FAILED }, { 3, MAP_FAILED },
{ 11, MAP_FAILED }, { 2, MAP_FAILED },
{ 5, MAP_FAILED }, { 3, MAP_FAILED },
{ 7, MAP_FAILED }, { 1, MAP_FAILED },
{ 57, MAP_FAILED }, { 3, MAP_FAILED },
{ 131, MAP_FAILED }, { 1, MAP_FAILED },
};
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
HX(mm[m].p = mmap(NULL,
mm[m].npg * pgs,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANON, -1,
(off_t)0), mm[m].p);
if (mm[m].p != MAP_FAILED) {
size_t mo;
/* Touch some memory... */
p = mm[m].p;
mo = cnt %
(mm[m].npg * pgs - 1);
p[mo] = 1;
cnt += (int)((long)(mm[m].p)
/ pgs);
}
/* Check cnts and times... */
mach_time = mach_absolute_time();
HD(mach_time);
cnt += (int)mach_time;
HX((e = getrusage(RUSAGE_SELF,
&ru)) == -1, ru);
if (e != -1) {
cnt += (int)ru.ru_utime.tv_sec;
cnt += (int)ru.ru_utime.tv_usec;
}
}
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
if (mm[m].p != MAP_FAILED)
munmap(mm[m].p, mm[m].npg * pgs);
mm[m].p = MAP_FAILED;
}
HX(stat(".", &st) == -1, st);
HX(statvfs(".", &stvfs) == -1, stvfs);
HX(statfs(".", &stfs) == -1, stfs);
HX(stat("/", &st) == -1, st);
HX(statvfs("/", &stvfs) == -1, stvfs);
HX(statfs("/", &stfs) == -1, stfs);
HX((e = fstat(0, &st)) == -1, st);
if (e == -1) {
if (S_ISREG(st.st_mode) ||
S_ISFIFO(st.st_mode) ||
S_ISSOCK(st.st_mode)) {
HX(fstatvfs(0, &stvfs) == -1,
stvfs);
HX(fstatfs(0, &stfs) == -1,
stfs);
HX((off = lseek(0, (off_t)0,
SEEK_CUR)) < 0, off);
}
if (S_ISCHR(st.st_mode)) {
HX(tcgetattr(0, &tios) == -1,
tios);
} else if (S_ISSOCK(st.st_mode)) {
memset(&ss, 0, sizeof ss);
ssl = sizeof(ss);
HX(getpeername(0,
(void *)&ss, &ssl) == -1,
ss);
}
}
HX((e = getrusage(RUSAGE_CHILDREN,
&ru)) == -1, ru);
if (e != -1) {
cnt += (int)ru.ru_utime.tv_sec;
cnt += (int)ru.ru_utime.tv_usec;
}
} else {
/* Subsequent hashes absorb previous result */
HD(results);
}
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
if (e != -1) {
cnt += (int)tv.tv_sec;
cnt += (int)tv.tv_usec;
}
HD(cnt);
}
SHA512_Final(results, &ctx);
memcpy((char *)buf + i, results, min(sizeof(results), len - i));
i += min(sizeof(results), len - i);
}
explicit_bzero(&ctx, sizeof ctx);
explicit_bzero(results, sizeof results);
errno = save_errno;
return (0); /* satisfied */
}
@@ -1,441 +0,0 @@
/* $OpenBSD: getentropy_solaris.c,v 1.4 2014/07/12 20:41:47 wouter Exp $ */
/*
* Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
* Copyright (c) 2014 Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/statvfs.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdlib.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#ifdef HAVE_SYS_SHA2_H
#include <sys/sha2.h>
#define SHA512_Init SHA512Init
#define SHA512_Update SHA512Update
#define SHA512_Final SHA512Final
#else
#include <openssl/sha.h>
#endif
#include <sys/vfs.h>
#include <sys/statfs.h>
#include <sys/loadavg.h>
#define REPEAT 5
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define HX(a, b) \
do { \
if ((a)) \
HD(errno); \
else \
HD(b); \
} while (0)
#define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l)))
#define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x)))
#define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*)))
int getentropy(void *buf, size_t len);
#ifdef CAN_REFERENCE_MAIN
extern int main(int, char *argv[]);
#endif
static int gotdata(char *buf, size_t len);
static int getentropy_urandom(void *buf, size_t len, const char *path,
int devfscheck);
static int getentropy_fallback(void *buf, size_t len);
int
getentropy(void *buf, size_t len)
{
int ret = -1;
if (len > 256) {
errno = EIO;
return -1;
}
/*
* Try to get entropy with /dev/urandom
*
* Solaris provides /dev/urandom as a symbolic link to
* /devices/pseudo/random@0:urandom which is provided by
* a devfs filesystem. Best practice is to use O_NOFOLLOW,
* so we must try the unpublished name directly.
*
* This can fail if the process is inside a chroot which lacks
* the devfs mount, or if file descriptors are exhausted.
*/
ret = getentropy_urandom(buf, len,
"/devices/pseudo/random@0:urandom", 1);
if (ret != -1)
return (ret);
/*
* Unfortunately, chroot spaces on Solaris are sometimes setup
* with direct device node of the well-known /dev/urandom name
* (perhaps to avoid dragging all of devfs into the space).
*
* This can fail if the process is inside a chroot or if file
* descriptors are exhausted.
*/
ret = getentropy_urandom(buf, len, "/dev/urandom", 0);
if (ret != -1)
return (ret);
/*
* Entropy collection via /dev/urandom has failed.
*
* No other API exists for collecting entropy, and we have
* no failsafe way to get it on Solaris that is not sensitive
* to resource exhaustion.
*
* We have very few options:
* - Even syslog_r is unsafe to call at this low level, so
* there is no way to alert the user or program.
* - Cannot call abort() because some systems have unsafe
* corefiles.
* - Could raise(SIGKILL) resulting in silent program termination.
* - Return EIO, to hint that arc4random's stir function
* should raise(SIGKILL)
* - Do the best under the circumstances....
*
* This code path exists to bring light to the issue that Solaris
* does not provide a failsafe API for entropy collection.
*
* We hope this demonstrates that Solaris should consider
* providing a new failsafe API which works in a chroot or
* when file descriptors are exhausted.
*/
#undef FAIL_INSTEAD_OF_TRYING_FALLBACK
#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK
raise(SIGKILL);
#endif
ret = getentropy_fallback(buf, len);
if (ret != -1)
return (ret);
errno = EIO;
return (ret);
}
/*
* Basic sanity checking; wish we could do better.
*/
static int
gotdata(char *buf, size_t len)
{
char any_set = 0;
size_t i;
for (i = 0; i < len; ++i)
any_set |= buf[i];
if (any_set == 0)
return -1;
return 0;
}
static int
getentropy_urandom(void *buf, size_t len, const char *path, int devfscheck)
{
struct stat st;
size_t i;
int fd, flags;
int save_errno = errno;
start:
flags = O_RDONLY;
#ifdef O_NOFOLLOW
flags |= O_NOFOLLOW;
#endif
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif
fd = open(path, flags, 0);
if (fd == -1) {
if (errno == EINTR)
goto start;
goto nodevrandom;
}
#ifndef O_CLOEXEC
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
#endif
/* Lightly verify that the device node looks sane */
if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode) ||
(devfscheck && (strcmp(st.st_fstype, "devfs") != 0))) {
close(fd);
goto nodevrandom;
}
for (i = 0; i < len; ) {
size_t wanted = len - i;
ssize_t ret = read(fd, (char *)buf + i, wanted);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
close(fd);
goto nodevrandom;
}
i += ret;
}
close(fd);
if (gotdata(buf, len) == 0) {
errno = save_errno;
return 0; /* satisfied */
}
nodevrandom:
errno = EIO;
return -1;
}
static const int cl[] = {
CLOCK_REALTIME,
#ifdef CLOCK_MONOTONIC
CLOCK_MONOTONIC,
#endif
#ifdef CLOCK_MONOTONIC_RAW
CLOCK_MONOTONIC_RAW,
#endif
#ifdef CLOCK_TAI
CLOCK_TAI,
#endif
#ifdef CLOCK_VIRTUAL
CLOCK_VIRTUAL,
#endif
#ifdef CLOCK_UPTIME
CLOCK_UPTIME,
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
CLOCK_PROCESS_CPUTIME_ID,
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
CLOCK_THREAD_CPUTIME_ID,
#endif
};
static int
getentropy_fallback(void *buf, size_t len)
{
uint8_t results[SHA512_DIGEST_LENGTH];
int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat;
static int cnt;
struct timespec ts;
struct timeval tv;
double loadavg[3];
struct rusage ru;
sigset_t sigset;
struct stat st;
SHA512_CTX ctx;
static pid_t lastpid;
pid_t pid;
size_t i, ii, m;
char *p;
pid = getpid();
if (lastpid == pid) {
faster = 1;
repeat = 2;
} else {
faster = 0;
lastpid = pid;
repeat = REPEAT;
}
for (i = 0; i < len; ) {
int j;
SHA512_Init(&ctx);
for (j = 0; j < repeat; j++) {
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
if (e != -1) {
cnt += (int)tv.tv_sec;
cnt += (int)tv.tv_usec;
}
for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++)
HX(clock_gettime(cl[ii], &ts) == -1, ts);
HX((pid = getpid()) == -1, pid);
HX((pid = getsid(pid)) == -1, pid);
HX((pid = getppid()) == -1, pid);
HX((pid = getpgid(0)) == -1, pid);
HX((e = getpriority(0, 0)) == -1, e);
HX((getloadavg(loadavg, 3) == -1), loadavg);
if (!faster) {
ts.tv_sec = 0;
ts.tv_nsec = 1;
(void) nanosleep(&ts, NULL);
}
HX(sigpending(&sigset) == -1, sigset);
HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
sigset);
#ifdef CAN_REFERENCE_MAIN
HF(main); /* an addr in program */
#endif
HF(getentropy); /* an addr in this library */
HF(printf); /* an addr in libc */
p = (char *)&p;
HD(p); /* an addr on stack */
p = (char *)&errno;
HD(p); /* the addr of errno */
if (i == 0) {
struct sockaddr_storage ss;
struct statvfs stvfs;
struct termios tios;
socklen_t ssl;
off_t off;
/*
* Prime-sized mappings encourage fragmentation;
* thus exposing some address entropy.
*/
struct mm {
size_t npg;
void *p;
} mm[] = {
{ 17, MAP_FAILED }, { 3, MAP_FAILED },
{ 11, MAP_FAILED }, { 2, MAP_FAILED },
{ 5, MAP_FAILED }, { 3, MAP_FAILED },
{ 7, MAP_FAILED }, { 1, MAP_FAILED },
{ 57, MAP_FAILED }, { 3, MAP_FAILED },
{ 131, MAP_FAILED }, { 1, MAP_FAILED },
};
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
HX(mm[m].p = mmap(NULL,
mm[m].npg * pgs,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANON, -1,
(off_t)0), mm[m].p);
if (mm[m].p != MAP_FAILED) {
size_t mo;
/* Touch some memory... */
p = mm[m].p;
mo = cnt %
(mm[m].npg * pgs - 1);
p[mo] = 1;
cnt += (int)((long)(mm[m].p)
/ pgs);
}
/* Check cnts and times... */
for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]);
ii++) {
HX((e = clock_gettime(cl[ii],
&ts)) == -1, ts);
if (e != -1)
cnt += (int)ts.tv_nsec;
}
HX((e = getrusage(RUSAGE_SELF,
&ru)) == -1, ru);
if (e != -1) {
cnt += (int)ru.ru_utime.tv_sec;
cnt += (int)ru.ru_utime.tv_usec;
}
}
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
if (mm[m].p != MAP_FAILED)
munmap(mm[m].p, mm[m].npg * pgs);
mm[m].p = MAP_FAILED;
}
HX(stat(".", &st) == -1, st);
HX(statvfs(".", &stvfs) == -1, stvfs);
HX(stat("/", &st) == -1, st);
HX(statvfs("/", &stvfs) == -1, stvfs);
HX((e = fstat(0, &st)) == -1, st);
if (e == -1) {
if (S_ISREG(st.st_mode) ||
S_ISFIFO(st.st_mode) ||
S_ISSOCK(st.st_mode)) {
HX(fstatvfs(0, &stvfs) == -1,
stvfs);
HX((off = lseek(0, (off_t)0,
SEEK_CUR)) < 0, off);
}
if (S_ISCHR(st.st_mode)) {
HX(tcgetattr(0, &tios) == -1,
tios);
} else if (S_ISSOCK(st.st_mode)) {
memset(&ss, 0, sizeof ss);
ssl = sizeof(ss);
HX(getpeername(0,
(void *)&ss, &ssl) == -1,
ss);
}
}
HX((e = getrusage(RUSAGE_CHILDREN,
&ru)) == -1, ru);
if (e != -1) {
cnt += (int)ru.ru_utime.tv_sec;
cnt += (int)ru.ru_utime.tv_usec;
}
} else {
/* Subsequent hashes absorb previous result */
HD(results);
}
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
if (e != -1) {
cnt += (int)tv.tv_sec;
cnt += (int)tv.tv_usec;
}
HD(cnt);
}
SHA512_Final(results, &ctx);
memcpy((char *)buf + i, results, min(sizeof(results), len - i));
i += min(sizeof(results), len - i);
}
memset(results, 0, sizeof results);
if (gotdata(buf, len) == 0) {
errno = save_errno;
return 0; /* satisfied */
}
errno = EIO;
return -1;
}
@@ -1,59 +0,0 @@
/* $OpenBSD: getentropy_win.c,v 1.5 2016/08/07 03:27:21 tb Exp $ */
/*
* Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
* Copyright (c) 2014, Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Emulation of getentropy(2) as documented at:
* http://man.openbsd.org/getentropy.2
*/
#include <windows.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <wincrypt.h>
#include <process.h>
int getentropy(void *buf, size_t len);
/*
* On Windows, CryptGenRandom is supposed to be a well-seeded
* cryptographically strong random number generator.
*/
int
getentropy(void *buf, size_t len)
{
HCRYPTPROV provider;
if (len > 256) {
errno = EIO;
return (-1);
}
if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT) == 0)
goto fail;
if (CryptGenRandom(provider, len, buf) == 0) {
CryptReleaseContext(provider, 0);
goto fail;
}
CryptReleaseContext(provider, 0);
return (0);
fail:
errno = EIO;
return (-1);
}
@@ -1,107 +0,0 @@
/*
* Taken from FreeBSD src / lib / libc / stdtime / localtime.c 1.43 revision.
* localtime.c 7.78.
* tzfile.h 1.8
* adapted to be replacement gmtime_r.
*/
#include "config.h"
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#define MONSPERYEAR 12
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERMIN 60
#define SECSPERHOUR (60*60)
#define SECSPERDAY (24*60*60)
#define DAYSPERWEEK 7
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_YEAR_BASE 1900
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
static const int mon_lengths[2][MONSPERYEAR] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
static const int year_lengths[2] = {
DAYSPERNYEAR, DAYSPERLYEAR
};
static void
timesub(timep, offset, tmp)
const time_t * const timep;
const long offset;
struct tm * const tmp;
{
long days;
long rem;
long y;
int yleap;
const int * ip;
days = *timep / SECSPERDAY;
rem = *timep % SECSPERDAY;
rem += (offset);
while (rem < 0) {
rem += SECSPERDAY;
--days;
}
while (rem >= SECSPERDAY) {
rem -= SECSPERDAY;
++days;
}
tmp->tm_hour = (int) (rem / SECSPERHOUR);
rem = rem % SECSPERHOUR;
tmp->tm_min = (int) (rem / SECSPERMIN);
/*
** A positive leap second requires a special
** representation. This uses "... ??:59:60" et seq.
*/
tmp->tm_sec = (int) (rem % SECSPERMIN) ;
tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
if (tmp->tm_wday < 0)
tmp->tm_wday += DAYSPERWEEK;
y = EPOCH_YEAR;
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
long newy;
newy = y + days / DAYSPERNYEAR;
if (days < 0)
--newy;
days -= (newy - y) * DAYSPERNYEAR +
LEAPS_THRU_END_OF(newy - 1) -
LEAPS_THRU_END_OF(y - 1);
y = newy;
}
tmp->tm_year = y - TM_YEAR_BASE;
tmp->tm_yday = (int) days;
ip = mon_lengths[yleap];
for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
days = days - (long) ip[tmp->tm_mon];
tmp->tm_mday = (int) (days + 1);
tmp->tm_isdst = 0;
}
/*
* Re-entrant version of gmtime.
*/
struct tm * gmtime_r(const time_t* timep, struct tm *tm)
{
timesub(timep, 0L, tm);
return tm;
}
@@ -1,182 +0,0 @@
/* From openssh4.3p2 compat/inet_aton.c */
/*
* Copyright (c) 1983, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
* -
* --Copyright--
*/
/* OPENBSD ORIGINAL: lib/libc/net/inet_addr.c */
#include <config.h>
#if !defined(HAVE_INET_ATON)
#include <sys/types.h>
#include <sys/param.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <ctype.h>
#if 0
/*
* Ascii internet address interpretation routine.
* The value returned is in network order.
*/
in_addr_t
inet_addr(const char *cp)
{
struct in_addr val;
if (inet_aton(cp, &val))
return (val.s_addr);
return (INADDR_NONE);
}
#endif
/*
* Check whether "cp" is a valid ascii representation
* of an Internet address and convert to a binary address.
* Returns 1 if the address is valid, 0 if not.
* This replaces inet_addr, the return value from which
* cannot distinguish between failure and a local broadcast address.
*/
int
inet_aton(const char *cp, struct in_addr *addr)
{
uint32_t val;
int base, n;
char c;
unsigned int parts[4];
unsigned int *pp = parts;
c = *cp;
for (;;) {
/*
* Collect number up to ``.''.
* Values are specified as for C:
* 0x=hex, 0=octal, isdigit=decimal.
*/
if (!isdigit((unsigned char)c))
return (0);
val = 0; base = 10;
if (c == '0') {
c = *++cp;
if (c == 'x' || c == 'X')
base = 16, c = *++cp;
else
base = 8;
}
for (;;) {
if (isascii((unsigned char)c) && isdigit((unsigned char)c)) {
val = (val * base) + (c - '0');
c = *++cp;
} else if (base == 16 && isascii((unsigned char)c) && isxdigit((unsigned char)c)) {
val = (val << 4) |
(c + 10 - (islower((unsigned char)c) ? 'a' : 'A'));
c = *++cp;
} else
break;
}
if (c == '.') {
/*
* Internet format:
* a.b.c.d
* a.b.c (with c treated as 16 bits)
* a.b (with b treated as 24 bits)
*/
if (pp >= parts + 3)
return (0);
*pp++ = val;
c = *++cp;
} else
break;
}
/*
* Check for trailing characters.
*/
if (c != '\0' && (!isascii((unsigned char)c) || !isspace((unsigned char)c)))
return (0);
/*
* Concoct the address according to
* the number of parts specified.
*/
n = pp - parts + 1;
switch (n) {
case 0:
return (0); /* initial nondigit */
case 1: /* a -- 32 bits */
break;
case 2: /* a.b -- 8.24 bits */
if ((val > 0xffffff) || (parts[0] > 0xff))
return (0);
val |= parts[0] << 24;
break;
case 3: /* a.b.c -- 8.8.16 bits */
if ((val > 0xffff) || (parts[0] > 0xff) || (parts[1] > 0xff))
return (0);
val |= (parts[0] << 24) | (parts[1] << 16);
break;
case 4: /* a.b.c.d -- 8.8.8.8 bits */
if ((val > 0xff) || (parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff))
return (0);
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
break;
}
if (addr)
addr->s_addr = htonl(val);
return (1);
}
#endif /* !defined(HAVE_INET_ATON) */
@@ -1,218 +0,0 @@
/* From openssh 4.3p2 compat/inet_ntop.c */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/* OPENBSD ORIGINAL: lib/libc/net/inet_ntop.c */
#include <config.h>
#ifndef HAVE_INET_NTOP
#include <sys/param.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <string.h>
#include <errno.h>
#include <stdio.h>
#ifndef IN6ADDRSZ
#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
#endif
#ifndef INT16SZ
#define INT16SZ 2 /* for systems without 16-bit ints */
#endif
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static const char *inet_ntop4(const u_char *src, char *dst, size_t size);
static const char *inet_ntop6(const u_char *src, char *dst, size_t size);
/* char *
* inet_ntop(af, src, dst, size)
* convert a network format address to presentation format.
* return:
* pointer to presentation format address (`dst'), or NULL (see errno).
* author:
* Paul Vixie, 1996.
*/
const char *
inet_ntop(int af, const void *src, char *dst, size_t size)
{
switch (af) {
case AF_INET:
return (inet_ntop4(src, dst, size));
case AF_INET6:
return (inet_ntop6(src, dst, size));
default:
#ifdef EAFNOSUPPORT
errno = EAFNOSUPPORT;
#else
errno = ENOSYS;
#endif
return (NULL);
}
/* NOTREACHED */
}
/* const char *
* inet_ntop4(src, dst, size)
* format an IPv4 address, more or less like inet_ntoa()
* return:
* `dst' (as a const)
* notes:
* (1) uses no statics
* (2) takes a u_char* not an in_addr as input
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop4(const u_char *src, char *dst, size_t size)
{
static const char fmt[] = "%u.%u.%u.%u";
char tmp[sizeof "255.255.255.255"];
int l;
l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]);
if (l <= 0 || l >= (int)size) {
errno = ENOSPC;
return (NULL);
}
strlcpy(dst, tmp, size);
return (dst);
}
/* const char *
* inet_ntop6(src, dst, size)
* convert IPv6 binary address into presentation (printable) format
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop6(const u_char *src, char *dst, size_t size)
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
char *tp, *ep;
struct { int base, len; } best, cur;
u_int words[IN6ADDRSZ / INT16SZ];
int i;
int advance;
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof words);
for (i = 0; i < IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
best.len = 0;
cur.base = -1;
cur.len = 0;
for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
ep = tmp + sizeof(tmp);
for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) {
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len)) {
if (i == best.base) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
if (!inet_ntop4(src+12, tp, (size_t)(ep - tp)))
return (NULL);
tp += strlen(tp);
break;
}
advance = snprintf(tp, ep - tp, "%x", words[i]);
if (advance <= 0 || advance >= ep - tp)
return (NULL);
tp += advance;
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
if (tp + 1 >= ep)
return (NULL);
*tp++ = '\0';
/*
* Check for overflow, copy, and we're done.
*/
if ((size_t)(tp - tmp) > size) {
errno = ENOSPC;
return (NULL);
}
strlcpy(dst, tmp, size);
return (dst);
}
#endif /* !HAVE_INET_NTOP */
@@ -1,230 +0,0 @@
/* $KAME: inet_pton.c,v 1.5 2001/08/20 02:32:40 itojun Exp $ */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#include <config.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static int inet_pton4 (const char *src, uint8_t *dst);
static int inet_pton6 (const char *src, uint8_t *dst);
/*
*
* The definitions we might miss.
*
*/
#ifndef NS_INT16SZ
#define NS_INT16SZ 2
#endif
#ifndef NS_IN6ADDRSZ
#define NS_IN6ADDRSZ 16
#endif
#ifndef NS_INADDRSZ
#define NS_INADDRSZ 4
#endif
/* int
* inet_pton(af, src, dst)
* convert from presentation format (which usually means ASCII printable)
* to network format (which is usually some kind of binary format).
* return:
* 1 if the address was valid for the specified address family
* 0 if the address wasn't valid (`dst' is untouched in this case)
* -1 if some other error occurred (`dst' is untouched in this case, too)
* author:
* Paul Vixie, 1996.
*/
int
inet_pton(af, src, dst)
int af;
const char *src;
void *dst;
{
switch (af) {
case AF_INET:
return (inet_pton4(src, dst));
case AF_INET6:
return (inet_pton6(src, dst));
default:
#ifdef EAFNOSUPPORT
errno = EAFNOSUPPORT;
#else
errno = ENOSYS;
#endif
return (-1);
}
/* NOTREACHED */
}
/* int
* inet_pton4(src, dst)
* like inet_aton() but without all the hexadecimal and shorthand.
* return:
* 1 if `src' is a valid dotted quad, else 0.
* notice:
* does not touch `dst' unless it's returning 1.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton4(src, dst)
const char *src;
uint8_t *dst;
{
static const char digits[] = "0123456789";
int saw_digit, octets, ch;
uint8_t tmp[NS_INADDRSZ], *tp;
saw_digit = 0;
octets = 0;
*(tp = tmp) = 0;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr(digits, ch)) != NULL) {
uint32_t new = *tp * 10 + (pch - digits);
if (new > 255)
return (0);
*tp = new;
if (! saw_digit) {
if (++octets > 4)
return (0);
saw_digit = 1;
}
} else if (ch == '.' && saw_digit) {
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
} else
return (0);
}
if (octets < 4)
return (0);
memcpy(dst, tmp, NS_INADDRSZ);
return (1);
}
/* int
* inet_pton6(src, dst)
* convert presentation level address to network order binary form.
* return:
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
* notice:
* (1) does not touch `dst' unless it's returning 1.
* (2) :: in a full address is silently ignored.
* credit:
* inspired by Mark Andrews.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton6(src, dst)
const char *src;
uint8_t *dst;
{
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
uint8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok;
int ch, saw_xdigit;
uint32_t val;
memset((tp = tmp), '\0', NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
return (0);
curtok = src;
saw_xdigit = 0;
val = 0;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if (pch != NULL) {
val <<= 4;
val |= (pch - xdigits);
if (val > 0xffff)
return (0);
saw_xdigit = 1;
continue;
}
if (ch == ':') {
curtok = src;
if (!saw_xdigit) {
if (colonp)
return (0);
colonp = tp;
continue;
}
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (uint8_t) (val >> 8) & 0xff;
*tp++ = (uint8_t) val & 0xff;
saw_xdigit = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
inet_pton4(curtok, tp) > 0) {
tp += NS_INADDRSZ;
saw_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
return (0);
}
if (saw_xdigit) {
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (uint8_t) (val >> 8) & 0xff;
*tp++ = (uint8_t) val & 0xff;
}
if (colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
for (i = 1; i <= n; i++) {
endp[- i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
return (0);
memcpy(dst, tmp, NS_IN6ADDRSZ);
return (1);
}
@@ -1,45 +0,0 @@
/* isblank - compatibility implementation of isblank
*
* Copyright (c) 2015, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
#include "config.h"
/* return true for a blank character: space or tab */
int isblank(int c);
/* implementation of isblank. unsigned char is the argument */
int
isblank(int c)
{
return (c==' ' || c=='\t');
}
@@ -1,24 +0,0 @@
/* Just a replacement, if the original malloc is not
GNU-compliant. See autoconf documentation. */
#include "config.h"
#undef malloc
#include <sys/types.h>
#ifndef USE_WINSOCK
void *malloc ();
#else
/* provide a prototype */
void *malloc (size_t n);
#endif
/* Allocate an N-byte block of memory from the heap.
If N is zero, allocate a 1-byte block. */
void *
rpl_malloc_unbound (size_t n)
{
if (n == 0)
n = 1;
return malloc (n);
}
@@ -1,25 +0,0 @@
/*
* memcmp.c: memcmp compat implementation.
*
* Copyright (c) 2010, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*/
#include <config.h>
int memcmp(const void *x, const void *y, size_t n);
int memcmp(const void *x, const void *y, size_t n)
{
const uint8_t* x8 = (const uint8_t*)x;
const uint8_t* y8 = (const uint8_t*)y;
size_t i;
for(i=0; i<n; i++) {
if(x8[i] < y8[i])
return -1;
else if(x8[i] > y8[i])
return 1;
}
return 0;
}
@@ -1,16 +0,0 @@
/*
* memcmp.h: undef memcmp for compat.
*
* Copyright (c) 2012, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*/
#ifndef COMPAT_MEMCMP_H
#define COMPAT_MEMCMP_H
#ifdef memcmp
/* undef here otherwise autoheader messes it up in config.h */
# undef memcmp
#endif
#endif /* COMPAT_MEMCMP_H */
@@ -1,43 +0,0 @@
/*
* memmove.c: memmove compat implementation.
*
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*/
#include <config.h>
#include <stdlib.h>
void *memmove(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n)
{
uint8_t* from = (uint8_t*) src;
uint8_t* to = (uint8_t*) dest;
if (from == to || n == 0)
return dest;
if (to > from && to-from < (int)n) {
/* to overlaps with from */
/* <from......> */
/* <to........> */
/* copy in reverse, to avoid overwriting from */
int i;
for(i=n-1; i>=0; i--)
to[i] = from[i];
return dest;
}
if (from > to && from-to < (int)n) {
/* to overlaps with from */
/* <from......> */
/* <to........> */
/* copy forwards, to avoid overwriting from */
size_t i;
for(i=0; i<n; i++)
to[i] = from[i];
return dest;
}
memcpy(dest, src, n);
return dest;
}
@@ -1,42 +0,0 @@
/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */
/*
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/types.h>
#include <errno.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <limits.h>
#include <stdlib.h>
/*
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
*/
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
void *
reallocarray(void *optr, size_t nmemb, size_t size)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
return NULL;
}
return realloc(optr, size * nmemb);
}
@@ -1,477 +0,0 @@
/*
* FILE: sha2.c
* AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
*
* Copyright (c) 2000-2001, Aaron D. Gifford
* All rights reserved.
*
* Modified by Jelte Jansen to fit in ldns, and not clash with any
* system-defined SHA code.
* Changes:
* - Renamed (external) functions and constants to fit ldns style
* - Removed _End and _Data functions
* - Added ldns_shaX(data, len, digest) convenience functions
* - Removed prototypes of _Transform functions and made those static
* Modified by Wouter, and trimmed, to provide SHA512 for getentropy_fallback.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the copyright holder nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``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 AUTHOR OR CONTRIBUTOR(S) 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.
*
* $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
*/
#include "config.h"
#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
#include <assert.h> /* assert() */
/* do we have sha512 header defs */
#ifndef SHA512_DIGEST_LENGTH
#define SHA512_BLOCK_LENGTH 128
#define SHA512_DIGEST_LENGTH 64
#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
typedef struct _SHA512_CTX {
uint64_t state[8];
uint64_t bitcount[2];
uint8_t buffer[SHA512_BLOCK_LENGTH];
} SHA512_CTX;
#endif /* do we have sha512 header defs */
void SHA512_Init(SHA512_CTX*);
void SHA512_Update(SHA512_CTX*, void*, size_t);
void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
unsigned char *SHA512(void *data, unsigned int data_len, unsigned char *digest);
/*** SHA-256/384/512 Machine Architecture Definitions *****************/
/*
* BYTE_ORDER NOTE:
*
* Please make sure that your system defines BYTE_ORDER. If your
* architecture is little-endian, make sure it also defines
* LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
* equivalent.
*
* If your system does not define the above, then you can do so by
* hand like this:
*
* #define LITTLE_ENDIAN 1234
* #define BIG_ENDIAN 4321
*
* And for little-endian machines, add:
*
* #define BYTE_ORDER LITTLE_ENDIAN
*
* Or for big-endian machines:
*
* #define BYTE_ORDER BIG_ENDIAN
*
* The FreeBSD machine this was written on defines BYTE_ORDER
* appropriately by including <sys/types.h> (which in turn includes
* <machine/endian.h> where the appropriate definitions are actually
* made).
*/
#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
#endif
typedef uint8_t sha2_byte; /* Exactly 1 byte */
typedef uint32_t sha2_word32; /* Exactly 4 bytes */
#ifdef S_SPLINT_S
typedef unsigned long long sha2_word64; /* lint 8 bytes */
#else
typedef uint64_t sha2_word64; /* Exactly 8 bytes */
#endif
/*** SHA-256/384/512 Various Length Definitions ***********************/
#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
/*** ENDIAN REVERSAL MACROS *******************************************/
#if BYTE_ORDER == LITTLE_ENDIAN
#define REVERSE32(w,x) { \
sha2_word32 tmp = (w); \
tmp = (tmp >> 16) | (tmp << 16); \
(x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
}
#ifndef S_SPLINT_S
#define REVERSE64(w,x) { \
sha2_word64 tmp = (w); \
tmp = (tmp >> 32) | (tmp << 32); \
tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
((tmp & 0x00ff00ff00ff00ffULL) << 8); \
(x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
((tmp & 0x0000ffff0000ffffULL) << 16); \
}
#else /* splint */
#define REVERSE64(w,x) /* splint */
#endif /* splint */
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
/*
* Macro for incrementally adding the unsigned 64-bit integer n to the
* unsigned 128-bit integer (represented using a two-element array of
* 64-bit words):
*/
#define ADDINC128(w,n) { \
(w)[0] += (sha2_word64)(n); \
if ((w)[0] < (n)) { \
(w)[1]++; \
} \
}
#ifdef S_SPLINT_S
#undef ADDINC128
#define ADDINC128(w,n) /* splint */
#endif
/*
* Macros for copying blocks of memory and for zeroing out ranges
* of memory. Using these macros makes it easy to switch from
* using memset()/memcpy() and using bzero()/bcopy().
*
* Please define either SHA2_USE_MEMSET_MEMCPY or define
* SHA2_USE_BZERO_BCOPY depending on which function set you
* choose to use:
*/
#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
/* Default to memset()/memcpy() if no option is specified */
#define SHA2_USE_MEMSET_MEMCPY 1
#endif
#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
/* Abort with an error if BOTH options are defined */
#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
#endif
#ifdef SHA2_USE_MEMSET_MEMCPY
#define MEMSET_BZERO(p,l) memset((p), 0, (l))
#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
#endif
#ifdef SHA2_USE_BZERO_BCOPY
#define MEMSET_BZERO(p,l) bzero((p), (l))
#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
#endif
/*** THE SIX LOGICAL FUNCTIONS ****************************************/
/*
* Bit shifting and rotation (used by the six SHA-XYZ logical functions:
*
* NOTE: The naming of R and S appears backwards here (R is a SHIFT and
* S is a ROTATION) because the SHA-256/384/512 description document
* (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
* same "backwards" definition.
*/
/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
#define R(b,x) ((x) >> (b))
/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
/* Four of six logical functions used in SHA-384 and SHA-512: */
#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
/* Hash constant words K for SHA-384 and SHA-512: */
static const sha2_word64 K512[80] = {
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
};
/* initial hash value H for SHA-512 */
static const sha2_word64 sha512_initial_hash_value[8] = {
0x6a09e667f3bcc908ULL,
0xbb67ae8584caa73bULL,
0x3c6ef372fe94f82bULL,
0xa54ff53a5f1d36f1ULL,
0x510e527fade682d1ULL,
0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL,
0x5be0cd19137e2179ULL
};
typedef union _ldns_sha2_buffer_union {
uint8_t* theChars;
uint64_t* theLongs;
} ldns_sha2_buffer_union;
/*** SHA-512: *********************************************************/
void SHA512_Init(SHA512_CTX* context) {
if (context == (SHA512_CTX*)0) {
return;
}
MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);
context->bitcount[0] = context->bitcount[1] = 0;
}
static void SHA512_Transform(SHA512_CTX* context,
const sha2_word64* data) {
sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer;
int j;
/* initialize registers with the prev. intermediate value */
a = context->state[0];
b = context->state[1];
c = context->state[2];
d = context->state[3];
e = context->state[4];
f = context->state[5];
g = context->state[6];
h = context->state[7];
j = 0;
do {
#if BYTE_ORDER == LITTLE_ENDIAN
/* Convert TO host byte order */
REVERSE64(*data++, W512[j]);
/* Apply the SHA-512 compression function to update a..h */
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
#else /* BYTE_ORDER == LITTLE_ENDIAN */
/* Apply the SHA-512 compression function to update a..h with copy */
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
T2 = Sigma0_512(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 16);
do {
/* Part of the message block expansion: */
s0 = W512[(j+1)&0x0f];
s0 = sigma0_512(s0);
s1 = W512[(j+14)&0x0f];
s1 = sigma1_512(s1);
/* Apply the SHA-512 compression function to update a..h */
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
(W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
T2 = Sigma0_512(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 80);
/* Compute the current intermediate hash value */
context->state[0] += a;
context->state[1] += b;
context->state[2] += c;
context->state[3] += d;
context->state[4] += e;
context->state[5] += f;
context->state[6] += g;
context->state[7] += h;
/* Clean up */
a = b = c = d = e = f = g = h = T1 = T2 = 0;
}
void SHA512_Update(SHA512_CTX* context, void *datain, size_t len) {
size_t freespace, usedspace;
const sha2_byte* data = (const sha2_byte*)datain;
if (len == 0) {
/* Calling with no data is valid - we do nothing */
return;
}
/* Sanity check: */
assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0);
usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
if (usedspace > 0) {
/* Calculate how much free space is available in the buffer */
freespace = SHA512_BLOCK_LENGTH - usedspace;
if (len >= freespace) {
/* Fill the buffer completely and process it */
MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
ADDINC128(context->bitcount, freespace << 3);
len -= freespace;
data += freespace;
SHA512_Transform(context, (sha2_word64*)context->buffer);
} else {
/* The buffer is not yet full */
MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
ADDINC128(context->bitcount, len << 3);
/* Clean up: */
usedspace = freespace = 0;
return;
}
}
while (len >= SHA512_BLOCK_LENGTH) {
/* Process as many complete blocks as we can */
SHA512_Transform(context, (sha2_word64*)data);
ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
len -= SHA512_BLOCK_LENGTH;
data += SHA512_BLOCK_LENGTH;
}
if (len > 0) {
/* There's left-overs, so save 'em */
MEMCPY_BCOPY(context->buffer, data, len);
ADDINC128(context->bitcount, len << 3);
}
/* Clean up: */
usedspace = freespace = 0;
}
static void SHA512_Last(SHA512_CTX* context) {
size_t usedspace;
ldns_sha2_buffer_union cast_var;
usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
#if BYTE_ORDER == LITTLE_ENDIAN
/* Convert FROM host byte order */
REVERSE64(context->bitcount[0],context->bitcount[0]);
REVERSE64(context->bitcount[1],context->bitcount[1]);
#endif
if (usedspace > 0) {
/* Begin padding with a 1 bit: */
context->buffer[usedspace++] = 0x80;
if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
/* Set-up for the last transform: */
MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
} else {
if (usedspace < SHA512_BLOCK_LENGTH) {
MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);
}
/* Do second-to-last transform: */
SHA512_Transform(context, (sha2_word64*)context->buffer);
/* And set-up for the last transform: */
MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);
}
} else {
/* Prepare for final transform: */
MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);
/* Begin padding with a 1 bit: */
*context->buffer = 0x80;
}
/* Store the length of input data (in bits): */
cast_var.theChars = context->buffer;
cast_var.theLongs[SHA512_SHORT_BLOCK_LENGTH / 8] = context->bitcount[1];
cast_var.theLongs[SHA512_SHORT_BLOCK_LENGTH / 8 + 1] = context->bitcount[0];
/* final transform: */
SHA512_Transform(context, (sha2_word64*)context->buffer);
}
void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
sha2_word64 *d = (sha2_word64*)digest;
/* Sanity check: */
assert(context != (SHA512_CTX*)0);
/* If no digest buffer is passed, we don't bother doing this: */
if (digest != (sha2_byte*)0) {
SHA512_Last(context);
/* Save the hash data for output: */
#if BYTE_ORDER == LITTLE_ENDIAN
{
/* Convert TO host byte order */
int j;
for (j = 0; j < 8; j++) {
REVERSE64(context->state[j],context->state[j]);
*d++ = context->state[j];
}
}
#else
MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
#endif
}
/* Zero out state data */
MEMSET_BZERO(context, sizeof(SHA512_CTX));
}
unsigned char *
SHA512(void *data, unsigned int data_len, unsigned char *digest)
{
SHA512_CTX ctx;
SHA512_Init(&ctx);
SHA512_Update(&ctx, data, data_len);
SHA512_Final(digest, &ctx);
return digest;
}
File diff suppressed because it is too large Load Diff
@@ -1,73 +0,0 @@
/* compat/strlcat.c */
/*-
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
*/
/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
#include "config.h"
#ifndef HAVE_STRLCAT
#include <sys/types.h>
#include <string.h>
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
#endif /* !HAVE_STRLCAT */
@@ -1,57 +0,0 @@
/* from openssh 4.3p2 compat/strlcpy.c */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
#include <config.h>
#ifndef HAVE_STRLCPY
#include <sys/types.h>
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif /* !HAVE_STRLCPY */
@@ -1,345 +0,0 @@
/** strptime workaround (for oa macos leopard)
* This strptime follows the man strptime (2001-11-12)
* conforming to SUSv2, POSIX.1-2001
*
* This very simple version of strptime has no:
* - E alternatives
* - O alternatives
* - Glibc additions
* - Does not process week numbers
* - Does not properly processes year day
*
* LICENSE
* Copyright (c) 2008, NLnet Labs, Matthijs Mekking
* 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 NLnetLabs nor the names of its
* 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 COPYRIGHT OWNER 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.
**/
#include "config.h"
#ifndef HAVE_CONFIG_H
#include <time.h>
#endif
#ifndef STRPTIME_WORKS
#define TM_YEAR_BASE 1900
#include <ctype.h>
#include <string.h>
static const char *abb_weekdays[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
};
static const char *full_weekdays[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", NULL
};
static const char *abb_months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
};
static const char *full_months[] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December", NULL
};
static const char *ampm[] = {
"am", "pm", NULL
};
static int
match_string(const char **buf, const char **strs)
{
int i = 0;
for (i = 0; strs[i] != NULL; i++) {
int len = strlen(strs[i]);
if (strncasecmp (*buf, strs[i], len) == 0) {
*buf += len;
return i;
}
}
return -1;
}
static int
str2int(const char **buf, int max)
{
int ret=0, count=0;
while (*buf[0] != '\0' && isdigit((unsigned char)*buf[0]) && count<max) {
ret = ret*10 + (*buf[0] - '0');
(*buf)++;
count++;
}
if (!count)
return -1;
return ret;
}
/** Converts the character string s to values which are stored in tm
* using the format specified by format
**/
char *
unbound_strptime(const char *s, const char *format, struct tm *tm)
{
int c, ret;
int split_year = 0;
while ((c = *format) != '\0') {
/* whitespace, literal or format */
if (isspace((unsigned char)c)) { /* whitespace */
/** whitespace matches zero or more whitespace characters in the
* input string.
**/
while (isspace((unsigned char)*s))
s++;
}
else if (c == '%') { /* format */
format++;
c = *format;
switch (c) {
case '%': /* %% is converted to % */
if (*s != c) {
return NULL;
}
s++;
break;
case 'a': /* weekday name, abbreviated or full */
case 'A':
ret = match_string(&s, full_weekdays);
if (ret < 0)
ret = match_string(&s, abb_weekdays);
if (ret < 0) {
return NULL;
}
tm->tm_wday = ret;
break;
case 'b': /* month name, abbreviated or full */
case 'B':
case 'h':
ret = match_string(&s, full_months);
if (ret < 0)
ret = match_string(&s, abb_months);
if (ret < 0) {
return NULL;
}
tm->tm_mon = ret;
break;
case 'c': /* date and time representation */
if (!(s = unbound_strptime(s, "%x %X", tm))) {
return NULL;
}
break;
case 'C': /* century number */
ret = str2int(&s, 2);
if (ret < 0 || ret > 99) { /* must be in [00,99] */
return NULL;
}
if (split_year) {
tm->tm_year = ret*100 + (tm->tm_year%100);
}
else {
tm->tm_year = ret*100 - TM_YEAR_BASE;
split_year = 1;
}
break;
case 'd': /* day of month */
case 'e':
ret = str2int(&s, 2);
if (ret < 1 || ret > 31) { /* must be in [01,31] */
return NULL;
}
tm->tm_mday = ret;
break;
case 'D': /* equivalent to %m/%d/%y */
if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) {
return NULL;
}
break;
case 'H': /* hour */
ret = str2int(&s, 2);
if (ret < 0 || ret > 23) { /* must be in [00,23] */
return NULL;
}
tm->tm_hour = ret;
break;
case 'I': /* 12hr clock hour */
ret = str2int(&s, 2);
if (ret < 1 || ret > 12) { /* must be in [01,12] */
return NULL;
}
if (ret == 12) /* actually [0,11] */
ret = 0;
tm->tm_hour = ret;
break;
case 'j': /* day of year */
ret = str2int(&s, 2);
if (ret < 1 || ret > 366) { /* must be in [001,366] */
return NULL;
}
tm->tm_yday = ret;
break;
case 'm': /* month */
ret = str2int(&s, 2);
if (ret < 1 || ret > 12) { /* must be in [01,12] */
return NULL;
}
/* months go from 0-11 */
tm->tm_mon = (ret-1);
break;
case 'M': /* minute */
ret = str2int(&s, 2);
if (ret < 0 || ret > 59) { /* must be in [00,59] */
return NULL;
}
tm->tm_min = ret;
break;
case 'n': /* arbitrary whitespace */
case 't':
while (isspace((unsigned char)*s))
s++;
break;
case 'p': /* am pm */
ret = match_string(&s, ampm);
if (ret < 0) {
return NULL;
}
if (tm->tm_hour < 0 || tm->tm_hour > 11) { /* %I */
return NULL;
}
if (ret == 1) /* pm */
tm->tm_hour += 12;
break;
case 'r': /* equivalent of %I:%M:%S %p */
if (!(s = unbound_strptime(s, "%I:%M:%S %p", tm))) {
return NULL;
}
break;
case 'R': /* equivalent of %H:%M */
if (!(s = unbound_strptime(s, "%H:%M", tm))) {
return NULL;
}
break;
case 'S': /* seconds */
ret = str2int(&s, 2);
/* 60 may occur for leap seconds */
/* earlier 61 was also allowed */
if (ret < 0 || ret > 60) { /* must be in [00,60] */
return NULL;
}
tm->tm_sec = ret;
break;
case 'T': /* equivalent of %H:%M:%S */
if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) {
return NULL;
}
break;
case 'U': /* week number, with the first Sun of Jan being w1 */
ret = str2int(&s, 2);
if (ret < 0 || ret > 53) { /* must be in [00,53] */
return NULL;
}
/** it is hard (and not necessary for nsd) to determine time
* data from week number.
**/
break;
case 'w': /* day of week */
ret = str2int(&s, 1);
if (ret < 0 || ret > 6) { /* must be in [0,6] */
return NULL;
}
tm->tm_wday = ret;
break;
case 'W': /* week number, with the first Mon of Jan being w1 */
ret = str2int(&s, 2);
if (ret < 0 || ret > 53) { /* must be in [00,53] */
return NULL;
}
/** it is hard (and not necessary for nsd) to determine time
* data from week number.
**/
break;
case 'x': /* date format */
if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) {
return NULL;
}
break;
case 'X': /* time format */
if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) {
return NULL;
}
break;
case 'y': /* last two digits of a year */
ret = str2int(&s, 2);
if (ret < 0 || ret > 99) { /* must be in [00,99] */
return NULL;
}
if (split_year) {
tm->tm_year = ((tm->tm_year/100) * 100) + ret;
}
else {
split_year = 1;
/** currently:
* if in [0,68] we are in 21th century,
* if in [69,99] we are in 20th century.
**/
if (ret < 69) /* 2000 */
ret += 100;
tm->tm_year = ret;
}
break;
case 'Y': /* year */
ret = str2int(&s, 4);
if (ret < 0 || ret > 9999) {
return NULL;
}
tm->tm_year = ret - TM_YEAR_BASE;
break;
case '\0':
default: /* unsupported, cannot match format */
return NULL;
break;
}
}
else { /* literal */
/* if input cannot match format, return NULL */
if (*s != c)
return NULL;
s++;
}
format++;
}
/* return pointer to remainder of s */
return (char*) s;
}
#endif /* STRPTIME_WORKS */
@@ -1,65 +0,0 @@
/**
* strsep implementation for compatibility.
*
* LICENSE
* Copyright (c) 2016, NLnet Labs
* 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 NLnetLabs nor the names of its
* 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 COPYRIGHT OWNER 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.
**/
#include "config.h"
/** see if character is in the delimiter array */
static int
in_delim(char c, const char* delim)
{
const char* p;
if(!delim)
return 0;
for(p=delim; *p; p++) {
if(*p == c)
return 1;
}
return 0;
}
char *strsep(char **stringp, const char *delim)
{
char* s;
char* orig;
if(stringp == NULL || *stringp == NULL)
return NULL;
orig = *stringp;
s = *stringp;
while(*s && !in_delim(*s, delim))
s++;
if(*s) {
*s = 0;
*stringp = s+1;
} else {
*stringp = NULL;
}
return orig;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,825 +0,0 @@
/*
* daemon/acl_list.h - client access control storage for the server.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file helps the server keep out queries from outside sources, that
* should not be answered.
*/
#include "config.h"
#include "daemon/acl_list.h"
#include "util/regional.h"
#include "util/log.h"
#include "util/config_file.h"
#include "util/net_help.h"
#include "services/localzone.h"
#include "services/listen_dnsport.h"
#include "sldns/str2wire.h"
struct acl_list*
acl_list_create(void)
{
struct acl_list* acl = (struct acl_list*)calloc(1,
sizeof(struct acl_list));
if(!acl)
return NULL;
acl->region = regional_create();
if(!acl->region) {
acl_list_delete(acl);
return NULL;
}
return acl;
}
void
acl_list_delete(struct acl_list* acl)
{
if(!acl)
return;
regional_destroy(acl->region);
free(acl);
}
/** insert new address into acl_list structure */
static struct acl_addr*
acl_list_insert(struct acl_list* acl, struct sockaddr_storage* addr,
socklen_t addrlen, int net, enum acl_access control,
int complain_duplicates)
{
struct acl_addr* node = regional_alloc_zero(acl->region,
sizeof(struct acl_addr));
if(!node)
return NULL;
node->control = control;
if(!addr_tree_insert(&acl->tree, &node->node, addr, addrlen, net)) {
if(complain_duplicates)
verbose(VERB_QUERY, "duplicate acl address ignored.");
}
return node;
}
/** parse str to acl_access enum */
static int
parse_acl_access(const char* str, enum acl_access* control)
{
if(strcmp(str, "allow") == 0)
*control = acl_allow;
else if(strcmp(str, "deny") == 0)
*control = acl_deny;
else if(strcmp(str, "refuse") == 0)
*control = acl_refuse;
else if(strcmp(str, "deny_non_local") == 0)
*control = acl_deny_non_local;
else if(strcmp(str, "refuse_non_local") == 0)
*control = acl_refuse_non_local;
else if(strcmp(str, "allow_snoop") == 0)
*control = acl_allow_snoop;
else if(strcmp(str, "allow_setrd") == 0)
*control = acl_allow_setrd;
else if (strcmp(str, "allow_cookie") == 0)
*control = acl_allow_cookie;
else {
log_err("access control type %s unknown", str);
return 0;
}
return 1;
}
/** apply acl_list string */
static int
acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2,
int complain_duplicates)
{
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
enum acl_access control;
if(!parse_acl_access(s2, &control)) {
return 0;
}
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
log_err("cannot parse access control: %s %s", str, s2);
return 0;
}
if(!acl_list_insert(acl, &addr, addrlen, net, control,
complain_duplicates)) {
log_err("out of memory");
return 0;
}
return 1;
}
/** find or create node (NULL on parse or error) */
static struct acl_addr*
acl_find_or_create_str2addr(struct acl_list* acl, const char* str,
int is_interface, int port)
{
struct acl_addr* node;
struct sockaddr_storage addr;
socklen_t addrlen;
int net = (str_is_ip6(str)?128:32);
if(is_interface) {
if(!extstrtoaddr(str, &addr, &addrlen, port)) {
log_err("cannot parse interface: %s", str);
return NULL;
}
} else {
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
log_err("cannot parse netblock: %s", str);
return NULL;
}
}
/* find or create node */
if(!(node=(struct acl_addr*)addr_tree_find(&acl->tree, &addr,
addrlen, net)) && !is_interface) {
/* create node, type 'allow' since otherwise tags are
* pointless, can override with specific access-control: cfg */
if(!(node=(struct acl_addr*)acl_list_insert(acl, &addr,
addrlen, net, acl_allow, 1))) {
log_err("out of memory");
return NULL;
}
}
return node;
}
/** find or create node (NULL on error) */
static struct acl_addr*
acl_find_or_create(struct acl_list* acl, struct sockaddr_storage* addr,
socklen_t addrlen, enum acl_access control)
{
struct acl_addr* node;
int net = (addr_is_ip6(addr, addrlen)?128:32);
/* find or create node */
if(!(node=(struct acl_addr*)addr_tree_find(&acl->tree, addr,
addrlen, net))) {
/* create node;
* can override with specific access-control: cfg */
if(!(node=(struct acl_addr*)acl_list_insert(acl, addr,
addrlen, net, control, 1))) {
log_err("out of memory");
return NULL;
}
}
return node;
}
/** apply acl_interface string */
static int
acl_interface_str_cfg(struct acl_list* acl_interface, const char* iface,
const char* s2, int port)
{
struct acl_addr* node;
enum acl_access control;
if(!parse_acl_access(s2, &control)) {
return 0;
}
if(!(node=acl_find_or_create_str2addr(acl_interface, iface, 1, port))) {
log_err("cannot update ACL on non-configured interface: %s %d",
iface, port);
return 0;
}
node->control = control;
return 1;
}
struct acl_addr*
acl_interface_insert(struct acl_list* acl_interface,
struct sockaddr_storage* addr, socklen_t addrlen,
enum acl_access control)
{
return acl_find_or_create(acl_interface, addr, addrlen, control);
}
/** apply acl_tag string */
static int
acl_list_tags_cfg(struct acl_list* acl, const char* str, uint8_t* bitmap,
size_t bitmaplen, int is_interface, int port)
{
struct acl_addr* node;
if(!(node=acl_find_or_create_str2addr(acl, str, is_interface, port))) {
if(is_interface)
log_err("non-configured interface: %s", str);
return 0;
}
node->taglen = bitmaplen;
node->taglist = regional_alloc_init(acl->region, bitmap, bitmaplen);
if(!node->taglist) {
log_err("out of memory");
return 0;
}
return 1;
}
/** apply acl_view string */
static int
acl_list_view_cfg(struct acl_list* acl, const char* str, const char* str2,
struct views* vs, int is_interface, int port)
{
struct acl_addr* node;
if(!(node=acl_find_or_create_str2addr(acl, str, is_interface, port))) {
if(is_interface)
log_err("non-configured interface: %s", str);
return 0;
}
node->view = views_find_view(vs, str2, 0 /* get read lock*/);
if(!node->view) {
log_err("no view with name: %s", str2);
return 0;
}
lock_rw_unlock(&node->view->lock);
return 1;
}
/** apply acl_tag_action string */
static int
acl_list_tag_action_cfg(struct acl_list* acl, struct config_file* cfg,
const char* str, const char* tag, const char* action,
int is_interface, int port)
{
struct acl_addr* node;
int tagid;
enum localzone_type t;
if(!(node=acl_find_or_create_str2addr(acl, str, is_interface, port))) {
if(is_interface)
log_err("non-configured interface: %s", str);
return 0;
}
/* allocate array if not yet */
if(!node->tag_actions) {
node->tag_actions = (uint8_t*)regional_alloc_zero(acl->region,
sizeof(*node->tag_actions)*cfg->num_tags);
if(!node->tag_actions) {
log_err("out of memory");
return 0;
}
node->tag_actions_size = (size_t)cfg->num_tags;
}
/* parse tag */
if((tagid=find_tag_id(cfg, tag)) == -1) {
log_err("cannot parse tag (define-tag it): %s %s", str, tag);
return 0;
}
if((size_t)tagid >= node->tag_actions_size) {
log_err("tagid too large for array %s %s", str, tag);
return 0;
}
if(!local_zone_str2type(action, &t)) {
log_err("cannot parse access control action type: %s %s %s",
str, tag, action);
return 0;
}
node->tag_actions[tagid] = (uint8_t)t;
return 1;
}
/** check wire data parse */
static int
check_data(const char* data, const struct config_strlist* head)
{
char buf[65536];
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t len = sizeof(rr);
int res;
/* '.' is sufficient for validation, and it makes the call to
* sldns_wirerr_get_type() simpler below. */
snprintf(buf, sizeof(buf), "%s %s", ".", data);
res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL, 0,
NULL, 0);
/* Reject it if we would end up having CNAME and other data (including
* another CNAME) for the same tag. */
if(res == 0 && head) {
const char* err_data = NULL;
if(sldns_wirerr_get_type(rr, len, 1) == LDNS_RR_TYPE_CNAME) {
/* adding CNAME while other data already exists. */
err_data = data;
} else {
snprintf(buf, sizeof(buf), "%s %s", ".", head->str);
len = sizeof(rr);
res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600,
NULL, 0, NULL, 0);
if(res != 0) {
/* This should be impossible here as head->str
* has been validated, but we check it just in
* case. */
return 0;
}
if(sldns_wirerr_get_type(rr, len, 1) ==
LDNS_RR_TYPE_CNAME) /* already have CNAME */
err_data = head->str;
}
if(err_data) {
log_err("redirect tag data '%s' must not coexist with "
"other data.", err_data);
return 0;
}
}
if(res == 0)
return 1;
log_err("rr data [char %d] parse error %s",
(int)LDNS_WIREPARSE_OFFSET(res)-2,
sldns_get_errorstr_parse(res));
return 0;
}
/** apply acl_tag_data string */
static int
acl_list_tag_data_cfg(struct acl_list* acl, struct config_file* cfg,
const char* str, const char* tag, const char* data,
int is_interface, int port)
{
struct acl_addr* node;
int tagid;
char* dupdata;
if(!(node=acl_find_or_create_str2addr(acl, str, is_interface, port))) {
if(is_interface)
log_err("non-configured interface: %s", str);
return 0;
}
/* allocate array if not yet */
if(!node->tag_datas) {
node->tag_datas = (struct config_strlist**)regional_alloc_zero(
acl->region, sizeof(*node->tag_datas)*cfg->num_tags);
if(!node->tag_datas) {
log_err("out of memory");
return 0;
}
node->tag_datas_size = (size_t)cfg->num_tags;
}
/* parse tag */
if((tagid=find_tag_id(cfg, tag)) == -1) {
log_err("cannot parse tag (define-tag it): %s %s", str, tag);
return 0;
}
if((size_t)tagid >= node->tag_datas_size) {
log_err("tagid too large for array %s %s", str, tag);
return 0;
}
/* check data? */
if(!check_data(data, node->tag_datas[tagid])) {
log_err("cannot parse access-control-tag data: %s %s '%s'",
str, tag, data);
return 0;
}
dupdata = regional_strdup(acl->region, data);
if(!dupdata) {
log_err("out of memory");
return 0;
}
if(!cfg_region_strlist_insert(acl->region,
&(node->tag_datas[tagid]), dupdata)) {
log_err("out of memory");
return 0;
}
return 1;
}
/** read acl_list config */
static int
read_acl_list(struct acl_list* acl, struct config_str2list* acls)
{
struct config_str2list* p;
for(p = acls; p; p = p->next) {
log_assert(p->str && p->str2);
if(!acl_list_str_cfg(acl, p->str, p->str2, 1))
return 0;
}
return 1;
}
/** read acl view config */
static int
read_acl_view(struct acl_list* acl, struct config_str2list** acl_view,
struct views* v)
{
struct config_str2list* np, *p = *acl_view;
*acl_view = NULL;
while(p) {
log_assert(p->str && p->str2);
if(!acl_list_view_cfg(acl, p->str, p->str2, v, 0, 0)) {
config_deldblstrlist(p);
return 0;
}
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p);
p = np;
}
return 1;
}
/** read acl tags config */
static int
read_acl_tags(struct acl_list* acl, struct config_strbytelist** acl_tags)
{
struct config_strbytelist* np, *p = *acl_tags;
*acl_tags = NULL;
while(p) {
log_assert(p->str && p->str2);
if(!acl_list_tags_cfg(acl, p->str, p->str2, p->str2len, 0, 0)) {
config_del_strbytelist(p);
return 0;
}
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p);
p = np;
}
return 1;
}
/** read acl tag actions config */
static int
read_acl_tag_actions(struct acl_list* acl, struct config_file* cfg,
struct config_str3list** acl_tag_actions)
{
struct config_str3list* p, *np;
p = *acl_tag_actions;
*acl_tag_actions = NULL;
while(p) {
log_assert(p->str && p->str2 && p->str3);
if(!acl_list_tag_action_cfg(acl, cfg, p->str, p->str2,
p->str3, 0, 0)) {
config_deltrplstrlist(p);
return 0;
}
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p->str3);
free(p);
p = np;
}
return 1;
}
/** read acl tag datas config */
static int
read_acl_tag_datas(struct acl_list* acl, struct config_file* cfg,
struct config_str3list** acl_tag_datas)
{
struct config_str3list* p, *np;
p = *acl_tag_datas;
*acl_tag_datas = NULL;
while(p) {
log_assert(p->str && p->str2 && p->str3);
if(!acl_list_tag_data_cfg(acl, cfg, p->str, p->str2, p->str3,
0, 0)) {
config_deltrplstrlist(p);
return 0;
}
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p->str3);
free(p);
p = np;
}
return 1;
}
int
acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg,
struct views* v)
{
regional_free_all(acl->region);
addr_tree_init(&acl->tree);
if(!read_acl_list(acl, cfg->acls))
return 0;
if(!read_acl_view(acl, &cfg->acl_view, v))
return 0;
if(!read_acl_tags(acl, &cfg->acl_tags))
return 0;
if(!read_acl_tag_actions(acl, cfg, &cfg->acl_tag_actions))
return 0;
if(!read_acl_tag_datas(acl, cfg, &cfg->acl_tag_datas))
return 0;
/* insert defaults, with '0' to ignore them if they are duplicates */
/* the 'refuse' defaults for /0 are now done per interface instead */
if(!acl_list_str_cfg(acl, "127.0.0.0/8", "allow", 0))
return 0;
if(cfg->do_ip6) {
if(!acl_list_str_cfg(acl, "::1", "allow", 0))
return 0;
if(!acl_list_str_cfg(acl, "::ffff:127.0.0.1", "allow", 0))
return 0;
}
addr_tree_init_parents(&acl->tree);
return 1;
}
int
acl_interface_compare(const void* k1, const void* k2)
{
struct addr_tree_node* n1 = (struct addr_tree_node*)k1;
struct addr_tree_node* n2 = (struct addr_tree_node*)k2;
return sockaddr_cmp(&n1->addr, n1->addrlen, &n2->addr,
n2->addrlen);
/* We don't care about comparing node->net. All addresses in the
* acl_interface tree have either 32 (ipv4) or 128 (ipv6). */
}
void
acl_interface_init(struct acl_list* acl_interface)
{
regional_free_all(acl_interface->region);
/* We want comparison in the tree to include only address and port.
* We don't care about comparing node->net. All addresses in the
* acl_interface->tree should have either 32 (ipv4) or 128 (ipv6).
* Initialise with the appropriate compare function but keep treating
* it as an addr_tree. */
addr_tree_addrport_init(&acl_interface->tree);
}
static int
read_acl_interface_action(struct acl_list* acl_interface,
struct config_str2list* acls, int port)
{
struct config_str2list* p;
for(p = acls; p; p = p->next) {
char** resif = NULL;
int num_resif = 0;
int i;
log_assert(p->str && p->str2);
if(!resolve_interface_names(&p->str, 1, NULL, &resif, &num_resif))
return 0;
for(i = 0; i<num_resif; i++) {
if(!acl_interface_str_cfg(acl_interface, resif[i], p->str2, port)){
config_del_strarray(resif, num_resif);
return 0;
}
}
config_del_strarray(resif, num_resif);
}
return 1;
}
/** read acl view config for interface */
static int
read_acl_interface_view(struct acl_list* acl_interface,
struct config_str2list** acl_view,
struct views* v, int port)
{
struct config_str2list* np, *p = *acl_view;
*acl_view = NULL;
while(p) {
char** resif = NULL;
int num_resif = 0;
int i;
log_assert(p->str && p->str2);
if(!resolve_interface_names(&p->str, 1, NULL, &resif, &num_resif)) {
config_deldblstrlist(p);
return 0;
}
for(i = 0; i<num_resif; i++) {
if(!acl_list_view_cfg(acl_interface, resif[i], p->str2,
v, 1, port)) {
config_del_strarray(resif, num_resif);
config_deldblstrlist(p);
return 0;
}
}
config_del_strarray(resif, num_resif);
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p);
p = np;
}
return 1;
}
/** read acl tags config for interface */
static int
read_acl_interface_tags(struct acl_list* acl_interface,
struct config_strbytelist** acl_tags, int port)
{
struct config_strbytelist* np, *p = *acl_tags;
*acl_tags = NULL;
while(p) {
char** resif = NULL;
int num_resif = 0;
int i;
log_assert(p->str && p->str2);
if(!resolve_interface_names(&p->str, 1, NULL, &resif, &num_resif)) {
config_del_strbytelist(p);
return 0;
}
for(i = 0; i<num_resif; i++) {
if(!acl_list_tags_cfg(acl_interface, resif[i], p->str2,
p->str2len, 1, port)) {
config_del_strbytelist(p);
config_del_strarray(resif, num_resif);
return 0;
}
}
config_del_strarray(resif, num_resif);
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p);
p = np;
}
return 1;
}
/** read acl tag actions config for interface*/
static int
read_acl_interface_tag_actions(struct acl_list* acl_interface,
struct config_file* cfg,
struct config_str3list** acl_tag_actions, int port)
{
struct config_str3list* p, *np;
p = *acl_tag_actions;
*acl_tag_actions = NULL;
while(p) {
char** resif = NULL;
int num_resif = 0;
int i;
log_assert(p->str && p->str2 && p->str3);
if(!resolve_interface_names(&p->str, 1, NULL, &resif, &num_resif)) {
config_deltrplstrlist(p);
return 0;
}
for(i = 0; i<num_resif; i++) {
if(!acl_list_tag_action_cfg(acl_interface, cfg,
resif[i], p->str2, p->str3, 1, port)) {
config_deltrplstrlist(p);
config_del_strarray(resif, num_resif);
return 0;
}
}
config_del_strarray(resif, num_resif);
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p->str3);
free(p);
p = np;
}
return 1;
}
/** read acl tag datas config for interface */
static int
read_acl_interface_tag_datas(struct acl_list* acl_interface,
struct config_file* cfg,
struct config_str3list** acl_tag_datas, int port)
{
struct config_str3list* p, *np;
p = *acl_tag_datas;
*acl_tag_datas = NULL;
while(p) {
char** resif = NULL;
int num_resif = 0;
int i;
log_assert(p->str && p->str2 && p->str3);
if(!resolve_interface_names(&p->str, 1, NULL, &resif, &num_resif)) {
config_deltrplstrlist(p);
return 0;
}
for(i = 0; i<num_resif; i++) {
if(!acl_list_tag_data_cfg(acl_interface, cfg,
resif[i], p->str2, p->str3, 1, port)) {
config_deltrplstrlist(p);
config_del_strarray(resif, num_resif);
return 0;
}
}
config_del_strarray(resif, num_resif);
/* free the items as we go to free up memory */
np = p->next;
free(p->str);
free(p->str2);
free(p->str3);
free(p);
p = np;
}
return 1;
}
int
acl_interface_apply_cfg(struct acl_list* acl_interface, struct config_file* cfg,
struct views* v)
{
if(!read_acl_interface_action(acl_interface, cfg->interface_actions,
cfg->port))
return 0;
if(!read_acl_interface_view(acl_interface, &cfg->interface_view, v,
cfg->port))
return 0;
if(!read_acl_interface_tags(acl_interface, &cfg->interface_tags,
cfg->port))
return 0;
if(!read_acl_interface_tag_actions(acl_interface, cfg,
&cfg->interface_tag_actions, cfg->port))
return 0;
if(!read_acl_interface_tag_datas(acl_interface, cfg,
&cfg->interface_tag_datas, cfg->port))
return 0;
addr_tree_init_parents(&acl_interface->tree);
return 1;
}
enum acl_access
acl_get_control(struct acl_addr* acl)
{
if(acl) return acl->control;
return acl_deny;
}
struct acl_addr*
acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
socklen_t addrlen)
{
return (struct acl_addr*)addr_tree_lookup(&acl->tree,
addr, addrlen);
}
size_t
acl_list_get_mem(struct acl_list* acl)
{
if(!acl) return 0;
return sizeof(*acl) + regional_get_mem(acl->region);
}
const char* acl_access_to_str(enum acl_access acl)
{
switch(acl) {
case acl_deny: return "deny";
case acl_refuse: return "refuse";
case acl_deny_non_local: return "deny_non_local";
case acl_refuse_non_local: return "refuse_non_local";
case acl_allow: return "allow";
case acl_allow_snoop: return "allow_snoop";
case acl_allow_setrd: return "allow_setrd";
default: break;
}
return "unknown";
}
void
log_acl_action(const char* action, struct sockaddr_storage* addr,
socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr)
{
char a[128], n[128];
uint16_t port;
addr_to_str(addr, addrlen, a, sizeof(a));
port = ntohs(((struct sockaddr_in*)addr)->sin_port);
if(acladdr) {
addr_to_str(&acladdr->node.addr, acladdr->node.addrlen,
n, sizeof(n));
verbose(VERB_ALGO, "%s query from %s port %d because of "
"%s/%d %s", action, a, (int)port, n, acladdr->node.net,
acl_access_to_str(acl));
} else {
verbose(VERB_ALGO, "%s query from %s port %d", action, a,
(int)port);
}
}
@@ -1,205 +0,0 @@
/*
* daemon/acl_list.h - client access control storage for the server.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file keeps track of the list of clients that are allowed to
* access the server.
*/
#ifndef DAEMON_ACL_LIST_H
#define DAEMON_ACL_LIST_H
#include "util/storage/dnstree.h"
#include "services/view.h"
struct config_file;
struct regional;
/**
* Enumeration of access control options for an address range.
* Allow or deny access.
*/
enum acl_access {
/** disallow any access whatsoever, drop it */
acl_deny = 0,
/** disallow access, send a polite 'REFUSED' reply */
acl_refuse,
/** disallow any access to zones that aren't local, drop it */
acl_deny_non_local,
/** disallow access to zones that aren't local, 'REFUSED' reply */
acl_refuse_non_local,
/** allow full access for recursion (+RD) queries */
acl_allow,
/** allow full access for all queries, recursion and cache snooping */
acl_allow_snoop,
/** allow full access for recursion queries and set RD flag regardless
* of request */
acl_allow_setrd,
/** allow full access for recursion (+RD) queries if valid cookie
* present or stateful transport */
acl_allow_cookie
};
/**
* Access control storage structure
*/
struct acl_list {
/** regional for allocation */
struct regional* region;
/**
* Tree of the addresses that are allowed/blocked.
* contents of type acl_addr.
*/
rbtree_type tree;
};
/**
*
* An address span with access control information
*/
struct acl_addr {
/** node in address tree */
struct addr_tree_node node;
/** access control on this netblock */
enum acl_access control;
/** tag bitlist */
uint8_t* taglist;
/** length of the taglist (in bytes) */
size_t taglen;
/** array per tagnumber of localzonetype(in one byte). NULL if none. */
uint8_t* tag_actions;
/** size of the tag_actions_array */
size_t tag_actions_size;
/** array per tagnumber, with per tag a list of rdata strings.
* NULL if none. strings are like 'A 127.0.0.1' 'AAAA ::1' */
struct config_strlist** tag_datas;
/** size of the tag_datas array */
size_t tag_datas_size;
/* view element, NULL if none */
struct view* view;
};
/**
* Create acl structure
* @return new structure or NULL on error.
*/
struct acl_list* acl_list_create(void);
/**
* Delete acl structure.
* @param acl: to delete.
*/
void acl_list_delete(struct acl_list* acl);
/**
* Insert interface in the acl_list. This should happen when the listening
* interface is setup.
* @param acl_interface: acl_list to insert to.
* @param addr: interface IP.
* @param addrlen: length of the interface IP.
* @param control: acl_access.
* @return new structure or NULL on error.
*/
struct acl_addr*
acl_interface_insert(struct acl_list* acl_interface,
struct sockaddr_storage* addr, socklen_t addrlen,
enum acl_access control);
/**
* Process access control config.
* @param acl: where to store.
* @param cfg: config options.
* @param v: views structure
* @return 0 on error.
*/
int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg,
struct views* v);
/** compare ACL interface "addr_tree" nodes (+port) */
int acl_interface_compare(const void* k1, const void* k2);
/**
* Initialise (also clean) the acl_interface struct.
* @param acl_interface: where to store.
*/
void acl_interface_init(struct acl_list* acl_interface);
/**
* Process interface control config.
* @param acl_interface: where to store.
* @param cfg: config options.
* @param v: views structure
* @return 0 on error.
*/
int acl_interface_apply_cfg(struct acl_list* acl_interface, struct config_file* cfg,
struct views* v);
/**
* Lookup access control status for acl structure.
* @param acl: structure for acl storage.
* @return: what to do with message from this address.
*/
enum acl_access acl_get_control(struct acl_addr* acl);
/**
* Lookup address to see its acl structure
* @param acl: structure for address storage.
* @param addr: address to check
* @param addrlen: length of addr.
* @return: acl structure from this address.
*/
struct acl_addr*
acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
socklen_t addrlen);
/**
* Get memory used by acl structure.
* @param acl: structure for address storage.
* @return bytes in use.
*/
size_t acl_list_get_mem(struct acl_list* acl);
/*
* Get string for acl access specification
* @param acl: access type value
* @return string
*/
const char* acl_access_to_str(enum acl_access acl);
/* log acl and addr for action */
void log_acl_action(const char* action, struct sockaddr_storage* addr,
socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr);
#endif /* DAEMON_ACL_LIST_H */
@@ -1,918 +0,0 @@
/*
* daemon/cachedump.c - dump the cache to text format.
*
* Copyright (c) 2008, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains functions to read and write the cache(s)
* to text format.
*/
#include "config.h"
#include <openssl/ssl.h>
#include "daemon/cachedump.h"
#include "daemon/remote.h"
#include "daemon/worker.h"
#include "services/cache/rrset.h"
#include "services/cache/dns.h"
#include "services/cache/infra.h"
#include "services/outside_network.h"
#include "util/data/msgreply.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/data/dname.h"
#include "util/config_file.h"
#include "iterator/iterator.h"
#include "iterator/iter_delegpt.h"
#include "iterator/iter_utils.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_hints.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
/** dump one rrset zonefile line */
static int
dump_rrset_line(RES* ssl, struct ub_packed_rrset_key* k, time_t now, size_t i)
{
char s[65535];
if(!packed_rr_to_string(k, i, now, s, sizeof(s))) {
return ssl_printf(ssl, "BADRR\n");
}
return ssl_printf(ssl, "%s", s);
}
/** dump rrset key and data info */
static int
dump_rrset(RES* ssl, struct ub_packed_rrset_key* k,
struct packed_rrset_data* d, time_t now)
{
size_t i;
/* rd lock held by caller */
if(!k || !d) return 1;
if(k->id == 0) return 1; /* deleted */
if(d->ttl < now) return 1; /* expired */
/* meta line */
if(!ssl_printf(ssl, ";rrset%s " ARG_LL "d %u %u %d %d\n",
(k->rk.flags & PACKED_RRSET_NSEC_AT_APEX)?" nsec_apex":"",
(long long)(d->ttl - now),
(unsigned)d->count, (unsigned)d->rrsig_count,
(int)d->trust, (int)d->security
))
return 0;
for(i=0; i<d->count + d->rrsig_count; i++) {
if(!dump_rrset_line(ssl, k, now, i))
return 0;
}
return 1;
}
/** dump lruhash rrset cache */
static int
dump_rrset_lruhash(RES* ssl, struct lruhash* h, time_t now)
{
struct lruhash_entry* e;
/* lruhash already locked by caller */
/* walk in order of lru; best first */
for(e=h->lru_start; e; e = e->lru_next) {
lock_rw_rdlock(&e->lock);
if(!dump_rrset(ssl, (struct ub_packed_rrset_key*)e->key,
(struct packed_rrset_data*)e->data, now)) {
lock_rw_unlock(&e->lock);
return 0;
}
lock_rw_unlock(&e->lock);
}
return 1;
}
/** dump rrset cache */
static int
dump_rrset_cache(RES* ssl, struct worker* worker)
{
struct rrset_cache* r = worker->env.rrset_cache;
size_t slab;
if(!ssl_printf(ssl, "START_RRSET_CACHE\n")) return 0;
for(slab=0; slab<r->table.size; slab++) {
lock_quick_lock(&r->table.array[slab]->lock);
if(!dump_rrset_lruhash(ssl, r->table.array[slab],
*worker->env.now)) {
lock_quick_unlock(&r->table.array[slab]->lock);
return 0;
}
lock_quick_unlock(&r->table.array[slab]->lock);
}
return ssl_printf(ssl, "END_RRSET_CACHE\n");
}
/** dump message to rrset reference */
static int
dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k)
{
char* nm, *tp, *cl;
nm = sldns_wire2str_dname(k->rk.dname, k->rk.dname_len);
tp = sldns_wire2str_type(ntohs(k->rk.type));
cl = sldns_wire2str_class(ntohs(k->rk.rrset_class));
if(!nm || !cl || !tp) {
free(nm);
free(tp);
free(cl);
return ssl_printf(ssl, "BADREF\n");
}
if(!ssl_printf(ssl, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags)) {
free(nm);
free(tp);
free(cl);
return 0;
}
free(nm);
free(tp);
free(cl);
return 1;
}
/** dump message entry */
static int
dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now)
{
size_t i;
char* nm, *tp, *cl;
if(!k || !d) return 1;
if(d->ttl < now) return 1; /* expired */
nm = sldns_wire2str_dname(k->qname, k->qname_len);
tp = sldns_wire2str_type(k->qtype);
cl = sldns_wire2str_class(k->qclass);
if(!nm || !tp || !cl) {
free(nm);
free(tp);
free(cl);
return 1; /* skip this entry */
}
if(!rrset_array_lock(d->ref, d->rrset_count, now)) {
/* rrsets have timed out or do not exist */
free(nm);
free(tp);
free(cl);
return 1; /* skip this entry */
}
/* meta line */
if(!ssl_printf(ssl, "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n",
nm, cl, tp,
(int)d->flags, (int)d->qdcount,
(long long)(d->ttl-now), (int)d->security,
(unsigned)d->an_numrrsets,
(unsigned)d->ns_numrrsets,
(unsigned)d->ar_numrrsets,
(int)d->reason_bogus,
d->reason_bogus_str?d->reason_bogus_str:"")) {
free(nm);
free(tp);
free(cl);
rrset_array_unlock(d->ref, d->rrset_count);
return 0;
}
free(nm);
free(tp);
free(cl);
for(i=0; i<d->rrset_count; i++) {
if(!dump_msg_ref(ssl, d->rrsets[i])) {
rrset_array_unlock(d->ref, d->rrset_count);
return 0;
}
}
rrset_array_unlock(d->ref, d->rrset_count);
return 1;
}
/** copy msg to worker pad */
static int
copy_msg(struct regional* region, struct lruhash_entry* e,
struct query_info** k, struct reply_info** d)
{
struct reply_info* rep = (struct reply_info*)e->data;
if(rep->rrset_count > RR_COUNT_MAX)
return 0; /* to protect against integer overflow */
*d = (struct reply_info*)regional_alloc_init(region, e->data,
sizeof(struct reply_info) +
sizeof(struct rrset_ref) * (rep->rrset_count-1) +
sizeof(struct ub_packed_rrset_key*) * rep->rrset_count);
if(!*d)
return 0;
(*d)->rrsets = (struct ub_packed_rrset_key**)(void *)(
(uint8_t*)(&((*d)->ref[0])) +
sizeof(struct rrset_ref) * rep->rrset_count);
*k = (struct query_info*)regional_alloc_init(region,
e->key, sizeof(struct query_info));
if(!*k)
return 0;
(*k)->qname = regional_alloc_init(region,
(*k)->qname, (*k)->qname_len);
return (*k)->qname != NULL;
}
/** dump lruhash msg cache */
static int
dump_msg_lruhash(RES* ssl, struct worker* worker, struct lruhash* h)
{
struct lruhash_entry* e;
struct query_info* k;
struct reply_info* d;
/* lruhash already locked by caller */
/* walk in order of lru; best first */
for(e=h->lru_start; e; e = e->lru_next) {
regional_free_all(worker->scratchpad);
lock_rw_rdlock(&e->lock);
/* make copy of rrset in worker buffer */
if(!copy_msg(worker->scratchpad, e, &k, &d)) {
lock_rw_unlock(&e->lock);
return 0;
}
lock_rw_unlock(&e->lock);
/* release lock so we can lookup the rrset references
* in the rrset cache */
if(!dump_msg(ssl, k, d, *worker->env.now)) {
return 0;
}
}
return 1;
}
/** dump msg cache */
static int
dump_msg_cache(RES* ssl, struct worker* worker)
{
struct slabhash* sh = worker->env.msg_cache;
size_t slab;
if(!ssl_printf(ssl, "START_MSG_CACHE\n")) return 0;
for(slab=0; slab<sh->size; slab++) {
lock_quick_lock(&sh->array[slab]->lock);
if(!dump_msg_lruhash(ssl, worker, sh->array[slab])) {
lock_quick_unlock(&sh->array[slab]->lock);
return 0;
}
lock_quick_unlock(&sh->array[slab]->lock);
}
return ssl_printf(ssl, "END_MSG_CACHE\n");
}
int
dump_cache(RES* ssl, struct worker* worker)
{
if(!dump_rrset_cache(ssl, worker))
return 0;
if(!dump_msg_cache(ssl, worker))
return 0;
return ssl_printf(ssl, "EOF\n");
}
/** read a line from ssl into buffer */
static int
ssl_read_buf(RES* ssl, sldns_buffer* buf)
{
return ssl_read_line(ssl, (char*)sldns_buffer_begin(buf),
sldns_buffer_capacity(buf));
}
/** check fixed text on line */
static int
read_fixed(RES* ssl, sldns_buffer* buf, const char* str)
{
if(!ssl_read_buf(ssl, buf)) return 0;
return (strcmp((char*)sldns_buffer_begin(buf), str) == 0);
}
/** load an RR into rrset */
static int
load_rr(RES* ssl, sldns_buffer* buf, struct regional* region,
struct ub_packed_rrset_key* rk, struct packed_rrset_data* d,
unsigned int i, int is_rrsig, int* go_on, time_t now)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t rr_len = sizeof(rr), dname_len = 0;
int status;
/* read the line */
if(!ssl_read_buf(ssl, buf))
return 0;
if(strncmp((char*)sldns_buffer_begin(buf), "BADRR\n", 6) == 0) {
*go_on = 0;
return 1;
}
status = sldns_str2wire_rr_buf((char*)sldns_buffer_begin(buf), rr,
&rr_len, &dname_len, 3600, NULL, 0, NULL, 0);
if(status != 0) {
log_warn("error cannot parse rr: %s: %s",
sldns_get_errorstr_parse(status),
(char*)sldns_buffer_begin(buf));
return 0;
}
if(is_rrsig && sldns_wirerr_get_type(rr, rr_len, dname_len)
!= LDNS_RR_TYPE_RRSIG) {
log_warn("error expected rrsig but got %s",
(char*)sldns_buffer_begin(buf));
return 0;
}
/* convert ldns rr into packed_rr */
d->rr_ttl[i] = (time_t)sldns_wirerr_get_ttl(rr, rr_len, dname_len) + now;
sldns_buffer_clear(buf);
d->rr_len[i] = sldns_wirerr_get_rdatalen(rr, rr_len, dname_len)+2;
d->rr_data[i] = (uint8_t*)regional_alloc_init(region,
sldns_wirerr_get_rdatawl(rr, rr_len, dname_len), d->rr_len[i]);
if(!d->rr_data[i]) {
log_warn("error out of memory");
return 0;
}
/* if first entry, fill the key structure */
if(i==0) {
rk->rk.type = htons(sldns_wirerr_get_type(rr, rr_len, dname_len));
rk->rk.rrset_class = htons(sldns_wirerr_get_class(rr, rr_len, dname_len));
rk->rk.dname_len = dname_len;
rk->rk.dname = regional_alloc_init(region, rr, dname_len);
if(!rk->rk.dname) {
log_warn("error out of memory");
return 0;
}
}
return 1;
}
/** move entry into cache */
static int
move_into_cache(struct ub_packed_rrset_key* k,
struct packed_rrset_data* d, struct worker* worker)
{
struct ub_packed_rrset_key* ak;
struct packed_rrset_data* ad;
size_t s, i, num = d->count + d->rrsig_count;
struct rrset_ref ref;
uint8_t* p;
ak = alloc_special_obtain(worker->alloc);
if(!ak) {
log_warn("error out of memory");
return 0;
}
ak->entry.data = NULL;
ak->rk = k->rk;
ak->entry.hash = rrset_key_hash(&k->rk);
ak->rk.dname = (uint8_t*)memdup(k->rk.dname, k->rk.dname_len);
if(!ak->rk.dname) {
log_warn("error out of memory");
ub_packed_rrset_parsedelete(ak, worker->alloc);
return 0;
}
s = sizeof(*ad) + (sizeof(size_t) + sizeof(uint8_t*) +
sizeof(time_t))* num;
for(i=0; i<num; i++)
s += d->rr_len[i];
ad = (struct packed_rrset_data*)malloc(s);
if(!ad) {
log_warn("error out of memory");
ub_packed_rrset_parsedelete(ak, worker->alloc);
return 0;
}
p = (uint8_t*)ad;
memmove(p, d, sizeof(*ad));
p += sizeof(*ad);
memmove(p, &d->rr_len[0], sizeof(size_t)*num);
p += sizeof(size_t)*num;
memmove(p, &d->rr_data[0], sizeof(uint8_t*)*num);
p += sizeof(uint8_t*)*num;
memmove(p, &d->rr_ttl[0], sizeof(time_t)*num);
p += sizeof(time_t)*num;
for(i=0; i<num; i++) {
memmove(p, d->rr_data[i], d->rr_len[i]);
p += d->rr_len[i];
}
packed_rrset_ptr_fixup(ad);
ak->entry.data = ad;
ref.key = ak;
ref.id = ak->id;
(void)rrset_cache_update(worker->env.rrset_cache, &ref,
worker->alloc, *worker->env.now);
return 1;
}
/** load an rrset entry */
static int
load_rrset(RES* ssl, sldns_buffer* buf, struct worker* worker)
{
char* s = (char*)sldns_buffer_begin(buf);
struct regional* region = worker->scratchpad;
struct ub_packed_rrset_key* rk;
struct packed_rrset_data* d;
unsigned int rr_count, rrsig_count, trust, security;
long long ttl;
unsigned int i;
int go_on = 1;
regional_free_all(region);
rk = (struct ub_packed_rrset_key*)regional_alloc_zero(region,
sizeof(*rk));
d = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*d));
if(!rk || !d) {
log_warn("error out of memory");
return 0;
}
if(strncmp(s, ";rrset", 6) != 0) {
log_warn("error expected ';rrset' but got %s", s);
return 0;
}
s += 6;
if(strncmp(s, " nsec_apex", 10) == 0) {
s += 10;
rk->rk.flags |= PACKED_RRSET_NSEC_AT_APEX;
}
if(sscanf(s, " " ARG_LL "d %u %u %u %u", &ttl, &rr_count, &rrsig_count,
&trust, &security) != 5) {
log_warn("error bad rrset spec %s", s);
return 0;
}
if(rr_count == 0 && rrsig_count == 0) {
log_warn("bad rrset without contents");
return 0;
}
if(rr_count > RR_COUNT_MAX || rrsig_count > RR_COUNT_MAX) {
log_warn("bad rrset with too many rrs");
return 0;
}
d->count = (size_t)rr_count;
d->rrsig_count = (size_t)rrsig_count;
d->security = (enum sec_status)security;
d->trust = (enum rrset_trust)trust;
d->ttl = (time_t)ttl + *worker->env.now;
d->rr_len = regional_alloc_zero(region,
sizeof(size_t)*(d->count+d->rrsig_count));
d->rr_ttl = regional_alloc_zero(region,
sizeof(time_t)*(d->count+d->rrsig_count));
d->rr_data = regional_alloc_zero(region,
sizeof(uint8_t*)*(d->count+d->rrsig_count));
if(!d->rr_len || !d->rr_ttl || !d->rr_data) {
log_warn("error out of memory");
return 0;
}
/* read the rr's themselves */
for(i=0; i<rr_count; i++) {
if(!load_rr(ssl, buf, region, rk, d, i, 0,
&go_on, *worker->env.now)) {
log_warn("could not read rr %u", i);
return 0;
}
}
for(i=0; i<rrsig_count; i++) {
if(!load_rr(ssl, buf, region, rk, d, i+rr_count, 1,
&go_on, *worker->env.now)) {
log_warn("could not read rrsig %u", i);
return 0;
}
}
if(!go_on) {
/* skip this entry */
return 1;
}
return move_into_cache(rk, d, worker);
}
/** load rrset cache */
static int
load_rrset_cache(RES* ssl, struct worker* worker)
{
sldns_buffer* buf = worker->env.scratch_buffer;
if(!read_fixed(ssl, buf, "START_RRSET_CACHE")) return 0;
while(ssl_read_buf(ssl, buf) &&
strcmp((char*)sldns_buffer_begin(buf), "END_RRSET_CACHE")!=0) {
if(!load_rrset(ssl, buf, worker))
return 0;
}
return 1;
}
/** read qinfo from next three words */
static char*
load_qinfo(char* str, struct query_info* qinfo, struct regional* region)
{
/* s is part of the buf */
char* s = str;
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t rr_len = sizeof(rr), dname_len = 0;
int status;
/* skip three words */
s = strchr(str, ' ');
if(s) s = strchr(s+1, ' ');
if(s) s = strchr(s+1, ' ');
if(!s) {
log_warn("error line too short, %s", str);
return NULL;
}
s[0] = 0;
s++;
/* parse them */
status = sldns_str2wire_rr_question_buf(str, rr, &rr_len, &dname_len,
NULL, 0, NULL, 0);
if(status != 0) {
log_warn("error cannot parse: %s %s",
sldns_get_errorstr_parse(status), str);
return NULL;
}
qinfo->qtype = sldns_wirerr_get_type(rr, rr_len, dname_len);
qinfo->qclass = sldns_wirerr_get_class(rr, rr_len, dname_len);
qinfo->qname_len = dname_len;
qinfo->qname = (uint8_t*)regional_alloc_init(region, rr, dname_len);
qinfo->local_alias = NULL;
if(!qinfo->qname) {
log_warn("error out of memory");
return NULL;
}
return s;
}
/** load a msg rrset reference */
static int
load_ref(RES* ssl, sldns_buffer* buf, struct worker* worker,
struct regional *region, struct ub_packed_rrset_key** rrset,
int* go_on)
{
char* s = (char*)sldns_buffer_begin(buf);
struct query_info qinfo;
unsigned int flags;
struct ub_packed_rrset_key* k;
/* read line */
if(!ssl_read_buf(ssl, buf))
return 0;
if(strncmp(s, "BADREF", 6) == 0) {
*go_on = 0; /* its bad, skip it and skip message */
return 1;
}
s = load_qinfo(s, &qinfo, region);
if(!s) {
return 0;
}
if(sscanf(s, " %u", &flags) != 1) {
log_warn("error cannot parse flags: %s", s);
return 0;
}
/* lookup in cache */
k = rrset_cache_lookup(worker->env.rrset_cache, qinfo.qname,
qinfo.qname_len, qinfo.qtype, qinfo.qclass,
(uint32_t)flags, *worker->env.now, 0);
if(!k) {
/* not found or expired */
*go_on = 0;
return 1;
}
/* store in result */
*rrset = packed_rrset_copy_region(k, region, *worker->env.now);
lock_rw_unlock(&k->entry.lock);
return (*rrset != NULL);
}
/** load a msg entry */
static int
load_msg(RES* ssl, sldns_buffer* buf, struct worker* worker)
{
struct regional* region = worker->scratchpad;
struct query_info qinf;
struct reply_info rep;
char* s = (char*)sldns_buffer_begin(buf);
unsigned int flags, qdcount, security, an, ns, ar;
long long ttl;
size_t i;
int go_on = 1;
int ede;
int consumed = 0;
char* ede_str = NULL;
regional_free_all(region);
if(strncmp(s, "msg ", 4) != 0) {
log_warn("error expected msg but got %s", s);
return 0;
}
s += 4;
s = load_qinfo(s, &qinf, region);
if(!s) {
return 0;
}
/* read remainder of line */
/* note the last space before any possible EDE text */
if(sscanf(s, " %u %u " ARG_LL "d %u %u %u %u %d %n", &flags, &qdcount, &ttl,
&security, &an, &ns, &ar, &ede, &consumed) != 8) {
log_warn("error cannot parse numbers: %s", s);
return 0;
}
/* there may be EDE text after the numbers */
if(consumed > 0 && (size_t)consumed < strlen(s))
ede_str = s + consumed;
memset(&rep, 0, sizeof(rep));
rep.flags = (uint16_t)flags;
rep.qdcount = (uint16_t)qdcount;
rep.ttl = (time_t)ttl;
rep.prefetch_ttl = PREFETCH_TTL_CALC(rep.ttl);
rep.serve_expired_ttl = rep.ttl + SERVE_EXPIRED_TTL;
rep.security = (enum sec_status)security;
if(an > RR_COUNT_MAX || ns > RR_COUNT_MAX || ar > RR_COUNT_MAX) {
log_warn("error too many rrsets");
return 0; /* protect against integer overflow in alloc */
}
rep.an_numrrsets = (size_t)an;
rep.ns_numrrsets = (size_t)ns;
rep.ar_numrrsets = (size_t)ar;
rep.rrset_count = (size_t)an+(size_t)ns+(size_t)ar;
rep.reason_bogus = (sldns_ede_code)ede;
rep.reason_bogus_str = ede_str?(char*)regional_strdup(region, ede_str):NULL;
rep.rrsets = (struct ub_packed_rrset_key**)regional_alloc_zero(
region, sizeof(struct ub_packed_rrset_key*)*rep.rrset_count);
/* fill repinfo with references */
for(i=0; i<rep.rrset_count; i++) {
if(!load_ref(ssl, buf, worker, region, &rep.rrsets[i],
&go_on)) {
return 0;
}
}
if(!go_on)
return 1; /* skip this one, not all references satisfied */
if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL, flags,
*worker->env.now)) {
log_warn("error out of memory");
return 0;
}
return 1;
}
/** load msg cache */
static int
load_msg_cache(RES* ssl, struct worker* worker)
{
sldns_buffer* buf = worker->env.scratch_buffer;
if(!read_fixed(ssl, buf, "START_MSG_CACHE")) return 0;
while(ssl_read_buf(ssl, buf) &&
strcmp((char*)sldns_buffer_begin(buf), "END_MSG_CACHE")!=0) {
if(!load_msg(ssl, buf, worker))
return 0;
}
return 1;
}
int
load_cache(RES* ssl, struct worker* worker)
{
if(!load_rrset_cache(ssl, worker))
return 0;
if(!load_msg_cache(ssl, worker))
return 0;
return read_fixed(ssl, worker->env.scratch_buffer, "EOF");
}
/** print details on a delegation point */
static void
print_dp_details(RES* ssl, struct worker* worker, struct delegpt* dp)
{
char buf[257];
struct delegpt_addr* a;
int lame, dlame, rlame, rto, edns_vs, to, delay,
tA = 0, tAAAA = 0, tother = 0;
long long entry_ttl;
struct rtt_info ri;
uint8_t edns_lame_known;
for(a = dp->target_list; a; a = a->next_target) {
addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf));
if(!ssl_printf(ssl, "%-16s\t", buf))
return;
if(a->bogus) {
if(!ssl_printf(ssl, "Address is BOGUS. "))
return;
}
/* lookup in infra cache */
delay=0;
entry_ttl = infra_get_host_rto(worker->env.infra_cache,
&a->addr, a->addrlen, dp->name, dp->namelen,
&ri, &delay, *worker->env.now, &tA, &tAAAA, &tother);
if(entry_ttl == -2 && ri.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
if(!ssl_printf(ssl, "expired, rto %d msec, tA %d "
"tAAAA %d tother %d.\n", ri.rto, tA, tAAAA,
tother))
return;
continue;
}
if(entry_ttl == -1 || entry_ttl == -2) {
if(!ssl_printf(ssl, "not in infra cache.\n"))
return;
continue; /* skip stuff not in infra cache */
}
/* uses type_A because most often looked up, but other
* lameness won't be reported then */
if(!infra_get_lame_rtt(worker->env.infra_cache,
&a->addr, a->addrlen, dp->name, dp->namelen,
LDNS_RR_TYPE_A, &lame, &dlame, &rlame, &rto,
*worker->env.now)) {
if(!ssl_printf(ssl, "not in infra cache.\n"))
return;
continue; /* skip stuff not in infra cache */
}
if(!ssl_printf(ssl, "%s%s%s%srto %d msec, ttl " ARG_LL "d, "
"ping %d var %d rtt %d, tA %d, tAAAA %d, tother %d",
lame?"LAME ":"", dlame?"NoDNSSEC ":"",
a->lame?"AddrWasParentSide ":"",
rlame?"NoAuthButRecursive ":"", rto, entry_ttl,
ri.srtt, ri.rttvar, rtt_notimeout(&ri),
tA, tAAAA, tother))
return;
if(delay)
if(!ssl_printf(ssl, ", probedelay %d", delay))
return;
if(infra_host(worker->env.infra_cache, &a->addr, a->addrlen,
dp->name, dp->namelen, *worker->env.now, &edns_vs,
&edns_lame_known, &to)) {
if(edns_vs == -1) {
if(!ssl_printf(ssl, ", noEDNS%s.",
edns_lame_known?" probed":" assumed"))
return;
} else {
if(!ssl_printf(ssl, ", EDNS %d%s.", edns_vs,
edns_lame_known?" probed":" assumed"))
return;
}
}
if(!ssl_printf(ssl, "\n"))
return;
}
}
/** print main dp info */
static void
print_dp_main(RES* ssl, struct delegpt* dp, struct dns_msg* msg)
{
size_t i, n_ns, n_miss, n_addr, n_res, n_avail;
/* print the dp */
if(msg)
for(i=0; i<msg->rep->rrset_count; i++) {
struct ub_packed_rrset_key* k = msg->rep->rrsets[i];
struct packed_rrset_data* d =
(struct packed_rrset_data*)k->entry.data;
if(d->security == sec_status_bogus) {
if(!ssl_printf(ssl, "Address is BOGUS:\n"))
return;
}
if(!dump_rrset(ssl, k, d, 0))
return;
}
delegpt_count_ns(dp, &n_ns, &n_miss);
delegpt_count_addr(dp, &n_addr, &n_res, &n_avail);
/* since dp has not been used by iterator, all are available*/
if(!ssl_printf(ssl, "Delegation with %d names, of which %d "
"can be examined to query further addresses.\n"
"%sIt provides %d IP addresses.\n",
(int)n_ns, (int)n_miss, (dp->bogus?"It is BOGUS. ":""),
(int)n_addr))
return;
}
int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm,
size_t nmlen, int ATTR_UNUSED(nmlabs))
{
/* deep links into the iterator module */
struct delegpt* dp;
struct dns_msg* msg;
struct regional* region = worker->scratchpad;
char b[260];
struct query_info qinfo;
struct iter_hints_stub* stub;
regional_free_all(region);
qinfo.qname = nm;
qinfo.qname_len = nmlen;
qinfo.qtype = LDNS_RR_TYPE_A;
qinfo.qclass = LDNS_RR_CLASS_IN;
qinfo.local_alias = NULL;
dname_str(nm, b);
if(!ssl_printf(ssl, "The following name servers are used for lookup "
"of %s\n", b))
return 0;
dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass);
if(dp) {
if(!ssl_printf(ssl, "forwarding request:\n"))
return 0;
print_dp_main(ssl, dp, NULL);
print_dp_details(ssl, worker, dp);
return 1;
}
while(1) {
dp = dns_cache_find_delegation(&worker->env, nm, nmlen,
qinfo.qtype, qinfo.qclass, region, &msg,
*worker->env.now, 0, NULL, 0);
if(!dp) {
return ssl_printf(ssl, "no delegation from "
"cache; goes to configured roots\n");
}
/* go up? */
if(iter_dp_is_useless(&qinfo, BIT_RD, dp,
(worker->env.cfg->do_ip4 && worker->back->num_ip4 != 0),
(worker->env.cfg->do_ip6 && worker->back->num_ip6 != 0),
worker->env.cfg->do_nat64)) {
print_dp_main(ssl, dp, msg);
print_dp_details(ssl, worker, dp);
if(!ssl_printf(ssl, "cache delegation was "
"useless (no IP addresses)\n"))
return 0;
if(dname_is_root(nm)) {
/* goes to root config */
return ssl_printf(ssl, "no delegation from "
"cache; goes to configured roots\n");
} else {
/* useless, goes up */
nm = dp->name;
nmlen = dp->namelen;
dname_remove_label(&nm, &nmlen);
dname_str(nm, b);
if(!ssl_printf(ssl, "going up, lookup %s\n", b))
return 0;
continue;
}
}
stub = hints_lookup_stub(worker->env.hints, nm, qinfo.qclass,
dp);
if(stub) {
if(stub->noprime) {
if(!ssl_printf(ssl, "The noprime stub servers "
"are used:\n"))
return 0;
} else {
if(!ssl_printf(ssl, "The stub is primed "
"with servers:\n"))
return 0;
}
print_dp_main(ssl, stub->dp, NULL);
print_dp_details(ssl, worker, stub->dp);
} else {
print_dp_main(ssl, dp, msg);
print_dp_details(ssl, worker, dp);
}
break;
}
return 1;
}
@@ -1,108 +0,0 @@
/*
* daemon/cachedump.h - dump the cache to text format.
*
* Copyright (c) 2008, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains functions to read and write the cache(s)
* to text format.
*
* The format of the file is as follows:
* [RRset cache]
* [Message cache]
* EOF -- fixed string "EOF" before end of the file.
*
* The RRset cache is:
* START_RRSET_CACHE
* [rrset]*
* END_RRSET_CACHE
*
* rrset is:
* ;rrset [nsec_apex] TTL rr_count rrsig_count trust security
* resource records, one per line, in zonefile format
* rrsig records, one per line, in zonefile format
* If the text conversion fails, BADRR is printed on the line.
*
* The Message cache is:
* START_MSG_CACHE
* [msg]*
* END_MSG_CACHE
*
* msg is:
* msg name class type flags qdcount ttl security an ns ar
* list of rrset references, one per line. If conversion fails, BADREF
* reference is:
* name class type flags
*
* Expired cache entries are not printed.
*/
#ifndef DAEMON_DUMPCACHE_H
#define DAEMON_DUMPCACHE_H
struct worker;
#include "daemon/remote.h"
/**
* Dump cache(s) to text
* @param ssl: to print to
* @param worker: worker that is available (buffers, etc) and has
* ptrs to the caches.
* @return false on ssl print error.
*/
int dump_cache(RES* ssl, struct worker* worker);
/**
* Load cache(s) from text
* @param ssl: to read from
* @param worker: worker that is available (buffers, etc) and has
* ptrs to the caches.
* @return false on ssl error.
*/
int load_cache(RES* ssl, struct worker* worker);
/**
* Print the delegation used to lookup for this name.
* @param ssl: to read from
* @param worker: worker that is available (buffers, etc) and has
* ptrs to the caches.
* @param nm: name to lookup
* @param nmlen: length of name.
* @param nmlabs: labels in name.
* @return false on ssl error.
*/
int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm,
size_t nmlen, int nmlabs);
#endif /* DAEMON_DUMPCACHE_H */
File diff suppressed because it is too large Load Diff
@@ -1,192 +0,0 @@
/*
* daemon/daemon.h - collection of workers that handles requests.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* The daemon consists of global settings and a number of workers.
*/
#ifndef DAEMON_H
#define DAEMON_H
#include "util/locks.h"
#include "util/alloc.h"
#include "services/modstack.h"
struct config_file;
struct worker;
struct listen_port;
struct slabhash;
struct module_env;
struct rrset_cache;
struct acl_list;
struct local_zones;
struct views;
struct ub_randstate;
struct daemon_remote;
struct respip_set;
struct shm_main_info;
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
struct dt_env;
#endif
#include "dnscrypt/dnscrypt_config.h"
#ifdef USE_DNSCRYPT
struct dnsc_env;
#endif
/**
* Structure holding worker list.
* Holds globally visible information.
*/
struct daemon {
/** The config settings */
struct config_file* cfg;
/** the chroot dir in use, NULL if none */
char* chroot;
/** pidfile that is used */
char* pidfile;
/** port number that has ports opened. */
int listening_port;
/** array of listening ports, opened. Listening ports per worker,
* or just one element[0] shared by the worker threads. */
struct listen_port** ports;
/** size of ports array */
size_t num_ports;
/** reuseport is enabled if true */
int reuseport;
/** port number for remote that has ports opened. */
int rc_port;
/** listening ports for remote control */
struct listen_port* rc_ports;
/** remote control connections management (for first worker) */
struct daemon_remote* rc;
/** ssl context for listening to dnstcp over ssl, and connecting ssl */
void* listen_sslctx, *connect_sslctx;
/** num threads allocated */
int num;
/** num threads allocated in the previous config or 0 at first */
int old_num;
/** the worker entries */
struct worker** workers;
/** per-worker allocation cache */
struct alloc_cache **worker_allocs;
/** do we need to exit unbound (or is it only a reload?) */
int need_to_exit;
/** master random table ; used for port div between threads on reload*/
struct ub_randstate* rand;
/** master allocation cache */
struct alloc_cache superalloc;
/** the module environment master value, copied and changed by threads*/
struct module_env* env;
/** stack of module callbacks */
struct module_stack mods;
/** access control, which client IPs are allowed to connect */
struct acl_list* acl;
/** access control, which interfaces are allowed to connect */
struct acl_list* acl_interface;
/** TCP connection limit, limit connections from client IPs */
struct tcl_list* tcl;
/** local authority zones */
struct local_zones* local_zones;
/** last time of statistics printout */
struct timeval time_last_stat;
/** time when daemon started */
struct timeval time_boot;
/** views structure containing view tree */
struct views* views;
#ifdef USE_DNSTAP
/** the dnstap environment master value, copied and changed by threads*/
struct dt_env* dtenv;
#endif
struct shm_main_info* shm_info;
/** response-ip set with associated actions and tags. */
struct respip_set* respip_set;
/** some response-ip tags or actions are configured if true */
int use_response_ip;
/** some RPZ policies are configured */
int use_rpz;
#ifdef USE_DNSCRYPT
/** the dnscrypt environment */
struct dnsc_env* dnscenv;
#endif
/** reuse existing cache on reload if other conditions allow it. */
int reuse_cache;
};
/**
* Initialize daemon structure.
* @return: The daemon structure, or NULL on error.
*/
struct daemon* daemon_init(void);
/**
* Open shared listening ports (if needed).
* The cfg member pointer must have been set for the daemon.
* @param daemon: the daemon.
* @return: false on error.
*/
int daemon_open_shared_ports(struct daemon* daemon);
/**
* Fork workers and start service.
* When the routine exits, it is no longer forked.
* @param daemon: the daemon.
*/
void daemon_fork(struct daemon* daemon);
/**
* Close off the worker thread information.
* Bring the daemon back into state ready for daemon_fork again.
* @param daemon: the daemon.
*/
void daemon_cleanup(struct daemon* daemon);
/**
* Delete workers, close listening ports.
* @param daemon: the daemon.
*/
void daemon_delete(struct daemon* daemon);
/**
* Apply config settings.
* @param daemon: the daemon.
* @param cfg: new config settings.
*/
void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg);
#endif /* DAEMON_H */
File diff suppressed because it is too large Load Diff
@@ -1,206 +0,0 @@
/*
* daemon/remote.h - remote control for the unbound daemon.
*
* Copyright (c) 2008, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains the remote control functionality for the daemon.
* The remote control can be performed using either the commandline
* unbound-control tool, or a SSLv3/TLS capable web browser.
* The channel is secured using SSLv3 or TLSv1, and certificates.
* Both the server and the client(control tool) have their own keys.
*/
#ifndef DAEMON_REMOTE_H
#define DAEMON_REMOTE_H
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
struct config_file;
struct listen_list;
struct listen_port;
struct worker;
struct comm_reply;
struct comm_point;
struct daemon_remote;
/** number of milliseconds timeout on incoming remote control handshake */
#define REMOTE_CONTROL_TCP_TIMEOUT 120000
/**
* a busy control command connection, SSL state
*/
struct rc_state {
/** the next item in list */
struct rc_state* next;
/** the commpoint */
struct comm_point* c;
/** in the handshake part */
enum { rc_none, rc_hs_read, rc_hs_write } shake_state;
#ifdef HAVE_SSL
/** the ssl state */
SSL* ssl;
#endif
/** file descriptor */
int fd;
/** the rc this is part of */
struct daemon_remote* rc;
};
/**
* The remote control tool state.
* The state is only created for the first thread, other threads
* are called from this thread. Only the first threads listens to
* the control port. The other threads do not, but are called on the
* command channel(pipe) from the first thread.
*/
struct daemon_remote {
/** the worker for this remote control */
struct worker* worker;
/** commpoints for accepting remote control connections */
struct listen_list* accept_list;
/* if certificates are used */
int use_cert;
/** number of active commpoints that are handling remote control */
int active;
/** max active commpoints */
int max_active;
/** current commpoints busy; should be a short list, malloced */
struct rc_state* busy_list;
#ifdef HAVE_SSL
/** the SSL context for creating new SSL streams */
SSL_CTX* ctx;
#endif
};
/**
* Connection to print to, either SSL or plain over fd
*/
struct remote_stream {
#ifdef HAVE_SSL
/** SSL structure, nonNULL if using SSL */
SSL* ssl;
#endif
/** file descriptor for plain transfer */
int fd;
};
typedef struct remote_stream RES;
/**
* Create new remote control state for the daemon.
* @param cfg: config file with key file settings.
* @return new state, or NULL on failure.
*/
struct daemon_remote* daemon_remote_create(struct config_file* cfg);
/**
* remote control state to delete.
* @param rc: state to delete.
*/
void daemon_remote_delete(struct daemon_remote* rc);
/**
* remote control state to clear up. Busy and accept points are closed.
* Does not delete the rc itself, or the ssl context (with its keys).
* @param rc: state to clear.
*/
void daemon_remote_clear(struct daemon_remote* rc);
/**
* Open and create listening ports for remote control.
* @param cfg: config options.
* @return list of ports or NULL on failure.
* can be freed with listening_ports_free().
*/
struct listen_port* daemon_remote_open_ports(struct config_file* cfg);
/**
* Setup comm points for accepting remote control connections.
* @param rc: state
* @param ports: already opened ports.
* @param worker: worker with communication base. and links to command channels.
* @return false on error.
*/
int daemon_remote_open_accept(struct daemon_remote* rc,
struct listen_port* ports, struct worker* worker);
/**
* Stop accept handlers for TCP (until enabled again)
* @param rc: state
*/
void daemon_remote_stop_accept(struct daemon_remote* rc);
/**
* Stop accept handlers for TCP (until enabled again)
* @param rc: state
*/
void daemon_remote_start_accept(struct daemon_remote* rc);
/**
* Handle nonthreaded remote cmd execution.
* @param worker: this worker (the remote worker).
*/
void daemon_remote_exec(struct worker* worker);
#ifdef HAVE_SSL
/**
* Print fixed line of text over ssl connection in blocking mode
* @param ssl: print to
* @param text: the text.
* @return false on connection failure.
*/
int ssl_print_text(RES* ssl, const char* text);
/**
* printf style printing to the ssl connection
* @param ssl: the RES connection to print to. Blocking.
* @param format: printf style format string.
* @return success or false on a network failure.
*/
int ssl_printf(RES* ssl, const char* format, ...)
ATTR_FORMAT(printf, 2, 3);
/**
* Read until \n is encountered
* If stream signals EOF, the string up to then is returned (without \n).
* @param ssl: the RES connection to read from. blocking.
* @param buf: buffer to read to.
* @param max: size of buffer.
* @return false on connection failure.
*/
int ssl_read_line(RES* ssl, char* buf, size_t max);
#endif /* HAVE_SSL */
#endif /* DAEMON_REMOTE_H */
@@ -1,586 +0,0 @@
/*
* daemon/stats.c - collect runtime performance indicators.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file describes the data structure used to collect runtime performance
* numbers. These 'statistics' may be of interest to the operator.
*/
#include "config.h"
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include "daemon/stats.h"
#include "daemon/worker.h"
#include "daemon/daemon.h"
#include "services/mesh.h"
#include "services/outside_network.h"
#include "services/listen_dnsport.h"
#include "util/config_file.h"
#include "util/tube.h"
#include "util/timehist.h"
#include "util/net_help.h"
#include "validator/validator.h"
#include "iterator/iterator.h"
#include "sldns/sbuffer.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "services/authzone.h"
#include "validator/val_kcache.h"
#include "validator/val_neg.h"
#ifdef CLIENT_SUBNET
#include "edns-subnet/subnetmod.h"
#endif
#ifdef HAVE_SSL
#include <openssl/ssl.h>
#endif
/** How long to wait for threads to transmit statistics, in msec. */
#define STATS_THREAD_WAIT 60000
/** add timers and the values do not overflow or become negative */
static void
stats_timeval_add(long long* d_sec, long long* d_usec, long long add_sec, long long add_usec)
{
#ifndef S_SPLINT_S
(*d_sec) += add_sec;
(*d_usec) += add_usec;
if((*d_usec) >= 1000000) {
(*d_usec) -= 1000000;
(*d_sec)++;
}
#endif
}
void server_stats_init(struct ub_server_stats* stats, struct config_file* cfg)
{
memset(stats, 0, sizeof(*stats));
stats->extended = cfg->stat_extended;
}
void server_stats_querymiss(struct ub_server_stats* stats, struct worker* worker)
{
stats->num_queries_missed_cache++;
stats->sum_query_list_size += worker->env.mesh->all.count;
if((long long)worker->env.mesh->all.count > stats->max_query_list_size)
stats->max_query_list_size = (long long)worker->env.mesh->all.count;
}
void server_stats_prefetch(struct ub_server_stats* stats, struct worker* worker)
{
stats->num_queries_prefetch++;
/* changes the query list size so account that, like a querymiss */
stats->sum_query_list_size += worker->env.mesh->all.count;
if((long long)worker->env.mesh->all.count > stats->max_query_list_size)
stats->max_query_list_size = (long long)worker->env.mesh->all.count;
}
void server_stats_log(struct ub_server_stats* stats, struct worker* worker,
int threadnum)
{
log_info("server stats for thread %d: %u queries, "
"%u answers from cache, %u recursions, %u prefetch, %u rejected by "
"ip ratelimiting",
threadnum, (unsigned)stats->num_queries,
(unsigned)(stats->num_queries -
stats->num_queries_missed_cache),
(unsigned)stats->num_queries_missed_cache,
(unsigned)stats->num_queries_prefetch,
(unsigned)stats->num_queries_ip_ratelimited);
log_info("server stats for thread %d: requestlist max %u avg %g "
"exceeded %u jostled %u", threadnum,
(unsigned)stats->max_query_list_size,
(stats->num_queries_missed_cache+stats->num_queries_prefetch)?
(double)stats->sum_query_list_size/
(double)(stats->num_queries_missed_cache+
stats->num_queries_prefetch) : 0.0,
(unsigned)worker->env.mesh->stats_dropped,
(unsigned)worker->env.mesh->stats_jostled);
}
#ifdef CLIENT_SUBNET
/** Set the EDNS Subnet stats. */
static void
set_subnet_stats(struct worker* worker, struct ub_server_stats* svr,
int reset)
{
int m = modstack_find(&worker->env.mesh->mods, "subnetcache");
struct subnet_env* sne;
if(m == -1)
return;
sne = (struct subnet_env*)worker->env.modinfo[m];
if(reset && !worker->env.cfg->stat_cumulative) {
lock_rw_wrlock(&sne->biglock);
} else {
lock_rw_rdlock(&sne->biglock);
}
svr->num_query_subnet = (long long)(sne->num_msg_nocache + sne->num_msg_cache);
svr->num_query_subnet_cache = (long long)sne->num_msg_cache;
if(reset && !worker->env.cfg->stat_cumulative) {
sne->num_msg_cache = 0;
sne->num_msg_nocache = 0;
}
lock_rw_unlock(&sne->biglock);
}
#endif /* CLIENT_SUBNET */
/** Set the neg cache stats. */
static void
set_neg_cache_stats(struct worker* worker, struct ub_server_stats* svr,
int reset)
{
int m = modstack_find(&worker->env.mesh->mods, "validator");
struct val_env* ve;
struct val_neg_cache* neg;
if(m == -1)
return;
ve = (struct val_env*)worker->env.modinfo[m];
if(!ve->neg_cache)
return;
neg = ve->neg_cache;
lock_basic_lock(&neg->lock);
svr->num_neg_cache_noerror = (long long)neg->num_neg_cache_noerror;
svr->num_neg_cache_nxdomain = (long long)neg->num_neg_cache_nxdomain;
if(reset && !worker->env.cfg->stat_cumulative) {
neg->num_neg_cache_noerror = 0;
neg->num_neg_cache_nxdomain = 0;
}
lock_basic_unlock(&neg->lock);
}
/** get rrsets bogus number from validator */
static size_t
get_rrset_bogus(struct worker* worker, int reset)
{
int m = modstack_find(&worker->env.mesh->mods, "validator");
struct val_env* ve;
size_t r;
if(m == -1)
return 0;
ve = (struct val_env*)worker->env.modinfo[m];
lock_basic_lock(&ve->bogus_lock);
r = ve->num_rrset_bogus;
if(reset && !worker->env.cfg->stat_cumulative)
ve->num_rrset_bogus = 0;
lock_basic_unlock(&ve->bogus_lock);
return r;
}
/** get number of ratelimited queries from iterator */
static size_t
get_queries_ratelimit(struct worker* worker, int reset)
{
int m = modstack_find(&worker->env.mesh->mods, "iterator");
struct iter_env* ie;
size_t r;
if(m == -1)
return 0;
ie = (struct iter_env*)worker->env.modinfo[m];
lock_basic_lock(&ie->queries_ratelimit_lock);
r = ie->num_queries_ratelimited;
if(reset && !worker->env.cfg->stat_cumulative)
ie->num_queries_ratelimited = 0;
lock_basic_unlock(&ie->queries_ratelimit_lock);
return r;
}
#ifdef USE_DNSCRYPT
/** get the number of shared secret cache miss */
static size_t
get_dnscrypt_cache_miss(struct worker* worker, int reset)
{
size_t r;
struct dnsc_env* de = worker->daemon->dnscenv;
if(!de) return 0;
lock_basic_lock(&de->shared_secrets_cache_lock);
r = de->num_query_dnscrypt_secret_missed_cache;
if(reset && !worker->env.cfg->stat_cumulative)
de->num_query_dnscrypt_secret_missed_cache = 0;
lock_basic_unlock(&de->shared_secrets_cache_lock);
return r;
}
/** get the number of replayed queries */
static size_t
get_dnscrypt_replay(struct worker* worker, int reset)
{
size_t r;
struct dnsc_env* de = worker->daemon->dnscenv;
lock_basic_lock(&de->nonces_cache_lock);
r = de->num_query_dnscrypt_replay;
if(reset && !worker->env.cfg->stat_cumulative)
de->num_query_dnscrypt_replay = 0;
lock_basic_unlock(&de->nonces_cache_lock);
return r;
}
#endif /* USE_DNSCRYPT */
void
server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
{
int i;
struct listen_list* lp;
s->svr = worker->stats;
s->mesh_num_states = (long long)worker->env.mesh->all.count;
s->mesh_num_reply_states = (long long)worker->env.mesh->num_reply_states;
s->mesh_jostled = (long long)worker->env.mesh->stats_jostled;
s->mesh_dropped = (long long)worker->env.mesh->stats_dropped;
s->mesh_replies_sent = (long long)worker->env.mesh->replies_sent;
s->mesh_replies_sum_wait_sec = (long long)worker->env.mesh->replies_sum_wait.tv_sec;
s->mesh_replies_sum_wait_usec = (long long)worker->env.mesh->replies_sum_wait.tv_usec;
s->mesh_time_median = timehist_quartile(worker->env.mesh->histogram,
0.50);
/* add in the values from the mesh */
s->svr.ans_secure += (long long)worker->env.mesh->ans_secure;
s->svr.ans_bogus += (long long)worker->env.mesh->ans_bogus;
s->svr.ans_rcode_nodata += (long long)worker->env.mesh->ans_nodata;
s->svr.ans_expired += (long long)worker->env.mesh->ans_expired;
for(i=0; i<UB_STATS_RCODE_NUM; i++)
s->svr.ans_rcode[i] += (long long)worker->env.mesh->ans_rcode[i];
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++)
s->svr.rpz_action[i] += (long long)worker->env.mesh->rpz_action[i];
timehist_export(worker->env.mesh->histogram, s->svr.hist,
NUM_BUCKETS_HIST);
/* values from outside network */
s->svr.unwanted_replies = (long long)worker->back->unwanted_replies;
s->svr.qtcp_outgoing = (long long)worker->back->num_tcp_outgoing;
s->svr.qudp_outgoing = (long long)worker->back->num_udp_outgoing;
/* get and reset validator rrset bogus number */
s->svr.rrset_bogus = (long long)get_rrset_bogus(worker, reset);
/* get and reset iterator query ratelimit number */
s->svr.queries_ratelimited = (long long)get_queries_ratelimit(worker, reset);
/* get cache sizes */
get_slabhash_stats(worker->env.msg_cache,
&s->svr.msg_cache_count, &s->svr.msg_cache_max_collisions);
get_slabhash_stats(&worker->env.rrset_cache->table,
&s->svr.rrset_cache_count, &s->svr.rrset_cache_max_collisions);
s->svr.infra_cache_count = (long long)count_slabhash_entries(worker->env.infra_cache->hosts);
if(worker->env.key_cache)
s->svr.key_cache_count = (long long)count_slabhash_entries(worker->env.key_cache->slab);
else s->svr.key_cache_count = 0;
#ifdef USE_DNSCRYPT
if(worker->daemon->dnscenv) {
s->svr.num_query_dnscrypt_secret_missed_cache =
(long long)get_dnscrypt_cache_miss(worker, reset);
s->svr.shared_secret_cache_count = (long long)count_slabhash_entries(
worker->daemon->dnscenv->shared_secrets_cache);
s->svr.nonce_cache_count = (long long)count_slabhash_entries(
worker->daemon->dnscenv->nonces_cache);
s->svr.num_query_dnscrypt_replay =
(long long)get_dnscrypt_replay(worker, reset);
} else {
s->svr.num_query_dnscrypt_secret_missed_cache = 0;
s->svr.shared_secret_cache_count = 0;
s->svr.nonce_cache_count = 0;
s->svr.num_query_dnscrypt_replay = 0;
}
#else
s->svr.num_query_dnscrypt_secret_missed_cache = 0;
s->svr.shared_secret_cache_count = 0;
s->svr.nonce_cache_count = 0;
s->svr.num_query_dnscrypt_replay = 0;
#endif /* USE_DNSCRYPT */
if(worker->env.auth_zones) {
if(reset && !worker->env.cfg->stat_cumulative) {
lock_rw_wrlock(&worker->env.auth_zones->lock);
} else {
lock_rw_rdlock(&worker->env.auth_zones->lock);
}
s->svr.num_query_authzone_up = (long long)worker->env.
auth_zones->num_query_up;
s->svr.num_query_authzone_down = (long long)worker->env.
auth_zones->num_query_down;
if(reset && !worker->env.cfg->stat_cumulative) {
worker->env.auth_zones->num_query_up = 0;
worker->env.auth_zones->num_query_down = 0;
}
lock_rw_unlock(&worker->env.auth_zones->lock);
}
s->svr.mem_stream_wait =
(long long)tcp_req_info_get_stream_buffer_size();
s->svr.mem_http2_query_buffer =
(long long)http2_get_query_buffer_size();
s->svr.mem_http2_response_buffer =
(long long)http2_get_response_buffer_size();
/* Set neg cache usage numbers */
set_neg_cache_stats(worker, &s->svr, reset);
#ifdef CLIENT_SUBNET
/* EDNS Subnet usage numbers */
set_subnet_stats(worker, &s->svr, reset);
#else
s->svr.num_query_subnet = 0;
s->svr.num_query_subnet_cache = 0;
#endif
#ifdef USE_CACHEDB
s->svr.num_query_cachedb = (long long)worker->env.mesh->ans_cachedb;
#else
s->svr.num_query_cachedb = 0;
#endif
/* get tcp accept usage */
s->svr.tcp_accept_usage = 0;
for(lp = worker->front->cps; lp; lp = lp->next) {
if(lp->com->type == comm_tcp_accept)
s->svr.tcp_accept_usage += (long long)lp->com->cur_tcp_count;
}
if(reset && !worker->env.cfg->stat_cumulative) {
worker_stats_clear(worker);
}
}
void server_stats_obtain(struct worker* worker, struct worker* who,
struct ub_stats_info* s, int reset)
{
uint8_t *reply = NULL;
uint32_t len = 0;
if(worker == who) {
/* just fill it in */
server_stats_compile(worker, s, reset);
return;
}
/* communicate over tube */
verbose(VERB_ALGO, "write stats cmd");
if(reset)
worker_send_cmd(who, worker_cmd_stats);
else worker_send_cmd(who, worker_cmd_stats_noreset);
verbose(VERB_ALGO, "wait for stats reply");
if(tube_wait_timeout(worker->cmd, STATS_THREAD_WAIT) == 0) {
verbose(VERB_OPS, "no response from thread %d"
#ifdef HAVE_GETTID
" LWP %u"
#endif
#if defined(HAVE_PTHREAD) && defined(SIZEOF_PTHREAD_T) && defined(SIZEOF_UNSIGNED_LONG)
# if SIZEOF_PTHREAD_T == SIZEOF_UNSIGNED_LONG
" pthread 0x%lx"
# endif
#endif
,
who->thread_num
#ifdef HAVE_GETTID
, (unsigned)who->thread_tid
#endif
#if defined(HAVE_PTHREAD) && defined(SIZEOF_PTHREAD_T) && defined(SIZEOF_UNSIGNED_LONG)
# if SIZEOF_PTHREAD_T == SIZEOF_UNSIGNED_LONG
, (unsigned long)*((unsigned long*)&who->thr_id)
# endif
#endif
);
}
if(!tube_read_msg(worker->cmd, &reply, &len, 0))
fatal_exit("failed to read stats over cmd channel");
if(len != (uint32_t)sizeof(*s))
fatal_exit("stats on cmd channel wrong length %d %d",
(int)len, (int)sizeof(*s));
memcpy(s, reply, (size_t)len);
free(reply);
}
void server_stats_reply(struct worker* worker, int reset)
{
struct ub_stats_info s;
server_stats_compile(worker, &s, reset);
verbose(VERB_ALGO, "write stats replymsg");
if(!tube_write_msg(worker->daemon->workers[0]->cmd,
(uint8_t*)&s, sizeof(s), 0))
fatal_exit("could not write stat values over cmd channel");
}
void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
{
total->svr.num_queries += a->svr.num_queries;
total->svr.num_queries_ip_ratelimited += a->svr.num_queries_ip_ratelimited;
total->svr.num_queries_cookie_valid += a->svr.num_queries_cookie_valid;
total->svr.num_queries_cookie_client += a->svr.num_queries_cookie_client;
total->svr.num_queries_cookie_invalid += a->svr.num_queries_cookie_invalid;
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
total->svr.num_queries_timed_out += a->svr.num_queries_timed_out;
if (total->svr.max_query_time_us < a->svr.max_query_time_us)
total->svr.max_query_time_us = a->svr.max_query_time_us;
total->svr.sum_query_list_size += a->svr.sum_query_list_size;
total->svr.ans_expired += a->svr.ans_expired;
#ifdef USE_DNSCRYPT
total->svr.num_query_dnscrypt_crypted += a->svr.num_query_dnscrypt_crypted;
total->svr.num_query_dnscrypt_cert += a->svr.num_query_dnscrypt_cert;
total->svr.num_query_dnscrypt_cleartext += \
a->svr.num_query_dnscrypt_cleartext;
total->svr.num_query_dnscrypt_crypted_malformed += \
a->svr.num_query_dnscrypt_crypted_malformed;
#endif /* USE_DNSCRYPT */
/* the max size reached is upped to higher of both */
if(a->svr.max_query_list_size > total->svr.max_query_list_size)
total->svr.max_query_list_size = a->svr.max_query_list_size;
if(a->svr.extended) {
int i;
total->svr.qtype_big += a->svr.qtype_big;
total->svr.qclass_big += a->svr.qclass_big;
total->svr.qtcp += a->svr.qtcp;
total->svr.qtcp_outgoing += a->svr.qtcp_outgoing;
total->svr.qudp_outgoing += a->svr.qudp_outgoing;
total->svr.qtls += a->svr.qtls;
total->svr.qtls_resume += a->svr.qtls_resume;
total->svr.qhttps += a->svr.qhttps;
total->svr.qipv6 += a->svr.qipv6;
total->svr.qbit_QR += a->svr.qbit_QR;
total->svr.qbit_AA += a->svr.qbit_AA;
total->svr.qbit_TC += a->svr.qbit_TC;
total->svr.qbit_RD += a->svr.qbit_RD;
total->svr.qbit_RA += a->svr.qbit_RA;
total->svr.qbit_Z += a->svr.qbit_Z;
total->svr.qbit_AD += a->svr.qbit_AD;
total->svr.qbit_CD += a->svr.qbit_CD;
total->svr.qEDNS += a->svr.qEDNS;
total->svr.qEDNS_DO += a->svr.qEDNS_DO;
total->svr.ans_rcode_nodata += a->svr.ans_rcode_nodata;
total->svr.ans_secure += a->svr.ans_secure;
total->svr.ans_bogus += a->svr.ans_bogus;
total->svr.unwanted_replies += a->svr.unwanted_replies;
total->svr.unwanted_queries += a->svr.unwanted_queries;
total->svr.tcp_accept_usage += a->svr.tcp_accept_usage;
#ifdef USE_CACHEDB
total->svr.num_query_cachedb += a->svr.num_query_cachedb;
#endif
for(i=0; i<UB_STATS_QTYPE_NUM; i++)
total->svr.qtype[i] += a->svr.qtype[i];
for(i=0; i<UB_STATS_QCLASS_NUM; i++)
total->svr.qclass[i] += a->svr.qclass[i];
for(i=0; i<UB_STATS_OPCODE_NUM; i++)
total->svr.qopcode[i] += a->svr.qopcode[i];
for(i=0; i<UB_STATS_RCODE_NUM; i++)
total->svr.ans_rcode[i] += a->svr.ans_rcode[i];
for(i=0; i<NUM_BUCKETS_HIST; i++)
total->svr.hist[i] += a->svr.hist[i];
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++)
total->svr.rpz_action[i] += a->svr.rpz_action[i];
}
total->mesh_num_states += a->mesh_num_states;
total->mesh_num_reply_states += a->mesh_num_reply_states;
total->mesh_jostled += a->mesh_jostled;
total->mesh_dropped += a->mesh_dropped;
total->mesh_replies_sent += a->mesh_replies_sent;
stats_timeval_add(&total->mesh_replies_sum_wait_sec, &total->mesh_replies_sum_wait_usec, a->mesh_replies_sum_wait_sec, a->mesh_replies_sum_wait_usec);
/* the medians are averaged together, this is not as accurate as
* taking the median over all of the data, but is good and fast
* added up here, division later*/
total->mesh_time_median += a->mesh_time_median;
}
void server_stats_insquery(struct ub_server_stats* stats, struct comm_point* c,
uint16_t qtype, uint16_t qclass, struct edns_data* edns,
struct comm_reply* repinfo)
{
uint16_t flags = sldns_buffer_read_u16_at(c->buffer, 2);
if(qtype < UB_STATS_QTYPE_NUM)
stats->qtype[qtype]++;
else stats->qtype_big++;
if(qclass < UB_STATS_QCLASS_NUM)
stats->qclass[qclass]++;
else stats->qclass_big++;
stats->qopcode[ LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) ]++;
if(c->type != comm_udp) {
stats->qtcp++;
if(c->ssl != NULL) {
stats->qtls++;
#ifdef HAVE_SSL
if(SSL_session_reused(c->ssl))
stats->qtls_resume++;
#endif
if(c->type == comm_http)
stats->qhttps++;
}
}
if(repinfo && addr_is_ip6(&repinfo->remote_addr, repinfo->remote_addrlen))
stats->qipv6++;
if( (flags&BIT_QR) )
stats->qbit_QR++;
if( (flags&BIT_AA) )
stats->qbit_AA++;
if( (flags&BIT_TC) )
stats->qbit_TC++;
if( (flags&BIT_RD) )
stats->qbit_RD++;
if( (flags&BIT_RA) )
stats->qbit_RA++;
if( (flags&BIT_Z) )
stats->qbit_Z++;
if( (flags&BIT_AD) )
stats->qbit_AD++;
if( (flags&BIT_CD) )
stats->qbit_CD++;
if(edns->edns_present) {
stats->qEDNS++;
if( (edns->bits & EDNS_DO) )
stats->qEDNS_DO++;
}
}
void server_stats_insrcode(struct ub_server_stats* stats, sldns_buffer* buf)
{
if(stats->extended && sldns_buffer_limit(buf) != 0) {
int r = (int)LDNS_RCODE_WIRE( sldns_buffer_begin(buf) );
stats->ans_rcode[r] ++;
if(r == 0 && LDNS_ANCOUNT( sldns_buffer_begin(buf) ) == 0)
stats->ans_rcode_nodata ++;
}
}
void server_stats_downstream_cookie(struct ub_server_stats* stats,
struct edns_data* edns)
{
if(!(edns->edns_present && edns->cookie_present)) return;
if(edns->cookie_valid) {
stats->num_queries_cookie_valid++;
} else if(edns->cookie_client) {
stats->num_queries_cookie_client++;
} else {
stats->num_queries_cookie_invalid++;
}
}
@@ -1,136 +0,0 @@
/*
* daemon/stats.h - collect runtime performance indicators.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file describes the data structure used to collect runtime performance
* numbers. These 'statistics' may be of interest to the operator.
*/
#ifndef DAEMON_STATS_H
#define DAEMON_STATS_H
#include "util/timehist.h"
struct worker;
struct config_file;
struct comm_point;
struct comm_reply;
struct edns_data;
struct sldns_buffer;
/* stats struct */
#include "libunbound/unbound.h"
/**
* Initialize server stats to 0.
* @param stats: what to init (this is alloced by the caller).
* @param cfg: with extended statistics option.
*/
void server_stats_init(struct ub_server_stats* stats, struct config_file* cfg);
/** add query if it missed the cache */
void server_stats_querymiss(struct ub_server_stats* stats, struct worker* worker);
/** add query if was cached and also resulted in a prefetch */
void server_stats_prefetch(struct ub_server_stats* stats, struct worker* worker);
/** display the stats to the log */
void server_stats_log(struct ub_server_stats* stats, struct worker* worker,
int threadnum);
/**
* Obtain the stats info for a given thread. Uses pipe to communicate.
* @param worker: the worker that is executing (the first worker).
* @param who: on who to get the statistics info.
* @param s: the stats block to fill in.
* @param reset: if stats can be reset.
*/
void server_stats_obtain(struct worker* worker, struct worker* who,
struct ub_stats_info* s, int reset);
/**
* Compile stats into structure for this thread worker.
* Also clears the statistics counters (if that is set by config file).
* @param worker: the worker to compile stats for, also the executing worker.
* @param s: stats block.
* @param reset: if true, depending on config stats are reset.
* if false, statistics are not reset.
*/
void server_stats_compile(struct worker* worker, struct ub_stats_info* s,
int reset);
/**
* Send stats over comm tube in reply to query cmd
* @param worker: this worker.
* @param reset: if true, depending on config stats are reset.
* if false, statistics are not reset.
*/
void server_stats_reply(struct worker* worker, int reset);
/**
* Addup stat blocks.
* @param total: sum of the two entries.
* @param a: to add to it.
*/
void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a);
/**
* Add stats for this query
* @param stats: the stats
* @param c: commpoint with type and buffer.
* @param qtype: query type
* @param qclass: query class
* @param edns: edns record
* @param repinfo: reply info with remote address
*/
void server_stats_insquery(struct ub_server_stats* stats, struct comm_point* c,
uint16_t qtype, uint16_t qclass, struct edns_data* edns,
struct comm_reply* repinfo);
/**
* Add rcode for this query.
* @param stats: the stats
* @param buf: buffer with rcode. If buffer is length0: not counted.
*/
void server_stats_insrcode(struct ub_server_stats* stats, struct sldns_buffer* buf);
/**
* Add DNS Cookie stats for this query
* @param stats: the stats
* @param edns: edns record
*/
void server_stats_downstream_cookie(struct ub_server_stats* stats,
struct edns_data* edns);
#endif /* DAEMON_STATS_H */
@@ -1,846 +0,0 @@
/*
* daemon/unbound.c - main program for unbound DNS resolver daemon.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*
*/
/**
* \file
*
* Main program to start the DNS resolver daemon.
*/
#include "config.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <sys/time.h>
#include "util/log.h"
#include "daemon/daemon.h"
#include "daemon/remote.h"
#include "util/config_file.h"
#include "util/storage/slabhash.h"
#include "services/listen_dnsport.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "util/fptr_wlist.h"
#include "util/data/msgreply.h"
#include "util/module.h"
#include "util/net_help.h"
#include "util/ub_event.h"
#include <signal.h>
#include <fcntl.h>
#include <openssl/crypto.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#include <openssl/ssl.h>
#ifndef S_SPLINT_S
/* splint chokes on this system header file */
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#endif /* S_SPLINT_S */
#ifdef HAVE_LOGIN_CAP_H
#include <login_cap.h>
#endif
#ifdef UB_ON_WINDOWS
# include "winrc/win_svc.h"
#endif
#ifdef HAVE_NSS
/* nss3 */
# include "nss.h"
#endif
#ifdef HAVE_TARGETCONDITIONALS_H
#include <TargetConditionals.h>
#endif
#if (defined(TARGET_OS_TV) && TARGET_OS_TV) || (defined(TARGET_OS_WATCH) && TARGET_OS_WATCH)
#undef HAVE_FORK
#endif
/** print build options. */
static void
print_build_options(void)
{
const char** m;
const char *evnm="event", *evsys="", *evmethod="";
time_t t;
struct timeval now;
struct ub_event_base* base;
printf("Version %s\n\n", PACKAGE_VERSION);
printf("Configure line: %s\n", CONFCMDLINE);
base = ub_default_event_base(0,&t,&now);
ub_get_event_sys(base, &evnm, &evsys, &evmethod);
printf("Linked libs: %s %s (it uses %s), %s\n",
evnm, evsys, evmethod,
#ifdef HAVE_SSL
# ifdef SSLEAY_VERSION
SSLeay_version(SSLEAY_VERSION)
# else
OpenSSL_version(OPENSSL_VERSION)
# endif
#elif defined(HAVE_NSS)
NSS_GetVersion()
#elif defined(HAVE_NETTLE)
"nettle"
#endif
);
printf("Linked modules:");
for(m = module_list_avail(); *m; m++)
printf(" %s", *m);
printf("\n");
#ifdef USE_DNSCRYPT
printf("DNSCrypt feature available\n");
#endif
#ifdef USE_TCP_FASTOPEN
printf("TCP Fastopen feature available\n");
#endif
ub_event_base_free(base);
printf("\nBSD licensed, see LICENSE in source package for details.\n");
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
}
/** print usage. */
static void
usage(void)
{
printf("usage: unbound [options]\n");
printf(" start unbound daemon DNS resolver.\n");
printf("-h this help.\n");
printf("-c file config file to read instead of %s\n", CONFIGFILE);
printf(" file format is described in unbound.conf(5).\n");
printf("-d do not fork into the background.\n");
printf("-p do not create a pidfile.\n");
printf("-v verbose (more times to increase verbosity).\n");
printf("-V show version number and build options.\n");
#ifdef UB_ON_WINDOWS
printf("-w opt windows option: \n");
printf(" install, remove - manage the services entry\n");
printf(" service - used to start from services control panel\n");
#endif
printf("\nVersion %s\n", PACKAGE_VERSION);
printf("BSD licensed, see LICENSE in source package for details.\n");
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
}
#ifndef unbound_testbound
int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
return 0;
}
#endif
/** check file descriptor count */
static void
checkrlimits(struct config_file* cfg)
{
#ifndef S_SPLINT_S
#ifdef HAVE_GETRLIMIT
/* list has number of ports to listen to, ifs number addresses */
int list = ((cfg->do_udp?1:0) + (cfg->do_tcp?1 +
(int)cfg->incoming_num_tcp:0));
size_t listen_ifs = (size_t)(cfg->num_ifs==0?
((cfg->do_ip4 && !cfg->if_automatic?1:0) +
(cfg->do_ip6?1:0)):cfg->num_ifs);
size_t listen_num = list*listen_ifs;
size_t outudpnum = (size_t)cfg->outgoing_num_ports;
size_t outtcpnum = cfg->outgoing_num_tcp;
size_t misc = 4; /* logfile, pidfile, stdout... */
size_t perthread_noudp = listen_num + outtcpnum +
2/*cmdpipe*/ + 2/*libevent*/ + misc;
size_t perthread = perthread_noudp + outudpnum;
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
int numthread = 1; /* it forks */
#else
int numthread = (cfg->num_threads?cfg->num_threads:1);
#endif
size_t total = numthread * perthread + misc;
size_t avail;
struct rlimit rlim;
size_t memsize_expect = cfg->msg_cache_size + cfg->rrset_cache_size
+ (cfg->do_tcp?cfg->stream_wait_size:0)
+ (cfg->ip_ratelimit?cfg->ip_ratelimit_size:0)
+ (cfg->ratelimit?cfg->ratelimit_size:0)
+ (cfg->dnscrypt?cfg->dnscrypt_shared_secret_cache_size + cfg->dnscrypt_nonce_cache_size:0)
+ cfg->infra_cache_numhosts * (sizeof(struct infra_key)+sizeof(struct infra_data));
if(strstr(cfg->module_conf, "validator") && (cfg->trust_anchor_file_list || cfg->trust_anchor_list || cfg->auto_trust_anchor_file_list || cfg->trusted_keys_file_list)) {
memsize_expect += cfg->key_cache_size + cfg->neg_cache_size;
}
#ifdef HAVE_NGHTTP2_NGHTTP2_H
if(cfg_has_https(cfg)) {
memsize_expect += cfg->http_query_buffer_size + cfg->http_response_buffer_size;
}
#endif
#ifdef RLIMIT_AS
if(getrlimit(RLIMIT_AS, &rlim) == 0) {
if(rlim.rlim_cur != (rlim_t)RLIM_INFINITY &&
rlim.rlim_cur < (rlim_t)memsize_expect) {
log_warn("the ulimit(max memory size) is smaller than the expected memory usage (added size of caches). %u < %u bytes", (unsigned)rlim.rlim_cur, (unsigned)memsize_expect);
}
}
#endif
if(getrlimit(RLIMIT_DATA, &rlim) == 0) {
if(rlim.rlim_cur != (rlim_t)RLIM_INFINITY &&
rlim.rlim_cur < (rlim_t)memsize_expect) {
log_warn("the ulimit(data seg size) is smaller than the expected memory usage (added size of caches). %u < %u bytes", (unsigned)rlim.rlim_cur, (unsigned)memsize_expect);
}
}
if(total > 1024 &&
strncmp(ub_event_get_version(), "mini-event", 10) == 0) {
log_warn("too many file descriptors requested. The builtin"
"mini-event cannot handle more than 1024. Config "
"for less fds or compile with libevent");
if(numthread*perthread_noudp+15 > 1024)
fatal_exit("too much tcp. not enough fds.");
cfg->outgoing_num_ports = (int)((1024
- numthread*perthread_noudp
- 10 /* safety margin */) /numthread);
log_warn("continuing with less udp ports: %u",
cfg->outgoing_num_ports);
total = 1024;
}
if(perthread > 64 &&
strncmp(ub_event_get_version(), "winsock-event", 13) == 0) {
log_err("too many file descriptors requested. The winsock"
" event handler cannot handle more than 64 per "
" thread. Config for less fds");
if(perthread_noudp+2 > 64)
fatal_exit("too much tcp. not enough fds.");
cfg->outgoing_num_ports = (int)((64
- perthread_noudp
- 2/* safety margin */));
log_warn("continuing with less udp ports: %u",
cfg->outgoing_num_ports);
total = numthread*(perthread_noudp+
(size_t)cfg->outgoing_num_ports)+misc;
}
if(getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
log_warn("getrlimit: %s", strerror(errno));
return;
}
if(rlim.rlim_cur == (rlim_t)RLIM_INFINITY)
return;
if((size_t)rlim.rlim_cur < total) {
avail = (size_t)rlim.rlim_cur;
rlim.rlim_cur = (rlim_t)(total + 10);
rlim.rlim_max = (rlim_t)(total + 10);
#ifdef HAVE_SETRLIMIT
if(setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
log_warn("setrlimit: %s", strerror(errno));
#endif
log_warn("cannot increase max open fds from %u to %u",
(unsigned)avail, (unsigned)total+10);
/* check that calculation below does not underflow,
* with 15 as margin */
if(numthread*perthread_noudp+15 > avail)
fatal_exit("too much tcp. not enough fds.");
cfg->outgoing_num_ports = (int)((avail
- numthread*perthread_noudp
- 10 /* safety margin */) /numthread);
log_warn("continuing with less udp ports: %u",
cfg->outgoing_num_ports);
log_warn("increase ulimit or decrease threads, "
"ports in config to remove this warning");
return;
#ifdef HAVE_SETRLIMIT
}
#endif
verbose(VERB_ALGO, "increased limit(open files) from %u to %u",
(unsigned)avail, (unsigned)total+10);
}
#else
(void)cfg;
#endif /* HAVE_GETRLIMIT */
#endif /* S_SPLINT_S */
}
/** set verbosity, check rlimits, cache settings */
static void
apply_settings(struct daemon* daemon, struct config_file* cfg,
int cmdline_verbose, int debug_mode)
{
/* apply if they have changed */
verbosity = cmdline_verbose + cfg->verbosity;
if (debug_mode > 1) {
cfg->use_syslog = 0;
free(cfg->logfile);
cfg->logfile = NULL;
}
daemon_apply_cfg(daemon, cfg);
checkrlimits(cfg);
if (cfg->use_systemd && cfg->do_daemonize) {
log_warn("use-systemd and do-daemonize should not be enabled at the same time");
}
log_ident_set_or_default(cfg->log_identity);
}
#ifdef HAVE_KILL
/** Read existing pid from pidfile.
* @param file: file name of pid file.
* @return: the pid from the file or -1 if none.
*/
static pid_t
readpid (const char* file)
{
int fd;
pid_t pid;
char pidbuf[32];
char* t;
ssize_t l;
if ((fd = open(file, O_RDONLY)) == -1) {
if(errno != ENOENT)
log_err("Could not read pidfile %s: %s",
file, strerror(errno));
return -1;
}
if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
if(errno != ENOENT)
log_err("Could not read pidfile %s: %s",
file, strerror(errno));
close(fd);
return -1;
}
close(fd);
/* Empty pidfile means no pidfile... */
if (l == 0) {
return -1;
}
pidbuf[sizeof(pidbuf)-1] = 0;
pid = (pid_t)strtol(pidbuf, &t, 10);
if (*t && *t != '\n') {
return -1;
}
return pid;
}
/** write pid to file.
* @param pidfile: file name of pid file.
* @param pid: pid to write to file.
* @return false on failure
*/
static int
writepid (const char* pidfile, pid_t pid)
{
int fd;
char pidbuf[32];
size_t count = 0;
snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long)pid);
if((fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC
#ifdef O_NOFOLLOW
| O_NOFOLLOW
#endif
, 0644)) == -1) {
log_err("cannot open pidfile %s: %s",
pidfile, strerror(errno));
return 0;
}
while(count < strlen(pidbuf)) {
ssize_t r = write(fd, pidbuf+count, strlen(pidbuf)-count);
if(r == -1) {
if(errno == EAGAIN || errno == EINTR)
continue;
log_err("cannot write to pidfile %s: %s",
pidfile, strerror(errno));
close(fd);
return 0;
} else if(r == 0) {
log_err("cannot write any bytes to pidfile %s: "
"write returns 0 bytes written", pidfile);
close(fd);
return 0;
}
count += r;
}
close(fd);
return 1;
}
/**
* check old pid file.
* @param pidfile: the file name of the pid file.
* @param inchroot: if pidfile is inchroot and we can thus expect to
* be able to delete it.
*/
static void
checkoldpid(char* pidfile, int inchroot)
{
pid_t old;
if((old = readpid(pidfile)) != -1) {
/* see if it is still alive */
if(kill(old, 0) == 0 || errno == EPERM)
log_warn("unbound is already running as pid %u.",
(unsigned)old);
else if(inchroot)
log_warn("did not exit gracefully last time (%u)",
(unsigned)old);
}
}
#endif /* HAVE_KILL */
/** detach from command line */
static void
detach(void)
{
#if defined(HAVE_DAEMON) && !defined(DEPRECATED_DAEMON)
/* use POSIX daemon(3) function */
if(daemon(1, 0) != 0)
fatal_exit("daemon failed: %s", strerror(errno));
#else /* no HAVE_DAEMON */
#ifdef HAVE_FORK
int fd;
/* Take off... */
switch (fork()) {
case 0:
break;
case -1:
fatal_exit("fork failed: %s", strerror(errno));
default:
/* exit interactive session */
exit(0);
}
/* detach */
#ifdef HAVE_SETSID
if(setsid() == -1)
fatal_exit("setsid() failed: %s", strerror(errno));
#endif
if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > 2)
(void)close(fd);
}
#endif /* HAVE_FORK */
#endif /* HAVE_DAEMON */
}
/** daemonize, drop user privileges and chroot if needed */
static void
perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
const char** cfgfile, int need_pidfile)
{
#ifdef HAVE_KILL
int pidinchroot;
#endif
#ifdef HAVE_GETPWNAM
struct passwd *pwd = NULL;
if(cfg->username && cfg->username[0]) {
if((pwd = getpwnam(cfg->username)) == NULL)
fatal_exit("user '%s' does not exist.", cfg->username);
/* endpwent below, in case we need pwd for setusercontext */
}
#endif
#ifdef UB_ON_WINDOWS
w_config_adjust_directory(cfg);
#endif
/* read ssl keys while superuser and outside chroot */
#ifdef HAVE_SSL
if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
if(!(daemon->listen_sslctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL)))
fatal_exit("could not set up listen SSL_CTX");
if(cfg->tls_ciphers && cfg->tls_ciphers[0]) {
if (!SSL_CTX_set_cipher_list(daemon->listen_sslctx, cfg->tls_ciphers)) {
fatal_exit("failed to set tls-cipher %s", cfg->tls_ciphers);
}
}
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
if(cfg->tls_ciphersuites && cfg->tls_ciphersuites[0]) {
if (!SSL_CTX_set_ciphersuites(daemon->listen_sslctx, cfg->tls_ciphersuites)) {
fatal_exit("failed to set tls-ciphersuites %s", cfg->tls_ciphersuites);
}
}
#endif
if(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0) {
if(!listen_sslctx_setup_ticket_keys(daemon->listen_sslctx, cfg->tls_session_ticket_keys.first)) {
fatal_exit("could not set session ticket SSL_CTX");
}
}
}
if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL,
cfg->tls_cert_bundle, cfg->tls_win_cert)))
fatal_exit("could not set up connect SSL_CTX");
#endif
/* init syslog (as root) if needed, before daemonize, otherwise
* a fork error could not be printed since daemonize closed stderr.*/
if(cfg->use_syslog) {
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
}
/* if using a logfile, we cannot open it because the logfile would
* be created with the wrong permissions, we cannot chown it because
* we cannot chown system logfiles, so we do not open at all.
* So, using a logfile, the user does not see errors unless -d is
* given to unbound on the commandline. */
#ifdef HAVE_KILL
/* true if pidfile is inside chrootdir, or nochroot */
pidinchroot = need_pidfile && (!(cfg->chrootdir && cfg->chrootdir[0]) ||
(cfg->chrootdir && cfg->chrootdir[0] &&
strncmp(cfg->pidfile, cfg->chrootdir,
strlen(cfg->chrootdir))==0));
/* check old pid file before forking */
if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) {
/* calculate position of pidfile */
if(cfg->pidfile[0] == '/')
daemon->pidfile = strdup(cfg->pidfile);
else daemon->pidfile = fname_after_chroot(cfg->pidfile,
cfg, 1);
if(!daemon->pidfile)
fatal_exit("pidfile alloc: out of memory");
checkoldpid(daemon->pidfile, pidinchroot);
}
#endif
/* daemonize because pid is needed by the writepid func */
if(!debug_mode && cfg->do_daemonize) {
detach();
}
/* write new pidfile (while still root, so can be outside chroot) */
#ifdef HAVE_KILL
if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) {
if(writepid(daemon->pidfile, getpid())) {
if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1 &&
pidinchroot) {
# ifdef HAVE_CHOWN
if(chown(daemon->pidfile, cfg_uid, cfg_gid) == -1) {
verbose(VERB_QUERY, "cannot chown %u.%u %s: %s",
(unsigned)cfg_uid, (unsigned)cfg_gid,
daemon->pidfile, strerror(errno));
}
# endif /* HAVE_CHOWN */
}
}
}
#else
(void)daemon;
(void)need_pidfile;
#endif /* HAVE_KILL */
/* Set user context */
#ifdef HAVE_GETPWNAM
if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) {
#ifdef HAVE_SETUSERCONTEXT
/* setusercontext does initgroups, setuid, setgid, and
* also resource limits from login config, but we
* still call setresuid, setresgid to be sure to set all uid*/
if(setusercontext(NULL, pwd, cfg_uid, (unsigned)
LOGIN_SETALL & ~LOGIN_SETUSER & ~LOGIN_SETGROUP) != 0)
log_warn("unable to setusercontext %s: %s",
cfg->username, strerror(errno));
#else
(void)pwd;
#endif /* HAVE_SETUSERCONTEXT */
}
#endif /* HAVE_GETPWNAM */
/* box into the chroot */
#ifdef HAVE_CHROOT
if(cfg->chrootdir && cfg->chrootdir[0]) {
if(chdir(cfg->chrootdir)) {
fatal_exit("unable to chdir to chroot %s: %s",
cfg->chrootdir, strerror(errno));
}
verbose(VERB_QUERY, "chdir to %s", cfg->chrootdir);
if(chroot(cfg->chrootdir))
fatal_exit("unable to chroot to %s: %s",
cfg->chrootdir, strerror(errno));
if(chdir("/"))
fatal_exit("unable to chdir to / in chroot %s: %s",
cfg->chrootdir, strerror(errno));
verbose(VERB_QUERY, "chroot to %s", cfg->chrootdir);
if(strncmp(*cfgfile, cfg->chrootdir,
strlen(cfg->chrootdir)) == 0)
(*cfgfile) += strlen(cfg->chrootdir);
/* adjust stored pidfile for chroot */
if(daemon->pidfile && daemon->pidfile[0] &&
strncmp(daemon->pidfile, cfg->chrootdir,
strlen(cfg->chrootdir))==0) {
char* old = daemon->pidfile;
daemon->pidfile = strdup(old+strlen(cfg->chrootdir));
free(old);
if(!daemon->pidfile)
log_err("out of memory in pidfile adjust");
}
daemon->chroot = strdup(cfg->chrootdir);
if(!daemon->chroot)
log_err("out of memory in daemon chroot dir storage");
}
#else
(void)cfgfile;
#endif
/* change to working directory inside chroot */
if(cfg->directory && cfg->directory[0]) {
char* dir = cfg->directory;
if(cfg->chrootdir && cfg->chrootdir[0] &&
strncmp(dir, cfg->chrootdir,
strlen(cfg->chrootdir)) == 0)
dir += strlen(cfg->chrootdir);
if(dir[0]) {
if(chdir(dir)) {
fatal_exit("Could not chdir to %s: %s",
dir, strerror(errno));
}
verbose(VERB_QUERY, "chdir to %s", dir);
}
}
/* drop permissions after chroot, getpwnam, pidfile, syslog done*/
#ifdef HAVE_GETPWNAM
if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) {
# ifdef HAVE_INITGROUPS
if(initgroups(cfg->username, cfg_gid) != 0)
log_warn("unable to initgroups %s: %s",
cfg->username, strerror(errno));
# endif /* HAVE_INITGROUPS */
# ifdef HAVE_ENDPWENT
endpwent();
# endif
#ifdef HAVE_SETRESGID
if(setresgid(cfg_gid,cfg_gid,cfg_gid) != 0)
#elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID)
if(setregid(cfg_gid,cfg_gid) != 0)
#else /* use setgid */
if(setgid(cfg_gid) != 0)
#endif /* HAVE_SETRESGID */
fatal_exit("unable to set group id of %s: %s",
cfg->username, strerror(errno));
#ifdef HAVE_SETRESUID
if(setresuid(cfg_uid,cfg_uid,cfg_uid) != 0)
#elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID)
if(setreuid(cfg_uid,cfg_uid) != 0)
#else /* use setuid */
if(setuid(cfg_uid) != 0)
#endif /* HAVE_SETRESUID */
fatal_exit("unable to set user id of %s: %s",
cfg->username, strerror(errno));
verbose(VERB_QUERY, "drop user privileges, run as %s",
cfg->username);
}
#endif /* HAVE_GETPWNAM */
/* file logging inited after chroot,chdir,setuid is done so that
* it would succeed on SIGHUP as well */
if(!cfg->use_syslog)
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
}
/**
* Run the daemon.
* @param cfgfile: the config file name.
* @param cmdline_verbose: verbosity resulting from commandline -v.
* These increase verbosity as specified in the config file.
* @param debug_mode: if set, do not daemonize.
* @param need_pidfile: if false, no pidfile is checked or created.
*/
static void
run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pidfile)
{
struct config_file* cfg = NULL;
struct daemon* daemon = NULL;
int done_setup = 0;
if(!(daemon = daemon_init()))
fatal_exit("alloc failure");
while(!daemon->need_to_exit) {
if(done_setup)
verbose(VERB_OPS, "Restart of %s.", PACKAGE_STRING);
else verbose(VERB_OPS, "Start of %s.", PACKAGE_STRING);
/* config stuff */
if(!(cfg = config_create()))
fatal_exit("Could not alloc config defaults");
if(!config_read(cfg, cfgfile, daemon->chroot)) {
if(errno != ENOENT)
fatal_exit("Could not read config file: %s."
" Maybe try unbound -dd, it stays on "
"the commandline to see more errors, "
"or unbound-checkconf", cfgfile);
log_warn("Continuing with default config settings");
}
apply_settings(daemon, cfg, cmdline_verbose, debug_mode);
if(!done_setup)
config_lookup_uid(cfg);
/* prepare */
if(!daemon_open_shared_ports(daemon))
fatal_exit("could not open ports");
if(!done_setup) {
perform_setup(daemon, cfg, debug_mode, &cfgfile, need_pidfile);
done_setup = 1;
} else {
/* reopen log after HUP to facilitate log rotation */
if(!cfg->use_syslog)
log_init(cfg->logfile, 0, cfg->chrootdir);
}
/* work */
daemon_fork(daemon);
/* clean up for restart */
verbose(VERB_ALGO, "cleanup.");
daemon_cleanup(daemon);
config_delete(cfg);
}
verbose(VERB_ALGO, "Exit cleanup.");
/* this unlink may not work if the pidfile is located outside
* of the chroot/workdir or we no longer have permissions */
if(daemon->pidfile) {
int fd;
/* truncate pidfile */
fd = open(daemon->pidfile, O_WRONLY | O_TRUNC, 0644);
if(fd != -1)
close(fd);
/* delete pidfile */
unlink(daemon->pidfile);
}
daemon_delete(daemon);
}
/** getopt global, in case header files fail to declare it. */
extern int optind;
/** getopt global, in case header files fail to declare it. */
extern char* optarg;
/**
* main program. Set options given commandline arguments.
* @param argc: number of commandline arguments.
* @param argv: array of commandline arguments.
* @return: exit status of the program.
*/
int
main(int argc, char* argv[])
{
int c;
const char* cfgfile = CONFIGFILE;
const char* winopt = NULL;
const char* log_ident_default;
int cmdline_verbose = 0;
int debug_mode = 0;
int need_pidfile = 1;
#ifdef UB_ON_WINDOWS
int cmdline_cfg = 0;
#endif
checklock_start();
log_init(NULL, 0, NULL);
log_ident_default = strrchr(argv[0],'/')?strrchr(argv[0],'/')+1:argv[0];
log_ident_set_default(log_ident_default);
log_ident_set(log_ident_default);
/* parse the options */
while( (c=getopt(argc, argv, "c:dhpvw:V")) != -1) {
switch(c) {
case 'c':
cfgfile = optarg;
#ifdef UB_ON_WINDOWS
cmdline_cfg = 1;
#endif
break;
case 'v':
cmdline_verbose++;
verbosity++;
break;
case 'p':
need_pidfile = 0;
break;
case 'd':
debug_mode++;
break;
case 'w':
winopt = optarg;
break;
case 'V':
print_build_options();
return 0;
case '?':
case 'h':
default:
usage();
return 1;
}
}
argc -= optind;
/* argv += optind; not using further arguments */
if(winopt) {
#ifdef UB_ON_WINDOWS
wsvc_command_option(winopt, cfgfile, cmdline_verbose,
cmdline_cfg);
#else
fatal_exit("option not supported");
#endif
}
if(argc != 0) {
usage();
return 1;
}
run_daemon(cfgfile, cmdline_verbose, debug_mode, need_pidfile);
log_init(NULL, 0, NULL); /* close logfile */
#ifndef unbound_testbound
if(log_get_lock()) {
lock_basic_destroy((lock_basic_type*)log_get_lock());
}
#endif
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -1,184 +0,0 @@
/*
* daemon/worker.h - worker that handles a pending list of requests.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file describes the worker structure that holds a list of
* pending requests and handles them.
*/
#ifndef DAEMON_WORKER_H
#define DAEMON_WORKER_H
#include "libunbound/worker.h"
#include "util/netevent.h"
#include "util/locks.h"
#include "util/alloc.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
#include "daemon/stats.h"
#include "util/module.h"
#include "dnstap/dnstap.h"
struct listen_dnsport;
struct outside_network;
struct config_file;
struct daemon;
struct listen_port;
struct ub_randstate;
struct regional;
struct tube;
struct daemon_remote;
struct query_info;
/** worker commands */
enum worker_commands {
/** make the worker quit */
worker_cmd_quit,
/** obtain statistics */
worker_cmd_stats,
/** obtain statistics without statsclear */
worker_cmd_stats_noreset,
/** execute remote control command */
worker_cmd_remote
};
/**
* Structure holding working information for unbound.
* Holds globally visible information.
*/
struct worker {
/** the thread number (in daemon array). First in struct for debug. */
int thread_num;
/** global shared daemon structure */
struct daemon* daemon;
/** thread id */
ub_thread_type thr_id;
#ifdef HAVE_GETTID
/** thread tid, the LWP id. */
pid_t thread_tid;
#endif
/** pipe, for commands for this worker */
struct tube* cmd;
/** the event base this worker works with */
struct comm_base* base;
/** the frontside listening interface where request events come in */
struct listen_dnsport* front;
/** the backside outside network interface to the auth servers */
struct outside_network* back;
/** ports to be used by this worker. */
int* ports;
/** number of ports for this worker */
int numports;
/** the signal handler */
struct comm_signal* comsig;
/** commpoint to listen to commands. */
struct comm_point* cmd_com;
/** timer for statistics */
struct comm_timer* stat_timer;
/** ratelimit for errors, time value */
time_t err_limit_time;
/** ratelimit for errors, packet count */
unsigned int err_limit_count;
/** random() table for this worker. */
struct ub_randstate* rndstate;
/** do we need to restart or quit (on signal) */
int need_to_exit;
/** allocation cache for this thread */
struct alloc_cache *alloc;
/** per thread statistics */
struct ub_server_stats stats;
/** thread scratch regional */
struct regional* scratchpad;
/** module environment passed to modules, changed for this thread */
struct module_env env;
#ifdef USE_DNSTAP
/** dnstap environment, changed for this thread */
struct dt_env dtenv;
#endif
/** reuse existing cache on reload if other conditions allow it. */
int reuse_cache;
};
/**
* Create the worker structure. Bare bones version, zeroed struct,
* with backpointers only. Use worker_init on it later.
* @param daemon: the daemon that this worker thread is part of.
* @param id: the thread number from 0.. numthreads-1.
* @param ports: the ports it is allowed to use, array.
* @param n: the number of ports.
* @return: the new worker or NULL on alloc failure.
*/
struct worker* worker_create(struct daemon* daemon, int id, int* ports, int n);
/**
* Initialize worker.
* Allocates event base, listens to ports
* @param worker: worker to initialize, created with worker_create.
* @param cfg: configuration settings.
* @param ports: list of shared query ports.
* @param do_sigs: if true, worker installs signal handlers.
* @return: false on error.
*/
int worker_init(struct worker* worker, struct config_file *cfg,
struct listen_port* ports, int do_sigs);
/**
* Make worker work.
*/
void worker_work(struct worker* worker);
/**
* Delete worker.
*/
void worker_delete(struct worker* worker);
/**
* Send a command to a worker. Uses blocking writes.
* @param worker: worker to send command to.
* @param cmd: command to send.
*/
void worker_send_cmd(struct worker* worker, enum worker_commands cmd);
/**
* Init worker stats - includes server_stats_init, outside network and mesh.
* @param worker: the worker to init
*/
void worker_stats_clear(struct worker* worker);
#endif /* DAEMON_WORKER_H */
File diff suppressed because it is too large Load Diff
@@ -1,71 +0,0 @@
/*
* dns64/dns64.h - DNS64 module
*
* Copyright (c) 2009, Viagénie. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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.
*/
/**
* \file
*
* This file contains a module that performs DNS64 query processing.
*/
#ifndef DNS64_DNS64_H
#define DNS64_DNS64_H
#include "util/module.h"
/**
* Get the dns64 function block.
* @return: function block with function pointers to dns64 methods.
*/
struct module_func_block *dns64_get_funcblock(void);
/** dns64 init */
int dns64_init(struct module_env* env, int id);
/** dns64 deinit */
void dns64_deinit(struct module_env* env, int id);
/** dns64 operate on a query */
void dns64_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound);
void dns64_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** dns64 cleanup query state */
void dns64_clear(struct module_qstate* qstate, int id);
/** dns64 alloc size routine */
size_t dns64_get_mem(struct module_env* env, int id);
#endif /* DNS64_DNS64_H */
@@ -1,32 +0,0 @@
#ifndef UNBOUND_DNSCRYPT_CERT_H
#define UNBOUND_DNSCRYPT_CERT_H
/**
* \file
* certificate type for dnscrypt for use in other header files
*/
#include <sodium.h>
#define CERT_MAGIC_CERT "DNSC"
#define CERT_MAJOR_VERSION 1
#define CERT_MINOR_VERSION 0
#define CERT_OLD_MAGIC_HEADER "7PYqwfzt"
#define CERT_FILE_EXPIRE_DAYS 365
struct SignedCert {
uint8_t magic_cert[4];
uint8_t version_major[2];
uint8_t version_minor[2];
// Signed Content
uint8_t signed_content[64];
uint8_t server_publickey[crypto_box_PUBLICKEYBYTES];
uint8_t magic_query[8];
uint8_t serial[4];
uint8_t ts_begin[4];
uint8_t ts_end[4];
};
#endif
File diff suppressed because it is too large Load Diff
@@ -1,175 +0,0 @@
#ifndef UNBOUND_DNSCRYPT_H
#define UNBOUND_DNSCRYPT_H
/**
* \file
* dnscrypt functions for encrypting DNS packets.
*/
#include "dnscrypt/dnscrypt_config.h"
#ifdef USE_DNSCRYPT
#define DNSCRYPT_MAGIC_HEADER_LEN 8U
#define DNSCRYPT_MAGIC_RESPONSE "r6fnvWj8"
#ifndef DNSCRYPT_MAX_PADDING
# define DNSCRYPT_MAX_PADDING 256U
#endif
#ifndef DNSCRYPT_BLOCK_SIZE
# define DNSCRYPT_BLOCK_SIZE 64U
#endif
#ifndef DNSCRYPT_MIN_PAD_LEN
# define DNSCRYPT_MIN_PAD_LEN 8U
#endif
#define crypto_box_HALF_NONCEBYTES (crypto_box_NONCEBYTES / 2U)
#include "config.h"
#include "dnscrypt/cert.h"
#include "util/locks.h"
#define DNSCRYPT_QUERY_HEADER_SIZE \
(DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES + crypto_box_MACBYTES)
#define DNSCRYPT_RESPONSE_HEADER_SIZE \
(DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_NONCEBYTES + crypto_box_MACBYTES)
#define DNSCRYPT_REPLY_HEADER_SIZE \
(DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES * 2 + crypto_box_MACBYTES)
struct sldns_buffer;
struct config_file;
struct comm_reply;
struct slabhash;
typedef struct KeyPair_ {
uint8_t crypt_publickey[crypto_box_PUBLICKEYBYTES];
uint8_t crypt_secretkey[crypto_box_SECRETKEYBYTES];
} KeyPair;
typedef struct cert_ {
uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN];
uint8_t es_version[2];
KeyPair *keypair;
} dnsccert;
struct dnsc_env {
struct SignedCert *signed_certs;
struct SignedCert **rotated_certs;
dnsccert *certs;
size_t signed_certs_count;
size_t rotated_certs_count;
uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES];
uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES];
KeyPair *keypairs;
size_t keypairs_count;
uint64_t nonce_ts_last;
unsigned char hash_key[crypto_shorthash_KEYBYTES];
char * provider_name;
/** Caches */
struct slabhash *shared_secrets_cache;
/** lock on shared secret cache counters */
lock_basic_type shared_secrets_cache_lock;
/** number of misses from shared_secrets_cache */
size_t num_query_dnscrypt_secret_missed_cache;
/** slabhash keeping track of nonce/cient pk/server sk pairs. */
struct slabhash *nonces_cache;
/** lock on nonces_cache, used to avoid race condition in updating the hash */
lock_basic_type nonces_cache_lock;
/** number of replayed queries */
size_t num_query_dnscrypt_replay;
};
struct dnscrypt_query_header {
uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN];
uint8_t publickey[crypto_box_PUBLICKEYBYTES];
uint8_t nonce[crypto_box_HALF_NONCEBYTES];
uint8_t mac[crypto_box_MACBYTES];
};
/**
* Initialize DNSCrypt environment.
* Initialize sodium library and allocate the dnsc_env structure.
* \return an uninitialized struct dnsc_env.
*/
struct dnsc_env * dnsc_create(void);
/**
* Apply configuration.
* Read certificates and secret keys from configuration. Initialize hashkey and
* provider name as well as loading cert TXT records.
* In case of issue applying configuration, this function fatals.
* \param[in] env the struct dnsc_env to populate.
* \param[in] cfg the config_file struct with dnscrypt options.
* \return 0 on success.
*/
int dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg);
/**
* Delete DNSCrypt environment
*
*/
void dnsc_delete(struct dnsc_env *env);
/**
* handle a crypted dnscrypt request.
* Determine whether or not a query is coming over the dnscrypt listener and
* attempt to uncurve it or detect if it is a certificate query.
* return 0 in case of failure.
*/
int dnsc_handle_curved_request(struct dnsc_env* dnscenv,
struct comm_reply* repinfo);
/**
* handle an unencrypted dnscrypt request.
* Determine whether or not a query is going over the dnscrypt channel and
* attempt to curve it unless it was not crypted like when it is a
* certificate query.
* \return 0 in case of failure.
*/
int dnsc_handle_uncurved_request(struct comm_reply *repinfo);
/**
* Computes the size of the shared secret cache entry.
*/
size_t dnsc_shared_secrets_sizefunc(void *k, void *d);
/**
* Compares two shared secret cache keys.
*/
int dnsc_shared_secrets_compfunc(void *m1, void *m2);
/**
* Function to delete a shared secret cache key.
*/
void dnsc_shared_secrets_delkeyfunc(void *k, void* arg);
/**
* Function to delete a share secret cache value.
*/
void dnsc_shared_secrets_deldatafunc(void* d, void* arg);
/**
* Computes the size of the nonce cache entry.
*/
size_t dnsc_nonces_sizefunc(void *k, void *d);
/**
* Compares two nonce cache keys.
*/
int dnsc_nonces_compfunc(void *m1, void *m2);
/**
* Function to delete a nonce cache key.
*/
void dnsc_nonces_delkeyfunc(void *k, void* arg);
/**
* Function to delete a nonce cache value.
*/
void dnsc_nonces_deldatafunc(void* d, void* arg);
#endif /* USE_DNSCRYPT */
#endif
@@ -1,44 +0,0 @@
# dnscrypt.m4
# dnsc_DNSCRYPT([action-if-true], [action-if-false])
# --------------------------------------------------------------------------
# Check for required dnscrypt libraries and add dnscrypt configure args.
AC_DEFUN([dnsc_DNSCRYPT],
[
AC_ARG_ENABLE([dnscrypt],
AS_HELP_STRING([--enable-dnscrypt],
[Enable dnscrypt support (requires libsodium)]),
[opt_dnscrypt=$enableval], [opt_dnscrypt=no])
if test "x$opt_dnscrypt" != "xno"; then
AC_ARG_WITH([libsodium], AS_HELP_STRING([--with-libsodium=path],
[Path where libsodium is installed, for dnscrypt]), [
CFLAGS="$CFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"
])
AC_SEARCH_LIBS([sodium_init], [sodium], [],
AC_MSG_ERROR([The sodium library was not found. Please install sodium!]))
AC_SEARCH_LIBS([crypto_box_curve25519xchacha20poly1305_beforenm], [sodium],
[
AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [1])
AC_DEFINE(
[USE_DNSCRYPT_XCHACHA20], [1],
[Define to 1 to enable dnscrypt with xchacha20 support])
],
[
AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [0])
])
AC_SEARCH_LIBS([sodium_set_misuse_handler], [sodium],
[
AC_DEFINE(
[SODIUM_MISUSE_HANDLER], [1],
[Define to 1 if libsodium supports sodium_set_misuse_handler])
],
[
])
$1
else
AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [0])
$2
fi
])
@@ -1 +0,0 @@
@@ -1,17 +0,0 @@
#ifndef UNBOUND_DNSCRYPT_CONFIG_H
#define UNBOUND_DNSCRYPT_CONFIG_H
/*
* Process this file (dnscrypt_config.h.in) with AC_CONFIG_FILES to generate
* dnscrypt_config.h.
*
* This file exists so that USE_DNSCRYPT can be used without including config.h.
*/
#if @ENABLE_DNSCRYPT@ /* ENABLE_DNSCRYPT */
# ifndef USE_DNSCRYPT
# define USE_DNSCRYPT 1
# endif
#endif
#endif /* UNBOUND_DNSCRYPT_CONFIG_H */
@@ -1,38 +0,0 @@
#!/bin/bash
CERT_EXPIRE_DAYS="$(( 365 * 15 ))"
DIR="$(dirname "$0")"
if [[ "$PWD" != *tdir ]]
then
echo "You should run this script with a .tdir directory"
exit 1
fi
for i in 1 2
do
# Ephemeral key
rm -f "${i}.key"
dnscrypt-wrapper --gen-crypt-keypair \
--crypt-secretkey-file="${i}.key" \
--provider-publickey-file="${DIR}/keys${i}/public.key" \
--provider-secretkey-file="${DIR}/keys${i}/secret.key"
# Cert file
for cipher in salsa chacha
do
rm -f "${i}_${cipher}.cert"
extraarg=""
if [ "${cipher}" == "chacha" ]
then
extraarg="-x"
fi
dnscrypt-wrapper ${extraarg} --gen-cert-file \
--provider-cert-file="${i}_${cipher}.cert" \
--crypt-secretkey-file="${i}.key" \
--provider-publickey-file="${DIR}/keys${i}/public.key" \
--provider-secretkey-file="${DIR}/keys${i}/secret.key" \
--cert-file-expire-days="${CERT_EXPIRE_DAYS}"
done
done
@@ -1 +0,0 @@
¸_A¡O#÷ÛÈfó—ÌoD¶_�eŶ)|'T¦éÜòO�
@@ -1 +0,0 @@
-&ç6\Uùõñh`^"…ºz/C9‚␍óˆ¸_A¡O#÷ÛÈfó—ÌoD¶_�eŶ)|'T¦éÜòO�
@@ -1 +0,0 @@
ΓR ς�ύeµτ{φl�ΑKΛ€Ξ:5r\±}KΣηƒ
@@ -1 +0,0 @@
ηi%‚ 5 fΝ™–ν Ιϋ�ν BnκΝ―.Ί“ΓR ς�ύeµτ{φl�ΑKΛ€Ξ:5r\±}KΣηƒ
@@ -1,570 +0,0 @@
/* dnstap support for Unbound */
/*
* Copyright (c) 2013-2014, Farsight Security, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder nor the names of its
* 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 COPYRIGHT HOLDER 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.
*/
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
#include "config.h"
#include <string.h>
#include <sys/time.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <errno.h>
#include "sldns/sbuffer.h"
#include "util/config_file.h"
#include "util/net_help.h"
#include "util/netevent.h"
#include "util/log.h"
#include <protobuf-c/protobuf-c.h>
#include "dnstap/dnstap.h"
#include "dnstap/dtstream.h"
#include "dnstap/dnstap.pb-c.h"
#define DNSTAP_INITIAL_BUF_SIZE 256
struct dt_msg {
void *buf;
size_t len_buf;
Dnstap__Dnstap d;
Dnstap__Message m;
};
static int
dt_pack(const Dnstap__Dnstap *d, void **buf, size_t *sz)
{
ProtobufCBufferSimple sbuf;
memset(&sbuf, 0, sizeof(sbuf));
sbuf.base.append = protobuf_c_buffer_simple_append;
sbuf.len = 0;
sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE;
sbuf.data = malloc(sbuf.alloced);
if (sbuf.data == NULL)
return 0;
sbuf.must_free_data = 1;
*sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf);
if (sbuf.data == NULL)
return 0;
*buf = sbuf.data;
return 1;
}
static void
dt_send(const struct dt_env *env, void *buf, size_t len_buf)
{
dt_msg_queue_submit(env->msgqueue, buf, len_buf);
}
static void
dt_msg_init(const struct dt_env *env,
struct dt_msg *dm,
Dnstap__Message__Type mtype)
{
memset(dm, 0, sizeof(*dm));
dm->d.base.descriptor = &dnstap__dnstap__descriptor;
dm->m.base.descriptor = &dnstap__message__descriptor;
dm->d.type = DNSTAP__DNSTAP__TYPE__MESSAGE;
dm->d.message = &dm->m;
dm->m.type = mtype;
if (env->identity != NULL) {
dm->d.identity.data = (uint8_t *) env->identity;
dm->d.identity.len = (size_t) env->len_identity;
dm->d.has_identity = 1;
}
if (env->version != NULL) {
dm->d.version.data = (uint8_t *) env->version;
dm->d.version.len = (size_t) env->len_version;
dm->d.has_version = 1;
}
}
/* check that the socket file can be opened and exists, print error if not */
static void
check_socket_file(const char* socket_path)
{
struct stat statbuf;
memset(&statbuf, 0, sizeof(statbuf));
if(stat(socket_path, &statbuf) < 0) {
log_warn("could not open dnstap-socket-path: %s, %s",
socket_path, strerror(errno));
}
}
struct dt_env *
dt_create(struct config_file* cfg)
{
struct dt_env *env;
if(cfg->dnstap && cfg->dnstap_socket_path && cfg->dnstap_socket_path[0] &&
(cfg->dnstap_ip==NULL || cfg->dnstap_ip[0]==0)) {
char* p = cfg->dnstap_socket_path;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(p,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
p += strlen(cfg->chrootdir);
verbose(VERB_OPS, "attempting to connect to dnstap socket %s",
p);
check_socket_file(p);
}
env = (struct dt_env *) calloc(1, sizeof(struct dt_env));
if (!env)
return NULL;
env->dtio = dt_io_thread_create();
if(!env->dtio) {
log_err("malloc failure");
free(env);
return NULL;
}
if(!dt_io_thread_apply_cfg(env->dtio, cfg)) {
dt_io_thread_delete(env->dtio);
free(env);
return NULL;
}
dt_apply_cfg(env, cfg);
return env;
}
static void
dt_apply_identity(struct dt_env *env, struct config_file *cfg)
{
char buf[MAXHOSTNAMELEN+1];
if (!cfg->dnstap_send_identity)
return;
free(env->identity);
if (cfg->dnstap_identity == NULL || cfg->dnstap_identity[0] == 0) {
if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
buf[MAXHOSTNAMELEN] = 0;
env->identity = strdup(buf);
} else {
fatal_exit("dt_apply_identity: gethostname() failed");
}
} else {
env->identity = strdup(cfg->dnstap_identity);
}
if (env->identity == NULL)
fatal_exit("dt_apply_identity: strdup() failed");
env->len_identity = (unsigned int)strlen(env->identity);
verbose(VERB_OPS, "dnstap identity field set to \"%s\"",
env->identity);
}
static void
dt_apply_version(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap_send_version)
return;
free(env->version);
if (cfg->dnstap_version == NULL || cfg->dnstap_version[0] == 0)
env->version = strdup(PACKAGE_STRING);
else
env->version = strdup(cfg->dnstap_version);
if (env->version == NULL)
fatal_exit("dt_apply_version: strdup() failed");
env->len_version = (unsigned int)strlen(env->version);
verbose(VERB_OPS, "dnstap version field set to \"%s\"",
env->version);
}
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap)
return;
dt_apply_identity(env, cfg);
dt_apply_version(env, cfg);
if ((env->log_resolver_query_messages = (unsigned int)
cfg->dnstap_log_resolver_query_messages))
{
verbose(VERB_OPS, "dnstap Message/RESOLVER_QUERY enabled");
}
if ((env->log_resolver_response_messages = (unsigned int)
cfg->dnstap_log_resolver_response_messages))
{
verbose(VERB_OPS, "dnstap Message/RESOLVER_RESPONSE enabled");
}
if ((env->log_client_query_messages = (unsigned int)
cfg->dnstap_log_client_query_messages))
{
verbose(VERB_OPS, "dnstap Message/CLIENT_QUERY enabled");
}
if ((env->log_client_response_messages = (unsigned int)
cfg->dnstap_log_client_response_messages))
{
verbose(VERB_OPS, "dnstap Message/CLIENT_RESPONSE enabled");
}
if ((env->log_forwarder_query_messages = (unsigned int)
cfg->dnstap_log_forwarder_query_messages))
{
verbose(VERB_OPS, "dnstap Message/FORWARDER_QUERY enabled");
}
if ((env->log_forwarder_response_messages = (unsigned int)
cfg->dnstap_log_forwarder_response_messages))
{
verbose(VERB_OPS, "dnstap Message/FORWARDER_RESPONSE enabled");
}
}
int
dt_init(struct dt_env *env, struct comm_base* base)
{
env->msgqueue = dt_msg_queue_create(base);
if(!env->msgqueue) {
log_err("malloc failure");
return 0;
}
if(!dt_io_thread_register_queue(env->dtio, env->msgqueue)) {
log_err("malloc failure");
dt_msg_queue_delete(env->msgqueue);
env->msgqueue = NULL;
return 0;
}
return 1;
}
void
dt_deinit(struct dt_env* env)
{
dt_io_thread_unregister_queue(env->dtio, env->msgqueue);
dt_msg_queue_delete(env->msgqueue);
}
void
dt_delete(struct dt_env *env)
{
if (!env)
return;
dt_io_thread_delete(env->dtio);
free(env->identity);
free(env->version);
free(env);
}
static void
dt_fill_timeval(const struct timeval *tv,
uint64_t *time_sec, protobuf_c_boolean *has_time_sec,
uint32_t *time_nsec, protobuf_c_boolean *has_time_nsec)
{
#ifndef S_SPLINT_S
*time_sec = tv->tv_sec;
*time_nsec = tv->tv_usec * 1000;
#endif
*has_time_sec = 1;
*has_time_nsec = 1;
}
static void
dt_fill_buffer(sldns_buffer *b, ProtobufCBinaryData *p, protobuf_c_boolean *has)
{
log_assert(b != NULL);
p->len = sldns_buffer_limit(b);
p->data = sldns_buffer_begin(b);
*has = 1;
}
static void
dt_msg_fill_net(struct dt_msg *dm,
struct sockaddr_storage *qs,
struct sockaddr_storage *rs,
enum comm_point_type cptype,
ProtobufCBinaryData *qaddr, protobuf_c_boolean *has_qaddr,
uint32_t *qport, protobuf_c_boolean *has_qport,
ProtobufCBinaryData *raddr, protobuf_c_boolean *has_raddr,
uint32_t *rport, protobuf_c_boolean *has_rport)
{
log_assert(qs->ss_family == AF_INET6 || qs->ss_family == AF_INET);
if (qs->ss_family == AF_INET6) {
struct sockaddr_in6 *q = (struct sockaddr_in6 *) qs;
/* socket_family */
dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6;
dm->m.has_socket_family = 1;
/* addr: query_address or response_address */
qaddr->data = q->sin6_addr.s6_addr;
qaddr->len = 16; /* IPv6 */
*has_qaddr = 1;
/* port: query_port or response_port */
*qport = ntohs(q->sin6_port);
*has_qport = 1;
} else if (qs->ss_family == AF_INET) {
struct sockaddr_in *q = (struct sockaddr_in *) qs;
/* socket_family */
dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET;
dm->m.has_socket_family = 1;
/* addr: query_address or response_address */
qaddr->data = (uint8_t *) &q->sin_addr.s_addr;
qaddr->len = 4; /* IPv4 */
*has_qaddr = 1;
/* port: query_port or response_port */
*qport = ntohs(q->sin_port);
*has_qport = 1;
}
/*
* This block is to fill second set of fields in DNSTAP-message defined as request_/response_ names.
* Additional responsive structure is: struct sockaddr_storage *rs
*/
if (rs && rs->ss_family == AF_INET6) {
struct sockaddr_in6 *r = (struct sockaddr_in6 *) rs;
/* addr: query_address or response_address */
raddr->data = r->sin6_addr.s6_addr;
raddr->len = 16; /* IPv6 */
*has_raddr = 1;
/* port: query_port or response_port */
*rport = ntohs(r->sin6_port);
*has_rport = 1;
} else if (rs && rs->ss_family == AF_INET) {
struct sockaddr_in *r = (struct sockaddr_in *) rs;
/* addr: query_address or response_address */
raddr->data = (uint8_t *) &r->sin_addr.s_addr;
raddr->len = 4; /* IPv4 */
*has_raddr = 1;
/* port: query_port or response_port */
*rport = ntohs(r->sin_port);
*has_rport = 1;
}
log_assert(cptype == comm_udp || cptype == comm_tcp);
if (cptype == comm_udp) {
/* socket_protocol */
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP;
dm->m.has_socket_protocol = 1;
} else if (cptype == comm_tcp) {
/* socket_protocol */
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP;
dm->m.has_socket_protocol = 1;
}
}
void
dt_msg_send_client_query(struct dt_env *env,
struct sockaddr_storage *qsock,
struct sockaddr_storage *rsock,
enum comm_point_type cptype,
sldns_buffer *qmsg,
struct timeval* tstamp)
{
struct dt_msg dm;
struct timeval qtime;
if(tstamp)
memcpy(&qtime, tstamp, sizeof(qtime));
else gettimeofday(&qtime, NULL);
/* type */
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__CLIENT_QUERY);
/* query_time */
dt_fill_timeval(&qtime,
&dm.m.query_time_sec, &dm.m.has_query_time_sec,
&dm.m.query_time_nsec, &dm.m.has_query_time_nsec);
/* query_message */
dt_fill_buffer(qmsg, &dm.m.query_message, &dm.m.has_query_message);
/* socket_family, socket_protocol, query_address, query_port, response_address, response_port */
log_assert(cptype == comm_udp || cptype == comm_tcp);
dt_msg_fill_net(&dm, qsock, rsock, cptype,
&dm.m.query_address, &dm.m.has_query_address,
&dm.m.query_port, &dm.m.has_query_port,
&dm.m.response_address, &dm.m.has_response_address,
&dm.m.response_port, &dm.m.has_response_port);
if (dt_pack(&dm.d, &dm.buf, &dm.len_buf))
dt_send(env, dm.buf, dm.len_buf);
}
void
dt_msg_send_client_response(struct dt_env *env,
struct sockaddr_storage *qsock,
struct sockaddr_storage *rsock,
enum comm_point_type cptype,
sldns_buffer *rmsg)
{
struct dt_msg dm;
struct timeval rtime;
gettimeofday(&rtime, NULL);
/* type */
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE);
/* response_time */
dt_fill_timeval(&rtime,
&dm.m.response_time_sec, &dm.m.has_response_time_sec,
&dm.m.response_time_nsec, &dm.m.has_response_time_nsec);
/* response_message */
dt_fill_buffer(rmsg, &dm.m.response_message, &dm.m.has_response_message);
/* socket_family, socket_protocol, query_address, query_port, response_address, response_port */
log_assert(cptype == comm_udp || cptype == comm_tcp);
dt_msg_fill_net(&dm, qsock, rsock, cptype,
&dm.m.query_address, &dm.m.has_query_address,
&dm.m.query_port, &dm.m.has_query_port,
&dm.m.response_address, &dm.m.has_response_address,
&dm.m.response_port, &dm.m.has_response_port);
if (dt_pack(&dm.d, &dm.buf, &dm.len_buf))
dt_send(env, dm.buf, dm.len_buf);
}
void
dt_msg_send_outside_query(struct dt_env *env,
struct sockaddr_storage *rsock,
struct sockaddr_storage *qsock,
enum comm_point_type cptype,
uint8_t *zone, size_t zone_len,
sldns_buffer *qmsg)
{
struct dt_msg dm;
struct timeval qtime;
uint16_t qflags;
gettimeofday(&qtime, NULL);
qflags = sldns_buffer_read_u16_at(qmsg, 2);
/* type */
if (qflags & BIT_RD) {
if (!env->log_forwarder_query_messages)
return;
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY);
} else {
if (!env->log_resolver_query_messages)
return;
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY);
}
/* query_zone */
dm.m.query_zone.data = zone;
dm.m.query_zone.len = zone_len;
dm.m.has_query_zone = 1;
/* query_time_sec, query_time_nsec */
dt_fill_timeval(&qtime,
&dm.m.query_time_sec, &dm.m.has_query_time_sec,
&dm.m.query_time_nsec, &dm.m.has_query_time_nsec);
/* query_message */
dt_fill_buffer(qmsg, &dm.m.query_message, &dm.m.has_query_message);
/* socket_family, socket_protocol, response_address, response_port, query_address, query_port */
log_assert(cptype == comm_udp || cptype == comm_tcp);
dt_msg_fill_net(&dm, rsock, qsock, cptype,
&dm.m.response_address, &dm.m.has_response_address,
&dm.m.response_port, &dm.m.has_response_port,
&dm.m.query_address, &dm.m.has_query_address,
&dm.m.query_port, &dm.m.has_query_port);
if (dt_pack(&dm.d, &dm.buf, &dm.len_buf))
dt_send(env, dm.buf, dm.len_buf);
}
void
dt_msg_send_outside_response(struct dt_env *env,
struct sockaddr_storage *rsock,
struct sockaddr_storage *qsock,
enum comm_point_type cptype,
uint8_t *zone, size_t zone_len,
uint8_t *qbuf, size_t qbuf_len,
const struct timeval *qtime,
const struct timeval *rtime,
sldns_buffer *rmsg)
{
struct dt_msg dm;
uint16_t qflags;
(void)qbuf_len; log_assert(qbuf_len >= sizeof(qflags));
memcpy(&qflags, qbuf, sizeof(qflags));
qflags = ntohs(qflags);
/* type */
if (qflags & BIT_RD) {
if (!env->log_forwarder_response_messages)
return;
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE);
} else {
if (!env->log_resolver_response_messages)
return;
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE);
}
/* query_zone */
dm.m.query_zone.data = zone;
dm.m.query_zone.len = zone_len;
dm.m.has_query_zone = 1;
/* query_time_sec, query_time_nsec */
dt_fill_timeval(qtime,
&dm.m.query_time_sec, &dm.m.has_query_time_sec,
&dm.m.query_time_nsec, &dm.m.has_query_time_nsec);
/* response_time_sec, response_time_nsec */
dt_fill_timeval(rtime,
&dm.m.response_time_sec, &dm.m.has_response_time_sec,
&dm.m.response_time_nsec, &dm.m.has_response_time_nsec);
/* response_message */
dt_fill_buffer(rmsg, &dm.m.response_message, &dm.m.has_response_message);
/* socket_family, socket_protocol, response_address, response_port, query_address, query_port */
log_assert(cptype == comm_udp || cptype == comm_tcp);
dt_msg_fill_net(&dm, rsock, qsock, cptype,
&dm.m.response_address, &dm.m.has_response_address,
&dm.m.response_port, &dm.m.has_response_port,
&dm.m.query_address, &dm.m.has_query_address,
&dm.m.query_port, &dm.m.has_query_port);
if (dt_pack(&dm.d, &dm.buf, &dm.len_buf))
dt_send(env, dm.buf, dm.len_buf);
}
#endif /* USE_DNSTAP */
@@ -1,203 +0,0 @@
/* dnstap support for Unbound */
/*
* Copyright (c) 2013-2014, Farsight Security, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder nor the names of its
* 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 COPYRIGHT HOLDER 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.
*/
#ifndef UNBOUND_DNSTAP_H
#define UNBOUND_DNSTAP_H
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
struct config_file;
struct sldns_buffer;
struct dt_msg_queue;
struct dt_env {
/** the io thread (made by the struct daemon) */
struct dt_io_thread* dtio;
/** valid in worker struct, not in daemon struct, the per-worker
* message list */
struct dt_msg_queue* msgqueue;
/** dnstap "identity" field, NULL if disabled */
char *identity;
/** dnstap "version" field, NULL if disabled */
char *version;
/** length of "identity" field */
unsigned len_identity;
/** length of "version" field */
unsigned len_version;
/** whether to log Message/RESOLVER_QUERY */
unsigned log_resolver_query_messages : 1;
/** whether to log Message/RESOLVER_RESPONSE */
unsigned log_resolver_response_messages : 1;
/** whether to log Message/CLIENT_QUERY */
unsigned log_client_query_messages : 1;
/** whether to log Message/CLIENT_RESPONSE */
unsigned log_client_response_messages : 1;
/** whether to log Message/FORWARDER_QUERY */
unsigned log_forwarder_query_messages : 1;
/** whether to log Message/FORWARDER_RESPONSE */
unsigned log_forwarder_response_messages : 1;
};
/**
* Create dnstap environment object. Afterwards, call dt_apply_cfg() to fill in
* the config variables and dt_init() to fill in the per-worker state. Each
* worker needs a copy of this object but with its own I/O queue (the fq field
* of the structure) to ensure lock-free access to its own per-worker circular
* queue. Duplicate the environment object if more than one worker needs to
* share access to the dnstap I/O socket.
* @param cfg: with config settings.
* @return dt_env object, NULL on failure.
*/
struct dt_env *
dt_create(struct config_file* cfg);
/**
* Apply config settings.
* @param env: dnstap environment object.
* @param cfg: new config settings.
*/
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg);
/**
* Initialize per-worker state in dnstap environment object.
* @param env: dnstap environment object to initialize, created with dt_create().
* @param base: event base for wakeup timer.
* @return: true on success, false on failure.
*/
int
dt_init(struct dt_env *env, struct comm_base* base);
/**
* Deletes the per-worker state created by dt_init
*/
void dt_deinit(struct dt_env *env);
/**
* Delete dnstap environment object. Closes dnstap I/O socket and deletes all
* per-worker I/O queues.
*/
void
dt_delete(struct dt_env *env);
/**
* Create and send a new dnstap "Message" event of type CLIENT_QUERY.
* @param env: dnstap environment object.
* @param qsock: address/port of client.
* @param rsock: local (service) address/port.
* @param cptype: comm_udp or comm_tcp.
* @param qmsg: query message.
* @param tstamp: timestamp or NULL if none provided.
*/
void
dt_msg_send_client_query(struct dt_env *env,
struct sockaddr_storage *qsock,
struct sockaddr_storage *rsock,
enum comm_point_type cptype,
struct sldns_buffer *qmsg,
struct timeval* tstamp);
/**
* Create and send a new dnstap "Message" event of type CLIENT_RESPONSE.
* @param env: dnstap environment object.
* @param qsock: address/port of client.
* @param rsock: local (service) address/port.
* @param cptype: comm_udp or comm_tcp.
* @param rmsg: response message.
*/
void
dt_msg_send_client_response(struct dt_env *env,
struct sockaddr_storage *qsock,
struct sockaddr_storage *rsock,
enum comm_point_type cptype,
struct sldns_buffer *rmsg);
/**
* Create and send a new dnstap "Message" event of type RESOLVER_QUERY or
* FORWARDER_QUERY. The type used is dependent on the value of the RD bit
* in the query header.
* @param env: dnstap environment object.
* @param rsock: address/port of server (upstream) the query is being sent to.
* @param qsock: address/port of server (local) the query is being sent from.
* @param cptype: comm_udp or comm_tcp.
* @param zone: query zone.
* @param zone_len: length of zone.
* @param qmsg: query message.
*/
void
dt_msg_send_outside_query(struct dt_env *env,
struct sockaddr_storage *rsock,
struct sockaddr_storage *qsock,
enum comm_point_type cptype,
uint8_t *zone, size_t zone_len,
struct sldns_buffer *qmsg);
/**
* Create and send a new dnstap "Message" event of type RESOLVER_RESPONSE or
* FORWARDER_RESPONSE. The type used is dependent on the value of the RD bit
* in the query header.
* @param env: dnstap environment object.
* @param rsock: address/port of server (upstream) the response was received from.
* @param qsock: address/port of server (local) the response was received to.
* @param cptype: comm_udp or comm_tcp.
* @param zone: query zone.
* @param zone_len: length of zone.
* @param qbuf: outside_network's qbuf key.
* @param qbuf_len: length of outside_network's qbuf key.
* @param qtime: time query message was sent.
* @param rtime: time response message was sent.
* @param rmsg: response message.
*/
void
dt_msg_send_outside_response(struct dt_env *env,
struct sockaddr_storage *rsock,
struct sockaddr_storage *qsock,
enum comm_point_type cptype,
uint8_t *zone, size_t zone_len,
uint8_t *qbuf, size_t qbuf_len,
const struct timeval *qtime,
const struct timeval *rtime,
struct sldns_buffer *rmsg);
#endif /* USE_DNSTAP */
#endif /* UNBOUND_DNSTAP_H */
@@ -1,49 +0,0 @@
# dnstap.m4
# dt_DNSTAP(default_dnstap_socket_path, [action-if-true], [action-if-false])
# --------------------------------------------------------------------------
# Check for required dnstap libraries and add dnstap configure args.
AC_DEFUN([dt_DNSTAP],
[
AC_ARG_ENABLE([dnstap],
AS_HELP_STRING([--enable-dnstap],
[Enable dnstap support (requires protobuf-c)]),
[opt_dnstap=$enableval], [opt_dnstap=no])
AC_ARG_WITH([dnstap-socket-path],
AS_HELP_STRING([--with-dnstap-socket-path=pathname],
[set default dnstap socket path]),
[opt_dnstap_socket_path=$withval], [opt_dnstap_socket_path="$1"])
if test "x$opt_dnstap" != "xno"; then
AC_PATH_PROG([PROTOC_C], [protoc-c])
if test -z "$PROTOC_C"; then
AC_MSG_ERROR([The protoc-c program was not found. Please install protobuf-c!])
fi
AC_ARG_WITH([protobuf-c], AS_HELP_STRING([--with-protobuf-c=path],
[Path where protobuf-c is installed, for dnstap]), [
# workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
if test -f $withval/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I$withval/include/google"
else
CFLAGS="$CFLAGS -I$withval/include"
fi
LDFLAGS="$LDFLAGS -L$withval/lib"
], [
# workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I/usr/include/google"
else
if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I/usr/local/include/google"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
fi
fi
])
AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [],
AC_MSG_ERROR([The protobuf-c library was not found. Please install protobuf-c!]))
$2
else
$3
fi
])
@@ -1,263 +0,0 @@
// dnstap: flexible, structured event replication format for DNS software
//
// This file contains the protobuf schemas for the "dnstap" structured event
// replication format for DNS software.
// Written in 2013-2014 by Farsight Security, Inc.
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this file to the public
// domain worldwide. This file is distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along
// with this file. If not, see:
//
// <http://creativecommons.org/publicdomain/zero/1.0/>.
syntax = "proto2";
package dnstap;
// "Dnstap": this is the top-level dnstap type, which is a "union" type that
// contains other kinds of dnstap payloads, although currently only one type
// of dnstap payload is defined.
// See: https://developers.google.com/protocol-buffers/docs/techniques#union
message Dnstap {
// DNS server identity.
// If enabled, this is the identity string of the DNS server which generated
// this message. Typically this would be the same string as returned by an
// "NSID" (RFC 5001) query.
optional bytes identity = 1;
// DNS server version.
// If enabled, this is the version string of the DNS server which generated
// this message. Typically this would be the same string as returned by a
// "version.bind" query.
optional bytes version = 2;
// Extra data for this payload.
// This field can be used for adding an arbitrary byte-string annotation to
// the payload. No encoding or interpretation is applied or enforced.
optional bytes extra = 3;
// Identifies which field below is filled in.
enum Type {
MESSAGE = 1;
}
required Type type = 15;
// One of the following will be filled in.
optional Message message = 14;
}
// SocketFamily: the network protocol family of a socket. This specifies how
// to interpret "network address" fields.
enum SocketFamily {
INET = 1; // IPv4 (RFC 791)
INET6 = 2; // IPv6 (RFC 2460)
}
// SocketProtocol: the transport protocol of a socket. This specifies how to
// interpret "transport port" fields.
enum SocketProtocol {
UDP = 1; // User Datagram Protocol (RFC 768)
TCP = 2; // Transmission Control Protocol (RFC 793)
}
// Message: a wire-format (RFC 1035 section 4) DNS message and associated
// metadata. Applications generating "Message" payloads should follow
// certain requirements based on the MessageType, see below.
message Message {
// There are eight types of "Message" defined that correspond to the
// four arrows in the following diagram, slightly modified from RFC 1035
// section 2:
// +---------+ +----------+ +--------+
// | | query | | query | |
// | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. |
// | Resolver| | Server | | Name |
// | |<-SR--------CR-| |<-RR----AR-| Server |
// +---------+ response | | response | |
// +----------+ +--------+
// Each arrow has two Type values each, one for each "end" of each arrow,
// because these are considered to be distinct events. Each end of each
// arrow on the diagram above has been marked with a two-letter Type
// mnemonic. Clockwise from upper left, these mnemonic values are:
//
// SQ: STUB_QUERY
// CQ: CLIENT_QUERY
// RQ: RESOLVER_QUERY
// AQ: AUTH_QUERY
// AR: AUTH_RESPONSE
// RR: RESOLVER_RESPONSE
// CR: CLIENT_RESPONSE
// SR: STUB_RESPONSE
// Two additional types of "Message" have been defined for the
// "forwarding" case where an upstream DNS server is responsible for
// further recursion. These are not shown on the diagram above, but have
// the following mnemonic values:
// FQ: FORWARDER_QUERY
// FR: FORWARDER_RESPONSE
// The "Message" Type values are defined below.
enum Type {
// AUTH_QUERY is a DNS query message received from a resolver by an
// authoritative name server, from the perspective of the authoritative
// name server.
AUTH_QUERY = 1;
// AUTH_RESPONSE is a DNS response message sent from an authoritative
// name server to a resolver, from the perspective of the authoritative
// name server.
AUTH_RESPONSE = 2;
// RESOLVER_QUERY is a DNS query message sent from a resolver to an
// authoritative name server, from the perspective of the resolver.
// Resolvers typically clear the RD (recursion desired) bit when
// sending queries.
RESOLVER_QUERY = 3;
// RESOLVER_RESPONSE is a DNS response message received from an
// authoritative name server by a resolver, from the perspective of
// the resolver.
RESOLVER_RESPONSE = 4;
// CLIENT_QUERY is a DNS query message sent from a client to a DNS
// server which is expected to perform further recursion, from the
// perspective of the DNS server. The client may be a stub resolver or
// forwarder or some other type of software which typically sets the RD
// (recursion desired) bit when querying the DNS server. The DNS server
// may be a simple forwarding proxy or it may be a full recursive
// resolver.
CLIENT_QUERY = 5;
// CLIENT_RESPONSE is a DNS response message sent from a DNS server to
// a client, from the perspective of the DNS server. The DNS server
// typically sets the RA (recursion available) bit when responding.
CLIENT_RESPONSE = 6;
// FORWARDER_QUERY is a DNS query message sent from a downstream DNS
// server to an upstream DNS server which is expected to perform
// further recursion, from the perspective of the downstream DNS
// server.
FORWARDER_QUERY = 7;
// FORWARDER_RESPONSE is a DNS response message sent from an upstream
// DNS server performing recursion to a downstream DNS server, from the
// perspective of the downstream DNS server.
FORWARDER_RESPONSE = 8;
// STUB_QUERY is a DNS query message sent from a stub resolver to a DNS
// server, from the perspective of the stub resolver.
STUB_QUERY = 9;
// STUB_RESPONSE is a DNS response message sent from a DNS server to a
// stub resolver, from the perspective of the stub resolver.
STUB_RESPONSE = 10;
}
// One of the Type values described above.
required Type type = 1;
// One of the SocketFamily values described above.
optional SocketFamily socket_family = 2;
// One of the SocketProtocol values described above.
optional SocketProtocol socket_protocol = 3;
// The network address of the message initiator.
// For SocketFamily INET, this field is 4 octets (IPv4 address).
// For SocketFamily INET6, this field is 16 octets (IPv6 address).
optional bytes query_address = 4;
// The network address of the message responder.
// For SocketFamily INET, this field is 4 octets (IPv4 address).
// For SocketFamily INET6, this field is 16 octets (IPv6 address).
optional bytes response_address = 5;
// The transport port of the message initiator.
// This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
optional uint32 query_port = 6;
// The transport port of the message responder.
// This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
optional uint32 response_port = 7;
// The time at which the DNS query message was sent or received, depending
// on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY.
// This is the number of seconds since the UNIX epoch.
optional uint64 query_time_sec = 8;
// The time at which the DNS query message was sent or received.
// This is the seconds fraction, expressed as a count of nanoseconds.
optional fixed32 query_time_nsec = 9;
// The initiator's original wire-format DNS query message, verbatim.
optional bytes query_message = 10;
// The "zone" or "bailiwick" pertaining to the DNS query message.
// This is a wire-format DNS domain name.
optional bytes query_zone = 11;
// The time at which the DNS response message was sent or received,
// depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or
// CLIENT_RESPONSE.
// This is the number of seconds since the UNIX epoch.
optional uint64 response_time_sec = 12;
// The time at which the DNS response message was sent or received.
// This is the seconds fraction, expressed as a count of nanoseconds.
optional fixed32 response_time_nsec = 13;
// The responder's original wire-format DNS response message, verbatim.
optional bytes response_message = 14;
}
// All fields except for 'type' in the Message schema are optional.
// It is recommended that at least the following fields be filled in for
// particular types of Messages.
// AUTH_QUERY:
// socket_family, socket_protocol
// query_address, query_port
// query_message
// query_time_sec, query_time_nsec
// AUTH_RESPONSE:
// socket_family, socket_protocol
// query_address, query_port
// query_time_sec, query_time_nsec
// response_message
// response_time_sec, response_time_nsec
// RESOLVER_QUERY:
// socket_family, socket_protocol
// query_name, query_type, query_class
// query_message
// query_time_sec, query_time_nsec
// query_zone
// response_address, response_port
// RESOLVER_RESPONSE:
// socket_family, socket_protocol
// query_name, query_type, query_class
// query_time_sec, query_time_nsec
// query_zone
// response_address, response_port
// response_message
// response_time_sec, response_time_nsec
// CLIENT_QUERY:
// socket_family, socket_protocol
// query_message
// query_time_sec, query_time_nsec
// CLIENT_RESPONSE:
// socket_family, socket_protocol
// query_time_sec, query_time_nsec
// response_message
// response_time_sec, response_time_nsec
@@ -1 +0,0 @@
@@ -1,17 +0,0 @@
#ifndef UNBOUND_DNSTAP_CONFIG_H
#define UNBOUND_DNSTAP_CONFIG_H
/*
* Process this file (dnstap_config.h.in) with AC_CONFIG_FILES to generate
* dnstap_config.h.
*
* This file exists so that USE_DNSTAP can be used without including config.h.
*/
#if @ENABLE_DNSTAP@ /* ENABLE_DNSTAP */
# ifndef USE_DNSTAP
# define USE_DNSTAP 1
# endif
#endif
#endif /* UNBOUND_DNSTAP_CONFIG_H */
@@ -1,236 +0,0 @@
/*
* dnstap/dnstap_fstrm.c - Frame Streams protocol for dnstap
*
* Copyright (c) 2020, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*
*/
/**
* \file
*
* Definitions for the Frame Streams data transport protocol for
* dnstap message logs.
*/
#include "config.h"
#include "dnstap/dnstap_fstrm.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
void* fstrm_create_control_frame_start(char* contenttype, size_t* len)
{
uint32_t* control;
size_t n;
/* start framestream message:
* 4byte 0: control indicator.
* 4byte bigendian: length of control frame
* 4byte bigendian: type START
* 4byte bigendian: option: content-type
* 4byte bigendian: length of string
* string of content type (dnstap)
*/
n = 4+4+4+4+4+strlen(contenttype);
control = malloc(n);
if(!control)
return NULL;
control[0] = 0;
control[1] = htonl(4+4+4+strlen(contenttype));
control[2] = htonl(FSTRM_CONTROL_FRAME_START);
control[3] = htonl(FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE);
control[4] = htonl(strlen(contenttype));
memmove(&control[5], contenttype, strlen(contenttype));
*len = n;
return control;
}
void* fstrm_create_control_frame_stop(size_t* len)
{
uint32_t* control;
size_t n;
/* stop framestream message:
* 4byte 0: control indicator.
* 4byte bigendian: length of control frame
* 4byte bigendian: type STOP
*/
n = 4+4+4;
control = malloc(n);
if(!control)
return NULL;
control[0] = 0;
control[1] = htonl(4);
control[2] = htonl(FSTRM_CONTROL_FRAME_STOP);
*len = n;
return control;
}
void* fstrm_create_control_frame_ready(char* contenttype, size_t* len)
{
uint32_t* control;
size_t n;
/* start bidirectional stream:
* 4 bytes 0 escape
* 4 bytes bigendian length of frame
* 4 bytes bigendian type READY
* 4 bytes bigendian frame option content type
* 4 bytes bigendian length of string
* string of content type.
*/
/* len includes the escape and framelength */
n = 4+4+4+4+4+strlen(contenttype);
control = malloc(n);
if(!control) {
return NULL;
}
control[0] = 0;
control[1] = htonl(4+4+4+strlen(contenttype));
control[2] = htonl(FSTRM_CONTROL_FRAME_READY);
control[3] = htonl(FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE);
control[4] = htonl(strlen(contenttype));
memmove(&control[5], contenttype, strlen(contenttype));
*len = n;
return control;
}
void* fstrm_create_control_frame_accept(char* contenttype, size_t* len)
{
uint32_t* control;
size_t n;
/* control frame on reply:
* 4 bytes 0 escape
* 4 bytes bigendian length of frame
* 4 bytes bigendian type ACCEPT
* 4 bytes bigendian frame option content type
* 4 bytes bigendian length of string
* string of content type.
*/
/* len includes the escape and framelength */
n = 4+4+4+4+4+strlen(contenttype);
control = malloc(n);
if(!control) {
return NULL;
}
control[0] = 0;
control[1] = htonl(4+4+4+strlen(contenttype));
control[2] = htonl(FSTRM_CONTROL_FRAME_ACCEPT);
control[3] = htonl(FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE);
control[4] = htonl(strlen(contenttype));
memmove(&control[5], contenttype, strlen(contenttype));
*len = n;
return control;
}
void* fstrm_create_control_frame_finish(size_t* len)
{
uint32_t* control;
size_t n;
/* control frame on reply:
* 4 bytes 0 escape
* 4 bytes bigendian length of frame
* 4 bytes bigendian type FINISH
*/
/* len includes the escape and framelength */
n = 4+4+4;
control = malloc(n);
if(!control) {
return NULL;
}
control[0] = 0;
control[1] = htonl(4);
control[2] = htonl(FSTRM_CONTROL_FRAME_FINISH);
*len = n;
return control;
}
char* fstrm_describe_control(void* pkt, size_t len)
{
uint32_t frametype = 0;
char buf[512];
char* str = buf;
size_t remain, slen = sizeof(buf);
uint8_t* pos;
buf[0]=0;
if(len < 4) {
snprintf(buf, sizeof(buf), "malformed control frame, "
"too short, len=%u", (unsigned int)len);
return strdup(buf);
}
frametype = sldns_read_uint32(pkt);
if(frametype == FSTRM_CONTROL_FRAME_ACCEPT) {
(void)sldns_str_print(&str, &slen, "accept");
} else if(frametype == FSTRM_CONTROL_FRAME_START) {
(void)sldns_str_print(&str, &slen, "start");
} else if(frametype == FSTRM_CONTROL_FRAME_STOP) {
(void)sldns_str_print(&str, &slen, "stop");
} else if(frametype == FSTRM_CONTROL_FRAME_READY) {
(void)sldns_str_print(&str, &slen, "ready");
} else if(frametype == FSTRM_CONTROL_FRAME_FINISH) {
(void)sldns_str_print(&str, &slen, "finish");
} else {
(void)sldns_str_print(&str, &slen, "type%d", (int)frametype);
}
/* show the content type options */
pos = pkt + 4;
remain = len - 4;
while(remain >= 8) {
uint32_t field_type = sldns_read_uint32(pos);
uint32_t field_len = sldns_read_uint32(pos+4);
if(remain < field_len) {
(void)sldns_str_print(&str, &slen, "malformed_field");
break;
}
if(field_type == FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE) {
char tempf[512];
(void)sldns_str_print(&str, &slen, " content-type(");
if(field_len < sizeof(tempf)-1) {
memmove(tempf, pos+8, field_len);
tempf[field_len] = 0;
(void)sldns_str_print(&str, &slen, "%s", tempf);
} else {
(void)sldns_str_print(&str, &slen, "<error-too-long>");
}
(void)sldns_str_print(&str, &slen, ")");
} else {
(void)sldns_str_print(&str, &slen,
" field(type %u, length %u)",
(unsigned int)field_type,
(unsigned int)field_len);
}
pos += 8 + field_len;
remain -= (8 + field_len);
}
if(remain > 0)
(void)sldns_str_print(&str, &slen, " trailing-bytes"
"(length %u)", (unsigned int)remain);
return strdup(buf);
}
@@ -1,194 +0,0 @@
/*
* dnstap/dnstap_fstrm.h - Frame Streams protocol for dnstap
*
* Copyright (c) 2020, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*
*/
/**
* \file
*
* Definitions for the Frame Streams data transport protocol for
* dnstap message logs.
*/
#ifndef DNSTAP_FSTRM_H
#define DNSTAP_FSTRM_H
/* Frame Streams data transfer protocol encode for DNSTAP messages.
* The protocol looks to be specified in the libfstrm library.
*
* Quick writeup for DNSTAP usage, from reading fstrm/control.h eloquent
* comments and fstrm/control.c for some bytesize details (the content type
* length).
*
* The Frame Streams can be unidirectional or bi-directional.
* bi-directional streams use control frame types READY, ACCEPT and FINISH.
* uni-directional streams use control frame types START and STOP.
* unknown control frame types should be ignored by the receiver, they
* do not change the data frame encoding.
*
* bi-directional control frames implement a simple handshake protocol
* between sender and receiver.
*
* The uni-directional control frames have one start and one stop frame,
* before and after the data. The start frame can have a content type.
* The start and stop frames are not optional.
*
* data frames are preceded by 4byte length, bigendian.
* zero length data frames are not possible, they are an escape that
* signals the presence of a control frame.
*
* a control frame consists of 0 value in 4byte bigendian, this is really
* the data frame length, with 0 the escape sequence that indicates one
* control frame follows.
* Then, 4byte bigendian, length of the control frame message.
* Then, the control frame payload (of that length). with in it:
* 4byte bigendian, control type (eg. START, STOP, READY, ACCEPT, FINISH).
* perhaps nothing more (STOP, FINISH), but for other types maybe
* control fields
* 4byte bigendian, the control-field-type, currently only content-type.
* 4byte bigendian, length of the string for this option.
* .. bytes of that string.
*
* The START type can have only one field. Field max len 256.
* control frame max frame length 512 (excludes the 0-escape and control
* frame length bytes).
*
* the bidirectional type of transmission is like this:
* client sends READY (with content type included),
* client waits for ACCEPT (with content type included),
* client sends START (with matched content type from ACCEPT)
* .. data frames
* client sends STOP.
* client waits for FINISH frame.
*
*/
/** max length of Frame Streams content type field string */
#define FSTRM_CONTENT_TYPE_LENGTH_MAX 256
/** control frame value to denote the control frame ACCEPT */
#define FSTRM_CONTROL_FRAME_ACCEPT 0x01
/** control frame value to denote the control frame START */
#define FSTRM_CONTROL_FRAME_START 0x02
/** control frame value to denote the control frame STOP */
#define FSTRM_CONTROL_FRAME_STOP 0x03
/** control frame value to denote the control frame READY */
#define FSTRM_CONTROL_FRAME_READY 0x04
/** control frame value to denote the control frame FINISH */
#define FSTRM_CONTROL_FRAME_FINISH 0x05
/** the constant that denotes the control field type that is the
* string for the content type of the stream. */
#define FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE 0x01
/** the content type for DNSTAP frame streams */
#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap"
/**
* This creates an FSTRM control frame of type START.
* @param contenttype: a zero delimited string with the content type.
* eg. use the constant DNSTAP_CONTENT_TYPE, which is defined as
* "protobuf:dnstap.Dnstap", for a dnstap frame stream.
* @param len: if a buffer is returned this is the length of that buffer.
* @return NULL on malloc failure. Returns a malloced buffer with the
* protocol message. The buffer starts with the 4 bytes of 0 that indicate
* a control frame. The buffer should be sent without preceding it with
* the 'len' variable (like data frames are), but straight the content of the
* buffer, because the lengths are included in the buffer. This is so that
* the zero control indicator can be included before the control frame length.
*/
void* fstrm_create_control_frame_start(char* contenttype, size_t* len);
/**
* This creates an FSTRM control frame of type READY.
* @param contenttype: a zero delimited string with the content type.
* eg. use the constant DNSTAP_CONTENT_TYPE, which is defined as
* "protobuf:dnstap.Dnstap", for a dnstap frame stream.
* @param len: if a buffer is returned this is the length of that buffer.
* @return NULL on malloc failure. Returns a malloced buffer with the
* protocol message. The buffer starts with the 4 bytes of 0 that indicate
* a control frame. The buffer should be sent without preceding it with
* the 'len' variable (like data frames are), but straight the content of the
* buffer, because the lengths are included in the buffer. This is so that
* the zero control indicator can be included before the control frame length.
*/
void* fstrm_create_control_frame_ready(char* contenttype, size_t* len);
/**
* This creates an FSTRM control frame of type STOP.
* @param len: if a buffer is returned this is the length of that buffer.
* @return NULL on malloc failure. Returns a malloced buffer with the
* protocol message. The buffer starts with the 4 bytes of 0 that indicate
* a control frame. The buffer should be sent without preceding it with
* the 'len' variable (like data frames are), but straight the content of the
* buffer, because the lengths are included in the buffer. This is so that
* the zero control indicator can be included before the control frame length.
*/
void* fstrm_create_control_frame_stop(size_t* len);
/**
* This creates an FSTRM control frame of type ACCEPT.
* @param contenttype: a zero delimited string with the content type.
* for dnstap streams use DNSTAP_CONTENT_TYPE.
* @param len: if a buffer is returned this is the length of that buffer.
* @return NULL on malloc failure. Returns a malloced buffer with the
* protocol message. The buffer starts with the 4 bytes of 0 that indicate
* a control frame. The buffer should be sent without preceding it with
* the 'len' variable (like data frames are), but straight the content of the
* buffer, because the lengths are included in the buffer. This is so that
* the zero control indicator can be included before the control frame length.
*/
void* fstrm_create_control_frame_accept(char* contenttype, size_t* len);
/**
* This creates an FSTRM control frame of type FINISH.
* @param len: if a buffer is returned this is the length of that buffer.
* @return NULL on malloc failure. Returns a malloced buffer with the
* protocol message. The buffer starts with the 4 bytes of 0 that indicate
* a control frame. The buffer should be sent without preceding it with
* the 'len' variable (like data frames are), but straight the content of the
* buffer, because the lengths are included in the buffer. This is so that
* the zero control indicator can be included before the control frame length.
*/
void* fstrm_create_control_frame_finish(size_t* len);
/**
* Return string that describes a control packet. For debug, logs.
* Like 'start content-type(protobuf:dnstap.Dnstap)' or 'stop'.
* @param pkt: the packet data, that is the data after the 4 zero start
* bytes and 4 length bytes.
* @param len: the length of the control packet data, in pkt. This is the
* ntohl of the 4 bytes length preceding the data.
* @return zero delimited string, malloced. Or NULL on malloc failure.
*/
char* fstrm_describe_control(void* pkt, size_t len);
#endif /* DNSTAP_FSTRM_H */
File diff suppressed because it is too large Load Diff
@@ -1,354 +0,0 @@
/*
* dnstap/dtstream.h - Frame Streams thread for unbound DNSTAP
*
* Copyright (c) 2020, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*
*/
/**
* \file
*
* An implementation of the Frame Streams data transport protocol for
* the Unbound DNSTAP message logging facility.
*/
#ifndef DTSTREAM_H
#define DTSTREAM_H
#include "util/locks.h"
struct dt_msg_entry;
struct dt_io_list_item;
struct dt_io_thread;
struct config_file;
struct comm_base;
/**
* A message buffer with dnstap messages queued up. It is per-worker.
* It has locks to synchronize. If the buffer is full, a new message
* cannot be added and is discarded. A thread reads the messages and sends
* them.
*/
struct dt_msg_queue {
/** lock of the buffer structure. Hold this lock to add or remove
* entries to the buffer. Release it so that other threads can also
* put messages to log, or a message can be taken out to send away
* by the writer thread.
*/
lock_basic_type lock;
/** the maximum size of the buffer, in bytes */
size_t maxsize;
/** current size of the buffer, in bytes. data bytes of messages.
* If a new message make it more than maxsize, the buffer is full */
size_t cursize;
/** number of messages in the queue */
int msgcount;
/** list of messages. The messages are added to the back and taken
* out from the front. */
struct dt_msg_entry* first, *last;
/** reference to the io thread to wakeup */
struct dt_io_thread* dtio;
/** the wakeup timer for dtio, on worker event base */
struct comm_timer* wakeup_timer;
};
/**
* An entry in the dt_msg_queue. contains one DNSTAP message.
* It is malloced.
*/
struct dt_msg_entry {
/** next in the list. */
struct dt_msg_entry* next;
/** the buffer with the data to send, an encoded DNSTAP message */
void* buf;
/** the length to send. */
size_t len;
};
/**
* Containing buffer and counter for reading DNSTAP frames.
*/
struct dt_frame_read_buf {
/** Buffer containing frame, except length counter(s). */
void* buf;
/** Number of bytes written to buffer. */
size_t buf_count;
/** Capacity of the buffer. */
size_t buf_cap;
/** Frame length field. Will contain the 2nd length field for control
* frames. */
uint32_t frame_len;
/** Number of bytes that have been written to the frame_length field. */
size_t frame_len_done;
/** Set to 1 if this is a control frame, 0 otherwise (ie data frame). */
int control_frame;
};
/**
* IO thread that reads from the queues and writes them.
*/
struct dt_io_thread {
/** the thread number for the dtio thread,
* must be first to cast thread arg to int* in checklock code. */
int threadnum;
/** event base, for event handling */
void* event_base;
/** list of queues that is registered to get written */
struct dt_io_list_item* io_list;
/** iterator point in the io_list, to pick from them in a
* round-robin fashion, instead of only from the first when busy.
* if NULL it means start at the start of the list. */
struct dt_io_list_item* io_list_iter;
/** thread id, of the io thread */
ub_thread_type tid;
/** if the io processing has started */
int started;
/** ssl context for the io thread, for tls connections. type SSL_CTX* */
void* ssl_ctx;
/** if SNI will be used for TLS connections. */
int tls_use_sni;
/** file descriptor that the thread writes to */
int fd;
/** event structure that the thread uses */
void* event;
/** the event is added */
int event_added;
/** event added is a write event */
int event_added_is_write;
/** check for nonblocking connect errors on fd */
int check_nb_connect;
/** ssl for current connection, type SSL* */
void* ssl;
/** true if the handshake for SSL is done, 0 if not */
int ssl_handshake_done;
/** true if briefly the SSL wants a read event, 0 if not.
* This happens during negotiation, we then do not want to write,
* but wait for a read event. */
int ssl_brief_read;
/** true if SSL_read is waiting for a write event. Set back to 0 after
* single write event is handled. */
int ssl_brief_write;
/** the buffer that currently getting written, or NULL if no
* (partial) message written now */
void* cur_msg;
/** length of the current message */
size_t cur_msg_len;
/** number of bytes written for the current message */
size_t cur_msg_done;
/** number of bytes of the length that have been written,
* for the current message length that precedes the frame */
size_t cur_msg_len_done;
/** lock on wakeup_timer_enabled */
lock_basic_type wakeup_timer_lock;
/** if wakeup timer is enabled in some thread */
int wakeup_timer_enabled;
/** command pipe that stops the pipe if closed. Used to quit
* the program. [0] is read, [1] is written to. */
int commandpipe[2];
/** the event to listen to the commandpipe */
void* command_event;
/** the io thread wants to exit */
int want_to_exit;
/** in stop flush, this is nonNULL and references the stop_ev */
void* stop_flush_event;
/** the timer event for connection retries */
void* reconnect_timer;
/** if the reconnect timer is added to the event base */
int reconnect_is_added;
/** the current reconnection timeout, it is increased with
* exponential backoff, in msec */
int reconnect_timeout;
/** If the log server is connected to over unix domain sockets,
* eg. a file is named that is created to log onto. */
int upstream_is_unix;
/** if the log server is connected to over TCP. The ip address and
* port are used */
int upstream_is_tcp;
/** if the log server is connected to over TLS. ip address, port,
* and client certificates can be used for authentication. */
int upstream_is_tls;
/** Perform bidirectional Frame Streams handshake before sending
* messages. */
int is_bidirectional;
/** Set if the READY control frame has been sent. */
int ready_frame_sent;
/** Set if valid ACCEPT frame is received. */
int accept_frame_received;
/** (partially) read frame */
struct dt_frame_read_buf read_frame;
/** the file path for unix socket (or NULL) */
char* socket_path;
/** the ip address and port number (or NULL) */
char* ip_str;
/** is the TLS upstream authenticated by name, if nonNULL,
* we use the same cert bundle as used by other TLS streams. */
char* tls_server_name;
/** are client certificates in use */
int use_client_certs;
/** client cert files: the .key file */
char* client_key_file;
/** client cert files: the .pem file */
char* client_cert_file;
};
/**
* IO thread list of queues list item
* lists a worker queue that should be looked at and sent to the log server.
*/
struct dt_io_list_item {
/** next in the list of buffers to inspect */
struct dt_io_list_item* next;
/** buffer of this worker */
struct dt_msg_queue* queue;
};
/**
* Create new (empty) worker message queue. Limit set to default on max.
* @param base: event base for wakeup timer.
* @return NULL on malloc failure or a new queue (not locked).
*/
struct dt_msg_queue* dt_msg_queue_create(struct comm_base* base);
/**
* Delete a worker message queue. It has to be unlinked from access,
* so it can be deleted without lock worries. The queue is emptied (deleted).
* @param mq: message queue.
*/
void dt_msg_queue_delete(struct dt_msg_queue* mq);
/**
* Submit a message to the queue. The queue is locked by the routine,
* the message is inserted, and then the queue is unlocked so the
* message can be picked up by the writer thread.
* @param mq: message queue.
* @param buf: buffer with message (dnstap contents).
* The buffer must have been malloced by caller. It is linked in
* the queue, and is free()d after use. If the routine fails
* the buffer is freed as well (and nothing happens, the item
* could not be logged).
* @param len: length of buffer.
*/
void dt_msg_queue_submit(struct dt_msg_queue* mq, void* buf, size_t len);
/** timer callback to wakeup dtio thread to process messages */
void mq_wakeup_cb(void* arg);
/**
* Create IO thread.
* @return new io thread object. not yet started. or NULL malloc failure.
*/
struct dt_io_thread* dt_io_thread_create(void);
/**
* Delete the IO thread structure.
* @param dtio: the io thread that is deleted. It must not be running.
*/
void dt_io_thread_delete(struct dt_io_thread* dtio);
/**
* Apply config to the dtio thread
* @param dtio: io thread, not yet started.
* @param cfg: config file struct.
* @return false on malloc failure.
*/
int dt_io_thread_apply_cfg(struct dt_io_thread* dtio,
struct config_file *cfg);
/**
* Register a msg queue to the io thread. It will be polled to see if
* there are messages and those then get removed and sent, when the thread
* is running.
* @param dtio: the io thread.
* @param mq: message queue to register.
* @return false on failure (malloc failure).
*/
int dt_io_thread_register_queue(struct dt_io_thread* dtio,
struct dt_msg_queue* mq);
/**
* Unregister queue from io thread.
* @param dtio: the io thread.
* @param mq: message queue.
*/
void dt_io_thread_unregister_queue(struct dt_io_thread* dtio,
struct dt_msg_queue* mq);
/**
* Start the io thread
* @param dtio: the io thread.
* @param event_base_nothr: the event base to attach the events to, in case
* we are running without threads. With threads, this is ignored
* and a thread is started to process the dnstap log messages.
* @param numworkers: number of worker threads. The dnstap io thread is
* that number +1 as the threadnumber (in logs).
* @return false on failure.
*/
int dt_io_thread_start(struct dt_io_thread* dtio, void* event_base_nothr,
int numworkers);
/**
* Stop the io thread
* @param dtio: the io thread.
*/
void dt_io_thread_stop(struct dt_io_thread* dtio);
/** callback for the dnstap reconnect, to start reconnecting to output */
void dtio_reconnect_timeout_cb(int fd, short bits, void* arg);
/** callback for the dnstap events, to write to the output */
void dtio_output_cb(int fd, short bits, void* arg);
/** callback for the dnstap commandpipe, to stop the dnstap IO */
void dtio_cmd_cb(int fd, short bits, void* arg);
/** callback for the timer when the thread stops and wants to finish up */
void dtio_stop_timer_cb(int fd, short bits, void* arg);
/** callback for the output when the thread stops and wants to finish up */
void dtio_stop_ev_cb(int fd, short bits, void* arg);
/** callback for unbound-dnstap-socket */
void dtio_tap_callback(int fd, short bits, void* arg);
/** callback for unbound-dnstap-socket */
void dtio_mainfdcallback(int fd, short bits, void* arg);
#endif /* DTSTREAM_H */
File diff suppressed because it is too large Load Diff
@@ -1,306 +0,0 @@
/**
* \file
* This file contains the dynamic library module for Unbound.
* This loads a dynamic library (.dll, .so) and calls that for the
* module actions.
*/
#include "config.h"
#include "dynlibmod/dynlibmod.h"
#include "util/module.h"
#include "util/config_file.h"
#if HAVE_WINDOWS_H
#include <windows.h>
#define __DYNMOD HMODULE
#define __DYNSYM FARPROC
#define __LOADSYM GetProcAddress
static void log_dlerror() {
DWORD dwLastError = GetLastError();
LPSTR MessageBuffer;
DWORD dwBufferLength;
DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM ;
if((dwBufferLength = FormatMessageA(
dwFormatFlags,
NULL, // module to get message from (NULL == system)
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPSTR) &MessageBuffer,
0,
NULL
)))
{
log_err("dynlibmod: %s (%ld)", MessageBuffer, dwLastError);
LocalFree(MessageBuffer);
}
}
static HMODULE open_library(const char* fname) {
return LoadLibrary(fname);
}
static void close_library(const char* fname, __DYNMOD handle) {
(void)fname;
(void)handle;
}
#else
#include <dlfcn.h>
#define __DYNMOD void*
#define __DYNSYM void*
#define __LOADSYM dlsym
static void log_dlerror() {
log_err("dynlibmod: %s", dlerror());
}
static void* open_library(const char* fname) {
return dlopen(fname, RTLD_LAZY | RTLD_GLOBAL);
}
static void close_library(const char* fname, __DYNMOD handle) {
if(!handle) return;
if(dlclose(handle) != 0) {
log_err("dlclose %s: %s", fname, strerror(errno));
}
}
#endif
/** module counter for multiple dynlib modules */
static int dynlib_mod_count = 0;
/** dynlib module init */
int dynlibmod_init(struct module_env* env, int id) {
int dynlib_mod_idx = dynlib_mod_count++;
struct config_strlist* cfg_item = env->cfg->dynlib_file;
struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env));
__DYNMOD dynamic_library;
if (!de)
{
log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx);
return 0;
}
env->modinfo[id] = (void*) de;
de->fname = NULL;
for(int i = dynlib_mod_idx;
i != 0 && cfg_item != NULL;
i--, cfg_item = cfg_item->next) {}
if (cfg_item == NULL || cfg_item->str == NULL || cfg_item->str[0] == 0) {
log_err("dynlibmod[%d]: no dynamic library given.", dynlib_mod_idx);
return 0;
} else {
de->fname = cfg_item->str;
}
verbose(VERB_ALGO, "dynlibmod[%d]: Trying to load library %s", dynlib_mod_idx, de->fname);
dynamic_library = open_library(de->fname);
de->dynamic_library = (void*)dynamic_library;
if (dynamic_library == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
__DYNSYM initializer;
__DYNSYM deinitializer;
__DYNSYM operate;
__DYNSYM inform;
__DYNSYM clear;
__DYNSYM get_mem;
initializer = __LOADSYM(dynamic_library,"init");
if (initializer == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load init procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_init = (func_init_t)(void*)initializer;
}
deinitializer = __LOADSYM(dynamic_library,"deinit");
if (deinitializer == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load deinit procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_deinit = (func_deinit_t)(void*)deinitializer;
}
operate = __LOADSYM(dynamic_library,"operate");
if (operate == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load operate procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_operate = (func_operate_t)(void*)operate;
}
inform = __LOADSYM(dynamic_library,"inform_super");
if (inform == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load inform_super procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_inform = (func_inform_t)(void*)inform;
}
clear = __LOADSYM(dynamic_library,"clear");
if (clear == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load clear procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_clear = (func_clear_t)(void*)clear;
}
get_mem = __LOADSYM(dynamic_library,"get_mem");
if (get_mem == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load get_mem procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_get_mem = (func_get_mem_t)(void*)get_mem;
}
}
de->inplace_cb_delete_wrapped = &inplace_cb_delete_wrapped;
de->inplace_cb_register_wrapped = &inplace_cb_register_wrapped;
return de->func_init(env, id);
}
/** dynlib module deinit */
void dynlibmod_deinit(struct module_env* env, int id) {
struct dynlibmod_env* de = env->modinfo[id];
if(de == NULL)
return;
de->func_deinit(env, id);
close_library(de->fname, (__DYNMOD)de->dynamic_library);
dynlib_mod_count--;
de->fname = NULL;
free(de);
}
/** dynlib module operate on a query */
void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound) {
struct dynlibmod_env* de = qstate->env->modinfo[id];
de->func_operate(qstate, event, id, outbound);
}
/** dynlib module */
void dynlibmod_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super) {
struct dynlibmod_env* de = qstate->env->modinfo[id];
de->func_inform(qstate, id, super);
}
/** dynlib module cleanup query state */
void dynlibmod_clear(struct module_qstate* qstate, int id) {
struct dynlibmod_env* de = qstate->env->modinfo[id];
de->func_clear(qstate, id);
}
/** dynlib module alloc size routine */
size_t dynlibmod_get_mem(struct module_env* env, int id) {
struct dynlibmod_env* de = (struct dynlibmod_env*)env->modinfo[id];
size_t size;
verbose(VERB_ALGO, "dynlibmod: get_mem, id: %d, de:%p", id, de);
if(!de)
return 0;
size = de->func_get_mem(env, id);
return size + sizeof(*de);
}
int dynlib_inplace_cb_reply_generic(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
struct comm_reply* repinfo, struct regional* region,
struct timeval* start_time, int id, void* callback) {
struct cb_pair* cb_pair = (struct cb_pair*) callback;
return ((inplace_cb_reply_func_type*) cb_pair->cb)(qinfo, qstate, rep, rcode, edns, opt_list_out, repinfo, region, start_time, id, cb_pair->cb_arg);
}
int dynlib_inplace_cb_query_generic(struct query_info* qinfo, uint16_t flags,
struct module_qstate* qstate, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region,
int id, void* callback) {
struct cb_pair* cb_pair = (struct cb_pair*) callback;
return ((inplace_cb_query_func_type*) cb_pair->cb)(qinfo, flags, qstate, addr, addrlen, zone, zonelen, region, id, cb_pair->cb_arg);
}
int dynlib_inplace_cb_edns_back_parsed(struct module_qstate* qstate,
int id, void* cb_args) {
struct cb_pair* cb_pair = (struct cb_pair*) cb_args;
return ((inplace_cb_edns_back_parsed_func_type*) cb_pair->cb)(qstate, id, cb_pair->cb_arg);
}
int dynlib_inplace_cb_query_response(struct module_qstate* qstate,
struct dns_msg* response, int id, void* cb_args) {
struct cb_pair* cb_pair = (struct cb_pair*) cb_args;
return ((inplace_cb_query_response_func_type*) cb_pair->cb)(qstate, response, id, cb_pair->cb_arg);
}
int
inplace_cb_register_wrapped(void* cb, enum inplace_cb_list_type type, void* cbarg,
struct module_env* env, int id) {
struct cb_pair* cb_pair = malloc(sizeof(struct cb_pair));
if(cb_pair == NULL) {
log_err("dynlibmod[%d]: malloc failure", id);
return 0;
}
cb_pair->cb = cb;
cb_pair->cb_arg = cbarg;
if(type >= inplace_cb_reply && type <= inplace_cb_reply_servfail) {
return inplace_cb_register(&dynlib_inplace_cb_reply_generic, type, (void*) cb_pair, env, id);
} else if(type == inplace_cb_query) {
return inplace_cb_register(&dynlib_inplace_cb_query_generic, type, (void*) cb_pair, env, id);
} else if(type == inplace_cb_query_response) {
return inplace_cb_register(&dynlib_inplace_cb_query_response, type, (void*) cb_pair, env, id);
} else if(type == inplace_cb_edns_back_parsed) {
return inplace_cb_register(&dynlib_inplace_cb_edns_back_parsed, type, (void*) cb_pair, env, id);
} else {
free(cb_pair);
return 0;
}
}
void
inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type,
int id) {
struct inplace_cb* temp = env->inplace_cb_lists[type];
struct inplace_cb* prev = NULL;
while(temp) {
if(temp->id == id) {
if(!prev) {
env->inplace_cb_lists[type] = temp->next;
free(temp->cb_arg);
free(temp);
temp = env->inplace_cb_lists[type];
}
else {
prev->next = temp->next;
free(temp->cb_arg);
free(temp);
temp = prev->next;
}
}
else {
prev = temp;
temp = temp->next;
}
}
}
/**
* The module function block
*/
static struct module_func_block dynlibmod_block = {
"dynlib",
&dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super,
&dynlibmod_clear, &dynlibmod_get_mem
};
struct module_func_block* dynlibmod_get_funcblock(void)
{
return &dynlibmod_block;
}
@@ -1,139 +0,0 @@
/*
* dynlibmod.h: module header file
*
* Copyright (c) 2019, Peter Munch-Ellingsen (peterme AT peterme.net)
*
* This software is open source.
*
* 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 the organization nor the names of its
* 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.
*/
/**
* \file
* Dynamic loading module for unbound. Loads dynamic library.
*/
#ifndef DYNLIBMOD_H
#define DYNLIBMOD_H
#include "util/module.h"
#include "services/outbound_list.h"
/**
* Get the module function block.
* @return: function block with function pointers to module methods.
*/
struct module_func_block* dynlibmod_get_funcblock(void);
/** dynlib module init */
int dynlibmod_init(struct module_env* env, int id);
/** dynlib module deinit */
void dynlibmod_deinit(struct module_env* env, int id);
/** dynlib module operate on a query */
void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound);
/** dynlib module */
void dynlibmod_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** dynlib module cleanup query state */
void dynlibmod_clear(struct module_qstate* qstate, int id);
/** dynlib module alloc size routine */
size_t dynlibmod_get_mem(struct module_env* env, int id);
int dynlib_inplace_cb_reply_generic(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
struct comm_reply* repinfo, struct regional* region,
struct timeval* start_time, int id, void* callback);
int dynlib_inplace_cb_query_generic(struct query_info* qinfo, uint16_t flags,
struct module_qstate* qstate, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region,
int id, void* callback);
int dynlib_inplace_cb_edns_back_parsed(struct module_qstate* qstate,
int id, void* cb_args);
int dynlib_inplace_cb_query_response(struct module_qstate* qstate,
struct dns_msg* response, int id, void* cb_args);
int
inplace_cb_register_wrapped(void* cb, enum inplace_cb_list_type type, void* cbarg,
struct module_env* env, int id);
void
inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type,
int id);
struct cb_pair {
void *cb;
void *cb_arg;
};
/**
* Global state for the module.
*/
typedef int (*func_init_t)(struct module_env*, int);
typedef void (*func_deinit_t)(struct module_env*, int);
typedef void (*func_operate_t)(struct module_qstate*, enum module_ev, int, struct outbound_entry*);
typedef void (*func_inform_t)(struct module_qstate*, int, struct module_qstate*);
typedef void (*func_clear_t)(struct module_qstate*, int);
typedef size_t (*func_get_mem_t)(struct module_env*, int);
typedef void (*inplace_cb_delete_wrapped_t)(struct module_env*, enum inplace_cb_list_type, int);
typedef int (*inplace_cb_register_wrapped_t)(void*, enum inplace_cb_list_type, void*, struct module_env*, int);
struct dynlibmod_env {
/** Dynamic library filename. */
const char* fname;
/** dynamic library handle */
void* dynamic_library;
/** Module init function */
func_init_t func_init;
/** Module deinit function */
func_deinit_t func_deinit;
/** Module operate function */
func_operate_t func_operate;
/** Module super_inform function */
func_inform_t func_inform;
/** Module clear function */
func_clear_t func_clear;
/** Module get_mem function */
func_get_mem_t func_get_mem;
/** Wrapped inplace callback functions to circumvent callback whitelisting */
inplace_cb_delete_wrapped_t inplace_cb_delete_wrapped;
inplace_cb_register_wrapped_t inplace_cb_register_wrapped;
/** Pointer to any data the dynamic library might want to keep */
void *dyn_env;
};
#endif /* DYNLIBMOD_H */
@@ -1,132 +0,0 @@
/**
* \file
*
* This is an example to show how dynamic libraries can be made to work with
* unbound. To build a .so file simply run:
* gcc -I../.. -shared -Wall -Werror -fpic -o helloworld.so helloworld.c
* And to build for windows, first make unbound with the --with-dynlibmod
* switch, then use this command:
* x86_64-w64-mingw32-gcc -m64 -I../.. -shared -Wall -Werror -fpic
* -o helloworld.dll helloworld.c -L../.. -l:libunbound.dll.a
* to cross-compile a 64-bit Windows DLL. The libunbound.dll.a is produced
* by the compile step that makes unbound.exe and allows the dynlib dll to
* access definitions in unbound.exe.
*/
#include "../../config.h"
#include "../../util/module.h"
#include "../../sldns/parseutil.h"
#include "../dynlibmod.h"
/* Declare the EXPORT macro that expands to exporting the symbol for DLLs when
* compiling for Windows. All procedures marked with EXPORT in this example are
* called directly by the dynlib module and must be present for the module to
* load correctly. */
#ifdef HAVE_WINDOWS_H
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
/* Forward declare a callback, implemented at the bottom of this file */
int reply_callback(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
struct comm_reply* repinfo, struct regional* region,
struct timeval* start_time, int id, void* callback);
/* Init is called when the module is first loaded. It should be used to set up
* the environment for this module and do any other initialisation required. */
EXPORT int init(struct module_env* env, int id) {
log_info("dynlib: hello world from init");
struct dynlibmod_env* de = (struct dynlibmod_env*) env->modinfo[id];
de->inplace_cb_register_wrapped(&reply_callback,
inplace_cb_reply,
NULL, env, id);
struct dynlibmod_env* local_env = env->modinfo[id];
local_env->dyn_env = NULL;
return 1;
}
/* Deinit is run as the program is shutting down. It should be used to clean up
* the environment and any left over data. */
EXPORT void deinit(struct module_env* env, int id) {
log_info("dynlib: hello world from deinit");
struct dynlibmod_env* de = (struct dynlibmod_env*) env->modinfo[id];
de->inplace_cb_delete_wrapped(env, inplace_cb_reply, id);
if (de->dyn_env != NULL) free(de->dyn_env);
}
/* Operate is called every time a query passes by this module. The event can be
* used to determine which direction in the module chain it came from. */
EXPORT void operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* entry) {
log_info("dynlib: hello world from operate");
log_info("dynlib: incoming query: %s %s(%d) %s(%d)",
qstate->qinfo.qname,
sldns_lookup_by_id(sldns_rr_classes, qstate->qinfo.qclass)->name,
qstate->qinfo.qclass,
sldns_rr_descript(qstate->qinfo.qtype)->_name,
qstate->qinfo.qtype);
if (event == module_event_new || event == module_event_pass) {
qstate->ext_state[id] = module_wait_module;
struct dynlibmod_env* env = qstate->env->modinfo[id];
if (env->dyn_env == NULL) {
env->dyn_env = calloc(3, sizeof(int));
((int *)env->dyn_env)[0] = 42;
((int *)env->dyn_env)[1] = 102;
((int *)env->dyn_env)[2] = 192;
} else {
log_err("dynlib: already has data!");
qstate->ext_state[id] = module_error;
}
} else if (event == module_event_moddone) {
qstate->ext_state[id] = module_finished;
} else {
qstate->ext_state[id] = module_error;
}
}
/* Inform super is called when a query is completed or errors out, but only if
* a sub-query has been registered to it by this module. Look at
* mesh_attach_sub in services/mesh.h to see how this is done. */
EXPORT void inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super) {
log_info("dynlib: hello world from inform_super");
}
/* Clear is called once a query is complete and the response has been sent
* back. It is used to clear up any per-query allocations. */
EXPORT void clear(struct module_qstate* qstate, int id) {
log_info("dynlib: hello world from clear");
struct dynlibmod_env* env = qstate->env->modinfo[id];
if (env->dyn_env != NULL) {
free(env->dyn_env);
env->dyn_env = NULL;
}
}
/* Get mem is called when Unbound is printing performance information. This
* only happens explicitly and is only used to show memory usage to the user. */
EXPORT size_t get_mem(struct module_env* env, int id) {
log_info("dynlib: hello world from get_mem");
return 0;
}
/* The callback that was forward declared earlier. It is registered in the init
* procedure to run when a query is being replied to. */
int reply_callback(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
struct comm_reply* repinfo, struct regional* region,
struct timeval* start_time, int id, void* callback) {
log_info("dynlib: hello world from callback");
struct dynlibmod_env* env = qstate->env->modinfo[id];
if (env->dyn_env != NULL) {
log_info("dynlib: numbers gotten from query: %d, %d, and %d",
((int *)env->dyn_env)[0],
((int *)env->dyn_env)[1],
((int *)env->dyn_env)[2]);
}
return 0;
}
@@ -1,537 +0,0 @@
/*
* edns-subnet/addrtree.c -- radix tree for edns subnet cache.
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/** \file
* addrtree -- radix tree for edns subnet cache.
*/
#include "config.h"
#include "util/log.h"
#include "util/data/msgreply.h"
#include "util/module.h"
#include "addrtree.h"
/**
* Create a new edge
* @param node: Child node this edge will connect to.
* @param addr: full key to this edge.
* @param addrlen: length of relevant part of key for this node
* @param parent_node: Parent node for node
* @param parent_index: Index of child node at parent node
* @return new addredge or NULL on failure
*/
static struct addredge *
edge_create(struct addrnode *node, const addrkey_t *addr,
addrlen_t addrlen, struct addrnode *parent_node, int parent_index)
{
size_t n;
struct addredge *edge = (struct addredge *)malloc( sizeof (*edge) );
if (!edge)
return NULL;
edge->node = node;
edge->len = addrlen;
edge->parent_index = parent_index;
edge->parent_node = parent_node;
/* ceil() */
n = (size_t)((addrlen / KEYWIDTH) + ((addrlen % KEYWIDTH != 0)?1:0));
edge->str = (addrkey_t *)calloc(n, sizeof (addrkey_t));
if (!edge->str) {
free(edge);
return NULL;
}
memcpy(edge->str, addr, n * sizeof (addrkey_t));
/* Only manipulate other objects after successful alloc */
node->parent_edge = edge;
log_assert(parent_node->edge[parent_index] == NULL);
parent_node->edge[parent_index] = edge;
return edge;
}
/**
* Create a new node
* @param tree: Tree the node lives in.
* @param elem: Element to store at this node
* @param scope: Scopemask from server reply
* @param ttl: Element is valid up to this time. Absolute, seconds
* @return new addrnode or NULL on failure
*/
static struct addrnode *
node_create(struct addrtree *tree, void *elem, addrlen_t scope,
time_t ttl)
{
struct addrnode* node = (struct addrnode *)malloc( sizeof (*node) );
if (!node)
return NULL;
node->elem = elem;
tree->node_count++;
node->scope = scope;
node->ttl = ttl;
node->only_match_scope_zero = 0;
node->edge[0] = NULL;
node->edge[1] = NULL;
node->parent_edge = NULL;
node->next = NULL;
node->prev = NULL;
return node;
}
/** Size in bytes of node and parent edge
* @param tree: tree the node lives in
* @param n: node which size must be calculated
* @return size in bytes.
**/
static inline size_t
node_size(const struct addrtree *tree, const struct addrnode *n)
{
return sizeof *n + sizeof *n->parent_edge + n->parent_edge->len +
(n->elem?tree->sizefunc(n->elem):0);
}
struct addrtree *
addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *),
size_t (*sizefunc)(void *), void *env, uint32_t max_node_count)
{
struct addrtree *tree;
log_assert(delfunc != NULL);
log_assert(sizefunc != NULL);
tree = (struct addrtree *)calloc(1, sizeof(*tree));
if (!tree)
return NULL;
tree->root = node_create(tree, NULL, 0, 0);
if (!tree->root) {
free(tree);
return NULL;
}
tree->size_bytes = sizeof *tree + sizeof *tree->root;
tree->first = NULL;
tree->last = NULL;
tree->max_depth = max_depth;
tree->delfunc = delfunc;
tree->sizefunc = sizefunc;
tree->env = env;
tree->node_count = 0;
tree->max_node_count = max_node_count;
return tree;
}
/**
* Scrub a node clean of elem
* @param tree: tree the node lives in.
* @param node: node to be cleaned.
*/
static void
clean_node(struct addrtree *tree, struct addrnode *node)
{
if (!node->elem) return;
tree->size_bytes -= tree->sizefunc(node->elem);
tree->delfunc(tree->env, node->elem);
node->only_match_scope_zero = 0;
node->elem = NULL;
}
/** Remove specified node from LRU list */
static void
lru_pop(struct addrtree *tree, struct addrnode *node)
{
if (node == tree->first) {
if (!node->next) { /* it is the last as well */
tree->first = NULL;
tree->last = NULL;
} else {
tree->first = node->next;
tree->first->prev = NULL;
}
} else if (node == tree->last) { /* but not the first */
tree->last = node->prev;
tree->last->next = NULL;
} else {
node->prev->next = node->next;
node->next->prev = node->prev;
}
}
/** Add node to LRU list as most recently used. */
static void
lru_push(struct addrtree *tree, struct addrnode *node)
{
if (!tree->first) {
tree->first = node;
node->prev = NULL;
} else {
tree->last->next = node;
node->prev = tree->last;
}
tree->last = node;
node->next = NULL;
}
/** Move node to the end of LRU list */
static void
lru_update(struct addrtree *tree, struct addrnode *node)
{
if (tree->root == node) return;
lru_pop(tree, node);
lru_push(tree, node);
}
/**
* Purge a node from the tree. Node and parentedge are cleaned and
* free'd.
* @param tree: Tree the node lives in.
* @param node: Node to be freed
*/
static void
purge_node(struct addrtree *tree, struct addrnode *node)
{
struct addredge *parent_edge, *child_edge = NULL;
int index;
int keep = node->edge[0] && node->edge[1];
clean_node(tree, node);
parent_edge = node->parent_edge;
if (keep || !parent_edge) return;
tree->node_count--;
index = parent_edge->parent_index;
child_edge = node->edge[!node->edge[0]];
if (child_edge) {
child_edge->parent_node = parent_edge->parent_node;
child_edge->parent_index = index;
}
parent_edge->parent_node->edge[index] = child_edge;
tree->size_bytes -= node_size(tree, node);
free(parent_edge->str);
free(parent_edge);
lru_pop(tree, node);
free(node);
}
/**
* If a limit is set remove old nodes while above that limit.
* @param tree: Tree to be cleaned up.
*/
static void
lru_cleanup(struct addrtree *tree)
{
struct addrnode *n, *p;
int children;
if (tree->max_node_count == 0) return;
while (tree->node_count > tree->max_node_count) {
n = tree->first;
if (!n) break;
children = (n->edge[0] != NULL) + (n->edge[1] != NULL);
/** Don't remove this node, it is either the root or we can't
* do without it because it has 2 children */
if (children == 2 || !n->parent_edge) {
lru_update(tree, n);
continue;
}
p = n->parent_edge->parent_node;
purge_node(tree, n);
/** Since we removed n, n's parent p is eligible for deletion
* if it is not the root node, caries no data and has only 1
* child */
children = (p->edge[0] != NULL) + (p->edge[1] != NULL);
if (!p->elem && children == 1 && p->parent_edge) {
purge_node(tree, p);
}
}
}
inline size_t
addrtree_size(const struct addrtree *tree)
{
return tree?tree->size_bytes:0;
}
void addrtree_delete(struct addrtree *tree)
{
struct addrnode *n;
if (!tree) return;
clean_node(tree, tree->root);
free(tree->root);
tree->size_bytes -= sizeof(struct addrnode);
while ((n = tree->first)) {
tree->first = n->next;
clean_node(tree, n);
tree->size_bytes -= node_size(tree, n);
free(n->parent_edge->str);
free(n->parent_edge);
free(n);
}
log_assert(sizeof *tree == addrtree_size(tree));
free(tree);
}
/**
* Get N'th bit from address
* @param addr: address to inspect
* @param addrlen: length of addr in bits
* @param n: index of bit to test. Must be in range [0, addrlen)
* @return 0 or 1
*/
static int
getbit(const addrkey_t *addr, addrlen_t addrlen, addrlen_t n)
{
log_assert(addrlen > n);
(void)addrlen;
return (int)(addr[n/KEYWIDTH]>>((KEYWIDTH-1)-(n%KEYWIDTH))) & 1;
}
/**
* Test for equality on N'th bit.
* @return 0 for equal, 1 otherwise
*/
static inline int
cmpbit(const addrkey_t *key1, const addrkey_t *key2, addrlen_t n)
{
addrkey_t c = key1[n/KEYWIDTH] ^ key2[n/KEYWIDTH];
return (int)(c >> ((KEYWIDTH-1)-(n%KEYWIDTH))) & 1;
}
/**
* Common number of bits in prefix.
* @param s1: first prefix.
* @param l1: length of s1 in bits.
* @param s2: second prefix.
* @param l2: length of s2 in bits.
* @param skip: nr of bits already checked.
* @return common number of bits.
*/
static addrlen_t
bits_common(const addrkey_t *s1, addrlen_t l1,
const addrkey_t *s2, addrlen_t l2, addrlen_t skip)
{
addrlen_t len, i;
len = (l1 > l2) ? l2 : l1;
log_assert(skip < len);
for (i = skip; i < len; i++) {
if (cmpbit(s1, s2, i)) return i;
}
return len;
}
/**
* Tests if s1 is a substring of s2
* @param s1: first prefix.
* @param l1: length of s1 in bits.
* @param s2: second prefix.
* @param l2: length of s2 in bits.
* @param skip: nr of bits already checked.
* @return 1 for substring, 0 otherwise
*/
static int
issub(const addrkey_t *s1, addrlen_t l1,
const addrkey_t *s2, addrlen_t l2, addrlen_t skip)
{
return bits_common(s1, l1, s2, l2, skip) == l1;
}
void
addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl,
time_t now, int only_match_scope_zero)
{
struct addrnode *newnode, *node;
struct addredge *edge;
int index;
addrlen_t common, depth;
node = tree->root;
log_assert(node != NULL);
/* Protect our cache against too much fine-grained data */
if (tree->max_depth < scope) scope = tree->max_depth;
/* Server answer was less specific than question */
if (scope < sourcemask) sourcemask = scope;
depth = 0;
while (1) {
log_assert(depth <= sourcemask);
/* Case 1: update existing node */
if (depth == sourcemask) {
/* update this node's scope and data */
clean_node(tree, node);
node->ttl = ttl;
node->only_match_scope_zero = only_match_scope_zero;
node->elem = elem;
node->scope = scope;
tree->size_bytes += tree->sizefunc(elem);
return;
}
index = getbit(addr, sourcemask, depth);
/* Get an edge to an unexpired node */
edge = node->edge[index];
while (edge) {
/* Purge all expired nodes on path */
if (!edge->node->elem || edge->node->ttl >= now)
break;
purge_node(tree, edge->node);
edge = node->edge[index];
}
/* Case 2: New leafnode */
if (!edge) {
newnode = node_create(tree, elem, scope, ttl);
if (!newnode) return;
if (!edge_create(newnode, addr, sourcemask, node,
index)) {
clean_node(tree, newnode);
tree->node_count--;
free(newnode);
return;
}
tree->size_bytes += node_size(tree, newnode);
lru_push(tree, newnode);
lru_cleanup(tree);
return;
}
/* Case 3: Traverse edge */
common = bits_common(edge->str, edge->len, addr, sourcemask,
depth);
if (common == edge->len) {
/* We update the scope of intermediate nodes. Apparently
* the * authority changed its mind. If we would not do
* this we might not be able to reach our new node. */
node->scope = scope;
depth = edge->len;
node = edge->node;
continue;
}
/* Case 4: split. */
if (!(newnode = node_create(tree, NULL, 0, 0)))
return;
node->edge[index] = NULL;
if (!edge_create(newnode, addr, common, node, index)) {
node->edge[index] = edge;
clean_node(tree, newnode);
tree->node_count--;
free(newnode);
return;
}
lru_push(tree, newnode);
/* connect existing child to our new node */
index = getbit(edge->str, edge->len, common);
newnode->edge[index] = edge;
edge->parent_node = newnode;
edge->parent_index = (int)index;
if (common == sourcemask) {
/* Data is stored in the node */
newnode->elem = elem;
newnode->scope = scope;
newnode->ttl = ttl;
newnode->only_match_scope_zero = only_match_scope_zero;
}
tree->size_bytes += node_size(tree, newnode);
if (common != sourcemask) {
/* Data is stored in other leafnode */
node = newnode;
newnode = node_create(tree, elem, scope, ttl);
if (!edge_create(newnode, addr, sourcemask, node,
index^1)) {
clean_node(tree, newnode);
tree->node_count--;
free(newnode);
return;
}
tree->size_bytes += node_size(tree, newnode);
lru_push(tree, newnode);
}
lru_cleanup(tree);
return;
}
}
struct addrnode *
addrtree_find(struct addrtree *tree, const addrkey_t *addr,
addrlen_t sourcemask, time_t now)
{
struct addrnode *node = tree->root;
struct addredge *edge = NULL;
addrlen_t depth = 0;
log_assert(node != NULL);
while (1) {
/* Current node more specific then question. */
log_assert(depth <= sourcemask);
/* does this node have data? if yes, see if we have a match */
if (node->elem && node->ttl >= now &&
!(sourcemask != 0 && node->only_match_scope_zero)) {
/* saved at wrong depth */;
log_assert(node->scope >= depth);
if (depth == node->scope ||
(node->scope > sourcemask &&
depth == sourcemask)) {
/* Authority indicates it does not have a more
* precise answer or we cannot ask a more
* specific question. */
lru_update(tree, node);
return node;
}
}
/* This is our final depth, but we haven't found an answer. */
if (depth == sourcemask)
return NULL;
/* Find an edge to traverse */
edge = node->edge[getbit(addr, sourcemask, depth)];
if (!edge || !edge->node)
return NULL;
if (edge->len > sourcemask )
return NULL;
if (!issub(edge->str, edge->len, addr, sourcemask, depth))
return NULL;
log_assert(depth < edge->len);
depth = edge->len;
node = edge->node;
}
}
/** Wrappers for static functions to unit test */
int unittest_wrapper_addrtree_cmpbit(const addrkey_t *key1,
const addrkey_t *key2, addrlen_t n) {
return cmpbit(key1, key2, n);
}
addrlen_t unittest_wrapper_addrtree_bits_common(const addrkey_t *s1,
addrlen_t l1, const addrkey_t *s2, addrlen_t l2, addrlen_t skip) {
return bits_common(s1, l1, s2, l2, skip);
}
int unittest_wrapper_addrtree_getbit(const addrkey_t *addr,
addrlen_t addrlen, addrlen_t n) {
return getbit(addr, addrlen, n);
}
int unittest_wrapper_addrtree_issub(const addrkey_t *s1, addrlen_t l1,
const addrkey_t *s2, addrlen_t l2, addrlen_t skip) {
return issub(s1, l1, s2, l2, skip);
}
@@ -1,192 +0,0 @@
/*
* edns-subnet/addrtree.h -- radix tree for edns subnet cache.
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
* The addrtree is a radix tree designed for edns subnet. Most notable
* is the addition of 'scope' to a node. Scope is only relevant for
* nodes with elem set, it indicates the number of bits the authority
* desires.
*
* For retrieving data one needs an address and address length
* (sourcemask). While traversing the tree the first matching node is
* returned. A node matches when
* node.scope<=sourcemask && node.elem!=NULL
* (This is the most specific answer the authority has.)
* or
* node.sourcemask==sourcemask && node.elem!=NULL
* (This is the most specific question the client can ask.)
*
* Insertion needs an address, sourcemask and scope. The length of the
* address is capped by min(sourcemask, scope). While traversing the
* tree the scope of all visited nodes is updated. This ensures we are
* always able to find the most specific answer available.
*/
#ifndef ADDRTREE_H
#define ADDRTREE_H
typedef uint8_t addrlen_t;
typedef uint8_t addrkey_t;
#define KEYWIDTH 8
struct addrtree {
struct addrnode *root;
/** Number of elements in the tree (not always equal to number of
* nodes) */
uint32_t node_count;
/** Maximum number of allowed nodes, will be enforced by LRU list.
* Excluding the root node, 0 for unlimited */
uint32_t max_node_count;
/** Size of tree in bytes */
size_t size_bytes;
/** Maximum prefix length we are willing to cache. */
addrlen_t max_depth;
/** External function to delete elem. Called as
* delfunc(addrnode->elem, addrtree->env) */
void (*delfunc)(void *, void *);
/** Environment for delfunc */
void *env;
/** External function returning size of elem. Called as
* sizefunc(addrnode->elem) */
size_t (*sizefunc)(void *);
/** first node in LRU list, first candidate to go */
struct addrnode* first;
/** last node in LRU list, last candidate to go */
struct addrnode *last;
};
struct addrnode {
/** Payload of node, may be NULL */
void *elem;
/** Abs time in seconds in which elem is meaningful */
time_t ttl;
/** Number of significant bits in address. */
addrlen_t scope;
/** Only use the element for queries for subnet/0. Set if the query
* for /0 was answered with scope 0. For query /x answer scope 0,
* they can match anything and this is false. */
int only_match_scope_zero;
/** A node can have 0-2 edges, set to NULL for unused */
struct addredge *edge[2];
/** edge between this node and parent */
struct addredge *parent_edge;
/** previous node in LRU list */
struct addrnode *prev;
/** next node in LRU list */
struct addrnode *next;
};
struct addredge {
/** address of connected node */
addrkey_t *str;
/** length in bits of str */
addrlen_t len;
/** child node this edge is connected to */
struct addrnode *node;
/** Parent node this ege is connected to */
struct addrnode *parent_node;
/** Index of this edge in parent_node */
int parent_index;
};
/**
* Size of tree in bytes.
* @param tree: Tree.
* @return size of tree in bytes.
*/
size_t addrtree_size(const struct addrtree *tree);
/**
* Create a new tree.
* @param max_depth: Tree will cap keys to this length.
* @param delfunc: f(element, env) delete element.
* @param sizefunc: f(element) returning the size of element.
* @param env: Module environment for alloc information.
* @param max_node_count: Maximum size of this data structure in nodes.
* 0 for unlimited.
* @return new addrtree or NULL on failure.
*/
struct addrtree *
addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *),
size_t (*sizefunc)(void *), void *env, uint32_t max_node_count);
/**
* Free tree and all nodes below.
* @param tree: Tree to be freed.
*/
void addrtree_delete(struct addrtree *tree);
/**
* Insert an element in the tree. Failures are silent. Sourcemask and
* scope might be changed according to local policy. Caller should no
* longer access elem, it could be free'd now or later during future
* inserts.
*
* @param tree: Tree insert elem in.
* @param addr: key for element lookup.
* @param sourcemask: Length of addr in bits.
* @param scope: Number of significant bits in addr.
* @param elem: data to store in the tree.
* @param ttl: elem is valid up to this time, seconds.
* @param only_match_scope_zero: set for when query /0 has scope /0 answer.
* @param now: Current time in seconds.
*/
void addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl,
time_t now, int only_match_scope_zero);
/**
* Find a node containing an element in the tree.
*
* @param tree: Tree to search.
* @param addr: key for element lookup.
* @param sourcemask: Length of addr in bits.
* @param now: Current time in seconds.
* @return addrnode or NULL on miss.
*/
struct addrnode * addrtree_find(struct addrtree *tree,
const addrkey_t *addr, addrlen_t sourcemask, time_t now);
/** Wrappers for static functions to unit test */
int unittest_wrapper_addrtree_cmpbit(const addrkey_t *key1,
const addrkey_t *key2, addrlen_t n);
addrlen_t unittest_wrapper_addrtree_bits_common(const addrkey_t *s1,
addrlen_t l1, const addrkey_t *s2, addrlen_t l2, addrlen_t skip);
int unittest_wrapper_addrtree_getbit(const addrkey_t *addr,
addrlen_t addrlen, addrlen_t n);
int unittest_wrapper_addrtree_issub(const addrkey_t *s1, addrlen_t l1,
const addrkey_t *s2, addrlen_t l2, addrlen_t skip);
#endif /* ADDRTREE_H */
@@ -1,65 +0,0 @@
/*
* edns-subnet/edns-subnet.c - Subnet option related constants
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
* Subnet option related constants.
*/
#include "config.h"
#ifdef CLIENT_SUBNET /* keeps splint happy */
#include "edns-subnet/edns-subnet.h"
#include <string.h>
int
copy_clear(uint8_t* dst, size_t dstlen, uint8_t* src, size_t srclen, size_t n)
{
size_t intpart = n / 8; /* bytes */
size_t fracpart = n % 8; /* bits */
size_t written = intpart;
if (intpart > dstlen || intpart > srclen)
return 1;
if (fracpart && (intpart+1 > dstlen || intpart+1 > srclen))
return 1;
memcpy(dst, src, intpart);
if (fracpart) {
dst[intpart] = src[intpart] & ~(0xFF >> fracpart);
written++;
}
memset(dst + written, 0, dstlen - written);
return 0;
}
#endif /* CLIENT_SUBNET */
@@ -1,67 +0,0 @@
/*
* edns-subnet/edns-subnet.h - Subnet option related constants
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
* Subnet option related constants.
*/
#include "util/net_help.h"
#ifndef EDNSSUBNET_EDNSSUBNET_H
#define EDNSSUBNET_EDNSSUBNET_H
/** In use by the edns subnet option code, as assigned by IANA */
#define EDNSSUBNET_ADDRFAM_IP4 1
#define EDNSSUBNET_ADDRFAM_IP6 2
/**
* ECS option
*/
struct ecs_data {
uint16_t subnet_addr_fam;
uint8_t subnet_source_mask;
uint8_t subnet_scope_mask;
uint8_t subnet_addr[INET6_SIZE];
int subnet_validdata;
};
/**
* copy the first n BITS from src to dst iff both src and dst
* are large enough, return 0 on success
*/
int
copy_clear(uint8_t* dst, size_t dstlen, uint8_t* src, size_t srclen, size_t n);
#endif /* EDNSSUBNET_EDNSSUBNET_H */
@@ -1,207 +0,0 @@
/*
* edns-subnet/subnet-whitelist.c - Hosts we actively try to send subnet option
* to.
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* Keep track of the white listed servers for subnet option. Based
* on acl_list.c|h
*/
#include "config.h"
#ifdef CLIENT_SUBNET /* keeps splint happy */
#include "edns-subnet/edns-subnet.h"
#include "edns-subnet/subnet-whitelist.h"
#include "util/regional.h"
#include "util/log.h"
#include "util/config_file.h"
#include "util/net_help.h"
#include "util/storage/dnstree.h"
#include "sldns/str2wire.h"
#include "util/data/dname.h"
struct ecs_whitelist*
ecs_whitelist_create(void)
{
struct ecs_whitelist* whitelist =
(struct ecs_whitelist*)calloc(1,
sizeof(struct ecs_whitelist));
if(!whitelist)
return NULL;
whitelist->region = regional_create();
if(!whitelist->region) {
ecs_whitelist_delete(whitelist);
return NULL;
}
return whitelist;
}
void
ecs_whitelist_delete(struct ecs_whitelist* whitelist)
{
if(!whitelist)
return;
regional_destroy(whitelist->region);
free(whitelist);
}
/** insert new address into whitelist structure */
static int
upstream_insert(struct ecs_whitelist* whitelist,
struct sockaddr_storage* addr, socklen_t addrlen, int net)
{
struct addr_tree_node* node = (struct addr_tree_node*)regional_alloc(
whitelist->region, sizeof(*node));
if(!node)
return 0;
if(!addr_tree_insert(&whitelist->upstream, node, addr, addrlen, net)) {
verbose(VERB_QUERY,
"duplicate send-client-subnet address ignored.");
}
return 1;
}
/** apply edns-subnet string */
static int
upstream_str_cfg(struct ecs_whitelist* whitelist, const char* str)
{
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
verbose(VERB_ALGO, "send-client-subnet: %s", str);
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
log_err("cannot parse send-client-subnet netblock: %s", str);
return 0;
}
if(!upstream_insert(whitelist, &addr, addrlen, net)) {
log_err("out of memory");
return 0;
}
return 1;
}
/** read client_subnet config */
static int
read_upstream(struct ecs_whitelist* whitelist, struct config_file* cfg)
{
struct config_strlist* p;
for(p = cfg->client_subnet; p; p = p->next) {
log_assert(p->str);
if(!upstream_str_cfg(whitelist, p->str))
return 0;
}
return 1;
}
/** read client_subnet_zone config */
static int
read_names(struct ecs_whitelist* whitelist, struct config_file* cfg)
{
/* parse names, report errors, insert into tree */
struct config_strlist* p;
struct name_tree_node* n;
uint8_t* nm, *nmr;
size_t nm_len;
int nm_labs;
for(p = cfg->client_subnet_zone; p; p = p->next) {
log_assert(p->str);
nm = sldns_str2wire_dname(p->str, &nm_len);
if(!nm) {
log_err("cannot parse client-subnet-zone: %s", p->str);
return 0;
}
nm_labs = dname_count_size_labels(nm, &nm_len);
nmr = (uint8_t*)regional_alloc_init(whitelist->region, nm,
nm_len);
free(nm);
if(!nmr) {
log_err("out of memory");
return 0;
}
n = (struct name_tree_node*)regional_alloc(whitelist->region,
sizeof(*n));
if(!n) {
log_err("out of memory");
return 0;
}
if(!name_tree_insert(&whitelist->dname, n, nmr, nm_len, nm_labs,
LDNS_RR_CLASS_IN)) {
verbose(VERB_QUERY, "ignoring duplicate "
"client-subnet-zone: %s", p->str);
}
}
return 1;
}
int
ecs_whitelist_apply_cfg(struct ecs_whitelist* whitelist,
struct config_file* cfg)
{
regional_free_all(whitelist->region);
addr_tree_init(&whitelist->upstream);
name_tree_init(&whitelist->dname);
if(!read_upstream(whitelist, cfg))
return 0;
if(!read_names(whitelist, cfg))
return 0;
addr_tree_init_parents(&whitelist->upstream);
name_tree_init_parents(&whitelist->dname);
return 1;
}
int
ecs_is_whitelisted(struct ecs_whitelist* whitelist,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* qname,
size_t qname_len, uint16_t qclass)
{
int labs;
if(addr_tree_lookup(&whitelist->upstream, addr, addrlen))
return 1;
/* Not in upstream whitelist, check dname whitelist. */
labs = dname_count_labels(qname);
return name_tree_lookup(&whitelist->dname, qname, qname_len, labs,
qclass) != NULL;
}
size_t
ecs_whitelist_get_mem(struct ecs_whitelist* whitelist)
{
if(!whitelist) return 0;
return sizeof(*whitelist) + regional_get_mem(whitelist->region);
}
#endif /* CLIENT_SUBNET */
@@ -1,111 +0,0 @@
/*
* edns-subnet/subnet-whitelist.h - Hosts we actively try to send subnet option
* to.
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* Keep track of the white listed servers and domain names for subnet option.
* Based on acl_list.c|h
*/
#ifndef EDNSSUBNET_WHITELIST_H
#define EDNSSUBNET_WHITELIST_H
#include "util/storage/dnstree.h"
struct config_file;
struct regional;
/**
* ecs_whitelist structure
*/
struct ecs_whitelist {
/** regional for allocation */
struct regional* region;
/**
* Tree of the address spans that are whitelisted.
* contents of type addr_tree_node. Each node is an address span
* Unbound will append subnet option for.
*/
rbtree_type upstream;
/**
* Tree of domain names for which Unbound will append an ECS option.
* rbtree of struct name_tree_node.
*/
rbtree_type dname;
};
/**
* Create ecs_whitelist structure
* @return new structure or NULL on error.
*/
struct ecs_whitelist* ecs_whitelist_create(void);
/**
* Delete ecs_whitelist structure.
* @param whitelist: to delete.
*/
void ecs_whitelist_delete(struct ecs_whitelist* whitelist);
/**
* Process ecs_whitelist config.
* @param whitelist: where to store.
* @param cfg: config options.
* @return 0 on error.
*/
int ecs_whitelist_apply_cfg(struct ecs_whitelist* whitelist,
struct config_file* cfg);
/**
* See if an address or domain is whitelisted.
* @param whitelist: structure for address storage.
* @param addr: address to check
* @param addrlen: length of addr.
* @param qname: dname in query
* @param qname_len: length of dname
* @param qclass: class in query
* @return: true if the address is whitelisted for subnet option.
*/
int ecs_is_whitelisted(struct ecs_whitelist* whitelist,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* qname,
size_t qname_len, uint16_t qclass);
/**
* Get memory used by ecs_whitelist structure.
* @param whitelist: structure for address storage.
* @return bytes in use.
*/
size_t ecs_whitelist_get_mem(struct ecs_whitelist* whitelist);
#endif /* EDNSSUBNET_WHITELIST_H */
@@ -1,957 +0,0 @@
/*
* edns-subnet/subnetmod.c - edns subnet module. Must be called before validator
* and iterator.
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
* subnet module for unbound.
*/
#include "config.h"
#ifdef CLIENT_SUBNET /* keeps splint happy */
#include "edns-subnet/subnetmod.h"
#include "edns-subnet/edns-subnet.h"
#include "edns-subnet/addrtree.h"
#include "edns-subnet/subnet-whitelist.h"
#include "services/mesh.h"
#include "services/cache/dns.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/storage/slabhash.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "iterator/iter_utils.h"
/** externally called */
void
subnet_data_delete(void *d, void *ATTR_UNUSED(arg))
{
struct subnet_msg_cache_data *r;
r = (struct subnet_msg_cache_data*)d;
addrtree_delete(r->tree4);
addrtree_delete(r->tree6);
free(r);
}
/** externally called */
size_t
msg_cache_sizefunc(void *k, void *d)
{
struct msgreply_entry *q = (struct msgreply_entry*)k;
struct subnet_msg_cache_data *r = (struct subnet_msg_cache_data*)d;
size_t s = sizeof(struct msgreply_entry)
+ sizeof(struct subnet_msg_cache_data)
+ q->key.qname_len + lock_get_mem(&q->entry.lock);
s += addrtree_size(r->tree4);
s += addrtree_size(r->tree6);
return s;
}
/** new query for ecs module */
static int
subnet_new_qstate(struct module_qstate *qstate, int id)
{
struct subnet_qstate *sq = (struct subnet_qstate*)regional_alloc(
qstate->region, sizeof(struct subnet_qstate));
if(!sq)
return 0;
qstate->minfo[id] = sq;
memset(sq, 0, sizeof(*sq));
sq->started_no_cache_store = qstate->no_cache_store;
sq->started_no_cache_lookup = qstate->no_cache_lookup;
return 1;
}
/** Add ecs struct to edns list, after parsing it to wire format. */
void
subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
struct module_qstate *qstate, struct regional *region)
{
size_t sn_octs, sn_octs_remainder;
sldns_buffer* buf = qstate->env->scratch_buffer;
if(ecs->subnet_validdata) {
log_assert(ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 ||
ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6);
log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP4 ||
ecs->subnet_source_mask <= INET_SIZE*8);
log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP6 ||
ecs->subnet_source_mask <= INET6_SIZE*8);
sn_octs = ecs->subnet_source_mask / 8;
sn_octs_remainder =
(size_t)((ecs->subnet_source_mask % 8)>0?1:0);
log_assert(sn_octs + sn_octs_remainder <= INET6_SIZE);
sldns_buffer_clear(buf);
sldns_buffer_write_u16(buf, ecs->subnet_addr_fam);
sldns_buffer_write_u8(buf, ecs->subnet_source_mask);
sldns_buffer_write_u8(buf, ecs->subnet_scope_mask);
sldns_buffer_write(buf, ecs->subnet_addr, sn_octs);
if(sn_octs_remainder)
sldns_buffer_write_u8(buf, ecs->subnet_addr[sn_octs] &
~(0xFF >> (ecs->subnet_source_mask % 8)));
sldns_buffer_flip(buf);
edns_opt_list_append(list,
qstate->env->cfg->client_subnet_opcode,
sn_octs + sn_octs_remainder + 4,
sldns_buffer_begin(buf), region);
}
}
int ecs_whitelist_check(struct query_info* qinfo,
uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
struct regional *region, int id, void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
struct subnet_env *sn_env;
if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
return 1;
sn_env = (struct subnet_env*)qstate->env->modinfo[id];
/* Cache by default, might be disabled after parsing EDNS option
* received from nameserver. */
if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) {
qstate->no_cache_store = 0;
}
if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream &&
qstate->env->cfg->client_subnet_always_forward) ||
ecs_is_whitelisted(sn_env->whitelist,
addr, addrlen, qinfo->qname, qinfo->qname_len,
qinfo->qclass))) {
/* Address on whitelist or client query contains ECS option, we
* want to sent out ECS. Only add option if it is not already
* set. */
if(!edns_opt_list_find(qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode)) {
subnet_ecs_opt_list_append(&sq->ecs_server_out,
&qstate->edns_opts_back_out, qstate, region);
}
sq->subnet_sent = 1;
}
else {
/* Outgoing ECS option is set, but we don't want to sent it to
* this address, remove option. */
if(edns_opt_list_find(qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode)) {
edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
}
sq->subnet_sent = 0;
}
return 1;
}
void
subnet_markdel(void* key)
{
struct msgreply_entry *e = (struct msgreply_entry*)key;
e->key.qtype = 0;
e->key.qclass = 0;
}
int
subnetmod_init(struct module_env *env, int id)
{
struct subnet_env *sn_env = (struct subnet_env*)calloc(1,
sizeof(struct subnet_env));
if(!sn_env) {
log_err("malloc failure");
return 0;
}
alloc_init(&sn_env->alloc, NULL, 0);
env->modinfo[id] = (void*)sn_env;
/* Warn that serve-expired and prefetch do not work with the subnet
* module cache. */
if(env->cfg->serve_expired)
log_warn(
"subnetcache: serve-expired is set but not working "
"for data originating from the subnet module cache.");
if(env->cfg->prefetch)
log_warn(
"subnetcache: prefetch is set but not working "
"for data originating from the subnet module cache.");
/* Copy msg_cache settings */
sn_env->subnet_msg_cache = slabhash_create(env->cfg->msg_cache_slabs,
HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size,
msg_cache_sizefunc, query_info_compare, query_entry_delete,
subnet_data_delete, NULL);
slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
if(!sn_env->subnet_msg_cache) {
log_err("subnetcache: could not create cache");
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
/* whitelist for edns subnet capable servers */
sn_env->whitelist = ecs_whitelist_create();
if(!sn_env->whitelist ||
!ecs_whitelist_apply_cfg(sn_env->whitelist, env->cfg)) {
log_err("subnetcache: could not create ECS whitelist");
slabhash_delete(sn_env->subnet_msg_cache);
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
verbose(VERB_QUERY, "subnetcache: option registered (%d)",
env->cfg->client_subnet_opcode);
/* Create new mesh state for all queries. */
env->unique_mesh = 1;
if(!edns_register_option(env->cfg->client_subnet_opcode,
env->cfg->client_subnet_always_forward /* bypass cache */,
1 /* no aggregation */, env)) {
log_err("subnetcache: could not register opcode");
ecs_whitelist_delete(sn_env->whitelist);
slabhash_delete(sn_env->subnet_msg_cache);
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
inplace_cb_register((void*)ecs_whitelist_check, inplace_cb_query, NULL,
env, id);
inplace_cb_register((void*)ecs_edns_back_parsed,
inplace_cb_edns_back_parsed, NULL, env, id);
inplace_cb_register((void*)ecs_query_response,
inplace_cb_query_response, NULL, env, id);
lock_rw_init(&sn_env->biglock);
return 1;
}
void
subnetmod_deinit(struct module_env *env, int id)
{
struct subnet_env *sn_env;
if(!env || !env->modinfo[id])
return;
sn_env = (struct subnet_env*)env->modinfo[id];
lock_rw_destroy(&sn_env->biglock);
inplace_cb_delete(env, inplace_cb_edns_back_parsed, id);
inplace_cb_delete(env, inplace_cb_query, id);
inplace_cb_delete(env, inplace_cb_query_response, id);
ecs_whitelist_delete(sn_env->whitelist);
slabhash_delete(sn_env->subnet_msg_cache);
alloc_clear(&sn_env->alloc);
free(sn_env);
env->modinfo[id] = NULL;
}
/** Tells client that upstream has no/improper support */
static void
cp_edns_bad_response(struct ecs_data *target, struct ecs_data *source)
{
target->subnet_scope_mask = 0;
target->subnet_source_mask = source->subnet_source_mask;
target->subnet_addr_fam = source->subnet_addr_fam;
memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE);
target->subnet_validdata = 1;
}
static void
delfunc(void *envptr, void *elemptr) {
struct reply_info *elem = (struct reply_info *)elemptr;
struct subnet_env *env = (struct subnet_env *)envptr;
reply_info_parsedelete(elem, &env->alloc);
}
static size_t
sizefunc(void *elemptr) {
struct reply_info *elem = (struct reply_info *)elemptr;
return sizeof (struct reply_info) - sizeof (struct rrset_ref)
+ elem->rrset_count * sizeof (struct rrset_ref)
+ elem->rrset_count * sizeof (struct ub_packed_rrset_key *);
}
/**
* Select tree from cache entry based on edns data.
* If for address family not present it will create a new one.
* NULL on failure to create. */
static struct addrtree*
get_tree(struct subnet_msg_cache_data *data, struct ecs_data *edns,
struct subnet_env *env, struct config_file* cfg)
{
struct addrtree *tree;
if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if (!data->tree4)
data->tree4 = addrtree_create(
cfg->max_client_subnet_ipv4, &delfunc,
&sizefunc, env, cfg->max_ecs_tree_size_ipv4);
tree = data->tree4;
} else {
if (!data->tree6)
data->tree6 = addrtree_create(
cfg->max_client_subnet_ipv6, &delfunc,
&sizefunc, env, cfg->max_ecs_tree_size_ipv6);
tree = data->tree6;
}
return tree;
}
static void
update_cache(struct module_qstate *qstate, int id)
{
struct msgreply_entry *mrep_entry;
struct addrtree *tree;
struct reply_info *rep;
struct query_info qinf;
struct subnet_env *sne = qstate->env->modinfo[id];
struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
struct slabhash *subnet_msg_cache = sne->subnet_msg_cache;
struct ecs_data *edns = &sq->ecs_client_in;
size_t i;
int only_match_scope_zero;
/* We already calculated hash upon lookup (lookup_and_reply) if we were
* allowed to look in the ECS cache */
hashvalue_type h = qstate->minfo[id] &&
((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated?
((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
query_info_hash(&qstate->qinfo, qstate->query_flags);
/* Step 1, general qinfo lookup */
struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h,
&qstate->qinfo, 1);
int need_to_insert = (lru_entry == NULL);
if (!lru_entry) {
void* data = calloc(1,
sizeof(struct subnet_msg_cache_data));
if(!data) {
log_err("malloc failed");
return;
}
qinf = qstate->qinfo;
qinf.qname = memdup(qstate->qinfo.qname,
qstate->qinfo.qname_len);
if(!qinf.qname) {
free(data);
log_err("memdup failed");
return;
}
mrep_entry = query_info_entrysetup(&qinf, data, h);
free(qinf.qname); /* if qname 'consumed', it is set to NULL */
if (!mrep_entry) {
free(data);
log_err("query_info_entrysetup failed");
return;
}
lru_entry = &mrep_entry->entry;
lock_rw_wrlock(&lru_entry->lock);
}
/* lru_entry->lock is locked regardless of how we got here,
* either from the slabhash_lookup, or above in the new allocated */
/* Step 2, find the correct tree */
if (!(tree = get_tree(lru_entry->data, edns, sne, qstate->env->cfg))) {
lock_rw_unlock(&lru_entry->lock);
log_err("subnetcache: cache insertion failed");
return;
}
lock_quick_lock(&sne->alloc.lock);
rep = reply_info_copy(qstate->return_msg->rep, &sne->alloc, NULL);
lock_quick_unlock(&sne->alloc.lock);
if (!rep) {
lock_rw_unlock(&lru_entry->lock);
log_err("subnetcache: cache insertion failed");
return;
}
/* store RRsets */
for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
}
reply_info_set_ttls(rep, *qstate->env->now);
rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */
rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */
if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0)
only_match_scope_zero = 1;
else only_match_scope_zero = 0;
addrtree_insert(tree, (addrkey_t*)edns->subnet_addr,
edns->subnet_source_mask, sq->max_scope, rep,
rep->ttl, *qstate->env->now, only_match_scope_zero);
lock_rw_unlock(&lru_entry->lock);
if (need_to_insert) {
slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data,
NULL);
}
}
/** Lookup in cache and reply true iff reply is sent. */
static int
lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch)
{
struct lruhash_entry *e;
struct module_env *env = qstate->env;
struct subnet_env *sne = (struct subnet_env*)env->modinfo[id];
hashvalue_type h = query_info_hash(&qstate->qinfo, qstate->query_flags);
struct subnet_msg_cache_data *data;
struct ecs_data *ecs = &sq->ecs_client_in;
struct addrtree *tree;
struct addrnode *node;
uint8_t scope;
memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out));
if (sq) {
sq->qinfo_hash = h; /* Might be useful on cache miss */
sq->qinfo_hash_calculated = 1;
}
e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1);
if (!e) return 0; /* qinfo not in cache */
data = e->data;
tree = (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4)?
data->tree4 : data->tree6;
if (!tree) { /* qinfo in cache but not for this family */
lock_rw_unlock(&e->lock);
return 0;
}
node = addrtree_find(tree, (addrkey_t*)ecs->subnet_addr,
ecs->subnet_source_mask, *env->now);
if (!node) { /* plain old cache miss */
lock_rw_unlock(&e->lock);
return 0;
}
qstate->return_msg = tomsg(NULL, &qstate->qinfo,
(struct reply_info *)node->elem, qstate->region, *env->now, 0,
env->scratch);
scope = (uint8_t)node->scope;
lock_rw_unlock(&e->lock);
if (!qstate->return_msg) { /* Failed allocation or expired TTL */
return 0;
}
if (sq->subnet_downstream) { /* relay to interested client */
sq->ecs_client_out.subnet_scope_mask = scope;
sq->ecs_client_out.subnet_addr_fam = ecs->subnet_addr_fam;
sq->ecs_client_out.subnet_source_mask = ecs->subnet_source_mask;
memcpy(&sq->ecs_client_out.subnet_addr, &ecs->subnet_addr,
INET6_SIZE);
sq->ecs_client_out.subnet_validdata = 1;
}
if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) {
qstate->need_refetch = 1;
}
return 1;
}
/**
* Test first bits of addresses for equality. Caller is responsible
* for making sure that both a and b are at least net/8 octets long.
* @param a: first address.
* @param a: seconds address.
* @param net: Number of bits to test.
* @return: 1 if equal, 0 otherwise.
*/
static int
common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
{
size_t n = (size_t)net / 8;
return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
}
static enum module_ext_state
eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
{
struct subnet_env *sne = qstate->env->modinfo[id];
struct ecs_data *c_in = &sq->ecs_client_in; /* rcvd from client */
struct ecs_data *c_out = &sq->ecs_client_out;/* will send to client */
struct ecs_data *s_in = &sq->ecs_server_in; /* rcvd from auth */
struct ecs_data *s_out = &sq->ecs_server_out;/* sent to auth */
memset(c_out, 0, sizeof(*c_out));
if (!qstate->return_msg) {
/* already an answer and its not a message, but retain
* the actual rcode, instead of module_error, so send
* module_finished */
return module_finished;
}
/* We have not asked for subnet data */
if (!sq->subnet_sent) {
if (s_in->subnet_validdata)
verbose(VERB_QUERY, "subnetcache: received spurious data");
if (sq->subnet_downstream) /* Copy back to client */
cp_edns_bad_response(c_out, c_in);
return module_finished;
}
/* subnet sent but nothing came back */
if (!s_in->subnet_validdata) {
/* The authority indicated no support for edns subnet. As a
* consequence the answer ended up in the regular cache. It
* is still useful to put it in the edns subnet cache for
* when a client explicitly asks for subnet specific answer. */
verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
if(!sq->started_no_cache_store) {
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
lock_rw_unlock(&sne->biglock);
}
if (sq->subnet_downstream)
cp_edns_bad_response(c_out, c_in);
return module_finished;
}
/* Being here means we have asked for and got a subnet specific
* answer. Also, the answer from the authority is not yet cached
* anywhere. */
/* can we accept response? */
if(s_out->subnet_addr_fam != s_in->subnet_addr_fam ||
s_out->subnet_source_mask != s_in->subnet_source_mask ||
!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
s_out->subnet_source_mask))
{
/* we can not accept, restart query without option */
verbose(VERB_QUERY, "subnetcache: forged data");
s_out->subnet_validdata = 0;
(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
return module_restart_next;
}
lock_rw_wrlock(&sne->biglock);
if(!sq->started_no_cache_store) {
update_cache(qstate, id);
}
sne->num_msg_nocache++;
lock_rw_unlock(&sne->biglock);
if (sq->subnet_downstream) {
/* Client wants to see the answer, echo option back
* and adjust the scope. */
c_out->subnet_addr_fam = c_in->subnet_addr_fam;
c_out->subnet_source_mask = c_in->subnet_source_mask;
memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE);
c_out->subnet_scope_mask = sq->max_scope;
/* Limit scope returned to client to scope used for caching. */
if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if(c_out->subnet_scope_mask >
qstate->env->cfg->max_client_subnet_ipv4) {
c_out->subnet_scope_mask =
qstate->env->cfg->max_client_subnet_ipv4;
}
}
else if(c_out->subnet_scope_mask >
qstate->env->cfg->max_client_subnet_ipv6) {
c_out->subnet_scope_mask =
qstate->env->cfg->max_client_subnet_ipv6;
}
c_out->subnet_validdata = 1;
}
return module_finished;
}
/** Parse EDNS opt data containing ECS */
static int
parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs)
{
memset(ecs, 0, sizeof(*ecs));
if (ecs_option->opt_len < 4)
return 0;
ecs->subnet_addr_fam = sldns_read_uint16(ecs_option->opt_data);
ecs->subnet_source_mask = ecs_option->opt_data[2];
ecs->subnet_scope_mask = ecs_option->opt_data[3];
/* remaining bytes indicate address */
/* validate input*/
/* option length matches calculated length? */
if (ecs_option->opt_len != (size_t)((ecs->subnet_source_mask+7)/8 + 4))
return 0;
if (ecs_option->opt_len - 4 > INET6_SIZE || ecs_option->opt_len == 0)
return 0;
if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if (ecs->subnet_source_mask > 32 || ecs->subnet_scope_mask > 32)
return 0;
} else if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6) {
if (ecs->subnet_source_mask > 128 ||
ecs->subnet_scope_mask > 128)
return 0;
} else
return 0;
/* valid ECS data, write to ecs_data */
if (copy_clear(ecs->subnet_addr, INET6_SIZE, ecs_option->opt_data + 4,
ecs_option->opt_len - 4, ecs->subnet_source_mask))
return 0;
ecs->subnet_validdata = 1;
return 1;
}
void
subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
struct config_file* cfg)
{
void* sinaddr;
/* Construct subnet option from original query */
if(((struct sockaddr_in*)ss)->sin_family == AF_INET) {
ecs->subnet_source_mask = cfg->max_client_subnet_ipv4;
ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP4;
sinaddr = &((struct sockaddr_in*)ss)->sin_addr;
if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
(uint8_t *)sinaddr, INET_SIZE,
ecs->subnet_source_mask)) {
ecs->subnet_validdata = 1;
}
}
#ifdef INET6
else {
ecs->subnet_source_mask = cfg->max_client_subnet_ipv6;
ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP6;
sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr;
if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
(uint8_t *)sinaddr, INET6_SIZE,
ecs->subnet_source_mask)) {
ecs->subnet_validdata = 1;
}
}
#else
/* We don't know how to handle ip6, just pass */
#endif /* INET6 */
}
int
ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
int id, void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
if(!response || !(sq=(struct subnet_qstate*)qstate->minfo[id]))
return 1;
if(sq->subnet_sent &&
FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_REFUSED) {
/* REFUSED response to ECS query, remove ECS option. */
edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out));
} else if (!sq->track_max_scope &&
FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR &&
response->rep->an_numrrsets > 0
) {
struct ub_packed_rrset_key* s = response->rep->rrsets[0];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(qstate->qinfo.qname,
s->rk.dname) == 0) {
/* CNAME response for QNAME. From now on keep track of
* longest received ECS prefix for all queries on this
* qstate. */
sq->track_max_scope = 1;
}
}
return 1;
}
/** verbose print edns subnet option in pretty print */
static void
subnet_log_print(const char* s, struct edns_option* ecs_opt)
{
if(verbosity >= VERB_ALGO) {
char buf[256];
char* str = buf;
size_t str_len = sizeof(buf);
if(!ecs_opt) {
verbose(VERB_ALGO, "%s (null)", s);
return;
}
(void)sldns_wire2str_edns_subnet_print(&str, &str_len,
ecs_opt->opt_data, ecs_opt->opt_len);
verbose(VERB_ALGO, "%s %s", s, buf);
}
}
int
ecs_edns_back_parsed(struct module_qstate* qstate, int id,
void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
struct edns_option* ecs_opt;
if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
return 1;
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_back_in,
qstate->env->cfg->client_subnet_opcode)) &&
parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
sq->subnet_sent && sq->ecs_server_in.subnet_validdata) {
subnet_log_print("answer has edns subnet", ecs_opt);
/* Only skip global cache store if we sent an ECS option
* and received one back. Answers from non-whitelisted
* servers will end up in global cache. Answers for
* queries with 0 source will not (unless nameserver
* does not support ECS). */
qstate->no_cache_store = 1;
if(!sq->track_max_scope || (sq->track_max_scope &&
sq->ecs_server_in.subnet_scope_mask >
sq->max_scope))
sq->max_scope = sq->ecs_server_in.subnet_scope_mask;
}
return 1;
}
void
subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
int id, struct outbound_entry* outbound)
{
struct subnet_env *sne = qstate->env->modinfo[id];
struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "subnetcache[module %d] operate: extstate:%s "
"event:%s", id, strextstate(qstate->ext_state[id]),
strmodulevent(event));
log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
if((event == module_event_new || event == module_event_pass) &&
sq == NULL) {
struct edns_option* ecs_opt;
if(!subnet_new_qstate(qstate, id)) {
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return;
}
sq = (struct subnet_qstate*)qstate->minfo[id];
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode))) {
if(!parse_subnet_option(ecs_opt, &sq->ecs_client_in)) {
/* Wrongly formatted ECS option. RFC mandates to
* return FORMERROR. */
qstate->return_rcode = LDNS_RCODE_FORMERR;
qstate->ext_state[id] = module_finished;
return;
}
subnet_log_print("query has edns subnet", ecs_opt);
sq->subnet_downstream = 1;
}
else if(qstate->mesh_info->reply_list) {
subnet_option_from_ss(
&qstate->mesh_info->reply_list->query_reply.client_addr,
&sq->ecs_client_in, qstate->env->cfg);
}
else if(qstate->client_addr.ss_family != AF_UNSPEC) {
subnet_option_from_ss(
&qstate->client_addr,
&sq->ecs_client_in, qstate->env->cfg);
}
if(sq->ecs_client_in.subnet_validdata == 0) {
/* No clients are interested in result or we could not
* parse it, we don't do client subnet */
sq->ecs_server_out.subnet_validdata = 0;
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
/* Limit to minimum allowed source mask */
if(sq->ecs_client_in.subnet_source_mask != 0 && (
(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 &&
sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv4) ||
(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 &&
sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv6))) {
qstate->return_rcode = LDNS_RCODE_REFUSED;
qstate->ext_state[id] = module_finished;
return;
}
if(!sq->started_no_cache_lookup && !qstate->blacklist) {
lock_rw_wrlock(&sne->biglock);
if(qstate->mesh_info->reply_list &&
lookup_and_reply(qstate, id, sq,
qstate->env->cfg->prefetch)) {
sne->num_msg_cache++;
lock_rw_unlock(&sne->biglock);
verbose(VERB_QUERY, "subnetcache: answered from cache");
qstate->ext_state[id] = module_finished;
subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate,
qstate->region);
if(verbosity >= VERB_ALGO) {
subnet_log_print("reply has edns subnet",
edns_opt_list_find(
qstate->edns_opts_front_out,
qstate->env->cfg->
client_subnet_opcode));
}
return;
}
lock_rw_unlock(&sne->biglock);
}
sq->ecs_server_out.subnet_addr_fam =
sq->ecs_client_in.subnet_addr_fam;
sq->ecs_server_out.subnet_source_mask =
sq->ecs_client_in.subnet_source_mask;
/* Limit source prefix to configured maximum */
if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4
&& sq->ecs_server_out.subnet_source_mask >
qstate->env->cfg->max_client_subnet_ipv4)
sq->ecs_server_out.subnet_source_mask =
qstate->env->cfg->max_client_subnet_ipv4;
else if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6
&& sq->ecs_server_out.subnet_source_mask >
qstate->env->cfg->max_client_subnet_ipv6)
sq->ecs_server_out.subnet_source_mask =
qstate->env->cfg->max_client_subnet_ipv6;
/* Safe to copy completely, even if the source is limited by the
* configuration. subnet_ecs_opt_list_append() will limit the address.
* */
memcpy(&sq->ecs_server_out.subnet_addr,
sq->ecs_client_in.subnet_addr, INET6_SIZE);
sq->ecs_server_out.subnet_scope_mask = 0;
sq->ecs_server_out.subnet_validdata = 1;
if(sq->ecs_server_out.subnet_source_mask != 0 &&
qstate->env->cfg->client_subnet_always_forward &&
sq->subnet_downstream)
/* ECS specific data required, do not look at the global
* cache in other modules. */
qstate->no_cache_lookup = 1;
/* pass request to next module */
verbose(VERB_ALGO,
"subnetcache: not found in cache. pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
/* Query handed back by next module, we have a 'final' answer */
if(sq && event == module_event_moddone) {
qstate->ext_state[id] = eval_response(qstate, id, sq);
if(qstate->ext_state[id] == module_finished &&
qstate->return_msg) {
subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate,
qstate->region);
if(verbosity >= VERB_ALGO) {
subnet_log_print("reply has edns subnet",
edns_opt_list_find(
qstate->edns_opts_front_out,
qstate->env->cfg->
client_subnet_opcode));
}
}
qstate->no_cache_store = sq->started_no_cache_store;
qstate->no_cache_lookup = sq->started_no_cache_lookup;
return;
}
if(sq && outbound) {
return;
}
/* We are being revisited */
if(event == module_event_pass || event == module_event_new) {
/* Just pass it on, we already did the work */
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
if(!sq && (event == module_event_moddone)) {
/* during priming, module done but we never started */
qstate->ext_state[id] = module_finished;
return;
}
log_err("subnetcache: bad event %s", strmodulevent(event));
qstate->ext_state[id] = module_error;
return;
}
void
subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id))
{
/* qstate has no data outside region */
}
void
subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
{
/* Not used */
}
size_t
subnetmod_get_mem(struct module_env *env, int id)
{
struct subnet_env *sn_env = env->modinfo[id];
if (!sn_env) return 0;
return sizeof(*sn_env) +
slabhash_get_mem(sn_env->subnet_msg_cache) +
ecs_whitelist_get_mem(sn_env->whitelist);
}
/**
* The module function block
*/
static struct module_func_block subnetmod_block = {
"subnetcache", &subnetmod_init, &subnetmod_deinit, &subnetmod_operate,
&subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem
};
struct module_func_block*
subnetmod_get_funcblock(void)
{
return &subnetmod_block;
}
/** Wrappers for static functions to unit test */
size_t
unittest_wrapper_subnetmod_sizefunc(void *elemptr)
{
return sizefunc(elemptr);
}
#endif /* CLIENT_SUBNET */
@@ -1,156 +0,0 @@
/*
* edns-subnet/subnetmod.h - edns subnet module. Must be called before validator
* and iterator.
*
* Copyright (c) 2013, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 the NLNET LABS nor the names of its 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
* subnet module for unbound.
*/
#ifndef SUBNETMOD_H
#define SUBNETMOD_H
#include "util/module.h"
#include "services/outbound_list.h"
#include "util/alloc.h"
#include "util/net_help.h"
#include "util/storage/slabhash.h"
#include "util/data/dname.h"
#include "edns-subnet/addrtree.h"
#include "edns-subnet/edns-subnet.h"
/**
* Global state for the subnet module.
*/
struct subnet_env {
/** shared message cache
* key: struct query_info*
* data: struct subnet_msg_cache_data* */
struct slabhash* subnet_msg_cache;
/** access control, which upstream servers we send client address */
struct ecs_whitelist* whitelist;
/** allocation service */
struct alloc_cache alloc;
lock_rw_type biglock;
/** number of messages from cache */
size_t num_msg_cache;
/** number of messages not from cache */
size_t num_msg_nocache;
};
struct subnet_msg_cache_data {
struct addrtree* tree4;
struct addrtree* tree6;
};
struct subnet_qstate {
/** We need the hash for both cache lookup and insert */
hashvalue_type qinfo_hash;
int qinfo_hash_calculated;
/** ecs_data for client communication */
struct ecs_data ecs_client_in;
struct ecs_data ecs_client_out;
/** ecss data for server communication */
struct ecs_data ecs_server_in;
struct ecs_data ecs_server_out;
int subnet_downstream;
int subnet_sent;
/** keep track of longest received scope, set after receiving CNAME for
* incoming QNAME. */
int track_max_scope;
/** longest received scope mask since track_max_scope is set. This value
* is used for caching and answereing to client. */
uint8_t max_scope;
/** has the subnet module been started with no_cache_store? */
int started_no_cache_store;
/** has the subnet module been started with no_cache_lookup? */
int started_no_cache_lookup;
};
void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));
size_t msg_cache_sizefunc(void* k, void* d);
/**
* Get the module function block.
* @return: function block with function pointers to module methods.
*/
struct module_func_block* subnetmod_get_funcblock(void);
/** subnet module init */
int subnetmod_init(struct module_env* env, int id);
/** subnet module deinit */
void subnetmod_deinit(struct module_env* env, int id);
/** subnet module operate on a query */
void subnetmod_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound);
/** subnet module */
void subnetmod_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** subnet module cleanup query state */
void subnetmod_clear(struct module_qstate* qstate, int id);
/** subnet module alloc size routine */
size_t subnetmod_get_mem(struct module_env* env, int id);
/** Wrappers for static functions to unit test */
size_t unittest_wrapper_subnetmod_sizefunc(void *elemptr);
/** Whitelist check, called just before query is sent upstream. */
int ecs_whitelist_check(struct query_info* qinfo, uint16_t flags,
struct module_qstate* qstate, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* zone, size_t zonelen,
struct regional* region, int id, void* cbargs);
/** Check whether response from server contains ECS record, if so, skip cache
* store. Called just after parsing EDNS data from server. */
int ecs_edns_back_parsed(struct module_qstate* qstate, int id, void* cbargs);
/** Remove ECS record from back_out when query resulted in REFUSED response. */
int ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
int id, void* cbargs);
/** mark subnet msg to be deleted */
void subnet_markdel(void* key);
/** Add ecs struct to edns list, after parsing it to wire format. */
void subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
struct module_qstate *qstate, struct regional *region);
/** Create ecs_data from the sockaddr_storage information. */
void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
struct config_file* cfg);
#endif /* SUBNETMOD_H */

Some files were not shown because too many files have changed in this diff Show More