8 "github.com/anacrolix/missinggo"
9 "github.com/anacrolix/missinggo/perf"
10 "github.com/pkg/errors"
13 type dialer interface {
14 dial(_ context.Context, addr string) (net.Conn, error)
18 type listener interface {
22 type socket interface {
27 func listen(n network, addr string, f firewallCallback) (socket, error) {
30 return listenTcp(n.String(), addr)
32 return listenUtp(n.String(), addr, f)
38 func listenTcp(network, address string) (s socket, err error) {
39 l, err := net.Listen(network, address)
46 type tcpSocket struct {
52 func (me tcpSocket) dial(ctx context.Context, addr string) (_ net.Conn, err error) {
53 defer perf.ScopeTimerErr(&err)()
54 return me.dialer.DialContext(ctx, me.network, addr)
57 func (me tcpSocket) LocalAddr() net.Addr {
58 return tcpSocketLocalAddr{me.network, me.Listener.Addr().String()}
61 type tcpSocketLocalAddr struct {
66 func (me tcpSocketLocalAddr) Network() string { return me.network }
68 func (me tcpSocketLocalAddr) String() string { return "" }
70 func listenAll(networks []network, getHost func(string) string, port int, f firewallCallback) ([]socket, error) {
71 if len(networks) == 0 {
74 var nahs []networkAndHost
75 for _, n := range networks {
76 nahs = append(nahs, networkAndHost{n, getHost(n.String())})
79 ss, retry, err := listenAllRetry(nahs, port, f)
86 type networkAndHost struct {
91 func listenAllRetry(nahs []networkAndHost, port int, f firewallCallback) (ss []socket, retry bool, err error) {
92 ss = make([]socket, 1, len(nahs))
93 portStr := strconv.FormatInt(int64(port), 10)
94 ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), f)
96 return nil, false, errors.Wrap(err, "first listen")
99 if err != nil || retry {
100 for _, s := range ss {
106 portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
107 for _, nah := range nahs[1:] {
108 s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), f)
111 missinggo.IsAddrInUse(err) && port == 0,
112 errors.Wrap(err, "subsequent listen")
119 type firewallCallback func(net.Addr) bool
121 func listenUtp(network, addr string, fc firewallCallback) (socket, error) {
122 us, err := NewUtpSocket(network, addr, fc)
123 return utpSocketSocket{us, network}, err
126 type utpSocketSocket struct {
131 func (me utpSocketSocket) dial(ctx context.Context, addr string) (conn net.Conn, err error) {
132 defer perf.ScopeTimerErr(&err)()
133 return me.utpSocket.DialContext(ctx, me.network, addr)