Compare commits

...

32 commits
deno ... master

Author SHA1 Message Date
grumbulon a68821fc2a test2 2022-07-01 03:08:47 -04:00
grumbulon fb1c40d6d9 test 2022-07-01 03:07:46 -04:00
grumbulon d845808a3c I hate vscode 2022-06-29 21:07:57 -04:00
grumbulon a62f0b0759 upstream 2022-06-29 21:05:49 -04:00
Sam Therapy 5035898c13
Add a LICENSE
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-06-29 23:12:30 +02:00
grumbulon 98a14b1bf8 Merge logawl package into master (#10)
This PR is to bring in the new logging package for awl, I wanted to do some refinements before I opened this to upstream

logawl works in the following ways

1. It prints logs directly to std.err
1. The default log level is Info (this can be changed multiple ways)
1. It supports four log levels Error, Fatal, Info, Debug
1. It differs from the syslog package in the stdlib wherin it default to std.err you do not _need_ to define an out file (syslog for example)
1. I added a "debug" flag so now we can go through and define verbose logging through the app
5.5 I made it so we can call `Logger.debug("message")` anywhere in the query file but unless the debug flag is set to true it will not print a message (it is working as intended finally).

Co-authored-by: grumbulon <grumbulon@dismail.de>
Reviewed-on: sam/awl#10
Co-authored-by: grumbulon <grumbulon@grumbulon.xyz>
Co-committed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-06-29 22:57:03 +00:00
grumbulon 4353aa9559 Accidentally left something I was testing in..oops 2022-06-29 09:25:06 -04:00
grumbulon f7e5a7adee Added fatal logging, made doc for package better, and some minor changes 2022-06-29 09:22:28 -04:00
grumbulon be3b81029e Merge branch 'master' into master 2022-06-29 13:20:45 +00:00
Renovate Bot 5fab5a08a7 Update module github.com/stretchr/testify to v1.8.0 2022-06-29 06:00:21 -05:00
grumbulon dfea8060dd Merge branch 'master' into master 2022-06-28 18:20:24 +00:00
grumbulon 6d30a5ad9b Logging works bretty good now 2022-06-28 14:11:49 -04:00
grumbulon a36f1b8685 Added debuge flag, and added init for logawl 2022-06-28 12:12:52 -04:00
Renovate Bot d4b8d1469f Update module github.com/urfave/cli/v2 to v2.10.3 2022-06-28 11:48:23 -04:00
Renovate Bot f50b793931 Update module github.com/stretchr/testify to v1.7.5 2022-06-28 11:48:23 -04:00
Renovate Bot 4754097a40 Update module github.com/urfave/cli/v2 to v2.10.3 2022-06-27 21:00:34 -05:00
Renovate Bot 68c3345e9b Update module github.com/stretchr/testify to v1.7.5 2022-06-27 20:00:21 -05:00
grumbulon 68e6e54741 Accidentally reverted to old commit with dns.go 2022-06-26 14:16:38 -04:00
grumbulon 8b1a5f9147 Merge remote-tracking branch 'upstream/master'
Merging upstream into fork
2022-06-26 14:10:35 -04:00
grumbulon 6da6a55f6e Logging refinements 2022-06-26 14:01:13 -04:00
grumbulon 8c61f9f409 logging (#1)
Promoting branch to master

Co-authored-by: grumbulon <grumbulon@dismail.de>
Reviewed-on: #1
2022-06-26 04:07:58 +00:00
grumbulon 182ec20469 Revert "hehe off-by-one"
This reverts commit db78235ecd.

Reverting to old commit because I fucked up
2022-06-25 13:46:38 -04:00
Renovate Bot c0c5eb4a06 Update golang.org/x/net digest to 1bab6f3 2022-06-24 18:00:22 -05:00
Sam Therapy db78235ecd
hehe off-by-one
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-06-23 23:18:54 +02:00
Sam Therapy bf06f7a3aa
I am still somehow getting paid to do this 2022-06-23 23:12:48 +02:00
Renovate Bot 027514b4cc Update golang.org/x/net digest to 263ec57 2022-06-23 07:29:27 +00:00
grumbulon 589b13320a Cannot defined things twice in same package (#5)
Co-authored-by: grumbulon <grumbulon@grumbulon.xyz>
Co-committed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-06-23 07:29:06 +00:00
Sam Therapy 160c89ca1e
I got paid to write this 2022-06-22 22:59:17 +02:00
Renovate Bot ff78fa1e50 Add renovate.json 2022-06-22 11:55:52 +00:00
grumbulon 9b979667d6 Make everything more readable (#2)
1. I made JSON print indented instead of inline
2. I created a util package and moved `ReverseDNS` and `ResolveHTTPS` there
3. I created a new struct called Answers where all variables for the output live now, and adjusted the response struct to have the Answers struct in it. This lets us reuse the struct more instead of having variables floating around, lets us make the response more custom, offers more understandable code reuse, and makes things (I think) more readable.

Aside: you will notice the struct has little json defs. This is because struct values encode as JSON objects and can preserve the output name this way

Most of this just makes expanding on awl easier in the future.

Co-authored-by: grumbulon <grumbulon@dismail.de>
Reviewed-on: sam/awl#2
Co-authored-by: grumbulon <grumbulon@grumbulon.xyz>
Co-committed-by: grumbulon <grumbulon@grumbulon.xyz>
2022-06-22 11:55:38 +00:00
Sam Therapy 50d7b9dc2a
Rewrite in Go :^)
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-06-21 22:54:50 +02:00
Sam Therapy 8c11828529
lint, again
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-06-19 16:04:42 +02:00
36 changed files with 1375 additions and 916 deletions

View file

@ -1,13 +1,8 @@
kind: pipeline
type: docker
name: default
steps:
- name: lint
image: denoland/deno:debian
commands:
- deno fmt --check
- deno lint
- name: test
image: denoland/deno:debian
commands:
- deno test -A
kind: pipeline
type: docker
name: default
steps:
- name: test
image: golang
commands:
- go test ./...

154
.gitignore vendored
View file

@ -1,136 +1,18 @@
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.dccache
result.json
*.vtt
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work

View file

@ -1,5 +0,0 @@
{
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true
}

11
LICENCE Normal file
View file

@ -0,0 +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.
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

@ -1,5 +0,0 @@
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,4 +1,35 @@
# awl: drill, writ small (and using deno)
Install:
`deno install --allow-net https://git.froth.zone/sam/awl/raw/branch/master/awl.ts`
# awl
`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)
This was made as my first major experiment with Go, so there are probably things that can be improved
The excellent [dns](https://github.com/miekg/dns) library for Go does most of the heavy
lifting.
## What works
- UDP
- TCP
- TLS
- HTTPS (maybe)
- QUIC (extreme maybe)
## What doesn't
- Your sanity after reading my awful code
- A motivation for making this after finding q and doggo
## What should change
- Make the CLI less abysmal (migrate to [cobra](https://github.com/spf13/cobra)?
or just use stdlib's flags)
- Optimize everything
- Make the code less spaghetti (partially completed)
- Feature parity with drill
- Making a drop-in replacement for drill?

54
args.ts
View file

@ -1,54 +0,0 @@
import { Args } from "./deps.ts";
import { isRecordType, ServerOptions } from "./lib/utils.ts";
/**
* A handler for parsing the arguments passed in
* @param {ServerOptions} server - The DNS server to query
* @param {Deno.RecordType} type - The type of DNS request, see Deno.RecordType for more info
* @param {string} name - Server to look up
*/
export type arguments = {
server?: ServerOptions;
type?: Deno.RecordType;
name?: string;
};
/**
* @param {Args} args - The arguments, directly passed in
* @returns {arguments} The arguments, parsed
*/
export function parseArgs(args: Args): arguments {
const parsed: arguments = {} as arguments;
args._.forEach((arg) => {
arg = arg.toString();
// if it starts with an @, it's a server
if (arg.includes("@")) {
parsed.server = {
server: arg.split("@").pop() as string,
port: args.port,
};
return;
}
// if there is a dot, it's a name
if (arg.includes(".")) {
parsed.name = arg;
return;
}
if (isRecordType(arg)) {
parsed.type = arg.toUpperCase() as Deno.RecordType;
return;
}
// if all else fails, assume it's a name
parsed.name = arg;
});
// Add a . to the end of the name if it's not there
if (parsed.name?.charAt(parsed.name.length - 1) !== ".") {
parsed.name = parsed.name?.concat(".");
}
return parsed;
}

17
awl.go Normal file
View file

@ -0,0 +1,17 @@
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"fmt"
"os"
)
func main() {
app := prepareCLI()
err := app.Run(os.Args)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

121
awl.ts
View file

@ -1,121 +0,0 @@
// SPDX-License-Identifier: MIT
import { bold, italic, parse, underline } from "./deps.ts";
import { QueryResponse, ServerOptions } from "./lib/utils.ts";
import { doQuery } from "./lib/query.ts";
import { parseResponse } from "./lib/response.ts";
import { parseArgs } from "./args.ts";
import { parseNAPTR, parsePTR } from "./lib/reverse.ts";
async function main() {
// Parse args
const args = parse(Deno.args, {
alias: {
h: "help",
p: "port",
s: "short",
x: "ptr",
V: "version",
},
boolean: ["help", "ptr", "short", "version"],
string: ["port"],
default: {
"port": "53",
},
});
if (args.help) args.version = true;
if (args.version) {
console.log(
`${
bold("awl")
} version 0.1.0 (running with deno ${Deno.version.deno}, TypeScript ${Deno.version.typescript}, on V8 ${Deno.version.v8})
Written by (YOUR NAME GOES HERE)`,
);
}
if (args.help) {
console.log(
` ${bold("Usage:")} awl name ${italic("type")} ${italic("@server")}
${bold("<name>")} domain name
${bold("<type>")} defaults to A
${bold("<@server>")} defaults to your local resolver
Order ${bold("DOES NOT")} matter\n
`,
`${underline("Options")}:
-p <port> use <port> for query, defaults to 53
-s Equivalent to dig +short, only return addresses
-x do a reverse lookup
-h print this helpful guide
-V get the version
`,
);
}
if (args.version) Deno.exit(0);
const parsedArgs = parseArgs(args);
let domain = parsedArgs.name || ".";
let query = parsedArgs.type || "A";
if (domain === ".") query = "NS";
if (query === "PTR") {
// The "server" is an IP address, it needs to become a canonical domain
domain = parsePTR(domain);
} else if (query === "NAPTR") {
// The "server" is a phone number, it needs to become a canonical domain
domain = parseNAPTR(domain);
}
const server = parsedArgs.server || { server: "", port: 53 };
const response: QueryResponse = await doQuery(domain, query, server);
if (!args.short) {
console.log(
`;; ->>HEADER<<- opcode: QUERY, rcode: ${response.response}
;; QUESTION SECTION:
;; ${domain} IN ${query}
;; ANSWER SECTION:`,
);
}
if (response.response === "NOERROR") {
const res = parseResponse(response, domain, query, args.short);
res.forEach((answer) => {
console.log(answer);
});
}
if (!args.short) {
console.log(`
;; ADDITONAL SECTION:
;; Query time: ${response.time} msec
;; SERVER: ${displayServer(server)}
;; WHEN: ${new Date().toLocaleString()}
`);
}
}
/**
* A handler for displaying the server
* @param {ServerOptions} server - The DNS server used
* @returns {string} The string used
*/
function displayServer(server: ServerOptions): string {
let val = "";
if (server.server) {
val += server.server;
}
if (server.port != 53) {
val += `#${server.port}`;
}
if (!val) val = "System";
return val;
}
if (import.meta.main) {
await main();
}

200
cli.go Normal file
View file

@ -0,0 +1,200 @@
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"fmt"
"math/rand"
"runtime"
"strings"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"github.com/urfave/cli/v2"
"golang.org/x/net/idna"
)
// Do all the magic CLI crap
func prepareCLI() *cli.App {
// Custom version string
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("%s version %s, built with %s\n", c.App.Name, c.App.Version, runtime.Version())
}
cli.VersionFlag = &cli.BoolFlag{
Name: "v",
Usage: "show version and exit",
}
cli.HelpFlag = &cli.BoolFlag{
Name: "h",
Usage: "show this help and exit",
}
// Hack to get rid of the annoying default on the CLI
oldFlagStringer := cli.FlagStringer
cli.FlagStringer = func(f cli.Flag) string {
return strings.TrimSuffix(oldFlagStringer(f), " (default: false)")
}
cli.AppHelpTemplate = `{{.Name}} - {{.Usage}}
Usage: {{.HelpName}} name [@server] [record]
<name> can be a name or an IP address
<record> defaults to A
arguments can be in any order
{{if .VisibleFlags}}
Options:
{{range .VisibleFlags}}{{.}}
{{end}}{{end}}`
app := &cli.App{
Name: "awl",
Usage: "drill, writ small",
Version: "v0.2.1",
Flags: []cli.Flag{
&cli.IntFlag{
Name: "port",
Aliases: []string{"p"},
Usage: "`<port>` to make DNS query",
DefaultText: "53 over plain TCP/UDP, 853 over TLS or QUIC",
},
&cli.BoolFlag{
Name: "4",
Usage: "force IPv4",
},
&cli.BoolFlag{
Name: "6",
Usage: "force IPv6",
},
&cli.BoolFlag{
Name: "dnssec",
Aliases: []string{"D"},
Usage: "enable DNSSEC",
},
&cli.BoolFlag{
Name: "json",
Aliases: []string{"j"},
Usage: "return the result(s) as JSON",
},
&cli.BoolFlag{
Name: "short",
Aliases: []string{"s"},
Usage: "print just the results, equivalent to dig +short",
},
&cli.BoolFlag{
Name: "tcp",
Aliases: []string{"t"},
Usage: "use TCP (default: use UDP)",
},
&cli.BoolFlag{
Name: "tls",
Aliases: []string{"T"},
Usage: "use DNS-over-TLS",
},
&cli.BoolFlag{
Name: "https",
Aliases: []string{"H"},
Usage: "use DNS-over-HTTPS",
},
&cli.BoolFlag{
Name: "quic",
Aliases: []string{"Q"},
Usage: "use DNS-over-QUIC",
},
&cli.BoolFlag{
Name: "no-truncate",
Usage: "ignore truncation if a UDP request truncates (default: retry with TCP)",
},
&cli.BoolFlag{
Name: "aa",
Usage: "set AA (Authoratative Answer) flag (default: not set)",
},
&cli.BoolFlag{
Name: "tc",
Usage: "set tc (TrunCated) flag (default: not set)",
},
&cli.BoolFlag{
Name: "z",
Usage: "set Z (Zero) flag (default: not set)",
},
&cli.BoolFlag{
Name: "cd",
Usage: "set CD (Checking Disabled) flag (default: not set)",
},
&cli.BoolFlag{
Name: "no-rd",
Usage: "UNset RD (Recursion Desired) flag (default: set)",
},
&cli.BoolFlag{
Name: "no-ra",
Usage: "UNset RA (Recursion Available) flag (default: set)",
},
&cli.BoolFlag{
Name: "reverse",
Aliases: []string{"x"},
Usage: "do a reverse lookup",
},
&cli.BoolFlag{
Name: "debug",
Usage: "enable debug logging",
Value: false,
},
},
Action: doQuery,
}
return app
}
// Parse the wildcard arguments, drill style
func parseArgs(args []string) (util.Answers, error) {
var (
resp util.Response
err error
)
for _, arg := range args {
r, ok := dns.StringToType[strings.ToUpper(arg)]
switch {
// If it starts with @, it's a DNS server
case strings.HasPrefix(arg, "@"):
resp.Answers.Server = strings.Split(arg, "@")[1]
case strings.Contains(arg, "."):
resp.Answers.Name, err = idna.ToUnicode(arg)
if err != nil {
return util.Answers{}, err
}
case ok:
// If it's a DNS request, it's a DNS request (obviously)
resp.Answers.Request = r
default:
//else, assume it's a name
resp.Answers.Name, err = idna.ToUnicode(arg)
if err != nil {
return util.Answers{}, err
}
}
}
// If nothing was set, set a default
if resp.Answers.Name == "" {
resp.Answers.Name = "."
if resp.Answers.Request == 0 {
resp.Answers.Request = dns.StringToType["NS"]
}
} else {
if resp.Answers.Request == 0 {
resp.Answers.Request = dns.StringToType["A"]
}
}
if resp.Answers.Server == "" {
resolv, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil { // Query Google by default, needed for Windows since the DNS library doesn't support Windows
// TODO: Actually find where windows stuffs its dns resolvers
resp.Answers.Server = "8.8.4.4"
} else {
resp.Answers.Server = resolv.Servers[rand.Intn(len(resolv.Servers))]
}
}
return util.Answers{Server: resp.Answers.Server, Request: resp.Answers.Request, Name: resp.Answers.Name}, nil
}

