Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wildcard fallback? #17

Open
rjocoleman opened this issue May 6, 2016 · 7 comments
Open

Wildcard fallback? #17

rjocoleman opened this issue May 6, 2016 · 7 comments

Comments

@rjocoleman
Copy link

rjocoleman commented May 6, 2016

@holic fantastic tool, thanks. The power:simplicity here is wonderful.

How do you feel about a PR to handle catch-all wildcards before the fallback function?

e.g.

             IN  ALIAS  alias.redirect.name ; domain apex using a non-standard record
_redirect    IN  TXT    "Redirects permanently to https://example.com/*"

*            IN  CNAME  alias.redirect.name
_redirect.*  IN  TXT    "Redirects permanently to https://example.com/*"

My use-case is extremely lazily redirecting miss-spelled domain names. This functionality would allow for a standard set of records to be added for each without care to past or future sub-domain changes.

Currently this would be handled by defining a _redirect TXT record for every possible subdomain.

(Also, if it's relevant, I'm self hosting via the published docker image - swift and stateless is great)

@rjocoleman
Copy link
Author

Interestingly, this is non-terminal wildcard notation is a bit contentious.

https://tools.ietf.org/html/rfc4592#section-2.1.3 allows it. AWS's Route53 treats it as a single record, which is what I based my proposal on.

Golang's net package doesn't recognise it as a valid domain.

@rjocoleman
Copy link
Author

In lieu of discovering if Go's net package has a bug, an alternative that makes this explicit is probably a better interface - i.e. catch-all or default subdomain at each level.

A PoC to illustrate is below.

; example.net

*                  IN  CNAME  alias.redirect.name
_redirect.default  IN  TXT    "Redirects to https://example.com/*"

In this example request is made to foo.example.net so _redirect.foo.example.net TXT is looked up.
This does not exist so _redirect.default.example.net TXT is looked up, a result is found and the redirection occurs. The order of execution allows explicit redirections to occur and the default only happens for the subdomains of the same level.

diff --git a/server.go b/server.go
index 846ee27..f39feac 100644
--- a/server.go
+++ b/server.go
@@ -7,6 +7,7 @@ import (
    "net/http"
    "net/url"
    "os"
+   "regexp"
    "strings"
    "time"
 )
@@ -19,12 +20,22 @@ func fallback(w http.ResponseWriter, r *http.Request, reason string) {
    http.Redirect(w, r, location, 302)
 }

+func hostnameLookup(host string) ([]string, error) {
+   hostname := fmt.Sprintf("_redirect.%s", host)
+   return net.LookupTXT(hostname)
+}
+
 func handler(w http.ResponseWriter, r *http.Request) {
    parts := strings.Split(r.Host, ":")
    host := parts[0]

-   hostname := fmt.Sprintf("_redirect.%s", host)
-   txt, err := net.LookupTXT(hostname)
+   txt, err := hostnameLookup(host)
+   if err != nil {
+       pattern := regexp.MustCompile("^(.*?)\\.(.*)$")
+       recursiveHost := pattern.ReplaceAllString(host, "default.$2")
+       txt, err = hostnameLookup(recursiveHost)
+   }
+
    if err != nil {
        fallback(w, r, fmt.Sprintf("Could not resolve hostname (%v)", err))
        return

I'm keen to hear your thoughts before moving further ahead. If/when appropriate I'll send this over as a proper PR with the relevant tests and documentation.

@holic
Copy link
Owner

holic commented May 7, 2016

Interesting idea! I think it would make sense to just fall back to the next level's _redirect configuration instead of a special name. For example:

*                IN  CNAME  alias.redirect.name
_redirect.zombo  IN  TXT    "Redirects to http://zombo.com/"
_redirect        IN  TXT    "Redirects to https://example.com/*"

Requests to zombo.yourdomain.com would redirect to http://zombo.com/ while all other requests (e.g. any.yourdomain.com or www.yourdomain.com) would redirect to https://example.com/.

That said, I'm not sure I actually want to implement this for a couple of reasons:

  • I would have to traverse the domain by level until it finds a match or runs out of hostnames. This would at least triple the number of DNS queries made for every request.
  • Alternatively, I would need to maintain a list of top-level domains (e.g. .com and .co.nz) to know when to "stop" looking to save DNS queries (since we know that _redirect.com and _redirect.co.nz will never resolve).

Both seem like a large burden on the server for a feature/side-effect that is unlikely to see significant usage.

@rjocoleman
Copy link
Author

I think the main blocker of a simple implementation is the responding server doesn't know if it was an explicit domain request or a wildcard that got it there.

If we did know, then when a wildcard was requested respond with the explicit _redirect if it exists, or the _redirect on the same level.
This could be achieved by an additional lookup to r.Host ala dig +short *.example.com to see if this record exists.

The negative of this is the 3 requests - first to explicit, second to dig-ish and third to default.

A named default mode has the advantage of only 2 requests - first to explicit and second to default.

I don't think traversal is a good idea is you could have unintended redirects with no way of opting out.

I don't think a list of domains needs to be maintained (though publicsuffix could work) because the failure mode of net.LookupTXT is enough - com or co.nz is never going to pass domain validation in net.LookupTXT so will return err and fail out.

I'm going to stick a named default behind a flag in a fork for my usage. If this or some remix of the feature is useful I'm always happy to PR :)

@holic
Copy link
Owner

holic commented May 11, 2016

Sounds good - curious to hear how well it works in practice.

One worry I have with relying on publicsuffix is that the current TLD landscape is wildly in flux (new TLDs released all the time). I would assume that each time I want to pick up new TLD changes, I would need to recompile and rerelease the Go binary?

@D1yt
Copy link

D1yt commented Oct 29, 2018

So I guess we don't see wildcard fallback anymore? Thought I could simplify adding CNAME entries to your site. But I guess I have to manually add them anyway

@treehousekingcomic
Copy link

@rjocoleman You made a fork with a catch-all?
This is exactly what I was looking for and I'm trying to see if its possible
I want to redirect *.blog.domain.com to blog.domain.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants