16 Commits
2.1.4 ... 2.2.3

Author SHA1 Message Date
b11f15ecfe Bump github.com/quic-go/quic-go from 0.40.1 to 0.42.0 (#28)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.40.1 to 0.42.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.40.1...v0.42.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-02 17:40:05 +02:00
454f65f087 Move models initial setup to server handler (not ideal) 2024-03-23 20:24:19 +01:00
1988241b98 chore: Bump workflows dependencies 2024-03-23 18:05:13 +01:00
901345a337 [chore] Update workflows dependecies 2024-03-23 17:47:39 +01:00
0c14419e7e Server handling refactor (#27) 2024-03-23 17:41:34 +01:00
db111642d2 Bump github.com/docker/docker (#26)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 24.0.7+incompatible to 24.0.9+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v24.0.7...v24.0.9)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-20 19:49:19 +01:00
d5b1373e17 Bump google.golang.org/protobuf from 1.32.0 to 1.33.0 (#25)
Bumps google.golang.org/protobuf from 1.32.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-14 15:40:42 +01:00
ba8a2ec494 Bump github.com/opencontainers/runc from 1.1.9 to 1.1.12 (#24)
Bumps [github.com/opencontainers/runc](https://github.com/opencontainers/runc) from 1.1.9 to 1.1.12.
- [Release notes](https://github.com/opencontainers/runc/releases)
- [Changelog](https://github.com/opencontainers/runc/blob/v1.1.12/CHANGELOG.md)
- [Commits](https://github.com/opencontainers/runc/compare/v1.1.9...v1.1.12)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/runc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-01 17:27:37 +01:00
f8e27bef56 Add endopoint to check is a given port is open on the client (#22) 2023-12-31 12:52:08 +01:00
2bbeeb34c5 Bump github.com/containerd/containerd from 1.7.7 to 1.7.11 (#20)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.7 to 1.7.11.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.7.7...v1.7.11)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 20:08:35 +01:00
0090b794ee Bump golang.org/x/crypto from 0.14.0 to 0.17.0 (#19)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 09:10:37 +01:00
93f561d6ef Bump github.com/docker/docker from 20.10.24+incompatible to 24.0.7+incompatible (#18)
* Bump github.com/docker/docker

Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.24+incompatible to 24.0.7+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v20.10.24...v24.0.7)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dependencies

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Daniel Carrillo <daniel.carrillo@gmail.com>
2023-10-30 19:17:40 +01:00
9da6d2fec5 Bump google.golang.org/grpc from 1.53.0 to 1.56.3 (#17)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.53.0 to 1.56.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.53.0...v1.56.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-26 09:25:00 +02:00
8e3d731719 [ci] Remove unused lines from codeql-analysis.yml 2023-10-12 12:52:44 +02:00
d5b244dc5f Bump go version to 1.21.3 (it fix HTTP/2 Stream Resets issue) 2023-10-12 12:14:49 +02:00
d767afd658 Update dependencies 2023-10-11 19:39:33 +02:00
16 changed files with 432 additions and 1257 deletions

View File

@ -29,21 +29,18 @@ jobs:
contents: read contents: read
security-events: write security-events: write
strategy:
fail-fast: false
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: install go - name: install go
uses: actions/setup-go@v3 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
cache: true cache: true
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
languages: go languages: go
@ -52,4 +49,4 @@ jobs:
make build make build
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v3

View File

@ -15,10 +15,10 @@ jobs:
matrix: matrix:
make: ["lint", "test"] make: ["lint", "test"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: install go - name: install go
uses: actions/setup-go@v4 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -33,11 +33,11 @@ jobs:
matrix: matrix:
goosarch: [linux-amd64] goosarch: [linux-amd64]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: install go - name: install go
uses: actions/setup-go@v3 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
cache: true cache: true
@ -61,7 +61,7 @@ jobs:
sha256sum whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz > whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz.sha256 sha256sum whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz > whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz.sha256
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
with: with:
body_path: changelog.txt body_path: changelog.txt
files: | files: |

View File

@ -7,11 +7,11 @@ test: unit-test integration-test
.PHONY: unit-test .PHONY: unit-test
unit-test: unit-test:
go test -race -short -cover ./... go test -count=1 -race -short -cover ./...
.PHONY: integration-test .PHONY: integration-test
integration-test: integration-test:
go test ./integration-tests -v go test -count=1 -v ./integration-tests
.PHONY: install-tools .PHONY: install-tools
install-tools: install-tools:
@ -22,14 +22,9 @@ install-tools:
@command $(GOPATH)/shadow > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @command $(GOPATH)/shadow > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest; \ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest; \
fi fi
@command $(GOPATH)/golines > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install github.com/segmentio/golines@latest; \
fi
.PHONY: lint .PHONY: lint
lint: install-tools lint: install-tools
gofmt -l . && test -z $$(gofmt -l .) gofmt -l . && test -z $$(gofmt -l .)
golines -l . && test -z $$(golines -l .)
golangci-lint run golangci-lint run
shadow ./... shadow ./...

View File

@ -21,7 +21,8 @@
- [Run a container locally using test databases](#run-a-container-locally-using-test-databases) - [Run a container locally using test databases](#run-a-container-locally-using-test-databases)
- [From Docker Hub](#from-docker-hub) - [From Docker Hub](#from-docker-hub)
Just another "what is my IP address" service, including geolocation and headers information, written in go with high performance in mind, it uses [gin](https://github.com/gin-gonic/gin) which uses [httprouter](https://github.com/julienschmidt/httprouter) a lightweight high performance HTTP multiplexer. Just another "what is my IP address" service, including geolocation, TCP open port checking, and headers information. Written in go with high performance in mind,
it uses [gin](https://github.com/gin-gonic/gin) which uses [httprouter](https://github.com/julienschmidt/httprouter) a lightweight high performance HTTP multiplexer.
Take a look at [ifconfig.es](https://ifconfig.es) a live site using `whatismyip` Take a look at [ifconfig.es](https://ifconfig.es) a live site using `whatismyip`
@ -42,6 +43,7 @@ curl -6 ifconfig.es
- Can run behind a proxy by trusting a custom header (usually `X-Real-IP`) to figure out the source IP address. It also supports a custom header to resolve the client port, if the proxy can only add a header for the IP (for example a fixed header from CDNs) the client port is shown as unknown. - Can run behind a proxy by trusting a custom header (usually `X-Real-IP`) to figure out the source IP address. It also supports a custom header to resolve the client port, if the proxy can only add a header for the IP (for example a fixed header from CDNs) the client port is shown as unknown.
- IPv4 and IPv6. - IPv4 and IPv6.
- Geolocation info including ASN. This feature is possible thanks to [maxmind](https://dev.maxmind.com/geoip/geolite2-free-geolocation-data?lang=en) GeoLite2 databases. In order to use these databases, a license key is needed. Please visit Maxmind site for further instructions and get a free license. - Geolocation info including ASN. This feature is possible thanks to [maxmind](https://dev.maxmind.com/geoip/geolite2-free-geolocation-data?lang=en) GeoLite2 databases. In order to use these databases, a license key is needed. Please visit Maxmind site for further instructions and get a free license.
- Checking TCP open ports.
- High performance. - High performance.
- Self-contained server that can reload GeoLite2 databases and/or SSL certificates without stop/start. The `hup` signal is honored. - Self-contained server that can reload GeoLite2 databases and/or SSL certificates without stop/start. The `hup` signal is honored.
- HTML templates for the landing page. - HTML templates for the landing page.
@ -66,6 +68,7 @@ curl -6 ifconfig.es
- https://ifconfig.es/all - https://ifconfig.es/all
- https://ifconfig.es/headers - https://ifconfig.es/headers
- https://ifconfig.es/<header_name> - https://ifconfig.es/<header_name>
- https://ifconfig.es/scan/tcp/<port_number>
## Build ## Build

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"flag" "flag"
"fmt" "fmt"
"net/http"
"os" "os"
"github.com/dcarrillo/whatismyip/internal/httputils" "github.com/dcarrillo/whatismyip/internal/httputils"
@ -11,7 +12,6 @@ import (
"github.com/dcarrillo/whatismyip/server" "github.com/dcarrillo/whatismyip/server"
"github.com/gin-contrib/secure" "github.com/gin-contrib/secure"
"github.com/dcarrillo/whatismyip/models"
"github.com/dcarrillo/whatismyip/router" "github.com/dcarrillo/whatismyip/router"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -26,12 +26,12 @@ func main() {
os.Exit(1) os.Exit(1)
} }
models.Setup(setting.App.GeodbPath.City, setting.App.GeodbPath.ASN)
engine := setupEngine() engine := setupEngine()
router.SetupTemplate(engine) router.SetupTemplate(engine)
router.Setup(engine) router.Setup(engine)
servers := setupHTTPServers(context.Background(), engine.Handler())
whatismyip := server.Setup(context.Background(), engine.Handler()) whatismyip := server.Setup(servers)
whatismyip.Run() whatismyip.Run()
} }
@ -55,3 +55,23 @@ func setupEngine() *gin.Engine {
return engine return engine
} }
func setupHTTPServers(ctx context.Context, handler http.Handler) []server.Server {
var servers []server.Server
if setting.App.BindAddress != "" {
tcpServer := server.NewTCPServer(ctx, &handler)
servers = append(servers, tcpServer)
}
if setting.App.TLSAddress != "" {
tlsServer := server.NewTLSServer(ctx, &handler)
servers = append(servers, tlsServer)
if setting.App.EnableHTTP3 {
quicServer := server.NewQuicServer(ctx, tlsServer)
servers = append(servers, quicServer)
}
}
return servers
}

97
go.mod
View File

@ -1,78 +1,89 @@
module github.com/dcarrillo/whatismyip module github.com/dcarrillo/whatismyip
go 1.21 go 1.21.3
require ( require (
github.com/gin-contrib/secure v0.0.1 github.com/gin-contrib/secure v0.0.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/oschwald/maxminddb-golang v1.12.0 github.com/oschwald/maxminddb-golang v1.12.0
github.com/quic-go/quic-go v0.37.4 github.com/quic-go/quic-go v0.42.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/testcontainers/testcontainers-go v0.13.0 github.com/testcontainers/testcontainers-go v0.27.0
golang.org/x/net v0.14.0
) )
require ( require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.9.6 // indirect github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/bytedance/sonic v1.10.0 // indirect github.com/bytedance/sonic v1.10.2 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/containerd/cgroups v1.0.4 // indirect github.com/containerd/containerd v1.7.11 // indirect
github.com/containerd/containerd v1.6.18 // indirect github.com/containerd/log v0.1.0 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v20.10.24+incompatible // indirect github.com/docker/docker v24.0.9+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.15.0 // indirect github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/pprof v0.0.0-20230811205829-9131a7e9cc17 // indirect github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/compress v1.16.5 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.6 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/moby/sys/mount v0.3.3 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/onsi/ginkgo/v2 v2.13.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opencontainers/runc v1.1.5 // indirect github.com/opencontainers/runc v1.1.12 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect github.com/shirou/gopsutil/v3 v3.23.11 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
go.opencensus.io v0.23.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect
golang.org/x/arch v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.12.0 // indirect golang.org/x/arch v0.6.0 // indirect
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect golang.org/x/crypto v0.18.0 // indirect
golang.org/x/mod v0.12.0 // indirect golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect
golang.org/x/sys v0.11.0 // indirect golang.org/x/mod v0.14.0 // indirect
golang.org/x/text v0.12.0 // indirect golang.org/x/net v0.20.0 // indirect
golang.org/x/tools v0.12.0 // indirect golang.org/x/sys v0.16.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect golang.org/x/text v0.14.0 // indirect
google.golang.org/grpc v1.53.0 // indirect golang.org/x/tools v0.17.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
) )

1204
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log"
"net/http" "net/http"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -30,12 +30,12 @@ func buildContainer() testcontainers.ContainerRequest {
Dockerfile: "Dockerfile", Dockerfile: "Dockerfile",
}, },
Cmd: []string{ Cmd: []string{
"-geoip2-city", "/tmp/GeoIP2-City-Test.mmdb", "-geoip2-city", "/GeoIP2-City-Test.mmdb",
"-geoip2-asn", "/tmp/GeoLite2-ASN-Test.mmdb", "-geoip2-asn", "/GeoLite2-ASN-Test.mmdb",
"-bind", ":8000", "-bind", ":8000",
"-tls-bind", ":8001", "-tls-bind", ":8001",
"-tls-crt", "/tmp/server.pem", "-tls-crt", "/server.pem",
"-tls-key", "/tmp/server.key", "-tls-key", "/server.key",
"-trusted-header", "X-Real-IP", "-trusted-header", "X-Real-IP",
"-enable-secure-headers", "-enable-secure-headers",
"-enable-http3", "-enable-http3",
@ -44,45 +44,55 @@ func buildContainer() testcontainers.ContainerRequest {
WaitingFor: wait.ForHTTP("/geo"). WaitingFor: wait.ForHTTP("/geo").
WithTLS(true, &tls.Config{InsecureSkipVerify: true}). WithTLS(true, &tls.Config{InsecureSkipVerify: true}).
WithPort("8001"), WithPort("8001"),
Mounts: testcontainers.Mounts( Files: []testcontainers.ContainerFile{
testcontainers.BindMount( {
filepath.Join(dir, "/../test/GeoIP2-City-Test.mmdb"), HostFilePath: filepath.Join(dir, "/../test/GeoIP2-City-Test.mmdb"),
"/tmp/GeoIP2-City-Test.mmdb", ContainerFilePath: "/GeoIP2-City-Test.mmdb",
), FileMode: 0644,
testcontainers.BindMount( },
filepath.Join(dir, "/../test/GeoLite2-ASN-Test.mmdb"), {
"/tmp/GeoLite2-ASN-Test.mmdb", HostFilePath: filepath.Join(dir, "/../test/GeoLite2-ASN-Test.mmdb"),
), ContainerFilePath: "/GeoLite2-ASN-Test.mmdb",
testcontainers.BindMount(filepath.Join(dir, "/../test/server.pem"), "/tmp/server.pem"), FileMode: 0644,
testcontainers.BindMount(filepath.Join(dir, "/../test/server.key"), "/tmp/server.key"), },
), {
HostFilePath: filepath.Join(dir, "/../test/server.pem"),
ContainerFilePath: "/server.pem",
FileMode: 0644,
},
{
HostFilePath: filepath.Join(dir, "/../test/server.key"),
ContainerFilePath: "/server.key",
FileMode: 0644,
},
},
} }
return req return req
} }
func initContainer(t assert.TestingT, request testcontainers.ContainerRequest) func() {
ctx := context.Background()
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: request,
Started: true,
})
assert.NoError(t, err)
return func() {
assert.NoError(t, container.Terminate(ctx))
}
}
func TestContainerIntegration(t *testing.T) { func TestContainerIntegration(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("Skiping integration tests") t.Skip("Skiping integration tests")
} }
ctx := context.Background() t.Cleanup(initContainer(t, buildContainer()))
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: buildContainer(),
Started: true,
})
if err != nil {
log.Fatal(err)
}
defer func() {
err = container.Terminate(ctx)
if err != nil {
log.Fatal(err)
}
}()
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
tests := []struct { tests := []struct {
name string name string
url string url string
@ -105,9 +115,27 @@ func TestContainerIntegration(t *testing.T) {
}, },
} }
testsPortScan := []struct {
name string
port int
want bool
}{
{
name: "RequestOpenPortScan",
port: 8000,
want: true,
},
{
name: "RequestClosedPortScan",
port: 65533,
want: false,
},
}
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
req, _ := http.NewRequest("GET", tt.url, nil) req, err := http.NewRequest("GET", tt.url, nil)
assert.NoError(t, err)
req.Header.Set("Accept", "application/json") req.Header.Set("Accept", "application/json")
var resp *http.Response var resp *http.Response
@ -131,6 +159,27 @@ func TestContainerIntegration(t *testing.T) {
assert.Equal(t, "1; mode=block", resp.Header.Get("X-Xss-Protection")) assert.Equal(t, "1; mode=block", resp.Header.Get("X-Xss-Protection"))
}) })
} }
for _, tt := range testsPortScan {
t.Run(tt.name, func(t *testing.T) {
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:8000/scan/tcp/%d", tt.port), nil)
assert.NoError(t, err)
req.Header.Set("Accept", "application/json")
req.Header.Set("X-Real-IP", "127.0.0.1")
client := &http.Client{}
resp, err := client.Do(req)
assert.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode)
body, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
j := router.JSONScanResponse{}
assert.NoError(t, json.Unmarshal(body, &j))
assert.Equal(t, tt.want, j.Reachable)
})
}
} }
func doQuicRequest(req *http.Request) (*http.Response, []byte, error) { func doQuicRequest(req *http.Request) (*http.Response, []byte, error) {

View File

@ -63,7 +63,7 @@ func GetLogFormatter(param gin.LogFormatterParams) string {
) )
} }
func normalizeLog(log interface{}) interface{} { func normalizeLog(log any) any {
switch v := log.(type) { switch v := log.(type) {
case string: case string:
if v == "" { if v == "" {

54
router/port_scanner.go Normal file
View File

@ -0,0 +1,54 @@
package router
import (
"fmt"
"net"
"net/http"
"strconv"
"github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
)
type JSONScanResponse struct {
IP string `json:"ip"`
Port int `json:"port"`
Reachable bool `json:"reachable"`
Reason string `json:"reason"`
}
func scanTCPPort(ctx *gin.Context) {
port, err := strconv.Atoi(ctx.Params.ByName("port"))
if err == nil && (port < 1 || port > 65535) {
err = fmt.Errorf("%d is not a valid port number", port)
}
if err != nil {
ctx.JSON(http.StatusBadRequest, JSONScanResponse{
Reason: err.Error(),
})
return
}
add := net.TCPAddr{
IP: net.ParseIP(ctx.ClientIP()),
Port: port,
}
scan := service.PortScanner{
Address: &add,
}
isOpen, err := scan.IsPortOpen()
reason := ""
if err != nil {
reason = err.Error()
}
response := JSONScanResponse{
IP: ctx.ClientIP(),
Port: port,
Reachable: isOpen,
Reason: reason,
}
ctx.JSON(http.StatusOK, response)
}

View File

@ -22,6 +22,7 @@ func SetupTemplate(r *gin.Engine) {
// Setup defines the endpoints // Setup defines the endpoints
func Setup(r *gin.Engine) { func Setup(r *gin.Engine) {
r.GET("/", getRoot) r.GET("/", getRoot)
r.GET("/scan/tcp/:port", scanTCPPort)
r.GET("/client-port", getClientPortAsString) r.GET("/client-port", getClientPortAsString)
r.GET("/geo", getGeoAsString) r.GET("/geo", getGeoAsString)
r.GET("/geo/:field", getGeoAsString) r.GET("/geo/:field", getGeoAsString)

View File

@ -2,6 +2,7 @@ package server
import ( import (
"context" "context"
"errors"
"log" "log"
"net/http" "net/http"
@ -9,20 +10,20 @@ import (
"github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/http3"
) )
type QuicServer struct { type Quic struct {
server *http3.Server server *http3.Server
tlsServer *TLSServer tlsServer *TLS
ctx context.Context ctx context.Context
} }
func NewQuicServer(ctx context.Context, tlsServer *TLSServer) *QuicServer { func NewQuicServer(ctx context.Context, tlsServer *TLS) *Quic {
return &QuicServer{ return &Quic{
tlsServer: tlsServer, tlsServer: tlsServer,
ctx: ctx, ctx: ctx,
} }
} }
func (q *QuicServer) Start() { func (q *Quic) Start() {
q.server = &http3.Server{ q.server = &http3.Server{
Addr: setting.App.TLSAddress, Addr: setting.App.TLSAddress,
Handler: q.tlsServer.server.Handler, Handler: q.tlsServer.server.Handler,
@ -40,13 +41,13 @@ func (q *QuicServer) Start() {
log.Printf("Starting QUIC server listening on %s (udp)", setting.App.TLSAddress) log.Printf("Starting QUIC server listening on %s (udp)", setting.App.TLSAddress)
go func() { go func() {
if err := q.server.ListenAndServeTLS(setting.App.TLSCrtPath, setting.App.TLSKeyPath); err != nil && if err := q.server.ListenAndServeTLS(setting.App.TLSCrtPath, setting.App.TLSKeyPath); err != nil &&
err.Error() != "quic: Server closed" { !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err) log.Fatal(err)
} }
}() }()
} }
func (q *QuicServer) Stop() { func (q *Quic) Stop() {
log.Printf("Stopping QUIC server...") log.Printf("Stopping QUIC server...")
if err := q.server.Close(); err != nil { if err := q.server.Close(); err != nil {
log.Printf("QUIC server forced to shutdown") log.Printf("QUIC server forced to shutdown")

View File

@ -2,14 +2,12 @@ package server
import ( import (
"log" "log"
"net/http"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/dcarrillo/whatismyip/internal/setting" "github.com/dcarrillo/whatismyip/internal/setting"
"github.com/dcarrillo/whatismyip/models" "github.com/dcarrillo/whatismyip/models"
"golang.org/x/net/context"
) )
type Server interface { type Server interface {
@ -17,80 +15,48 @@ type Server interface {
Stop() Stop()
} }
type Factory struct { type Manager struct {
tcpServer *TCPServer servers []Server
tlsServer *TLSServer
quicServer *QuicServer
} }
func Setup(ctx context.Context, handler http.Handler) *Factory { func Setup(servers []Server) *Manager {
var tcpServer *TCPServer return &Manager{
var tlsServer *TLSServer servers: servers,
var quicServer *QuicServer
if setting.App.BindAddress != "" {
tcpServer = NewTCPServer(ctx, &handler)
}
if setting.App.TLSAddress != "" {
tlsServer = NewTLSServer(ctx, &handler)
if setting.App.EnableHTTP3 {
quicServer = NewQuicServer(ctx, tlsServer)
}
}
return &Factory{
tcpServer: tcpServer,
tlsServer: tlsServer,
quicServer: quicServer,
} }
} }
func (f *Factory) Run() { func (m *Manager) Run() {
f.start() m.start()
signalChan := make(chan os.Signal, 3) models.Setup(setting.App.GeodbPath.City, setting.App.GeodbPath.ASN)
signalChan := make(chan os.Signal, len(m.servers))
signal.Notify(signalChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) signal.Notify(signalChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
var s os.Signal var s os.Signal
for { for {
s = <-signalChan s = <-signalChan
if s == syscall.SIGHUP { if s == syscall.SIGHUP {
f.stop() m.stop()
models.CloseDBs() models.CloseDBs()
models.Setup(setting.App.GeodbPath.City, setting.App.GeodbPath.ASN) models.Setup(setting.App.GeodbPath.City, setting.App.GeodbPath.ASN)
f.start() m.start()
} else { } else {
log.Printf("Shutting down...") log.Printf("Shutting down...")
f.stop() m.stop()
models.CloseDBs() models.CloseDBs()
break break
} }
} }
} }
func (f *Factory) start() { func (m *Manager) start() {
if f.tcpServer != nil { for _, s := range m.servers {
f.tcpServer.Start() s.Start()
}
if f.tlsServer != nil {
f.tlsServer.Start()
if f.quicServer != nil {
f.quicServer.Start()
}
} }
} }
func (f *Factory) stop() { func (m *Manager) stop() {
if f.tcpServer != nil { for _, s := range m.servers {
f.tcpServer.Stop() s.Stop()
}
if f.tlsServer != nil {
if f.quicServer != nil {
f.quicServer.Stop()
}
f.tlsServer.Stop()
} }
} }

View File

@ -9,20 +9,20 @@ import (
"github.com/dcarrillo/whatismyip/internal/setting" "github.com/dcarrillo/whatismyip/internal/setting"
) )
type TCPServer struct { type TCP struct {
server *http.Server server *http.Server
handler *http.Handler handler *http.Handler
ctx context.Context ctx context.Context
} }
func NewTCPServer(ctx context.Context, handler *http.Handler) *TCPServer { func NewTCPServer(ctx context.Context, handler *http.Handler) *TCP {
return &TCPServer{ return &TCP{
handler: handler, handler: handler,
ctx: ctx, ctx: ctx,
} }
} }
func (t *TCPServer) Start() { func (t *TCP) Start() {
t.server = &http.Server{ t.server = &http.Server{
Addr: setting.App.BindAddress, Addr: setting.App.BindAddress,
Handler: *t.handler, Handler: *t.handler,
@ -38,7 +38,7 @@ func (t *TCPServer) Start() {
}() }()
} }
func (t *TCPServer) Stop() { func (t *TCP) Stop() {
log.Printf("Stopping TCP server...") log.Printf("Stopping TCP server...")
if err := t.server.Shutdown(t.ctx); err != nil { if err := t.server.Shutdown(t.ctx); err != nil {
log.Printf("TCP server forced to shutdown: %s", err) log.Printf("TCP server forced to shutdown: %s", err)

View File

@ -9,20 +9,20 @@ import (
"github.com/dcarrillo/whatismyip/internal/setting" "github.com/dcarrillo/whatismyip/internal/setting"
) )
type TLSServer struct { type TLS struct {
server *http.Server server *http.Server
handler *http.Handler handler *http.Handler
ctx context.Context ctx context.Context
} }
func NewTLSServer(ctx context.Context, handler *http.Handler) *TLSServer { func NewTLSServer(ctx context.Context, handler *http.Handler) *TLS {
return &TLSServer{ return &TLS{
handler: handler, handler: handler,
ctx: ctx, ctx: ctx,
} }
} }
func (t *TLSServer) Start() { func (t *TLS) Start() {
t.server = &http.Server{ t.server = &http.Server{
Addr: setting.App.TLSAddress, Addr: setting.App.TLSAddress,
Handler: *t.handler, Handler: *t.handler,
@ -39,7 +39,7 @@ func (t *TLSServer) Start() {
}() }()
} }
func (t *TLSServer) Stop() { func (t *TLS) Stop() {
log.Printf("Stopping TLS server...") log.Printf("Stopping TLS server...")
if err := t.server.Shutdown(t.ctx); err != nil { if err := t.server.Shutdown(t.ctx); err != nil {
log.Printf("TLS server forced to shutdown: %s", err) log.Printf("TLS server forced to shutdown: %s", err)

24
service/port_scanner.go Normal file
View File

@ -0,0 +1,24 @@
package service
import (
"net"
"time"
)
const scannerTimeOut = 3 * time.Second
type PortScanner struct {
Address net.Addr
}
func (p *PortScanner) IsPortOpen() (bool, error) {
conn, err := net.DialTimeout(p.Address.Network(), p.Address.String(), scannerTimeOut)
if err != nil {
return false, err
}
if conn != nil {
defer conn.Close()
}
return true, nil
}