42
cli_test.go Normal file
View file

@ -0,0 +1,42 @@
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"testing"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestApp(t *testing.T) {
app := prepareCLI()
// What more can even be done lmao
require.NotNil(t, app)
}
func TestArgParse(t *testing.T) {
tests := []struct {
in []string
want util.Answers
}{
{
[]string{"@::1", "localhost", "AAAA"},
util.Answers{Server: "::1", Request: dns.TypeAAAA, Name: "localhost"},
},
{
[]string{"@1.0.0.1", "google.com"},
util.Answers{Server: "1.0.0.1", Request: dns.TypeA, Name: "google.com"},
},
{
[]string{"@8.8.4.4"},
util.Answers{Server: "8.8.4.4", Request: dns.TypeNS, Name: "."},
},
}
for _, test := range tests {
act, err := parseArgs(test.in)
assert.Nil(t, err)
assert.Equal(t, test.want, act)
}
}

View file

@ -1,7 +0,0 @@
export { parse } from "https://deno.land/std@0.144.0/flags/mod.ts";
export type { Args } from "https://deno.land/std@0.144.0/flags/mod.ts";
export {
bold,
italic,
underline,
} from "https://deno.land/std@0.139.0/fmt/colors.ts";

12
docs.go Normal file
View file

@ -0,0 +1,12 @@
/*
awl is a DNS lookup tool written in Go,
similar to (and heavily inspired by) drill.
It runs and displays similar outputs to drill, without any frills.
Options are given to print with JSON
Supports results from DNS-over-[UDP, TCP, TLS, HTTPS, QUIC] servers
Why use this over the alternatives? Good question.
*/
package main

37
go.mod Normal file
View file

@ -0,0 +1,37 @@
module git.froth.zone/sam/awl
go 1.18
require (
github.com/lucas-clemente/quic-go v0.27.2
github.com/miekg/dns v1.1.50
github.com/urfave/cli/v2 v2.10.3
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/cheekybits/genny v1.0.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/testify v1.8.0
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.11 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)

312
go.sum Normal file
View file

@ -0,0 +1,312 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
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/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
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.3.1/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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucas-clemente/quic-go v0.27.2 h1:zsMwwniyybb8B/UDNXRSYee7WpQJVOcjQEGgpw2ikXs=
github.com/lucas-clemente/quic-go v0.27.2/go.mod h1:vXgO/11FBSKM+js1NxoaQ/bPtVFYfB7uxhfHXyMhl1A=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ=
github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
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.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
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/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/urfave/cli/v2 v2.10.3 h1:oi571Fxz5aHugfBAJd5nkwSk3fzATXtMlpxdLylSCMo=
github.com/urfave/cli/v2 v2.10.3/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/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-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-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-20210615035016-665e8c7367d1/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-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
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/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
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.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.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
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/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
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/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View file

@ -1,65 +0,0 @@
// SPDX-License-Identifier: MIT
import { QueryResponse, ServerOptions } from "./utils.ts";
/**
* @param domain Domain to query
* @param query
* @param server {@link utils.ts/ServerOptions}
* @returns
*/
export async function doQuery(
domain: string,
query: Deno.RecordType,
server?: ServerOptions,
) {
const response: QueryResponse = {} as QueryResponse;
if (!server?.server) {
const t0 = performance.now();
await Deno.resolveDns(domain, query)
// If there's no error
.then((value) => {
const t1 = performance.now();
response.time = t1 - t0;
response.response = "NOERROR";
response.dnsResponse = value;
})
// If there is an error
.catch((e: Error) => {
const t1 = performance.now();
response.time = t1 - t0;
switch (e.name) {
case "NotFound":
response.response = "NXDOMAIN";
break;
default:
console.dir(e);
response.response = "SERVFAIL";
}
});
} else {
const t0 = performance.now();
await Deno.resolveDns(domain, query, {
nameServer: { "ipAddr": server.server, "port": server.port },
})
// If there's no error
.then((value) => {
const t1 = performance.now();
response.time = t1 - t0;
response.response = "NOERROR";
response.dnsResponse = value;
})
// If there is an error
.catch((e: Error) => {
const t1 = performance.now();
response.time = t1 - t0;
switch (e.name) {
case "NotFound":
response.response = "NXDOMAIN";
break;
default:
response.response = "SERVFAIL";
}
});
}
return response;
}

View file

@ -1,117 +0,0 @@
// SPDX-License-Identifier: MIT
import {
isCAA,
isMX,
isNAPTR,
isSOA,
isSRV,
isTXT,
QueryResponse,
} from "./utils.ts";
/**
* @param res A DNS {@link QueryResponse}
* @param domain The domain (or IP, if doing it in reverse) queried
* @param query The DNS query that was queried
* @returns {string} The DNS response, put in canonical form
*/
export function parseResponse(
res: QueryResponse,
domain: string,
query: string,
short: boolean,
): string[] {
const answer: string[] = [];
switch (query) {
case "A":
case "AAAA":
case "CNAME":
case "NS":
case "PTR":
res.dnsResponse.forEach((ip) => {
let dnsQuery = "";
if (!short) dnsQuery += `${domain} IN ${query} `;
dnsQuery += `${ip}`;
answer.push(dnsQuery);
});
break;
case "MX":
if (isMX(res.dnsResponse)) {
res.dnsResponse.forEach((record) => {
let dnsQuery = "";
if (!short) {
dnsQuery += `${domain} IN ${query} `;
}
dnsQuery += `${record.preference} ${record.exchange}`;
answer.push(dnsQuery);
});
}
break;
case "CAA":
if (isCAA(res.dnsResponse)) {
res.dnsResponse.forEach((record) => {
let dnsQuery = "";
if (!short) {
dnsQuery += `${domain} IN ${query} `;
}
dnsQuery += `${
record.critical ? "1" : "0"
} ${record.tag} "${record.value}"`;
answer.push(dnsQuery);
});
}
break;
case "NAPTR":
if (isNAPTR(res.dnsResponse)) {
res.dnsResponse.forEach((record) => {
let dnsQuery = "";
if (!short) dnsQuery += `${domain} IN ${query} `;
dnsQuery +=
`${record.order} ${record.preference} "${record.flags}" "${record.services}" ${record.regexp} ${record.replacement}`;
answer.push(dnsQuery);
});
}
break;
case "SOA":
if (isSOA(res.dnsResponse)) {
res.dnsResponse.forEach((record) => {
let dnsQuery = "";
if (!short) dnsQuery += `${domain} IN ${query} `;
dnsQuery +=
`${record.mname} ${record.rname} ${record.serial} ${record.refresh} ${record.retry} ${record.expire} ${record.minimum}`;
answer.push(dnsQuery);
});
}
break;
case "SRV":
if (isSRV(res.dnsResponse)) {
res.dnsResponse.forEach((record) => {
let dnsQuery = "";
if (!short) dnsQuery += `${domain} IN ${query} `;
dnsQuery +=
`${record.priority} ${record.weight} ${record.port} ${record.target}`;
answer.push(dnsQuery);
});
}
break;
case "TXT":
if (isTXT(res.dnsResponse)) {
res.dnsResponse.forEach((record) => {
let dnsQuery = "";
let txt = "";
record.forEach((value) => {
txt += `"${value}"`;
});
if (!short) {
dnsQuery += `${domain} IN ${query} `;
}
dnsQuery += `${txt}`;
answer.push(dnsQuery);
});
}
break;
default:
throw new Error("Not yet implemented");
}
return answer;
}

View file

@ -1,73 +0,0 @@
// SPDX-License-Identifier: MIT
export function parsePTR(ip: string) {
if (ip.includes(".")) {
// It's an IPv4 address
const ptr = ip.split(".");
let pop: string | undefined = "not undefined";
let domain = "";
do {
pop = ptr.pop();
if (pop) {
domain += `${pop}.`;
}
} while (pop !== undefined);
domain += "in-addr.arpa";
return domain;
} else if (ip.includes(":")) {
const parsedIP = parseIPv6(ip);
const ptr = parsedIP.split(":");
// It's an IPv6 address
let pop: string[] | undefined = ["e"];
let domain = "";
do {
pop = ptr.pop()?.split("").reverse();
if (pop) {
for (const part of pop) {
domain += `${part}.`;
}
}
} while (pop !== undefined);
domain += "ip6.arpa";
return domain;
} else {
// It's not an address
return "";
}
}
export function parseIPv6(addr: string) {
addr = addr.replace(/^:|:$/g, "");
const ipv6 = addr.split(":");
for (let i = 0; i < ipv6.length; i++) {
let hex: string | string[] = ipv6[i];
if (hex != "") {
// normalize leading zeros
// TODO: make this not deprecated
ipv6[i] = ("0000" + hex).substr(-4);
} else {
// normalize grouped zeros ::
hex = [];
for (let j = ipv6.length; j <= 8; j++) {
hex.push("0000");
}
ipv6[i] = hex.join(":");
}
}
return ipv6.join(":");
}
export function parseNAPTR(phNum: string) {
phNum = phNum.toString();
phNum = phNum.replace("+", "").replaceAll(" ", "").replaceAll("-", "");
const rev = phNum.split("").reverse();
let ptr = "";
rev.forEach((n) => {
ptr += `${n}.`;
});
ptr += "e164.arpa";
return ptr;
}

View file

