mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-14 14:11:23 -04:00
Compare commits
No commits in common. "main" and "v2.11.0" have entirely different histories.
20 changed files with 523 additions and 738 deletions
|
@ -1,6 +1,3 @@
|
||||||
variables:
|
|
||||||
DOCKER_REGISTRY_URL: docker.io
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
|
@ -9,12 +6,9 @@ stages:
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
DEBIAN_OLD_STABLE: bullseye
|
DEBIAN_OLD_STABLE: buster
|
||||||
DEBIAN_STABLE: bookworm
|
DEBIAN_STABLE: bullseye
|
||||||
REPRODUCIBLE_FLAGS: -trimpath -ldflags=-buildid=
|
REPRODUCIBLE_FLAGS: -trimpath -ldflags=-buildid=
|
||||||
# Don't fail pulling images if dependency_proxy.yml is not included
|
|
||||||
DOCKER_REGISTRY_URL: "docker.io"
|
|
||||||
|
|
||||||
|
|
||||||
# set up apt for automated use
|
# set up apt for automated use
|
||||||
.apt-template: &apt-template
|
.apt-template: &apt-template
|
||||||
|
@ -98,7 +92,7 @@ variables:
|
||||||
# -- jobs ------------------------------------------------------------
|
# -- jobs ------------------------------------------------------------
|
||||||
|
|
||||||
android:
|
android:
|
||||||
image: ${DOCKER_REGISTRY_URL}/golang:1.24-$DEBIAN_OLD_STABLE
|
image: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:golang-1.23-$DEBIAN_STABLE
|
||||||
variables:
|
variables:
|
||||||
ANDROID_HOME: /usr/lib/android-sdk
|
ANDROID_HOME: /usr/lib/android-sdk
|
||||||
LANG: C.UTF-8
|
LANG: C.UTF-8
|
||||||
|
@ -148,39 +142,36 @@ android:
|
||||||
- go get golang.org/x/mobile/bind
|
- go get golang.org/x/mobile/bind
|
||||||
- gomobile bind -v -target=android $REPRODUCIBLE_FLAGS .
|
- gomobile bind -v -target=android $REPRODUCIBLE_FLAGS .
|
||||||
|
|
||||||
go-1.23:
|
go-1.21:
|
||||||
image: ${DOCKER_REGISTRY_URL}/golang:1.23-$DEBIAN_STABLE
|
image: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:golang-1.21-$DEBIAN_STABLE
|
||||||
<<: *golang-docker-debian-template
|
<<: *golang-docker-debian-template
|
||||||
<<: *test-template
|
<<: *test-template
|
||||||
script:
|
script:
|
||||||
- *go-test
|
- *go-test
|
||||||
|
|
||||||
go-1.24:
|
go-1.23:
|
||||||
image: ${DOCKER_REGISTRY_URL}/golang:1.24-$DEBIAN_STABLE
|
image: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:golang-1.23-$DEBIAN_STABLE
|
||||||
<<: *golang-docker-debian-template
|
<<: *golang-docker-debian-template
|
||||||
<<: *test-template
|
<<: *test-template
|
||||||
script:
|
script:
|
||||||
- *go-test
|
- *go-test
|
||||||
|
|
||||||
debian-testing:
|
debian-testing:
|
||||||
image: containers.torproject.org/tpo/tpa/base-images/debian:testing
|
image: debian:testing
|
||||||
<<: *debian-native-template
|
<<: *debian-native-template
|
||||||
<<: *test-template
|
<<: *test-template
|
||||||
script:
|
script:
|
||||||
- *go-test
|
- *go-test
|
||||||
|
|
||||||
shadow-integration:
|
shadow-integration:
|
||||||
image: ${DOCKER_REGISTRY_URL}/golang:1.23-$DEBIAN_STABLE
|
image: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:golang-1.21-$DEBIAN_STABLE
|
||||||
variables:
|
variables:
|
||||||
SHADOW_VERSION: "27d0bcf2cf1c7f0d403b6ad3efd575e45ae93126"
|
SHADOW_VERSION: "193924aae0dab30ffda0abe29467f552949849fa"
|
||||||
TGEN_VERSION: "v1.1.2"
|
TGEN_VERSION: "v1.1.2"
|
||||||
cache:
|
cache:
|
||||||
- key: sf-integration-shadow-$SHADOW_VERSION
|
key: sf-integration-$SHADOW_VERSION-$TGEN_VERSION
|
||||||
paths:
|
paths:
|
||||||
- opt/shadow
|
- /opt/
|
||||||
- key: sf-integration-tgen-$TGEN_VERSION
|
|
||||||
paths:
|
|
||||||
- opt/tgen
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- shadow.data.tar.gz
|
- shadow.data.tar.gz
|
||||||
|
@ -190,15 +181,15 @@ shadow-integration:
|
||||||
- tpa
|
- tpa
|
||||||
script:
|
script:
|
||||||
- apt-get update
|
- apt-get update
|
||||||
- apt-get install -y git tor libglib2.0-0 libigraph3
|
- apt-get install -y git tor
|
||||||
- mkdir -p ~/.local/bin
|
- mkdir -p ~/.local/bin
|
||||||
- mkdir -p ~/.local/src
|
- mkdir -p ~/.local/src
|
||||||
- export PATH=$PATH:$CI_PROJECT_DIR/opt/shadow/bin/:$CI_PROJECT_DIR/opt/tgen/bin/
|
- export PATH=$PATH:$CI_PROJECT_DIR/opt/bin/
|
||||||
|
|
||||||
# Install shadow and tgen
|
# Install shadow and tgen
|
||||||
- pushd ~/.local/src
|
- pushd ~/.local/src
|
||||||
- |
|
- |
|
||||||
if [ ! -f $CI_PROJECT_DIR/opt/shadow/bin/shadow ]
|
if [ ! -f opt/shadow/bin/shadow ]
|
||||||
then
|
then
|
||||||
echo "The required version of shadow was not cached, building from source"
|
echo "The required version of shadow was not cached, building from source"
|
||||||
git clone --shallow-since=2021-08-01 https://github.com/shadow/shadow.git
|
git clone --shallow-since=2021-08-01 https://github.com/shadow/shadow.git
|
||||||
|
@ -207,24 +198,24 @@ shadow-integration:
|
||||||
CONTAINER=debian:stable-slim ci/container_scripts/install_deps.sh
|
CONTAINER=debian:stable-slim ci/container_scripts/install_deps.sh
|
||||||
CC=gcc CONTAINER=debian:stable-slim ci/container_scripts/install_extra_deps.sh
|
CC=gcc CONTAINER=debian:stable-slim ci/container_scripts/install_extra_deps.sh
|
||||||
export PATH="$HOME/.cargo/bin:${PATH}"
|
export PATH="$HOME/.cargo/bin:${PATH}"
|
||||||
./setup build --jobs $(nproc) --prefix $CI_PROJECT_DIR/opt/shadow
|
./setup build --jobs $(nproc) --prefix $CI_PROJECT_DIR/opt/
|
||||||
./setup install
|
./setup install
|
||||||
popd
|
popd
|
||||||
fi
|
fi
|
||||||
- |
|
- |
|
||||||
if [ ! -f $CI_PROJECT_DIR/opt/tgen/bin/tgen ]
|
if [ ! -f opt/shadow/bin/tgen ]
|
||||||
then
|
then
|
||||||
echo "The required version of tgen was not cached, building from source"
|
echo "The required version of tgen was not cached, building from source"
|
||||||
git clone --branch $TGEN_VERSION --depth 1 https://github.com/shadow/tgen.git
|
git clone --branch $TGEN_VERSION --depth 1 https://github.com/shadow/tgen.git
|
||||||
pushd tgen/
|
pushd tgen/
|
||||||
apt-get install -y cmake libglib2.0-dev libigraph-dev
|
apt-get install -y cmake libglib2.0-dev libigraph-dev
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -DCMAKE_INSTALL_PREFIX=$CI_PROJECT_DIR/opt/tgen
|
cmake .. -DCMAKE_INSTALL_PREFIX=$CI_PROJECT_DIR/opt/
|
||||||
make
|
make
|
||||||
make install
|
make install
|
||||||
popd
|
popd
|
||||||
fi
|
fi
|
||||||
install $CI_PROJECT_DIR/opt/tgen/bin/tgen ~/.local/bin/tgen
|
install $CI_PROJECT_DIR/opt/bin/tgen ~/.local/bin/tgen
|
||||||
- popd
|
- popd
|
||||||
|
|
||||||
# Apply snowflake patch(es)
|
# Apply snowflake patch(es)
|
||||||
|
@ -262,7 +253,7 @@ shadow-integration:
|
||||||
|
|
||||||
generate_tarball:
|
generate_tarball:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: ${DOCKER_REGISTRY_URL}/golang:1.22-$DEBIAN_STABLE
|
image: golang:1.21-$DEBIAN_STABLE
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG
|
- if: $CI_COMMIT_TAG
|
||||||
script:
|
script:
|
||||||
|
@ -307,6 +298,7 @@ build-container:
|
||||||
matrix:
|
matrix:
|
||||||
- ARCH: amd64
|
- ARCH: amd64
|
||||||
- ARCH: arm64
|
- ARCH: arm64
|
||||||
|
- ARCH: s390x
|
||||||
tags:
|
tags:
|
||||||
- $ARCH
|
- $ARCH
|
||||||
image:
|
image:
|
||||||
|
@ -331,7 +323,7 @@ merge-manifests:
|
||||||
- job: build-container
|
- job: build-container
|
||||||
artifacts: false
|
artifacts: false
|
||||||
image:
|
image:
|
||||||
name: ${DOCKER_REGISTRY_URL}/mplatform/manifest-tool:alpine
|
name: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:mplatform-manifest-tool-alpine
|
||||||
entrypoint: [""]
|
entrypoint: [""]
|
||||||
script:
|
script:
|
||||||
- if [ $CI_COMMIT_REF_NAME == "main" ]; then export TAG='nightly'; fi
|
- if [ $CI_COMMIT_REF_NAME == "main" ]; then export TAG='nightly'; fi
|
||||||
|
@ -340,7 +332,7 @@ merge-manifests:
|
||||||
--username="${CI_REGISTRY_USER}"
|
--username="${CI_REGISTRY_USER}"
|
||||||
--password="${CI_REGISTRY_PASSWORD}"
|
--password="${CI_REGISTRY_PASSWORD}"
|
||||||
push from-args
|
push from-args
|
||||||
--platforms linux/amd64,linux/arm64
|
--platforms linux/amd64,linux/arm64,linux/s390x
|
||||||
--template "${CI_REGISTRY_IMAGE}:${TAG}_ARCH"
|
--template "${CI_REGISTRY_IMAGE}:${TAG}_ARCH"
|
||||||
--target "${CI_REGISTRY_IMAGE}:${TAG}"
|
--target "${CI_REGISTRY_IMAGE}:${TAG}"
|
||||||
rules:
|
rules:
|
||||||
|
|
51
Dockerfile
51
Dockerfile
|
@ -1,5 +1,15 @@
|
||||||
FROM docker.io/library/golang:latest AS build
|
FROM docker.io/library/golang:1.23-bookworm AS build
|
||||||
|
|
||||||
|
# Set some labels
|
||||||
|
# io.containers.autoupdate label will instruct podman to reach out to the corres
|
||||||
|
# corresponding registry to check if the image has been updated. If an image
|
||||||
|
# must be updated, Podman pulls it down and restarts the systemd unit executing
|
||||||
|
# the container. See podman-auto-update(1) for more details, or
|
||||||
|
# https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html
|
||||||
|
LABEL io.containers.autoupdate=registry
|
||||||
|
LABEL org.opencontainers.image.authors="anti-censorship-team@lists.torproject.org"
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y tor-geoipdb
|
||||||
|
|
||||||
ADD . /app
|
ADD . /app
|
||||||
|
|
||||||
|
@ -7,44 +17,11 @@ WORKDIR /app/proxy
|
||||||
RUN go get
|
RUN go get
|
||||||
RUN CGO_ENABLED=0 go build -o proxy -ldflags '-extldflags "-static" -w -s' .
|
RUN CGO_ENABLED=0 go build -o proxy -ldflags '-extldflags "-static" -w -s' .
|
||||||
|
|
||||||
FROM containers.torproject.org/tpo/tpa/base-images/debian:bookworm as debian-base
|
|
||||||
|
|
||||||
# Install dependencies to add Tor's repository.
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
gpg \
|
|
||||||
gpg-agent \
|
|
||||||
ca-certificates \
|
|
||||||
libcap2-bin \
|
|
||||||
--no-install-recommends
|
|
||||||
|
|
||||||
# See: <https://2019.www.torproject.org/docs/debian.html.en>
|
|
||||||
RUN curl https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --import
|
|
||||||
RUN gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add -
|
|
||||||
|
|
||||||
RUN printf "deb https://deb.torproject.org/torproject.org bookworm main\n" >> /etc/apt/sources.list.d/tor.list
|
|
||||||
|
|
||||||
# Install remaining dependencies.
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
tor \
|
|
||||||
tor-geoipdb \
|
|
||||||
--no-install-recommends
|
|
||||||
|
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
|
|
||||||
COPY --from=debian-base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
COPY --from=debian-base /usr/share/zoneinfo /usr/share/zoneinfo
|
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
COPY --from=debian-base /usr/share/tor/geoip* /usr/share/tor/
|
COPY --from=build /usr/share/tor/geoip* /usr/share/tor/
|
||||||
COPY --from=build /app/proxy/proxy /bin/proxy
|
COPY --from=build /app/proxy/proxy /bin/proxy
|
||||||
|
|
||||||
ENTRYPOINT [ "/bin/proxy" ]
|
ENTRYPOINT [ "/bin/proxy" ]
|
||||||
|
|
||||||
# Set some labels
|
|
||||||
# io.containers.autoupdate label will instruct podman to reach out to the
|
|
||||||
# corresponding registry to check if the image has been updated. If an image
|
|
||||||
# must be updated, Podman pulls it down and restarts the systemd unit executing
|
|
||||||
# the container. See podman-auto-update(1) for more details, or
|
|
||||||
# https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html
|
|
||||||
LABEL io.containers.autoupdate=registry
|
|
||||||
LABEL org.opencontainers.image.authors="anti-censorship-team@lists.torproject.org"
|
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/amp"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/amp"
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
||||||
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ampClientOffers is the AMP-speaking endpoint for client poll messages,
|
// ampClientOffers is the AMP-speaking endpoint for client poll messages,
|
||||||
|
@ -17,12 +16,9 @@ import (
|
||||||
// HTTP request body (because an AMP cache does not support POST), and the
|
// HTTP request body (because an AMP cache does not support POST), and the
|
||||||
// encoded client poll response is sent back as AMP-armored HTML.
|
// encoded client poll response is sent back as AMP-armored HTML.
|
||||||
func ampClientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
func ampClientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), ClientTimeout*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// The encoded client poll message immediately follows the /amp/client/
|
// The encoded client poll message immediately follows the /amp/client/
|
||||||
// path prefix, so this function unfortunately needs to be aware of and
|
// path prefix, so this function unfortunately needs to be aware of and
|
||||||
// remove its own routing prefix.
|
// remote its own routing prefix.
|
||||||
path := strings.TrimPrefix(r.URL.Path, "/amp/client/")
|
path := strings.TrimPrefix(r.URL.Path, "/amp/client/")
|
||||||
if path == r.URL.Path {
|
if path == r.URL.Path {
|
||||||
// The path didn't start with the expected prefix. This probably
|
// The path didn't start with the expected prefix. This probably
|
||||||
|
@ -40,9 +36,8 @@ func ampClientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
arg := messages.Arg{
|
arg := messages.Arg{
|
||||||
Body: encPollReq,
|
Body: encPollReq,
|
||||||
RemoteAddr: "",
|
RemoteAddr: util.GetClientIp(r),
|
||||||
RendezvousMethod: messages.RendezvousAmpCache,
|
RendezvousMethod: messages.RendezvousAmpCache,
|
||||||
Context: ctx,
|
|
||||||
}
|
}
|
||||||
err = i.ClientOffers(arg, &response)
|
err = i.ClientOffers(arg, &response)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -46,6 +46,7 @@ type BrokerContext struct {
|
||||||
|
|
||||||
bridgeList BridgeListHolderFileBased
|
bridgeList BridgeListHolderFileBased
|
||||||
allowedRelayPattern string
|
allowedRelayPattern string
|
||||||
|
presumedPatternForLegacyClient string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *BrokerContext) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprint) (BridgeInfo, error) {
|
func (ctx *BrokerContext) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprint) (BridgeInfo, error) {
|
||||||
|
@ -54,7 +55,8 @@ func (ctx *BrokerContext) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprin
|
||||||
|
|
||||||
func NewBrokerContext(
|
func NewBrokerContext(
|
||||||
metricsLogger *log.Logger,
|
metricsLogger *log.Logger,
|
||||||
allowedRelayPattern string,
|
allowedRelayPattern,
|
||||||
|
presumedPatternForLegacyClient string,
|
||||||
) *BrokerContext {
|
) *BrokerContext {
|
||||||
snowflakes := new(SnowflakeHeap)
|
snowflakes := new(SnowflakeHeap)
|
||||||
heap.Init(snowflakes)
|
heap.Init(snowflakes)
|
||||||
|
@ -84,6 +86,7 @@ func NewBrokerContext(
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
bridgeList: bridgeListHolder,
|
bridgeList: bridgeListHolder,
|
||||||
allowedRelayPattern: allowedRelayPattern,
|
allowedRelayPattern: allowedRelayPattern,
|
||||||
|
presumedPatternForLegacyClient: presumedPatternForLegacyClient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +176,7 @@ func (ctx *BrokerContext) InstallBridgeListProfile(reader io.Reader) error {
|
||||||
|
|
||||||
func (ctx *BrokerContext) CheckProxyRelayPattern(pattern string, nonSupported bool) bool {
|
func (ctx *BrokerContext) CheckProxyRelayPattern(pattern string, nonSupported bool) bool {
|
||||||
if nonSupported {
|
if nonSupported {
|
||||||
return false
|
pattern = ctx.presumedPatternForLegacyClient
|
||||||
}
|
}
|
||||||
proxyPattern := namematcher.NewNameMatcher(pattern)
|
proxyPattern := namematcher.NewNameMatcher(pattern)
|
||||||
brokerPattern := namematcher.NewNameMatcher(ctx.allowedRelayPattern)
|
brokerPattern := namematcher.NewNameMatcher(ctx.allowedRelayPattern)
|
||||||
|
@ -194,7 +197,7 @@ func main() {
|
||||||
var addr string
|
var addr string
|
||||||
var geoipDatabase string
|
var geoipDatabase string
|
||||||
var geoip6Database string
|
var geoip6Database string
|
||||||
var bridgeListFilePath, allowedRelayPattern string
|
var bridgeListFilePath, allowedRelayPattern, presumedPatternForLegacyClient string
|
||||||
var brokerSQSQueueName, brokerSQSQueueRegion string
|
var brokerSQSQueueName, brokerSQSQueueRegion string
|
||||||
var disableTLS bool
|
var disableTLS bool
|
||||||
var certFilename, keyFilename string
|
var certFilename, keyFilename string
|
||||||
|
@ -212,6 +215,7 @@ func main() {
|
||||||
flag.StringVar(&geoip6Database, "geoip6db", "/usr/share/tor/geoip6", "path to correctly formatted geoip database mapping IPv6 address ranges to country codes")
|
flag.StringVar(&geoip6Database, "geoip6db", "/usr/share/tor/geoip6", "path to correctly formatted geoip database mapping IPv6 address ranges to country codes")
|
||||||
flag.StringVar(&bridgeListFilePath, "bridge-list-path", "", "file path for bridgeListFile")
|
flag.StringVar(&bridgeListFilePath, "bridge-list-path", "", "file path for bridgeListFile")
|
||||||
flag.StringVar(&allowedRelayPattern, "allowed-relay-pattern", "", "allowed pattern for relay host name. The broker will reject proxies whose AcceptedRelayPattern is more restrictive than this")
|
flag.StringVar(&allowedRelayPattern, "allowed-relay-pattern", "", "allowed pattern for relay host name. The broker will reject proxies whose AcceptedRelayPattern is more restrictive than this")
|
||||||
|
flag.StringVar(&presumedPatternForLegacyClient, "default-relay-pattern", "", "presumed pattern for legacy client")
|
||||||
flag.StringVar(&brokerSQSQueueName, "broker-sqs-name", "", "name of broker SQS queue to listen for incoming messages on")
|
flag.StringVar(&brokerSQSQueueName, "broker-sqs-name", "", "name of broker SQS queue to listen for incoming messages on")
|
||||||
flag.StringVar(&brokerSQSQueueRegion, "broker-sqs-region", "", "name of AWS region of broker SQS queue")
|
flag.StringVar(&brokerSQSQueueRegion, "broker-sqs-region", "", "name of AWS region of broker SQS queue")
|
||||||
flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS")
|
flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS")
|
||||||
|
@ -244,7 +248,7 @@ func main() {
|
||||||
|
|
||||||
metricsLogger := log.New(metricsFile, "", 0)
|
metricsLogger := log.New(metricsFile, "", 0)
|
||||||
|
|
||||||
ctx := NewBrokerContext(metricsLogger, allowedRelayPattern)
|
ctx := NewBrokerContext(metricsLogger, allowedRelayPattern, presumedPatternForLegacyClient)
|
||||||
|
|
||||||
if bridgeListFilePath != "" {
|
if bridgeListFilePath != "" {
|
||||||
bridgeListFile, err := os.Open(bridgeListFilePath)
|
bridgeListFile, err := os.Open(bridgeListFilePath)
|
||||||
|
|
|
@ -2,14 +2,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
|
||||||
|
@ -134,9 +132,6 @@ snowflake proxy, which responds with the SDP answer to be sent in
|
||||||
the HTTP response back to the client.
|
the HTTP response back to the client.
|
||||||
*/
|
*/
|
||||||
func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), ClientTimeout*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, readLimit))
|
body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, readLimit))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading client request: %s", err.Error())
|
log.Printf("Error reading client request: %s", err.Error())
|
||||||
|
@ -168,7 +163,6 @@ func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
Body: body,
|
Body: body,
|
||||||
RemoteAddr: util.GetClientIp(r),
|
RemoteAddr: util.GetClientIp(r),
|
||||||
RendezvousMethod: messages.RendezvousHttp,
|
RendezvousMethod: messages.RendezvousHttp,
|
||||||
Context: ctx,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var response []byte
|
var response []byte
|
||||||
|
@ -220,6 +214,7 @@ func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
err = validateSDP(body)
|
err = validateSDP(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("Error proxy SDP: ", err.Error())
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/bridgefingerprint"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/bridgefingerprint"
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/constants"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/constants"
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
||||||
|
@ -74,17 +74,24 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !relayPatternSupported {
|
if !relayPatternSupported {
|
||||||
i.ctx.metrics.IncrementCounter("proxy-poll-without-relay-url")
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.proxyPollWithoutRelayURLExtension++
|
||||||
i.ctx.metrics.promMetrics.ProxyPollWithoutRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc()
|
i.ctx.metrics.promMetrics.ProxyPollWithoutRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc()
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
i.ctx.metrics.IncrementCounter("proxy-poll-with-relay-url")
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.proxyPollWithRelayURLExtension++
|
||||||
i.ctx.metrics.promMetrics.ProxyPollWithRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc()
|
i.ctx.metrics.promMetrics.ProxyPollWithRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc()
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !i.ctx.CheckProxyRelayPattern(relayPattern, !relayPatternSupported) {
|
if !i.ctx.CheckProxyRelayPattern(relayPattern, !relayPatternSupported) {
|
||||||
i.ctx.metrics.IncrementCounter("proxy-poll-rejected-relay-url")
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.proxyPollRejectedWithRelayURLExtension++
|
||||||
i.ctx.metrics.promMetrics.ProxyPollRejectedForRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc()
|
i.ctx.metrics.promMetrics.ProxyPollRejectedForRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc()
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
|
|
||||||
|
log.Printf("bad request: rejected relay pattern from proxy = %v", messages.ErrBadRequest)
|
||||||
b, err := messages.EncodePollResponseWithRelayURL("", false, "", "", "incorrect relay pattern")
|
b, err := messages.EncodePollResponseWithRelayURL("", false, "", "", "incorrect relay pattern")
|
||||||
*response = b
|
*response = b
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -98,7 +105,9 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Warning: cannot process proxy IP: ", err.Error())
|
log.Println("Warning: cannot process proxy IP: ", err.Error())
|
||||||
} else {
|
} else {
|
||||||
i.ctx.metrics.UpdateProxyStats(remoteIP, proxyType, natType)
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.UpdateCountryStats(remoteIP, proxyType, natType)
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
var b []byte
|
var b []byte
|
||||||
|
@ -107,8 +116,10 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error {
|
||||||
offer := i.ctx.RequestOffer(sid, proxyType, natType, clients)
|
offer := i.ctx.RequestOffer(sid, proxyType, natType, clients)
|
||||||
|
|
||||||
if offer == nil {
|
if offer == nil {
|
||||||
i.ctx.metrics.IncrementCounter("proxy-idle")
|
i.ctx.metrics.lock.Lock()
|
||||||
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "type": proxyType, "status": "idle"}).Inc()
|
i.ctx.metrics.proxyIdleCount++
|
||||||
|
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "status": "idle"}).Inc()
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
|
|
||||||
b, err = messages.EncodePollResponse("", false, "")
|
b, err = messages.EncodePollResponse("", false, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -119,7 +130,7 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "type": proxyType, "status": "matched"}).Inc()
|
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "status": "matched"}).Inc()
|
||||||
var relayURL string
|
var relayURL string
|
||||||
bridgeFingerprint, err := bridgefingerprint.FingerprintFromBytes(offer.fingerprint)
|
bridgeFingerprint, err := bridgefingerprint.FingerprintFromBytes(offer.fingerprint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -152,24 +163,13 @@ func sendClientResponse(resp *messages.ClientPollResponse, response *[]byte) err
|
||||||
|
|
||||||
func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
|
func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
req, err := messages.DecodeClientPollRequest(arg.Body)
|
req, err := messages.DecodeClientPollRequest(arg.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response)
|
return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we couldn't extract the remote IP from the rendezvous method
|
|
||||||
// pull it from the offer SDP
|
|
||||||
remoteAddr := arg.RemoteAddr
|
|
||||||
if remoteAddr == "" {
|
|
||||||
sdp, err := util.DeserializeSessionDescription(req.Offer)
|
|
||||||
if err == nil {
|
|
||||||
candidateAddrs := util.GetCandidateAddrs(sdp.SDP)
|
|
||||||
if len(candidateAddrs) > 0 {
|
|
||||||
remoteAddr = candidateAddrs[0].String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offer := &ClientOffer{
|
offer := &ClientOffer{
|
||||||
natType: req.NAT,
|
natType: req.NAT,
|
||||||
sdp: []byte(req.Offer),
|
sdp: []byte(req.Offer),
|
||||||
|
@ -198,7 +198,9 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
|
||||||
if snowflake != nil {
|
if snowflake != nil {
|
||||||
snowflake.offerChannel <- offer
|
snowflake.offerChannel <- offer
|
||||||
} else {
|
} else {
|
||||||
i.ctx.metrics.UpdateClientStats(remoteAddr, arg.RendezvousMethod, offer.natType, "denied")
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.UpdateRendezvousStats(arg.RemoteAddr, arg.RendezvousMethod, offer.natType, "denied")
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
resp := &messages.ClientPollResponse{Error: messages.StrNoProxies}
|
resp := &messages.ClientPollResponse{Error: messages.StrNoProxies}
|
||||||
return sendClientResponse(resp, response)
|
return sendClientResponse(resp, response)
|
||||||
}
|
}
|
||||||
|
@ -206,11 +208,19 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
|
||||||
// Wait for the answer to be returned on the channel or timeout.
|
// Wait for the answer to be returned on the channel or timeout.
|
||||||
select {
|
select {
|
||||||
case answer := <-snowflake.answerChannel:
|
case answer := <-snowflake.answerChannel:
|
||||||
i.ctx.metrics.UpdateClientStats(remoteAddr, arg.RendezvousMethod, offer.natType, "matched")
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.UpdateRendezvousStats(arg.RemoteAddr, arg.RendezvousMethod, offer.natType, "matched")
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
resp := &messages.ClientPollResponse{Answer: answer}
|
resp := &messages.ClientPollResponse{Answer: answer}
|
||||||
err = sendClientResponse(resp, response)
|
err = sendClientResponse(resp, response)
|
||||||
case <-arg.Context.Done():
|
// Initial tracking of elapsed time.
|
||||||
i.ctx.metrics.UpdateClientStats(remoteAddr, arg.RendezvousMethod, offer.natType, "timeout")
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.clientRoundtripEstimate = time.Since(startTime) / time.Millisecond
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
|
case <-time.After(time.Second * ClientTimeout):
|
||||||
|
i.ctx.metrics.lock.Lock()
|
||||||
|
i.ctx.metrics.UpdateRendezvousStats(arg.RemoteAddr, arg.RendezvousMethod, offer.natType, "timeout")
|
||||||
|
i.ctx.metrics.lock.Unlock()
|
||||||
resp := &messages.ClientPollResponse{Error: messages.StrTimedOut}
|
resp := &messages.ClientPollResponse{Error: messages.StrTimedOut}
|
||||||
err = sendClientResponse(resp, response)
|
err = sendClientResponse(resp, response)
|
||||||
}
|
}
|
||||||
|
@ -253,7 +263,6 @@ func (i *IPC) ProxyAnswers(arg messages.Arg, response *[]byte) error {
|
||||||
// The snowflake took too long to respond with an answer, so its client
|
// The snowflake took too long to respond with an answer, so its client
|
||||||
// disappeared / the snowflake is no longer recognized by the Broker.
|
// disappeared / the snowflake is no longer recognized by the Broker.
|
||||||
success = false
|
success = false
|
||||||
i.ctx.metrics.promMetrics.ProxyAnswerTotal.With(prometheus.Labels{"type": "", "status": "timeout"}).Inc()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := messages.EncodeAnswerResponse(success)
|
b, err := messages.EncodeAnswerResponse(success)
|
||||||
|
@ -264,7 +273,6 @@ func (i *IPC) ProxyAnswers(arg messages.Arg, response *[]byte) error {
|
||||||
*response = b
|
*response = b
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
i.ctx.metrics.promMetrics.ProxyAnswerTotal.With(prometheus.Labels{"type": snowflake.proxyType, "status": "success"}).Inc()
|
|
||||||
snowflake.answerChannel <- answer
|
snowflake.answerChannel <- answer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,10 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
@ -26,105 +25,130 @@ const (
|
||||||
metricsResolution = 60 * 60 * 24 * time.Second //86400 seconds
|
metricsResolution = 60 * 60 * 24 * time.Second //86400 seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var rendezvoudMethodList = [...]messages.RendezvousMethod{
|
||||||
|
messages.RendezvousHttp,
|
||||||
|
messages.RendezvousAmpCache,
|
||||||
|
messages.RendezvousSqs,
|
||||||
|
}
|
||||||
|
|
||||||
|
type CountryStats struct {
|
||||||
|
// map[proxyType][address]bool
|
||||||
|
proxies map[string]map[string]bool
|
||||||
|
unknown map[string]bool
|
||||||
|
|
||||||
|
natRestricted map[string]bool
|
||||||
|
natUnrestricted map[string]bool
|
||||||
|
natUnknown map[string]bool
|
||||||
|
|
||||||
|
counts map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Observable
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
geoipdb *geoip.Geoip
|
geoipdb *geoip.Geoip
|
||||||
|
|
||||||
ips *sync.Map // proxy IP addresses we've seen before
|
countryStats CountryStats
|
||||||
counters *sync.Map // counters for ip-based metrics
|
clientRoundtripEstimate time.Duration
|
||||||
|
proxyIdleCount uint
|
||||||
|
clientDeniedCount map[messages.RendezvousMethod]uint
|
||||||
|
clientRestrictedDeniedCount map[messages.RendezvousMethod]uint
|
||||||
|
clientUnrestrictedDeniedCount map[messages.RendezvousMethod]uint
|
||||||
|
clientProxyMatchCount map[messages.RendezvousMethod]uint
|
||||||
|
clientProxyTimeoutCount map[messages.RendezvousMethod]uint
|
||||||
|
|
||||||
// counters for country-based metrics
|
rendezvousCountryStats map[messages.RendezvousMethod]map[string]int
|
||||||
proxies *sync.Map // ip-based counts of proxy country codes
|
|
||||||
clientHTTPPolls *sync.Map // poll-based counts of client HTTP rendezvous
|
proxyPollWithRelayURLExtension uint
|
||||||
clientAMPPolls *sync.Map // poll-based counts of client AMP cache rendezvous
|
proxyPollWithoutRelayURLExtension uint
|
||||||
clientSQSPolls *sync.Map // poll-based counts of client SQS rendezvous
|
proxyPollRejectedWithRelayURLExtension uint
|
||||||
|
|
||||||
|
// synchronization for access to snowflake metrics
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
promMetrics *PromMetrics
|
promMetrics *PromMetrics
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMetrics(metricsLogger *log.Logger) (*Metrics, error) {
|
type record struct {
|
||||||
m := new(Metrics)
|
cc string
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
type records []record
|
||||||
|
|
||||||
m.logger = metricsLogger
|
func (r records) Len() int { return len(r) }
|
||||||
m.promMetrics = initPrometheus()
|
func (r records) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||||
m.ips = new(sync.Map)
|
func (r records) Less(i, j int) bool {
|
||||||
m.counters = new(sync.Map)
|
if r[i].count == r[j].count {
|
||||||
m.proxies = new(sync.Map)
|
return r[i].cc > r[j].cc
|
||||||
m.clientHTTPPolls = new(sync.Map)
|
}
|
||||||
m.clientAMPPolls = new(sync.Map)
|
return r[i].count < r[j].count
|
||||||
m.clientSQSPolls = new(sync.Map)
|
|
||||||
|
|
||||||
// Write to log file every day with updated metrics
|
|
||||||
go m.logMetrics()
|
|
||||||
|
|
||||||
return m, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func incrementMapCounter(counters *sync.Map, key string) {
|
func (s CountryStats) Display() string {
|
||||||
start := uint64(1)
|
output := ""
|
||||||
val, loaded := counters.LoadOrStore(key, &start)
|
|
||||||
if loaded {
|
// Use the records struct to sort our counts map by value.
|
||||||
ptr := val.(*uint64)
|
rs := records{}
|
||||||
atomic.AddUint64(ptr, 1)
|
for cc, count := range s.counts {
|
||||||
|
rs = append(rs, record{cc: cc, count: count})
|
||||||
}
|
}
|
||||||
|
sort.Sort(sort.Reverse(rs))
|
||||||
|
for _, r := range rs {
|
||||||
|
output += fmt.Sprintf("%s=%d,", r.cc, r.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) IncrementCounter(key string) {
|
// cut off trailing ","
|
||||||
incrementMapCounter(m.counters, key)
|
if len(output) > 0 {
|
||||||
|
return output[:len(output)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) UpdateProxyStats(addr string, proxyType string, natType string) {
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) UpdateCountryStats(addr string, proxyType string, natType string) {
|
||||||
|
var country string
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
addresses, ok := m.countryStats.proxies[proxyType]
|
||||||
|
if !ok {
|
||||||
|
if m.countryStats.unknown[addr] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.countryStats.unknown[addr] = true
|
||||||
|
} else {
|
||||||
|
if addresses[addr] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addresses[addr] = true
|
||||||
|
}
|
||||||
|
|
||||||
// perform geolocation of IP address
|
|
||||||
ip := net.ParseIP(addr)
|
ip := net.ParseIP(addr)
|
||||||
if m.geoipdb == nil {
|
if m.geoipdb == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
country, ok := m.geoipdb.GetCountryByAddr(ip)
|
country, ok = m.geoipdb.GetCountryByAddr(ip)
|
||||||
if !ok {
|
if !ok {
|
||||||
country = "??"
|
country = "??"
|
||||||
}
|
}
|
||||||
|
m.countryStats.counts[country]++
|
||||||
|
|
||||||
// check whether we've seen this proxy ip before
|
|
||||||
if _, loaded := m.ips.LoadOrStore(addr, true); !loaded {
|
|
||||||
m.IncrementCounter("proxy-total")
|
|
||||||
incrementMapCounter(m.proxies, country)
|
|
||||||
m.promMetrics.ProxyTotal.With(prometheus.Labels{
|
m.promMetrics.ProxyTotal.With(prometheus.Labels{
|
||||||
"nat": natType,
|
"nat": natType,
|
||||||
"type": proxyType,
|
"type": proxyType,
|
||||||
"cc": country,
|
"cc": country,
|
||||||
}).Inc()
|
}).Inc()
|
||||||
}
|
|
||||||
|
|
||||||
// update unique IP proxy NAT metrics
|
|
||||||
key := fmt.Sprintf("%s-%s", addr, natType)
|
|
||||||
if _, loaded := m.ips.LoadOrStore(key, true); !loaded {
|
|
||||||
switch natType {
|
switch natType {
|
||||||
case NATRestricted:
|
case NATRestricted:
|
||||||
m.IncrementCounter("proxy-nat-restricted")
|
m.countryStats.natRestricted[addr] = true
|
||||||
case NATUnrestricted:
|
case NATUnrestricted:
|
||||||
m.IncrementCounter("proxy-nat-unrestricted")
|
m.countryStats.natUnrestricted[addr] = true
|
||||||
default:
|
default:
|
||||||
m.IncrementCounter("proxy-nat-unknown")
|
m.countryStats.natUnknown[addr] = true
|
||||||
}
|
|
||||||
}
|
|
||||||
// update unique IP proxy type metrics
|
|
||||||
key = fmt.Sprintf("%s-%s", addr, proxyType)
|
|
||||||
if _, loaded := m.ips.LoadOrStore(key, true); !loaded {
|
|
||||||
switch proxyType {
|
|
||||||
case "standalone":
|
|
||||||
m.IncrementCounter("proxy-standalone")
|
|
||||||
case "badge":
|
|
||||||
m.IncrementCounter("proxy-badge")
|
|
||||||
case "iptproxy":
|
|
||||||
m.IncrementCounter("proxy-iptproxy")
|
|
||||||
case "webext":
|
|
||||||
m.IncrementCounter("proxy-webext")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) UpdateClientStats(addr string, rendezvousMethod messages.RendezvousMethod, natType, status string) {
|
func (m *Metrics) UpdateRendezvousStats(addr string, rendezvousMethod messages.RendezvousMethod, natType, status string) {
|
||||||
ip := net.ParseIP(addr)
|
ip := net.ParseIP(addr)
|
||||||
country := "??"
|
country := "??"
|
||||||
if m.geoipdb != nil {
|
if m.geoipdb != nil {
|
||||||
|
@ -136,31 +160,20 @@ func (m *Metrics) UpdateClientStats(addr string, rendezvousMethod messages.Rende
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
case "denied":
|
case "denied":
|
||||||
m.IncrementCounter("client-denied")
|
m.clientDeniedCount[rendezvousMethod]++
|
||||||
if natType == NATUnrestricted {
|
if natType == NATUnrestricted {
|
||||||
m.IncrementCounter("client-unrestricted-denied")
|
m.clientUnrestrictedDeniedCount[rendezvousMethod]++
|
||||||
} else {
|
} else {
|
||||||
m.IncrementCounter("client-restricted-denied")
|
m.clientRestrictedDeniedCount[rendezvousMethod]++
|
||||||
}
|
}
|
||||||
case "matched":
|
case "matched":
|
||||||
m.IncrementCounter("client-match")
|
m.clientProxyMatchCount[rendezvousMethod]++
|
||||||
case "timeout":
|
case "timeout":
|
||||||
m.IncrementCounter("client-timeout")
|
m.clientProxyTimeoutCount[rendezvousMethod]++
|
||||||
default:
|
default:
|
||||||
log.Printf("Unknown rendezvous status: %s", status)
|
log.Printf("Unknown rendezvous status: %s", status)
|
||||||
}
|
}
|
||||||
|
m.rendezvousCountryStats[rendezvousMethod][country]++
|
||||||
switch rendezvousMethod {
|
|
||||||
case messages.RendezvousHttp:
|
|
||||||
m.IncrementCounter("client-http")
|
|
||||||
incrementMapCounter(m.clientHTTPPolls, country)
|
|
||||||
case messages.RendezvousAmpCache:
|
|
||||||
m.IncrementCounter("client-amp")
|
|
||||||
incrementMapCounter(m.clientAMPPolls, country)
|
|
||||||
case messages.RendezvousSqs:
|
|
||||||
m.IncrementCounter("client-sqs")
|
|
||||||
incrementMapCounter(m.clientSQSPolls, country)
|
|
||||||
}
|
|
||||||
m.promMetrics.ClientPollTotal.With(prometheus.Labels{
|
m.promMetrics.ClientPollTotal.With(prometheus.Labels{
|
||||||
"nat": natType,
|
"nat": natType,
|
||||||
"status": status,
|
"status": status,
|
||||||
|
@ -169,51 +182,25 @@ func (m *Metrics) UpdateClientStats(addr string, rendezvousMethod messages.Rende
|
||||||
}).Inc()
|
}).Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Types to facilitate sorting in formatAndClearCountryStats.
|
func (m *Metrics) DisplayRendezvousStatsByCountry(rendezvoudMethod messages.RendezvousMethod) string {
|
||||||
type record struct {
|
output := ""
|
||||||
cc string
|
|
||||||
count uint64
|
|
||||||
}
|
|
||||||
type records []record
|
|
||||||
|
|
||||||
// Implementation of sort.Interface for records. The ordering is lexicographic:
|
// Use the records struct to sort our counts map by value.
|
||||||
// first by count (descending), then by cc (ascending).
|
|
||||||
func (r records) Len() int { return len(r) }
|
|
||||||
func (r records) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
|
||||||
func (r records) Less(i, j int) bool {
|
|
||||||
return r[i].count > r[j].count || (r[i].count == r[j].count && r[i].cc < r[j].cc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatAndClearCountryStats takes a map from country codes to counts, and
|
|
||||||
// returns a formatted string of comma-separated CC=COUNT. Entries are sorted by
|
|
||||||
// count from largest to smallest. When counts are equal, entries are sorted by
|
|
||||||
// country code in ascending order.
|
|
||||||
//
|
|
||||||
// formatAndClearCountryStats has the side effect of deleting all entries in m.
|
|
||||||
func formatAndClearCountryStats(m *sync.Map, binned bool) string {
|
|
||||||
// Extract entries from the map into a slice of records, binning counts
|
|
||||||
// if asked to.
|
|
||||||
rs := records{}
|
rs := records{}
|
||||||
m.Range(func(cc, countPtr any) bool {
|
for cc, count := range m.rendezvousCountryStats[rendezvoudMethod] {
|
||||||
count := *countPtr.(*uint64)
|
rs = append(rs, record{cc: cc, count: count})
|
||||||
if binned {
|
|
||||||
count = binCount(count)
|
|
||||||
}
|
}
|
||||||
rs = append(rs, record{cc: cc.(string), count: count})
|
sort.Sort(sort.Reverse(rs))
|
||||||
m.Delete(cc)
|
for _, r := range rs {
|
||||||
return true
|
output += fmt.Sprintf("%s=%d,", r.cc, binCount(uint(r.count)))
|
||||||
})
|
|
||||||
// Sort the records.
|
|
||||||
sort.Sort(rs)
|
|
||||||
// Format and concatenate.
|
|
||||||
var output strings.Builder
|
|
||||||
for i, r := range rs {
|
|
||||||
if i != 0 {
|
|
||||||
output.WriteString(",")
|
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&output, "%s=%d", r.cc, r.count)
|
|
||||||
|
// cut off trailing ","
|
||||||
|
if len(output) > 0 {
|
||||||
|
return output[:len(output)-1]
|
||||||
}
|
}
|
||||||
return output.String()
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) LoadGeoipDatabases(geoipDB string, geoip6DB string) error {
|
func (m *Metrics) LoadGeoipDatabases(geoipDB string, geoip6DB string) error {
|
||||||
|
@ -225,64 +212,126 @@ func (m *Metrics) LoadGeoipDatabases(geoipDB string, geoip6DB string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMetrics(metricsLogger *log.Logger) (*Metrics, error) {
|
||||||
|
m := new(Metrics)
|
||||||
|
|
||||||
|
m.clientDeniedCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.clientRestrictedDeniedCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.clientUnrestrictedDeniedCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.clientProxyMatchCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.clientProxyTimeoutCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
|
||||||
|
m.rendezvousCountryStats = make(map[messages.RendezvousMethod]map[string]int)
|
||||||
|
for _, rendezvousMethod := range rendezvoudMethodList {
|
||||||
|
m.rendezvousCountryStats[rendezvousMethod] = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.countryStats = CountryStats{
|
||||||
|
counts: make(map[string]int),
|
||||||
|
proxies: make(map[string]map[string]bool),
|
||||||
|
unknown: make(map[string]bool),
|
||||||
|
natRestricted: make(map[string]bool),
|
||||||
|
natUnrestricted: make(map[string]bool),
|
||||||
|
natUnknown: make(map[string]bool),
|
||||||
|
}
|
||||||
|
for pType := range messages.KnownProxyTypes {
|
||||||
|
m.countryStats.proxies[pType] = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.logger = metricsLogger
|
||||||
|
m.promMetrics = initPrometheus()
|
||||||
|
|
||||||
|
// Write to log file every day with updated metrics
|
||||||
|
go m.logMetrics()
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Logs metrics in intervals specified by metricsResolution
|
// Logs metrics in intervals specified by metricsResolution
|
||||||
func (m *Metrics) logMetrics() {
|
func (m *Metrics) logMetrics() {
|
||||||
heartbeat := time.Tick(metricsResolution)
|
heartbeat := time.Tick(metricsResolution)
|
||||||
for range heartbeat {
|
for range heartbeat {
|
||||||
m.printMetrics()
|
m.printMetrics()
|
||||||
|
m.zeroMetrics()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) loadAndZero(key string) uint64 {
|
|
||||||
count, loaded := m.counters.LoadAndDelete(key)
|
|
||||||
if !loaded {
|
|
||||||
count = new(uint64)
|
|
||||||
}
|
|
||||||
ptr := count.(*uint64)
|
|
||||||
return *ptr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Metrics) printMetrics() {
|
func (m *Metrics) printMetrics() {
|
||||||
|
m.lock.Lock()
|
||||||
m.logger.Println(
|
m.logger.Println(
|
||||||
"snowflake-stats-end",
|
"snowflake-stats-end",
|
||||||
time.Now().UTC().Format("2006-01-02 15:04:05"),
|
time.Now().UTC().Format("2006-01-02 15:04:05"),
|
||||||
fmt.Sprintf("(%d s)", int(metricsResolution.Seconds())),
|
fmt.Sprintf("(%d s)", int(metricsResolution.Seconds())),
|
||||||
)
|
)
|
||||||
m.logger.Println("snowflake-ips", formatAndClearCountryStats(m.proxies, false))
|
m.logger.Println("snowflake-ips", m.countryStats.Display())
|
||||||
m.logger.Printf("snowflake-ips-iptproxy %d\n", m.loadAndZero("proxy-iptproxy"))
|
total := len(m.countryStats.unknown)
|
||||||
m.logger.Printf("snowflake-ips-standalone %d\n", m.loadAndZero("proxy-standalone"))
|
for pType, addresses := range m.countryStats.proxies {
|
||||||
m.logger.Printf("snowflake-ips-webext %d\n", m.loadAndZero("proxy-webext"))
|
m.logger.Printf("snowflake-ips-%s %d\n", pType, len(addresses))
|
||||||
m.logger.Printf("snowflake-ips-badge %d\n", m.loadAndZero("proxy-badge"))
|
total += len(addresses)
|
||||||
m.logger.Println("snowflake-ips-total", m.loadAndZero("proxy-total"))
|
}
|
||||||
m.logger.Println("snowflake-idle-count", binCount(m.loadAndZero("proxy-idle")))
|
m.logger.Println("snowflake-ips-total", total)
|
||||||
m.logger.Println("snowflake-proxy-poll-with-relay-url-count", binCount(m.loadAndZero("proxy-poll-with-relay-url")))
|
m.logger.Println("snowflake-idle-count", binCount(m.proxyIdleCount))
|
||||||
m.logger.Println("snowflake-proxy-poll-without-relay-url-count", binCount(m.loadAndZero("proxy-poll-without-relay-url")))
|
m.logger.Println("snowflake-proxy-poll-with-relay-url-count", binCount(m.proxyPollWithRelayURLExtension))
|
||||||
m.logger.Println("snowflake-proxy-rejected-for-relay-url-count", binCount(m.loadAndZero("proxy-poll-rejected-relay-url")))
|
m.logger.Println("snowflake-proxy-poll-without-relay-url-count", binCount(m.proxyPollWithoutRelayURLExtension))
|
||||||
|
m.logger.Println("snowflake-proxy-rejected-for-relay-url-count", binCount(m.proxyPollRejectedWithRelayURLExtension))
|
||||||
|
|
||||||
m.logger.Println("client-denied-count", binCount(m.loadAndZero("client-denied")))
|
m.logger.Println("client-denied-count", binCount(sumMapValues(&m.clientDeniedCount)))
|
||||||
m.logger.Println("client-restricted-denied-count", binCount(m.loadAndZero("client-restricted-denied")))
|
m.logger.Println("client-restricted-denied-count", binCount(sumMapValues(&m.clientRestrictedDeniedCount)))
|
||||||
m.logger.Println("client-unrestricted-denied-count", binCount(m.loadAndZero("client-unrestricted-denied")))
|
m.logger.Println("client-unrestricted-denied-count", binCount(sumMapValues(&m.clientUnrestrictedDeniedCount)))
|
||||||
m.logger.Println("client-snowflake-match-count", binCount(m.loadAndZero("client-match")))
|
m.logger.Println("client-snowflake-match-count", binCount(sumMapValues(&m.clientProxyMatchCount)))
|
||||||
m.logger.Println("client-snowflake-timeout-count", binCount(m.loadAndZero("client-timeout")))
|
m.logger.Println("client-snowflake-timeout-count", binCount(sumMapValues(&m.clientProxyTimeoutCount)))
|
||||||
|
|
||||||
m.logger.Printf("client-http-count %d\n", binCount(m.loadAndZero("client-http")))
|
for _, rendezvousMethod := range rendezvoudMethodList {
|
||||||
m.logger.Printf("client-http-ips %s\n", formatAndClearCountryStats(m.clientHTTPPolls, true))
|
m.logger.Printf("client-%s-count %d\n", rendezvousMethod, binCount(
|
||||||
m.logger.Printf("client-ampcache-count %d\n", binCount(m.loadAndZero("client-amp")))
|
m.clientDeniedCount[rendezvousMethod]+m.clientProxyMatchCount[rendezvousMethod],
|
||||||
m.logger.Printf("client-ampcache-ips %s\n", formatAndClearCountryStats(m.clientAMPPolls, true))
|
))
|
||||||
m.logger.Printf("client-sqs-count %d\n", binCount(m.loadAndZero("client-sqs")))
|
m.logger.Printf("client-%s-ips %s\n", rendezvousMethod, m.DisplayRendezvousStatsByCountry(rendezvousMethod))
|
||||||
m.logger.Printf("client-sqs-ips %s\n", formatAndClearCountryStats(m.clientSQSPolls, true))
|
|
||||||
|
|
||||||
m.logger.Println("snowflake-ips-nat-restricted", m.loadAndZero("proxy-nat-restricted"))
|
|
||||||
m.logger.Println("snowflake-ips-nat-unrestricted", m.loadAndZero("proxy-nat-unrestricted"))
|
|
||||||
m.logger.Println("snowflake-ips-nat-unknown", m.loadAndZero("proxy-nat-unknown"))
|
|
||||||
|
|
||||||
m.ips.Clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// binCount rounds count up to the next multiple of 8. Returns 0 on integer
|
m.logger.Println("snowflake-ips-nat-restricted", len(m.countryStats.natRestricted))
|
||||||
// overflow.
|
m.logger.Println("snowflake-ips-nat-unrestricted", len(m.countryStats.natUnrestricted))
|
||||||
func binCount(count uint64) uint64 {
|
m.logger.Println("snowflake-ips-nat-unknown", len(m.countryStats.natUnknown))
|
||||||
return (count + 7) / 8 * 8
|
m.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restores all metrics to original values
|
||||||
|
func (m *Metrics) zeroMetrics() {
|
||||||
|
m.proxyIdleCount = 0
|
||||||
|
m.clientDeniedCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.clientRestrictedDeniedCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.clientUnrestrictedDeniedCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.proxyPollRejectedWithRelayURLExtension = 0
|
||||||
|
m.proxyPollWithRelayURLExtension = 0
|
||||||
|
m.proxyPollWithoutRelayURLExtension = 0
|
||||||
|
m.clientProxyMatchCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
m.clientProxyTimeoutCount = make(map[messages.RendezvousMethod]uint)
|
||||||
|
|
||||||
|
m.rendezvousCountryStats = make(map[messages.RendezvousMethod]map[string]int)
|
||||||
|
for _, rendezvousMethod := range rendezvoudMethodList {
|
||||||
|
m.rendezvousCountryStats[rendezvousMethod] = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.countryStats.counts = make(map[string]int)
|
||||||
|
for pType := range m.countryStats.proxies {
|
||||||
|
m.countryStats.proxies[pType] = make(map[string]bool)
|
||||||
|
}
|
||||||
|
m.countryStats.unknown = make(map[string]bool)
|
||||||
|
m.countryStats.natRestricted = make(map[string]bool)
|
||||||
|
m.countryStats.natUnrestricted = make(map[string]bool)
|
||||||
|
m.countryStats.natUnknown = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rounds up a count to the nearest multiple of 8.
|
||||||
|
func binCount(count uint) uint {
|
||||||
|
return uint((math.Ceil(float64(count) / 8)) * 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sumMapValues(m *map[messages.RendezvousMethod]uint) uint {
|
||||||
|
var s uint = 0
|
||||||
|
for _, v := range *m {
|
||||||
|
s += v
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
type PromMetrics struct {
|
type PromMetrics struct {
|
||||||
|
@ -290,7 +339,6 @@ type PromMetrics struct {
|
||||||
ProxyTotal *prometheus.CounterVec
|
ProxyTotal *prometheus.CounterVec
|
||||||
ProxyPollTotal *safeprom.CounterVec
|
ProxyPollTotal *safeprom.CounterVec
|
||||||
ClientPollTotal *safeprom.CounterVec
|
ClientPollTotal *safeprom.CounterVec
|
||||||
ProxyAnswerTotal *safeprom.CounterVec
|
|
||||||
AvailableProxies *prometheus.GaugeVec
|
AvailableProxies *prometheus.GaugeVec
|
||||||
|
|
||||||
ProxyPollWithRelayURLExtensionTotal *safeprom.CounterVec
|
ProxyPollWithRelayURLExtensionTotal *safeprom.CounterVec
|
||||||
|
@ -329,16 +377,7 @@ func initPrometheus() *PromMetrics {
|
||||||
Name: "rounded_proxy_poll_total",
|
Name: "rounded_proxy_poll_total",
|
||||||
Help: "The number of snowflake proxy polls, rounded up to a multiple of 8",
|
Help: "The number of snowflake proxy polls, rounded up to a multiple of 8",
|
||||||
},
|
},
|
||||||
[]string{"nat", "type", "status"},
|
[]string{"nat", "status"},
|
||||||
)
|
|
||||||
|
|
||||||
promMetrics.ProxyAnswerTotal = safeprom.NewCounterVec(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Namespace: prometheusNamespace,
|
|
||||||
Name: "rounded_proxy_answer_total",
|
|
||||||
Help: "The number of snowflake proxy answers, rounded up to a multiple of 8",
|
|
||||||
},
|
|
||||||
[]string{"type", "status"},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
promMetrics.ProxyPollWithRelayURLExtensionTotal = safeprom.NewCounterVec(
|
promMetrics.ProxyPollWithRelayURLExtensionTotal = safeprom.NewCounterVec(
|
||||||
|
@ -380,7 +419,7 @@ func initPrometheus() *PromMetrics {
|
||||||
// We need to register our metrics so they can be exported.
|
// We need to register our metrics so they can be exported.
|
||||||
promMetrics.registry.MustRegister(
|
promMetrics.registry.MustRegister(
|
||||||
promMetrics.ClientPollTotal, promMetrics.ProxyPollTotal,
|
promMetrics.ClientPollTotal, promMetrics.ProxyPollTotal,
|
||||||
promMetrics.ProxyTotal, promMetrics.ProxyAnswerTotal, promMetrics.AvailableProxies,
|
promMetrics.ProxyTotal, promMetrics.AvailableProxies,
|
||||||
promMetrics.ProxyPollWithRelayURLExtensionTotal,
|
promMetrics.ProxyPollWithRelayURLExtensionTotal,
|
||||||
promMetrics.ProxyPollWithoutRelayURLExtensionTotal,
|
promMetrics.ProxyPollWithoutRelayURLExtensionTotal,
|
||||||
promMetrics.ProxyPollRejectedForRelayURLExtensionTotal,
|
promMetrics.ProxyPollRejectedForRelayURLExtensionTotal,
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFormatAndClearCountryStats(t *testing.T) {
|
|
||||||
Convey("given a mapping of country stats", t, func() {
|
|
||||||
stats := new(sync.Map)
|
|
||||||
for _, record := range []struct {
|
|
||||||
cc string
|
|
||||||
count uint64
|
|
||||||
}{
|
|
||||||
{"IT", 50},
|
|
||||||
{"FR", 200},
|
|
||||||
{"TZ", 100},
|
|
||||||
{"CN", 250},
|
|
||||||
{"RU", 150},
|
|
||||||
{"CA", 1},
|
|
||||||
{"BE", 1},
|
|
||||||
{"PH", 1},
|
|
||||||
// The next 3 bin to the same value, 112. When not
|
|
||||||
// binned, they should go in the order MY,ZA,AT (ordered
|
|
||||||
// by count). When binned, they should go in the order
|
|
||||||
// AT,MY,ZA (ordered by country code).
|
|
||||||
{"AT", 105},
|
|
||||||
{"MY", 112},
|
|
||||||
{"ZA", 108},
|
|
||||||
} {
|
|
||||||
stats.Store(record.cc, &record.count)
|
|
||||||
}
|
|
||||||
|
|
||||||
Convey("the order should be correct with binned=false", func() {
|
|
||||||
So(formatAndClearCountryStats(stats, false), ShouldEqual, "CN=250,FR=200,RU=150,MY=112,ZA=108,AT=105,TZ=100,IT=50,BE=1,CA=1,PH=1")
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("the order should be correct with binned=true", func() {
|
|
||||||
So(formatAndClearCountryStats(stats, true), ShouldEqual, "CN=256,FR=200,RU=152,AT=112,MY=112,ZA=112,TZ=104,IT=56,BE=8,CA=8,PH=8")
|
|
||||||
})
|
|
||||||
|
|
||||||
// The map should be cleared on return.
|
|
||||||
stats.Range(func(_, _ any) bool { panic("map was not cleared") })
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -3,8 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -12,7 +10,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -49,7 +46,6 @@ var (
|
||||||
"a=candidate:1000 1 udp 2000 8.8.8.8 3000 typ host\r\n" +
|
"a=candidate:1000 1 udp 2000 8.8.8.8 3000 typ host\r\n" +
|
||||||
"a=end-of-candidates\r\n"
|
"a=end-of-candidates\r\n"
|
||||||
|
|
||||||
rawOffer = `{"type":"offer","sdp":"v=0\r\no=- 4358805017720277108 2 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=group:BUNDLE data\r\na=msid-semantic: WMS\r\nm=application 56688 DTLS/SCTP 5000\r\nc=IN IP4 0.0.0.0\r\na=candidate:3769337065 1 udp 2122260223 129.97.208.23 56688 typ host generation 0 network-id 1 network-cost 50\r\na=candidate:2921887769 1 tcp 1518280447 129.97.208.23 35441 typ host tcptype passive generation 0 network-id 1 network-cost 50\r\na=ice-ufrag:aMAZ\r\na=ice-pwd:jcHb08Jjgrazp2dzjdrvPPvV\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C8:88:EE:B9:E7:02:2E:21:37:ED:7A:D1:EB:2B:A3:15:A2:3B:5B:1C:3D:D4:D5:1F:06:CF:52:40:03:F8:DD:66\r\na=setup:actpass\r\na=mid:data\r\na=sctpmap:5000 webrtc-datachannel 1024\r\n"}`
|
|
||||||
sid = "ymbcCMto7KHNGYlp"
|
sid = "ymbcCMto7KHNGYlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,7 +89,7 @@ func TestBroker(t *testing.T) {
|
||||||
|
|
||||||
Convey("Context", t, func() {
|
Convey("Context", t, func() {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
ctx := NewBrokerContext(log.New(buf, "", 0), "snowflake.torproject.net")
|
ctx := NewBrokerContext(log.New(buf, "", 0), "", "")
|
||||||
i := &IPC{ctx}
|
i := &IPC{ctx}
|
||||||
|
|
||||||
Convey("Adds Snowflake", func() {
|
Convey("Adds Snowflake", func() {
|
||||||
|
@ -406,45 +402,12 @@ client-sqs-ips
|
||||||
So(body, ShouldEqual, `{"error":"timed out waiting for answer!"}`)
|
So(body, ShouldEqual, `{"error":"timed out waiting for answer!"}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("and correctly geolocates remote addr.", func() {
|
|
||||||
err := ctx.metrics.LoadGeoipDatabases("test_geoip", "test_geoip6")
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
clientRequest := &messages.ClientPollRequest{
|
|
||||||
Offer: rawOffer,
|
|
||||||
NAT: NATUnknown,
|
|
||||||
Fingerprint: "",
|
|
||||||
}
|
|
||||||
encOffer, err := clientRequest.EncodeClientPollRequest()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
r, err = http.NewRequest("GET", "/amp/client/"+amp.EncodePath(encOffer), nil)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
ampClientOffers(i, w, r)
|
|
||||||
So(w.Code, ShouldEqual, http.StatusOK)
|
|
||||||
body, err := decodeAMPArmorToString(w.Body)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(body, ShouldEqual, `{"error":"no snowflake proxies currently available"}`)
|
|
||||||
|
|
||||||
ctx.metrics.printMetrics()
|
|
||||||
So(buf.String(), ShouldContainSubstring, `client-denied-count 8
|
|
||||||
client-restricted-denied-count 8
|
|
||||||
client-unrestricted-denied-count 0
|
|
||||||
client-snowflake-match-count 0
|
|
||||||
client-snowflake-timeout-count 0
|
|
||||||
client-http-count 0
|
|
||||||
client-http-ips
|
|
||||||
client-ampcache-count 8
|
|
||||||
client-ampcache-ips CA=8
|
|
||||||
client-sqs-count 0
|
|
||||||
client-sqs-ips
|
|
||||||
`)
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Responds to proxy polls...", func() {
|
Convey("Responds to proxy polls...", func() {
|
||||||
done := make(chan bool)
|
done := make(chan bool)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0", "AcceptedRelayPattern": "snowflake.torproject.net"}`))
|
data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`))
|
||||||
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
@ -530,7 +493,7 @@ client-sqs-ips
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("End-To-End", t, func() {
|
Convey("End-To-End", t, func() {
|
||||||
ctx := NewBrokerContext(NullLogger(), "snowflake.torproject.net")
|
ctx := NewBrokerContext(NullLogger(), "", "")
|
||||||
i := &IPC{ctx}
|
i := &IPC{ctx}
|
||||||
|
|
||||||
Convey("Check for client/proxy data race", func() {
|
Convey("Check for client/proxy data race", func() {
|
||||||
|
@ -541,7 +504,7 @@ client-sqs-ips
|
||||||
|
|
||||||
// Make proxy poll
|
// Make proxy poll
|
||||||
wp := httptest.NewRecorder()
|
wp := httptest.NewRecorder()
|
||||||
datap := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
datap := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`))
|
||||||
rp, err := http.NewRequest("POST", "snowflake.broker/proxy", datap)
|
rp, err := http.NewRequest("POST", "snowflake.broker/proxy", datap)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
@ -586,7 +549,7 @@ client-sqs-ips
|
||||||
polled := make(chan bool)
|
polled := make(chan bool)
|
||||||
|
|
||||||
// Proxy polls with its ID first...
|
// Proxy polls with its ID first...
|
||||||
dataP := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
dataP := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`))
|
||||||
wP := httptest.NewRecorder()
|
wP := httptest.NewRecorder()
|
||||||
rP, err := http.NewRequest("POST", "snowflake.broker/proxy", dataP)
|
rP, err := http.NewRequest("POST", "snowflake.broker/proxy", dataP)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -683,11 +646,11 @@ func TestSnowflakeHeap(t *testing.T) {
|
||||||
func TestInvalidGeoipFile(t *testing.T) {
|
func TestInvalidGeoipFile(t *testing.T) {
|
||||||
Convey("Geoip", t, func() {
|
Convey("Geoip", t, func() {
|
||||||
// Make sure things behave properly if geoip file fails to load
|
// Make sure things behave properly if geoip file fails to load
|
||||||
ctx := NewBrokerContext(NullLogger(), "")
|
ctx := NewBrokerContext(NullLogger(), "", "")
|
||||||
if err := ctx.metrics.LoadGeoipDatabases("invalid_filename", "invalid_filename6"); err != nil {
|
if err := ctx.metrics.LoadGeoipDatabases("invalid_filename", "invalid_filename6"); err != nil {
|
||||||
log.Printf("loading geo ip databases returned error: %v", err)
|
log.Printf("loading geo ip databases returned error: %v", err)
|
||||||
}
|
}
|
||||||
ctx.metrics.UpdateProxyStats("127.0.0.1", "", NATUnrestricted)
|
ctx.metrics.UpdateCountryStats("127.0.0.1", "", NATUnrestricted)
|
||||||
So(ctx.metrics.geoipdb, ShouldBeNil)
|
So(ctx.metrics.geoipdb, ShouldBeNil)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -697,7 +660,7 @@ func TestMetrics(t *testing.T) {
|
||||||
Convey("Test metrics...", t, func() {
|
Convey("Test metrics...", t, func() {
|
||||||
done := make(chan bool)
|
done := make(chan bool)
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
ctx := NewBrokerContext(log.New(buf, "", 0), "snowflake.torproject.net")
|
ctx := NewBrokerContext(log.New(buf, "", 0), "", "")
|
||||||
i := &IPC{ctx}
|
i := &IPC{ctx}
|
||||||
|
|
||||||
err := ctx.metrics.LoadGeoipDatabases("test_geoip", "test_geoip6")
|
err := ctx.metrics.LoadGeoipDatabases("test_geoip", "test_geoip6")
|
||||||
|
@ -706,9 +669,9 @@ func TestMetrics(t *testing.T) {
|
||||||
//Test addition of proxy polls
|
//Test addition of proxy polls
|
||||||
Convey("for proxy polls", func() {
|
Convey("for proxy polls", func() {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
data := bytes.NewReader([]byte("{\"Sid\":\"ymbcCMto7KHNGYlp\",\"Version\":\"1.0\",\"AcceptedRelayPattern\":\"snowflake.torproject.net\"}"))
|
data := bytes.NewReader([]byte("{\"Sid\":\"ymbcCMto7KHNGYlp\",\"Version\":\"1.0\"}"))
|
||||||
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
r.RemoteAddr = "129.97.208.23" //CA geoip
|
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
go func(i *IPC) {
|
go func(i *IPC) {
|
||||||
proxyPolls(i, w, r)
|
proxyPolls(i, w, r)
|
||||||
|
@ -719,9 +682,9 @@ func TestMetrics(t *testing.T) {
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","Type":"standalone","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","Type":"standalone"}`))
|
||||||
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
r.RemoteAddr = "129.97.208.24" //CA geoip
|
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
go func(i *IPC) {
|
go func(i *IPC) {
|
||||||
proxyPolls(i, w, r)
|
proxyPolls(i, w, r)
|
||||||
|
@ -732,9 +695,9 @@ func TestMetrics(t *testing.T) {
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","Type":"badge","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","Type":"badge"}`))
|
||||||
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
r.RemoteAddr = "129.97.208.25" //CA geoip
|
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
go func(i *IPC) {
|
go func(i *IPC) {
|
||||||
proxyPolls(i, w, r)
|
proxyPolls(i, w, r)
|
||||||
|
@ -745,9 +708,9 @@ func TestMetrics(t *testing.T) {
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","Type":"webext","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","Type":"webext"}`))
|
||||||
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
r.RemoteAddr = "129.97.208.26" //CA geoip
|
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
go func(i *IPC) {
|
go func(i *IPC) {
|
||||||
proxyPolls(i, w, r)
|
proxyPolls(i, w, r)
|
||||||
|
@ -765,8 +728,8 @@ func TestMetrics(t *testing.T) {
|
||||||
So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-webext 1\n")
|
So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-webext 1\n")
|
||||||
So(metricsStr, ShouldEndWith, `snowflake-ips-total 4
|
So(metricsStr, ShouldEndWith, `snowflake-ips-total 4
|
||||||
snowflake-idle-count 8
|
snowflake-idle-count 8
|
||||||
snowflake-proxy-poll-with-relay-url-count 8
|
snowflake-proxy-poll-with-relay-url-count 0
|
||||||
snowflake-proxy-poll-without-relay-url-count 0
|
snowflake-proxy-poll-without-relay-url-count 8
|
||||||
snowflake-proxy-rejected-for-relay-url-count 0
|
snowflake-proxy-rejected-for-relay-url-count 0
|
||||||
client-denied-count 0
|
client-denied-count 0
|
||||||
client-restricted-denied-count 0
|
client-restricted-denied-count 0
|
||||||
|
@ -781,7 +744,7 @@ client-sqs-count 0
|
||||||
client-sqs-ips
|
client-sqs-ips
|
||||||
snowflake-ips-nat-restricted 0
|
snowflake-ips-nat-restricted 0
|
||||||
snowflake-ips-nat-unrestricted 0
|
snowflake-ips-nat-unrestricted 0
|
||||||
snowflake-ips-nat-unknown 4
|
snowflake-ips-nat-unknown 1
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -811,6 +774,7 @@ client-sqs-ips `)
|
||||||
|
|
||||||
// Test reset
|
// Test reset
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
ctx.metrics.zeroMetrics()
|
||||||
ctx.metrics.printMetrics()
|
ctx.metrics.printMetrics()
|
||||||
So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips \n")
|
So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips \n")
|
||||||
So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-standalone 0\n")
|
So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-standalone 0\n")
|
||||||
|
@ -917,6 +881,9 @@ snowflake-ips-nat-unknown 0
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
clientOffers(i, w, r)
|
clientOffers(i, w, r)
|
||||||
|
|
||||||
|
ctx.metrics.printMetrics()
|
||||||
|
So(buf.String(), ShouldContainSubstring, "client-denied-count 8\nclient-restricted-denied-count 8\nclient-unrestricted-denied-count 0\n")
|
||||||
|
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
data, err = createClientOffer(sdp, NATRestricted, "")
|
data, err = createClientOffer(sdp, NATRestricted, "")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -932,7 +899,7 @@ snowflake-ips-nat-unknown 0
|
||||||
//Test unique ip
|
//Test unique ip
|
||||||
Convey("proxy counts by unique ip", func() {
|
Convey("proxy counts by unique ip", func() {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`))
|
||||||
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -944,7 +911,7 @@ snowflake-ips-nat-unknown 0
|
||||||
p.offerChannel <- nil
|
p.offerChannel <- nil
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`))
|
||||||
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unable to get NewRequest with error: %v", err)
|
log.Printf("unable to get NewRequest with error: %v", err)
|
||||||
|
@ -966,7 +933,7 @@ snowflake-ips-nat-unknown 0
|
||||||
//Test NAT types
|
//Test NAT types
|
||||||
Convey("proxy counts by NAT type", func() {
|
Convey("proxy counts by NAT type", func() {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"restricted","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"restricted"}`))
|
||||||
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -978,7 +945,10 @@ snowflake-ips-nat-unknown 0
|
||||||
p.offerChannel <- nil
|
p.offerChannel <- nil
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"unrestricted","AcceptedRelayPattern":"snowflake.torproject.net"}`))
|
ctx.metrics.printMetrics()
|
||||||
|
So(buf.String(), ShouldContainSubstring, "snowflake-ips-nat-restricted 1\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 0")
|
||||||
|
|
||||||
|
data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"unrestricted"}`))
|
||||||
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unable to get NewRequest with error: %v", err)
|
log.Printf("unable to get NewRequest with error: %v", err)
|
||||||
|
@ -1009,6 +979,7 @@ snowflake-ips-nat-unknown 0
|
||||||
So(buf.String(), ShouldContainSubstring, "client-denied-count 8\nclient-restricted-denied-count 8\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0")
|
So(buf.String(), ShouldContainSubstring, "client-denied-count 8\nclient-restricted-denied-count 8\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0")
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
ctx.metrics.zeroMetrics()
|
||||||
|
|
||||||
data, err = createClientOffer(sdp, NATUnrestricted, "")
|
data, err = createClientOffer(sdp, NATUnrestricted, "")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -1021,6 +992,7 @@ snowflake-ips-nat-unknown 0
|
||||||
So(buf.String(), ShouldContainSubstring, "client-denied-count 8\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 8\nclient-snowflake-match-count 0")
|
So(buf.String(), ShouldContainSubstring, "client-denied-count 8\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 8\nclient-snowflake-match-count 0")
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
ctx.metrics.zeroMetrics()
|
||||||
|
|
||||||
data, err = createClientOffer(sdp, NATUnknown, "")
|
data, err = createClientOffer(sdp, NATUnknown, "")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -1032,105 +1004,20 @@ snowflake-ips-nat-unknown 0
|
||||||
ctx.metrics.printMetrics()
|
ctx.metrics.printMetrics()
|
||||||
So(buf.String(), ShouldContainSubstring, "client-denied-count 8\nclient-restricted-denied-count 8\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0")
|
So(buf.String(), ShouldContainSubstring, "client-denied-count 8\nclient-restricted-denied-count 8\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0")
|
||||||
})
|
})
|
||||||
Convey("that seen IPs map is cleared after each print", func() {
|
Convey("for country stats order", func() {
|
||||||
w := httptest.NewRecorder()
|
|
||||||
data := bytes.NewReader([]byte("{\"Sid\":\"ymbcCMto7KHNGYlp\",\"Version\":\"1.0\",\"AcceptedRelayPattern\":\"snowflake.torproject.net\"}"))
|
|
||||||
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
|
|
||||||
r.RemoteAddr = "129.97.208.23" //CA geoip
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
go func(i *IPC) {
|
|
||||||
proxyPolls(i, w, r)
|
|
||||||
done <- true
|
|
||||||
}(i)
|
|
||||||
p := <-ctx.proxyPolls //manually unblock poll
|
|
||||||
p.offerChannel <- nil
|
|
||||||
<-done
|
|
||||||
|
|
||||||
ctx.metrics.printMetrics()
|
|
||||||
So(buf.String(), ShouldContainSubstring, "snowflake-ips CA=1")
|
|
||||||
So(buf.String(), ShouldContainSubstring, "snowflake-ips-total 1")
|
|
||||||
buf.Reset()
|
|
||||||
|
|
||||||
w = httptest.NewRecorder()
|
|
||||||
data = bytes.NewReader([]byte("{\"Sid\":\"ymbcCMto7KHNGYlp\",\"Version\":\"1.0\",\"AcceptedRelayPattern\":\"snowflake.torproject.net\"}"))
|
|
||||||
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
|
|
||||||
r.RemoteAddr = "129.97.208.23" //CA geoip
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
go func(i *IPC) {
|
|
||||||
proxyPolls(i, w, r)
|
|
||||||
done <- true
|
|
||||||
}(i)
|
|
||||||
p = <-ctx.proxyPolls //manually unblock poll
|
|
||||||
p.offerChannel <- nil
|
|
||||||
<-done
|
|
||||||
|
|
||||||
ctx.metrics.printMetrics()
|
|
||||||
So(buf.String(), ShouldContainSubstring, "snowflake-ips CA=1")
|
|
||||||
So(buf.String(), ShouldContainSubstring, "snowflake-ips-total 1")
|
|
||||||
buf.Reset()
|
|
||||||
|
|
||||||
})
|
stats := map[string]int{
|
||||||
})
|
"IT": 50,
|
||||||
}
|
"FR": 200,
|
||||||
|
"TZ": 100,
|
||||||
func TestConcurrency(t *testing.T) {
|
"CN": 250,
|
||||||
Convey("Test concurency with", t, func() {
|
"RU": 150,
|
||||||
ctx := NewBrokerContext(NullLogger(), "snowflake.torproject.net")
|
"CA": 1,
|
||||||
i := &IPC{ctx}
|
"BE": 1,
|
||||||
Convey("multiple simultaneous polls", func(c C) {
|
"PH": 1,
|
||||||
go ctx.Broker()
|
}
|
||||||
|
ctx.metrics.countryStats.counts = stats
|
||||||
var proxies sync.WaitGroup
|
So(ctx.metrics.countryStats.Display(), ShouldEqual, "CN=250,FR=200,RU=150,TZ=100,IT=50,BE=1,CA=1,PH=1")
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
proxies.Add(1000)
|
|
||||||
// Multiple proxy polls
|
|
||||||
for x := 0; x < 1000; x++ {
|
|
||||||
wp := httptest.NewRecorder()
|
|
||||||
buf := make([]byte, 16)
|
|
||||||
_, err := rand.Read(buf)
|
|
||||||
id := strings.TrimRight(base64.StdEncoding.EncodeToString(buf), "=")
|
|
||||||
|
|
||||||
datap := bytes.NewReader([]byte(fmt.Sprintf("{\"Sid\": \"%s\",\"Version\":\"1.0\",\"AcceptedRelayPattern\":\"snowflake.torproject.net\"}", id)))
|
|
||||||
rp, err := http.NewRequest("POST", "snowflake.broker/proxy", datap)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
proxies.Done()
|
|
||||||
proxyPolls(i, wp, rp)
|
|
||||||
c.So(wp.Code, ShouldEqual, http.StatusOK)
|
|
||||||
|
|
||||||
// Proxy answers
|
|
||||||
wp = httptest.NewRecorder()
|
|
||||||
datap, err = createProxyAnswer(sdp, id)
|
|
||||||
c.So(err, ShouldBeNil)
|
|
||||||
rp, err = http.NewRequest("POST", "snowflake.broker/answer", datap)
|
|
||||||
c.So(err, ShouldBeNil)
|
|
||||||
go func() {
|
|
||||||
proxyAnswers(i, wp, rp)
|
|
||||||
}()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
// Wait for all proxies to poll before sending client offers
|
|
||||||
proxies.Wait()
|
|
||||||
|
|
||||||
// Multiple client offers
|
|
||||||
for x := 0; x < 500; x++ {
|
|
||||||
wg.Add(1)
|
|
||||||
wc := httptest.NewRecorder()
|
|
||||||
datac, err := createClientOffer(sdp, NATUnrestricted, "")
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
rc, err := http.NewRequest("POST", "snowflake.broker/client", datac)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
clientOffers(i, wc, rc)
|
|
||||||
c.So(wc.Code, ShouldEqual, http.StatusOK)
|
|
||||||
c.So(wc.Body.String(), ShouldContainSubstring, "8.8.8.8")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
||||||
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/sqsclient"
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/sqsclient"
|
||||||
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -118,25 +119,23 @@ func (r *sqsHandler) cleanupClientQueues(ctx context.Context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Printf("SQSHandler: finished running iteration of client queue cleanup. found and deleted %d client queues.\n", numDeleted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *sqsHandler) handleMessage(mainCtx context.Context, message *types.Message) {
|
func (r *sqsHandler) handleMessage(context context.Context, message *types.Message) {
|
||||||
var encPollReq []byte
|
var encPollReq []byte
|
||||||
var response []byte
|
var response []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(mainCtx, ClientTimeout*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
clientID := message.MessageAttributes["ClientID"].StringValue
|
clientID := message.MessageAttributes["ClientID"].StringValue
|
||||||
if clientID == nil {
|
if clientID == nil {
|
||||||
log.Println("SQSHandler: got SDP offer in SQS message with no client ID. ignoring this message.")
|
log.Println("SQSHandler: got SDP offer in SQS message with no client ID. ignoring this message.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := r.SQSClient.CreateQueue(ctx, &sqs.CreateQueueInput{
|
res, err := r.SQSClient.CreateQueue(context, &sqs.CreateQueueInput{
|
||||||
QueueName: aws.String("snowflake-client-" + *clientID),
|
QueueName: aws.String("snowflake-client-" + *clientID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -147,11 +146,27 @@ func (r *sqsHandler) handleMessage(mainCtx context.Context, message *types.Messa
|
||||||
|
|
||||||
encPollReq = []byte(*message.Body)
|
encPollReq = []byte(*message.Body)
|
||||||
|
|
||||||
|
// Get best guess Client IP for geolocating
|
||||||
|
remoteAddr := ""
|
||||||
|
req, err := messages.DecodeClientPollRequest(encPollReq)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("SQSHandler: error encounted when decoding client poll request %s: %v\n", *clientID, err)
|
||||||
|
} else {
|
||||||
|
sdp, err := util.DeserializeSessionDescription(req.Offer)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("SQSHandler: error encounted when deserializing session desc %s: %v\n", *clientID, err)
|
||||||
|
} else {
|
||||||
|
candidateAddrs := util.GetCandidateAddrs(sdp.SDP)
|
||||||
|
if len(candidateAddrs) > 0 {
|
||||||
|
remoteAddr = candidateAddrs[0].String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
arg := messages.Arg{
|
arg := messages.Arg{
|
||||||
Body: encPollReq,
|
Body: encPollReq,
|
||||||
RemoteAddr: "",
|
RemoteAddr: remoteAddr,
|
||||||
RendezvousMethod: messages.RendezvousSqs,
|
RendezvousMethod: messages.RendezvousSqs,
|
||||||
Context: ctx,
|
|
||||||
}
|
}
|
||||||
err = r.IPC.ClientOffers(arg, &response)
|
err = r.IPC.ClientOffers(arg, &response)
|
||||||
|
|
||||||
|
@ -160,7 +175,7 @@ func (r *sqsHandler) handleMessage(mainCtx context.Context, message *types.Messa
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.SQSClient.SendMessage(ctx, &sqs.SendMessageInput{
|
r.SQSClient.SendMessage(context, &sqs.SendMessageInput{
|
||||||
QueueUrl: answerSQSURL,
|
QueueUrl: answerSQSURL,
|
||||||
MessageBody: aws.String(string(response)),
|
MessageBody: aws.String(string(response)),
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,7 +23,7 @@ func TestSQS(t *testing.T) {
|
||||||
|
|
||||||
Convey("Context", t, func() {
|
Convey("Context", t, func() {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
ipcCtx := NewBrokerContext(log.New(buf, "", 0), "")
|
ipcCtx := NewBrokerContext(log.New(buf, "", 0), "", "")
|
||||||
i := &IPC{ipcCtx}
|
i := &IPC{ipcCtx}
|
||||||
|
|
||||||
Convey("Responds to SQS client offers...", func() {
|
Convey("Responds to SQS client offers...", func() {
|
||||||
|
@ -138,7 +138,7 @@ func TestSQS(t *testing.T) {
|
||||||
sqsHandlerContext, sqsCancelFunc := context.WithCancel(context.Background())
|
sqsHandlerContext, sqsCancelFunc := context.WithCancel(context.Background())
|
||||||
var numTimes atomic.Uint32
|
var numTimes atomic.Uint32
|
||||||
|
|
||||||
mockSQSClient.EXPECT().ReceiveMessage(gomock.Any(), &sqsReceiveMessageInput).AnyTimes().DoAndReturn(
|
mockSQSClient.EXPECT().ReceiveMessage(sqsHandlerContext, &sqsReceiveMessageInput).AnyTimes().DoAndReturn(
|
||||||
func(ctx context.Context, input *sqs.ReceiveMessageInput, optFns ...func(*sqs.Options)) (*sqs.ReceiveMessageOutput, error) {
|
func(ctx context.Context, input *sqs.ReceiveMessageInput, optFns ...func(*sqs.Options)) (*sqs.ReceiveMessageOutput, error) {
|
||||||
|
|
||||||
n := numTimes.Add(1)
|
n := numTimes.Add(1)
|
||||||
|
@ -153,11 +153,11 @@ func TestSQS(t *testing.T) {
|
||||||
return nil, errors.New("error")
|
return nil, errors.New("error")
|
||||||
|
|
||||||
})
|
})
|
||||||
mockSQSClient.EXPECT().CreateQueue(gomock.Any(), &sqsCreateQueueInput).Return(&sqs.CreateQueueOutput{
|
mockSQSClient.EXPECT().CreateQueue(sqsHandlerContext, &sqsCreateQueueInput).Return(&sqs.CreateQueueOutput{
|
||||||
QueueUrl: responseQueueURL,
|
QueueUrl: responseQueueURL,
|
||||||
}, nil).AnyTimes()
|
}, nil).AnyTimes()
|
||||||
mockSQSClient.EXPECT().DeleteMessage(gomock.Any(), gomock.Any()).AnyTimes()
|
mockSQSClient.EXPECT().DeleteMessage(gomock.Any(), gomock.Any()).AnyTimes()
|
||||||
mockSQSClient.EXPECT().SendMessage(gomock.Any(), gomock.Any()).Times(1).DoAndReturn(
|
mockSQSClient.EXPECT().SendMessage(sqsHandlerContext, gomock.Any()).Times(1).DoAndReturn(
|
||||||
func(ctx context.Context, input *sqs.SendMessageInput, optFns ...func(*sqs.Options)) (*sqs.SendMessageOutput, error) {
|
func(ctx context.Context, input *sqs.SendMessageInput, optFns ...func(*sqs.Options)) (*sqs.SendMessageOutput, error) {
|
||||||
c.So(input.MessageBody, ShouldEqual, aws.String("{\"answer\":\"fake answer\"}"))
|
c.So(input.MessageBody, ShouldEqual, aws.String("{\"answer\":\"fake answer\"}"))
|
||||||
// Ensure that match is correctly recorded in metrics
|
// Ensure that match is correctly recorded in metrics
|
||||||
|
|
|
@ -35,18 +35,7 @@ UseBridges 1
|
||||||
|
|
||||||
ClientTransportPlugin snowflake exec ./client -log snowflake.log
|
ClientTransportPlugin snowflake exec ./client -log snowflake.log
|
||||||
|
|
||||||
# CDN77
|
Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ fronts=foursquare.com,github.githubassets.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn
|
||||||
|
|
||||||
Bridge snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.mixvoip.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn
|
|
||||||
Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.mixvoip.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn
|
|
||||||
|
|
||||||
# ampcache
|
|
||||||
#Bridge snowflake 192.0.2.5:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net/ ampcache=https://cdn.ampproject.org/ front=www.google.com ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.mixvoip.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn
|
|
||||||
#Bridge snowflake 192.0.2.6:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://snowflake-broker.torproject.net/ ampcache=https://cdn.ampproject.org/ front=www.google.com ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.mixvoip.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn
|
|
||||||
|
|
||||||
# sqs
|
|
||||||
#Bridge snowflake 192.0.2.5:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 sqsqueue=https://sqs.us-east-1.amazonaws.com/893902434899/snowflake-broker sqscreds=eyJhd3MtYWNjZXNzLWtleS1pZCI6IkFLSUE1QUlGNFdKSlhTN1lIRUczIiwiYXdzLXNlY3JldC1rZXkiOiI3U0RNc0pBNHM1RitXZWJ1L3pMOHZrMFFXV0lsa1c2Y1dOZlVsQ0tRIn0= ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn
|
|
||||||
#Bridge snowflake 192.0.2.6:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA sqsqueue=https://sqs.us-east-1.amazonaws.com/893902434899/snowflake-broker sqscreds=eyJhd3MtYWNjZXNzLWtleS1pZCI6IkFLSUE1QUlGNFdKSlhTN1lIRUczIiwiYXdzLXNlY3JldC1rZXkiOiI3U0RNc0pBNHM1RitXZWJ1L3pMOHZrMFFXV0lsa1c2Y1dOZlVsQ0tRIn0= ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`fingerprint=` is the fingerprint of bridge that the client will ultimately be connecting to.
|
`fingerprint=` is the fingerprint of bridge that the client will ultimately be connecting to.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package messages
|
package messages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +16,6 @@ type Arg struct {
|
||||||
Body []byte
|
Body []byte
|
||||||
RemoteAddr string
|
RemoteAddr string
|
||||||
RendezvousMethod RendezvousMethod
|
RendezvousMethod RendezvousMethod
|
||||||
Context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -150,13 +150,6 @@ func (c *TranscriptPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
||||||
return c.PacketConn.WriteTo(p, addr)
|
return c.PacketConn.WriteTo(p, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TranscriptPacketConn) Length() int {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
return len(c.Transcript)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that QueuePacketConn.WriteTo is compatible with the way kcp-go uses
|
// Tests that QueuePacketConn.WriteTo is compatible with the way kcp-go uses
|
||||||
// PacketConn, allocating source buffers in a sync.Pool.
|
// PacketConn, allocating source buffers in a sync.Pool.
|
||||||
//
|
//
|
||||||
|
@ -215,16 +208,15 @@ func TestQueuePacketConnWriteToKCP(t *testing.T) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A sleep after the Write makes buffer reuse more likely,
|
|
||||||
// and to allow the connection to flush before close
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
|
||||||
|
|
||||||
err = conn.Close()
|
err = conn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if transcript.Length() == 0 {
|
// A sleep after the Write makes buffer reuse more likely.
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
if len(transcript.Transcript) == 0 {
|
||||||
panic("empty transcript")
|
panic("empty transcript")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,13 +82,6 @@ Metrics data from the Snowflake broker can be retrieved by sending an HTTP GET r
|
||||||
A count of the number of times a client successfully received a
|
A count of the number of times a client successfully received a
|
||||||
proxy from the broker, rounded up to the nearest multiple of 8.
|
proxy from the broker, rounded up to the nearest multiple of 8.
|
||||||
|
|
||||||
"client-snowflake-timeout-count" NUM NL
|
|
||||||
[At most once.]
|
|
||||||
|
|
||||||
A count of the number of times a client was matched with a proxy
|
|
||||||
but timed out before receiving the proxy's WebRTC answer,
|
|
||||||
rounded up to the nearest multiple of 8.
|
|
||||||
|
|
||||||
"client-http-count" NUM NL
|
"client-http-count" NUM NL
|
||||||
[At most once.]
|
[At most once.]
|
||||||
|
|
||||||
|
@ -104,9 +97,6 @@ Metrics data from the Snowflake broker can be retrieved by sending an HTTP GET r
|
||||||
rounded up to the nearest multiple of 8. Each country code only appears
|
rounded up to the nearest multiple of 8. Each country code only appears
|
||||||
once.
|
once.
|
||||||
|
|
||||||
Note that this descriptor field name is misleading. We use IP addresses
|
|
||||||
to partition by country, but this metric counts polls, not unique IPs.
|
|
||||||
|
|
||||||
"client-ampcache-count" NUM NL
|
"client-ampcache-count" NUM NL
|
||||||
[At most once.]
|
[At most once.]
|
||||||
|
|
||||||
|
@ -122,9 +112,6 @@ Metrics data from the Snowflake broker can be retrieved by sending an HTTP GET r
|
||||||
method, rounded up to the nearest multiple of 8. Each country code only
|
method, rounded up to the nearest multiple of 8. Each country code only
|
||||||
appears once.
|
appears once.
|
||||||
|
|
||||||
Note that this descriptor field name is misleading. We use IP addresses
|
|
||||||
to partition by country, but this metric counts polls, not unique IPs.
|
|
||||||
|
|
||||||
"client-sqs-count" NUM NL
|
"client-sqs-count" NUM NL
|
||||||
[At most once.]
|
[At most once.]
|
||||||
|
|
||||||
|
@ -140,9 +127,6 @@ Metrics data from the Snowflake broker can be retrieved by sending an HTTP GET r
|
||||||
rounded up to the nearest multiple of 8. Each country code only appears
|
rounded up to the nearest multiple of 8. Each country code only appears
|
||||||
once.
|
once.
|
||||||
|
|
||||||
Note that this descriptor field name is misleading. We use IP addresses
|
|
||||||
to partition by country, but this metric counts polls, not unique IPs.
|
|
||||||
|
|
||||||
"snowflake-ips-nat-restricted" NUM NL
|
"snowflake-ips-nat-restricted" NUM NL
|
||||||
[At most once.]
|
[At most once.]
|
||||||
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
services:
|
|
||||||
snowflake-proxy:
|
|
||||||
network_mode: host
|
|
||||||
image: containers.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake:latest
|
|
||||||
container_name: snowflake-proxy
|
|
||||||
restart: unless-stopped
|
|
||||||
# For a full list of Snowflake Proxy CLI parameters see
|
|
||||||
# https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/tree/main/proxy?ref_type=heads#running-a-standalone-snowflake-proxy
|
|
||||||
#command: [ "-ephemeral-ports-range", "30000:60000" ]
|
|
||||||
watchtower:
|
|
||||||
image: containrrr/watchtower
|
|
||||||
container_name: watchtower
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
command: snowflake-proxy
|
|
84
go.mod
84
go.mod
|
@ -1,48 +1,48 @@
|
||||||
module gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2
|
module gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2
|
||||||
|
|
||||||
go 1.23.0
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2 v1.39.0
|
github.com/aws/aws-sdk-go-v2 v1.36.1
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.8
|
github.com/aws/aws-sdk-go-v2/config v1.29.6
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.12
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.59
|
||||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.42.5
|
github.com/aws/aws-sdk-go-v2/service/sqs v1.37.14
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/miekg/dns v1.1.65
|
github.com/miekg/dns v1.1.63
|
||||||
github.com/pion/ice/v4 v4.0.10
|
github.com/pion/ice/v4 v4.0.7
|
||||||
github.com/pion/sdp/v3 v3.0.16
|
github.com/pion/sdp/v3 v3.0.11
|
||||||
github.com/pion/stun/v3 v3.0.0
|
github.com/pion/stun/v3 v3.0.0
|
||||||
github.com/pion/transport/v3 v3.0.7
|
github.com/pion/transport/v3 v3.0.7
|
||||||
github.com/pion/webrtc/v4 v4.1.4
|
github.com/pion/webrtc/v4 v4.0.13
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.21.0
|
||||||
github.com/realclientip/realclientip-go v1.0.0
|
github.com/realclientip/realclientip-go v1.0.0
|
||||||
github.com/refraction-networking/utls v1.6.7
|
github.com/refraction-networking/utls v1.6.7
|
||||||
github.com/smartystreets/goconvey v1.8.1
|
github.com/smartystreets/goconvey v1.8.1
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301
|
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.24
|
github.com/xtaci/kcp-go/v5 v5.6.8
|
||||||
github.com/xtaci/smux v1.5.35
|
github.com/xtaci/smux v1.5.34
|
||||||
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01
|
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01
|
||||||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.6.0
|
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.6.0
|
||||||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250815012447-418f76dcf315
|
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250130151315-efaf4e0ec0d3
|
||||||
golang.org/x/crypto v0.41.0
|
golang.org/x/crypto v0.33.0
|
||||||
golang.org/x/net v0.42.0
|
golang.org/x/net v0.35.0
|
||||||
golang.org/x/sys v0.35.0
|
golang.org/x/sys v0.30.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
|
||||||
github.com/aws/smithy-go v1.23.0 // indirect
|
github.com/aws/smithy-go v1.22.2 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.7 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
|
@ -50,37 +50,37 @@ require (
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||||
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.12.0 // indirect
|
github.com/klauspost/reedsolomon v1.12.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pion/datachannel v1.5.10 // indirect
|
github.com/pion/datachannel v1.5.10 // indirect
|
||||||
github.com/pion/dtls/v3 v3.0.7 // indirect
|
github.com/pion/dtls/v3 v3.0.4 // indirect
|
||||||
github.com/pion/interceptor v0.1.40 // indirect
|
github.com/pion/interceptor v0.1.37 // indirect
|
||||||
github.com/pion/logging v0.2.4 // indirect
|
github.com/pion/logging v0.2.3 // indirect
|
||||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||||
github.com/pion/randutil v0.1.0 // indirect
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
github.com/pion/rtcp v1.2.15 // indirect
|
github.com/pion/rtcp v1.2.15 // indirect
|
||||||
github.com/pion/rtp v1.8.21 // indirect
|
github.com/pion/rtp v1.8.12 // indirect
|
||||||
github.com/pion/sctp v1.8.39 // indirect
|
github.com/pion/sctp v1.8.37 // indirect
|
||||||
github.com/pion/srtp/v3 v3.0.7 // indirect
|
github.com/pion/srtp/v3 v3.0.4 // indirect
|
||||||
github.com/pion/turn/v4 v4.1.1 // indirect
|
github.com/pion/turn/v4 v4.0.0 // 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/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.62.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/smarty/assertions v1.15.0 // indirect
|
github.com/smarty/assertions v1.15.0 // indirect
|
||||||
|
github.com/templexxx/cpu v0.1.0 // indirect
|
||||||
|
github.com/templexxx/xorsimd v0.4.2 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
|
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
|
||||||
github.com/wlynxg/anet v0.0.5 // indirect
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
golang.org/x/mod v0.26.0 // indirect
|
golang.org/x/mod v0.18.0 // indirect
|
||||||
golang.org/x/sync v0.16.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/text v0.28.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
golang.org/x/tools v0.35.0 // indirect
|
golang.org/x/tools v0.22.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/refraction-networking/utls v1.6.7 => gitlab.torproject.org/shelikhoo/utls-temporary v0.0.0-20250428152032-7f32539913c8
|
|
||||||
|
|
168
go.sum
168
go.sum
|
@ -2,34 +2,34 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.39.0 h1:xm5WV/2L4emMRmMjHFykqiA4M/ra0DJVSWUkDyBjbg4=
|
github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
|
github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.8 h1:kQjtOLlTU4m4A64TsRcqwNChhGCwaPBt+zCQt/oWsHU=
|
github.com/aws/aws-sdk-go-v2/config v1.29.6 h1:fqgqEKK5HaZVWLQoLiC9Q+xDlSp+1LYidp6ybGE2OGg=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.8/go.mod h1:QPpc7IgljrKwH0+E6/KolCgr4WPLerURiU592AYzfSY=
|
github.com/aws/aws-sdk-go-v2/config v1.29.6/go.mod h1:Ft+WLODzDQmCTHDvqAH1JfC2xxbZ0MxpZAcJqmE1LTQ=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 h1:zmc9e1q90wMn8wQbjryy8IwA6Q4XlaL9Bx2zIqdNNbk=
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.59 h1:9btwmrt//Q6JcSdgJOLI98sdr5p7tssS9yAsGe8aKP4=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.12/go.mod h1:3VzdRDR5u3sSJRI4kYcOSIBbeYsgtVk7dG5R/U6qLWY=
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.59/go.mod h1:NM8fM6ovI3zak23UISdWidyZuI1ghNe2xjzUZAyT+08=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 h1:Is2tPmieqGS2edBnmOJIbdvOA6Op+rRpaYR60iBAwXM=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 h1:UCxq0X9O3xrlENdKf1r9eRJoKz/b0AfGkpp3a7FPlhg=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 h1:Y6DTZUn7ZUC4th9FMBbo8LVE+1fyq3ofw+tRwkUd3PY=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 h1:mLgc5QIgOy26qyh5bvW+nDoAppxgn3J2WV3m9ewq7+8=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.42.5 h1:HbaHWaTkGec2pMa/UQa3+WNWtUaFFF1ZLfwCeVFtBns=
|
github.com/aws/aws-sdk-go-v2/service/sqs v1.37.14 h1:KSVbQW2umLp7i4Lo6mvBUz5PqV+Ze/IL6LCTasxQWEk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.42.5/go.mod h1:wCAPjT7bNg5+4HSNefwNEC2hM3d+NSD5w5DU/8jrPrI=
|
github.com/aws/aws-sdk-go-v2/service/sqs v1.37.14/go.mod h1:jiaEkIw2Bb6IsoY9PDAZqVXJjNaKSxQGGj10CiloDWU=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 h1:7PKX3VYsZ8LUWceVRuv0+PU+E7OtQb1lgmi5vmUE9CM=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 h1:e0XBRn3AptQotkyBFrHAxFB8mDhAIOfsG+7KyJ0dg98=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 h1:PR00NXRYgY4FWHqOGx3fC3lhVKjsp1GdloDv2ynMSd8=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc=
|
||||||
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
|
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
|
||||||
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
@ -61,8 +61,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
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.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.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
@ -72,8 +72,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno=
|
github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno=
|
||||||
|
@ -85,50 +85,50 @@ 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 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c=
|
github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c=
|
||||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
|
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
|
||||||
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
|
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
|
||||||
github.com/pion/dtls/v3 v3.0.7 h1:bItXtTYYhZwkPFk4t1n3Kkf5TDrfj6+4wG+CZR8uI9Q=
|
github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
|
||||||
github.com/pion/dtls/v3 v3.0.7/go.mod h1:uDlH5VPrgOQIw59irKYkMudSFprY9IEFCqz/eTz16f8=
|
github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
|
||||||
github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4=
|
github.com/pion/ice/v4 v4.0.7 h1:mnwuT3n3RE/9va41/9QJqN5+Bhc0H/x/ZyiVlWMw35M=
|
||||||
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
github.com/pion/ice/v4 v4.0.7/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
||||||
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4=
|
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
|
||||||
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic=
|
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
|
||||||
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
|
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
|
||||||
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
|
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
|
||||||
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||||
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
||||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
||||||
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
||||||
github.com/pion/rtp v1.8.21 h1:3yrOwmZFyUpcIosNcWRpQaU+UXIJ6yxLuJ8Bx0mw37Y=
|
github.com/pion/rtp v1.8.12 h1:nsKs8Wi0jQyBFHU3qmn/OvtZrhktVfJY0vRxwACsL5U=
|
||||||
github.com/pion/rtp v1.8.21/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
|
github.com/pion/rtp v1.8.12/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4=
|
||||||
github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE=
|
github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs=
|
||||||
github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
||||||
github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo=
|
github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI=
|
||||||
github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo=
|
github.com/pion/sdp/v3 v3.0.11/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
|
||||||
github.com/pion/srtp/v3 v3.0.7 h1:QUElw0A/FUg3MP8/KNMZB3i0m8F9XeMnTum86F7S4bs=
|
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
|
||||||
github.com/pion/srtp/v3 v3.0.7/go.mod h1:qvnHeqbhT7kDdB+OGB05KA/P067G3mm7XBfLaLiaNF0=
|
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
|
||||||
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
||||||
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||||
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||||
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||||
github.com/pion/turn/v4 v4.1.1 h1:9UnY2HB99tpDyz3cVVZguSxcqkJ1DsTSZ+8TGruh4fc=
|
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
|
||||||
github.com/pion/turn/v4 v4.1.1/go.mod h1:2123tHk1O++vmjI5VSD0awT50NywDAq5A2NNNU4Jjs8=
|
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
|
||||||
github.com/pion/webrtc/v4 v4.1.4 h1:/gK1ACGHXQmtyVVbJFQDxNoODg4eSRiFLB7t9r9pg8M=
|
github.com/pion/webrtc/v4 v4.0.13 h1:XuUaWTjRufsiGJRC+G71OgiSMe7tl7mQ0kkd4bAqIaQ=
|
||||||
github.com/pion/webrtc/v4 v4.1.4/go.mod h1:Oab9npu1iZtQRMic3K3toYq5zFPvToe/QBw7dMI2ok4=
|
github.com/pion/webrtc/v4 v4.0.13/go.mod h1:Fadzxm0CbY99YdCEfxrgiVr0L4jN1l8bf8DBkPPpJbs=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
|
||||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
@ -138,6 +138,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/realclientip/realclientip-go v1.0.0 h1:+yPxeC0mEaJzq1BfCt2h4BxlyrvIIBzR6suDc3BEF1U=
|
github.com/realclientip/realclientip-go v1.0.0 h1:+yPxeC0mEaJzq1BfCt2h4BxlyrvIIBzR6suDc3BEF1U=
|
||||||
github.com/realclientip/realclientip-go v1.0.0/go.mod h1:CXnUdVwFRcXFJIRb/dTYqbT7ud48+Pi2pFm80bxDmcI=
|
github.com/realclientip/realclientip-go v1.0.0/go.mod h1:CXnUdVwFRcXFJIRb/dTYqbT7ud48+Pi2pFm80bxDmcI=
|
||||||
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
|
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||||
|
@ -146,8 +148,12 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
|
||||||
|
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||||
|
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
|
||||||
|
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
|
||||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||||
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0=
|
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0=
|
||||||
|
@ -156,29 +162,27 @@ github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 h1:d/Wr/Vl/wiJHc
|
||||||
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301/go.mod h1:ntmMHL/xPq1WLeKiw8p/eRATaae6PiVRNipHFJxI8PM=
|
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301/go.mod h1:ntmMHL/xPq1WLeKiw8p/eRATaae6PiVRNipHFJxI8PM=
|
||||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.24 h1:0tZL4NfpoESDrhaScrZfVDnYZ/3LhyVAbN/dQ2b4hbI=
|
github.com/xtaci/kcp-go/v5 v5.6.8 h1:jlI/0jAyjoOjT/SaGB58s4bQMJiNS41A2RKzR6TMWeI=
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.24/go.mod h1:7cAxNX/qFGeRUmUSnnDMoOg53FbXDK9IWBXAUfh+aBA=
|
github.com/xtaci/kcp-go/v5 v5.6.8/go.mod h1:oE9j2NVqAkuKO5o8ByKGch3vgVX3BNf8zqP8JiGq0bM=
|
||||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||||
github.com/xtaci/smux v1.5.35 h1:RosihGJBeaS8gxOZ17HNxbhONwnqQwNwusHx4+SEGhk=
|
github.com/xtaci/smux v1.5.34 h1:OUA9JaDFHJDT8ZT3ebwLWPAgEfE6sWo2LaTy3anXqwg=
|
||||||
github.com/xtaci/smux v1.5.35/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
github.com/xtaci/smux v1.5.34/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
gitlab.torproject.org/shelikhoo/utls-temporary v0.0.0-20250428152032-7f32539913c8 h1:zZ1r9UjJ4qSPoLZG/vzITRsO0Qacpm20HlRAg7JVJ8Y=
|
|
||||||
gitlab.torproject.org/shelikhoo/utls-temporary v0.0.0-20250428152032-7f32539913c8/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
|
||||||
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01 h1:4949mHh9Vj2/okk48yG8nhP6TosFWOUfSfSr502sKGE=
|
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01 h1:4949mHh9Vj2/okk48yG8nhP6TosFWOUfSfSr502sKGE=
|
||||||
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01/go.mod h1:K3LOI4H8fa6j+7E10ViHeGEQV10304FG4j94ypmKLjY=
|
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01/go.mod h1:K3LOI4H8fa6j+7E10ViHeGEQV10304FG4j94ypmKLjY=
|
||||||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.6.0 h1:KD9m+mRBwtEdqe94Sv72uiedMWeRdIr4sXbrRyzRiIo=
|
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.6.0 h1:KD9m+mRBwtEdqe94Sv72uiedMWeRdIr4sXbrRyzRiIo=
|
||||||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.6.0/go.mod h1:70bhd4JKW/+1HLfm+TMrgHJsUHG4coelMWwiVEJ2gAg=
|
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.6.0/go.mod h1:70bhd4JKW/+1HLfm+TMrgHJsUHG4coelMWwiVEJ2gAg=
|
||||||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250815012447-418f76dcf315 h1:9lmXguW9aH5sdZR5h5jOrdInCt0tQ9NRa7+wFD4MQBk=
|
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250130151315-efaf4e0ec0d3 h1:pwWCiqrB6b3SynILsv3M+76utmcgMiTZ2aqfccjWmxo=
|
||||||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250815012447-418f76dcf315/go.mod h1:PK7EvweKeypdelDyh1m7N922aldSeCAG8n0lJ7RAXWQ=
|
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250130151315-efaf4e0ec0d3/go.mod h1:PK7EvweKeypdelDyh1m7N922aldSeCAG8n0lJ7RAXWQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
@ -186,8 +190,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -199,8 +203,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -208,8 +212,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.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/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -222,8 +226,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.30.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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
|
@ -231,8 +235,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
@ -243,8 +247,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -263,8 +267,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|
|
@ -1,28 +1,3 @@
|
||||||
FROM docker.io/library/golang:latest AS build
|
FROM golang:1.23
|
||||||
|
|
||||||
|
COPY probetest /go/bin
|
||||||
ADD . /app
|
|
||||||
|
|
||||||
WORKDIR /app/probetest
|
|
||||||
RUN go get
|
|
||||||
RUN CGO_ENABLED=0 go build -o probetest -ldflags '-extldflags "-static" -w -s' .
|
|
||||||
|
|
||||||
FROM containers.torproject.org/tpo/tpa/base-images/debian:bookworm as debian-base
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
gpg \
|
|
||||||
gpg-agent \
|
|
||||||
ca-certificates \
|
|
||||||
libcap2-bin \
|
|
||||||
--no-install-recommends
|
|
||||||
|
|
||||||
FROM scratch
|
|
||||||
|
|
||||||
COPY --from=debian-base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
|
||||||
COPY --from=debian-base /usr/share/zoneinfo /usr/share/zoneinfo
|
|
||||||
COPY --from=build /app/probetest/probetest /bin/probetest
|
|
||||||
|
|
||||||
ENTRYPOINT [ "/bin/probetest" ]
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.authors="anti-censorship-team@lists.torproject.org"
|
|
||||||
|
|
|
@ -24,13 +24,6 @@ but you should use TLS in production.
|
||||||
To build the probe server, run
|
To build the probe server, run
|
||||||
```go build```
|
```go build```
|
||||||
|
|
||||||
Or alternatively:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd .. # switch to the repo root directory or $(git rev-parse --show-toplevel)
|
|
||||||
docker build -t snowflake-probetest -f probetest/Dockerfile .
|
|
||||||
```
|
|
||||||
|
|
||||||
To deploy the probe server, first set the necessary env variables with
|
To deploy the probe server, first set the necessary env variables with
|
||||||
```
|
```
|
||||||
export HOSTNAMES=${YOUR HOSTNAMES}
|
export HOSTNAMES=${YOUR HOSTNAMES}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue