Skip to content

Commit

Permalink
Merge pull request #10 from BOOMfinity/slave
Browse files Browse the repository at this point in the history
rewritten to client
  • Loading branch information
MrBoombastic authored Aug 25, 2023
2 parents d2fe884 + 04a3614 commit 3b50b76
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 246 deletions.
81 changes: 58 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

[![Go Reference](https://pkg.go.dev/badge/github.com/BOOMfinity/GrOxyP.svg)](https://pkg.go.dev/github.com/BOOMfinity/GrOxyP)
[![CodeFactor](https://www.codefactor.io/repository/github/boomfinity/groxyp/badge)](https://www.codefactor.io/repository/github/boomfinity/groxyp)
[![BCH compliance](https://bettercodehub.com/edge/badge/BOOMfinity/GrOxyP?branch=master)](https://bettercodehub.com/)

Check if user is behind a VPN or proxy via simple HTTP request.

Expand All @@ -15,59 +14,70 @@ Sources of code are mentioned in the comments.

## Benchmarks

Ran on Windows 11, AMD Ryzen 7 3700X, 32GB RAM 3200MHz.
Ran on Windows 11 22631.2262, AMD Ryzen 7 3700X, 32GB RAM 3200MHz, Go 1.21.

[go-wrk](https://github.com/tsliwowicz/go-wrk) benchmark:

- 100 connections, 20 seconds:

```shell
$ go-wrk -c 100 -d 20 http://localhost:5656/ip?q=194.35.232.123
$ go-wrk -c 100 -d 20 "http://localhost:5656/ip?q=194.35.232.123&token=token"

Running 20s test @ http://localhost:5656/ip?q=194.35.232.123
Running 20s test @ http://localhost:5656/ip?q=194.35.232.123&token=token
100 goroutine(s) running concurrently
574077 requests in 17.079976921s, 83.76MB read
Requests/sec: 33611.11
Transfer/sec: 4.90MB
Avg Req Time: 2.975206ms
1356936 requests in 18.422209262s, 197.99MB read
Requests/sec: 73657.62
Transfer/sec: 10.75MB
Avg Req Time: 1.357632ms
Fastest Request: 0s
Slowest Request: 32.4233ms
Slowest Request: 18.6332ms
Number of Errors: 0
# Stats: ~20% CPU, ~50MB RAM
```

Stats (Task Manager, eyeballing): ~30% CPU, ~40MB RAM

- 1 connection, 20 seconds:

```shell
$ go-wrk -c 1 -d 20 http://localhost:5656/ip?q=194.35.232.123
$ go-wrk -c 1 -d 20 "http://localhost:5656/ip?q=194.35.232.123&token=token"

Running 20s test @ http://localhost:5656/ip?q=194.35.232.123
Running 20s test @ http://localhost:5656/ip?q=194.35.232.123&token=token
1 goroutine(s) running concurrently
283966 requests in 18.9446991s, 41.43MB read
Requests/sec: 14989.21
Transfer/sec: 2.19MB
Avg Req Time: 66.714µs
243087 requests in 19.0139044s, 35.47MB read
Requests/sec: 12784.70
Transfer/sec: 1.87MB
Avg Req Time: 78.218µs
Fastest Request: 0s
Slowest Request: 3.641ms
Slowest Request: 4.6746ms
Number of Errors: 0
# Stats: ~10% CPU, ~38MB RAM
```

Stats (Task Manager, eyeballing): ~11% CPU, ~38MB RAM

# Installation and usage

## As a server

1. Clone: `git clone https://github.com/BOOMfinity/GrOxyP`.
2. Go to directory: `cd GrOxyP/cmd/groxyp`.
3. Build: `go build`.
4. Set environmental variables as in example:

```shell
GROXYP_DB_URL = "https://raw.githubusercontent.com/X4BNet/lists_vpn/main/ipv4.txt"
GROXYP_DB_FILE = "ips.txt"
```sh
GROXYP_DB_URL = "https://raw.githubusercontent.com/X4BNet/lists_vpn/main/output/datacenter/ipv4.txt"
GROXYP_DB_UPDATE_INTERVAL = "4h0m0s"
GROXYP_PORT = 5656,
GROXYP_TOKEN: = "such_a_token_wow"
GROXYP_TOKEN = "such_a_token_wow"
GROXYP_DEBUG = false
```

> [!NOTE]
> Port and token are only needed, when you want to spin up an HTTP server.
> [!IMPORTANT]
> Always refer to [X4BNet's repo](https://github.com/X4BNet/lists_vpn) for more information about IP lists. You might
> want to replace `datacenter` with `vpn` in the example above.
5. Run!

HTTP server will be waiting for requests at default port 5656. Query `ip` endpoint like so:
Expand All @@ -77,9 +87,34 @@ $ curl http://localhost:5656/ip?q=194.35.232.123&token=such_a_token_wow
{"ip":"194.35.232.123","proxy":true,"rule":"194.35.232.0/22"}
```

Invalid token will cause `401 Unauthorized` messages. Other endpoints should respond with `404` message.
Invalid token will cause `401 Unauthorized` messages. Other endpoints will respond with `404` message.

## Programmatically

Use example code below:

```go
package main

import (
"fmt"
groxyp "github.com/BOOMfinity/GrOxyP/pkg/client"
"net"
)

var ipChecker, _ = groxyp.NewClient(groxyp.Config{
DatabaseDownloadURL: "https://raw.githubusercontent.com/X4BNet/lists_vpn/main/output/datacenter/ipv4.txt",
DatabaseUpdateInterval: "8h0m0s",
})

func main() {
found, reason := ipChecker.FindIP(net.ParseIP("8.8.8.8"))
fmt.Printf("IP found in the list: %v. IP block: %v", found, reason)
}
```

# Discord support server

Because why not?

[![Discord Widget](https://discordapp.com/api/guilds/1036320104486547466/widget.png?style=banner4)](https://labs.boomfinity.xyz)
56 changes: 31 additions & 25 deletions cmd/groxyp/groxyp.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,44 @@ package main

import (
"fmt"
"github.com/BOOMfinity/GrOxyP/pkg/config"
"github.com/BOOMfinity/GrOxyP/pkg/database"
"github.com/BOOMfinity/GrOxyP/pkg/webserver"
"log"
"time"
"github.com/BOOMfinity/GrOxyP/pkg/client"
"os"
)

// getConfig reads environmental variables
func getConfig() (client.Config, error) {
if os.Getenv("GROXYP_DB_URL") == "" {
return client.Config{}, fmt.Errorf("GROXYP_DB_URL is not set")
}
if os.Getenv("GROXYP_PORT") == "" {
return client.Config{}, fmt.Errorf("GROXYP_PORT is not set")
}
if os.Getenv("GROXYP_TOKEN") == "" {
return client.Config{}, fmt.Errorf("GROXYP_TOKEN is not set")
}
return client.Config{
DatabaseDownloadURL: os.Getenv("GROXYP_DB_URL"),
DatabaseUpdateInterval: os.Getenv("GROXYP_DB_UPDATE_INTERVAL"),
WebserverPort: os.Getenv("GROXYP_PORT"),
Debug: os.Getenv("GROXYP_DEBUG") == "true",
WebserverToken: os.Getenv("GROXYP_TOKEN")}, nil
}

func main() {
// Getting config from config.json
var cfg = config.Get()
// Downloading fresh database immediately
err := database.Update()
// Check envs
conf, err := getConfig()
if err != nil {
return
panic(err)
}
// Spinning up new client
c, err := client.NewClient(conf)
if err != nil {
panic(err)
}
// Updating database "in background" at given interval
go func() {
// Parsing duration
interval, err := time.ParseDuration(cfg.DatabaseUpdateInterval)
if err != nil {
log.Fatal(err)
}
// Starting interval
err = database.SetUpdateInterval(interval)
if err != nil {
fmt.Println(err)
}
}()
// Starting webserver to listen HTTP queries
err = webserver.Listen(cfg.WebserverPort)
err = c.StartServer()
if err != nil {
fmt.Println(err)
panic(err)
return
}
}
12 changes: 2 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
module github.com/BOOMfinity/GrOxyP

go 1.18
go 1.21

require (
github.com/segmentio/encoding v0.3.6
github.com/yl2chen/cidranger v1.0.2
)

require (
github.com/segmentio/asm v1.2.0 // indirect
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect
)
require github.com/yl2chen/cidranger v1.0.2
8 changes: 0 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ=
github.com/segmentio/encoding v0.3.6/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
55 changes: 55 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package client

import (
"fmt"
"github.com/yl2chen/cidranger"
"net"
"net/http"
"os"
)

// Config is a structure of environmental variables
type Config struct {
DatabaseDownloadURL string
DatabaseUpdateInterval string
WebserverPort string
WebserverToken string
Debug bool
}

type Client struct {
Conf Config
Database cidranger.Ranger
HTTPClient *http.Client
}

// NewClient creates new client with the given config and immediately updates its database. It will also run automatic updates, if interval is specified.
func NewClient(conf Config) (client *Client, err error) {
client = &Client{
Conf: conf,
HTTPClient: &http.Client{},
Database: cidranger.NewPCTrieRanger(),
}
err = client.Update()
if err != nil {
return &Client{}, err
}
if client.Conf.DatabaseUpdateInterval != "" {
go func() {
err = client.runAutoUpdates()
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "GrOxyP auto-update error:", err)
}
}()
}
return client, nil
}

// FindIP checks if a given IP is on the list. If so, returns `true` and the reason (IP block).
func (c *Client) FindIP(query net.IP) (bool, string) {
if containingNetworks, err := c.Database.ContainingNetworks(query); len(containingNetworks) > 0 && err == nil {
network := containingNetworks[0].Network()
return true, network.String()
}
return false, ""
}
64 changes: 64 additions & 0 deletions pkg/client/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package client

import (
"bufio"
"errors"
"fmt"
"github.com/yl2chen/cidranger"
"log"
"net"
"time"
)

// Update will flush and repopulate database with freshly downloaded list.
func (c *Client) Update() (err error) {
if c.Conf.Debug {
fmt.Println("INFO: Downloading database...")
}
response, err := c.HTTPClient.Get(c.Conf.DatabaseDownloadURL)
if err != nil {
return
}
defer response.Body.Close()
if response.StatusCode != 200 {
return errors.New(fmt.Sprintf("received code %v while downloading database", response.StatusCode))
}
if c.Conf.Debug {
fmt.Println("INFO: Database downloaded. Parsing...")
}
// Flushing entries
c.Database = cidranger.NewPCTrieRanger()

// Importing
scanner := bufio.NewScanner(response.Body)
for scanner.Scan() {
if _, currNet, err := net.ParseCIDR(scanner.Text()); err == nil {
err := c.Database.Insert(cidranger.NewBasicRangerEntry(*currNet))
if err != nil {
fmt.Printf("Error while inserting CIDR to database: %v\n", err.Error())
}
}
}
if c.Conf.Debug {
fmt.Printf("INFO: Database parsed. %v entries.\n", c.Database.Len())
}
return scanner.Err()
}

// runAutoUpdates is simple function to run Update at set interval
func (c *Client) runAutoUpdates() error {
interval, err := time.ParseDuration(c.Conf.DatabaseUpdateInterval)
if err != nil {
log.Fatal(err)
}
for range time.Tick(interval) {
if c.Conf.Debug {
fmt.Println("INFO: Database update started...")
}
err := c.Update()
if err != nil {
return err
}
}
return nil
}
Loading

0 comments on commit 3b50b76

Please sign in to comment.