diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3d433c8..8c03390 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,3 @@ -variables: - DOCKER_REGISTRY_URL: docker.io - stages: - test - deploy @@ -9,12 +6,9 @@ stages: variables: DEBIAN_FRONTEND: noninteractive - DEBIAN_OLD_STABLE: bullseye - DEBIAN_STABLE: bookworm + DEBIAN_OLD_STABLE: buster + DEBIAN_STABLE: bullseye 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 .apt-template: &apt-template @@ -98,7 +92,7 @@ variables: # -- jobs ------------------------------------------------------------ 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: ANDROID_HOME: /usr/lib/android-sdk LANG: C.UTF-8 @@ -148,39 +142,36 @@ android: - go get golang.org/x/mobile/bind - gomobile bind -v -target=android $REPRODUCIBLE_FLAGS . -go-1.23: - image: ${DOCKER_REGISTRY_URL}/golang:1.23-$DEBIAN_STABLE +go-1.21: + image: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:golang-1.21-$DEBIAN_STABLE <<: *golang-docker-debian-template <<: *test-template script: - *go-test -go-1.24: - image: ${DOCKER_REGISTRY_URL}/golang:1.24-$DEBIAN_STABLE +go-1.23: + image: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:golang-1.23-$DEBIAN_STABLE <<: *golang-docker-debian-template <<: *test-template script: - *go-test debian-testing: - image: containers.torproject.org/tpo/tpa/base-images/debian:testing + image: debian:testing <<: *debian-native-template <<: *test-template script: - *go-test 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: - SHADOW_VERSION: "27d0bcf2cf1c7f0d403b6ad3efd575e45ae93126" + SHADOW_VERSION: "193924aae0dab30ffda0abe29467f552949849fa" TGEN_VERSION: "v1.1.2" cache: - - key: sf-integration-shadow-$SHADOW_VERSION - paths: - - opt/shadow - - key: sf-integration-tgen-$TGEN_VERSION - paths: - - opt/tgen + key: sf-integration-$SHADOW_VERSION-$TGEN_VERSION + paths: + - /opt/ artifacts: paths: - shadow.data.tar.gz @@ -190,15 +181,15 @@ shadow-integration: - tpa script: - 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/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 - pushd ~/.local/src - | - if [ ! -f $CI_PROJECT_DIR/opt/shadow/bin/shadow ] + if [ ! -f opt/shadow/bin/shadow ] then 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 @@ -207,24 +198,24 @@ shadow-integration: CONTAINER=debian:stable-slim ci/container_scripts/install_deps.sh CC=gcc CONTAINER=debian:stable-slim ci/container_scripts/install_extra_deps.sh 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 popd fi - | - if [ ! -f $CI_PROJECT_DIR/opt/tgen/bin/tgen ] + if [ ! -f opt/shadow/bin/tgen ] then 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 pushd tgen/ apt-get install -y cmake libglib2.0-dev libigraph-dev mkdir build && cd build - cmake .. -DCMAKE_INSTALL_PREFIX=$CI_PROJECT_DIR/opt/tgen + cmake .. -DCMAKE_INSTALL_PREFIX=$CI_PROJECT_DIR/opt/ make make install popd fi - install $CI_PROJECT_DIR/opt/tgen/bin/tgen ~/.local/bin/tgen + install $CI_PROJECT_DIR/opt/bin/tgen ~/.local/bin/tgen - popd # Apply snowflake patch(es) @@ -262,7 +253,7 @@ shadow-integration: generate_tarball: stage: deploy - image: ${DOCKER_REGISTRY_URL}/golang:1.22-$DEBIAN_STABLE + image: golang:1.21-$DEBIAN_STABLE rules: - if: $CI_COMMIT_TAG script: @@ -307,6 +298,7 @@ build-container: matrix: - ARCH: amd64 - ARCH: arm64 + - ARCH: s390x tags: - $ARCH image: @@ -331,7 +323,7 @@ merge-manifests: - job: build-container artifacts: false image: - name: ${DOCKER_REGISTRY_URL}/mplatform/manifest-tool:alpine + name: containers.torproject.org/tpo/anti-censorship/duplicatedcontainerimages:mplatform-manifest-tool-alpine entrypoint: [""] script: - if [ $CI_COMMIT_REF_NAME == "main" ]; then export TAG='nightly'; fi @@ -340,7 +332,7 @@ merge-manifests: --username="${CI_REGISTRY_USER}" --password="${CI_REGISTRY_PASSWORD}" push from-args - --platforms linux/amd64,linux/arm64 + --platforms linux/amd64,linux/arm64,linux/s390x --template "${CI_REGISTRY_IMAGE}:${TAG}_ARCH" --target "${CI_REGISTRY_IMAGE}:${TAG}" rules: diff --git a/Dockerfile b/Dockerfile index 77673dd..f0344f4 100644 --- a/Dockerfile +++ b/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 @@ -7,44 +17,11 @@ WORKDIR /app/proxy RUN go get 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: -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 -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=debian-base /usr/share/tor/geoip* /usr/share/tor/ +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo +COPY --from=build /usr/share/tor/geoip* /usr/share/tor/ COPY --from=build /app/proxy/proxy /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" diff --git a/broker/amp.go b/broker/amp.go index 4d19a6c..99289de 100644 --- a/broker/amp.go +++ b/broker/amp.go @@ -1,14 +1,13 @@ package main import ( - "context" "log" "net/http" "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/messages" + "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util" ) // 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 // encoded client poll response is sent back as AMP-armored HTML. 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/ // 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/") if path == r.URL.Path { // 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 { arg := messages.Arg{ Body: encPollReq, - RemoteAddr: "", + RemoteAddr: util.GetClientIp(r), RendezvousMethod: messages.RendezvousAmpCache, - Context: ctx, } err = i.ClientOffers(arg, &response) } else { diff --git a/broker/broker.go b/broker/broker.go index 8351d8f..a1252b7 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -44,8 +44,9 @@ type BrokerContext struct { proxyPolls chan *ProxyPoll metrics *Metrics - bridgeList BridgeListHolderFileBased - allowedRelayPattern string + bridgeList BridgeListHolderFileBased + allowedRelayPattern string + presumedPatternForLegacyClient string } func (ctx *BrokerContext) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprint) (BridgeInfo, error) { @@ -54,7 +55,8 @@ func (ctx *BrokerContext) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprin func NewBrokerContext( metricsLogger *log.Logger, - allowedRelayPattern string, + allowedRelayPattern, + presumedPatternForLegacyClient string, ) *BrokerContext { snowflakes := new(SnowflakeHeap) heap.Init(snowflakes) @@ -77,13 +79,14 @@ func NewBrokerContext( bridgeListHolder.LoadBridgeInfo(bytes.NewReader([]byte(DefaultBridges))) return &BrokerContext{ - snowflakes: snowflakes, - restrictedSnowflakes: rSnowflakes, - idToSnowflake: make(map[string]*Snowflake), - proxyPolls: make(chan *ProxyPoll), - metrics: metrics, - bridgeList: bridgeListHolder, - allowedRelayPattern: allowedRelayPattern, + snowflakes: snowflakes, + restrictedSnowflakes: rSnowflakes, + idToSnowflake: make(map[string]*Snowflake), + proxyPolls: make(chan *ProxyPoll), + metrics: metrics, + bridgeList: bridgeListHolder, + 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 { if nonSupported { - return false + pattern = ctx.presumedPatternForLegacyClient } proxyPattern := namematcher.NewNameMatcher(pattern) brokerPattern := namematcher.NewNameMatcher(ctx.allowedRelayPattern) @@ -194,7 +197,7 @@ func main() { var addr string var geoipDatabase string var geoip6Database string - var bridgeListFilePath, allowedRelayPattern string + var bridgeListFilePath, allowedRelayPattern, presumedPatternForLegacyClient string var brokerSQSQueueName, brokerSQSQueueRegion string var disableTLS bool 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(&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(&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(&brokerSQSQueueRegion, "broker-sqs-region", "", "name of AWS region of broker SQS queue") flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS") @@ -244,7 +248,7 @@ func main() { metricsLogger := log.New(metricsFile, "", 0) - ctx := NewBrokerContext(metricsLogger, allowedRelayPattern) + ctx := NewBrokerContext(metricsLogger, allowedRelayPattern, presumedPatternForLegacyClient) if bridgeListFilePath != "" { bridgeListFile, err := os.Open(bridgeListFilePath) diff --git a/broker/http.go b/broker/http.go index ed8e24a..b6f449d 100644 --- a/broker/http.go +++ b/broker/http.go @@ -2,14 +2,12 @@ package main import ( "bytes" - "context" "errors" "fmt" "io" "log" "net/http" "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/util" @@ -134,9 +132,6 @@ snowflake proxy, which responds with the SDP answer to be sent in the HTTP response back to the client. */ 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)) if err != nil { 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, RemoteAddr: util.GetClientIp(r), RendezvousMethod: messages.RendezvousHttp, - Context: ctx, } var response []byte @@ -220,6 +214,7 @@ func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) { err = validateSDP(body) if err != nil { + log.Println("Error proxy SDP: ", err.Error()) w.WriteHeader(http.StatusBadRequest) return } diff --git a/broker/ipc.go b/broker/ipc.go index d58baec..64cefcd 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -5,10 +5,10 @@ import ( "encoding/hex" "fmt" "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/constants" - "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util" "github.com/prometheus/client_golang/prometheus" "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 { - 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.lock.Unlock() } 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.lock.Unlock() } 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.lock.Unlock() + log.Printf("bad request: rejected relay pattern from proxy = %v", messages.ErrBadRequest) b, err := messages.EncodePollResponseWithRelayURL("", false, "", "", "incorrect relay pattern") *response = b if err != nil { @@ -98,7 +105,9 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { if err != nil { log.Println("Warning: cannot process proxy IP: ", err.Error()) } 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 @@ -107,8 +116,10 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { offer := i.ctx.RequestOffer(sid, proxyType, natType, clients) if offer == nil { - i.ctx.metrics.IncrementCounter("proxy-idle") - i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "type": proxyType, "status": "idle"}).Inc() + i.ctx.metrics.lock.Lock() + 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, "") if err != nil { @@ -119,7 +130,7 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { 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 bridgeFingerprint, err := bridgefingerprint.FingerprintFromBytes(offer.fingerprint) 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 { + startTime := time.Now() + req, err := messages.DecodeClientPollRequest(arg.Body) if err != nil { 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{ natType: req.NAT, sdp: []byte(req.Offer), @@ -198,7 +198,9 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error { if snowflake != nil { snowflake.offerChannel <- offer } 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} 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. select { 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} err = sendClientResponse(resp, response) - case <-arg.Context.Done(): - i.ctx.metrics.UpdateClientStats(remoteAddr, arg.RendezvousMethod, offer.natType, "timeout") + // Initial tracking of elapsed time. + 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} 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 // disappeared / the snowflake is no longer recognized by the Broker. success = false - i.ctx.metrics.promMetrics.ProxyAnswerTotal.With(prometheus.Labels{"type": "", "status": "timeout"}).Inc() } b, err := messages.EncodeAnswerResponse(success) @@ -264,7 +273,6 @@ func (i *IPC) ProxyAnswers(arg messages.Arg, response *[]byte) error { *response = b if success { - i.ctx.metrics.promMetrics.ProxyAnswerTotal.With(prometheus.Labels{"type": snowflake.proxyType, "status": "success"}).Inc() snowflake.answerChannel <- answer } diff --git a/broker/metrics.go b/broker/metrics.go index c1d4e4a..53f315b 100644 --- a/broker/metrics.go +++ b/broker/metrics.go @@ -8,11 +8,10 @@ package main import ( "fmt" "log" + "math" "net" "sort" - "strings" "sync" - "sync/atomic" "time" "github.com/prometheus/client_golang/prometheus" @@ -26,105 +25,130 @@ const ( 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 { logger *log.Logger geoipdb *geoip.Geoip - ips *sync.Map // proxy IP addresses we've seen before - counters *sync.Map // counters for ip-based metrics + countryStats CountryStats + 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 - proxies *sync.Map // ip-based counts of proxy country codes - clientHTTPPolls *sync.Map // poll-based counts of client HTTP rendezvous - clientAMPPolls *sync.Map // poll-based counts of client AMP cache rendezvous - clientSQSPolls *sync.Map // poll-based counts of client SQS rendezvous + rendezvousCountryStats map[messages.RendezvousMethod]map[string]int + + proxyPollWithRelayURLExtension uint + proxyPollWithoutRelayURLExtension uint + proxyPollRejectedWithRelayURLExtension uint + + // synchronization for access to snowflake metrics + lock sync.Mutex promMetrics *PromMetrics } -func NewMetrics(metricsLogger *log.Logger) (*Metrics, error) { - m := new(Metrics) - - m.logger = metricsLogger - m.promMetrics = initPrometheus() - m.ips = new(sync.Map) - m.counters = new(sync.Map) - m.proxies = new(sync.Map) - m.clientHTTPPolls = new(sync.Map) - m.clientAMPPolls = new(sync.Map) - m.clientSQSPolls = new(sync.Map) - - // Write to log file every day with updated metrics - go m.logMetrics() - - return m, nil +type record struct { + cc string + count int } +type records []record -func incrementMapCounter(counters *sync.Map, key string) { - start := uint64(1) - val, loaded := counters.LoadOrStore(key, &start) - if loaded { - ptr := val.(*uint64) - atomic.AddUint64(ptr, 1) +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 { + if r[i].count == r[j].count { + return r[i].cc > r[j].cc } + return r[i].count < r[j].count } -func (m *Metrics) IncrementCounter(key string) { - incrementMapCounter(m.counters, key) +func (s CountryStats) Display() string { + output := "" + + // Use the records struct to sort our counts map by value. + rs := records{} + 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) + } + + // cut off trailing "," + if len(output) > 0 { + return output[:len(output)-1] + } + + return output } -func (m *Metrics) UpdateProxyStats(addr string, proxyType string, natType string) { +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) if m.geoipdb == nil { return } - country, ok := m.geoipdb.GetCountryByAddr(ip) + country, ok = m.geoipdb.GetCountryByAddr(ip) if !ok { 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{ - "nat": natType, - "type": proxyType, - "cc": country, - }).Inc() - } + m.promMetrics.ProxyTotal.With(prometheus.Labels{ + "nat": natType, + "type": proxyType, + "cc": country, + }).Inc() - // update unique IP proxy NAT metrics - key := fmt.Sprintf("%s-%s", addr, natType) - if _, loaded := m.ips.LoadOrStore(key, true); !loaded { - switch natType { - case NATRestricted: - m.IncrementCounter("proxy-nat-restricted") - case NATUnrestricted: - m.IncrementCounter("proxy-nat-unrestricted") - default: - m.IncrementCounter("proxy-nat-unknown") - } - } - // 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") - } + switch natType { + case NATRestricted: + m.countryStats.natRestricted[addr] = true + case NATUnrestricted: + m.countryStats.natUnrestricted[addr] = true + default: + m.countryStats.natUnknown[addr] = true } } -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) country := "??" if m.geoipdb != nil { @@ -136,31 +160,20 @@ func (m *Metrics) UpdateClientStats(addr string, rendezvousMethod messages.Rende switch status { case "denied": - m.IncrementCounter("client-denied") + m.clientDeniedCount[rendezvousMethod]++ if natType == NATUnrestricted { - m.IncrementCounter("client-unrestricted-denied") + m.clientUnrestrictedDeniedCount[rendezvousMethod]++ } else { - m.IncrementCounter("client-restricted-denied") + m.clientRestrictedDeniedCount[rendezvousMethod]++ } case "matched": - m.IncrementCounter("client-match") + m.clientProxyMatchCount[rendezvousMethod]++ case "timeout": - m.IncrementCounter("client-timeout") + m.clientProxyTimeoutCount[rendezvousMethod]++ default: log.Printf("Unknown rendezvous status: %s", status) } - - 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.rendezvousCountryStats[rendezvousMethod][country]++ m.promMetrics.ClientPollTotal.With(prometheus.Labels{ "nat": natType, "status": status, @@ -169,51 +182,25 @@ func (m *Metrics) UpdateClientStats(addr string, rendezvousMethod messages.Rende }).Inc() } -// Types to facilitate sorting in formatAndClearCountryStats. -type record struct { - cc string - count uint64 -} -type records []record +func (m *Metrics) DisplayRendezvousStatsByCountry(rendezvoudMethod messages.RendezvousMethod) string { + output := "" -// Implementation of sort.Interface for records. The ordering is lexicographic: -// 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. + // Use the records struct to sort our counts map by value. rs := records{} - m.Range(func(cc, countPtr any) bool { - count := *countPtr.(*uint64) - if binned { - count = binCount(count) - } - rs = append(rs, record{cc: cc.(string), count: count}) - m.Delete(cc) - return true - }) - // 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) + for cc, count := range m.rendezvousCountryStats[rendezvoudMethod] { + rs = append(rs, record{cc: cc, count: count}) } - return output.String() + sort.Sort(sort.Reverse(rs)) + for _, r := range rs { + output += fmt.Sprintf("%s=%d,", r.cc, binCount(uint(r.count))) + } + + // cut off trailing "," + if len(output) > 0 { + return output[:len(output)-1] + } + + return output } func (m *Metrics) LoadGeoipDatabases(geoipDB string, geoip6DB string) error { @@ -225,64 +212,126 @@ func (m *Metrics) LoadGeoipDatabases(geoipDB string, geoip6DB string) error { 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 func (m *Metrics) logMetrics() { heartbeat := time.Tick(metricsResolution) for range heartbeat { 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() { + m.lock.Lock() m.logger.Println( "snowflake-stats-end", time.Now().UTC().Format("2006-01-02 15:04:05"), fmt.Sprintf("(%d s)", int(metricsResolution.Seconds())), ) - m.logger.Println("snowflake-ips", formatAndClearCountryStats(m.proxies, false)) - m.logger.Printf("snowflake-ips-iptproxy %d\n", m.loadAndZero("proxy-iptproxy")) - m.logger.Printf("snowflake-ips-standalone %d\n", m.loadAndZero("proxy-standalone")) - m.logger.Printf("snowflake-ips-webext %d\n", m.loadAndZero("proxy-webext")) - m.logger.Printf("snowflake-ips-badge %d\n", m.loadAndZero("proxy-badge")) - 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-proxy-poll-with-relay-url-count", binCount(m.loadAndZero("proxy-poll-with-relay-url"))) - m.logger.Println("snowflake-proxy-poll-without-relay-url-count", binCount(m.loadAndZero("proxy-poll-without-relay-url"))) - m.logger.Println("snowflake-proxy-rejected-for-relay-url-count", binCount(m.loadAndZero("proxy-poll-rejected-relay-url"))) + m.logger.Println("snowflake-ips", m.countryStats.Display()) + total := len(m.countryStats.unknown) + for pType, addresses := range m.countryStats.proxies { + m.logger.Printf("snowflake-ips-%s %d\n", pType, len(addresses)) + total += len(addresses) + } + m.logger.Println("snowflake-ips-total", total) + m.logger.Println("snowflake-idle-count", binCount(m.proxyIdleCount)) + m.logger.Println("snowflake-proxy-poll-with-relay-url-count", binCount(m.proxyPollWithRelayURLExtension)) + 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-restricted-denied-count", binCount(m.loadAndZero("client-restricted-denied"))) - m.logger.Println("client-unrestricted-denied-count", binCount(m.loadAndZero("client-unrestricted-denied"))) - m.logger.Println("client-snowflake-match-count", binCount(m.loadAndZero("client-match"))) - m.logger.Println("client-snowflake-timeout-count", binCount(m.loadAndZero("client-timeout"))) + m.logger.Println("client-denied-count", binCount(sumMapValues(&m.clientDeniedCount))) + m.logger.Println("client-restricted-denied-count", binCount(sumMapValues(&m.clientRestrictedDeniedCount))) + m.logger.Println("client-unrestricted-denied-count", binCount(sumMapValues(&m.clientUnrestrictedDeniedCount))) + m.logger.Println("client-snowflake-match-count", binCount(sumMapValues(&m.clientProxyMatchCount))) + m.logger.Println("client-snowflake-timeout-count", binCount(sumMapValues(&m.clientProxyTimeoutCount))) - m.logger.Printf("client-http-count %d\n", binCount(m.loadAndZero("client-http"))) - m.logger.Printf("client-http-ips %s\n", formatAndClearCountryStats(m.clientHTTPPolls, true)) - m.logger.Printf("client-ampcache-count %d\n", binCount(m.loadAndZero("client-amp"))) - 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-sqs-ips %s\n", formatAndClearCountryStats(m.clientSQSPolls, true)) + for _, rendezvousMethod := range rendezvoudMethodList { + m.logger.Printf("client-%s-count %d\n", rendezvousMethod, binCount( + m.clientDeniedCount[rendezvousMethod]+m.clientProxyMatchCount[rendezvousMethod], + )) + m.logger.Printf("client-%s-ips %s\n", rendezvousMethod, m.DisplayRendezvousStatsByCountry(rendezvousMethod)) + } - 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() + m.logger.Println("snowflake-ips-nat-restricted", len(m.countryStats.natRestricted)) + m.logger.Println("snowflake-ips-nat-unrestricted", len(m.countryStats.natUnrestricted)) + m.logger.Println("snowflake-ips-nat-unknown", len(m.countryStats.natUnknown)) + m.lock.Unlock() } -// binCount rounds count up to the next multiple of 8. Returns 0 on integer -// overflow. -func binCount(count uint64) uint64 { - return (count + 7) / 8 * 8 +// 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 { @@ -290,7 +339,6 @@ type PromMetrics struct { ProxyTotal *prometheus.CounterVec ProxyPollTotal *safeprom.CounterVec ClientPollTotal *safeprom.CounterVec - ProxyAnswerTotal *safeprom.CounterVec AvailableProxies *prometheus.GaugeVec ProxyPollWithRelayURLExtensionTotal *safeprom.CounterVec @@ -329,16 +377,7 @@ func initPrometheus() *PromMetrics { Name: "rounded_proxy_poll_total", Help: "The number of snowflake proxy polls, rounded up to a multiple of 8", }, - []string{"nat", "type", "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"}, + []string{"nat", "status"}, ) promMetrics.ProxyPollWithRelayURLExtensionTotal = safeprom.NewCounterVec( @@ -380,7 +419,7 @@ func initPrometheus() *PromMetrics { // We need to register our metrics so they can be exported. promMetrics.registry.MustRegister( promMetrics.ClientPollTotal, promMetrics.ProxyPollTotal, - promMetrics.ProxyTotal, promMetrics.ProxyAnswerTotal, promMetrics.AvailableProxies, + promMetrics.ProxyTotal, promMetrics.AvailableProxies, promMetrics.ProxyPollWithRelayURLExtensionTotal, promMetrics.ProxyPollWithoutRelayURLExtensionTotal, promMetrics.ProxyPollRejectedForRelayURLExtensionTotal, diff --git a/broker/metrics_test.go b/broker/metrics_test.go deleted file mode 100644 index fad1958..0000000 --- a/broker/metrics_test.go +++ /dev/null @@ -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") }) - }) -} diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go index 3eb4ba5..2f6f9e1 100644 --- a/broker/snowflake-broker_test.go +++ b/broker/snowflake-broker_test.go @@ -3,8 +3,6 @@ package main import ( "bytes" "container/heap" - "crypto/rand" - "encoding/base64" "encoding/hex" "fmt" "io" @@ -12,7 +10,6 @@ import ( "net/http" "net/http/httptest" "os" - "strings" "sync" "testing" "time" @@ -49,8 +46,7 @@ var ( "a=candidate:1000 1 udp 2000 8.8.8.8 3000 typ host\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" ) func createClientOffer(sdp, nat, fingerprint string) (*bytes.Reader, error) { @@ -93,7 +89,7 @@ func TestBroker(t *testing.T) { Convey("Context", t, func() { buf := new(bytes.Buffer) - ctx := NewBrokerContext(log.New(buf, "", 0), "snowflake.torproject.net") + ctx := NewBrokerContext(log.New(buf, "", 0), "", "") i := &IPC{ctx} Convey("Adds Snowflake", func() { @@ -406,45 +402,12 @@ client-sqs-ips 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() { done := make(chan bool) 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) So(err, ShouldBeNil) @@ -530,7 +493,7 @@ client-sqs-ips }) Convey("End-To-End", t, func() { - ctx := NewBrokerContext(NullLogger(), "snowflake.torproject.net") + ctx := NewBrokerContext(NullLogger(), "", "") i := &IPC{ctx} Convey("Check for client/proxy data race", func() { @@ -541,7 +504,7 @@ client-sqs-ips // Make proxy poll 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) So(err, ShouldBeNil) @@ -586,7 +549,7 @@ client-sqs-ips polled := make(chan bool) // 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() rP, err := http.NewRequest("POST", "snowflake.broker/proxy", dataP) So(err, ShouldBeNil) @@ -683,11 +646,11 @@ func TestSnowflakeHeap(t *testing.T) { func TestInvalidGeoipFile(t *testing.T) { Convey("Geoip", t, func() { // 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 { 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) }) @@ -697,7 +660,7 @@ func TestMetrics(t *testing.T) { Convey("Test metrics...", t, func() { done := make(chan bool) buf := new(bytes.Buffer) - ctx := NewBrokerContext(log.New(buf, "", 0), "snowflake.torproject.net") + ctx := NewBrokerContext(log.New(buf, "", 0), "", "") i := &IPC{ctx} err := ctx.metrics.LoadGeoipDatabases("test_geoip", "test_geoip6") @@ -706,9 +669,9 @@ func TestMetrics(t *testing.T) { //Test addition of proxy polls Convey("for proxy polls", func() { 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.RemoteAddr = "129.97.208.23" //CA geoip + r.RemoteAddr = "129.97.208.23:8888" //CA geoip So(err, ShouldBeNil) go func(i *IPC) { proxyPolls(i, w, r) @@ -719,9 +682,9 @@ func TestMetrics(t *testing.T) { <-done 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.RemoteAddr = "129.97.208.24" //CA geoip + r.RemoteAddr = "129.97.208.23:8888" //CA geoip So(err, ShouldBeNil) go func(i *IPC) { proxyPolls(i, w, r) @@ -732,9 +695,9 @@ func TestMetrics(t *testing.T) { <-done 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.RemoteAddr = "129.97.208.25" //CA geoip + r.RemoteAddr = "129.97.208.23:8888" //CA geoip So(err, ShouldBeNil) go func(i *IPC) { proxyPolls(i, w, r) @@ -745,9 +708,9 @@ func TestMetrics(t *testing.T) { <-done 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.RemoteAddr = "129.97.208.26" //CA geoip + r.RemoteAddr = "129.97.208.23:8888" //CA geoip So(err, ShouldBeNil) go func(i *IPC) { proxyPolls(i, w, r) @@ -765,8 +728,8 @@ func TestMetrics(t *testing.T) { So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-webext 1\n") So(metricsStr, ShouldEndWith, `snowflake-ips-total 4 snowflake-idle-count 8 -snowflake-proxy-poll-with-relay-url-count 8 -snowflake-proxy-poll-without-relay-url-count 0 +snowflake-proxy-poll-with-relay-url-count 0 +snowflake-proxy-poll-without-relay-url-count 8 snowflake-proxy-rejected-for-relay-url-count 0 client-denied-count 0 client-restricted-denied-count 0 @@ -781,7 +744,7 @@ client-sqs-count 0 client-sqs-ips snowflake-ips-nat-restricted 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 buf.Reset() + ctx.metrics.zeroMetrics() ctx.metrics.printMetrics() So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips \n") So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-standalone 0\n") @@ -917,6 +881,9 @@ snowflake-ips-nat-unknown 0 So(err, ShouldBeNil) 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() data, err = createClientOffer(sdp, NATRestricted, "") So(err, ShouldBeNil) @@ -932,7 +899,7 @@ snowflake-ips-nat-unknown 0 //Test unique ip Convey("proxy counts by unique ip", func() { 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.RemoteAddr = "129.97.208.23:8888" //CA geoip So(err, ShouldBeNil) @@ -944,7 +911,7 @@ snowflake-ips-nat-unknown 0 p.offerChannel <- nil <-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) if err != nil { log.Printf("unable to get NewRequest with error: %v", err) @@ -966,7 +933,7 @@ snowflake-ips-nat-unknown 0 //Test NAT types Convey("proxy counts by NAT type", func() { 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.RemoteAddr = "129.97.208.23:8888" //CA geoip So(err, ShouldBeNil) @@ -978,7 +945,10 @@ snowflake-ips-nat-unknown 0 p.offerChannel <- nil <-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) if err != nil { 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") buf.Reset() + ctx.metrics.zeroMetrics() data, err = createClientOffer(sdp, NATUnrestricted, "") 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") buf.Reset() + ctx.metrics.zeroMetrics() data, err = createClientOffer(sdp, NATUnknown, "") So(err, ShouldBeNil) @@ -1032,105 +1004,20 @@ snowflake-ips-nat-unknown 0 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") }) - Convey("that seen IPs map is cleared after each print", 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() + Convey("for country stats order", func() { - }) - }) -} - -func TestConcurrency(t *testing.T) { - Convey("Test concurency with", t, func() { - ctx := NewBrokerContext(NullLogger(), "snowflake.torproject.net") - i := &IPC{ctx} - Convey("multiple simultaneous polls", func(c C) { - go ctx.Broker() - - var proxies sync.WaitGroup - 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() + stats := map[string]int{ + "IT": 50, + "FR": 200, + "TZ": 100, + "CN": 250, + "RU": 150, + "CA": 1, + "BE": 1, + "PH": 1, + } + ctx.metrics.countryStats.counts = stats + So(ctx.metrics.countryStats.Display(), ShouldEqual, "CN=250,FR=200,RU=150,TZ=100,IT=50,BE=1,CA=1,PH=1") }) }) } diff --git a/broker/sqs.go b/broker/sqs.go index 16a97c9..fb1164e 100644 --- a/broker/sqs.go +++ b/broker/sqs.go @@ -12,6 +12,7 @@ import ( "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/sqsclient" + "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util" ) 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 response []byte var err error - ctx, cancel := context.WithTimeout(mainCtx, ClientTimeout*time.Second) - defer cancel() - clientID := message.MessageAttributes["ClientID"].StringValue if clientID == nil { log.Println("SQSHandler: got SDP offer in SQS message with no client ID. ignoring this message.") return } - res, err := r.SQSClient.CreateQueue(ctx, &sqs.CreateQueueInput{ + res, err := r.SQSClient.CreateQueue(context, &sqs.CreateQueueInput{ QueueName: aws.String("snowflake-client-" + *clientID), }) if err != nil { @@ -147,11 +146,27 @@ func (r *sqsHandler) handleMessage(mainCtx context.Context, message *types.Messa 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{ Body: encPollReq, - RemoteAddr: "", + RemoteAddr: remoteAddr, RendezvousMethod: messages.RendezvousSqs, - Context: ctx, } err = r.IPC.ClientOffers(arg, &response) @@ -160,7 +175,7 @@ func (r *sqsHandler) handleMessage(mainCtx context.Context, message *types.Messa return } - r.SQSClient.SendMessage(ctx, &sqs.SendMessageInput{ + r.SQSClient.SendMessage(context, &sqs.SendMessageInput{ QueueUrl: answerSQSURL, MessageBody: aws.String(string(response)), }) diff --git a/broker/sqs_test.go b/broker/sqs_test.go index 59fe701..33e38f1 100644 --- a/broker/sqs_test.go +++ b/broker/sqs_test.go @@ -23,7 +23,7 @@ func TestSQS(t *testing.T) { Convey("Context", t, func() { buf := new(bytes.Buffer) - ipcCtx := NewBrokerContext(log.New(buf, "", 0), "") + ipcCtx := NewBrokerContext(log.New(buf, "", 0), "", "") i := &IPC{ipcCtx} Convey("Responds to SQS client offers...", func() { @@ -138,7 +138,7 @@ func TestSQS(t *testing.T) { sqsHandlerContext, sqsCancelFunc := context.WithCancel(context.Background()) 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) { n := numTimes.Add(1) @@ -153,11 +153,11 @@ func TestSQS(t *testing.T) { return nil, errors.New("error") }) - mockSQSClient.EXPECT().CreateQueue(gomock.Any(), &sqsCreateQueueInput).Return(&sqs.CreateQueueOutput{ + mockSQSClient.EXPECT().CreateQueue(sqsHandlerContext, &sqsCreateQueueInput).Return(&sqs.CreateQueueOutput{ QueueUrl: responseQueueURL, }, nil).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) { c.So(input.MessageBody, ShouldEqual, aws.String("{\"answer\":\"fake answer\"}")) // Ensure that match is correctly recorded in metrics diff --git a/client/README.md b/client/README.md index 2cbfb8f..1529e8d 100644 --- a/client/README.md +++ b/client/README.md @@ -35,18 +35,7 @@ UseBridges 1 ClientTransportPlugin snowflake exec ./client -log snowflake.log -# CDN77 - -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 +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 ``` `fingerprint=` is the fingerprint of bridge that the client will ultimately be connecting to. diff --git a/common/messages/ipc.go b/common/messages/ipc.go index 91eccdb..2a61b9d 100644 --- a/common/messages/ipc.go +++ b/common/messages/ipc.go @@ -1,7 +1,6 @@ package messages import ( - "context" "errors" ) @@ -17,7 +16,6 @@ type Arg struct { Body []byte RemoteAddr string RendezvousMethod RendezvousMethod - Context context.Context } var ( diff --git a/common/turbotunnel/queuepacketconn_test.go b/common/turbotunnel/queuepacketconn_test.go index 0ff19b2..30e69e3 100644 --- a/common/turbotunnel/queuepacketconn_test.go +++ b/common/turbotunnel/queuepacketconn_test.go @@ -150,13 +150,6 @@ func (c *TranscriptPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) { 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 // PacketConn, allocating source buffers in a sync.Pool. // @@ -215,16 +208,15 @@ func TestQueuePacketConnWriteToKCP(t *testing.T) { 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() if err != nil { 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") } diff --git a/doc/broker-spec.txt b/doc/broker-spec.txt index b502cb4..3f9ce7a 100644 --- a/doc/broker-spec.txt +++ b/doc/broker-spec.txt @@ -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 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 [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 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 [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 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 [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 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 [At most once.] diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index e8d8724..0000000 --- a/docker-compose.yml +++ /dev/null @@ -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 diff --git a/go.mod b/go.mod index 6fbbf38..622c4cc 100644 --- a/go.mod +++ b/go.mod @@ -1,48 +1,48 @@ module gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2 -go 1.23.0 +go 1.21 require ( - github.com/aws/aws-sdk-go-v2 v1.39.0 - github.com/aws/aws-sdk-go-v2/config v1.31.8 - github.com/aws/aws-sdk-go-v2/credentials v1.18.12 - github.com/aws/aws-sdk-go-v2/service/sqs v1.42.5 + github.com/aws/aws-sdk-go-v2 v1.36.1 + github.com/aws/aws-sdk-go-v2/config v1.29.6 + github.com/aws/aws-sdk-go-v2/credentials v1.17.59 + github.com/aws/aws-sdk-go-v2/service/sqs v1.37.14 github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.3 - github.com/miekg/dns v1.1.65 - github.com/pion/ice/v4 v4.0.10 - github.com/pion/sdp/v3 v3.0.16 + github.com/miekg/dns v1.1.63 + github.com/pion/ice/v4 v4.0.7 + github.com/pion/sdp/v3 v3.0.11 github.com/pion/stun/v3 v3.0.0 github.com/pion/transport/v3 v3.0.7 - github.com/pion/webrtc/v4 v4.1.4 - github.com/prometheus/client_golang v1.22.0 + github.com/pion/webrtc/v4 v4.0.13 + github.com/prometheus/client_golang v1.21.0 github.com/realclientip/realclientip-go v1.0.0 github.com/refraction-networking/utls v1.6.7 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/xtaci/kcp-go/v5 v5.6.24 - github.com/xtaci/smux v1.5.35 + github.com/xtaci/kcp-go/v5 v5.6.8 + 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/pluggable-transports/goptlib v1.6.0 - gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250815012447-418f76dcf315 - golang.org/x/crypto v0.41.0 - golang.org/x/net v0.42.0 - golang.org/x/sys v0.35.0 + gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil v0.0.0-20250130151315-efaf4e0ec0d3 + golang.org/x/crypto v0.33.0 + golang.org/x/net v0.35.0 + golang.org/x/sys v0.30.0 ) require ( 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/internal/configsources v1.4.7 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // 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/presigned-url v1.13.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect - github.com/aws/smithy-go v1.23.0 // 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.3.32 // 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.2 // 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.12.13 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect @@ -50,37 +50,37 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/gopherjs/gopherjs v1.17.2 // 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/reedsolomon v1.12.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pion/datachannel v1.5.10 // indirect - github.com/pion/dtls/v3 v3.0.7 // indirect - github.com/pion/interceptor v0.1.40 // indirect - github.com/pion/logging v0.2.4 // indirect + github.com/pion/dtls/v3 v3.0.4 // indirect + github.com/pion/interceptor v0.1.37 // indirect + github.com/pion/logging v0.2.3 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.15 // indirect - github.com/pion/rtp v1.8.21 // indirect - github.com/pion/sctp v1.8.39 // indirect - github.com/pion/srtp/v3 v3.0.7 // indirect - github.com/pion/turn/v4 v4.1.1 // indirect + github.com/pion/rtp v1.8.12 // indirect + github.com/pion/sctp v1.8.37 // indirect + github.com/pion/srtp/v3 v3.0.4 // indirect + github.com/pion/turn/v4 v4.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // 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/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect github.com/wlynxg/anet v0.0.5 // indirect - golang.org/x/mod v0.26.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/tools v0.35.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.22.0 // indirect + google.golang.org/protobuf v1.36.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 diff --git a/go.sum b/go.sum index 2e54433..d0bc507 100644 --- a/go.sum +++ b/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/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= 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.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= -github.com/aws/aws-sdk-go-v2/config v1.31.8 h1:kQjtOLlTU4m4A64TsRcqwNChhGCwaPBt+zCQt/oWsHU= -github.com/aws/aws-sdk-go-v2/config v1.31.8/go.mod h1:QPpc7IgljrKwH0+E6/KolCgr4WPLerURiU592AYzfSY= -github.com/aws/aws-sdk-go-v2/credentials v1.18.12 h1:zmc9e1q90wMn8wQbjryy8IwA6Q4XlaL9Bx2zIqdNNbk= -github.com/aws/aws-sdk-go-v2/credentials v1.18.12/go.mod h1:3VzdRDR5u3sSJRI4kYcOSIBbeYsgtVk7dG5R/U6qLWY= -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.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w= -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.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k= -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.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8= -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.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -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.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= -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.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA= -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.42.5/go.mod h1:wCAPjT7bNg5+4HSNefwNEC2hM3d+NSD5w5DU/8jrPrI= -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.29.3/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY= -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.34.4/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 h1:PR00NXRYgY4FWHqOGx3fC3lhVKjsp1GdloDv2ynMSd8= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8= -github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= -github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= +github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= +github.com/aws/aws-sdk-go-v2/config v1.29.6 h1:fqgqEKK5HaZVWLQoLiC9Q+xDlSp+1LYidp6ybGE2OGg= +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.17.59 h1:9btwmrt//Q6JcSdgJOLI98sdr5p7tssS9yAsGe8aKP4= +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.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso= +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.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA= +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.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4= +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.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= +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.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= +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.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw= +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.37.14 h1:KSVbQW2umLp7i4Lo6mvBUz5PqV+Ze/IL6LCTasxQWEk= +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.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ= +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.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE= +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.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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.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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 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= @@ -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/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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +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/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 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/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +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/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/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 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/dtls/v3 v3.0.7 h1:bItXtTYYhZwkPFk4t1n3Kkf5TDrfj6+4wG+CZR8uI9Q= -github.com/pion/dtls/v3 v3.0.7/go.mod h1:uDlH5VPrgOQIw59irKYkMudSFprY9IEFCqz/eTz16f8= -github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= -github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= -github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= -github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= -github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= -github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= +github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= +github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= +github.com/pion/ice/v4 v4.0.7 h1:mnwuT3n3RE/9va41/9QJqN5+Bhc0H/x/ZyiVlWMw35M= +github.com/pion/ice/v4 v4.0.7/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= +github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +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/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= 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/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= 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.21/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= -github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= -github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= -github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo= -github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo= -github.com/pion/srtp/v3 v3.0.7 h1:QUElw0A/FUg3MP8/KNMZB3i0m8F9XeMnTum86F7S4bs= -github.com/pion/srtp/v3 v3.0.7/go.mod h1:qvnHeqbhT7kDdB+OGB05KA/P067G3mm7XBfLaLiaNF0= +github.com/pion/rtp v1.8.12 h1:nsKs8Wi0jQyBFHU3qmn/OvtZrhktVfJY0vRxwACsL5U= +github.com/pion/rtp v1.8.12/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= +github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs= +github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI= +github.com/pion/sdp/v3 v3.0.11/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= +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/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/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.1.1 h1:9UnY2HB99tpDyz3cVVZguSxcqkJ1DsTSZ+8TGruh4fc= -github.com/pion/turn/v4 v4.1.1/go.mod h1:2123tHk1O++vmjI5VSD0awT50NywDAq5A2NNNU4Jjs8= -github.com/pion/webrtc/v4 v4.1.4 h1:/gK1ACGHXQmtyVVbJFQDxNoODg4eSRiFLB7t9r9pg8M= -github.com/pion/webrtc/v4 v4.1.4/go.mod h1:Oab9npu1iZtQRMic3K3toYq5zFPvToe/QBw7dMI2ok4= +github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= +github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= +github.com/pion/webrtc/v4 v4.0.13 h1:XuUaWTjRufsiGJRC+G71OgiSMe7tl7mQ0kkd4bAqIaQ= +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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +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.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= 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/realclientip/realclientip-go v1.0.0 h1:+yPxeC0mEaJzq1BfCt2h4BxlyrvIIBzR6suDc3BEF1U= 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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= 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.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +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/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= 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/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= 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.24/go.mod h1:7cAxNX/qFGeRUmUSnnDMoOg53FbXDK9IWBXAUfh+aBA= +github.com/xtaci/kcp-go/v5 v5.6.8 h1:jlI/0jAyjoOjT/SaGB58s4bQMJiNS41A2RKzR6TMWeI= +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/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= -github.com/xtaci/smux v1.5.35 h1:RosihGJBeaS8gxOZ17HNxbhONwnqQwNwusHx4+SEGhk= -github.com/xtaci/smux v1.5.35/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= +github.com/xtaci/smux v1.5.34 h1:OUA9JaDFHJDT8ZT3ebwLWPAgEfE6sWo2LaTy3anXqwg= +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.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/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/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-20250815012447-418f76dcf315/go.mod h1:PK7EvweKeypdelDyh1m7N922aldSeCAG8n0lJ7RAXWQ= +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-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-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-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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +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/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= @@ -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.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.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +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-20180826012351-8a410e7b638d/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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +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/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= @@ -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-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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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.2.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.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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.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.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +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-20191011141410-1b5146add898/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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 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.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +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 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/probetest/Dockerfile b/probetest/Dockerfile index 63fdd44..f73fbc1 100644 --- a/probetest/Dockerfile +++ b/probetest/Dockerfile @@ -1,28 +1,3 @@ -FROM docker.io/library/golang:latest AS build +FROM golang:1.23 - -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" +COPY probetest /go/bin diff --git a/probetest/README.md b/probetest/README.md index 41451a9..44c7837 100644 --- a/probetest/README.md +++ b/probetest/README.md @@ -24,13 +24,6 @@ but you should use TLS in production. To build the probe server, run ```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 ``` export HOSTNAMES=${YOUR HOSTNAMES}