1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
// Package main implements a TURN client with support for TCP
package main
import (
"flag"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/pion/logging"
"github.com/pion/turn/v2"
)
func main() {
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\")")
ping := flag.Bool("ping", false, "Run ping test")
flag.Parse()
if len(*host) == 0 {
log.Fatalf("'host' is required")
}
if len(*user) == 0 {
log.Fatalf("'user' is required")
}
// Dial TURN Server
turnServerAddr := fmt.Sprintf("%s:%d", *host, *port)
conn, err := net.Dial("tcp", 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: turnServerAddr,
TURNServerAddr: turnServerAddr,
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 net.PacketConn which represents the remote
// socket.
relayConn, err := client.Allocate()
if err != nil {
log.Panicf("Failed to allocate: %s", err)
}
defer func() {
if closeErr := relayConn.Close(); closeErr != nil {
log.Fatalf("Failed to close connection: %s", closeErr)
}
}()
// The relayConn's local address is actually the transport
// address assigned on the TURN server.
log.Printf("relayed-address=%s", relayConn.LocalAddr().String())
// If you provided `-ping`, perform a ping test against the
// relayConn we have just allocated.
if *ping {
err = doPingTest(client, relayConn)
if err != nil {
log.Panicf("Failed to ping: %s", err)
}
}
}
func doPingTest(client *turn.Client, relayConn net.PacketConn) error {
// Send BindingRequest to learn our external IP
mappedAddr, err := client.SendBindingRequest()
if err != nil {
return err
}
// Set up pinger socket (pingerConn)
pingerConn, err := net.ListenPacket("udp4", "0.0.0.0:0")
if err != nil {
log.Panicf("Failed to listen: %s", err)
}
defer func() {
if closeErr := pingerConn.Close(); closeErr != nil {
log.Panicf("Failed to close connection: %s", closeErr)
}
}()
// Punch a UDP hole for the relayConn by sending a data to the mappedAddr.
// This will trigger a TURN client to generate a permission request to the
// TURN server. After this, packets from the IP address will be accepted by
// the TURN server.
_, err = relayConn.WriteTo([]byte("Hello"), mappedAddr)
if err != nil {
return err
}
// Start read-loop on pingerConn
go func() {
buf := make([]byte, 1600)
for {
n, from, pingerErr := pingerConn.ReadFrom(buf)
if pingerErr != nil {
break
}
msg := string(buf[:n])
if sentAt, pingerErr := time.Parse(time.RFC3339Nano, msg); pingerErr == nil {
rtt := time.Since(sentAt)
log.Printf("%d bytes from from %s time=%d ms\n", n, from.String(), int(rtt.Seconds()*1000))
}
}
}()
// Start read-loop on relayConn
go func() {
buf := make([]byte, 1600)
for {
n, from, readerErr := relayConn.ReadFrom(buf)
if readerErr != nil {
break
}
// Echo back
if _, readerErr = relayConn.WriteTo(buf[:n], from); readerErr != nil {
break
}
}
}()
time.Sleep(500 * time.Millisecond)
// Send 10 packets from relayConn to the echo server
for i := 0; i < 10; i++ {
msg := time.Now().Format(time.RFC3339Nano)
_, err = pingerConn.WriteTo([]byte(msg), relayConn.LocalAddr())
if err != nil {
return err
}
// For simplicity, this example does not wait for the pong (reply).
// Instead, sleep 1 second.
time.Sleep(time.Second)
}
return nil
}
|