| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902 |
- // dnstt-server is the server end of a DNS tunnel.
- //
- // Usage:
- // dnstt-server -gen-key [-privkey-file PRIVKEYFILE] [-pubkey-file PUBKEYFILE]
- // dnstt-server -udp ADDR [-privkey PRIVKEY|-privkey-file PRIVKEYFILE] DOMAIN UPSTREAMADDR
- //
- // Example:
- // dnstt-server -gen-key -privkey-file server.key -pubkey-file server.pub
- // dnstt-server -udp :53 -privkey-file server.key t.example.com 127.0.0.1:8000
- //
- // To generate a persistent server private key, first run with the -gen-key
- // option. By default the generated private and public keys are printed to
- // standard output. To save them to files instead, use the -privkey-file and
- // -pubkey-file options.
- // dnstt-server -gen-key
- // dnstt-server -gen-key -privkey-file server.key -pubkey-file server.pub
- //
- // You can give the server's private key as a file or as a hex string.
- // -privkey-file server.key
- // -privkey 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
- //
- // The -udp option controls the address that will listen for incoming DNS
- // queries.
- //
- // The -mtu option controls the maximum size of response UDP payloads.
- // Queries that do not advertise requester support for responses of at least
- // this size at least this size will be responded to with a FORMERR. The default
- // value is maxUDPPayload.
- //
- // DOMAIN is the root of the DNS zone reserved for the tunnel. See README for
- // instructions on setting it up.
- //
- // UPSTREAMADDR is the TCP address to which incoming tunnelled streams will be
- // forwarded.
- package main
- import (
- "bytes"
- "encoding/base32"
- "encoding/binary"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net"
- "os"
- "sync"
- "time"
- "github.com/xtaci/kcp-go/v5"
- "github.com/xtaci/smux"
- "www.bamsoftware.com/git/dnstt.git/dns"
- "www.bamsoftware.com/git/dnstt.git/noise"
- "www.bamsoftware.com/git/dnstt.git/turbotunnel"
- )
- const (
- // smux streams will be closed after this much time without receiving data.
- idleTimeout = 10 * time.Minute
- // How to set the TTL field in Answer resource records.
- responseTTL = 60
- // How long we may wait for downstream data before sending an empty
- // response. If another query comes in while we are waiting, we'll send
- // an empty response anyway and restart the delay timer for the next
- // response.
- //
- // This number should be less than 2 seconds, which in 2019 was reported
- // to be the query timeout of the Quad9 DoH server.
- // https://dnsencryption.info/imc19-doe.html Section 4.2, Finding 2.4
- maxResponseDelay = 1 * time.Second
- )
- var (
- // We don't send UDP payloads larger than this, in an attempt to avoid
- // network-layer fragmentation. 1280 is the minimum IPv6 MTU, 40 bytes
- // is the size of an IPv6 header (though without any extension headers),
- // and 8 bytes is the size of a UDP header.
- //
- // Control this value with the -mtu command-line option.
- //
- // https://dnsflagday.net/2020/#message-size-considerations
- // "An EDNS buffer size of 1232 bytes will avoid fragmentation on nearly
- // all current networks."
- //
- // On 2020-04-19, the Quad9 resolver was seen to have a UDP payload size
- // of 1232. Cloudflare's was 1452, and Google's was 4096.
- maxUDPPayload = 1280 - 40 - 8
- )
- // base32Encoding is a base32 encoding without padding.
- var base32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
- // generateKeypair generates a private key and the corresponding public key. If
- // privkeyFilename and pubkeyFilename are respectively empty, it prints the
- // corresponding key to standard output; otherwise it saves the key to the given
- // file name. The private key is saved with mode 0400 and the public key is
- // saved with 0666 (before umask). In case of any error, it attempts to delete
- // any files it has created before returning.
- func generateKeypair(privkeyFilename, pubkeyFilename string) (err error) {
- // Filenames to delete in case of error (avoid leaving partially written
- // files).
- var toDelete []string
- defer func() {
- for _, filename := range toDelete {
- fmt.Fprintf(os.Stderr, "deleting partially written file %s\n", filename)
- if closeErr := os.Remove(filename); closeErr != nil {
- fmt.Fprintf(os.Stderr, "cannot remove %s: %v\n", filename, closeErr)
- if err == nil {
- err = closeErr
- }
- }
- }
- }()
- privkey, pubkey, err := noise.GenerateKeypair()
- if err != nil {
- return err
- }
- if privkeyFilename != "" {
- // Save the privkey to a file.
- f, err := os.OpenFile(privkeyFilename, os.O_RDWR|os.O_CREATE, 0400)
- if err != nil {
- return err
- }
- toDelete = append(toDelete, privkeyFilename)
- err = noise.WriteKey(f, privkey)
- if err2 := f.Close(); err == nil {
- err = err2
- }
- if err != nil {
- return err
- }
- }
- if pubkeyFilename != "" {
- // Save the pubkey to a file.
- f, err := os.Create(pubkeyFilename)
- if err != nil {
- return err
- }
- toDelete = append(toDelete, pubkeyFilename)
- err = noise.WriteKey(f, pubkey)
- if err2 := f.Close(); err == nil {
- err = err2
- }
- if err != nil {
- return err
- }
- }
- // All good, allow the written files to remain.
- toDelete = nil
- if privkeyFilename != "" {
- fmt.Printf("privkey written to %s\n", privkeyFilename)
- } else {
- fmt.Printf("privkey %x\n", privkey)
- }
- if pubkeyFilename != "" {
- fmt.Printf("pubkey written to %s\n", pubkeyFilename)
- } else {
- fmt.Printf("pubkey %x\n", pubkey)
- }
- return nil
- }
- // readKeyFromFile reads a key from a named file.
- func readKeyFromFile(filename string) ([]byte, error) {
- f, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- return noise.ReadKey(f)
- }
- // handleStream bidirectionally connects a client stream with a TCP socket
- // addressed by upstream.
- func handleStream(stream *smux.Stream, upstream *net.TCPAddr, conv uint32) error {
- conn, err := net.DialTCP("tcp", nil, upstream)
- if err != nil {
- return fmt.Errorf("stream %08x:%d connect upstream: %v", conv, stream.ID(), err)
- }
- defer conn.Close()
- var wg sync.WaitGroup
- wg.Add(2)
- go func() {
- defer wg.Done()
- _, err := io.Copy(stream, conn)
- if err == io.EOF {
- // smux Stream.Write may return io.EOF.
- err = nil
- }
- if err != nil {
- log.Printf("stream %08x:%d copy stream←upstream: %v\n", conv, stream.ID(), err)
- }
- conn.CloseRead()
- stream.Close()
- }()
- go func() {
- defer wg.Done()
- _, err := io.Copy(conn, stream)
- if err == io.EOF {
- // smux Stream.WriteTo may return io.EOF.
- err = nil
- }
- if err != nil && err != io.ErrClosedPipe {
- log.Printf("stream %08x:%d copy upstream←stream: %v\n", conv, stream.ID(), err)
- }
- conn.CloseWrite()
- }()
- wg.Wait()
- return nil
- }
- // acceptStreams wraps a KCP session in a Noise channel and an smux.Session,
- // then awaits smux streams. It passes each stream to handleStream.
- func acceptStreams(conn *kcp.UDPSession, privkey, pubkey []byte, upstream *net.TCPAddr) error {
- // Put a Noise channel on top of the KCP conn.
- rw, err := noise.NewServer(conn, privkey, pubkey)
- if err != nil {
- return err
- }
- // Put an smux session on top of the encrypted Noise channel.
- smuxConfig := smux.DefaultConfig()
- smuxConfig.Version = 2
- smuxConfig.KeepAliveTimeout = idleTimeout
- sess, err := smux.Server(rw, smuxConfig)
- if err != nil {
- return err
- }
- defer sess.Close()
- for {
- stream, err := sess.AcceptStream()
- if err != nil {
- if err, ok := err.(net.Error); ok && err.Temporary() {
- continue
- }
- return err
- }
- log.Printf("begin stream %08x:%d", conn.GetConv(), stream.ID())
- go func() {
- defer func() {
- log.Printf("end stream %08x:%d", conn.GetConv(), stream.ID())
- stream.Close()
- }()
- err := handleStream(stream, upstream, conn.GetConv())
- if err != nil {
- log.Printf("stream %08x:%d handleStream: %v\n", conn.GetConv(), stream.ID(), err)
- }
- }()
- }
- }
- // acceptSessions listens for incoming KCP connections and passes them to
- // acceptStreams.
- func acceptSessions(ln *kcp.Listener, privkey, pubkey []byte, mtu int, upstream *net.TCPAddr) error {
- for {
- conn, err := ln.AcceptKCP()
- if err != nil {
- if err, ok := err.(net.Error); ok && err.Temporary() {
- continue
- }
- return err
- }
- log.Printf("begin session %08x", conn.GetConv())
- // Permit coalescing the payloads of consecutive sends.
- conn.SetStreamMode(true)
- // Disable the dynamic congestion window (limit only by the
- // maximum of local and remote static windows).
- conn.SetNoDelay(
- 0, // default nodelay
- 0, // default interval
- 0, // default resend
- 1, // nc=1 => congestion window off
- )
- if rc := conn.SetMtu(mtu); !rc {
- panic(rc)
- }
- go func() {
- defer func() {
- log.Printf("end session %08x", conn.GetConv())
- conn.Close()
- }()
- err := acceptStreams(conn, privkey, pubkey, upstream)
- if err != nil {
- log.Printf("session %08x acceptStreams: %v\n", conn.GetConv(), err)
- }
- }()
- }
- }
- // nextPacket reads the next length-prefixed packet from r, ignoring padding. It
- // returns a nil error only when a packet was read successfully. It returns
- // io.EOF only when there were 0 bytes remaining to read from r. It returns
- // io.ErrUnexpectedEOF when EOF occurs in the middle of an encoded packet.
- //
- // The prefixing scheme is as follows. A length prefix L < 0xe0 means a data
- // packet of L bytes. A length prefix L >= 0xe0 means padding of L - 0xe0 bytes
- // (not counting the length of the length prefix itself).
- func nextPacket(r *bytes.Reader) ([]byte, error) {
- // Convert io.EOF to io.ErrUnexpectedEOF.
- eof := func(err error) error {
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- }
- return err
- }
- for {
- prefix, err := r.ReadByte()
- if err != nil {
- // We may return a real io.EOF only here.
- return nil, err
- }
- if prefix >= 224 {
- paddingLen := prefix - 224
- _, err := io.CopyN(ioutil.Discard, r, int64(paddingLen))
- if err != nil {
- return nil, eof(err)
- }
- } else {
- p := make([]byte, int(prefix))
- _, err = io.ReadFull(r, p)
- return p, eof(err)
- }
- }
- }
- // responseFor constructs a response dns.Message that is appropriate for query.
- // Along with the dns.Message, it returns the query's decoded data payload. If
- // the returned dns.Message is nil, it means that there should be no response to
- // this query. If the returned dns.Message has an Rcode() of dns.RcodeNoError,
- // the message is a candidate for for carrying downstream data in a TXT record.
- func responseFor(query *dns.Message, domain dns.Name) (*dns.Message, []byte) {
- resp := &dns.Message{
- ID: query.ID,
- Flags: 0x8000, // QR = 1, RCODE = no error
- Question: query.Question,
- }
- if query.Flags&0x8000 != 0 {
- // QR != 0, this is not a query. Don't even send a response.
- return nil, nil
- }
- // Check for EDNS(0) support. Include our own OPT RR only if we receive
- // one from the requester.
- // https://tools.ietf.org/html/rfc6891#section-6.1.1
- // "Lack of presence of an OPT record in a request MUST be taken as an
- // indication that the requester does not implement any part of this
- // specification and that the responder MUST NOT include an OPT record
- // in its response."
- payloadSize := 0
- for _, rr := range query.Additional {
- if rr.Type != dns.RRTypeOPT {
- continue
- }
- if len(resp.Additional) != 0 {
- // https://tools.ietf.org/html/rfc6891#section-6.1.1
- // "If a query message with more than one OPT RR is
- // received, a FORMERR (RCODE=1) MUST be returned."
- resp.Flags |= dns.RcodeFormatError
- log.Printf("FORMERR: more than one OPT RR")
- return resp, nil
- }
- resp.Additional = append(resp.Additional, dns.RR{
- Name: dns.Name{},
- Type: dns.RRTypeOPT,
- Class: 4096, // responder's UDP payload size
- TTL: 0,
- Data: []byte{},
- })
- additional := &resp.Additional[0]
- version := (rr.TTL >> 16) & 0xff
- if version != 0 {
- // https://tools.ietf.org/html/rfc6891#section-6.1.1
- // "If a responder does not implement the VERSION level
- // of the request, then it MUST respond with
- // RCODE=BADVERS."
- resp.Flags |= dns.ExtendedRcodeBadVers & 0xf
- additional.TTL = (dns.ExtendedRcodeBadVers >> 4) << 24
- log.Printf("BADVERS: EDNS version %d != 0", version)
- return resp, nil
- }
- payloadSize = int(rr.Class)
- }
- if payloadSize < 512 {
- // https://tools.ietf.org/html/rfc6891#section-6.1.1 "Values
- // lower than 512 MUST be treated as equal to 512."
- payloadSize = 512
- }
- // We will return RcodeFormatError if payloadSize is too small, but
- // first, check the name in order to set the AA bit properly.
- // There must be exactly one question.
- if len(query.Question) != 1 {
- resp.Flags |= dns.RcodeFormatError
- log.Printf("FORMERR: too few or too many questions (%d)", len(query.Question))
- return resp, nil
- }
- question := query.Question[0]
- // Check the name to see if it ends in our chosen domain, and extract
- // all that comes before the domain if it does. If it does not, we will
- // return RcodeNameError below, but prefer to return RcodeFormatError
- // for payload size if that applies as well.
- prefix, ok := question.Name.TrimSuffix(domain)
- if !ok {
- // Not a name we are authoritative for.
- resp.Flags |= dns.RcodeNameError
- log.Printf("NXDOMAIN: not authoritative for %s", question.Name)
- return resp, nil
- }
- resp.Flags |= 0x0400 // AA = 1
- if query.Opcode() != 0 {
- // We don't support OPCODE != QUERY.
- resp.Flags |= dns.RcodeNotImplemented
- log.Printf("NOTIMPL: unrecognized OPCODE %d", query.Opcode())
- return resp, nil
- }
- if question.Type != dns.RRTypeTXT {
- // We only support QTYPE == TXT.
- resp.Flags |= dns.RcodeNameError
- // No log message here; it's common for recursive resolvers to
- // send NS or A queries when the client only asked for a TXT. I
- // suspect this is related to QNAME minimization, but I'm not
- // sure. https://tools.ietf.org/html/rfc7816
- // log.Printf("NXDOMAIN: QTYPE %d != TXT", question.Type)
- return resp, nil
- }
- encoded := bytes.ToUpper(bytes.Join(prefix, nil))
- payload := make([]byte, base32Encoding.DecodedLen(len(encoded)))
- n, err := base32Encoding.Decode(payload, encoded)
- if err != nil {
- // Base32 error, make like the name doesn't exist.
- resp.Flags |= dns.RcodeNameError
- log.Printf("NXDOMAIN: base32 decoding: %v", err)
- return resp, nil
- }
- payload = payload[:n]
- // We require clients to support EDNS(0) with a minimum payload size;
- // otherwise we would have to set a small KCP MTU (only around 200
- // bytes). https://tools.ietf.org/html/rfc6891#section-7 "If there is a
- // problem with processing the OPT record itself, such as an option
- // value that is badly formatted or that includes out-of-range values, a
- // FORMERR MUST be returned."
- if payloadSize < maxUDPPayload {
- resp.Flags |= dns.RcodeFormatError
- log.Printf("FORMERR: requester payload size %d is too small (minimum %d)", payloadSize, maxUDPPayload)
- return resp, nil
- }
- return resp, payload
- }
- // record represents a DNS message appropriate for a response to a previously
- // received query, along with metadata necessary for sending the response.
- // recvLoop sends instances of record to sendLoop via a channel. sendLoop
- // receives instances of record and may fill in the message's Answer section
- // before sending it.
- type record struct {
- Resp *dns.Message
- Addr net.Addr
- ClientID turbotunnel.ClientID
- }
- // recvLoop repeatedly calls dnsConn.ReadFrom, extracts the packets contained in
- // the incoming DNS queries, and puts them on ttConn's incoming queue. Whenever
- // a query calls for a response, constructs a partial response and passes it to
- // sendLoop over ch.
- func recvLoop(domain dns.Name, dnsConn net.PacketConn, ttConn *turbotunnel.QueuePacketConn, ch chan<- *record) error {
- for {
- var buf [4096]byte
- n, addr, err := dnsConn.ReadFrom(buf[:])
- if err != nil {
- if err, ok := err.(net.Error); ok && err.Temporary() {
- log.Printf("ReadFrom temporary error: %v", err)
- continue
- }
- return err
- }
- // Got a UDP packet. Try to parse it as a DNS message.
- query, err := dns.MessageFromWireFormat(buf[:n])
- if err != nil {
- log.Printf("cannot parse DNS query: %v", err)
- continue
- }
- resp, payload := responseFor(&query, domain)
- // Extract the ClientID from the payload.
- var clientID turbotunnel.ClientID
- n = copy(clientID[:], payload)
- payload = payload[n:]
- if n == len(clientID) {
- // Discard padding and pull out the packets contained in
- // the payload.
- r := bytes.NewReader(payload)
- for {
- p, err := nextPacket(r)
- if err != nil {
- break
- }
- // Feed the incoming packet to KCP.
- ttConn.QueueIncoming(p, clientID)
- }
- } else {
- // Payload is not long enough to contain a ClientID.
- if resp != nil && resp.Rcode() == dns.RcodeNoError {
- resp.Flags |= dns.RcodeNameError
- log.Printf("NXDOMAIN: %d bytes are too short to contain a ClientID", n)
- }
- }
- // If a response is called for, pass it to sendLoop via the channel.
- if resp != nil {
- select {
- case ch <- &record{resp, addr, clientID}:
- default:
- }
- }
- }
- }
- // sendLoop repeatedly receives records from ch. Those that represent an error
- // response, it sends on the network immediately. Those that represent a
- // response capable of carrying data, it packs full of as many packets as will
- // fit while keeping the total size under maxEncodedPayload, then sends it.
- func sendLoop(dnsConn net.PacketConn, ttConn *turbotunnel.QueuePacketConn, ch <-chan *record, maxEncodedPayload int) error {
- var nextRec *record
- for {
- rec := nextRec
- nextRec = nil
- if rec == nil {
- var ok bool
- rec, ok = <-ch
- if !ok {
- break
- }
- }
- if rec.Resp.Rcode() == dns.RcodeNoError && len(rec.Resp.Question) == 1 {
- // If it's a non-error response, we can fill the Answer
- // section with downstream packets.
- // Any changes to how responses are built need to happen
- // also in computeMaxEncodedPayload.
- rec.Resp.Answer = []dns.RR{
- {
- Name: rec.Resp.Question[0].Name,
- Type: rec.Resp.Question[0].Type,
- Class: rec.Resp.Question[0].Class,
- TTL: responseTTL,
- Data: nil, // will be filled in below
- },
- }
- var payload bytes.Buffer
- limit := maxEncodedPayload
- // We loop and bundle as many packets from OutgoingQueue
- // into the response as will fit. Any packet that would
- // overflow the capacity of the DNS response, we stash
- // to be bundled into a future response.
- timer := time.NewTimer(maxResponseDelay)
- loop:
- for {
- var p []byte
- select {
- // Check the nextRec, timer, and stash cases
- // before considering the OutgoingQueue case.
- // Only if all these cases fail do we enter the
- // default arm, where they are checked again in
- // addition to OutgoingQueue.
- case nextRec = <-ch:
- // If there's another response waiting
- // to be sent, wait no longer for a
- // payload for this one.
- break loop
- case <-timer.C:
- break loop
- case p = <-ttConn.Unstash(rec.ClientID):
- default:
- select {
- case nextRec = <-ch:
- break loop
- case <-timer.C:
- break loop
- case p = <-ttConn.Unstash(rec.ClientID):
- case p = <-ttConn.OutgoingQueue(rec.ClientID):
- }
- }
- // We wait for the first packet in a bundle
- // only. The second and later packets must be
- // immediately available or they will be omitted
- // from this bundle.
- timer.Reset(0)
- limit -= 2 + len(p)
- if payload.Len() == 0 {
- // No packet length check for the first
- // packet; if it's too large, we allow
- // it to be truncated and dropped by the
- // receiver.
- } else if limit < 0 {
- // Stash this packet to send in the next
- // response.
- ttConn.Stash(p, rec.ClientID)
- break loop
- }
- if int(uint16(len(p))) != len(p) {
- panic(len(p))
- }
- binary.Write(&payload, binary.BigEndian, uint16(len(p)))
- payload.Write(p)
- }
- timer.Stop()
- rec.Resp.Answer[0].Data = dns.EncodeRDataTXT(payload.Bytes())
- }
- buf, err := rec.Resp.WireFormat()
- if err != nil {
- log.Printf("resp WireFormat: %v", err)
- continue
- }
- // Truncate if necessary.
- // https://tools.ietf.org/html/rfc1035#section-4.1.1
- if len(buf) > maxUDPPayload {
- log.Printf("truncating response of %d bytes to max of %d", len(buf), maxUDPPayload)
- buf = buf[:maxUDPPayload]
- buf[2] |= 0x02 // TC = 1
- }
- // Now we actually send the message as a UDP packet.
- _, err = dnsConn.WriteTo(buf, rec.Addr)
- if err != nil {
- if err, ok := err.(net.Error); ok && err.Temporary() {
- log.Printf("WriteTo temporary error: %v", err)
- continue
- }
- return err
- }
- }
- return nil
- }
- // computeMaxEncodedPayload computes the maximum amount of downstream TXT RR
- // data that keep the overall response size less than maxUDPPayload, in the
- // worst case when the response answers a query that has a maximum-length name
- // in its Question section. Returns 0 in the case that no amount of data makes
- // the overall response size small enough.
- //
- // This function needs to be kept in sync with sendLoop with regard to how it
- // builds candidate responses.
- func computeMaxEncodedPayload(limit int) int {
- // 64+64+64+62 octets, needs to be base32-decodable.
- maxLengthName, err := dns.NewName([][]byte{
- []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
- []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
- []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
- []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
- })
- if err != nil {
- panic(err)
- }
- if len(maxLengthName.String())+2 != 255 {
- panic(fmt.Sprintf("max-length name is %d octets, should be %d %s",
- len(maxLengthName.String())+2, 255, maxLengthName))
- }
- queryLimit := uint16(limit)
- if int(queryLimit) != limit {
- queryLimit = 0xffff
- }
- query := &dns.Message{
- Question: []dns.Question{
- {
- Name: maxLengthName,
- Type: dns.RRTypeTXT,
- Class: dns.RRTypeTXT,
- },
- },
- // EDNS(0)
- Additional: []dns.RR{
- {
- Name: dns.Name{},
- Type: dns.RRTypeOPT,
- Class: queryLimit, // requester's UDP payload size
- TTL: 0, // extended RCODE and flags
- Data: []byte{},
- },
- },
- }
- resp, _ := responseFor(query, dns.Name([][]byte{}))
- // As in sendLoop.
- resp.Answer = []dns.RR{
- {
- Name: query.Question[0].Name,
- Type: query.Question[0].Type,
- Class: query.Question[0].Class,
- TTL: responseTTL,
- Data: nil, // will be filled in below
- },
- }
- // Binary search to find the maximum payload length that does not result
- // in a wire-format message whose length exceeds the limit.
- low := 0
- high := 32768
- for low+1 < high {
- mid := (low + high) / 2
- resp.Answer[0].Data = dns.EncodeRDataTXT(make([]byte, mid))
- buf, err := resp.WireFormat()
- if err != nil {
- panic(err)
- }
- if len(buf) <= limit {
- low = mid
- } else {
- high = mid
- }
- }
- return low
- }
- func run(privkey, pubkey []byte, domain dns.Name, upstream net.Addr, dnsConn net.PacketConn) error {
- defer dnsConn.Close()
- log.Printf("pubkey %x", pubkey)
- // We have a variable amount of room in which to encode downstream
- // packets in each response, because each response must contain the
- // query's Question section, which is of variable length. But we cannot
- // give dynamic packet size limits to KCP; the best we can do is set a
- // global maximum which no packet will exceed. We choose that maximum to
- // keep the UDP payload size under maxUDPPayload, even in the worst case
- // of a maximum-length name in the query's Question section.
- maxEncodedPayload := computeMaxEncodedPayload(maxUDPPayload)
- // 2 bytes accounts for a packet length prefix.
- mtu := maxEncodedPayload - 2
- if mtu < 80 {
- if mtu < 0 {
- mtu = 0
- }
- return fmt.Errorf("maximum UDP payload size of %d leaves only %d bytes for payload", maxUDPPayload, mtu)
- }
- log.Printf("effective MTU %d\n", mtu)
- // Start up the virtual PacketConn for turbotunnel.
- ttConn := turbotunnel.NewQueuePacketConn(turbotunnel.DummyAddr{}, idleTimeout*2)
- ln, err := kcp.ServeConn(nil, 0, 0, ttConn)
- if err != nil {
- return fmt.Errorf("opening KCP listener: %v", err)
- }
- defer ln.Close()
- go func() {
- err := acceptSessions(ln, privkey, pubkey, mtu, upstream.(*net.TCPAddr))
- if err != nil {
- log.Printf("acceptSessions: %v\n", err)
- }
- }()
- ch := make(chan *record, 100)
- defer close(ch)
- // We could run multiple copies of sendLoop; that would allow more time
- // for each response to collect downstream data before being evicted by
- // another response that needs to be sent.
- go func() {
- err := sendLoop(dnsConn, ttConn, ch, maxEncodedPayload)
- if err != nil {
- log.Printf("sendLoop: %v", err)
- }
- }()
- return recvLoop(domain, dnsConn, ttConn, ch)
- }
- func main() {
- var genKey bool
- var privkeyFilename string
- var privkeyString string
- var pubkeyFilename string
- var udpAddr string
- flag.Usage = func() {
- fmt.Fprintf(flag.CommandLine.Output(), `Usage:
- %[1]s -gen-key -privkey-file PRIVKEYFILE -pubkey-file PUBKEYFILE
- %[1]s -udp ADDR -privkey-file PRIVKEYFILE DOMAIN UPSTREAMADDR
- Example:
- %[1]s -gen-key -privkey-file server.key -pubkey-file server.pub
- %[1]s -udp :53 -privkey-file server.key t.example.com 127.0.0.1:8000
- `, os.Args[0])
- flag.PrintDefaults()
- }
- flag.BoolVar(&genKey, "gen-key", false, "generate a server keypair; print to stdout or save to files")
- flag.IntVar(&maxUDPPayload, "mtu", maxUDPPayload, "maximum size of DNS responses")
- flag.StringVar(&privkeyString, "privkey", "", fmt.Sprintf("server private key (%d hex digits)", noise.KeyLen*2))
- flag.StringVar(&privkeyFilename, "privkey-file", "", "read server private key from file (with -gen-key, write to file)")
- flag.StringVar(&pubkeyFilename, "pubkey-file", "", "with -gen-key, write server public key to file")
- flag.StringVar(&udpAddr, "udp", "", "UDP address to listen on (required)")
- flag.Parse()
- log.SetFlags(log.LstdFlags | log.LUTC)
- if genKey {
- // -gen-key mode.
- if flag.NArg() != 0 || privkeyString != "" || udpAddr != "" {
- flag.Usage()
- os.Exit(1)
- }
- if err := generateKeypair(privkeyFilename, pubkeyFilename); err != nil {
- fmt.Fprintf(os.Stderr, "cannot generate keypair: %v\n", err)
- os.Exit(1)
- }
- } else {
- // Ordinary server mode.
- if flag.NArg() != 2 {
- flag.Usage()
- os.Exit(1)
- }
- domain, err := dns.ParseName(flag.Arg(0))
- if err != nil {
- fmt.Fprintf(os.Stderr, "invalid domain %+q: %v\n", flag.Arg(0), err)
- os.Exit(1)
- }
- upstream, err := net.ResolveTCPAddr("tcp", flag.Arg(1))
- if err != nil {
- fmt.Fprintf(os.Stderr, "cannot resolve %+q: %v\n", flag.Arg(1), err)
- os.Exit(1)
- }
- if udpAddr == "" {
- fmt.Fprintf(os.Stderr, "the -udp option is required\n")
- os.Exit(1)
- }
- dnsConn, err := net.ListenPacket("udp", udpAddr)
- if err != nil {
- fmt.Fprintf(os.Stderr, "opening UDP listener: %v\n", err)
- os.Exit(1)
- }
- if pubkeyFilename != "" {
- fmt.Fprintf(os.Stderr, "-pubkey-file may only be used with -gen-key\n")
- os.Exit(1)
- }
- var privkey []byte
- if privkeyFilename != "" && privkeyString != "" {
- fmt.Fprintf(os.Stderr, "only one of -privkey and -privkey-file may be used\n")
- os.Exit(1)
- } else if privkeyFilename != "" {
- var err error
- privkey, err = readKeyFromFile(privkeyFilename)
- if err != nil {
- fmt.Fprintf(os.Stderr, "cannot read privkey from file: %v\n", err)
- os.Exit(1)
- }
- } else if privkeyString != "" {
- var err error
- privkey, err = noise.DecodeKey(privkeyString)
- if err != nil {
- fmt.Fprintf(os.Stderr, "privkey format error: %v\n", err)
- os.Exit(1)
- }
- }
- if len(privkey) == 0 {
- log.Println("generating a temporary one-time keypair")
- log.Println("use the -privkey or -privkey-file option for a persistent server keypair")
- var err error
- privkey, _, err = noise.GenerateKeypair()
- if err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- }
- pubkey := noise.PubkeyFromPrivkey(privkey)
- err = run(privkey, pubkey, domain, upstream, dnsConn)
- if err != nil {
- log.Fatal(err)
- }
- }
- }
|