Как стать автором
Обновить

Комментарии 16

GOOS=android GOARCH=arm64 go build -o bin/simple-proxy

Вот это для меня основная киллер фича в Go. Простой командой, не качая ничего больше кроме самого Go, можно собрать бинарник который запустится на любом, даж самом сильно урезанном линуксе. Не знаю есть ли другой такой язык, позволяющий так просто собирать под практически любые таргет системы.

CGO_ENABLED=0 можно еще указать, чтобы сбилдился без общения с внешними библиотеками. Например, в scratch(пустой) контейнере не будет работать без этого

Да. Go очень радует и простотой программирования и удобством сборки. Сделано как для людей. Чего только стоит возможность одним комментарием в коде встроить любые файлы в итоговый бинарник, типа:

//go:embed css/* js/* icon.png manifest.json
staticFiles embed.FS

Потом в несколько строк кода поднять https. И без танцев с бубном получить работающий веб-сервер в виде одного исполняемого файла.

Если есть вызовы библиотек написанных например на СИ , Го не сможет их в бинарник засунуть все, так что внешние зависимости на libc останутся. Можно musl использовать для сборки тогда оно будет одним бинарником. Но просто переменной окружения не достаточно. (Это всё для линукса я написал)

Нужно стараться не усложнять и обходиться без C :-)

Хотя реальность далека от намерений. А в андроиде реальность ещё суровее: даже если нужная либа есть в системе (например, /system/lib64/libc++.so), то линкер не разрешит её загрузить. В старых версиях (до Android 8) можно было, а потом запретили.

В этом случае приходится таскать свою копию файла libc++.so в комплекте с приложением.

.NET 8 умеет AOT компиляцию под целевые системы (с вырезанием "лишних" зависимостей).

Не имею релевантного опыта на Go, но интересно было бы их сравнение performance/usability.

Идея и реализация классные. Но думаю можно проще - список хостов в /etc/hosts https://github.com/StevenBlack/hosts, ну и я добавляю туда yandex.ru (основной генератор русскоязычной рекламы) для полного счастья.

На ноуте /etc/hosts - это первый уровень защиты. Но довольно много времени я провожу за телефоном. А в андроиде hosts особо не поредактируешь без рутования.

Вы почти прошли мой путь полугодовой давности.
Делал заказ с прокси встроенным в андроид приложение. Проблема была в том, что хотелось соксы, а они в андроиде не умеют в авторизацию, пришлось поднимать прокси сервер внутри приложения который читал соксы без авторизации и перенаправлял в реальный сервер с авторизацией.
Большая часть проблем со сборкой упрощается проектом gomobile, дико удобная штука.

Мы ходим похожими путями.

Поначалу у меня это тоже был сокс-прокси (правда тогда я использовал либу с гитхаба: github.com/things-go/go-socks5). Потом переделал всё на свой простой HTTP CONNECT.

И gomobile мне тоже очень помог на старте. Но так как я не использую его GoNativeActivity и сам собираю APK, то вместо `gomobile build` я делаю:

GOOS=android GOARCH=arm64 CGO_ENABLED=1 \
  CGO_CFLAGS="-I/home/user/android_sdk/sources-misc/android-10.0.0_r47/platform/system/netd/include" \
  CC=/home/user/android_sdk/ndk/26.2.11394342/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang \
  CXX=/home/user/android_sdk/ndk/26.2.11394342/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++ \
  go build -ldflags -w -trimpath -buildmode=c-shared -o tmp/android-29/arm64/libdetoxy_proxy.so ./mobile/detoxy-proxy

И получаю точно такую же so-шку, которую делает gomobile, но зато с большим контролем над CGO_CFLAGS и CGO_LDFLAGS

Правильно я понимаю, что прокси не умеет ходить по HTTP/2 (метод CONNECT и общаться чанками)? В данной реализации будто весь трафик сможет ходить туда сюда только по HTTP/1.1 и ниже...

Ага. Тут используется стандартный go net/http сервер. Контроль над соединением получается через интерфейс Hijacker, а он, согласно документации:

The default ResponseWriter for HTTP/1.x connections supports Hijacker,
but HTTP/2 connections intentionally do not.

Но для прокси, по-хорошему, http-сервер и не нужен вовсе. На обычных сокетах можно сделать. Кода чуть больше получится, но и возможностей на порядок больше.

Я тут пытаюсь реализовать для http/2 усердно.
Как вы этом месте вытаскиваете данные самих http запросов для логов и ui?

  log.Println("Transferring:", r.RemoteAddr, "->", r.URL.Host)
  go func() {
    io.Copy(targetConn, clientConn)
    targetConn.Close()
  }()
  io.Copy(clientConn, targetConn)

Я оборачиваю функцию Write перед обработкой данных в io.Copy()

В настоящей прокси всего побольше, и код приёма/передачи данных там выглядит вот так:

type writeFunc func(bytes []byte) (int, error)

func (wf writeFunc) Write(bytes []byte) (int, error) {
	return wf(bytes)
}

func (c *connect) transmit() {
	writeWrapper := writeFunc(func(bytes []byte) (int, error) {
		n, err := c.targetConn.Write(bytes)
		log.Println(logPrefix, "TX:", c.clientAddr, "->", c.targetAddr, n)
		c.proxyStats.RegTx(c.clientHost, c.targetHost, int64(n))
		return n, err
	})
	if _, err := io.Copy(writeWrapper, c.clientConn); err != nil {
		log.Println(logPrefix, "TX err:", c.clientAddr, "->", c.targetAddr, err)
		c.proxyStats.RegError(c.clientHost, c.targetHost)
	}
	c.targetConn.Close()
}

func (c *connect) receive() {
	writeWrapper := writeFunc(func(bytes []byte) (int, error) {
		n, err := c.clientConn.Write(bytes)
		log.Println(logPrefix, "RX:", c.clientAddr, "<-", c.targetAddr, n)
		c.proxyStats.RegRx(c.clientHost, c.targetHost, int64(n))
		return n, err
	})
	if _, err := io.Copy(writeWrapper, c.targetConn); err != nil {
		log.Println(logPrefix, "RX err:", c.clientAddr, "<-", c.targetAddr, err)
		c.proxyStats.RegError(c.clientHost, c.targetHost)
	}
}

connect - это по-сути Handler

proxyStats - структура для хранения статистики, а RegTx и RegRx - функции для регистрации объёма перекачиваемых данных

Вот тут детали того, как я это сделал: https://github.com/detoxy-proxy/detoxy-proxy/tree/master/internal/httpproxy

Весьма занятно.
Если хотите смотреть внутрь https запросов, вот вам моя реализация. Из интересного, это поддержка TLS handshake и менеджер сертификатов. Местами неотлаженное, но работающее детище:
https://github.com/MrDjeb/trueproxy

По итогу поддержка HTTP/2 весьма не тривиальная задача, не делал.

Спасибо за статью! Жду продолжения и исходников )

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории