Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
TAG?=$(shell git describe --tags --always --dirty)
IMAGE_NAME?=guamulo/shadowtest:$(TAG)
SSR_DIR?=/tmp/shadowsocksr

.PHONY:
start_test_server:
start_ss_test_server:
- pkill ss-server
ss-server -v -p 6276 -k password &

.PHONY:
test: start_test_server
start_ssr_test_server: $(SSR_DIR)
- pkill -f "shadowsocks/server.py" 2>/dev/null; true
cd $(SSR_DIR) && python3 shadowsocks/server.py -p 16276 -k testpassword -m aes-256-cfb -O origin -o plain &
@for i in 1 2 3 4 5 6 7 8 9 10; do \
if nc -z 127.0.0.1 16276 2>/dev/null; then break; fi; \
sleep 1; \
done

$(SSR_DIR):
git clone --depth 1 https://github.com/shadowsocksrr/shadowsocksr.git $(SSR_DIR)
sed -i 's/collections\.MutableMapping/collections.abc.MutableMapping/g' $(SSR_DIR)/shadowsocks/lru_cache.py

.PHONY:
test: start_ss_test_server start_ssr_test_server
go test ./... -count=1

.PHONY:
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# ShadowTest

A service to test shadowsocks keys.
A service to test Shadowsocks (SS) and ShadowsocksR (SSR) keys.

