Compare commits

...

232 Commits

Author SHA1 Message Date
Renovate Bot c1a4a7f93a fix(deps): update module github.com/quic-go/quic-go to v0.43.0
Mirror Push / mirror (push) Successful in 6s Details
Test / test (oldstable) (push) Successful in 43s Details
Test / test (stable) (push) Successful in 40s Details
2024-04-27 15:00:46 +00:00
Sam Therapy 9aebf89f57
docs: make the README a little nicer
Mirror Push / mirror (push) Successful in 6s Details
Test / test (oldstable) (push) Successful in 44s Details
Test / test (stable) (push) Successful in 37s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-04-19 23:01:11 +02:00
Renovate Bot 37e0c1e9b5 fix(deps): update module github.com/miekg/dns to v1.1.59
Mirror Push / mirror (push) Successful in 7s Details
Test / test (oldstable) (push) Successful in 43s Details
Test / test (stable) (push) Successful in 39s Details
2024-04-17 21:00:38 +00:00
Sam Therapy 40278ed22f
chore: Add Snap and Docker with Goreleaser
Forgejo Release / release (push) Failing after 1m6s Details
Mirror Push / mirror (push) Successful in 6s Details
Test / test (oldstable) (push) Successful in 45s Details
Test / test (stable) (push) Successful in 38s Details
Why not though snap sucks and docker seems kind of pointless since this is just a static file

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-04-17 19:00:51 +02:00
Renovate Bot 7631dd7a7b fix(deps): update module golang.org/x/net to v0.24.0
Mirror Push / mirror (push) Successful in 5s Details
Test / test (oldstable) (push) Successful in 44s Details
Test / test (stable) (push) Successful in 39s Details
2024-04-04 20:01:14 +00:00
Renovate Bot 2140c1582f fix(deps): update module golang.org/x/sys to v0.19.0
Mirror Push / mirror (push) Successful in 6s Details
Test / test (oldstable) (push) Successful in 45s Details
Test / test (stable) (push) Successful in 38s Details
2024-04-04 16:01:12 +00:00
Renovate Bot 8c00f85636 fix(deps): update module golang.org/x/net to v0.23.0
Mirror Push / mirror (push) Successful in 5s Details
Test / test (oldstable) (push) Successful in 45s Details
Test / test (stable) (push) Successful in 45s Details
2024-04-03 19:01:10 +00:00
Renovate Bot 8e54337787 chore(deps): update dependency go to v1.22.2
Mirror Push / mirror (push) Successful in 6s Details
Test / test (oldstable) (push) Successful in 43s Details
Test / test (stable) (push) Successful in 44s Details
2024-04-03 16:01:11 +00:00
Sam Therapy a5980be3cc
docs: Use the docs folder more
Mirror Push / mirror (push) Successful in 7s Details
Test / test (stable) (push) Successful in 36s Details
Test / test (oldstable) (push) Successful in 56s Details
Also rename it from doc to docs

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-23 22:05:57 +01:00
Sam Therapy 58cee5b3c9
docs: Add more documentation
Mirror Push / mirror (push) Successful in 6s Details
Test / test (stable) (push) Successful in 39s Details
Test / test (oldstable) (push) Successful in 56s Details
Add the "official" logo.
Also run golangci-lint

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-23 22:00:01 +01:00
Renovate Bot 3881caff0d fix(deps): update module github.com/quic-go/quic-go to v0.42.0
Mirror Push / mirror (push) Successful in 6s Details
Test / test (oldstable) (push) Successful in 57s Details
Test / test (stable) (push) Successful in 29s Details
2024-03-18 02:01:09 +00:00
Sam Therapy d8c40abf5a
misc(deps): Re-enable automerge
Mirror Push / mirror (push) Successful in 5s Details
Test / test (stable) (push) Successful in 34s Details
Test / test (oldstable) (push) Successful in 55s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-16 23:47:30 +01:00
Sam Therapy 92cf0b465b
ci: Improve all actions
Mirror Push / mirror (push) Successful in 6s Details
Test / test (stable) (push) Successful in 50s Details
Test / test (oldstable) (push) Successful in 1m12s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-16 23:26:02 +01:00
Sam Therapy b5f4ea9c42
ci: Get rid of dedicated submodules step
Mirror Push / mirror (push) Successful in 9s Details
Test / test (stable) (push) Successful in 46s Details
Test / test (oldstable) (push) Successful in 53s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-16 21:28:49 +01:00
Sam Therapy 8434739516
fix(AAAA): AAAAA
Mirror Push / mirror (push) Successful in 8s Details
Test / test (oldstable) (push) Successful in 1m9s Details
Test / test (stable) (push) Successful in 41s Details
Gitea Release / release (push) Failing after 1m14s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-15 18:42:29 +01:00
Sam Therapy 556b7a48af
[SKIP CI] fix(readme): correct the manpage URL
Mirror Push / mirror (push) Successful in 7s Details
Test / test (oldstable) (push) Successful in 1m9s Details
Gitea Release / release (push) Failing after 1m19s Details
Test / test (stable) (push) Successful in 40s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-15 18:32:09 +01:00
Sam Therapy 21047275bf
fix(quic): A workaround for adguard not working over QUIC
Mirror Push / mirror (push) Successful in 6s Details
Test / test (stable) (push) Successful in 41s Details
Test / test (oldstable) (push) Successful in 52s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2024-03-15 18:20:03 +01:00
Sam Therapy 835cb8c950
fix(docs): Totally redesign the readme
Also, use the American spelling of LICENSE instead of LICENCE since that is what everyone else already does
2024-03-15 18:16:58 +01:00
Sam Therapy 20f5b21be1
fix(deps): go get -u 2024-03-15 15:35:02 +01:00
Renovate Bot b975dd8c00 fix(deps): update module golang.org/x/net to v0.22.0
Mirror Push / mirror (push) Successful in 9s Details
Test / test (oldstable) (push) Failing after 43s Details
Test / test (stable) (push) Failing after 41s Details
2024-03-04 21:00:28 +00:00
Renovate Bot 42e50d3e93 fix(deps): update module github.com/miekg/dns to v1.1.58
Mirror Push / mirror (push) Successful in 7s Details
Test / test (oldstable) (push) Successful in 37s Details
Test / test (stable) (push) Successful in 40s Details
2024-01-18 12:00:21 +00:00
Sam Therapy 7030e68100
fix(deps): go get -u
Mirror Push / mirror (push) Successful in 7s Details
Test / test (oldstable) (push) Successful in 34s Details
Test / test (stable) (push) Successful in 39s Details
2024-01-09 21:10:07 +01:00
Renovate Bot c4a267cec9 fix(deps): update module golang.org/x/net to v0.20.0
Mirror Push / mirror (push) Successful in 8s Details
Test / test (oldstable) (push) Successful in 35s Details
Test / test (stable) (push) Successful in 40s Details
2024-01-08 19:00:21 +00:00
Renovate Bot 434de0e5b4 fix(deps): update module golang.org/x/sys to v0.16.0
Mirror Push / mirror (push) Successful in 6s Details
Test / test (stable) (push) Successful in 38s Details
Test / test (oldstable) (push) Successful in 44s Details
2024-01-04 16:00:25 +00:00
Renovate Bot 52cef2e3a3 fix(deps): update module github.com/quic-go/quic-go to v0.40.1
Mirror Push / mirror (push) Successful in 6s Details
Test / test (stable) (push) Successful in 34s Details
Test / test (oldstable) (push) Successful in 41s Details
2023-12-13 05:00:25 +00:00
Renovate Bot e0b14b8b00 chore(deps): update actions/setup-go action to v5
Mirror Push / mirror (push) Successful in 8s Details
Test / test (stable) (push) Successful in 32s Details
Test / test (oldstable) (push) Successful in 40s Details
2023-12-06 16:00:23 +00:00
Renovate Bot c580ac78d4 fix(deps): update module golang.org/x/net to v0.19.0
Mirror Push / mirror (push) Successful in 7s Details
Test / test (stable) (push) Successful in 35s Details
Test / test (oldstable) (push) Successful in 41s Details
2023-11-27 18:00:40 +00:00
Renovate Bot 99e75afcd3 fix(deps): update module golang.org/x/sys to v0.15.0
Mirror Push / mirror (push) Successful in 8s Details
Test / test (oldstable) (push) Successful in 34s Details
Test / test (stable) (push) Successful in 40s Details
2023-11-27 17:00:24 +00:00
Renovate Bot 6ddbf854d8 fix(deps): update module github.com/miekg/dns to v1.1.57
Mirror Push / mirror (push) Successful in 9s Details
Test / test (stable) (push) Successful in 35s Details
Test / test (oldstable) (push) Successful in 39s Details
2023-11-15 10:00:52 +00:00
Sam Therapy 16ef3de591
docs(readme): Use a better file name for deb
Mirror Push / mirror (push) Successful in 8s Details
Test / test (stable) (push) Successful in 33s Details
Test / test (oldstable) (push) Successful in 41s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-13 18:44:48 +01:00
Renovate Bot f9263300c7 fix(deps): update module golang.org/x/net to v0.18.0
Mirror Push / mirror (push) Successful in 8s Details
Test / test (oldstable) (push) Successful in 36s Details
Test / test (stable) (push) Successful in 39s Details
2023-11-08 23:00:24 +00:00
Sam Therapy d43543e48a
fix(deps): Go sum update from goreleaser
Mirror Push / mirror (push) Successful in 8s Details
Test / test (oldstable) (push) Successful in 34s Details
Test / test (stable) (push) Successful in 39s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-09 00:38:52 +01:00
Sam Therapy 0a6d6b432c
fix(ci): Force goreleaser to use gitea
Mirror Push / mirror (push) Successful in 11s Details
Test / test (oldstable) (push) Successful in 43s Details
Test / test (stable) (push) Successful in 38s Details
Gitea Release / release (push) Failing after 1m10s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 22:04:09 +01:00
Sam Therapy ffb16ff7fa
fix)ci): make apt update first
Mirror Push / mirror (push) Successful in 7s Details
Gitea Release / release (push) Failing after 31s Details
Test / test (stable) (push) Successful in 39s Details
Test / test (oldstable) (push) Successful in 41s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 21:57:50 +01:00
Sam Therapy efb4cd6c4a
[SKIP CI] fix(ci): remove sudo
Mirror Push / mirror (push) Successful in 7s Details
Gitea Release / release (push) Failing after 29s Details
Test / test (stable) (push) Successful in 31s Details
Test / test (oldstable) (push) Successful in 37s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 21:54:57 +01:00
Sam Therapy 59fa146d7f
feat!(quic): Remove gccgo support
Mirror Push / mirror (push) Successful in 7s Details
Gitea Release / release (push) Failing after 22s Details
Test / test (stable) (push) Successful in 38s Details
Test / test (oldstable) (push) Successful in 1m23s Details
Since gccgo is stuck at 1.18 without generics, drop support
sadge

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 21:51:44 +01:00
Sam Therapy c5c90b97af
fix(deps): go get -u
Mirror Push / mirror (push) Successful in 10s Details
Test / test (stable) (push) Successful in 33s Details
Test / test (oldstable) (push) Successful in 41s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 21:47:19 +01:00
Sam Therapy e1122a9de7
misc: Cleanup before a release
Mirror Push / mirror (push) Successful in 8s Details
Test / test (stable) (push) Successful in 33s Details
Test / test (oldstable) (push) Successful in 42s Details
Kept a lot waiting, haven't I?

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 21:45:39 +01:00
Sam Therapy 26ef04b969 feat(goreleaser): Vastly simplify upload process (#229)
Mirror Push / mirror (push) Successful in 8s Details
Test / test (stable) (push) Successful in 38s Details
Test / test (oldstable) (push) Successful in 1m1s Details
Make the upload process to Gitea packages much less of a clusterbomb

Reviewed-on: #229
2023-11-08 18:02:34 +00:00
Sam Therapy 2565d34ade
docs(readme): Update mailing list
Mirror Push / mirror (push) Successful in 10s Details
Test / test (oldstable) (push) Successful in 35s Details
Test / test (stable) (push) Successful in 38s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 01:04:30 +01:00
Sam Therapy 7dd9b138a3
ci: Add sr.ht mirror
Mirror Push / mirror (push) Successful in 9s Details
Test / test (oldstable) (push) Successful in 34s Details
Test / test (stable) (push) Successful in 39s Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-11-08 00:54:53 +01:00
Sam Therapy b8a2407169 ci: Add Gitea Actions (#228)
Test / test (oldstable) (push) Successful in 1m0s Details
Test / test (stable) (push) Successful in 39s Details
Use Gitea Actions instead of Drone.

Reviewed-on: #228
2023-11-07 20:27:18 +00:00
Renovate Bot 9ef46d33e8 fix(deps): update module golang.org/x/sys to v0.14.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-11-04 16:00:24 +00:00
Renovate Bot 939a09da5b fix(deps): update module github.com/quic-go/quic-go to v0.40.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-31 05:00:30 +00:00
Renovate Bot 87c59551ec fix(deps): update module github.com/quic-go/quic-go to v0.39.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-27 07:00:24 +00:00
Renovate Bot 7efc9523bf fix(deps): update module github.com/quic-go/quic-go to v0.39.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-25 05:00:39 +00:00
Renovate Bot 2084ae69f6 fix(deps): update module github.com/quic-go/quic-go to v0.39.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-17 07:00:22 +00:00
Renovate Bot 5130b6c3cd fix(deps): update module golang.org/x/net to v0.17.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-10 17:00:24 +00:00
Renovate Bot 8c949ce4ca fix(deps): update module golang.org/x/net to v0.16.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-06 00:00:19 +00:00
Renovate Bot b3fad7abcf fix(deps): update module golang.org/x/sys to v0.13.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-05 13:00:22 +00:00
Renovate Bot b713aeb7d4 fix(deps): update module github.com/quic-go/quic-go to v0.39.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-24 12:00:23 +00:00
Renovate Bot 260eb979b3 fix(deps): update module gotest.tools/v3 to v3.5.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-16 20:00:23 +00:00
Renovate Bot 83ecfbaea1 fix(deps): update module github.com/miekg/dns to v1.1.56
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-12 11:00:22 +00:00
Renovate Bot 10faea804b chore(deps): update goreleaser/goreleaser-action action to v5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-11 19:00:21 +00:00
Renovate Bot 0f80004790 fix(deps): update module golang.org/x/net to v0.15.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-05 17:00:24 +00:00
Renovate Bot 12863559c5 chore(deps): update actions/checkout action to v4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-04 13:00:20 +00:00
Renovate Bot 8e9574f870 fix(deps): update module golang.org/x/sys to v0.12.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-02 13:00:21 +00:00
Renovate Bot fadb04f49f fix(deps): update module github.com/quic-go/quic-go to v0.38.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-08-25 01:00:23 +00:00
Renovate Bot 5704c6a0dd fix(deps): update module github.com/quic-go/quic-go to v0.38.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-08-21 04:00:25 +00:00
Renovate Bot 9c8e5349bf fix(deps): update module github.com/quic-go/quic-go to v0.37.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-08-20 00:00:25 +00:00
Sam Therapy dcd932c42e
[SKIP CI] docs: Bump go version in README
Maybe with the removal of the quic TLS fork go version can be supported
longer

gccgo generic support soon (tm) :(
2023-08-11 20:37:07 +02:00
Sam Therapy f4c3283954
[SKIP CI] fix(goreleaser): Unbreak termux?
Hopefully this unbreaks termux packages because they try going into
/usr/bin which makes termux very sad
2023-08-11 20:23:31 +02:00
Sam Therapy 824a60aef1
fix(mk): Add version number in -V
awk(1) magic saves the day
2023-08-11 20:22:16 +02:00
Sam Therapy 43e53a4055
fix(ci): Update GitHub Actions to Go 1.21
continuous-integration/drone/push Build is passing Details
2023-08-10 19:43:43 +02:00
Sam Therapy 60b0300eec
fix(goreleaser): Remove deprecation warnings
continuous-integration/drone/push Build is passing Details
2023-08-10 19:37:54 +02:00
Sam Therapy 9740e6b348
fix(deps): Update indirect dependencies
continuous-integration/drone/push Build is passing Details
2023-08-10 19:35:03 +02:00
Renovate Bot 8c888a90f1 fix(deps): update module gotest.tools/v3 to v3.5.0 (#202)
continuous-integration/drone/push Build is passing Details
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Co-committed-by: Renovate Bot <renovate@whitesourcesoftware.com>
2023-08-10 15:07:46 +00:00
Renovate Bot 71477684d9 fix(deps): update module github.com/quic-go/quic-go to v0.37.4 (#206)
continuous-integration/drone/push Build is running Details
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Co-committed-by: Renovate Bot <renovate@whitesourcesoftware.com>
2023-08-10 15:07:32 +00:00
Renovate Bot e3951db486 fix(deps): update module golang.org/x/net to v0.14.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-08-04 23:00:28 +00:00
Renovate Bot dfe4cca176 fix(deps): update module golang.org/x/sys to v0.11.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-08-04 16:00:23 +00:00
Renovate Bot b5102c91e6 fix(deps): update module golang.org/x/net to v0.13.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-08-01 22:00:24 +00:00
Renovate Bot 03e8f6be61 Merge pull request 'fix(deps): update module golang.org/x/net to v0.12.0' (#205) from renovate/golang.org-x-net-0.x into master
continuous-integration/drone/push Build is passing Details
2023-07-15 17:00:20 +00:00
Renovate Bot f60cd1c315 Merge pull request 'fix(deps): update module golang.org/x/sys to v0.10.0' (#204) from renovate/golang.org-x-sys-0.x into master
continuous-integration/drone/push Build is passing Details
2023-07-15 15:35:47 +00:00
Renovate Bot 00b116abf4 Merge pull request 'fix(deps): update module github.com/quic-go/quic-go to v0.36.2' (#203) from renovate/github.com-quic-go-quic-go-0.x into master
continuous-integration/drone/push Build was killed Details
2023-07-15 15:35:40 +00:00
Renovate Bot 821149b463 Merge pull request 'fix(deps): update module golang.org/x/net to v0.11.0' (#199) from renovate/golang.org-x-net-0.x into master
continuous-integration/drone/push Build is passing Details
2023-06-21 19:07:12 +00:00
Renovate Bot f2f53795ef Merge pull request 'fix(deps): update module github.com/miekg/dns to v1.1.55' (#200) from renovate/github.com-miekg-dns-1.x into master
continuous-integration/drone/push Build is running Details
2023-06-21 19:06:39 +00:00
Renovate Bot 322345a758 Merge pull request 'fix(deps): update module github.com/quic-go/quic-go to v0.36.0' (#201) from renovate/github.com-quic-go-quic-go-0.x into master
continuous-integration/drone/push Build is passing Details
2023-06-21 18:58:40 +00:00
Renovate Bot b4de2ba89e Merge pull request 'fix(deps): update module golang.org/x/sys to v0.9.0' (#198) from renovate/golang.org-x-sys-0.x into master
continuous-integration/drone/push Build is running Details
2023-06-21 18:58:15 +00:00
Sam Therapy 3421066420
[SKIP CI] docs(readme): I hate RPM
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-06-09 01:32:26 +02:00
Sam Therapy aef8a73a28
[SKIP CI] packaging: Correct RPM documentation
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-06-09 01:07:02 +02:00
Sam Therapy ba93384f9b
misc: lint
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-06-02 23:21:56 +02:00
Sam Therapy 66855b5542 make test happy for some reason
continuous-integration/drone/push Build is failing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-06-02 21:10:05 +00:00
Sam Therapy ccc9df39b6 update QUIC code to work with new quic-go version
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-06-02 21:10:05 +00:00
Renovate Bot c6bbda17b2 fix(deps): update module github.com/quic-go/quic-go to v0.35.1 2023-06-02 21:10:05 +00:00
Sam Therapy db77f2315c
fix(https): Small HTTPS annoyance fixes
continuous-integration/drone/push Build is failing Details
Fixes /dns-query being appended on failure
Fixes /dns-query from being appended when it should not be

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-06-02 22:54:02 +02:00
Sam Therapy a157dcb7fb
[SKIP CI] feat(goreleaser): Target termux too 2023-05-15 17:02:00 +02:00
Sam Therapy 428c218405
[SKIP CI] docs(readme): Update packaging location
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-05-14 23:21:39 +02:00
Sam Therapy 8df4863cf4
feat(ci): use my new package utility instead of pages
continuous-integration/drone/push Build is passing Details
2023-05-14 22:45:54 +02:00
Sam Therapy c0cd4d7771
feat(goreleaser): Enable APK and RPM
Gitea supports them now so add them!
2023-05-12 23:19:24 +02:00
Renovate Bot 5f1b81a2be fix(deps): update module golang.org/x/net to v0.10.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-08 17:00:21 +00:00
Renovate Bot 45d1aaf535 fix(deps): update module golang.org/x/sys to v0.8.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-04 16:00:24 +00:00
Renovate Bot 4f42657294 fix(deps): update module github.com/quic-go/quic-go to v0.34.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-30 00:00:27 +00:00
Renovate Bot b6bd22a8fe fix(deps): update module github.com/miekg/dns to v1.1.54
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-29 23:00:27 +00:00
Sam Therapy b0d8e325d6
fix(gomod): Explicitly require 1.19
continuous-integration/drone/push Build is passing Details
2023-04-30 00:42:09 +02:00
Sam Therapy fb9fd7689f
fix(trace): Remove wrong error message
continuous-integration/drone/push Build is passing Details
Reverse queries *are* actually supported, so the error message was wrong
Even dig supports it naturally.
2023-04-27 22:04:28 +02:00
Sam Therapy 329d81c001
fix(deps): go get -u
continuous-integration/drone/push Build is passing Details
2023-04-12 14:46:46 +02:00
Renovate Bot 5329a6f2e0 fix(deps): update module golang.org/x/net to v0.9.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-10 20:00:30 +00:00
Renovate Bot 95ff6f554c fix(deps): update module golang.org/x/sys to v0.7.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-04 17:00:43 +00:00
Sam Therapy 928123edbd
fix(deps): go get -u
continuous-integration/drone/push Build is passing Details
2023-03-28 22:21:18 +02:00
Renovate Bot fd3ceeb066 chore(deps): update actions/setup-go action to v4
continuous-integration/drone/push Build is passing Details
2023-03-28 20:18:02 +00:00
Renovate Bot 1acd48b16d fix(deps): update module github.com/miekg/dns to v1.1.53
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is running Details
2023-03-28 19:00:34 +00:00
Sam Therapy 27ece358c6
fix(resolvers): QUIC: close as soon as the writing is done
continuous-integration/drone/push Build is passing Details
This is required by RFC 9250, and would break some requests when done wrong

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-03-09 23:39:00 +01:00
Sam Therapy 58fa7af2ea
fix(release): Correct scoop bucket location
continuous-integration/drone/push Build is passing Details
Use the scoop bucket template
2023-03-07 18:45:45 +01:00
Sam Therapy 75fb8f2fc6
fix(docs): We're so back
continuous-integration/drone/push Build is passing Details
Using my fork of the DNSCrypt library brings back go install

This reverts commit daff2b7687.
2023-03-07 00:13:26 +01:00
Sam Therapy 3423958809
fix(deps): Use my fork of the dnscrypt lib (#185)
continuous-integration/drone/push Build is passing Details
- Uses newer versions of libraries
- Nicer URL (subjective)
- it is mine :)

Reviewed-on: #185
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2023-03-06 22:41:12 +00:00
Renovate Bot 60491c39c1 fix(deps): update module golang.org/x/net to v0.8.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-05 04:00:19 +00:00
Renovate Bot 39f605740a fix(deps): update module golang.org/x/sys to v0.6.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-04 16:00:22 +00:00
Sam Therapy 171f5d0d11
fix(doc): fix literally unusable typo in manpage
continuous-integration/drone/push Build is passing Details
2023-03-01 16:16:21 +01:00
Renovate Bot 1e4ff59631 fix(deps): update module github.com/miekg/dns to v1.1.51
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-26 12:00:19 +00:00
Renovate Bot f1583fef52 fix(deps): update module github.com/quic-go/quic-go to v0.33.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-22 06:00:28 +00:00
Sam Therapy fa709ca327
fix(ci): :(
continuous-integration/drone/push Build was killed Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-20 20:44:59 +01:00
Sam Therapy 30277b5028
fix(goreleaser): add capitalize FreeBSD
continuous-integration/drone/push Build is passing Details
I give up.

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-20 20:39:43 +01:00
Sam Therapy 028c423d89
fix(goreleaser): stop using universal binaries
continuous-integration/drone/push Build is passing Details
Fixes an extremely annoying goreleaser problem that I cannot replicate at all

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-20 20:35:52 +01:00
Renovate Bot 641b003381 chore(deps): update goreleaser/goreleaser-action action to v4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-17 22:00:14 +00:00
Sam Therapy b15584f436
fix(ci): I hate you, GHA
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-17 22:44:21 +01:00
Sam Therapy 9c8f30f432
fix(ci): target 1.20, drop 1.19
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-17 22:40:16 +01:00
Sam Therapy da172f9de5
Revert "ci: Purge GHA"
This reverts commit 0bcc63de8a.
2023-02-17 22:36:18 +01:00
Sam Therapy feb0617ce2
fix(deps): go get -u
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-17 20:40:50 +01:00
Sam Therapy daff2b7687
fix(README): go install does not like replace
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-17 19:34:31 +01:00
Sam Therapy e782c9cb23
feat: change import URL
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-17 19:18:58 +01:00
Sam Therapy 00ec4ef893
fix(Make): stop compressing the manpages
continuous-integration/drone/push Build is passing Details
Let whatever installs them do it themselves

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-16 23:23:10 +01:00
Sam Therapy bf04f59468
fix(goreleaser): add vendor dependency tarball
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-16 18:53:36 +01:00
Renovate Bot db66371420 fix(deps): update module golang.org/x/net to v0.7.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-14 18:00:21 +00:00
Renovate Bot 42b17a7912 fix(deps): update module golang.org/x/net to v0.6.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-08 22:00:20 +00:00
Renovate Bot 4286c16ae2 fix(deps): update module golang.org/x/sys to v0.5.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-07 01:00:31 +00:00
Sam Therapy 6d9690c44f
fix: lint
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-04 15:37:06 -06:00
Sam Therapy bc6e7cd759
fix(deps): Use new quic-go location + update
continuous-integration/drone/push Build is failing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-02-04 14:27:47 +01:00
Sam Therapy 2312fecfc8 fix(trace): Ensure traces stop when request fails (#175)
continuous-integration/drone/push Build is passing Details
Closes #174

Reviewed-on: #175
2023-01-31 16:08:07 +00:00
Sam Therapy 75479f5060
fix(deps): update external dependencies
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2023-01-31 17:06:29 +01:00
Renovate Bot 0c8000d598 fix(deps): update module golang.org/x/net to v0.5.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-01-04 17:00:18 +00:00
Sam Therapy 30be18b474
[SKIP CI] update wiki
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-12-27 21:52:41 +01:00
grumbulon d93eccc064
feat: RFC-8427 (#171)
continuous-integration/drone/push Build is passing Details
This PR will make the JSON response body be based off [RFC-8427](https://www.rfc-editor.org/rfc/rfc8427.html) which will be similar to [kdig's](https://www.knot-dns.cz/docs/2.6/html/man_kdig.html) JSON output.

Co-authored-by: Sam Therapy <sam@samtherapy.net>
Reviewed-on: #171
Reviewed-by: Sam Therapy <sam@samtherapy.net>
Co-authored-by: grumbulon <grumbulon@grumbulon.xyz>
Co-committed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-27 20:07:09 +00:00
Sam Therapy fdba9a0a41
refactor: Add named returns (#168)
continuous-integration/drone/push Build is passing Details
Add some named returns

Co-authored-by: grumbulon <grumbulon@grumbulon.xyz>
Reviewed-on: #168
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-17 16:52:50 +00:00
Sam Therapy a7f7498d8b
docs(man): overhaul the manpage [SKIP CI] (#170)
Make everything alphabetical, make it look more like dig and kdig

Reviewed-on: #170
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-15 19:30:22 +00:00
Sam Therapy 792ddecf6c
fix(print): use any on TTL (#169)
continuous-integration/drone/push Build is passing Details
This allows JSON to use either an int or string depending on output

Reviewed-on: #169
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-15 19:30:01 +00:00
Sam Therapy 10d1c4cd2b fix(trace): Only trace when tracing (#167)
continuous-integration/drone/push Build is passing Details
AAAAAAAAAAAAAAAAAAAA

Reviewed-on: #167
2022-12-10 18:19:30 +00:00
Sam Therapy 530ef06ee1
feat: Trace (#162)
continuous-integration/drone/push Build is passing Details
This PR should add the trace option, used by +trace and --trace

TODO
- [x] Making the trace work properly
- [x] Documentation adding
- [x] Completion adding

Co-authored-by: grumbulon <grumbulon@grumbulon.xyz>
Reviewed-on: #162
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-10 17:51:49 +00:00
Sam Therapy 0b042af3e2
fix(win): Fix occasional buffer overflows (#166)
continuous-integration/drone/push Build is running Details
This fixes a problem I have had where I get the following message sometimes:
Error: config, windows: The file name is too long.

Delv says it is a buffer overflow so this fixes it at the cost of just allocating more memory.

TODO: Make actually good

Reviewed-on: #166
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-10 17:51:00 +00:00
Renovate Bot 4349aec1f2 fix(deps): update module github.com/lucas-clemente/quic-go to v0.31.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-12-08 21:00:15 +00:00
Sam Therapy 185a79e1c2
fix(printable): actually hide sections if requested (#164)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #164
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-08 15:21:10 +00:00
Renovate Bot 11ebd2c69f fix(deps): update module golang.org/x/net to v0.4.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-12-07 10:00:15 +00:00
Sam Therapy 64768f0956
fix(errors): put exported errors in util (#161)
continuous-integration/drone/push Build is passing Details
Centralize them in one place

Reviewed-on: #161
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-06 21:39:28 +00:00
Renovate Bot 45acd03dff fix(deps): update module golang.org/x/net to v0.3.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-12-06 05:00:16 +00:00
Sam Therapy 78bbb8f23d
fix(make): use correct tag when version is greater than 10
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-12-05 15:25:24 +01:00
Renovate Bot 7e7c88ddd5 fix(deps): update module golang.org/x/sys to v0.3.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-12-04 16:00:20 +00:00
Sam Therapy a2e7b262ea
fix(misc): minor changes (#158)
continuous-integration/drone/push Build is passing Details
1. update indirect dependencies
2. Set timeout to 5 seconds instead of 1
- this helps with slow queries
3. Put class back in the default query output
- this fixes a problem with using awl to make a root file

Reviewed-on: #158
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-12-03 20:36:23 +00:00
Sam Therapy 90f5b92ffd
[SKIP CI] fix(ci): fix typo in downstream plugin
Grafana changed the name of the container

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-11-21 23:12:11 +01:00
grumbulon 5d67ffaad3
fix(log): log levels were wrong for two format statements (#157)
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build encountered an error Details
Reviewed-on: #157
Reviewed-by: Sam Therapy <sam@samtherapy.net>
Co-authored-by: grumbulon <grumbulon@grumbulon.xyz>
Co-committed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-11-20 00:25:35 +00:00
Sam Therapy 303eb974c8
fix(readme): [SKIP CI] add / dev/null to debian
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-11-19 22:52:53 +01:00
Sam Therapy dfa14cbcdf
feat(gccgo): allow compilation with gccgo (#156)
continuous-integration/drone/push Build is passing Details
This PR disables quic-go when compiling with gccgo since gccgo does not support generics

Reviewed-on: #156
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-11-19 18:25:04 +00:00
Sam Therapy 216431f591
fix(make): default file should be static linked [CI SKIP]
Follow the go way
2022-11-17 20:25:34 +01:00
Renovate Bot 07260fb593 fix(deps): update module github.com/lucas-clemente/quic-go to v0.31.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-11-16 23:00:19 +00:00
Renovate Bot 9a9658c6f8 fix(deps): update module golang.org/x/net to v0.2.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-11-08 19:00:16 +00:00
Renovate Bot e456d409ef fix(deps): update module golang.org/x/sys to v0.2.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-11-07 20:00:20 +00:00
Sam Therapy ea3943dd63
fix(ci): use Grafana's downstream trigger
continuous-integration/drone/push Build is passing Details
Newer and includes PRs that should fix the problems I've had
2022-11-07 16:41:21 +01:00
Sam Therapy feef2cddaf
fix(https): add default endpoint (#152)
continuous-integration/drone/push Build is passing Details
say you do -H @dns.froth.zone/sodd
It should send a request to dns.froth.zone/sodd
but if you do -H @dns.froth.zone
it should send a request to dns.froth.zone/dns-query

This does that

Reviewed-on: #152
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-11-07 15:07:44 +00:00
Sam Therapy 2836633c62
fix(make): Make makefile more like the arch default.
continuous-integration/drone/push Build is passing Details
This also fixes the AUR problem I had and forces an upgrade.
2022-11-07 15:50:42 +01:00
Sam Therapy a64a260c55
fix(test): typo fix
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-31 13:56:10 +01:00
Sam Therapy dd542b9f18
ci: replace concat with format strings
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-27 17:56:00 +02:00
Sam Therapy c658b6b796
fix(deps): go get -u
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-27 15:05:33 +02:00
Renovate Bot da7ce61152 Update module github.com/lucas-clemente/quic-go to v0.30.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-22 12:00:15 +00:00
Sam Therapy 852c87c3ab
[SKIP CI] fix(goreleaser): remove broken brew HEAD
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-21 17:10:12 +02:00
Sam Therapy ec6e723eaf
fix(goreleaser) correct URL
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-21 16:16:01 +02:00
Sam Therapy 698b0d3ad1
fix(goreleaser) correct URL
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-21 15:19:21 +02:00
Sam Therapy 8c3a5cb369
[SKIP CI] docs(readme): add more packages
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-21 03:40:19 +02:00
Sam Therapy 60e22e6d33
[SKIP CI] chore(goreleaser): add completions and manpage
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-21 03:30:07 +02:00
Sam Therapy 7feaab7103
[SKIP CI] docs(readme): update apt instructions
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-20 23:15:13 +00:00
Sam Therapy b1fa25a9a0
feat(dig): Dig +https (#147)
continuous-integration/drone/push Build is passing Details
BREAKING CHANGE: DNS-over-HTTPS requests are now dealt with differently

Using +https or -H now implies adding /dns-query (like dig)
Using the implied https:// prefix follows the old behaviour (nothing added or implied)

Reviewed-on: #147
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-10-20 21:34:24 +00:00
Sam Therapy 59212798f7
[SKIP CI] fix(make): Real actual macOS support (#148)
Tested again on the lone Apple device I have.

This time it does not break make on Linux 🌝

Reviewed-on: #148
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-10-20 21:34:16 +00:00
Sam Therapy a386960076
[SKIP CI] chore(goreleaser): Add scoop/brew support (#149)
Reviewed-on: #149
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-10-20 21:34:07 +00:00
Sam Therapy 6144c7219a
[SKIP CI] ci: put token secret in right place
hehe

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-20 23:32:44 +02:00
Renovate Bot 967363f34b fix(deps): update module golang.org/x/net to v0.1.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-19 16:00:15 +00:00
Sam Therapy 2db6ee3935
[SKIP CI] fix(main): Remove webasm restriction
https://www.arp242.net/wasm-cli.html
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-19 14:51:19 +02:00
Renovate Bot 3a1ebaafe1 fix(deps): update golang.org/x/net digest to cb67ada
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-19 03:00:14 +00:00
Renovate Bot 354128d449 fix(deps): update module golang.org/x/sys to v0.1.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-17 19:00:14 +00:00
Sam Therapy def649de4f [SKIP CI] docs: fix HTTPS
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-17 17:38:42 +00:00
Renovate Bot d6a9bd9b0b fix(deps): update golang.org/x/net digest to f25eb7e
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-17 16:00:15 +00:00
Sam Therapy 0bcc63de8a
ci: Purge GHA
continuous-integration/drone/push Build is passing Details
I would rather dedicate to only Gitea (and maybe SRHT)

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-17 15:07:55 +02:00
Sam Therapy 39639c0f7f
fix(deps): go get -u
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-16 16:27:05 +02:00
Sam Therapy b80219019e
Add check for port at the end (#142)
continuous-integration/drone/push Build is passing Details
Fixes #141

Before, a failure would add on the port, eg.
```
127.0.0.1:53
127.0.0.1:53:53
127.0.0.1:53:53:53 // Go actually thinks this is now an IPv6 address, interesting
```

Now a check is added so this doesn't happen

Reviewed-on: #142
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-10-16 14:25:13 +00:00
Sam Therapy bf0e44e80c
ci: Remove server header that was broken
continuous-integration/drone/push Build is failing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-14 20:15:32 +02:00
Sam Therapy 9ec28f9b1a
[SKIP CI] docs: add apt repo to README
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-14 18:59:23 +02:00
Sam Therapy dc4edd55bb
fix(query): fix printing irregularities (#140)
continuous-integration/drone/push Build is passing Details
1. Before, the port printed was duplicated, eg.
`;; SERVER: [::1]:53:53 (UDP)` when making query. This has been fixed (not sure what caused it)
2. JSON/XML/YAML date formatting to be compliant with RFC3339 (thanks, std/time!)

Co-authored-by: Sam Therapy <sam@samtherapy.net>
Reviewed-on: #140
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-10-14 16:51:27 +00:00
Sam Therapy add1ef61a2
feat(packaging): Prepare for apt packaging (#139)
continuous-integration/drone/push Build is passing Details
This needs to be tested (preferably extensively), which I will do now

Reviewed-on: #139
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-10-14 14:11:01 +00:00
Sam Therapy 2d94ea6838 revert e6a3d6040a
continuous-integration/drone/push Build is passing Details
This broke every other operating system (and the AUR)
2022-10-14 12:47:44 +00:00
Renovate Bot 216a4b6dd7 fix(deps): update golang.org/x/net digest to f15817d
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-14 09:00:14 +00:00
Renovate Bot 66422e1512 fix(deps): update golang.org/x/sys digest to 95e765b
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-13 18:00:14 +00:00
Sam Therapy 81da49093d
refactor: Make all calls to options pointers (#132)
continuous-integration/drone/push Build is passing Details
Instead of copying the opts struct every time it gets passed around, it should be created once and passed through reference.

This should reduce memory utilization, unfortunately I cannot test it since this program runs so fast pprof won't report anything useful.

I think I found all of them 🙂

Co-authored-by: Sam Therapy <sam@samtherapy.net>
Reviewed-on: #132
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-10-13 12:49:36 +00:00
Sam Therapy e6a3d6040a [SKIP CI] fix: Make makefiles portable
-D isn't supported on MacOS (sample size: 1)

Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-13 12:49:15 +00:00
Renovate Bot c1b5961717 fix(deps): update module github.com/lucas-clemente/quic-go to v0.29.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-12 15:00:22 +00:00
Renovate Bot 3d97359070 fix(deps): update golang.org/x/net digest to 0b7e1fb
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-12 14:00:14 +00:00
Renovate Bot 561958c1b0 fix(deps): update golang.org/x/sys digest to 090e330
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-10 18:00:14 +00:00
Renovate Bot d2c6ed317e fix(deps): update module gotest.tools/v3 to v3.4.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-07 17:00:13 +00:00
Renovate Bot 9734f8ddad fix(deps): update module github.com/dchest/uniuri to v1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-07 13:00:13 +00:00
Renovate Bot 296f5f0a0b fix(deps): update golang.org/x/sys digest to 84dc82d
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-06 22:00:17 +00:00
Sam Therapy 4d0605bd1e
fix(deps): go get -u
continuous-integration/drone/push Build is passing Details
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-10-06 16:49:48 +02:00
Renovate Bot a74fbf525b fix(deps): update golang.org/x/net digest to 8021a29
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-04 17:03:36 +00:00
Renovate Bot cf095313a4 fix(deps): update golang.org/x/net digest to bcab684
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-10-02 03:00:20 +00:00
Renovate Bot 6749361395 fix(deps): update golang.org/x/net digest to 107f3e3
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-09-30 22:00:19 +00:00
Sam Therapy 4495da2f3e
[SKIP CI] correct README
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-09-30 19:38:26 +02:00
Renovate Bot 11c06f9662 fix(deps): update github.com/dchest/uniuri digest to 3027df4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-29 11:00:18 +00:00
Sam Therapy d701059b5f [SKIP CI] docs(readme): add links
Signed-off-by: Sam <sam@samtherapy.net>
2022-09-28 21:09:11 +00:00
Sam Therapy 56baff4e18 test: forgot one
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-28 19:07:46 +00:00
Sam Therapy 6a839ba8e5 [SKIP CI] Update wiki
continuous-integration/drone/tag Build is passing Details
2022-09-28 18:36:38 +00:00
Sam Therapy f2cf35ca31 misc: Snapcraft support (#87)
continuous-integration/drone/push Build is passing Details
Preliminary shell for supporting a snap

Reviewed-on: #87
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-28 18:32:20 +00:00
Sam Therapy 607c321de3
feat: fish completions (#122)
continuous-integration/drone/push Build is passing Details
These are basically thrown together but more is always good

Reviewed-on: #122
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-28 18:11:05 +00:00
Sam Therapy f01f2bc15a
feat(resolvers/HTTPS): add HTTP proxy support (#119)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #119
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-28 18:08:56 +00:00
Sam Therapy c053c077c8
test: Add retries to all tests (#121)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #121
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-28 17:51:35 +00:00
Renovate Bot 0a371cd335 fix(deps): update golang.org/x/sys digest to f11e5e4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-28 15:00:18 +00:00
Sam Therapy f2218481ee
feat(completions): add bash (#118)
continuous-integration/drone/push Build is passing Details
well, at least the same Dig and drill do

Reviewed-on: #118
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-28 12:40:27 +00:00
Renovate Bot 14416d5aec fix(deps): update golang.org/x/net digest to f486391
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-27 19:00:18 +00:00
Renovate Bot 7b11583d6e fix(deps): update golang.org/x/sys digest to d9d178b
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-27 18:00:21 +00:00
Renovate Bot 7bd481cd7a fix(deps): update golang.org/x/net digest to aa73b25
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-27 16:00:20 +00:00
Sam Therapy 60643e2b3e
Remove AUR submodule
continuous-integration/drone/push Build was killed Details
2022-09-27 15:36:34 +00:00
Sam Therapy 434632884c
chore(test): Add more retires (#115)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #115
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-27 13:54:17 +00:00
Renovate Bot 284c0646f2 fix(deps): update golang.org/x/net digest to 02166a9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2022-09-26 20:00:19 +00:00
grumbulon 3a0a8f015a chore(Refactor) (#110)
continuous-integration/drone/push Build is passing Details
refactor

Co-authored-by: Sam Therapy <sam@samtherapy.net>
Reviewed-on: #110
Reviewed-by: Sam <sam@samtherapy.net>
2022-09-24 23:11:09 +00:00
Sam Therapy ac55b21b25
[SKIP CI] go mod tidy
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-09-24 18:49:02 +02:00
Renovate Bot 840397a85a fix(deps): update golang.org/x/net digest to 8be6392
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-23 21:00:16 +00:00
Renovate Bot f24608667a fix(deps): update module github.com/lucas-clemente/quic-go to v0.29.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2022-09-23 12:00:18 +00:00
Sam Therapy 0845ae2a82
refactor: Make tests less ugly (#102)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #102
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-21 23:08:31 +00:00
Renovate Bot 94b7f523b7 fix(deps): update golang.org/x/net digest to d300de1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2022-09-21 21:00:15 +00:00
Renovate Bot c75db286dc fix(deps): update golang.org/x/net digest to db77216
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-21 16:00:16 +00:00
Renovate Bot 261e5fd6c1 fix(deps): update golang.org/x/net digest to d0c6ba3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-20 21:00:16 +00:00
Renovate Bot 75ca107e55 fix(deps): update golang.org/x/net digest to f2f64eb
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-20 00:00:20 +00:00
Renovate Bot 98c641848e fix(deps): update golang.org/x/net digest to f8f703f
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-19 18:00:14 +00:00
Renovate Bot 86f115f361 fix(deps): update golang.org/x/sys digest to fb04ddd
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-19 10:00:14 +00:00
Sam Therapy ef87175190
fix: remove race conditions with flags (#101)
continuous-integration/drone/push Build is passing Details
Mainly for testing but a nice to have.

Proceeds a larger testing refactor

Reviewed-on: #101
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-17 10:42:49 +00:00
Sam Therapy 933967016f [SKIP CI] misc: makefile refactoring (#100)
Mainly making it so that it doesn't always run tests when making coverage

Also, making it more cross platform and less splintered is nice.

Co-authored-by: Sam Therapy <sam@samtherapy.net>
Reviewed-on: #100
2022-09-16 23:38:46 +00:00
Sam Therapy 8df0347891 fix: EDNS cookies work properly (#98)
continuous-integration/drone/push Build is passing Details
also slight refactors :)

closes #96

Co-authored-by: Sam Therapy <sam@samtherapy.net>
Reviewed-on: #98
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-16 23:26:10 +00:00
Sam Therapy ddd0277de9 [SKIP CI] ci: add caching (#99)
Reviewed-on: #99
Reviewed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-09-16 23:25:56 +00:00
Renovate Bot 55b965d084 fix(deps): update golang.org/x/sys digest to 7b5979e
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-09-15 21:00:17 +00:00
88 changed files with 3463 additions and 2620 deletions

View File

@ -1,92 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
local testing(version, arch) = {
kind: "pipeline",
type: "docker",
name: version + "-" + arch ,
platform: {
arch: arch
},
steps: [
{
name: "lint",
image: "rancher/drone-golangci-lint:latest",
},
{
name: "test",
image: "golang:" + version,
commands: [
"make test-ci"
],
depends_on: [
"lint",
],
},
{
name: "fuzz",
image: "golang:" + version,
commands: [
"make fuzz-ci",
],
depends_on: [
"lint",
],
},
],
trigger: {
event: {
exclude: [
"tag"
],
}
},
};
// "Inspired by" https://goreleaser.com/ci/drone/
local release() = {
kind: "pipeline",
type: "docker",
name: "release",
trigger: {
event: [
"tag"
],
},
steps: [
{
name: "fetch",
image: "alpine/git",
commands : [
"git fetch --tags",
]
},
{
name: "test",
image: "golang",
commands: [
"make test-ci"
]
},
{
name: "release",
image: "goreleaser/goreleaser",
environment: {
"GITEA_TOKEN": {
from_secret: "GITEA_TOKEN"
}
},
commands: [
"goreleaser release"
],
}
]
};
[
testing("1.19", "amd64"),
testing("1.19", "arm64"),
testing("1.18", "amd64"),
testing("1.18", "arm64"),
release()
]

View File

@ -4,9 +4,7 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -0,0 +1,35 @@
name: Forgejo Release
on:
push:
tags:
- "*"
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: stable
- name: Install scdoc
run: apt-get update && apt-get install -y scdoc
- name: Release with GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser
version: latest
args: release --clean --skip docker,snapcraft
env:
GORELEASER_FORCE_TOKEN: gitea
GITEA_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
UPLOAD_PACKAGES_SECRET: ${{ secrets.PUBLISH_TOKEN }}

View File

@ -0,0 +1,14 @@
name: Mirror Push
on: push
jobs:
mirror:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pixta-dev/repository-mirroring-action@v1
with:
target_repo_url: git@git.sr.ht:~sammefishe/awl
ssh_private_key: ${{ secrets.SRHT_SSH_KEY }}

View File

@ -0,0 +1,24 @@
name: Test
on: push
jobs:
test:
strategy:
fail-fast: true
matrix:
goVer: ["oldstable", "stable"]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.goVer }}
- name: Test
run: make test-ci

View File

@ -4,29 +4,53 @@ on:
push:
tags:
- "*"
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
release:
runs-on: ubuntu-latest
env:
SNAPCRAFT_STORE_CREDENTIALS : ${{ secrets.SNAPCRAFT_TOKEN }}
steps:
# Workaround a dumb docker problem where everything has to be lowercase
- id: lowercase
run: |
echo IMAGE_NAME=$(echo $IMAGE_NAME | tr '[:upper:]' '[:lower:]') >> "${GITHUB_ENV}"
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- name: Checkout submodules
run: git submodule update --init --recursive
- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v2
- name: Set up Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.19
go-version: stable
- name: Install scdoc
run: sudo apt-get install -y scdoc
- name: Workaround a dumb Snap bug
run: mkdir -p $HOME/.cache/snapcraft/download && mkdir -p $HOME/.cache/snapcraft/stage-packages
- name: Release with GoReleaser
uses: goreleaser/goreleaser-action@v3
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser
version: latest
args: release --rm-dist
args: release --clean --skip=aur,homebrew,nix,scoop
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -7,19 +7,18 @@ jobs:
fail-fast: true
matrix:
platform: [macos, windows]
goVer: [1.18, 1.19]
goVer: ["oldstable", "stable"]
runs-on: ${{ matrix.platform }}-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.goVer }}
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout submodules
run: git submodule update --init --recursive
- name: Test
run: make test-ci

7
.gitignore vendored
View File

@ -12,7 +12,7 @@
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
vendor/
# Go workspace file
go.work
@ -23,6 +23,7 @@ coverage/*
!coverage/.gitkeep
awl
doc/awl.1
docs/awl.1
docs/awl.1.gz
.dccache
.dccache

7
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "doc/wiki"]
path = doc/wiki
[submodule "docs/wiki"]
path = docs/wiki
url = ../awl.wiki
[submodule "pkg/awl-dns-git"]
path = pkg/awl-dns-git
url = https://aur.archlinux.org/awl-dns-git.git

View File

@ -2,37 +2,153 @@
# Make sure to check the documentation at https://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- make clean
- go mod tidy
# you may remove this if you don't need go generate
# - go generate ./...
# Make manpages
- docs/makeman.sh
# Vendor dependencies
- go mod vendor
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
targets:
- go_first_class
- plan9_amd64
- freebsd_amd64
universal_binaries:
- replace: true
archives:
-
files:
- LICENCE
- files:
- LICENSE
- completions/**
replacements:
darwin: macOS
linux: Linux
windows: Windows
amd64: x86_64
- docs/awl.1.gz
name_template: >-
{{ .ProjectName }}_
{{- if eq .Os "darwin" }}MacOS_
{{- else if eq .Os "freebsd" }}FreeBSD_
{{- else }}{{- title .Os }}_{{ end }}
{{- if eq .Arch "386" }}i386
{{- else if eq .Arch "mips64" }}mips64_hardfloat
{{- else if eq .Arch "mips64le" }}mips64le_hardfloat
{{- else }}{{ .Arch }}{{ end -}}
format_overrides:
- goos: windows
format: zip
- files:
- vendor/**
id: vendor
format: tar.xz
name_template: "{{ .ProjectName }}-{{ .Version }}-deps"
meta: true
wrap_in_directory: "{{ .ProjectName }}"
nfpms:
- id: packages
package_name: awl-dns
vendor: Sam Therapy <sam@samtherapy.net>
maintainer: Sam Therapy <sam@samtherapy.net>
homepage: https://dns.froth.zone/awl
description: |-
Command-line DNS query tool.
Awl supports DNS-over-[UDP,TCP,HTTPS,QUIC] and DNSCrypt.
license: BSD-3-Clause
section: utils
bindir: /usr/bin
formats:
- apk
- archlinux
- deb
- rpm
contents:
- src: completions/bash.bash
dst: /usr/share/bash-completion/completions/awl
- src: docs/awl.1.gz
dst: /usr/share/man/man1/awl.1.gz
- src: LICENSE
dst: /usr/share/docs/awl/copyright
- src: completions/fish.fish
dst: /usr/share/fish/vendor_completions.d/awl.fish
# DEB only
- src: completions/zsh.zsh
dst: /usr/share/zsh/vendor-completions/_awl
packager: deb
# Alpine .apk only
- src: completions/zsh.zsh
dst: /usr/share/zsh/site-functions/_awl
packager: apk
# RPM only
- src: completions/zsh.zsh
dst: /usr/share/zsh/site-functions/_awl
packager: rpm
deb:
lintian_overrides:
- statically-linked-binary
- changelog-file-missing-in-native-package
overrides:
deb:
file_name_template: >-
{{- .PackageName }}_
{{- .Version }}_
{{- if eq .Arch "386" }}i386
{{- else if eq .Arch "arm" }}armel
{{- else }}{{ .Arch }}{{ end -}}
rpm:
file_name_template: >-
{{- .PackageName }}-
{{- .Version }}-
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i686
{{- else if eq .Arch "arm" }}armhfp
{{- else if eq .Arch "arm64" }}aarch64
{{- else }}{{ .Arch }}{{ end -}}
- id: termux
package_name: awl-dns
vendor: Sam Therapy <sam@samtherapy.net>
maintainer: Sam Therapy <sam@samtherapy.net>
homepage: https://dns.froth.zone/awl
description: |-
Command-line DNS query tool.
Awl supports DNS-over-[UDP,TCP,HTTPS,QUIC] and DNSCrypt.
license: BSD-3-Clause
section: utils
formats:
- termux.deb
file_name_template: >-
{{- .PackageName }}_
{{- .Version }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i686
{{- else if eq .Arch "arm" }}arm
{{- else if eq .Arch "arm64" }}aarch64
{{- else }}{{ .Arch }}{{ end -}}
snapcrafts:
-
name: awl-dns
grade: stable
publish: true
summary: A command-line DNS query tool
description: |-
Awl is a command-line DNS query tool.
Awl supports DNS-over-[UDP,TCP,HTTPS,QUIC] and DNSCrypt.
confinement: strict
license: BSD-3-Clause
base: bare
apps:
awl-dns:
command: awl
plugs:
- network
completer: completions/bash.bash
dockers:
-
image_templates:
- "{{ .Env.REGISTRY }}/{{ .Env.IMAGE_NAME }}:latest"
- "{{ .Env.REGISTRY }}/{{ .Env.IMAGE_NAME }}:{{ .Tag }}"
checksum:
name_template: "checksums.txt"
@ -40,29 +156,81 @@ checksum:
snapshot:
name_template: "{{ incpatch .Version }}-next"
brews:
- repository:
owner: packaging
name: homebrew
homepage: https://dns.froth.zone/awl
description: A DNS query tool
license: BSD-3-Clause
# custom_block: |
# head "https://git.froth.zone/sam/awl.git"
install: |-
bin.install "awl"
bash_completion.install "completions/bash.bash" => "awl"
zsh_completion.install "completions/zsh.zsh" => "_awl"
fish_completion.install "completions/fish.fish" => "awl.fish"
man1.install "docs/awl.1.gz"
nix:
- repository:
owner: packaging
name: nur
homepage: https://dns.froth.zone/awl
description: A DNS query client
license: bsd3
extra_install: |-
installManPage ./docs/awl.1.gz
installShellCompletion ./completions/*
scoops:
- repository:
owner: packaging
name: scoop
folder: bucket
homepage: https://dns.froth.zone/awl
description: A DNS query client
license: BSD-3-Clause
changelog:
sort: asc
groups:
- title: 'Dependency Updates'
- title: "Dependency Updates"
regexp: "^.*fix\\(deps\\)*:+.*$"
order: 2
- title: 'Features'
- title: "Features"
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: 'Bug fixes'
- title: "Bug fixes"
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: 'Other'
- title: "Other"
order: 999
filters:
exclude:
- "SKIP CI"
- "^test:"
- "^docs?:"
- "typo"
- "^ci:"
uploads:
- name: packages
method: PUT
mode: archive
exts:
- deb
- rpm
- apk
- termux.deb
username: sam
target: >-
https://git.froth.zone/api/packages/sam/
{{- if eq .ArtifactExt "deb" }}debian/pool/sid/main/upload
{{- else if eq .ArtifactExt "termux.deb" }}debian/pool/termux/main/upload
{{- else if eq .ArtifactExt "rpm" }}rpm/upload
{{- else if eq .ArtifactExt "apk" }}alpine/edge/main{{ end -}}
custom_artifact_name: true # Truncate the artifact name from the upload URL
gitea_urls:
api: https://git.froth.zone/api/v1/
api: https://git.froth.zone/api/v1
download: https://git.froth.zone

3
Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM scratch
ENTRYPOINT ["/awl"]
COPY awl /

View File

@ -7,6 +7,11 @@ ifeq ($(OS),Windows_NT)
EXE := $(PROG).exe
else
EXE := $(PROG)
ifeq ($(shell uname), Darwin)
INSTALLFLAGS :=
else
INSTALLFLAGS := D
endif
endif
## install: installs awl
@ -16,8 +21,10 @@ install:
$(GO) install $(GOFLAGS) .
else
install: all
install -Dm755 $(PROG) $(DESTDIR)$(PREFIX)/$(BIN)/$(PROG)
install -Dm644 doc/$(PROG).1 $(DESTDIR)$(MAN)/man1/$(PROG).1
install -$(INSTALLFLAGS)m755 $(PROG) $(DESTDIR)$(PREFIX)/$(BIN)/$(PROG)
install -$(INSTALLFLAGS)m644 docs/$(PROG).1 $(DESTDIR)$(MAN)/man1/$(PROG).1
# completions need to go in one specific place :)
install -Dm644 completions/zsh.zsh $(DESTDIR)/$(PREFIX)/$(SHARE)/zsh/site-functions/_$(PROG)
install -$(INSTALLFLAGS)m644 completions/bash.bash $(DESTDIR)$(PREFIX)/$(SHARE)/bash-completion/completions/$(PROG)
install -$(INSTALLFLAGS)m644 completions/fish.fish $(DESTDIR)$(PREFIX)/$(SHARE)/fish/vendor_completions.d/$(PROG).fish
install -$(INSTALLFLAGS)m644 completions/zsh.zsh $(DESTDIR)$(PREFIX)/$(SHARE)/zsh/site-functions/_$(PROG)
endif

View File

@ -1,11 +1,11 @@
Copyright 2022 Sam Therapy, Gregward Bulon
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
Copyright 2022 Sam Therapy, Gregward Bulon
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -9,6 +9,8 @@ EXE := $(PROG)
.PHONY: install
install: all
install -Dm755 $(PROG) $(DESTDIR)$(PREFIX)/$(BIN)/$(PROG)
install -Dm644 doc/$(PROG).1 $(DESTDIR)$(MAN)/man1/$(PROG).1
install -Dm644 docs/$(PROG).1 $(DESTDIR)$(MAN)/man1/$(PROG).1
# completions need to go in one specific place :)
install -Dm644 completions/zsh.zsh $(DESTDIR)/$(PREFIX)/$(SHARE)/zsh/site-functions/_$(PROG)
install -Dm644 completions/bash.bash $(DESTDIR)$(PREFIX)$(SHARE)/bash-completion/completions/$(PROG)
install -Dm644 completions/fish.fish $(DESTDIR)$(PREFIX)$(SHARE)/fish/vendor_completions.d/$(PROG).fish
install -Dm644 completions/zsh.zsh $(DESTDIR)$(PREFIX)$(SHARE)/zsh/site-functions/_$(PROG)

220
README.md
View File

@ -1,63 +1,195 @@
# awl
<!-- markdownlint-disable MD033 -->
# <img src="./docs/img/awl-text.png" width="50%" title="awl logo" alt="awl">
[![Build Status](https://ci.git.froth.zone/api/badges/sam/awl/status.svg)](https://ci.git.froth.zone/sam/awl)
> awl *(noun)*: A pointed tool for making small holes in wood or leather
`awl` is a command-line DNS client, much like
[`drill`](https://github.com/NLnetLabs/ldns),
[`dig`](https://bind9.readthedocs.io/en/v9_18_3/manpages.html#dig-dns-lookup-utility),
[`dog`](https://github.com/ogham/dog),
[`doggo`](https://github.com/mr-karan/doggo), or
[`q`](https://github.com/natesales/q).
A command-line DNS lookup tool that supports DNS queries over UDP, TCP, TLS, HTTPS, DNSCrypt, and QUIC.
`awl` is designed to be a drop-in replacement for the venerable dig, but support
newer RFC query types, such as DNS-over-HTTPS and DNS-over-QUIC.
[![Gitea Release](https://img.shields.io/gitea/v/release/sam/awl?gitea_url=https%3A%2F%2Fgit.froth.zone&display_name=release&style=for-the-badge)](https://git.froth.zone/sam/awl)
[![Last Commit](https://img.shields.io/gitea/last-commit/sam/awl?gitea_url=https%3A%2F%2Fgit.froth.zone&style=for-the-badge)](https://git.froth.zone/sam/awl/commits/branch/master)
[![License](https://img.shields.io/github/license/samtherapy/awl?style=for-the-badge)](https://spdx.org/licenses/BSD-3-Clause.html)
[![Go Report](https://goreportcard.com/badge/dns.froth.zone/awl?style=for-the-badge)](https://goreportcard.com/report/dns.froth.zone/awl)
## Usage
- [Feature wiki](https://git.froth.zone/sam/awl/wiki/Supported)
- [Manpage](https://git.froth.zone/sam/awl/wiki/awl.1)
Awl is designed to be a drop-in replacement for [dig](https://bind9.readthedocs.io/en/v9_18_3/manpages.html#dig-dns-lookup-utility).
## Building and installing
## Examples
### From releases
```shell
# Query a domain over UDP
awl example.com
Grab a prebuilt binary from the
[release](https://git.froth.zone/sam/awl/releases) section.
# Query a domain over HTTPS, print only the results
awl example.com +https --short
### Package Managers
- AUR: [awl-dns-git](https://aur.archlinux.org/packages/awl-dns-git)
### From source
Dependencies:
- Go >= 1.18
- GNU/BSD make or Plan 9 mk (if using the makefile/mkfile)
- scdoc (optional, for man page)
Using `go install` (recommended):
```sh
go install git.froth.zone/sam/awl@latest
# Query a domain over TLS, print as JSON
awl example.com +tls +json
```
Using the makefile:
For more and the usage, see the [manpage](https://git.froth.zone/sam/awl/wiki/awl.1).
```sh
make
make && sudo make install
## Installing
On any platform, with [Go](https://go.dev) installed, run the following command to install:
```shell
go install dns.froth.zone/awl@latest
```
### Packaging
Alternatively, many package managers are supported:
<details>
<summary>Linux</summary>
#### Distro-specific
<details>
<summary>Alpine Linux</summary>
Provided by [Gitea packages](https://git.froth.zone/sam/-/packages/alpine/awl-dns) \
***Any distro that uses apk should also work***
```shell
# Add the repository
echo "https://git.froth.zone/api/packages/sam/alpine/edge/main" | tee -a /etc/apk/repositories
# Get the signing key
curl -JO https://git.froth.zone/api/packages/sam/alpine/key --output-dir /etc/apk/keys
# Install
apk add awl-dns
```
</details>
<details>
<summary>Arch</summary>
AUR package available as [awl-dns-git](https://aur.archlinux.org/packages/awl-dns-git/)
```shell
yay -S awl-dns-git ||
paru -S awl-dns-git
```
</details>
<details>
<summary>Debian / Ubuntu</summary>
Provided by [Gitea packages](https://git.froth.zone/sam/-/packages/debian/awl-dns/) \
***Any distro that uses deb/dpkg should also work***
```shell
# Add PGP key
sudo curl https://git.froth.zone/api/packages/sam/debian/repository.key -o /usr/share/keyrings/git-froth-zone-sam.asc
# Add repo
echo "deb [signed-by=/usr/share/keyrings/git-froth-zone-sam.asc] https://git.froth.zone/api/packages/sam/debian sid main" | sudo tee /etc/apt/sources.list.d/git-froth-zone-sam.list
# Update and install
sudo apt update
sudo apt install awl-dns
```
</details>
<details>
<summary>Fedora / RHEL / SUSE</summary>
Provided by [Gitea packages](https://git.froth.zone/sam/-/packages/rpm/awl-dns/) \
***Any distro that uses rpm/dnf might also work, I've never tried it***
```shell
# Add the repository
dnf config-manager --add-repo https://git.froth.zone/api/packages/sam/rpm.repo ||
zypper addrepo https://git.froth.zone/api/packages/sam/rpm.repo
# Install
dnf install awl-dns ||
zypper install awl-dns
```
</details>
<details>
<summary>Gentoo</summary>
```shell
# Add the ebuild repository
eselect repository add froth-zone git https://git.froth.zone/packaging/portage.git
emaint sync -r froth-zone
# Install
emerge -av net-dns/awl
```
</details>
#### Distro-agnostic
<details>
<summary><a href="https://brew.sh" nofollow>Homebrew</a></summary>
```shell
brew install SamTherapy/tap/awl
```
</details>
<details>
<summary>Snap</summary>
Snap package available as [awl-dns](https://snapcraft.io/awl-dns)
```shell
snap install awl-dns ||
sudo snap install awl-dns
```
</details>
</details>
<hr />
<details>
<summary>macOS</summary>
<details open>
<summary><a href="https://brew.sh" nofollow>Homebrew</a></summary>
```shell
brew install SamTherapy/tap/awl
```
</details>
</details>
<hr />
<details>
<summary>Windows</summary>
<details open>
<summary><a href="https://scoop.sh" nofollow>Scoop</a></summary>
```pwsh
scoop bucket add froth https://git.froth.zone/packages/scoop.git
scoop install awl
```
</details>
</details>
## Contributing
Send a [pull request](https://git.froth.zone/sam/awl/pulls) our way. Prefer
emails? Send a patch to the
[mailing list](https://lists.sr.ht/~sammefishe/awl-dev).
Please see the [CONTRIBUTING.md](./docs/CONTRIBUTING.md) file for more information.
Found a bug or want a new feature? Create an issue
[here](https://git.froth.zone/sam/awl/issues).
TL;DR: If you like the project, spread the word! If you want to contribute, [use the issue tracker](https://git.froth.zone/sam/awl/issues) or [open a pull request](https://git.froth.zone/sam/awl/pulls).
Want to use email instead? Use our [mailing list](https://lists.sr.ht/~sammefishe/awl-devel)!
### Licence
### Mirrors
Revised BSD, See [LICENCE](./LICENCE)
The canonical repository is located on [my personal Forgejo instance](https://git.froth.zone/sam/awl). \
Official mirrors are located on [GitHub](https://github.com/SamTherapy/awl) and [SourceHut](https://git.sr.ht/~sammefishe/awl/).
Contributions are accepted on all mirrors, but the Forgejo instance is preferred.
## License
[BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html)
### Credits
- Awl image taken from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Awl.tif), imaged is licensed CC0.

View File

@ -1,239 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package cli
import (
"errors"
"fmt"
"runtime"
"strconv"
"strings"
"time"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
flag "github.com/stefansundin/go-zflag"
)
// ParseCLI parses arguments given from the CLI and passes them into an `Options`
// struct.
func ParseCLI(args []string, version string) (util.Options, error) {
flag.CommandLine = flag.NewFlagSet(args[0], flag.ContinueOnError)
flag.Usage = func() {
fmt.Println(`awl - drill, writ small
Usage: awl name [@server] [record]
<name> domain, IP address, phone number
<record> defaults to A
Arguments may be in any order, including flags.
Dig-like +[no]commands are also supported, see dig(1) or dig -h
Options:`)
flag.PrintDefaults()
}
// CLI flags
//
// Remember, when adding a flag edit the manpage and the completions :)
var (
port = flag.Int("port", 0, "`port` to make DNS query (default: 53 for UDP/TCP, 853 for TLS/QUIC)", flag.OptShorthand('p'), flag.OptDisablePrintDefault(true))
query = flag.String("query", "", "domain name to `query` (default: .)", flag.OptShorthand('q'))
class = flag.String("class", "IN", "DNS `class` to query", flag.OptShorthand('c'))
qType = flag.String("qType", "", "`type` to query (default: A)", flag.OptShorthand('t'))
ipv4 = flag.Bool("4", false, "force IPv4", flag.OptShorthand('4'))
ipv6 = flag.Bool("6", false, "force IPv6", flag.OptShorthand('6'))
reverse = flag.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x'))
timeout = flag.Float32("timeout", 1, "Timeout, in `seconds`")
retry = flag.Int("retries", 2, "number of `times` to retry")
edns = flag.Bool("no-edns", false, "disable EDNS entirely")
ednsVer = flag.Uint8("edns-ver", 0, "set EDNS version")
dnssec = flag.Bool("dnssec", false, "enable DNSSEC", flag.OptShorthand('D'))
expire = flag.Bool("expire", false, "set EDNS expire")
nsid = flag.Bool("nsid", false, "set EDNS NSID", flag.OptShorthand('n'))
cookie = flag.Bool("no-cookie", false, "disable sending EDNS cookie (default: cookie sent)")
tcpKeepAlive = flag.Bool("keep-alive", false, "send EDNS TCP keep-alive")
udpBufSize = flag.Uint16("buffer-size", 1232, "set EDNS UDP buffer size", flag.OptShorthand('b'))
mbzflag = flag.String("zflag", "0", "set EDNS z-flag `value`")
subnet = flag.String("subnet", "", "set EDNS client subnet")
padding = flag.Bool("pad", false, "set EDNS padding")
truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)")
tcp = flag.Bool("tcp", false, "use TCP")
dnscrypt = flag.Bool("dnscrypt", false, "use DNSCrypt")
tls = flag.Bool("tls", false, "use DNS-over-TLS", flag.OptShorthand('T'))
https = flag.Bool("https", false, "use DNS-over-HTTPS", flag.OptShorthand('H'))
quic = flag.Bool("quic", false, "use DNS-over-QUIC", flag.OptShorthand('Q'))
tlsHost = flag.String("tls-host", "", "Server name to use for TLS verification")
noVerify = flag.Bool("tls-no-verify", false, "Disable TLS cert verification")
aaflag = flag.Bool("aa", false, "set/unset AA (Authoratative Answer) flag (default: not set)")
adflag = flag.Bool("ad", false, "set/unset AD (Authenticated Data) flag (default: not set)")
cdflag = flag.Bool("cd", false, "set/unset CD (Checking Disabled) flag (default: not set)")
qrflag = flag.Bool("qr", false, "set/unset QR (QueRy) flag (default: not set)")
rdflag = flag.Bool("rd", true, "set/unset RD (Recursion Desired) flag (default: set)", flag.OptDisablePrintDefault(true))
raflag = flag.Bool("ra", false, "set/unset RA (Recursion Available) flag (default: not set)")
tcflag = flag.Bool("tc", false, "set/unset TC (TrunCated) flag (default: not set)")
zflag = flag.Bool("z", false, "set/unset Z (Zero) flag (default: not set)", flag.OptShorthand('z'))
short = flag.Bool("short", false, "print just the results", flag.OptShorthand('s'))
json = flag.Bool("json", false, "print the result(s) as JSON", flag.OptShorthand('j'))
xml = flag.Bool("xml", false, "print the result(s) as XML", flag.OptShorthand('X'))
yaml = flag.Bool("yaml", false, "print the result(s) as yaml", flag.OptShorthand('y'))
noC = flag.Bool("no-comments", false, "disable printing the comments")
noQ = flag.Bool("no-question", false, "disable printing the question section")
noOpt = flag.Bool("no-opt", false, "disable printing the OPT pseudosection")
noAns = flag.Bool("no-answer", false, "disable printing the answer section")
noAuth = flag.Bool("no-authority", false, "disable printing the authority section")
noAdd = flag.Bool("no-additional", false, "disable printing the additional section")
noStats = flag.Bool("no-statistics", false, "disable printing the statistics section")
verbosity = flag.Int("verbosity", 1, "sets verbosity `level`", flag.OptShorthand('v'), flag.OptNoOptDefVal("2"))
versionFlag = flag.Bool("version", false, "print version information", flag.OptShorthand('V'))
)
// Don't sort the flags when -h is given
flag.CommandLine.SortFlags = false
// Parse the flags
if err := flag.CommandLine.Parse(args[1:]); err != nil {
return util.Options{Logger: util.InitLogger(*verbosity)}, fmt.Errorf("flag: %w", err)
}
// TODO: DRY, dumb dumb.
mbz, err := strconv.ParseInt(*mbzflag, 0, 16)
if err != nil {
return util.Options{Logger: util.InitLogger(*verbosity)}, fmt.Errorf("EDNS MBZ: %w", err)
}
opts := util.Options{
Logger: util.InitLogger(*verbosity),
IPv4: *ipv4,
IPv6: *ipv6,
Short: *short,
TCP: *tcp,
DNSCrypt: *dnscrypt,
TLS: *tls,
TLSHost: *tlsHost,
TLSNoVerify: *noVerify,
HTTPS: *https,
QUIC: *quic,
Truncate: *truncate,
Reverse: *reverse,
JSON: *json,
XML: *xml,
YAML: *yaml,
HeaderFlags: util.HeaderFlags{
AA: *aaflag,
AD: *adflag,
TC: *tcflag,
Z: *zflag,
CD: *cdflag,
QR: *qrflag,
RD: *rdflag,
RA: *raflag,
},
Request: util.Request{
Type: dns.StringToType[strings.ToUpper(*qType)],
Class: dns.StringToClass[strings.ToUpper(*class)],
Name: *query,
Timeout: time.Duration(*timeout * float32(time.Second)),
Retries: *retry,
Port: *port,
},
Display: util.Display{
Comments: !*noC,
Question: !*noQ,
Opt: !*noOpt,
Answer: !*noAns,
Authority: !*noAuth,
Additional: !*noAdd,
Statistics: !*noStats,
HumanTTL: false,
ShowQuery: false,
TTL: true,
},
EDNS: util.EDNS{
EnableEDNS: !*edns,
Cookie: !*cookie,
DNSSEC: *dnssec,
BufSize: *udpBufSize,
Version: *ednsVer,
Expire: *expire,
KeepOpen: *tcpKeepAlive,
Nsid: *nsid,
ZFlag: uint16(mbz & 0x7FFF),
Padding: *padding,
},
}
// TODO: DRY
if *subnet != "" {
if err = util.ParseSubnet(*subnet, &opts); err != nil {
return opts, fmt.Errorf("%w", err)
}
}
opts.Logger.Info("POSIX flags parsed")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
if *versionFlag {
fmt.Printf("awl version %s, built with %s\n", version, runtime.Version())
return opts, ErrNotError
}
// Parse all the arguments that don't start with - or --
// This includes the dig-style (+) options
err = ParseMiscArgs(flag.Args(), &opts)
if err != nil {
return opts, err
}
opts.Logger.Info("Dig/Drill flags parsed")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
if opts.Request.Port == 0 {
if opts.TLS || opts.QUIC {
opts.Request.Port = 853
} else {
opts.Request.Port = 53
}
}
opts.Logger.Info("Port set to", opts.Request.Port)
// Set timeout to 0.5 seconds if set below 0.5
if opts.Request.Timeout < (time.Second / 2) {
opts.Request.Timeout = (time.Second / 2)
}
if opts.Request.Retries < 0 {
opts.Request.Retries = 0
}
opts.Logger.Info("Options fully populated")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
return opts, nil
}
// ErrNotError is for returning not error.
var ErrNotError = errors.New("not an error")
var errNoArg = errors.New("no argument given")
type errInvalidArg struct {
arg string
}
func (e *errInvalidArg) Error() string {
return fmt.Sprintf("digflags: invalid argument %s", e.arg)
}

269
cmd/cli.go Normal file
View File

@ -0,0 +1,269 @@
// SPDX-License-Identifier: BSD-3-Clause
package cli
import (
"errors"
"fmt"
"runtime"
"strconv"
"strings"
"time"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
flag "github.com/stefansundin/go-zflag"
)
// ParseCLI parses arguments given from the CLI and passes them into an `Options`
// struct.
func ParseCLI(args []string, version string) (opts *util.Options, err error) {
// Parse the standard flags
opts, misc, err := parseFlags(args, version)
if err != nil {
return opts, err
}
// Parse all the arguments that don't start with - or --
// This includes the dig-style (+) options
err = ParseMiscArgs(misc, opts)
if err != nil {
return opts, err
}
opts.Logger.Info("Dig/Drill flags parsed")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
// Special options and exceptions time
if opts.Request.Port == 0 {
if opts.TLS || opts.QUIC {
opts.Request.Port = 853
} else {
opts.Request.Port = 53
}
}
opts.Logger.Info("Port set to", opts.Request.Port)
// Set timeout to 0.5 seconds if set below 0.5
if opts.Request.Timeout < (time.Second / 2) {
opts.Request.Timeout = (time.Second / 2)
}
if opts.Request.Retries < 0 {
opts.Request.Retries = 0
}
if opts.Trace {
if opts.TLS || opts.HTTPS || opts.QUIC {
opts.Logger.Warn("Every query after the root query will only use UDP/TCP")
}
opts.RD = true
}
opts.Logger.Info("Options fully populated")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
return
}
// Everything that has to do with CLI flags goes here (the posix style, eg. -a and --bbbb).
func parseFlags(args []string, version string) (opts *util.Options, flags []string, err error) {
flagSet := flag.NewFlagSet(args[0], flag.ContinueOnError)
flagSet.Usage = func() {
fmt.Println(`awl - drill, writ small
Usage: awl name [@server] [record]
<name> domain, IP address, phone number
<record> defaults to A
Arguments may be in any order, including flags.
Dig-like +[no]commands are also supported, see dig(1) or dig -h
Options:`)
flagSet.PrintDefaults()
}
// CLI flags
//
// Remember, when adding a flag edit the manpage and the completions :)
var (
port = flagSet.Int("port", 0, "`port` to make DNS query (default: 53 for UDP/TCP, 853 for TLS/QUIC)", flag.OptShorthand('p'), flag.OptDisablePrintDefault(true))
query = flagSet.String("query", "", "domain name to `query` (default: .)", flag.OptShorthand('q'))
class = flagSet.String("class", "IN", "DNS `class` to query", flag.OptShorthand('c'))
qType = flagSet.String("qType", "", "`type` to query (default: A)", flag.OptShorthand('t'))
ipv4 = flagSet.Bool("4", false, "force IPv4", flag.OptShorthand('4'))
ipv6 = flagSet.Bool("6", false, "force IPv6", flag.OptShorthand('6'))
reverse = flagSet.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x'))
trace = flagSet.Bool("trace", false, "trace from the root")
timeout = flagSet.Float32("timeout", 5, "Timeout, in `seconds`")
retry = flagSet.Int("retries", 2, "number of `times` to retry")
edns = flagSet.Bool("no-edns", false, "disable EDNS entirely")
ednsVer = flagSet.Uint8("edns-ver", 0, "set EDNS version")
dnssec = flagSet.Bool("dnssec", false, "enable DNSSEC", flag.OptShorthand('D'))
expire = flagSet.Bool("expire", false, "set EDNS expire")
nsid = flagSet.Bool("nsid", false, "set EDNS NSID", flag.OptShorthand('n'))
cookie = flagSet.Bool("no-cookie", false, "disable sending EDNS cookie (default: cookie sent)")
tcpKeepAlive = flagSet.Bool("keep-alive", false, "send EDNS TCP keep-alive")
udpBufSize = flagSet.Uint16("buffer-size", 1232, "set EDNS UDP buffer size", flag.OptShorthand('b'))
mbzflag = flagSet.String("zflag", "0", "set EDNS z-flag `value`")
subnet = flagSet.String("subnet", "", "set EDNS client subnet")
padding = flagSet.Bool("pad", false, "set EDNS padding")
badCookie = flagSet.Bool("no-bad-cookie", false, "ignore BADCOOKIE EDNS responses (default: retry with correct cookie")
truncate = flagSet.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)")
tcp = flagSet.Bool("tcp", false, "use TCP")
dnscrypt = flagSet.Bool("dnscrypt", false, "use DNSCrypt")
tls = flagSet.Bool("tls", false, "use DNS-over-TLS", flag.OptShorthand('T'))
https = flagSet.Bool("https", false, "use DNS-over-HTTPS", flag.OptShorthand('H'))
quic = flagSet.Bool("quic", false, "use DNS-over-QUIC", flag.OptShorthand('Q'))
tlsHost = flagSet.String("tls-host", "", "Server name to use for TLS verification")
noVerify = flagSet.Bool("tls-no-verify", false, "Disable TLS cert verification")
aaflag = flagSet.Bool("aa", false, "set/unset AA (Authoratative Answer) flag (default: not set)")
adflag = flagSet.Bool("ad", false, "set/unset AD (Authenticated Data) flag (default: not set)")
cdflag = flagSet.Bool("cd", false, "set/unset CD (Checking Disabled) flag (default: not set)")
qrflag = flagSet.Bool("qr", false, "set/unset QR (QueRy) flag (default: not set)")
rdflag = flagSet.Bool("rd", true, "set/unset RD (Recursion Desired) flag (default: set)", flag.OptDisablePrintDefault(true))
raflag = flagSet.Bool("ra", false, "set/unset RA (Recursion Available) flag (default: not set)")
tcflag = flagSet.Bool("tc", false, "set/unset TC (TrunCated) flag (default: not set)")
zflag = flagSet.Bool("z", false, "set/unset Z (Zero) flag (default: not set)", flag.OptShorthand('z'))
short = flagSet.Bool("short", false, "print just the results", flag.OptShorthand('s'))
json = flagSet.Bool("json", false, "print the result(s) as JSON", flag.OptShorthand('j'))
xml = flagSet.Bool("xml", false, "print the result(s) as XML", flag.OptShorthand('X'))
yaml = flagSet.Bool("yaml", false, "print the result(s) as yaml", flag.OptShorthand('y'))
noC = flagSet.Bool("no-comments", false, "disable printing the comments")
noQ = flagSet.Bool("no-question", false, "disable printing the question section")
noOpt = flagSet.Bool("no-opt", false, "disable printing the OPT pseudosection")
noAns = flagSet.Bool("no-answer", false, "disable printing the answer section")
noAuth = flagSet.Bool("no-authority", false, "disable printing the authority section")
noAdd = flagSet.Bool("no-additional", false, "disable printing the additional section")
noStats = flagSet.Bool("no-statistics", false, "disable printing the statistics section")
verbosity = flagSet.Int("verbosity", 1, "sets verbosity `level`", flag.OptShorthand('v'), flag.OptNoOptDefVal("2"))
versionFlag = flagSet.Bool("version", false, "print version information", flag.OptShorthand('V'))
)
// Don't sort the flags when -h is given
flagSet.SortFlags = true
// Parse the flags
if err = flagSet.Parse(args[1:]); err != nil {
return &util.Options{Logger: util.InitLogger(*verbosity)}, nil, fmt.Errorf("flag: %w", err)
}
// TODO: DRY, dumb dumb.
mbz, err := strconv.ParseInt(*mbzflag, 0, 16)
if err != nil {
return &util.Options{Logger: util.InitLogger(*verbosity)}, nil, fmt.Errorf("EDNS MBZ: %w", err)
}
opts = &util.Options{
Logger: util.InitLogger(*verbosity),
IPv4: *ipv4,
IPv6: *ipv6,
Trace: *trace,
Short: *short,
TCP: *tcp,
DNSCrypt: *dnscrypt,
TLS: *tls,
TLSHost: *tlsHost,
TLSNoVerify: *noVerify,
HTTPS: *https,
QUIC: *quic,
Truncate: *truncate,
BadCookie: *badCookie,
Reverse: *reverse,
JSON: *json,
XML: *xml,
YAML: *yaml,
HeaderFlags: util.HeaderFlags{
AA: *aaflag,
AD: *adflag,
TC: *tcflag,
Z: *zflag,
CD: *cdflag,
QR: *qrflag,
RD: *rdflag,
RA: *raflag,
},
Request: util.Request{
Type: dns.StringToType[strings.ToUpper(*qType)],
Class: dns.StringToClass[strings.ToUpper(*class)],
Name: *query,
Timeout: time.Duration(*timeout * float32(time.Second)),
Retries: *retry,
Port: *port,
},
Display: util.Display{
Comments: !*noC,
Question: !*noQ,
Opt: !*noOpt,
Answer: !*noAns,
Authority: !*noAuth,
Additional: !*noAdd,
Statistics: !*noStats,
TTL: true,
ShowClass: true,
ShowQuery: false,
HumanTTL: false,
UcodeTranslate: true,
},
EDNS: util.EDNS{
EnableEDNS: !*edns,
Cookie: !*cookie,
DNSSEC: *dnssec,
BufSize: *udpBufSize,
Version: *ednsVer,
Expire: *expire,
KeepOpen: *tcpKeepAlive,
Nsid: *nsid,
ZFlag: uint16(mbz & 0x7FFF),
Padding: *padding,
},
HTTPSOptions: util.HTTPSOptions{
Endpoint: "/dns-query",
Get: false,
},
}
// TODO: DRY
if *subnet != "" {
if err = util.ParseSubnet(*subnet, opts); err != nil {
return opts, nil, fmt.Errorf("%w", err)
}
}
opts.Logger.Info("POSIX flags parsed")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
if *versionFlag {
fmt.Printf("awl version %s, built with %s\n", version, runtime.Version())
return opts, nil, util.ErrNotError
}
flags = flagSet.Args()
return
}
var errNoArg = errors.New("no argument given")
type errInvalidArg struct {
arg string
}
func (e *errInvalidArg) Error() string {
return fmt.Sprintf("digflags: invalid argument %s", e.arg)
}

View File

@ -6,56 +6,70 @@ import (
"testing"
"time"
"git.froth.zone/sam/awl/cli"
cli "dns.froth.zone/awl/cmd"
"dns.froth.zone/awl/pkg/util"
"gotest.tools/v3/assert"
)
func TestEmpty(t *testing.T) {
t.Parallel()
args := []string{"awl", "-4"}
opts, err := cli.ParseCLI(args, "TEST")
assert.NilError(t, err)
assert.Equal(t, opts.Request.Port, 53)
assert.Assert(t, opts.IPv4)
assert.Equal(t, opts.Request.Port, 53)
}
func TestTLSPort(t *testing.T) {
t.Parallel()
args := []string{"awl", "-T"}
opts, err := cli.ParseCLI(args, "TEST")
assert.NilError(t, err)
assert.Equal(t, opts.Request.Port, 853)
}
func TestSubnet(t *testing.T) {
args := []string{"awl", "--subnet", "127.0.0.1/32"}
func TestValidSubnet(t *testing.T) {
t.Parallel()
opts, err := cli.ParseCLI(args, "TEST")
tests := []struct {
args []string
want uint16
}{
{[]string{"awl", "--subnet", "127.0.0.1/32"}, uint16(1)},
{[]string{"awl", "--subnet", "0"}, uint16(1)},
{[]string{"awl", "--subnet", "::/0"}, uint16(2)},
}
assert.NilError(t, err)
assert.Equal(t, opts.EDNS.Subnet.Family, uint16(1))
for _, test := range tests {
test := test
args = []string{"awl", "--subnet", "0"}
t.Run(test.args[2], func(t *testing.T) {
t.Parallel()
opts, err = cli.ParseCLI(args, "TEST")
assert.NilError(t, err)
assert.Equal(t, opts.EDNS.Subnet.Family, uint16(1))
opts, err := cli.ParseCLI(test.args, "TEST")
args = []string{"awl", "--subnet", "::/0"}
assert.NilError(t, err)
assert.Equal(t, opts.EDNS.Subnet.Family, test.want)
})
}
}
opts, err = cli.ParseCLI(args, "TEST")
assert.NilError(t, err)
assert.Equal(t, opts.EDNS.Subnet.Family, uint16(2))
func TestInvalidSubnet(t *testing.T) {
t.Parallel()
args = []string{"awl", "--subnet", "/"}
args := []string{"awl", "--subnet", "/"}
opts, err = cli.ParseCLI(args, "TEST")
_, err := cli.ParseCLI(args, "TEST")
assert.ErrorContains(t, err, "EDNS subnet")
}
func TestMBZ(t *testing.T) {
t.Parallel()
args := []string{"awl", "--zflag", "G"}
_, err := cli.ParseCLI(args, "TEST")
@ -64,6 +78,8 @@ func TestMBZ(t *testing.T) {
}
func TestInvalidFlag(t *testing.T) {
t.Parallel()
args := []string{"awl", "--treebug"}
_, err := cli.ParseCLI(args, "TEST")
@ -72,6 +88,8 @@ func TestInvalidFlag(t *testing.T) {
}
func TestInvalidDig(t *testing.T) {
t.Parallel()
args := []string{"awl", "+a"}
_, err := cli.ParseCLI(args, "TEST")
@ -80,14 +98,18 @@ func TestInvalidDig(t *testing.T) {
}
func TestVersion(t *testing.T) {
t.Parallel()
args := []string{"awl", "--version"}
_, err := cli.ParseCLI(args, "test")
assert.ErrorType(t, err, cli.ErrNotError)
assert.ErrorIs(t, err, util.ErrNotError)
}
func TestTimeout(t *testing.T) {
t.Parallel()
args := [][]string{
{"awl", "+timeout=0"},
{"awl", "--timeout", "0"},
@ -95,14 +117,20 @@ func TestTimeout(t *testing.T) {
for _, test := range args {
test := test
opt, err := cli.ParseCLI(test, "TEST")
t.Run(test[1], func(t *testing.T) {
t.Parallel()
assert.NilError(t, err)
assert.Equal(t, opt.Request.Timeout, time.Second/2)
opt, err := cli.ParseCLI(test, "TEST")
assert.NilError(t, err)
assert.Equal(t, opt.Request.Timeout, time.Second/2)
})
}
}
func TestRetries(t *testing.T) {
t.Parallel()
args := [][]string{
{"awl", "+retry=-2"},
{"awl", "+tries=-2"},
@ -111,10 +139,36 @@ func TestRetries(t *testing.T) {
for _, test := range args {
test := test
opt, err := cli.ParseCLI(test, "TEST")
t.Run(test[1], func(t *testing.T) {
t.Parallel()
assert.NilError(t, err)
assert.Equal(t, opt.Request.Retries, 0)
opt, err := cli.ParseCLI(test, "TEST")
assert.NilError(t, err)
assert.Equal(t, opt.Request.Retries, 0)
})
}
}
func TestSetHTTPS(t *testing.T) {
t.Parallel()
args := [][]string{
{"awl", "-H", "@dns.froth.zone/dns-query"},
{"awl", "+https", "@dns.froth.zone"},
}
for _, test := range args {
test := test
t.Run(test[1], func(t *testing.T) {
t.Parallel()
opt, err := cli.ParseCLI(test, "TEST")
assert.NilError(t, err)
assert.Equal(t, opt.Request.Server, "dns.froth.zone")
assert.Equal(t, opt.HTTPSOptions.Endpoint, "/dns-query")
})
}
}

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
"git.froth.zone/sam/awl/util"
"dns.froth.zone/awl/pkg/util"
)
// ParseDig parses commands from the popular DNS tool dig.
@ -25,6 +25,18 @@ func ParseDig(arg string, opts *util.Options) error {
opts.Logger.Info("Setting", arg)
switch arg {
case "trace", "notrace":
opts.Trace = isNo
if isNo {
opts.DNSSEC = true
opts.Display.Comments = false
opts.Display.Question = false
opts.Display.Opt = false
opts.Display.Answer = true
opts.Display.Authority = true
opts.Display.Additional = false
opts.Display.Statistics = false
}
// Set DNS query flags
case "aa", "aaflag", "aaonly":
opts.AA = isNo
@ -54,7 +66,7 @@ func ParseDig(arg string, opts *util.Options) error {
opts.Display.ShowClass = isNo
// EDNS queries
case "dnssec":
case "do", "dnssec":
opts.EDNS.DNSSEC = isNo
case "expire":
opts.EDNS.Expire = isNo
@ -73,12 +85,12 @@ func ParseDig(arg string, opts *util.Options) error {
opts.TCP = isNo
case "ignore":
opts.Truncate = isNo
case "badcookie":
opts.BadCookie = !isNo
case "tls":
opts.TLS = isNo
case "dnscrypt":
opts.DNSCrypt = isNo
case "https":
opts.HTTPS = isNo
case "quic":
opts.QUIC = isNo
// End DNS-over-X
@ -204,6 +216,18 @@ func parseDigEq(startNo bool, arg string, opts *util.Options) error {
opts.EDNS.Version = 0
}
case "https", "https-get", "https-post":
opts.HTTPS = startNo
if isSplit && val != "" {
opts.HTTPSOptions.Endpoint = val
} else {
opts.HTTPSOptions.Endpoint = "/dns-query"
}
if strings.HasSuffix(arg, "get") {
opts.HTTPSOptions.Get = true
}
case "subnet":
if isSplit && val != "" {
err := util.ParseSubnet(val, opts)

View File

@ -5,8 +5,8 @@ package cli_test
import (
"testing"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/util"
cli "dns.froth.zone/awl/cmd"
"dns.froth.zone/awl/pkg/util"
"gotest.tools/v3/assert"
)
@ -25,12 +25,12 @@ func FuzzDig(f *testing.F) {
"qr", "noqr",
"ttlunits", "nottlunits",
"ttlid", "nottlid",
"dnssec", "nodnssec",
"do", "dnssec", "nodo", "nodnssec",
"edns", "edns=a", "edns=0", "noedns",
"expire", "noexpire",
"ednsflags", "ednsflags=\"", "ednsflags=1", "noednsflags",
"subnet=0.0.0.0/0", "subnet=::0/0", "subnet=b", "subnet=0", "subnet",
"cookie", "nocookeie",
"cookie", "nocookie",
"keepopen", "keepalive", "nokeepopen", "nokeepalive",
"nsid", "nonsid",
"padding", "nopadding",
@ -40,9 +40,10 @@ func FuzzDig(f *testing.F) {
"tries=2", "tries=b", "tries",
"tcp", "vc", "notcp", "novc",
"ignore", "noignore",
"badcookie", "nobadcookie",
"tls", "notls",
"dnscrypt", "nodnscrypt",
"https", "nohttps",
"https", "https=/dns", "https-get", "https-get=/", "nohttps",
"quic", "noquic",
"short", "noshort",
"identify", "noidentify",
@ -59,6 +60,7 @@ func FuzzDig(f *testing.F) {
"all", "noall",
"idnout", "noidnout",
"class", "noclass",
"trace", "notrace",
"invalid",
}

View File

@ -7,8 +7,8 @@ import (
"math/rand"
"strings"
"git.froth.zone/sam/awl/conf"
"git.froth.zone/sam/awl/util"
"dns.froth.zone/awl/conf"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"golang.org/x/net/idna"
)
@ -29,22 +29,44 @@ func ParseMiscArgs(args []string, opts *util.Options) error {
switch {
case strings.HasPrefix(arg, "tls://"):
opts.TLS = true
opts.Request.Server = strings.TrimPrefix(opts.Request.Server, "tls://")
opts.Request.Server = strings.TrimPrefix(arg, "tls://")
opts.Logger.Info("DNS-over-TLS implicitly set")
case strings.HasPrefix(arg, "https://"):
opts.HTTPS = true
opts.Request.Server = arg
opts.Logger.Info("DNS-over-HTTPS implicitly set")
_, endpoint, isSplit := strings.Cut(arg, "/")
if isSplit {
opts.HTTPSOptions.Endpoint = "/" + endpoint
}
case strings.HasPrefix(arg, "quic://"):
opts.QUIC = true
opts.Request.Server = strings.TrimPrefix(opts.Request.Server, "quic://")
opts.Request.Server = strings.TrimPrefix(arg, "quic://")
opts.Logger.Info("DNS-over-QUIC implicitly set.")
case strings.HasPrefix(arg, "sdns://"):
opts.DNSCrypt = true
opts.Request.Server = arg
opts.Logger.Info("DNSCrypt implicitly set")
case strings.HasPrefix(arg, "tcp://"):
opts.TCP = true
opts.Request.Server = strings.TrimPrefix(arg, "tcp://")
opts.Logger.Info("TCP implicitly set")
case strings.HasPrefix(arg, "udp://"):
opts.Request.Server = strings.TrimPrefix(arg, "udp://")
default:
opts.Request.Server = arg
// Allow HTTPS queries to have a fallback default
if opts.HTTPS {
server, endpoint, isSplit := strings.Cut(arg, "/")
if isSplit {
opts.HTTPSOptions.Endpoint = "/" + endpoint
opts.Request.Server = server
} else {
opts.Request.Server = server
}
} else {
opts.Request.Server = arg
}
}
// Dig-style +queries
@ -61,7 +83,6 @@ func ParseMiscArgs(args []string, opts *util.Options) error {
opts.Logger.Info(arg, "detected as a domain name")
opts.Request.Name, err = idna.ToASCII(arg)
if err != nil {
return fmt.Errorf("unicode to punycode: %w", err)
}
@ -77,7 +98,6 @@ func ParseMiscArgs(args []string, opts *util.Options) error {
opts.Logger.Info(arg, "is unknown. Assuming domain")
opts.Request.Name, err = idna.ToASCII(arg)
if err != nil {
return fmt.Errorf("unicode to punycode: %w", err)
}
@ -108,16 +128,16 @@ func ParseMiscArgs(args []string, opts *util.Options) error {
case opts.TLS:
opts.Request.Server = "dns.google"
case opts.HTTPS:
opts.Request.Server = "https://dns.cloudflare.com/dns-query"
opts.Request.Server = "https://dns.cloudflare.com"
case opts.QUIC:
opts.Request.Server = "dns.adguard.com"
opts.Request.Server = "dns.froth.zone"
default:
var err error
resolv, err := conf.GetDNSConfig()
if err != nil {
// :^)
opts.Logger.Warn("Could not query system for server. Using localhost")
opts.Logger.Warn("Could not query system for server. Using localhost\n", "Error:", err)
opts.Request.Server = "127.0.0.1"
} else {
// Make sure that if IPv4 or IPv6 is asked for it actually uses it

View File

@ -3,11 +3,11 @@
package cli_test
import (
"strconv"
"strings"
"testing"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/util"
cli "dns.froth.zone/awl/cmd"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
@ -87,21 +87,22 @@ func TestDefaultServer(t *testing.T) {
in string
want string
}{
{"DNSCRYPT", "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"},
{"DNSCrypt", "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"},
{"TLS", "dns.google"},
{"HTTPS", "https://dns.cloudflare.com/dns-query"},
{"QUIC", "dns.adguard.com"},
{"HTTPS", "https://dns.cloudflare.com"},
{"QUIC", "dns.froth.zone"},
}
for _, test := range tests {
test := test
t.Run(test.in, func(t *testing.T) {
t.Parallel()
args := []string{}
opts := new(util.Options)
opts.Logger = util.InitLogger(0)
switch test.in {
case "DNSCRYPT":
case "DNSCrypt":
opts.DNSCrypt = true
case "TLS":
opts.TLS = true
@ -121,32 +122,49 @@ func TestFlagSetting(t *testing.T) {
t.Parallel()
tests := []struct {
in []string
in string
expected string
over string
}{
{[]string{"@sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"}},
{[]string{"@tls://dns.google"}},
{[]string{"@https://dns.cloudflare.com/dns-query"}},
{[]string{"@quic://dns.adguard.com"}},
{"@sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", "DNSCrypt"},
{"@tls://dns.google", "dns.google", "TLS"},
{"@https://dns.cloudflare.com/dns-query", "https://dns.cloudflare.com/dns-query", "HTTPS"},
{"@https://dns.example.net/a", "https://dns.example.net/a", "HTTPS with a set path"},
{"@quic://dns.adguard.com", "dns.adguard.com", "QUIC"},
{"@tcp://dns.froth.zone", "dns.froth.zone", "TCP"},
{"@udp://dns.example.com", "dns.example.com", "UDP"},
}
for i, test := range tests {
for _, test := range tests {
test := test
i := i
t.Run(strconv.Itoa(i), func(t *testing.T) {
t.Run(test.over, func(t *testing.T) {
t.Parallel()
opts := new(util.Options)
opts.Logger = util.InitLogger(0)
t.Parallel()
err := cli.ParseMiscArgs(test.in, opts)
err := cli.ParseMiscArgs([]string{test.in}, opts)
assert.NilError(t, err)
switch i {
case 0:
switch {
case strings.HasPrefix(test.over, "DNSCrypt"):
assert.Assert(t, opts.DNSCrypt)
case 1:
assert.Equal(t, opts.Request.Server, test.expected)
case strings.HasPrefix(test.over, "TLS"):
assert.Assert(t, opts.TLS)
case 2:
assert.Equal(t, opts.Request.Server, test.expected)
case strings.HasPrefix(test.over, "HTTPS"):
assert.Assert(t, opts.HTTPS)
case 3:
assert.Equal(t, opts.Request.Server, test.expected)
case strings.HasPrefix(test.over, "QUIC"):
assert.Assert(t, opts.QUIC)
assert.Equal(t, opts.Request.Server, test.expected)
case strings.HasPrefix(test.over, "TCP"):
assert.Assert(t, opts.TCP)
assert.Equal(t, opts.Request.Server, test.expected)
case strings.HasPrefix(test.over, "UDP"):
assert.Assert(t, true)
assert.Equal(t, opts.Request.Server, test.expected)
}
})
}

6
completions/bash.bash Normal file
View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# bash completion for awl -*- shell-script -*-
# TODO: MAKE THIS A REAL THING
complete -F _known_hosts awl

75
completions/fish.fish Normal file
View File

@ -0,0 +1,75 @@
# SPDX-License-Identifier: BSD-3-Clause
function __fish_complete_awl
set -l token (commandline -ct)
switch $token
case '+tries=*' '+retry=*' '+time=*' '+bufsize=*' '+edns=*'
printf '%s\n' $token(seq 0 255)
case '-v=*'
printf '%s\n' $token(seq -1 3)
end
end
complete -c awl -x -a "(__fish_print_hostnames) A AAAA AFSDB APL CAA CDNSKEY CDS CERT CNAME DHCID DLV DNAME DNSKEY DS HIP IPSECKEY KEY KX LOC MX NAPTR NS NSEC NSEC3 NSEC3PARAM PTR RRSIG RP SIG SOA SRV SSHFP TA TKEY TLSA TSIG TXT URI"
complete -c awl -x -a "@(__fish_print_hostnames)"
complete -f -c awl -s 4 -d 'Use IPv4 query transport only'
complete -f -c awl -s 6 -d 'Use IPv6 query transport only'
complete -c awl -s c -l class -x -a 'IN CH HS QCLASS' -d 'Specify query class'
complete -c awl -s p -l port -x -d 'Specify port number'
complete -c awl -s q -l query -x -a "(__fish_print_hostnames)" -d 'Query domain'
complete -c awl -s t -l qType -x -a 'A AAAA AFSDB APL CAA CDNSKEY CDS CERT CNAME DHCID DLV DNAME DNSKEY DS HIP IPSECKEY KEY KX LOC MX NAPTR NS NSEC NSEC3 NSEC3PARAM PTR RRSIG RP SIG SOA SRV SSHFP TA TKEY TLSA TSIG TXT URI' -d 'Specify query type'
complete -c awl -l timeout -x -d 'Set timeout'
complete -c awl -l retries -x -d 'Set number of query retries'
complete -c awl -l no-edns -x -d 'Disable EDNS'
complete -f -c awl -l tcp -a '+vc +novc +tcp +notcp' -d 'TCP mode'
complete -f -c awl -l dnscrypt -a '+dnscrypt +nodnscrypt' -d 'Use DNSCrypt'
complete -c awl -s T -l tls -a '+tls +notls' -d 'Use DNS-over-TLS'
complete -c awl -s H -l https -a '+https +nohttps' -d 'Use DNS-over-HTTPS'
complete -c awl -s Q -l quic -a '+quic +noquic' -d 'Use DNS-over-QUIC'
complete -c awl -s j -l json -a '+json +nojson' -d 'Print as JSON'
complete -c awl -s j -l xml -a '+xml +noxml' -d 'Print as XML'
complete -c awl -s j -l yaml -a '+yaml +noyaml' -d 'Print as YAML'
complete -c awl -s x -l reverse -x -d 'Reverse lookup'
complete -f -c awl -s h -l help -d 'Print help and exit'
complete -f -c awl -s V -l version -d 'Print version and exit'
# complete -f -c awl -a '+search +nosearch' -d 'Set whether to use searchlist'
# complete -f -c awl -a '+showsearch +noshowsearch' -d 'Search with intermediate results'
complete -f -c awl -a '+recurse +norecurse' -d 'Recursive mode'
complete -f -c awl -l no-truncate -a '+ignore +noignore' -d 'Dont revert to TCP for TC responses.'
# complete -f -c awl -a '+fail +nofail' -d 'Dont try next server on SERVFAIL'
# complete -f -c awl -a '+besteffort +nobesteffort' -d 'Try to parse even illegal messages'
complete -f -c awl -a '+aaonly +noaaonly' -d 'Set AA flag in query (+[no]aaflag)'
complete -f -c awl -a '+adflag +noadflag' -d 'Set AD flag in query'
complete -f -c awl -a '+cdflag +nocdflag' -d 'Set CD flag in query'
complete -f -c awl -a '+cl +nocl' -d 'Control display of class in records'
# complete -f -c awl -a '+cmd +nocmd' -d 'Control display of command line'
complete -f -c awl -a '+comments +nocomments' -d 'Control display of comment lines'
complete -f -c awl -a '+question +noquestion' -d 'Control display of question'
complete -f -c awl -a '+answer +noanswer' -d 'Control display of answer'
complete -f -c awl -a '+authority +noauthority' -d 'Control display of authority'
complete -f -c awl -a '+additional +noadditional' -d 'Control display of additional'
complete -f -c awl -a '+stats +nostats' -d 'Control display of statistics'
complete -f -c awl -s s -l short -a '+short +noshort' -d 'Disable everything except short form of answer'
complete -f -c awl -a '+ttlid +nottlid' -d 'Control display of ttls in records'
complete -f -c awl -a '+all +noall' -d 'Set or clear all display flags'
complete -f -c awl -a '+qr +noqr' -d 'Print question before sending'
# complete -f -c awl -a '+nssearch +nonssearch' -d 'Search all authoritative nameservers'
complete -f -c awl -a '+identify +noidentify' -d 'ID responders in short answers'
complete -f -c awl -a '+trace +notrace' -d 'Trace delegation down from root'
complete -f -c awl -l dnssec -a '+dnssec +nodnssec +do +nodo' -d 'Request DNSSEC records'
complete -f -c awl -a '+nsid +nonsid' -d 'Request Name Server ID'
# complete -f -c awl -a '+multiline +nomultiline' -d 'Print records in an expanded format'
# complete -f -c awl -a '+onesoa +noonesoa' -d 'AXFR prints only one soa record'
complete -f -c awl -a '+tries=' -d 'Set number of UDP attempts'
complete -f -c awl -a '+retry=' -d 'Set number of UDP retries'
complete -f -c awl -a '+time=' -d 'Set query timeout'
complete -f -c awl -a '+bufsize=' -d 'Set EDNS0 Max UDP packet size'
complete -f -c awl -a '+ndots=' -d 'Set NDOTS value'
complete -f -c awl -a '+edns=' -d 'Set EDNS version'
complete -c awl -a '(__fish_complete_awl)'

View File

@ -8,11 +8,12 @@ local -a alts args
'*+'{no,}'ignore[ignore truncation in UDP responses]'
'*+'{no,}'tls[use DNS-over-TLS for queries]'
'*+'{no,}'dnscrypt[use DNSCrypt for queries]'
'*+'{no,}'https[use DNS-over-HTTPS for queries]'
'*+'{no,}'https=[use DNS-over-HTTPS for queries]:endpoint [/dns-query]'
'*+'{no,}'quic[use DNS-over-QUIC for queries]'
'*+'{no,}'aaonly[set aa flag in the query]'
'*+'{no,}'additional[print additional section of a reply]'
'*+'{no,}'adflag[set the AD (authentic data) bit in the query]'
'*+'{no,}'badcookie[retry BADCOOKIE responses]'
'*+'{no,}'cdflag[set the CD (checking disabled) bit in the query]'
'*+'{no,}'cookie[add a COOKIE option to the request]'
'*+edns=[specify EDNS version for query]:version (0-255)'
@ -27,7 +28,7 @@ local -a alts args
'*+'{no,}'keepopen[keep TCP socket open between queries]'
'*+'{no,}'recurse[set the RD (recursion desired) bit in the query]'
# '*+'{no,}'nssearch[search all authoritative nameservers]'
# '*+'{no,}'trace[trace delegation down from root]'
'*+'{no,}'trace[trace delegation down from root]'
# '*+'{no,}'cmd[print initial comment in output]'
'*+'{no,}'short[print terse output]'
'*+'{no,}'identify[print IP and port of responder]'
@ -49,7 +50,7 @@ local -a alts args
# '*+'{no,}'rrcomments[set display of per-record comments]'
# '*+ndots=[specify number of dots to be considered absolute]:dots'
'*+bufsize=[specify UDP buffer size]:size (bytes)'
'*+'{no,}'dnssec[enable DNSSEC]'
'*+'{no,}''{dnssec,do}'[enable DNSSEC]'
'*+'{no,}'nsid[include EDNS name server ID request in query]'
'*+'{no,}'class[display the class whening printing the answer]'
'*+'{no,}'ttlid[display the TTL whening printing the record]'
@ -95,8 +96,9 @@ _arguments -s -C $args \
'*--tls-host+[set TLS lookup hostname]:host:_hosts' \
'*-'{s,-short}'+[print terse output]' \
'*-'{j,-json}'+[present the results as JSON]' \
'*-'{x,-xml}'+[present the results as XML]' \
'*-'{X,-xml}'+[present the results as XML]' \
'*-'{y,-yaml}'+[present the results as YAML]' \
'*--trace+[trace from the root]' \
'*: :->args' && ret=0
if [[ -n $state ]]; then
@ -107,4 +109,4 @@ if [[ -n $state ]]; then
fi
fi
return ret
return ret

View File

@ -4,53 +4,22 @@
package conf_test
import (
"runtime"
"testing"
"git.froth.zone/sam/awl/conf"
"dns.froth.zone/awl/conf"
"gotest.tools/v3/assert"
)
func TestGetPlan9Config(t *testing.T) {
func TestPlan9Config(t *testing.T) {
t.Parallel()
if runtime.GOOS != "plan9" {
t.Skip("Not running Plan 9, skipping")
}
ndbs := []struct {
in string
want string
}{
{`ip=192.168.122.45 ipmask=255.255.255.0 ipgw=192.168.122.1
sys=chog9
dns=192.168.122.1`, "192.168.122.1"},
{`ipnet=murray-hill ip=135.104.0.0 ipmask=255.255.0.0
dns=135.104.10.1
ntp=ntp.cs.bell-labs.com
ipnet=plan9 ip=135.104.9.0 ipmask=255.255.255.0
ntp=oncore.cs.bell-labs.com
smtp=smtp1.cs.bell-labs.com
ip=135.104.9.6 sys=anna dom=anna.cs.bell-labs.com
smtp=smtp2.cs.bell-labs.com`, "135.104.10.1"},
}
conf, err := conf.GetDNSConfig()
for _, ndb := range ndbs {
// Go is a little quirky
ndb := ndb
t.Run(ndb.want, func(t *testing.T) {
t.Parallel()
act, err := conf.GetPlan9Config(ndb.in)
assert.NilError(t, err)
assert.Equal(t, ndb.want, act.Servers[0])
})
}
invalid := `sys = spindle
dom=spindle.research.bell-labs.com
bootf=/mips/9powerboot
ip=135.104.117.32 ether=080069020677
proto=il`
act, err := conf.GetPlan9Config(invalid)
assert.ErrorContains(t, err, "no DNS servers found")
assert.Assert(t, act == nil)
assert.NilError(t, err)
assert.Assert(t, len(conf.Servers) != 0)
}

View File

@ -9,12 +9,14 @@ import (
"runtime"
"testing"
"git.froth.zone/sam/awl/conf"
"dns.froth.zone/awl/conf"
"gotest.tools/v3/assert"
)
func TestNonWinConfig(t *testing.T) {
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
func TestUnixConfig(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" || runtime.GOOS == "js" || runtime.GOOS == "zos" {
t.Skip("Not running Unix-like, skipping")
}

View File

@ -21,7 +21,7 @@ https://gist.github.com/moloch--/9fb1c8497b09b45c840fe93dd23b1e98
//
// Here be dragons.
func GetDNSConfig() (*dns.ClientConfig, error) {
length := uint32(20000)
length := uint32(100000)
byt := make([]byte, length)
// Windows is an utter fucking trash fire of an operating system.

View File

@ -7,7 +7,7 @@ import (
"runtime"
"testing"
"git.froth.zone/sam/awl/conf"
"dns.froth.zone/awl/conf"
"gotest.tools/v3/assert"
)

@ -1 +0,0 @@
Subproject commit 6f3070f5933d0cc48bc8bb5290a1d0cc1825cd75

1
docs/CODE_OF_CONDUCT.md Symbolic link
View File

@ -0,0 +1 @@
/dev/null

57
docs/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,57 @@
# Contributing to awl
First off, thank you! We appreciate your interest in wanting to contribute to awl.
> If you like the project, spread the word! Help us grow by sharing the project with anyone you thing might be interested. Here are some ways you can help:
>
> - Star the project on GitHub
> - Share the project on social media
> - Tell your friends about the project
## How to contribute
If you want to contribute to awl, you can do so by:
- [Reporting a bug](#reporting-a-bug)
- [Requesting a feature](#requesting-a-feature)
- [Submitting a pull request](#submitting-a-pull-request)
### Reporting a bug
If you find a bug in awl, please [open an issue](https://git.froth.zone/sam/awl/issues) on the project's issue tracker. When reporting a bug, please include as much information as possible, such as:
- The version of awl you are using
- The operating system you are using
- The steps to reproduce the bug
- Any error messages you received
### Requesting a feature
If you have an idea for a feature you would like to see in awl, please [open an issue](https://git.froth.zone/sam/awl/issues) on the project's issue tracker. When requesting a feature, please include as much information as possible, such as:
- A description of the feature
- Why you think the feature would be useful
- Any other relevant information
### Submitting a pull request
If you would like to contribute code to awl, you can do so by submitting a pull request. To submit a pull request, follow these steps:
1. Fork the project on Git
2. Create a new branch for your changes
3. Make your changes
4. Push your changes to your fork
5. [Open a pull request](https://git.froth.zone/sam/awl/pulls) on the project's Git repository
When submitting a pull request, please include as much information as possible, such as:
- A description of the changes you made
- Why you made the changes
- Any other relevant information
Alternatively, you can also contribute by sending an email to the project's [mailing list](https://lists.sr.ht/~sammefishe/awl-devel). For more information about using Git over email, refer to [git-send-email.io](https://git-send-email.io/)
#### Code Style
Before submitting a pull request, please run `make lint` to ensure your code adheres to the project's code style.
Make sure that you have `golangci-lint` installed, that is our linter of choice.

View File

@ -25,17 +25,38 @@ some more recent RFCs and output options.
When no arguments are given, *awl* will perform an _NS_ query on the root ('_._').
When a nameserver is not given, *awl* will query a random system nameserver.
If one cannot be found, *awl* will query localhost.
If one cannot be found, *awl* will query the localhost.
# OPTIONS
Anything in [brackets] is optional.
*-4*
Force only IPv4
*-D*, *--dnssec*, *+dnssec*
Enable DNSSEC. This needs to be manually enabled.
*-6*
Force only IPv6
*-c*, *--class* _class_
DNS class to query (eg. IN, CH)
The default is IN.
*-h*
Show a "short" help message.
*-p*, *--port* _port_
Sets the port to query. Default ports listed below.
- _53_ for *UDP* and *TCP*
- _853_ for *TLS* and *QUIC*
- _443_ for *HTTPS*
*-q*, *--query* _domain_
Explicitly set a domain to query (eg. example.com)
*-t*, *--qType* _type_
Explicitly set a DNS type to query (eg. A, AAAA, NS)
The default is A.
*-v*[=_int_]
Set verbosity
Set verbosity of output
Accepted values are as follows:
- _0_: Only log errors.
- _1_: Log warnings. *This is the default.*
@ -46,47 +67,122 @@ Anything in [brackets] is optional.
By default, specifying just *-v* sets the verbosity to 2 (info).
*-x*, *--reverse*
Do a reverse lookup. Sets default *type* to PTR.
*awl* automatically makes an IP or phone number canonical.
*-V*
Print the version and exit.
*-h*
Show a "short" help message.
# QUERY OPTIONS
## Query Options
Anything in [brackets] is optional.
Many options are inherited from *dig*(1).
*-4*
Only make query over IPv4
*--aa*[=_bool_], *+*[no]*aaflag*, *+*[no]*aaonly*
Sets the AA (Authoritative Answer) flag.
*-6*
Only make query over IPv6
*--ad*[=_bool_], *+*[no]*adflag*
Sets the AD (Authenticated Data) flag.
*-p*, *--port* *port*
Sets the port to query. Default ports listed below.
- _53_ for *UDP* and *TCP*
- _853_ for *TLS* and *QUIC*
- _443_ for *HTTPS*
*--no-additional*, *+*[no]*additional*
Toggle the display of the Additional section.
*-q*, *--query* _domain_
Domain to query (eg. example.com)
*--no-answer*, *+*[no]*answer*
Toggle the display of the Answer section.
*-c*, *--class* _class_
DNS class to query (eg. IN, CH)
The default is IN.
*--no-authority*, *+*[no]*authority*
Toggle the display of the Authority section.
*-t*, *--qType* _type_
DNS type to query (eg. A, AAAA, NS)
The default is A.
*--no-bad-cookie*, *+*[no]*badcookie*
\[Do not\] ignore BADCOOKIE responses
*--buffer-size* _int_, *+bufize*=_int_
Set the UDP message buffer size, using EDNS.
Max is 65535, minimum is zero.
The default value is 1232.
*--cd*[=_bool_], *+*[no]*cdflag*
(Set, Unset) CD (Checking Disabled) flag.
*--no-cookie*, *+*[no]*cookie*[=_string_]
Send an EDNS cookie.
This is enabled by default with a random string.
*-D*, *--dnssec*, *+dnssec*, *+do*
Request DNSSEC records as well.
This sets the DNSSEC OK bit (DO)
*--dnscrypt*, *+*[no]*dnscrypt*
Use DNSCrypt.
*--expire*. *+*[no]*expire*
Send an EDNS Expire.
*--edns-ver*, *+edns*[=_int_]
Enable EDNS and set EDNS version.
The maximum value is 255, and the minimum (default) value is 0.
*--no-edns*, *+noedns*
Disable EDNS.
*-H*, *--https*, *+*[no]*https*[=_endpoint_], *+*[no]*https-post*[=_endpoint_]
Use DNS-over-HTTPS (see RFC 8484).
The default endpoint is _/dns-query_
*+*[no]*https-get*[=_endpoint_]
Use an HTTP GET instead of an HTTP POST when making a DNS-over-HTTPS query.
*+*[no]*idnout*
Converts [or leaves] punycode on output.
Input is automatically translated to punycode.
*--no-truncate*, *+ignore*
Ignore UDP truncation (by default, awl *retries with TCP*).
*--tcp*, *+tcp*, *+vc*
*-j*, *--json*, *+*[no]*json*
Print the query results as JSON.
The result is *not* in compliance with RFC 8427.
*--keep-alive*, *+*[no]*keepalive*, *+*[no]*keepopen*
Send an EDNS keep-alive.
This does nothing unless using TCP.
*--nsid*, *+*[no]*nsid*
Send an EDNS name server ID request.
*--qr*[=_bool_], *+*[no]*qrflag*
Sets the QR (QueRy) flag.
*--no-question*, *+*[no]*question*
Toggle the display of the Question section.
*-Q*. *--quic*, *+*[no]*quic*
Use DNS-over-QUIC (see RFC 9250).
*-s*, *--short*, *+*[no]*short*
Print just the address of the answer.
*--no-statistics*, *+*[no]*stats*
Toggle the display of the Statistics (additional comments) section.
*--subnet* _ip_[_/prefix_], *+*[no]*subnet*[=_ip_[_/prefix_]]
Send an EDNS Client Subnet option with the specified address.
Like *dig*(1), setting the IP to _0.0.0.0/0_, _::/0_ or _0_ will signal the resolver to not use any client information when returning the query.
*--tc*[=_bool_], *+*[no]*tcflag*
Sets the TC (TrunCated) flag
*--tcp*, *+*[no]*tcp*, *+*[no]*vc*
Use TCP for the query (see RFC 7766).
*--dnscrypt*, *+dnscrypt*
Use DNSCrypt.
*--timeout* _seconds_, *+timeout*=_seconds_
Set the timeout period. Floating point numbers are accepted.
0.5 seconds is the minimum.
*-T*, *--tls*, *+tls*
*-T*, *--tls*, *+*[no]*tls*
Use DNS-over-TLS, implies *--tcp* (see RFC 7858)
*--tls-host* _string_
@ -96,123 +192,29 @@ Anything in [brackets] is optional.
*--tls-no-verify*
Ignore TLS validation when performing a DNS query.
*-H*. *--https*, *+https*
Use DNS-over-HTTPS (see RFC 8484).
*-Q*. *--quic*, *+quic*
Use DNS-over-QUIC (see RFC 9250).
*-x*, *--reverse*
Do a reverse lookup. Sets default *type* to PTR.
*awl* automatically makes an IP or phone number canonical.
*--timeout* _seconds_, *+timeout*=_seconds_
Set the timeout period. Floating point numbers are accepted.
0.5 seconds is the minimum.
*--trace*, *+trace*
Trace the path of the query from the root, acting like its own resolver.
This option enables DNSSEC.
When *@server* is specified, this will only affect the initial query.
*--retries* _int_, *+tries*=_int_, *+retry*=_int_
Set the number of retries.
Retry is one more than tries, dig style.
## DNS Flags
*-X*, *--xml*, *+*[no]*xml*
Print the query results as XML.
*--aa*[=_bool_], *+[no]aaflag*
(Set, Unset) AA (Authoritative Answer) flag.
*-y*, *--yaml*, *+*[no]*yaml*
Print the query results as YAML.
*--ad*[=_bool_], *+[no]adflag*
(Set, Unset) AD (Authenticated Data) flag.
*--tc*[=_bool_], *+[no]tcflag*
(Set, Unset) TC (TrunCated) flag
*-z*[=_bool_], *+[no]zflag*
(Set, Unset) Z (Zero) flag.
*--cd*[=_bool_], *+[no]cdflag*
(Set, Unset) CD (Checking Disabled) flag.
*--qr*[=_bool_], *+[no]qrflag*
(Set, Unset) QR (QueRy) flag.
*--rd*[=_bool_], *+[no]rdflag*
(Set, Unset) RD (Recursion Desired) flag.
*--ra*[=_bool_], *+[no]raflag*
(Set, Unset) RA (Recursion Available) flag.
## EDNS
All of these options except disabling EDNS imply *+edns*.
*--no-edns*, *+noedns*
Disable EDNS.
*--edns-ver*, *+edns*[=_int_]
Enable EDNS and set EDNS version.
The maximum value is 255, and the minimum (default) value is 0.
*--expire*. *+[no]expire*
Send an EDNS Expire.
*--nsid*, *+[no]nsid*
Send an EDNS name server ID request.
*--no-cookie*, *+[no]cookie*[=_string_]
Send an EDNS cookie.
This is enabled by default with a random string.
*--keep-alive*, *+[no]keepalive*, *+[no]keepopen*
Send an EDNS keep-alive.
This does nothing unless using TCP.
*--buffer-size* _int_, *+bufize*=_int_
Set the UDP message buffer size, using EDNS.
Max is 65535, minimum is zero.
The default value is 1232.
*-z*[=_bool_], *+*[no]*zflag*
Sets the Z (Zero) flag.
*--zflag* _int_, *+ednsflags*=_int_
Set the must-be-zero EDNS flags.
Decimal, hexadecimal and octal are supported.
Trying to set DO will be ignored.
*--subnet* _ip_[_/prefix_], *+[no]subnet*=_ip_[_/prefix_]
Send an EDNS Client Subnet option with the specified address.
Like *dig*(1), setting the IP to _0.0.0.0/0_, _::/0_ or _0_ will signal the resolver to not use any client information when returning the query.
## Output Display
*--no-question*, *+[no]question*
Toggle the display of the Question section.
*--no-answer*, *+[no]answer*
Toggle the display of the Answer section.
*--no-answer*, *+[no]answer*
Toggle the display of the Answer section.
*--no-authority*, *+[no]authority*
Toggle the display of the Authority section.
*--no-additional*, *+[no]additional*
Toggle the display of the Additional section.
*--no-statistics*, *+[no]stats*
Toggle the display of the Statistics (additional comments) section.
## Output Formats
*-j*, *--json*, *+json*
Print the query results as JSON.
*-X*, *--xml*, *+xml*
Print the query results as XML.
*-y*, *--yaml*, *+yaml*
Print the query results as YAML.
*-s*, *--short*, *+short*
Print just the address of the answer.
# EXIT STATUS
The exit code is 0 when a query is successfully made and received.
@ -251,12 +253,10 @@ Probably more, _https://www.statdns.com/rfc_
# BUGS
OPT records are only printed when using a standard output, not JSON/XML/YAML.
Full parity with *dig*(1) is not complete.
This man page is probably not complete.
Likely numerous more, report them either to the tracker
_https://git.froth.zone/sam/awl/issues_ or via email
_~sammefishe/awl-dev@lists.sr.ht_
_~sammefishe/awl-develop@lists.sr.ht_

BIN
docs/img/awl-text.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

1
docs/img/awl.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 505 KiB

7
docs/makeman.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
rm -f docs/awl.1.gz
scdoc <docs/awl.1.scd >docs/awl.1
gzip -9 -n docs/awl.1

1
docs/wiki Submodule

@ -0,0 +1 @@
Subproject commit ab0ac7e0bd1b92339cc97ba026f148478df5a860

50
go.mod
View File

@ -1,37 +1,33 @@
module git.froth.zone/sam/awl
module dns.froth.zone/awl
go 1.18
go 1.21.9
toolchain go1.22.2
require (
github.com/ameshkov/dnscrypt/v2 v2.2.5
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
github.com/lucas-clemente/quic-go v0.29.0
github.com/miekg/dns v1.1.50
dns.froth.zone/dnscrypt v0.0.2
github.com/dchest/uniuri v1.2.0
github.com/miekg/dns v1.1.59
github.com/quic-go/quic-go v0.43.0
github.com/stefansundin/go-zflag v1.1.1
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
golang.org/x/sys v0.0.0-20220913175220-63ea55921009
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.3.0
gotest.tools/v3 v3.5.1
)
require (
github.com/AdguardTeam/golibs v0.10.9 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/AdguardTeam/golibs v0.20.3 // indirect
github.com/ameshkov/dnsstamps v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/exp v0.0.0-20220914170420-dc92f8653013 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.12 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/onsi/ginkgo/v2 v2.16.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
)

198
go.sum
View File

@ -1,159 +1,75 @@
github.com/AdguardTeam/golibs v0.10.9 h1:F9oP2da0dQ9RQDM1lGR7LxUTfUWu8hEFOs4icwAkKM0=
github.com/AdguardTeam/golibs v0.10.9/go.mod h1:W+5rznZa1cSNSFt+gPS7f4Wytnr9fOrd5ZYqwadPw14=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/ameshkov/dnscrypt/v2 v2.2.5 h1:Ju1gQeez+6XLtk/b/k3RoJ2t+Ls+BSItLTZjZeedneY=
github.com/ameshkov/dnscrypt/v2 v2.2.5/go.mod h1:Cu5GgMvCR10BeXgACiGDwXyOpfMktsSIidml1XBp6uM=
dns.froth.zone/dnscrypt v0.0.2 h1:ytqjic/Qway4OuLw8ee0ubxdNzy+F3igUGDrEVwyLls=
dns.froth.zone/dnscrypt v0.0.2/go.mod h1:QZ0HAm7mWe8wz1dTqbKmTZhlr06x5qpe6ZCTPJ7uY30=
github.com/AdguardTeam/golibs v0.20.3 h1:5RiDypxBebd4Y2eftwm6JJla18oBqRHwanR7q0rnrxw=
github.com/AdguardTeam/golibs v0.20.3/go.mod h1:/votX6WK1PdcZ3T2kBOPjPCGmfhlKixhI6ljYrFRPvI=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs=
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g=
github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucas-clemente/quic-go v0.29.0 h1:Vw0mGTfmWqGzh4jx/kMymsIkFK6rErFVmg+t9RLrnZE=
github.com/lucas-clemente/quic-go v0.29.0/go.mod h1:CTcNfLYJS2UuRNB+zcNlgvkjBhxX6Hm3WUxxAQx2mgE=
github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM=
github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/quic-go/quic-go v0.43.0 h1:sjtsTKWX0dsHpuMJvLxGqoQdtgJnbAPWY+W+5vjYW/g=
github.com/quic-go/quic-go v0.43.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/stefansundin/go-zflag v1.1.1 h1:XabhzWS588bVvV1z1UctSa6i8zHkXc5W9otqtnDSHw8=
github.com/stefansundin/go-zflag v1.1.1/go.mod h1:HXX5rABl1AoTcZ2jw+CqJ7R8irczaLquGNZlFabZooc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20220914170420-dc92f8653013 h1:ZjglnWxEUdPyXl4o/j4T89SRCI+4X6NW6185PNLEOF4=
golang.org/x/exp v0.0.0-20220914170420-dc92f8653013/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw=
golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=

123
main.go
View File

@ -1,17 +1,19 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build !js
package main
import (
"errors"
"fmt"
"math/rand"
"os"
"strings"
"time"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
cli "dns.froth.zone/awl/cmd"
"dns.froth.zone/awl/pkg/query"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
)
var version = "DEV"
@ -19,7 +21,7 @@ var version = "DEV"
func main() {
if opts, code, err := run(os.Args); err != nil {
// TODO: Make not ew
if errors.Is(err, cli.ErrNotError) || strings.Contains(err.Error(), "help requested") {
if errors.Is(err, util.ErrNotError) || strings.Contains(err.Error(), "help requested") {
os.Exit(0)
} else {
opts.Logger.Error(err)
@ -28,43 +30,106 @@ func main() {
}
}
func run(args []string) (opts util.Options, code int, err error) {
func run(args []string) (opts *util.Options, code int, err error) {
//nolint:gosec //Secure source not needed
r := rand.New(rand.NewSource(time.Now().Unix()))
opts, err = cli.ParseCLI(args, version)
if err != nil {
return opts, 1, fmt.Errorf("parse: %w", err)
}
var resp util.Response
var (
resp util.Response
keepTracing bool
tempDomain string
tempQueryType uint16
)
// Retry queries if a query fails
for i := 0; i <= opts.Request.Retries; i++ {
resp, err = query.CreateQuery(opts)
if err == nil {
break
} else if i != opts.Request.Retries {
opts.Logger.Warn("Retrying request, error:", err)
for ok := true; ok; ok = keepTracing {
if opts.Trace {
if keepTracing {
opts.Request.Name = tempDomain
opts.Request.Type = tempQueryType
} else {
tempDomain = opts.Request.Name
tempQueryType = opts.Request.Type
// Override the query because it needs to be done
opts.Request.Name = "."
opts.Request.Type = dns.TypeNS
}
}
}
// Retry queries if a query fails
for i := 0; i <= opts.Request.Retries; i++ {
resp, err = query.CreateQuery(opts)
if err == nil {
keepTracing = opts.Trace && (!resp.DNS.Authoritative || (opts.Request.Name == "." && tempDomain != ".")) && resp.DNS.MsgHdr.Rcode == 0
// Query failed, make it fail
if err != nil {
return opts, 9, fmt.Errorf("query: %w", err)
}
break
} else if i != opts.Request.Retries {
opts.Logger.Warn("Retrying request, error:", err)
}
}
var str string
if opts.JSON || opts.XML || opts.YAML {
str, err = query.PrintSpecial(resp, opts)
// Query failed, make it fail
if err != nil {
return opts, 10, fmt.Errorf("format print: %w", err)
return opts, 9, fmt.Errorf("query: %w", err)
}
} else {
str, err = query.ToString(resp, opts)
if err != nil {
return opts, 15, fmt.Errorf("standard print: %w", err)
var str string
if opts.JSON || opts.XML || opts.YAML {
str, err = query.PrintSpecial(resp, opts)
if err != nil {
return opts, 10, fmt.Errorf("format print: %w", err)
}
} else {
str, err = query.ToString(resp, opts)
if err != nil {
return opts, 15, fmt.Errorf("standard print: %w", err)
}
}
fmt.Println(str)
if keepTracing {
var records []dns.RR
if opts.Request.Name == "." {
records = resp.DNS.Answer
} else {
records = resp.DNS.Ns
}
want := func(rr dns.RR) bool {
temp := strings.Split(rr.String(), "\t")
return temp[len(temp)-2] == "NS"
}
i := 0
for _, x := range records {
if want(x) {
records[i] = x
i++
}
}
records = records[:i]
randomRR := records[r.Intn(len(records))]
v := strings.Split(randomRR.String(), "\t")
opts.Request.Server = strings.TrimSuffix(v[len(v)-1], ".")
opts.TLS = false
opts.HTTPS = false
opts.QUIC = false
opts.RD = false
opts.Request.Port = 53
}
}
fmt.Println(str)
return opts, 0, nil
}

View File

@ -3,31 +3,43 @@
package main
import (
"os"
"testing"
"github.com/stefansundin/go-zflag"
"gotest.tools/v3/assert"
)
func TestMain(t *testing.T) { //nolint: paralleltest // Race conditions
os.Stdout = os.NewFile(0, os.DevNull)
os.Stderr = os.NewFile(0, os.DevNull)
func TestRun(t *testing.T) {
// t.Parallel()
args := [][]string{
{"awl", "+yaml", "@1.1.1.1"},
{"awl", "+short", "@1.1.1.1"},
}
args := []string{"awl", "+yaml", "@1.1.1.1"}
for _, test := range args {
test := test
_, code, err := run(args)
assert.NilError(t, err)
assert.Equal(t, code, 0)
t.Run("", func(t *testing.T) {
_, code, err := run(test)
assert.NilError(t, err)
assert.Equal(t, code, 0)
})
}
}
args = []string{"awl", "+short", "@1.1.1.1"}
func TestTrace(t *testing.T) {
domains := []string{"git.froth.zone", "google.com", "amazon.com", "freecumextremist.com", "dns.froth.zone", "sleepy.cafe", "pkg.go.dev"}
_, code, err = run(args)
assert.NilError(t, err)
assert.Equal(t, code, 0)
for i := range domains {
args := []string{"awl", "+trace", domains[i], "@1.1.1.1"}
_, code, err := run(args)
assert.NilError(t, err)
assert.Equal(t, code, 0)
}
}
func TestHelp(t *testing.T) {
// t.Parallel()
args := []string{"awl", "-h"}
_, code, err := run(args)

8
mkfile
View File

@ -1,10 +1,12 @@
# SPDX-License-Identifier: BSD-3-Clause
# Plan 9 mkfile
</$objtype/mkfile
GO = go
PROG = awl
GOFLAGS = -ldflags=-s -ldflags=-w -ldflags=-X=main.version=PLAN9 -trimpath
VERSION = `{awk '{print substr($0,0,8)}' .git/refs/heads/master}
GOFLAGS = -ldflags=-s -ldflags=-w -ldflags=-X=main.version=$VERSION -trimpath
CGO_ENABLED = 0
@ -15,7 +17,7 @@ $PROG:
install:V:
$GO install $GOFLAGS .
cp doc/$PROG.1 /sys/man/1/$PROG
# cp docs/$PROG.1 /sys/man/1/$PROG
test:V:
$GO test -v -cover ./...

@ -1 +0,0 @@
Subproject commit d876d6de34a78298ed041f575662015fb7eccdb5

View File

@ -14,7 +14,7 @@ because awl is a cli utility it writes directly to std err.
// You can call specific logging levels from your new logger using
//
// logger.Debug("Message to log")
// logger.Fatal("Message to log")
// logger.Warning("Message to log")
// logger.Info("Message to log")
// logger.Error("Message to log")
//

View File

@ -25,22 +25,22 @@ type (
)
// SetLevel stores whatever input value is in mem address of l.level.
func (l *Logger) SetLevel(level Level) {
atomic.StoreInt32((*int32)(&l.Level), int32(level))
func (logger *Logger) SetLevel(level Level) {
atomic.StoreInt32((*int32)(&logger.Level), int32(level))
}
// GetLevel gets the logger level.
func (l *Logger) GetLevel() Level {
return l.level()
func (logger *Logger) GetLevel() Level {
return logger.level()
}
// Retrieves whatever was stored in mem address of l.level.
func (l *Logger) level() Level {
return Level(atomic.LoadInt32((*int32)(&l.Level)))
func (logger *Logger) level() Level {
return Level(atomic.LoadInt32((*int32)(&logger.Level)))
}
// UnMarshalLevel unmarshalls the int value of level for writing the header.
func (l *Logger) UnMarshalLevel(lv Level) (string, error) {
func (logger *Logger) UnMarshalLevel(lv Level) (string, error) {
switch lv {
case ErrLevel:
return "ERROR ", nil
@ -56,8 +56,8 @@ func (l *Logger) UnMarshalLevel(lv Level) (string, error) {
}
// IsLevel returns true if the logger level is above the level given.
func (l *Logger) IsLevel(level Level) bool {
return l.level() >= level
func (logger *Logger) IsLevel(level Level) bool {
return logger.level() >= level
}
// AllLevels is an array of all valid log levels.

View File

@ -20,28 +20,28 @@ func New() *Logger {
}
// Println takes any and prints it out to Logger -> Out (io.Writer (default is std.Err)).
func (l *Logger) Println(level Level, v ...any) {
if atomic.LoadInt32(&l.isDiscard) != 0 {
func (logger *Logger) Println(level Level, in ...any) {
if atomic.LoadInt32(&logger.isDiscard) != 0 {
return
}
// If verbose is not set --debug etc print _nothing_
if l.IsLevel(level) {
if logger.IsLevel(level) {
switch level { // Goes through log levels and does stuff based on them (currently nothing)
case ErrLevel:
if err := l.Printer(ErrLevel, fmt.Sprintln(v...)); err != nil {
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
if err := logger.Printer(ErrLevel, fmt.Sprintln(in...)); err != nil {
fmt.Fprintln(logger.Out, "Logger failed: ", err)
}
case WarnLevel:
if err := l.Printer(WarnLevel, fmt.Sprintln(v...)); err != nil {
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
if err := logger.Printer(WarnLevel, fmt.Sprintln(in...)); err != nil {
fmt.Fprintln(logger.Out, "Logger failed: ", err)
}
case InfoLevel:
if err := l.Printer(InfoLevel, fmt.Sprintln(v...)); err != nil {
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
if err := logger.Printer(InfoLevel, fmt.Sprintln(in...)); err != nil {
fmt.Fprintln(logger.Out, "Logger failed: ", err)
}
case DebugLevel:
if err := l.Printer(DebugLevel, fmt.Sprintln(v...)); err != nil {
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
if err := logger.Printer(DebugLevel, fmt.Sprintln(in...)); err != nil {
fmt.Fprintln(logger.Out, "Logger failed: ", err)
}
default:
break
@ -50,8 +50,8 @@ func (l *Logger) Println(level Level, v ...any) {
}
// FormatHeader formats the log header as such <LogLevel> YYYY/MM/DD HH:MM:SS (local time) <the message to log>.
func (l *Logger) FormatHeader(buf *[]byte, t time.Time, line int, level Level) error {
if lvl, err := l.UnMarshalLevel(level); err == nil {
func (logger *Logger) FormatHeader(buf *[]byte, t time.Time, line int, level Level) error {
if lvl, err := logger.UnMarshalLevel(level); err == nil {
// This is ugly but functional
// maybe there can be an append func or something in the future
*buf = append(*buf, lvl...)
@ -81,27 +81,27 @@ func (l *Logger) FormatHeader(buf *[]byte, t time.Time, line int, level Level) e
}
// Printer prints the formatted message directly to stdErr.
func (l *Logger) Printer(level Level, s string) error {
func (logger *Logger) Printer(level Level, s string) error {
now := time.Now()
var line int
l.Mu.Lock()
defer l.Mu.Unlock()
logger.Mu.Lock()
defer logger.Mu.Unlock()
l.buf = l.buf[:0]
logger.buf = logger.buf[:0]
if err := l.FormatHeader(&l.buf, now, line, level); err != nil {
if err := logger.FormatHeader(&logger.buf, now, line, level); err != nil {
return err
}
l.buf = append(l.buf, s...)
logger.buf = append(logger.buf, s...)
if len(s) == 0 || s[len(s)-1] != '\n' {
l.buf = append(l.buf, '\n')
logger.buf = append(logger.buf, '\n')
}
_, err := l.Out.Write(l.buf)
_, err := logger.Out.Write(logger.buf)
if err != nil {
return fmt.Errorf("logger printing: %w", err)
}
@ -135,41 +135,41 @@ func formatter(buf *[]byte, i int, wid int) {
}
// Debug calls print directly with Debug level.
func (l *Logger) Debug(v ...any) {
l.Println(DebugLevel, v...)
func (logger *Logger) Debug(in ...any) {
logger.Println(DebugLevel, in...)
}
// Debugf calls print after formatting the string with Debug level.
func (l *Logger) Debugf(format string, v ...any) {
l.Println(ErrLevel, fmt.Sprintf(format, v...))
func (logger *Logger) Debugf(format string, in ...any) {
logger.Println(DebugLevel, fmt.Sprintf(format, in...))
}
// Info calls print directly with Info level.
func (l *Logger) Info(v ...any) {
l.Println(InfoLevel, v...)
func (logger *Logger) Info(in ...any) {
logger.Println(InfoLevel, in...)
}
// Infof calls print after formatting the string with Info level.
func (l *Logger) Infof(format string, v ...any) {
l.Println(ErrLevel, fmt.Sprintf(format, v...))
func (logger *Logger) Infof(format string, in ...any) {
logger.Println(InfoLevel, fmt.Sprintf(format, in...))
}
// Warn calls print directly with Warn level.
func (l *Logger) Warn(v ...any) {
l.Println(WarnLevel, v...)
func (logger *Logger) Warn(in ...any) {
logger.Println(WarnLevel, in...)
}
// Warnf calls print after formatting the string with Warn level.
func (l *Logger) Warnf(format string, v ...any) {
l.Println(WarnLevel, fmt.Sprintf(format, v...))
func (logger *Logger) Warnf(format string, in ...any) {
logger.Println(WarnLevel, fmt.Sprintf(format, in...))
}
// Error calls print directly with Error level.
func (l *Logger) Error(v ...any) {
l.Println(ErrLevel, v...)
func (logger *Logger) Error(in ...any) {
logger.Println(ErrLevel, in...)
}
// Errorf calls print after formatting the string with Error level.
func (l *Logger) Errorf(format string, v ...any) {
l.Println(ErrLevel, fmt.Sprintf(format, v...))
func (logger *Logger) Errorf(format string, in ...any) {
logger.Println(ErrLevel, fmt.Sprintf(format, in...))
}

View File

@ -7,7 +7,7 @@ import (
"testing"
"time"
"git.froth.zone/sam/awl/logawl"
"dns.froth.zone/awl/pkg/logawl"
"gotest.tools/v3/assert"
)

313
pkg/query/print.go Normal file
View File

@ -0,0 +1,313 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
"time"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"golang.org/x/net/idna"
"gopkg.in/yaml.v3"
)
// ToString turns the response into something that looks a lot like dig
//
// Much of this is taken from https://github.com/miekg/dns/blob/master/msg.go#L900
func ToString(res util.Response, opts *util.Options) (s string, err error) {
if res.DNS == nil {
return "<nil> MsgHdr", errNoMessage
}
var opt *dns.OPT
if !opts.Short {
if opts.Display.Comments {
s += res.DNS.MsgHdr.String() + " "
s += "QUERY: " + strconv.Itoa(len(res.DNS.Question)) + ", "
s += "ANSWER: " + strconv.Itoa(len(res.DNS.Answer)) + ", "
s += "AUTHORITY: " + strconv.Itoa(len(res.DNS.Ns)) + ", "
s += "ADDITIONAL: " + strconv.Itoa(len(res.DNS.Extra)) + "\n"
opt = res.DNS.IsEdns0()
if opt != nil && opts.Display.Opt {
// OPT PSEUDOSECTION
s += opt.String() + "\n"
}
}
if opts.Display.Question {
if len(res.DNS.Question) > 0 {
if opts.Display.Comments {
s += "\n;; QUESTION SECTION:\n"
}
for _, r := range res.DNS.Question {
str, err := stringParse(r.String(), false, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
if opts.Display.Answer {
if len(res.DNS.Answer) > 0 {
if opts.Display.Comments {
s += "\n;; ANSWER SECTION:\n"
}
for _, r := range res.DNS.Answer {
if r != nil {
str, err := stringParse(r.String(), true, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
}
if opts.Display.Authority {
if len(res.DNS.Ns) > 0 {
if opts.Display.Comments {
s += "\n;; AUTHORITY SECTION:\n"
}
for _, r := range res.DNS.Ns {
if r != nil {
str, err := stringParse(r.String(), true, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
}
if opts.Display.Additional {
if len(res.DNS.Extra) > 0 && (opt == nil || len(res.DNS.Extra) > 1) {
if opts.Display.Comments {
s += "\n;; ADDITIONAL SECTION:\n"
}
for _, r := range res.DNS.Extra {
if r != nil && r.Header().Rrtype != dns.TypeOPT {
str, err := stringParse(r.String(), true, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
}
if opts.Display.Statistics {
s += "\n;; Query time: " + res.RTT.String()
s += "\n;; SERVER: " + opts.Request.Server + serverExtra(opts)
s += "\n;; WHEN: " + time.Now().Format(time.RFC1123Z)
s += "\n;; MSG SIZE rcvd: " + strconv.Itoa(res.DNS.Len()) + "\n"
}
} else {
// Print just the responses, nothing else
for i, resp := range res.DNS.Answer {
temp := strings.Split(resp.String(), "\t")
s += temp[len(temp)-1]
if opts.Identify {
s += " from server " + opts.Request.Server + " in " + res.RTT.String()
}
// Don't print newline on last line
if i != len(res.DNS.Answer)-1 {
s += "\n"
}
}
}
return
}
func serverExtra(opts *util.Options) string {
switch {
case opts.TCP:
return " (TCP)"
case opts.TLS:
return " (TLS)"
case opts.HTTPS, opts.DNSCrypt:
return ""
case opts.QUIC:
return " (QUIC)"
default:
return " (UDP)"
}
}
// stringParse edits the raw responses to user requests.
func stringParse(str string, isAns bool, opts *util.Options) (string, error) {
split := strings.Split(str, "\t")
// Make edits if so requested
// TODO: make less ew?
// This exists because the question section should be left alone EXCEPT for punycode.
if isAns {
if !opts.Display.TTL {
// Remove from existence
split = append(split[:1], split[2:]...)
}
if !opts.Display.ShowClass {
// Position depends on if the TTL is there or not.
if opts.Display.TTL {
split = append(split[:2], split[3:]...)
} else {
split = append(split[:1], split[2:]...)
}
}
if opts.Display.TTL && opts.Display.HumanTTL {
ttl, _ := strconv.Atoi(split[1])
split[1] = (time.Duration(ttl) * time.Second).String()
}
}
if opts.Display.UcodeTranslate {
var (
err error
semi string
)
if strings.HasPrefix(split[0], ";") {
split[0] = strings.TrimPrefix(split[0], ";")
semi = ";"
}
split[0], err = idna.ToUnicode(split[0])
if err != nil {
return "", fmt.Errorf("punycode: %w", err)
}
split[0] = semi + split[0]
}
return strings.Join(split, "\t"), nil
}
// PrintSpecial is for printing as JSON, XML or YAML.
// As of now JSON and XML use the stdlib version.
func PrintSpecial(res util.Response, opts *util.Options) (string, error) {
formatted, err := MakePrintable(res, opts)
if err != nil {
return "", err
}
switch {
case opts.JSON:
opts.Logger.Info("Printing as JSON")
json, err := json.MarshalIndent(formatted, " ", " ")
return string(json), err
case opts.XML:
opts.Logger.Info("Printing as XML")
xml, err := xml.MarshalIndent(formatted, " ", " ")
return string(xml), err
case opts.YAML:
opts.Logger.Info("Printing as YAML")
yaml, err := yaml.Marshal(formatted)
return string(yaml), err
default:
return "", errInvalidFormat
}
}
// MakePrintable takes a DNS message and makes it nicer to be printed as JSON,YAML,
// and XML. Little is changed beyond naming.
func MakePrintable(res util.Response, opts *util.Options) (*Message, error) {
var (
err error
msg = res.DNS
)
// The things I do for compatibility
ret := &Message{
DateString: time.Now().Format(time.RFC3339),
DateSeconds: time.Now().Unix(),
MsgSize: res.DNS.Len(),
ID: msg.Id,
Opcode: msg.Opcode,
Response: msg.Response,
Authoritative: msg.Authoritative,
Truncated: msg.Truncated,
RecursionDesired: msg.RecursionDesired,
RecursionAvailable: msg.RecursionAvailable,
AuthenticatedData: msg.AuthenticatedData,
CheckingDisabled: msg.CheckingDisabled,
Zero: msg.Zero,
QdCount: len(msg.Question),
AnCount: len(msg.Answer),
NsCount: len(msg.Ns),
ArCount: len(msg.Extra),
}
opt := msg.IsEdns0()
if opt != nil && opts.Display.Opt {
ret.EDNS0, err = ret.ParseOpt(msg.Rcode, *opt)
if err != nil {
return nil, fmt.Errorf("edns print: %w", err)
}
}
if opts.Display.Question {
err = ret.displayQuestion(msg, opts, opt)
if err != nil {
return nil, fmt.Errorf("unable to display questions: %w", err)
}
}
if opts.Display.Answer {
err = ret.displayAnswers(msg, opts, opt)
if err != nil {
return nil, fmt.Errorf("unable to display answers: %w", err)
}
}
if opts.Display.Authority {
err = ret.displayAuthority(msg, opts, opt)
if err != nil {
return nil, fmt.Errorf("unable to display authority: %w", err)
}
}
if opts.Display.Additional {
err = ret.displayAdditional(msg, opts, opt)
if err != nil {
return nil, fmt.Errorf("unable to display additional: %w", err)
}
}
return ret, nil
}
var errInvalidFormat = errors.New("this should never happen")

View File

@ -5,8 +5,8 @@ package query_test
import (
"testing"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
"dns.froth.zone/awl/pkg/query"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
@ -14,7 +14,7 @@ import (
func TestRealPrint(t *testing.T) {
t.Parallel()
opts := []util.Options{
opts := []*util.Options{
{
Logger: util.InitLogger(0),
@ -38,11 +38,12 @@ func TestRealPrint(t *testing.T) {
ShowQuery: true,
},
Request: util.Request{
Server: "a.gtld-servers.net",
Port: 53,
Type: dns.StringToType["NS"],
Class: 1,
Name: "google.com.",
Server: "a.gtld-servers.net",
Port: 53,
Type: dns.StringToType["NS"],
Class: 1,
Name: "google.com.",
Retries: 3,
},
EDNS: util.EDNS{
EnableEDNS: false,
@ -77,8 +78,7 @@ func TestRealPrint(t *testing.T) {
Type: dns.StringToType["NS"],
Class: 1,
Name: "google.com.",
Timeout: 0,
Retries: 0,
Retries: 3,
},
EDNS: util.EDNS{
EnableEDNS: false,
@ -99,7 +99,7 @@ func TestRealPrint(t *testing.T) {
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: false,
UcodeTranslate: true,
TTL: true,
HumanTTL: true,
ShowQuery: true,
@ -110,8 +110,7 @@ func TestRealPrint(t *testing.T) {
Type: dns.StringToType["NS"],
Class: 1,
Name: "freecumextremist.com.",
Timeout: 0,
Retries: 0,
Retries: 3,
},
EDNS: util.EDNS{
EnableEDNS: false,
@ -137,11 +136,12 @@ func TestRealPrint(t *testing.T) {
ShowQuery: true,
},
Request: util.Request{
Server: "dns.google",
Port: 853,
Type: dns.StringToType["NS"],
Class: 1,
Name: "freecumextremist.com.",
Server: "dns.google",
Port: 853,
Type: dns.StringToType["NS"],
Class: 1,
Name: "freecumextremist.com.",
Retries: 3,
},
},
{
@ -172,8 +172,7 @@ func TestRealPrint(t *testing.T) {
Type: dns.StringToType["A"],
Class: 1,
Name: "froth.zone.",
Timeout: 0,
Retries: 0,
Retries: 3,
},
EDNS: util.EDNS{
EnableEDNS: true,
@ -188,16 +187,26 @@ func TestRealPrint(t *testing.T) {
t.Run("", func(t *testing.T) {
t.Parallel()
resp, err := query.CreateQuery(test)
var (
res util.Response
err error
)
for i := 0; i <= test.Request.Retries; i++ {
res, err = query.CreateQuery(test)
if err == nil {
break
}
}
assert.NilError(t, err)
if test.JSON || test.XML || test.YAML {
str := ""
str, err = query.PrintSpecial(resp, test)
str, err = query.PrintSpecial(res, test)
assert.NilError(t, err)
assert.Assert(t, str != "")
}
str, err := query.ToString(resp, test)
str, err := query.ToString(res, test)
assert.NilError(t, err)
assert.Assert(t, str != "")
})
@ -207,14 +216,14 @@ func TestRealPrint(t *testing.T) {
func TestBadFormat(t *testing.T) {
t.Parallel()
_, err := query.PrintSpecial(util.Response{DNS: new(dns.Msg)}, util.Options{})
_, err := query.PrintSpecial(util.Response{DNS: new(dns.Msg)}, new(util.Options))
assert.ErrorContains(t, err, "never happen")
}
func TestEmpty(t *testing.T) {
t.Parallel()
str, err := query.ToString(util.Response{}, util.Options{})
str, err := query.ToString(util.Response{}, new(util.Options))
assert.Error(t, err, "no message")
assert.Assert(t, str == "<nil> MsgHdr")

View File

@ -6,19 +6,15 @@ import (
"fmt"
"strconv"
"git.froth.zone/sam/awl/util"
"dns.froth.zone/awl/pkg/resolvers"
"dns.froth.zone/awl/pkg/util"
"github.com/dchest/uniuri"
"github.com/miekg/dns"
)
const (
tcp = "tcp"
udp = "udp"
)
// CreateQuery creates a DNS query from the options given.
// It sets query flags and EDNS flags from the respective options.
func CreateQuery(opts util.Options) (util.Response, error) {
func CreateQuery(opts *util.Options) (util.Response, error) {
req := new(dns.Msg)
req.SetQuestion(opts.Request.Name, opts.Request.Type)
req.Question[0].Qclass = opts.Request.Class
@ -35,64 +31,64 @@ func CreateQuery(opts util.Options) (util.Response, error) {
// EDNS time :)
if opts.EDNS.EnableEDNS {
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
edns := new(dns.OPT)
edns.Hdr.Name = "."
edns.Hdr.Rrtype = dns.TypeOPT
o.SetVersion(opts.EDNS.Version)
edns.SetVersion(opts.EDNS.Version)
if opts.EDNS.Cookie {
e := new(dns.EDNS0_COOKIE)
e.Code = dns.EDNS0COOKIE
e.Cookie = uniuri.NewLenChars(8, []byte("1234567890abcdef"))
o.Option = append(o.Option, e)
cookie := new(dns.EDNS0_COOKIE)
cookie.Code = dns.EDNS0COOKIE
cookie.Cookie = uniuri.NewLenChars(16, []byte("1234567890abcdef"))
edns.Option = append(edns.Option, cookie)
opts.Logger.Info("Setting EDNS cookie to", e.Cookie)
opts.Logger.Info("Setting EDNS cookie to", cookie.Cookie)
}
if opts.EDNS.Expire {
o.Option = append(o.Option, new(dns.EDNS0_EXPIRE))
edns.Option = append(edns.Option, new(dns.EDNS0_EXPIRE))
opts.Logger.Info("Setting EDNS Expire option")
}
if opts.EDNS.KeepOpen {
o.Option = append(o.Option, new(dns.EDNS0_TCP_KEEPALIVE))
edns.Option = append(edns.Option, new(dns.EDNS0_TCP_KEEPALIVE))
opts.Logger.Info("Setting EDNS TCP Keepalive option")
}
if opts.EDNS.Nsid {
o.Option = append(o.Option, new(dns.EDNS0_NSID))
edns.Option = append(edns.Option, new(dns.EDNS0_NSID))
opts.Logger.Info("Setting EDNS NSID option")
}
if opts.EDNS.Padding {
o.Option = append(o.Option, new(dns.EDNS0_PADDING))
edns.Option = append(edns.Option, new(dns.EDNS0_PADDING))
opts.Logger.Info("Setting EDNS padding")
}
o.SetUDPSize(opts.EDNS.BufSize)
edns.SetUDPSize(opts.EDNS.BufSize)
opts.Logger.Info("EDNS UDP buffer set to", opts.EDNS.BufSize)
o.SetZ(opts.EDNS.ZFlag)
edns.SetZ(opts.EDNS.ZFlag)
opts.Logger.Info("EDNS Z flag set to", opts.EDNS.ZFlag)
if opts.EDNS.DNSSEC {
o.SetDo()
edns.SetDo()
opts.Logger.Info("EDNS DNSSEC OK set")
}
if opts.EDNS.Subnet.Address != nil {
o.Option = append(o.Option, &opts.EDNS.Subnet)
edns.Option = append(edns.Option, &opts.EDNS.Subnet)
}
req.Extra = append(req.Extra, o)
req.Extra = append(req.Extra, edns)
} else if opts.EDNS.DNSSEC {
req.SetEdns0(1232, true)
opts.Logger.Warn("DNSSEC implies EDNS, EDNS enabled")
@ -137,9 +133,9 @@ func CreateQuery(opts util.Options) (util.Response, error) {
}
}
resolver, err := LoadResolver(opts)
resolver, err := resolvers.LoadResolver(opts)
if err != nil {
return util.Response{}, err
return util.Response{}, fmt.Errorf("unable to load resolvers: %w", err)
}
opts.Logger.Info("Query successfully loaded")

157
pkg/query/query_test.go Normal file
View File

@ -0,0 +1,157 @@
// SPDX-License-Identifier: BSD-3-Clause
package query_test
import (
"testing"
"time"
"dns.froth.zone/awl/pkg/query"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestCreateQ(t *testing.T) {
t.Parallel()
//nolint:govet // I could not be assed to refactor this, and it is only for tests
tests := []struct {
name string
opts *util.Options
}{
{
"1",
&util.Options{
Logger: util.InitLogger(0),
HeaderFlags: util.HeaderFlags{
Z: true,
},
YAML: true,
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeA,
Name: "example.com.",
Retries: 3,
},
Display: util.Display{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
ShowQuery: true,
},
EDNS: util.EDNS{
ZFlag: 1,
BufSize: 1500,
EnableEDNS: true,
Cookie: true,
DNSSEC: true,
Expire: true,
KeepOpen: true,
Nsid: true,
Padding: true,
Version: 0,
},
},
},
{
"2",
&util.Options{
Logger: util.InitLogger(0),
HeaderFlags: util.HeaderFlags{
Z: true,
},
XML: true,
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeA,
Name: "example.com.",
Retries: 3,
},
Display: util.Display{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
ShowQuery: true,
},
},
},
{
"3",
&util.Options{
Logger: util.InitLogger(0),
JSON: true,
QUIC: true,
Request: util.Request{
Server: "dns.froth.zone",
Port: 853,
Type: dns.TypeA,
Name: "example.com.",
Retries: 3,
Timeout: time.Second,
},
Display: util.Display{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
ShowQuery: true,
},
EDNS: util.EDNS{
EnableEDNS: true,
DNSSEC: true,
Cookie: true,
Expire: true,
Nsid: true,
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
var (
res util.Response
err error
)
for i := 0; i <= test.opts.Request.Retries; i++ {
res, err = query.CreateQuery(test.opts)
if err == nil {
break
}
}
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
str, err := query.PrintSpecial(res, test.opts)
assert.NilError(t, err)
assert.Assert(t, str != "")
str, err = query.ToString(res, test.opts)
assert.NilError(t, err)
assert.Assert(t, str != "")
})
}
}

116
pkg/query/struct.go Normal file
View File

@ -0,0 +1,116 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"errors"
)
// Message is for overall DNS responses.
//
//nolint:govet,tagliatelle // Better looking output is worth a few bytes.
type Message struct {
DateString string `json:"dateString,omitempty" xml:"dateString,omitempty" yaml:"dateString,omitempty"`
DateSeconds int64 `json:"dateSeconds,omitempty" xml:"dateSeconds,omitempty" yaml:"dateSeconds,omitempty"`
MsgSize int `json:"msgLength,omitempty" xml:"msgSize,omitempty" yaml:"msgSize,omitempty"`
ID uint16 `json:"ID" xml:"ID" yaml:"ID" example:"12"`
Opcode int `json:"opcode" xml:"opcode" yaml:"opcode" example:"QUERY"`
Response bool `json:"QR" xml:"QR" yaml:"QR" example:"true"`
Authoritative bool `json:"AA" xml:"AA" yaml:"AA" example:"false"`
Truncated bool `json:"TC" xml:"TC" yaml:"TC" example:"false"`
RecursionDesired bool `json:"RD" xml:"RD" yaml:"RD" example:"true"`
RecursionAvailable bool `json:"RA" xml:"RA" yaml:"RA" example:"true"`
AuthenticatedData bool `json:"AD" xml:"AD" yaml:"AD" example:"false"`
CheckingDisabled bool `json:"CD" xml:"CD" yaml:"CD" example:"false"`
Zero bool `json:"Z" xml:"Z" yaml:"Z" example:"false"`
QdCount int `json:"QDCOUNT" xml:"QDCOUNT" yaml:"QDCOUNT" example:"0"`
AnCount int `json:"ANCOUNT" xml:"ANCOUNT" yaml:"ANCOUNT" example:"0"`
NsCount int `json:"NSCOUNT" xml:"NSCOUNT" yaml:"NSCOUNT" example:"0"`
ArCount int `json:"ARCOUNT" xml:"ARCOUNT" yaml:"ARCOUNT" example:"0"`
Name string `json:"QNAME,omitempty" xml:"QNAME,omitempty" yaml:"QNAME,omitempty" example:"localhost"`
Type uint16 `json:"QTYPE,omitempty" xml:"QTYPE,omitempty" yaml:"QTYPE,omitempty" example:"IN"`
TypeName string `json:"QTYPEname,omitempty" xml:"QTYPEname,omitempty" yaml:"QTYPEname,omitempty" example:"IN"`
Class uint16 `json:"QCLASS,omitempty" xml:"QCLASS,omitempty" yaml:"QCLASS,omitempty" example:"A"`
ClassName string `json:"QCLASSname,omitempty" xml:"QCLASSname,omitempty" yaml:"QCLASSname,omitempty" example:"1"`
EDNS0 EDNS0 `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
// Answer Section
AnswerRRs []Answer `json:"answersRRs,omitempty" xml:"answersRRs,omitempty" yaml:"answersRRs,omitempty" example:"false"`
AuthoritativeRRs []Answer `json:"authorityRRs,omitempty" xml:"authorityRRs,omitempty" yaml:"authorityRRs,omitempty" example:"false"`
AdditionalRRs []Answer `json:"additionalRRs,omitempty" xml:"additionalRRs,omitempty" yaml:"additionalRRs,omitempty" example:"false"`
}
// Answer is for DNS Resource Headers.
//
//nolint:govet,tagliatelle
type Answer struct {
Name string `json:"NAME,omitempty" xml:"NAME,omitempty" yaml:"NAME,omitempty" example:"127.0.0.1"`
Type uint16 `json:"TYPE,omitempty" xml:"TYPE,omitempty" yaml:"TYPE,omitempty" example:"1"`
TypeName string `json:"TYPEname,omitempty" xml:"TYPEname,omitempty" yaml:"TYPEname,omitempty" example:"A"`
Class uint16 `json:"CLASS,omitempty" xml:"CLASS,omitempty" yaml:"CLASS,omitempty" example:"1"`
ClassName string `json:"CLASSname,omitempty" xml:"CLASSname,omitempty" yaml:"CLASSname,omitempty" example:"IN"`
TTL any `json:"TTL,omitempty" xml:"TTL,omitempty" yaml:"TTL,omitempty" example:"0ms"`
Value string `json:"rdata,omitempty" xml:"rdata,omitempty" yaml:"rdata,omitempty"`
Rdlength uint16 `json:"RDLENGTH,omitempty" xml:"RDLENGTH,omitempty" yaml:"RDLENGTH,omitempty"`
Rdhex string `json:"RDATAHEX,omitempty" xml:"RDATAHEX,omitempty" yaml:"RDATAHEX,omitempty"`
}
// EDNS0 is for all EDNS options.
//
// RFC: https://datatracker.ietf.org/docs/draft-peltan-edns-presentation-format/
//
//nolint:govet,tagliatelle
type EDNS0 struct {
Flags []string `json:"FLAGS" xml:"FLAGS" yaml:"FLAGS"`
Rcode string `json:"RCODE" xml:"RCODE" yaml:"RCODE"`
PayloadSize uint16 `json:"UDPSIZE" xml:"UDPSIZE" yaml:"UDPSIZE"`
LLQ *EdnsLLQ `json:"LLQ,omitempty" xml:"LLQ,omitempty" yaml:"LLQ,omitempty"`
NsidHex string `json:"NSIDHEX,omitempty" xml:"NSIDHEX,omitempty" yaml:"NSIDHEX,omitempty"`
Nsid string `json:"NSID,omitempty" xml:"NSID,omitempty" yaml:"NSID,omitempty"`
Dau []uint8 `json:"DAU,omitempty" xml:"DAU,omitempty" yaml:"DAU,omitempty"`
Dhu []uint8 `json:"DHU,omitempty" xml:"DHU,omitempty" yaml:"DHU,omitempty"`
N3u []uint8 `json:"N3U,omitempty" xml:"N3U,omitempty" yaml:"N3U,omitempty"`
Subnet *EDNSSubnet `json:"ECS,omitempty" xml:"ECS,omitempty" yaml:"ECS,omitempty"`
Expire uint32 `json:"EXPIRE,omitempty" xml:"EXPIRE,omitempty" yaml:"EXPIRE,omitempty"`
Cookie []string `json:"COOKIE,omitempty" xml:"COOKIE,omitempty" yaml:"COOKIE,omitempty"`
KeepAlive uint16 `json:"KEEPALIVE,omitempty" xml:"KEEPALIVE,omitempty" yaml:"KEEPALIVE,omitempty"`
Padding string `json:"PADDING,omitempty" xml:"PADDING,omitempty" yaml:"PADDING,omitempty"`
Chain string `json:"CHAIN,omitempty" xml:"CHAIN,omitempty" yaml:"CHAIN,omitempty"`
EDE *EDNSErr `json:"EDE,omitempty" xml:"EDE,omitempty" yaml:"EDE,omitempty"`
}
// EdnsLLQ is for Long-lived queries.
//
//nolint:tagliatelle
type EdnsLLQ struct {
Version uint16 `json:"LLQ-VERSION" xml:"LLQ-VERSION" yaml:"LLQ-VERSION"`
Opcode uint16 `json:"LLQ-OPCODE" xml:"LLQ-OPCODE" yaml:"LLQ-OPCODE"`
Error uint16 `json:"LLQ-ERROR" xml:"LLQ-ERROR" yaml:"LLQ-ERROR"`
ID uint64 `json:"LLQ-ID" xml:"LLQ-ID" yaml:"LLQ-ID"`
Lease uint32 `json:"LLQ-LEASE" xml:"LLQ-LEASE" yaml:"LLQ-LEASE"`
}
// EDNSSubnet is for EDNS subnet options,
//
//nolint:govet,tagliatelle
type EDNSSubnet struct {
Family uint16 `json:"FAMILY" xml:"FAMILY" yaml:"FAMILY"`
IP string
Source uint8 `json:"SOURCE" xml:"SOURCE" yaml:"SOURCE"`
Scope uint8 `json:"SCOPE,omitempty" xml:"SCOPE,omitempty" yaml:"SCOPE,omitempty"`
}
// EDNSErr is for EDE codes
//
//nolint:govet,tagliatelle
type EDNSErr struct {
Code uint16 `json:"INFO-CODE" xml:"INFO-CODE" yaml:"INFO-CODE"`
Purpose string
Text string `json:"EXTRA-TEXT,omitempty" xml:"EXTRA-TEXT,omitempty" yaml:"EXTRA-TEXT,omitempty"`
}
var errNoMessage = errors.New("no message")

258
pkg/query/util.go Normal file
View File

@ -0,0 +1,258 @@
package query
import (
"encoding/hex"
"fmt"
"strings"
"time"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"golang.org/x/net/idna"
)
func (message *Message) displayQuestion(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error {
var (
name string
err error
)
for _, question := range msg.Question {
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(question.Name)
if err != nil {
return fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = question.Name
}
message.Name = name
message.Type = question.Qtype
message.TypeName = dns.TypeToString[question.Qtype]
message.Class = question.Qclass
message.ClassName = dns.ClassToString[question.Qclass]
}
return nil
}
func (message *Message) displayAnswers(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error {
var (
ttl any
name string
err error
)
for _, answer := range msg.Answer {
temp := strings.Split(answer.String(), "\t")
if opts.Display.TTL {
if opts.Display.HumanTTL {
ttl = (time.Duration(answer.Header().Ttl) * time.Second).String()
} else {
ttl = answer.Header().Ttl
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(answer.Header().Name)
if err != nil {
return fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = answer.Header().Name
}
message.AnswerRRs = append(message.AnswerRRs, Answer{
Name: name,
ClassName: dns.ClassToString[answer.Header().Class],
Class: answer.Header().Class,
TypeName: dns.TypeToString[answer.Header().Rrtype],
Type: answer.Header().Rrtype,
Rdlength: answer.Header().Rdlength,
TTL: ttl,
Value: temp[len(temp)-1],
})
}
return nil
}
func (message *Message) displayAuthority(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error {
var (
ttl any
name string
err error
)
for _, ns := range msg.Ns {
temp := strings.Split(ns.String(), "\t")
if opts.Display.TTL {
if opts.Display.HumanTTL {
ttl = (time.Duration(ns.Header().Ttl) * time.Second).String()
} else {
ttl = ns.Header().Ttl
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(ns.Header().Name)
if err != nil {
return fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = ns.Header().Name
}
message.AuthoritativeRRs = append(message.AuthoritativeRRs, Answer{
Name: name,
TypeName: dns.TypeToString[ns.Header().Rrtype],
Type: ns.Header().Rrtype,
Class: ns.Header().Class,
ClassName: dns.ClassToString[ns.Header().Class],
Rdlength: ns.Header().Rdlength,
TTL: ttl,
Value: temp[len(temp)-1],
})
}
return nil
}
func (message *Message) displayAdditional(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error {
var (
ttl any
name string
err error
)
for _, additional := range msg.Extra {
if additional.Header().Rrtype == dns.StringToType["OPT"] {
continue
} else {
temp := strings.Split(additional.String(), "\t")
if opts.Display.TTL {
if opts.Display.HumanTTL {
ttl = (time.Duration(additional.Header().Ttl) * time.Second).String()
} else {
ttl = additional.Header().Ttl
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(additional.Header().Name)
if err != nil {
return fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = additional.Header().Name
}
message.AdditionalRRs = append(message.AdditionalRRs, Answer{
Name: name,
TypeName: dns.TypeToString[additional.Header().Rrtype],
Type: additional.Header().Rrtype,
Class: additional.Header().Class,
ClassName: dns.ClassToString[additional.Header().Class],
Rdlength: additional.Header().Rdlength,
TTL: ttl,
Value: temp[len(temp)-1],
})
}
}
return nil
}
// ParseOpt parses opts.
func (message *Message) ParseOpt(rcode int, rr dns.OPT) (ret EDNS0, err error) {
ret.Rcode = dns.RcodeToString[rcode]
// Most of this is taken from https://github.com/miekg/dns/blob/master/edns.go#L76
if rr.Do() {
ret.Flags = append(ret.Flags, "DO")
}
for i := uint32(1); i <= 0x7FFF; i <<= 1 {
if rr.Hdr.Ttl&i != 0 {
ret.Flags = append(ret.Flags, fmt.Sprintf("BIT%d", i))
}
}
ret.PayloadSize = rr.UDPSize()
for _, opt := range rr.Option {
switch opt := opt.(type) {
case *dns.EDNS0_NSID:
str := opt.String()
hex, err := hex.DecodeString(str)
if err != nil {
return ret, fmt.Errorf("%w", err)
}
ret.NsidHex = string(hex)
ret.Nsid = str
case *dns.EDNS0_SUBNET:
ret.Subnet = &EDNSSubnet{
Source: opt.SourceNetmask,
Family: opt.Family,
}
// 1: IPv4 2: IPv6
if ret.Subnet.Family <= 2 {
ret.Subnet.IP = opt.Address.String()
} else {
ret.Subnet.IP = hex.EncodeToString([]byte(opt.Address))
}
if opt.SourceScope != 0 {
ret.Subnet.Scope = opt.SourceScope
}
case *dns.EDNS0_COOKIE:
ret.Cookie = append(ret.Cookie, opt.String())
case *dns.EDNS0_EXPIRE:
ret.Expire = opt.Expire
case *dns.EDNS0_TCP_KEEPALIVE:
ret.KeepAlive = opt.Timeout
case *dns.EDNS0_LLQ:
ret.LLQ = &EdnsLLQ{
Version: opt.Version,
Opcode: opt.Opcode,
Error: opt.Error,
ID: opt.Id,
Lease: opt.LeaseLife,
}
case *dns.EDNS0_DAU:
ret.Dau = opt.AlgCode
case *dns.EDNS0_DHU:
ret.Dhu = opt.AlgCode
case *dns.EDNS0_N3U:
ret.N3u = opt.AlgCode
case *dns.EDNS0_PADDING:
ret.Padding = string(opt.Padding)
case *dns.EDNS0_EDE:
ret.EDE = &EDNSErr{
Code: opt.InfoCode,
Purpose: dns.ExtendedErrorCodeToString[opt.InfoCode],
Text: opt.ExtraText,
}
}
}
return ret, nil
}

64
pkg/resolvers/DNSCrypt.go Normal file
View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: BSD-3-Clause
package resolvers
import (
"fmt"
"time"
"dns.froth.zone/awl/pkg/util"
"dns.froth.zone/dnscrypt"
"github.com/miekg/dns"
)
// DNSCryptResolver is for making DNSCrypt queries.
type DNSCryptResolver struct {
opts *util.Options
}
var _ Resolver = (*DNSCryptResolver)(nil)
// LookUp performs a DNS query.
func (resolver *DNSCryptResolver) LookUp(msg *dns.Msg) (resp util.Response, err error) {
client := dnscrypt.Client{
Timeout: resolver.opts.Request.Timeout,
UDPSize: 1232,
}
if resolver.opts.TCP || resolver.opts.TLS {
client.Net = tcp
} else {
client.Net = udp
}
switch {
case resolver.opts.IPv4:
client.Net += "4"
case resolver.opts.IPv6:
client.Net += "6"
}
resolver.opts.Logger.Debug("Using", client.Net, "for making the request")
resolverInf, err := client.Dial(resolver.opts.Request.Server)
if err != nil {
return resp, fmt.Errorf("dnscrypt: dial: %w", err)
}
now := time.Now()
res, err := client.Exchange(msg, resolverInf)
rtt := time.Since(now)
if err != nil {
return resp, fmt.Errorf("dnscrypt: exchange: %w", err)
}
resp = util.Response{
DNS: res,
RTT: rtt,
}
resolver.opts.Logger.Info("Request successful")
return
}

View File

@ -0,0 +1,93 @@
// SPDX-License-Identifier: BSD-3-Clause
package resolvers_test
import (
"errors"
"testing"
"dns.froth.zone/awl/pkg/query"
"dns.froth.zone/awl/pkg/util"
"dns.froth.zone/dnscrypt"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestDNSCrypt(t *testing.T) {
t.Parallel()
//nolint:govet // I could not be assed to refactor this, and it is only for tests
tests := []struct {
name string
opts *util.Options
}{
{
"Valid",
&util.Options{
Logger: util.InitLogger(0),
DNSCrypt: true,
Request: util.Request{
Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
Type: dns.TypeA,
Name: "example.com.",
Retries: 3,
},
},
},
{
"Valid (TCP)",
&util.Options{
Logger: util.InitLogger(0),
DNSCrypt: true,
TCP: true,
IPv4: true,
Request: util.Request{
Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
Type: dns.TypeAAAA,
Name: "example.com.",
Retries: 3,
},
},
},
{
"Invalid",
&util.Options{
Logger: util.InitLogger(0),
DNSCrypt: true,
TCP: true,
IPv4: true,
Request: util.Request{
Server: "QMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
Type: dns.TypeAAAA,
Name: "example.com.",
Retries: 0,
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
var (
res util.Response
err error
)
for i := 0; i <= test.opts.Request.Retries; i++ {
res, err = query.CreateQuery(test.opts)
if err == nil || errors.Is(err, dnscrypt.ErrInvalidDNSStamp) {
break
}
}
if err == nil {
assert.Assert(t, res != util.Response{})
} else {
assert.ErrorContains(t, err, "unsupported stamp")
}
})
}
}

104
pkg/resolvers/HTTPS.go Normal file
View File

@ -0,0 +1,104 @@
// SPDX-License-Identifier: BSD-3-Clause
package resolvers
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"net/http"
"time"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
)
// HTTPSResolver is for DNS-over-HTTPS queries.
type HTTPSResolver struct {
opts *util.Options
client http.Client
}
var _ Resolver = (*HTTPSResolver)(nil)
// LookUp performs a DNS query.
func (resolver *HTTPSResolver) LookUp(msg *dns.Msg) (resp util.Response, err error) {
resolver.client = http.Client{
Timeout: resolver.opts.Request.Timeout,
Transport: &http.Transport{
MaxConnsPerHost: 1,
MaxIdleConns: 1,
MaxIdleConnsPerHost: 1,
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
//nolint:gosec // This is intentional if the user requests it
InsecureSkipVerify: resolver.opts.TLSNoVerify,
ServerName: resolver.opts.TLSHost,
},
},
}
buf, err := msg.Pack()
if err != nil {
return resp, fmt.Errorf("doh: packing: %w", err)
}
resolver.opts.Logger.Debug("https: sending HTTPS request")
var method string
if resolver.opts.HTTPSOptions.Get {
method = "GET"
} else {
method = "POST"
}
req, err := http.NewRequest(method, resolver.opts.Request.Server, bytes.NewBuffer(buf))
if err != nil {
return resp, fmt.Errorf("doh: request creation: %w", err)
}
req.Header.Set("Content-Type", "application/dns-message")
req.Header.Set("Accept", "application/dns-message")
now := time.Now()
res, err := resolver.client.Do(req)
resp.RTT = time.Since(now)
if err != nil {
// overwrite RTT or else tests will fail
resp.RTT = 0
return resp, fmt.Errorf("doh: HTTP request: %w", err)
}
if res.StatusCode != http.StatusOK {
// overwrite RTT or else tests will fail
resp.RTT = 0
return resp, &util.ErrHTTPStatus{Code: res.StatusCode}
}
resolver.opts.Logger.Debug("https: reading response")
fullRes, err := io.ReadAll(res.Body)
if err != nil {
return resp, fmt.Errorf("doh: body read: %w", err)
}
err = res.Body.Close()
if err != nil {
return resp, fmt.Errorf("doh: body close: %w", err)
}
resolver.opts.Logger.Debug("https: unpacking response")
resp.DNS = &dns.Msg{}
err = resp.DNS.Unpack(fullRes)
if err != nil {
return resp, fmt.Errorf("doh: dns message unpack: %w", err)
}
return resp, nil
}

102
pkg/resolvers/HTTPS_test.go Normal file
View File

@ -0,0 +1,102 @@
// SPDX-License-Identifier: BSD-3-Clause
package resolvers_test
import (
"errors"
"testing"
"dns.froth.zone/awl/pkg/query"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestHTTPS(t *testing.T) {
t.Parallel()
//nolint:govet // I could not be assed to refactor this, and it is only for tests
tests := []struct {
name string
opts *util.Options
}{
{
"Good",
&util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "https://dns9.quad9.net/dns-query",
Type: dns.TypeA,
Name: "git.froth.zone.",
Retries: 3,
},
},
},
{
"404",
&util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "https://dns9.quad9.net/dns",
Type: dns.TypeA,
Name: "git.froth.zone.",
},
},
},
{
"Bad request domain",
&util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "dns9.quad9.net/dns-query",
Type: dns.TypeA,
Name: "git.froth.zone",
},
},
},
{
"Bad server domain",
&util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "dns9..quad9.net/dns-query",
Type: dns.TypeA,
Name: "git.froth.zone.",
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
var (
res util.Response
err error
)
for i := 0; i <= test.opts.Request.Retries; i++ {
res, err = query.CreateQuery(test.opts)
if err == nil || errors.Is(err, &util.ErrHTTPStatus{}) {
break
}
}
if err == nil {
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
} else {
if errors.Is(err, &util.ErrHTTPStatus{}) {
assert.ErrorContains(t, err, "404")
}
assert.Equal(t, res, util.Response{})
}
})
}
}

110
pkg/resolvers/QUIC.go Normal file
View File

@ -0,0 +1,110 @@
// SPDX-License-Identifier: BSD-3-Clause
package resolvers
import (
"context"
"crypto/tls"
"fmt"
"io"
"strings"
"time"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"github.com/quic-go/quic-go"
)
// QUICResolver is for DNS-over-QUIC queries.
type QUICResolver struct {
opts *util.Options
}
var _ Resolver = (*QUICResolver)(nil)
// LookUp performs a DNS query.
func (resolver *QUICResolver) LookUp(msg *dns.Msg) (resp util.Response, err error) {
tls := &tls.Config{
//nolint:gosec // This is intentional if the user requests it
InsecureSkipVerify: resolver.opts.TLSNoVerify,
ServerName: resolver.opts.TLSHost,
MinVersion: tls.VersionTLS12,
NextProtos: []string{"doq"},
}
// Make sure that TLSHost is ALWAYS set
if resolver.opts.TLSHost == "" {
tls.ServerName = strings.Split(resolver.opts.Request.Server, ":")[0]
}
conf := new(quic.Config)
conf.HandshakeIdleTimeout = resolver.opts.Request.Timeout
resolver.opts.Logger.Debug("quic: making query")
ctx, cancel := context.WithTimeout(context.Background(), resolver.opts.Request.Timeout)
defer cancel()
connection, err := quic.DialAddr(ctx, resolver.opts.Request.Server, tls, conf)
if err != nil {
return resp, fmt.Errorf("doq: dial: %w", err)
}
resolver.opts.Logger.Debug("quic: packing query")
// Compress request to over-the-wire
buf, err := msg.Pack()
if err != nil {
return resp, fmt.Errorf("doq: pack: %w", err)
}
t := time.Now()
resolver.opts.Logger.Debug("quic: creating stream")
stream, err := connection.OpenStream()
if err != nil {
return resp, fmt.Errorf("doq: quic stream creation: %w", err)
}
resolver.opts.Logger.Debug("quic: writing to stream")
_, err = stream.Write(buf)
if err != nil {
return resp, fmt.Errorf("doq: quic stream write: %w", err)
}
err = stream.Close()
if err != nil {
return resp, fmt.Errorf("doq: quic stream close: %w", err)
}
resolver.opts.Logger.Debug("quic: reading stream")
fullRes, err := io.ReadAll(stream)
if err != nil {
return resp, fmt.Errorf("doq: quic stream read: %w", err)
}
resp.RTT = time.Since(t)
resolver.opts.Logger.Debug("quic: closing connection")
// Close with error: no error
err = connection.CloseWithError(0, "")
if err != nil {
return resp, fmt.Errorf("doq: quic connection close: %w", err)
}
resolver.opts.Logger.Debug("quic: closing stream")
resp.DNS = &dns.Msg{}
resolver.opts.Logger.Debug("quic: unpacking response")
err = resp.DNS.Unpack(fullRes)
if err != nil {
return resp, fmt.Errorf("doq: unpack: %w", err)
}
return
}

125
pkg/resolvers/QUIC_test.go Normal file
View File

@ -0,0 +1,125 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build !gccgo
package resolvers_test
import (
"testing"
"time"
"dns.froth.zone/awl/pkg/query"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestQuic(t *testing.T) {
t.Parallel()
//nolint:govet // I could not be assed to refactor this, and it is only for tests
tests := []struct {
name string
opts *util.Options
}{
{
"Valid, AdGuard",
&util.Options{
QUIC: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "dns.adguard.com",
Type: dns.TypeNS,
Port: 853,
Timeout: 750 * time.Millisecond,
Retries: 3,
},
},
},
{
"Valid, Froth",
&util.Options{
QUIC: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "dns.froth.zone",
Type: dns.TypeA,
Name: "git.freecumextremist.com",
Port: 853,
Timeout: 750 * time.Millisecond,
Retries: 3,
},
},
},
{
"Bad domain",
&util.Options{
QUIC: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "dns.//./,,adguard\a.com",
Port: 853,
Type: dns.TypeA,
Name: "git.froth.zone",
Timeout: 100 * time.Millisecond,
Retries: 0,
},
},
},
{
"Not canonical",
&util.Options{
QUIC: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "dns.adguard.com",
Port: 853,
Type: dns.TypeA,
Name: "git.froth.zone",
Timeout: 100 * time.Millisecond,
Retries: 0,
},
},
},
{
"Invalid query domain",
&util.Options{
QUIC: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "example.com",
Port: 853,
Type: dns.TypeA,
Name: "git.froth.zone",
Timeout: 10 * time.Millisecond,
Retries: 0,
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
var (
res util.Response
err error
)
for i := 0; i <= test.opts.Request.Retries; i++ {
res, err = query.CreateQuery(test.opts)
if err == nil {
break
}
}
if err == nil {
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
} else {
assert.Assert(t, res == util.Response{})
}
})
}
}

4
pkg/resolvers/docs.go Normal file
View File

@ -0,0 +1,4 @@
/*
Package resolvers contain the various DNS resolvers to use.
*/
package resolvers

96
pkg/resolvers/general.go Normal file
View File

@ -0,0 +1,96 @@
// SPDX-License-Identifier: BSD-3-Clause
package resolvers
import (
"crypto/tls"
"fmt"
"net"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
)
// StandardResolver is for UDP/TCP resolvers.
type StandardResolver struct {
opts *util.Options
}
var _ Resolver = (*StandardResolver)(nil)
// LookUp performs a DNS query.
func (resolver *StandardResolver) LookUp(msg *dns.Msg) (resp util.Response, err error) {
dnsClient := new(dns.Client)
dnsClient.Dialer = &net.Dialer{
Timeout: resolver.opts.Request.Timeout,
}
if resolver.opts.TCP || resolver.opts.TLS {
dnsClient.Net = tcp
} else {
dnsClient.Net = udp
}
switch {
case resolver.opts.IPv4:
dnsClient.Net += "4"
case resolver.opts.IPv6:
dnsClient.Net += "6"
}
if resolver.opts.TLS {
dnsClient.Net += "-tls"
dnsClient.TLSConfig = &tls.Config{
//nolint:gosec // This is intentional if the user requests it
InsecureSkipVerify: resolver.opts.TLSNoVerify,
ServerName: resolver.opts.TLSHost,
}
}
resolver.opts.Logger.Info("Using", dnsClient.Net, "for making the request")
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, resolver.opts.Request.Server)
if err != nil {
return resp, fmt.Errorf("standard: DNS exchange: %w", err)
}
switch dns.RcodeToString[resp.DNS.MsgHdr.Rcode] {
case "BADCOOKIE":
if !resolver.opts.BadCookie {
fmt.Printf(";; BADCOOKIE, retrying.\n\n")
msg.Extra = resp.DNS.Extra
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, resolver.opts.Request.Server)
if err != nil {
return resp, fmt.Errorf("badcookie: DNS exchange: %w", err)
}
}
case "NOERR":
break
}
resolver.opts.Logger.Info("Request successful")
if resp.DNS.MsgHdr.Truncated && !resolver.opts.Truncate {
fmt.Printf(";; Truncated, retrying with TCP\n\n")
dnsClient.Net = tcp
switch {
case resolver.opts.IPv4:
dnsClient.Net += "4"
case resolver.opts.IPv6:
dnsClient.Net += "6"
}
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, resolver.opts.Request.Server)
}
if err != nil {
return resp, fmt.Errorf("standard: DNS exchange: %w", err)
}
return
}

View File

@ -0,0 +1,141 @@
// SPDX-License-Identifier: BSD-3-Clause
package resolvers_test
import (
"errors"
"os"
"testing"
"time"
"dns.froth.zone/awl/pkg/query"
"dns.froth.zone/awl/pkg/util"
"dns.froth.zone/dnscrypt"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestResolve(t *testing.T) {
t.Parallel()
//nolint:govet // I could not be assed to refactor this, and it is only for tests
tests := []struct {
name string
opts *util.Options
}{
{
"UDP",
&util.Options{
Logger: util.InitLogger(0),
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeAAAA,
Name: "example.com.",
Retries: 3,
},
},
},
{
"UDP (Bad Cookie)",
&util.Options{
Logger: util.InitLogger(0),
BadCookie: false,
Request: util.Request{
Server: "b.root-servers.net",
Port: 53,
Type: dns.TypeNS,
Name: "example.com.",
Retries: 3,
},
EDNS: util.EDNS{
EnableEDNS: true,
Cookie: true,
},
},
},
{
"UDP (Truncated)",
&util.Options{
Logger: util.InitLogger(0),
IPv4: true,
Request: util.Request{
Server: "madns.binarystar.systems",
Port: 5301,
Type: dns.TypeTXT,
Name: "limit.txt.example.",
Retries: 3,
},
},
},
{
"TCP",
&util.Options{
Logger: util.InitLogger(0),
TCP: true,
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeA,
Name: "example.com.",
Retries: 3,
},
},
},
{
"TLS",
&util.Options{
Logger: util.InitLogger(0),
TLS: true,
Request: util.Request{
Server: "dns.google",
Port: 853,
Type: dns.TypeAAAA,
Name: "example.com.",
Retries: 3,
},
},
},
{
"Timeout",
&util.Options{
Logger: util.InitLogger(0),
Request: util.Request{
Server: "8.8.4.1",
Port: 1,
Type: dns.TypeA,
Name: "example.com.",
Timeout: time.Millisecond * 100,
Retries: 0,
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
var (
res util.Response
err error
)
for i := 0; i <= test.opts.Request.Retries; i++ {
res, err = query.CreateQuery(test.opts)
if err == nil || errors.Is(err, dnscrypt.ErrInvalidDNSStamp) {
break
}
}
if err == nil {
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
} else {
assert.ErrorIs(t, err, os.ErrDeadlineExceeded)
}
})
}
}

View File

@ -1,23 +1,28 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
package resolvers
import (
"net"
"strconv"
"strings"
"git.froth.zone/sam/awl/util"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
)
const (
tcp = "tcp"
udp = "udp"
)
// Resolver is the main resolver interface.
type Resolver interface {
LookUp(*dns.Msg) (util.Response, error)
}
// LoadResolver loads the respective resolver for performing a DNS query.
func LoadResolver(opts util.Options) (Resolver, error) {
func LoadResolver(opts *util.Options) (resolver Resolver, err error) {
switch {
case opts.HTTPS:
opts.Logger.Info("loading DNS-over-HTTPS resolver")
@ -26,16 +31,28 @@ func LoadResolver(opts util.Options) (Resolver, error) {
opts.Request.Server = "https://" + opts.Request.Server
}
return &HTTPSResolver{
// Make sure that the endpoint is defaulted to /dns-query
if !strings.HasSuffix(opts.Request.Server, opts.HTTPSOptions.Endpoint) {
opts.Request.Server += opts.HTTPSOptions.Endpoint
}
resolver = &HTTPSResolver{
opts: opts,
}, nil
}
return
case opts.QUIC:
opts.Logger.Info("loading DNS-over-QUIC resolver")
opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Request.Port))
return &QUICResolver{
if !strings.HasSuffix(opts.Request.Server, ":"+strconv.Itoa(opts.Request.Port)) {
opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Request.Port))
}
resolver = &QUICResolver{
opts: opts,
}, nil
}
return
case opts.DNSCrypt:
opts.Logger.Info("loading DNSCrypt resolver")
@ -43,15 +60,22 @@ func LoadResolver(opts util.Options) (Resolver, error) {
opts.Request.Server = "sdns://" + opts.Request.Server
}
return &DNSCryptResolver{
resolver = &DNSCryptResolver{
opts: opts,
}, nil
}
return
default:
opts.Logger.Info("loading standard/DNS-over-TLS resolver")
opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Request.Port))
return &StandardResolver{
if !strings.HasSuffix(opts.Request.Server, ":"+strconv.Itoa(opts.Request.Port)) {
opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Request.Port))
}
resolver = &StandardResolver{
opts: opts,
}, nil
}
return
}
}

19
pkg/util/errors.go Normal file
View File

@ -0,0 +1,19 @@
package util
import (
"errors"
"fmt"
)
// ErrHTTPStatus is returned when DoH returns a bad status code.
type ErrHTTPStatus struct {
// Status code
Code int
}
func (e *ErrHTTPStatus) Error() string {
return fmt.Sprintf("doh server responded with HTTP %d", e.Code)
}
// ErrNotError is an error that is not actually an error.
var ErrNotError = errors.New("not an error")

View File

@ -2,7 +2,7 @@
package util
import "git.froth.zone/sam/awl/logawl"
import "dns.froth.zone/awl/pkg/logawl"
// InitLogger initializes the logawl instance.
func InitLogger(verbosity int) (log *logawl.Logger) {

View File

@ -5,8 +5,8 @@ package util_test
import (
"testing"
"git.froth.zone/sam/awl/logawl"
"git.froth.zone/sam/awl/util"
"dns.froth.zone/awl/pkg/logawl"
"dns.froth.zone/awl/pkg/util"
"gotest.tools/v3/assert"
)

View File

@ -6,7 +6,7 @@ import (
"fmt"
"net"
"git.froth.zone/sam/awl/logawl"
"dns.froth.zone/awl/pkg/logawl"
"github.com/miekg/dns"
)
@ -18,8 +18,12 @@ type Options struct {
TLSHost string `json:"tlsHost" example:""`
// EDNS Options
EDNS
// HTTPS options :)
HTTPSOptions
// DNS request :)
Request Request
Request
// Verbosity levels, see [logawl.AllLevels]
Verbosity int `json:"-" example:"0"`
@ -27,6 +31,8 @@ type Options struct {
Display Display
// Ignore Truncation
Truncate bool `json:"ignoreTruncate" example:"false"`
// Ignore BADCOOKIE
BadCookie bool `json:"ignoreBadCookie" example:"false"`
// Print only the answer
Short bool `json:"short" example:"false"`
// When Short is true, display where the query came from
@ -61,6 +67,19 @@ type Options struct {
IPv4 bool `json:"forceIPv4" example:"false"`
// Force IPv6 only
IPv6 bool `json:"forceIPv6" example:"false"`
// Trace from the root
Trace bool `json:"trace" example:"false"`
}
// HTTPSOptions are options exclusively for DNS-over-HTTPS queries.
type HTTPSOptions struct {
// URL endpoint
Endpoint string `json:"endpoint" example:"/dns-query"`
// True, make GET request.
// False, make POST request.
Get bool `json:"get" example:"false"`
}
// HeaderFlags are the flags that are in DNS headers.

View File

@ -5,7 +5,7 @@ package util_test
import (
"testing"
"git.froth.zone/sam/awl/util"
"dns.froth.zone/awl/pkg/util"
"gotest.tools/v3/assert"
)
@ -17,21 +17,20 @@ func TestSubnet(t *testing.T) {
"::0/0",
"0",
"127.0.0.1/32",
"Invalid",
}
for _, test := range subnet {
test := test
t.Run(test, func(t *testing.T) {
t.Parallel()
err := util.ParseSubnet(test, new(util.Options))
assert.NilError(t, err)
if err != nil {
assert.ErrorContains(t, err, "invalid CIDR address")
} else {
assert.NilError(t, err)
}
})
}
}
func TestInvalidSub(t *testing.T) {
t.Parallel()
err := util.ParseSubnet("1", new(util.Options))
assert.ErrorContains(t, err, "invalid CIDR address")
}

View File

@ -13,8 +13,8 @@ type errReverseDNS struct {
addr string
}
func (e *errReverseDNS) Error() string {
return fmt.Sprintf("reverseDNS: invalid value %s given", e.addr)
func (errDNS *errReverseDNS) Error() string {
return fmt.Sprintf("reverseDNS: invalid value %s given", errDNS.addr)
}
// ReverseDNS is given an IP or phone number and returns a canonical string to be queried.

View File

@ -0,0 +1,79 @@
// SPDX-License-Identifier: BSD-3-Clause
package util_test
import (
"testing"
"dns.froth.zone/awl/pkg/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestPTR(t *testing.T) {
t.Parallel()
tests := []struct {
name string
in string
expected string
}{
{
"IPv4",
"8.8.4.4", "4.4.8.8.in-addr.arpa.",
},
{
"IPv6",
"2606:4700:4700::1111", "1.1.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.4.0.0.7.4.6.0.6.2.ip6.arpa.",
},
{
"Inavlid value",
"AAAAA", "",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
act, err := util.ReverseDNS(test.in, dns.StringToType["PTR"])
if err == nil {
assert.NilError(t, err)
} else {
assert.ErrorContains(t, err, "unrecognized address")
}
assert.Equal(t, act, test.expected)
})
}
}
func TestNAPTR(t *testing.T) {
t.Parallel()
tests := []struct {
in string
want string
}{
{"1-800-555-1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."},
{"+1 800555 1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."},
{"+46766861004", "4.0.0.1.6.8.6.6.7.6.4.e164.arpa."},
{"17705551212", "2.1.2.1.5.5.5.0.7.7.1.e164.arpa."},
}
for _, test := range tests {
// Thanks Goroutines, very cool!
test := test
t.Run(test.in, func(t *testing.T) {
t.Parallel()
act, err := util.ReverseDNS(test.in, dns.StringToType["NAPTR"])
assert.NilError(t, err)
assert.Equal(t, test.want, act)
})
}
}
func TestInvalidAll(t *testing.T) {
_, err := util.ReverseDNS("q", 15236)
assert.ErrorContains(t, err, "invalid value")
}

View File

@ -1,62 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"fmt"
"time"
"git.froth.zone/sam/awl/util"
"github.com/ameshkov/dnscrypt/v2"
"github.com/miekg/dns"
)
// DNSCryptResolver is for making DNSCrypt queries.
type DNSCryptResolver struct {
opts util.Options
}
var _ Resolver = (*DNSCryptResolver)(nil)
// LookUp performs a DNS query.
func (r *DNSCryptResolver) LookUp(msg *dns.Msg) (util.Response, error) {
client := dnscrypt.Client{
Timeout: r.opts.Request.Timeout,
UDPSize: 1232,
}
if r.opts.TCP || r.opts.TLS {
client.Net = tcp
} else {
client.Net = udp
}
switch {
case r.opts.IPv4:
client.Net += "4"
case r.opts.IPv6:
client.Net += "6"
}
r.opts.Logger.Debug("Using", client.Net, "for making the request")
resolverInf, err := client.Dial(r.opts.Request.Server)
if err != nil {
return util.Response{}, fmt.Errorf("dnscrypt: dial: %w", err)
}
now := time.Now()
res, err := client.Exchange(msg, resolverInf)
rtt := time.Since(now)
if err != nil {
return util.Response{}, fmt.Errorf("dnscrypt: exchange: %w", err)
}
r.opts.Logger.Info("Request successful")
return util.Response{
DNS: res,
RTT: rtt,
}, nil
}

View File

@ -1,73 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query_test
import (
"testing"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestDNSCrypt(t *testing.T) {
t.Parallel()
tests := []struct {
opt util.Options
}{
{
util.Options{
Logger: util.InitLogger(0),
DNSCrypt: true,
Request: util.Request{
Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
Type: dns.TypeA,
Name: "example.com.",
},
},
},
{
util.Options{
Logger: util.InitLogger(0),
DNSCrypt: true,
TCP: true,
IPv4: true,
Request: util.Request{
Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
Type: dns.TypeAAAA,
Name: "example.com.",
},
},
},
{
util.Options{
Logger: util.InitLogger(0),
DNSCrypt: true,
TCP: true,
IPv4: true,
Request: util.Request{
Server: "QMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
Type: dns.TypeAAAA,
Name: "example.com.",
},
},
},
}
for _, test := range tests {
test := test
t.Run("", func(t *testing.T) {
t.Parallel()
res, err := query.CreateQuery(test.opt)
if err == nil {
assert.Assert(t, res != util.Response{})
} else {
assert.ErrorContains(t, err, "unsupported stamp")
}
})
}
}

View File

@ -1,88 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"bytes"
"fmt"
"io"
"net/http"
"time"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
)
// HTTPSResolver is for DNS-over-HTTPS queries.
type HTTPSResolver struct {
opts util.Options
}
var _ Resolver = (*HTTPSResolver)(nil)
// LookUp performs a DNS query.
func (r *HTTPSResolver) LookUp(msg *dns.Msg) (util.Response, error) {
var resp util.Response
httpR := &http.Client{
Timeout: r.opts.Request.Timeout,
}
buf, err := msg.Pack()
if err != nil {
return util.Response{}, fmt.Errorf("doh: packing: %w", err)
}
r.opts.Logger.Debug("https: sending HTTPS request")
req, err := http.NewRequest("POST", r.opts.Request.Server, bytes.NewBuffer(buf))
if err != nil {
return util.Response{}, fmt.Errorf("doh: request creation: %w", err)
}
req.Header.Set("Content-Type", "application/dns-message")
req.Header.Set("Accept", "application/dns-message")
now := time.Now()
res, err := httpR.Do(req)
resp.RTT = time.Since(now)
if err != nil {
return util.Response{}, fmt.Errorf("doh: HTTP request: %w", err)
}
if res.StatusCode != http.StatusOK {
return util.Response{}, &errHTTPStatus{res.StatusCode}
}
r.opts.Logger.Debug("https: reading response")
fullRes, err := io.ReadAll(res.Body)
if err != nil {
return util.Response{}, fmt.Errorf("doh: body read: %w", err)
}
err = res.Body.Close()
if err != nil {
return util.Response{}, fmt.Errorf("doh: body close: %w", err)
}
r.opts.Logger.Debug("https: unpacking response")
resp.DNS = &dns.Msg{}
err = resp.DNS.Unpack(fullRes)
if err != nil {
return util.Response{}, fmt.Errorf("doh: dns message unpack: %w", err)
}
return resp, nil
}
type errHTTPStatus struct {
code int
}
func (e *errHTTPStatus) Error() string {
return fmt.Sprintf("doh server responded with HTTP %d", e.code)
}

View File

@ -1,115 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query_test
import (
"testing"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestResolveHTTPS(t *testing.T) {
t.Parallel()
var err error
opts := util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "https://dns9.quad9.net/dns-query",
Type: dns.TypeA,
Name: "git.froth.zone.",
},
}
// testCase := util.Request{Server: "https://dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."}
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
msg := new(dns.Msg)
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
res, err := resolver.LookUp(msg)
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
}
func Test2ResolveHTTPS(t *testing.T) {
t.Parallel()
opts := util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."},
}
var err error
testCase := util.Request{Type: dns.TypeA, Name: "git.froth.zone"}
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
msg := new(dns.Msg)
msg.SetQuestion(testCase.Name, testCase.Type)
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
res, err := resolver.LookUp(msg)
assert.ErrorContains(t, err, "fully qualified")
assert.Equal(t, res, util.Response{})
}
func Test3ResolveHTTPS(t *testing.T) {
t.Parallel()
opts := util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{Server: "dns9..quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."},
}
var err error
// testCase :=
// if the domain is not canonical, make it canonical
// if !strings.HasSuffix(testCase.Name, ".") {
// testCase.Name = fmt.Sprintf("%s.", testCase.Name)
// }
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
msg := new(dns.Msg)
// msg.SetQuestion(testCase.Name, testCase.Type)
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
res, err := resolver.LookUp(msg)
assert.ErrorContains(t, err, "doh: HTTP request")
assert.Equal(t, res, util.Response{})
}
func Test404ResolveHTTPS(t *testing.T) {
t.Parallel()
var err error
opts := util.Options{
HTTPS: true,
Logger: util.InitLogger(0),
Request: util.Request{
Server: "https://dns9.quad9.net/dns",
Type: dns.TypeA,
Name: "git.froth.zone.",
},
}
// testCase := util.Request{Server: "https://dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."}
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
msg := new(dns.Msg)
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
res, err := resolver.LookUp(msg)
assert.ErrorContains(t, err, "404")
assert.Equal(t, res, util.Response{})
}

View File

@ -1,102 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"crypto/tls"
"fmt"
"io"
"time"
"git.froth.zone/sam/awl/util"
"github.com/lucas-clemente/quic-go"
"github.com/miekg/dns"
)
// QUICResolver is for DNS-over-QUIC queries.
type QUICResolver struct {
opts util.Options
}
var _ Resolver = (*QUICResolver)(nil)
// LookUp performs a DNS query.
func (r *QUICResolver) LookUp(msg *dns.Msg) (util.Response, error) {
var resp util.Response
tls := &tls.Config{
//nolint:gosec // This is intentional if the user requests it
InsecureSkipVerify: r.opts.TLSNoVerify,
ServerName: r.opts.TLSHost,
MinVersion: tls.VersionTLS12,
NextProtos: []string{"doq"},
}
conf := new(quic.Config)
conf.HandshakeIdleTimeout = r.opts.Request.Timeout
r.opts.Logger.Debug("quic: making query")
connection, err := quic.DialAddr(r.opts.Request.Server, tls, conf)
if err != nil {
return util.Response{}, fmt.Errorf("doq: dial: %w", err)
}
r.opts.Logger.Debug("quic: packing query")
// Compress request to over-the-wire
buf, err := msg.Pack()
if err != nil {
return util.Response{}, fmt.Errorf("doq: pack: %w", err)
}
t := time.Now()
r.opts.Logger.Debug("quic: creating stream")
stream, err := connection.OpenStream()
if err != nil {
return util.Response{}, fmt.Errorf("doq: quic stream creation: %w", err)
}
r.opts.Logger.Debug("quic: writing to stream")
_, err = stream.Write(buf)
if err != nil {
return util.Response{}, fmt.Errorf("doq: quic stream write: %w", err)
}
r.opts.Logger.Debug("quic: reading stream")
fullRes, err := io.ReadAll(stream)
if err != nil {
return util.Response{}, fmt.Errorf("doq: quic stream read: %w", err)
}
resp.RTT = time.Since(t)
r.opts.Logger.Debug("quic: closing connection")
// Close with error: no error
err = connection.CloseWithError(0, "")
if err != nil {
return util.Response{}, fmt.Errorf("doq: quic connection close: %w", err)
}
r.opts.Logger.Debug("quic: closing stream")
err = stream.Close()
if err != nil {
return util.Response{}, fmt.Errorf("doq: quic stream close: %w", err)
}
resp.DNS = &dns.Msg{}
r.opts.Logger.Debug("quic: unpacking response")
err = resp.DNS.Unpack(fullRes)
if err != nil {
return util.Response{}, fmt.Errorf("doq: unpack: %w", err)
}
return resp, nil
}

View File

@ -1,90 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query_test
import (
"fmt"
"net"
"strconv"
"strings"
"testing"
"time"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestQuic(t *testing.T) {
t.Parallel()
opts := util.Options{
QUIC: true,
Logger: util.InitLogger(0),
Request: util.Request{Server: "dns.adguard.com", Port: 853},
}
testCase := util.Request{Server: "dns.//./,,adguard.com", Type: dns.TypeA, Name: "git.froth.zone"}
testCase2 := util.Request{Server: "dns.adguard.com", Type: dns.TypeA, Name: "git.froth.zone"}
var testCases []util.Request
testCases = append(testCases, testCase)
testCases = append(testCases, testCase2)
for i := range testCases {
switch i {
case 0:
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
// if the domain is not canonical, make it canonical
if !strings.HasSuffix(testCase.Name, ".") {
testCases[i].Name = fmt.Sprintf("%s.", testCases[i].Name)
}
msg := new(dns.Msg)
msg.SetQuestion(testCase.Name, testCase.Type)
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
res, err := resolver.LookUp(msg)
assert.ErrorContains(t, err, "fully qualified")
assert.Equal(t, res, util.Response{})
case 1:
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Request.Port))
// if the domain is not canonical, make it canonical
if !strings.HasSuffix(testCase2.Name, ".") {
testCase2.Name = fmt.Sprintf("%s.", testCase2.Name)
}
msg := new(dns.Msg)
msg.SetQuestion(testCase2.Name, testCase2.Type)
res, err := resolver.LookUp(msg)
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
}
}
}
func TestInvalidQuic(t *testing.T) {
t.Parallel()
opts := util.Options{
QUIC: true,
Logger: util.InitLogger(0),
Request: util.Request{Server: "example.com", Port: 853, Type: dns.TypeA, Name: "git.froth.zone", Timeout: 10 * time.Millisecond},
}
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
msg := new(dns.Msg)
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
res, err := resolver.LookUp(msg)
assert.ErrorContains(t, err, "timeout")
assert.Equal(t, res, util.Response{})
}

View File

@ -1,84 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"crypto/tls"
"fmt"
"net"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
)
// StandardResolver is for UDP/TCP resolvers.
type StandardResolver struct {
opts util.Options
}
var _ Resolver = (*StandardResolver)(nil)
// LookUp performs a DNS query.
func (r *StandardResolver) LookUp(msg *dns.Msg) (util.Response, error) {
var (
resp util.Response
err error
)
dnsClient := new(dns.Client)
dnsClient.Dialer = &net.Dialer{
Timeout: r.opts.Request.Timeout,
}
if r.opts.TCP || r.opts.TLS {
dnsClient.Net = tcp
} else {
dnsClient.Net = udp
}
switch {
case r.opts.IPv4:
dnsClient.Net += "4"
case r.opts.IPv6:
dnsClient.Net += "6"
}
if r.opts.TLS {
dnsClient.Net += "-tls"
dnsClient.TLSConfig = &tls.Config{
//nolint:gosec // This is intentional if the user requests it
InsecureSkipVerify: r.opts.TLSNoVerify,
ServerName: r.opts.TLSHost,
}
}
r.opts.Logger.Debug("Using", dnsClient.Net, "for making the request")
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server)
if err != nil {
return util.Response{}, fmt.Errorf("standard: DNS exchange: %w", err)
}
r.opts.Logger.Info("Request successful")
if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate {
fmt.Printf(";; Truncated, retrying with TCP\n\n")
dnsClient.Net = tcp
switch {
case r.opts.IPv4:
dnsClient.Net += "4"
case r.opts.IPv6:
dnsClient.Net += "6"
}
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server)
}
if err != nil {
return util.Response{}, fmt.Errorf("standard: DNS exchange: %w", err)
}
return resp, nil
}

View File

@ -1,117 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query_test
import (
"testing"
"time"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestResolve(t *testing.T) {
t.Parallel()
opts := util.Options{
Logger: util.InitLogger(0),
Request: util.Request{
Server: "8.8.4.1",
Port: 53,
Type: dns.TypeA,
Name: "example.com.",
Timeout: time.Second / 2,
Retries: 0,
},
}
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
msg := new(dns.Msg)
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
_, err = resolver.LookUp(msg)
assert.ErrorContains(t, err, "timeout")
}
func TestTruncate(t *testing.T) {
t.Parallel()
opts := util.Options{
Logger: util.InitLogger(0),
IPv4: true,
Request: util.Request{
Server: "madns.binarystar.systems",
Port: 5301,
Type: dns.TypeTXT,
Name: "limit.txt.example.",
},
}
resolver, err := query.LoadResolver(opts)
assert.NilError(t, err)
msg := new(dns.Msg)
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
res, err := resolver.LookUp(msg)
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
}
func TestResolveAgain(t *testing.T) {
t.Parallel()
tests := []struct {
opt util.Options
}{
{
util.Options{
Logger: util.InitLogger(0),
TCP: true,
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeA,
Name: "example.com.",
},
},
},
{
util.Options{
Logger: util.InitLogger(0),
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeAAAA,
Name: "example.com.",
},
},
},
{
util.Options{
Logger: util.InitLogger(0),
TLS: true,
Request: util.Request{
Server: "dns.google",
Port: 853,
Type: dns.TypeAAAA,
Name: "example.com.",
},
},
},
}
for _, test := range tests {
test := test
t.Run("", func(t *testing.T) {
t.Parallel()
res, err := query.CreateQuery(test.opt)
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
})
}
}

View File

@ -1,550 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"encoding/hex"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
"time"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"golang.org/x/net/idna"
"gopkg.in/yaml.v3"
)
// ToString turns the response into something that looks a lot like dig
//
// Much of this is taken from https://github.com/miekg/dns/blob/master/msg.go#L900
func ToString(res util.Response, opts util.Options) (string, error) {
if res.DNS == nil {
return "<nil> MsgHdr", errNoMessage
}
var (
s string
opt *dns.OPT
)
if !opts.Short {
if opts.Display.Comments {
s += res.DNS.MsgHdr.String() + " "
s += "QUERY: " + strconv.Itoa(len(res.DNS.Question)) + ", "
s += "ANSWER: " + strconv.Itoa(len(res.DNS.Answer)) + ", "
s += "AUTHORITY: " + strconv.Itoa(len(res.DNS.Ns)) + ", "
s += "ADDITIONAL: " + strconv.Itoa(len(res.DNS.Extra)) + "\n"
opt = res.DNS.IsEdns0()
if opt != nil && opts.Display.Opt {
// OPT PSEUDOSECTION
s += opt.String() + "\n"
}
}
if opts.Display.Question {
if len(res.DNS.Question) > 0 {
if opts.Display.Comments {
s += "\n;; QUESTION SECTION:\n"
}
for _, r := range res.DNS.Question {
str, err := stringParse(r.String(), false, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
if opts.Display.Answer {
if len(res.DNS.Answer) > 0 {
if opts.Display.Comments {
s += "\n;; ANSWER SECTION:\n"
}
for _, r := range res.DNS.Answer {
if r != nil {
str, err := stringParse(r.String(), true, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
}
if opts.Display.Authority {
if len(res.DNS.Ns) > 0 {
if opts.Display.Comments {
s += "\n;; AUTHORITY SECTION:\n"
}
for _, r := range res.DNS.Ns {
if r != nil {
str, err := stringParse(r.String(), true, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
}
if opts.Display.Additional {
if len(res.DNS.Extra) > 0 && (opt == nil || len(res.DNS.Extra) > 1) {
if opts.Display.Comments {
s += "\n;; ADDITIONAL SECTION:\n"
}
for _, r := range res.DNS.Extra {
if r != nil && r.Header().Rrtype != dns.TypeOPT {
str, err := stringParse(r.String(), true, opts)
if err != nil {
return "", fmt.Errorf("%w", err)
}
s += str + "\n"
}
}
}
}
if opts.Display.Statistics {
s += "\n;; Query time: " + res.RTT.String()
s += "\n;; SERVER: " + opts.Request.Server + serverExtra(opts)
s += "\n;; WHEN: " + time.Now().Format(time.RFC1123Z)
s += "\n;; MSG SIZE rcvd: " + strconv.Itoa(res.DNS.Len()) + "\n"
}
} else {
// Print just the responses, nothing else
for i, resp := range res.DNS.Answer {
temp := strings.Split(resp.String(), "\t")
s += temp[len(temp)-1]
if opts.Identify {
s += " from server " + opts.Request.Server + " in " + res.RTT.String()
}
// Don't print newline on last line
if i != len(res.DNS.Answer)-1 {
s += "\n"
}
}
}
return s, nil
}
func serverExtra(opts util.Options) string {
// Add extra information to server string
var extra string
switch {
case opts.TCP:
extra = ":" + strconv.Itoa(opts.Request.Port) + " (TCP)"
case opts.TLS:
extra = ":" + strconv.Itoa(opts.Request.Port) + " (TLS)"
case opts.HTTPS, opts.DNSCrypt:
extra = ""
case opts.QUIC:
extra = ":" + strconv.Itoa(opts.Request.Port) + " (QUIC)"
default:
extra = ":" + strconv.Itoa(opts.Request.Port) + " (UDP)"
}
return extra
}
// stringParse edits the raw responses to user requests.
func stringParse(str string, isAns bool, opts util.Options) (string, error) {
split := strings.Split(str, "\t")
// Make edits if so requested
// TODO: make less ew?
// This exists because the question section should be left alone EXCEPT for punycode.
if isAns {
if !opts.Display.TTL {
// Remove from existence
split = append(split[:1], split[2:]...)
}
if !opts.Display.ShowClass {
// Position depends on if the TTL is there or not.
if opts.Display.TTL {
split = append(split[:2], split[3:]...)
} else {
split = append(split[:1], split[2:]...)
}
}
if opts.Display.TTL && opts.Display.HumanTTL {
ttl, _ := strconv.Atoi(split[1])
split[1] = (time.Duration(ttl) * time.Second).String()
}
}
if opts.Display.UcodeTranslate {
var (
err error
semi string
)
if strings.HasPrefix(split[0], ";") {
split[0] = strings.TrimPrefix(split[0], ";")
semi = ";"
}
split[0], err = idna.ToUnicode(split[0])
if err != nil {
return "", fmt.Errorf("punycode: %w", err)
}
split[0] = semi + split[0]
}
return strings.Join(split, "\t"), nil
}
// PrintSpecial is for printing as JSON, XML or YAML.
// As of now JSON and XML use the stdlib version.
func PrintSpecial(res util.Response, opts util.Options) (string, error) {
formatted, err := MakePrintable(res, opts)
if err != nil {
return "", err
}
switch {
case opts.JSON:
opts.Logger.Info("Printing as JSON")
json, err := json.MarshalIndent(formatted, " ", " ")
return string(json), err
case opts.XML:
opts.Logger.Info("Printing as XML")
xml, err := xml.MarshalIndent(formatted, " ", " ")
return string(xml), err
case opts.YAML:
opts.Logger.Info("Printing as YAML")
yaml, err := yaml.Marshal(formatted)
return string(yaml), err
default:
return "", errInvalidFormat
}
}
// MakePrintable takes a DNS message and makes it nicer to be printed as JSON,YAML,
// and XML. Little is changed beyond naming.
func MakePrintable(res util.Response, opts util.Options) (*Message, error) {
var (
err error
msg = res.DNS
)
// The things I do for compatibility
ret := Message{
Header: Header{
ID: msg.Id,
Response: msg.Response,
Opcode: dns.OpcodeToString[msg.Opcode],
Authoritative: msg.Authoritative,
Truncated: msg.Truncated,
RecursionDesired: msg.RecursionDesired,
RecursionAvailable: msg.RecursionAvailable,
Zero: msg.Zero,
AuthenticatedData: msg.AuthenticatedData,
CheckingDisabled: msg.CheckingDisabled,
Status: dns.RcodeToString[msg.Rcode],
},
}
opt := msg.IsEdns0()
if opt != nil && opts.Display.Opt {
ret.Opt, err = parseOpt(*opt)
if err != nil {
return nil, fmt.Errorf("edns print: %w", err)
}
}
for _, question := range msg.Question {
var name string
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(question.Name)
if err != nil {
return nil, fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = question.Name
}
ret.Question = append(ret.Question, Question{
Name: name,
Type: dns.TypeToString[question.Qtype],
Class: dns.ClassToString[question.Qclass],
})
}
for _, answer := range msg.Answer {
temp := strings.Split(answer.String(), "\t")
var (
ttl string
name string
)
if opts.Display.TTL {
if opts.Display.HumanTTL {
ttl = (time.Duration(answer.Header().Ttl) * time.Second).String()
} else {
ttl = strconv.Itoa(int(answer.Header().Ttl))
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(answer.Header().Name)
if err != nil {
return nil, fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = answer.Header().Name
}
ret.Answer = append(ret.Answer, Answer{
RRHeader: RRHeader{
Name: name,
Type: dns.TypeToString[answer.Header().Rrtype],
Class: dns.ClassToString[answer.Header().Class],
Rdlength: answer.Header().Rdlength,
TTL: ttl,
},
Value: temp[len(temp)-1],
})
}
for _, ns := range msg.Ns {
temp := strings.Split(ns.String(), "\t")
var (
ttl string
name string
)
if opts.Display.TTL {
if opts.Display.HumanTTL {
ttl = (time.Duration(ns.Header().Ttl) * time.Second).String()
} else {
ttl = strconv.Itoa(int(ns.Header().Ttl))
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(ns.Header().Name)
if err != nil {
return nil, fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = ns.Header().Name
}
ret.Authority = append(ret.Authority, Answer{
RRHeader: RRHeader{
Name: name,
Type: dns.TypeToString[ns.Header().Rrtype],
Class: dns.ClassToString[ns.Header().Class],
Rdlength: ns.Header().Rdlength,
TTL: ttl,
},
Value: temp[len(temp)-1],
})
}
for _, additional := range msg.Extra {
if additional.Header().Rrtype == dns.StringToType["OPT"] {
continue
} else {
temp := strings.Split(additional.String(), "\t")
var (
ttl string
name string
)
if opts.Display.TTL {
if opts.Display.HumanTTL {
ttl = (time.Duration(additional.Header().Ttl) * time.Second).String()
} else {
ttl = strconv.Itoa(int(additional.Header().Ttl))
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(additional.Header().Name)
if err != nil {
return nil, fmt.Errorf("punycode to unicode: %w", err)
}
} else {
name = additional.Header().Name
}
ret.Additional = append(ret.Additional, Answer{
RRHeader: RRHeader{
Name: name,
Type: dns.TypeToString[additional.Header().Rrtype],
Class: dns.ClassToString[additional.Header().Class],
Rdlength: additional.Header().Rdlength,
TTL: ttl,
},
Value: temp[len(temp)-1],
})
}
}
if opts.Display.Statistics {
ret.Statistics = Statistics{
RTT: res.RTT.String(),
Server: opts.Request.Server + serverExtra(opts),
When: time.Now().Format(time.RFC1123Z),
MsgSize: res.DNS.Len(),
}
} else {
ret.Statistics = Statistics{}
}
return &ret, nil
}
func parseOpt(rr dns.OPT) ([]Opts, error) {
ret := []Opts{}
// Most of this is taken from https://github.com/miekg/dns/blob/master/edns.go#L76
ret = append(ret, Opts{
Name: "Version",
Value: strconv.Itoa(int(rr.Version())),
})
if rr.Do() {
ret = append(ret, Opts{
Name: "Flags",
Value: "do",
})
} else {
ret = append(ret, Opts{
Name: "Flags",
Value: "",
})
}
if rr.Hdr.Ttl&0x7FFF != 0 {
ret = append(ret, Opts{
Name: "MBZ",
Value: fmt.Sprintf("0x%04x", rr.Hdr.Ttl&0x7FFF),
})
}
ret = append(ret, Opts{
Name: "UDP Buffer Size",
Value: strconv.Itoa(int(rr.UDPSize())),
})
for _, opt := range rr.Option {
switch opt.(type) {
case *dns.EDNS0_NSID:
str := opt.String()
hex, err := hex.DecodeString(str)
if err != nil {
return nil, fmt.Errorf("%w", err)
}
ret = append(ret, Opts{
Name: "NSID",
Value: fmt.Sprintf("%s (%s)", str, string(hex)),
})
case *dns.EDNS0_SUBNET:
ret = append(ret, Opts{
Name: "Subnet",
Value: opt.String(),
})
case *dns.EDNS0_COOKIE:
ret = append(ret, Opts{
Name: "Cookie",
Value: opt.String(),
})
case *dns.EDNS0_EXPIRE:
ret = append(ret, Opts{
Name: "Expire",
Value: opt.String(),
})
case *dns.EDNS0_TCP_KEEPALIVE:
ret = append(ret, Opts{
Name: "TCP Keepalive",
Value: opt.String(),
})
case *dns.EDNS0_UL:
ret = append(ret, Opts{
Name: "Update Lease",
Value: opt.String(),
})
case *dns.EDNS0_LLQ:
ret = append(ret, Opts{
Name: "Long Lived Queries",
Value: opt.String(),
})
case *dns.EDNS0_DAU:
ret = append(ret, Opts{
Name: "DNSSEC Algorithm Understood",
Value: opt.String(),
})
case *dns.EDNS0_DHU:
ret = append(ret, Opts{
Name: "DS Hash Understood",
Value: opt.String(),
})
case *dns.EDNS0_N3U:
ret = append(ret, Opts{
Name: "NSEC3 Hash Understood",
Value: opt.String(),
})
case *dns.EDNS0_LOCAL:
ret = append(ret, Opts{
Name: "Local OPT",
Value: opt.String(),
})
case *dns.EDNS0_PADDING:
ret = append(ret, Opts{
Name: "Padding",
Value: opt.String(),
})
case *dns.EDNS0_EDE:
ret = append(ret, Opts{
Name: "EDE",
Value: opt.String(),
})
case *dns.EDNS0_ESU:
ret = append(ret, Opts{
Name: "ESU",
Value: opt.String(),
})
}
}
return ret, nil
}
var errInvalidFormat = errors.New("this should never happen")

View File

@ -1,130 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query_test
import (
"testing"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
func TestCreateQ(t *testing.T) {
t.Parallel()
in := []util.Options{
{
Logger: util.InitLogger(0),
HeaderFlags: util.HeaderFlags{
Z: true,
},
YAML: true,
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeA,
Name: "example.com.",
},
Display: util.Display{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
ShowQuery: true,
},
EDNS: util.EDNS{
ZFlag: 1,
BufSize: 1500,
EnableEDNS: true,
Cookie: true,
DNSSEC: true,
Expire: true,
KeepOpen: true,
Nsid: true,
Padding: true,
Version: 0,
},
},
{
Logger: util.InitLogger(0),
HeaderFlags: util.HeaderFlags{
Z: true,
},
XML: true,
Request: util.Request{
Server: "8.8.4.4",
Port: 53,
Type: dns.TypeA,
Name: "example.com.",
},
Display: util.Display{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
ShowQuery: true,
},
},
{
Logger: util.InitLogger(0),
JSON: true,
QUIC: true,
Request: util.Request{
Server: "dns.adguard.com",
Port: 853,
Type: dns.TypeA,
Name: "example.com.",
},
Display: util.Display{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
ShowQuery: true,
},
EDNS: util.EDNS{
EnableEDNS: true,
DNSSEC: true,
Cookie: true,
Expire: true,
Nsid: true,
},
},
}
for _, opt := range in {
opt := opt
t.Run("", func(t *testing.T) {
t.Parallel()
res, err := query.CreateQuery(opt)
assert.NilError(t, err)
assert.Assert(t, res != util.Response{})
str, err := query.PrintSpecial(res, opt)
assert.NilError(t, err)
assert.Assert(t, str != "")
str, err = query.ToString(res, opt)
assert.NilError(t, err)
assert.Assert(t, str != "")
})
}
}

View File

@ -1,80 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"errors"
)
// Message is for overall DNS responses.
//
//nolint:govet // Better looking output is worth a few bytes.
type Message struct {
// Header section
Header `json:"header,omitempty" xml:"header,omitempty" yaml:"header,omitempty"`
// Opt Pseudosection
Opt []Opts `json:"opt,omitempty" xml:"opt,omitempty" yaml:"opt,omitempty"`
// Question Section
Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:"question,omitempty"`
// Answer Section
Answer []Answer `json:"answer,omitempty" xml:"answer,omitempty" yaml:"answer,omitempty"`
// Authority Section
Authority []Answer `json:"authority,omitempty" xml:"authority,omitempty" yaml:"authority,omitempty"`
// Additional Section
Additional []Answer `json:"additional,omitempty" xml:"additional,omitempty" yaml:"additional,omitempty"`
// Statistics :)
Statistics `json:"statistics,omitempty" xml:"statistics,omitempty" yaml:"statistics,omitempty"`
}
// Header is the header.
type Header struct {
Opcode string `json:"opcode," xml:"opcode," yaml:"opcode" example:"QUERY"`
Status string `json:"status," xml:"status," yaml:"status" example:"NOERR"`
ID uint16 `json:"id," xml:"id," yaml:"id" example:"12"`
Response bool `json:"response," xml:"response," yaml:"response" example:"true"`
Authoritative bool `json:"authoritative," xml:"authoritative," yaml:"authoritative" example:"false"`
Truncated bool `json:"truncated," xml:"truncated," yaml:"truncated" example:"false"`
RecursionDesired bool `json:"recursionDesired," xml:"recursionDesired," yaml:"recursionDesired" example:"true"`
RecursionAvailable bool `json:"recursionAvailable," xml:"recursionAvailable," yaml:"recursionAvailable" example:"true"`
Zero bool `json:"zero," xml:"zero," yaml:"zero" example:"false"`
AuthenticatedData bool `json:"authenticatedData," xml:"authenticatedData," yaml:"authenticatedData" example:"false"`
CheckingDisabled bool `json:"checkingDisabled," xml:"checkingDisabled," yaml:"checkingDisabled" example:"false"`
}
// Question is a DNS Query.
type Question struct {
Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"localhost"`
Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"`
Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"`
}
// RRHeader is for DNS Resource Headers.
type RRHeader struct {
Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"127.0.0.1"`
TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:"ttl,omitempty" example:"0ms"`
Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"`
Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"`
Rdlength uint16 `json:"-" xml:"-" yaml:"-"`
}
// Opts is for the OPT pseudosection, nearly exclusively for EDNS.
type Opts struct {
Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"`
Value string `json:"value" xml:"value" yaml:"value"`
}
// Answer is for a DNS Response.
type Answer struct {
Value string `json:"response,omitempty" xml:"response,omitempty" yaml:"response,omitempty"`
RRHeader `json:"header,omitempty" xml:"header,omitempty" yaml:"header,omitempty"`
}
// Statistics is the little bit at the bottom :).
type Statistics struct {
RTT string `json:"queryTime,omitempty" xml:"queryTime,omitempty" yaml:"queryTime,omitempty"`
Server string `json:"server,omitempty" xml:"server,omitempty" yaml:"server,omitempty"`
When string `json:"when,omitempty" xml:"when,omitempty" yaml:"when,omitempty"`
MsgSize int `json:"msgSize,omitempty" xml:"msgSize,omitempty" yaml:"msgSize,omitempty"`
}
var errNoMessage = errors.New("no message")

View File

@ -1,4 +1,5 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base", ":npm", ":gomod"]
"extends": ["config:base", ":npm", ":gomod"],
"automerge": true
}

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: BSD-3-Clause
# Template for the BSD/GNU makefiles
HASH ?= `git describe --always --dirty --broken | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' || echo "UNKNOWN"`
HASH ?= `git describe --tags --always --dirty --broken | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' || echo "UNKNOWN"`
SOURCES ?= $(shell find . -name "*.go" -type f ! -name '*_test*')
TEST_SOURCES ?= $(shell find . -name "*_test.go" -type f)
@ -10,7 +10,7 @@ CGO_ENABLED ?= 0
GO ?= go
TEST ?= $(GO) test -race -cover
COVER ?= $(GO) tool cover
GOFLAGS ?= -ldflags="-s -w -X=main.version=$(HASH)" -trimpath
GOFLAGS ?= -trimpath -ldflags="-s -w -X=main.version=$(HASH)"
DESTDIR :=
PREFIX ?= /usr/local
@ -23,19 +23,19 @@ MAN ?= $(PREFIX)/$(SHARE)/man
PROG ?= awl
# hehe
all: $(PROG) doc/$(PROG).1
all: $(PROG) docs/$(PROG).1
$(PROG): $(SOURCES)
$(GO) build -o $(EXE) $(GOFLAGS) .
doc/$(PROG).1: doc/$(PROG).1.scd
$(SCDOC) <$< >$@
docs/$(PROG).1: docs/$(PROG).1.scd
$(SCDOC) <$? >$@
doc/wiki/$(PROG).1.md: doc/$(PROG).1
pandoc --from man --to gfm -o $@ $<
docs/wiki/$(PROG).1.md: docs/$(PROG).1
pandoc --from man --to gfm -o $@ $?
## update_doc: update documentation (requires pandoc)
update_doc: doc/wiki/$(PROG).1.md
update_doc: docs/wiki/$(PROG).1.md
.PHONY: fmt
fmt:
@ -50,9 +50,12 @@ vet:
lint: fmt vet
golangci-lint run --fix
coverage/coverage.out: $(TEST_SOURCES)
$(TEST) -coverprofile=$@ ./...
.PHONY: test
## test: run go test
test: $(TEST_SOURCES)
$(TEST) -v -coverprofile=coverage/coverage.out ./...
test: coverage/coverage.out
.PHONY: test-ci
test-ci:
@ -60,31 +63,33 @@ test-ci:
## fuzz: runs fuzz tests
fuzz: $(TEST_SOURCES)
$(TEST) -fuzz=FuzzFlags -fuzztime 10000x ./cli
$(TEST) -fuzz=FuzzDig -fuzztime 10000x ./cli
$(TEST) -fuzz=FuzzParseArgs -fuzztime 10000x ./cli
$(TEST) -fuzz=FuzzFlags -fuzztime 10000x ./cmd
$(TEST) -fuzz=FuzzDig -fuzztime 10000x ./cmd
$(TEST) -fuzz=FuzzParseArgs -fuzztime 10000x ./cmd
fuzz-ci: $(TEST_SOURCES)
$(TEST) -fuzz=FuzzFlags -fuzztime 1000x ./cli
$(TEST) -fuzz=FuzzDig -fuzztime 1000x ./cli
$(TEST) -fuzz=FuzzParseArgs -fuzztime 1000x ./cli
$(TEST) -fuzz=FuzzFlags -fuzztime 1000x ./cmd
$(TEST) -fuzz=FuzzDig -fuzztime 1000x ./cmd
$(TEST) -fuzz=FuzzParseArgs -fuzztime 1000x ./cmd
.PHONY: full_test
full_test: test fuzz
coverage/coverage.out: test
$(COVER) -func=$@
$(COVER) -html=$@ -o coverage/cover.html
coverage/cover.html: coverage/coverage.out
$(COVER) -func=$?
$(COVER) -html=$? -o $@
## cover: generates test coverage, output as HTML
cover: coverage/coverage.out
cover: coverage/cover.html
## clean: clean the build files
.PHONY: clean
clean:
$(GO) clean
# Ignore errors if you remove something that doesn't exist
rm -f doc/$(PROG).1
rm -f docs/$(PROG).1
rm -f coverage/cover*
rm -rf vendor
## help: Prints this help message
.PHONY: help

View File

@ -1,70 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
package util_test
import (
"testing"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
var (
PTR = dns.StringToType["PTR"]
NAPTR = dns.StringToType["NAPTR"]
)
func TestIPv4(t *testing.T) {
t.Parallel()
act, err := util.ReverseDNS("8.8.4.4", PTR)
assert.NilError(t, err)
assert.Equal(t, act, "4.4.8.8.in-addr.arpa.", "IPv4 reverse")
}
func TestIPv6(t *testing.T) {
t.Parallel()
act, err := util.ReverseDNS("2606:4700:4700::1111", PTR)
assert.NilError(t, err)
assert.Equal(t, act, "1.1.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.4.0.0.7.4.6.0.6.2.ip6.arpa.", "IPv6 reverse")
}
func TestNAPTR(t *testing.T) {
t.Parallel()
tests := []struct {
in string
want string
}{
{"1-800-555-1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."},
{"+1 800555 1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."},
{"+46766861004", "4.0.0.1.6.8.6.6.7.6.4.e164.arpa."},
{"17705551212", "2.1.2.1.5.5.5.0.7.7.1.e164.arpa."},
}
for _, test := range tests {
// Thanks Goroutines, very cool!
test := test
t.Run(test.in, func(t *testing.T) {
t.Parallel()
act, err := util.ReverseDNS(test.in, NAPTR)
assert.NilError(t, err)
assert.Equal(t, test.want, act)
})
}
}
func TestInvalid(t *testing.T) {
t.Parallel()
_, err := util.ReverseDNS("AAAAA", 1)
assert.ErrorContains(t, err, "invalid value AAAAA given")
}
func TestInvalid2(t *testing.T) {
t.Parallel()
_, err := util.ReverseDNS("1.0", PTR)
assert.ErrorContains(t, err, "PTR reverse")
}