diff --git a/common/settings/settings.go b/common/settings/settings.go index c76e23f6..e5caa052 100644 --- a/common/settings/settings.go +++ b/common/settings/settings.go @@ -37,6 +37,7 @@ const ( LoginResponseKey = "login_response" SmartRoutingKey = "smart_routing" AdBlockKey = "ad_block" + UnboundedKey = "unbounded" filePathKey = "file_path" settingsFileName = "local.json" diff --git a/go.mod b/go.mod index 59b61ab7..608a1830 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,9 @@ replace github.com/sagernet/wireguard-go => github.com/getlantern/wireguard-go v replace github.com/tetratelabs/wazero => github.com/refraction-networking/wazero v1.7.1-w -// replace github.com/getlantern/common => ../common +replace github.com/enobufs/go-nats => github.com/noahlevenson/go-nats v0.0.0-20230720174341-49df1f749775 + +replace github.com/quic-go/quic-go => github.com/getlantern/quic-go-unbounded-fork v0.51.3-unbounded // replace github.com/sagernet/sing => ../sing @@ -20,13 +22,15 @@ replace github.com/tetratelabs/wazero => github.com/refraction-networking/wazero // replace github.com/getlantern/lantern-box => ../lantern-box +require github.com/getlantern/broflake v0.0.0-20260223195036-4065257e0911 + require ( github.com/1Password/srp v0.2.0 github.com/Microsoft/go-winio v0.6.2 github.com/alitto/pond v1.9.2 github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 - github.com/getlantern/common v1.2.1-0.20260121160752-d8ee5791108f + github.com/getlantern/common v1.2.1-0.20260223192400-cc00002ef6c7 github.com/getlantern/dnstt v0.0.0-20260112160750-05100563bd0d github.com/getlantern/fronted v0.0.0-20260219001615-7eabaa834efe github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d @@ -100,6 +104,7 @@ require ( github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/enobufs/go-nats v0.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/noise v1.0.1-0.20220214164934-d803f5c4b0f4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect @@ -114,12 +119,14 @@ require ( github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 // indirect github.com/go-llsqlite/crawshaw v0.4.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gobwas/ws v1.4.0 // indirect github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect + github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/websocket v1.5.3 // indirect @@ -144,25 +151,36 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/nwaples/rardecode v1.1.2 // indirect + github.com/onsi/ginkgo/v2 v2.12.0 // indirect github.com/pion/datachannel v1.5.10 // indirect - github.com/pion/dtls/v2 v2.2.7 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/dtls/v3 v3.0.6 // indirect github.com/pion/ice/v2 v2.3.24 // indirect - github.com/pion/interceptor v0.1.37 // indirect - github.com/pion/logging v0.2.3 // indirect + github.com/pion/ice/v4 v4.0.10 // indirect + github.com/pion/interceptor v0.1.40 // indirect + github.com/pion/logging v0.2.4 // indirect github.com/pion/mdns v0.0.12 // 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.12 // indirect - github.com/pion/sctp v1.8.37 // indirect - github.com/pion/sdp/v3 v3.0.11 // indirect + github.com/pion/rtp v1.8.19 // indirect + github.com/pion/sctp v1.8.39 // indirect + github.com/pion/sdp/v3 v3.0.14 // indirect github.com/pion/srtp/v2 v2.0.18 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect github.com/pion/stun v0.6.1 // indirect - github.com/pion/transport/v2 v2.2.4 // indirect + github.com/pion/stun/v3 v3.0.0 // indirect + github.com/pion/transport v0.14.1 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/turn v1.3.7 // indirect github.com/pion/turn/v2 v2.1.3 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect github.com/pion/webrtc/v3 v3.2.40 // indirect + github.com/pion/webrtc/v4 v4.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus-community/pro-bing v0.4.0 // indirect + github.com/quic-go/quic-go v0.51.0 // indirect github.com/refraction-networking/utls v1.8.2 // indirect github.com/refraction-networking/water v0.7.1-alpha // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect @@ -185,6 +203,7 @@ require ( github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tkuchiki/go-timezone v0.2.0 // indirect github.com/ulikunitz/xz v0.5.10 // indirect + github.com/wlynxg/anet v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xtaci/kcp-go/v5 v5.6.20 // indirect diff --git a/go.sum b/go.sum index 84fed3f1..2f829205 100644 --- a/go.sum +++ b/go.sum @@ -192,8 +192,10 @@ github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f h1:NLGftemDrbGf7Wce github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f/go.mod h1:qnMv9szb8JK3kA9W4N2FlYUMj1GkA0x7QEUEPD7tk4o= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 h1:Mmeh4/DA1OKN9tVWRAvTL5efFx4c7v9/55hoK17NclA= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01/go.mod h1:3vR6+jQdWfWojZ77w+htCqEF5MO/Y2twJOpAvFuM9po= -github.com/getlantern/common v1.2.1-0.20260121160752-d8ee5791108f h1:EqRKCaOBuvVkFsIjeWUYluE4s4TZtVQSClfIWFqcSks= -github.com/getlantern/common v1.2.1-0.20260121160752-d8ee5791108f/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= +github.com/getlantern/broflake v0.0.0-20260223195036-4065257e0911 h1:HmTK1otLrb3zOi3b+7VEODbKdMKKxs0Ka6Ux17PjgLc= +github.com/getlantern/broflake v0.0.0-20260223195036-4065257e0911/go.mod h1:b40tuN7lS/j6OXOlUyo4m/CY+JbZ/dj1SBOtGfqPUKI= +github.com/getlantern/common v1.2.1-0.20260223192400-cc00002ef6c7 h1:WhfyxuQ6cf7Ia9NF5PNkhCyfY2nhogXx/CCRRqy7DhI= +github.com/getlantern/common v1.2.1-0.20260223192400-cc00002ef6c7/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 h1:oEZYEpZo28Wdx+5FZo4aU7JFXu0WG/4wJWese5reQSA= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201/go.mod h1:Y9WZUHEb+mpra02CbQ/QczLUe6f0Dezxaw5DCJlJQGo= @@ -239,6 +241,8 @@ github.com/getlantern/osversion v0.0.0-20240418205916-2e84a4a4e175 h1:JWH5BB2o0e github.com/getlantern/osversion v0.0.0-20240418205916-2e84a4a4e175/go.mod h1:h3S9LBmmzN/xM+lwYZHE4abzTtCTtidKtG+nxZcCZX0= github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 h1:rtDmW8YLAuT8r51ApR5z0d8/qjhHu3TW+divQ2C98Ac= github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535/go.mod h1:WKJEdjMOD4IuTRYwjQHjT4bmqDl5J82RShMLxPAvi0Q= +github.com/getlantern/quic-go-unbounded-fork v0.51.3-unbounded h1:qA1oi5so1/C6psHLPlyPGyq6JhZsPvA4EutsNhjzodc= +github.com/getlantern/quic-go-unbounded-fork v0.51.3-unbounded/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= github.com/getlantern/samizdat v0.0.2 h1:PkMu6jsfUz7DLZUH2xh548XfzgPASmq5CajZyUKj/9Y= github.com/getlantern/samizdat v0.0.2/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0= github.com/getlantern/sing v0.7.18-lantern h1:QKGgIUA3LwmKYP/7JlQTRkxj9jnP4cX2Q/B+nd8XEjo= @@ -292,6 +296,8 @@ github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= @@ -352,6 +358,8 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= +github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ= +github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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= @@ -482,12 +490,18 @@ github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOl github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/noahlevenson/go-nats v0.0.0-20230720174341-49df1f749775 h1:CVBqDCqhtrS2etCKGuwruUkwg3f/axVpa2Il5IQQtEs= +github.com/noahlevenson/go-nats v0.0.0-20230720174341-49df1f749775/go.mod h1:dXVvPZcJIwdWDH5ZXQ8oVA7dz+Ecu9TPF+6biaMl1dY= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M= github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= +github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= @@ -500,45 +514,68 @@ github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4 github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 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/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= +github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= github.com/pion/ice/v2 v2.3.24 h1:RYgzhH/u5lH0XO+ABatVKCtRd+4U1GEaCXSMjNr13tI= github.com/pion/ice/v2 v2.3.24/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= -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/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.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -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/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= +github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk= +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.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= 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.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -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/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c= +github.com/pion/rtp v1.8.19/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.14 h1:1h7gBr9FhOWH5GjWWY5lcw/U85MtdcibTyt/o6RxRUI= +github.com/pion/sdp/v3 v3.0.14/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo= github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= +github.com/pion/stun v0.3.2/go.mod h1:xrCld6XM+6GWDZdvjPlLMsTU21rNxnO6UO8XsAvHr/M= +github.com/pion/stun v0.3.3/go.mod h1:xrCld6XM+6GWDZdvjPlLMsTU21rNxnO6UO8XsAvHr/M= github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= +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 v0.8.8/go.mod h1:lpeSM6KJFejVtZf8k0fgeN7zE73APQpTF83WvA1FVP8= +github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40= +github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc= github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= 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 v1.3.7 h1:/nyM2XrlZILD7KKfnh0oYEBTRG5JlbH21ibjluRoCeo= +github.com/pion/turn v1.3.7/go.mod h1:js0LBFqMcKAlaWAXoYqNjefGI7kfJCrkCBfHGuTToXE= github.com/pion/turn/v2 v2.1.3 h1:pYxTVWG2gpC97opdRc5IGsQ1lJ9O/IlNhkzj7MMrGAA= github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= github.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU= github.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY= +github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= +github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -699,6 +736,9 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= @@ -820,6 +860,7 @@ 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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -868,6 +909,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -882,6 +924,7 @@ golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= @@ -897,6 +940,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= diff --git a/radiance.go b/radiance.go index 521068e2..811524ae 100644 --- a/radiance.go +++ b/radiance.go @@ -146,7 +146,9 @@ func NewRadiance(opts Options) (*Radiance, error) { } }) registerPreStartTest(dataDir) + vpn.InitUnboundedSubscription() r.confHandler = config.NewConfigHandler(cOpts) + r.addShutdownFunc(vpn.StopUnbounded) r.addShutdownFunc(telemetry.Close, kindling.Close) return r, nil } diff --git a/vpn/unbounded.go b/vpn/unbounded.go new file mode 100644 index 00000000..0cbdd6e2 --- /dev/null +++ b/vpn/unbounded.go @@ -0,0 +1,203 @@ +package vpn + +import ( + "context" + "log/slog" + "net" + "sync" + + C "github.com/getlantern/common" + + "github.com/getlantern/broflake/clientcore" + + "github.com/getlantern/radiance/common/settings" + "github.com/getlantern/radiance/config" + "github.com/getlantern/radiance/events" +) + +// UnboundedConnectionEvent is emitted when a consumer connection changes state +// in the broflake widget proxy. State: 1 = connected, -1 = disconnected. +type UnboundedConnectionEvent struct { + events.Event + State int `json:"state"` + WorkerIdx int `json:"workerIdx"` + Addr string `json:"addr"` +} + +var unbounded = &unboundedManager{} + +type unboundedManager struct { + mu sync.Mutex + cancel context.CancelFunc + lastCfg *C.UnboundedConfig // most recent config from server +} + +// UnboundedEnabled reports whether the Unbounded widget proxy is enabled in local settings. +func UnboundedEnabled() bool { + return settings.GetBool(settings.UnboundedKey) +} + +// SetUnbounded enables or disables the Unbounded widget proxy. When enabling, +// the proxy starts immediately if server config is already available; otherwise +// it will start on the next config event. When disabling, the proxy stops. +func SetUnbounded(enable bool) error { + if UnboundedEnabled() == enable { + return nil + } + if err := settings.Set(settings.UnboundedKey, enable); err != nil { + return err + } + slog.Info("Updated Unbounded widget proxy", "enabled", enable) + if enable { + unbounded.mu.Lock() + cfg := unbounded.lastCfg + unbounded.mu.Unlock() + if cfg != nil { + unbounded.start(cfg) + } else { + slog.Info("Unbounded: enabled locally, will start when server config arrives") + } + } else { + unbounded.stop() + } + return nil +} + +// InitUnboundedSubscription subscribes to config changes and starts/stops the +// broflake widget proxy based on three conditions: +// 1. settings.UnboundedKey is true (local opt-in) +// 2. cfg.Features["unbounded"] is true (server says run it) +// 3. cfg.Unbounded != nil (server provided discovery/egress URLs) +func InitUnboundedSubscription() { + events.Subscribe(func(evt config.NewConfigEvent) { + if evt.New == nil { + return + } + cfg := evt.New.ConfigResponse + + // Always store the latest unbounded config for use by SetUnbounded + unbounded.mu.Lock() + unbounded.lastCfg = cfg.Unbounded + running := unbounded.cancel != nil + unbounded.mu.Unlock() + + shouldRun := shouldRunUnbounded(cfg) + if shouldRun && !running { + // start() is internally guarded against being called when already running. + unbounded.start(cfg.Unbounded) + } else if !shouldRun && running { + // stop() is internally guarded and idempotent; safe to call even + // if another goroutine changed the running state since we read it. + unbounded.stop() + } + }) +} + +func shouldRunUnbounded(cfg C.ConfigResponse) bool { + if !settings.GetBool(settings.UnboundedKey) { + return false + } + if !cfg.Features[C.UNBOUNDED] { + return false + } + if cfg.Unbounded == nil { + return false + } + return true +} + +func (m *unboundedManager) start(ucfg *C.UnboundedConfig) { + m.mu.Lock() + defer m.mu.Unlock() + if m.cancel != nil { + return // already running + } + + ctx, cancel := context.WithCancel(context.Background()) + m.cancel = cancel + + go func() { + slog.Info("Unbounded: starting broflake widget proxy") + + bfOpt := clientcore.NewDefaultBroflakeOptions() + bfOpt.ClientType = "widget" + if ucfg != nil { + if ucfg.CTableSize > 0 { + bfOpt.CTableSize = ucfg.CTableSize + } + if ucfg.PTableSize > 0 { + bfOpt.PTableSize = ucfg.PTableSize + } + } + + // Wire up connection change callback to emit radiance events + bfOpt.OnConnectionChangeFunc = func(state int, workerIdx int, addr net.IP) { + addrStr := "" + if addr != nil { + addrStr = addr.String() + } + slog.Debug("Unbounded: consumer connection change", "state", state, "workerIdx", workerIdx, "addr", addrStr) + events.Emit(UnboundedConnectionEvent{ + State: state, + WorkerIdx: workerIdx, + Addr: addrStr, + }) + } + + rtcOpt := clientcore.NewDefaultWebRTCOptions() + if ucfg != nil { + if ucfg.DiscoverySrv != "" { + rtcOpt.DiscoverySrv = ucfg.DiscoverySrv + } + if ucfg.DiscoveryEndpoint != "" { + rtcOpt.Endpoint = ucfg.DiscoveryEndpoint + } + } + + egOpt := clientcore.NewDefaultEgressOptions() + if ucfg != nil { + if ucfg.EgressAddr != "" { + egOpt.Addr = ucfg.EgressAddr + } + if ucfg.EgressEndpoint != "" { + egOpt.Endpoint = ucfg.EgressEndpoint + } + } + + // BroflakeConn is for clients routing traffic through the mesh; + // a widget proxy only donates bandwidth, so the conn is unused. + _, ui, err := clientcore.NewBroflake(bfOpt, rtcOpt, egOpt) + if err != nil { + slog.Error("Unbounded: failed to create broflake widget", "error", err) + cancel() // cancel the context to avoid a leak + m.mu.Lock() + m.cancel = nil + m.mu.Unlock() + return + } + + slog.Info("Unbounded: broflake widget proxy started") + <-ctx.Done() + slog.Info("Unbounded: stopping broflake widget proxy") + ui.Stop() + m.mu.Lock() + m.cancel = nil + m.mu.Unlock() + slog.Info("Unbounded: broflake widget proxy stopped") + }() +} + +func (m *unboundedManager) stop() { + m.mu.Lock() + defer m.mu.Unlock() + if m.cancel != nil { + m.cancel() + m.cancel = nil + } +} + +// StopUnbounded stops the Unbounded widget proxy. Used as a shutdown hook. +func StopUnbounded(_ context.Context) error { + unbounded.stop() + return nil +} diff --git a/vpn/unbounded_test.go b/vpn/unbounded_test.go new file mode 100644 index 00000000..73abb20d --- /dev/null +++ b/vpn/unbounded_test.go @@ -0,0 +1,91 @@ +package vpn + +import ( + "testing" + + C "github.com/getlantern/common" + "github.com/getlantern/radiance/common/settings" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func initTestSettings(t *testing.T) { + t.Helper() + tmp := t.TempDir() + require.NoError(t, settings.InitSettings(tmp)) + t.Cleanup(settings.Reset) +} + +func TestShouldRunUnbounded(t *testing.T) { + initTestSettings(t) + settings.Set(settings.UnboundedKey, false) + + cfg := C.ConfigResponse{ + Features: map[string]bool{C.UNBOUNDED: true}, + Unbounded: &C.UnboundedConfig{}, + } + + assert.False(t, shouldRunUnbounded(cfg), "should be false when setting is off") + + settings.Set(settings.UnboundedKey, true) + assert.True(t, shouldRunUnbounded(cfg), "should be true when all conditions met") + + // Missing feature flag + cfg.Features[C.UNBOUNDED] = false + assert.False(t, shouldRunUnbounded(cfg), "should be false when feature flag is off") + cfg.Features[C.UNBOUNDED] = true + + // Missing config + cfg.Unbounded = nil + assert.False(t, shouldRunUnbounded(cfg), "should be false when config is nil") +} + +func TestSetUnboundedToggle(t *testing.T) { + initTestSettings(t) + settings.Set(settings.UnboundedKey, false) + + require.NoError(t, SetUnbounded(true)) + assert.True(t, UnboundedEnabled()) + + require.NoError(t, SetUnbounded(false)) + assert.False(t, UnboundedEnabled()) + + // Idempotent + require.NoError(t, SetUnbounded(false)) + assert.False(t, UnboundedEnabled()) +} + +func TestStopWhenNotRunning(t *testing.T) { + unbounded.stop() + unbounded.mu.Lock() + assert.Nil(t, unbounded.cancel) + unbounded.mu.Unlock() +} + +func TestStartStopLifecycle(t *testing.T) { + // Ensure we start from a stopped state. + unbounded.mu.Lock() + unbounded.cancel = nil + unbounded.mu.Unlock() + + // stop is safe when already stopped + unbounded.stop() + unbounded.mu.Lock() + assert.Nil(t, unbounded.cancel, "cancel should remain nil when stopping an already stopped unbounded") + unbounded.mu.Unlock() + + // Simulate a running state by setting a non-nil cancel function. + unbounded.mu.Lock() + unbounded.cancel = func() {} + unbounded.mu.Unlock() + + unbounded.mu.Lock() + assert.NotNil(t, unbounded.cancel, "cancel should be non-nil in simulated running state") + unbounded.mu.Unlock() + + // Now stopping should clear the cancel function. + unbounded.stop() + unbounded.mu.Lock() + assert.Nil(t, unbounded.cancel, "cancel should be nil after stopping from a running state") + unbounded.mu.Unlock() +}