1package main 2 3import ( 4 "flag" 5 "log" 6 "net" 7 "os" 8 "os/signal" 9 "regexp" 10 "strconv" 11 "syscall" 12 13 "github.com/pion/stun" 14 "github.com/pion/turn/v2" 15) 16 17// attributeAdder wraps a PacketConn and appends the SOFTWARE attribute to STUN packets 18// This pattern could be used to capture/inspect/modify data as well 19type attributeAdder struct { 20 net.PacketConn 21} 22 23func (s *attributeAdder) WriteTo(p []byte, addr net.Addr) (n int, err error) { 24 if stun.IsMessage(p) { 25 m := &stun.Message{Raw: p} 26 if err = m.Decode(); err != nil { 27 return 28 } 29 30 if err = stun.NewSoftware("CustomTURNServer").AddTo(m); err != nil { 31 return 32 } 33 34 m.Encode() 35 p = m.Raw 36 } 37 38 return s.PacketConn.WriteTo(p, addr) 39} 40 41func main() { 42 publicIP := flag.String("public-ip", "", "IP Address that TURN can be contacted by.") 43 port := flag.Int("port", 3478, "Listening port.") 44 users := flag.String("users", "", "List of username and password (e.g. \"user=pass,user=pass\")") 45 realm := flag.String("realm", "pion.ly", "Realm (defaults to \"pion.ly\")") 46 flag.Parse() 47 48 if len(*publicIP) == 0 { 49 log.Fatalf("'public-ip' is required") 50 } else if len(*users) == 0 { 51 log.Fatalf("'users' is required") 52 } 53 54 // Create a UDP listener to pass into pion/turn 55 // pion/turn itself doesn't allocate any UDP sockets, but lets the user pass them in 56 // this allows us to add logging, storage or modify inbound/outbound traffic 57 udpListener, err := net.ListenPacket("udp4", "0.0.0.0:"+strconv.Itoa(*port)) 58 if err != nil { 59 log.Panicf("Failed to create TURN server listener: %s", err) 60 } 61 62 // Cache -users flag for easy lookup later 63 // If passwords are stored they should be saved to your DB hashed using turn.GenerateAuthKey 64 usersMap := map[string][]byte{} 65 for _, kv := range regexp.MustCompile(`(\w+)=(\w+)`).FindAllStringSubmatch(*users, -1) { 66 usersMap[kv[1]] = turn.GenerateAuthKey(kv[1], *realm, kv[2]) 67 } 68 69 s, err := turn.NewServer(turn.ServerConfig{ 70 Realm: *realm, 71 // Set AuthHandler callback 72 // This is called everytime a user tries to authenticate with the TURN server 73 // Return the key for that user, or false when no user is found 74 AuthHandler: func(username string, realm string, srcAddr net.Addr) ([]byte, bool) { 75 if key, ok := usersMap[username]; ok { 76 return key, true 77 } 78 return nil, false 79 }, 80 // PacketConnConfigs is a list of UDP Listeners and the configuration around them 81 PacketConnConfigs: []turn.PacketConnConfig{ 82 { 83 PacketConn: &attributeAdder{udpListener}, 84 RelayAddressGenerator: &turn.RelayAddressGeneratorStatic{ 85 RelayAddress: net.ParseIP(*publicIP), // Claim that we are listening on IP passed by user (This should be your Public IP) 86 Address: "0.0.0.0", // But actually be listening on every interface 87 }, 88 }, 89 }, 90 }) 91 if err != nil { 92 log.Panic(err) 93 } 94 95 // Block until user sends SIGINT or SIGTERM 96 sigs := make(chan os.Signal, 1) 97 signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 98 <-sigs 99 100 if err = s.Close(); err != nil { 101 log.Panic(err) 102 } 103} 104