~ols/dns-url-shortener

4c5572b1a1c00664045732bc3f4cada2ea10af08 — Oliver Leaver-Smith 5 months ago master
Initial commit
5 files changed, 103 insertions(+), 0 deletions(-)

A LICENCE
A README.md
A go.mod
A go.sum
A main.go
A  => LICENCE +15 -0
@@ 1,15 @@
ISC License

Copyright (c) 2021, Oliver Leaver-Smith

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

A  => README.md +1 -0
@@ 1,1 @@
# DNS URL Shortener

A  => go.mod +8 -0
@@ 1,8 @@
module dns-short

go 1.13

require (
	github.com/gorilla/mux v1.8.0
	github.com/miekg/dns v1.1.40
)

A  => go.sum +20 -0
@@ 1,20 @@
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

A  => main.go +59 -0
@@ 1,59 @@
package main

import (
	"context"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"time"

	"github.com/gorilla/mux"
)

const timeout = 1 * time.Second

var attemptForward = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	baseDomain := os.Getenv("BASE_DOMAIN")
	vars := mux.Vars(r)
	slug := vars["shortcode"]
	domain := slug + "." + baseDomain
	ctx, cancel := context.WithTimeout(context.TODO(), timeout)
	defer cancel()
	var res net.Resolver
	txtRecords, err := res.LookupTXT(ctx, domain)
	if err != nil {
		log.Printf("No TXT records found: %s", err)
		w.Write([]byte("No entry found for that short code"))
		return
	}
	redirect := txtRecords[0]
	if len(txtRecords) > 0 {
		isUrl := IsUrl(redirect)
		if !isUrl {
			log.Printf("Provided forward (%s) is not a URL", redirect)
			w.Write([]byte("No entry found for that short code"))
			return
		}
		log.Printf("Matching entry found for %s, redirecting to %s", slug, redirect)
		http.Redirect(w, r, redirect, 302)
	} else {
		log.Printf("No entry found for %s", slug)
		w.Write([]byte("No entry found for that short code"))
	}

})

func IsUrl(str string) bool {
	// We do this because url.ParseRequestURI now says
	// that a relative URL like /cabbage.html is legit
	u, err := url.Parse(str)
	return err == nil && u.Scheme != "" && u.Host != ""
}

func main() {
	r := mux.NewRouter()
	r.Handle("/{shortcode}", attemptForward).Methods("GET")
	http.ListenAndServe(":"+os.Getenv("PORT"), r)
}