pax_global_header00006660000000000000000000000064135510331460014513gustar00rootroot0000000000000052 comment=3e6e00f2b602d3f3e4949e8091e3c4a858c77479 go-dnsstamps-0.1.0/000077500000000000000000000000001355103314600141305ustar00rootroot00000000000000go-dnsstamps-0.1.0/.gitignore000066400000000000000000000004231355103314600161170ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ go-dnsstamps-0.1.0/LICENSE000066400000000000000000000020611355103314600151340ustar00rootroot00000000000000MIT License Copyright (c) 2018-2019 Frank Denis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-dnsstamps-0.1.0/README.md000066400000000000000000000000511355103314600154030ustar00rootroot00000000000000# go-dnsstamps DNS Stamps library for Go go-dnsstamps-0.1.0/dnsstamps.go000066400000000000000000000242451355103314600165020ustar00rootroot00000000000000package dnsstamps import ( "encoding/base64" "encoding/binary" "encoding/hex" "errors" "fmt" "net" "strconv" "strings" ) const DefaultPort = 443 type ServerInformalProperties uint64 const ( ServerInformalPropertyDNSSEC = ServerInformalProperties(1) << 0 ServerInformalPropertyNoLog = ServerInformalProperties(1) << 1 ServerInformalPropertyNoFilter = ServerInformalProperties(1) << 2 ) type StampProtoType uint8 const ( StampProtoTypePlain = StampProtoType(0x00) StampProtoTypeDNSCrypt = StampProtoType(0x01) StampProtoTypeDoH = StampProtoType(0x02) StampProtoTypeTLS = StampProtoType(0x03) StampProtoTypeDNSCryptRelay = StampProtoType(0x81) ) func (stampProtoType *StampProtoType) String() string { switch *stampProtoType { case StampProtoTypePlain: return "Plain" case StampProtoTypeDNSCrypt: return "DNSCrypt" case StampProtoTypeDoH: return "DoH" case StampProtoTypeDNSCryptRelay: return "Anonymized DNSCrypt" default: panic("Unexpected protocol") } } type ServerStamp struct { ServerAddrStr string ServerPk []uint8 Hashes [][]uint8 ProviderName string Path string Props ServerInformalProperties Proto StampProtoType } func NewDNSCryptServerStampFromLegacy(serverAddrStr string, serverPkStr string, providerName string, props ServerInformalProperties) (ServerStamp, error) { if net.ParseIP(serverAddrStr) != nil { serverAddrStr = fmt.Sprintf("%s:%d", serverAddrStr, DefaultPort) } serverPk, err := hex.DecodeString(strings.Replace(serverPkStr, ":", "", -1)) if err != nil || len(serverPk) != 32 { return ServerStamp{}, fmt.Errorf("Unsupported public key: [%s]", serverPkStr) } return ServerStamp{ ServerAddrStr: serverAddrStr, ServerPk: serverPk, ProviderName: providerName, Props: props, Proto: StampProtoTypeDNSCrypt, }, nil } func NewServerStampFromString(stampStr string) (ServerStamp, error) { if !strings.HasPrefix(stampStr, "sdns:") { return ServerStamp{}, errors.New("Stamps are expected to start with sdns:") } stampStr = stampStr[5:] if strings.HasPrefix(stampStr, "//") { stampStr = stampStr[2:] } bin, err := base64.RawURLEncoding.Strict().DecodeString(stampStr) if err != nil { return ServerStamp{}, err } if len(bin) < 1 { return ServerStamp{}, errors.New("Stamp is too short") } if bin[0] == uint8(StampProtoTypeDNSCrypt) { return newDNSCryptServerStamp(bin) } else if bin[0] == uint8(StampProtoTypeDoH) { return newDoHServerStamp(bin) } else if bin[0] == uint8(StampProtoTypeDNSCryptRelay) { return newDNSCryptRelayStamp(bin) } return ServerStamp{}, errors.New("Unsupported stamp version or protocol") } // id(u8)=0x01 props addrLen(1) serverAddr pkStrlen(1) pkStr providerNameLen(1) providerName func newDNSCryptServerStamp(bin []byte) (ServerStamp, error) { stamp := ServerStamp{Proto: StampProtoTypeDNSCrypt} if len(bin) < 66 { return stamp, errors.New("Stamp is too short") } stamp.Props = ServerInformalProperties(binary.LittleEndian.Uint64(bin[1:9])) binLen := len(bin) pos := 9 length := int(bin[pos]) if 1+length >= binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ stamp.ServerAddrStr = string(bin[pos : pos+length]) pos += length colIndex := strings.LastIndex(stamp.ServerAddrStr, ":") bracketIndex := strings.LastIndex(stamp.ServerAddrStr, "]") if colIndex < bracketIndex { colIndex = -1 } if colIndex < 0 { colIndex = len(stamp.ServerAddrStr) stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, DefaultPort) } if colIndex >= len(stamp.ServerAddrStr)-1 { return stamp, errors.New("Invalid stamp (empty port)") } ipOnly := stamp.ServerAddrStr[:colIndex] portOnly := stamp.ServerAddrStr[colIndex+1:] if _, err := strconv.ParseUint(portOnly, 10, 16); err != nil { return stamp, errors.New("Invalid stamp (port range)") } if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil { return stamp, errors.New("Invalid stamp (IP address)") } length = int(bin[pos]) if 1+length >= binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ stamp.ServerPk = bin[pos : pos+length] pos += length length = int(bin[pos]) if length >= binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ stamp.ProviderName = string(bin[pos : pos+length]) pos += length if pos != binLen { return stamp, errors.New("Invalid stamp (garbage after end)") } return stamp, nil } // id(u8)=0x02 props addrLen(1) serverAddr hashLen(1) hash providerNameLen(1) providerName pathLen(1) path func newDoHServerStamp(bin []byte) (ServerStamp, error) { stamp := ServerStamp{Proto: StampProtoTypeDoH} if len(bin) < 22 { return stamp, errors.New("Stamp is too short") } stamp.Props = ServerInformalProperties(binary.LittleEndian.Uint64(bin[1:9])) binLen := len(bin) pos := 9 length := int(bin[pos]) if 1+length >= binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ stamp.ServerAddrStr = string(bin[pos : pos+length]) pos += length for { vlen := int(bin[pos]) length = vlen & ^0x80 if 1+length >= binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ if length > 0 { stamp.Hashes = append(stamp.Hashes, bin[pos:pos+length]) } pos += length if vlen&0x80 != 0x80 { break } } length = int(bin[pos]) if 1+length >= binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ stamp.ProviderName = string(bin[pos : pos+length]) pos += length length = int(bin[pos]) if length >= binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ stamp.Path = string(bin[pos : pos+length]) pos += length if pos != binLen { return stamp, errors.New("Invalid stamp (garbage after end)") } if len(stamp.ServerAddrStr) > 0 { colIndex := strings.LastIndex(stamp.ServerAddrStr, ":") bracketIndex := strings.LastIndex(stamp.ServerAddrStr, "]") if colIndex < bracketIndex { colIndex = -1 } if colIndex < 0 { colIndex = len(stamp.ServerAddrStr) stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, DefaultPort) } if colIndex >= len(stamp.ServerAddrStr)-1 { return stamp, errors.New("Invalid stamp (empty port)") } ipOnly := stamp.ServerAddrStr[:colIndex] portOnly := stamp.ServerAddrStr[colIndex+1:] if _, err := strconv.ParseUint(portOnly, 10, 16); err != nil { return stamp, errors.New("Invalid stamp (port range)") } if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil { return stamp, errors.New("Invalid stamp (IP address)") } } return stamp, nil } // id(u8)=0x81 addrLen(1) serverAddr func newDNSCryptRelayStamp(bin []byte) (ServerStamp, error) { stamp := ServerStamp{Proto: StampProtoTypeDNSCryptRelay} if len(bin) < 13 { return stamp, errors.New("Stamp is too short") } binLen := len(bin) pos := 1 length := int(bin[pos]) if 1+length > binLen-pos { return stamp, errors.New("Invalid stamp") } pos++ stamp.ServerAddrStr = string(bin[pos : pos+length]) pos += length colIndex := strings.LastIndex(stamp.ServerAddrStr, ":") bracketIndex := strings.LastIndex(stamp.ServerAddrStr, "]") if colIndex < bracketIndex { colIndex = -1 } if colIndex < 0 { colIndex = len(stamp.ServerAddrStr) stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, DefaultPort) } if colIndex >= len(stamp.ServerAddrStr)-1 { return stamp, errors.New("Invalid stamp (empty port)") } ipOnly := stamp.ServerAddrStr[:colIndex] portOnly := stamp.ServerAddrStr[colIndex+1:] if _, err := strconv.ParseUint(portOnly, 10, 16); err != nil { return stamp, errors.New("Invalid stamp (port range)") } if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil { return stamp, errors.New("Invalid stamp (IP address)") } if pos != binLen { return stamp, errors.New("Invalid stamp (garbage after end)") } return stamp, nil } func (stamp *ServerStamp) String() string { if stamp.Proto == StampProtoTypeDNSCrypt { return stamp.dnsCryptString() } else if stamp.Proto == StampProtoTypeDoH { return stamp.dohString() } else if stamp.Proto == StampProtoTypeDNSCryptRelay { return stamp.dnsCryptRelayString() } panic("Unsupported protocol") } func (stamp *ServerStamp) dnsCryptString() string { bin := make([]uint8, 9) bin[0] = uint8(StampProtoTypeDNSCrypt) binary.LittleEndian.PutUint64(bin[1:9], uint64(stamp.Props)) serverAddrStr := stamp.ServerAddrStr if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(DefaultPort)) { serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(DefaultPort))] } bin = append(bin, uint8(len(serverAddrStr))) bin = append(bin, []uint8(serverAddrStr)...) bin = append(bin, uint8(len(stamp.ServerPk))) bin = append(bin, stamp.ServerPk...) bin = append(bin, uint8(len(stamp.ProviderName))) bin = append(bin, []uint8(stamp.ProviderName)...) str := base64.RawURLEncoding.EncodeToString(bin) return "sdns://" + str } func (stamp *ServerStamp) dohString() string { bin := make([]uint8, 9) bin[0] = uint8(StampProtoTypeDoH) binary.LittleEndian.PutUint64(bin[1:9], uint64(stamp.Props)) serverAddrStr := stamp.ServerAddrStr if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(DefaultPort)) { serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(DefaultPort))] } bin = append(bin, uint8(len(serverAddrStr))) bin = append(bin, []uint8(serverAddrStr)...) last := len(stamp.Hashes) - 1 for i, hash := range stamp.Hashes { vlen := len(hash) if i < last { vlen |= 0x80 } bin = append(bin, uint8(vlen)) bin = append(bin, hash...) } bin = append(bin, uint8(len(stamp.ProviderName))) bin = append(bin, []uint8(stamp.ProviderName)...) bin = append(bin, uint8(len(stamp.Path))) bin = append(bin, []uint8(stamp.Path)...) str := base64.RawURLEncoding.EncodeToString(bin) return "sdns://" + str } func (stamp *ServerStamp) dnsCryptRelayString() string { bin := make([]uint8, 1) bin[0] = uint8(StampProtoTypeDNSCryptRelay) serverAddrStr := stamp.ServerAddrStr if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(DefaultPort)) { serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(DefaultPort))] } bin = append(bin, uint8(len(serverAddrStr))) bin = append(bin, []uint8(serverAddrStr)...) str := base64.RawURLEncoding.EncodeToString(bin) return "sdns://" + str } go-dnsstamps-0.1.0/go.mod000066400000000000000000000000611355103314600152330ustar00rootroot00000000000000module github.com/jedisct1/go-dnsstamps go 1.12