]> Sergey Matveev's repositories - btrtrc.git/blob - socket.go
Support different hosts for each network
[btrtrc.git] / socket.go
1 package torrent
2
3 import (
4         "context"
5         "fmt"
6         "net"
7         "strconv"
8         "strings"
9
10         "github.com/anacrolix/missinggo"
11 )
12
13 type dialer interface {
14         dial(_ context.Context, addr string) (net.Conn, error)
15 }
16
17 type socket interface {
18         net.Listener
19         dialer
20 }
21
22 func listen(network, addr string) (socket, error) {
23         if isTcpNetwork(network) {
24                 return listenTcp(network, addr)
25         } else if isUtpNetwork(network) {
26                 return listenUtp(network, addr)
27         } else {
28                 panic(fmt.Sprintf("unknown network %q", network))
29         }
30 }
31
32 func isTcpNetwork(s string) bool {
33         return strings.Contains(s, "tcp")
34 }
35
36 func isUtpNetwork(s string) bool {
37         return strings.Contains(s, "utp") || strings.Contains(s, "udp")
38 }
39
40 func listenTcp(network, address string) (s socket, err error) {
41         l, err := net.Listen(network, address)
42         if err != nil {
43                 return
44         }
45         return tcpSocket{l}, nil
46 }
47
48 type tcpSocket struct {
49         net.Listener
50 }
51
52 func (me tcpSocket) dial(ctx context.Context, addr string) (net.Conn, error) {
53         return net.Dial(me.Addr().Network(), addr)
54 }
55
56 func setPort(addr string, port int) string {
57         host, _, err := net.SplitHostPort(addr)
58         if err != nil {
59                 panic(err)
60         }
61         return net.JoinHostPort(host, strconv.FormatInt(int64(port), 10))
62 }
63
64 func listenAll(networks []string, getHost func(string) string, port int) ([]socket, error) {
65         if len(networks) == 0 {
66                 return nil, nil
67         }
68         var nahs []networkAndHost
69         for _, n := range networks {
70                 nahs = append(nahs, networkAndHost{n, getHost(n)})
71         }
72         for {
73                 ss, retry, err := listenAllRetry(nahs, port)
74                 if !retry {
75                         return ss, err
76                 }
77         }
78 }
79
80 type networkAndHost struct {
81         Network string
82         Host    string
83 }
84
85 func listenAllRetry(nahs []networkAndHost, port int) (ss []socket, retry bool, err error) {
86         ss = make([]socket, 1, len(nahs))
87         portStr := strconv.FormatInt(int64(port), 10)
88         ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr))
89         if err != nil {
90                 return nil, false, fmt.Errorf("first listen: %s", err)
91         }
92         defer func() {
93                 if err != nil || retry {
94                         for _, s := range ss {
95                                 s.Close()
96                         }
97                         ss = nil
98                 }
99         }()
100         portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
101         for _, nah := range nahs[1:] {
102                 s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr))
103                 if err != nil {
104                         return ss,
105                                 missinggo.IsAddrInUse(err) && port == 0,
106                                 fmt.Errorf("subsequent listen: %s", err)
107                 }
108                 ss = append(ss, s)
109         }
110         return
111 }
112
113 func listenUtp(network, addr string) (s socket, err error) {
114         us, err := NewUtpSocket(network, addr)
115         if err != nil {
116                 return
117         }
118         return utpSocketSocket{us, network}, nil
119 }
120
121 type utpSocketSocket struct {
122         utpSocket
123         network string
124 }
125
126 func (me utpSocketSocket) dial(ctx context.Context, addr string) (net.Conn, error) {
127         return me.utpSocket.DialContext(ctx, me.network, addr)
128 }