Add basic DNS and DNS-over-TLS implementation
- add random and incremental DNS fallback strategy - add option to handle DNS requests based on regex patterns - made Makefiles plugin independent - extended config - set default plugins directory in Dockerfile
This commit is contained in:
parent
0ed90708d8
commit
63ba6da810
15 changed files with 546 additions and 27 deletions
|
@ -32,6 +32,8 @@ RUN make CONTAINER=yes
|
|||
|
||||
FROM scratch
|
||||
|
||||
ENV INETMOCK_PLUGINS_DIRECTORY=/app/plugins/
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /etc/passwd /etc/group /etc/
|
||||
|
|
36
config.yaml
36
config.yaml
|
@ -38,4 +38,38 @@ endpoints:
|
|||
certCachePath: /tmp/inetmock/
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
port: 80
|
||||
plainDns:
|
||||
handler: dns_mock
|
||||
listenAddress: 0.0.0.0
|
||||
port: 53
|
||||
options:
|
||||
fallback:
|
||||
strategy: incremental
|
||||
args:
|
||||
startIP: 10.0.0.0
|
||||
rules:
|
||||
- pattern: ".*\\.google\\.com"
|
||||
response: 1.1.1.1
|
||||
- pattern: ".*\\.reddit\\.com"
|
||||
response: 2.2.2.2
|
||||
dnsOverTlsDowngrade:
|
||||
handler: tls_interceptor
|
||||
listenAddress: 0.0.0.0
|
||||
port: 853
|
||||
options:
|
||||
ecdsaCurve: P256
|
||||
validity:
|
||||
ca:
|
||||
notBeforeRelative: 17520h
|
||||
notAfterRelative: 17520h
|
||||
domain:
|
||||
notBeforeRelative: 168h
|
||||
notAfterRelative: 168h
|
||||
rootCaCert:
|
||||
publicKey: ./ca.pem
|
||||
privateKey: ./ca.key
|
||||
certCachePath: /tmp/inetmock/
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 53
|
2
go.mod
2
go.mod
|
@ -3,10 +3,10 @@ module github.com/baez90/inetmock
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/miekg/dns v1.1.29
|
||||
github.com/spf13/cobra v0.0.6
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.4.0
|
||||
go.uber.org/zap v1.14.1
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
)
|
||||
|
|
15
go.sum
15
go.sum
|
@ -60,6 +60,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
|
@ -129,11 +131,14 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -141,10 +146,13 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -154,6 +162,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv
|
|||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -164,9 +174,10 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
|||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f h1:3MlESg/jvTr87F4ttA/q4B+uhe/q6qleC9/DP+IwQmY=
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
|
|
44
pkg/plugins/dns_mock/Makefile
Normal file
44
pkg/plugins/dns_mock/Makefile
Normal file
|
@ -0,0 +1,44 @@
|
|||
VERSION = $(shell git describe --dirty --tags --always)
|
||||
DIR = $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
PKGS = $(shell go list ./...)
|
||||
TEST_PKGS = $(shell find . -type f -name "*_test.go" -printf '%h\n' | sort -u)
|
||||
GOARGS = GOOS=linux GOARCH=amd64
|
||||
GO_BUILD_ARGS = -buildmode=plugin -ldflags="-w -s"
|
||||
GO_CONTAINER_BUILD_ARGS = -buildmode=plugin -ldflags="-w -s" -a -installsuffix cgo
|
||||
GO_DEBUG_BUILD_ARGS = -buildmode=plugin -gcflags "all=-N -l"
|
||||
PLUGIN_NAME = $(shell basename $(DIR)).so
|
||||
OUT_DIR = $(DIR)../../../plugins
|
||||
DEBUG_PORT = 2345
|
||||
|
||||
.PHONY: deps format compile test cli-cover-report html-cover-report
|
||||
|
||||
all: format compile test
|
||||
|
||||
deps:
|
||||
@go mod tidy
|
||||
@go build -buildmode=plugin -v $(DIR)...
|
||||
|
||||
format:
|
||||
@go fmt $(PKGS)
|
||||
|
||||
compile: deps
|
||||
@mkdir -p $(OUT_DIR)
|
||||
ifdef DEBUG
|
||||
@echo 'Compiling for debugging...'
|
||||
@$(GOARGS) go build $(GO_DEBUG_BUILD_ARGS) -o $(OUT_DIR)/$(PLUGIN_NAME) $(DIR)
|
||||
else ifdef CONTAINER
|
||||
@$(GOARGS) go build $(GO_CONTAINER_BUILD_ARGS) -o $(OUT_DIR)/$(PLUGIN_NAME) $(DIR)
|
||||
else
|
||||
@$(GOARGS) go build $(GO_BUILD_ARGS) -o $(OUT_DIR)/$(PLUGIN_NAMEs) $(DIR)
|
||||
endif
|
||||
|
||||
test:
|
||||
@go test -coverprofile=./cov-raw.out -v $(TEST_PKGS)
|
||||
@cat ./cov-raw.out | grep -v "generated" > ./cov.out
|
||||
@rm -f $(DIR)$(PLUGIN_NAME)
|
||||
|
||||
cli-cover-report:
|
||||
@go tool cover -func=cov.out
|
||||
|
||||
html-cover-report:
|
||||
@go tool cover -html=cov.out -o .coverage.html
|
75
pkg/plugins/dns_mock/fallback.go
Normal file
75
pkg/plugins/dns_mock/fallback.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/spf13/viper"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
randomIPStrategyName = "random"
|
||||
incrementalIPStrategyName = "incremental"
|
||||
startIPConfigKey = "startIP"
|
||||
)
|
||||
|
||||
var (
|
||||
fallbackStrategies map[string]ResolverFactory
|
||||
)
|
||||
|
||||
type ResolverFactory func(conf *viper.Viper) ResolverFallback
|
||||
|
||||
func init() {
|
||||
fallbackStrategies = make(map[string]ResolverFactory)
|
||||
fallbackStrategies[incrementalIPStrategyName] = func(conf *viper.Viper) ResolverFallback {
|
||||
return &incrementalIPFallback{
|
||||
latestIp: ipToInt32(net.ParseIP(conf.GetString(startIPConfigKey))),
|
||||
}
|
||||
}
|
||||
fallbackStrategies[randomIPStrategyName] = func(conf *viper.Viper) ResolverFallback {
|
||||
return &randomIPFallback{}
|
||||
}
|
||||
}
|
||||
|
||||
func CreateResolverFallback(name string, config *viper.Viper) ResolverFallback {
|
||||
if factory, ok := fallbackStrategies[name]; ok {
|
||||
return factory(config)
|
||||
} else {
|
||||
return fallbackStrategies[randomIPStrategyName](config)
|
||||
}
|
||||
}
|
||||
|
||||
type ResolverFallback interface {
|
||||
GetIP() net.IP
|
||||
}
|
||||
|
||||
type incrementalIPFallback struct {
|
||||
latestIp uint32
|
||||
}
|
||||
|
||||
func (i *incrementalIPFallback) GetIP() net.IP {
|
||||
if i.latestIp < math.MaxInt32 {
|
||||
i.latestIp += 1
|
||||
}
|
||||
return uint32ToIP(i.latestIp)
|
||||
}
|
||||
|
||||
type randomIPFallback struct {
|
||||
}
|
||||
|
||||
func (randomIPFallback) GetIP() net.IP {
|
||||
return uint32ToIP(uint32(rand.Int31()))
|
||||
}
|
||||
|
||||
func uint32ToIP(i uint32) net.IP {
|
||||
bytes := (*[4]byte)(unsafe.Pointer(&i))[:]
|
||||
return net.IPv4(bytes[3], bytes[2], bytes[1], bytes[0])
|
||||
}
|
||||
|
||||
func ipToInt32(ip net.IP) uint32 {
|
||||
v4 := ip.To4()
|
||||
result := binary.BigEndian.Uint32(v4)
|
||||
return result
|
||||
}
|
106
pkg/plugins/dns_mock/fallback_test.go
Normal file
106
pkg/plugins/dns_mock/fallback_test.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_randomIPFallback_GetIP(t *testing.T) {
|
||||
ra := randomIPFallback{}
|
||||
for i := 0; i < 1000; i++ {
|
||||
if got := ra.GetIP(); reflect.DeepEqual(got, net.IP{}) {
|
||||
t.Errorf("GetIP() = %v", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_incrementalIPFallback_GetIP(t *testing.T) {
|
||||
type fields struct {
|
||||
latestIp uint32
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want []net.IP
|
||||
}{
|
||||
{
|
||||
name: "Expect the next icremental IP",
|
||||
fields: fields{
|
||||
latestIp: 167772160,
|
||||
},
|
||||
want: []net.IP{
|
||||
net.IPv4(10, 0, 0, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Expect a sequence of 5",
|
||||
fields: fields{
|
||||
latestIp: 167772160,
|
||||
},
|
||||
want: []net.IP{
|
||||
net.IPv4(10, 0, 0, 1),
|
||||
net.IPv4(10, 0, 0, 2),
|
||||
net.IPv4(10, 0, 0, 3),
|
||||
net.IPv4(10, 0, 0, 4),
|
||||
net.IPv4(10, 0, 0, 5),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Expect next block to be incremented",
|
||||
fields: fields{
|
||||
latestIp: 167772413,
|
||||
},
|
||||
want: []net.IP{
|
||||
net.IPv4(10, 0, 0, 254),
|
||||
net.IPv4(10, 0, 0, 255),
|
||||
net.IPv4(10, 0, 1, 0),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
i := &incrementalIPFallback{
|
||||
latestIp: tt.fields.latestIp,
|
||||
}
|
||||
for k := 0; k < len(tt.want); k++ {
|
||||
if got := i.GetIP(); !reflect.DeepEqual(got, tt.want[k]) {
|
||||
t.Errorf("GetIP() = %v, want %v", got, tt.want[k])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ipToInt32(t *testing.T) {
|
||||
type args struct {
|
||||
ip net.IP
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want uint32
|
||||
}{
|
||||
{
|
||||
name: "Convert 188.193.106.113 to int",
|
||||
args: args{
|
||||
ip: net.ParseIP("188.193.106.113"),
|
||||
},
|
||||
want: 3166792305,
|
||||
},
|
||||
{
|
||||
name: "Convert 192.168.178.10 to int",
|
||||
args: args{
|
||||
ip: net.ParseIP("192.168.178.10"),
|
||||
},
|
||||
want: 3232281098,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ipToInt32(tt.args.ip); got != tt.want {
|
||||
t.Errorf("ipToInt32() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
25
pkg/plugins/dns_mock/init.go
Normal file
25
pkg/plugins/dns_mock/init.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/baez90/inetmock/internal/plugins"
|
||||
"github.com/baez90/inetmock/pkg/api"
|
||||
"github.com/baez90/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
name = "dns_mock"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger, _ := logging.CreateLogger()
|
||||
logger = logger.With(
|
||||
zap.String("ProtocolHandler", name),
|
||||
)
|
||||
|
||||
plugins.Registry().RegisterHandler(name, func() api.ProtocolHandler {
|
||||
return &dnsHandler{
|
||||
logger: logger,
|
||||
}
|
||||
})
|
||||
}
|
75
pkg/plugins/dns_mock/main.go
Normal file
75
pkg/plugins/dns_mock/main.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/baez90/inetmock/internal/config"
|
||||
"github.com/miekg/dns"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type dnsHandler struct {
|
||||
logger *zap.Logger
|
||||
dnsServer []*dns.Server
|
||||
}
|
||||
|
||||
func (d *dnsHandler) Run(config config.HandlerConfig) {
|
||||
options := loadFromConfig(config.Options())
|
||||
addr := fmt.Sprintf("%s:%d", config.ListenAddress(), config.Port())
|
||||
|
||||
handler := ®exHandler{
|
||||
fallback: options.Fallback,
|
||||
logger: d.logger,
|
||||
}
|
||||
|
||||
for _, rule := range options.Rules {
|
||||
d.logger.Info(
|
||||
"register DNS rule",
|
||||
zap.String("pattern", rule.pattern.String()),
|
||||
zap.String("response", rule.response.String()),
|
||||
)
|
||||
handler.AddRule(rule)
|
||||
}
|
||||
|
||||
d.logger = d.logger.With(
|
||||
zap.String("address", addr),
|
||||
)
|
||||
|
||||
d.dnsServer = []*dns.Server{
|
||||
{
|
||||
Addr: addr,
|
||||
Net: "udp",
|
||||
Handler: handler,
|
||||
},
|
||||
{
|
||||
Addr: addr,
|
||||
Net: "tcp",
|
||||
Handler: handler,
|
||||
},
|
||||
}
|
||||
|
||||
for _, dnsServer := range d.dnsServer {
|
||||
go d.startServer(dnsServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dnsHandler) startServer(dnsServer *dns.Server) {
|
||||
if err := dnsServer.ListenAndServe(); err != nil {
|
||||
d.logger.Error(
|
||||
"failed to start DNS server listener",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dnsHandler) Shutdown(wg *sync.WaitGroup) {
|
||||
for _, dnsServer := range d.dnsServer {
|
||||
if err := dnsServer.Shutdown(); err != nil {
|
||||
d.logger.Error(
|
||||
"failed to shutdown server",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}
|
56
pkg/plugins/dns_mock/protocol_options.go
Normal file
56
pkg/plugins/dns_mock/protocol_options.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"net"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
rulesConfigKey = "rules"
|
||||
patternConfigKey = "pattern"
|
||||
responseConfigKey = "response"
|
||||
fallbackStrategyConfigKey = "fallback.strategy"
|
||||
fallbackArgsConfigKey = "fallback.args"
|
||||
)
|
||||
|
||||
type resolverRule struct {
|
||||
pattern *regexp.Regexp
|
||||
response net.IP
|
||||
}
|
||||
|
||||
type dnsOptions struct {
|
||||
Rules []resolverRule
|
||||
Fallback ResolverFallback
|
||||
}
|
||||
|
||||
func loadFromConfig(config *viper.Viper) dnsOptions {
|
||||
options := dnsOptions{}
|
||||
|
||||
anonRules := config.Get(rulesConfigKey).([]interface{})
|
||||
for _, rule := range anonRules {
|
||||
innerData := rule.(map[interface{}]interface{})
|
||||
var err error
|
||||
var compiledPattern *regexp.Regexp
|
||||
var response net.IP
|
||||
if compiledPattern, err = regexp.Compile(innerData[patternConfigKey].(string)); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if response = net.ParseIP(innerData[responseConfigKey].(string)); response == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
options.Rules = append(options.Rules, resolverRule{
|
||||
pattern: compiledPattern,
|
||||
response: response,
|
||||
})
|
||||
}
|
||||
|
||||
options.Fallback = CreateResolverFallback(
|
||||
config.GetString(fallbackStrategyConfigKey),
|
||||
config.Sub(fallbackArgsConfigKey),
|
||||
)
|
||||
|
||||
return options
|
||||
}
|
86
pkg/plugins/dns_mock/regex_handler.go
Normal file
86
pkg/plugins/dns_mock/regex_handler.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type regexHandler struct {
|
||||
routes []resolverRule
|
||||
fallback ResolverFallback
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (r2 *regexHandler) AddRule(rule resolverRule) {
|
||||
r2.routes = append(r2.routes, rule)
|
||||
}
|
||||
|
||||
func (r2 regexHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
m := new(dns.Msg)
|
||||
m.Compress = false
|
||||
m.SetReply(r)
|
||||
|
||||
switch r.Opcode {
|
||||
case dns.OpcodeQuery:
|
||||
r2.handleQuery(m)
|
||||
}
|
||||
if err := w.WriteMsg(m); err != nil {
|
||||
r2.logger.Error(
|
||||
"Failed to write DNS response message",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (r2 regexHandler) handleQuery(m *dns.Msg) {
|
||||
for _, q := range m.Question {
|
||||
r2.logger.Info(
|
||||
"handling question",
|
||||
zap.String("question", q.Name),
|
||||
)
|
||||
switch q.Qtype {
|
||||
case dns.TypeA:
|
||||
for _, rule := range r2.routes {
|
||||
if rule.pattern.MatchString(q.Name) {
|
||||
m.Authoritative = true
|
||||
answer := &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 60,
|
||||
},
|
||||
A: rule.response,
|
||||
}
|
||||
m.Answer = append(m.Answer, answer)
|
||||
r2.logger.Info(
|
||||
"matched DNS rule",
|
||||
zap.String("pattern", rule.pattern.String()),
|
||||
zap.String("response", rule.response.String()),
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
r2.handleFallbackForMessage(m, q)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r2 regexHandler) handleFallbackForMessage(m *dns.Msg, q dns.Question) {
|
||||
fallbackIP := r2.fallback.GetIP()
|
||||
answer := &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 60,
|
||||
},
|
||||
A: fallbackIP,
|
||||
}
|
||||
r2.logger.Info(
|
||||
"Falling back to generated IP",
|
||||
zap.String("response", fallbackIP.String()),
|
||||
)
|
||||
m.Authoritative = true
|
||||
m.Answer = append(m.Answer, answer)
|
||||
}
|
|
@ -6,7 +6,7 @@ GOARGS = GOOS=linux GOARCH=amd64
|
|||
GO_BUILD_ARGS = -buildmode=plugin -ldflags="-w -s"
|
||||
GO_CONTAINER_BUILD_ARGS = -buildmode=plugin -ldflags="-w -s" -a -installsuffix cgo
|
||||
GO_DEBUG_BUILD_ARGS = -buildmode=plugin -gcflags "all=-N -l"
|
||||
PLUGIN_NAME = http_mock.so
|
||||
PLUGIN_NAME = $(shell basename $(DIR)).so
|
||||
OUT_DIR = $(DIR)../../../plugins
|
||||
DEBUG_PORT = 2345
|
||||
|
||||
|
|
21
pkg/plugins/http_mock/init.go
Normal file
21
pkg/plugins/http_mock/init.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/baez90/inetmock/internal/plugins"
|
||||
"github.com/baez90/inetmock/pkg/api"
|
||||
"github.com/baez90/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger, _ := logging.CreateLogger()
|
||||
logger = logger.With(
|
||||
zap.String("ProtocolHandler", name),
|
||||
)
|
||||
plugins.Registry().RegisterHandler(name, func() api.ProtocolHandler {
|
||||
return &httpHandler{
|
||||
logger: logger,
|
||||
router: &RegexpHandler{},
|
||||
}
|
||||
})
|
||||
}
|
|
@ -4,9 +4,6 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"github.com/baez90/inetmock/internal/config"
|
||||
"github.com/baez90/inetmock/internal/plugins"
|
||||
"github.com/baez90/inetmock/pkg/api"
|
||||
"github.com/baez90/inetmock/pkg/logging"
|
||||
"github.com/baez90/inetmock/pkg/path"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
|
@ -19,13 +16,13 @@ const (
|
|||
name = "http_mock"
|
||||
)
|
||||
|
||||
type httpPlugin struct {
|
||||
type httpHandler struct {
|
||||
logger *zap.Logger
|
||||
router *RegexpHandler
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
func (p *httpPlugin) Run(config config.HandlerConfig) {
|
||||
func (p *httpHandler) Run(config config.HandlerConfig) {
|
||||
options := loadFromConfig(config.Options())
|
||||
addr := fmt.Sprintf("%s:%d", config.ListenAddress(), config.Port())
|
||||
p.server = &http.Server{Addr: addr, Handler: p.router}
|
||||
|
@ -40,7 +37,7 @@ func (p *httpPlugin) Run(config config.HandlerConfig) {
|
|||
go p.startServer()
|
||||
}
|
||||
|
||||
func (p *httpPlugin) Shutdown(wg *sync.WaitGroup) {
|
||||
func (p *httpHandler) Shutdown(wg *sync.WaitGroup) {
|
||||
if err := p.server.Close(); err != nil {
|
||||
p.logger.Error(
|
||||
"failed to shutdown HTTP server",
|
||||
|
@ -51,7 +48,7 @@ func (p *httpPlugin) Shutdown(wg *sync.WaitGroup) {
|
|||
wg.Done()
|
||||
}
|
||||
|
||||
func (p *httpPlugin) startServer() {
|
||||
func (p *httpHandler) startServer() {
|
||||
if err := p.server.ListenAndServe(); err != nil {
|
||||
p.logger.Error(
|
||||
"failed to start http listener",
|
||||
|
@ -60,7 +57,7 @@ func (p *httpPlugin) startServer() {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *httpPlugin) setupRoute(rule targetRule) {
|
||||
func (p *httpHandler) setupRoute(rule targetRule) {
|
||||
var compiled *regexp.Regexp
|
||||
var err error
|
||||
if compiled, err = regexp.Compile(rule.pattern); err != nil {
|
||||
|
@ -101,16 +98,3 @@ func createHandlerForTarget(logger *zap.Logger, targetPath string) http.Handler
|
|||
http.ServeFile(writer, request, targetFilePath)
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
logger, _ := logging.CreateLogger()
|
||||
logger = logger.With(
|
||||
zap.String("ProtocolHandler", name),
|
||||
)
|
||||
plugins.Registry().RegisterHandler(name, func() api.ProtocolHandler {
|
||||
return &httpPlugin{
|
||||
logger: logger,
|
||||
router: &RegexpHandler{},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ GOARGS = GOOS=linux GOARCH=amd64
|
|||
GO_BUILD_ARGS = -buildmode=plugin -ldflags="-w -s"
|
||||
GO_CONTAINER_BUILD_ARGS = -buildmode=plugin -ldflags="-w -s" -a -installsuffix cgo
|
||||
GO_DEBUG_BUILD_ARGS = -buildmode=plugin -gcflags "all=-N -l"
|
||||
PLUGIN_NAME = tls_interceptor.so
|
||||
PLUGIN_NAME = $(shell basename $(DIR)).so
|
||||
OUT_DIR = $(DIR)../../../plugins
|
||||
DEBUG_PORT = 2345
|
||||
|
||||
|
|
Loading…
Reference in a new issue