├── .circleci └── config.yml ├── .codecov.yml ├── .dockerignore ├── .dreck.yaml ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── enhancement.md ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── fixup_file_mtime.sh └── workflows │ ├── go.tidy.yml │ └── make.doc.yml ├── .gitignore ├── .stickler.yml ├── .travis.yml ├── ADOPTERS.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── GOVERNANCE.md ├── LICENSE ├── Makefile ├── Makefile.doc ├── Makefile.fuzz ├── Makefile.release ├── README.md ├── SECURITY.md ├── 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_dnscrypt.go │ ├── server_grpc.go │ ├── server_https.go │ ├── server_quic.go │ ├── server_test.go │ ├── server_tls.go │ └── zdirectives.go └── plugin │ └── zplugin.go ├── coredns.1.md ├── coredns.go ├── corefile.5.md ├── coremain ├── run.go └── version.go ├── directives_generate.go ├── go.mod ├── go.sum ├── man ├── coredns-acl.7 ├── coredns-any.7 ├── coredns-auto.7 ├── coredns-autopath.7 ├── coredns-azure.7 ├── coredns-bind.7 ├── coredns-bufsize.7 ├── coredns-cache.7 ├── coredns-cancel.7 ├── coredns-chaos.7 ├── coredns-clouddns.7 ├── coredns-debug.7 ├── coredns-dns64.7 ├── coredns-dnssec.7 ├── coredns-dnstap.7 ├── coredns-erratic.7 ├── coredns-errors.7 ├── coredns-etcd.7 ├── coredns-file.7 ├── coredns-forward.7 ├── coredns-grpc.7 ├── coredns-health.7 ├── coredns-hosts.7 ├── coredns-import.7 ├── coredns-k8s_external.7 ├── coredns-kubernetes.7 ├── coredns-loadbalance.7 ├── coredns-log.7 ├── coredns-loop.7 ├── coredns-metadata.7 ├── coredns-metrics.7 ├── coredns-nsid.7 ├── coredns-pprof.7 ├── coredns-ready.7 ├── coredns-reload.7 ├── coredns-rewrite.7 ├── coredns-root.7 ├── coredns-route53.7 ├── coredns-secondary.7 ├── coredns-sign.7 ├── coredns-template.7 ├── coredns-tls.7 ├── coredns-trace.7 ├── coredns-transfer.7 ├── coredns-whoami.7 ├── coredns.1 └── corefile.5 ├── notes ├── coredns-0.9.10.md ├── coredns-0.9.9.md ├── coredns-001.md ├── coredns-002.md ├── coredns-003.md ├── coredns-004.md ├── coredns-005.md ├── coredns-006.md ├── coredns-007.md ├── coredns-008.md ├── coredns-009.md ├── coredns-010.md ├── coredns-011.md ├── coredns-1.0.0.md ├── coredns-1.0.1.md ├── coredns-1.0.2.md ├── coredns-1.0.3.md ├── coredns-1.0.4.md ├── coredns-1.0.5.md ├── coredns-1.0.6.md ├── coredns-1.1.0.md ├── coredns-1.1.1.md ├── coredns-1.1.2.md ├── coredns-1.1.3.md ├── coredns-1.1.4.md ├── coredns-1.2.0.md ├── coredns-1.2.1.md ├── coredns-1.2.2.md ├── coredns-1.2.3.md ├── coredns-1.2.4.md ├── coredns-1.2.5.md ├── coredns-1.2.6.md ├── coredns-1.3.0.md ├── coredns-1.3.1.md ├── coredns-1.4.0.md ├── coredns-1.5.0.md ├── coredns-1.5.1.md ├── coredns-1.5.2.md ├── coredns-1.6.0.md ├── coredns-1.6.1.md ├── coredns-1.6.2.md ├── coredns-1.6.3.md ├── coredns-1.6.4.md ├── coredns-1.6.5.md ├── coredns-1.6.6.md ├── coredns-1.6.7.md ├── coredns-1.6.8.md ├── coredns-1.6.9.md ├── coredns-1.7.0.md └── coredns-1.7.1.md ├── owners_generate.go ├── pb ├── Makefile ├── dns.pb.go └── dns.proto ├── plugin.cfg ├── plugin.md ├── 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 │ └── 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 │ ├── 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 ├── dnscrypt │ ├── README.md │ └── dnscrypt.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 │ ├── context_test.go │ ├── dnstapio │ │ ├── dnstap_encoder.go │ │ ├── dnstap_encoder_test.go │ │ ├── io.go │ │ ├── io_test.go │ │ └── log_test.go │ ├── gocontext.go │ ├── handler.go │ ├── handler_test.go │ ├── log_test.go │ ├── msg │ │ ├── msg.go │ │ └── msg_test.go │ ├── setup.go │ ├── setup_test.go │ ├── taprw │ │ ├── writer.go │ │ └── writer_test.go │ └── test │ │ └── helpers.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 │ ├── dnstap_test.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 ├── httpproxy │ ├── README.md │ └── httpproxy.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_transfer_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 │ ├── metrics.go │ ├── test_ca.pem │ ├── test_cert.pem │ ├── test_key.pem │ ├── test_session_ticket.key │ ├── tls.go │ └── tls_test.go ├── trace │ ├── README.md │ ├── log_test.go │ ├── setup.go │ ├── setup_test.go │ ├── trace.go │ └── trace_test.go ├── transfer │ ├── README.md │ ├── 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 ├── dnscrypt_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 ├── 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 /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: 50% 6 | threshold: null 7 | patch: false 8 | changes: false 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !coredns 3 | -------------------------------------------------------------------------------- /.dreck.yaml: -------------------------------------------------------------------------------- 1 | features: 2 | - aliases 3 | - exec 4 | 5 | aliases: 6 | - | 7 | /plugin (.*) -> /label plugin/$1 8 | - | 9 | /wai -> /label works as intended 10 | - | 11 | /release (.*) -> /exec /opt/bin/release-coredns $1 12 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Coredns Community Code of Conduct 2 | 3 | Coredns follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug encountered while using CoreDNS 4 | labels: bug 5 | 6 | --- 7 | 8 | 12 | 13 | **What happened**: 14 | 15 | **What you expected to happen**: 16 | 17 | **How to reproduce it (as minimally and precisely as possible)**: 18 | 19 | **Anything else we need to know?**: 20 | 21 | **Environment**: 22 | 23 | - the version of CoreDNS: 24 | - Corefile: 25 | - logs, if applicable: 26 | - OS (e.g: `cat /etc/os-release`): 27 | - Others: 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Suggest an enhancement to the CoreDNS project 4 | labels: enhancement 5 | 6 | --- 7 | 8 | 9 | **What would you like to be added**: 10 | 11 | **Why is this needed**: 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### 1. Why is this pull request needed and what does it do? 7 | 8 | ### 2. Which issues (if any) are related? 9 | 10 | ### 3. Which documentation changes (if any) need to be made? 11 | 12 | ### 4. Does this introduce a backward incompatible change or deprecation? 13 | -------------------------------------------------------------------------------- /.github/fixup_file_mtime.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Description: Fix up the file mtimes based on the git log. 4 | 5 | set -u -o pipefail 6 | 7 | if [[ ! -f 'coredns.1.md' ]]; then 8 | echo 'ERROR: Must be run from the top of the git repo.' 9 | exit 1 10 | fi 11 | 12 | for file in coredns.1.md corefile.5.md plugin/*/README.md; do 13 | time=$(git log --pretty=format:%cd -n 1 --date='format:%Y%m%d%H%M.%S' "${file}") 14 | touch -m -t "${time}" "${file}" 15 | done 16 | -------------------------------------------------------------------------------- /.github/workflows/go.tidy.yml: -------------------------------------------------------------------------------- 1 | name: go tidy 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | paths: 8 | - '.github/workflows/go.tidy.yml' 9 | - 'go.mod' 10 | - 'go.sum' 11 | 12 | jobs: 13 | fix: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - 17 | name: Checkout 18 | uses: actions/checkout@v2 19 | - 20 | name: Tidy 21 | run: | 22 | rm -f go.sum 23 | go mod tidy 24 | - 25 | name: Set up Git 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | run: | 29 | git config user.name "coredns-auto-go-mod-tidy[bot]" 30 | git config user.email "coredns-auto-go-mod-tidy[bot]@users.noreply.github.com" 31 | git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git 32 | - 33 | name: Commit and push changes 34 | run: | 35 | git add . 36 | if output=$(git status --porcelain) && [ ! -z "$output" ]; then 37 | git commit -m 'auto go mod tidy' 38 | git push 39 | fi 40 | -------------------------------------------------------------------------------- /.github/workflows/make.doc.yml: -------------------------------------------------------------------------------- 1 | name: make doc 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | paths: 8 | - '.github/workflows/make.doc.yml' 9 | - 'coredns.1.md' 10 | - 'corefile.5.md' 11 | - 'plugin/*/README.md' 12 | 13 | jobs: 14 | fix: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - 18 | name: Checkout 19 | uses: actions/checkout@v2 20 | - 21 | name: Setup Go 22 | uses: actions/setup-go@v2-beta 23 | with: 24 | go-version: '^1.14.1' 25 | - 26 | name: Update Docs 27 | run: | 28 | ./.github/fixup_file_mtime.sh 29 | make -f Makefile.doc 30 | - 31 | name: Set up Git 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | run: | 35 | git config user.name "coredns-auto-go-mod-tidy[bot]" 36 | git config user.email "coredns-auto-go-mod-tidy[bot]@users.noreply.github.com" 37 | git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git 38 | - 39 | name: Commit and push changes 40 | run: | 41 | git add . 42 | if output=$(git status --porcelain) && [ ! -z "$output" ]; then 43 | git commit -m 'auto make -f Makefile.doc' 44 | git push 45 | fi 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # only add build artifacts concerning coredns - no editor related files 2 | coredns 3 | coredns.exe 4 | build/ 5 | release/ 6 | vendor/ 7 | .idea 8 | 9 | # for debugging locally 10 | Corefile 11 | testserver/ 12 | -------------------------------------------------------------------------------- /.stickler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | linters: 3 | golint: 4 | min_confidence: 0.85 5 | fixer: true 6 | 7 | files: 8 | ignore: 9 | - 'vendor/*' 10 | - 'pb/*' 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | .github/CODE_OF_CONDUCT.md -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | .github/CONTRIBUTING.md -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:stable-slim 2 | 3 | RUN apt-get update && apt-get -uy upgrade 4 | RUN apt-get -y install ca-certificates && update-ca-certificates 5 | 6 | FROM scratch 7 | 8 | COPY --from=0 /etc/ssl/certs /etc/ssl/certs 9 | ADD coredns /coredns 10 | 11 | EXPOSE 53 53/udp 12 | ENTRYPOINT ["/coredns"] 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | .github/SECURITY.md -------------------------------------------------------------------------------- /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/coredns/coredns/core/dnsserver" 7 | ) 8 | -------------------------------------------------------------------------------- /core/dnsserver/https.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /core/dnsserver/log_test.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /core/dnsserver/onstartup.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import "fmt" 4 | 5 | // startUpZones create 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 | -------------------------------------------------------------------------------- /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 | "metadata", 14 | "cancel", 15 | "tls", 16 | "reload", 17 | "nsid", 18 | "bufsize", 19 | "root", 20 | "bind", 21 | "debug", 22 | "trace", 23 | "ready", 24 | "health", 25 | "pprof", 26 | "prometheus", 27 | "errors", 28 | "log", 29 | "dnstap", 30 | "dns64", 31 | "dnscrypt", 32 | "acl", 33 | "any", 34 | "chaos", 35 | "loadbalance", 36 | "cache", 37 | "rewrite", 38 | "dnssec", 39 | "autopath", 40 | "template", 41 | "transfer", 42 | "hosts", 43 | "httpproxy", 44 | "route53", 45 | "azure", 46 | "clouddns", 47 | "k8s_external", 48 | "kubernetes", 49 | "file", 50 | "auto", 51 | "secondary", 52 | "etcd", 53 | "loop", 54 | "forward", 55 | "grpc", 56 | "erratic", 57 | "whoami", 58 | "on", 59 | "sign", 60 | } 61 | -------------------------------------------------------------------------------- /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/coredns/coredns/core/plugin" // Plug in CoreDNS. 8 | "github.com/coredns/coredns/coremain" 9 | ) 10 | 11 | func main() { 12 | coremain.Run() 13 | } 14 | -------------------------------------------------------------------------------- /coremain/version.go: -------------------------------------------------------------------------------- 1 | package coremain 2 | 3 | // Various CoreDNS constants. 4 | const ( 5 | CoreVersion = "1.7.0" 6 | coreName = "CoreDNS" 7 | serverType = "dns" 8 | ) 9 | -------------------------------------------------------------------------------- /man/coredns-any.7: -------------------------------------------------------------------------------- 1 | .\" Generated by Mmark Markdown Processer - mmark.miek.nl 2 | .TH "COREDNS-ANY" 7 "August 2020" "CoreDNS" "CoreDNS Plugins" 3 | 4 | .SH "NAME" 5 | .PP 6 | \fIany\fP - gives a minimal response to ANY queries. 7 | 8 | .SH "DESCRIPTION" 9 | .PP 10 | \fIany\fP basically blocks ANY queries by responding to them with a short HINFO reply. See RFC 11 | 8482 12 | \[la]https://tools.ietf.org/html/rfc8482\[ra] for details. 13 | 14 | .SH "SYNTAX" 15 | .PP 16 | .RS 17 | 18 | .nf 19 | any 20 | 21 | .fi 22 | .RE 23 | 24 | .SH "EXAMPLES" 25 | .PP 26 | .RS 27 | 28 | .nf 29 | example.org { 30 | whoami 31 | any 32 | } 33 | 34 | .fi 35 | .RE 36 | 37 | .PP 38 | A \fB\fCdig +nocmd ANY example.org +noall +answer\fR now returns: 39 | 40 | .PP 41 | .RS 42 | 43 | .nf 44 | example.org. 8482 IN HINFO "ANY obsoleted" "See RFC 8482" 45 | 46 | .fi 47 | .RE 48 | 49 | .SH "ALSO SEE" 50 | .PP 51 | RFC 8482 52 | \[la]https://tools.ietf.org/html/rfc8482\[ra]. 53 | 54 | -------------------------------------------------------------------------------- /man/coredns-bufsize.7: -------------------------------------------------------------------------------- 1 | .\" Generated by Mmark Markdown Processer - mmark.miek.nl 2 | .TH "COREDNS-BUFSIZE" 7 "August 2020" "CoreDNS" "CoreDNS Plugins" 3 | 4 | .SH "NAME" 5 | .PP 6 | \fIbufsize\fP - sizes EDNS0 buffer size to prevent IP fragmentation. 7 | 8 | .SH "DESCRIPTION" 9 | .PP 10 | \fIbufsize\fP limits a requester's UDP payload size. 11 | It prevents IP fragmentation, mitigating certain DNS vulnerabilities. 12 | 13 | .SH "SYNTAX" 14 | .PP 15 | .RS 16 | 17 | .nf 18 | bufsize [SIZE] 19 | 20 | .fi 21 | .RE 22 | 23 | .PP 24 | \fB[SIZE]\fP is an int value for setting the buffer size. 25 | The default value is 512, and the value must be within 512 - 4096. 26 | Only one argument is acceptable, and it covers both IPv4 and IPv6. 27 | 28 | .SH "EXAMPLES" 29 | .PP 30 | Enable limiting the buffer size of outgoing query to the resolver (172.31.0.10): 31 | 32 | .PP 33 | .RS 34 | 35 | .nf 36 | \&. { 37 | bufsize 512 38 | forward . 172.31.0.10 39 | log 40 | } 41 | 42 | .fi 43 | .RE 44 | 45 | .PP 46 | Enable limiting the buffer size as an authoritative nameserver: 47 | 48 | .PP 49 | .RS 50 | 51 | .nf 52 | \&. { 53 | bufsize 512 54 | file db.example.org 55 | log 56 | } 57 | 58 | .fi 59 | .RE 60 | 61 | .SH "CONSIDERATIONS" 62 | .IP \(bu 4 63 | 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. 64 | .IP \(bu 4 65 | For now, if a client does not use EDNS, this plugin adds OPT RR. 66 | 67 | 68 | -------------------------------------------------------------------------------- /man/coredns-cancel.7: -------------------------------------------------------------------------------- 1 | .\" Generated by Mmark Markdown Processer - mmark.miek.nl 2 | .TH "COREDNS-CANCEL" 7 "August 2020" "CoreDNS" "CoreDNS Plugins" 3 | 4 | .SH "NAME" 5 | .PP 6 | \fIcancel\fP - cancels a request's context after 5001 milliseconds. 7 | 8 | .SH "DESCRIPTION" 9 | .PP 10 | The \fIcancel\fP plugin creates a canceling context for each request. It adds a timeout that gets 11 | triggered after 5001 milliseconds. 12 | 13 | .PP 14 | The 5001 number was chosen because the default timeout for DNS clients is 5 seconds, after that they 15 | give up. 16 | 17 | .PP 18 | A plugin interested in the cancellation status should call \fB\fCplugin.Done()\fR on the context. If the 19 | context was canceled due to a timeout the plugin should not write anything back to the client and 20 | return a value indicating CoreDNS should not either; a zero return value should suffice for that. 21 | 22 | .SH "SYNTAX" 23 | .PP 24 | .RS 25 | 26 | .nf 27 | cancel [TIMEOUT] 28 | 29 | .fi 30 | .RE 31 | 32 | .IP \(bu 4 33 | \fBTIMEOUT\fP allows setting a custom timeout. The default timeout is 5001 milliseconds (\fB\fC5001 ms\fR) 34 | 35 | 36 | .SH "EXAMPLES" 37 | .PP 38 | .RS 39 | 40 | .nf 41 | example.org { 42 | cancel 43 | whoami 44 | } 45 | 46 | .fi 47 | .RE 48 | 49 | .PP 50 | Or with a custom timeout: 51 | 52 | .PP 53 | .RS 54 | 55 | .nf 56 | example.org { 57 | cancel 1s 58 | whoami 59 | } 60 | 61 | .fi 62 | .RE 63 | 64 | .SH "ALSO SEE" 65 | .PP 66 | The Go documentation for the context package. 67 | 68 | -------------------------------------------------------------------------------- /man/coredns-loadbalance.7: -------------------------------------------------------------------------------- 1 | .\" Generated by Mmark Markdown Processer - mmark.miek.nl 2 | .TH "COREDNS-LOADBALANCE" 7 "August 2020" "CoreDNS" "CoreDNS Plugins" 3 | 4 | .SH "NAME" 5 | .PP 6 | \fIloadbalance\fP - randomizes the order of A, AAAA and MX records. 7 | 8 | .SH "DESCRIPTION" 9 | .PP 10 | The \fIloadbalance\fP will act as a round-robin DNS load balancer by randomizing the order of A, AAAA, 11 | and MX records in the answer. 12 | 13 | .PP 14 | See Wikipedia 15 | \[la]https://en.wikipedia.org/wiki/Round-robin_DNS\[ra] about the pros and cons of this 16 | setup. It will take care to sort any CNAMEs before any address records, because some stub resolver 17 | implementations (like glibc) are particular about that. 18 | 19 | .SH "SYNTAX" 20 | .PP 21 | .RS 22 | 23 | .nf 24 | loadbalance [POLICY] 25 | 26 | .fi 27 | .RE 28 | 29 | .IP \(bu 4 30 | \fBPOLICY\fP is how to balance. The default, and only option, is "round_robin". 31 | 32 | 33 | .SH "EXAMPLES" 34 | .PP 35 | Load balance replies coming back from Google Public DNS: 36 | 37 | .PP 38 | .RS 39 | 40 | .nf 41 | \&. { 42 | loadbalance round\_robin 43 | forward . 8.8.8.8 8.8.4.4 44 | } 45 | 46 | .fi 47 | .RE 48 | 49 | -------------------------------------------------------------------------------- /man/coredns-root.7: -------------------------------------------------------------------------------- 1 | .\" Generated by Mmark Markdown Processer - mmark.miek.nl 2 | .TH "COREDNS-ROOT" 7 "August 2020" "CoreDNS" "CoreDNS Plugins" 3 | 4 | .SH "NAME" 5 | .PP 6 | \fIroot\fP - simply specifies the root of where to find (zone) files. 7 | 8 | .SH "DESCRIPTION" 9 | .PP 10 | The default root is the current working directory of CoreDNS. The \fIroot\fP plugin allows you to change 11 | this. A relative root path is relative to the current working directory. 12 | 13 | .PP 14 | This plugin can only be used once per Server Block. 15 | 16 | .SH "SYNTAX" 17 | .PP 18 | .RS 19 | 20 | .nf 21 | root PATH 22 | 23 | .fi 24 | .RE 25 | 26 | .PP 27 | \fBPATH\fP is the directory to set as CoreDNS' root. 28 | 29 | .SH "EXAMPLES" 30 | .PP 31 | Serve zone data (when the \fIfile\fP plugin is used) from \fB\fC/etc/coredns/zones\fR: 32 | 33 | .PP 34 | .RS 35 | 36 | .nf 37 | \&. { 38 | root /etc/coredns/zones 39 | } 40 | 41 | .fi 42 | .RE 43 | 44 | -------------------------------------------------------------------------------- /man/coredns-transfer.7: -------------------------------------------------------------------------------- 1 | .\" Generated by Mmark Markdown Processer - mmark.miek.nl 2 | .TH "COREDNS-TRANSFER" 7 "August 2020" "CoreDNS" "CoreDNS Plugins" 3 | 4 | .SH "NAME" 5 | .PP 6 | \fItransfer\fP - perform zone transfers for other plugins. 7 | 8 | .SH "DESCRIPTION" 9 | .PP 10 | This plugin answers zone transfers for authoritative plugins that implement 11 | \fB\fCtransfer.Transferer\fR. Currently, no internal plugins implement this interface. 12 | 13 | .PP 14 | Transfer answers full zone transfer (AXFR) requests and incremental zone transfer (IXFR) requests 15 | with AXFR fallback if the zone has changed. 16 | 17 | .PP 18 | Notifies are not currently supported. 19 | 20 | .SH "SYNTAX" 21 | .PP 22 | .RS 23 | 24 | .nf 25 | transfer [ZONE...] { 26 | to HOST... 27 | } 28 | 29 | .fi 30 | .RE 31 | 32 | .IP \(bu 4 33 | \fBZONES\fP The zones \fItransfer\fP will answer zone requests for. If left blank, 34 | the zones are inherited from the enclosing server block. To answer zone 35 | transfers for a given zone, there must be another plugin in the same server 36 | block that serves the same zone, and implements \fB\fCtransfer.Transferer\fR. 37 | .IP \(bu 4 38 | \fB\fCto\fR \fBHOST...\fP The hosts \fItransfer\fP will transfer to. Use \fB\fC*\fR to permit 39 | transfers to all hosts. 40 | 41 | 42 | .SH "EXAMPLES" 43 | .PP 44 | TODO 45 | 46 | -------------------------------------------------------------------------------- /notes/coredns-0.9.10.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-0.9.10 Release" 3 | description = "CoreDNS-0.9.10 Release Notes." 4 | tags = ["Release", "0.9.10", "Notes"] 5 | draft = false 6 | release = "0.9.10" 7 | date = "2017-11-03T20:45:43-00:00" 8 | author = "coredns" 9 | +++ 10 | 11 | CoreDNS-0.9.10 has been [released](https://github.com/coredns/coredns/releases/tag/v0.9.10)! 12 | 13 | CoreDNS is a DNS server that chains plugins, where each plugin implements a DNS feature. 14 | 15 | Release 0.9.10 is a minor release, with some fixes. 16 | 17 | ## Core 18 | 19 | * The reverse zone syntax was extended to allow non-octet boundaries: 20 | 21 | ~~~ 22 | 192.168.1.0/17 { 23 | ... 24 | } 25 | ~~~ 26 | 27 | Will behave correctly. 28 | 29 | * Lots of documentation clean ups. 30 | * More platforms have binaries for each release. 31 | 32 | ## Plugins 33 | 34 | * *dnssec* will now insert DS records (and sign them) when it signs a delegation response. 35 | * *host* now checks for /etc/hosts updates in a separate go-routine. 36 | 37 | ## Contributors 38 | 39 | The following people helped with getting this release done: 40 | Chris O'Haver, 41 | Miek Gieben, 42 | Pat Moroney, 43 | Paul Hoffman, 44 | Sandeep Rajan, 45 | Yong Tang. 46 | 47 | If you want to help, please check out one of the [issues](https://github.com/coredns/coredns/issues/) 48 | and start coding! 49 | 50 | For documentation and help, see our [community page](https://coredns.io/community/). 51 | -------------------------------------------------------------------------------- /notes/coredns-009.md: -------------------------------------------------------------------------------- 1 | +++ 2 | date = "2017-07-13T22:52:11Z" 3 | release = "009" 4 | description = "CoreDNS-009 Release Notes." 5 | tags = ["Release", "009", "Notes"] 6 | title = "CoreDNS-009 Release" 7 | author = "coredns" 8 | +++ 9 | 10 | CoreDNS-009 has been [released](https://github.com/coredns/coredns/releases/tag/v009)! 11 | 12 | CoreDNS is a DNS server that chains plugins, where each plugin implements a DNS feature. 13 | 14 | Release v009 is mostly a bugfix release, with a few new features in the plugin. 15 | 16 | # Core 17 | 18 | No changes. 19 | 20 | # Plugins 21 | 22 | * *secondary*: fix functionality and improve matching of notify queries. 23 | * *cache*: fix data race. 24 | * *proxy*: async healthchecks. 25 | * *reverse*: new option `wildcard` that also catches all subdomains of a template. 26 | * *kubernetes*: experimental new option `autopath` that optimizes the search path and ndots 27 | combinatorial explosion, so clients with a large search path and high ndots will get a reply on 28 | the first query. 29 | 30 | # Contributors 31 | 32 | The following people helped with getting this release done: 33 | 34 | Athir Nuaimi, 35 | Chris O'Haver, 36 | ghostflame, 37 | jremond, 38 | Mia Boulay, 39 | Miek Gieben, 40 | Ning Xie, 41 | Roman Mazur, 42 | Yong Tang. 43 | 44 | If you want to help, please check out one of the [issues](https://github.com/coredns/coredns/issues/) 45 | and start coding! 46 | 47 | For documentation and help, see our [community page](https://coredns.io/community/). 48 | -------------------------------------------------------------------------------- /notes/coredns-1.0.1.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.0.1 Release" 3 | description = "CoreDNS-1.0.1 Release Notes." 4 | tags = ["Release", "1.0.1", "Notes"] 5 | draft = false 6 | release = "1.0.1" 7 | date = "2017-12-11T14:43:43-00:00" 8 | author = "coredns" 9 | +++ 10 | 11 | We are pleased to announce the [release](https://github.com/coredns/coredns/releases/tag/v1.0.1) of CoreDNS-1.0.1! 12 | 13 | This release fixes a crash in the *file* plugin and has some minor bug fixes for other plugins. 14 | One new plugin was added: *nsid*, that implements [RFC 5001](https://tools.ietf.org/html/rfc5001). 15 | 16 | ## Plugins 17 | * *file* fixes a crash when an request with a DO bit (pretty much the default) hits an unsigned zone. The default configuration should recover the go-routine, but this is nonetheless serious. *file* received some other fixes when returning (secure) delegations. 18 | * *dnstap* plugin is now 50% faster. 19 | * *metrics* fixed the start time bucket for the duration. 20 | 21 | ## Contributors 22 | 23 | The following people helped with getting this release done: 24 | Brad Beam, 25 | James Hartig, 26 | Miek Gieben, 27 | Rene Treffer, 28 | Ruslan Drozhdzh, 29 | Seansean2, 30 | Yong Tang. 31 | 32 | If you want to help, please check out one of the 33 | [issues](https://github.com/coredns/coredns/issues/) and start coding! For documentation and help, 34 | see our [community page](https://coredns.io/community/). 35 | -------------------------------------------------------------------------------- /notes/coredns-1.0.4.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.0.4 Release" 3 | description = "CoreDNS-1.0.4 Release Notes." 4 | tags = ["Release", "1.0.4", "Notes"] 5 | draft = false 6 | release = "1.0.4" 7 | date = "2018-01-18T15:54:29+00:00" 8 | author = "coredns" 9 | +++ 10 | 11 | We are announcing the [release](https://github.com/coredns/coredns/releases/tag/v1.0.4) of CoreDNS-1.0.4! 12 | 13 | This is a release that fixes a vulnerability in the underlying DNS library. 14 | See and the (still embargoed) CVE-2017-15133. 15 | Thanks to Tom Thorogood for bringing this issue to our attention. 16 | 17 | CoreDNS-1.0.4 is [CoreDNS-1.0.3](https://coredns.io/2018/01/10/coredns-1.0.3-release/) recompiled with a patched DNS library. 18 | -------------------------------------------------------------------------------- /notes/coredns-1.1.1.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.1.1 Release" 3 | description = "CoreDNS-1.1.1 Release Notes." 4 | tags = ["Release", "1.1.1", "Notes"] 5 | release = "1.1.1" 6 | date = "2018-03-25T18:04:29+01:00" 7 | author = "coredns" 8 | +++ 9 | 10 | We are pleased to announce the [release](https://github.com/coredns/coredns/releases/tag/v1.1.1) of 11 | CoreDNS-1.1.1! 12 | 13 | This release fixes a **critical bug** in the *cache* plugin found by [Cure53](/2018/03/15/cure53-security-assessment/). 14 | 15 | All users are encouraged to upgrade. 16 | 17 | ## Core 18 | 19 | Fix a bug when scrubbing the reply to fit the request's buffer consumes 100% CPU and does not return 20 | the reply. 21 | 22 | ## Plugins 23 | 24 | * [*cache*](/plugins/cache) fixes the critical spoof vulnerability. 25 | * [*route53*](/plugins/route53) adds support for PTR records. 26 | 27 | ## Contributors 28 | 29 | The following people helped with getting this release done: 30 | 31 | Chris O'Haver, 32 | Mario Kleinsasser, 33 | Miek Gieben, 34 | Yong Tang. 35 | 36 | And of course the people in [Cure53](https://cure53.de). Also special shout out to Mario Kleinsasser 37 | for helping to debug the [scrubbing issue](https://github.com/coredns/coredns/issues/1625). 38 | 39 | For documentation see our (in progress!) [manual](/manual). For help and other resources, see our 40 | [community page](https://coredns.io/community/). 41 | -------------------------------------------------------------------------------- /notes/coredns-1.2.1.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.2.1 Release" 3 | description = "CoreDNS-1.2.1 Release Notes." 4 | tags = ["Release", "1.2.1", "Notes"] 5 | release = "1.2.1" 6 | date = "2018-08-28T07:10:29+01:00" 7 | author = "coredns" 8 | +++ 9 | 10 | We are pleased to announce the [release](https://github.com/coredns/coredns/releases/tag/v1.2.1) of 11 | CoreDNS-1.2.1! 12 | 13 | This release features bugfixes (mostly in the [*kubernetes*](/plugins/kubernetes) plugin), 14 | documentation improvements and one new plugin: [*loop*](/plugins/loop). 15 | 16 | # Plugins 17 | 18 | * A new plugin called [*loop*](/plugins/loop) was added. When starting up it detects resolver loops 19 | and stops the process if one is detected. 20 | 21 | ## Contributors 22 | 23 | The following people helped with getting this release done. Good to see a whole bunch of new names, 24 | as well as the usual suspects: 25 | 26 | Bingshen Wang, 27 | Chris O'Haver, 28 | Eugen Kleiner, 29 | Francois Tur, 30 | Jiacheng Xu, 31 | Karsten Weiss, 32 | Lorenzo Fontana, 33 | Miek Gieben, 34 | Nitish Tiwari, 35 | Stanislav Zapolsky, 36 | varyoo, 37 | Yong Tang, 38 | Zach Eddy. 39 | 40 | For documentation see our (in progress!) [manual](/manual). For help and other resources, see our 41 | [community page](https://coredns.io/community/). 42 | -------------------------------------------------------------------------------- /notes/coredns-1.2.2.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.2.2 Release" 3 | description = "CoreDNS-1.2.2 Release Notes." 4 | tags = ["Release", "1.2.2", "Notes"] 5 | release = "1.2.2" 6 | date = "2018-08-29T07:10:29+01:00" 7 | author = "coredns" 8 | +++ 9 | 10 | We are pleased to announce the [release](https://github.com/coredns/coredns/releases/tag/v1.2.2) of 11 | CoreDNS-1.2.2! 12 | 13 | This is a (small) release that helps out our friends at 14 | [kops](https://github.com/kubernetes/kops/issues/5652): make the default cache size smaller. 15 | 16 | ## Contributors 17 | 18 | The following people helped with getting this release done: 19 | 20 | Chris O'Haver, 21 | Miek Gieben. 22 | 23 | For documentation see our (in progress!) [manual](/manual). For help and other resources, see our 24 | [community page](https://coredns.io/community/). 25 | -------------------------------------------------------------------------------- /notes/coredns-1.2.4.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.2.4 Release" 3 | description = "CoreDNS-1.2.4 Release Notes." 4 | tags = ["Release", "1.2.4", "Notes"] 5 | release = "1.2.4" 6 | date = "2018-10-17T20:01:29+01:00" 7 | author = "coredns" 8 | +++ 9 | 10 | We are pleased to announce the [release](https://github.com/coredns/coredns/releases/tag/v1.2.4) of 11 | CoreDNS-1.2.4! 12 | 13 | Remember we [said the 1.2.3 release](/2018/10/16/coredns-1.2.3-release/) was a big release and took 14 | quite a while? Well, we've fixed that glitch; as 1.2.4 is here now. 15 | 16 | CoreDNS v1.2.3's *kubernetes* plugin **DOES NOT WORK IN KUBERNETES** and our testing that didn't catch 17 | that regression, nor the Kubernetes scale testing which doesn't really exercise the *whole* API. 18 | 19 | ## Plugins 20 | 21 | * [*cache*](/plugins/cache) use zero of the minimal negative TTL (if no suitable TTL was found in 22 | the packet). 23 | * [*kubernetes*](/plugins/kubernetes) fix a grave bug that made plugin **unusable** in Kubernetes. 24 | 25 | ## Brought to you by: 26 | 27 | Chris O'Haver, 28 | Miek Gieben. 29 | 30 | For documentation see our (in progress!) [manual](/manual). For help and other resources, see our 31 | [community page](https://coredns.io/community/). 32 | -------------------------------------------------------------------------------- /notes/coredns-1.2.5.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.2.5 Release" 3 | description = "CoreDNS-1.2.5 Release Notes." 4 | tags = ["Release", "1.2.5", "Notes"] 5 | release = "1.2.5" 6 | date = "2018-10-24T20:40:29+01:00" 7 | author = "coredns" 8 | +++ 9 | 10 | We are pleased to announce the [release](https://github.com/coredns/coredns/releases/tag/v1.2.5) of 11 | CoreDNS-1.2.5! 12 | 13 | ## Core 14 | 15 | Correctly make a reply fit in the client's buffer, *especially* when EDNS0 is not used. 16 | This used to be the responsibility of a plugin, now the server will handle it. 17 | 18 | ## Plugins 19 | 20 | Documentation and smaller updates for various plugins, as well as: 21 | 22 | * [*cache*](/plugins/cache) - resets min TTL default back to 5 second (instead of 0). 23 | * [*dnssec*](/plugins/dnssec) - now allows aZSK/KSK split as well as a CSK setup. 24 | * [*rewrite*](/plugins/rewrite) - answer rewrite is now automatic for _exact_ name rewrites. 25 | 26 | ## Brought to you by 27 | 28 | Andrey Meshkov, 29 | Chris O'Haver, 30 | Francois Tur, 31 | Kevin Nisbet, 32 | Manuel Stocker, 33 | Miek Gieben, 34 | Paul G, 35 | Ruslan Drozhdzh, 36 | Sandeep Rajan, 37 | Yong Tang. 38 | 39 | For documentation see our (in progress!) [manual](/manual). For help and other resources, see our 40 | [community page](https://coredns.io/community/). 41 | -------------------------------------------------------------------------------- /notes/coredns-1.6.1.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.6.1 Release" 3 | description = "CoreDNS-1.6.1 Release Notes." 4 | tags = ["Release", "1.6.1", "Notes"] 5 | release = "1.6.1" 6 | date = 2019-08-02T14:35:47+01:00 7 | author = "coredns" 8 | +++ 9 | 10 | The CoreDNS team has released 11 | [CoreDNS-1.6.1](https://github.com/coredns/coredns/releases/tag/v1.6.1). 12 | 13 | This is a small (bug fix) release. 14 | 15 | # Plugins 16 | 17 | * Fix a panic in the [*hosts*](/plugins/hosts) plugin. 18 | * The [*reload*](/plugins/reload) now detects changes in files imported from the main Corefile. 19 | * [*route53*](/plugins/route53) increases the paging size when talking to the AWS API, this 20 | decreases the chances of getting throttled. 21 | 22 | ## Brought to You By 23 | 24 | Alan, 25 | AllenZMC, 26 | dzzg, 27 | Erik Wilson, 28 | Matt Kulka, 29 | Miek Gieben, 30 | Yong Tang. 31 | 32 | ## Noteworthy Changes 33 | 34 | core: log panics (https://github.com/coredns/coredns/pull/3072) 35 | plugin/hosts: create inline map in setup (https://github.com/coredns/coredns/pull/3071) 36 | plugin/reload: Graceful reload of imported files (https://github.com/coredns/coredns/pull/3068) 37 | plugin/route53: Increase ListResourceRecordSets paging size. (https://github.com/coredns/coredns/pull/3073) 38 | -------------------------------------------------------------------------------- /notes/coredns-1.6.4.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "CoreDNS-1.6.4 Release" 3 | description = "CoreDNS-1.6.4 Release Notes." 4 | tags = ["Release", "1.6.4", "Notes"] 5 | release = "1.6.4" 6 | date = 2019-09-27T10:00:00+00:00 7 | author = "coredns" 8 | +++ 9 | 10 | The CoreDNS team has released 11 | [CoreDNS-1.6.4](https://github.com/coredns/coredns/releases/tag/v1.6.4). 12 | 13 | Various code cleanups and documentation improvements. We've added one new plugin: *acl*, that allows 14 | blocking requests. 15 | 16 | # Plugins 17 | 18 | * [*acl*](/plugins/acl) block request from IPs or IP ranges. 19 | * [*kubernetes*](/plugins/kubernetes) received some bug fixes, see below for specific PRs. 20 | * [*hosts*](/plugins/hosts) exports metrics on the number of entries and last reload time. 21 | 22 | ## Brought to You By 23 | 24 | An Xiao, 25 | Chris O'Haver, 26 | Cricket Liu, 27 | Guangming Wang, 28 | Kasisnu, 29 | li mengyang, 30 | Miek Gieben, 31 | orangelynx, 32 | xieyanker, 33 | yeya24, 34 | Yong Tang. 35 | 36 | ## Noteworthy Changes 37 | 38 | * plugin/hosts: add host metrics (https://github.com/coredns/coredns/pull/3277) 39 | * plugin/kubernetes: Don't duplicate service record for every port (https://github.com/coredns/coredns/pull/3240) 40 | * plugin/kubernetes: Handle multiple local IPs and bind (https://github.com/coredns/coredns/pull/3208) 41 | * Add plugin ACL for source IP filtering (https://github.com/coredns/coredns/pull/3103) 42 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/acl/metrics.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/any/any.go: -------------------------------------------------------------------------------- 1 | package any 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/any/any_test.go: -------------------------------------------------------------------------------- 1 | package any 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/coredns/coredns/plugin/pkg/dnstest" 8 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/any/setup.go: -------------------------------------------------------------------------------- 1 | package any 2 | 3 | import ( 4 | "github.com/coredns/coredns/core/dnsserver" 5 | "github.com/coredns/coredns/plugin" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func init() { plugin.Register("any", setup) } 11 | 12 | func setup(c *caddy.Controller) error { 13 | a := Any{} 14 | 15 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 16 | a.Next = next 17 | return a 18 | }) 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /plugin/auto/log_test.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/autopath/metrics.go: -------------------------------------------------------------------------------- 1 | package autopath 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coredns/coredns/plugin" 5 | 6 | func init() { plugin.Register("bind", setup) } 7 | -------------------------------------------------------------------------------- /plugin/bind/log_test.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/bind/setup.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/coredns/coredns/core/dnsserver" 8 | "github.com/coredns/coredns/plugin" 9 | 10 | "github.com/caddyserver/caddy" 11 | ) 12 | 13 | func setup(c *caddy.Controller) error { 14 | config := dnsserver.GetConfig(c) 15 | 16 | // addresses will be consolidated over all BIND directives available in that BlocServer 17 | all := []string{} 18 | for c.Next() { 19 | addrs := c.RemainingArgs() 20 | if len(addrs) == 0 { 21 | return plugin.Error("bind", fmt.Errorf("at least one address is expected")) 22 | } 23 | for _, addr := range addrs { 24 | if net.ParseIP(addr) == nil { 25 | return plugin.Error("bind", fmt.Errorf("not a valid IP address: %s", addr)) 26 | } 27 | } 28 | all = append(all, addrs...) 29 | } 30 | config.ListenHosts = all 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /plugin/bind/setup_test.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/coredns/coredns/core/dnsserver" 7 | 8 | "github.com/caddyserver/caddy" 9 | ) 10 | 11 | func TestSetup(t *testing.T) { 12 | for i, test := range []struct { 13 | config string 14 | expected []string 15 | failing bool 16 | }{ 17 | {`bind 1.2.3.4`, []string{"1.2.3.4"}, false}, 18 | {`bind`, nil, true}, 19 | {`bind 1.2.3.invalid`, nil, true}, 20 | {`bind 1.2.3.4 ::5`, []string{"1.2.3.4", "::5"}, false}, 21 | {`bind ::1 1.2.3.4 ::5 127.9.9.0`, []string{"::1", "1.2.3.4", "::5", "127.9.9.0"}, false}, 22 | {`bind ::1 1.2.3.4 ::5 127.9.9.0 noone`, nil, true}, 23 | } { 24 | c := caddy.NewTestController("dns", test.config) 25 | err := setup(c) 26 | if err != nil { 27 | if !test.failing { 28 | t.Fatalf("Test %d, expected no errors, but got: %v", i, err) 29 | } 30 | continue 31 | } 32 | if test.failing { 33 | t.Fatalf("Test %d, expected to failed but did not, returned values", i) 34 | } 35 | cfg := dnsserver.GetConfig(c) 36 | if len(cfg.ListenHosts) != len(test.expected) { 37 | t.Errorf("Test %d : expected the config's ListenHosts size to be %d, was %d", i, len(test.expected), len(cfg.ListenHosts)) 38 | continue 39 | } 40 | for i, v := range test.expected { 41 | if got, want := cfg.ListenHosts[i], v; got != want { 42 | t.Errorf("Test %d : expected the config's ListenHost to be %s, was %s", i, want, got) 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/bufsize/setup.go: -------------------------------------------------------------------------------- 1 | package bufsize 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/coredns/coredns/core/dnsserver" 7 | "github.com/coredns/coredns/plugin" 8 | 9 | "github.com/caddyserver/caddy" 10 | ) 11 | 12 | func init() { plugin.Register("bufsize", setup) } 13 | 14 | func setup(c *caddy.Controller) error { 15 | bufsize, err := parse(c) 16 | if err != nil { 17 | return plugin.Error("bufsize", err) 18 | } 19 | 20 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 21 | return Bufsize{Next: next, Size: bufsize} 22 | }) 23 | 24 | return nil 25 | } 26 | 27 | func parse(c *caddy.Controller) (int, error) { 28 | const defaultBufSize = 512 29 | for c.Next() { 30 | args := c.RemainingArgs() 31 | switch len(args) { 32 | case 0: 33 | // Nothing specified; use 512 as default 34 | return defaultBufSize, nil 35 | case 1: 36 | // Specified value is needed to verify 37 | bufsize, err := strconv.Atoi(args[0]) 38 | if err != nil { 39 | return -1, plugin.Error("bufsize", c.ArgErr()) 40 | } 41 | // Follows RFC 6891 42 | if bufsize < 512 || bufsize > 4096 { 43 | return -1, plugin.Error("bufsize", c.ArgErr()) 44 | } 45 | return bufsize, nil 46 | default: 47 | // Only 1 argument is acceptable 48 | return -1, plugin.Error("bufsize", c.ArgErr()) 49 | } 50 | } 51 | return -1, plugin.Error("bufsize", c.ArgErr()) 52 | } 53 | -------------------------------------------------------------------------------- /plugin/bufsize/setup_test.go: -------------------------------------------------------------------------------- 1 | package bufsize 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func TestSetupBufsize(t *testing.T) { 11 | tests := []struct { 12 | input string 13 | shouldErr bool 14 | expectedData int 15 | expectedErrContent string // substring from the expected error. Empty for positive cases. 16 | }{ 17 | {`bufsize`, false, 512, ""}, 18 | {`bufsize "1232"`, false, 1232, ""}, 19 | {`bufsize "5000"`, true, -1, "plugin"}, 20 | {`bufsize "512 512"`, true, -1, "plugin"}, 21 | {`bufsize "abc123"`, true, -1, "plugin"}, 22 | } 23 | 24 | for i, test := range tests { 25 | c := caddy.NewTestController("dns", test.input) 26 | bufsize, err := parse(c) 27 | 28 | if test.shouldErr && err == nil { 29 | t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input) 30 | } 31 | 32 | if err != nil { 33 | if !test.shouldErr { 34 | t.Errorf("Test %d: Error found for input %s. Error: %v", i, test.input, err) 35 | } 36 | 37 | if !strings.Contains(err.Error(), test.expectedErrContent) { 38 | t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input) 39 | } 40 | } 41 | 42 | if !test.shouldErr && bufsize != test.expectedData { 43 | t.Errorf("Test %d: Bufsize not correctly set for input %s. Expected: %d, actual: %d", i, test.input, test.expectedData, bufsize) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugin/cache/error_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/coredns/coredns/plugin" 8 | "github.com/coredns/coredns/plugin/pkg/dnstest" 9 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/cache/freq/freq.go: -------------------------------------------------------------------------------- 1 | // Package freq keeps track of last X seen events. The events themselves are not stored 2 | // here. So the Freq type should be added next to the thing it is tracking. 3 | package freq 4 | 5 | import ( 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // Freq tracks the frequencies of things. 11 | type Freq struct { 12 | // Last time we saw a query for this element. 13 | last time.Time 14 | // Number of this in the last time slice. 15 | hits int 16 | 17 | sync.RWMutex 18 | } 19 | 20 | // New returns a new initialized Freq. 21 | func New(t time.Time) *Freq { 22 | return &Freq{last: t, hits: 0} 23 | } 24 | 25 | // Update updates the number of hits. Last time seen will be set to now. 26 | // If the last time we've seen this entity is within now - d, we increment hits, otherwise 27 | // we reset hits to 1. It returns the number of hits. 28 | func (f *Freq) Update(d time.Duration, now time.Time) int { 29 | earliest := now.Add(-1 * d) 30 | f.Lock() 31 | defer f.Unlock() 32 | if f.last.Before(earliest) { 33 | f.last = now 34 | f.hits = 1 35 | return f.hits 36 | } 37 | f.last = now 38 | f.hits++ 39 | return f.hits 40 | } 41 | 42 | // Hits returns the number of hits that we have seen, according to the updates we have done to f. 43 | func (f *Freq) Hits() int { 44 | f.RLock() 45 | defer f.RUnlock() 46 | return f.hits 47 | } 48 | 49 | // Reset resets f to time t and hits to hits. 50 | func (f *Freq) Reset(t time.Time, hits int) { 51 | f.Lock() 52 | defer f.Unlock() 53 | f.last = t 54 | f.hits = hits 55 | } 56 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/cache/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package cache 4 | 5 | import ( 6 | "github.com/coredns/coredns/plugin/pkg/fuzz" 7 | ) 8 | 9 | // Fuzz fuzzes cache. 10 | func Fuzz(data []byte) int { 11 | return fuzz.Do(New(), data) 12 | } 13 | -------------------------------------------------------------------------------- /plugin/cache/log_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/cancel/cancel_test.go: -------------------------------------------------------------------------------- 1 | package cancel 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/coredns/coredns/plugin" 9 | "github.com/coredns/coredns/plugin/pkg/dnstest" 10 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/cancel/setup_test.go: -------------------------------------------------------------------------------- 1 | package cancel 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/chaos/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package chaos 4 | 5 | import ( 6 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/chaos/log_test.go: -------------------------------------------------------------------------------- 1 | package chaos 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/clouddns/log_test.go: -------------------------------------------------------------------------------- 1 | package clouddns 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/clouddns/setup_test.go: -------------------------------------------------------------------------------- 1 | package clouddns 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/caddyserver/caddy" 8 | "google.golang.org/api/option" 9 | ) 10 | 11 | func TestSetupCloudDNS(t *testing.T) { 12 | f = func(ctx context.Context, opt option.ClientOption) (gcpDNS, error) { 13 | return fakeGCPClient{}, nil 14 | } 15 | 16 | tests := []struct { 17 | body string 18 | expectedError bool 19 | }{ 20 | {`clouddns`, false}, 21 | {`clouddns :`, true}, 22 | {`clouddns ::`, true}, 23 | {`clouddns example.org.:example-project:zone-name`, false}, 24 | {`clouddns example.org.:example-project:zone-name { }`, false}, 25 | {`clouddns example.org.:example-project: { }`, true}, 26 | {`clouddns example.org.:example-project:zone-name { }`, false}, 27 | {`clouddns example.org.:example-project:zone-name { wat 28 | }`, true}, 29 | {`clouddns example.org.:example-project:zone-name { 30 | fallthrough 31 | }`, false}, 32 | {`clouddns example.org.:example-project:zone-name { 33 | credentials 34 | }`, true}, 35 | {`clouddns example.org.:example-project:zone-name example.org.:example-project:zone-name { 36 | }`, true}, 37 | 38 | {`clouddns example.org { 39 | }`, true}, 40 | } 41 | 42 | for _, test := range tests { 43 | c := caddy.NewTestController("dns", test.body) 44 | if err := setup(c); (err == nil) == test.expectedError { 45 | t.Errorf("Unexpected errors: %v", err) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /plugin/debug/README.md: -------------------------------------------------------------------------------- 1 | # debug 2 | 3 | ## Name 4 | 5 | *debug* - disables the automatic recovery upon a crash so that you'll get a nice stack trace. 6 | 7 | ## Description 8 | 9 | Normally CoreDNS will recover from panics; using *debug* inhibits this. The main use of *debug* is 10 | to help in testing. A side effect of using *debug* is that `log.Debug` and `log.Debugf` messages 11 | will be printed to standard output. 12 | 13 | Note that the *errors* plugin (if loaded) will also set a `recover`, negating this setting. 14 | 15 | ## Syntax 16 | 17 | ~~~ txt 18 | debug 19 | ~~~ 20 | 21 | Some plugins will send debug log DNS messages. This is done in the following format: 22 | 23 | ~~~ 24 | debug: 000000 00 0a 01 00 00 01 00 00 00 00 00 01 07 65 78 61 25 | debug: 000010 6d 70 6c 65 05 6c 6f 63 61 6c 00 00 01 00 01 00 26 | debug: 000020 00 29 10 00 00 00 80 00 00 00 27 | debug: 00002a 28 | ~~~ 29 | 30 | Using `text2pcap` (part of Wireshark), this can be converted back to binary, with the following 31 | command line: `text2pcap -i 17 -u 53,53`, where 17 is the protocol (UDP) and 53 are the ports. These 32 | ports allow Wireshark to detect these packets as DNS messages. 33 | 34 | Each plugin can decide whether to dump messages to aid in debugging. 35 | 36 | ## Examples 37 | 38 | Disable the ability to recover from crashes and show debug logging: 39 | 40 | ~~~ corefile 41 | . { 42 | debug 43 | } 44 | ~~~ 45 | 46 | ## Also See 47 | 48 | . 49 | -------------------------------------------------------------------------------- /plugin/debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "github.com/coredns/coredns/core/dnsserver" 5 | "github.com/coredns/coredns/plugin" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func init() { plugin.Register("debug", setup) } 11 | 12 | func setup(c *caddy.Controller) error { 13 | config := dnsserver.GetConfig(c) 14 | 15 | for c.Next() { 16 | if c.NextArg() { 17 | return plugin.Error("debug", c.ArgErr()) 18 | } 19 | config.Debug = true 20 | } 21 | 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /plugin/debug/debug_test.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/coredns/coredns/core/dnsserver" 7 | 8 | "github.com/caddyserver/caddy" 9 | ) 10 | 11 | func TestDebug(t *testing.T) { 12 | tests := []struct { 13 | input string 14 | shouldErr bool 15 | expectedDebug bool 16 | }{ 17 | // positive 18 | { 19 | `debug`, false, true, 20 | }, 21 | // negative 22 | { 23 | `debug off`, true, false, 24 | }, 25 | } 26 | 27 | for i, test := range tests { 28 | c := caddy.NewTestController("dns", test.input) 29 | err := setup(c) 30 | cfg := dnsserver.GetConfig(c) 31 | 32 | if test.shouldErr && err == nil { 33 | t.Fatalf("Test %d: Expected error but found %s for input %s", i, err, test.input) 34 | } 35 | 36 | if err != nil { 37 | if !test.shouldErr { 38 | t.Fatalf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err) 39 | } 40 | } 41 | if cfg.Debug != test.expectedDebug { 42 | t.Fatalf("Test %d: Expected debug to be: %t, but got: %t, input: %s", i, test.expectedDebug, cfg.Debug, test.input) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /plugin/debug/log_test.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/deprecated/setup.go: -------------------------------------------------------------------------------- 1 | // Package deprecated is used when we deprecated plugin. In plugin.cfg just go from 2 | // 3 | // startup:github.com/caddyserver/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/coredns/coredns/plugin" 18 | 19 | "github.com/caddyserver/caddy" 20 | ) 21 | 22 | // removed has the names of the plugins that need to error on startup. 23 | var removed = []string{""} 24 | 25 | func setup(c *caddy.Controller) error { 26 | c.Next() 27 | x := c.Val() 28 | return plugin.Error(x, errors.New("this plugin has been deprecated")) 29 | } 30 | 31 | func init() { 32 | for _, plug := range removed { 33 | plugin.Register(plug, setup) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /plugin/dns64/metrics.go: -------------------------------------------------------------------------------- 1 | package dns64 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 return 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 | -------------------------------------------------------------------------------- /plugin/dnssec/handler.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coredns/coredns/plugin" 7 | "github.com/coredns/coredns/plugin/metrics" 8 | "github.com/coredns/coredns/request" 9 | 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | // ServeDNS implements the plugin.Handler interface. 14 | func (d Dnssec) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { 15 | state := request.Request{W: w, Req: r} 16 | 17 | do := state.Do() 18 | qname := state.Name() 19 | qtype := state.QType() 20 | zone := plugin.Zones(d.zones).Matches(qname) 21 | if zone == "" { 22 | return plugin.NextOrFailure(d.Name(), d.Next, ctx, w, r) 23 | } 24 | 25 | state.Zone = zone 26 | server := metrics.WithServer(ctx) 27 | 28 | // Intercept queries for DNSKEY, but only if one of the zones matches the qname, otherwise we let 29 | // the query through. 30 | if qtype == dns.TypeDNSKEY { 31 | for _, z := range d.zones { 32 | if qname == z { 33 | resp := d.getDNSKEY(state, z, do, server) 34 | resp.Authoritative = true 35 | w.WriteMsg(resp) 36 | return dns.RcodeSuccess, nil 37 | } 38 | } 39 | } 40 | 41 | if do { 42 | drr := &ResponseWriter{w, d, server} 43 | return plugin.NextOrFailure(d.Name(), d.Next, ctx, drr, r) 44 | } 45 | 46 | return plugin.NextOrFailure(d.Name(), d.Next, ctx, w, r) 47 | } 48 | 49 | // Name implements the Handler interface. 50 | func (d Dnssec) Name() string { return "dnssec" } 51 | -------------------------------------------------------------------------------- /plugin/dnssec/log_test.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/dnssec/metrics.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/dnssec/responsewriter.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/coredns/coredns/plugin" 7 | "github.com/coredns/coredns/request" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | // ResponseWriter sign the response on the fly. 13 | type ResponseWriter struct { 14 | dns.ResponseWriter 15 | d Dnssec 16 | server string // server label for metrics. 17 | } 18 | 19 | // WriteMsg implements the dns.ResponseWriter interface. 20 | func (d *ResponseWriter) WriteMsg(res *dns.Msg) error { 21 | // By definition we should sign anything that comes back, we should still figure out for 22 | // which zone it should be. 23 | state := request.Request{W: d.ResponseWriter, Req: res} 24 | 25 | zone := plugin.Zones(d.d.zones).Matches(state.Name()) 26 | if zone == "" { 27 | return d.ResponseWriter.WriteMsg(res) 28 | } 29 | state.Zone = zone 30 | 31 | res = d.d.Sign(state, time.Now().UTC(), d.server) 32 | cacheSize.WithLabelValues(d.server, "signature").Set(float64(d.d.cache.Len())) 33 | // No need for EDNS0 trickery, as that is handled by the server. 34 | 35 | return d.ResponseWriter.WriteMsg(res) 36 | } 37 | 38 | // Write implements the dns.ResponseWriter interface. 39 | func (d *ResponseWriter) Write(buf []byte) (int, error) { 40 | log.Warning("Dnssec called with Write: not signing reply") 41 | n, err := d.ResponseWriter.Write(buf) 42 | return n, err 43 | } 44 | -------------------------------------------------------------------------------- /plugin/dnssec/rrsig.go: -------------------------------------------------------------------------------- 1 | package dnssec 2 | 3 | import "github.com/miekg/dns" 4 | 5 | // newRRSIG return 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 | -------------------------------------------------------------------------------- /plugin/dnstap/context_test.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func TestDnstapContext(t *testing.T) { 9 | ctx := ContextWithTapper(context.TODO(), Dnstap{}) 10 | tapper := TapperFromContext(ctx) 11 | 12 | if tapper == nil { 13 | t.Fatal("Can't get tapper") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/dnstap/dnstapio/log_test.go: -------------------------------------------------------------------------------- 1 | package dnstapio 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/dnstap/gocontext.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import "context" 4 | 5 | type contextKey struct{} 6 | 7 | var dnstapKey = contextKey{} 8 | 9 | // ContextWithTapper returns a new `context.Context` that holds a reference to 10 | // `t`'s Tapper. 11 | func ContextWithTapper(ctx context.Context, t Tapper) context.Context { 12 | return context.WithValue(ctx, dnstapKey, t) 13 | } 14 | 15 | // TapperFromContext returns the `Tapper` previously associated with `ctx`, or 16 | // `nil` if no such `Tapper` could be found. 17 | func TapperFromContext(ctx context.Context) Tapper { 18 | val := ctx.Value(dnstapKey) 19 | if sp, ok := val.(Tapper); ok { 20 | return sp 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /plugin/dnstap/log_test.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/dnstap/msg/msg_test.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "net" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/coredns/coredns/plugin/test" 9 | "github.com/coredns/coredns/request" 10 | 11 | tap "github.com/dnstap/golang-dnstap" 12 | "github.com/miekg/dns" 13 | ) 14 | 15 | func testRequest(t *testing.T, expected Builder, r request.Request) { 16 | d := Builder{} 17 | d.Addr(r.W.RemoteAddr()) 18 | if d.SocketProto != expected.SocketProto || 19 | d.SocketFam != expected.SocketFam || 20 | !reflect.DeepEqual(d.Address, expected.Address) || 21 | d.Port != expected.Port { 22 | t.Fatalf("Expected: %v, have: %v", expected, d) 23 | return 24 | } 25 | } 26 | func TestRequest(t *testing.T) { 27 | testRequest(t, Builder{ 28 | SocketProto: tap.SocketProtocol_UDP, 29 | SocketFam: tap.SocketFamily_INET, 30 | Address: net.ParseIP("10.240.0.1"), 31 | Port: 40212, 32 | }, testingRequest()) 33 | } 34 | func testingRequest() request.Request { 35 | m := new(dns.Msg) 36 | m.SetQuestion("example.com.", dns.TypeA) 37 | m.SetEdns0(4097, true) 38 | return request.Request{W: &test.ResponseWriter{}, Req: m} 39 | } 40 | -------------------------------------------------------------------------------- /plugin/dnstap/setup_test.go: -------------------------------------------------------------------------------- 1 | package dnstap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/erratic/autopath.go: -------------------------------------------------------------------------------- 1 | package erratic 2 | 3 | import "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/erratic/log_test.go: -------------------------------------------------------------------------------- 1 | package erratic 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/erratic/xfr.go: -------------------------------------------------------------------------------- 1 | package erratic 2 | 3 | import ( 4 | "strings" 5 | "sync" 6 | 7 | "github.com/coredns/coredns/plugin/test" 8 | "github.com/coredns/coredns/request" 9 | 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | // allRecords returns a small zone file. The first RR must be a SOA. 14 | func allRecords(name string) []dns.RR { 15 | var rrs = []dns.RR{ 16 | test.SOA("xx. 0 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2018050825 7200 3600 1209600 3600"), 17 | test.NS("xx. 0 IN NS b.xx."), 18 | test.NS("xx. 0 IN NS a.xx."), 19 | test.AAAA("a.xx. 0 IN AAAA 2001:bd8::53"), 20 | test.AAAA("b.xx. 0 IN AAAA 2001:500::54"), 21 | } 22 | 23 | for _, r := range rrs { 24 | r.Header().Name = strings.Replace(r.Header().Name, "xx.", name, 1) 25 | 26 | if n, ok := r.(*dns.NS); ok { 27 | n.Ns = strings.Replace(n.Ns, "xx.", name, 1) 28 | } 29 | } 30 | return rrs 31 | } 32 | 33 | func xfr(state request.Request, truncate bool) { 34 | rrs := allRecords(state.QName()) 35 | 36 | ch := make(chan *dns.Envelope) 37 | tr := new(dns.Transfer) 38 | 39 | go func() { 40 | // So the rrs we have don't have a closing SOA, only add that when truncate is false, 41 | // so we send an incomplete AXFR. 42 | if !truncate { 43 | rrs = append(rrs, rrs[0]) 44 | } 45 | 46 | ch <- &dns.Envelope{RR: rrs} 47 | close(ch) 48 | }() 49 | 50 | wg := new(sync.WaitGroup) 51 | wg.Add(1) 52 | go func() { 53 | tr.Out(state.W, state.Req, ch) 54 | wg.Done() 55 | }() 56 | wg.Wait() 57 | } 58 | -------------------------------------------------------------------------------- /plugin/errors/benchmark_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/errors/log_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/etcd/log_test.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/etcd/xfr.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/coredns/coredns/request" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | // Serial implements the Transferer interface. 13 | func (e *Etcd) Serial(state request.Request) uint32 { 14 | return uint32(time.Now().Unix()) 15 | } 16 | 17 | // MinTTL implements the Transferer interface. 18 | func (e *Etcd) MinTTL(state request.Request) uint32 { 19 | return 30 20 | } 21 | 22 | // Transfer implements the Transferer interface. 23 | func (e *Etcd) Transfer(ctx context.Context, state request.Request) (int, error) { 24 | return dns.RcodeServerFailure, nil 25 | } 26 | -------------------------------------------------------------------------------- /plugin/file/closest.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/file/dname.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "github.com/coredns/coredns/plugin/pkg/dnsutil" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // substituteDNAME performs the DNAME substitution defined by RFC 6672, 10 | // assuming the QTYPE of the query is not DNAME. It returns an empty 11 | // string if there is no match. 12 | func substituteDNAME(qname, owner, target string) string { 13 | if dns.IsSubDomain(owner, qname) && qname != owner { 14 | labels := dns.SplitDomainName(qname) 15 | labels = append(labels[0:len(labels)-dns.CountLabel(owner)], dns.SplitDomainName(target)...) 16 | 17 | return dnsutil.Join(labels...) 18 | } 19 | 20 | return "" 21 | } 22 | 23 | // synthesizeCNAME returns a CNAME RR pointing to the resulting name of 24 | // the DNAME substitution. The owner name of the CNAME is the QNAME of 25 | // the query and the TTL is the same as the corresponding DNAME RR. 26 | // 27 | // It returns nil if the DNAME substitution has no match. 28 | func synthesizeCNAME(qname string, d *dns.DNAME) *dns.CNAME { 29 | target := substituteDNAME(qname, d.Header().Name, d.Target) 30 | if target == "" { 31 | return nil 32 | } 33 | 34 | r := new(dns.CNAME) 35 | r.Hdr = dns.RR_Header{ 36 | Name: qname, 37 | Rrtype: dns.TypeCNAME, 38 | Class: dns.ClassINET, 39 | Ttl: d.Header().Ttl, 40 | } 41 | r.Target = target 42 | 43 | return r 44 | } 45 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/file/include_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/file/log_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/file/tree/glue.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/forward/dnstap.go: -------------------------------------------------------------------------------- 1 | package forward 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/coredns/coredns/plugin/dnstap" 8 | "github.com/coredns/coredns/plugin/dnstap/msg" 9 | "github.com/coredns/coredns/request" 10 | 11 | tap "github.com/dnstap/golang-dnstap" 12 | "github.com/miekg/dns" 13 | ) 14 | 15 | func toDnstap(ctx context.Context, host string, f *Forward, state request.Request, reply *dns.Msg, start time.Time) error { 16 | tapper := dnstap.TapperFromContext(ctx) 17 | if tapper == nil { 18 | return nil 19 | } 20 | // Query 21 | b := msg.New().Time(start).HostPort(host) 22 | opts := f.opts 23 | t := "" 24 | switch { 25 | case opts.forceTCP: // TCP flag has precedence over UDP flag 26 | t = "tcp" 27 | case opts.preferUDP: 28 | t = "udp" 29 | default: 30 | t = state.Proto() 31 | } 32 | 33 | if t == "tcp" { 34 | b.SocketProto = tap.SocketProtocol_TCP 35 | } else { 36 | b.SocketProto = tap.SocketProtocol_UDP 37 | } 38 | 39 | if tapper.Pack() { 40 | b.Msg(state.Req) 41 | } 42 | m, err := b.ToOutsideQuery(tap.Message_FORWARDER_QUERY) 43 | if err != nil { 44 | return err 45 | } 46 | tapper.TapMessage(m) 47 | 48 | // Response 49 | if reply != nil { 50 | if tapper.Pack() { 51 | b.Msg(reply) 52 | } 53 | m, err := b.Time(time.Now()).ToOutsideResponse(tap.Message_FORWARDER_RESPONSE) 54 | if err != nil { 55 | return err 56 | } 57 | tapper.TapMessage(m) 58 | } 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/forward/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package forward 4 | 5 | import ( 6 | "github.com/coredns/coredns/plugin/pkg/dnstest" 7 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/forward/log_test.go: -------------------------------------------------------------------------------- 1 | package forward 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/forward/setup_policy_test.go: -------------------------------------------------------------------------------- 1 | package forward 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func TestSetupPolicy(t *testing.T) { 11 | tests := []struct { 12 | input string 13 | shouldErr bool 14 | expectedPolicy string 15 | expectedErr string 16 | }{ 17 | // positive 18 | {"forward . 127.0.0.1 {\npolicy random\n}\n", false, "random", ""}, 19 | {"forward . 127.0.0.1 {\npolicy round_robin\n}\n", false, "round_robin", ""}, 20 | {"forward . 127.0.0.1 {\npolicy sequential\n}\n", false, "sequential", ""}, 21 | // negative 22 | {"forward . 127.0.0.1 {\npolicy random2\n}\n", true, "random", "unknown policy"}, 23 | } 24 | 25 | for i, test := range tests { 26 | c := caddy.NewTestController("dns", test.input) 27 | f, err := parseForward(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, got: %v", i, test.input, err) 36 | } 37 | 38 | if !strings.Contains(err.Error(), test.expectedErr) { 39 | t.Errorf("Test %d: expected error to contain: %v, found error: %v, input: %s", i, test.expectedErr, err, test.input) 40 | } 41 | } 42 | 43 | if !test.shouldErr && f.p.String() != test.expectedPolicy { 44 | t.Errorf("Test %d: expected: %s, got: %s", i, test.expectedPolicy, f.p.String()) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/grpc/metrics.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/grpc/policy.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "math/rand" 5 | "sync/atomic" 6 | ) 7 | 8 | // Policy defines a policy we use for selecting upstreams. 9 | type Policy interface { 10 | List([]*Proxy) []*Proxy 11 | String() string 12 | } 13 | 14 | // random is a policy that implements random upstream selection. 15 | type random struct{} 16 | 17 | func (r *random) String() string { return "random" } 18 | 19 | func (r *random) List(p []*Proxy) []*Proxy { 20 | switch len(p) { 21 | case 1: 22 | return p 23 | case 2: 24 | if rand.Int()%2 == 0 { 25 | return []*Proxy{p[1], p[0]} // swap 26 | } 27 | return p 28 | } 29 | 30 | perms := rand.Perm(len(p)) 31 | rnd := make([]*Proxy, len(p)) 32 | 33 | for i, p1 := range perms { 34 | rnd[i] = p[p1] 35 | } 36 | return rnd 37 | } 38 | 39 | // roundRobin is a policy that selects hosts based on round robin ordering. 40 | type roundRobin struct { 41 | robin uint32 42 | } 43 | 44 | func (r *roundRobin) String() string { return "round_robin" } 45 | 46 | func (r *roundRobin) List(p []*Proxy) []*Proxy { 47 | poolLen := uint32(len(p)) 48 | i := atomic.AddUint32(&r.robin, 1) % poolLen 49 | 50 | robin := []*Proxy{p[i]} 51 | robin = append(robin, p[:i]...) 52 | robin = append(robin, p[i+1:]...) 53 | 54 | return robin 55 | } 56 | 57 | // sequential is a policy that selects hosts based on sequential ordering. 58 | type sequential struct{} 59 | 60 | func (r *sequential) String() string { return "sequential" } 61 | 62 | func (r *sequential) List(p []*Proxy) []*Proxy { 63 | return p 64 | } 65 | -------------------------------------------------------------------------------- /plugin/grpc/setup_policy_test.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func TestSetupPolicy(t *testing.T) { 11 | tests := []struct { 12 | input string 13 | shouldErr bool 14 | expectedPolicy string 15 | expectedErr string 16 | }{ 17 | // positive 18 | {"grpc . 127.0.0.1 {\npolicy random\n}\n", false, "random", ""}, 19 | {"grpc . 127.0.0.1 {\npolicy round_robin\n}\n", false, "round_robin", ""}, 20 | {"grpc . 127.0.0.1 {\npolicy sequential\n}\n", false, "sequential", ""}, 21 | // negative 22 | {"grpc . 127.0.0.1 {\npolicy random2\n}\n", true, "random", "unknown policy"}, 23 | } 24 | 25 | for i, test := range tests { 26 | c := caddy.NewTestController("dns", test.input) 27 | g, err := parseGRPC(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, got: %v", i, test.input, err) 36 | } 37 | 38 | if !strings.Contains(err.Error(), test.expectedErr) { 39 | t.Errorf("Test %d: expected error to contain: %v, found error: %v, input: %s", i, test.expectedErr, err, test.input) 40 | } 41 | } 42 | 43 | if !test.shouldErr && g.p.String() != test.expectedPolicy { 44 | t.Errorf("Test %d: expected: %s, got: %s", i, test.expectedPolicy, g.p.String()) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/health/log_test.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/health/overloaded.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/coredns/coredns/plugin" 8 | 9 | "github.com/prometheus/client_golang/prometheus" 10 | "github.com/prometheus/client_golang/prometheus/promauto" 11 | ) 12 | 13 | // overloaded queries the health end point and updates a metrics showing how long it took. 14 | func (h *health) overloaded() { 15 | timeout := time.Duration(5 * time.Second) 16 | client := http.Client{ 17 | Timeout: timeout, 18 | } 19 | url := "http://" + h.Addr 20 | tick := time.NewTicker(1 * time.Second) 21 | defer tick.Stop() 22 | 23 | for { 24 | select { 25 | case <-tick.C: 26 | start := time.Now() 27 | resp, err := client.Get(url) 28 | if err != nil { 29 | HealthDuration.Observe(timeout.Seconds()) 30 | continue 31 | } 32 | resp.Body.Close() 33 | HealthDuration.Observe(time.Since(start).Seconds()) 34 | 35 | case <-h.stop: 36 | return 37 | } 38 | } 39 | } 40 | 41 | var ( 42 | // HealthDuration is the metric used for exporting how fast we can retrieve the /health endpoint. 43 | HealthDuration = promauto.NewHistogram(prometheus.HistogramOpts{ 44 | Namespace: plugin.Namespace, 45 | Subsystem: "health", 46 | Name: "request_duration_seconds", 47 | Buckets: plugin.TimeBuckets, 48 | Help: "Histogram of the time (in seconds) each request took.", 49 | }) 50 | ) 51 | -------------------------------------------------------------------------------- /plugin/health/setup_test.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /plugin/hosts/log_test.go: -------------------------------------------------------------------------------- 1 | package hosts 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/hosts/metrics.go: -------------------------------------------------------------------------------- 1 | package hosts 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/httpproxy/README.md: -------------------------------------------------------------------------------- 1 | # httpproxy 2 | 3 | ## Name 4 | 5 | *httpproxy* is a very simple plugin that determines what to do with DOH HTTP requests that go to a location different from `/dns-query`. 6 | 7 | ## Description 8 | 9 | The problem with DOH is that you usually would like to serve something useful from root and not just return 404. The `httpproxy` plugin is supposed to solve that problem. You just specify `host:port` where to proxy all requests that aren't served by the DOH server. 10 | 11 | ## Syntax 12 | 13 | ~~~ txt 14 | httpproxy HOST:PORT 15 | ~~~ 16 | 17 | ## Examples 18 | 19 | Here's a very simple example: 20 | 21 | ~~~ txt 22 | httpproxy 127.0.0.1:8080 23 | ~~~ 24 | 25 | -------------------------------------------------------------------------------- /plugin/httpproxy/httpproxy.go: -------------------------------------------------------------------------------- 1 | package httpproxy 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http/httputil" 7 | "net/url" 8 | 9 | "github.com/caddyserver/caddy" 10 | "github.com/coredns/coredns/core/dnsserver" 11 | "github.com/coredns/coredns/plugin" 12 | ) 13 | 14 | const pluginName = "httpproxy" 15 | 16 | func init() { 17 | plugin.Register(pluginName, setup) 18 | } 19 | 20 | func setup(c *caddy.Controller) error { 21 | err := parseHTTPProxy(c) 22 | if err != nil { 23 | return plugin.Error(pluginName, err) 24 | } 25 | return nil 26 | } 27 | 28 | func parseHTTPProxy(c *caddy.Controller) error { 29 | config := dnsserver.GetConfig(c) 30 | 31 | if config.HTTPProxy != nil { 32 | return plugin.Error(pluginName, c.Errf("HTTPProxy already configured for this server instance")) 33 | } 34 | 35 | for c.Next() { 36 | args := c.RemainingArgs() 37 | if len(args) != 1 { 38 | return plugin.Error(pluginName, c.ArgErr()) 39 | } 40 | 41 | host, port, err := net.SplitHostPort(args[0]) 42 | if err != nil { 43 | return c.ArgErr() 44 | } 45 | 46 | proxyURL, err := url.Parse(fmt.Sprintf("http://%s:%s", host, port)) 47 | if err != nil { 48 | return c.ArgErr() 49 | } 50 | 51 | reverseProxy := httputil.NewSingleHostReverseProxy(proxyURL) 52 | config.HTTPProxy = reverseProxy 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /plugin/k8s_external/setup_test.go: -------------------------------------------------------------------------------- 1 | package external 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/caddy" 7 | ) 8 | 9 | func TestSetup(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | shouldErr bool 13 | expectedZone string 14 | expectedApex string 15 | }{ 16 | {`k8s_external`, false, "", "dns"}, 17 | {`k8s_external example.org`, false, "example.org.", "dns"}, 18 | {`k8s_external example.org { 19 | apex testdns 20 | }`, false, "example.org.", "testdns"}, 21 | } 22 | 23 | for i, test := range tests { 24 | c := caddy.NewTestController("dns", test.input) 25 | e, err := parse(c) 26 | 27 | if test.shouldErr && err == nil { 28 | t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input) 29 | } 30 | 31 | if err != nil { 32 | if !test.shouldErr { 33 | t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err) 34 | } 35 | } 36 | 37 | if !test.shouldErr && test.expectedZone != "" { 38 | if test.expectedZone != e.Zones[0] { 39 | t.Errorf("Test %d, expected zone %q for input %s, got: %q", i, test.expectedZone, test.input, e.Zones[0]) 40 | } 41 | } 42 | if !test.shouldErr { 43 | if test.expectedApex != e.apex { 44 | t.Errorf("Test %d, expected apex %q for input %s, got: %q", i, test.expectedApex, test.input, e.apex) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /plugin/kubernetes/local.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/caddyserver/caddy" 7 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/kubernetes/log_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/kubernetes/setup_reverse_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /plugin/kubernetes/setup_transfer_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/caddy" 7 | ) 8 | 9 | func TestKubernetesParseTransfer(t *testing.T) { 10 | tests := []struct { 11 | input string // Corefile data as string 12 | expected string 13 | shouldErr bool 14 | }{ 15 | {`kubernetes cluster.local { 16 | transfer to 1.2.3.4 17 | }`, "1.2.3.4:53", false}, 18 | {`kubernetes cluster.local { 19 | transfer to 1.2.3.4:53 20 | }`, "1.2.3.4:53", false}, 21 | {`kubernetes cluster.local { 22 | transfer to * 23 | }`, "*", false}, 24 | {`kubernetes cluster.local { 25 | transfer 26 | }`, "", true}, 27 | } 28 | 29 | for i, tc := range tests { 30 | c := caddy.NewTestController("dns", tc.input) 31 | k, err := kubernetesParse(c) 32 | if err != nil && !tc.shouldErr { 33 | t.Fatalf("Test %d: Expected no error, got %q", i, err) 34 | } 35 | if err == nil && tc.shouldErr { 36 | t.Fatalf("Test %d: Expected error, got none", i) 37 | } 38 | if err != nil && tc.shouldErr { 39 | // input should error 40 | continue 41 | } 42 | 43 | if k.TransferTo[0] != tc.expected { 44 | t.Errorf("Test %d: Expected Transfer To to be %s, got %s", i, tc.expected, k.TransferTo[0]) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /plugin/kubernetes/setup_ttl_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/loadbalance/log_test.go: -------------------------------------------------------------------------------- 1 | package loadbalance 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/loadbalance/setup.go: -------------------------------------------------------------------------------- 1 | package loadbalance 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/coredns/coredns/core/dnsserver" 7 | "github.com/coredns/coredns/plugin" 8 | clog "github.com/coredns/coredns/plugin/pkg/log" 9 | 10 | "github.com/caddyserver/caddy" 11 | ) 12 | 13 | var log = clog.NewWithPlugin("loadbalance") 14 | 15 | func init() { plugin.Register("loadbalance", setup) } 16 | 17 | func setup(c *caddy.Controller) error { 18 | err := parse(c) 19 | if err != nil { 20 | return plugin.Error("loadbalance", err) 21 | } 22 | 23 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 24 | return RoundRobin{Next: next} 25 | }) 26 | 27 | return nil 28 | } 29 | 30 | func parse(c *caddy.Controller) error { 31 | for c.Next() { 32 | args := c.RemainingArgs() 33 | switch len(args) { 34 | case 0: 35 | return nil 36 | case 1: 37 | if args[0] != "round_robin" { 38 | return fmt.Errorf("unknown policy: %s", args[0]) 39 | 40 | } 41 | return nil 42 | } 43 | } 44 | return c.ArgErr() 45 | } 46 | -------------------------------------------------------------------------------- /plugin/loadbalance/setup_test.go: -------------------------------------------------------------------------------- 1 | package loadbalance 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /plugin/log_test.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/loop/log_test.go: -------------------------------------------------------------------------------- 1 | package loop 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/loop/setup_test.go: -------------------------------------------------------------------------------- 1 | package loop 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /plugin/metadata/log_test.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/metadata/metadata.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coredns/coredns/plugin" 7 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/metadata/setup.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "github.com/coredns/coredns/core/dnsserver" 5 | "github.com/coredns/coredns/plugin" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func init() { plugin.Register("metadata", setup) } 11 | 12 | func setup(c *caddy.Controller) error { 13 | m, err := metadataParse(c) 14 | if err != nil { 15 | return err 16 | } 17 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 18 | m.Next = next 19 | return m 20 | }) 21 | 22 | c.OnStartup(func() error { 23 | plugins := dnsserver.GetConfig(c).Handlers() 24 | for _, p := range plugins { 25 | if met, ok := p.(Provider); ok { 26 | m.Providers = append(m.Providers, met) 27 | } 28 | } 29 | return nil 30 | }) 31 | 32 | return nil 33 | } 34 | 35 | func metadataParse(c *caddy.Controller) (*Metadata, error) { 36 | m := &Metadata{} 37 | c.Next() 38 | zones := c.RemainingArgs() 39 | 40 | if len(zones) != 0 { 41 | m.Zones = zones 42 | for i := 0; i < len(m.Zones); i++ { 43 | m.Zones[i] = plugin.Host(m.Zones[i]).Normalize() 44 | } 45 | } else { 46 | m.Zones = make([]string, len(c.ServerBlockKeys)) 47 | for i := 0; i < len(c.ServerBlockKeys); i++ { 48 | m.Zones[i] = plugin.Host(c.ServerBlockKeys[i]).Normalize() 49 | } 50 | } 51 | 52 | if c.NextBlock() || c.Next() { 53 | return nil, plugin.Error("metadata", c.ArgErr()) 54 | } 55 | return m, nil 56 | } 57 | -------------------------------------------------------------------------------- /plugin/metrics/context.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/metrics/handler.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coredns/coredns/plugin" 7 | "github.com/coredns/coredns/plugin/metrics/vars" 8 | "github.com/coredns/coredns/plugin/pkg/dnstest" 9 | "github.com/coredns/coredns/plugin/pkg/rcode" 10 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/metrics/log_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/metrics/setup_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /plugin/nsid/README.md: -------------------------------------------------------------------------------- 1 | # nsid 2 | 3 | ## Name 4 | 5 | *nsid* - adds an identifier of this server to each reply. 6 | 7 | ## Description 8 | 9 | This plugin implements [RFC 5001](https://tools.ietf.org/html/rfc5001) and adds an EDNS0 OPT 10 | resource record to replies that uniquely identify the server. This is useful in anycast setups to 11 | see which server was responsible for generating the reply and for debugging. 12 | 13 | This plugin can only be used once per Server Block. 14 | 15 | 16 | ## Syntax 17 | 18 | ~~~ txt 19 | nsid [DATA] 20 | ~~~ 21 | 22 | **DATA** is the string to use in the nsid record. 23 | 24 | If **DATA** is not given, the host's name is used. 25 | 26 | ## Examples 27 | 28 | Enable nsid: 29 | 30 | ~~~ corefile 31 | example.org { 32 | whoami 33 | nsid Use The Force 34 | } 35 | ~~~ 36 | 37 | And now a client with NSID support will see an OPT record with the NSID option: 38 | 39 | ~~~ sh 40 | % dig +nsid @localhost a whoami.example.org 41 | 42 | ;; Got answer: 43 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46880 44 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 45 | 46 | .... 47 | 48 | ; OPT PSEUDOSECTION: 49 | ; EDNS: version: 0, flags:; udp: 4096 50 | ; NSID: 55 73 65 20 54 68 65 20 46 6f 72 63 65 ("Use The Force") 51 | ;; QUESTION SECTION: 52 | ;whoami.example.org. IN A 53 | ~~~ 54 | 55 | ## Also See 56 | 57 | [RFC 5001](https://tools.ietf.org/html/rfc5001) 58 | -------------------------------------------------------------------------------- /plugin/nsid/log_test.go: -------------------------------------------------------------------------------- 1 | package nsid 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/nsid/setup.go: -------------------------------------------------------------------------------- 1 | package nsid 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/coredns/coredns/core/dnsserver" 8 | "github.com/coredns/coredns/plugin" 9 | 10 | "github.com/caddyserver/caddy" 11 | ) 12 | 13 | func init() { plugin.Register("nsid", setup) } 14 | 15 | func setup(c *caddy.Controller) error { 16 | nsid, err := nsidParse(c) 17 | if err != nil { 18 | return plugin.Error("nsid", err) 19 | } 20 | 21 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 22 | return Nsid{Next: next, Data: nsid} 23 | }) 24 | 25 | return nil 26 | } 27 | 28 | func nsidParse(c *caddy.Controller) (string, error) { 29 | // Use hostname as the default 30 | nsid, err := os.Hostname() 31 | if err != nil { 32 | nsid = "localhost" 33 | } 34 | i := 0 35 | for c.Next() { 36 | if i > 0 { 37 | return nsid, plugin.ErrOnce 38 | } 39 | i++ 40 | args := c.RemainingArgs() 41 | if len(args) > 0 { 42 | nsid = strings.Join(args, " ") 43 | } 44 | } 45 | return nsid, nil 46 | } 47 | -------------------------------------------------------------------------------- /plugin/pkg/cache/cache_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "testing" 4 | 5 | func TestCacheAddAndGet(t *testing.T) { 6 | const N = shardSize * 4 7 | c := New(N) 8 | c.Add(1, 1) 9 | 10 | if _, found := c.Get(1); !found { 11 | t.Fatal("Failed to find inserted record") 12 | } 13 | 14 | for i := 0; i < N; i++ { 15 | c.Add(uint64(i), 1) 16 | } 17 | for i := 0; i < N; i++ { 18 | c.Add(uint64(i), 1) 19 | if c.Len() != N { 20 | t.Fatal("A item was unnecessarily evicted from the cache") 21 | } 22 | } 23 | } 24 | 25 | func TestCacheLen(t *testing.T) { 26 | c := New(4) 27 | 28 | c.Add(1, 1) 29 | if l := c.Len(); l != 1 { 30 | t.Fatalf("Cache size should %d, got %d", 1, l) 31 | } 32 | 33 | c.Add(1, 1) 34 | if l := c.Len(); l != 1 { 35 | t.Fatalf("Cache size should %d, got %d", 1, l) 36 | } 37 | 38 | c.Add(2, 2) 39 | if l := c.Len(); l != 2 { 40 | t.Fatalf("Cache size should %d, got %d", 2, l) 41 | } 42 | } 43 | 44 | func TestCacheSharding(t *testing.T) { 45 | c := New(shardSize) 46 | for i := 0; i < shardSize*2; i++ { 47 | c.Add(uint64(i), 1) 48 | } 49 | for i, s := range c.shards { 50 | if s.Len() == 0 { 51 | t.Errorf("Failed to populate shard: %d", i) 52 | } 53 | } 54 | } 55 | 56 | func BenchmarkCache(b *testing.B) { 57 | b.ReportAllocs() 58 | 59 | c := New(4) 60 | for n := 0; n < b.N; n++ { 61 | c.Add(1, 1) 62 | c.Get(1) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/pkg/dnsutil/doc.go: -------------------------------------------------------------------------------- 1 | // Package dnsutil contains DNS related helper functions. 2 | package dnsutil 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/pkg/doh/doh_test.go: -------------------------------------------------------------------------------- 1 | package doh 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/miekg/dns" 8 | ) 9 | 10 | func TestPostRequest(t *testing.T) { 11 | m := new(dns.Msg) 12 | m.SetQuestion("example.org.", dns.TypeDNSKEY) 13 | 14 | req, err := NewRequest(http.MethodPost, "https://example.org:443", m) 15 | if err != nil { 16 | t.Errorf("Failure to make request: %s", err) 17 | } 18 | 19 | m, err = RequestToMsg(req) 20 | if err != nil { 21 | t.Fatalf("Failure to get message from request: %s", err) 22 | } 23 | 24 | if x := m.Question[0].Name; x != "example.org." { 25 | t.Errorf("Qname expected %s, got %s", "example.org.", x) 26 | } 27 | if x := m.Question[0].Qtype; x != dns.TypeDNSKEY { 28 | t.Errorf("Qname expected %d, got %d", x, dns.TypeDNSKEY) 29 | } 30 | } 31 | 32 | func TestGetRequest(t *testing.T) { 33 | m := new(dns.Msg) 34 | m.SetQuestion("example.org.", dns.TypeDNSKEY) 35 | 36 | req, err := NewRequest(http.MethodGet, "https://example.org:443", m) 37 | if err != nil { 38 | t.Errorf("Failure to make request: %s", err) 39 | } 40 | 41 | m, err = RequestToMsg(req) 42 | if err != nil { 43 | t.Fatalf("Failure to get message from request: %s", err) 44 | } 45 | 46 | if x := m.Question[0].Name; x != "example.org." { 47 | t.Errorf("Qname expected %s, got %s", "example.org.", x) 48 | } 49 | if x := m.Question[0].Qtype; x != dns.TypeDNSKEY { 50 | t.Errorf("Qname expected %d, got %d", x, dns.TypeDNSKEY) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coredns/coredns/plugin" 8 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coredns/coredns/plugin/pkg/transport" 8 | 9 | "github.com/caddyserver/caddy" 10 | ) 11 | 12 | // Transfer parses transfer statements: 'transfer [to|from] [address...]'. 13 | func Transfer(c *caddy.Controller, secondary bool) (tos, froms []string, err error) { 14 | if !c.NextArg() { 15 | return nil, nil, c.ArgErr() 16 | } 17 | value := c.Val() 18 | switch value { 19 | case "to": 20 | tos = c.RemainingArgs() 21 | for i := range tos { 22 | if tos[i] != "*" { 23 | normalized, err := HostPort(tos[i], transport.Port) 24 | if err != nil { 25 | return nil, nil, err 26 | } 27 | tos[i] = normalized 28 | } 29 | } 30 | 31 | case "from": 32 | if !secondary { 33 | return nil, nil, fmt.Errorf("can't use `transfer from` when not being a secondary") 34 | } 35 | froms = c.RemainingArgs() 36 | for i := range froms { 37 | if froms[i] != "*" { 38 | normalized, err := HostPort(froms[i], transport.Port) 39 | if err != nil { 40 | return nil, nil, err 41 | } 42 | froms[i] = normalized 43 | } else { 44 | return nil, nil, fmt.Errorf("can't use '*' in transfer from") 45 | } 46 | } 47 | } 48 | return 49 | } 50 | -------------------------------------------------------------------------------- /plugin/pkg/parse/transport.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/coredns/coredns/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 | return transport.HTTPS, s 29 | 30 | case strings.HasPrefix(s, transport.QUIC+"://"): 31 | s = s[len(transport.QUIC+"://"):] 32 | return transport.QUIC, s 33 | 34 | case strings.HasPrefix(s, transport.DNSCrypt+"://"): 35 | s = s[len(transport.DNSCrypt+"://"):] 36 | return transport.DNSCrypt, s 37 | } 38 | 39 | return transport.DNS, s 40 | } 41 | -------------------------------------------------------------------------------- /plugin/pkg/parse/transport_test.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/coredns/coredns/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 | {"quic://example.org ", transport.QUIC}, 20 | {"dnscrypt://example.org ", transport.DNSCrypt}, 21 | } { 22 | actual, _ := Transport(test.input) 23 | if actual != test.expected { 24 | t.Errorf("Test %d: Expected %s but got %s", i, test.expected, actual) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/pkg/response/classify.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "fmt" 4 | 5 | // Class holds sets of Types 6 | type Class int 7 | 8 | const ( 9 | // All is a meta class encompassing all the classes. 10 | All Class = iota 11 | // Success is a class for a successful response. 12 | Success 13 | // Denial is a class for denying existence (NXDOMAIN, or a nodata: type does not exist) 14 | Denial 15 | // Error is a class for errors, right now defined as not Success and not Denial 16 | Error 17 | ) 18 | 19 | func (c Class) String() string { 20 | switch c { 21 | case All: 22 | return "all" 23 | case Success: 24 | return "success" 25 | case Denial: 26 | return "denial" 27 | case Error: 28 | return "error" 29 | } 30 | return "" 31 | } 32 | 33 | // ClassFromString returns the class from the string s. If not class matches 34 | // the All class and an error are returned 35 | func ClassFromString(s string) (Class, error) { 36 | switch s { 37 | case "all": 38 | return All, nil 39 | case "success": 40 | return Success, nil 41 | case "denial": 42 | return Denial, nil 43 | case "error": 44 | return Error, nil 45 | } 46 | return All, fmt.Errorf("invalid Class: %s", s) 47 | } 48 | 49 | // Classify classifies the Type t, it returns its Class. 50 | func Classify(t Type) Class { 51 | switch t { 52 | case NoError, Delegation: 53 | return Success 54 | case NameError, NoData: 55 | return Denial 56 | case OtherError: 57 | fallthrough 58 | default: 59 | return Error 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /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/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/pkg/trace/trace.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | QUIC = "quic" 10 | DNSCrypt = "dnscrypt" 11 | ) 12 | 13 | // Port numbers for the various transports. 14 | const ( 15 | // Port is the default port for DNS 16 | Port = "53" 17 | // TLSPort is the default port for DNS-over-TLS. 18 | TLSPort = "853" 19 | // GRPCPort is the default port for DNS-over-gRPC. 20 | GRPCPort = "443" 21 | // HTTPSPort is the default port for DNS-over-HTTPS. 22 | HTTPSPort = "443" 23 | // QUICPort is the default port for DNS-over-QUIC. 24 | // https://datatracker.ietf.org/doc/html/draft-ietf-dprive-dnsoquic-02#section-10.2.1 25 | // Early experiments MAY use port 8853. This port is marked in the IANA registry as unassigned. 26 | // (Note that prior to version -02 of this draft, experiments were directed to use port 784.) 27 | QUICPort = "8853" 28 | // DNSCryptPort is the default port for DNSCrypt 29 | DNSCryptPort = "5443" 30 | ) 31 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coredns/coredns/core/dnsserver" 9 | "github.com/coredns/coredns/plugin/pkg/nonwriter" 10 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/pprof/log_test.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/pprof/setup_test.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/ready/setup_test.go: -------------------------------------------------------------------------------- 1 | package ready 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /plugin/register.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /plugin/reload/log_test.go: -------------------------------------------------------------------------------- 1 | package reload 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/reload/metrics.go: -------------------------------------------------------------------------------- 1 | package reload 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/rewrite/class.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/coredns/coredns/request" 9 | 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | type classRule struct { 14 | fromClass uint16 15 | toClass uint16 16 | NextAction string 17 | } 18 | 19 | // newClassRule creates a class matching rule 20 | func newClassRule(nextAction string, args ...string) (Rule, error) { 21 | var from, to uint16 22 | var ok bool 23 | if from, ok = dns.StringToClass[strings.ToUpper(args[0])]; !ok { 24 | return nil, fmt.Errorf("invalid class %q", strings.ToUpper(args[0])) 25 | } 26 | if to, ok = dns.StringToClass[strings.ToUpper(args[1])]; !ok { 27 | return nil, fmt.Errorf("invalid class %q", strings.ToUpper(args[1])) 28 | } 29 | return &classRule{from, to, nextAction}, nil 30 | } 31 | 32 | // Rewrite rewrites the current request. 33 | func (rule *classRule) Rewrite(ctx context.Context, state request.Request) Result { 34 | if rule.fromClass > 0 && rule.toClass > 0 { 35 | if state.Req.Question[0].Qclass == rule.fromClass { 36 | state.Req.Question[0].Qclass = rule.toClass 37 | return RewriteDone 38 | } 39 | } 40 | return RewriteIgnored 41 | } 42 | 43 | // Mode returns the processing mode. 44 | func (rule *classRule) Mode() string { return rule.NextAction } 45 | 46 | // GetResponseRule return a rule to rewrite the response with. Currently not implemented. 47 | func (rule *classRule) GetResponseRule() ResponseRule { return ResponseRule{} } 48 | -------------------------------------------------------------------------------- /plugin/rewrite/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package rewrite 4 | 5 | import ( 6 | "github.com/coredns/coredns/plugin/pkg/fuzz" 7 | 8 | "github.com/caddyserver/caddy" 9 | ) 10 | 11 | // Fuzz fuzzes rewrite. 12 | func Fuzz(data []byte) int { 13 | c := caddy.NewTestController("dns", "rewrite edns0 subnet set 24 56") 14 | rules, err := rewriteParse(c) 15 | if err != nil { 16 | return 0 17 | } 18 | r := Rewrite{Rules: rules} 19 | 20 | return fuzz.Do(r, data) 21 | } 22 | -------------------------------------------------------------------------------- /plugin/rewrite/log_test.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/rewrite/setup.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import ( 4 | "github.com/coredns/coredns/core/dnsserver" 5 | "github.com/coredns/coredns/plugin" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func init() { plugin.Register("rewrite", setup) } 11 | 12 | func setup(c *caddy.Controller) error { 13 | rewrites, err := rewriteParse(c) 14 | if err != nil { 15 | return plugin.Error("rewrite", err) 16 | } 17 | 18 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 19 | return Rewrite{Next: next, Rules: rewrites} 20 | }) 21 | 22 | return nil 23 | } 24 | 25 | func rewriteParse(c *caddy.Controller) ([]Rule, error) { 26 | var rules []Rule 27 | 28 | for c.Next() { 29 | args := c.RemainingArgs() 30 | if len(args) < 2 { 31 | // Handles rules out of nested instructions, i.e. the ones enclosed in curly brackets 32 | for c.NextBlock() { 33 | args = append(args, c.Val()) 34 | } 35 | } 36 | rule, err := newRule(args...) 37 | if err != nil { 38 | return nil, err 39 | } 40 | rules = append(rules, rule) 41 | } 42 | return rules, nil 43 | } 44 | -------------------------------------------------------------------------------- /plugin/rewrite/type.go: -------------------------------------------------------------------------------- 1 | // Package rewrite is a plugin for rewriting requests internally to something different. 2 | package rewrite 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | "strings" 8 | 9 | "github.com/coredns/coredns/request" 10 | 11 | "github.com/miekg/dns" 12 | ) 13 | 14 | // typeRule is a type rewrite rule. 15 | type typeRule struct { 16 | fromType uint16 17 | toType uint16 18 | nextAction string 19 | } 20 | 21 | func newTypeRule(nextAction string, args ...string) (Rule, error) { 22 | var from, to uint16 23 | var ok bool 24 | if from, ok = dns.StringToType[strings.ToUpper(args[0])]; !ok { 25 | return nil, fmt.Errorf("invalid type %q", strings.ToUpper(args[0])) 26 | } 27 | if to, ok = dns.StringToType[strings.ToUpper(args[1])]; !ok { 28 | return nil, fmt.Errorf("invalid type %q", strings.ToUpper(args[1])) 29 | } 30 | return &typeRule{from, to, nextAction}, nil 31 | } 32 | 33 | // Rewrite rewrites the current request. 34 | func (rule *typeRule) Rewrite(ctx context.Context, state request.Request) Result { 35 | if rule.fromType > 0 && rule.toType > 0 { 36 | if state.QType() == rule.fromType { 37 | state.Req.Question[0].Qtype = rule.toType 38 | return RewriteDone 39 | } 40 | } 41 | return RewriteIgnored 42 | } 43 | 44 | // Mode returns the processing mode. 45 | func (rule *typeRule) Mode() string { return rule.nextAction } 46 | 47 | // GetResponseRule return a rule to rewrite the response with. Currently not implemented. 48 | func (rule *typeRule) GetResponseRule() ResponseRule { return ResponseRule{} } 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/root/log_test.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/root/root.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/coredns/coredns/core/dnsserver" 7 | "github.com/coredns/coredns/plugin" 8 | clog "github.com/coredns/coredns/plugin/pkg/log" 9 | 10 | "github.com/caddyserver/caddy" 11 | ) 12 | 13 | var log = clog.NewWithPlugin("root") 14 | 15 | func init() { plugin.Register("root", setup) } 16 | 17 | func setup(c *caddy.Controller) error { 18 | config := dnsserver.GetConfig(c) 19 | 20 | for c.Next() { 21 | if !c.NextArg() { 22 | return plugin.Error("root", c.ArgErr()) 23 | } 24 | config.Root = c.Val() 25 | } 26 | 27 | // Check if root path exists 28 | _, err := os.Stat(config.Root) 29 | if err != nil { 30 | if os.IsNotExist(err) { 31 | // Allow this, because the folder might appear later. 32 | // But make sure the user knows! 33 | log.Warningf("Root path does not exist: %s", config.Root) 34 | } else { 35 | return plugin.Error("root", c.Errf("unable to access root path '%s': %v", config.Root, err)) 36 | } 37 | } 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /plugin/route53/log_test.go: -------------------------------------------------------------------------------- 1 | package route53 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/secondary/log_test.go: -------------------------------------------------------------------------------- 1 | package secondary 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/secondary/secondary.go: -------------------------------------------------------------------------------- 1 | // Package secondary implements a secondary plugin. 2 | package secondary 3 | 4 | import "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/sign/log_test.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/sign/nsec.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/coredns/coredns/plugin/file" 7 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/sign/nsec_test.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/sign/sign.go: -------------------------------------------------------------------------------- 1 | // Package sign implements a zone signer as a plugin. 2 | package sign 3 | 4 | import ( 5 | "path/filepath" 6 | "time" 7 | ) 8 | 9 | // Sign contains signers that sign the zones files. 10 | type Sign struct { 11 | signers []*Signer 12 | } 13 | 14 | // OnStartup scans all signers and signs or resigns zones if needed. 15 | func (s *Sign) OnStartup() error { 16 | for _, signer := range s.signers { 17 | why := signer.resign() 18 | if why == nil { 19 | log.Infof("Skipping signing zone %q in %q: signatures are valid", signer.origin, filepath.Join(signer.directory, signer.signedfile)) 20 | continue 21 | } 22 | go signAndLog(signer, why) 23 | } 24 | return nil 25 | } 26 | 27 | // Various duration constants for signing of the zones. 28 | const ( 29 | durationExpireDays = 7 * 24 * time.Hour // max time allowed before expiration 30 | durationResignDays = 6 * 24 * time.Hour // if the last sign happened this long ago, sign again 31 | durationSignatureExpireDays = 32 * 24 * time.Hour // sign for 32 days 32 | durationRefreshHours = 5 * time.Hour // check zones every 5 hours 33 | durationInceptionJitter = -18 * time.Hour // default max jitter for the inception 34 | durationExpirationDayJitter = 5 * 24 * time.Hour // default max jitter for the expiration 35 | durationSignatureInceptionHours = -3 * time.Hour // -(2+1) hours, be sure to catch daylight saving time and such, jitter is subtracted 36 | ) 37 | 38 | const timeFmt = "2006-01-02T15:04:05.000Z07:00" 39 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/template/log_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/template/metrics.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "github.com/coredns/coredns/plugin" 5 | "github.com/prometheus/client_golang/prometheus" 6 | "github.com/prometheus/client_golang/prometheus/promauto" 7 | ) 8 | 9 | var ( 10 | // templateMatchesCount is the counter of template regex matches. 11 | templateMatchesCount = promauto.NewCounterVec(prometheus.CounterOpts{ 12 | Namespace: plugin.Namespace, 13 | Subsystem: "template", 14 | Name: "matches_total", 15 | Help: "Counter of template regex matches.", 16 | }, []string{"server", "zone", "class", "type"}) 17 | // templateFailureCount is the counter of go template failures. 18 | templateFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{ 19 | Namespace: plugin.Namespace, 20 | Subsystem: "template", 21 | Name: "template_failures_total", 22 | Help: "Counter of go template failures.", 23 | }, []string{"server", "zone", "class", "type", "section", "template"}) 24 | // templateRRFailureCount is the counter of mis-templated RRs. 25 | templateRRFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{ 26 | Namespace: plugin.Namespace, 27 | Subsystem: "template", 28 | Name: "rr_failures_total", 29 | Help: "Counter of mis-templated RRs.", 30 | }, []string{"server", "zone", "class", "type", "section", "template"}) 31 | ) 32 | -------------------------------------------------------------------------------- /plugin/test/doc.go: -------------------------------------------------------------------------------- 1 | // Package test contains helper functions for writing plugin tests. 2 | package test 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/tls/log_test.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/tls/metrics.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import ( 4 | "github.com/coredns/coredns/plugin" 5 | "github.com/prometheus/client_golang/prometheus" 6 | "github.com/prometheus/client_golang/prometheus/promauto" 7 | ) 8 | 9 | var ( 10 | tlsSessionTicketsRotateStatus = promauto.NewGauge(prometheus.GaugeOpts{ 11 | Namespace: plugin.Namespace, 12 | Subsystem: "tls", 13 | Name: "session_tickets_rotate_status", 14 | Help: "Status of the last tickets rotation.", 15 | }) 16 | tlsSessionTicketsRotateTime = promauto.NewGauge(prometheus.GaugeOpts{ 17 | Namespace: plugin.Namespace, 18 | Subsystem: "tls", 19 | Name: "session_tickets_rotate_time", 20 | Help: "Time when the TLS session tickets were rotated.", 21 | }) 22 | tlsHandshakeTotal = promauto.NewCounterVec(prometheus.CounterOpts{ 23 | Namespace: plugin.Namespace, 24 | Subsystem: "tls", 25 | Name: "handshake_total", 26 | Help: "Total count of TLS handshakes", 27 | }, []string{"proto", "server_name", "tls_version", "did_resume", "cipher_suite", "negotiated_proto"}) 28 | ) 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/tls/test_session_ticket.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/coredns/5ffb4d5d491f0a3e876ff6712e2e6fe2a1e07f4b/plugin/tls/test_session_ticket.key -------------------------------------------------------------------------------- /plugin/trace/log_test.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/transfer/README.md: -------------------------------------------------------------------------------- 1 | # transfer 2 | 3 | ## Name 4 | 5 | *transfer* - perform zone transfers for other plugins. 6 | 7 | ## Description 8 | 9 | This plugin answers zone transfers for authoritative plugins that implement 10 | `transfer.Transferer`. Currently, no internal plugins implement this interface. 11 | 12 | Transfer answers full zone transfer (AXFR) requests and incremental zone transfer (IXFR) requests 13 | with AXFR fallback if the zone has changed. 14 | 15 | Notifies are not currently supported. 16 | 17 | ## Syntax 18 | 19 | ~~~ 20 | transfer [ZONE...] { 21 | to HOST... 22 | } 23 | ~~~ 24 | 25 | * **ZONES** The zones *transfer* will answer zone requests for. If left blank, 26 | the zones are inherited from the enclosing server block. To answer zone 27 | transfers for a given zone, there must be another plugin in the same server 28 | block that serves the same zone, and implements `transfer.Transferer`. 29 | 30 | * `to ` **HOST...** The hosts *transfer* will transfer to. Use `*` to permit 31 | transfers to all hosts. 32 | 33 | ## Examples 34 | 35 | TODO 36 | -------------------------------------------------------------------------------- /plugin/whoami/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package whoami 4 | 5 | import ( 6 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /plugin/whoami/log_test.go: -------------------------------------------------------------------------------- 1 | package whoami 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /plugin/whoami/setup.go: -------------------------------------------------------------------------------- 1 | package whoami 2 | 3 | import ( 4 | "github.com/coredns/coredns/core/dnsserver" 5 | "github.com/coredns/coredns/plugin" 6 | 7 | "github.com/caddyserver/caddy" 8 | ) 9 | 10 | func init() { plugin.Register("whoami", setup) } 11 | 12 | func setup(c *caddy.Controller) error { 13 | c.Next() // 'whoami' 14 | if c.NextArg() { 15 | return plugin.Error("whoami", c.ArgErr()) 16 | } 17 | 18 | dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { 19 | return Whoami{} 20 | }) 21 | 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /plugin/whoami/setup_test.go: -------------------------------------------------------------------------------- 1 | package whoami 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/caddyserver/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 | -------------------------------------------------------------------------------- /request/edns0.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coredns/coredns/coremain" 8 | 9 | "github.com/caddyserver/caddy" 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 bo %s, got %s", version, chTxt) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/doc.go: -------------------------------------------------------------------------------- 1 | // Package test contains function and types useful for writing tests. 2 | package test 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/file_srv_additional_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /test/file_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/coredns/coredns/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/grpc_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/coredns/coredns/pb" 9 | 10 | "github.com/miekg/dns" 11 | "google.golang.org/grpc" 12 | ) 13 | 14 | func TestGrpc(t *testing.T) { 15 | corefile := `grpc://.:0 { 16 | whoami 17 | }` 18 | 19 | g, _, tcp, err := CoreDNSServerAndPorts(corefile) 20 | if err != nil { 21 | t.Fatalf("Could not get CoreDNS serving instance: %s", err) 22 | } 23 | defer g.Stop() 24 | 25 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 26 | defer cancel() 27 | conn, err := grpc.DialContext(ctx, tcp, grpc.WithInsecure(), grpc.WithBlock()) 28 | if err != nil { 29 | t.Fatalf("Expected no error but got: %s", err) 30 | } 31 | defer conn.Close() 32 | 33 | client := pb.NewDnsServiceClient(conn) 34 | 35 | m := new(dns.Msg) 36 | m.SetQuestion("whoami.example.org.", dns.TypeA) 37 | msg, _ := m.Pack() 38 | 39 | reply, err := client.Query(context.TODO(), &pb.DnsPacket{Msg: msg}) 40 | if err != nil { 41 | t.Errorf("Expected no error but got: %s", err) 42 | } 43 | 44 | d := new(dns.Msg) 45 | err = d.Unpack(reply.Msg) 46 | if err != nil { 47 | t.Errorf("Expected no error but got: %s", err) 48 | } 49 | 50 | if d.Rcode != dns.RcodeSuccess { 51 | t.Errorf("Expected success but got %d", d.Rcode) 52 | } 53 | 54 | if len(d.Extra) != 2 { 55 | t.Errorf("Expected 2 RRs in additional section, but got %d", len(d.Extra)) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/log_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import clog "github.com/coredns/coredns/plugin/pkg/log" 4 | 5 | func init() { clog.Discard() } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------