Files
sdns-proxy/internal/protocols/doh/doh.go

122 lines
2.7 KiB
Go

package doh
import (
"bufio"
"bytes"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"os"
"github.com/afonsofrancof/sdns-perf/internal/protocols/do53"
"github.com/miekg/dns"
)
type DoHClient struct {
tcpConn *net.TCPConn
tlsConn *tls.Conn
keyLogFile *os.File
target string
path string
proxy string
}
func New(target, path, proxy string) (*DoHClient, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", target)
if err != nil {
return nil, fmt.Errorf("failed to resolve TCP address: %v", err)
}
tcpConn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
return nil, fmt.Errorf("failed to establish TCP connection: %v", err)
}
keyLogFile, err := os.OpenFile(
"tls-key-log.txt",
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
0600,
)
if err != nil {
return nil, fmt.Errorf("failed opening key log file: %v", err)
}
tlsConfig := &tls.Config{
// FIX: Actually check the domain name
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS12,
KeyLogWriter: keyLogFile,
}
tlsConn := tls.Client(tcpConn, tlsConfig)
err = tlsConn.Handshake()
if err != nil {
return nil, fmt.Errorf("failed to execute the TLS handshake: %v", err)
}
return &DoHClient{tcpConn: tcpConn, keyLogFile: keyLogFile, tlsConn: tlsConn, target: target, path: path, proxy: proxy}, err
}
func (c *DoHClient) Close() {
if c.tcpConn != nil {
c.tcpConn.Close()
}
if c.keyLogFile != nil {
c.keyLogFile.Close()
}
if c.tlsConn != nil {
c.tlsConn.Close()
}
}
func (c *DoHClient) Query(domain, queryType string, dnssec bool) error {
DNSMessage, err := do53.NewDNSMessage(domain, queryType)
if err != nil {
return err
}
httpReq, err := http.NewRequest("POST", "https://"+c.target+"/"+c.path, bytes.NewBuffer(DNSMessage))
if err != nil {
return fmt.Errorf("failed to create HTTP request: %v", err)
}
httpReq.Header.Add("Content-Type", "application/dns-message")
httpReq.Header.Set("Accept", "application/dns-message")
err = httpReq.Write(c.tlsConn)
if err != nil {
return fmt.Errorf("failed writing HTTP request: %v", err)
}
reader := bufio.NewReader(c.tlsConn)
resp, err := http.ReadResponse(reader, httpReq)
if err != nil {
return fmt.Errorf("failed reading HTTP response: %v", err)
}
defer resp.Body.Close()
responseBody := make([]byte, 4096)
n, err := resp.Body.Read(responseBody)
if err != nil && err != io.EOF {
return fmt.Errorf("failed reading response body: %v", err)
}
recvMsg := new(dns.Msg)
err = recvMsg.Unpack(responseBody[:n])
if err != nil {
return fmt.Errorf("failed to parse DNS response: %v", err)
}
// TODO: Check if the response had no errors or TD bit set
for _, answer := range recvMsg.Answer {
fmt.Println(answer.String())
}
return nil
}