-
Notifications
You must be signed in to change notification settings - Fork 31
/
runturn.go
168 lines (152 loc) · 5.86 KB
/
runturn.go
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
// WebCall Copyright 2023 timur.mobi. All rights reserved.
package main
import (
"fmt"
"net"
"strconv"
"strings"
"sync"
"time"
//"github.com/pion/turn/v2" // see: https://github.com/pion/turn/issues/206#issuecomment-907091251
"github.com/mehrvarz/turn/v2" // this _is_ pion/turn but with a minor patch for FF on Android
"github.com/pion/logging"
)
type TurnCallee struct {
CalleeID string
TimeStored time.Time
}
// recentTurnCalleeIps is accessed from timer.go
// recentTurnCalleeIps provides a mapping: TurnCallee <-- [callerIP]
var recentTurnCalleeIps map[string]TurnCallee
var recentTurnCalleeIpMutex sync.RWMutex
func runTurnServer() {
if turnPort <= 0 {
return
}
recentTurnCalleeIps = make(map[string]TurnCallee)
fmt.Printf("turn server listening on '%s' port=%d\n", turnIP, turnPort)
udpListener, err := net.ListenPacket("udp4", "0.0.0.0:"+strconv.Itoa(turnPort))
if err != nil {
fmt.Printf("# Failed to create TURN server listener: %s\n", err)
return
}
readConfigLock.RLock()
ourRealm := turnRealm
loggerFactory := logging.NewDefaultLoggerFactory()
loggerFactory.DefaultLogLevel = logging.LogLevel(turnDebugLevel) // 3=info 4=LogLevelDebug
readConfigLock.RUnlock()
_, err = turn.NewServer(turn.ServerConfig{
Realm: ourRealm,
AuthHandler: func(username string, realm string, srcAddr net.Addr) ([]byte, bool) {
// AuthHandler callback is called everytime a caller tries to authenticate with the TURN server
// - username is the "iceServers" username from Javascript
// - srcAddr is ip:port of caller (we receive 2 calls: same caller ip, but two different ports)
// note that for a relay connection to become available for both sides,
// only ONE side needs to successfully authenticate
// we will:
// - return authKey,true if we find a ConnectedCallerIp in the global hub == srcAddr (without port)
// - otherwise we return nil,false
//if logWantedFor("turn") {
// fmt.Printf("turnauth username=(%s) srcAddr=(%v)\n", username, srcAddr)
//}
timeNow := time.Now()
foundIp := false
foundByMap := false
foundCalleeId := ""
// search for ipAddr without the port number
// bc srcAddr is from the turn client and IpInHubMap is from the websocket client (different ports)
// TODO this could create issues; srcAddr should be same as IpInHubMap
// so we don't need to cut the port
// ipAddr is the caller ip without :port
ipAddr := srcAddr.String()
if portIdx := strings.Index(ipAddr, ":"); portIdx >= 0 {
ipAddr = ipAddr[:portIdx]
}
recentTurnCalleeIpMutex.RLock()
turnCallee, ok := recentTurnCalleeIps[ipAddr]
recentTurnCalleeIpMutex.RUnlock()
if ok {
timeSinceFirstFound := timeNow.Sub(turnCallee.TimeStored)
readConfigLock.RLock()
maxTalkSecsIfNoP2pTmp := maxTalkSecsIfNoP2p
readConfigLock.RUnlock()
if timeSinceFirstFound.Seconds() <= float64(maxTalkSecsIfNoP2pTmp) {
foundIp = true
foundCalleeId = turnCallee.CalleeID
foundByMap = true
//fmt.Printf("turnauth session foundIp foundByMap %v\n", foundCalleeId)
} else {
// turn session is outdated, will not anymore be authenticated
// check if callee is offline or not connected, in which case we will not log session outdated
_, locHub, _, err := GetOnlineCallee(turnCallee.CalleeID, true, true, true, "", "turn")
if err==nil && locHub==nil {
// turnCallee.CalleeID is offline: don't log
} else if err==nil && locHub.ConnectedCallerIp == "" {
// turnCallee.CalleeID is online but not connected: don't log
} else {
fmt.Printf("turnauth (%s) session outdated %s %v %d\n",
turnCallee.CalleeID, ipAddr, timeSinceFirstFound.Seconds(), maxTalkSecsIfNoP2pTmp)
}
}
} else {
// here I check if ipAddr is listed anywhere in hubMap as a callerIp
// in other words: the connection will be authenticated to use turn
// by the caller (!), not by the callee
// we do this bc only one of the two sides needs to authenticate
// SearchCallerIpInHubMap() returns the callee ip and ID for the given caller ipAddr
foundIp, foundCalleeId, err = SearchCallerIpInHubMap(ipAddr)
if err != nil {
fmt.Printf("# turnauth for %s err=%v\n", ipAddr, err)
return nil, false
}
if foundIp {
//fmt.Printf("turn service approved for (%s) %v\n", foundCalleeId, ipAddr)
if !foundByMap {
recentTurnCalleeIpMutex.Lock()
recentTurnCalleeIps[ipAddr] = TurnCallee{foundCalleeId, timeNow}
//if logWantedFor("turn") {
// fmt.Printf("turn auth added (%s) to recentTurnCalleeIps len=%d\n",
// ipAddr, len(recentTurnCalleeIps))
//}
recentTurnCalleeIpMutex.Unlock()
// NOTE: recentTurnCalleeIps[ipAddr] will be deleted
// in wsClient.go peerConHasEnded() on 'peer callee discon'
}
} else {
//fmt.Printf("turn service not approved for %v\n", ipAddr)
}
}
if foundIp {
if !foundByMap /*&& logWantedFor("turn") */ {
recentTurnCalleeIpMutex.RLock()
fmt.Printf("turnauth (%s) for caller %v %d\n",
foundCalleeId, ipAddr, len(recentTurnCalleeIps))
recentTurnCalleeIpMutex.RUnlock()
}
// NOTE: the same key strings are used in caller.js and callee.js
// it doesn't matter what they are, but they must be the same
authKey := turn.GenerateAuthKey("c807ec29df3c9ff", realm, "736518fb4232d44")
return authKey, true
}
if logWantedFor("turn") {
fmt.Printf("turnauth denied for %v\n", ipAddr)
}
return nil, false
},
// PacketConnConfigs is a list of UDP Listeners and the configuration around them
PacketConnConfigs: []turn.PacketConnConfig{
{
PacketConn: udpListener,
RelayAddressGenerator: &turn.RelayAddressGeneratorStatic{
RelayAddress: net.ParseIP(turnIP),
Address: "0.0.0.0",
},
},
},
LoggerFactory: loggerFactory,
})
if err != nil {
fmt.Printf("turn err %v ===========================\n", err)
return
}
}