├── .gitignore ├── COPYING ├── Makefile ├── README.md ├── binutils ├── binutils.go ├── binutils_darwin.go ├── binutils_linux.go ├── binutils_windows.go ├── parent_check_unix.go ├── parent_check_windows.go ├── set_env.go ├── set_env_darwin.go ├── set_env_linux.go └── set_env_windows.go ├── build.sh ├── build_linux.sh ├── build_macos.sh ├── build_windows.bat ├── conn ├── boundif_android.go ├── boundif_windows.go ├── conn.go ├── conn_default.go ├── conn_linux.go ├── mark_default.go └── mark_unix.go ├── device ├── allowedips.go ├── allowedips_rand_test.go ├── allowedips_test.go ├── bind_test.go ├── constants.go ├── cookie.go ├── cookie_test.go ├── device.go ├── device_test.go ├── endpoint_test.go ├── indextable.go ├── ip.go ├── kdf_test.go ├── keypair.go ├── logger.go ├── misc.go ├── noise-helpers.go ├── noise-protocol.go ├── noise-types.go ├── noise_test.go ├── peer.go ├── peer_test.go ├── pools.go ├── queueconstants_android.go ├── queueconstants_default.go ├── queueconstants_ios.go ├── receive.go ├── send.go ├── sticky_default.go ├── sticky_linux.go ├── timers.go ├── tun.go ├── tun_test.go ├── uapi.go └── version.go ├── dns ├── LICENSE ├── Makefile.doc ├── core │ ├── coredns.go │ ├── dnsserver │ │ ├── address.go │ │ ├── address_test.go │ │ ├── config.go │ │ ├── https.go │ │ ├── log_test.go │ │ ├── onstartup.go │ │ ├── register.go │ │ ├── register_test.go │ │ ├── server.go │ │ ├── server_grpc.go │ │ ├── server_https.go │ │ ├── server_test.go │ │ ├── server_tls.go │ │ └── zdirectives.go │ └── plugin │ │ └── zplugin.go ├── coredns.go ├── coremain │ ├── run.go │ └── version.go ├── directives_generate.go ├── owners_generate.go ├── pb │ ├── Makefile │ ├── dns.pb.go │ └── dns.proto ├── plugin.cfg ├── plugin │ ├── acl │ │ ├── README.md │ │ ├── acl.go │ │ ├── acl_test.go │ │ ├── metrics.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── any │ │ ├── README.md │ │ ├── any.go │ │ ├── any_test.go │ │ └── setup.go │ ├── auto │ │ ├── README.md │ │ ├── auto.go │ │ ├── log_test.go │ │ ├── regexp.go │ │ ├── regexp_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── walk.go │ │ ├── walk_test.go │ │ ├── watcher_test.go │ │ ├── xfr.go │ │ └── zone.go │ ├── autopath │ │ ├── README.md │ │ ├── autopath.go │ │ ├── autopath_test.go │ │ ├── cname.go │ │ ├── metrics.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── azure │ │ ├── README.md │ │ ├── azure.go │ │ ├── azure_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── backend.go │ ├── backend_lookup.go │ ├── bind │ │ ├── README.md │ │ ├── bind.go │ │ ├── log_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── bufsize │ │ ├── README.md │ │ ├── bufsize.go │ │ ├── bufsize_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── cache │ │ ├── README.md │ │ ├── cache.go │ │ ├── cache_test.go │ │ ├── dnssec.go │ │ ├── dnssec_test.go │ │ ├── error_test.go │ │ ├── freq │ │ │ ├── freq.go │ │ │ └── freq_test.go │ │ ├── fuzz.go │ │ ├── handler.go │ │ ├── item.go │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── prefech_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ └── spoof_test.go │ ├── cancel │ │ ├── README.md │ │ ├── cancel.go │ │ ├── cancel_test.go │ │ └── setup_test.go │ ├── chaos │ │ ├── README.md │ │ ├── chaos.go │ │ ├── chaos_test.go │ │ ├── fuzz.go │ │ ├── log_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ └── zowners.go │ ├── clouddns │ │ ├── README.md │ │ ├── clouddns.go │ │ ├── clouddns_test.go │ │ ├── gcp.go │ │ ├── log_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── debug │ │ ├── README.md │ │ ├── debug.go │ │ ├── debug_test.go │ │ ├── log_test.go │ │ ├── pcap.go │ │ └── pcap_test.go │ ├── deprecated │ │ └── setup.go │ ├── dns64 │ │ ├── README.md │ │ ├── dns64.go │ │ ├── dns64_test.go │ │ ├── metrics.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── dnsredir │ │ ├── .drone.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── _config.yml │ │ ├── checkdown.go │ │ ├── dnsredir.go │ │ ├── doh-ietf.go │ │ ├── doh-json.go │ │ ├── healthcheck.go │ │ ├── healthcheck_test.go │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── namelist.go │ │ ├── parse.go │ │ ├── parse_test.go │ │ ├── policy.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── string_set.go │ │ ├── type.go │ │ ├── upstream.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── dnssec │ │ ├── README.md │ │ ├── black_lies.go │ │ ├── black_lies_bitmap_test.go │ │ ├── black_lies_test.go │ │ ├── cache.go │ │ ├── cache_test.go │ │ ├── dnskey.go │ │ ├── dnssec.go │ │ ├── dnssec_test.go │ │ ├── handler.go │ │ ├── handler_test.go │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── responsewriter.go │ │ ├── rrsig.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── dnstap │ │ ├── README.md │ │ ├── dnstapio │ │ │ ├── dnstap_encoder.go │ │ │ ├── dnstap_encoder_test.go │ │ │ ├── io.go │ │ │ └── io_test.go │ │ ├── handler.go │ │ ├── handler_test.go │ │ ├── log_test.go │ │ ├── msg │ │ │ └── msg.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ └── writer.go │ ├── done.go │ ├── erratic │ │ ├── README.md │ │ ├── autopath.go │ │ ├── erratic.go │ │ ├── erratic_test.go │ │ ├── log_test.go │ │ ├── ready.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ └── xfr.go │ ├── errors │ │ ├── README.md │ │ ├── benchmark_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── log_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── etcd │ │ ├── README.md │ │ ├── cname_test.go │ │ ├── etcd.go │ │ ├── group_test.go │ │ ├── handler.go │ │ ├── log_test.go │ │ ├── lookup_test.go │ │ ├── msg │ │ │ ├── path.go │ │ │ ├── path_test.go │ │ │ ├── service.go │ │ │ ├── service_test.go │ │ │ ├── type.go │ │ │ └── type_test.go │ │ ├── multi_test.go │ │ ├── other_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ └── xfr.go │ ├── file │ │ ├── README.md │ │ ├── closest.go │ │ ├── closest_test.go │ │ ├── delegation_test.go │ │ ├── delete_test.go │ │ ├── dname.go │ │ ├── dname_test.go │ │ ├── dnssec_test.go │ │ ├── dnssex_test.go │ │ ├── ds_test.go │ │ ├── ent_test.go │ │ ├── example_org.go │ │ ├── file.go │ │ ├── file_test.go │ │ ├── fuzz.go │ │ ├── glue_test.go │ │ ├── include_test.go │ │ ├── log_test.go │ │ ├── lookup.go │ │ ├── lookup_test.go │ │ ├── notify.go │ │ ├── nsec3_test.go │ │ ├── reload.go │ │ ├── reload_test.go │ │ ├── rrutil │ │ │ └── util.go │ │ ├── secondary.go │ │ ├── secondary_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── shutdown.go │ │ ├── tree │ │ │ ├── all.go │ │ │ ├── auth_walk.go │ │ │ ├── elem.go │ │ │ ├── glue.go │ │ │ ├── less.go │ │ │ ├── less_test.go │ │ │ ├── print.go │ │ │ ├── tree.go │ │ │ └── walk.go │ │ ├── wildcard.go │ │ ├── wildcard_test.go │ │ ├── xfr.go │ │ ├── xfr_test.go │ │ ├── zone.go │ │ └── zone_test.go │ ├── forward │ │ ├── README.md │ │ ├── connect.go │ │ ├── dnstap.go │ │ ├── forward.go │ │ ├── forward_test.go │ │ ├── fuzz.go │ │ ├── health.go │ │ ├── health_test.go │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── persistent.go │ │ ├── persistent_test.go │ │ ├── policy.go │ │ ├── proxy.go │ │ ├── proxy_test.go │ │ ├── setup.go │ │ ├── setup_policy_test.go │ │ ├── setup_test.go │ │ └── type.go │ ├── grpc │ │ ├── README.md │ │ ├── grpc.go │ │ ├── grpc_test.go │ │ ├── metrics.go │ │ ├── policy.go │ │ ├── proxy.go │ │ ├── proxy_test.go │ │ ├── setup.go │ │ ├── setup_policy_test.go │ │ └── setup_test.go │ ├── health │ │ ├── README.md │ │ ├── health.go │ │ ├── health_test.go │ │ ├── log_test.go │ │ ├── overloaded.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── hosts │ │ ├── README.md │ │ ├── hosts.go │ │ ├── hosts_test.go │ │ ├── hostsfile.go │ │ ├── hostsfile_test.go │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── import │ │ └── README.md │ ├── k8s_external │ │ ├── README.md │ │ ├── apex.go │ │ ├── apex_test.go │ │ ├── external.go │ │ ├── external_test.go │ │ ├── msg_to_dns.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── kubernetes │ │ ├── README.md │ │ ├── autopath.go │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── external.go │ │ ├── external_test.go │ │ ├── handler.go │ │ ├── handler_case_test.go │ │ ├── handler_ignore_emptyservice_test.go │ │ ├── handler_pod_disabled_test.go │ │ ├── handler_pod_insecure_test.go │ │ ├── handler_pod_verified_test.go │ │ ├── handler_test.go │ │ ├── informer_test.go │ │ ├── kubernetes.go │ │ ├── kubernetes_apex_test.go │ │ ├── kubernetes_test.go │ │ ├── local.go │ │ ├── log_test.go │ │ ├── metadata.go │ │ ├── metadata_test.go │ │ ├── metrics.go │ │ ├── metrics_test.go │ │ ├── namespace.go │ │ ├── namespace_test.go │ │ ├── ns.go │ │ ├── ns_test.go │ │ ├── object │ │ │ ├── endpoint.go │ │ │ ├── informer.go │ │ │ ├── object.go │ │ │ ├── pod.go │ │ │ └── service.go │ │ ├── parse.go │ │ ├── parse_test.go │ │ ├── ready.go │ │ ├── reverse.go │ │ ├── reverse_test.go │ │ ├── setup.go │ │ ├── setup_reverse_test.go │ │ ├── setup_test.go │ │ ├── setup_ttl_test.go │ │ ├── watch.go │ │ ├── xfr.go │ │ └── xfr_test.go │ ├── loadbalance │ │ ├── README.md │ │ ├── handler.go │ │ ├── loadbalance.go │ │ ├── loadbalance_test.go │ │ ├── log_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── log │ │ ├── README.md │ │ ├── log.go │ │ ├── log_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── log_test.go │ ├── loop │ │ ├── README.md │ │ ├── log_test.go │ │ ├── loop.go │ │ ├── loop_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── metadata │ │ ├── README.md │ │ ├── log_test.go │ │ ├── metadata.go │ │ ├── metadata_test.go │ │ ├── provider.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── metrics │ │ ├── README.md │ │ ├── context.go │ │ ├── handler.go │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── metrics_test.go │ │ ├── registry.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ └── vars │ │ │ ├── report.go │ │ │ └── vars.go │ ├── normalize.go │ ├── normalize_test.go │ ├── nsid │ │ ├── README.md │ │ ├── log_test.go │ │ ├── nsid.go │ │ ├── nsid_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── pkg │ │ ├── cache │ │ │ ├── cache.go │ │ │ ├── cache_test.go │ │ │ └── shard_test.go │ │ ├── dnstest │ │ │ ├── multirecorder.go │ │ │ ├── multirecorder_test.go │ │ │ ├── recorder.go │ │ │ ├── recorder_test.go │ │ │ ├── server.go │ │ │ └── server_test.go │ │ ├── dnsutil │ │ │ ├── cname.go │ │ │ ├── cname_test.go │ │ │ ├── doc.go │ │ │ ├── join.go │ │ │ ├── join_test.go │ │ │ ├── reverse.go │ │ │ ├── reverse_test.go │ │ │ ├── ttl.go │ │ │ ├── ttl_test.go │ │ │ ├── zone.go │ │ │ └── zone_test.go │ │ ├── doh │ │ │ ├── doh.go │ │ │ └── doh_test.go │ │ ├── edns │ │ │ ├── edns.go │ │ │ └── edns_test.go │ │ ├── fall │ │ │ ├── fall.go │ │ │ └── fall_test.go │ │ ├── fuzz │ │ │ └── do.go │ │ ├── log │ │ │ ├── log.go │ │ │ ├── log_test.go │ │ │ ├── plugin.go │ │ │ └── plugin_test.go │ │ ├── nonwriter │ │ │ ├── nonwriter.go │ │ │ └── nonwriter_test.go │ │ ├── parse │ │ │ ├── host.go │ │ │ ├── host_test.go │ │ │ ├── parse.go │ │ │ ├── parse_test.go │ │ │ ├── transport.go │ │ │ └── transport_test.go │ │ ├── rcode │ │ │ ├── rcode.go │ │ │ └── rcode_test.go │ │ ├── replacer │ │ │ ├── replacer.go │ │ │ └── replacer_test.go │ │ ├── response │ │ │ ├── classify.go │ │ │ ├── typify.go │ │ │ └── typify_test.go │ │ ├── reuseport │ │ │ ├── listen_go111.go │ │ │ └── listen_go_not111.go │ │ ├── singleflight │ │ │ ├── singleflight.go │ │ │ └── singleflight_test.go │ │ ├── tls │ │ │ ├── tls.go │ │ │ └── tls_test.go │ │ ├── trace │ │ │ └── trace.go │ │ ├── transport │ │ │ └── transport.go │ │ ├── uniq │ │ │ ├── uniq.go │ │ │ └── uniq_test.go │ │ ├── up │ │ │ ├── up.go │ │ │ └── up_test.go │ │ └── upstream │ │ │ └── upstream.go │ ├── plugin.go │ ├── pprof │ │ ├── README.md │ │ ├── log_test.go │ │ ├── pprof.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── ready │ │ ├── README.md │ │ ├── list.go │ │ ├── readiness.go │ │ ├── ready.go │ │ ├── ready_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── register.go │ ├── reload │ │ ├── README.md │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── reload.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── rewrite │ │ ├── README.md │ │ ├── class.go │ │ ├── edns0.go │ │ ├── fuzz.go │ │ ├── log_test.go │ │ ├── name.go │ │ ├── name_test.go │ │ ├── reverter.go │ │ ├── reverter_test.go │ │ ├── rewrite.go │ │ ├── rewrite_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── ttl.go │ │ ├── ttl_test.go │ │ ├── type.go │ │ └── wire.go │ ├── root │ │ ├── README.md │ │ ├── log_test.go │ │ ├── root.go │ │ └── root_test.go │ ├── route53 │ │ ├── README.md │ │ ├── log_test.go │ │ ├── route53.go │ │ ├── route53_test.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── secondary │ │ ├── README.md │ │ ├── log_test.go │ │ ├── secondary.go │ │ ├── setup.go │ │ └── setup_test.go │ ├── sign │ │ ├── README.md │ │ ├── dnssec.go │ │ ├── file.go │ │ ├── file_test.go │ │ ├── keys.go │ │ ├── log_test.go │ │ ├── nsec.go │ │ ├── nsec_test.go │ │ ├── resign_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── sign.go │ │ ├── signer.go │ │ ├── signer_test.go │ │ └── testdata │ │ │ ├── Kmiek.nl.+013+59725.key │ │ │ ├── Kmiek.nl.+013+59725.private │ │ │ ├── db.miek.nl │ │ │ └── db.miek.nl_ns │ ├── template │ │ ├── README.md │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── template.go │ │ └── template_test.go │ ├── test │ │ ├── doc.go │ │ ├── file.go │ │ ├── file_test.go │ │ ├── helpers.go │ │ ├── responsewriter.go │ │ └── scrape.go │ ├── tls │ │ ├── README.md │ │ ├── log_test.go │ │ ├── test_ca.pem │ │ ├── test_cert.pem │ │ ├── test_key.pem │ │ ├── tls.go │ │ └── tls_test.go │ ├── trace │ │ ├── README.md │ │ ├── log_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── trace.go │ │ └── trace_test.go │ ├── transfer │ │ ├── README.md │ │ ├── notify.go │ │ ├── select_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── transfer.go │ │ └── transfer_test.go │ └── whoami │ │ ├── README.md │ │ ├── fuzz.go │ │ ├── log_test.go │ │ ├── setup.go │ │ ├── setup_test.go │ │ ├── whoami.go │ │ └── whoami_test.go ├── request │ ├── edns0.go │ ├── request.go │ ├── request_test.go │ └── writer.go └── test │ ├── auto_test.go │ ├── cache_test.go │ ├── chaos_test.go │ ├── compression_scrub_test.go │ ├── doc.go │ ├── ds_file_test.go │ ├── edns0_test.go │ ├── erratic_autopath_test.go │ ├── etcd_cache_test.go │ ├── etcd_credentials_test.go │ ├── etcd_test.go │ ├── example_test.go │ ├── file_cname_proxy_test.go │ ├── file_reload_test.go │ ├── file_serve_test.go │ ├── file_srv_additional_test.go │ ├── file_test.go │ ├── file_upstream_test.go │ ├── file_xfr_test.go │ ├── fuzz_corefile.go │ ├── grpc_test.go │ ├── hosts_file_test.go │ ├── log_test.go │ ├── metric_naming_test.go │ ├── metrics_test.go │ ├── miek_test.go │ ├── no_plugins_test.go │ ├── plugin_dnssec_test.go │ ├── presubmit_test.go │ ├── proxy_health_test.go │ ├── proxy_test.go │ ├── readme_test.go │ ├── reload_test.go │ ├── reverse_test.go │ ├── rewrite_test.go │ ├── secondary_test.go │ ├── server.go │ ├── server_reverse_test.go │ ├── server_test.go │ ├── template_upstream_test.go │ └── wildcard_test.go ├── examples └── bridge │ └── main.go ├── filter ├── filter.go └── filter_test.go ├── go.mod ├── go.sum ├── guiwrapper ├── .gitignore ├── logo.ico ├── main.go ├── password_entry.go ├── start_app.go ├── table.go └── util.go ├── ipc ├── uapi_bsd.go ├── uapi_linux.go ├── uapi_unix.go ├── uapi_windows.go └── winpipe │ ├── file.go │ ├── mksyscall.go │ ├── pipe.go │ └── zsyscall_windows.go ├── macos_installer ├── .gitignore ├── Info.plist ├── README.md ├── build.sh ├── installer_background.png └── packetfence-zero-trust-client.icns ├── main_darwin.go ├── main_detect_connection.go ├── main_dns.go ├── main_linux.go ├── main_shared.go ├── main_windows.go ├── outputlog └── outputlog.go ├── ratelimiter ├── ratelimiter.go └── ratelimiter_test.go ├── replay ├── replay.go └── replay_test.go ├── routes ├── routes_darwin.go ├── routes_linux.go └── routes_windows.go ├── rwcancel ├── fdset.go ├── rwcancel.go ├── rwcancel_windows.go ├── select_default.go └── select_linux.go ├── services ├── make_services.go ├── service.go └── service_map.go ├── tai64n ├── tai64n.go └── tai64n_test.go ├── tests └── netns.sh ├── traywrapper ├── main.go ├── manifest.xml ├── resources.rc └── version │ └── version.h ├── tun ├── operateonfd.go ├── tun.go ├── tun_darwin.go ├── tun_freebsd.go ├── tun_linux.go ├── tun_openbsd.go ├── tun_windows.go ├── tuntest │ └── tuntest.go └── wintun │ ├── iphlpapi │ ├── conversion_windows.go │ ├── mksyscall.go │ └── zsyscall_windows.go │ ├── namespace_windows.go │ ├── namespaceapi │ ├── mksyscall.go │ ├── namespaceapi_windows.go │ └── zsyscall_windows.go │ ├── nci │ ├── mksyscall.go │ ├── nci_windows.go │ └── zsyscall_windows.go │ ├── registry │ ├── mksyscall.go │ ├── registry_windows.go │ ├── registry_windows_test.go │ └── zregistry_windows.go │ ├── ring_windows.go │ ├── setupapi │ ├── mksyscall.go │ ├── setupapi_windows.go │ ├── setupapi_windows_test.go │ ├── types32_windows.go │ ├── types64_windows.go │ ├── types_windows.go │ ├── zsetupapi_windows.go │ └── zsetupapi_windows_test.go │ └── wintun_windows.go ├── ui └── icon │ └── wireguard.ico ├── util ├── app_name.go ├── icon │ ├── iconunix.go │ ├── iconwin.go │ ├── logo.ico │ ├── logo.png │ ├── make_icon.bat │ └── make_icon.sh ├── printappname │ └── main.go ├── udp.go ├── util.go ├── util_unix.go └── util_windows.go ├── wgrpc ├── Makefile ├── handlers.go ├── rpc.go ├── stop_unix.go ├── stop_windows.go ├── wgrpc.pb.go ├── wgrpc.proto └── wgrpc_grpc.pb.go ├── windows_installer ├── .editorconfig ├── .gitignore ├── README.md ├── build.bat ├── customactions.c ├── run.bat └── wireguard.wxs └── ztn ├── Makefile ├── api_client.go ├── bind_technique_base.go ├── bind_techniques.go ├── bind_techniques_test.go ├── bind_through_peer.go ├── buffer_pool.go ├── config.go ├── connection.go ├── constants.go ├── env_constants.go ├── event.go ├── natpmp.go ├── network_connection.go ├── peer_connection.go ├── peerrpc.go ├── peerrpc.pb.go ├── peerrpc.proto ├── peerrpc_grpc.pb.go ├── peerrpc_handlers.go ├── profile.go ├── profile_darwin.go ├── profile_linux.go ├── profile_windows.go ├── publicport.go ├── read_password.go ├── read_password_windows.go ├── upnpigd.go ├── util.go └── wg.go /.gitignore: -------------------------------------------------------------------------------- 1 | wireguard-go 2 | vendor 3 | .gopath 4 | ireallywantobuildon_linux.go 5 | 6 | # windows build related 7 | amd64 8 | x86 9 | .deps 10 | resources.syso -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | DESTDIR ?= 3 | BINDIR ?= $(PREFIX)/bin 4 | export GO111MODULE := on 5 | 6 | all: generate-version-and-build 7 | 8 | MAKEFLAGS += --no-print-directory 9 | 10 | generate-version-and-build: 11 | @export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \ 12 | tag="$$(git describe --dirty 2>/dev/null)" && \ 13 | ver="$$(printf 'package device\nconst WireGuardGoVersion = "%s"\n' "$${tag#v}")" && \ 14 | [ "$$(cat device/version.go 2>/dev/null)" != "$$ver" ] && \ 15 | echo "$$ver" > device/version.go && \ 16 | git update-index --assume-unchanged device/version.go || true 17 | @$(MAKE) wireguard-go 18 | 19 | wireguard-go: $(wildcard *.go) $(wildcard */*.go) 20 | go build -v -o "$@" 21 | 22 | install: wireguard-go 23 | @install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go" 24 | 25 | clean: 26 | rm -f wireguard-go 27 | 28 | .PHONY: all clean install generate-version-and-build 29 | -------------------------------------------------------------------------------- /binutils/binutils.go: -------------------------------------------------------------------------------- 1 | package binutils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path" 8 | "path/filepath" 9 | "runtime/debug" 10 | 11 | "github.com/inverse-inc/packetfence/go/sharedutils" 12 | ) 13 | 14 | var wireguardCmd *exec.Cmd 15 | 16 | func BinDir() string { 17 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 18 | sharedutils.CheckError(err) 19 | return dir 20 | } 21 | 22 | func BinPath(name string) string { 23 | return path.Join(BinDir(), name) 24 | } 25 | 26 | func RunCmd(cmd *exec.Cmd) { 27 | cmd.Stdout = os.Stdout 28 | cmd.Stderr = os.Stderr 29 | err := cmd.Start() 30 | sharedutils.CheckError(err) 31 | cmd.Wait() 32 | } 33 | 34 | func RunTunnelFG(envPath string) { 35 | cmd := exec.Command(BinPath("wireguard"), envPath, "--master-controlled") 36 | RunCmd(cmd) 37 | } 38 | 39 | func CapturePanic() { 40 | if err := recover(); err != nil { 41 | fmt.Println("Recoved panic in program:", err) 42 | debug.PrintStack() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /binutils/binutils_darwin.go: -------------------------------------------------------------------------------- 1 | package binutils 2 | 3 | import ( 4 | "os/exec" 5 | "time" 6 | ) 7 | 8 | func RunTunnel() { 9 | cmd := exec.Command("/usr/bin/osascript", "-e", `do shell script "'`+BinPath("wireguard")+`' `+Wgenv.Name()+` --master & sleep 10" with administrator privileges`) 10 | wireguardCmd = cmd 11 | go RunCmd(cmd) 12 | time.Sleep(10 * time.Second) 13 | cmd.Process.Kill() 14 | } 15 | 16 | func Elevate() { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /binutils/binutils_linux.go: -------------------------------------------------------------------------------- 1 | package binutils 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | func RunTunnel() { 8 | cmd := exec.Command("pkexec", BinPath("wireguard"), Wgenv.Name(), "--master") 9 | wireguardCmd = cmd 10 | RunCmd(cmd) 11 | } 12 | 13 | func Elevate() { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /binutils/parent_check_unix.go: -------------------------------------------------------------------------------- 1 | // +build darwin linux 2 | 3 | package binutils 4 | 5 | import "github.com/inverse-inc/wireguard-go/util" 6 | 7 | func CheckParentIsAlive(quit func()) { 8 | util.CheckGUIIsAliveUNIX(quit) 9 | } 10 | -------------------------------------------------------------------------------- /binutils/parent_check_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package binutils 4 | 5 | import "github.com/inverse-inc/wireguard-go/util" 6 | 7 | func CheckParentIsAlive(quit func()) { 8 | util.CheckGUIIsAliveWindows(quit) 9 | } 10 | -------------------------------------------------------------------------------- /binutils/set_env.go: -------------------------------------------------------------------------------- 1 | package binutils 2 | 3 | import "os" 4 | 5 | var Wgenv *os.File 6 | -------------------------------------------------------------------------------- /binutils/set_env_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package binutils 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path" 9 | "time" 10 | 11 | "github.com/inverse-inc/packetfence/go/sharedutils" 12 | ) 13 | 14 | func init() { 15 | var err error 16 | tmp := os.Getenv("HOME") 17 | Wgenv, err = os.OpenFile(path.Join(tmp, fmt.Sprintf(".wgenv-%d", time.Now().Unix())), os.O_RDWR|os.O_CREATE, 0600) 18 | sharedutils.CheckError(err) 19 | } 20 | 21 | func Setenv(k, v string) { 22 | _, err := Wgenv.WriteString(fmt.Sprintf("%s=%s\n", k, v)) 23 | sharedutils.CheckError(err) 24 | } 25 | -------------------------------------------------------------------------------- /binutils/set_env_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package binutils 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path" 9 | "time" 10 | 11 | "github.com/inverse-inc/packetfence/go/sharedutils" 12 | ) 13 | 14 | func init() { 15 | var err error 16 | tmp := os.Getenv("HOME") 17 | Wgenv, err = os.OpenFile(path.Join(tmp, fmt.Sprintf(".wgenv-%d", time.Now().Unix())), os.O_RDWR|os.O_CREATE, 0600) 18 | sharedutils.CheckError(err) 19 | } 20 | 21 | func Setenv(k, v string) { 22 | _, err := Wgenv.WriteString(fmt.Sprintf("%s=%s\n", k, v)) 23 | sharedutils.CheckError(err) 24 | } 25 | -------------------------------------------------------------------------------- /binutils/set_env_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package binutils 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path" 9 | "time" 10 | 11 | "github.com/inverse-inc/packetfence/go/sharedutils" 12 | ) 13 | 14 | func Setenv(k, v string) { 15 | if Wgenv == nil { 16 | var err error 17 | tmp := os.Getenv("TMP") 18 | Wgenv, err = os.Create(path.Join(tmp, fmt.Sprintf("wgenv-%d", time.Now().Unix()))) 19 | sharedutils.CheckError(err) 20 | } 21 | _, err := Wgenv.WriteString(fmt.Sprintf("%s=%s\n", k, v)) 22 | sharedutils.CheckError(err) 23 | } 24 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$GOOS" ]; then 4 | GOOS=linux 5 | fi 6 | 7 | if [ -z "$ARCH" ]; then 8 | ARCH=amd64 9 | fi 10 | 11 | if ! [ -f .deps/prepared ]; then 12 | mkdir .deps 13 | curl -L https://golang.org/dl/go1.15.1.$GOOS-$ARCH.tar.gz > .deps/go.tar.gz 14 | cd .deps/ 15 | tar -xvf go.tar.gz 16 | cd - 17 | 18 | touch .deps/prepared 19 | fi 20 | 21 | if [ -z "$BIN_OUTPUT" ]; then 22 | BIN_OUTPUT=wireguard-go 23 | fi 24 | 25 | ./.deps/go/bin/go build -v -o $BIN_OUTPUT || exit 1 26 | 27 | cd guiwrapper 28 | if [ -z "$GUIWRAPPER_BIN_OUTPUT" ]; then 29 | GUIWRAPPER_BIN_OUTPUT=guiwrapper 30 | fi 31 | 32 | ../.deps/go/bin/go build -v -o $GUIWRAPPER_BIN_OUTPUT || exit 1 33 | cd .. 34 | 35 | cd traywrapper 36 | if [ -z "$TRAYWRAPPER_BIN_OUTPUT" ]; then 37 | TRAYWRAPPER_BIN_OUTPUT=traywrapper 38 | fi 39 | 40 | ../.deps/go/bin/go build -v -o $TRAYWRAPPER_BIN_OUTPUT || exit 1 41 | cd .. 42 | -------------------------------------------------------------------------------- /build_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$ARCH" ]; then 4 | ARCH=amd64 5 | fi 6 | 7 | export ARCH=$ARCH 8 | 9 | mkdir -p $ARCH 10 | 11 | if [ ! -f .deps/prepared ]; then 12 | if which apt-get; then 13 | sudo apt-get update 14 | sudo apt-get install gcc libgl1-mesa-dev xorg-dev libgtk-3-dev libappindicator3-dev -y 15 | fi 16 | fi 17 | 18 | GOOS=linux \ 19 | GOARCH=$ARCH \ 20 | BIN_OUTPUT=$ARCH/wireguard \ 21 | GUIWRAPPER_BIN_OUTPUT=../$ARCH/guiwrapper \ 22 | TRAYWRAPPER_BIN_OUTPUT=../$ARCH/traywrapper \ 23 | ./build.sh 24 | 25 | cp util/icon/logo.png $ARCH/ 26 | 27 | -------------------------------------------------------------------------------- /build_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p amd64 4 | 5 | GOOS=darwin \ 6 | GOARCH=amd64 \ 7 | BIN_OUTPUT=amd64/wireguard \ 8 | GUIWRAPPER_BIN_OUTPUT=../amd64/guiwrapper \ 9 | TRAYWRAPPER_BIN_OUTPUT=../amd64/traywrapper \ 10 | ./build.sh 11 | 12 | -------------------------------------------------------------------------------- /conn/boundif_android.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package conn 7 | 8 | func (bind *nativeBind) PeekLookAtSocketFd4() (fd int, err error) { 9 | sysconn, err := bind.ipv4.SyscallConn() 10 | if err != nil { 11 | return -1, err 12 | } 13 | err = sysconn.Control(func(f uintptr) { 14 | fd = int(f) 15 | }) 16 | if err != nil { 17 | return -1, err 18 | } 19 | return 20 | } 21 | 22 | func (bind *nativeBind) PeekLookAtSocketFd6() (fd int, err error) { 23 | sysconn, err := bind.ipv6.SyscallConn() 24 | if err != nil { 25 | return -1, err 26 | } 27 | err = sysconn.Control(func(f uintptr) { 28 | fd = int(f) 29 | }) 30 | if err != nil { 31 | return -1, err 32 | } 33 | return 34 | } 35 | -------------------------------------------------------------------------------- /conn/mark_default.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!openbsd,!freebsd 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package conn 9 | 10 | func (bind *nativeBind) SetMark(mark uint32) error { 11 | return nil 12 | } 13 | -------------------------------------------------------------------------------- /device/endpoint_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "math/rand" 10 | "net" 11 | ) 12 | 13 | type DummyEndpoint struct { 14 | src [16]byte 15 | dst [16]byte 16 | } 17 | 18 | func CreateDummyEndpoint() (*DummyEndpoint, error) { 19 | var end DummyEndpoint 20 | if _, err := rand.Read(end.src[:]); err != nil { 21 | return nil, err 22 | } 23 | _, err := rand.Read(end.dst[:]) 24 | return &end, err 25 | } 26 | 27 | func (e *DummyEndpoint) ClearSrc() {} 28 | 29 | func (e *DummyEndpoint) SrcToString() string { 30 | var addr net.UDPAddr 31 | addr.IP = e.SrcIP() 32 | addr.Port = 1000 33 | return addr.String() 34 | } 35 | 36 | func (e *DummyEndpoint) DstToString() string { 37 | var addr net.UDPAddr 38 | addr.IP = e.DstIP() 39 | addr.Port = 1000 40 | return addr.String() 41 | } 42 | 43 | func (e *DummyEndpoint) SrcToBytes() []byte { 44 | return e.src[:] 45 | } 46 | 47 | func (e *DummyEndpoint) DstIP() net.IP { 48 | return e.dst[:] 49 | } 50 | 51 | func (e *DummyEndpoint) SrcIP() net.IP { 52 | return e.src[:] 53 | } 54 | -------------------------------------------------------------------------------- /device/ip.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "net" 10 | ) 11 | 12 | const ( 13 | IPv4offsetTotalLength = 2 14 | IPv4offsetSrc = 12 15 | IPv4offsetDst = IPv4offsetSrc + net.IPv4len 16 | ) 17 | 18 | const ( 19 | IPv6offsetPayloadLength = 4 20 | IPv6offsetSrc = 8 21 | IPv6offsetDst = IPv6offsetSrc + net.IPv6len 22 | ) 23 | -------------------------------------------------------------------------------- /device/misc.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "sync/atomic" 10 | ) 11 | 12 | /* Atomic Boolean */ 13 | 14 | const ( 15 | AtomicFalse = int32(iota) 16 | AtomicTrue 17 | ) 18 | 19 | type AtomicBool struct { 20 | int32 21 | } 22 | 23 | func (a *AtomicBool) Get() bool { 24 | return atomic.LoadInt32(&a.int32) == AtomicTrue 25 | } 26 | 27 | func (a *AtomicBool) Swap(val bool) bool { 28 | flag := AtomicFalse 29 | if val { 30 | flag = AtomicTrue 31 | } 32 | return atomic.SwapInt32(&a.int32, flag) == AtomicTrue 33 | } 34 | 35 | func (a *AtomicBool) Set(val bool) { 36 | flag := AtomicFalse 37 | if val { 38 | flag = AtomicTrue 39 | } 40 | atomic.StoreInt32(&a.int32, flag) 41 | } 42 | 43 | func min(a, b uint) uint { 44 | if a > b { 45 | return b 46 | } 47 | return a 48 | } 49 | -------------------------------------------------------------------------------- /device/peer_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "reflect" 10 | "testing" 11 | "unsafe" 12 | ) 13 | 14 | func checkAlignment(t *testing.T, name string, offset uintptr) { 15 | t.Helper() 16 | if offset%8 != 0 { 17 | t.Errorf("offset of %q within struct is %d bytes, which does not align to 64-bit word boundaries (missing %d bytes). Atomic operations will crash on 32-bit systems.", name, offset, 8-(offset%8)) 18 | } 19 | } 20 | 21 | // TestPeerAlignment checks that atomically-accessed fields are 22 | // aligned to 64-bit boundaries, as required by the atomic package. 23 | // 24 | // Unfortunately, violating this rule on 32-bit platforms results in a 25 | // hard segfault at runtime. 26 | func TestPeerAlignment(t *testing.T) { 27 | var p Peer 28 | 29 | typ := reflect.TypeOf(p) 30 | t.Logf("Peer type size: %d, with fields:", typ.Size()) 31 | for i := 0; i < typ.NumField(); i++ { 32 | field := typ.Field(i) 33 | t.Logf("\t%30s\toffset=%3v\t(type size=%3d, align=%d)", 34 | field.Name, 35 | field.Offset, 36 | field.Type.Size(), 37 | field.Type.Align(), 38 | ) 39 | } 40 | 41 | checkAlignment(t, "Peer.stats", unsafe.Offsetof(p.stats)) 42 | checkAlignment(t, "Peer.isRunning", unsafe.Offsetof(p.isRunning)) 43 | } 44 | -------------------------------------------------------------------------------- /device/queueconstants_android.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | /* Reduce memory consumption for Android */ 9 | 10 | const ( 11 | QueueOutboundSize = 1024 12 | QueueInboundSize = 1024 13 | QueueHandshakeSize = 1024 14 | MaxSegmentSize = 2200 15 | PreallocatedBuffersPerPool = 4096 16 | ) 17 | -------------------------------------------------------------------------------- /device/queueconstants_default.go: -------------------------------------------------------------------------------- 1 | // +build !android,!ios 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package device 9 | 10 | const ( 11 | QueueOutboundSize = 1024 12 | QueueInboundSize = 1024 13 | QueueHandshakeSize = 1024 14 | MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram 15 | PreallocatedBuffersPerPool = 0 // Disable and allow for infinite memory growth 16 | ) 17 | -------------------------------------------------------------------------------- /device/queueconstants_ios.go: -------------------------------------------------------------------------------- 1 | // +build ios 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package device 9 | 10 | /* Fit within memory limits for iOS's Network Extension API, which has stricter requirements */ 11 | 12 | const ( 13 | QueueOutboundSize = 1024 14 | QueueInboundSize = 1024 15 | QueueHandshakeSize = 1024 16 | MaxSegmentSize = 1700 17 | PreallocatedBuffersPerPool = 1024 18 | ) 19 | -------------------------------------------------------------------------------- /device/sticky_default.go: -------------------------------------------------------------------------------- 1 | // +build !linux android 2 | 3 | package device 4 | 5 | import ( 6 | "github.com/inverse-inc/wireguard-go/conn" 7 | "github.com/inverse-inc/wireguard-go/rwcancel" 8 | ) 9 | 10 | func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) { 11 | return nil, nil 12 | } 13 | -------------------------------------------------------------------------------- /device/version.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | const WireGuardGoVersion = "0.0.20200320" 4 | -------------------------------------------------------------------------------- /dns/core/coredns.go: -------------------------------------------------------------------------------- 1 | // Package core registers the server and all plugins we support. 2 | package core 3 | 4 | import ( 5 | // plug in the server 6 | _ "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 7 | ) 8 | -------------------------------------------------------------------------------- /dns/core/dnsserver/https.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/nonwriter" 7 | ) 8 | 9 | // DoHWriter is a nonwriter.Writer that adds more specific LocalAddr and RemoteAddr methods. 10 | type DoHWriter struct { 11 | nonwriter.Writer 12 | 13 | // raddr is the remote's address. This can be optionally set. 14 | raddr net.Addr 15 | // laddr is our address. This can be optionally set. 16 | laddr net.Addr 17 | } 18 | 19 | // RemoteAddr returns the remote address. 20 | func (d *DoHWriter) RemoteAddr() net.Addr { return d.raddr } 21 | 22 | // LocalAddr returns the local address. 23 | func (d *DoHWriter) LocalAddr() net.Addr { return d.laddr } 24 | -------------------------------------------------------------------------------- /dns/core/dnsserver/log_test.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/core/dnsserver/onstartup.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import "fmt" 4 | 5 | // startUpZones creates the text that we show when starting up: 6 | // grpc://example.com.:1055 7 | // example.com.:1053 on 127.0.0.1 8 | func startUpZones(protocol, addr string, zones map[string]*Config) string { 9 | s := "" 10 | 11 | for zone := range zones { 12 | // split addr into protocol, IP and Port 13 | _, ip, port, err := SplitProtocolHostPort(addr) 14 | 15 | if err != nil { 16 | // this should not happen, but we need to take care of it anyway 17 | s += fmt.Sprintln(protocol + zone + ":" + addr) 18 | continue 19 | } 20 | if ip == "" { 21 | s += fmt.Sprintln(protocol + zone + ":" + port) 22 | continue 23 | } 24 | // if the server is listening on a specific address let's make it visible in the log, 25 | // so one can differentiate between all active listeners 26 | s += fmt.Sprintln(protocol + zone + ":" + port + " on " + ip) 27 | } 28 | return s 29 | } 30 | -------------------------------------------------------------------------------- /dns/core/dnsserver/zdirectives.go: -------------------------------------------------------------------------------- 1 | // generated by directives_generate.go; DO NOT EDIT 2 | 3 | package dnsserver 4 | 5 | // Directives are registered in the order they should be 6 | // executed. 7 | // 8 | // Ordering is VERY important. Every plugin will 9 | // feel the effects of all other plugin below 10 | // (after) them during a request, but they must not 11 | // care what plugin above them are doing. 12 | var Directives = []string{ 13 | "cancel", 14 | "tls", 15 | "reload", 16 | "bufsize", 17 | "bind", 18 | "debug", 19 | "trace", 20 | "errors", 21 | "log", 22 | "any", 23 | "chaos", 24 | "cache", 25 | "hosts", 26 | "dnsredir", 27 | "forward", 28 | "on", 29 | } 30 | -------------------------------------------------------------------------------- /dns/core/plugin/zplugin.go: -------------------------------------------------------------------------------- 1 | // generated by directives_generate.go; DO NOT EDIT 2 | 3 | package plugin 4 | 5 | import ( 6 | // Include all plugins. 7 | _ "github.com/inverse-inc/coredns-caddy/onevent" 8 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/bind" 9 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/cache" 10 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/debug" 11 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/dnsredir" 12 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/errors" 13 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/forward" 14 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/hosts" 15 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/log" 16 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/reload" 17 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/tls" 18 | _ "github.com/inverse-inc/wireguard-go/dns/plugin/trace" 19 | ) 20 | -------------------------------------------------------------------------------- /dns/coredns.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //go:generate go run directives_generate.go 4 | //go:generate go run owners_generate.go 5 | 6 | import ( 7 | _ "github.com/inverse-inc/wireguard-go/dns/core/plugin" // Plug in CoreDNS. 8 | "github.com/inverse-inc/wireguard-go/dns/coremain" 9 | ) 10 | 11 | func main() { 12 | coremain.Run() 13 | } 14 | -------------------------------------------------------------------------------- /dns/coremain/version.go: -------------------------------------------------------------------------------- 1 | package coremain 2 | 3 | // Various CoreDNS constants. 4 | const ( 5 | CoreVersion = "1.8.0" 6 | coreName = "CoreDNS" 7 | serverType = "dns" 8 | ) 9 | -------------------------------------------------------------------------------- /dns/pb/Makefile: -------------------------------------------------------------------------------- 1 | # Generate the Go files from the dns.proto protobuf, you need the utilities 2 | # from: https://github.com/golang/protobuf to make this work. 3 | # The generate dns.pb.go is checked into git, so for normal builds we don't need 4 | # to run this generation step. 5 | 6 | all: dns.pb.go 7 | 8 | dns.pb.go: dns.proto 9 | protoc --go_out=plugins=grpc:. dns.proto 10 | 11 | .PHONY: clean 12 | clean: 13 | rm dns.pb.go 14 | -------------------------------------------------------------------------------- /dns/pb/dns.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package coredns.dns; 4 | option go_package = "pb"; 5 | 6 | message DnsPacket { 7 | bytes msg = 1; 8 | } 9 | 10 | service DnsService { 11 | rpc Query (DnsPacket) returns (DnsPacket); 12 | } 13 | -------------------------------------------------------------------------------- /dns/plugin/acl/metrics.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | var ( 11 | // RequestBlockCount is the number of DNS requests being blocked. 12 | RequestBlockCount = promauto.NewCounterVec(prometheus.CounterOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: pluginName, 15 | Name: "blocked_requests_total", 16 | Help: "Counter of DNS requests being blocked.", 17 | }, []string{"server", "zone"}) 18 | // RequestAllowCount is the number of DNS requests being Allowed. 19 | RequestAllowCount = promauto.NewCounterVec(prometheus.CounterOpts{ 20 | Namespace: plugin.Namespace, 21 | Subsystem: pluginName, 22 | Name: "allowed_requests_total", 23 | Help: "Counter of DNS requests being allowed.", 24 | }, []string{"server"}) 25 | ) 26 | -------------------------------------------------------------------------------- /dns/plugin/any/README.md: -------------------------------------------------------------------------------- 1 | 2 | # any 3 | 4 | ## Name 5 | 6 | *any* - gives a minimal response to ANY queries. 7 | 8 | ## Description 9 | 10 | *any* basically blocks ANY queries by responding to them with a short HINFO reply. See [RFC 11 | 8482](https://tools.ietf.org/html/rfc8482) for details. 12 | 13 | ## Syntax 14 | 15 | ~~~ txt 16 | any 17 | ~~~ 18 | 19 | ## Examples 20 | 21 | ~~~ corefile 22 | example.org { 23 | whoami 24 | any 25 | } 26 | ~~~ 27 | 28 | A `dig +nocmd ANY example.org +noall +answer` now returns: 29 | 30 | ~~~ txt 31 | example.org. 8482 IN HINFO "ANY obsoleted" "See RFC 8482" 32 | ~~~ 33 | 34 | ## Also See 35 | 36 | [RFC 8482](https://tools.ietf.org/html/rfc8482). 37 | -------------------------------------------------------------------------------- /dns/plugin/any/any.go: -------------------------------------------------------------------------------- 1 | package any 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | 8 | "github.com/miekg/dns" 9 | ) 10 | 11 | // Any is a plugin that returns a HINFO reply to ANY queries. 12 | type Any struct { 13 | Next plugin.Handler 14 | } 15 | 16 | // ServeDNS implements the plugin.Handler interface. 17 | func (a Any) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 18 | if r.Question[0].Qtype != dns.TypeANY { 19 | return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r) 20 | } 21 | 22 | m := new(dns.Msg) 23 | m.SetReply(r) 24 | hdr := dns.RR_Header{Name: r.Question[0].Name, Ttl: 8482, Class: dns.ClassINET, Rrtype: dns.TypeHINFO} 25 | m.Answer = []dns.RR{&dns.HINFO{Hdr: hdr, Cpu: "ANY obsoleted", Os: "See RFC 8482"}} 26 | 27 | w.WriteMsg(m) 28 | return 0, nil 29 | } 30 | 31 | // Name implements the Handler interface. 32 | func (a Any) Name() string { return "any" } 33 | -------------------------------------------------------------------------------- /dns/plugin/any/any_test.go: -------------------------------------------------------------------------------- 1 | package any 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/dnstest" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 9 | 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | func TestAny(t *testing.T) { 14 | req := new(dns.Msg) 15 | req.SetQuestion("example.org.", dns.TypeANY) 16 | a := &Any{} 17 | 18 | rec := dnstest.NewRecorder(&test.ResponseWriter{}) 19 | _, err := a.ServeDNS(context.TODO(), rec, req) 20 | 21 | if err != nil { 22 | t.Errorf("Expected no error, but got %q", err) 23 | } 24 | 25 | if rec.Msg.Answer[0].(*dns.HINFO).Cpu != "ANY obsoleted" { 26 | t.Errorf("Expected HINFO, but got %q", rec.Msg.Answer[0].(*dns.HINFO).Cpu) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /dns/plugin/any/setup.go: -------------------------------------------------------------------------------- 1 | package any 2 | 3 | import ( 4 | "github.com/inverse-inc/coredns-caddy" 5 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | ) 8 | 9 | func init() { plugin.Register("any", setup) } 10 | 11 | func setup(c *caddy.Controller) error { 12 | a := Any{} 13 | 14 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 15 | a.Next = next 16 | return a 17 | }) 18 | 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /dns/plugin/auto/log_test.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/auto/regexp.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | // rewriteToExpand rewrites our template string to one that we can give to regexp.ExpandString. This basically 4 | // involves prefixing any '{' with a '$'. 5 | func rewriteToExpand(s string) string { 6 | // Pretty dumb at the moment, every { will get a $ prefixed. 7 | // Also wasteful as we build the string with +=. This is OKish 8 | // as we do this during config parsing. 9 | 10 | copy := "" 11 | 12 | for _, c := range s { 13 | if c == '{' { 14 | copy += "$" 15 | } 16 | copy += string(c) 17 | } 18 | 19 | return copy 20 | } 21 | -------------------------------------------------------------------------------- /dns/plugin/auto/regexp_test.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import "testing" 4 | 5 | func TestRewriteToExpand(t *testing.T) { 6 | tests := []struct { 7 | in string 8 | expected string 9 | }{ 10 | {in: "", expected: ""}, 11 | {in: "{1}", expected: "${1}"}, 12 | {in: "{1", expected: "${1"}, 13 | } 14 | for i, tc := range tests { 15 | got := rewriteToExpand(tc.in) 16 | if got != tc.expected { 17 | t.Errorf("Test %d: Expected error %v, but got %v", i, tc.expected, got) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dns/plugin/auto/xfr.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin/transfer" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // Transfer implements the transfer.Transfer interface. 10 | func (a Auto) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) { 11 | a.Zones.RLock() 12 | z, ok := a.Zones.Z[zone] 13 | a.Zones.RUnlock() 14 | 15 | if !ok || z == nil { 16 | return nil, transfer.ErrNotAuthoritative 17 | } 18 | return z.Transfer(serial) 19 | } 20 | -------------------------------------------------------------------------------- /dns/plugin/autopath/cname.go: -------------------------------------------------------------------------------- 1 | package autopath 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // cnamer will prefix the answer section with a cname that points from original qname to the 10 | // name of the first RR. It will also update the question section and put original in there. 11 | func cnamer(m *dns.Msg, original string) { 12 | for _, a := range m.Answer { 13 | if strings.EqualFold(original, a.Header().Name) { 14 | continue 15 | } 16 | m.Answer = append(m.Answer, nil) 17 | copy(m.Answer[1:], m.Answer) 18 | m.Answer[0] = &dns.CNAME{ 19 | Hdr: dns.RR_Header{Name: original, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: a.Header().Ttl}, 20 | Target: a.Header().Name, 21 | } 22 | break 23 | } 24 | m.Question[0].Name = original 25 | } 26 | -------------------------------------------------------------------------------- /dns/plugin/autopath/metrics.go: -------------------------------------------------------------------------------- 1 | package autopath 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | var ( 11 | // autoPathCount is counter of successfully autopath-ed queries. 12 | autoPathCount = promauto.NewCounterVec(prometheus.CounterOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: "autopath", 15 | Name: "success_total", 16 | Help: "Counter of requests that did autopath.", 17 | }, []string{"server"}) 18 | ) 19 | -------------------------------------------------------------------------------- /dns/plugin/bind/README.md: -------------------------------------------------------------------------------- 1 | # bind 2 | 3 | ## Name 4 | 5 | *bind* - overrides the host to which the server should bind. 6 | 7 | ## Description 8 | 9 | Normally, the listener binds to the wildcard host. However, you may want the listener to bind to 10 | another IP instead. 11 | 12 | If several addresses are provided, a listener will be open on each of the IP provided. 13 | 14 | Each address has to be an IP of one of the interfaces of the host. 15 | 16 | ## Syntax 17 | 18 | ~~~ txt 19 | bind ADDRESS ... 20 | ~~~ 21 | 22 | **ADDRESS** is an IP address to bind to. 23 | When several addresses are provided a listener will be opened on each of the addresses. 24 | 25 | ## Examples 26 | 27 | To make your socket accessible only to that machine, bind to IP 127.0.0.1 (localhost): 28 | 29 | ~~~ corefile 30 | . { 31 | bind 127.0.0.1 32 | } 33 | ~~~ 34 | 35 | To allow processing DNS requests only local host on both IPv4 and IPv6 stacks, use the syntax: 36 | 37 | ~~~ corefile 38 | . { 39 | bind 127.0.0.1 ::1 40 | } 41 | ~~~ 42 | 43 | If the configuration comes up with several *bind* plugins, all addresses are consolidated together: 44 | The following sample is equivalent to the preceding: 45 | 46 | ~~~ corefile 47 | . { 48 | bind 127.0.0.1 49 | bind ::1 50 | } 51 | ~~~ 52 | -------------------------------------------------------------------------------- /dns/plugin/bind/bind.go: -------------------------------------------------------------------------------- 1 | // Package bind allows binding to a specific interface instead of bind to all of them. 2 | package bind 3 | 4 | import "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | func init() { plugin.Register("bind", setup) } 7 | -------------------------------------------------------------------------------- /dns/plugin/bind/log_test.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/bind/setup.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/inverse-inc/coredns-caddy" 8 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 9 | "github.com/inverse-inc/wireguard-go/dns/plugin" 10 | ) 11 | 12 | func setup(c *caddy.Controller) error { 13 | config := dnsserver.GetConfig(c) 14 | 15 | // addresses will be consolidated over all BIND directives available in that BlocServer 16 | all := []string{} 17 | for c.Next() { 18 | addrs := c.RemainingArgs() 19 | if len(addrs) == 0 { 20 | return plugin.Error("bind", fmt.Errorf("at least one address is expected")) 21 | } 22 | for _, addr := range addrs { 23 | if net.ParseIP(addr) == nil { 24 | return plugin.Error("bind", fmt.Errorf("not a valid IP address: %s", addr)) 25 | } 26 | } 27 | all = append(all, addrs...) 28 | } 29 | config.ListenHosts = all 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /dns/plugin/bufsize/README.md: -------------------------------------------------------------------------------- 1 | # bufsize 2 | ## Name 3 | *bufsize* - sizes EDNS0 buffer size to prevent IP fragmentation. 4 | 5 | ## Description 6 | *bufsize* limits a requester's UDP payload size. 7 | It prevents IP fragmentation, mitigating certain DNS vulnerabilities. 8 | 9 | ## Syntax 10 | ```txt 11 | bufsize [SIZE] 12 | ``` 13 | 14 | **[SIZE]** is an int value for setting the buffer size. 15 | The default value is 512, and the value must be within 512 - 4096. 16 | Only one argument is acceptable, and it covers both IPv4 and IPv6. 17 | 18 | ## Examples 19 | Enable limiting the buffer size of outgoing query to the resolver (172.31.0.10): 20 | ```corefile 21 | . { 22 | bufsize 512 23 | forward . 172.31.0.10 24 | log 25 | } 26 | ``` 27 | 28 | Enable limiting the buffer size as an authoritative nameserver: 29 | ```corefile 30 | . { 31 | bufsize 512 32 | file db.example.org 33 | log 34 | } 35 | ``` 36 | 37 | ## Considerations 38 | - Setting 1232 bytes to bufsize may avoid fragmentation on the majority of networks in use today, but it depends on the MTU of the physical network links. 39 | - For now, if a client does not use EDNS, this plugin adds OPT RR. 40 | -------------------------------------------------------------------------------- /dns/plugin/bufsize/bufsize.go: -------------------------------------------------------------------------------- 1 | // Package bufsize implements a plugin that modifies EDNS0 buffer size. 2 | package bufsize 3 | 4 | import ( 5 | "context" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | // Bufsize implements bufsize plugin. 13 | type Bufsize struct { 14 | Next plugin.Handler 15 | Size int 16 | } 17 | 18 | // ServeDNS implements the plugin.Handler interface. 19 | func (buf Bufsize) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 20 | if option := r.IsEdns0(); option != nil { 21 | option.SetUDPSize(uint16(buf.Size)) 22 | } else { 23 | // If a client does not use EDNS, add it 24 | r.SetEdns0(uint16(buf.Size), false) 25 | } 26 | 27 | return plugin.NextOrFailure(buf.Name(), buf.Next, ctx, w, r) 28 | } 29 | 30 | // Name implements the Handler interface. 31 | func (buf Bufsize) Name() string { return "bufsize" } 32 | -------------------------------------------------------------------------------- /dns/plugin/cache/dnssec.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "github.com/miekg/dns" 4 | 5 | // isDNSSEC returns true if r is a DNSSEC record. NSEC,NSEC3,DS and RRSIG/SIG 6 | // are DNSSEC records. DNSKEYs is not in this list on the assumption that the 7 | // client explictly asked for it. 8 | func isDNSSEC(r dns.RR) bool { 9 | switch r.Header().Rrtype { 10 | case dns.TypeNSEC: 11 | return true 12 | case dns.TypeNSEC3: 13 | return true 14 | case dns.TypeDS: 15 | return true 16 | case dns.TypeRRSIG: 17 | return true 18 | case dns.TypeSIG: 19 | return true 20 | } 21 | return false 22 | } 23 | 24 | // filterRRSlice filters rrs and removes DNSSEC RRs when do is false. In the returned slice 25 | // the TTLs are set to ttl. If dup is true the RRs in rrs are _copied_ into the slice that is 26 | // returned. 27 | func filterRRSlice(rrs []dns.RR, ttl uint32, do, dup bool) []dns.RR { 28 | j := 0 29 | rs := make([]dns.RR, len(rrs), len(rrs)) 30 | for _, r := range rrs { 31 | if !do && isDNSSEC(r) { 32 | continue 33 | } 34 | if r.Header().Rrtype == dns.TypeOPT { 35 | continue 36 | } 37 | r.Header().Ttl = ttl 38 | if dup { 39 | rs[j] = dns.Copy(r) 40 | } else { 41 | rs[j] = r 42 | } 43 | j++ 44 | } 45 | return rs[:j] 46 | } 47 | -------------------------------------------------------------------------------- /dns/plugin/cache/error_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/dnstest" 9 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 10 | 11 | "github.com/miekg/dns" 12 | ) 13 | 14 | func TestFormErr(t *testing.T) { 15 | c := New() 16 | c.Next = formErrHandler() 17 | 18 | req := new(dns.Msg) 19 | req.SetQuestion("example.org.", dns.TypeA) 20 | rec := dnstest.NewRecorder(&test.ResponseWriter{}) 21 | 22 | c.ServeDNS(context.TODO(), rec, req) 23 | 24 | if c.pcache.Len() != 0 { 25 | t.Errorf("Cached %s, while reply had %d", "example.org.", rec.Msg.Rcode) 26 | } 27 | } 28 | 29 | // formErrHandler is a fake plugin implementation which returns a FORMERR for a reply. 30 | func formErrHandler() plugin.Handler { 31 | return plugin.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 32 | m := new(dns.Msg) 33 | m.SetQuestion("example.net.", dns.TypeA) 34 | m.Rcode = dns.RcodeFormatError 35 | w.WriteMsg(m) 36 | return dns.RcodeSuccess, nil 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /dns/plugin/cache/freq/freq_test.go: -------------------------------------------------------------------------------- 1 | package freq 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestFreqUpdate(t *testing.T) { 9 | now := time.Now().UTC() 10 | f := New(now) 11 | window := 1 * time.Minute 12 | 13 | f.Update(window, time.Now().UTC()) 14 | f.Update(window, time.Now().UTC()) 15 | f.Update(window, time.Now().UTC()) 16 | hitsCheck(t, f, 3) 17 | 18 | f.Reset(now, 0) 19 | history := time.Now().UTC().Add(-3 * time.Minute) 20 | f.Update(window, history) 21 | hitsCheck(t, f, 1) 22 | } 23 | 24 | func TestReset(t *testing.T) { 25 | f := New(time.Now().UTC()) 26 | f.Update(1*time.Minute, time.Now().UTC()) 27 | hitsCheck(t, f, 1) 28 | f.Reset(time.Now().UTC(), 0) 29 | hitsCheck(t, f, 0) 30 | } 31 | 32 | func hitsCheck(t *testing.T, f *Freq, expected int) { 33 | if x := f.Hits(); x != expected { 34 | t.Fatalf("Expected hits to be %d, got %d", expected, x) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /dns/plugin/cache/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package cache 4 | 5 | import ( 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/fuzz" 7 | ) 8 | 9 | // Fuzz fuzzes cache. 10 | func Fuzz(data []byte) int { 11 | return fuzz.Do(New(), data) 12 | } 13 | -------------------------------------------------------------------------------- /dns/plugin/cache/log_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/cancel/README.md: -------------------------------------------------------------------------------- 1 | # cancel 2 | 3 | ## Name 4 | 5 | *cancel* - cancels a request's context after 5001 milliseconds. 6 | 7 | ## Description 8 | 9 | The *cancel* plugin creates a canceling context for each request. It adds a timeout that gets 10 | triggered after 5001 milliseconds. 11 | 12 | The 5001 number was chosen because the default timeout for DNS clients is 5 seconds, after that they 13 | give up. 14 | 15 | A plugin interested in the cancellation status should call `plugin.Done()` on the context. If the 16 | context was canceled due to a timeout the plugin should not write anything back to the client and 17 | return a value indicating CoreDNS should not either; a zero return value should suffice for that. 18 | 19 | ## Syntax 20 | 21 | ~~~ txt 22 | cancel [TIMEOUT] 23 | ~~~ 24 | 25 | * **TIMEOUT** allows setting a custom timeout. The default timeout is 5001 milliseconds (`5001 ms`) 26 | 27 | ## Examples 28 | 29 | ~~~ corefile 30 | example.org { 31 | cancel 32 | whoami 33 | } 34 | ~~~ 35 | 36 | Or with a custom timeout: 37 | 38 | ~~~ corefile 39 | example.org { 40 | cancel 1s 41 | whoami 42 | } 43 | ~~~ 44 | 45 | ## Also See 46 | 47 | The Go documentation for the context package. 48 | -------------------------------------------------------------------------------- /dns/plugin/cancel/cancel_test.go: -------------------------------------------------------------------------------- 1 | package cancel 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/inverse-inc/wireguard-go/dns/plugin" 9 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/dnstest" 10 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 11 | 12 | "github.com/miekg/dns" 13 | ) 14 | 15 | type sleepPlugin struct{} 16 | 17 | func (s sleepPlugin) Name() string { return "sleep" } 18 | 19 | func (s sleepPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 20 | i := 0 21 | m := new(dns.Msg) 22 | m.SetReply(r) 23 | for { 24 | if plugin.Done(ctx) { 25 | m.Rcode = dns.RcodeBadTime // use BadTime to return something time related 26 | w.WriteMsg(m) 27 | return 0, nil 28 | } 29 | time.Sleep(20 * time.Millisecond) 30 | i++ 31 | if i > 2 { 32 | m.Rcode = dns.RcodeServerFailure 33 | w.WriteMsg(m) 34 | return 0, nil 35 | } 36 | } 37 | } 38 | 39 | func TestCancel(t *testing.T) { 40 | ca := Cancel{Next: sleepPlugin{}, timeout: 20 * time.Millisecond} 41 | ctx := context.Background() 42 | 43 | w := dnstest.NewRecorder(&test.ResponseWriter{}) 44 | m := new(dns.Msg) 45 | m.SetQuestion("aaa.example.com.", dns.TypeTXT) 46 | 47 | ca.ServeDNS(ctx, w, m) 48 | if w.Rcode != dns.RcodeBadTime { 49 | t.Error("Expected ServeDNS to be canceled by context") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /dns/plugin/cancel/setup_test.go: -------------------------------------------------------------------------------- 1 | package cancel 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestSetup(t *testing.T) { 10 | c := caddy.NewTestController("dns", `cancel`) 11 | if err := setup(c); err != nil { 12 | t.Errorf("Test 1, expected no errors, but got: %q", err) 13 | } 14 | 15 | c = caddy.NewTestController("dns", `cancel 5s`) 16 | if err := setup(c); err != nil { 17 | t.Errorf("Test 2, expected no errors, but got: %q", err) 18 | } 19 | 20 | c = caddy.NewTestController("dns", `cancel 5`) 21 | if err := setup(c); err == nil { 22 | t.Errorf("Test 3, expected errors, but got none") 23 | } 24 | 25 | c = caddy.NewTestController("dns", `cancel -1s`) 26 | if err := setup(c); err == nil { 27 | t.Errorf("Test 4, expected errors, but got none") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /dns/plugin/chaos/README.md: -------------------------------------------------------------------------------- 1 | # chaos 2 | 3 | ## Name 4 | 5 | *chaos* - allows for responding to TXT queries in the CH class. 6 | 7 | ## Description 8 | 9 | This is useful for retrieving version or author information from the server by querying a TXT record 10 | for a special domain name in the CH class. 11 | 12 | ## Syntax 13 | 14 | ~~~ 15 | chaos [VERSION] [AUTHORS...] 16 | ~~~ 17 | 18 | * **VERSION** is the version to return. Defaults to `CoreDNS-`, if not set. 19 | * **AUTHORS** is what authors to return. This defaults to all GitHub handles in the OWNERS files. 20 | 21 | Note that you have to make sure that this plugin will get actual queries for the 22 | following zones: `version.bind`, `version.server`, `authors.bind`, `hostname.bind` and 23 | `id.server`. 24 | 25 | ## Examples 26 | 27 | Specify all the zones in full. 28 | 29 | ~~~ corefile 30 | version.bind version.server authors.bind hostname.bind id.server { 31 | chaos CoreDNS-001 info@coredns.io 32 | } 33 | ~~~ 34 | 35 | Or just default to `.`: 36 | 37 | ~~~ corefile 38 | . { 39 | chaos CoreDNS-001 info@coredns.io 40 | } 41 | ~~~ 42 | 43 | And test with `dig`: 44 | 45 | ~~~ txt 46 | % dig @localhost CH TXT version.bind 47 | ... 48 | ;; ANSWER SECTION: 49 | version.bind. 0 CH TXT "CoreDNS-001" 50 | ... 51 | ~~~ 52 | -------------------------------------------------------------------------------- /dns/plugin/chaos/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package chaos 4 | 5 | import ( 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/fuzz" 7 | ) 8 | 9 | // Fuzz fuzzes cache. 10 | func Fuzz(data []byte) int { 11 | c := Chaos{} 12 | return fuzz.Do(c, data) 13 | } 14 | -------------------------------------------------------------------------------- /dns/plugin/chaos/log_test.go: -------------------------------------------------------------------------------- 1 | package chaos 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/chaos/zowners.go: -------------------------------------------------------------------------------- 1 | package chaos 2 | 3 | // Owners are all GitHub handlers of all maintainers. 4 | var Owners = []string{"bradbeam", "chrisohaver", "darshanime", "dilyevsky", "ekleiner", "fastest963", "greenpau", "ihac", "inigohu", "isolus", "johnbelamaric", "miekg", "nchrisdk", "nitisht", "pmoroney", "rajansandeep", "rdrozhdzh", "rtreffer", "stp-ip", "superq", "varyoo", "ykhr53", "yongtang", "zouyee"} 5 | -------------------------------------------------------------------------------- /dns/plugin/clouddns/gcp.go: -------------------------------------------------------------------------------- 1 | package clouddns 2 | 3 | import gcp "google.golang.org/api/dns/v1" 4 | 5 | type gcpDNS interface { 6 | zoneExists(projectName, hostedZoneName string) error 7 | listRRSets(projectName, hostedZoneName string) (*gcp.ResourceRecordSetsListResponse, error) 8 | } 9 | 10 | type gcpClient struct { 11 | *gcp.Service 12 | } 13 | 14 | // zoneExists is a wrapper method around `gcp.Service.ManagedZones.Get` 15 | // it checks if the provided zone name for a given project exists. 16 | func (c gcpClient) zoneExists(projectName, hostedZoneName string) error { 17 | _, err := c.ManagedZones.Get(projectName, hostedZoneName).Do() 18 | if err != nil { 19 | return err 20 | } 21 | return nil 22 | } 23 | 24 | // listRRSets is a wrapper method around `gcp.Service.ResourceRecordSets.List` 25 | // it fetches and returns the record sets for a hosted zone. 26 | func (c gcpClient) listRRSets(projectName, hostedZoneName string) (*gcp.ResourceRecordSetsListResponse, error) { 27 | rr, err := c.ResourceRecordSets.List(projectName, hostedZoneName).Do() 28 | if err != nil { 29 | return nil, err 30 | } 31 | return rr, nil 32 | } 33 | -------------------------------------------------------------------------------- /dns/plugin/clouddns/log_test.go: -------------------------------------------------------------------------------- 1 | package clouddns 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "github.com/inverse-inc/coredns-caddy" 5 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | ) 8 | 9 | func init() { plugin.Register("debug", setup) } 10 | 11 | func setup(c *caddy.Controller) error { 12 | config := dnsserver.GetConfig(c) 13 | 14 | for c.Next() { 15 | if c.NextArg() { 16 | return plugin.Error("debug", c.ArgErr()) 17 | } 18 | config.Debug = true 19 | } 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /dns/plugin/debug/debug_test.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 8 | ) 9 | 10 | func TestDebug(t *testing.T) { 11 | tests := []struct { 12 | input string 13 | shouldErr bool 14 | expectedDebug bool 15 | }{ 16 | // positive 17 | { 18 | `debug`, false, true, 19 | }, 20 | // negative 21 | { 22 | `debug off`, true, false, 23 | }, 24 | } 25 | 26 | for i, test := range tests { 27 | c := caddy.NewTestController("dns", test.input) 28 | err := setup(c) 29 | cfg := dnsserver.GetConfig(c) 30 | 31 | if test.shouldErr && err == nil { 32 | t.Fatalf("Test %d: Expected error but found %s for input %s", i, err, test.input) 33 | } 34 | 35 | if err != nil { 36 | if !test.shouldErr { 37 | t.Fatalf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err) 38 | } 39 | } 40 | if cfg.Debug != test.expectedDebug { 41 | t.Fatalf("Test %d: Expected debug to be: %t, but got: %t, input: %s", i, test.expectedDebug, cfg.Debug, test.input) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /dns/plugin/debug/log_test.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/deprecated/setup.go: -------------------------------------------------------------------------------- 1 | // Package deprecated is used when we deprecated plugin. In plugin.cfg just go from 2 | // 3 | // startup:github.com/inverse-inc/coredns-caddy/startupshutdown 4 | // 5 | // To: 6 | // 7 | // startup:deprecated 8 | // 9 | // And things should work as expected. This means starting CoreDNS will fail with an error. We can only 10 | // point to the release notes to details what next steps a user should take. I.e. there is no way to add this 11 | // to the error generated. 12 | package deprecated 13 | 14 | import ( 15 | "errors" 16 | 17 | "github.com/inverse-inc/coredns-caddy" 18 | "github.com/inverse-inc/wireguard-go/dns/plugin" 19 | ) 20 | 21 | // removed has the names of the plugins that need to error on startup. 22 | var removed = []string{""} 23 | 24 | func setup(c *caddy.Controller) error { 25 | c.Next() 26 | x := c.Val() 27 | return plugin.Error(x, errors.New("this plugin has been deprecated")) 28 | } 29 | 30 | func init() { 31 | for _, plug := range removed { 32 | plugin.Register(plug, setup) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dns/plugin/dns64/metrics.go: -------------------------------------------------------------------------------- 1 | package dns64 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | var ( 11 | // RequestsTranslatedCount is the number of DNS requests translated by dns64. 12 | RequestsTranslatedCount = promauto.NewCounterVec(prometheus.CounterOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: pluginName, 15 | Name: "requests_translated_total", 16 | Help: "Counter of DNS requests translated by dns64.", 17 | }, []string{"server"}) 18 | ) 19 | -------------------------------------------------------------------------------- /dns/plugin/dnsredir/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /dns/plugin/dnsredir/checkdown.go: -------------------------------------------------------------------------------- 1 | package dnsredir 2 | 3 | import "sync/atomic" 4 | 5 | // Default downFunc used in dnsredir plugin 6 | // Taken from https://github.com/coredns/proxy/proxy/down.go 7 | var checkDownFunc = func(u *reloadableUpstream) UpstreamHostDownFunc { 8 | return func(uh *UpstreamHost) bool { 9 | fails := atomic.LoadInt32(&uh.fails) 10 | return fails >= u.maxFails && u.maxFails > 0 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /dns/plugin/dnsredir/log_test.go: -------------------------------------------------------------------------------- 1 | package dnsredir 2 | 3 | func init() { 4 | // [sic] Discard sets the log output to /dev/null 5 | //clog.Discard() 6 | } 7 | -------------------------------------------------------------------------------- /dns/plugin/dnsredir/setup.go: -------------------------------------------------------------------------------- 1 | package dnsredir 2 | 3 | import ( 4 | "github.com/inverse-inc/coredns-caddy" 5 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | ) 8 | 9 | func init() { plugin.Register(pluginName, setup) } 10 | 11 | func setup(c *caddy.Controller) error { 12 | log.Infof("Initializing, version %v, HEAD %v", pluginVersion, pluginHeadCommit) 13 | 14 | ups, err := NewReloadableUpstreams(c) 15 | if err != nil { 16 | return PluginError(err) 17 | } 18 | 19 | r := &Dnsredir{Upstreams: &ups} 20 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 21 | r.Next = next 22 | return r 23 | }) 24 | 25 | c.OnStartup(func() error { 26 | return r.OnStartup() 27 | }) 28 | 29 | c.OnShutdown(func() error { 30 | return r.OnShutdown() 31 | }) 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /dns/plugin/dnsredir/string_set.go: -------------------------------------------------------------------------------- 1 | package dnsredir 2 | 3 | // XXX: not thread safe 4 | type StringSet map[string]struct{} 5 | 6 | func (s *StringSet) Add(str string) { 7 | (*s)[str] = struct{}{} 8 | } 9 | 10 | func (s *StringSet) Contains(str string) bool { 11 | if s == nil { 12 | return false 13 | } 14 | _, found := (*s)[str] 15 | return found 16 | } 17 | -------------------------------------------------------------------------------- /dns/plugin/dnsredir/type.go: -------------------------------------------------------------------------------- 1 | // Taken from github.com/coredns/plugin/forward/type.go with modification 2 | 3 | package dnsredir 4 | 5 | import ( 6 | "crypto/tls" 7 | "fmt" 8 | "net" 9 | ) 10 | 11 | type transportType int 12 | 13 | const ( 14 | typeUdp transportType = iota 15 | typeTcp 16 | typeTls 17 | typeTotalCount // Dummy type 18 | ) 19 | 20 | func stringToTransportType(s string) transportType { 21 | switch s { 22 | case "udp": 23 | return typeUdp 24 | case "tcp": 25 | return typeTcp 26 | case "tcp-tls": 27 | return typeTls 28 | } 29 | 30 | log.Warningf("Unknown protocol %q, fallback to UDP", s) 31 | return typeUdp 32 | } 33 | 34 | func (t *Transport) transportTypeFromConn(pc *persistConn) transportType { 35 | if _, ok := pc.c.Conn.(*net.UDPConn); ok { 36 | return typeUdp 37 | } 38 | 39 | if t.tlsConfig == nil { 40 | if _, ok := pc.c.Conn.(*net.TCPConn); !ok { 41 | panic(fmt.Sprintf("Expected TCP connection, got %T", pc.c.Conn)) 42 | } 43 | return typeTcp 44 | } 45 | 46 | if _, ok := pc.c.Conn.(*tls.Conn); !ok { 47 | panic(fmt.Sprintf("Expected TLS connection, got %T", pc.c.Conn)) 48 | } 49 | return typeTls 50 | } 51 | -------------------------------------------------------------------------------- /dns/plugin/dnssec/cache.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import ( 4 | "hash/fnv" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // hash serializes the RRset and returns a signature cache key. 10 | func hash(rrs []dns.RR) uint64 { 11 | h := fnv.New64() 12 | buf := make([]byte, 256) 13 | for _, r := range rrs { 14 | off, err := dns.PackRR(r, buf, 0, nil, false) 15 | if err == nil { 16 | h.Write(buf[:off]) 17 | } 18 | } 19 | 20 | i := h.Sum64() 21 | return i 22 | } 23 | -------------------------------------------------------------------------------- /dns/plugin/dnssec/log_test.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/dnssec/metrics.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | var ( 11 | // cacheSize is the number of elements in the dnssec cache. 12 | cacheSize = promauto.NewGaugeVec(prometheus.GaugeOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: "dnssec", 15 | Name: "cache_entries", 16 | Help: "The number of elements in the dnssec cache.", 17 | }, []string{"server", "type"}) 18 | // cacheHits is the count of cache hits. 19 | cacheHits = promauto.NewCounterVec(prometheus.CounterOpts{ 20 | Namespace: plugin.Namespace, 21 | Subsystem: "dnssec", 22 | Name: "cache_hits_total", 23 | Help: "The count of cache hits.", 24 | }, []string{"server"}) 25 | // cacheMisses is the count of cache misses. 26 | cacheMisses = promauto.NewCounterVec(prometheus.CounterOpts{ 27 | Namespace: plugin.Namespace, 28 | Subsystem: "dnssec", 29 | Name: "cache_misses_total", 30 | Help: "The count of cache misses.", 31 | }, []string{"server"}) 32 | ) 33 | -------------------------------------------------------------------------------- /dns/plugin/dnssec/rrsig.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import "github.com/miekg/dns" 4 | 5 | // newRRSIG returns a new RRSIG, with all fields filled out, except the signed data. 6 | func (k *DNSKEY) newRRSIG(signerName string, ttl, incep, expir uint32) *dns.RRSIG { 7 | sig := new(dns.RRSIG) 8 | 9 | sig.Hdr.Rrtype = dns.TypeRRSIG 10 | sig.Algorithm = k.K.Algorithm 11 | sig.KeyTag = k.tag 12 | sig.SignerName = signerName 13 | sig.Hdr.Ttl = ttl 14 | sig.OrigTtl = origTTL 15 | 16 | sig.Inception = incep 17 | sig.Expiration = expir 18 | 19 | return sig 20 | } 21 | 22 | type rrset struct { 23 | qname string 24 | qtype uint16 25 | } 26 | 27 | // rrSets returns rrs as a map of RRsets. It skips RRSIG and OPT records as those don't need to be signed. 28 | func rrSets(rrs []dns.RR) map[rrset][]dns.RR { 29 | m := make(map[rrset][]dns.RR) 30 | 31 | for _, r := range rrs { 32 | if r.Header().Rrtype == dns.TypeRRSIG || r.Header().Rrtype == dns.TypeOPT { 33 | continue 34 | } 35 | 36 | if s, ok := m[rrset{r.Header().Name, r.Header().Rrtype}]; ok { 37 | s = append(s, r) 38 | m[rrset{r.Header().Name, r.Header().Rrtype}] = s 39 | continue 40 | } 41 | 42 | s := make([]dns.RR, 1, 3) 43 | s[0] = r 44 | m[rrset{r.Header().Name, r.Header().Rrtype}] = s 45 | } 46 | 47 | if len(m) > 0 { 48 | return m 49 | } 50 | return nil 51 | } 52 | 53 | const origTTL = 3600 54 | -------------------------------------------------------------------------------- /dns/plugin/dnstap/dnstapio/dnstap_encoder_test.go: -------------------------------------------------------------------------------- 1 | package dnstapio 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | tap "github.com/dnstap/golang-dnstap" 8 | fs "github.com/farsightsec/golang-framestream" 9 | "github.com/golang/protobuf/proto" 10 | ) 11 | 12 | func dnstapMsg() *tap.Dnstap { 13 | t := tap.Dnstap_MESSAGE 14 | mt := tap.Message_CLIENT_RESPONSE 15 | msg := &tap.Message{Type: &mt} 16 | return &tap.Dnstap{Type: &t, Message: msg} 17 | } 18 | 19 | func TestEncoderCompatibility(t *testing.T) { 20 | opts := &fs.EncoderOptions{ 21 | ContentType: []byte("protobuf:dnstap.DnstapTest"), 22 | Bidirectional: false, 23 | } 24 | msg := dnstapMsg() 25 | 26 | //framestream encoder 27 | fsW := new(bytes.Buffer) 28 | fsEnc, err := fs.NewEncoder(fsW, opts) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | data, err := proto.Marshal(msg) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | fsEnc.Write(data) 37 | fsEnc.Close() 38 | 39 | //dnstap encoder 40 | dnstapW := new(bytes.Buffer) 41 | dnstapEnc := newDnstapEncoder(opts) 42 | if err := dnstapEnc.resetWriter(dnstapW); err != nil { 43 | t.Fatal(err) 44 | } 45 | dnstapEnc.writeMsg(msg) 46 | dnstapEnc.flushBuffer() 47 | dnstapEnc.close() 48 | 49 | //compare results 50 | if !bytes.Equal(fsW.Bytes(), dnstapW.Bytes()) { 51 | t.Fatal("DnstapEncoder is not compatible with framestream Encoder") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /dns/plugin/dnstap/handler.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin/dnstap/dnstapio" 9 | 10 | tap "github.com/dnstap/golang-dnstap" 11 | "github.com/miekg/dns" 12 | ) 13 | 14 | // Dnstap is the dnstap handler. 15 | type Dnstap struct { 16 | Next plugin.Handler 17 | io dnstapio.Tapper 18 | 19 | // IncludeRawMessage will include the raw DNS message into the dnstap messages if true. 20 | IncludeRawMessage bool 21 | } 22 | 23 | // TapMessage sends the message m to the dnstap interface. 24 | func (h Dnstap) TapMessage(m *tap.Message) { 25 | t := tap.Dnstap_MESSAGE 26 | h.io.Dnstap(tap.Dnstap{Type: &t, Message: m}) 27 | } 28 | 29 | // ServeDNS logs the client query and response to dnstap and passes the dnstap Context. 30 | func (h Dnstap) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 31 | rw := &ResponseWriter{ 32 | ResponseWriter: w, 33 | Dnstap: h, 34 | Query: r, 35 | QueryTime: time.Now(), 36 | } 37 | 38 | return plugin.NextOrFailure(h.Name(), h.Next, ctx, rw, r) 39 | } 40 | 41 | // Name implements the plugin.Plugin interface. 42 | func (h Dnstap) Name() string { return "dnstap" } 43 | -------------------------------------------------------------------------------- /dns/plugin/dnstap/log_test.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/dnstap/setup_test.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestConfig(t *testing.T) { 10 | tests := []struct { 11 | file string 12 | path string 13 | full bool 14 | socket bool 15 | fail bool 16 | }{ 17 | {"dnstap dnstap.sock full", "dnstap.sock", true, true, false}, 18 | {"dnstap unix://dnstap.sock", "dnstap.sock", false, true, false}, 19 | {"dnstap tcp://127.0.0.1:6000", "127.0.0.1:6000", false, false, false}, 20 | {"dnstap", "fail", false, true, true}, 21 | } 22 | for _, c := range tests { 23 | cad := caddy.NewTestController("dns", c.file) 24 | conf, err := parseConfig(cad) 25 | if c.fail { 26 | if err == nil { 27 | t.Errorf("%s: %s", c.file, err) 28 | } 29 | } else if err != nil || conf.target != c.path || conf.full != c.full || conf.socket != c.socket { 30 | t.Errorf("Expected: %+v\nhave: %+v\nerror: %s", c, conf, err) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dns/plugin/dnstap/writer.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/dnstap/msg" 7 | tap "github.com/dnstap/golang-dnstap" 8 | "github.com/miekg/dns" 9 | ) 10 | 11 | // ResponseWriter captures the client response and logs the query to dnstap. 12 | // Single request use. 13 | type ResponseWriter struct { 14 | QueryTime time.Time 15 | Query *dns.Msg 16 | dns.ResponseWriter 17 | Dnstap 18 | } 19 | 20 | // WriteMsg writes back the response to the client and THEN works on logging the request 21 | // and response to dnstap. 22 | func (w *ResponseWriter) WriteMsg(resp *dns.Msg) error { 23 | err := w.ResponseWriter.WriteMsg(resp) 24 | 25 | q := new(tap.Message) 26 | msg.SetQueryTime(q, w.QueryTime) 27 | msg.SetQueryAddress(q, w.RemoteAddr()) 28 | 29 | if w.IncludeRawMessage { 30 | buf, _ := w.Query.Pack() 31 | q.QueryMessage = buf 32 | } 33 | msg.SetType(q, tap.Message_CLIENT_QUERY) 34 | w.TapMessage(q) 35 | 36 | if err != nil { 37 | return err 38 | } 39 | 40 | r := new(tap.Message) 41 | msg.SetQueryTime(r, w.QueryTime) 42 | msg.SetResponseTime(r, time.Now()) 43 | msg.SetQueryAddress(r, w.RemoteAddr()) 44 | 45 | if w.IncludeRawMessage { 46 | buf, _ := resp.Pack() 47 | r.ResponseMessage = buf 48 | } 49 | 50 | msg.SetType(r, tap.Message_CLIENT_RESPONSE) 51 | w.TapMessage(r) 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /dns/plugin/done.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import "context" 4 | 5 | // Done is a non-blocking function that returns true if the context has been canceled. 6 | func Done(ctx context.Context) bool { 7 | select { 8 | case <-ctx.Done(): 9 | return true 10 | default: 11 | return false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dns/plugin/erratic/autopath.go: -------------------------------------------------------------------------------- 1 | package erratic 2 | 3 | import "github.com/inverse-inc/wireguard-go/dns/request" 4 | 5 | // AutoPath implements the AutoPathFunc call from the autopath plugin. 6 | func (e *Erratic) AutoPath(state request.Request) []string { 7 | return []string{"a.example.org.", "b.example.org.", ""} 8 | } 9 | -------------------------------------------------------------------------------- /dns/plugin/erratic/log_test.go: -------------------------------------------------------------------------------- 1 | package erratic 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/erratic/ready.go: -------------------------------------------------------------------------------- 1 | package erratic 2 | 3 | import "sync/atomic" 4 | 5 | // Ready returns true if the number of received queries is in the range [3, 5). All other values return false. 6 | // To aid in testing we want to this flip between ready and not ready. 7 | func (e *Erratic) Ready() bool { 8 | q := atomic.LoadUint64(&e.q) 9 | if q >= 3 && q < 5 { 10 | return true 11 | } 12 | return false 13 | } 14 | -------------------------------------------------------------------------------- /dns/plugin/errors/benchmark_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | func BenchmarkServeDNS(b *testing.B) { 13 | h := &errorHandler{} 14 | h.Next = test.ErrorHandler() 15 | 16 | r := new(dns.Msg) 17 | r.SetQuestion("example.org.", dns.TypeA) 18 | w := &test.ResponseWriter{} 19 | ctx := context.TODO() 20 | 21 | for i := 0; i < b.N; i++ { 22 | _, err := h.ServeDNS(ctx, w, r) 23 | if err != nil { 24 | b.Errorf("ServeDNS returned error: %s", err) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /dns/plugin/errors/log_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/etcd/log_test.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/etcd/msg/path_test.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import "testing" 4 | 5 | func TestPath(t *testing.T) { 6 | for _, path := range []string{"mydns", "skydns"} { 7 | result := Path("service.staging.skydns.local.", path) 8 | if result != "/"+path+"/local/skydns/staging/service" { 9 | t.Errorf("Failure to get domain's path with prefix: %s", result) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dns/plugin/etcd/msg/type.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // HostType returns the DNS type of what is encoded in the Service Host field. We're reusing 10 | // dns.TypeXXX to not reinvent a new set of identifiers. 11 | // 12 | // dns.TypeA: the service's Host field contains an A record. 13 | // dns.TypeAAAA: the service's Host field contains an AAAA record. 14 | // dns.TypeCNAME: the service's Host field contains a name. 15 | // 16 | // Note that a service can double/triple as a TXT record or MX record. 17 | func (s *Service) HostType() (what uint16, normalized net.IP) { 18 | 19 | ip := net.ParseIP(s.Host) 20 | 21 | switch { 22 | 23 | case ip == nil: 24 | if len(s.Text) == 0 { 25 | return dns.TypeCNAME, nil 26 | } 27 | return dns.TypeTXT, nil 28 | 29 | case ip.To4() != nil: 30 | return dns.TypeA, ip.To4() 31 | 32 | case ip.To4() == nil: 33 | return dns.TypeAAAA, ip.To16() 34 | } 35 | // This should never be reached. 36 | return dns.TypeNone, nil 37 | } 38 | -------------------------------------------------------------------------------- /dns/plugin/etcd/msg/type_test.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestType(t *testing.T) { 10 | tests := []struct { 11 | serv Service 12 | expectedType uint16 13 | }{ 14 | {Service{Host: "example.org"}, dns.TypeCNAME}, 15 | {Service{Host: "127.0.0.1"}, dns.TypeA}, 16 | {Service{Host: "2000::3"}, dns.TypeAAAA}, 17 | {Service{Host: "2000..3"}, dns.TypeCNAME}, 18 | {Service{Host: "127.0.0.257"}, dns.TypeCNAME}, 19 | {Service{Host: "127.0.0.252", Mail: true}, dns.TypeA}, 20 | {Service{Host: "127.0.0.252", Mail: true, Text: "a"}, dns.TypeA}, 21 | {Service{Host: "127.0.0.254", Mail: false, Text: "a"}, dns.TypeA}, 22 | } 23 | 24 | for i, tc := range tests { 25 | what, _ := tc.serv.HostType() 26 | if what != tc.expectedType { 27 | t.Errorf("Test %d: Expected what %v, but got %v", i, tc.expectedType, what) 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /dns/plugin/etcd/xfr.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/request" 7 | ) 8 | 9 | // Serial returns the serial number to use. 10 | func (e *Etcd) Serial(state request.Request) uint32 { 11 | return uint32(time.Now().Unix()) 12 | } 13 | 14 | // MinTTL returns the minimal TTL. 15 | func (e *Etcd) MinTTL(state request.Request) uint32 { 16 | return 30 17 | } 18 | -------------------------------------------------------------------------------- /dns/plugin/file/closest.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin/file/tree" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // ClosestEncloser returns the closest encloser for qname. 10 | func (z *Zone) ClosestEncloser(qname string) (*tree.Elem, bool) { 11 | 12 | offset, end := dns.NextLabel(qname, 0) 13 | for !end { 14 | elem, _ := z.Tree.Search(qname) 15 | if elem != nil { 16 | return elem, true 17 | } 18 | qname = qname[offset:] 19 | 20 | offset, end = dns.NextLabel(qname, offset) 21 | } 22 | 23 | return z.Tree.Search(z.origin) 24 | } 25 | -------------------------------------------------------------------------------- /dns/plugin/file/closest_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestClosestEncloser(t *testing.T) { 9 | z, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin", 0) 10 | if err != nil { 11 | t.Fatalf("Expect no error when reading zone, got %q", err) 12 | } 13 | 14 | tests := []struct { 15 | in, out string 16 | }{ 17 | {"miek.nl.", "miek.nl."}, 18 | {"www.miek.nl.", "www.miek.nl."}, 19 | 20 | {"blaat.miek.nl.", "miek.nl."}, 21 | {"blaat.www.miek.nl.", "www.miek.nl."}, 22 | {"www.blaat.miek.nl.", "miek.nl."}, 23 | {"blaat.a.miek.nl.", "a.miek.nl."}, 24 | } 25 | 26 | for _, tc := range tests { 27 | ce, _ := z.ClosestEncloser(tc.in) 28 | if ce == nil { 29 | if z.origin != tc.out { 30 | t.Errorf("Expected ce to be %s for %s, got %s", tc.out, tc.in, ce.Name()) 31 | } 32 | continue 33 | } 34 | if ce.Name() != tc.out { 35 | t.Errorf("Expected ce to be %s for %s, got %s", tc.out, tc.in, ce.Name()) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dns/plugin/file/file_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkFileParseInsert(b *testing.B) { 9 | for i := 0; i < b.N; i++ { 10 | Parse(strings.NewReader(dbMiekENTNL), testzone, "stdin", 0) 11 | } 12 | } 13 | 14 | func TestParseNoSOA(t *testing.T) { 15 | _, err := Parse(strings.NewReader(dbNoSOA), "example.org.", "stdin", 0) 16 | if err == nil { 17 | t.Fatalf("Zone %q should have failed to load", "example.org.") 18 | } 19 | if !strings.Contains(err.Error(), "no SOA record") { 20 | t.Fatalf("Zone %q should have failed to load with no soa error: %s", "example.org.", err) 21 | } 22 | } 23 | 24 | const dbNoSOA = ` 25 | $TTL 1M 26 | $ORIGIN example.org. 27 | 28 | www IN A 192.168.0.14 29 | mail IN A 192.168.0.15 30 | imap IN CNAME mail 31 | ` 32 | -------------------------------------------------------------------------------- /dns/plugin/file/include_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 8 | ) 9 | 10 | // Make sure the external miekg/dns dependency is up to date 11 | 12 | func TestInclude(t *testing.T) { 13 | 14 | name, rm, err := test.TempFile(".", "foo\tIN\tA\t127.0.0.1\n") 15 | if err != nil { 16 | t.Fatalf("Unable to create tmpfile %q: %s", name, err) 17 | } 18 | defer rm() 19 | 20 | zone := `$ORIGIN example.org. 21 | @ IN SOA sns.dns.icann.org. noc.dns.icann.org. 2017042766 7200 3600 1209600 3600 22 | $INCLUDE ` + name + "\n" 23 | 24 | z, err := Parse(strings.NewReader(zone), "example.org.", "test", 0) 25 | if err != nil { 26 | t.Errorf("Unable to parse zone %q: %s", "example.org.", err) 27 | } 28 | 29 | if _, ok := z.Search("foo.example.org."); !ok { 30 | t.Errorf("Failed to find %q in parsed zone", "foo.example.org.") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dns/plugin/file/log_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/file/notify.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/request" 7 | 8 | "github.com/miekg/dns" 9 | ) 10 | 11 | // isNotify checks if state is a notify message and if so, will *also* check if it 12 | // is from one of the configured masters. If not it will not be a valid notify 13 | // message. If the zone z is not a secondary zone the message will also be ignored. 14 | func (z *Zone) isNotify(state request.Request) bool { 15 | if state.Req.Opcode != dns.OpcodeNotify { 16 | return false 17 | } 18 | if len(z.TransferFrom) == 0 { 19 | return false 20 | } 21 | // If remote IP matches we accept. 22 | remote := state.IP() 23 | for _, f := range z.TransferFrom { 24 | from, _, err := net.SplitHostPort(f) 25 | if err != nil { 26 | continue 27 | } 28 | if from == remote { 29 | return true 30 | } 31 | } 32 | return false 33 | } 34 | -------------------------------------------------------------------------------- /dns/plugin/file/nsec3_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestParseNSEC3PARAM(t *testing.T) { 9 | _, err := Parse(strings.NewReader(nsec3paramTest), "miek.nl", "stdin", 0) 10 | if err == nil { 11 | t.Fatalf("Expected error when reading zone, got nothing") 12 | } 13 | } 14 | 15 | func TestParseNSEC3(t *testing.T) { 16 | _, err := Parse(strings.NewReader(nsec3Test), "miek.nl", "stdin", 0) 17 | if err == nil { 18 | t.Fatalf("Expected error when reading zone, got nothing") 19 | } 20 | } 21 | 22 | const nsec3paramTest = `miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1460175181 14400 3600 604800 14400 23 | miek.nl. 1800 IN NS omval.tednet.nl. 24 | miek.nl. 0 IN NSEC3PARAM 1 0 5 A3DEBC9CC4F695C7` 25 | 26 | const nsec3Test = `example.org. 1800 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082508 7200 3600 1209600 3600 27 | aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN NSEC3 1 1 5 D0CBEAAF0AC77314 AUB95P93VPKP55G6U5S4SGS7LS61ND85 NS SOA TXT RRSIG DNSKEY NSEC3PARAM 28 | aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN RRSIG NSEC3 8 2 600 20160910232502 20160827231002 14028 example.org. XBNpA7KAIjorPbXvTinOHrc1f630aHic2U716GHLHA4QMx9cl9ss4QjR Wj2UpDM9zBW/jNYb1xb0yjQoez/Jv200w0taSWjRci5aUnRpOi9bmcrz STHb6wIUjUsbJ+NstQsUwVkj6679UviF1FqNwr4GlJnWG3ZrhYhE+NI6 s0k=` 29 | -------------------------------------------------------------------------------- /dns/plugin/file/rrutil/util.go: -------------------------------------------------------------------------------- 1 | // Package rrutil provides function to find certain RRs in slices. 2 | package rrutil 3 | 4 | import "github.com/miekg/dns" 5 | 6 | // SubTypeSignature returns the RRSIG for the subtype. 7 | func SubTypeSignature(rrs []dns.RR, subtype uint16) []dns.RR { 8 | sigs := []dns.RR{} 9 | // there may be multiple keys that have signed this subtype 10 | for _, sig := range rrs { 11 | if s, ok := sig.(*dns.RRSIG); ok { 12 | if s.TypeCovered == subtype { 13 | sigs = append(sigs, s) 14 | } 15 | } 16 | } 17 | return sigs 18 | } 19 | 20 | // CNAMEForType returns the RR that have the qtype from targets. 21 | func CNAMEForType(rrs []dns.RR, qtype uint16) []dns.RR { 22 | ret := []dns.RR{} 23 | for _, target := range rrs { 24 | if target.Header().Rrtype == qtype { 25 | ret = append(ret, target) 26 | } 27 | } 28 | return ret 29 | } 30 | -------------------------------------------------------------------------------- /dns/plugin/file/shutdown.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | // OnShutdown shuts down any running go-routines for this zone. 4 | func (z *Zone) OnShutdown() error { 5 | if 0 < z.ReloadInterval { 6 | z.reloadShutdown <- true 7 | } 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /dns/plugin/file/tree/all.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | // All traverses tree and returns all elements. 4 | func (t *Tree) All() []*Elem { 5 | if t.Root == nil { 6 | return nil 7 | } 8 | found := t.Root.all(nil) 9 | return found 10 | } 11 | 12 | func (n *Node) all(found []*Elem) []*Elem { 13 | if n.Left != nil { 14 | found = n.Left.all(found) 15 | } 16 | found = append(found, n.Elem) 17 | if n.Right != nil { 18 | found = n.Right.all(found) 19 | } 20 | return found 21 | } 22 | -------------------------------------------------------------------------------- /dns/plugin/file/tree/glue.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin/file/rrutil" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // Glue returns any potential glue records for nsrrs. 10 | func (t *Tree) Glue(nsrrs []dns.RR, do bool) []dns.RR { 11 | glue := []dns.RR{} 12 | for _, rr := range nsrrs { 13 | if ns, ok := rr.(*dns.NS); ok && dns.IsSubDomain(ns.Header().Name, ns.Ns) { 14 | glue = append(glue, t.searchGlue(ns.Ns, do)...) 15 | } 16 | } 17 | return glue 18 | } 19 | 20 | // searchGlue looks up A and AAAA for name. 21 | func (t *Tree) searchGlue(name string, do bool) []dns.RR { 22 | glue := []dns.RR{} 23 | 24 | // A 25 | if elem, found := t.Search(name); found { 26 | glue = append(glue, elem.Type(dns.TypeA)...) 27 | if do { 28 | sigs := elem.Type(dns.TypeRRSIG) 29 | sigs = rrutil.SubTypeSignature(sigs, dns.TypeA) 30 | glue = append(glue, sigs...) 31 | } 32 | } 33 | 34 | // AAAA 35 | if elem, found := t.Search(name); found { 36 | glue = append(glue, elem.Type(dns.TypeAAAA)...) 37 | if do { 38 | sigs := elem.Type(dns.TypeRRSIG) 39 | sigs = rrutil.SubTypeSignature(sigs, dns.TypeAAAA) 40 | glue = append(glue, sigs...) 41 | } 42 | } 43 | return glue 44 | } 45 | -------------------------------------------------------------------------------- /dns/plugin/file/tree/print.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import "fmt" 4 | 5 | // Print prints a Tree. Main use is to aid in debugging. 6 | func (t *Tree) Print() { 7 | if t.Root == nil { 8 | fmt.Println("") 9 | } 10 | t.Root.print() 11 | } 12 | 13 | func (n *Node) print() { 14 | q := newQueue() 15 | q.push(n) 16 | 17 | nodesInCurrentLevel := 1 18 | nodesInNextLevel := 0 19 | 20 | for !q.empty() { 21 | do := q.pop() 22 | nodesInCurrentLevel-- 23 | 24 | if do != nil { 25 | fmt.Print(do.Elem.Name(), " ") 26 | q.push(do.Left) 27 | q.push(do.Right) 28 | nodesInNextLevel += 2 29 | } 30 | if nodesInCurrentLevel == 0 { 31 | fmt.Println() 32 | } 33 | nodesInCurrentLevel = nodesInNextLevel 34 | nodesInNextLevel = 0 35 | } 36 | fmt.Println() 37 | } 38 | 39 | type queue []*Node 40 | 41 | // newQueue returns a new queue. 42 | func newQueue() queue { 43 | q := queue([]*Node{}) 44 | return q 45 | } 46 | 47 | // push pushes n to the end of the queue. 48 | func (q *queue) push(n *Node) { 49 | *q = append(*q, n) 50 | } 51 | 52 | // pop pops the first element off the queue. 53 | func (q *queue) pop() *Node { 54 | n := (*q)[0] 55 | *q = (*q)[1:] 56 | return n 57 | } 58 | 59 | // empty returns true when the queue contains zero nodes. 60 | func (q *queue) empty() bool { 61 | return len(*q) == 0 62 | } 63 | -------------------------------------------------------------------------------- /dns/plugin/file/tree/walk.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import "github.com/miekg/dns" 4 | 5 | // Walk performs fn on all authoritative values stored in the tree in 6 | // in-order depth first. If a non-nil error is returned the Walk was interrupted 7 | // by an fn returning that error. If fn alters stored values' sort 8 | // relationships, future tree operation behaviors are undefined. 9 | func (t *Tree) Walk(fn func(*Elem, map[uint16][]dns.RR) error) error { 10 | if t.Root == nil { 11 | return nil 12 | } 13 | return t.Root.walk(fn) 14 | } 15 | 16 | func (n *Node) walk(fn func(*Elem, map[uint16][]dns.RR) error) error { 17 | if n.Left != nil { 18 | if err := n.Left.walk(fn); err != nil { 19 | return err 20 | } 21 | } 22 | 23 | if err := fn(n.Elem, n.Elem.m); err != nil { 24 | return err 25 | } 26 | 27 | if n.Right != nil { 28 | if err := n.Right.walk(fn); err != nil { 29 | return err 30 | } 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /dns/plugin/file/wildcard.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import "github.com/miekg/dns" 4 | 5 | // replaceWithWildcard replaces the left most label with '*'. 6 | func replaceWithAsteriskLabel(qname string) (wildcard string) { 7 | i, shot := dns.NextLabel(qname, 0) 8 | if shot { 9 | return "" 10 | } 11 | 12 | return "*." + qname[i:] 13 | } 14 | -------------------------------------------------------------------------------- /dns/plugin/file/xfr.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin/file/tree" 5 | "github.com/inverse-inc/wireguard-go/dns/plugin/transfer" 6 | 7 | "github.com/miekg/dns" 8 | ) 9 | 10 | // Transfer implements the transfer.Transfer interface. 11 | func (f File) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) { 12 | z, ok := f.Zones.Z[zone] 13 | if !ok || z == nil { 14 | return nil, transfer.ErrNotAuthoritative 15 | } 16 | return z.Transfer(serial) 17 | } 18 | 19 | // Transfer transfers a zone with serial in the returned channel and implements IXFR fallback, by just 20 | // sending a single SOA record. 21 | func (z *Zone) Transfer(serial uint32) (<-chan []dns.RR, error) { 22 | // get soa and apex 23 | apex, err := z.ApexIfDefined() 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | ch := make(chan []dns.RR) 29 | go func() { 30 | if serial != 0 && apex[0].(*dns.SOA).Serial == serial { // ixfr fallback, only send SOA 31 | ch <- []dns.RR{apex[0]} 32 | 33 | close(ch) 34 | return 35 | } 36 | 37 | ch <- apex 38 | z.Walk(func(e *tree.Elem, _ map[uint16][]dns.RR) error { ch <- e.All(); return nil }) 39 | ch <- []dns.RR{apex[0]} 40 | 41 | close(ch) 42 | }() 43 | 44 | return ch, nil 45 | } 46 | -------------------------------------------------------------------------------- /dns/plugin/file/zone_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import "testing" 4 | 5 | func TestNameFromRight(t *testing.T) { 6 | z := NewZone("example.org.", "stdin") 7 | 8 | tests := []struct { 9 | in string 10 | labels int 11 | shot bool 12 | expected string 13 | }{ 14 | {"example.org.", 0, false, "example.org."}, 15 | {"a.example.org.", 0, false, "example.org."}, 16 | {"a.example.org.", 1, false, "a.example.org."}, 17 | {"a.example.org.", 2, true, "a.example.org."}, 18 | {"a.b.example.org.", 2, false, "a.b.example.org."}, 19 | } 20 | 21 | for i, tc := range tests { 22 | got, shot := z.nameFromRight(tc.in, tc.labels) 23 | if got != tc.expected { 24 | t.Errorf("Test %d: expected %s, got %s", i, tc.expected, got) 25 | } 26 | if shot != tc.shot { 27 | t.Errorf("Test %d: expected shot to be %t, got %t", i, tc.shot, shot) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dns/plugin/forward/forward_test.go: -------------------------------------------------------------------------------- 1 | package forward 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestList(t *testing.T) { 8 | f := Forward{ 9 | proxies: []*Proxy{{addr: "1.1.1.1:53"}, {addr: "2.2.2.2:53"}, {addr: "3.3.3.3:53"}}, 10 | p: &roundRobin{}, 11 | } 12 | 13 | expect := []*Proxy{{addr: "2.2.2.2:53"}, {addr: "1.1.1.1:53"}, {addr: "3.3.3.3:53"}} 14 | got := f.List() 15 | 16 | if len(got) != len(expect) { 17 | t.Fatalf("Expected: %v results, got: %v", len(expect), len(got)) 18 | } 19 | for i, p := range got { 20 | if p.addr != expect[i].addr { 21 | t.Fatalf("Expected proxy %v to be '%v', got: '%v'", i, expect[i].addr, p.addr) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /dns/plugin/forward/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package forward 4 | 5 | import ( 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/dnstest" 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/fuzz" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | var f *Forward 13 | 14 | // abuse init to setup an environment to test against. This start another server to that will 15 | // reflect responses. 16 | func init() { 17 | f = New() 18 | s := dnstest.NewServer(r{}.reflectHandler) 19 | f.SetProxy(NewProxy(s.Addr, "tcp")) 20 | f.SetProxy(NewProxy(s.Addr, "udp")) 21 | } 22 | 23 | // Fuzz fuzzes forward. 24 | func Fuzz(data []byte) int { 25 | return fuzz.Do(f, data) 26 | } 27 | 28 | type r struct{} 29 | 30 | func (r r) reflectHandler(w dns.ResponseWriter, req *dns.Msg) { 31 | m := new(dns.Msg) 32 | m.SetReply(req) 33 | w.WriteMsg(m) 34 | } 35 | -------------------------------------------------------------------------------- /dns/plugin/forward/log_test.go: -------------------------------------------------------------------------------- 1 | package forward 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/forward/type.go: -------------------------------------------------------------------------------- 1 | package forward 2 | 3 | import "net" 4 | 5 | type transportType int 6 | 7 | const ( 8 | typeUdp transportType = iota 9 | typeTcp 10 | typeTls 11 | typeTotalCount // keep this last 12 | ) 13 | 14 | func stringToTransportType(s string) transportType { 15 | switch s { 16 | case "udp": 17 | return typeUdp 18 | case "tcp": 19 | return typeTcp 20 | case "tcp-tls": 21 | return typeTls 22 | } 23 | 24 | return typeUdp 25 | } 26 | 27 | func (t *Transport) transportTypeFromConn(pc *persistConn) transportType { 28 | if _, ok := pc.c.Conn.(*net.UDPConn); ok { 29 | return typeUdp 30 | } 31 | 32 | if t.tlsConfig == nil { 33 | return typeTcp 34 | } 35 | 36 | return typeTls 37 | } 38 | -------------------------------------------------------------------------------- /dns/plugin/grpc/metrics.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | // Variables declared for monitoring. 11 | var ( 12 | RequestCount = promauto.NewCounterVec(prometheus.CounterOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: "grpc", 15 | Name: "requests_total", 16 | Help: "Counter of requests made per upstream.", 17 | }, []string{"to"}) 18 | RcodeCount = promauto.NewCounterVec(prometheus.CounterOpts{ 19 | Namespace: plugin.Namespace, 20 | Subsystem: "grpc", 21 | Name: "responses_total", 22 | Help: "Counter of requests made per upstream.", 23 | }, []string{"rcode", "to"}) 24 | RequestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ 25 | Namespace: plugin.Namespace, 26 | Subsystem: "grpc", 27 | Name: "request_duration_seconds", 28 | Buckets: plugin.TimeBuckets, 29 | Help: "Histogram of the time each request took.", 30 | }, []string{"to"}) 31 | ) 32 | -------------------------------------------------------------------------------- /dns/plugin/health/health_test.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestHealth(t *testing.T) { 12 | h := &health{Addr: ":0", stop: make(chan bool)} 13 | 14 | if err := h.OnStartup(); err != nil { 15 | t.Fatalf("Unable to startup the health server: %v", err) 16 | } 17 | defer h.OnFinalShutdown() 18 | 19 | address := fmt.Sprintf("http://%s%s", h.ln.Addr().String(), "/health") 20 | 21 | response, err := http.Get(address) 22 | if err != nil { 23 | t.Fatalf("Unable to query %s: %v", address, err) 24 | } 25 | if response.StatusCode != 200 { 26 | t.Errorf("Invalid status code: expecting '200', got '%d'", response.StatusCode) 27 | } 28 | content, err := ioutil.ReadAll(response.Body) 29 | if err != nil { 30 | t.Fatalf("Unable to get response body from %s: %v", address, err) 31 | } 32 | response.Body.Close() 33 | 34 | if string(content) != http.StatusText(http.StatusOK) { 35 | t.Errorf("Invalid response body: expecting 'OK', got '%s'", string(content)) 36 | } 37 | } 38 | 39 | func TestHealthLameduck(t *testing.T) { 40 | h := &health{Addr: ":0", stop: make(chan bool), lameduck: 250 * time.Millisecond} 41 | 42 | if err := h.OnStartup(); err != nil { 43 | t.Fatalf("Unable to startup the health server: %v", err) 44 | } 45 | 46 | h.OnFinalShutdown() 47 | } 48 | -------------------------------------------------------------------------------- /dns/plugin/health/log_test.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/health/setup_test.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestSetupHealth(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | shouldErr bool 13 | }{ 14 | {`health`, false}, 15 | {`health localhost:1234`, false}, 16 | {`health localhost:1234 { 17 | lameduck 4s 18 | }`, false}, 19 | {`health bla:a`, false}, 20 | 21 | {`health bla`, true}, 22 | {`health bla bla`, true}, 23 | {`health localhost:1234 { 24 | lameduck a 25 | }`, true}, 26 | {`health localhost:1234 { 27 | lamedudk 4 28 | } `, true}, 29 | } 30 | 31 | for i, test := range tests { 32 | c := caddy.NewTestController("dns", test.input) 33 | _, _, err := parse(c) 34 | 35 | if test.shouldErr && err == nil { 36 | t.Errorf("Test %d: Expected error but found none for input %s", i, test.input) 37 | } 38 | 39 | if err != nil { 40 | if !test.shouldErr { 41 | t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /dns/plugin/hosts/log_test.go: -------------------------------------------------------------------------------- 1 | package hosts 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/hosts/metrics.go: -------------------------------------------------------------------------------- 1 | package hosts 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | var ( 11 | // hostsEntries is the combined number of entries in hosts and Corefile. 12 | hostsEntries = promauto.NewGaugeVec(prometheus.GaugeOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: "hosts", 15 | Name: "entries", 16 | Help: "The combined number of entries in hosts and Corefile.", 17 | }, []string{}) 18 | // hostsReloadTime is the timestamp of the last reload of hosts file. 19 | hostsReloadTime = promauto.NewGauge(prometheus.GaugeOpts{ 20 | Namespace: plugin.Namespace, 21 | Subsystem: "hosts", 22 | Name: "reload_timestamp_seconds", 23 | Help: "The timestamp of the last reload of hosts file.", 24 | }) 25 | ) 26 | -------------------------------------------------------------------------------- /dns/plugin/kubernetes/local.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 8 | ) 9 | 10 | // boundIPs returns the list of non-loopback IPs that CoreDNS is bound to 11 | func boundIPs(c *caddy.Controller) (ips []net.IP) { 12 | conf := dnsserver.GetConfig(c) 13 | hosts := conf.ListenHosts 14 | if hosts == nil || hosts[0] == "" { 15 | hosts = nil 16 | addrs, err := net.InterfaceAddrs() 17 | if err != nil { 18 | return nil 19 | } 20 | for _, addr := range addrs { 21 | hosts = append(hosts, addr.String()) 22 | } 23 | } 24 | for _, host := range hosts { 25 | ip, _, _ := net.ParseCIDR(host) 26 | ip4 := ip.To4() 27 | if ip4 != nil && !ip4.IsLoopback() { 28 | ips = append(ips, ip4) 29 | continue 30 | } 31 | ip6 := ip.To16() 32 | if ip6 != nil && !ip6.IsLoopback() { 33 | ips = append(ips, ip6) 34 | } 35 | } 36 | return ips 37 | } 38 | -------------------------------------------------------------------------------- /dns/plugin/kubernetes/log_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/kubernetes/namespace.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | // filteredNamespaceExists checks if namespace exists in this cluster 4 | // according to any `namespace_labels` plugin configuration specified. 5 | // Returns true even for namespaces not exposed by plugin configuration, 6 | // see namespaceExposed. 7 | func (k *Kubernetes) filteredNamespaceExists(namespace string) bool { 8 | ns, err := k.APIConn.GetNamespaceByName(namespace) 9 | if err != nil { 10 | return false 11 | } 12 | return ns.ObjectMeta.Name == namespace 13 | } 14 | 15 | // configuredNamespace returns true when the namespace is exposed through the plugin 16 | // `namespaces` configuration. 17 | func (k *Kubernetes) configuredNamespace(namespace string) bool { 18 | _, ok := k.Namespaces[namespace] 19 | if len(k.Namespaces) > 0 && !ok { 20 | return false 21 | } 22 | return true 23 | } 24 | 25 | func (k *Kubernetes) namespaceExposed(namespace string) bool { 26 | return k.configuredNamespace(namespace) && k.filteredNamespaceExists(namespace) 27 | } 28 | -------------------------------------------------------------------------------- /dns/plugin/kubernetes/ready.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | // Ready implements the ready.Readiness interface. 4 | func (k *Kubernetes) Ready() bool { return k.APIConn.HasSynced() } 5 | -------------------------------------------------------------------------------- /dns/plugin/kubernetes/setup_reverse_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestKubernetesParseReverseZone(t *testing.T) { 10 | tests := []struct { 11 | input string // Corefile data as string 12 | expectedZones []string // expected count of defined zones. 13 | }{ 14 | {`kubernetes coredns.local 10.0.0.0/16`, []string{"coredns.local.", "0.10.in-addr.arpa."}}, 15 | {`kubernetes coredns.local 10.0.0.0/17`, []string{"coredns.local.", "0.10.in-addr.arpa."}}, 16 | {`kubernetes coredns.local fd00:77:30::0/110`, []string{"coredns.local.", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3.0.0.7.7.0.0.0.0.d.f.ip6.arpa."}}, 17 | } 18 | 19 | for i, tc := range tests { 20 | c := caddy.NewTestController("dns", tc.input) 21 | k, err := kubernetesParse(c) 22 | if err != nil { 23 | t.Fatalf("Test %d: Expected no error, got %q", i, err) 24 | } 25 | 26 | zl := len(k.Zones) 27 | if zl != len(tc.expectedZones) { 28 | t.Errorf("Test %d: Expected kubernetes to be initialized with %d zones, found %d zones", i, len(tc.expectedZones), zl) 29 | } 30 | for i, z := range tc.expectedZones { 31 | if k.Zones[i] != z { 32 | t.Errorf("Test %d: Expected zones to be %q, got %q", i, z, k.Zones[i]) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /dns/plugin/kubernetes/setup_ttl_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestKubernetesParseTTL(t *testing.T) { 10 | tests := []struct { 11 | input string // Corefile data as string 12 | expectedTTL uint32 // expected count of defined zones. 13 | shouldErr bool 14 | }{ 15 | {`kubernetes cluster.local { 16 | ttl 56 17 | }`, 56, false}, 18 | {`kubernetes cluster.local`, defaultTTL, false}, 19 | {`kubernetes cluster.local { 20 | ttl -1 21 | }`, 0, true}, 22 | {`kubernetes cluster.local { 23 | ttl 3601 24 | }`, 0, true}, 25 | } 26 | 27 | for i, tc := range tests { 28 | c := caddy.NewTestController("dns", tc.input) 29 | k, err := kubernetesParse(c) 30 | if err != nil && !tc.shouldErr { 31 | t.Fatalf("Test %d: Expected no error, got %q", i, err) 32 | } 33 | if err == nil && tc.shouldErr { 34 | t.Fatalf("Test %d: Expected error, got none", i) 35 | } 36 | if err != nil && tc.shouldErr { 37 | // input should error 38 | continue 39 | } 40 | 41 | if k.ttl != tc.expectedTTL { 42 | t.Errorf("Test %d: Expected TTl to be %d, got %d", i, tc.expectedTTL, k.ttl) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /dns/plugin/loadbalance/README.md: -------------------------------------------------------------------------------- 1 | # loadbalance 2 | 3 | ## Name 4 | 5 | *loadbalance* - randomizes the order of A, AAAA and MX records. 6 | 7 | ## Description 8 | 9 | The *loadbalance* will act as a round-robin DNS load balancer by randomizing the order of A, AAAA, 10 | and MX records in the answer. 11 | 12 | See [Wikipedia](https://en.wikipedia.org/wiki/Round-robin_DNS) about the pros and cons of this 13 | setup. It will take care to sort any CNAMEs before any address records, because some stub resolver 14 | implementations (like glibc) are particular about that. 15 | 16 | ## Syntax 17 | 18 | ~~~ 19 | loadbalance [POLICY] 20 | ~~~ 21 | 22 | * **POLICY** is how to balance. The default, and only option, is "round_robin". 23 | 24 | ## Examples 25 | 26 | Load balance replies coming back from Google Public DNS: 27 | 28 | ~~~ corefile 29 | . { 30 | loadbalance round_robin 31 | forward . 8.8.8.8 8.8.4.4 32 | } 33 | ~~~ 34 | -------------------------------------------------------------------------------- /dns/plugin/loadbalance/handler.go: -------------------------------------------------------------------------------- 1 | // Package loadbalance is a plugin for rewriting responses to do "load balancing" 2 | package loadbalance 3 | 4 | import ( 5 | "context" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | // RoundRobin is a plugin to rewrite responses for "load balancing". 13 | type RoundRobin struct { 14 | Next plugin.Handler 15 | } 16 | 17 | // ServeDNS implements the plugin.Handler interface. 18 | func (rr RoundRobin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 19 | wrr := &RoundRobinResponseWriter{w} 20 | return plugin.NextOrFailure(rr.Name(), rr.Next, ctx, wrr, r) 21 | } 22 | 23 | // Name implements the Handler interface. 24 | func (rr RoundRobin) Name() string { return "loadbalance" } 25 | -------------------------------------------------------------------------------- /dns/plugin/loadbalance/log_test.go: -------------------------------------------------------------------------------- 1 | package loadbalance 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/loadbalance/setup.go: -------------------------------------------------------------------------------- 1 | package loadbalance 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin" 9 | clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 10 | ) 11 | 12 | var log = clog.NewWithPlugin("loadbalance") 13 | 14 | func init() { plugin.Register("loadbalance", setup) } 15 | 16 | func setup(c *caddy.Controller) error { 17 | err := parse(c) 18 | if err != nil { 19 | return plugin.Error("loadbalance", err) 20 | } 21 | 22 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 23 | return RoundRobin{Next: next} 24 | }) 25 | 26 | return nil 27 | } 28 | 29 | func parse(c *caddy.Controller) error { 30 | for c.Next() { 31 | args := c.RemainingArgs() 32 | switch len(args) { 33 | case 0: 34 | return nil 35 | case 1: 36 | if args[0] != "round_robin" { 37 | return fmt.Errorf("unknown policy: %s", args[0]) 38 | 39 | } 40 | return nil 41 | } 42 | } 43 | return c.ArgErr() 44 | } 45 | -------------------------------------------------------------------------------- /dns/plugin/loadbalance/setup_test.go: -------------------------------------------------------------------------------- 1 | package loadbalance 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/inverse-inc/coredns-caddy" 8 | ) 9 | 10 | func TestSetup(t *testing.T) { 11 | tests := []struct { 12 | input string 13 | shouldErr bool 14 | expectedPolicy string 15 | expectedErrContent string // substring from the expected error. Empty for positive cases. 16 | }{ 17 | // positive 18 | {`loadbalance`, false, "round_robin", ""}, 19 | {`loadbalance round_robin`, false, "round_robin", ""}, 20 | // negative 21 | {`loadbalance fleeb`, true, "", "unknown policy"}, 22 | {`loadbalance a b`, true, "", "argument count or unexpected line"}, 23 | } 24 | 25 | for i, test := range tests { 26 | c := caddy.NewTestController("dns", test.input) 27 | err := parse(c) 28 | 29 | if test.shouldErr && err == nil { 30 | t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input) 31 | } 32 | 33 | if err != nil { 34 | if !test.shouldErr { 35 | t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err) 36 | } 37 | 38 | if !strings.Contains(err.Error(), test.expectedErrContent) { 39 | t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input) 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /dns/plugin/log_test.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/loop/log_test.go: -------------------------------------------------------------------------------- 1 | package loop 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/loop/loop_test.go: -------------------------------------------------------------------------------- 1 | package loop 2 | 3 | import "testing" 4 | 5 | func TestLoop(t *testing.T) { 6 | l := New(".") 7 | l.inc() 8 | if l.seen() != 1 { 9 | t.Errorf("Failed to inc loop, expected %d, got %d", 1, l.seen()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dns/plugin/loop/setup_test.go: -------------------------------------------------------------------------------- 1 | package loop 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestSetup(t *testing.T) { 10 | c := caddy.NewTestController("dns", `loop`) 11 | if err := setup(c); err != nil { 12 | t.Fatalf("Expected no errors, but got: %v", err) 13 | } 14 | 15 | c = caddy.NewTestController("dns", `loop argument`) 16 | if err := setup(c); err == nil { 17 | t.Fatal("Expected errors, but got none") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dns/plugin/metadata/log_test.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/metadata/metadata.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | "github.com/inverse-inc/wireguard-go/dns/request" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | // Metadata implements collecting metadata information from all plugins that 13 | // implement the Provider interface. 14 | type Metadata struct { 15 | Zones []string 16 | Providers []Provider 17 | Next plugin.Handler 18 | } 19 | 20 | // Name implements the Handler interface. 21 | func (m *Metadata) Name() string { return "metadata" } 22 | 23 | // ContextWithMetadata is exported for use by provider tests 24 | func ContextWithMetadata(ctx context.Context) context.Context { 25 | return context.WithValue(ctx, key{}, md{}) 26 | } 27 | 28 | // ServeDNS implements the plugin.Handler interface. 29 | func (m *Metadata) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 30 | 31 | ctx = ContextWithMetadata(ctx) 32 | 33 | state := request.Request{W: w, Req: r} 34 | if plugin.Zones(m.Zones).Matches(state.Name()) != "" { 35 | // Go through all Providers and collect metadata. 36 | for _, p := range m.Providers { 37 | ctx = p.Metadata(ctx, state) 38 | } 39 | } 40 | 41 | rcode, err := plugin.NextOrFailure(m.Name(), m.Next, ctx, w, r) 42 | 43 | return rcode, err 44 | } 45 | -------------------------------------------------------------------------------- /dns/plugin/metrics/context.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 7 | ) 8 | 9 | // WithServer returns the current server handling the request. It returns the 10 | // server listening address: ://[]: Normally this is 11 | // something like "dns://:53", but if the bind plugin is used, i.e. "bind 12 | // 127.0.0.53", it will be "dns://127.0.0.53:53", etc. If not address is found 13 | // the empty string is returned. 14 | // 15 | // Basic usage with a metric: 16 | // 17 | // .WithLabelValues(metrics.WithServer(ctx), labels..).Add(1) 18 | func WithServer(ctx context.Context) string { 19 | srv := ctx.Value(dnsserver.Key{}) 20 | if srv == nil { 21 | return "" 22 | } 23 | return srv.(*dnsserver.Server).Addr 24 | } 25 | -------------------------------------------------------------------------------- /dns/plugin/metrics/handler.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/metrics/vars" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/dnstest" 9 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/rcode" 10 | "github.com/inverse-inc/wireguard-go/dns/request" 11 | 12 | "github.com/miekg/dns" 13 | ) 14 | 15 | // ServeDNS implements the Handler interface. 16 | func (m *Metrics) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 17 | state := request.Request{W: w, Req: r} 18 | 19 | qname := state.QName() 20 | zone := plugin.Zones(m.ZoneNames()).Matches(qname) 21 | if zone == "" { 22 | zone = "." 23 | } 24 | 25 | // Record response to get status code and size of the reply. 26 | rw := dnstest.NewRecorder(w) 27 | status, err := plugin.NextOrFailure(m.Name(), m.Next, ctx, rw, r) 28 | 29 | vars.Report(WithServer(ctx), state, zone, rcode.ToString(rw.Rcode), rw.Len, rw.Start) 30 | 31 | return status, err 32 | } 33 | 34 | // Name implements the Handler interface. 35 | func (m *Metrics) Name() string { return "prometheus" } 36 | -------------------------------------------------------------------------------- /dns/plugin/metrics/log_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/metrics/registry.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | ) 8 | 9 | type reg struct { 10 | sync.RWMutex 11 | r map[string]*prometheus.Registry 12 | } 13 | 14 | func newReg() *reg { return ®{r: make(map[string]*prometheus.Registry)} } 15 | 16 | // update sets the registry if not already there and returns the input. Or it returns 17 | // a previous set value. 18 | func (r *reg) getOrSet(addr string, pr *prometheus.Registry) *prometheus.Registry { 19 | r.Lock() 20 | defer r.Unlock() 21 | 22 | if v, ok := r.r[addr]; ok { 23 | return v 24 | } 25 | 26 | r.r[addr] = pr 27 | return pr 28 | } 29 | -------------------------------------------------------------------------------- /dns/plugin/metrics/setup_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestPrometheusParse(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | shouldErr bool 13 | addr string 14 | }{ 15 | // oks 16 | {`prometheus`, false, "localhost:9153"}, 17 | {`prometheus localhost:53`, false, "localhost:53"}, 18 | // fails 19 | {`prometheus {}`, true, ""}, 20 | {`prometheus /foo`, true, ""}, 21 | {`prometheus a b c`, true, ""}, 22 | } 23 | for i, test := range tests { 24 | c := caddy.NewTestController("dns", test.input) 25 | m, err := parse(c) 26 | if test.shouldErr && err == nil { 27 | t.Errorf("Test %v: Expected error but found nil", i) 28 | continue 29 | } else if !test.shouldErr && err != nil { 30 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 31 | continue 32 | } 33 | 34 | if test.shouldErr { 35 | continue 36 | } 37 | 38 | if test.addr != m.Addr { 39 | t.Errorf("Test %v: Expected address %s but found: %s", i, test.addr, m.Addr) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /dns/plugin/nsid/log_test.go: -------------------------------------------------------------------------------- 1 | package nsid 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/nsid/setup.go: -------------------------------------------------------------------------------- 1 | package nsid 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/inverse-inc/coredns-caddy" 8 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 9 | "github.com/inverse-inc/wireguard-go/dns/plugin" 10 | ) 11 | 12 | func init() { plugin.Register("nsid", setup) } 13 | 14 | func setup(c *caddy.Controller) error { 15 | nsid, err := nsidParse(c) 16 | if err != nil { 17 | return plugin.Error("nsid", err) 18 | } 19 | 20 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 21 | return Nsid{Next: next, Data: nsid} 22 | }) 23 | 24 | return nil 25 | } 26 | 27 | func nsidParse(c *caddy.Controller) (string, error) { 28 | // Use hostname as the default 29 | nsid, err := os.Hostname() 30 | if err != nil { 31 | nsid = "localhost" 32 | } 33 | i := 0 34 | for c.Next() { 35 | if i > 0 { 36 | return nsid, plugin.ErrOnce 37 | } 38 | i++ 39 | args := c.RemainingArgs() 40 | if len(args) > 0 { 41 | nsid = strings.Join(args, " ") 42 | } 43 | } 44 | return nsid, nil 45 | } 46 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnstest/multirecorder.go: -------------------------------------------------------------------------------- 1 | package dnstest 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // MultiRecorder is a type of ResponseWriter that captures all messages written to it. 10 | type MultiRecorder struct { 11 | Len int 12 | Msgs []*dns.Msg 13 | Start time.Time 14 | dns.ResponseWriter 15 | } 16 | 17 | // NewMultiRecorder makes and returns a new MultiRecorder. 18 | func NewMultiRecorder(w dns.ResponseWriter) *MultiRecorder { 19 | return &MultiRecorder{ 20 | ResponseWriter: w, 21 | Msgs: make([]*dns.Msg, 0), 22 | Start: time.Now(), 23 | } 24 | } 25 | 26 | // WriteMsg records the message and its length written to it and call the 27 | // underlying ResponseWriter's WriteMsg method. 28 | func (r *MultiRecorder) WriteMsg(res *dns.Msg) error { 29 | r.Len += res.Len() 30 | r.Msgs = append(r.Msgs, res) 31 | return r.ResponseWriter.WriteMsg(res) 32 | } 33 | 34 | // Write is a wrapper that records the length of the messages that get written to it. 35 | func (r *MultiRecorder) Write(buf []byte) (int, error) { 36 | n, err := r.ResponseWriter.Write(buf) 37 | if err == nil { 38 | r.Len += n 39 | } 40 | return n, err 41 | } 42 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnstest/multirecorder_test.go: -------------------------------------------------------------------------------- 1 | package dnstest 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestMultiWriteMsg(t *testing.T) { 10 | w := &responseWriter{} 11 | record := NewMultiRecorder(w) 12 | 13 | responseTestName := "testmsg.example.org." 14 | responseTestMsg := new(dns.Msg) 15 | responseTestMsg.SetQuestion(responseTestName, dns.TypeA) 16 | 17 | record.WriteMsg(responseTestMsg) 18 | record.WriteMsg(responseTestMsg) 19 | 20 | if len(record.Msgs) != 2 { 21 | t.Fatalf("Expected 2 messages to be written, but instead found %d\n", len(record.Msgs)) 22 | 23 | } 24 | if record.Len != responseTestMsg.Len()*2 { 25 | t.Fatalf("Expected the bytes written counter to be %d, but instead found %d\n", responseTestMsg.Len()*2, record.Len) 26 | } 27 | } 28 | 29 | func TestMultiWrite(t *testing.T) { 30 | w := &responseWriter{} 31 | record := NewRecorder(w) 32 | responseTest := []byte("testmsg.example.org.") 33 | 34 | record.Write(responseTest) 35 | record.Write(responseTest) 36 | if record.Len != len(responseTest)*2 { 37 | t.Fatalf("Expected the bytes written counter to be %d, but instead found %d\n", len(responseTest)*2, record.Len) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnstest/server_test.go: -------------------------------------------------------------------------------- 1 | package dnstest 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestNewServer(t *testing.T) { 10 | s := NewServer(func(w dns.ResponseWriter, r *dns.Msg) { 11 | ret := new(dns.Msg) 12 | ret.SetReply(r) 13 | w.WriteMsg(ret) 14 | }) 15 | defer s.Close() 16 | 17 | c := new(dns.Client) 18 | c.Net = "tcp" 19 | m := new(dns.Msg) 20 | m.SetQuestion("example.org.", dns.TypeSOA) 21 | ret, _, err := c.Exchange(m, s.Addr) 22 | if err != nil { 23 | t.Fatalf("Could not send message to dnstest.Server: %s", err) 24 | } 25 | if ret.Id != m.Id { 26 | t.Fatalf("Msg ID's should match, expected %d, got %d", m.Id, ret.Id) 27 | } 28 | 29 | c.Net = "udp" 30 | ret, _, err = c.Exchange(m, s.Addr) 31 | if err != nil { 32 | t.Fatalf("Could not send message to dnstest.Server: %s", err) 33 | } 34 | if ret.Id != m.Id { 35 | t.Fatalf("Msg ID's should match, expected %d, got %d", m.Id, ret.Id) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnsutil/cname.go: -------------------------------------------------------------------------------- 1 | package dnsutil 2 | 3 | import "github.com/miekg/dns" 4 | 5 | // DuplicateCNAME returns true if r already exists in records. 6 | func DuplicateCNAME(r *dns.CNAME, records []dns.RR) bool { 7 | for _, rec := range records { 8 | if v, ok := rec.(*dns.CNAME); ok { 9 | if v.Target == r.Target { 10 | return true 11 | } 12 | } 13 | } 14 | return false 15 | } 16 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnsutil/doc.go: -------------------------------------------------------------------------------- 1 | // Package dnsutil contains DNS related helper functions. 2 | package dnsutil 3 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnsutil/join.go: -------------------------------------------------------------------------------- 1 | package dnsutil 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // Join joins labels to form a fully qualified domain name. If the last label is 10 | // the root label it is ignored. Not other syntax checks are performed. 11 | func Join(labels ...string) string { 12 | ll := len(labels) 13 | if labels[ll-1] == "." { 14 | return strings.Join(labels[:ll-1], ".") + "." 15 | } 16 | return dns.Fqdn(strings.Join(labels, ".")) 17 | } 18 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnsutil/join_test.go: -------------------------------------------------------------------------------- 1 | package dnsutil 2 | 3 | import "testing" 4 | 5 | func TestJoin(t *testing.T) { 6 | tests := []struct { 7 | in []string 8 | out string 9 | }{ 10 | {[]string{"bla", "bliep", "example", "org"}, "bla.bliep.example.org."}, 11 | {[]string{"example", "."}, "example."}, 12 | {[]string{"example", "org."}, "example.org."}, // technically we should not be called like this. 13 | {[]string{"."}, "."}, 14 | } 15 | 16 | for i, tc := range tests { 17 | if x := Join(tc.in...); x != tc.out { 18 | t.Errorf("Test %d, expected %s, got %s", i, tc.out, x) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnsutil/zone.go: -------------------------------------------------------------------------------- 1 | package dnsutil 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // TrimZone removes the zone component from q. It returns the trimmed 10 | // name or an error is zone is longer then qname. The trimmed name will be returned 11 | // without a trailing dot. 12 | func TrimZone(q string, z string) (string, error) { 13 | zl := dns.CountLabel(z) 14 | i, ok := dns.PrevLabel(q, zl) 15 | if ok || i-1 < 0 { 16 | return "", errors.New("trimzone: overshot qname: " + q + "for zone " + z) 17 | } 18 | // This includes the '.', remove on return 19 | return q[:i-1], nil 20 | } 21 | -------------------------------------------------------------------------------- /dns/plugin/pkg/dnsutil/zone_test.go: -------------------------------------------------------------------------------- 1 | package dnsutil 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/miekg/dns" 8 | ) 9 | 10 | func TestTrimZone(t *testing.T) { 11 | tests := []struct { 12 | qname string 13 | zone string 14 | expected string 15 | err error 16 | }{ 17 | {"a.example.org", "example.org", "a", nil}, 18 | {"a.b.example.org", "example.org", "a.b", nil}, 19 | {"b.", ".", "b", nil}, 20 | {"example.org", "example.org", "", errors.New("should err")}, 21 | {"org", "example.org", "", errors.New("should err")}, 22 | } 23 | 24 | for i, tc := range tests { 25 | got, err := TrimZone(dns.Fqdn(tc.qname), dns.Fqdn(tc.zone)) 26 | if tc.err != nil && err == nil { 27 | t.Errorf("Test %d, expected error got nil", i) 28 | continue 29 | } 30 | if tc.err == nil && err != nil { 31 | t.Errorf("Test %d, expected no error got %v", i, err) 32 | continue 33 | } 34 | if got != tc.expected { 35 | t.Errorf("Test %d, expected %s, got %s", i, tc.expected, got) 36 | continue 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dns/plugin/pkg/edns/edns_test.go: -------------------------------------------------------------------------------- 1 | package edns 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestVersion(t *testing.T) { 10 | m := ednsMsg() 11 | m.Extra[0].(*dns.OPT).SetVersion(2) 12 | 13 | _, err := Version(m) 14 | if err == nil { 15 | t.Errorf("Expected wrong version, but got OK") 16 | } 17 | } 18 | 19 | func TestVersionNoEdns(t *testing.T) { 20 | m := ednsMsg() 21 | m.Extra = nil 22 | 23 | _, err := Version(m) 24 | if err != nil { 25 | t.Errorf("Expected no error, but got one: %s", err) 26 | } 27 | } 28 | 29 | func ednsMsg() *dns.Msg { 30 | m := new(dns.Msg) 31 | m.SetQuestion("example.com.", dns.TypeA) 32 | o := new(dns.OPT) 33 | o.Hdr.Name = "." 34 | o.Hdr.Rrtype = dns.TypeOPT 35 | m.Extra = append(m.Extra, o) 36 | return m 37 | } 38 | -------------------------------------------------------------------------------- /dns/plugin/pkg/fuzz/do.go: -------------------------------------------------------------------------------- 1 | // Package fuzz contains functions that enable fuzzing of plugins. 2 | package fuzz 3 | 4 | import ( 5 | "context" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 9 | 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | // Do will fuzz p - used by gofuzz. See Makefile.fuzz for comments and context. 14 | func Do(p plugin.Handler, data []byte) int { 15 | ctx := context.TODO() 16 | r := new(dns.Msg) 17 | if err := r.Unpack(data); err != nil { 18 | return 0 // plugin will never be called when this happens. 19 | } 20 | // If the data unpack into a dns msg, but does not have a proper question section discard it. 21 | // The server parts make sure this is true before calling the plugins; mimic this behavior. 22 | if len(r.Question) == 0 { 23 | return 0 24 | } 25 | 26 | if _, err := p.ServeDNS(ctx, &test.ResponseWriter{}, r); err != nil { 27 | return 1 28 | } 29 | 30 | return 0 31 | } 32 | -------------------------------------------------------------------------------- /dns/plugin/pkg/log/plugin_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | golog "log" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestPlugins(t *testing.T) { 11 | var f bytes.Buffer 12 | const ts = "test" 13 | golog.SetOutput(&f) 14 | 15 | lg := NewWithPlugin("testplugin") 16 | 17 | lg.Info(ts) 18 | if x := f.String(); !strings.Contains(x, "plugin/testplugin") { 19 | t.Errorf("Expected log to be %s, got %s", info+ts, x) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dns/plugin/pkg/nonwriter/nonwriter.go: -------------------------------------------------------------------------------- 1 | // Package nonwriter implements a dns.ResponseWriter that never writes, but captures the dns.Msg being written. 2 | package nonwriter 3 | 4 | import ( 5 | "github.com/miekg/dns" 6 | ) 7 | 8 | // Writer is a type of ResponseWriter that captures the message, but never writes to the client. 9 | type Writer struct { 10 | dns.ResponseWriter 11 | Msg *dns.Msg 12 | } 13 | 14 | // New makes and returns a new NonWriter. 15 | func New(w dns.ResponseWriter) *Writer { return &Writer{ResponseWriter: w} } 16 | 17 | // WriteMsg records the message, but doesn't write it itself. 18 | func (w *Writer) WriteMsg(res *dns.Msg) error { 19 | w.Msg = res 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /dns/plugin/pkg/nonwriter/nonwriter_test.go: -------------------------------------------------------------------------------- 1 | package nonwriter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestNonWriter(t *testing.T) { 10 | nw := New(nil) 11 | m := new(dns.Msg) 12 | m.SetQuestion("example.org.", dns.TypeA) 13 | if err := nw.WriteMsg(m); err != nil { 14 | t.Errorf("Got error when writing to nonwriter: %s", err) 15 | } 16 | if x := nw.Msg.Question[0].Name; x != "example.org." { 17 | t.Errorf("Expacted 'example.org.' got %q:", x) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dns/plugin/pkg/parse/parse.go: -------------------------------------------------------------------------------- 1 | // Package parse contains functions that can be used in the setup code for plugins. 2 | package parse 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/inverse-inc/coredns-caddy" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/transport" 9 | ) 10 | 11 | // TransferIn parses transfer statements: 'transfer from [address...]'. 12 | func TransferIn(c *caddy.Controller) (froms []string, err error) { 13 | if !c.NextArg() { 14 | return nil, c.ArgErr() 15 | } 16 | value := c.Val() 17 | switch value { 18 | default: 19 | return nil, c.Errf("unknown property %s", value) 20 | case "from": 21 | froms = c.RemainingArgs() 22 | if len(froms) == 0 { 23 | return nil, c.ArgErr() 24 | } 25 | for i := range froms { 26 | if froms[i] != "*" { 27 | normalized, err := HostPort(froms[i], transport.Port) 28 | if err != nil { 29 | return nil, err 30 | } 31 | froms[i] = normalized 32 | } else { 33 | return nil, fmt.Errorf("can't use '*' in transfer from") 34 | } 35 | } 36 | } 37 | return froms, nil 38 | } 39 | -------------------------------------------------------------------------------- /dns/plugin/pkg/parse/transport.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/transport" 7 | ) 8 | 9 | // Transport returns the transport defined in s and a string where the 10 | // transport prefix is removed (if there was any). If no transport is defined 11 | // we default to TransportDNS 12 | func Transport(s string) (trans string, addr string) { 13 | switch { 14 | case strings.HasPrefix(s, transport.TLS+"://"): 15 | s = s[len(transport.TLS+"://"):] 16 | return transport.TLS, s 17 | 18 | case strings.HasPrefix(s, transport.DNS+"://"): 19 | s = s[len(transport.DNS+"://"):] 20 | return transport.DNS, s 21 | 22 | case strings.HasPrefix(s, transport.GRPC+"://"): 23 | s = s[len(transport.GRPC+"://"):] 24 | return transport.GRPC, s 25 | 26 | case strings.HasPrefix(s, transport.HTTPS+"://"): 27 | s = s[len(transport.HTTPS+"://"):] 28 | 29 | return transport.HTTPS, s 30 | } 31 | 32 | return transport.DNS, s 33 | } 34 | -------------------------------------------------------------------------------- /dns/plugin/pkg/parse/transport_test.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/transport" 7 | ) 8 | 9 | func TestTransport(t *testing.T) { 10 | for i, test := range []struct { 11 | input string 12 | expected string 13 | }{ 14 | {"dns://.:53", transport.DNS}, 15 | {"2003::1/64.:53", transport.DNS}, 16 | {"grpc://example.org:1443 ", transport.GRPC}, 17 | {"tls://example.org ", transport.TLS}, 18 | {"https://example.org ", transport.HTTPS}, 19 | } { 20 | actual, _ := Transport(test.input) 21 | if actual != test.expected { 22 | t.Errorf("Test %d: Expected %s but got %s", i, test.expected, actual) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dns/plugin/pkg/rcode/rcode.go: -------------------------------------------------------------------------------- 1 | package rcode 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // ToString convert the rcode to the official DNS string, or to "RCODE"+value if the RCODE value is unknown. 10 | func ToString(rcode int) string { 11 | if str, ok := dns.RcodeToString[rcode]; ok { 12 | return str 13 | } 14 | return "RCODE" + strconv.Itoa(rcode) 15 | } 16 | -------------------------------------------------------------------------------- /dns/plugin/pkg/rcode/rcode_test.go: -------------------------------------------------------------------------------- 1 | package rcode 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestToString(t *testing.T) { 10 | tests := []struct { 11 | in int 12 | expected string 13 | }{ 14 | { 15 | dns.RcodeSuccess, 16 | "NOERROR", 17 | }, 18 | { 19 | 28, 20 | "RCODE28", 21 | }, 22 | } 23 | for i, test := range tests { 24 | got := ToString(test.in) 25 | if got != test.expected { 26 | t.Errorf("Test %d, expected %s, got %s", i, test.expected, got) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /dns/plugin/pkg/reuseport/listen_go111.go: -------------------------------------------------------------------------------- 1 | // +build go1.11 2 | // +build aix darwin dragonfly freebsd linux netbsd openbsd 3 | 4 | package reuseport 5 | 6 | import ( 7 | "context" 8 | "net" 9 | "syscall" 10 | 11 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 12 | 13 | "golang.org/x/sys/unix" 14 | ) 15 | 16 | func control(network, address string, c syscall.RawConn) error { 17 | c.Control(func(fd uintptr) { 18 | if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { 19 | log.Warningf("Failed to set SO_REUSEPORT on socket: %s", err) 20 | } 21 | }) 22 | return nil 23 | } 24 | 25 | // Listen announces on the local network address. See net.Listen for more information. 26 | // If SO_REUSEPORT is available it will be set on the socket. 27 | func Listen(network, addr string) (net.Listener, error) { 28 | lc := net.ListenConfig{Control: control} 29 | return lc.Listen(context.Background(), network, addr) 30 | } 31 | 32 | // ListenPacket announces on the local network address. See net.ListenPacket for more information. 33 | // If SO_REUSEPORT is available it will be set on the socket. 34 | func ListenPacket(network, addr string) (net.PacketConn, error) { 35 | lc := net.ListenConfig{Control: control} 36 | return lc.ListenPacket(context.Background(), network, addr) 37 | } 38 | -------------------------------------------------------------------------------- /dns/plugin/pkg/reuseport/listen_go_not111.go: -------------------------------------------------------------------------------- 1 | // +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd 2 | 3 | package reuseport 4 | 5 | import "net" 6 | 7 | // Listen is a wrapper around net.Listen. 8 | func Listen(network, addr string) (net.Listener, error) { return net.Listen(network, addr) } 9 | 10 | // ListenPacket is a wrapper around net.ListenPacket. 11 | func ListenPacket(network, addr string) (net.PacketConn, error) { 12 | return net.ListenPacket(network, addr) 13 | } 14 | -------------------------------------------------------------------------------- /dns/plugin/pkg/trace/trace.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | ot "github.com/opentracing/opentracing-go" 6 | ) 7 | 8 | // Trace holds the tracer and endpoint info 9 | type Trace interface { 10 | plugin.Handler 11 | Tracer() ot.Tracer 12 | } 13 | -------------------------------------------------------------------------------- /dns/plugin/pkg/transport/transport.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | // These transports are supported by CoreDNS. 4 | const ( 5 | DNS = "dns" 6 | TLS = "tls" 7 | GRPC = "grpc" 8 | HTTPS = "https" 9 | ) 10 | 11 | // Port numbers for the various transports. 12 | const ( 13 | // Port is the default port for DNS 14 | Port = "53" 15 | // TLSPort is the default port for DNS-over-TLS. 16 | TLSPort = "853" 17 | // GRPCPort is the default port for DNS-over-gRPC. 18 | GRPCPort = "443" 19 | // HTTPSPort is the default port for DNS-over-HTTPS. 20 | HTTPSPort = "443" 21 | ) 22 | -------------------------------------------------------------------------------- /dns/plugin/pkg/uniq/uniq.go: -------------------------------------------------------------------------------- 1 | // Package uniq keeps track of "thing" that are either "todo" or "done". Multiple 2 | // identical events will only be processed once. 3 | package uniq 4 | 5 | // U keeps track of item to be done. 6 | type U struct { 7 | u map[string]item 8 | } 9 | 10 | type item struct { 11 | state int // either todo or done 12 | f func() error // function to be executed. 13 | } 14 | 15 | // New returns a new initialized U. 16 | func New() U { return U{u: make(map[string]item)} } 17 | 18 | // Set sets function f in U under key. If the key already exists it is not overwritten. 19 | func (u U) Set(key string, f func() error) { 20 | if _, ok := u.u[key]; ok { 21 | return 22 | } 23 | u.u[key] = item{todo, f} 24 | } 25 | 26 | // Unset removes the key. 27 | func (u U) Unset(key string) { 28 | delete(u.u, key) 29 | } 30 | 31 | // ForEach iterates over u and executes f for each element that is 'todo' and sets it to 'done'. 32 | func (u U) ForEach() error { 33 | for k, v := range u.u { 34 | if v.state == todo { 35 | v.f() 36 | } 37 | v.state = done 38 | u.u[k] = v 39 | } 40 | return nil 41 | } 42 | 43 | const ( 44 | todo = 1 45 | done = 2 46 | ) 47 | -------------------------------------------------------------------------------- /dns/plugin/pkg/uniq/uniq_test.go: -------------------------------------------------------------------------------- 1 | package uniq 2 | 3 | import "testing" 4 | 5 | func TestForEach(t *testing.T) { 6 | u, i := New(), 0 7 | u.Set("test", func() error { i++; return nil }) 8 | 9 | u.ForEach() 10 | if i != 1 { 11 | t.Errorf("Failed to executed f for %s", "test") 12 | } 13 | u.ForEach() 14 | if i != 1 { 15 | t.Errorf("Executed f twice instead of once") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /dns/plugin/pkg/up/up_test.go: -------------------------------------------------------------------------------- 1 | package up 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestUp(t *testing.T) { 11 | pr := New() 12 | wg := sync.WaitGroup{} 13 | hits := int32(0) 14 | 15 | upfunc := func() error { 16 | atomic.AddInt32(&hits, 1) 17 | // Sleep tiny amount so that our other pr.Do() calls hit the lock. 18 | time.Sleep(3 * time.Millisecond) 19 | wg.Done() 20 | return nil 21 | } 22 | 23 | pr.Start(5 * time.Millisecond) 24 | defer pr.Stop() 25 | 26 | // These functions AddInt32 to the same hits variable, but we only want to wait when 27 | // upfunc finishes, as that only calls Done() on the waitgroup. 28 | upfuncNoWg := func() error { atomic.AddInt32(&hits, 1); return nil } 29 | wg.Add(1) 30 | pr.Do(upfunc) 31 | pr.Do(upfuncNoWg) 32 | pr.Do(upfuncNoWg) 33 | 34 | wg.Wait() 35 | 36 | h := atomic.LoadInt32(&hits) 37 | if h != 1 { 38 | t.Errorf("Expected hits to be %d, got %d", 1, h) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dns/plugin/pkg/upstream/upstream.go: -------------------------------------------------------------------------------- 1 | // Package upstream abstracts a upstream lookups so that plugins can handle them in an unified way. 2 | package upstream 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | 8 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 9 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/nonwriter" 10 | "github.com/inverse-inc/wireguard-go/dns/request" 11 | 12 | "github.com/miekg/dns" 13 | ) 14 | 15 | // Upstream is used to resolve CNAME or other external targets via CoreDNS itself. 16 | type Upstream struct{} 17 | 18 | // New creates a new Upstream to resolve names using the coredns process. 19 | func New() *Upstream { return &Upstream{} } 20 | 21 | // Lookup routes lookups to our selves or forward to a remote. 22 | func (u *Upstream) Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error) { 23 | server, ok := ctx.Value(dnsserver.Key{}).(*dnsserver.Server) 24 | if !ok { 25 | return nil, fmt.Errorf("no full server is running") 26 | } 27 | 28 | size := state.Size() 29 | do := state.Do() 30 | req := new(dns.Msg) 31 | req.SetQuestion(name, typ) 32 | req.SetEdns0(uint16(size), do) 33 | 34 | nw := nonwriter.New(state.W) 35 | 36 | server.ServeDNS(ctx, nw, req) 37 | 38 | return nw.Msg, nil 39 | } 40 | -------------------------------------------------------------------------------- /dns/plugin/pprof/log_test.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/pprof/setup_test.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestPProf(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | shouldErr bool 13 | }{ 14 | {`pprof`, false}, 15 | {`pprof 1.2.3.4:1234`, false}, 16 | {`pprof :1234`, false}, 17 | {`pprof :1234 -1`, true}, 18 | {`pprof { 19 | }`, false}, 20 | {`pprof /foo`, true}, 21 | {`pprof { 22 | a b 23 | }`, true}, 24 | {`pprof { block 25 | }`, false}, 26 | {`pprof :1234 { 27 | block 20 28 | }`, false}, 29 | {`pprof { 30 | block 20 30 31 | }`, true}, 32 | {`pprof 33 | pprof`, true}, 34 | } 35 | for i, test := range tests { 36 | c := caddy.NewTestController("dns", test.input) 37 | err := setup(c) 38 | if test.shouldErr && err == nil { 39 | t.Errorf("Test %v: Expected error but found nil", i) 40 | } else if !test.shouldErr && err != nil { 41 | t.Errorf("Test %v: Expected no error but found error: %v", i, err) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /dns/plugin/ready/list.go: -------------------------------------------------------------------------------- 1 | package ready 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | "sync" 7 | ) 8 | 9 | // list is a structure that holds the plugins that signals readiness for this server block. 10 | type list struct { 11 | sync.RWMutex 12 | rs []Readiness 13 | names []string 14 | } 15 | 16 | // Append adds a new readiness to l. 17 | func (l *list) Append(r Readiness, name string) { 18 | l.Lock() 19 | defer l.Unlock() 20 | l.rs = append(l.rs, r) 21 | l.names = append(l.names, name) 22 | } 23 | 24 | // Ready return true when all plugins ready, if the returned value is false the string 25 | // contains a comma separated list of plugins that are not ready. 26 | func (l *list) Ready() (bool, string) { 27 | l.RLock() 28 | defer l.RUnlock() 29 | ok := true 30 | s := []string{} 31 | for i, r := range l.rs { 32 | if r == nil { 33 | continue 34 | } 35 | if !r.Ready() { 36 | ok = false 37 | s = append(s, l.names[i]) 38 | } else { 39 | // if ok, this plugin is ready and will not be queried anymore. 40 | l.rs[i] = nil 41 | } 42 | } 43 | if ok { 44 | return true, "" 45 | } 46 | sort.Strings(s) 47 | return false, strings.Join(s, ",") 48 | } 49 | -------------------------------------------------------------------------------- /dns/plugin/ready/readiness.go: -------------------------------------------------------------------------------- 1 | package ready 2 | 3 | // The Readiness interface needs to be implemented by each plugin willing to provide a readiness check. 4 | type Readiness interface { 5 | // Ready is called by ready to see whether the plugin is ready. 6 | Ready() bool 7 | } 8 | -------------------------------------------------------------------------------- /dns/plugin/ready/setup_test.go: -------------------------------------------------------------------------------- 1 | package ready 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestSetupReady(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | shouldErr bool 13 | }{ 14 | {`ready`, false}, 15 | {`ready localhost:1234`, false}, 16 | {`ready localhost:1234 b`, true}, 17 | {`ready bla`, true}, 18 | {`ready bla bla`, true}, 19 | } 20 | 21 | for i, test := range tests { 22 | _, err := parse(caddy.NewTestController("dns", test.input)) 23 | 24 | if test.shouldErr && err == nil { 25 | t.Errorf("Test %d: Expected error but found none for input %s", i, test.input) 26 | } 27 | 28 | if err != nil { 29 | if !test.shouldErr { 30 | t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err) 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dns/plugin/register.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import "github.com/inverse-inc/coredns-caddy" 4 | 5 | // Register registers your plugin with CoreDNS and allows it to be called when the server is running. 6 | func Register(name string, action caddy.SetupFunc) { 7 | caddy.RegisterPlugin(name, caddy.Plugin{ 8 | ServerType: "dns", 9 | Action: action, 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /dns/plugin/reload/log_test.go: -------------------------------------------------------------------------------- 1 | package reload 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/reload/metrics.go: -------------------------------------------------------------------------------- 1 | package reload 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | // Metrics for the reload plugin 11 | var ( 12 | // failedCount is the counter of the number of failed reload attempts. 13 | failedCount = promauto.NewCounter(prometheus.CounterOpts{ 14 | Namespace: plugin.Namespace, 15 | Subsystem: "reload", 16 | Name: "failed_total", 17 | Help: "Counter of the number of failed reload attempts.", 18 | }) 19 | // reloadInfo is record the hash value during reload. 20 | reloadInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ 21 | Namespace: plugin.Namespace, 22 | Subsystem: "reload", 23 | Name: "version_info", 24 | Help: "A metric with a constant '1' value labeled by hash, and value which type of hash generated.", 25 | }, []string{"hash", "value"}) 26 | ) 27 | -------------------------------------------------------------------------------- /dns/plugin/rewrite/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package rewrite 4 | 5 | import ( 6 | "github.com/inverse-inc/coredns-caddy" 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/fuzz" 8 | ) 9 | 10 | // Fuzz fuzzes rewrite. 11 | func Fuzz(data []byte) int { 12 | c := caddy.NewTestController("dns", "rewrite edns0 subnet set 24 56") 13 | rules, err := rewriteParse(c) 14 | if err != nil { 15 | return 0 16 | } 17 | r := Rewrite{Rules: rules} 18 | 19 | return fuzz.Do(r, data) 20 | } 21 | -------------------------------------------------------------------------------- /dns/plugin/rewrite/log_test.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/rewrite/setup.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import ( 4 | "github.com/inverse-inc/coredns-caddy" 5 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | ) 8 | 9 | func init() { plugin.Register("rewrite", setup) } 10 | 11 | func setup(c *caddy.Controller) error { 12 | rewrites, err := rewriteParse(c) 13 | if err != nil { 14 | return plugin.Error("rewrite", err) 15 | } 16 | 17 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 18 | return Rewrite{Next: next, Rules: rewrites} 19 | }) 20 | 21 | return nil 22 | } 23 | 24 | func rewriteParse(c *caddy.Controller) ([]Rule, error) { 25 | var rules []Rule 26 | 27 | for c.Next() { 28 | args := c.RemainingArgs() 29 | if len(args) < 2 { 30 | // Handles rules out of nested instructions, i.e. the ones enclosed in curly brackets 31 | for c.NextBlock() { 32 | args = append(args, c.Val()) 33 | } 34 | } 35 | rule, err := newRule(args...) 36 | if err != nil { 37 | return nil, err 38 | } 39 | rules = append(rules, rule) 40 | } 41 | return rules, nil 42 | } 43 | -------------------------------------------------------------------------------- /dns/plugin/rewrite/wire.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | ) 9 | 10 | // ipToWire writes IP address to wire/binary format, 4 or 16 bytes depends on IPV4 or IPV6. 11 | func ipToWire(family int, ipAddr string) ([]byte, error) { 12 | 13 | switch family { 14 | case 1: 15 | return net.ParseIP(ipAddr).To4(), nil 16 | case 2: 17 | return net.ParseIP(ipAddr).To16(), nil 18 | } 19 | return nil, fmt.Errorf("invalid IP address family (i.e. version) %d", family) 20 | } 21 | 22 | // uint16ToWire writes unit16 to wire/binary format 23 | func uint16ToWire(data uint16) []byte { 24 | buf := make([]byte, 2) 25 | binary.BigEndian.PutUint16(buf, uint16(data)) 26 | return buf 27 | } 28 | 29 | // portToWire writes port to wire/binary format, 2 bytes 30 | func portToWire(portStr string) ([]byte, error) { 31 | 32 | port, err := strconv.ParseUint(portStr, 10, 16) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return uint16ToWire(uint16(port)), nil 37 | } 38 | -------------------------------------------------------------------------------- /dns/plugin/root/README.md: -------------------------------------------------------------------------------- 1 | # root 2 | 3 | ## Name 4 | 5 | *root* - simply specifies the root of where to find (zone) files. 6 | 7 | ## Description 8 | 9 | The default root is the current working directory of CoreDNS. The *root* plugin allows you to change 10 | this. A relative root path is relative to the current working directory. 11 | 12 | This plugin can only be used once per Server Block. 13 | 14 | ## Syntax 15 | 16 | ~~~ txt 17 | root PATH 18 | ~~~ 19 | 20 | **PATH** is the directory to set as CoreDNS' root. 21 | 22 | ## Examples 23 | 24 | Serve zone data (when the *file* plugin is used) from `/etc/coredns/zones`: 25 | 26 | ~~~ corefile 27 | . { 28 | root /etc/coredns/zones 29 | } 30 | ~~~ 31 | -------------------------------------------------------------------------------- /dns/plugin/root/log_test.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/root/root.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 8 | "github.com/inverse-inc/wireguard-go/dns/plugin" 9 | clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 10 | ) 11 | 12 | var log = clog.NewWithPlugin("root") 13 | 14 | func init() { plugin.Register("root", setup) } 15 | 16 | func setup(c *caddy.Controller) error { 17 | config := dnsserver.GetConfig(c) 18 | 19 | for c.Next() { 20 | if !c.NextArg() { 21 | return plugin.Error("root", c.ArgErr()) 22 | } 23 | config.Root = c.Val() 24 | } 25 | 26 | // Check if root path exists 27 | _, err := os.Stat(config.Root) 28 | if err != nil { 29 | if os.IsNotExist(err) { 30 | // Allow this, because the folder might appear later. 31 | // But make sure the user knows! 32 | log.Warningf("Root path does not exist: %s", config.Root) 33 | } else { 34 | return plugin.Error("root", c.Errf("unable to access root path '%s': %v", config.Root, err)) 35 | } 36 | } 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /dns/plugin/route53/log_test.go: -------------------------------------------------------------------------------- 1 | package route53 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/secondary/log_test.go: -------------------------------------------------------------------------------- 1 | package secondary 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/secondary/secondary.go: -------------------------------------------------------------------------------- 1 | // Package secondary implements a secondary plugin. 2 | package secondary 3 | 4 | import "github.com/inverse-inc/wireguard-go/dns/plugin/file" 5 | 6 | // Secondary implements a secondary plugin that allows CoreDNS to retrieve (via AXFR) 7 | // zone information from a primary server. 8 | type Secondary struct { 9 | file.File 10 | } 11 | -------------------------------------------------------------------------------- /dns/plugin/sign/dnssec.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "github.com/miekg/dns" 5 | ) 6 | 7 | func (p Pair) signRRs(rrs []dns.RR, signerName string, ttl, incep, expir uint32) (*dns.RRSIG, error) { 8 | rrsig := &dns.RRSIG{ 9 | Hdr: dns.RR_Header{Rrtype: dns.TypeRRSIG, Ttl: ttl}, 10 | Algorithm: p.Public.Algorithm, 11 | SignerName: signerName, 12 | KeyTag: p.KeyTag, 13 | OrigTtl: ttl, 14 | Inception: incep, 15 | Expiration: expir, 16 | } 17 | 18 | e := rrsig.Sign(p.Private, rrs) 19 | return rrsig, e 20 | } 21 | -------------------------------------------------------------------------------- /dns/plugin/sign/file_test.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/miekg/dns" 8 | ) 9 | 10 | func TestFileParse(t *testing.T) { 11 | f, err := os.Open("testdata/db.miek.nl") 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | z, err := Parse(f, "miek.nl.", "testdata/db.miek.nl") 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | s := &Signer{ 20 | directory: ".", 21 | signedfile: "db.miek.nl.test", 22 | } 23 | 24 | s.write(z) 25 | defer os.Remove("db.miek.nl.test") 26 | 27 | f, err = os.Open("db.miek.nl.test") 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | z, err = Parse(f, "miek.nl.", "db.miek.nl.test") 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | if x := z.Apex.SOA.Header().Name; x != "miek.nl." { 36 | t.Errorf("Expected SOA name to be %s, got %s", x, "miek.nl.") 37 | } 38 | apex, _ := z.Search("miek.nl.") 39 | key := apex.Type(dns.TypeDNSKEY) 40 | if key != nil { 41 | t.Errorf("Expected no DNSKEYs, but got %d", len(key)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /dns/plugin/sign/log_test.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/sign/nsec.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/file" 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/file/tree" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | // names returns the elements of the zone in nsec order. 13 | func names(origin string, z *file.Zone) []string { 14 | // There will also be apex records other than NS and SOA (who are kept separate), as we 15 | // are adding DNSKEY and CDS/CDNSKEY records in the apex *before* we sign. 16 | n := []string{} 17 | z.AuthWalk(func(e *tree.Elem, _ map[uint16][]dns.RR, auth bool) error { 18 | if !auth { 19 | return nil 20 | } 21 | n = append(n, e.Name()) 22 | return nil 23 | }) 24 | return n 25 | } 26 | 27 | // NSEC returns an NSEC record according to name, next, ttl and bitmap. Note that the bitmap is sorted before use. 28 | func NSEC(name, next string, ttl uint32, bitmap []uint16) *dns.NSEC { 29 | sort.Slice(bitmap, func(i, j int) bool { return bitmap[i] < bitmap[j] }) 30 | 31 | return &dns.NSEC{ 32 | Hdr: dns.RR_Header{Name: name, Ttl: ttl, Rrtype: dns.TypeNSEC, Class: dns.ClassINET}, 33 | NextDomain: next, 34 | TypeBitMap: bitmap, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /dns/plugin/sign/nsec_test.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/inverse-inc/wireguard-go/dns/plugin/file" 8 | ) 9 | 10 | func TestNames(t *testing.T) { 11 | f, err := os.Open("testdata/db.miek.nl_ns") 12 | if err != nil { 13 | t.Error(err) 14 | } 15 | z, err := file.Parse(f, "db.miek.nl_ns", "miek.nl", 0) 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | 20 | names := names("miek.nl.", z) 21 | expected := []string{"miek.nl.", "child.miek.nl.", "www.miek.nl."} 22 | for i := range names { 23 | if names[i] != expected[i] { 24 | t.Errorf("Expected %s, got %s", expected[i], names[i]) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /dns/plugin/sign/testdata/Kmiek.nl.+013+59725.key: -------------------------------------------------------------------------------- 1 | ; This is a key-signing key, keyid 59725, for miek.nl. 2 | ; Created: 20190709192036 (Tue Jul 9 20:20:36 2019) 3 | ; Publish: 20190709192036 (Tue Jul 9 20:20:36 2019) 4 | ; Activate: 20190709192036 (Tue Jul 9 20:20:36 2019) 5 | miek.nl. IN DNSKEY 257 3 13 sfzRg5nDVxbeUc51su4MzjgwpOpUwnuu81SlRHqJuXe3SOYOeypR69tZ 52XLmE56TAmPHsiB8Rgk+NTpf0o1Cw== 6 | -------------------------------------------------------------------------------- /dns/plugin/sign/testdata/Kmiek.nl.+013+59725.private: -------------------------------------------------------------------------------- 1 | Private-key-format: v1.3 2 | Algorithm: 13 (ECDSAP256SHA256) 3 | PrivateKey: rm7EdHRca//6xKpJzeoLt/mrfgQnltJ0WpQGtOG59yo= 4 | Created: 20190709192036 5 | Publish: 20190709192036 6 | Activate: 20190709192036 7 | -------------------------------------------------------------------------------- /dns/plugin/sign/testdata/db.miek.nl: -------------------------------------------------------------------------------- 1 | $TTL 30M 2 | $ORIGIN miek.nl. 3 | @ IN SOA linode.atoom.net. miek.miek.nl. ( 1282630060 4H 1H 7D 4H ) 4 | IN NS linode.atoom.net. 5 | IN MX 1 aspmx.l.google.com. 6 | IN AAAA 2a01:7e00::f03c:91ff:fe79:234c 7 | IN DNSKEY 257 3 13 sfzRg5nDVxbeUc51su4MzjgwpOpUwnuu81SlRHqJuXe3SOYOeypR69tZ52XLmE56TAmPHsiB8Rgk+NTpf0o1Cw== 8 | 9 | a IN AAAA 2a01:7e00::f03c:91ff:fe79:234c 10 | www IN CNAME a 11 | 12 | 13 | bla IN NS ns1.bla.com. 14 | ns3.blaaat.miek.nl. IN AAAA ::1 ; non-glue, should be signed. 15 | ; in baliwick nameserver that requires glue, should not be signed 16 | bla IN NS ns2.bla.miek.nl. 17 | ns2.bla.miek.nl. IN A 127.0.0.1 18 | -------------------------------------------------------------------------------- /dns/plugin/sign/testdata/db.miek.nl_ns: -------------------------------------------------------------------------------- 1 | $TTL 30M 2 | $ORIGIN miek.nl. 3 | @ IN SOA linode.atoom.net. miek.miek.nl. ( 1282630060 4H 1H 7D 4H ) 4 | NS linode.atoom.net. 5 | DNSKEY 257 3 13 sfzRg5nDVxbeUc51su4MzjgwpOpUwnuu81SlRHqJuXe3SOYOeypR69tZ52XLmE56TAmPHsiB8Rgk+NTpf0o1Cw== 6 | 7 | www AAAA ::1 8 | child NS ns.child 9 | ns.child AAAA ::1 10 | child DS 34385 13 2 fc7397c77afbccb6742fcff19c7b1410d0044661e7085fc200ae1ab3d15a5842 11 | -------------------------------------------------------------------------------- /dns/plugin/template/log_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/template/metrics.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | "github.com/prometheus/client_golang/prometheus/promauto" 8 | ) 9 | 10 | var ( 11 | // templateMatchesCount is the counter of template regex matches. 12 | templateMatchesCount = promauto.NewCounterVec(prometheus.CounterOpts{ 13 | Namespace: plugin.Namespace, 14 | Subsystem: "template", 15 | Name: "matches_total", 16 | Help: "Counter of template regex matches.", 17 | }, []string{"server", "zone", "class", "type"}) 18 | // templateFailureCount is the counter of go template failures. 19 | templateFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{ 20 | Namespace: plugin.Namespace, 21 | Subsystem: "template", 22 | Name: "template_failures_total", 23 | Help: "Counter of go template failures.", 24 | }, []string{"server", "zone", "class", "type", "section", "template"}) 25 | // templateRRFailureCount is the counter of mis-templated RRs. 26 | templateRRFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{ 27 | Namespace: plugin.Namespace, 28 | Subsystem: "template", 29 | Name: "rr_failures_total", 30 | Help: "Counter of mis-templated RRs.", 31 | }, []string{"server", "zone", "class", "type", "section", "template"}) 32 | ) 33 | -------------------------------------------------------------------------------- /dns/plugin/test/doc.go: -------------------------------------------------------------------------------- 1 | // Package test contains helper functions for writing plugin tests. 2 | package test 3 | -------------------------------------------------------------------------------- /dns/plugin/test/file_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import "testing" 4 | 5 | func TestTempFile(t *testing.T) { 6 | _, f, e := TempFile(".", "test") 7 | if e != nil { 8 | t.Fatalf("Failed to create temp file: %s", e) 9 | } 10 | defer f() 11 | } 12 | -------------------------------------------------------------------------------- /dns/plugin/tls/log_test.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/tls/test_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDPzCCAiegAwIBAgIJAPjCWTu1wGapMA0GCSqGSIb3DQEBCwUAMDUxCzAJBgNV 3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQKDAhJbmZvYmxveDAg 4 | Fw0xOTA1MTEwMDI3NDRaGA8yMTE5MDQxNzAwMjc0NFowNTELMAkGA1UEBhMCVVMx 5 | EzARBgNVBAgMCkNhbGlmb3JuaWExETAPBgNVBAoMCEluZm9ibG94MIIBIjANBgkq 6 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAYiw1UjlYj+nITRUlj5hA7j8U2qWcyN 7 | YcDfqQnt173Z8yR7NJokqt3Bd3PlrBZS2XtYSNohxRr4qeJu/g7UBre/fSEU/ZOM 8 | Gl7NjBGKQEymJ0d8rBg52iiGNwU+ERI9pcQRA6DCEjVbOmjDiUd5yzuVotG/Sxep 9 | GUJ2puJ0p0gWCMEL9sdqY6HHd/hdj6B6+u2xD9NUCkX9pLC7CPFJHnP0vLO4WIWL 10 | z5C7yzpeLO9r7Nfnu+2HcRLmuFZVPNxkMq7UymqR1w5ZYJQ5p9E7pyxDVXxHnTqQ 11 | yLaAS2/9umrOwVnD1NaN3OdAhDedXbH0cF08GcIQD9rnlkLMW4CKtwIDAQABo1Aw 12 | TjAdBgNVHQ4EFgQUHcxJPBmHF0nSv+FJJI/kwrSThf8wHwYDVR0jBBgwFoAUHcxJ 13 | PBmHF0nSv+FJJI/kwrSThf8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC 14 | AQEAByItgyhlXDv2wnnMVXHHlUCbsKCOtBJZ8EumvKjeOx5G4gqJpQIQPNeBv1Od 15 | QT7d15HfT7RQqHSL0uAoGuNuyGjZGWWbLMkVt8T0tXY2v9Dd8eWC/lFaaA0vkqTG 16 | GpADSmH+SoFAdPPcYN/sXmEHvZcIQ0wUxuF48ZMwOh7ZOcrZggxlA9+BKHU4fO03 17 | o7krzpQZQmEDXNN8bt1R0DIhVADw/G2oJAzK0LGhh4eu6hj6k/cAWS6ujRBGqN0Z 18 | fURCrMEyjzbNybhkU1KqSr7eSJOWkl4UJ5Ns/dt9/yw2BBrKH3Mijch7UA8mlbEE 19 | 29M28u2W7GMXLSSwmtCqDBRNhg== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /dns/plugin/tls/test_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDPzCCAiegAwIBAgIJAPezzzshGRiTMA0GCSqGSIb3DQEBCwUAMDUxCzAJBgNV 3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQKDAhJbmZvYmxveDAg 4 | Fw0xOTA1MTEwMDI2MjNaGA8yMTE5MDQxNzAwMjYyM1owNTELMAkGA1UEBhMCVVMx 5 | EzARBgNVBAgMCkNhbGlmb3JuaWExETAPBgNVBAoMCEluZm9ibG94MIIBIjANBgkq 6 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAYiw1UjlYj+nITRUlj5hA7j8U2qWcyN 7 | YcDfqQnt173Z8yR7NJokqt3Bd3PlrBZS2XtYSNohxRr4qeJu/g7UBre/fSEU/ZOM 8 | Gl7NjBGKQEymJ0d8rBg52iiGNwU+ERI9pcQRA6DCEjVbOmjDiUd5yzuVotG/Sxep 9 | GUJ2puJ0p0gWCMEL9sdqY6HHd/hdj6B6+u2xD9NUCkX9pLC7CPFJHnP0vLO4WIWL 10 | z5C7yzpeLO9r7Nfnu+2HcRLmuFZVPNxkMq7UymqR1w5ZYJQ5p9E7pyxDVXxHnTqQ 11 | yLaAS2/9umrOwVnD1NaN3OdAhDedXbH0cF08GcIQD9rnlkLMW4CKtwIDAQABo1Aw 12 | TjAdBgNVHQ4EFgQUHcxJPBmHF0nSv+FJJI/kwrSThf8wHwYDVR0jBBgwFoAUHcxJ 13 | PBmHF0nSv+FJJI/kwrSThf8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC 14 | AQEAQyN9nLImdtufuSjXcrCJ3alt/vffHJIzlPgDsNw8+tjI7aRX7CzuurOOEQUC 15 | fJ9A6O+dat5k5yqVb9hDcD42HXtOjRQDYpQ6dOGirLFThIFSMC/7RiqHk0YtxojM 16 | ZNBbgXo4o1d+P9b25oc/+pRDzlOvqNL7IzW/LDHnJ4j6tBNguujCB5QFUF5dOa1z 17 | UR5rupMvv2KpEgRcfW/d3kwcAxH9nI0SHKJenhtweyajUgInK88TC+aT4909c2XA 18 | EADYyWxj1DMz3/sMpvGegHsfTPegNoDgz2yEKdu53dr4BUpF6E+eoCX9Hv78SWH3 19 | /rAlkbffzCL5d+I8y0jzEpLEqA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /dns/plugin/trace/log_test.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/whoami/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package whoami 4 | 5 | import ( 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/fuzz" 7 | ) 8 | 9 | // Fuzz fuzzes cache. 10 | func Fuzz(data []byte) int { 11 | w := Whoami{} 12 | return fuzz.Do(w, data) 13 | } 14 | -------------------------------------------------------------------------------- /dns/plugin/whoami/log_test.go: -------------------------------------------------------------------------------- 1 | package whoami 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/plugin/whoami/setup.go: -------------------------------------------------------------------------------- 1 | package whoami 2 | 3 | import ( 4 | "github.com/inverse-inc/coredns-caddy" 5 | "github.com/inverse-inc/wireguard-go/dns/core/dnsserver" 6 | "github.com/inverse-inc/wireguard-go/dns/plugin" 7 | ) 8 | 9 | func init() { plugin.Register("whoami", setup) } 10 | 11 | func setup(c *caddy.Controller) error { 12 | c.Next() // 'whoami' 13 | if c.NextArg() { 14 | return plugin.Error("whoami", c.ArgErr()) 15 | } 16 | 17 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 18 | return Whoami{} 19 | }) 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /dns/plugin/whoami/setup_test.go: -------------------------------------------------------------------------------- 1 | package whoami 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/coredns-caddy" 7 | ) 8 | 9 | func TestSetup(t *testing.T) { 10 | c := caddy.NewTestController("dns", `whoami`) 11 | if err := setup(c); err != nil { 12 | t.Fatalf("Expected no errors, but got: %v", err) 13 | } 14 | 15 | c = caddy.NewTestController("dns", `whoami example.org`) 16 | if err := setup(c); err == nil { 17 | t.Fatalf("Expected errors, but got: %v", err) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dns/request/edns0.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/edns" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func supportedOptions(o []dns.EDNS0) []dns.EDNS0 { 10 | var supported = make([]dns.EDNS0, 0, 3) 11 | // For as long as possible try avoid looking up in the map, because that need an Rlock. 12 | for _, opt := range o { 13 | switch code := opt.Option(); code { 14 | case dns.EDNS0NSID: 15 | fallthrough 16 | case dns.EDNS0EXPIRE: 17 | fallthrough 18 | case dns.EDNS0COOKIE: 19 | fallthrough 20 | case dns.EDNS0TCPKEEPALIVE: 21 | fallthrough 22 | case dns.EDNS0PADDING: 23 | supported = append(supported, opt) 24 | default: 25 | if edns.SupportedOption(code) { 26 | supported = append(supported, opt) 27 | } 28 | } 29 | } 30 | return supported 31 | } 32 | -------------------------------------------------------------------------------- /dns/request/writer.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import "github.com/miekg/dns" 4 | 5 | // ScrubWriter will, when writing the message, call scrub to make it fit the client's buffer. 6 | type ScrubWriter struct { 7 | dns.ResponseWriter 8 | req *dns.Msg // original request 9 | } 10 | 11 | // NewScrubWriter returns a new and initialized ScrubWriter. 12 | func NewScrubWriter(req *dns.Msg, w dns.ResponseWriter) *ScrubWriter { return &ScrubWriter{w, req} } 13 | 14 | // WriteMsg overrides the default implementation of the underlying dns.ResponseWriter and calls 15 | // scrub on the message m and will then write it to the client. 16 | func (s *ScrubWriter) WriteMsg(m *dns.Msg) error { 17 | state := Request{Req: s.req, W: s.ResponseWriter} 18 | state.SizeAndDo(m) 19 | state.Scrub(m) 20 | return s.ResponseWriter.WriteMsg(m) 21 | } 22 | -------------------------------------------------------------------------------- /dns/test/chaos_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | // Plug in CoreDNS, needed for AppVersion and AppName in this test. 7 | "github.com/inverse-inc/coredns-caddy" 8 | _ "github.com/inverse-inc/wireguard-go/dns/coremain" 9 | 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | func TestChaos(t *testing.T) { 14 | corefile := `.:0 { 15 | chaos 16 | }` 17 | 18 | i, udp, _, err := CoreDNSServerAndPorts(corefile) 19 | if err != nil { 20 | t.Fatalf("Could not get CoreDNS serving instance: %s", err) 21 | } 22 | defer i.Stop() 23 | 24 | m := new(dns.Msg) 25 | m.SetQuestion("version.bind.", dns.TypeTXT) 26 | m.Question[0].Qclass = dns.ClassCHAOS 27 | 28 | resp, err := dns.Exchange(m, udp) 29 | if err != nil { 30 | t.Fatalf("Expected to receive reply, but didn't: %v", err) 31 | } 32 | chTxt := resp.Answer[0].(*dns.TXT).Txt[0] 33 | version := caddy.AppName + "-" + caddy.AppVersion 34 | if chTxt != version { 35 | t.Fatalf("Expected version to be %s, got %s", version, chTxt) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /dns/test/doc.go: -------------------------------------------------------------------------------- 1 | // Package test contains function and types useful for writing tests. 2 | package test 3 | -------------------------------------------------------------------------------- /dns/test/edns0_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestEDNS0(t *testing.T) { 10 | corefile := `.:0 { 11 | whoami 12 | }` 13 | 14 | i, udp, _, err := CoreDNSServerAndPorts(corefile) 15 | if err != nil { 16 | t.Fatalf("Could not get CoreDNS serving instance: %s", err) 17 | } 18 | defer i.Stop() 19 | 20 | m := new(dns.Msg) 21 | m.SetQuestion("example.org.", dns.TypeSOA) 22 | m.SetEdns0(4096, true) 23 | 24 | resp, err := dns.Exchange(m, udp) 25 | if err != nil { 26 | t.Fatalf("Expected to receive reply, but didn't: %v", err) 27 | } 28 | opt := resp.Extra[len(resp.Extra)-1] 29 | if opt.Header().Rrtype != dns.TypeOPT { 30 | t.Errorf("Last RR must be OPT record") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dns/test/example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | const exampleOrg = `; example.org test file 4 | $TTL 3600 5 | @ IN SOA sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600 6 | @ IN NS b.iana-servers.net. 7 | @ IN NS a.iana-servers.net. 8 | @ IN A 127.0.0.1 9 | @ IN A 127.0.0.2 10 | short 1 IN A 127.0.0.3 11 | 12 | *.w 3600 IN TXT "Wildcard" 13 | a.b.c.w IN TXT "Not a wildcard" 14 | cname IN CNAME www.example.net. 15 | service IN SRV 8080 10 10 @ 16 | ` 17 | -------------------------------------------------------------------------------- /dns/test/file_srv_additional_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 7 | 8 | "github.com/miekg/dns" 9 | ) 10 | 11 | func TestZoneSRVAdditional(t *testing.T) { 12 | t.Parallel() 13 | 14 | name, rm, err := test.TempFile(".", exampleOrg) 15 | if err != nil { 16 | t.Fatalf("Failed to create zone: %s", err) 17 | } 18 | defer rm() 19 | 20 | // Corefile with for example without proxy section. 21 | corefile := `example.org:0 { 22 | file ` + name + ` 23 | }` 24 | 25 | i, udp, _, err := CoreDNSServerAndPorts(corefile) 26 | if err != nil { 27 | t.Fatalf("Could not get CoreDNS serving instance: %s", err) 28 | } 29 | defer i.Stop() 30 | 31 | m := new(dns.Msg) 32 | m.SetQuestion("service.example.org.", dns.TypeSRV) 33 | resp, err := dns.Exchange(m, udp) 34 | if err != nil { 35 | t.Fatalf("Expected to receive reply, but didn't: %s", err) 36 | } 37 | 38 | // There should be 2 A records in the additional section. 39 | if len(resp.Extra) != 2 { 40 | t.Fatalf("Expected 2 RR in additional section got %d", len(resp.Extra)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /dns/test/file_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/inverse-inc/wireguard-go/dns/plugin/test" 7 | ) 8 | 9 | func TestTempFile(t *testing.T) { 10 | t.Parallel() 11 | _, f, e := test.TempFile(".", "test") 12 | if e != nil { 13 | t.Fatalf("Failed to create temp file: %s", e) 14 | } 15 | defer f() 16 | } 17 | -------------------------------------------------------------------------------- /dns/test/fuzz_corefile.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package test 4 | 5 | // Fuzz fuzzes a corefile. 6 | func Fuzz(data []byte) int { 7 | _, _, _, err := CoreDNSServerAndPorts(string(data)) 8 | if err != nil { 9 | return 1 10 | } 11 | return 0 12 | } 13 | -------------------------------------------------------------------------------- /dns/test/hosts_file_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestHostsInlineLookup(t *testing.T) { 10 | corefile := `example.org:0 { 11 | hosts highly_unlikely_to_exist_hosts_file example.org { 12 | 10.0.0.1 example.org 13 | fallthrough 14 | } 15 | }` 16 | 17 | i, udp, _, err := CoreDNSServerAndPorts(corefile) 18 | if err != nil { 19 | t.Fatalf("Could not get CoreDNS serving instance: %s", err) 20 | } 21 | defer i.Stop() 22 | 23 | m := new(dns.Msg) 24 | m.SetQuestion("example.org.", dns.TypeA) 25 | resp, err := dns.Exchange(m, udp) 26 | if err != nil { 27 | t.Fatal("Expected to receive reply, but didn't") 28 | } 29 | // expect answer section with A record in it 30 | if len(resp.Answer) == 0 { 31 | t.Fatal("Expected to at least one RR in the answer section, got none") 32 | } 33 | if resp.Answer[0].Header().Rrtype != dns.TypeA { 34 | t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype) 35 | } 36 | if resp.Answer[0].(*dns.A).A.String() != "10.0.0.1" { 37 | t.Errorf("Expected 10.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dns/test/log_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import clog "github.com/inverse-inc/wireguard-go/dns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /dns/test/miek_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | const miekNL = `; miek.nl test zone 4 | $TTL 30M 5 | $ORIGIN miek.nl. 6 | @ IN SOA linode.atoom.net. miek.miek.nl. ( 7 | 1282630059 ; Serial 8 | 4H ; Refresh 9 | 1H ; Retry 10 | 7D ; Expire 11 | 4H ) ; Negative Cache TTL 12 | IN NS linode.atoom.net. 13 | IN NS ns-ext.nlnetlabs.nl. 14 | IN NS omval.tednet.nl. 15 | IN NS ext.ns.whyscream.net. 16 | 17 | IN MX 1 aspmx.l.google.com. 18 | IN MX 5 alt1.aspmx.l.google.com. 19 | IN MX 5 alt2.aspmx.l.google.com. 20 | IN MX 10 aspmx2.googlemail.com. 21 | IN MX 10 aspmx3.googlemail.com. 22 | 23 | IN A 176.58.119.54 24 | IN AAAA 2a01:7e00::f03c:91ff:fe79:234c 25 | IN HINFO "Please stop asking for ANY" "See draft-ietf-dnsop-refuse-any" 26 | 27 | a IN A 176.58.119.54 28 | IN AAAA 2a01:7e00::f03c:91ff:fe79:234c 29 | www IN CNAME a 30 | archive IN CNAME a 31 | ` 32 | -------------------------------------------------------------------------------- /dns/test/no_plugins_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestNoPlugins(t *testing.T) { 10 | corefile := `example.org:0 { 11 | }` 12 | 13 | i, udp, _, err := CoreDNSServerAndPorts(corefile) 14 | if err != nil { 15 | t.Fatalf("Could not get CoreDNS serving instance: %s", err) 16 | } 17 | defer i.Stop() 18 | 19 | m := new(dns.Msg) 20 | m.SetQuestion("example.org.", dns.TypeA) 21 | resp, err := dns.Exchange(m, udp) 22 | if err != nil { 23 | t.Fatalf("Expected to receive reply, but didn't: %v", err) 24 | } 25 | if resp.Rcode != dns.RcodeRefused { 26 | t.Fatalf("Expected rcode to be %d, got %d", dns.RcodeRefused, resp.Rcode) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /dns/test/reverse_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func TestReverseCorefile(t *testing.T) { 10 | corefile := `10.0.0.0/24:0 { 11 | whoami 12 | }` 13 | 14 | i, err := CoreDNSServer(corefile) 15 | if err != nil { 16 | t.Fatalf("Could not get CoreDNS serving instance: %s", err) 17 | } 18 | defer i.Stop() 19 | 20 | udp, _ := CoreDNSServerPorts(i, 0) 21 | if udp == "" { 22 | t.Fatalf("Could not get UDP listening port") 23 | } 24 | 25 | m := new(dns.Msg) 26 | m.SetQuestion("17.0.0.10.in-addr.arpa.", dns.TypePTR) 27 | resp, err := dns.Exchange(m, udp) 28 | if err != nil { 29 | t.Fatal("Expected to receive reply, but didn't") 30 | } 31 | 32 | if len(resp.Extra) != 2 { 33 | t.Fatal("Expected to at least two RRs in the extra section, got none") 34 | } 35 | // Second one is SRV, first one can be A or AAAA depending on system. 36 | if resp.Extra[1].Header().Rrtype != dns.TypeSRV { 37 | t.Errorf("Expected RR to SRV, got: %d", resp.Extra[1].Header().Rrtype) 38 | } 39 | if resp.Extra[1].Header().Name != "_udp.17.0.0.10.in-addr.arpa." { 40 | t.Errorf("Expected _udp.17.0.0.10.in-addr.arpa. got: %s", resp.Extra[1].Header().Name) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/bridge/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/binary" 6 | "fmt" 7 | "net" 8 | "time" 9 | 10 | "github.com/davecgh/go-spew/spew" 11 | "github.com/inverse-inc/packetfence/go/sharedutils" 12 | "github.com/inverse-inc/wireguard-go/ztn" 13 | ) 14 | 15 | func main() { 16 | c := ztn.ConnectPeerServiceClient("127.0.0.1:6970") 17 | res, err := c.SetupForwarding(context.Background(), &ztn.SetupForwardingRequest{Name: "testing", PeerConnectionType: ztn.ConnectionTypeLAN}) 18 | sharedutils.CheckError(err) 19 | 20 | spew.Dump(res) 21 | 22 | addr, err := net.ResolveUDPAddr("udp4", res.Raddr) 23 | sharedutils.CheckError(err) 24 | 25 | time.Sleep(2 * time.Second) 26 | 27 | conn, err := net.DialUDP("udp4", nil, addr) 28 | sharedutils.CheckError(err) 29 | 30 | pkt := make([]byte, 1024) 31 | binary.PutUvarint(pkt, res.Id) 32 | binary.PutUvarint(pkt[binary.MaxVarintLen64:], res.Token) 33 | binary.PutUvarint(pkt[2*binary.MaxVarintLen64:], ztn.MsgNcBindPeerBridge) 34 | 35 | conn.Write(pkt) 36 | 37 | for { 38 | buf := make([]byte, 1024) 39 | 40 | n, raddr, err := conn.ReadFromUDP(buf) 41 | if err != nil { 42 | return 43 | } 44 | buf = buf[:n] 45 | 46 | fmt.Printf("Received %s from %s on local addr %s\n", string(buf), raddr, conn.LocalAddr()) 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /guiwrapper/.gitignore: -------------------------------------------------------------------------------- 1 | guiwrapper 2 | -------------------------------------------------------------------------------- /guiwrapper/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inverse-inc/wireguard-go/2b8e069b83ae73eb256bf12ed9f10522449e2047/guiwrapper/logo.ico -------------------------------------------------------------------------------- /guiwrapper/password_entry.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fyne.io/fyne" 5 | "fyne.io/fyne/widget" 6 | ) 7 | 8 | type PasswordField struct { 9 | widget.Entry 10 | onEnter func() 11 | } 12 | 13 | func NewPasswordField() *PasswordField { 14 | e := &PasswordField{} 15 | e.ExtendBaseWidget(e) 16 | e.Password = true 17 | return e 18 | } 19 | 20 | func (p *PasswordField) KeyUp(k *fyne.KeyEvent) { 21 | p.Entry.KeyUp(k) 22 | switch k.Name { 23 | case fyne.KeyReturn: 24 | p.onEnter() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /guiwrapper/util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func rowsToColumns(headings []string, rows [][]string) [][]string { 4 | columns := make([][]string, len(headings)) 5 | for _, row := range rows { 6 | for colK := range row { 7 | columns[colK] = append(columns[colK], row[colK]) 8 | } 9 | } 10 | return columns 11 | } 12 | -------------------------------------------------------------------------------- /ipc/winpipe/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2005 Microsoft 4 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 5 | */ 6 | 7 | package winpipe 8 | 9 | //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go pipe.go file.go 10 | -------------------------------------------------------------------------------- /macos_installer/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.dmg 3 | .deps/ 4 | -------------------------------------------------------------------------------- /macos_installer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleGetInfoString 6 | PacketFence Zero Trust Client 7 | CFBundleExecutable 8 | PacketFenceZeroTrustClient 9 | CFBundleIdentifier 10 | ca.inverse.packetfence-zero-trust-client 11 | CFBundleName 12 | PacketFence Zero Trust Client 13 | CFBundleIconFile 14 | packetfence-zero-trust-client.icns 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundlePackageType 20 | APPL 21 | IFMajorVersion 22 | 0 23 | IFMinorVersion 24 | 1 25 | LSUIElement 26 | 1 27 | LSMinimumSystemVersion 28 | 10.10 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /macos_installer/README.md: -------------------------------------------------------------------------------- 1 | 2 | # How to bump the version 3 | 4 | Edit macos_installer/Info.plist and change the string value below the property CFBundleShortVersionString 5 | 6 | # How to build the Mac OS agent 7 | 8 | NOTE: Although you can build the wireguard-go binary on any platform, you can only build the DMG on Mac OS itself. Also, the guiwrapper will not build if you're not on Mac OS. 9 | 10 | First you need to build the binaries using the build script in the parent directory: 11 | 12 | ``` 13 | cd .. 14 | ./build_macos.sh 15 | ``` 16 | 17 | Next, you need to build the DMG: 18 | 19 | ``` 20 | cd macos_installer 21 | ./build.sh 22 | ``` 23 | 24 | Once the build is complete, you will find the DMG in dist/ 25 | 26 | The build will also attempt to sign the code while building it using a key in your Mac OS keystore named 'Inverse' 27 | 28 | -------------------------------------------------------------------------------- /macos_installer/installer_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inverse-inc/wireguard-go/2b8e069b83ae73eb256bf12ed9f10522449e2047/macos_installer/installer_background.png -------------------------------------------------------------------------------- /macos_installer/packetfence-zero-trust-client.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inverse-inc/wireguard-go/2b8e069b83ae73eb256bf12ed9f10522449e2047/macos_installer/packetfence-zero-trust-client.icns -------------------------------------------------------------------------------- /outputlog/outputlog.go: -------------------------------------------------------------------------------- 1 | package outputlog 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/inverse-inc/packetfence/go/sharedutils" 9 | "gopkg.in/natefinch/lumberjack.v2" 10 | ) 11 | 12 | func RedirectOutputToFilePrefix(fname string) { 13 | RedirectOutputToFile(fmt.Sprintf("%s-%d.log", fname, time.Now().Unix())) 14 | } 15 | func RedirectOutputToFile(fname string) { 16 | f, err := os.OpenFile(fname, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 17 | sharedutils.CheckError(err) 18 | os.Stdout = f 19 | os.Stderr = f 20 | } 21 | func RedirectOutputToRotatedLog(fname string) { 22 | r, w, err := os.Pipe() 23 | sharedutils.CheckError(err) 24 | 25 | l := &lumberjack.Logger{ 26 | Filename: fname, 27 | MaxSize: 10, //megabytes 28 | MaxBackups: 5, 29 | } 30 | os.Stdout = w 31 | os.Stderr = w 32 | 33 | go func() { 34 | buf := make([]byte, 5000) 35 | for { 36 | n, err := r.Read(buf) 37 | sharedutils.CheckError(err) 38 | l.Write(buf[:n]) 39 | } 40 | }() 41 | } 42 | -------------------------------------------------------------------------------- /routes/routes_darwin.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os/exec" 7 | ) 8 | 9 | func Add(ipnet *net.IPNet, gw net.IP) error { 10 | res, err := exec.Command("route", "-n", "add", "-net", ipnet.String(), gw.String()).Output() 11 | if err != nil { 12 | fmt.Println(string(res)) 13 | } 14 | return err 15 | } 16 | -------------------------------------------------------------------------------- /routes/routes_linux.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os/exec" 7 | ) 8 | 9 | func Add(ipnet *net.IPNet, gw net.IP) error { 10 | res, err := exec.Command("ip", "route", "add", ipnet.String(), "via", gw.String()).Output() 11 | if err != nil { 12 | fmt.Println(string(res)) 13 | } 14 | return err 15 | } 16 | -------------------------------------------------------------------------------- /routes/routes_windows.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os/exec" 7 | ) 8 | 9 | func Add(ipnet *net.IPNet, gw net.IP) error { 10 | res, err := exec.Command("route", "add", ipnet.IP.String(), "mask", net.IPv4(ipnet.Mask[0], ipnet.Mask[1], ipnet.Mask[2], ipnet.Mask[3]).String(), gw.String()).Output() 11 | if err != nil { 12 | fmt.Println(string(res)) 13 | } 14 | return err 15 | } 16 | -------------------------------------------------------------------------------- /rwcancel/fdset.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package rwcancel 9 | 10 | import "golang.org/x/sys/unix" 11 | 12 | type fdSet struct { 13 | unix.FdSet 14 | } 15 | 16 | func (fdset *fdSet) set(i int) { 17 | bits := 32 << (^uint(0) >> 63) 18 | fdset.Bits[i/bits] |= 1 << uint(i%bits) 19 | } 20 | 21 | func (fdset *fdSet) check(i int) bool { 22 | bits := 32 << (^uint(0) >> 63) 23 | return (fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0 24 | } 25 | -------------------------------------------------------------------------------- /rwcancel/rwcancel_windows.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | package rwcancel 4 | 5 | type RWCancel struct { 6 | } 7 | 8 | func (*RWCancel) Cancel() {} 9 | -------------------------------------------------------------------------------- /rwcancel/select_default.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!windows 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package rwcancel 9 | 10 | import "golang.org/x/sys/unix" 11 | 12 | func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) error { 13 | _, err := unix.Select(nfd, r, w, e, timeout) 14 | return err 15 | } 16 | -------------------------------------------------------------------------------- /rwcancel/select_linux.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package rwcancel 7 | 8 | import "golang.org/x/sys/unix" 9 | 10 | func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) (err error) { 11 | _, err = unix.Select(nfd, r, w, e, timeout) 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /services/make_services.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "github.com/inverse-inc/wireguard-go/services" 9 | "go/format" 10 | "text/template" 11 | ) 12 | 13 | func main() { 14 | t := template.New("") 15 | buffer := bytes.NewBuffer(nil) 16 | template := `package services 17 | 18 | var SERVICE_MAP = ServiceMap{ 19 | {{range $k, $v := .}}"{{$k}}" : map[string]Service{ 20 | {{ range $p, $s := $v }}"{{$p}}" : Service{ 21 | Proto:"{{$s.Proto}}", 22 | Name:"{{$s.Name}}", 23 | Port:{{$s.Port}}, 24 | }, 25 | {{end}} }, 26 | {{end}} 27 | } 28 | ` 29 | t.Parse(template) 30 | s, _ := services.GetServices() 31 | t.Execute(buffer, s) 32 | formatted, err := format.Source(buffer.Bytes()) 33 | if err != nil { 34 | fmt.Printf("Error: %s\n%s\n", err.Error(), buffer.Bytes()) 35 | } else { 36 | fmt.Print(string(formatted)) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tai64n/tai64n.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tai64n 7 | 8 | import ( 9 | "bytes" 10 | "encoding/binary" 11 | "time" 12 | ) 13 | 14 | const TimestampSize = 12 15 | const base = uint64(0x400000000000000a) 16 | const whitenerMask = uint32(0x1000000 - 1) 17 | 18 | type Timestamp [TimestampSize]byte 19 | 20 | func stamp(t time.Time) Timestamp { 21 | var tai64n Timestamp 22 | secs := base + uint64(t.Unix()) 23 | nano := uint32(t.Nanosecond()) &^ whitenerMask 24 | binary.BigEndian.PutUint64(tai64n[:], secs) 25 | binary.BigEndian.PutUint32(tai64n[8:], nano) 26 | return tai64n 27 | } 28 | 29 | func Now() Timestamp { 30 | return stamp(time.Now()) 31 | } 32 | 33 | func (t1 Timestamp) After(t2 Timestamp) bool { 34 | return bytes.Compare(t1[:], t2[:]) > 0 35 | } 36 | -------------------------------------------------------------------------------- /tai64n/tai64n_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tai64n 7 | 8 | import ( 9 | "testing" 10 | "time" 11 | ) 12 | 13 | // Test that timestamps are monotonic as required by Wireguard and that 14 | // nanosecond-level information is whitened to prevent side channel attacks. 15 | func TestMonotonic(t *testing.T) { 16 | startTime := time.Unix(0, 123456789) // a nontrivial bit pattern 17 | // Whitening should reduce timestamp granularity 18 | // to more than 10 but fewer than 20 milliseconds. 19 | tests := []struct { 20 | name string 21 | t1, t2 time.Time 22 | wantAfter bool 23 | }{ 24 | {"after_10_ns", startTime, startTime.Add(10 * time.Nanosecond), false}, 25 | {"after_10_us", startTime, startTime.Add(10 * time.Microsecond), false}, 26 | {"after_1_ms", startTime, startTime.Add(time.Millisecond), false}, 27 | {"after_10_ms", startTime, startTime.Add(10 * time.Millisecond), false}, 28 | {"after_20_ms", startTime, startTime.Add(20 * time.Millisecond), true}, 29 | } 30 | 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | ts1, ts2 := stamp(tt.t1), stamp(tt.t2) 34 | got := ts2.After(ts1) 35 | if got != tt.wantAfter { 36 | t.Errorf("after = %v; want %v", got, tt.wantAfter) 37 | } 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /traywrapper/version/version.h: -------------------------------------------------------------------------------- 1 | #define WIREGUARD_WINDOWS_VERSION_ARRAY 1,0,0 2 | #define WIREGUARD_WINDOWS_VERSION_STRING "1.0.0" 3 | -------------------------------------------------------------------------------- /tun/operateonfd.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package tun 9 | 10 | import ( 11 | "fmt" 12 | ) 13 | 14 | func (tun *NativeTun) operateOnFd(fn func(fd uintptr)) { 15 | sysconn, err := tun.tunFile.SyscallConn() 16 | if err != nil { 17 | tun.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error()) 18 | return 19 | } 20 | err = sysconn.Control(fn) 21 | if err != nil { 22 | tun.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tun/tun.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | import ( 9 | "os" 10 | ) 11 | 12 | type Event int 13 | 14 | const ( 15 | EventUp = 1 << iota 16 | EventDown 17 | EventMTUUpdate 18 | ) 19 | 20 | type Device interface { 21 | File() *os.File // returns the file descriptor of the device 22 | Read([]byte, int) (int, error) // read a packet from the device (without any additional headers) 23 | Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers) 24 | Flush() error // flush all previous writes to the device 25 | MTU() (int, error) // returns the MTU of the device 26 | Name() (string, error) // fetches and returns the current name 27 | Events() chan Event // returns a constant channel of events related to the device 28 | Close() error // stops the device and closes the event channel 29 | } 30 | -------------------------------------------------------------------------------- /tun/wintun/iphlpapi/conversion_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package iphlpapi 7 | 8 | import "golang.org/x/sys/windows" 9 | 10 | //sys convertInterfaceLUIDToGUID(interfaceLUID *uint64, interfaceGUID *windows.GUID) (ret error) = iphlpapi.ConvertInterfaceLuidToGuid 11 | //sys convertInterfaceAliasToLUID(interfaceAlias *uint16, interfaceLUID *uint64) (ret error) = iphlpapi.ConvertInterfaceAliasToLuid 12 | 13 | func InterfaceGUIDFromAlias(alias string) (*windows.GUID, error) { 14 | var luid uint64 15 | var guid windows.GUID 16 | err := convertInterfaceAliasToLUID(windows.StringToUTF16Ptr(alias), &luid) 17 | if err != nil { 18 | return nil, err 19 | } 20 | err = convertInterfaceLUIDToGUID(&luid, &guid) 21 | if err != nil { 22 | return nil, err 23 | } 24 | return &guid, nil 25 | } 26 | -------------------------------------------------------------------------------- /tun/wintun/iphlpapi/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package iphlpapi 7 | 8 | //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go conversion_windows.go 9 | -------------------------------------------------------------------------------- /tun/wintun/namespaceapi/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package namespaceapi 7 | 8 | //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go namespaceapi_windows.go 9 | -------------------------------------------------------------------------------- /tun/wintun/nci/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package nci 7 | 8 | //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go nci_windows.go 9 | -------------------------------------------------------------------------------- /tun/wintun/nci/nci_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package nci 7 | 8 | import "golang.org/x/sys/windows" 9 | 10 | //sys nciSetConnectionName(guid *windows.GUID, newName *uint16) (ret error) = nci.NciSetConnectionName 11 | //sys nciGetConnectionName(guid *windows.GUID, destName *uint16, inDestNameBytes uint32, outDestNameBytes *uint32) (ret error) = nci.NciGetConnectionName 12 | 13 | func SetConnectionName(guid *windows.GUID, newName string) error { 14 | newName16, err := windows.UTF16PtrFromString(newName) 15 | if err != nil { 16 | return err 17 | } 18 | return nciSetConnectionName(guid, newName16) 19 | } 20 | 21 | func ConnectionName(guid *windows.GUID) (string, error) { 22 | var name [0x400]uint16 23 | err := nciGetConnectionName(guid, &name[0], uint32(len(name)*2), nil) 24 | if err != nil { 25 | return "", err 26 | } 27 | return windows.UTF16ToString(name[:]), nil 28 | } 29 | -------------------------------------------------------------------------------- /tun/wintun/registry/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package registry 7 | 8 | //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zregistry_windows.go registry_windows.go 9 | -------------------------------------------------------------------------------- /tun/wintun/setupapi/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package setupapi 7 | 8 | //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsetupapi_windows.go setupapi_windows.go 9 | -------------------------------------------------------------------------------- /tun/wintun/setupapi/types32_windows.go: -------------------------------------------------------------------------------- 1 | // +build 386 arm 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package setupapi 9 | 10 | const ( 11 | sizeofDevInfoListDetailData uint32 = 550 12 | sizeofDrvInfoDetailData uint32 = 1570 13 | ) 14 | -------------------------------------------------------------------------------- /tun/wintun/setupapi/types64_windows.go: -------------------------------------------------------------------------------- 1 | // +build amd64 arm64 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package setupapi 9 | 10 | const ( 11 | sizeofDevInfoListDetailData uint32 = 560 12 | sizeofDrvInfoDetailData uint32 = 1584 13 | ) 14 | -------------------------------------------------------------------------------- /tun/wintun/setupapi/zsetupapi_windows_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2020 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package setupapi 7 | 8 | import ( 9 | "syscall" 10 | "testing" 11 | 12 | "golang.org/x/sys/windows" 13 | ) 14 | 15 | func TestSetupDiDestroyDeviceInfoList(t *testing.T) { 16 | err := SetupDiDestroyDeviceInfoList(DevInfo(windows.InvalidHandle)) 17 | if errWin, ok := err.(syscall.Errno); !ok || errWin != windows.ERROR_INVALID_HANDLE { 18 | t.Errorf("SetupDiDestroyDeviceInfoList(nil, ...) should fail with ERROR_INVALID_HANDLE") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ui/icon/wireguard.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inverse-inc/wireguard-go/2b8e069b83ae73eb256bf12ed9f10522449e2047/ui/icon/wireguard.ico -------------------------------------------------------------------------------- /util/app_name.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | const ( 4 | AppName = "PacketFence Zero Trust Client" 5 | ) 6 | -------------------------------------------------------------------------------- /util/icon/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inverse-inc/wireguard-go/2b8e069b83ae73eb256bf12ed9f10522449e2047/util/icon/logo.ico -------------------------------------------------------------------------------- /util/icon/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inverse-inc/wireguard-go/2b8e069b83ae73eb256bf12ed9f10522449e2047/util/icon/logo.png -------------------------------------------------------------------------------- /util/icon/make_icon.bat: -------------------------------------------------------------------------------- 1 | 2 | @ECHO OFF 3 | 4 | set GOPATH=..\..\.deps\go\ 5 | 6 | 7 | echo %GOPATH% 8 | 9 | IF "%GOPATH%"=="" GOTO NOGO 10 | IF NOT EXIST %GOPATH%\bin\2goarray.exe GOTO INSTALL 11 | :POSTINSTALL 12 | IF "%1"=="" GOTO NOICO 13 | IF NOT EXIST %1 GOTO BADFILE 14 | ECHO Creating iconwin.go 15 | ECHO //+build windows > iconwin.go 16 | ECHO. >> iconwin.go 17 | TYPE %1 | %GOPATH%\bin\2goarray Data icon >> iconwin.go 18 | GOTO DONE 19 | 20 | :CREATEFAIL 21 | ECHO Unable to create output file 22 | GOTO DONE 23 | 24 | :INSTALL 25 | ECHO Installing 2goarray... 26 | ..\..\.deps\go\bin\go get github.com/cratonica/2goarray 27 | IF ERRORLEVEL 1 GOTO GETFAIL 28 | GOTO POSTINSTALL 29 | 30 | :GETFAIL 31 | ECHO Failure running go get github.com/cratonica/2goarray. Ensure that go and git are in PATH 32 | GOTO DONE 33 | 34 | :NOGO 35 | ECHO GOPATH environment variable not set 36 | GOTO DONE 37 | 38 | :NOICO 39 | ECHO Please specify a .ico file 40 | GOTO DONE 41 | 42 | :BADFILE 43 | ECHO %1 is not a valid file 44 | GOTO DONE 45 | 46 | :DONE 47 | 48 | -------------------------------------------------------------------------------- /util/icon/make_icon.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | if [ -z "$GOPATH" ]; then 4 | echo GOPATH environment variable not set 5 | exit 6 | fi 7 | 8 | if [ ! -e "$GOPATH/bin/2goarray" ]; then 9 | echo "Installing 2goarray..." 10 | go get github.com/cratonica/2goarray 11 | if [ $? -ne 0 ]; then 12 | echo Failure executing go get github.com/cratonica/2goarray 13 | exit 14 | fi 15 | fi 16 | 17 | if [ -z "$1" ]; then 18 | echo Please specify a PNG file 19 | exit 20 | fi 21 | 22 | if [ ! -f "$1" ]; then 23 | echo $1 is not a valid file 24 | exit 25 | fi 26 | 27 | OUTPUT=iconunix.go 28 | echo Generating $OUTPUT 29 | echo "//+build linux darwin" > $OUTPUT 30 | echo >> $OUTPUT 31 | cat "$1" | $GOPATH/bin/2goarray Data icon >> $OUTPUT 32 | if [ $? -ne 0 ]; then 33 | echo Failure generating $OUTPUT 34 | exit 35 | fi 36 | echo Finished 37 | -------------------------------------------------------------------------------- /util/printappname/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/inverse-inc/wireguard-go/util" 7 | ) 8 | 9 | func main() { 10 | fmt.Print(util.AppName) 11 | } 12 | -------------------------------------------------------------------------------- /util/udp.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | func UDPSend(msg []byte, conn *net.UDPConn, addr *net.UDPAddr) error { 9 | _, err := conn.WriteToUDP(msg, addr) 10 | if err != nil { 11 | return fmt.Errorf("send: %v", err) 12 | } 13 | 14 | return nil 15 | } 16 | 17 | func UDPSendStr(msg string, conn *net.UDPConn, addr *net.UDPAddr) error { 18 | return UDPSend([]byte(msg), conn, addr) 19 | } 20 | -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/inverse-inc/packetfence/go/sharedutils" 10 | ) 11 | 12 | func CheckGUIIsAliveUNIX(quit func()) { 13 | for { 14 | ppid64, err := strconv.Atoi(os.Getenv("WG_GUI_PID")) 15 | sharedutils.CheckError(err) 16 | ppid := int(ppid64) 17 | if !CheckPIDIsAlive(ppid) { 18 | fmt.Println("Parent process is dead, exiting") 19 | quit() 20 | } 21 | time.Sleep(1 * time.Second) 22 | } 23 | } 24 | 25 | func CheckGUIIsAliveWindows(quit func()) { 26 | for { 27 | ppid64, err := strconv.Atoi(os.Getenv("WG_GUI_PID")) 28 | sharedutils.CheckError(err) 29 | if !CheckPIDIsAlive(int(ppid64)) { 30 | fmt.Println("GUI is dead, exiting", ppid64) 31 | quit() 32 | } 33 | time.Sleep(1 * time.Second) 34 | } 35 | } 36 | 37 | func Pause() { 38 | time.Sleep(365 * 24 * time.Hour) 39 | } 40 | -------------------------------------------------------------------------------- /util/util_unix.go: -------------------------------------------------------------------------------- 1 | //+build linux darwin 2 | 3 | package util 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "syscall" 9 | ) 10 | 11 | func CheckPIDIsAlive(pid int) bool { 12 | process, err := os.FindProcess(pid) 13 | if err != nil || pid == 1 { 14 | return false 15 | } else if err := process.Signal(syscall.Signal(0)); err != nil { 16 | return false 17 | } 18 | return true 19 | } 20 | 21 | func KillProcess(p *os.Process) { 22 | fmt.Println("Killing", p.Pid) 23 | p.Signal(syscall.SIGTERM) 24 | } 25 | -------------------------------------------------------------------------------- /util/util_windows.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package util 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "regexp" 10 | "strings" 11 | "syscall" 12 | 13 | "github.com/inverse-inc/packetfence/go/sharedutils" 14 | ) 15 | 16 | func CheckPIDIsAlive(pid int) bool { 17 | cmd := exec.Command("tasklist") 18 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 19 | output, err := cmd.Output() 20 | if err != nil { 21 | fmt.Println("Unable to run tasklist: ", err, string(output)) 22 | } 23 | re := regexp.MustCompile(fmt.Sprintf(`^\S+\s+%d\s+`, pid)) 24 | lines := strings.Split(string(output), "\n") 25 | for _, l := range lines { 26 | if re.MatchString(l) { 27 | return true 28 | } 29 | } 30 | return false 31 | } 32 | 33 | func KillProcess(p *os.Process) { 34 | fmt.Println("Killing", p.Pid) 35 | h, err := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(p.Pid)) 36 | sharedutils.CheckError(err) 37 | defer syscall.CloseHandle(h) 38 | err = syscall.TerminateProcess(h, uint32(1)) 39 | sharedutils.CheckError(err) 40 | } 41 | -------------------------------------------------------------------------------- /wgrpc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | build: 3 | protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative wgrpc.proto 4 | -------------------------------------------------------------------------------- /wgrpc/rpc.go: -------------------------------------------------------------------------------- 1 | package wgrpc 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/inverse-inc/packetfence/go/sharedutils" 8 | "github.com/inverse-inc/wireguard-go/device" 9 | "github.com/inverse-inc/wireguard-go/ztn" 10 | "google.golang.org/grpc" 11 | "google.golang.org/grpc/reflection" 12 | ) 13 | 14 | const ServerPort = 6970 15 | 16 | var WGRPCServer *WGServiceServerHandler 17 | 18 | func StartRPC(logger *device.Logger, connection *ztn.Connection, onexit func()) { 19 | lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", ServerPort)) 20 | sharedutils.CheckError(err) 21 | grpcServer := grpc.NewServer() 22 | 23 | WGRPCServer = NewWGServiceServerHandler(connection, onexit) 24 | RegisterWGServiceServer(grpcServer, WGRPCServer) 25 | 26 | reflection.Register(grpcServer) 27 | grpcServer.Serve(lis) 28 | } 29 | 30 | func WGRPCClient() WGServiceClient { 31 | conn, err := grpc.Dial( 32 | fmt.Sprintf("127.0.0.1:%d", ServerPort), 33 | grpc.WithInsecure(), 34 | ) 35 | sharedutils.CheckError(err) 36 | client := NewWGServiceClient(conn) 37 | return client 38 | } 39 | -------------------------------------------------------------------------------- /wgrpc/stop_unix.go: -------------------------------------------------------------------------------- 1 | //+build linux darwin 2 | 3 | package wgrpc 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/inverse-inc/wireguard-go/util" 9 | ) 10 | 11 | func stopMasterProcess() { 12 | p, err := os.FindProcess(os.Getppid()) 13 | if err == nil { 14 | util.KillProcess(p) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /wgrpc/stop_windows.go: -------------------------------------------------------------------------------- 1 | package wgrpc 2 | 3 | import ( 4 | "os" 5 | 6 | godnschange "github.com/inverse-inc/go-dnschange" 7 | "github.com/inverse-inc/wireguard-go/util" 8 | ) 9 | 10 | func stopMasterProcess() { 11 | c := godnschange.NewDNSChange() 12 | c.GetDNS() 13 | c.RestoreDNS("127.0.0.69") 14 | p, err := os.FindProcess(os.Getppid()) 15 | if err == nil { 16 | util.KillProcess(p) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wgrpc/wgrpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "wgrpc"; 4 | 5 | service WGService { 6 | rpc GetStatus (StatusRequest) returns (StatusReply) {} 7 | rpc GetPeers (PeersRequest) returns (PeersReply) {} 8 | rpc Stop (StopRequest) returns (StopReply) {} 9 | rpc PrintDebug(PrintDebugRequest) returns (PrintDebugReply) {} 10 | } 11 | 12 | message StatusRequest { 13 | } 14 | 15 | message StatusReply { 16 | string status = 1; 17 | string lastError = 2; 18 | string currentBindTechnique = 3; 19 | } 20 | 21 | message PeerReply { 22 | string ipAddress = 1; 23 | string status = 2; 24 | string hostname = 3; 25 | } 26 | 27 | message PeersRequest { 28 | } 29 | 30 | message PeersReply { 31 | repeated PeerReply peers = 1; 32 | } 33 | 34 | message StopRequest { 35 | bool killMasterProcess = 1; 36 | } 37 | 38 | message StopReply { 39 | } 40 | 41 | message PrintDebugRequest { 42 | } 43 | 44 | message PrintDebugReply { 45 | } 46 | -------------------------------------------------------------------------------- /windows_installer/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{bat,wxs}] 2 | indent_style = tab 3 | -------------------------------------------------------------------------------- /windows_installer/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /.deps 3 | 4 | # Build Output 5 | /dist 6 | /x86 7 | /amd64 8 | -------------------------------------------------------------------------------- /windows_installer/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Bumping the version 3 | 4 | In order to adjust the version of the installer, modify traywrapper/version/version.h 5 | 6 | # How to build the Windows agent 7 | 8 | NOTE: Although you can build the wireguard-go binary on any platform, you can only build the DMG on Windows itself. Also, the guiwrapper will not build if you're not on Windows. 9 | 10 | First you need to build the binaries using the build script in the parent directory: 11 | 12 | ``` 13 | cd .. 14 | .\build_windows.bat 15 | ``` 16 | 17 | Next, you need to build the MSI: 18 | 19 | ``` 20 | cd windows_installer 21 | .\build.bat 22 | ``` 23 | 24 | Once the build is complete, you will find the MSI in dist/ 25 | 26 | -------------------------------------------------------------------------------- /windows_installer/run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: BatchGotAdmin 4 | :------------------------------------- 5 | REM --> Check for permissions 6 | IF "%PROCESSOR_ARCHITECTURE%" EQU "amd64" ( 7 | >nul 2>&1 "%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system" 8 | ) ELSE ( 9 | >nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" 10 | ) 11 | 12 | REM --> If error flag set, we do not have admin. 13 | if '%errorlevel%' NEQ '0' ( 14 | echo Requesting administrative privileges... 15 | goto UACPrompt 16 | ) else ( goto gotAdmin ) 17 | 18 | :UACPrompt 19 | echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" 20 | set params= %* 21 | echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params:"=""%", "", "runas", 1 >> "%temp%\getadmin.vbs" 22 | 23 | "%temp%\getadmin.vbs" 24 | del "%temp%\getadmin.vbs" 25 | rem exit /B 26 | 27 | :gotAdmin 28 | pushd "%CD%" 29 | CD /D "%~dp0" 30 | :-------------------------------------- 31 | 32 | "C:\Program Files\PacketFence-Zero-Trust-Client\PsExec.exe" -accepteula -h -u "nt authority\network service" -s "C:\Program Files\PacketFence-Zero-Trust-Client\wireguard.exe" %1 --master 33 | pause 34 | -------------------------------------------------------------------------------- /ztn/Makefile: -------------------------------------------------------------------------------- 1 | 2 | build: 3 | protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative peerrpc.proto 4 | -------------------------------------------------------------------------------- /ztn/bind_techniques_test.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | import "testing" 4 | 5 | func TestBindTechniques(t *testing.T) { 6 | // Should default to STUN when empty 7 | if BindTechniques.Next() != BindSTUN { 8 | t.Error("Invalid default bind technique") 9 | } 10 | 11 | BindTechniques.Add(BindSTUN) 12 | BindTechniques.Add(BindUPNPGID) 13 | 14 | if BindTechniques.Next() != BindUPNPGID { 15 | t.Error("Invalid bind technique") 16 | } 17 | 18 | if BindTechniques.Next() != BindSTUN { 19 | t.Error("Invalid bind technique") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ztn/buffer_pool.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var defaultBufferPool = NewBufferPool() 8 | 9 | const maxBufferPoolPktSize = 1500 10 | 11 | type BufferPool struct { 12 | sync.Pool 13 | sync.Mutex 14 | aliveBuffers int 15 | } 16 | 17 | func NewBufferPool() *BufferPool { 18 | return &BufferPool{ 19 | Mutex: sync.Mutex{}, 20 | Pool: sync.Pool{ 21 | New: func() interface{} { 22 | return make([]byte, maxBufferPoolPktSize) 23 | }, 24 | }, 25 | } 26 | } 27 | 28 | func (bp *BufferPool) GetAliveBuffers() int { 29 | return bp.aliveBuffers 30 | } 31 | 32 | func (bp *BufferPool) Get() []byte { 33 | bp.Lock() 34 | bp.aliveBuffers++ 35 | bp.Unlock() 36 | return bp.Pool.Get().([]byte) 37 | } 38 | 39 | func (bp *BufferPool) Put(a []byte) { 40 | bp.Lock() 41 | bp.aliveBuffers-- 42 | bp.Unlock() 43 | a = a[:maxBufferPoolPktSize] 44 | bp.Pool.Put(a) 45 | } 46 | -------------------------------------------------------------------------------- /ztn/config.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | 8 | "github.com/inverse-inc/wireguard-go/device" 9 | ) 10 | 11 | func SetConfig(device *device.Device, k, v string) { 12 | device.IpcSetOperation(bufio.NewReader(bytes.NewBufferString(fmt.Sprintf("%s=%s", k, v)))) 13 | } 14 | 15 | func SetConfigMulti(device *device.Device, conf string) { 16 | device.IpcSetOperation(bufio.NewReader(bytes.NewBufferString(conf))) 17 | } 18 | -------------------------------------------------------------------------------- /ztn/constants.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/inverse-inc/wireguard-go/device" 7 | ) 8 | 9 | var PublicPortLivenessTolerance = 10 * time.Minute 10 | var InitialConnectionLivenessTolerance = device.RekeyTimeout * 2 11 | var ConnectedConnectionLivenessTolerance = 10 * time.Second 12 | 13 | var InboundAttemptsTryAtLeast = 20 * time.Second 14 | var InboundAttemptsTolerance = 2 15 | 16 | var AutomatedMaxBindFailures = 8 17 | var UserDefinedMaxBindFailures = 120 18 | 19 | const udp = "udp" 20 | const pingMsg = "ping" 21 | 22 | var stunServer = "" 23 | 24 | const ( 25 | MsgNcBindPeerBridge = uint64(1) 26 | ) 27 | 28 | const ( 29 | ConnectionTypeLANIN = "LAN IN" 30 | ConnectionTypeLANOUT = "LAN OUT" 31 | ConnectionTypeWANIN = "WAN IN" 32 | ConnectionTypeWANOUT = "WAN OUT" 33 | ConnectionTypeWANSTUN = "WAN STUN" 34 | ) 35 | 36 | func PublicPortTTL() int { 37 | // 1 day 38 | return 24 * 60 * 60 39 | } 40 | -------------------------------------------------------------------------------- /ztn/env_constants.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | const ( 4 | EnvServer = "WG_SERVER" 5 | EnvServerPort = "WG_SERVER_PORT" 6 | EnvServerVerifyTLS = "WG_SERVER_VERIFY_TLS" 7 | 8 | EnvUsername = "WG_USERNAME" 9 | EnvPassword = "WG_PASSWORD" 10 | 11 | EnvHonorRoutes = "WG_HONOR_ROUTES" 12 | 13 | EnvBindTechnique = "WG_BIND_TECHNIQUE" 14 | EnvStaticBindTechnique = "WG_STATIC_BIND_TECHNIQUE" 15 | 16 | EnvPublicPortIP = "WG_PUBLIC_PORT_IP" 17 | 18 | EnvOffersBridging = "WG_OFFERS_BRIDGING" 19 | EnvMaxPeerBridges = "WG_MAX_PEER_BRIDGES" 20 | 21 | EnvGatewayOutboundInterface = "WG_GATEWAY_OUTBOUND_INTERFACE" 22 | 23 | EnvCLIInterractive = "WG_CLI_INTERACTIVE" 24 | EnvCLI = "WG_CLI" 25 | 26 | EnvGUIPID = "WG_GUI_PID" 27 | 28 | EnvSetupDNS = "WG_SETUP_DNS" 29 | ) 30 | -------------------------------------------------------------------------------- /ztn/peerrpc.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/inverse-inc/packetfence/go/sharedutils" 8 | "github.com/inverse-inc/wireguard-go/device" 9 | grpc "google.golang.org/grpc" 10 | "google.golang.org/grpc/reflection" 11 | ) 12 | 13 | const PeerServiceServerPort = 12676 14 | 15 | func ConnectPeerServiceClient(addr string) (PeerServiceClient, *grpc.ClientConn) { 16 | conn, err := grpc.Dial( 17 | addr, 18 | grpc.WithInsecure(), 19 | ) 20 | sharedutils.CheckError(err) 21 | client := NewPeerServiceClient(conn) 22 | return client, conn 23 | } 24 | 25 | func StartPeerServiceRPC(ip net.IP, logger *device.Logger, profile Profile) { 26 | lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ip, PeerServiceServerPort)) 27 | sharedutils.CheckError(err) 28 | grpcServer := grpc.NewServer() 29 | 30 | PeerServer := NewPeerServiceServerHandler(logger, profile) 31 | RegisterPeerServiceServer(grpcServer, PeerServer) 32 | 33 | reflection.Register(grpcServer) 34 | grpcServer.Serve(lis) 35 | } 36 | -------------------------------------------------------------------------------- /ztn/peerrpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "ztn"; 4 | 5 | service PeerService { 6 | rpc CanOfferForwarding (CanOfferForwardingRequest) returns (CanOfferForwardingReply) {} 7 | rpc SetupForwarding (SetupForwardingRequest) returns (SetupForwardingReply) {} 8 | rpc ForwardingIsAlive(ForwardingIsAliveRequest) returns (ForwardingIsAliveReply) {} 9 | } 10 | 11 | message CanOfferForwardingRequest{ 12 | } 13 | 14 | message CanOfferForwardingReply { 15 | bool result = 1; 16 | } 17 | 18 | message SetupForwardingRequest { 19 | string name = 1; 20 | string peerConnectionType = 2; 21 | } 22 | 23 | message SetupForwardingReply { 24 | uint64 id = 1; 25 | uint64 token = 2; 26 | string raddr = 3; 27 | bytes publicIP = 4; 28 | int32 publicPort = 5; 29 | } 30 | 31 | message ForwardingIsAliveRequest { 32 | uint64 id = 1; 33 | uint64 token = 2; 34 | } 35 | 36 | message ForwardingIsAliveReply { 37 | bool result = 1; 38 | } 39 | -------------------------------------------------------------------------------- /ztn/profile_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package ztn 4 | 5 | import ( 6 | "os/exec" 7 | 8 | "github.com/inverse-inc/wireguard-go/device" 9 | ) 10 | 11 | func (p *Profile) setupInterface(device *device.Device, WGInterface string) (error) { 12 | // ipconfig set utun0 MANUAL 192.168.69.10 255.255.255.0 13 | cmd := exec.Command("ipconfig", "set", WGInterface, "MANUAL", p.WireguardIP.String(), ipv4MaskString(p.WireguardNetmask)) 14 | err := cmd.Run() 15 | if err != nil { 16 | return err 17 | } 18 | err = exec.Command("ifconfig", WGInterface, "up").Run() 19 | if err != nil { 20 | return err 21 | } 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /ztn/profile_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package ztn 4 | 5 | import ( 6 | "fmt" 7 | "os/exec" 8 | 9 | "github.com/inverse-inc/wireguard-go/device" 10 | ) 11 | 12 | func (p *Profile) setupInterface(device *device.Device, WGInterface string) error { 13 | err := exec.Command("ip", "address", "add", "dev", WGInterface, fmt.Sprintf("%s/%d", p.WireguardIP, p.WireguardNetmask)).Run() 14 | if err != nil { 15 | return err 16 | } 17 | err = exec.Command("ip", "link", "set", WGInterface, "up").Run() 18 | if err != nil { 19 | return err 20 | } 21 | 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /ztn/profile_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package ztn 4 | 5 | import ( 6 | "fmt" 7 | "os/exec" 8 | 9 | "github.com/inverse-inc/wireguard-go/device" 10 | ) 11 | 12 | func (p *Profile) setupInterface(device *device.Device, WGInterface string) error { 13 | cmd := exec.Command("netsh", "interface", "ipv4", "set", "address", fmt.Sprintf(`name="%s"`, WGInterface), "static", p.WireguardIP.String(), ipv4MaskString(p.WireguardNetmask)) 14 | err := cmd.Run() 15 | return err 16 | } 17 | -------------------------------------------------------------------------------- /ztn/publicport.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "os" 7 | "sync" 8 | ) 9 | 10 | type PublicPort struct { 11 | sync.Mutex 12 | BindTechniqueBase 13 | remoteIP net.IP 14 | remotePort int 15 | } 16 | 17 | func NewPublicPort() *PublicPort { 18 | pp := &PublicPort{} 19 | pp.InitID() 20 | return pp 21 | } 22 | 23 | func (pp *PublicPort) BindRequest(conn *net.UDPConn, sendTo chan *pkt) error { 24 | pp.Lock() 25 | defer pp.Unlock() 26 | 27 | if pp.remotePort != 0 { 28 | return nil 29 | } 30 | 31 | pp.remoteIP = net.ParseIP(os.Getenv(EnvPublicPortIP)) 32 | if pp.remoteIP == nil { 33 | return errors.New(EnvPublicPortIP + " is not defined in the environment or is not a valid IPv4 address") 34 | } 35 | pp.remotePort = localWGPort 36 | 37 | go func() { 38 | sendTo <- &pkt{message: pp.BindRequestPkt(pp.remoteIP, pp.remotePort)} 39 | }() 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /ztn/read_password.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package ztn 4 | 5 | import "golang.org/x/crypto/ssh/terminal" 6 | 7 | func ReadPassword() string { 8 | bytePassword, _ := terminal.ReadPassword(0) 9 | password := string(bytePassword) 10 | return password 11 | } 12 | -------------------------------------------------------------------------------- /ztn/read_password_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package ztn 4 | 5 | import ( 6 | "bufio" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | func ReadPassword() string { 12 | reader := bufio.NewReader(os.Stdin) 13 | password, _ := reader.ReadString('\n') 14 | password = strings.Trim(password, "\r\n") 15 | return password 16 | } 17 | -------------------------------------------------------------------------------- /ztn/wg.go: -------------------------------------------------------------------------------- 1 | package ztn 2 | 3 | import "net" 4 | 5 | var localWGIP = net.ParseIP("127.0.0.1") 6 | 7 | const localWGPort = 12674 8 | --------------------------------------------------------------------------------