Files

196 lines
5.1 KiB
Go

// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package main implements a TURN client with support for TCP
package main
import (
"bufio"
"flag"
"fmt"
"log"
"net"
"strings"
"github.com/pion/logging"
"github.com/hanzokms/turn/v4"
)
func setupSignalingChannel(addrCh chan string, signaling bool, relayAddr string) {
addr := "127.0.0.1:5000"
if signaling { // nolint:nestif
go func() {
listener, err := net.Listen("tcp", addr)
if err != nil {
log.Panicf("Failed to create signaling server: %s", err)
}
defer listener.Close() //nolint:errcheck,gosec
for {
conn, err := listener.Accept()
if err != nil {
log.Panicf("Failed to accept: %s", err)
}
go func() {
var message string
message, err = bufio.NewReader(conn).ReadString('\n')
if err != nil {
log.Panicf("Failed to read from relayAddr: %s", err)
}
addrCh <- message[:len(message)-1]
}()
if _, err = conn.Write([]byte(fmt.Sprintf("%s\n", relayAddr))); err != nil {
log.Panicf("Failed to write relayAddr: %s", err)
}
}
}()
} else {
conn, err := net.Dial("tcp", addr)
if err != nil {
log.Panicf("Error dialing: %s", err)
}
message, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
log.Panicf("Failed to read relayAddr: %s", err)
}
addrCh <- message[:len(message)-1]
if _, err = conn.Write([]byte(fmt.Sprintf("%s\n", relayAddr))); err != nil {
log.Panicf("Failed to write relayAddr: %s", err)
}
}
}
func main() { //nolint:cyclop
host := flag.String("host", "", "TURN Server name.")
port := flag.Int("port", 3478, "Listening port.")
user := flag.String("user", "", "A pair of username and password (e.g. \"user=pass\")")
realm := flag.String("realm", "pion.ly", "Realm (defaults to \"pion.ly\")")
signaling := flag.Bool("signaling", false, "Whether to start signaling server otherwise connect")
flag.Parse()
if len(*host) == 0 {
log.Panicf("'host' is required")
}
if len(*user) == 0 {
log.Panicf("'user' is required")
}
// Dial TURN Server
turnServerAddrStr := fmt.Sprintf("%s:%d", *host, *port)
turnServerAddr, err := net.ResolveTCPAddr("tcp", turnServerAddrStr)
if err != nil {
log.Panicf("Failed to resolve TURN server address: %s", err)
}
conn, err := net.DialTCP("tcp", nil, turnServerAddr)
if err != nil {
log.Panicf("Failed to connect to TURN server: %s", err)
}
cred := strings.SplitN(*user, "=", 2)
// 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: turnServerAddrStr,
TURNServerAddr: turnServerAddrStr,
Conn: turn.NewSTUNConn(conn),
Username: cred[0],
Password: cred[1],
Realm: *realm,
LoggerFactory: logging.NewDefaultLoggerFactory(),
}
client, err := turn.NewClient(cfg)
if err != nil {
log.Panicf("Failed to create TURN client: %s", err)
}
defer client.Close()
// Start listening on the conn provided.
err = client.Listen()
if err != nil {
log.Panicf("Failed to listen: %s", err)
}
// Allocate a relay socket on the TURN server. On success, it
// will return a client.TCPAllocation which represents the remote
// socket.
allocation, err := client.AllocateTCP()
if err != nil {
log.Panicf("Failed to allocate: %s", err)
}
defer func() {
if closeErr := allocation.Close(); closeErr != nil {
log.Panicf("Failed to close connection: %s", closeErr)
}
}()
log.Printf("relayed-address=%s", allocation.Addr())
// Learn the peers relay address via signaling channel
addrCh := make(chan string, 5)
setupSignalingChannel(addrCh, *signaling, allocation.Addr().String())
// Get peer address
peerAddrStr := <-addrCh
peerAddr, err := net.ResolveTCPAddr("tcp", peerAddrStr)
if err != nil {
log.Panicf("Failed to resolve peer address: %s", err)
}
log.Printf("Received peer address: %s", peerAddrStr)
buf := make([]byte, 4096)
var n int
if *signaling { // nolint:nestif
conn, err := allocation.DialTCP("tcp", nil, peerAddr)
if err != nil {
log.Panicf("Failed to dial: %s", err)
}
if _, err = conn.Write([]byte("hello!")); err != nil {
log.Panicf("Failed to write: %s", err)
}
n, err = conn.Read(buf)
if err != nil {
log.Panicf("Failed to read from relay connection: %s", err)
}
if err := conn.Close(); err != nil {
log.Panicf("Failed to close: %s", err)
}
} else {
if err := client.CreatePermission(peerAddr); err != nil {
log.Panicf("Failed to create permission: %s", err)
}
conn, err := allocation.AcceptTCP()
if err != nil {
log.Panicf("Failed to accept TCP connection: %s", err)
}
log.Printf("Accepted connection from: %s", conn.RemoteAddr())
n, err = conn.Read(buf)
if err != nil {
log.Panicf("Failed to read from relay conn: %s", err)
}
if _, err := conn.Write([]byte("hello back!")); err != nil {
log.Panicf("Failed to write: %s", err)
}
if err := conn.Close(); err != nil {
log.Panicf("Failed to close: %s", err)
}
}
log.Printf("Read message: %s", string(buf[:n]))
}