committed by
GitHub
7 changed files with 823 additions and 2 deletions
@ -0,0 +1,546 @@ |
|||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|||
// SPDX-License-Identifier: MIT
|
|||
|
|||
package main |
|||
|
|||
import ( |
|||
"bytes" |
|||
"context" |
|||
"crypto/tls" |
|||
"encoding/json" |
|||
"flag" |
|||
"fmt" |
|||
"io" |
|||
"log" |
|||
"net" |
|||
"net/http" |
|||
"os" |
|||
"os/signal" |
|||
"sync" |
|||
"sync/atomic" |
|||
"syscall" |
|||
"time" |
|||
|
|||
"github.com/cbeuw/connutil" |
|||
"github.com/google/uuid" |
|||
"github.com/pion/dtls/v3" |
|||
"github.com/pion/dtls/v3/pkg/crypto/selfsign" |
|||
"github.com/pion/logging" |
|||
"github.com/pion/turn/v4" |
|||
) |
|||
|
|||
func doRequest(data string, url string) (resp map[string]interface{}, err error) { |
|||
client := &http.Client{ |
|||
Timeout: 20 * time.Second, |
|||
Transport: &http.Transport{ |
|||
MaxIdleConns: 100, |
|||
MaxIdleConnsPerHost: 100, |
|||
IdleConnTimeout: 90 * time.Second, |
|||
}, |
|||
} |
|||
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data))) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0") |
|||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded") |
|||
|
|||
httpResp, err := client.Do(req) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
defer httpResp.Body.Close() |
|||
|
|||
body, err := io.ReadAll(httpResp.Body) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
err = json.Unmarshal(body, &resp) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return resp, nil |
|||
} |
|||
|
|||
func getCreds(link string) (string, string, string, error) { |
|||
var resp map[string]interface{} |
|||
defer func() { |
|||
if r := recover(); r != nil { |
|||
log.Panicf("get TURN creds error: %v\n\n", resp) |
|||
} |
|||
}() |
|||
|
|||
data := "client_secret=QbYic1K3lEV5kTGiqlq2&client_id=6287487&scopes=audio_anonymous%2Cvideo_anonymous%2Cphotos_anonymous%2Cprofile_anonymous&isApiOauthAnonymEnabled=false&version=1&app_id=6287487" |
|||
url := "https://login.vk.ru/?act=get_anonym_token" |
|||
|
|||
resp, err := doRequest(data, url) |
|||
if err != nil { |
|||
return "", "", "", fmt.Errorf("request error:%s", err) |
|||
} |
|||
|
|||
token1 := resp["data"].(map[string]interface{})["access_token"].(string) |
|||
|
|||
data = fmt.Sprintf("access_token=%s", token1) |
|||
url = "https://api.vk.ru/method/calls.getAnonymousAccessTokenPayload?v=5.264&client_id=6287487" |
|||
|
|||
resp, err = doRequest(data, url) |
|||
if err != nil { |
|||
return "", "", "", fmt.Errorf("request error:%s", err) |
|||
} |
|||
|
|||
token2 := resp["response"].(map[string]interface{})["payload"].(string) |
|||
|
|||
data = fmt.Sprintf("client_id=6287487&token_type=messages&payload=%s&client_secret=QbYic1K3lEV5kTGiqlq2&version=1&app_id=6287487", token2) |
|||
url = "https://login.vk.ru/?act=get_anonym_token" |
|||
|
|||
resp, err = doRequest(data, url) |
|||
if err != nil { |
|||
return "", "", "", fmt.Errorf("request error:%s", err) |
|||
} |
|||
|
|||
token3 := resp["data"].(map[string]interface{})["access_token"].(string) |
|||
|
|||
data = fmt.Sprintf("vk_join_link=https://vk.com/call/join/%s&name=123&access_token=%s", link, token3) |
|||
url = "https://api.vk.ru/method/calls.getAnonymousToken?v=5.264" |
|||
|
|||
resp, err = doRequest(data, url) |
|||
if err != nil { |
|||
return "", "", "", fmt.Errorf("request error:%s", err) |
|||
} |
|||
|
|||
token4 := resp["response"].(map[string]interface{})["token"].(string) |
|||
|
|||
data = fmt.Sprintf("%s%s%s", "session_data=%7B%22version%22%3A2%2C%22device_id%22%3A%22", uuid.New(), "%22%2C%22client_version%22%3A1.1%2C%22client_type%22%3A%22SDK_JS%22%7D&method=auth.anonymLogin&format=JSON&application_key=CGMMEJLGDIHBABABA") |
|||
url = "https://calls.okcdn.ru/fb.do" |
|||
|
|||
resp, err = doRequest(data, url) |
|||
if err != nil { |
|||
return "", "", "", fmt.Errorf("request error:%s", err) |
|||
} |
|||
|
|||
token5 := resp["session_key"].(string) |
|||
|
|||
data = fmt.Sprintf("joinLink=%s&isVideo=false&protocolVersion=5&anonymToken=%s&method=vchat.joinConversationByLink&format=JSON&application_key=CGMMEJLGDIHBABABA&session_key=%s", link, token4, token5) |
|||
url = "https://calls.okcdn.ru/fb.do" |
|||
|
|||
resp, err = doRequest(data, url) |
|||
if err != nil { |
|||
return "", "", "", fmt.Errorf("request error:%s", err) |
|||
} |
|||
|
|||
user := resp["turn_server"].(map[string]interface{})["username"].(string) |
|||
pass := resp["turn_server"].(map[string]interface{})["credential"].(string) |
|||
turn := resp["turn_server"].(map[string]interface{})["urls"].([]interface{})[0].(string) |
|||
|
|||
return user, pass, turn, nil |
|||
} |
|||
|
|||
func dtlsFunc(ctx context.Context, conn net.PacketConn, peer *net.UDPAddr) (net.Conn, error) { |
|||
certificate, err := selfsign.GenerateSelfSigned() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
config := &dtls.Config{ |
|||
Certificates: []tls.Certificate{certificate}, |
|||
InsecureSkipVerify: true, |
|||
ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, |
|||
CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, |
|||
ConnectionIDGenerator: dtls.OnlySendCIDGenerator(), |
|||
} |
|||
ctx1, cancel := context.WithTimeout(ctx, 30*time.Second) |
|||
defer cancel() |
|||
dtlsConn, err := dtls.Client(conn, peer, config) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
if err := dtlsConn.HandshakeContext(ctx1); err != nil { |
|||
return nil, err |
|||
} |
|||
return dtlsConn, nil |
|||
} |
|||
|
|||
func oneDtlsConnection(ctx context.Context, peer *net.UDPAddr, listenConn net.PacketConn, connchan chan<- net.PacketConn, okchan chan<- struct{}, c1 chan<- error) { |
|||
var err error = nil |
|||
defer func() { c1 <- err }() |
|||
dtlsctx, dtlscancel := context.WithCancel(ctx) |
|||
defer dtlscancel() |
|||
var conn1, conn2 net.PacketConn |
|||
conn1, conn2 = connutil.AsyncPacketPipe() |
|||
go func() { |
|||
for { |
|||
select { |
|||
case <-dtlsctx.Done(): |
|||
return |
|||
case connchan <- conn2: |
|||
} |
|||
} |
|||
}() |
|||
dtlsConn, err1 := dtlsFunc(dtlsctx, conn1, peer) |
|||
if err1 != nil { |
|||
err = fmt.Errorf("failed to connect DTLS: %s", err1) |
|||
return |
|||
} |
|||
defer func() { |
|||
if closeErr := dtlsConn.Close(); closeErr != nil { |
|||
err = fmt.Errorf("failed to close DTLS connection: %s", closeErr) |
|||
return |
|||
} |
|||
log.Printf("Closed DTLS connection\n") |
|||
}() |
|||
log.Printf("Established DTLS connection!\n") |
|||
go func() { |
|||
for { |
|||
select { |
|||
case <-dtlsctx.Done(): |
|||
return |
|||
case okchan <- struct{}{}: |
|||
} |
|||
} |
|||
}() |
|||
|
|||
wg := sync.WaitGroup{} |
|||
wg.Add(2) |
|||
context.AfterFunc(dtlsctx, func() { |
|||
listenConn.SetDeadline(time.Now()) |
|||
dtlsConn.SetDeadline(time.Now()) |
|||
}) |
|||
var addr atomic.Value |
|||
// Start read-loop on listenConn
|
|||
go func() { |
|||
defer wg.Done() |
|||
defer dtlscancel() |
|||
buf := make([]byte, 1600) |
|||
for { |
|||
select { |
|||
case <-dtlsctx.Done(): |
|||
return |
|||
default: |
|||
} |
|||
n, addr1, err1 := listenConn.ReadFrom(buf) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
|
|||
addr.Store(addr1) // store peer
|
|||
|
|||
_, err1 = dtlsConn.Write(buf[:n]) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
|
|||
// Start read-loop on dtlsConn
|
|||
go func() { |
|||
defer wg.Done() |
|||
defer dtlscancel() |
|||
buf := make([]byte, 1600) |
|||
for { |
|||
select { |
|||
case <-dtlsctx.Done(): |
|||
return |
|||
default: |
|||
} |
|||
n, err1 := dtlsConn.Read(buf) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
addr1, ok := addr.Load().(net.Addr) |
|||
if !ok { |
|||
log.Printf("Failed: no listener ip") |
|||
return |
|||
} |
|||
|
|||
_, err1 = listenConn.WriteTo(buf[:n], addr1) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
|
|||
wg.Wait() |
|||
listenConn.SetDeadline(time.Time{}) |
|||
dtlsConn.SetDeadline(time.Time{}) |
|||
} |
|||
|
|||
func oneTurnConnection(ctx context.Context, host string, port string, link string, udp bool, realm string, peer net.Addr, conn2 net.PacketConn, c chan<- error) { |
|||
var err error = nil |
|||
defer func() { c <- err }() |
|||
user, pass, url, err1 := getCreds(link) |
|||
if err1 != nil { |
|||
err = fmt.Errorf("failed to get TURN credentials: %s", err1) |
|||
return |
|||
} |
|||
var turnServerAddr string |
|||
if host == "" || port == "" { |
|||
turnServerAddr = url[5:] |
|||
} else { |
|||
turnServerAddr = net.JoinHostPort(host, port) |
|||
} |
|||
turnServerUdpAddr, err1 := net.ResolveUDPAddr("udp", turnServerAddr) |
|||
if err1 != nil { |
|||
err = fmt.Errorf("failed to resolve TURN server address: %s", err1) |
|||
return |
|||
} |
|||
turnServerAddr = turnServerUdpAddr.String() |
|||
fmt.Println(turnServerUdpAddr.IP) |
|||
// Dial TURN Server
|
|||
var cfg *turn.ClientConfig |
|||
var turnConn net.PacketConn |
|||
var d net.Dialer |
|||
ctx1, cancel := context.WithTimeout(ctx, 5*time.Second) |
|||
defer cancel() |
|||
if udp { |
|||
turnConn, err1 = net.ListenPacket("udp", "") // nolint: noctx
|
|||
if err1 != nil { |
|||
err = fmt.Errorf("failed to connect to TURN server: %s", err1) |
|||
return |
|||
} |
|||
defer func() { |
|||
if err1 = turnConn.Close(); err1 != nil { |
|||
err = fmt.Errorf("failed to close TURN server connection: %s", err1) |
|||
return |
|||
} |
|||
}() |
|||
} else { |
|||
conn, err2 := d.DialContext(ctx1, "tcp", turnServerAddr) // nolint: noctx
|
|||
if err2 != nil { |
|||
err = fmt.Errorf("failed to connect to TURN server: %s", err2) |
|||
return |
|||
} |
|||
defer func() { |
|||
if err1 = conn.Close(); err1 != nil { |
|||
err = fmt.Errorf("failed to close TURN server connection: %s", err1) |
|||
return |
|||
} |
|||
}() |
|||
turnConn = turn.NewSTUNConn(conn) |
|||
} |
|||
// Start a new TURN Client and wrap our net.Conn in a STUNConn
|
|||
// This allows us to simulate datagram based communication over a net.Conn
|
|||
cfg = &turn.ClientConfig{ |
|||
STUNServerAddr: turnServerAddr, |
|||
TURNServerAddr: turnServerAddr, |
|||
Conn: turnConn, |
|||
Username: user, |
|||
Password: pass, |
|||
Realm: realm, |
|||
LoggerFactory: logging.NewDefaultLoggerFactory(), |
|||
} |
|||
|
|||
client, err1 := turn.NewClient(cfg) |
|||
if err1 != nil { |
|||
err = fmt.Errorf("failed to create TURN client: %s", err1) |
|||
return |
|||
} |
|||
defer client.Close() |
|||
|
|||
// Start listening on the conn provided.
|
|||
err1 = client.Listen() |
|||
if err1 != nil { |
|||
err = fmt.Errorf("failed to listen: %s", err1) |
|||
return |
|||
} |
|||
|
|||
// Allocate a relay socket on the TURN server. On success, it
|
|||
// will return a net.PacketConn which represents the remote
|
|||
// socket.
|
|||
relayConn, err1 := client.Allocate() |
|||
if err1 != nil { |
|||
err = fmt.Errorf("failed to allocate: %s", err1) |
|||
return |
|||
} |
|||
defer func() { |
|||
if err1 := relayConn.Close(); err1 != nil { |
|||
err = fmt.Errorf("failed to close TURN allocated connection: %s", err1) |
|||
} |
|||
}() |
|||
|
|||
// The relayConn's local address is actually the transport
|
|||
// address assigned on the TURN server.
|
|||
log.Printf("relayed-address=%s", relayConn.LocalAddr().String()) |
|||
|
|||
wg := sync.WaitGroup{} |
|||
wg.Add(2) |
|||
turnctx, turncancel := context.WithCancel(context.Background()) |
|||
context.AfterFunc(turnctx, func() { |
|||
relayConn.SetDeadline(time.Now()) |
|||
conn2.SetDeadline(time.Now()) |
|||
}) |
|||
// Start read-loop on conn2 (output of DTLS)
|
|||
go func() { |
|||
defer wg.Done() |
|||
defer turncancel() |
|||
buf := make([]byte, 1600) |
|||
for { |
|||
select { |
|||
case <-turnctx.Done(): |
|||
return |
|||
default: |
|||
} |
|||
n, _, err1 := conn2.ReadFrom(buf) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
|
|||
_, err1 = relayConn.WriteTo(buf[:n], peer) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
|
|||
// Start read-loop on relayConn
|
|||
go func() { |
|||
defer wg.Done() |
|||
defer turncancel() |
|||
buf := make([]byte, 1600) |
|||
for { |
|||
select { |
|||
case <-turnctx.Done(): |
|||
return |
|||
default: |
|||
} |
|||
n, _, err1 := relayConn.ReadFrom(buf) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
|
|||
_, err1 = conn2.WriteTo(buf[:n], nil) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
|
|||
wg.Wait() |
|||
relayConn.SetDeadline(time.Time{}) |
|||
conn2.SetDeadline(time.Time{}) |
|||
} |
|||
|
|||
func main() { //nolint:cyclop
|
|||
ctx, cancel := context.WithCancel(context.Background()) |
|||
defer cancel() |
|||
signalChan := make(chan os.Signal, 1) |
|||
signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT) |
|||
go func() { |
|||
<-signalChan |
|||
log.Printf("Terminating...\n") |
|||
cancel() |
|||
select { |
|||
case <-signalChan: |
|||
case <-time.After(5 * time.Second): |
|||
} |
|||
log.Fatalf("Exit...\n") |
|||
}() |
|||
|
|||
host := flag.String("turn", "", "override TURN server ip") |
|||
port := flag.String("port", "19302", "override TURN port") |
|||
listen := flag.String("listen", "127.0.0.1:9000", "listen on ip:port") |
|||
realm := flag.String("realm", "call6-7.vkuser.net", "TURN Realm") |
|||
link := flag.String("link", "", "VK calls invite link \"https://vk.com/call/join/...\"") |
|||
peerAddr := flag.String("peer", "", "peer server address (host:port)") |
|||
n := flag.Int("n", 16, "connections to TURN") |
|||
udp := flag.Bool("udp", false, "connect to TURN with UDP") |
|||
flag.Parse() |
|||
if *peerAddr == "" { |
|||
log.Panicf("Need peer address!") |
|||
} |
|||
if *link == "" { |
|||
log.Panicf("Need invite link!") |
|||
} |
|||
peer, err := net.ResolveUDPAddr("udp", *peerAddr) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
*link = (*link)[len(*link)-43:] |
|||
|
|||
listenConn, err := net.ListenPacket("udp", *listen) // nolint: noctx
|
|||
if err != nil { |
|||
log.Panicf("Failed to listen: %s", err) |
|||
} |
|||
defer func() { |
|||
if closeErr := listenConn.Close(); closeErr != nil { |
|||
log.Panicf("Failed to close local connection: %s", closeErr) |
|||
} |
|||
}() |
|||
|
|||
okchan := make(chan struct{}) |
|||
connchan := make(chan net.PacketConn) |
|||
|
|||
wg1 := sync.WaitGroup{} |
|||
wg1.Go(func() { |
|||
for { |
|||
c1 := make(chan error) |
|||
go oneDtlsConnection(ctx, peer, listenConn, connchan, okchan, c1) |
|||
if err := <-c1; err != nil { |
|||
log.Printf("%s", err) |
|||
} |
|||
select { |
|||
case <-ctx.Done(): |
|||
return |
|||
default: |
|||
} |
|||
} |
|||
}) |
|||
|
|||
t := time.Tick(100 * time.Millisecond) |
|||
wg1.Go(func() { |
|||
for { |
|||
select { |
|||
case <-ctx.Done(): |
|||
return |
|||
case conn2 := <-connchan: |
|||
select { |
|||
case <-t: |
|||
c := make(chan error) |
|||
go oneTurnConnection(ctx, *host, *port, *link, *udp, *realm, peer, conn2, c) |
|||
if err := <-c; err != nil { |
|||
log.Printf("%s", err) |
|||
} |
|||
default: |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
for i := 0; i < *n-1; i++ { |
|||
wg1.Go(func() { |
|||
for { |
|||
select { |
|||
case <-ctx.Done(): |
|||
return |
|||
case <-okchan: |
|||
select { |
|||
case conn2 := <-connchan: |
|||
select { |
|||
case <-t: |
|||
c2 := make(chan error) |
|||
go oneTurnConnection(ctx, *host, *port, *link, *udp, *realm, peer, conn2, c2) |
|||
if err := <-c2; err != nil { |
|||
log.Printf("%s", err) |
|||
} |
|||
default: |
|||
} |
|||
default: |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
wg1.Wait() |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
module github.com/cacggghp/vk-turn-proxy |
|||
|
|||
go 1.25.5 |
|||
|
|||
require ( |
|||
github.com/cbeuw/connutil v1.0.1 |
|||
github.com/google/uuid v1.6.0 |
|||
github.com/pion/dtls/v3 v3.0.10 |
|||
github.com/pion/logging v0.2.4 |
|||
github.com/pion/turn/v4 v4.1.4 |
|||
) |
|||
|
|||
require ( |
|||
github.com/pion/randutil v0.1.0 // indirect |
|||
github.com/pion/stun/v3 v3.1.1 // indirect |
|||
github.com/pion/transport/v4 v4.0.1 // indirect |
|||
github.com/wlynxg/anet v0.0.5 // indirect |
|||
golang.org/x/crypto v0.46.0 // indirect |
|||
golang.org/x/sys v0.40.0 // indirect |
|||
) |
|||
@ -0,0 +1,34 @@ |
|||
github.com/cbeuw/connutil v1.0.1 h1:LWuNYjwm7JEDYG/ISAO1TfU4G+q2dA5NhR97eq2roCA= |
|||
github.com/cbeuw/connutil v1.0.1/go.mod h1:lKofNtrW7Atmosgp1eNnTt2j2NjA2IkifapgLVI1QtA= |
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
|||
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= |
|||
github.com/pion/dtls/v3 v3.0.10 h1:k9ekkq1kaZoxnNEbyLKI8DI37j/Nbk1HWmMuywpQJgg= |
|||
github.com/pion/dtls/v3 v3.0.10/go.mod h1:YEmmBYIoBsY3jmG56dsziTv/Lca9y4Om83370CXfqJ8= |
|||
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/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= |
|||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= |
|||
github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw= |
|||
github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM= |
|||
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o= |
|||
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM= |
|||
github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ= |
|||
github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ= |
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= |
|||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= |
|||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= |
|||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= |
|||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= |
|||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= |
|||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= |
|||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= |
|||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= |
|||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= |
|||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= |
|||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= |
|||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
|||
@ -0,0 +1,27 @@ |
|||
# Получаем default gateway (IPv4) |
|||
$gateway = Get-NetRoute ` |
|||
-DestinationPrefix "0.0.0.0/0" ` |
|||
| Sort-Object RouteMetric ` |
|||
| Select-Object -First 1 -ExpandProperty NextHop |
|||
|
|||
if (-not $gateway) { |
|||
Write-Error "Не удалось определить default gateway" |
|||
exit 1 |
|||
} |
|||
|
|||
Write-Host "Default gateway: $gateway" |
|||
|
|||
# Читаем адреса из stdin |
|||
$input | ForEach-Object { |
|||
$addr = $_.Trim() |
|||
if ($addr -eq "") { return } |
|||
|
|||
Write-Host "Добавляем маршрут к $addr через $gateway" |
|||
|
|||
New-NetRoute ` |
|||
-DestinationPrefix "$addr/32" ` |
|||
-NextHop $gateway ` |
|||
-PolicyStore ActiveStore ` |
|||
-ErrorAction Stop |
|||
} |
|||
|
|||
@ -0,0 +1,5 @@ |
|||
#!/bin/bash |
|||
gateway="$(ip -o -4 route show to default | awk '/via/ {print $3}' | head -1)" |
|||
while read -r remote; do |
|||
sudo ip r add $remote via $gateway |
|||
done |
|||
@ -0,0 +1,189 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"context" |
|||
"crypto/tls" |
|||
"flag" |
|||
"fmt" |
|||
"log" |
|||
"net" |
|||
"os" |
|||
"os/signal" |
|||
"sync" |
|||
"syscall" |
|||
"time" |
|||
|
|||
"github.com/pion/dtls/v3" |
|||
"github.com/pion/dtls/v3/pkg/crypto/selfsign" |
|||
) |
|||
|
|||
func main() { |
|||
listen := flag.String("listen", "0.0.0.0:56000", "listen on ip:port") |
|||
connect := flag.String("connect", "", "connect to ip:port") |
|||
flag.Parse() |
|||
|
|||
ctx, cancel := context.WithCancel(context.Background()) |
|||
defer cancel() |
|||
signalChan := make(chan os.Signal, 1) |
|||
signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT) |
|||
go func() { |
|||
<-signalChan |
|||
log.Printf("Terminating...\n") |
|||
cancel() |
|||
<-signalChan |
|||
log.Fatalf("Exit...\n") |
|||
}() |
|||
|
|||
addr, err := net.ResolveUDPAddr("udp", *listen) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
if len(*connect) == 0 { |
|||
log.Panicf("server address is required") |
|||
} |
|||
// Generate a certificate and private key to secure the connection
|
|||
certificate, genErr := selfsign.GenerateSelfSigned() |
|||
if genErr != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
//
|
|||
// Everything below is the pion-DTLS API! Thanks for using it ❤️.
|
|||
//
|
|||
|
|||
// Prepare the configuration of the DTLS connection
|
|||
config := &dtls.Config{ |
|||
Certificates: []tls.Certificate{certificate}, |
|||
ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, |
|||
CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, |
|||
ConnectionIDGenerator: dtls.RandomCIDGenerator(8), |
|||
} |
|||
|
|||
// Connect to a DTLS server
|
|||
listener, err := dtls.Listen("udp", addr, config) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
context.AfterFunc(ctx, func() { |
|||
if err = listener.Close(); err != nil { |
|||
panic(err) |
|||
} |
|||
}) |
|||
|
|||
fmt.Println("Listening") |
|||
|
|||
wg1 := sync.WaitGroup{} |
|||
for { |
|||
select { |
|||
case <-ctx.Done(): |
|||
wg1.Wait() |
|||
return |
|||
default: |
|||
} |
|||
// Wait for a connection.
|
|||
conn, err := listener.Accept() |
|||
if err != nil { |
|||
log.Println(err) |
|||
continue |
|||
} |
|||
wg1.Add(1) |
|||
go func(conn net.Conn) { |
|||
defer wg1.Done() |
|||
defer conn.Close() // graceful shutdown
|
|||
var err error = nil |
|||
log.Printf("Connection from %s\n", conn.RemoteAddr()) |
|||
// `conn` is of type `net.Conn` but may be casted to `dtls.Conn`
|
|||
// using `dtlsConn := conn.(*dtls.Conn)` in order to to expose
|
|||
// functions like `ConnectionState` etc.
|
|||
|
|||
// Perform the handshake with a 30-second timeout
|
|||
ctx1, cancel1 := context.WithTimeout(ctx, 30*time.Second) |
|||
dtlsConn, ok := conn.(*dtls.Conn) |
|||
if !ok { |
|||
log.Println("Type error") |
|||
cancel1() |
|||
return |
|||
} |
|||
log.Println("Start handshake") |
|||
if err = dtlsConn.HandshakeContext(ctx1); err != nil { |
|||
log.Println(err) |
|||
cancel1() |
|||
return |
|||
} |
|||
cancel1() |
|||
log.Println("Handshake done") |
|||
|
|||
serverConn, err := net.Dial("udp", *connect) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
defer func() { |
|||
if err = serverConn.Close(); err != nil { |
|||
log.Printf("failed to close outgoing connection: %s", err) |
|||
return |
|||
} |
|||
}() |
|||
|
|||
var wg sync.WaitGroup |
|||
wg.Add(2) |
|||
ctx2, cancel2 := context.WithCancel(ctx) |
|||
context.AfterFunc(ctx2, func() { |
|||
conn.SetDeadline(time.Now()) |
|||
serverConn.SetDeadline(time.Now()) |
|||
}) |
|||
go func() { |
|||
defer wg.Done() |
|||
defer cancel2() |
|||
buf := make([]byte, 1600) |
|||
for { |
|||
select { |
|||
case <-ctx2.Done(): |
|||
return |
|||
default: |
|||
} |
|||
conn.SetReadDeadline(time.Now().Add(time.Minute * 30)) |
|||
n, err1 := conn.Read(buf) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
|
|||
serverConn.SetWriteDeadline(time.Now().Add(time.Minute * 30)) |
|||
_, err1 = serverConn.Write(buf[:n]) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
go func() { |
|||
defer wg.Done() |
|||
defer cancel2() |
|||
buf := make([]byte, 1600) |
|||
for { |
|||
select { |
|||
case <-ctx2.Done(): |
|||
return |
|||
default: |
|||
} |
|||
serverConn.SetReadDeadline(time.Now().Add(time.Minute * 30)) |
|||
n, err1 := serverConn.Read(buf) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
|
|||
conn.SetWriteDeadline(time.Now().Add(time.Minute * 30)) |
|||
_, err1 = conn.Write(buf[:n]) |
|||
if err1 != nil { |
|||
log.Printf("Failed: %s", err1) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
wg.Wait() |
|||
log.Printf("Connection closed: %s\n", conn.RemoteAddr()) |
|||
}(conn) |
|||
} |
|||
} |
|||
Loading…
Reference in new issue