-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
puncher.go
162 lines (142 loc) · 3.64 KB
/
puncher.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
// Copyright 2021-22 Kirill Scherba <kirill@scherba.ru>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Teonet Get IPs and Puncher module
package teonet
import (
"net"
"strconv"
"strings"
"sync"
"time"
"github.com/teonet-go/tru"
)
// Allow IPv6 connection between peers
const IPv6Allow = true
// puncher struct and methods receiver
type puncher struct {
tru *tru.Tru
m map[string]*PuncherData
sync.RWMutex
}
// PuncherData is puncher data struct
type PuncherData struct {
wait *chan *net.UDPAddr
}
// IPs struct contain peers local and global IPs and ports
type IPs struct {
LocalIPs []string
LocalPort uint32
IP string
Port uint32
}
// getIPs return string slice with this host local IPs address
func (teo Teonet) getIPs() (ips []string, err error) {
ifaces, err := net.Interfaces()
if err != nil {
return
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
a := ip.String()
// Check ipv6 address, add [ ... ] if ipv6 allowed and
// skip this address if ipv6 not allowed
a, ok := teo.safeIPv6(a)
if !IPv6Allow && ok {
continue
}
ips = append(ips, a)
}
}
return
}
// safeIPv6 check ipv6 address and add [ ... ] to it, set ok to true if address
// is IPv6
func (teo Teonet) safeIPv6(ipin string) (ipout string, ok bool) {
if strings.IndexByte(ipin, ':') >= 0 {
ok = true
ipout = "[" + ipin + "]"
} else {
ipout = ipin
}
return
}
// newPuncher create new puncher and set punch callback to tru
func (teo *Teonet) newPuncher() {
if teo.tru == nil {
panic("trudp should be Init befor call to newPuncher()")
}
teo.puncher = &puncher{tru: teo.tru, m: make(map[string]*PuncherData)}
// Connect puncher to TRU - set punch callback
teo.tru.SetPunchCb(func(addr net.Addr, data []byte) {
log.Debugv.Printf("puncher get %s from %s\n", string(data[:6]), addr.String())
teo.puncher.callback(data, addr.(*net.UDPAddr))
})
}
// subscribe to puncher - add to map
func (p *puncher) subscribe(key string, punch *PuncherData) {
p.Lock()
defer p.Unlock()
p.m[key] = punch
}
// unsubscribe from puncher - delete from map, return ok = true and PuncherData
// if subscribe exists
func (p *puncher) unsubscribe(key string) (punch *PuncherData, ok bool) {
p.Lock()
defer p.Unlock()
punch, ok = p.m[key]
if ok {
delete(p.m, key)
}
return
}
// callback process received puch packet
func (p *puncher) callback(data []byte, addr *net.UDPAddr) (ok bool) {
var punch *PuncherData
if punch, ok = p.unsubscribe(string(data)); ok {
*punch.wait <- addr
}
return
}
// send puncher key to list of IP:Port
func (p *puncher) send(key string, ips IPs, stop ...func() bool) (err error) {
sendKey := func(ip string, port uint32) (err error) {
addr := ip + ":" + strconv.Itoa(int(port))
dst, err := p.tru.WriteToPunch([]byte(key), addr)
log.Debugv.Printf("puncher send %s to %s\n", key[:6], dst.String())
return
}
for i := range ips.LocalIPs {
if len(stop) > 0 && stop[0]() {
return
}
sendKey(ips.LocalIPs[i], ips.LocalPort)
}
sendKey(ips.IP, ips.Port)
return
}
// Punch client ip:ports (send udp packets to received IPs)
//
// delays parameter is start punch delay
func (p *puncher) punch(key string, ips IPs, stop func() bool, delays ...time.Duration) {
go func() {
if len(delays) > 0 {
time.Sleep(delays[0])
}
for i := 0; i < 5 && !stop(); i++ {
p.send(key, ips, stop)
time.Sleep(time.Duration(((i + 1) * 30)) * time.Millisecond)
}
}()
}