@ -1,90 +0,0 @@
// SPDX-License-Identifier: MIT
/**
* A DNS response
*/
export type QueryResponse = {
dnsResponse:
| string[]
| Deno.CAARecord[]
| Deno.MXRecord[]
| Deno.NAPTRRecord[]
| Deno.SOARecord[]
| Deno.SRVRecord[]
| string[][];
response: string;
time: number;
};
/**
* Options for which DNS server to query
*/
export type ServerOptions = {
server: string;
port?: number;
};
export function isRecordType(type: string): type is Deno.RecordType {
return type.toUpperCase() === "A" || type.toUpperCase() === "AAAA" || type.toUpperCase() === "CNAME" || type.toUpperCase() === "MX" ||
type.toUpperCase() === "NS" || type.toUpperCase() === "PTR" || type.toUpperCase() === "SOA" || type.toUpperCase() === "TXT" ||
type.toUpperCase() === "NAPTR" || type.toUpperCase() === "SRV" || type.toUpperCase() === "CAA";
}
/**
* Test if the DNS query is an MX record
* @param {QueryResponse["dnsResponse"]} record - DNS response
* @returns {boolean} - true if the record is an MX record
*/
export function isMX(
record: QueryResponse["dnsResponse"],
): record is Deno.MXRecord[] {
return (record as unknown as Deno.MXRecord[])[0].exchange !== undefined;
}
/**
* Test if the DNS query is a CAA record
* @param {QueryResponse["dnsResponse"]} record - DNS response
* @returns {boolean} - true if the record is a CAA record
*/
export function isCAA(
record: QueryResponse["dnsResponse"],
): record is Deno.CAARecord[] {
return (record as unknown as Deno.CAARecord[])[0].critical !== undefined;
}
/**
* Test if the DNS query is an NAPTR record
* @param {QueryResponse["dnsResponse"]} record - DNS response
* @returns {boolean} - true if the record is an NAPTR record
*/
export function isNAPTR(
record: QueryResponse["dnsResponse"],
): record is Deno.NAPTRRecord[] {
return (record as unknown as Deno.NAPTRRecord[])[0].regexp !== undefined;
}
/**
* Test if the DNS query is an SOA record
* @param {QueryResponse["dnsResponse"]} record - DNS response
* @returns {boolean} - true if the record is an SOA record
*/
export function isSOA(
record: QueryResponse["dnsResponse"],
): record is Deno.SOARecord[] {
return (record as unknown as Deno.SOARecord[])[0].rname !== undefined;
}
/**
* Test if the DNS query is an SRV record
* @param {QueryResponse["dnsResponse"]} record - DNS response
* @returns {boolean} - true if the record is an SRV record
*/
export function isSRV(
record: QueryResponse["dnsResponse"],
): record is Deno.SRVRecord[] {
return (record as unknown as Deno.SRVRecord[])[0].port !== undefined;
}
export function isTXT(
record: QueryResponse["dnsResponse"],
): record is string[][] {
return Array.isArray(record as unknown as Array<string>);
}

30
logawl/doc.go Normal file
View file

@ -0,0 +1,30 @@
/*
LogAwl is a package for custom logging needs
LogAwl extends the standard log library with support for log levels
This is _different_ from the syslog package in the standard library because you do not define a file
because awl is a cli utility it writes directly to std err.
*/
// Use the New() function to init logawl
//
// logger := logawl.New()
//
// You can call specific logging levels from your new logger using
//
// logger.Debug("Message to log")
// logger.Fatal("Message to log")
// logger.Info("Message to log")
// logger.Error("Message to log")
//
// You may also set the log level on the fly with
//
// Logger.SetLevel(3)
// This allows you to change the default level (Info) and prevent log messages from being posted at higher verbosity levels
//for example if
// Logger.SetLevel(3)
// is not called and you call
// Logger.Debug()
// this runs through
// IsLevel(level)
// to verify if the debug log should be sent to std.Err or not based on the current expected log level
package logawl

30
logawl/docs.go Normal file
View file

@ -0,0 +1,30 @@
/*
LogAwl is a package for custom logging needs
LogAwl extends the standard log library with support for log levels
This is _different_ from the syslog package in the standard library because you do not define a file
because awl is a cli utility it writes directly to std err.
*/
// Use the New() function to init logawl
//
// logger := logawl.New()
//
// You can call specific logging levels from your new logger using
//
// logger.Debug("Message to log")
// logger.Fatal("Message to log")
// logger.Info("Message to log")
// logger.Error("Message to log")
//
// You may also set the log level on the fly with
//
// Logger.SetLevel(3)
// This allows you to change the default level (Info) and prevent log messages from being posted at higher verbosity levels
//for example if
// Logger.SetLevel(3)
// is not called and you call
// Logger.Debug()
// this runs through
// IsLevel(level)
// to verify if the debug log should be sent to std.Err or not based on the current expected log level
package logawl

74
logawl/logawl.go Normal file
View file

@ -0,0 +1,74 @@
// SPDX-License-Identifier: BSD-3-Clause
package logawl
import (
"fmt"
"io"
"sync"
"sync/atomic"
)
type Level int32
type Logger struct {
Mu sync.Mutex
Level Level
Prefix string
Out io.Writer
buf []byte
isDiscard int32
}
// Stores whatever input value is in mem address of l.level
func (l *Logger) SetLevel(level Level) {
atomic.StoreInt32((*int32)(&l.Level), int32(level))
}
// Mostly nothing
func (l *Logger) GetLevel() Level {
return l.level()
}
// Retrieves whatever was stored in mem address of l.level
func (l *Logger) level() Level {
return Level(atomic.LoadInt32((*int32)(&l.Level)))
}
// Unmarshalls the int value of level for writing the header
func (l *Logger) UnMarshalLevel(lv Level) (string, error) {
switch lv {
case 0:
return "FATAL ", nil
case 1:
return "ERROR ", nil
case 2:
return "INFO ", nil
case 3:
return "DEBUG ", nil
}
return "", fmt.Errorf("Invalid log level choice")
}
func (l *Logger) IsLevel(level Level) bool {
return l.level() >= level
}
var AllLevels = []Level{
FatalLevel,
ErrorLevel,
InfoLevel,
DebugLevel,
}
const (
// Fatal logs (will call exit(1))
FatalLevel Level = iota
// Error logs
ErrorLevel
// What is going on level
InfoLevel
// Verbose log level.
DebugLevel
)

132
logawl/logger.go Normal file
View file