[![Go](https://github.com/jadolg/ShadowTest/actions/workflows/go.yml/badge.svg)](https://github.com/jadolg/ShadowTest/actions/workflows/go.yml)

## How to use

Using curl, call te test endpoint with a SIP002 compatible address:
Using curl, call the test endpoint with a Shadowsocks SIP002 address:
`curl -i localhost:8080/v2/test -d "address=ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpiYWRwYXNzd29yZA@localhost:6276/?outline=1"`

Or with a ShadowsocksR address:
`curl -i localhost:8080/v2/test -d "address=ssr://MTAuMC4wLjE6NDQzOm9yaWdpbjphZXMtMTI4LWNmYjpwbGFpbjpjR0Z6Y3pFeU13"`

#### Results

- 200: Everything went well and there's data for you in the https://wtfismyip.com/json format
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.26.1

require (
github.com/getsentry/sentry-go v0.43.0
github.com/metacubex/mihomo v1.19.21
github.com/prometheus/client_golang v1.23.2
github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/sirupsen/logrus v1.9.4
Expand All @@ -16,12 +17,16 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/metacubex/chacha v0.1.5 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/sing v0.5.7 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/sys v0.42.0 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/mihomo v1.19.21 h1:1VNlN124abEZQyRVc1u7vmFAxsm5p/u753EY2Gb/hac=
github.com/metacubex/mihomo v1.19.21/go.mod h1:6MgQnQfKgHAnZ/ThGfD2qNO5b9Q0i4oxiARYaVo2syI=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.5.7 h1:8OC+fhKFSv/l9ehEhJRaZZAOuthfZo68SteBVLe8QqM=
github.com/metacubex/sing v0.5.7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
Expand Down Expand Up @@ -48,6 +56,8 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4=
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
Expand Down
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
_\ \ | | | (_| | (_| | (_) \ V V / / / | __/\__ \ |_
\__/_| |_|\__,_|\__,_|\___/ \_/\_/ \/ \___||___/\__|
</pre>
<h4>A simple tool to test your Shadowsocks proxy</h4>
<h5>Enter your Shadowsocks URI below and hit the refresh button or press enter</h5>
<h4>A simple tool to test your Shadowsocks / ShadowsocksR proxy</h4>
<h5>Enter your Shadowsocks or ShadowsocksR URI below and hit the refresh button or press enter</h5>
</div>
<div class="row center-align">
<span class="input-field">
<input class="waves-effect" v-on:keyup.enter="testProxy" v-model="proxy" placeholder="ss://a key here" type="text"/>
<input class="waves-effect" v-on:keyup.enter="testProxy" v-model="proxy" placeholder="ss:// or ssr:// address" type="text"/>
</span>
<a v-on:click="testProxy" class="btn-large red">
<i class="material-icons">refresh</i>
Expand Down
17 changes: 13 additions & 4 deletions metrics.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package main

import (
"strings"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)

var (
testsTotal = promauto.NewCounter(prometheus.CounterOpts{
testsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "shadowtest_tests_total",
Help: "The total number of tests performed",
})
}, []string{"proxy_type"})

failuresTotal = promauto.NewCounter(prometheus.CounterOpts{
failuresTotal = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "shadowtest_failures_total",
Help: "The total number of failed tests",
})
}, []string{"proxy_type"})
)

func proxyTypeFromAddress(address string) string {
if strings.HasPrefix(address, "ssr://") {
return "ssr"
}
return "ss"
}
26 changes: 14 additions & 12 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"html"
"io"
"net"
"net/http"
"os"
Expand Down Expand Up @@ -77,10 +76,11 @@ func getRouter(ipv4Only bool) (*http.ServeMux, error) {
return
}

details, err := ssproxy.GetShadowsocksProxyDetails(address, ipv4Only, timeout)
testsTotal.Inc()
proxyType := proxyTypeFromAddress(address)
details, err := ssproxy.TestProxy(address, ipv4Only, timeout)
testsTotal.WithLabelValues(proxyType).Inc()
if err != nil {
failuresTotal.Inc()
failuresTotal.WithLabelValues(proxyType).Inc()
fillCheckError(w, err, address)
return
}
Expand Down Expand Up @@ -119,6 +119,11 @@ func getRouter(ipv4Only bool) (*http.ServeMux, error) {
}

func fillCheckError(w http.ResponseWriter, err error, address string) {
log.WithFields(log.Fields{
"address": address,
"error": err,
}).Warn("Proxy test failed")

message := fmt.Sprintf("Unable to get information for address %s", address)
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
Expand All @@ -136,15 +141,12 @@ func fillCheckError(w http.ResponseWriter, err error, address string) {
}

func closeBody(r *http.Request) {
func(Body io.ReadCloser) {
if Body != nil {
err := Body.Close()
if err != nil {
log.Errorf("impossible to close request body %v", err)
sentry.CaptureException(err)
}
if r.Body != nil {
if err := r.Body.Close(); err != nil {
log.Errorf("impossible to close request body %v", err)
sentry.CaptureException(err)
}
}(r.Body)
}
}

func getAddressAndTimeout(r *http.Request) (string, int, error) {
Expand Down
30 changes: 30 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"ShadowTest/ssproxy"
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand All @@ -12,6 +13,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestHealthcheck(t *testing.T) {
Expand Down Expand Up @@ -194,3 +196,31 @@ func TestTestInvalidTimeoutForm(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Equal(t, "unable to parse timeout\n", rr.Body.String())
}

func buildSSRURLMain(host, port, protocol, method, obfs, password string) string {
b64pass := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte(password))
inner := fmt.Sprintf("%s:%s:%s:%s:%s:%s", host, port, protocol, method, obfs, b64pass)
encoded := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte(inner))
return "ssr://" + encoded
}

func TestGetProxyDetailsFromServerJSON_SSR(t *testing.T) {
ssrURL := buildSSRURLMain("127.0.0.1", "16276", "origin", "aes-256-cfb", "plain", "testpassword")

router, err := getRouter(true)
require.NoError(t, err)

body := bytes.NewBuffer([]byte(fmt.Sprintf(`{"address":"%s","timeout":30}`, ssrURL)))
req, _ := http.NewRequest("POST", "/v2/test", body)
req.Header.Set("Content-Type", "application/json")

rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)

details := ssproxy.WTFIsMyIPData{}
err = json.NewDecoder(rr.Body).Decode(&details)
assert.NoError(t, err)
assert.NotEmpty(t, details.YourFuckingIPAddress)
}
Loading
Loading