Last active
June 14, 2022 14:50
-
-
Save juniorz/5d25c1c32821df283850 to your computer and use it in GitHub Desktop.
XMPP SRV dns over Tor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package xmpp | |
import ( | |
"errors" | |
"fmt" | |
"log" | |
"net" | |
"net/url" | |
"github.com/miekg/dns" | |
"golang.org/x/net/proxy" | |
) | |
type LookupType int | |
const ( | |
SERVER_DNS_LOOKUP LookupType = iota | |
CLIENT_DNS_LOOKUP | |
) | |
var lookupServices = map[LookupType]string{ | |
CLIENT_DNS_LOOKUP: "xmpp-client", | |
SERVER_DNS_LOOKUP: "xmpp-server", | |
} | |
// XMPP spec, section 3.2.1. "Preferred Process: SRV Lookup" | |
func LookupDNS(t LookupType, server string) error { | |
service, ok := lookupServices[t] | |
if !ok { | |
return errors.New("invalid lookup type") | |
} | |
// addrs are already sorted as per RFC 2782 | |
cname, addrs, err := net.LookupSRV(service, "tcp", server) | |
if err != nil { | |
//Fallback ? | |
return err | |
} | |
fmt.Printf("%#v, %#v, %s\n", cname, addrs, err) | |
if len(addrs) == 1 && addrs[0].Target == "." { | |
return errors.New("The service is decidedly not available at this domain. Aborting.") | |
} | |
onion := ".onion." | |
for _, a := range addrs { | |
if a.Target[len(a.Target)-len(onion):] == onion { | |
//skipping .onion | |
continue | |
} | |
names, err := net.LookupIP(a.Target) | |
fmt.Println(names, err) | |
} | |
return nil | |
} | |
func SRVTorLookup(t LookupType, server string) error { | |
service, ok := lookupServices[t] | |
if !ok { | |
return errors.New("invalid lookup type") | |
} | |
host := "_" + service + "._tcp." + server + "." | |
u, _ := url.Parse("socks5://127.0.0.1:9050") | |
dialer, _ := proxy.FromURL(u, proxy.Direct) | |
conn, _ := dialer.Dial("tcp", "208.67.222.222:53") //OpenDNS | |
m := new(dns.Msg) | |
m.SetQuestion(host, dns.TypeSRV) | |
m.RecursionDesired = true | |
co := &dns.Conn{Conn: conn} | |
defer co.Close() | |
co.WriteMsg(m) | |
r, err := co.ReadMsg() | |
if r == nil { | |
log.Fatalf("*** error: %s\n", err.Error()) | |
} | |
if r.Rcode != dns.RcodeSuccess { | |
fmt.Println(r.Rcode) | |
} | |
// Stuff must be in the answer section | |
for _, a := range r.Answer { | |
fmt.Printf("%v\n", a) | |
} | |
return nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package xmpp | |
import "testing" | |
func TestDNSLookup(t *testing.T) { | |
LookupDNS(CLIENT_DNS_LOOKUP, "riseup.net") | |
LookupDNS(CLIENT_DNS_LOOKUP, "jabber.calyxinstitute.org") | |
SRVTorLookup(CLIENT_DNS_LOOKUP, "riseup.net") | |
SRVTorLookup(CLIENT_DNS_LOOKUP, "jabber.calyxinstitute.org") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package xmpp | |
import ( | |
"fmt" | |
"math/rand" | |
"net" | |
"sort" | |
"golang.org/x/net/proxy" | |
"github.com/miekg/dns" | |
) | |
type Server struct { | |
originDomain string | |
} | |
// ResolveClientFQDN resolves an XMPP server's FQDN for an "xmpp-client" request according to rfc6120, section 3.2.1 | |
// | |
func ResolveClientFQDN(originDomain string, conn net.Conn) (sname string, addrs []*net.SRV, err error) { | |
c := &dns.Conn{Conn: conn} | |
defer c.Close() | |
return lookupSRV(c, "xmpp-client", "tcp", originDomain+".") | |
} | |
// ResolveServerFQDN resolves an XMPP server's FQDN for an "xmpp-server" request according to rfc6120, section 3.2.1 | |
// | |
func ResolveServerFQDN(originDomain string, conn net.Conn) (sname string, addrs []*net.SRV, err error) { | |
c := &dns.Conn{Conn: conn} | |
defer c.Close() | |
return lookupSRV(c, "xmpp-server", "tcp", originDomain+".") | |
} | |
var fallbackDNSServer = "208.67.222.222:53" // OpenDNS | |
func newDNSConn(dialer proxy.Dialer) (net.Conn, error) { | |
config, err := dns.ClientConfigFromFile("/etc/resolv.conf") | |
if err != nil { | |
config = &dns.ClientConfig{} | |
} | |
server := fallbackDNSServer | |
for _, s := range config.Servers { | |
//Should not use a proxy to access a loopback IP | |
//Tor will not forward such requests | |
if dialer != proxy.Direct && net.ParseIP(s).IsLoopback() { | |
continue | |
} | |
server = s + ":" + config.Port | |
break | |
} | |
return dialer.Dial("tcp", server) | |
} | |
func lookupSRV(conn *dns.Conn, service, proto, name string) (sname string, addrs []*net.SRV, err error) { | |
sname = "_" + service + "._" + proto + "." + name | |
m := &dns.Msg{} | |
m.SetQuestion(sname, dns.TypeSRV) | |
m.RecursionDesired = true | |
err = conn.WriteMsg(m) | |
if err != nil { | |
return | |
} | |
var r *dns.Msg | |
r, err = conn.ReadMsg() | |
if err != nil { | |
return | |
} | |
if r.Rcode != dns.RcodeSuccess { | |
err = fmt.Errorf("DNS Error %d", r.Rcode) | |
return | |
} | |
if len(r.Answer) == 0 { | |
return | |
} | |
addrs = make([]*net.SRV, 0, len(r.Answer)) | |
for _, a := range r.Answer { | |
srv, ok := a.(*dns.SRV) | |
if !ok { | |
continue | |
} | |
addrs = append(addrs, &net.SRV{ | |
Target: srv.Target, | |
Port: srv.Port, | |
Priority: srv.Priority, | |
Weight: srv.Weight, | |
}) | |
} | |
byPriorityWeight(addrs).sort() | |
return | |
} | |
/* Copied from golang net package */ | |
// byPriorityWeight sorts SRV records by ascending priority and weight. | |
type byPriorityWeight []*net.SRV | |
func (s byPriorityWeight) Len() int { return len(s) } | |
func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |
func (s byPriorityWeight) Less(i, j int) bool { | |
return s[i].Priority < s[j].Priority || | |
(s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight) | |
} | |
// shuffleByWeight shuffles SRV records by weight using the algorithm | |
// described in RFC 2782. | |
func (addrs byPriorityWeight) shuffleByWeight() { | |
sum := 0 | |
for _, addr := range addrs { | |
sum += int(addr.Weight) | |
} | |
for sum > 0 && len(addrs) > 1 { | |
s := 0 | |
n := rand.Intn(sum) | |
for i := range addrs { | |
s += int(addrs[i].Weight) | |
if s > n { | |
if i > 0 { | |
addrs[0], addrs[i] = addrs[i], addrs[0] | |
} | |
break | |
} | |
} | |
sum -= int(addrs[0].Weight) | |
addrs = addrs[1:] | |
} | |
} | |
// sort reorders SRV records as specified in RFC 2782. | |
func (addrs byPriorityWeight) sort() { | |
sort.Sort(addrs) | |
i := 0 | |
for j := 1; j < len(addrs); j++ { | |
if addrs[i].Priority != addrs[j].Priority { | |
addrs[i:j].shuffleByWeight() | |
i = j | |
} | |
} | |
addrs[i:].shuffleByWeight() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package xmpp | |
import ( | |
"fmt" | |
"net/url" | |
"testing" | |
"golang.org/x/net/proxy" | |
) | |
func TestClientFQDN(t *testing.T) { | |
u, _ := url.Parse("socks5://dns:pass@127.0.0.1:9050") | |
dialer, _ := proxy.FromURL(u, proxy.Direct) | |
conn, _ := newDNSConn(dialer) | |
addr, _, err := ResolveClientFQDN("olabini.ec", conn) | |
fmt.Println(addr, err) | |
} | |
func TestServerFQDN(t *testing.T) { | |
u, _ := url.Parse("socks5://dns:pass@127.0.0.1:9050") | |
dialer, _ := proxy.FromURL(u, proxy.Direct) | |
conn, _ := newDNSConn(dialer) | |
addr, _, err := ResolveServerFQDN("olabini.ec", conn) | |
fmt.Println(addr, err) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment