diff --git a/.gitignore b/.gitignore index 6a916d7..fb26201 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ coverage/ +reports/ .vscode/ diff --git a/Dockerfile b/Dockerfile index 4511fe2..db15686 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,8 @@ ################################################################################ FROM golang:1.21-alpine3.18 as builder +ARG BUILD_TAGS + RUN apk update && apk add \ make \ linux-headers \ @@ -16,13 +18,17 @@ COPY internal internal COPY pkg pkg COPY go.mod go.sum main.go Makefile ./ -RUN make static +RUN make static build_tags="${BUILD_TAGS}" ################################################################################ # Final ################################################################################ FROM alpine:3.18 +RUN apk update && apk add ca-certificates + COPY --from=builder /app/build/go-lanscan /scan -ENTRYPOINT [ "/scan" ] +RUN mkdir -p /reports + +ENTRYPOINT [ "/scan", "--out-file", "/reports/scan-report.json" ] diff --git a/Makefile b/Makefile index f4b60cd..20f3191 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,8 @@ tag = $(shell git describe --tags $(shell git rev-list --tags --max-count=1)) flags ?= -ldflags '-s -w' +build_tags ?= + #### Build Objects #### component = $(app_name)_$(tag) component_path = $(prefix)/$(component) @@ -46,7 +48,7 @@ all: $(app_name) # builds main executable $(prefix)/$(app_name): $(go_deps) - go build $(flags) -o $(@) + go build $(flags) -o $(@) -tags $(build_tags) # build main executable .PHONY: $(app_name) diff --git a/README.md b/README.md index 6b48851..fbbc18f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # go-lanscan -![Coverage](https://img.shields.io/badge/Coverage-90.6%25-brightgreen) +![Coverage](https://img.shields.io/badge/Coverage-91.2%25-brightgreen) A network cli and golang package that allows you to perform arp and syn scanning on a local area network. @@ -62,10 +62,6 @@ sudo go-lanscan --no-progress --json # run only arp scanning (skip syn scanning) sudo go-lanscan --arp-only - -# set accuracy to low, which results in a faster scan but may -# miss some open ports -sudo go-lanscan --accuracy low ``` ## Package Usage @@ -166,29 +162,5 @@ queries against this file. The file is stored at `~/.config/go-lanscan/oui.txt` option(arpScanner) ``` -- Set accuracy of scanning (LOW, MEDIUM, HIGH). Low results in a faster scan - but may miss some open ports. The default is HIGH. This option can be set - on any scanner - -```go - synScanner := scanner.NewSynScanner( - targets, - netInfo, - ports, - listenPort, - synResults, - synDone, - scanner.WithAccuracy(scanner.LOW_ACCURACY), - ) - - // or - synScanner.SetAccuracy(scanner.LOW_ACCURACY) - - // or - option := scanner.WithAccuracy(scanner.LOW_ACCURACY) - option(synScanner) -``` - - [golang]: https://go.dev/doc/install [libpcap]: https://github.com/the-tcpdump-group/libpcap diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..785fafc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +version: "3" + +services: + test-server-1: + build: + context: test-server + dockerfile: Dockerfile + environment: + PORT: '3232' + networks: + go-lanscan_default: + + test-server-2: + build: + context: test-server + dockerfile: Dockerfile + environment: + PORT: '2323' + networks: + go-lanscan_default: + + go-lanscan: + build: + args: + - BUILD_TAGS=debug + context: . + dockerfile: Dockerfile + command: --ports 2323,3232 --json + depends_on: + - test-server-1 + - test-server-2 + volumes: + - ./reports:/reports + networks: + go-lanscan_default: + +networks: + go-lanscan_default: + driver: bridge + ipam: + config: + - subnet: 172.18.0.1/28 + gateway: 172.18.0.1 diff --git a/go.mod b/go.mod index 7b4459a..537dcda 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,13 @@ go 1.21 require ( github.com/google/gopacket v1.1.19 - github.com/jackpal/gateway v1.0.10 + github.com/jackpal/gateway v1.0.13 github.com/jedib0t/go-pretty v4.3.0+incompatible github.com/klauspost/oui v0.0.0-20150225163751-35b4deb627f8 github.com/rs/zerolog v1.31.0 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 - github.com/thediveo/netdb v1.0.2 + github.com/thediveo/netdb v1.0.3 go.uber.org/mock v0.3.0 ) @@ -18,10 +18,10 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-openapi/errors v0.20.4 // indirect - github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/strfmt v0.21.9 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oklog/ulid v1.3.1 // indirect @@ -29,9 +29,10 @@ require ( github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/spf13/pflag v1.0.5 // indirect - go.mongodb.org/mongo-driver v1.12.1 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sys v0.13.0 // indirect + github.com/stretchr/objx v0.5.1 // indirect + go.mongodb.org/mongo-driver v1.13.1 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect golang.org/x/tools v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 87fc35c..e676d5a 100644 --- a/go.sum +++ b/go.sum @@ -1,62 +1,34 @@ -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M= github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= -github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-openapi/strfmt v0.21.9 h1:LnEGOO9qyEC1v22Bzr323M98G13paIUGPU7yeJtG9Xs= +github.com/go-openapi/strfmt v0.21.9/go.mod h1:0k3v301mglEaZRJdDDGSlN6Npq4VMVU69DE0LUyf7uA= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackpal/gateway v1.0.10 h1:7g3fDo4Cd3RnTu6PzAfw6poO4Y81uNxrxFQFsBFSzJM= -github.com/jackpal/gateway v1.0.10/go.mod h1:+uPBgIllrbkwYCAoDkGSZbjvpre/bGYAFCYIcrH+LHs= +github.com/jackpal/gateway v1.0.13 h1:fJccMvawxx0k7S1q7Fy/SXFE0R3hMXkMuw8y9SofWAk= +github.com/jackpal/gateway v1.0.13/go.mod h1:6c8LjW+FVESFmwxaXySkt7fU98Yv806ADS3OY6Cvh2U= github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -69,8 +41,9 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -78,38 +51,12 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= -github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= -github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= -github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= -github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= -github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= -github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -122,164 +69,82 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/thediveo/netdb v1.0.2 h1:icuZWO8btuubgjFFFhxWmXALATlQO6bqEer7DPxRPco= -github.com/thediveo/netdb v1.0.2/go.mod h1:Mz/McdR84D8xUX7rWk0cRgNLrLvqfDPzTAQKUeCR0OY= -github.com/xanzy/go-gitlab v0.81.0/go.mod h1:VMbY3JIWdZ/ckvHbQqkyd3iYk2aViKrNIQ23IbFMQDo= +github.com/thediveo/netdb v1.0.3 h1:Kk5HkhO0MFhoNGMZSL3Z2pz7FPgVUlClzZOi7URvf38= +github.com/thediveo/netdb v1.0.3/go.mod h1:yr3xaPR82VKhHg30BtJuYktFzZBjsWkEhPadHVYCOmo= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= -go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= +go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= +go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= 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= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cli/root.go b/internal/cli/root.go index f44fef6..273fe50 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -28,8 +28,8 @@ func Root( var ifaceName string var targets []string var vendorInfo bool - var accuracy string var arpOnly bool + var outFile string cmd := &cobra.Command{ Use: "go-lanscan", @@ -52,19 +52,6 @@ func Root( targets = []string{} } - var scannerAccuracy scanner.Accuracy - - switch strings.ToLower(accuracy) { - case "low": - scannerAccuracy = scanner.LOW_ACCURACY - case "medium": - scannerAccuracy = scanner.MEDIUM_ACCURACY - case "high": - scannerAccuracy = scanner.HIGH_ACCURACY - default: - scannerAccuracy = scanner.HIGH_ACCURACY - } - var coreScanner scanner.Scanner if arpOnly { @@ -72,7 +59,6 @@ func Root( targets, userNet, scanner.WithIdleTimeout(time.Second*time.Duration(idleTimeoutSeconds)), - scanner.WithAccuracy(scannerAccuracy), ) } else { coreScanner = scanner.NewFullScanner( @@ -81,7 +67,6 @@ func Root( portList, listenPort, scanner.WithIdleTimeout(time.Second*time.Duration(idleTimeoutSeconds)), - scanner.WithAccuracy(scannerAccuracy), ) } @@ -104,6 +89,7 @@ func Root( noProgress, arpOnly, printJson, + outFile, ) return runner.Run() @@ -118,7 +104,7 @@ func Root( cmd.Flags().Uint16Var(&listenPort, "listen-port", 54321, "set the port on which the scanner will listen for packets") cmd.Flags().StringVarP(&ifaceName, "interface", "i", userNet.Interface().Name, "set the interface for scanning") cmd.Flags().StringSliceVarP(&targets, "targets", "t", []string{userNet.Cidr()}, "set targets for scanning") - cmd.Flags().StringVar(&accuracy, "accuracy", "high", "sets throttle to ensure fewer packets are dropped. Valid values are high (slower more accurate), medium, low (faster less accurate)") + cmd.Flags().StringVar(&outFile, "out-file", "", "outputs final report to file") cmd.Flags().BoolVar(&vendorInfo, "vendor", false, "include vendor info (takes a little longer)") cmd.AddCommand(newVersion()) diff --git a/internal/cli/root_test.go b/internal/cli/root_test.go index 19fb90e..e5a047c 100644 --- a/internal/cli/root_test.go +++ b/internal/cli/root_test.go @@ -18,7 +18,7 @@ func TestRootCommand(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - t.Run("initializes high accuracy and runs", func(st *testing.T) { + t.Run("initializes and runs full scanner", func(st *testing.T) { mockNetwork := mock_network.NewMockNetwork(ctrl) mockRunner := mock_core.NewMockRunner(ctrl) @@ -41,12 +41,13 @@ func TestRootCommand(t *testing.T) { mockNetwork.EXPECT().IPNet().AnyTimes().Return(mockIPNet) mockRunner.EXPECT().Initialize( - gomock.Any(), gomock.Any(), 1, + 65535, false, false, false, + "", ) mockRunner.EXPECT().Run().Return(nil) @@ -55,14 +56,14 @@ func TestRootCommand(t *testing.T) { assert.NoError(st, err) - cmd.SetArgs([]string{"--ports", "22", "--accuracy", "high"}) + cmd.SetArgs([]string{}) err = cmd.Execute() assert.NoError(st, err) }) - t.Run("initializes medium accuracy and runs", func(st *testing.T) { + t.Run("initializes and runs arp-only scanner", func(st *testing.T) { mockNetwork := mock_network.NewMockNetwork(ctrl) mockRunner := mock_core.NewMockRunner(ctrl) @@ -91,6 +92,7 @@ func TestRootCommand(t *testing.T) { true, true, true, + "", ) mockRunner.EXPECT().Run().Return(nil) @@ -103,105 +105,6 @@ func TestRootCommand(t *testing.T) { "--targets", "172.17.1.1", "--ports", "22", - "--accuracy", "medium", - "--arp-only", - "--json", - "--no-progress", - }) - - err = cmd.Execute() - - assert.NoError(st, err) - }) - - t.Run("initializes low accuracy and runs", func(st *testing.T) { - mockNetwork := mock_network.NewMockNetwork(ctrl) - - mockRunner := mock_core.NewMockRunner(ctrl) - - mockVendor := mock_oui.NewMockVendorRepo(ctrl) - - mockMAC, _ := net.ParseMAC("00:00:00:00:00:00") - - mockCidr := "172.17.1.1/32" - - _, mockIPNet, _ := net.ParseCIDR(mockCidr) - - mockNetwork.EXPECT().Interface().AnyTimes().Return(&net.Interface{ - Name: "test-interface", - HardwareAddr: mockMAC, - }) - - mockNetwork.EXPECT().Cidr().AnyTimes().Return(mockCidr) - - mockNetwork.EXPECT().IPNet().AnyTimes().Return(mockIPNet) - - mockRunner.EXPECT().Initialize( - gomock.Any(), - gomock.Any(), - 1, - false, - false, - false, - ) - - mockRunner.EXPECT().Run().Return(nil) - - cmd, err := cli.Root(mockRunner, mockNetwork, mockVendor) - - assert.NoError(st, err) - - cmd.SetArgs([]string{ - "--ports", "22", - "--accuracy", "low", - }) - - err = cmd.Execute() - - assert.NoError(st, err) - }) - - t.Run("initializes unknown accuracy and runs", func(st *testing.T) { - mockNetwork := mock_network.NewMockNetwork(ctrl) - - mockRunner := mock_core.NewMockRunner(ctrl) - - mockVendor := mock_oui.NewMockVendorRepo(ctrl) - - mockMAC, _ := net.ParseMAC("00:00:00:00:00:00") - - mockCidr := "172.17.1.1/32" - - _, mockIPNet, _ := net.ParseCIDR(mockCidr) - - mockNetwork.EXPECT().Interface().AnyTimes().Return(&net.Interface{ - Name: "test-interface", - HardwareAddr: mockMAC, - }) - - mockNetwork.EXPECT().Cidr().AnyTimes().Return(mockCidr) - - mockNetwork.EXPECT().IPNet().AnyTimes().Return(mockIPNet) - - mockRunner.EXPECT().Initialize( - gomock.Any(), - 1, - 1, - true, - true, - true, - ) - - mockRunner.EXPECT().Run().Return(nil) - - cmd, err := cli.Root(mockRunner, mockNetwork, mockVendor) - - assert.NoError(st, err) - - cmd.SetArgs([]string{ - "--targets", "172.17.1.1", - "--ports", "22", - "--accuracy", "unknown", "--arp-only", "--json", "--no-progress", diff --git a/internal/core/core.go b/internal/core/core.go index 967d694..d52adbe 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -62,6 +62,7 @@ type Core struct { arpOnly bool printJson bool noProgress bool + outFile string portLen int results *Results pw progress.Writer @@ -87,6 +88,7 @@ func (c *Core) Initialize( noProgress bool, arpOnly bool, printJson bool, + outFile string, ) { pw := progressWriter() @@ -113,6 +115,7 @@ func (c *Core) Initialize( c.noProgress = noProgress c.arpOnly = arpOnly c.printJson = printJson + c.outFile = outFile } func (c *Core) Run() error { @@ -229,15 +232,13 @@ func (c *Core) processArpDone() { c.mux.RLock() defer c.mux.RUnlock() - if !c.noProgress { - c.printArpResults() + c.printArpResults() - if !c.arpOnly && len(c.results.Devices) > 0 { - c.synTracker.Total = int64( - len(c.results.Devices) * c.portLen, - ) - c.pw.AppendTracker(c.synTracker) - } + if !c.noProgress && !c.arpOnly && len(c.results.Devices) > 0 { + c.synTracker.Total = int64( + len(c.results.Devices) * c.portLen, + ) + c.pw.AppendTracker(c.synTracker) } if !c.arpOnly && len(c.results.Devices) == 0 { @@ -260,12 +261,10 @@ func (c *Core) requestCallback(r *scanner.Request) { if c.arpTracker.IsDone() { message = "arp - scan complete" - go func() { - // delay to print line after message is updated - time.AfterFunc(time.Millisecond*100, func() { - c.log.Info().Msg("compiling arp results...") - }) - }() + // delay to print line after message is updated + time.AfterFunc(time.Millisecond*100, func() { + c.log.Info().Msg("compiling arp results...") + }) } c.arpTracker.Message = message @@ -280,12 +279,10 @@ func (c *Core) requestCallback(r *scanner.Request) { if c.synTracker.IsDone() { message = "syn - scan complete" - go func() { - // delay to print line after message is updated - time.AfterFunc(time.Millisecond*100, func() { - c.log.Info().Msg("compiling syn results...") - }) - }() + // delay to print line after message is updated + time.AfterFunc(time.Millisecond*100, func() { + c.log.Info().Msg("compiling syn results...") + }) } c.synTracker.Message = message @@ -306,7 +303,16 @@ func (c *Core) printArpResults() { }() } - fmt.Println(string(data)) + if !c.noProgress { + fmt.Println(string(data)) + } + + if c.arpOnly && c.outFile != "" { + if err := os.WriteFile(c.outFile, data, 0644); err != nil { + c.log.Error().Err(err).Msg("failed to write output report") + } + } + return } @@ -318,7 +324,14 @@ func (c *Core) printArpResults() { arpTable.AppendRow(table.Row{t.IP.String(), t.MAC.String(), t.Vendor}) } - arpTable.Render() + output := arpTable.Render() + + if c.arpOnly && c.outFile != "" { + if err := os.WriteFile(c.outFile, []byte(output), 0644); err != nil { + c.log.Error().Err(err).Msg("failed to write output report") + } + + } } func (c *Core) printSynResults() { @@ -335,6 +348,13 @@ func (c *Core) printSynResults() { } fmt.Println(string(data)) + + if c.outFile != "" { + if err := os.WriteFile(c.outFile, data, 0644); err != nil { + c.log.Error().Err(err).Msg("failed to write output report") + } + } + return } @@ -358,7 +378,13 @@ func (c *Core) printSynResults() { }) } - synTable.Render() + output := synTable.Render() + + if c.outFile != "" { + if err := os.WriteFile(c.outFile, []byte(output), 0644); err != nil { + c.log.Error().Err(err).Msg("failed to write output report") + } + } } // helpers diff --git a/internal/core/core_test.go b/internal/core/core_test.go index c95bd0b..4334cd5 100644 --- a/internal/core/core_test.go +++ b/internal/core/core_test.go @@ -6,7 +6,7 @@ import ( "encoding/json" "errors" "net" - "sync" + "os" "testing" "time" @@ -127,6 +127,7 @@ func TestCore(t *testing.T) { false, false, false, + "", ) }) @@ -142,6 +143,7 @@ func TestCore(t *testing.T) { true, true, true, + "", ) }) @@ -165,6 +167,7 @@ func TestCore(t *testing.T) { false, true, false, + "", ) err := runner.Run() @@ -179,11 +182,13 @@ func TestCore(t *testing.T) { scanResults := make(chan *scanner.ScanResult) - mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() + outFile := "arp-report.txt" - wg := sync.WaitGroup{} + defer func() { + os.RemoveAll(outFile) + }() - wg.Add(1) + mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() var callback func(r *scanner.Request) @@ -201,21 +206,22 @@ func TestCore(t *testing.T) { IP: "172.17.0.1", }) - scanResults <- &scanner.ScanResult{ - Type: scanner.ARPResult, - Payload: &scanner.ArpScanResult{ - IP: net.ParseIP("172.17.0.1"), - MAC: mac, - Vendor: "Apple", - }, - } - time.AfterFunc(time.Millisecond*100, func() { + scanResults <- &scanner.ScanResult{ + Type: scanner.ARPResult, + Payload: &scanner.ArpScanResult{ + IP: net.ParseIP("172.17.0.1"), + MAC: mac, + Vendor: "Apple", + }, + } + }) + time.AfterFunc(time.Millisecond*200, func() { scanResults <- &scanner.ScanResult{ Type: scanner.ARPDone, } - wg.Done() }) + return nil }) @@ -228,13 +234,14 @@ func TestCore(t *testing.T) { false, true, false, + outFile, ) err := runner.Run() assert.NoError(st, err) - wg.Wait() + assert.FileExists(st, outFile) }) t.Run("performs arp only scan and prints json", func(st *testing.T) { @@ -244,11 +251,13 @@ func TestCore(t *testing.T) { scanResults := make(chan *scanner.ScanResult) - mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() + outFile := "arp-report.json" - wg := sync.WaitGroup{} + defer func() { + os.RemoveAll(outFile) + }() - wg.Add(1) + mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() var callback func(r *scanner.Request) @@ -266,21 +275,22 @@ func TestCore(t *testing.T) { IP: "172.17.0.1", }) - scanResults <- &scanner.ScanResult{ - Type: scanner.ARPResult, - Payload: &scanner.ArpScanResult{ - IP: net.ParseIP("172.17.0.1"), - MAC: mac, - Vendor: "Apple", - }, - } - time.AfterFunc(time.Millisecond*100, func() { + scanResults <- &scanner.ScanResult{ + Type: scanner.ARPResult, + Payload: &scanner.ArpScanResult{ + IP: net.ParseIP("172.17.0.1"), + MAC: mac, + Vendor: "Apple", + }, + } + }) + time.AfterFunc(time.Millisecond*200, func() { scanResults <- &scanner.ScanResult{ Type: scanner.ARPDone, } - wg.Done() }) + return nil }) @@ -293,13 +303,14 @@ func TestCore(t *testing.T) { false, true, true, + outFile, ) err := runner.Run() assert.NoError(st, err) - wg.Wait() + assert.FileExists(st, outFile) }) t.Run("performs arp only scan and silences output", func(st *testing.T) { @@ -309,11 +320,13 @@ func TestCore(t *testing.T) { scanResults := make(chan *scanner.ScanResult) - mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() + outFile := "arp-silent-report.txt" - wg := sync.WaitGroup{} + defer func() { + os.RemoveAll(outFile) + }() - wg.Add(1) + mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() mockScanner.EXPECT().Scan().DoAndReturn(func() error { mac, _ := net.ParseMAC("00:00:00:00:00:00") @@ -329,7 +342,6 @@ func TestCore(t *testing.T) { scanResults <- &scanner.ScanResult{ Type: scanner.ARPDone, } - wg.Done() }) return nil }) @@ -343,13 +355,13 @@ func TestCore(t *testing.T) { true, true, true, + outFile, ) err := runner.Run() assert.NoError(st, err) - - wg.Wait() + assert.FileExists(st, outFile) }) t.Run("performs syn scan and prints text table", func(st *testing.T) { @@ -359,11 +371,13 @@ func TestCore(t *testing.T) { scanResults := make(chan *scanner.ScanResult) - mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() + outFile := "syn-report.txt" - wg := sync.WaitGroup{} + defer func() { + os.RemoveAll(outFile) + }() - wg.Add(3) + mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() var callback func(r *scanner.Request) @@ -388,22 +402,22 @@ func TestCore(t *testing.T) { Port: 22, }) - scanResults <- &scanner.ScanResult{ - Type: scanner.ARPResult, - Payload: &scanner.ArpScanResult{ - IP: ip, - MAC: mac, - Vendor: "Apple", - }, - } - time.AfterFunc(time.Millisecond*100, func() { scanResults <- &scanner.ScanResult{ - Type: scanner.ARPDone, + Type: scanner.ARPResult, + Payload: &scanner.ArpScanResult{ + IP: ip, + MAC: mac, + Vendor: "Apple", + }, } - wg.Done() }) time.AfterFunc(time.Millisecond*200, func() { + scanResults <- &scanner.ScanResult{ + Type: scanner.ARPDone, + } + }) + time.AfterFunc(time.Millisecond*300, func() { scanResults <- &scanner.ScanResult{ Type: scanner.SYNResult, Payload: &scanner.SynScanResult{ @@ -417,13 +431,11 @@ func TestCore(t *testing.T) { }, }, } - wg.Done() }) - time.AfterFunc(time.Millisecond*300, func() { + time.AfterFunc(time.Millisecond*400, func() { scanResults <- &scanner.ScanResult{ Type: scanner.SYNDone, } - wg.Done() }) return nil @@ -438,13 +450,14 @@ func TestCore(t *testing.T) { false, false, false, + outFile, ) err := runner.Run() assert.NoError(st, err) - wg.Wait() + assert.FileExists(st, outFile) }) t.Run("performs syn scan and prints json", func(st *testing.T) { @@ -454,11 +467,13 @@ func TestCore(t *testing.T) { scanResults := make(chan *scanner.ScanResult) - mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() + outFile := "syn-report.json" - wg := sync.WaitGroup{} + defer func() { + os.RemoveAll(outFile) + }() - wg.Add(3) + mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() var callback func(r *scanner.Request) @@ -483,21 +498,22 @@ func TestCore(t *testing.T) { Port: 22, }) - scanResults <- &scanner.ScanResult{ - Type: scanner.ARPResult, - Payload: &scanner.ArpScanResult{ - IP: ip, - MAC: mac, - Vendor: "Apple", - }, - } time.AfterFunc(time.Millisecond*100, func() { scanResults <- &scanner.ScanResult{ - Type: scanner.ARPDone, + Type: scanner.ARPResult, + Payload: &scanner.ArpScanResult{ + IP: ip, + MAC: mac, + Vendor: "Apple", + }, } - wg.Done() }) time.AfterFunc(time.Millisecond*200, func() { + scanResults <- &scanner.ScanResult{ + Type: scanner.ARPDone, + } + }) + time.AfterFunc(time.Millisecond*300, func() { scanResults <- &scanner.ScanResult{ Type: scanner.SYNResult, Payload: &scanner.SynScanResult{ @@ -511,13 +527,11 @@ func TestCore(t *testing.T) { }, }, } - wg.Done() }) - time.AfterFunc(time.Millisecond*300, func() { + time.AfterFunc(time.Millisecond*400, func() { scanResults <- &scanner.ScanResult{ Type: scanner.SYNDone, } - wg.Done() }) return nil @@ -532,13 +546,14 @@ func TestCore(t *testing.T) { false, false, true, + outFile, ) err := runner.Run() assert.NoError(st, err) - wg.Wait() + assert.FileExists(st, outFile) }) t.Run("performs syn scan silences output", func(st *testing.T) { @@ -548,11 +563,13 @@ func TestCore(t *testing.T) { scanResults := make(chan *scanner.ScanResult) - mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() + outFile := "syn-silent-report.txt" - wg := sync.WaitGroup{} + defer func() { + os.RemoveAll(outFile) + }() - wg.Add(3) + mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() mockScanner.EXPECT().Scan().DoAndReturn(func() error { ip := net.ParseIP("172.17.0.1") @@ -569,7 +586,6 @@ func TestCore(t *testing.T) { scanResults <- &scanner.ScanResult{ Type: scanner.ARPDone, } - wg.Done() }) time.AfterFunc(time.Millisecond*200, func() { scanResults <- &scanner.ScanResult{ @@ -585,13 +601,11 @@ func TestCore(t *testing.T) { }, }, } - wg.Done() }) time.AfterFunc(time.Millisecond*300, func() { scanResults <- &scanner.ScanResult{ Type: scanner.SYNDone, } - wg.Done() }) return nil @@ -606,12 +620,46 @@ func TestCore(t *testing.T) { true, false, true, + outFile, ) err := runner.Run() assert.NoError(st, err) + assert.FileExists(st, outFile) + }) - wg.Wait() + t.Run("exits syn scan quickly if no devices found", func(st *testing.T) { + mockScanner := mock_scanner.NewMockScanner(ctrl) + + runner := core.New() + + scanResults := make(chan *scanner.ScanResult) + + mockScanner.EXPECT().Results().Return(scanResults).AnyTimes() + + mockScanner.EXPECT().Scan().DoAndReturn(func() error { + scanResults <- &scanner.ScanResult{ + Type: scanner.ARPDone, + } + + return nil + }) + + mockScanner.EXPECT().Stop() + + runner.Initialize( + mockScanner, + 10, + 10, + true, + false, + true, + "", + ) + + err := runner.Run() + + assert.NoError(st, err) }) } diff --git a/internal/core/interface.go b/internal/core/interface.go index 5fac596..da2035f 100644 --- a/internal/core/interface.go +++ b/internal/core/interface.go @@ -14,6 +14,7 @@ type Runner interface { noProgress bool, arpOnly bool, printJson bool, + outFile string, ) Run() error } diff --git a/internal/logger/debug.go b/internal/logger/debug.go new file mode 100644 index 0000000..b02ad51 --- /dev/null +++ b/internal/logger/debug.go @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package logger + +import ( + "io" + + "github.com/rs/zerolog" +) + +var debugLogger DebugLogger + +func init() { + // initialize a disabled logger + // builds with the "debug" tag will enable logging + // see enable-debug.go + + consoleWriter := zerolog.ConsoleWriter{Out: io.Discard} + + zl := zerolog.New(consoleWriter). + Level(zerolog.Disabled). + With(). + Timestamp(). + Caller(). + Logger() + + debugLogger = DebugLogger{ + zl: &zl, + } +} + +func NewDebugLogger() DebugLogger { + return debugLogger +} + +type DebugLogger struct { + zl *zerolog.Logger +} + +// Info wrapper around zerolog Info +func (l DebugLogger) Info() *zerolog.Event { + return l.zl.Info() +} + +// Debug wrapper around zerolog Debug +func (l DebugLogger) Debug() *zerolog.Event { + return l.zl.Debug() +} + +// Warn wrapper around zerolog Warn +func (l DebugLogger) Warn() *zerolog.Event { + return l.zl.Warn() +} + +// Error wrapper around zerolog Error +func (l DebugLogger) Error() *zerolog.Event { + return l.zl.Error() +} + +// Fatal wrapper around zerolog Fatal +func (l DebugLogger) Fatal() *zerolog.Event { + return l.zl.Fatal() +} diff --git a/internal/logger/debug_test.go b/internal/logger/debug_test.go new file mode 100644 index 0000000..107d26b --- /dev/null +++ b/internal/logger/debug_test.go @@ -0,0 +1,18 @@ +package logger_test + +import ( + "testing" + + "github.com/robgonnella/go-lanscan/internal/logger" +) + +func TestDebugLogging(t *testing.T) { + debug := logger.NewDebugLogger() + + t.Run("prints nothing since not built with debug flag", func(st *testing.T) { + debug.Debug().Msg("debug message") + debug.Info().Msg("info message") + debug.Error().Msg("error message") + debug.Warn().Msg("warning message") + }) +} diff --git a/internal/logger/enable-debug.go b/internal/logger/enable-debug.go new file mode 100644 index 0000000..eaaaf1d --- /dev/null +++ b/internal/logger/enable-debug.go @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build debug + +package logger + +import ( + "os" + + "github.com/rs/zerolog" +) + +func init() { + consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr} + + zl := zerolog.New(consoleWriter). + Level(zerolog.DebugLevel). + With(). + Timestamp(). + Caller(). + Logger() + + *debugLogger.zl = zl +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 77cfee8..2e04aca 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -66,7 +66,9 @@ func SetBufferOutput(buf *bytes.Buffer) { // Reset resets logger to default values func Reset() { - zl := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}). + consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr} + + zl := zerolog.New(consoleWriter). With(). Timestamp(). Logger() diff --git a/internal/mock/core/core.go b/internal/mock/core/core.go index 4c86612..bf9c36d 100644 --- a/internal/mock/core/core.go +++ b/internal/mock/core/core.go @@ -39,15 +39,15 @@ func (m *MockRunner) EXPECT() *MockRunnerMockRecorder { } // Initialize mocks base method. -func (m *MockRunner) Initialize(arg0 scanner.Scanner, arg1, arg2 int, arg3, arg4, arg5 bool) { +func (m *MockRunner) Initialize(arg0 scanner.Scanner, arg1, arg2 int, arg3, arg4, arg5 bool, arg6 string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "Initialize", arg0, arg1, arg2, arg3, arg4, arg5) + m.ctrl.Call(m, "Initialize", arg0, arg1, arg2, arg3, arg4, arg5, arg6) } // Initialize indicates an expected call of Initialize. -func (mr *MockRunnerMockRecorder) Initialize(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { +func (mr *MockRunnerMockRecorder) Initialize(arg0, arg1, arg2, arg3, arg4, arg5, arg6 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Initialize", reflect.TypeOf((*MockRunner)(nil).Initialize), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Initialize", reflect.TypeOf((*MockRunner)(nil).Initialize), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } // Run mocks base method. diff --git a/mock/scanner/scanner.go b/mock/scanner/scanner.go index 2e8262a..8f48373 100644 --- a/mock/scanner/scanner.go +++ b/mock/scanner/scanner.go @@ -81,18 +81,6 @@ func (mr *MockScannerMockRecorder) Scan() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scan", reflect.TypeOf((*MockScanner)(nil).Scan)) } -// SetAccuracy mocks base method. -func (m *MockScanner) SetAccuracy(arg0 scanner.Accuracy) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetAccuracy", arg0) -} - -// SetAccuracy indicates an expected call of SetAccuracy. -func (mr *MockScannerMockRecorder) SetAccuracy(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccuracy", reflect.TypeOf((*MockScanner)(nil).SetAccuracy), arg0) -} - // SetIdleTimeout mocks base method. func (m *MockScanner) SetIdleTimeout(arg0 time.Duration) { m.ctrl.T.Helper() diff --git a/pkg/scanner/arpscan.go b/pkg/scanner/arpscan.go index ffb865f..4681dae 100644 --- a/pkg/scanner/arpscan.go +++ b/pkg/scanner/arpscan.go @@ -12,6 +12,7 @@ import ( "github.com/google/gopacket" "github.com/google/gopacket/layers" + "github.com/robgonnella/go-lanscan/internal/logger" "github.com/robgonnella/go-lanscan/internal/util" "github.com/robgonnella/go-lanscan/pkg/network" "github.com/robgonnella/go-lanscan/pkg/oui" @@ -29,10 +30,10 @@ type ArpScanner struct { scanning bool lastPacketSentAt time.Time idleTimeout time.Duration - accuracy time.Duration vendorRepo oui.VendorRepo scanningMux *sync.RWMutex packetSentAtMux *sync.RWMutex + debug logger.DebugLogger } func NewArpScanner( @@ -52,9 +53,9 @@ func NewArpScanner( idleTimeout: time.Second * 5, scanning: false, lastPacketSentAt: time.Time{}, - accuracy: time.Millisecond, scanningMux: &sync.RWMutex{}, packetSentAtMux: &sync.RWMutex{}, + debug: logger.NewDebugLogger(), } for _, o := range options { @@ -69,6 +70,13 @@ func (s *ArpScanner) Results() chan *ScanResult { } func (s *ArpScanner) Scan() error { + fields := map[string]interface{}{ + "interface": s.networkInfo.Interface().Name, + "cidr": s.networkInfo.Cidr(), + "targets": s.targets, + } + s.debug.Info().Fields(fields).Msg("starting arp scan") + s.scanningMux.RLock() scanning := s.scanning s.scanningMux.RUnlock() @@ -99,7 +107,7 @@ func (s *ArpScanner) Scan() error { go s.readPackets() - limiter := time.NewTicker(s.accuracy) + limiter := time.NewTicker(defaultAccuracy) defer limiter.Stop() if len(s.targets) == 0 { @@ -148,10 +156,6 @@ func (s *ArpScanner) IncludeVendorInfo(repo oui.VendorRepo) { } } -func (s *ArpScanner) SetAccuracy(accuracy Accuracy) { - s.accuracy = accuracy.Duration() -} - func (s *ArpScanner) SetPacketCapture(cap PacketCapture) { s.cap = cap } diff --git a/pkg/scanner/arpscan_test.go b/pkg/scanner/arpscan_test.go index b80d865..1237a05 100644 --- a/pkg/scanner/arpscan_test.go +++ b/pkg/scanner/arpscan_test.go @@ -30,7 +30,8 @@ func TestArpScanner(t *testing.T) { HardwareAddr: net.HardwareAddr([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), } - _, mockIPNet, _ := net.ParseCIDR("172.17.1.1/32") + cidr := "172.17.1.1/32" + _, mockIPNet, _ := net.ParseCIDR(cidr) mockUserIP := net.ParseIP("172.17.1.1") mockNonIncludedArpSrcIP := net.ParseIP("172.17.1.2") @@ -49,6 +50,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().IPNet().Return(mockIPNet).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) mockNetInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( @@ -95,6 +97,7 @@ func TestArpScanner(t *testing.T) { mockErr := errors.New("mock open-live error") mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -125,6 +128,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().IPNet().Return(mockIPNet).AnyTimes() mockNetInfo.EXPECT().UserIP().Return(mockUserIP) + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -174,6 +178,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -223,6 +228,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -269,6 +275,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -318,6 +325,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -371,6 +379,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -431,6 +440,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -509,6 +519,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -560,6 +571,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -606,6 +618,7 @@ func TestArpScanner(t *testing.T) { mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) mockNetInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), diff --git a/pkg/scanner/fullscan.go b/pkg/scanner/fullscan.go index b40e9d1..1e50aca 100644 --- a/pkg/scanner/fullscan.go +++ b/pkg/scanner/fullscan.go @@ -159,11 +159,6 @@ func (s *FullScanner) IncludeVendorInfo(repo oui.VendorRepo) { s.synScanner.IncludeVendorInfo(repo) } -func (s *FullScanner) SetAccuracy(accuracy Accuracy) { - s.arpScanner.SetAccuracy(accuracy) - s.synScanner.SetAccuracy(accuracy) -} - func (s *FullScanner) SetPacketCapture(cap PacketCapture) { s.arpScanner.SetPacketCapture(cap) s.synScanner.SetPacketCapture(cap) diff --git a/pkg/scanner/fullscan_test.go b/pkg/scanner/fullscan_test.go index 6dceb57..6cfd357 100644 --- a/pkg/scanner/fullscan_test.go +++ b/pkg/scanner/fullscan_test.go @@ -27,7 +27,8 @@ func TestFullScanner(t *testing.T) { HardwareAddr: net.HardwareAddr([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), } - _, mockIPNet, _ := net.ParseCIDR("172.17.1.1/32") + cidr := "172.17.1.1/32" + _, mockIPNet, _ := net.ParseCIDR(cidr) mockUserIP := net.ParseIP("172.17.1.1") @@ -47,6 +48,7 @@ func TestFullScanner(t *testing.T) { netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) netInfo.EXPECT().IPNet().Return(mockIPNet) netInfo.EXPECT().UserIP().Return(mockUserIP) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -101,6 +103,7 @@ func TestFullScanner(t *testing.T) { netInfo.EXPECT().Interface().Return(mockInterface).AnyTimes() netInfo.EXPECT().IPNet().Return(mockIPNet).AnyTimes() netInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -172,6 +175,7 @@ func TestFullScanner(t *testing.T) { netInfo.EXPECT().Interface().Return(mockInterface).AnyTimes() netInfo.EXPECT().UserIP().Return(mockUserIP).AnyTimes() netInfo.EXPECT().IPNet().Return(mockIPNet).AnyTimes() + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), diff --git a/pkg/scanner/options.go b/pkg/scanner/options.go index 0cfd7be..4994157 100644 --- a/pkg/scanner/options.go +++ b/pkg/scanner/options.go @@ -8,34 +8,12 @@ import ( "github.com/robgonnella/go-lanscan/pkg/oui" ) -type ScannerOption = func(s Scanner) - -type Accuracy int - -const ( - LOW_ACCURACY Accuracy = 0 - MEDIUM_ACCURACY Accuracy = 1 - HIGH_ACCURACY Accuracy = 2 -) +// How long to wait before sending next packet +// the faster you send packets the more packets +// will be missed when reading +const defaultAccuracy = time.Millisecond * 100 -func (a Accuracy) Duration() time.Duration { - switch a { - case LOW_ACCURACY: - return time.Microsecond * 100 - case MEDIUM_ACCURACY: - return time.Microsecond * 500 - case HIGH_ACCURACY: - return time.Millisecond - default: - return time.Millisecond - } -} - -func WithAccuracy(accuracy Accuracy) ScannerOption { - return func(s Scanner) { - s.SetAccuracy(accuracy) - } -} +type ScannerOption = func(s Scanner) func WithRequestNotifications(cb func(a *Request)) ScannerOption { return func(s Scanner) { diff --git a/pkg/scanner/options_test.go b/pkg/scanner/options_test.go index d336ec0..4ee1261 100644 --- a/pkg/scanner/options_test.go +++ b/pkg/scanner/options_test.go @@ -10,21 +10,9 @@ import ( mock_oui "github.com/robgonnella/go-lanscan/mock/oui" mock_scanner "github.com/robgonnella/go-lanscan/mock/scanner" "github.com/robgonnella/go-lanscan/pkg/scanner" - "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) -func TestAccuracy(t *testing.T) { - t.Run("returns duration for accuracy type", func(st *testing.T) { - unknownAccuracy := scanner.Accuracy(3) - - assert.Equal(st, time.Microsecond*100, scanner.LOW_ACCURACY.Duration()) - assert.Equal(st, time.Microsecond*500, scanner.MEDIUM_ACCURACY.Duration()) - assert.Equal(st, time.Millisecond, scanner.HIGH_ACCURACY.Duration()) - assert.Equal(st, time.Millisecond, unknownAccuracy.Duration()) - }) -} - func TestOptions(t *testing.T) { ctrl := gomock.NewController(t) @@ -41,7 +29,6 @@ func TestOptions(t *testing.T) { scanner.NewArpScanner( []string{}, netInfo, - scanner.WithAccuracy(scanner.HIGH_ACCURACY), scanner.WithIdleTimeout(time.Second*5), scanner.WithPacketCapture(testPacketCapture), scanner.WithRequestNotifications(func(r *scanner.Request) {}), @@ -53,7 +40,6 @@ func TestOptions(t *testing.T) { []string{}, []string{}, 54321, - scanner.WithAccuracy(scanner.HIGH_ACCURACY), scanner.WithIdleTimeout(time.Second*5), scanner.WithPacketCapture(testPacketCapture), scanner.WithRequestNotifications(func(r *scanner.Request) {}), @@ -65,7 +51,6 @@ func TestOptions(t *testing.T) { netInfo, []string{}, 54321, - scanner.WithAccuracy(scanner.HIGH_ACCURACY), scanner.WithIdleTimeout(time.Second*5), scanner.WithPacketCapture(testPacketCapture), scanner.WithRequestNotifications(func(r *scanner.Request) {}), diff --git a/pkg/scanner/synscan.go b/pkg/scanner/synscan.go index 76ffac1..5601b8d 100644 --- a/pkg/scanner/synscan.go +++ b/pkg/scanner/synscan.go @@ -13,6 +13,7 @@ import ( "github.com/google/gopacket/layers" "github.com/thediveo/netdb" + "github.com/robgonnella/go-lanscan/internal/logger" "github.com/robgonnella/go-lanscan/internal/util" "github.com/robgonnella/go-lanscan/pkg/network" "github.com/robgonnella/go-lanscan/pkg/oui" @@ -32,10 +33,10 @@ type SynScanner struct { scanning bool lastPacketSentAt time.Time idleTimeout time.Duration - accuracy time.Duration scanningMux *sync.RWMutex packetSentAtMux *sync.RWMutex serviceQueryMux *sync.Mutex + debug logger.DebugLogger } func NewSynScanner( @@ -59,10 +60,10 @@ func NewSynScanner( idleTimeout: time.Second * 5, scanning: false, lastPacketSentAt: time.Time{}, - accuracy: time.Millisecond, scanningMux: &sync.RWMutex{}, packetSentAtMux: &sync.RWMutex{}, serviceQueryMux: &sync.Mutex{}, + debug: logger.NewDebugLogger(), } for _, o := range options { @@ -77,6 +78,15 @@ func (s *SynScanner) Results() chan *ScanResult { } func (s *SynScanner) Scan() error { + fields := map[string]interface{}{ + "interface": s.networkInfo.Interface().Name, + "cidr": s.networkInfo.Cidr(), + "targets": s.targets, + "ports": s.ports, + } + + s.debug.Info().Fields(fields).Msg("starting syn scan") + s.scanningMux.RLock() scanning := s.scanning s.scanningMux.RUnlock() @@ -120,7 +130,7 @@ func (s *SynScanner) Scan() error { go s.readPackets() - limiter := time.NewTicker(s.accuracy) + limiter := time.NewTicker(defaultAccuracy) defer limiter.Stop() for _, target := range s.targets { @@ -163,10 +173,6 @@ func (s *SynScanner) SetIdleTimeout(duration time.Duration) { s.idleTimeout = duration } -func (s *SynScanner) SetAccuracy(accuracy Accuracy) { - s.accuracy = accuracy.Duration() -} - func (s *SynScanner) IncludeVendorInfo(repo oui.VendorRepo) { // nothing to do } diff --git a/pkg/scanner/synscan_test.go b/pkg/scanner/synscan_test.go index 3796301..c9a19ad 100644 --- a/pkg/scanner/synscan_test.go +++ b/pkg/scanner/synscan_test.go @@ -28,6 +28,8 @@ func TestSynScanner(t *testing.T) { HardwareAddr: net.HardwareAddr{}, } + cidr := "172.17.1.1/32" + mockUserIP := net.ParseIP("172.17.1.1") t.Run("returns immediately if already scanning", func(st *testing.T) { @@ -50,6 +52,7 @@ func TestSynScanner(t *testing.T) { ) netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) netInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( @@ -102,6 +105,7 @@ func TestSynScanner(t *testing.T) { mockErr := errors.New("mock open-live error") netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -137,6 +141,7 @@ func TestSynScanner(t *testing.T) { mockErr := errors.New("mock SetBPFFilter error") netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) cap.EXPECT().OpenLive( gomock.Any(), @@ -174,6 +179,7 @@ func TestSynScanner(t *testing.T) { mockErr := errors.New("mock WritePacketData error") netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) netInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( @@ -226,6 +232,7 @@ func TestSynScanner(t *testing.T) { mockErr := errors.New("mock SerializeLayers error") netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) netInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( @@ -285,6 +292,7 @@ func TestSynScanner(t *testing.T) { ) netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) netInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( @@ -346,6 +354,7 @@ func TestSynScanner(t *testing.T) { ) netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) netInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( @@ -407,6 +416,7 @@ func TestSynScanner(t *testing.T) { ) netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) netInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( @@ -475,6 +485,7 @@ func TestSynScanner(t *testing.T) { ) netInfo.EXPECT().Interface().AnyTimes().Return(mockInterface) + netInfo.EXPECT().Cidr().AnyTimes().Return(cidr) netInfo.EXPECT().UserIP().Return(mockUserIP) cap.EXPECT().OpenLive( diff --git a/pkg/scanner/types.go b/pkg/scanner/types.go index 301ad8c..d7dcd78 100644 --- a/pkg/scanner/types.go +++ b/pkg/scanner/types.go @@ -33,7 +33,6 @@ type Scanner interface { SetRequestNotifications(cb func(a *Request)) SetIdleTimeout(d time.Duration) IncludeVendorInfo(repo oui.VendorRepo) - SetAccuracy(accuracy Accuracy) SetPacketCapture(cap PacketCapture) } diff --git a/test-server/Dockerfile b/test-server/Dockerfile new file mode 100644 index 0000000..d66feb4 --- /dev/null +++ b/test-server/Dockerfile @@ -0,0 +1,19 @@ +################################################################################ +# Build +################################################################################ +FROM golang:1.21-alpine3.18 as builder + +WORKDIR /app + +COPY main.go go.mod ./ + +RUN go build -ldflags '-s -w' + +################################################################################ +# Final +################################################################################ +FROM alpine:3.18 + +COPY --from=builder /app/test-server /test-server + +ENTRYPOINT [ "/test-server" ] diff --git a/test-server/go.mod b/test-server/go.mod new file mode 100644 index 0000000..8d9bc73 --- /dev/null +++ b/test-server/go.mod @@ -0,0 +1,3 @@ +module test-server + +go 1.21.0 diff --git a/test-server/main.go b/test-server/main.go new file mode 100644 index 0000000..8916871 --- /dev/null +++ b/test-server/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "net/http" + "os" +) + +var port = os.Getenv("PORT") + +func main() { + if port == "" { + port = "8080" + } + + addr := ":" + port + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Printf("received request: %+v\n", r) + }) + + fmt.Printf("starting server: listening on %s\n", addr) + http.ListenAndServe(addr, nil) +}