Skip to content

Instantly share code, notes, and snippets.

@juniorz
Last active June 14, 2022 14:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juniorz/5d25c1c32821df283850 to your computer and use it in GitHub Desktop.
Save juniorz/5d25c1c32821df283850 to your computer and use it in GitHub Desktop.
XMPP SRV dns over Tor
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
}
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")
}
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()
}
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