-
Notifications
You must be signed in to change notification settings - Fork 78
Sub domain replace feature #307
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
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Basic DNS proxy. Translate all plain DNS queries received on port 53 | ||
| # into DNS-over-TLS queries to Cloudflare's DNS server. | ||
| [bootstrap-resolver] | ||
| address = "1.1.1.1:853" | ||
| protocol = "dot" | ||
|
|
||
| [resolvers.cloudflare-dot] | ||
| address = "gcp.ctptech.dev:853" | ||
| protocol = "dot" | ||
|
|
||
| [listeners.local-udp] | ||
| address = ":53" | ||
| protocol = "udp" | ||
| resolver = "sub-domain-replace" | ||
|
|
||
| [listeners.local-tcp] | ||
| address = ":53" | ||
| protocol = "tcp" | ||
| resolver = "sub-domain-replace" | ||
|
|
||
| [groups.sub-domain-replace] | ||
| type = "subdomain-replace" | ||
| resolvers = [ "cloudflare-dot" ] | ||
| subdomain = [ | ||
| { from = '{}.ctptech.dev.', to = '{}.whiskey.software.' }, | ||
| { from = '{}.whiskey.software.', to = '{}.charlesp.tech.' }, | ||
| { from = '{}.whisky.software.', to = '{}.whiskey.dev.' }, | ||
| { from = '{}.local.', to = '{}.' }, | ||
| ] | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,115 @@ | ||||||||||||
| package rdns | ||||||||||||
|
|
||||||||||||
| import ( | ||||||||||||
| "errors" | ||||||||||||
| "strings" | ||||||||||||
|
|
||||||||||||
| "github.com/miekg/dns" | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| // Replace is a resolver that modifies queries according to regular expressions | ||||||||||||
| // and forwards the modified queries to another resolver. Responses are then | ||||||||||||
| // mapped back to the original query string. | ||||||||||||
| type SubDomainReplace struct { | ||||||||||||
| id string | ||||||||||||
| resolver Resolver | ||||||||||||
| exp subDomainReplaceExpressions | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| var _ Resolver = &SubDomainReplace{} | ||||||||||||
|
|
||||||||||||
| type subDomainReplaceExp struct { | ||||||||||||
| from string | ||||||||||||
| to string | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| type subDomainReplaceExpressions []subDomainReplaceExp | ||||||||||||
| func (r subDomainReplaceExp) substitute (qname string) (string, bool) { | ||||||||||||
| fromDomain := r.from | ||||||||||||
| toDomain := r.to | ||||||||||||
| // from {}.ctptech.dev to {}.charlesp.tech | ||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
| // from test.ctptech.dev to test.charlesp.tech | ||||||||||||
| fromDomain = strings.Replace(fromDomain, "{}", "", 1) | ||||||||||||
|
charlieporth1 marked this conversation as resolved.
Outdated
|
||||||||||||
| toDomain = strings.Replace(toDomain, "{}", "", 1) | ||||||||||||
| if strings.HasSuffix(qname,fromDomain ) { | ||||||||||||
| str := strings.Replace(qname, fromDomain, toDomain, 1) | ||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's a small issue with just doing a simple replace since the "fromDomain" string can show up more than once in the domain. Let's say you have sub := strings.TrimSuffix(qname, fromDomain)
str := sub+toDomain
return str, true |
||||||||||||
| Log.WithField("qname", qname).Debug("matches: modifying query") | ||||||||||||
| return str, true | ||||||||||||
| } else { | ||||||||||||
| return "", false | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+37
to
+39
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
| } | ||||||||||||
| func (r subDomainReplaceExpressions) apply(qname string) string { | ||||||||||||
| for _, e := range r { | ||||||||||||
| s, result := e.substitute(qname) | ||||||||||||
| if result { | ||||||||||||
| return s | ||||||||||||
| } else { | ||||||||||||
|
charlieporth1 marked this conversation as resolved.
Outdated
|
||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| return qname | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| type SubDomainReplaceOperation struct { | ||||||||||||
| From string | ||||||||||||
| To string | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // NewReplace returns a new instance of a Replace resolver. | ||||||||||||
| func NewSubDomainReplace(id string, resolver Resolver, list ...SubDomainReplaceOperation) (*SubDomainReplace, error) { | ||||||||||||
| var exp subDomainReplaceExpressions | ||||||||||||
| for _, o := range list { | ||||||||||||
| if strings.Contains(o.From, "{}") && strings.Contains(o.To, "{}") { | ||||||||||||
| exp = append(exp, subDomainReplaceExp{o.From, o.To}) | ||||||||||||
| } else { | ||||||||||||
| return nil, errors.New("{} not found") | ||||||||||||
|
charlieporth1 marked this conversation as resolved.
Outdated
|
||||||||||||
| } | ||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you still need this since |
||||||||||||
| } | ||||||||||||
| Log.WithField("exp", exp).Debug("exp query") | ||||||||||||
| return &SubDomainReplace{id: id, resolver: resolver, exp: exp}, nil | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Resolve a DNS query by first replacing the query string with another | ||||||||||||
| // sending the query upstream and replace the name in the response with | ||||||||||||
| // the original query string again. | ||||||||||||
| func (r *SubDomainReplace) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) { | ||||||||||||
| if len(q.Question) < 1 { | ||||||||||||
| return nil, errors.New("no question in query") | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| oldName := q.Question[0].Name | ||||||||||||
| newName := r.exp.apply(oldName) | ||||||||||||
| log := logger(r.id, q, ci) | ||||||||||||
|
|
||||||||||||
| // if nothing needs modifying, we can stop here and use the original query | ||||||||||||
| if newName == oldName { | ||||||||||||
| log.Debug("forwarding unmodified query to resolver") | ||||||||||||
| return r.resolver.Resolve(q, ci) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Modify the query string | ||||||||||||
| q.Question[0].Name = newName | ||||||||||||
|
|
||||||||||||
| // Send the query upstream | ||||||||||||
| log.WithField("new-qname", newName).WithField("resolver", r.resolver).Debug("forwarding modified query to resolver") | ||||||||||||
| a, err := r.resolver.Resolve(q, ci) | ||||||||||||
| if err != nil || a == nil { | ||||||||||||
| return nil, err | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Set the question back to the original name | ||||||||||||
| a.Question[0].Name = oldName | ||||||||||||
|
|
||||||||||||
| // Now put the original name in all answer records that have the | ||||||||||||
| // new name | ||||||||||||
| for _, answer := range a.Answer { | ||||||||||||
| if answer.Header().Name == newName { | ||||||||||||
| answer.Header().Name = oldName | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| return a, nil | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| func (r *SubDomainReplace) String() string { | ||||||||||||
| return r.id | ||||||||||||
| } | ||||||||||||
Uh oh!
There was an error while loading. Please reload this page.