parent_proxy.go
paquete principal
importar (
“codificación / base64”
“codificación / binario”
“errores”
“fmt”
ss “github.com/shadowsocks/shadowsocks-go/shadowsocks”
“io”
“matemáticas / rand”
“red”
“ordenar”
“strconv”
“sincronizar”
“hora”
)
// Interfaz que todos los tipos de proxies primarios deberían admitir.
escriba la interfaz ParentProxy {
connect (* URL) (net.Conn, error)
getServer () string // para usar en la actualización de la latencia del servidor
cadena GenConfig () // para actualizar la configuración
}
- ¿Es posible para mí encontrar qué programas tienen la mayor prioridad de ancho de banda?
- ¿Cuáles son los diversos estándares de red?
- ¿Por qué dosificar el protocolo Openflow existe la tabla de grupo? ¿Y cuál es la relación entre la tubería y la tabla de grupo?
- Cómo manejar la autenticación y autorización para conexiones TCP
- ¿Cómo podemos entender el concepto de envío / recepción de bytes en términos físicos?
// Interfaz para diferentes estrategias de selección de proxy.
escriba la interfaz ParentPool {
agregar (ParentProxy)
vacío () bool
// Seleccione un proxy del grupo y conéctese. Puede probar varios proxies hasta
// uno que tenga éxito, devuelve nil y error si fallan todos los proxies principales.
connect (* URL) (net.Conn, error)
}
// Inicia parentProxy para que sea un grupo de respaldo. Entonces, el análisis de configuración tiene un grupo para agregar
// proxies principales.
var parentProxy ParentPool = & backupParentPool {}
func initParentPool () {
backPool, ok: = parentProxy. (* backupParentPool)
si! ok {
pánico (“el grupo primario inicial debe ser un grupo de respaldo”)
}
si depurar {
printParentProxy (backPool.parent)
}
if len (backPool.parent) == 0 {
info.Println (“sin servidor proxy padre”)
regreso
}
if len (backPool.parent) == 1 && config.LoadBalance! = loadBalanceBackup {
debug.Println (“solo 1 padre, sin necesidad de equilibrio de carga”)
config.LoadBalance = loadBalanceBackup
}
cambiar config.LoadBalance {
case loadBalanceHash:
debug.Println (“grupo padre hash”, len (backPool.parent))
parentProxy = & hashParentPool {* backPool}
case loadBalanceLatency:
debug.Println (“grupo primario de latencia”, len (backPool.parent))
ir a updateParentProxyLatency ()
parentProxy = newLatencyParentPool (backPool.parent)
}
}
func printParentProxy (parent [] ParentWithFail) {
debug.Println (“proxies padre disponibles:”)
para _, pp: = rango padre {
cambiar pc: = pp.ParentProxy. (tipo) {
caso * shadowsocks
debug.Println (“\ tshadowsocks:”, pc.server)
case * httpParent:
debug.Println (“\ thttp parent:”, pc.server)
estuche * calcetines
debug.Println (“\ tsocks parent:”, pc.server)
caso * vaca
debug.Println (“\ tcow parent:”, pc.server)
}
}
}
escriba ParentWithFail struct {
ParentProxy
fallar int
}
// Estrategia de equilibrio de carga de respaldo:
// Seleccione proxy en el orden en que aparecen en la configuración.
escriba backupParentPool struct {
padre [] ParentWithFail
}
func (pp * backupParentPool) empty () bool {
return len (pp.parent) == 0
}
func (pp * backupParentPool) add (parent ParentProxy) {
pp.parent = append (pp.parent, ParentWithFail {parent, 0})
}
func (pp * backupParentPool) connect (url * URL) (srvconn net.Conn, error de error) {
return connectInOrder (url, pp.parent, 0)
}
// Estrategia de equilibrio de carga de hash:
// Cada host usará un proxy basado en un valor hash.
escriba hashParentPool struct {
backupParentPool
}
func (pp * hashParentPool) connect (url * URL) (srvconn net.Conn, error de error) {
inicio: = int (stringHash (url.Host)% uint64 (len (pp.parent)))
debug.Printf (“hash host% s try% d parent first”, url.Host, start)
return connectInOrder (url, pp.parent, start)
}
func (parent * ParentWithFail) connect (url * URL) (srvconn net.Conn, error de error) {
const maxFailCnt = 30
srvconn, err = parent.ParentProxy.connect (url)
if err! = nil {
if parent.fail <maxFailCnt &&! networkBad () {
parent.fail ++
}
regreso
}
parent.fail = 0
regreso
}
func connectInOrder (url * URL, pp [] ParentWithFail, start int) (srvconn net.Conn, error de error) {
const baseFailCnt = 9
var omitió [] int
nproxy: = len (pp)
si nproxy == 0 {
return nil, errors.New (“sin proxy principal”)
}
para i: = 0; i <nproxy; i ++ {
proxyId: = (inicio + i)% nproxy
padre: = & pp [proxyId]
// omite el servidor fallido, pero pruébelo con cierta probabilidad
if parent.fail> 0 && rand.Intn (parent.fail + baseFailCnt)! = 0 {
omitido = agregar (omitido, proxyId)
continuar
}
si srvconn, err = parent.connect (url); err == nil {
regreso
}
}
// último recurso, intente omitir uno, no es probable que tenga éxito
para _, skippedId: = rango omitido {
si srvconn, err = pp [skippedId] .connect (url); err == nil {
regreso
}
}
volver nil, err
}
escriba ParentWithLatency struct {
ParentProxy
tiempo de latencia.
}
type latencyParentPool struct {
parent [] ParentWithLatency
}
func newLatencyParentPool (parent [] ParentWithFail) * latencyParentPool {
lp: = & latencyParentPool {}
para _, p: = rango padre {
lp.add (p.ParentProxy)
}
volver lp
}
func (pp * latencyParentPool) empty () bool {
return len (pp.parent) == 0
}
func (pp * latencyParentPool) add (parent ParentProxy) {
pp.parent = append (pp.parent, ParentWithLatency {parent, 0})
}
// Ordenar la interfaz.
func (pp * latencyParentPool) Len () int {
return len (pp. padre)
}
func (pp * latencyParentPool) Swap (i, j int) {
p: = pp.parent
p [i], p [j] = p [j], p [i]
}
func (pp * latencyParentPool) Less (i, j int) bool {
p: = pp.parent
devuelve p [i] .latency <p [j] .latency
}
const latencyMax = time.Hour
var latencyMutex sync.RWMutex
func (pp * latencyParentPool) connect (url * URL) (srvconn net.Conn, error de error) {
var lp [] ParentWithLatency
// Leer rebanada primero.
latencyMutex.RLock ()
lp = pp.parent
latencyMutex.RUnlock ()
var omitió [] int
nproxy: = len (lp)
si nproxy == 0 {
return nil, errors.New (“sin proxy principal”)
}
para i: = 0; i <nproxy; i ++ {
padre: = lp [i]
if parent.latency> = latencyMax {
omitido = agregar (omitido, i)
continuar
}
si srvconn, err = parent.connect (url); err == nil {
debug.Println (“proxy de latencia más baja”, parent.getServer ())
regreso
}
parent.latency = latencyMax
}
// último recurso, intente omitir uno, no es probable que tenga éxito
para _, skippedId: = rango omitido {
si srvconn, err = lp [skippedId] .connect (url); err == nil {
regreso
}
}
volver nil, err
}
func (parent * ParentWithLatency) updateLatency (wg * sync.WaitGroup) {
diferir wg.Done ()
proxy: = parent.ParentProxy
servidor: = proxy.getServer ()
host, puerto, err: = net.SplitHostPort (servidor)
if err! = nil {
pánico (“error del servidor principal del puerto de host dividido” + err.Error ())
}
// Primero se resuelve el nombre de host, por lo que la latencia no incluye el tiempo de resolución.
ip, err: = net.LookupHost (host)
if err! = nil {
parent.latency = latencyMax
regreso
}
ipPort: = net.JoinHostPort (ip [0], puerto)
const N = 3
var tiempo total. Duración
para i: = 0; i <N; i ++ {
ahora: = time.Now ()
cn, err: = net.DialTimeout (“tcp”, ipPort, dialTimeout)
if err! = nil {
debug.Println (“marcado de actualización de latencia:”, err)
total + = tiempo Minuto // 1 minuto como penalización
continuar
}
total + = tiempo.Ahora (). Sub (ahora)
cn.Close ()
time.Sleep (5 * time.Millisecond)
}
parent.latency = total / N
debug.Println (“latencia”, servidor, latencia principal)
}
func (pp * latencyParentPool) updateLatency () {
// Cree una copia, actualice la latencia para la copia.
latencia var cpParentPool
cp.parent = append (cp.parent, pp.parent …)
// cp.parent es valor en lugar de puntero, si usamos `_, p: = rango cp.parent`,
// el valor en cp.parent no se actualizará.
var wg sync.WaitGroup
wg.Add (len (cp.parent))
para i, _: = rango cp.parent {
cp.parent [i] .updateLatency (& wg)
}
wg.Wait ()
// Ordenar según la latencia.
sort.Stable (& cp)
debug.Println (“proxy más bajo de latencia”, cp.parent [0] .getServer ())
// Actualizar segmento principal.
latencyMutex.Lock ()
pp.parent = cp.parent
latenciaMutex.Unlock ()
}
func updateParentProxyLatency () {
lp, ok: = parentProxy. (* latencyParentPool)
si! ok {
regreso
}
para {
lp.updateLatency ()
time.Sleep (60 * time.Second)
}
}
// proxy padre http
escriba httpParent struct {
cadena de servidor
cadena userPasswd // para la configuración de actualización
authHeader [] byte
}
escriba httpConn struct {
net.Conn
padre * httpParent
}
func (s httpConn) String () string {
devuelve “proxy padre http” + s.parent.server
}
func newHttpParent (cadena del servidor) * httpParent {
return & httpParent {servidor: servidor}
}
func (hp * httpParent) getServer () cadena {
return hp.server
}
cadena func (hp * httpParent) genConfig () {
if hp.userPasswd! = “” {
return fmt.Sprintf (“proxy = http: //% [correo electrónico protegido] % s”, hp.userPasswd, hp.server)
} más {
return fmt.Sprintf (“proxy = http: //% s”, hp.server)
}
}
func (hp * httpParent) initAuth (cadena userPasswd) {
if userPasswd == “” {
regreso
}
hp.userPasswd = userPasswd
b64: = base64.StdEncoding.EncodeToString ([] byte (userPasswd))
hp.authHeader = [] byte (headerProxyAuthorization + “: Basic” + b64 + CRLF)
}
func (hp * httpParent) connect (url * URL) (net.Conn, error) {
c, err: = net.Dial (“tcp”, hp.server)
if err! = nil {
errl.Printf (“no se puede conectar al padre http% s para% s:% v \ n”,
hp.server, url.HostPort, err)
volver nil, err
}
debug.Printf (“conectado a:% s a través de http padre:% s \ n”,
url.HostPort, hp.server)
return httpConn {c, hp}, nil
}
// proxy principal shadowsocks
type shadowsocksParent struct {
cadena de servidor
cadena de método // método y passwd son para configuración de actualización
cadena passwd
cifrado * ss.Cipher
}
escriba shadowsocksConn struct {
net.Conn
padre * shadowsocksParent
}
func (s shadowsocksConn) String () string {
devuelve “shadowsocks proxy” + s.parent.server
}
// Para usar el proxy padre en el orden especificado en el archivo de configuración, nosotros
// inserte un proxy no inicializado en la lista de proxy padre e inicialícelo
// cuando se ha analizado toda su configuración.
func newShadowsocksParent (cadena del servidor) * shadowsocksParent {
return & shadowsocksParent {servidor: servidor}
}
func (sp * shadowsocksParent) getServer () string {
volver sp.server
}
func (sp * shadowsocksParent) genConfig () string {
método: = método sp.
si método == “” {
método = “tabla”
}
return fmt.Sprintf (“proxy = ss: //% s:% [correo electrónico protegido] % s”, método, sp.passwd, sp.server)
}
func (sp * shadowsocksParent) initCipher (método, cadena passwd) {
sp.method = método
sp.passwd = passwd
cifrado, err: = ss.NewCipher (método, contraseña)
if err! = nil {
Fatal (“crear cifrado shadowsocks:”, err)
}
sp.cipher = cifrado
}
func (sp * shadowsocksParent) connect (url * URL) (net.Conn, error) {
c, err: = ss.Dial (url.HostPort, sp.server, sp.cipher.Copy ())
if err! = nil {
errl.Printf (“no se puede conectar a shadowsocks parent% s para% s:% v \ n”,
sp.server, url.HostPort, err)
volver nil, err
}
debug.Println (“conectado a:”, url.HostPort, “a través de shadowsocks:”, sp.server)
return shadowsocksConn {c, sp}, nil
}
// proxy de padre vaca
tipo cowParent struct {
cadena de servidor
cadena de método
cadena passwd
cifrado * ss.Cipher
}
escriba cowConn struct {
net.Conn
padre * vaca
}
func (s cowConn) String () string {
devuelve “proxy de vaca” + s.parent.server
}
func newCowParent (srv, método, cadena passwd) * cowParent {
cifrado, err: = ss.NewCipher (método, contraseña)
if err! = nil {
Fatal (“crear cifrado de vaca:”, err)
}
return & cowParent {srv, method, passwd, cipher}
}
func (cp * cowParent) getServer () cadena {
return cp.server
}
func (cp * cowParent) genConfig () string {
método: = método cp.
si método == “” {
método = “tabla”
}
return fmt.Sprintf (“proxy = vaca: //% s:% [correo electrónico protegido] % s”, método, cp.passwd, cp.server)
}
func (cp * cowParent) connect (url * URL) (net.Conn, error) {
c, err: = net.Dial (“tcp”, cp.server)
if err! = nil {
errl.Printf (“no se puede conectar al padre de vaca% s para% s:% v \ n”,
cp.server, url.HostPort, err)
volver nil, err
}
debug.Printf (“conectado a:% s a través del padre de vaca:% s \ n”,
url.HostPort, cp.server)
ssconn: = ss.NewConn (c, cp.cipher.Copy ())
return cowConn {ssconn, cp}, nil
}
// Para la documentación de calcetines, consulte rfc 1928 http://www.ietf.org/rfc/rfc1928.txt
var socksError = […] cadena {
1: “Error general del servidor SOCKS”,
2: “Conexión no permitida por el conjunto de reglas”,
3: “Red inalcanzable”,
4: “Host inalcanzable”,
5: “Conexión rechazada”,
6: “TTL expiró”,
7: “Comando no compatible”,
8: “Tipo de dirección no compatible”,
9: “a X’FF ‘sin asignar”,
}
var socksProtocolErr = errors.New (“error de protocolo de calcetines”)
var socksMsgVerMethodSelection = [] byte {
0x5, // versión 5
1, // método n
0, // no se requiere autorización
}
// calcetines5 proxy padre
tipo calcetines Estructura paterna {
cadena de servidor
}
tipo calcetinesConn struct {
net.Conn
padres * calcetines
}
func (s socksConn) String () string {
return “calcetines proxy” + s.parent.server
}
func newSocksParent (cadena del servidor) * socksParent {
return & socksParent {servidor}
}
func (sp * socksParent) getServer () string {
return sp.server
}
func (sp * socksParent) genConfig () string {
return fmt.Sprintf (“proxy = socks5: //% s”, sp.server)
}
func (sp * socksParent) connect (url * URL) (net.Conn, error) {
c, err: = net.Dial (“tcp”, sp.server)
if err! = nil {
errl.Printf (“no se puede conectar a los calcetines padre% s para% s:% v \ n”,
sp.server, url.HostPort, err)
volver nil, err
}
hasErr: = falso
diferir func () {
si hasErr {
c. Cerrar ()
}
} ()
var n int
si n, err = c.Write (socksMsgVerMethodSelection); n! = 3 || err! = nil {
errl.Printf (“enviando selección de ver / método msg% vn =% v \ n”, err, n)
hasErr = verdadero
volver nil, err
}
// selección de versión / método
repBuf: = make ([] byte, 2)
_, err = io.ReadFull (c, repBuf)
if err! = nil {
errl.Printf (“leer ver / método error de selección% v \ n”, err)
hasErr = verdadero
volver nil, err
}
si repBuf [0]! = 5 || repBuf [1]! = 0 {
errl.Printf (“calcetines ver / método selección error respuesta ver% d método% d”,
repBuf [0], repBuf [1])
hasErr = verdadero
volver nil, err
}
// debug.Println (“Selección de versión de calcetines realizada”)
// enviar solicitud de conexión
host: = url.Host
puerto, err: = strconv.Atoi (url.Port)
if err! = nil {
errl.Printf (“no debería suceder, error de puerto% v \ n”, puerto)
hasErr = verdadero
volver nil, err
}
hostLen: = len (host)
bufLen: = 5 + hostLen + 2 // last 2 is port
reqBuf: = make ([] byte, bufLen)
reqBuf [0] = 5 // versión 5
reqBuf [1] = 1 // cmd: conectar
// reqBuf [2] = 0 // rsv: establecido en 0 al inicializar
reqBuf [3] = 3 // atyp: nombre de dominio
reqBuf [4] = byte (hostLen)
copia (reqBuf [5:], host)
binary.BigEndian.PutUint16 (reqBuf [5 + hostLen: 5 + hostLen + 2], uint16 (puerto))
si n, err = c. Escribir (reqBuf); err! = nil || n! = bufLen {
errl.Printf (“enviar solicitud de calcetines err% vn% d \ n”, err, n)
hasErr = verdadero
volver nil, err
}
// No estoy claro por qué el búfer está fijado en 10. El documento rfc no dice esto.
// Polipo estableció esto en 10 y también observé que la respuesta siempre es 10.
replyBuf: = make ([] byte, 10)
si n, err = c. Leer (replyBuf); err! = nil {
// Parece que el servidor de calcetines cerrará la conexión si no puede encontrar el host
if err! = io.EOF {
errl.Printf (“leer calcetines responder err% vn% d \ n”, err, n)
}
hasErr = verdadero
return nil, errors.New (“falló la conexión (por servidor de calcetines” + sp.server + “). ¿No hay tal host?”)
}
// debug.Printf (“Longitud de respuesta de calcetines% d \ n”, n)
if replyBuf [0]! = 5 {
errl.Printf (“los calcetines responden connect% s VER% d no son compatibles \ n”, url.HostPort, replyBuf [0])
hasErr = verdadero
return nil, socksProtocolErr
}
if replyBuf [1]! = 0 {
errl.Printf (“los calcetines responden conectan% s error% s \ n”, url.HostPort, socksError [replyBuf [1]])
hasErr = verdadero
return nil, socksProtocolErr
}
if replyBuf [3]! = 1 {
errl.Printf (“los calcetines responden connect% s ATYP% d \ n”, url.HostPort, replyBuf [3])
hasErr = verdadero
return nil, socksProtocolErr
}
debug.Println (“conectado a:”, url.HostPort, “a través del servidor de calcetines:”, servidor sp.)
// Ahora el socket se puede usar para pasar datos.
calcetines de retornoConn {c, sp}, nil
}