diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 28db573..29fc64a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,9 @@ on: tags: - "*" +env: + REGISTRY: ghcr.io + jobs: goreleaser: runs-on: ubuntu-latest @@ -16,17 +19,14 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.16 - - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + go-version: 1.19 + - name: Log in to the Container registry + uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 with: - registry: ghcr.io + registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} - password: ${{ secrets.GH_GORELEASER_TOKEN }} - - name: Install Snapcraft and Log In - uses: samuelmeuli/action-snapcraft@v1 - with: - snapcraft_token: ${{ secrets.SNAPCRAFT_TOKEN }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: @@ -34,4 +34,4 @@ jobs: args: release --rm-dist env: DOCKER_CLI_EXPERIMENTAL: enabled - GITHUB_TOKEN: ${{ secrets.GH_GORELEASER_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 2bdbba7..4d06b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ vendor/ config.yml .DS_Store bin/* +node_modules \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml index 2b3b359..49159b4 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -17,7 +17,7 @@ builds: - 7 ldflags: - -s -w -X "main.buildVersion={{ .Tag }} ({{ .ShortCommit }} {{ .Date }})" - dir: ./cmd/doggo/cli/ + dir: ./cmd/doggo/ - binary: doggo-api.bin id: api @@ -33,64 +33,62 @@ builds: - 7 ldflags: - -s -w -X "main.buildVersion={{ .Tag }} ({{ .ShortCommit }} {{ .Date }})" - dir: ./cmd/doggo/api/ + dir: ./cmd/api/ archives: - format: tar.gz files: - README.md - LICENSE - -snapcrafts: - - name_template: "{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" - summary: Command-line DNS client. - description: | - doggo is a command-line DNS client written in Go. It supports protocols like DoH, DoT and output formats like JSON. - grade: stable - confinement: strict - publish: true + - completions/ dockers: - image_templates: - - "ghcr.io/mr-karan/doggo-cli:{{ .Tag }}" - - "ghcr.io/mr-karan/doggo-cli:latest" - binaries: - - doggo + - "ghcr.io/mr-karan/doggo:{{ .Tag }}" + - "ghcr.io/mr-karan/doggo:latest" + id: doggo + # IDs to filter the binaries/packages. + ids: + - cli dockerfile: Dockerfile-cli build_flag_templates: - "--build-arg" - "ARCH=amd64" - image_templates: - - "ghcr.io/mr-karan/doggo-cli:{{ .Tag }}-arm64v8" - - "ghcr.io/mr-karan/doggo-cli:latest-arm64v8" - binaries: - - doggo + - "ghcr.io/mr-karan/doggo:{{ .Tag }}-arm64v8" + - "ghcr.io/mr-karan/doggo:latest-arm64v8" + id: doggo-arm + # IDs to filter the binaries/packages. + ids: + - cli goarch: arm64 dockerfile: Dockerfile-cli build_flag_templates: - "--build-arg" - "ARCH=arm64v8" - - image_templates: - - "ghcr.io/mr-karan/doggo-api:{{ .Tag }}" - - "ghcr.io/mr-karan/doggo-api:latest" - binaries: - - doggo-api.bin - dockerfile: Dockerfile-api - build_flag_templates: - - "--build-arg" - - "ARCH=amd64" - extra_files: - - config-api-sample.toml - - image_templates: - - "ghcr.io/mr-karan/doggo-api:{{ .Tag }}-arm64v8" - - "ghcr.io/mr-karan/doggo-api:latest-arm64v8" - binaries: - - doggo-api.bin - goarch: arm64 - dockerfile: Dockerfile-api - build_flag_templates: - - "--build-arg" - - "ARCH=arm64v8" - extra_files: - - config-api-sample.toml + # - image_templates: + # - "ghcr.io/mr-karan/doggo-api:{{ .Tag }}" + # - "ghcr.io/mr-karan/doggo-api:latest" + # id: doggo-api + # ids: + # - api + # dockerfile: Dockerfile-api + # build_flag_templates: + # - "--build-arg" + # - "ARCH=amd64" + # extra_files: + # - config-api-sample.toml + # - image_templates: + # - "ghcr.io/mr-karan/doggo-api:{{ .Tag }}-arm64v8" + # - "ghcr.io/mr-karan/doggo-api:latest-arm64v8" + # id: doggo-api-arm + # ids: + # - api + # goarch: arm64 + # dockerfile: Dockerfile-api + # build_flag_templates: + # - "--build-arg" + # - "ARCH=arm64v8" + # extra_files: + # - config-api-sample.toml diff --git a/Makefile b/Makefile index 0b039c9..fb8eb83 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CLI_BIN := ./bin/doggo-cli.bin +CLI_BIN := ./bin/doggo.bin API_BIN := ./bin/doggo-api.bin HASH := $(shell git rev-parse --short HEAD) @@ -7,12 +7,11 @@ VERSION := ${HASH} .PHONY: build-cli build-cli: - go build -o ${CLI_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/cli/ + go build -o ${CLI_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/ .PHONY: build-api build-api: - go build -o ${API_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/api/ - + go build -o ${API_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/api/ .PHONY: build build: build-api build-cli diff --git a/README.md b/README.md index a637ebc..d2ae781 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,15 @@
🐶 Command-line DNS client for humans
+
+ doggo.mrkaran.dev
---
-**doggo** is a modern command-line DNS client (like _dig_) written in Golang. It outputs information in a neat concise manner and supports protocols like DoH, DoT as well.
+**doggo** is a modern command-line DNS client (like _dig_) written in Golang. It outputs information in a neat concise manner and supports protocols like DoH, DoT, DoQ, and DNSCrypt as well.
It's totally inspired from [dog](https://github.com/ogham/dog/) which is written in Rust. I wanted to add some features to it but since I don't know Rust, I found it as a nice opportunity
to experiment with writing a DNS Client from scratch in `Go` myself. Hence the name `dog` +`go` => **doggo**.
@@ -22,23 +24,28 @@ to experiment with writing a DNS Client from scratch in `Go` myself. Hence the n
- Has support for multiple transport protocols:
- DNS over **HTTPS** (DoH)
- DNS over **TLS** (DoT)
+ - DNS over **QUIC** (DoQ)
- DNS over **TCP**
- DNS over **UDP**
+ - DNS over **DNSCrypt**
- Supports **ndots** and **search** configurations from `resolv.conf` or command-line arguments.
- Supports multiple resolvers at once.
- Supports IPv4 **and** IPv6 _both_.
+- Available as a web tool as well: [https://doggo.mrkaran.dev](https://doggo.mrkaran.dev).
+- Shell completions for `zsh` and `fish`.
+- Reverse DNS Lookups.
## Installation
### Binary
-You can grab the latest binaries for Linux, MacOS and Windows from the [Releases](https://github.com/mr-karan/doggo/releases) section.
+You can grab the latest binaries for Linux, MacOS and Windows from the [Releases](https://git.zio.sh/astra/doggo/releases) section.
-For eg, to pull the latest Linux binary:
+For eg, to pull the latest `linux-amd64` binary:
```shell
$ cd "$(mktemp -d)"
-$ curl -sL "https://github.com/mr-karan/doggo/releases/download/v0.2.0/doggo_0.2.0_linux_amd64.tar.gz" | tar xz
+$ curl -sL "https://git.zio.sh/astra/doggo/releases/download/v0.3.7/doggo_0.3.7_linux_amd64.tar.gz" | tar xz
$ mv doggo /usr/local/bin
# doggo should be available now in your $PATH
$ doggo
@@ -58,24 +65,36 @@ You can supply all arguments to the CLI directly to `docker run` command. Eg:
`docker run ghcr.io/mr-karan/doggo:latest mrkaran.dev @1.1.1.1 MX`
-### Using snap
+### Package Managers
-[](https://snapcraft.io/doggo)
+#### Homebrew
-```sh
-$ sudo snap install doggo
+Install via [Homebrew](https://brew.sh/)
+
+```bash
+$ brew install doggo
```
-**NOTE**: Since the [confinement](https://snapcraft.io/docs/snap-confinement) mode is strict as of now, it cannot access your host's `/etc/resolv.conf`.
-I'll be making a request in the Snap forums soon so that it can be manually reviewed and allowed to use `--classic`. Until then, please specify a nameserver manually
-if using `snap`.
+#### Arch
+
+```bash
+yay -S doggo-bin
+```
+
+#### Scoop
+
+Install via [Scoop](https://scoop.sh/)
+
+```bash
+scoop install doggo
+```
### From Source
You need to have `go` installed in your system.
```bash
-$ go get github.com/mr-karan/doggo/cmd/doggo
+$ go install git.zio.sh/astra/doggo/cmd/doggo@latest
```
The binary will be available at `$GOPATH/bin/doggo`.
@@ -190,6 +209,10 @@ URL scheme of the server is used to identify which resolver to use for lookups.
@tcp:// eg: @1.1.1.1 initiates a TCP resolver for 1.1.1.1:53.
@https:// eg: @https://cloudflare-dns.com/dns-query initiates a DOH resolver for Cloudflare DoH server.
@tls:// eg: @1.1.1.1 initiates a DoT resolver for 1.1.1.1:853.
+ @sdns:// eg: @sdns://AgcAAAAAAAAABzEuMC4wLjEAEmRucy5jbG91ZGZsYXJlLmNvbQovZG5zLXF1ZXJ5
+ initiates a DNSCrypt or DoH resolver using its DNS stamp.
+ @quic:// eg: @quic://dns.adguard.com
+ initiates a DNS over QUIC resolver for Adguard DNS Resolver.
```
### Query Options
@@ -204,11 +227,14 @@ URL scheme of the server is used to identify which resolver to use for lookups.
### Resolver Options
```
- --ndots=INT Specify ndots parameter. Takes value from /etc/resolv.conf if using the system nameserver or 1 otherwise.
- --search Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list.
- --timeout Specify timeout (in seconds) for the resolver to return a response.
- -4 --ipv4 Use IPv4 only.
- -6 --ipv6 Use IPv6 only.
+ --strategy=STRATEGY Specify strategy to query nameserver listed in etc/resolv.conf. Defaults to `all` (`random`, `first`, `all`).
+ --ndots=INT Specify ndots parameter. Takes value from /etc/resolv.conf if using the system nameserver or 1 otherwise.
+ --search Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list.
+ --timeout Specify timeout (in seconds) for the resolver to return a response.
+ -4 --ipv4 Use IPv4 only.
+ -6 --ipv6 Use IPv6 only.
+ --tls-hostname=HOSTNAME Provide a hostname for doing verification of the certificate if the provided DoT nameserver is an IP.
+ --skip-hostname-verification Skip TLS Hostname Verification in case of DOT Lookups.
```
@@ -219,6 +245,7 @@ URL scheme of the server is used to identify which resolver to use for lookups.
--color Defaults to true. Set --color=false to disable colored output.
--debug Enable debug logging.
--time Shows how long the response took from the server.
+ --short Short output format. Shows only the response section.
```
---
@@ -231,4 +258,4 @@ For now I am focussing more on [planned features](TODO.md) for a **stable** v1.0
## License
-[LICENSE](LICENSE)
+[LICENSE](./LICENSE)
diff --git a/TODO.md b/TODO.md
index 30effea..30cca2f 100644
--- a/TODO.md
+++ b/TODO.md
@@ -54,14 +54,16 @@
# Future Release
- [ ] Support obscure protocol tweaks in `dig`
-- [ ] Read from file with `-f`
- [x] Support more DNS Record Types
-- [ ] Shell completions
- - [ ] bash
- - [ ] zsh
- - [ ] fish
+- [x] Shell completions
+ - [x] zsh
+ - [x] fish
- [ ] Add tests for Resolvers.
- [ ] Add tests for CLI Output.
- [ ] Homebrew - Goreleaser
+- [ ] Add support for `dig +trace` like functionality.
+- [ ] Add `dig +short` short output
+- [x] Add `--strategy` for picking nameservers.
+- [ ] Explore `dig.rc` kinda file
- [x] Separate Authority/Answer in JSON output.
- [x] Error on NXDomain (Related upstream [bug](https://github.com/miekg/dns/issues/1198))
diff --git a/cmd/doggo/api/api.go b/cmd/api/api.go
similarity index 95%
rename from cmd/doggo/api/api.go
rename to cmd/api/api.go
index 43b42d9..c467f9c 100644
--- a/cmd/doggo/api/api.go
+++ b/cmd/api/api.go
@@ -6,8 +6,8 @@ import (
"net/http"
"time"
- "github.com/mr-karan/doggo/internal/app"
- "github.com/mr-karan/doggo/pkg/utils"
+ "git.zio.sh/astra/doggo/internal/app"
+ "git.zio.sh/astra/doggo/pkg/utils"
"github.com/sirupsen/logrus"
"github.com/go-chi/chi"
diff --git a/cmd/api/assets/dark.css b/cmd/api/assets/dark.css
new file mode 100644
index 0000000..4e4777d
--- /dev/null
+++ b/cmd/api/assets/dark.css
@@ -0,0 +1,48 @@
+:root {
+ --primary: #58a6ff;
+ --background: #0d1117;
+}
+
+body {
+ background-color: var(--background);
+ color: #c9d1d9;
+}
+
+.box {
+ border: 1px solid #30363d;
+ box-shadow: unset;
+}
+
+.help a {
+ text-decoration: none;
+}
+
+a:hover {
+ color: var(--primary);
+ text-decoration: underline;
+}
+
+input, select, button {
+ background-color: #010409;
+ border: 1px solid #30363d;
+ color: #ffffff;
+}
+
+button {
+ color: #c9d1d9;
+ background-color: #21262d;
+ box-shadow: 0 0 transparent, 0 0 transparent;
+}
+
+button:hover,
+button:focus {
+ background-color: #30363d;
+ border-color: #8b949e;
+ transition-duration: .1s;
+}
+
+table th {
+ background: #161b22;
+ border-bottom: unset;
+ color: #c9d1d9;
+}
\ No newline at end of file
diff --git a/cmd/api/assets/main.js b/cmd/api/assets/main.js
new file mode 100644
index 0000000..dc405a2
--- /dev/null
+++ b/cmd/api/assets/main.js
@@ -0,0 +1,90 @@
+const $ = document.querySelector.bind(document);
+const $new = document.createElement.bind(document);
+const $show = (el) => {
+ el.classList.remove('hidden');
+};
+const $hide = (el) => {
+ el.classList.add('hidden');
+};
+
+const apiURL = '/api/lookup/';
+
+(function () {
+ const fields = ['name', 'address', 'type', 'ttl', 'rtt'];
+
+ // createRow creates a table row with the given cell values.
+ function createRow(item) {
+ const tr = $new('tr');
+ fields.forEach((f) => {
+ const td = $new('td');
+ td.innerText = item[f];
+ td.classList.add(f);
+ tr.appendChild(td);
+ });
+ return tr;
+ }
+
+ const handleSubmit = async () => {
+ const tbody = $('#table tbody'),
+ tbl = $('#table');
+ tbody.innerHTML = '';
+ $hide(tbl);
+
+ const q = $('input[name=q]').value.trim(),
+ typ = $('select[name=type]').value,
+ addr = $('input[name=address]').value.trim();
+
+ // Post to the API.
+ const req = await fetch(apiURL, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ query: [q,], type: [typ,], nameservers: [addr,] })
+ });
+
+ const res = await req.json();
+
+ if (res.status != 'success') {
+ const error = (res && res.message) || response.statusText;
+ throw(error);
+ return;
+ }
+
+ if (res.data[0].answers == null) {
+ throw('No records found.');
+ return;
+ }
+
+ res.data[0].answers.forEach((item) => {
+ tbody.appendChild(createRow(item));
+ });
+
+ $show(tbl);
+ };
+
+ // Capture the form submit.
+ $('#form').onsubmit = async (e) => {
+ e.preventDefault();
+
+ const msg = $('#message');
+ $hide(msg);
+
+ try {
+ await handleSubmit();
+ } catch(e) {
+ msg.innerText = e.toString();
+ $show(msg);
+ throw e;
+ }
+ };
+
+ // Change the address on ns change.
+ const ns = $("#ns"), addr = $("#address");
+ addr.value = ns.value;
+
+ ns.onchange = (e) => {
+ addr.value = e.target.value;
+ if(addr.value === "") {
+ addr.focus();
+ }
+ };
+})();
\ No newline at end of file
diff --git a/cmd/api/assets/style.css b/cmd/api/assets/style.css
new file mode 100644
index 0000000..84b2194
--- /dev/null
+++ b/cmd/api/assets/style.css
@@ -0,0 +1,201 @@
+:root {
+ --primary: #4338ca;
+ --secondary: #333;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+:focus {
+ outline: 0;
+}
+
+body {
+ font-family: "Segoe UI", "Helvetica Neue", Inter, sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ color: #111;
+ margin: 0 auto;
+}
+
+h1, h2, h3, h4 {
+ line-height: 1.3em;
+}
+
+a {
+ color: var(--primary);
+}
+ a:hover {
+ color: #111;
+ text-decoration: none;
+ }
+
+input, select, button {
+ border-radius: 5px;
+ border: 1px solid #ddd;
+ font-size: 1.3rem;
+ padding: 10px 15px;
+ width: 100%;
+}
+ input:focus, select:focus {
+ border-color: var(--primary);
+ }
+ button {
+ border-color: var(--primary);
+ background: var(--primary);
+ color: #fff;
+ cursor: pointer;
+ width: auto;
+ padding: 10px 30px;
+ }
+ button:focus,
+ button:hover {
+ border-color: var(--secondary);
+ background: var(--secondary);
+ }
+
+label {
+ display: block;
+ padding-bottom: 0.5rem;
+}
+
+.box {
+ box-shadow: 1px 1px 4px #eee;
+ border: 1px solid #eee;
+ padding: 30px;
+ border-radius: 3px;
+}
+
+.hidden {
+ display: none !important;
+}
+
+.main {
+ margin: 60px auto 30px auto;
+ max-width: 900px;
+}
+
+header {
+ text-align: center;
+ font-size: 1.5em;
+ margin-bottom: 60px;
+}
+ .logo span {
+ color: var(--primary);
+ font-weight: 900;
+ }
+
+form {
+ margin-bottom: 45px;
+}
+
+ .row {
+ display: flex;
+ margin-bottom: 15px;
+ }
+ .row .field {
+ flex: 50%;
+ }
+ .row .field:last-child {
+ margin-left: 30px;
+ }
+
+ .submit {
+ text-align: right;
+ }
+ .help {
+ color: #666;
+ font-size: 0.875em;
+ }
+
+#message {
+ color: #ff3300;
+}
+
+table.box {
+ width: 100%;
+ max-width: 100%;
+ padding: 0;
+}
+ table th {
+ background: #f9fafb;
+ color: #666;
+ font-size: 0.875em;
+ border-bottom: 1px solid #ddd;
+ }
+ table th, tbody td {
+ padding: 10px 15px;
+ text-align: left;
+ }
+ td.name {
+ font-weight: bold;
+ }
+ th.type, td.type {
+ text-align: center;
+ }
+ td.type {
+ background: #d1fae5;
+ color: #065f46;
+ font-weight: bold;
+ }
+
+footer {
+ margin: 60px 0 0 0;
+ text-align: center;
+}
+ footer a {
+ text-decoration: none;
+ }
+
+
+@media (max-width: 650px) {
+ .main {
+ margin: 60px 30px 30px 30px;
+ }
+ .box {
+ box-shadow: none;
+ border: 0;
+ padding: 0;
+ }
+
+ .row {
+ display: block;
+ }
+ .field {
+ margin: 0 0 20px 0;
+ }
+ .row .field:last-child {
+ margin: 0;
+ }
+ .submit button {
+ width: 100%;
+ }
+
+ table {
+ table-layout: fixed;
+ }
+ table th {
+ width: 100%;
+ }
+ table tr {
+ border-bottom: 0;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ margin-bottom: 30px;
+ }
+ table td {
+ border: 1px solid #eee;
+ margin: 0 -1px -1px 0;
+ position: relative;
+ width: 100%;
+ word-wrap:break-word;
+ }
+ table th.type, table td.type {
+ text-align: left;
+ }
+ table td span {
+ display: block;
+ }
+}
\ No newline at end of file
diff --git a/cmd/doggo/api/config.go b/cmd/api/config.go
similarity index 100%
rename from cmd/doggo/api/config.go
rename to cmd/api/config.go
diff --git a/cmd/doggo/api/handlers.go b/cmd/api/handlers.go
similarity index 86%
rename from cmd/doggo/api/handlers.go
rename to cmd/api/handlers.go
index c9488c9..388ff92 100644
--- a/cmd/doggo/api/handlers.go
+++ b/cmd/api/handlers.go
@@ -8,9 +8,9 @@ import (
"net/http"
"time"
- "github.com/mr-karan/doggo/internal/app"
- "github.com/mr-karan/doggo/pkg/models"
- "github.com/mr-karan/doggo/pkg/resolvers"
+ "git.zio.sh/astra/doggo/internal/app"
+ "git.zio.sh/astra/doggo/pkg/models"
+ "git.zio.sh/astra/doggo/pkg/resolvers"
)
type httpResp struct {
@@ -20,7 +20,11 @@ type httpResp struct {
}
func handleIndexAPI(w http.ResponseWriter, r *http.Request) {
- sendResponse(w, http.StatusOK, "Welcome to Doggo API.")
+ var (
+ app = r.Context().Value("app").(app.App)
+ )
+
+ sendResponse(w, http.StatusOK, fmt.Sprintf("Welcome to Doggo API. Version: %s", app.Version))
return
}
@@ -66,7 +70,7 @@ func handleLookup(w http.ResponseWriter, r *http.Request) {
err = app.LoadNameservers()
if err != nil {
app.Logger.WithError(err).Error("error loading nameservers")
- sendErrorResponse(w, fmt.Sprintf("Error lookuping up for records."), http.StatusInternalServerError, nil)
+ sendErrorResponse(w, fmt.Sprintf("Error looking up for records."), http.StatusInternalServerError, nil)
return
}
@@ -82,7 +86,7 @@ func handleLookup(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
app.Logger.WithError(err).Error("error loading resolver")
- sendErrorResponse(w, fmt.Sprintf("Error lookuping up for records."), http.StatusInternalServerError, nil)
+ sendErrorResponse(w, fmt.Sprintf("Error looking up for records."), http.StatusInternalServerError, nil)
return
}
app.Resolvers = rslvrs
@@ -93,7 +97,7 @@ func handleLookup(w http.ResponseWriter, r *http.Request) {
resp, err := rslv.Lookup(q)
if err != nil {
app.Logger.WithError(err).Error("error looking up DNS records")
- sendErrorResponse(w, fmt.Sprintf("Error lookuping up for records."), http.StatusInternalServerError, nil)
+ sendErrorResponse(w, fmt.Sprintf("Error looking up for records."), http.StatusInternalServerError, nil)
return
}
responses = append(responses, resp)
diff --git a/cmd/api/index.html b/cmd/api/index.html
new file mode 100644
index 0000000..c5de849
--- /dev/null
+++ b/cmd/api/index.html
@@ -0,0 +1,101 @@
+
+
+
+ Oops! Found no records for this query.
-{{apiErrorMessage}}
-