feat(mobile,client,mux): Android integration + SOCKS5 auth + reliability fixes

- mobile/: gomobile-bindable entry point for Android (combined libgojni.so)
- internal/protect/: Android socket protect via VpnService for olcRTC sockets
- internal/names/data/: embedded name pools for client identity generation
- client: add SOCKS5 USER/PASS auth (RFC 1929) and bind to 127.0.0.1
- mux: infinite backpressure via waitForBufferSpace, raise buffer to 32MB,
  remove close-on-overflow (was corrupting reliable TCP streams over DC)
- peer: remove 3-second drop in send worker — wait for SCTP buffer to drain
  instead of dropping packets (broke large HTTP/2 transfers like Instagram/X)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Qtozdec
2026-04-10 13:45:33 +03:00
parent baea7d63d3
commit f48a63a0b9
10 changed files with 1835 additions and 49 deletions

View File

@@ -87,7 +87,7 @@ func main() {
case "srv":
errCh <- server.Run(ctx, roomURL, keyHex, duo, dnsServer)
case "cnc":
errCh <- client.Run(ctx, roomURL, keyHex, socksPort, duo)
errCh <- client.Run(ctx, roomURL, keyHex, socksPort, duo, "", "")
}
}()

View File

@@ -31,7 +31,7 @@ type Client struct {
wg sync.WaitGroup
}
func Run(ctx context.Context, roomURL, keyHex string, socksPort int, duo bool) error {
func Run(ctx context.Context, roomURL, keyHex string, socksPort int, duo bool, socksUser, socksPass string) error {
var key []byte
var err error
@@ -150,7 +150,7 @@ func Run(ctx context.Context, roomURL, keyHex string, socksPort int, duo bool) e
}
log.Printf("Sent reset signal to server (clientID=%d)", c.clientID)
err = c.runSOCKS5(ctx, socksPort)
err = c.runSOCKS5(ctx, socksPort, socksUser, socksPass)
log.Println("Waiting for client goroutines...")
c.wg.Wait()
@@ -170,13 +170,13 @@ func (c *Client) onData(data []byte) {
c.mux.HandleFrame(plaintext)
}
func (c *Client) runSOCKS5(ctx context.Context, port int) error {
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
func (c *Client) runSOCKS5(ctx context.Context, port int, username, password string) error {
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
return err
}
log.Printf("SOCKS5 proxy listening on 0.0.0.0:%d", port)
log.Printf("SOCKS5 proxy listening on 127.0.0.1:%d (auth=%v)", port, username != "")
go func() {
<-ctx.Done()
@@ -190,11 +190,11 @@ func (c *Client) runSOCKS5(ctx context.Context, port int) error {
select {
case <-ctx.Done():
log.Println("SOCKS5 listener closed")
for _, peer := range c.peers {
peer.Close()
}
return nil
default:
log.Printf("Accept error: %v", err)
@@ -202,15 +202,15 @@ func (c *Client) runSOCKS5(ctx context.Context, port int) error {
}
}
go c.handleSOCKS5(conn)
go c.handleSOCKS5(conn, username, password)
}
}
func (c *Client) handleSOCKS5(conn net.Conn) {
func (c *Client) handleSOCKS5(conn net.Conn, username, password string) {
defer conn.Close()
startTime := time.Now()
buf := make([]byte, 256)
buf := make([]byte, 513)
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
return
@@ -225,7 +225,48 @@ func (c *Client) handleSOCKS5(conn net.Conn) {
return
}
conn.Write([]byte{5, 0})
requireAuth := username != ""
wantMethod := byte(0x00)
if requireAuth {
wantMethod = 0x02
}
hasMethod := false
for i := 0; i < int(nmethods); i++ {
if buf[i] == wantMethod {
hasMethod = true
break
}
}
if !hasMethod {
conn.Write([]byte{5, 0xFF})
return
}
conn.Write([]byte{5, wantMethod})
if requireAuth {
// RFC 1929: VER ULEN UNAME PLEN PASSWD
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
return
}
if buf[0] != 0x01 {
return
}
ulen := int(buf[1])
if _, err := io.ReadFull(conn, buf[:ulen+1]); err != nil {
return
}
gotUser := string(buf[:ulen])
plen := int(buf[ulen])
if _, err := io.ReadFull(conn, buf[:plen]); err != nil {
return
}
gotPass := string(buf[:plen])
if gotUser != username || gotPass != password {
conn.Write([]byte{0x01, 0x01})
return
}
conn.Write([]byte{0x01, 0x00})
}
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
return

View File

@@ -7,18 +7,19 @@ package mux
import (
"encoding/binary"
"sync"
"time"
"github.com/openlibrecommunity/olcrtc/internal/logger"
)
type Stream struct {
ID uint16
ClientID uint32
recvBuf []byte
closed bool
mu sync.Mutex
nextSeq uint32
outOfOrder map[uint32][]byte
ID uint16
ClientID uint32
recvBuf []byte
closed bool
mu sync.Mutex
nextSeq uint32
outOfOrder map[uint32][]byte
}
func (s *Stream) RecvBuf() []byte {
@@ -48,7 +49,7 @@ func New(clientID uint32, onSend func([]byte) error) *Multiplexer {
clientID: clientID,
onSend: onSend,
maxStreams: 10000,
maxBufferSize: 1024 * 1024,
maxBufferSize: 32 * 1024 * 1024,
dataReady: make(map[uint16]chan struct{}),
sendSeq: make(map[uint16]uint32),
}
@@ -220,26 +221,37 @@ func (m *Multiplexer) HandleFrame(frame []byte) {
}
if seq == stream.nextSeq {
if len(stream.recvBuf)+len(data) > m.maxBufferSize {
stream.closed = true
// Backpressure: if the stream buffer is full, release the mux lock and
// wait for the reader to drain it. Dropping/closing here would corrupt
// the TCP stream carried over the mux — large HTTP/2 downloads (X,
// Instagram, YouTube) that push data faster than conn.Write can accept
// would lose bytes and hang forever.
if s := m.waitForBufferSpace(sid, clientID, len(data)); s == nil {
return
} else {
stream = s
}
stream.recvBuf = append(stream.recvBuf, data...)
stream.nextSeq++
for {
if nextData, ok := stream.outOfOrder[stream.nextSeq]; ok {
if len(stream.recvBuf)+len(nextData) > m.maxBufferSize {
stream.closed = true
return
}
stream.recvBuf = append(stream.recvBuf, nextData...)
delete(stream.outOfOrder, stream.nextSeq)
stream.nextSeq++
logger.Verbose("Applied out-of-order packet sid=%d seq=%d", sid, stream.nextSeq-1)
} else {
nextData, ok := stream.outOfOrder[stream.nextSeq]
if !ok {
break
}
if s := m.waitForBufferSpace(sid, clientID, len(nextData)); s == nil {
return
} else {
stream = s
}
nextData, ok = stream.outOfOrder[stream.nextSeq]
if !ok {
break
}
stream.recvBuf = append(stream.recvBuf, nextData...)
delete(stream.outOfOrder, stream.nextSeq)
stream.nextSeq++
logger.Verbose("Applied out-of-order packet sid=%d seq=%d", sid, stream.nextSeq-1)
}
m.dataReadyMu.Lock()
@@ -260,6 +272,25 @@ func (m *Multiplexer) HandleFrame(frame []byte) {
}
}
// waitForBufferSpace releases m.mu and waits until the stream's recvBuf has
// room for `need` more bytes, then re-acquires the lock. Returns the (possibly
// re-fetched) stream, or nil if the stream disappeared / was reset / closed.
// Caller must hold m.mu (write-locked) on entry and will hold it on return.
func (m *Multiplexer) waitForBufferSpace(sid uint16, clientID uint32, need int) *Stream {
for {
stream, ok := m.streams[sid]
if !ok || stream.ClientID != clientID || stream.closed {
return nil
}
if len(stream.recvBuf)+need <= m.maxBufferSize {
return stream
}
m.mu.Unlock()
time.Sleep(5 * time.Millisecond)
m.mu.Lock()
}
}
func (m *Multiplexer) ReadStream(sid uint16) []byte {
m.mu.Lock()
defer m.mu.Unlock()

735
internal/names/data/names Normal file
View File

@@ -0,0 +1,735 @@
Аарон
Аба
Аббас
Абд аль-Узза
Абдуллах
Абид
Аботур
Аввакум
Август
Авдей
Авель
Аверкий
Авигдор
Авирмэд
Авксентий
Авл
Авнер
Аврелий
Автандил
Автоном
Агапит
Агафангел
Агафодор
Агафон
Аги
Агриппа
Адам
Адар
Адиль
Адольф
Адонирам
Адриан
Азамат
Азарий
Азат
Азиз
Азим
Айварс
Айдар
Айрат
Акакий
Аквилий
Акиф
Акоп
Аксель
Алан
Аланус
Алек
Александр
Алексей
Алемдар
Алик
Алим
Алипий
Алишер
Алмат
Алоиз
Алон
Альберик
Альберт
Альбин
Альваро
Альвиан
Альвизе
Альфонс
Альфред
Амадис
Амвросий
Амедей
Амин
Амир
Амр
Амфилохий
Анания
Анас
Анастасий
Анатолий
Ангеляр
Андокид
Андрей
Андроник
Аннерс
Анри
Ансельм
Антипа
Антон
Антоний
Антонин
Антуан
Арам
Арефа
Арзуман
Аристарх
Аристон
Ариф
Аркадий
Арсений
Артём
Артур
Арфаксад
Асаф
Атанасий
Атом
Аттик
Афанасий
Афинагор
Афиней
Афиф
Африкан
Ахилл
Ахмад
Ахтям
Ашот
Бадар
Барни
Бартоломео
Басир
Бахтияр
Баян
Безсон
Бен
Беньямин
Берт
Бехруз
Билял
Богдан
Болеслав
Бонавентура
Борис
Борислав
Боян
Бронислав
Брячислав
Бурхан
Бутрос
Бямбасурэн
Вадим
Валентин
Валентино
Валерий
Валерьян
Вальдемар
Вангьял
Варлам
Варнава
Варфоломей
Василий
Вахтанг
Велвел
Венансио
Венедикт
Вениамин
Венцеслав
Вигго
Викентий
Виктор
Викторин
Вильгельм
Винцас
Виссарион
Виталий
Витаутас
Вито
Владимир
Владислав
Владлен
Влас
Воислав
Володарь
Вольфганг
Вописк
Всеволод
Всеслав
Вук
Вукол
Вышеслав
Вячеслав
Габриеле
Гавриил
Гай
Галактион
Галымжан
Гамлет
Гаспар
Гафур
Гвидо
Гейдар
Геласий
Гелий
Гельмут
Геннадий
Генри
Генрих
Георге
Георгий
Гераклид
Герасим
Герберт
Герман
Германн
Геронтий
Герхард
Гийом
Гильем
Гинкмар
Глеб
Гней
Гоар
Горацио
Гордей
Градислав
Григорий
Гримоальд
Гуго
Гурий
Густав
Гьялцен
Давид
Дамдинсурэн
Дамир
Даниил
Дарий
Демид
Демьян
Денеш
Денис
Децим
Джаббар
Джамиль
Джан
Джанер
Джанфранко
Джафар
Джейкоб
Джихангир
Джованни
Джон
Джохар
Джулиано
Джулиус
Дино
Диодор
Дитер
Дитмар
Дитрих
Дмитрий
Доминик
Дональд
Донат
Дорофей
Досифей
Евгений
Евграф
Евдоким
Еврит
Евсей
Евстафий
Евтихан
Евтихий
Егор
Елеазар
Елисей
Емельян
Епифаний
Ербол
Ерванд
Еремей
Ермак
Ермолай
Ерофей
Ефим
Ефрем
Жан
Ждан
Жером
Жоан
Захар
Захария
Збигнев
Зденек
Зейналабдин
Зенон
Зеэв
Зигмунд
Зинон
Зия
Золтан
Зосима
Иакинф
Иан
Ибрагим
Ибрахим
Иван
Игнатий
Игорь
Иероним
Иерофей
Израиль
Икрима
Иларий
Илия
Илларион
Илмари
Ильфат
Илья
Имран
Иннокентий
Иоаким
Иоанн
Иоанникий
Иоахим
Иов
Иоганн
Иоганнес
Ионафан
Иосафат
Ираклий
Иржи
Иринарх
Ириней
Иродион
Иса
Исаак
Исаакий
Исаия
Исидор
Ислам
Исмаил
Истислав
Истома
Истукарий
Иштван
Йюрген
Кадваллон
Кадир
Казимир
Каликст
Калин
Каллистрат
Кальман
Канат
Карен
Карлос
Карп
Картерий
Кассиан
Кассий
Касторий
Касьян
Катберт
Квинт
Кехлер
Киллиан
Ким
Кир
Кириак
Кирилл
Клаас
Клавдиан
Клеоник
Климент
Кондрат
Конон
Конрад
Константин
Корнелиус
Корнилий
Коррадо
Косьма
Кратет
Кратипп
Крис
Криспин
Кристиан
Кронид
Кузьма
Куприян
Курбан
Курт
Кутлуг-Буга
Кэлин
Лаврентий
Лавс
Ладислав
Лазарь
Лайл
Лампрехт
Ландульф
Лев
Леви
Ленни
Леонид
Леонтий
Леонхард
Лиам
Линкей
Логгин
Лоренц
Лоренцо
Луи
Луитпольд
Лука
Лукас
Лукий
Лукьян
Луций
Людовик
Люцифер
Макар
Максим
Максимиан
Максимилиан
Малик
Малх
Мамбет
Маний
Мануил
Мануэль
Мариан
Мариус
Марк
Маркел
Мартын
Марчелло
Матвей
Матео
Матиас
Матфей
Матфий
Махмуд
Меир
Мелентий
Мелитон
Менахем-Мендель
Месроп
Мефодий
Мечислав
Мика
Микеланджело
Микулаш
Милорад
Мина
Мирко
Мирон
Мирослав
Митрофан
Михаил
Михей
Младан
Модест
Моисей
Мордехай
Мстислав
Мурад
Мухаммед
Мэдисон
Мэлор
Мэлс
Назар
Наиль
Насиф
Натан
Натаниэль
Наум
Нафанаил
Нацагдорж
Нестор
Никандр
Никанор
Никита
Никифор
Никодим
Николай
Нил
Нильс
Ноа
Ной
Норд
Нуржан
Нурлан
Овадья
Оге
Одинец
Октав
Октавиан
Октавий
Октавио
Олаф
Оле
Олег
Оливер
Ольгерд
Онисим
Орест
Осип
Оскар
Осман
Отто
Оттон
Очирбат
Пабло
Павел
Павлин
Павсикакий
Паисий
Палладий
Панкратий
Пантелеймон
Папа
Паруйр
Парфений
Патрик
Пафнутий
Пахомий
Педро
Пётр
Пимен
Пинхас
Пипин
Питирим
Пол
Полидор
Полиевкт
Поликарп
Поликрат
Порфирий
Потап
Предраг
Премысл
Приск
Прокл
Прокопий
Прокул
Протасий
Прохор
Публий
Рагнар
Рагуил
Радмир
Радослав
Разумник
Раймонд
Рамадан
Рамазан
Рахман
Рашад
Рейнхард
Ренат
Реститут
Ричард
Роберт
Родерик
Родион
Рожер
Розарио
Роман
Ромен
Рон
Ронан
Ростислав
Рудольф
Руслан
Руф
Руфин
Рушан
Сабит
Савва
Савватий
Савелий
Савин
Саддам
Садик
Саид
Салават
Салих
Саллюстий
Салман
Самуил
Сармат
Святослав
Севастьян
Северин
Секст
Секунд
Семён
Септимий
Серапион
Сергей
Серж
Сигеберт
Сильвестр
Симеон
Симон
Созон
Соломон
Сонам
Софрон
Спиридон
Срджан
Станислав
Степан
Стефано
Стивен
Таврион
Тавус
Тадеуш
Тарас
Тарасий
Тейс
Тендзин
Теофил
Терентий
Терри
Тиберий
Тигран
Тимофей
Тимур
Тихомир
Тихон
Томас
Томоми
Торос
Тофик
Трифон
Трофим
Тудхалия
Тутмос
Тьерри
Тьяго
Уве
Уильям
Улдис
Ульрих
Ульф
Умар
Урызмаг
Усама
Усман
Фавст
Фаддей
Файзулла
Фарид
Фахраддин
Федериго
Федосей
Федот
Фейсал
Феликс
Феоктист
Феофан
Феофил
Феофилакт
Фердинанд
Ференц
Фёдор
Фидель
Филарет
Филат
Филип
Филипп
Философ
Филострат
Фирс
Фока
Фома
Фотий
Франц
Франческо
Фредерик
Фридрих
Фродо
Фрол
Фульк
Хайме
Ханс
Харальд
Харитон
Харри
Харрисон
Хасан
Хетаг
Хильдерик
Хирам
Хлодвиг
Хокон
Хорив
Хоселито
Хосрой
Хрисанф
Христофор
Хуан
Цэрэндорж
Чеслав
Шалом
Шамиль
Шамсуддин
Шапур
Шарль
Шейх-Хайдар
Шон
Эберхард
Эдмунд
Эдна
Эдуард
Элбэгдорж
Элджернон
Элиас
Эллиот
Эмиль
Энрик
Энрико
Энтони
Эразм
Эраст
Эрик
Эрнст
Эсекьель
Эстебан
Этьен
Ювеналий
Юлиан
Юлий
Юлиус
Юрий
Юстас
Юстин
Яков
Якуб
Якун
Ян
Яни
Януарий
Яромир
Ярополк
Ярослав

View File

@@ -0,0 +1,735 @@
Аарон
Аба
Аббас
Абд аль-Узза
Абдуллах
Абид
Аботур
Аввакум
Август
Авдей
Авель
Аверкий
Авигдор
Авирмэд
Авксентий
Авл
Авнер
Аврелий
Автандил
Автоном
Агапит
Агафангел
Агафодор
Агафон
Аги
Агриппа
Адам
Адар
Адиль
Адольф
Адонирам
Адриан
Азамат
Азарий
Азат
Азиз
Азим
Айварс
Айдар
Айрат
Акакий
Аквилий
Акиф
Акоп
Аксель
Алан
Аланус
Алек
Александр
Алексей
Алемдар
Алик
Алим
Алипий
Алишер
Алмат
Алоиз
Алон
Альберик
Альберт
Альбин
Альваро
Альвиан
Альвизе
Альфонс
Альфред
Амадис
Амвросий
Амедей
Амин
Амир
Амр
Амфилохий
Анания
Анас
Анастасий
Анатолий
Ангеляр
Андокид
Андрей
Андроник
Аннерс
Анри
Ансельм
Антипа
Антон
Антоний
Антонин
Антуан
Арам
Арефа
Арзуман
Аристарх
Аристон
Ариф
Аркадий
Арсений
Артём
Артур
Арфаксад
Асаф
Атанасий
Атом
Аттик
Афанасий
Афинагор
Афиней
Афиф
Африкан
Ахилл
Ахмад
Ахтям
Ашот
Бадар
Барни
Бартоломео
Басир
Бахтияр
Баян
Безсон
Бен
Беньямин
Берт
Бехруз
Билял
Богдан
Болеслав
Бонавентура
Борис
Борислав
Боян
Бронислав
Брячислав
Бурхан
Бутрос
Бямбасурэн
Вадим
Валентин
Валентино
Валерий
Валерьян
Вальдемар
Вангьял
Варлам
Варнава
Варфоломей
Василий
Вахтанг
Велвел
Венансио
Венедикт
Вениамин
Венцеслав
Вигго
Викентий
Виктор
Викторин
Вильгельм
Винцас
Виссарион
Виталий
Витаутас
Вито
Владимир
Владислав
Владлен
Влас
Воислав
Володарь
Вольфганг
Вописк
Всеволод
Всеслав
Вук
Вукол
Вышеслав
Вячеслав
Габриеле
Гавриил
Гай
Галактион
Галымжан
Гамлет
Гаспар
Гафур
Гвидо
Гейдар
Геласий
Гелий
Гельмут
Геннадий
Генри
Генрих
Георге
Георгий
Гераклид
Герасим
Герберт
Герман
Германн
Геронтий
Герхард
Гийом
Гильем
Гинкмар
Глеб
Гней
Гоар
Горацио
Гордей
Градислав
Григорий
Гримоальд
Гуго
Гурий
Густав
Гьялцен
Давид
Дамдинсурэн
Дамир
Даниил
Дарий
Демид
Демьян
Денеш
Денис
Децим
Джаббар
Джамиль
Джан
Джанер
Джанфранко
Джафар
Джейкоб
Джихангир
Джованни
Джон
Джохар
Джулиано
Джулиус
Дино
Диодор
Дитер
Дитмар
Дитрих
Дмитрий
Доминик
Дональд
Донат
Дорофей
Досифей
Евгений
Евграф
Евдоким
Еврит
Евсей
Евстафий
Евтихан
Евтихий
Егор
Елеазар
Елисей
Емельян
Епифаний
Ербол
Ерванд
Еремей
Ермак
Ермолай
Ерофей
Ефим
Ефрем
Жан
Ждан
Жером
Жоан
Захар
Захария
Збигнев
Зденек
Зейналабдин
Зенон
Зеэв
Зигмунд
Зинон
Зия
Золтан
Зосима
Иакинф
Иан
Ибрагим
Ибрахим
Иван
Игнатий
Игорь
Иероним
Иерофей
Израиль
Икрима
Иларий
Илия
Илларион
Илмари
Ильфат
Илья
Имран
Иннокентий
Иоаким
Иоанн
Иоанникий
Иоахим
Иов
Иоганн
Иоганнес
Ионафан
Иосафат
Ираклий
Иржи
Иринарх
Ириней
Иродион
Иса
Исаак
Исаакий
Исаия
Исидор
Ислам
Исмаил
Истислав
Истома
Истукарий
Иштван
Йюрген
Кадваллон
Кадир
Казимир
Каликст
Калин
Каллистрат
Кальман
Канат
Карен
Карлос
Карп
Картерий
Кассиан
Кассий
Касторий
Касьян
Катберт
Квинт
Кехлер
Киллиан
Ким
Кир
Кириак
Кирилл
Клаас
Клавдиан
Клеоник
Климент
Кондрат
Конон
Конрад
Константин
Корнелиус
Корнилий
Коррадо
Косьма
Кратет
Кратипп
Крис
Криспин
Кристиан
Кронид
Кузьма
Куприян
Курбан
Курт
Кутлуг-Буга
Кэлин
Лаврентий
Лавс
Ладислав
Лазарь
Лайл
Лампрехт
Ландульф
Лев
Леви
Ленни
Леонид
Леонтий
Леонхард
Лиам
Линкей
Логгин
Лоренц
Лоренцо
Луи
Луитпольд
Лука
Лукас
Лукий
Лукьян
Луций
Людовик
Люцифер
Макар
Максим
Максимиан
Максимилиан
Малик
Малх
Мамбет
Маний
Мануил
Мануэль
Мариан
Мариус
Марк
Маркел
Мартын
Марчелло
Матвей
Матео
Матиас
Матфей
Матфий
Махмуд
Меир
Мелентий
Мелитон
Менахем-Мендель
Месроп
Мефодий
Мечислав
Мика
Микеланджело
Микулаш
Милорад
Мина
Мирко
Мирон
Мирослав
Митрофан
Михаил
Михей
Младан
Модест
Моисей
Мордехай
Мстислав
Мурад
Мухаммед
Мэдисон
Мэлор
Мэлс
Назар
Наиль
Насиф
Натан
Натаниэль
Наум
Нафанаил
Нацагдорж
Нестор
Никандр
Никанор
Никита
Никифор
Никодим
Николай
Нил
Нильс
Ноа
Ной
Норд
Нуржан
Нурлан
Овадья
Оге
Одинец
Октав
Октавиан
Октавий
Октавио
Олаф
Оле
Олег
Оливер
Ольгерд
Онисим
Орест
Осип
Оскар
Осман
Отто
Оттон
Очирбат
Пабло
Павел
Павлин
Павсикакий
Паисий
Палладий
Панкратий
Пантелеймон
Папа
Паруйр
Парфений
Патрик
Пафнутий
Пахомий
Педро
Пётр
Пимен
Пинхас
Пипин
Питирим
Пол
Полидор
Полиевкт
Поликарп
Поликрат
Порфирий
Потап
Предраг
Премысл
Приск
Прокл
Прокопий
Прокул
Протасий
Прохор
Публий
Рагнар
Рагуил
Радмир
Радослав
Разумник
Раймонд
Рамадан
Рамазан
Рахман
Рашад
Рейнхард
Ренат
Реститут
Ричард
Роберт
Родерик
Родион
Рожер
Розарио
Роман
Ромен
Рон
Ронан
Ростислав
Рудольф
Руслан
Руф
Руфин
Рушан
Сабит
Савва
Савватий
Савелий
Савин
Саддам
Садик
Саид
Салават
Салих
Саллюстий
Салман
Самуил
Сармат
Святослав
Севастьян
Северин
Секст
Секунд
Семён
Септимий
Серапион
Сергей
Серж
Сигеберт
Сильвестр
Симеон
Симон
Созон
Соломон
Сонам
Софрон
Спиридон
Срджан
Станислав
Степан
Стефано
Стивен
Таврион
Тавус
Тадеуш
Тарас
Тарасий
Тейс
Тендзин
Теофил
Терентий
Терри
Тиберий
Тигран
Тимофей
Тимур
Тихомир
Тихон
Томас
Томоми
Торос
Тофик
Трифон
Трофим
Тудхалия
Тутмос
Тьерри
Тьяго
Уве
Уильям
Улдис
Ульрих
Ульф
Умар
Урызмаг
Усама
Усман
Фавст
Фаддей
Файзулла
Фарид
Фахраддин
Федериго
Федосей
Федот
Фейсал
Феликс
Феоктист
Феофан
Феофил
Феофилакт
Фердинанд
Ференц
Фёдор
Фидель
Филарет
Филат
Филип
Филипп
Философ
Филострат
Фирс
Фока
Фома
Фотий
Франц
Франческо
Фредерик
Фридрих
Фродо
Фрол
Фульк
Хайме
Ханс
Харальд
Харитон
Харри
Харрисон
Хасан
Хетаг
Хильдерик
Хирам
Хлодвиг
Хокон
Хорив
Хоселито
Хосрой
Хрисанф
Христофор
Хуан
Цэрэндорж
Чеслав
Шалом
Шамиль
Шамсуддин
Шапур
Шарль
Шейх-Хайдар
Шон
Эберхард
Эдмунд
Эдна
Эдуард
Элбэгдорж
Элджернон
Элиас
Эллиот
Эмиль
Энрик
Энрико
Энтони
Эразм
Эраст
Эрик
Эрнст
Эсекьель
Эстебан
Этьен
Ювеналий
Юлиан
Юлий
Юлиус
Юрий
Юстас
Юстин
Яков
Якуб
Якун
Ян
Яни
Януарий
Яромир
Ярополк
Ярослав

View File

@@ -2,11 +2,18 @@ package names
import (
"bufio"
_ "embed"
"math/rand/v2"
"os"
"strings"
)
//go:embed data/names
var embeddedNames string
//go:embed data/surnames
var embeddedSurnames string
var (
firstNames []string
lastNames []string
@@ -24,6 +31,17 @@ var defaultLastNames = []string{
"Орлов", "Андреев", "Макаров", "Никитин", "Захаров", "Зайцев", "Соловьёв", "Борисов", "Яковлев", "Григорьев",
}
func parseEmbedded(raw string) []string {
var names []string
for _, line := range strings.Split(raw, "\n") {
line = strings.TrimSpace(line)
if line != "" {
names = append(names, line)
}
}
return names
}
func loadNames(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
@@ -43,10 +61,21 @@ func loadNames(path string) ([]string, error) {
return names, scanner.Err()
}
func LoadNameFiles(firstPath, lastPath string) error {
firstNames = defaultFirstNames
lastNames = defaultLastNames
func init() {
if names := parseEmbedded(embeddedNames); len(names) > 0 {
firstNames = names
} else {
firstNames = defaultFirstNames
}
if names := parseEmbedded(embeddedSurnames); len(names) > 0 {
lastNames = names
} else {
lastNames = defaultLastNames
}
}
func LoadNameFiles(firstPath, lastPath string) error {
if names, err := loadNames(firstPath); err == nil {
firstNames = names
}

View File

@@ -0,0 +1,66 @@
package protect
import (
"context"
"net"
"net/http"
"syscall"
"time"
)
// Protector is called with a socket file descriptor before connect.
// On Android, this calls VpnService.protect(fd) to bypass VPN routing.
var Protector func(fd int) bool
func controlFunc(network, address string, c syscall.RawConn) error {
if Protector == nil {
return nil
}
var err error
c.Control(func(fd uintptr) {
if !Protector(int(fd)) {
err = &net.OpError{Op: "protect", Net: network, Err: net.ErrClosed}
}
})
return err
}
// NewDialer returns a net.Dialer that calls Protector on each new socket.
func NewDialer() *net.Dialer {
return &net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 30 * time.Second,
Control: controlFunc,
}
}
// NewHTTPClient returns an http.Client using protected sockets.
func NewHTTPClient() *http.Client {
dialer := NewDialer()
transport := &http.Transport{
DialContext: dialer.DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
}
return &http.Client{Transport: transport}
}
// DialContext dials using a protected socket.
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return NewDialer().DialContext(ctx, network, address)
}
// proxyDialer implements golang.org/x/net/proxy.Dialer for pion ICE.
type proxyDialer struct{}
func (d *proxyDialer) Dial(network, addr string) (net.Conn, error) {
return NewDialer().Dial(network, addr)
}
// NewProxyDialer returns a proxy.Dialer that protects ICE sockets.
func NewProxyDialer() *proxyDialer {
return &proxyDialer{}
}

View File

@@ -8,6 +8,7 @@ import (
"net/url"
"github.com/google/uuid"
"github.com/openlibrecommunity/olcrtc/internal/protect"
)
const apiBase = "https://cloud-api.yandex.ru/telemost_front/v2/telemost"
@@ -44,7 +45,8 @@ func GetConnectionInfo(roomURL, displayName string) (*ConnectionInfo, error) {
req.Header.Set("Origin", "https://telemost.yandex.ru")
req.Header.Set("Referer", "https://telemost.yandex.ru/")
resp, err := http.DefaultClient.Do(req)
client := protect.NewHTTPClient()
resp, err := client.Do(req)
if err != nil {
return nil, err
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"github.com/gorilla/websocket"
"github.com/openlibrecommunity/olcrtc/internal/logger"
"github.com/openlibrecommunity/olcrtc/internal/protect"
"github.com/pion/webrtc/v4"
)
@@ -75,6 +76,9 @@ func (p *Peer) Connect(ctx context.Context) error {
}
settingEngine := webrtc.SettingEngine{}
if protect.Protector != nil {
settingEngine.SetICEProxyDialer(protect.NewProxyDialer())
}
api := webrtc.NewAPI(webrtc.WithSettingEngine(settingEngine))
var err error
@@ -169,7 +173,11 @@ func (p *Peer) Connect(ctx context.Context) error {
})
})
ws, _, err := websocket.DefaultDialer.Dial(p.conn.ClientConfig.MediaServerURL, nil)
wsDialer := websocket.Dialer{
NetDialContext: protect.DialContext,
HandshakeTimeout: 15 * time.Second,
}
ws, _, err := wsDialer.Dial(p.conn.ClientConfig.MediaServerURL, nil)
if err != nil {
return err
}
@@ -723,31 +731,37 @@ func (p *Peer) processSendQueue(workerID int) {
if p.dc == nil || p.dc.ReadyState() != webrtc.DataChannelStateOpen {
continue
}
start := time.Now()
// Wait until SCTP buffer drains. Dropping here would corrupt the
// carried TCP streams (the mux is a reliable transport) — large
// downloads like Instagram/Twitter assets would hang forever
// waiting for the missing bytes. Backpressure already propagates
// upstream via CanSend() / the sendQueue length.
waitStart := time.Now()
for p.dc.BufferedAmount() > 64*1024 {
time.Sleep(10 * time.Millisecond)
if time.Since(start) > 3*time.Second {
log.Printf("[WORKER-%d] Buffer wait timeout, dropping packet size=%d", workerID, len(data))
if p.dc.ReadyState() != webrtc.DataChannelStateOpen {
break
}
time.Sleep(10 * time.Millisecond)
}
if time.Since(start) > 3*time.Second {
if waited := time.Since(waitStart); waited > 500*time.Millisecond {
logger.Verbose("[WORKER-%d] Buffer drained after %v", workerID, waited)
}
if p.dc == nil || p.dc.ReadyState() != webrtc.DataChannelStateOpen {
continue
}
sendStart := time.Now()
if err := p.dc.Send(data); err != nil {
log.Printf("[WORKER-%d] Send error: %v", workerID, err)
} else {
elapsed := time.Since(sendStart)
if elapsed > 50*time.Millisecond {
log.Printf("[WORKER-%d] Sent %d bytes in %v (buffered: %d)",
log.Printf("[WORKER-%d] Sent %d bytes in %v (buffered: %d)",
workerID, len(data), elapsed, p.dc.BufferedAmount())
} else {
logger.Verbose("[WORKER-%d] Sent %d bytes (buffered: %d)",
logger.Verbose("[WORKER-%d] Sent %d bytes (buffered: %d)",
workerID, len(data), p.dc.BufferedAmount())
}
}

133
mobile/mobile.go Normal file
View File

@@ -0,0 +1,133 @@
// Package mobile provides a gomobile-compatible API for olcRTC.
// Build with: gomobile bind -target=android ./mobile
package mobile
import (
"context"
"fmt"
"log"
"sync"
"github.com/openlibrecommunity/olcrtc/internal/client"
"github.com/openlibrecommunity/olcrtc/internal/logger"
"github.com/openlibrecommunity/olcrtc/internal/protect"
)
// SocketProtector protects sockets from VPN routing on Android.
// Implement this interface in Kotlin/Java and pass to SetProtector.
type SocketProtector interface {
Protect(fd int) bool
}
// LogWriter receives log messages from olcRTC.
type LogWriter interface {
WriteLog(msg string)
}
var (
mu sync.Mutex
cancel context.CancelFunc
done chan error
)
// SetProtector sets the Android VPN socket protector.
// Must be called before Start.
func SetProtector(p SocketProtector) {
if p == nil {
protect.Protector = nil
return
}
protect.Protector = func(fd int) bool {
return p.Protect(fd)
}
}
// SetLogWriter sets a custom log writer for olcRTC output.
func SetLogWriter(w LogWriter) {
if w != nil {
log.SetOutput(&logBridge{w: w})
}
}
// SetDebug enables or disables verbose logging.
func SetDebug(enabled bool) {
logger.SetVerbose(enabled)
if enabled {
log.SetFlags(log.Ltime | log.Lshortfile)
} else {
log.SetFlags(log.Ltime)
}
}
// Start launches the olcRTC client in background.
// roomID: Telemost room ID (e.g. "xxx-xxx-xxx")
// keyHex: 64-char hex encryption key
// socksPort: local SOCKS5 proxy port (e.g. 10808)
// duo: use dual channels for higher throughput
// socksUser/socksPass: SOCKS5 credentials (empty = no auth)
func Start(roomID, keyHex string, socksPort int, duo bool, socksUser, socksPass string) error {
mu.Lock()
defer mu.Unlock()
if cancel != nil {
return fmt.Errorf("olcRTC already running")
}
if roomID == "" {
return fmt.Errorf("roomID is required")
}
if keyHex == "" {
return fmt.Errorf("keyHex is required")
}
roomURL := "https://telemost.yandex.ru/j/" + roomID
ctx, c := context.WithCancel(context.Background())
cancel = c
done = make(chan error, 1)
go func() {
err := client.Run(ctx, roomURL, keyHex, socksPort, duo, socksUser, socksPass)
mu.Lock()
cancel = nil
mu.Unlock()
done <- err
}()
return nil
}
// Stop gracefully stops the olcRTC client.
func Stop() {
mu.Lock()
c := cancel
d := done
mu.Unlock()
if c == nil {
return
}
c()
if d != nil {
<-d
}
}
// IsRunning returns true if the olcRTC client is active.
func IsRunning() bool {
mu.Lock()
defer mu.Unlock()
return cancel != nil
}
// logBridge adapts LogWriter to io.Writer for log package.
type logBridge struct {
w LogWriter
}
func (b *logBridge) Write(p []byte) (n int, err error) {
b.w.WriteLog(string(p))
return len(p), nil
}