@ -0,0 +1,132 @@
// SPDX-License-Identifier: BSD-3-Clause
package logawl
import (
"fmt"
"os"
"sync/atomic"
"time"
)
// Calling New instantiates Logger
//
// Level can be changed to one of the other log levels (FatalLevel, ErrorLevel, InfoLevel, DebugLevel)
func New() *Logger {
return &Logger{
Out: os.Stderr,
Level: InfoLevel, //Default value is InfoLevel
}
}
// 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 {
return
}
//If verbose is not set --debug etc print _nothing_
if l.IsLevel(level) {
switch level { //Goes through log levels and does stuff based on them (Fatal os.Exit...etc)
case 0:
l.Printer(0, fmt.Sprintln(v...)) //Fatal level
os.Exit(1)
case 1:
l.Printer(1, fmt.Sprintln(v...)) //Error level
os.Exit(2)
case 2:
l.Printer(2, fmt.Sprintln(v...)) //Info level
case 3:
l.Printer(3, fmt.Sprintln(v...)) //Debug level
default:
break
}
}
}
// 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) {
if lvl, err := l.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...)
year, month, day := t.Date()
*buf = append(*buf, '[')
formatter(buf, year, 4)
*buf = append(*buf, '/')
formatter(buf, int(month), 2)
*buf = append(*buf, '/')
formatter(buf, day, 2)
*buf = append(*buf, ' ')
hour, min, sec := t.Clock()
formatter(buf, hour, 2)
*buf = append(*buf, ':')
formatter(buf, min, 2)
*buf = append(*buf, ':')
formatter(buf, sec, 2)
*buf = append(*buf, ']')
*buf = append(*buf, ':')
*buf = append(*buf, ' ')
} else {
fmt.Printf("Unable to unmarshal log level: %v", err)
os.Exit(2) //Fucking kill him
}
}
// Printer prints the formatted message directly to stdErr
func (l *Logger) Printer(level Level, s string) error {
now := time.Now()
var line int
l.Mu.Lock()
defer l.Mu.Unlock()
l.buf = l.buf[:0]
l.formatHeader(&l.buf, now, line, level)
l.buf = append(l.buf, s...)
if len(s) == 0 || s[len(s)-1] != '\n' {
l.buf = append(l.buf, '\n')
}
_, err := l.Out.Write(l.buf)
return err
}
// Some line formatting stuff from Golang log stdlib file
//
// Please view https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/log/log.go;drc=41e1d9075e428c2fc32d966b3752a3029b620e2c;l=96
//
// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
func formatter(buf *[]byte, i int, wid int) {
// Assemble decimal in reverse order.
var b [20]byte
bp := len(b) - 1
for i >= 10 || wid > 1 {
wid--
q := i / 10
b[bp] = byte('0' + i - q*10)
bp--
i = q
}
// i < 10
b[bp] = byte('0' + i)
*buf = append(*buf, b[bp:]...)
}
// Call print directly with Debug level
func (l *Logger) Debug(v ...any) {
l.Println(DebugLevel, v...)
}
// Call print directly with Info level
func (l *Logger) Info(v ...any) {
l.Println(InfoLevel, v...)
}
// Call print directly with Error level
func (l *Logger) Error(v ...any) {
l.Println(ErrorLevel, v...)
}
// Call print directly with Fatal level
func (l *Logger) Fatal(v ...any) {
l.Println(FatalLevel, v...)
}

8
mod.ts
View file

@ -1,8 +0,0 @@
// SPDX-License-Identifier: MIT
// Exports for lawl, the library for awl
export type { QueryResponse, ServerOptions } from "./lib/utils.ts";
export { isRecordType } from "./lib/utils.ts";
export { doQuery } from "./lib/query.ts";
export { parseResponse } from "./lib/response.ts";
export { parseIPv6, parseNAPTR, parsePTR } from "./lib/reverse.ts";

186
query.go Normal file
View file

@ -0,0 +1,186 @@
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"encoding/json"
"fmt"
"net"
"strconv"
"strings"
"time"
"git.froth.zone/sam/awl/logawl"
"git.froth.zone/sam/awl/query"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"github.com/urfave/cli/v2"
)
func doQuery(c *cli.Context) error {
var (
err error
resp util.Response
isHTTPS bool
Logger = logawl.New() //init logger
)
resp.Answers, err = parseArgs(c.Args().Slice())
if err != nil {
Logger.Error("Unable to parse args")
return err
}
port := c.Int("port")
if c.Bool("debug") {
Logger.SetLevel(3)
}
Logger.Debug("Starting awl")
// If port is not set, set it
if port == 0 {
if c.Bool("tls") || c.Bool("quic") {
port = 853
} else {
port = 53
}
}
if c.Bool("https") || strings.HasPrefix(resp.Answers.Server, "https://") {
// add https:// if it doesn't already exist
if !strings.HasPrefix(resp.Answers.Server, "https://") {
resp.Answers.Server = "https://" + resp.Answers.Server
}
isHTTPS = true
} else {
resp.Answers.Server = net.JoinHostPort(resp.Answers.Server, strconv.Itoa(port))
}
// Process the IP/Phone number so a PTR/NAPTR can be done
if c.Bool("reverse") {
if dns.TypeToString[resp.Answers.Request] == "A" {
resp.Answers.Request = dns.StringToType["PTR"]
}
resp.Answers.Name, err = util.ReverseDNS(resp.Answers.Name, dns.TypeToString[resp.Answers.Request])
if err != nil {
return err
}
}
// if the domain is not canonical, make it canonical
if !strings.HasSuffix(resp.Answers.Name, ".") {
resp.Answers.Name = fmt.Sprintf("%s.", resp.Answers.Name)
}
msg := new(dns.Msg)
msg.SetQuestion(resp.Answers.Name, resp.Answers.Request)
// Make this authoritative (does this do anything?)
if c.Bool("aa") {
msg.Authoritative = true
}
// Set truncated flag (why)
if c.Bool("tc") {
msg.Truncated = true
}
// Set the zero flag if requested (does nothing)
if c.Bool("z") {
Logger.Debug("Setting message to zero")
msg.Zero = true
}
// Disable DNSSEC validation
if c.Bool("cd") {
msg.CheckingDisabled = true
}
// Disable wanting recursion
if c.Bool("no-rd") {
msg.RecursionDesired = false
}
// Disable recursion being available (I don't think this does anything)
if c.Bool("no-ra") {
msg.RecursionAvailable = false
}
// Set DNSSEC if requested
if c.Bool("dnssec") {
Logger.Debug("Using DNSSEC")
msg.SetEdns0(1232, true)
}
var in *dns.Msg
// Make the DNS request
if isHTTPS {
in, resp.Answers.RTT, err = query.ResolveHTTPS(msg, resp.Answers.Server)
} else if c.Bool("quic") {
in, resp.Answers.RTT, err = query.ResolveQUIC(msg, resp.Answers.Server)
} else {
d := new(dns.Client)
// Set TCP/UDP, depending on flags
if c.Bool("tcp") || c.Bool("tls") {
d.Net = "tcp"
} else {
d.Net = "udp"
}
// Set IPv4 or IPv6, depending on flags
switch {
case c.Bool("4"):
d.Net += "4"
case c.Bool("6"):
d.Net += "6"
}
// Add TLS, if requested
if c.Bool("tls") {
d.Net += "-tls"
}
in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server)
if err != nil {
return err
}
// If UDP truncates, use TCP instead (unless truncation is to be ignored)
if in.MsgHdr.Truncated && !c.Bool("no-truncate") {
fmt.Printf(";; Truncated, retrying with TCP\n\n")
d.Net = "tcp"
switch {
case c.Bool("4"):
d.Net += "4"
case c.Bool("6"):
d.Net += "6"
}
in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server)
}
}
if err != nil {
return err
}
if c.Bool("json") {
json, err := json.MarshalIndent(in, "", " ")
if err != nil {
return err
}
fmt.Println(string(json))
} else {
if !c.Bool("short") {
// Print everything
fmt.Println(in)
fmt.Println(";; Query time:", resp.Answers.RTT)
fmt.Println(";; SERVER:", resp.Answers.Server)
fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123Z))
fmt.Println(";; MSG SIZE rcvd:", in.Len())
} else {
// Print just the responses, nothing else
for _, res := range in.Answer {
temp := strings.Split(res.String(), "\t")
fmt.Println(temp[len(temp)-1])
}
}
}
return nil
}

56
query/HTTPS.go Normal file
View file

@ -0,0 +1,56 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"bytes"
"fmt"
"io"
"net/http"
"time"
"github.com/miekg/dns"
)
// Resolve a DNS-over-HTTPS query
//
// Currently only supports POST requests
func ResolveHTTPS(msg *dns.Msg, server string) (*dns.Msg, time.Duration, error) {
httpR := &http.Client{}
buf, err := msg.Pack()
if err != nil {
return nil, 0, err
}
// query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf)
req, err := http.NewRequest("POST", server, bytes.NewBuffer(buf))
if err != nil {
return nil, 0, fmt.Errorf("DoH: %s", err.Error())
}
req.Header.Set("Content-Type", "application/dns-message")
req.Header.Set("Accept", "application/dns-message")
now := time.Now()
res, err := httpR.Do(req)
rtt := time.Since(now)
if err != nil {
return nil, 0, fmt.Errorf("DoH HTTP request error: %s", err.Error())
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, 0, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode)
}
fullRes, err := io.ReadAll(res.Body)
if err != nil {
return nil, 0, fmt.Errorf("DoH body read error: %s", err.Error())
}
response := dns.Msg{}
err = response.Unpack(fullRes)
if err != nil {
return nil, 0, fmt.Errorf("DoH dns message unpack error: %s", err.Error())
}
return &response, rtt, nil
}

63
query/QUIC.go Normal file
View file

@ -0,0 +1,63 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"crypto/tls"
"io"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/miekg/dns"
)
// Resolve DNS over QUIC, the hip new standard (for privacy I think, IDK)
func ResolveQUIC(msg *dns.Msg, server string) (*dns.Msg, time.Duration, error) {
tls := &tls.Config{
NextProtos: []string{"doq"},
}
connection, err := quic.DialAddr(server, tls, nil)
if err != nil {
return nil, 0, err
}
// Compress request to over-the-wire
buf, err := msg.Pack()
if err != nil {
return nil, 0, err
}
t := time.Now()
stream, err := connection.OpenStream()
if err != nil {
return nil, 0, err
}
_, err = stream.Write(buf)
if err != nil {
return nil, 0, err
}
fullRes, err := io.ReadAll(stream)
if err != nil {
return nil, 0, err
}
rtt := time.Since(t)
// Close with error: no error
err = connection.CloseWithError(0, "")
if err != nil {
return nil, 0, err
}
err = stream.Close()
if err != nil {
return nil, 0, err
}
response := dns.Msg{}
err = response.Unpack(fullRes)
if err != nil {
return nil, 0, err
}
return &response, rtt, nil
}

2
query/docs.go Normal file
View file

@ -0,0 +1,2 @@
// Package for the special query types
package query

8
renovate.json Normal file
View file

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

View file

@ -1,59 +0,0 @@
// SPDX-License-Identifier: MIT
import { assertEquals } from "./testDeps.ts";
import { doQuery } from "../lib/query.ts";
Deno.test("Get localhost", async () => {
const res = await doQuery("localhost", "A");
assertEquals(res.dnsResponse, ["127.0.0.1"]);
assertEquals(res.response, "NOERROR");
});
Deno.test("Get localhost, external NS", async () => {
const res = await doQuery("localhost", "AAAA", { server: "1.1.1.1" });
assertEquals(res.dnsResponse, ["::1"]);
assertEquals(res.response, "NOERROR");
});
Deno.test("PTR localhost", async () => {
const res = await doQuery("1.0.0.127.in-addr.arpa.", "PTR");
assertEquals(res.dnsResponse, ["localhost."]);
});
// This test will fail if this random Bri ish phone number goes down
// It's also unreliable, so it's disabled
// Deno.test("NAPTR, Remote",async () => {
// const res = await doQuery("4.4.2.2.3.3.5.6.8.1.4.4.e164.arpa.", "NAPTR");
// assertStrictEquals(res.dnsResponse, [
// {
// order: 100,
// preference: 10,
// flags: "u",
// services: "E2U+sip",
// regexp: "!^\\+441865332(.*)$!sip:\\1@nominet.org.uk!",
// replacement: "."
// },
// {
// order: 100,
// preference: 20,
// flags: "u",
// services: "E2U+pstn:tel",
// regexp: "!^(.*)$!tel:\\1!",
// replacement: "."
// }
// ])
// })
Deno.test("Get invalid IP, regular NS", async () => {
const res = await doQuery("l", "A");
assertEquals(res.dnsResponse, undefined);
assertEquals(res.response, "NXDOMAIN");
});
// This isn't supposed to SERVFAIL
// It also takes forever
Deno.test("Get invalid IP, external NS", async () => {
const res = await doQuery("b", "AAAA", { server: "1.1.1.1" });
assertEquals(res.dnsResponse, undefined);
assertEquals(res.response, "SERVFAIL");
});

View file

@ -1,112 +0,0 @@
// SPDX-License-Identifier: MIT
import { assertEquals, assertThrows } from "./testDeps.ts";
import { parseResponse } from "../lib/response.ts";
import { QueryResponse } from "../lib/utils.ts";
const mockResponse: QueryResponse = {
dnsResponse: [],
response: "NOERROR",
time: 0,
};
let domain = "localhost.";
Deno.test("A query", () => {
mockResponse.dnsResponse = ["127.0.0.1"];
assertEquals(parseResponse(mockResponse, domain, "A", false), [
"localhost. IN A 127.0.0.1",
]);
});
Deno.test("AAAA query, short", () => {
mockResponse.dnsResponse = ["::1"];
assertEquals(parseResponse(mockResponse, domain, "AAAA", true), [
"::1",
]);
});
Deno.test("MX query", () => {
mockResponse.dnsResponse = [{
exchange: "mail.localhost",
preference: 10,
}];
assertEquals(parseResponse(mockResponse, domain, "MX", false), [
"localhost. IN MX 10 mail.localhost",
]);
});
Deno.test("CAA query", () => {
mockResponse.dnsResponse = [{
critical: false,
tag: "issue",
value: "pki.goog",
}];
assertEquals(parseResponse(mockResponse, domain, "CAA", false), [
'localhost. IN CAA 0 issue "pki.goog"',
]);
});
Deno.test("NAPTR query", () => {
domain = "4.3.2.1.5.5.5.0.0.8.1.e164.arpa.";
mockResponse.dnsResponse = [{
flags: "u",
order: 100,
preference: 10,
services: "E2U+sip",
regexp: "!^.*$!sip:customer-service@example.com!",
replacement: ".",
}, {
flags: "u",
order: 102,
preference: 10,
services: "E2U+email",
regexp: "!^.*$!mailto:information@example.com!",
replacement: ".",
}];
assertEquals(parseResponse(mockResponse, domain, "NAPTR", false), [
`4.3.2.1.5.5.5.0.0.8.1.e164.arpa. IN NAPTR 100 10 "u" "E2U+sip" !^.*$!sip:customer-service@example.com! .`,
`4.3.2.1.5.5.5.0.0.8.1.e164.arpa. IN NAPTR 102 10 "u" "E2U+email" !^.*$!mailto:information@example.com! .`,
]);
});
Deno.test("SOA query", () => {
domain = "cloudflare.com.";
mockResponse.dnsResponse = [{
mname: "ns3.cloudflare.com.",
rname: "dns.cloudflare.com.",
serial: 2280958559,
refresh: 10000,
retry: 2400,
expire: 604800,
minimum: 300,
}];
assertEquals(parseResponse(mockResponse, domain, "SOA", false), [
"cloudflare.com. IN SOA ns3.cloudflare.com. dns.cloudflare.com. 2280958559 10000 2400 604800 300",
]);
});
Deno.test("SRV query", () => {
domain = "localhost";
mockResponse.dnsResponse = [{
port: 22,
priority: 0,
target: "localhost",
weight: 10,
}];
assertEquals(parseResponse(mockResponse, domain, "SRV", false), [
"localhost IN SRV 0 10 22 localhost",
]);
});
Deno.test("TXT query", () => {
mockResponse.dnsResponse = [["a"]];
assertEquals(parseResponse(mockResponse, domain, "TXT", false), [
'localhost IN TXT "a"',
]);
});
Deno.test("Invalid query", () => {
mockResponse.dnsResponse = [["a"]];
assertThrows((): void => {
parseResponse(mockResponse, domain, "E", true);
});
});

View file

@ -1,43 +0,0 @@
// SPDX-License-Identifier: MIT
import { parseIPv6, parseNAPTR, parsePTR } from "../lib/reverse.ts";
import { assertEquals } from "./testDeps.ts";
Deno.test("IPv6 Parse, localhost", () => {
assertEquals(parseIPv6("::1"), "0000:0000:0000:0000:0000:0000:0000:0001");
});
Deno.test("IPv6 Parse, :: in middle of address", () => {
assertEquals(
parseIPv6("2001:4860:4860::8844"),
"2001:4860:4860:0000:0000:0000:0000:8844",
);
});
Deno.test("IPv4 PTR, localhost", () => {
assertEquals(parsePTR("127.0.0.1"), "1.0.0.127.in-addr.arpa");
});
Deno.test("IPv6 PTR, actual IP", () => {
assertEquals(
parsePTR("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",
);
});
Deno.test("PTR, Fallback lel", () => {
assertEquals(parsePTR("1367218g3a1"), "");
});
Deno.test("NAPTR, US number", () => {
assertEquals(
parseNAPTR("+1-800-555-1234"),
"4.3.2.1.5.5.5.0.0.8.1.e164.arpa",
);
});
Deno.test("NAPTR, non-US number", () => {
assertEquals(
parseNAPTR("44 186 533 2244"),
"4.4.2.2.3.3.5.6.8.1.4.4.e164.arpa",
);
});

View file

@ -1,4 +0,0 @@
export {
assertEquals,
assertThrows,
} from "https://deno.land/std@0.144.0/testing/asserts.ts";

2
util/docs.go Normal file
View file

@ -0,0 +1,2 @@
// Helper functions
package util

57
util/helpers.go Normal file
View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: BSD-3-Clause
package util
import (
"errors"
"fmt"
"strings"
"time"
"github.com/miekg/dns"
)
type Response struct {
Answers Answers `json:"Response"` // These be DNS query answers
}
// The Answers struct is the basic structure of a DNS request
// to be returned to the user upon making a request
type Answers struct {
Server string `json:"Server"` // The server to make the DNS request from
Request uint16 `json:"Request"` // The type of request
Name string `json:"Name"` // The domain name to make a DNS request for
RTT time.Duration `json:"RTT"` // The time it took to make the DNS query
}
// Given an IP or phone number, return a canonical string to be queried
func ReverseDNS(address string, query string) (string, error) {
if query == "PTR" {
return dns.ReverseAddr(address)
} else if query == "NAPTR" {
// get rid of characters not needed
replacer := strings.NewReplacer("+", "", " ", "", "-", "")
address = replacer.Replace(address)
// reverse the order of the string
address = reverse(address)
var arpa strings.Builder
// Make it canonical
for _, c := range address {
fmt.Fprintf(&arpa, "%c.", c)
}
arpa.WriteString("e164.arpa.")
return arpa.String(), nil
}
return "", errors.New("ReverseDNS: -x flag given but no IP found")
}
// Reverse a string, return the string in reverse
func reverse(s string) string {
rns := []rune(s)
for i, j := 0, len(rns)-1; i < j; i, j = i+1, j-1 {
rns[i], rns[j] = rns[j], rns[i]
}
return string(rns)
}

43
util/helpers_test.go Normal file
View file

@ -0,0 +1,43 @@
// SPDX-License-Identifier: BSD-3-Clause
package util
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIPv4(t *testing.T) {
act, err := ReverseDNS("8.8.4.4", "PTR")
assert.Nil(t, err)
assert.Equal(t, act, "4.4.8.8.in-addr.arpa.", "IPv4 reverse")
}
func TestIPv6(t *testing.T) {
act, err := ReverseDNS("2606:4700:4700::1111", "PTR")
assert.Nil(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) {
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 {
act, err := ReverseDNS(test.in, "NAPTR")
assert.Nil(t, err)
assert.Equal(t, test.want, act)
}
}
func TestInvalid(t *testing.T) {
_, err := ReverseDNS("AAAAA", "A")
assert.NotNil(t, err)
}