Merge pull request #29 from alananisimov/mobile-provider-config

add mobile provider configuration
This commit is contained in:
zarazaex
2026-05-03 11:18:46 +03:00
committed by GitHub
3 changed files with 189 additions and 17 deletions

1
go.mod
View File

@@ -15,6 +15,7 @@ require (
github.com/zarazaex69/gr v0.0.0-20260430043628-45b595f4fef0
golang.org/x/crypto v0.50.0
golang.org/x/mobile v0.0.0-20260410095206-2cfb76559b7b
google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57
)
require (

2
go.sum
View File

@@ -345,6 +345,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57 h1:uZSB/r2MjH9IsqpG2vRNSV1Juteix90oHe8oTcLW9tk=
google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:nGuPfp0lnDJcJD0J47StV0Skgnw3qMSQhjsLKiejq5Y=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=

View File

@@ -10,11 +10,13 @@ import (
"sync"
"time"
"github.com/openlibrecommunity/olcrtc/internal/app/session"
"github.com/openlibrecommunity/olcrtc/internal/client"
"github.com/openlibrecommunity/olcrtc/internal/logger"
"github.com/openlibrecommunity/olcrtc/internal/protect"
_ "golang.org/x/mobile/bind" // ensure gomobile bind is available
_ "golang.org/x/mobile/bind" // ensure gomobile bind is available
_ "google.golang.org/genproto/protobuf/field_mask" // keep gomobile on post-split genproto modules
)
// SocketProtector protects sockets from VPN routing on Android.
@@ -30,6 +32,7 @@ type LogWriter interface {
var (
errAlreadyRunning = errors.New("olcRTC already running")
errCarrierRequired = errors.New("carrier is required")
errRoomIDRequired = errors.New("roomID is required")
errKeyHexRequired = errors.New("keyHex is required")
errNotRunning = errors.New("olcRTC is not running")
@@ -37,15 +40,32 @@ var (
errStartTimedOut = errors.New("olcRTC start timed out")
)
const (
defaultLink = "direct"
defaultTransport = "vp8channel"
dataTransport = "datachannel"
defaultDNSServer = "1.1.1.1:53"
)
//nolint:gochecknoglobals // Mobile bindings expose a singleton runtime controlled by the embedding app.
var (
mu sync.Mutex
cancel context.CancelFunc
done chan struct{}
ready chan struct{}
errRun error
mu sync.Mutex
defaults mobileConfig
defaultsSet sync.Once
cancel context.CancelFunc
done chan struct{}
ready chan struct{}
errRun error
)
type mobileConfig struct {
link string
transport string
dnsServer string
vp8FPS int
vp8BatchSize int
}
// SetProtector sets the Android VPN socket protector.
// Must be called before Start.
func SetProtector(p SocketProtector) {
@@ -65,6 +85,46 @@ func SetLogWriter(w LogWriter) {
}
}
// SetProviders registers built-in carriers, links, and transports.
func SetProviders() {
registerDefaults()
}
// SetTransport selects the transport used by Start.
// Supported values: vp8channel and datachannel.
func SetTransport(transport string) {
mu.Lock()
defer mu.Unlock()
ensureDefaultConfigLocked()
defaults.transport = normalizeTransport(transport)
}
// SetLink selects the link used by Start.
// Supported value today: direct.
func SetLink(link string) {
mu.Lock()
defer mu.Unlock()
ensureDefaultConfigLocked()
defaults.link = link
}
// SetDNS selects the DNS server used by the tunnel.
func SetDNS(dnsServer string) {
mu.Lock()
defer mu.Unlock()
ensureDefaultConfigLocked()
defaults.dnsServer = dnsServer
}
// SetVP8Options configures vp8channel.
func SetVP8Options(fps, batchSize int) {
mu.Lock()
defer mu.Unlock()
ensureDefaultConfigLocked()
defaults.vp8FPS = clamp(fps, 1, 120)
defaults.vp8BatchSize = clamp(batchSize, 1, 32)
}
// SetDebug enables or disables verbose logging.
func SetDebug(enabled bool) {
logger.SetVerbose(enabled)
@@ -77,24 +137,62 @@ func SetDebug(enabled bool) {
}
// Start launches the olcRTC client in background.
// roomID: Telemost room ID (e.g. "xxx-xxx-xxx")
// carrierName: carrier/provider name ("telemost", "jazz", "wbstream", "wb_stream")
// roomID: carrier-specific room ID
// keyHex: 64-char hex encryption key
// socksPort: local SOCKS5 proxy port (e.g. 10808)
// socksUser/socksPass: SOCKS5 credentials (empty = no auth).
func Start(roomID, keyHex string, socksPort int, socksUser, socksPass string) error {
func Start(carrierName, roomID, keyHex string, socksPort int, socksUser, socksPass string) error {
mu.Lock()
ensureDefaultConfigLocked()
cfg := defaults
mu.Unlock()
return startWithConfig(carrierName, cfg.transport, roomID, keyHex, socksPort, socksUser, socksPass, cfg)
}
// StartWithTransport launches the client with an explicit transport for this start.
func StartWithTransport(
carrierName, transportName, roomID, keyHex string,
socksPort int,
socksUser, socksPass string,
) error {
mu.Lock()
ensureDefaultConfigLocked()
cfg := defaults
cfg.transport = transportName
mu.Unlock()
return startWithConfig(carrierName, transportName, roomID, keyHex, socksPort, socksUser, socksPass, cfg)
}
func startWithConfig(
carrierName, transportName, roomID, keyHex string,
socksPort int,
socksUser, socksPass string,
cfg mobileConfig,
) error {
mu.Lock()
defer mu.Unlock()
registerDefaults()
carrierName = normalizeCarrier(carrierName)
if transportName != "" {
cfg.transport = normalizeTransport(transportName)
}
switch {
case cancel != nil:
return errAlreadyRunning
case roomID == "":
case carrierName == "":
return errCarrierRequired
case roomID == "" && carrierName != "jazz":
return errRoomIDRequired
case keyHex == "":
return errKeyHexRequired
}
roomURL := "https://telemost.yandex.ru/j/" + roomID
roomURL := buildRoomURL(carrierName, roomID)
ctx, cancelFunc := context.WithCancel(context.Background())
cancel = cancelFunc
@@ -109,13 +207,13 @@ func Start(roomID, keyHex string, socksPort int, socksUser, socksPass string) er
err := client.RunWithReady(
ctx,
"direct",
"datachannel",
"telemost",
cfg.link,
cfg.transport,
carrierName,
roomURL,
keyHex,
fmt.Sprintf("127.0.0.1:%d", socksPort),
"",
cfg.dnsServer,
socksUser,
socksPass,
func() {
@@ -123,7 +221,18 @@ func Start(roomID, keyHex string, socksPort int, socksUser, socksPass string) er
close(localReady)
})
},
0, 0, 0, "", "", 0, "", "", 0, 0, 0, 0,
0,
0,
0,
"",
"",
0,
"",
"",
0,
0,
cfg.vp8FPS,
cfg.vp8BatchSize,
)
mu.Lock()
@@ -136,7 +245,7 @@ func Start(roomID, keyHex string, socksPort int, socksUser, socksPass string) er
return nil
}
// WaitReady blocks until the Telemost peers are connected and the local SOCKS5 listener is ready.
// WaitReady blocks until the selected transport is connected and the local SOCKS5 listener is ready.
//
//nolint:cyclop // The control flow is intentionally linear so mobile callers can observe each startup state clearly.
func WaitReady(timeoutMillis int) error {
@@ -214,7 +323,67 @@ func IsRunning() bool {
return cancel != nil
}
// logBridge adapts LogWriter to io.Writer for log package.
func registerDefaults() {
session.RegisterDefaults()
}
func ensureDefaultConfigLocked() {
defaultsSet.Do(func() {
defaults = mobileConfig{
link: defaultLink,
transport: defaultTransport,
dnsServer: defaultDNSServer,
vp8FPS: 60,
vp8BatchSize: 8,
}
})
}
func normalizeTransport(value string) string {
switch value {
case dataTransport, "data", "dc":
return dataTransport
case defaultTransport, "vp8":
return defaultTransport
default:
return defaultTransport
}
}
func normalizeCarrier(carrierName string) string {
if carrierName == "wb_stream" {
return "wbstream"
}
return carrierName
}
func buildRoomURL(carrierName, roomID string) string {
switch carrierName {
case "telemost":
return "https://telemost.yandex.ru/j/" + roomID
case "jazz":
if roomID == "" {
return "any"
}
return roomID
case "wbstream":
return roomID
default:
return roomID
}
}
func clamp(value, minValue, maxValue int) int {
if value < minValue {
return minValue
}
if value > maxValue {
return maxValue
}
return value
}
// logBridge adapts LogWriter to io.Writer.
type logBridge struct {
w LogWriter
}