11 "github.com/anacrolix/missinggo"
12 "github.com/anacrolix/missinggo/perf"
13 "github.com/pkg/errors"
14 "golang.org/x/net/proxy"
17 type dialer interface {
18 dial(_ context.Context, addr string) (net.Conn, error)
21 type socket interface {
26 func getProxyDialer(proxyURL string) (proxy.Dialer, error) {
27 fixedURL, err := url.Parse(proxyURL)
32 return proxy.FromURL(fixedURL, proxy.Direct)
35 func listen(network, addr, proxyURL string, f firewallCallback) (socket, error) {
36 if isTcpNetwork(network) {
37 return listenTcp(network, addr, proxyURL)
38 } else if isUtpNetwork(network) {
39 return listenUtp(network, addr, proxyURL, f)
41 panic(fmt.Sprintf("unknown network %q", network))
45 func isTcpNetwork(s string) bool {
46 return strings.Contains(s, "tcp")
49 func isUtpNetwork(s string) bool {
50 return strings.Contains(s, "utp") || strings.Contains(s, "udp")
53 func listenTcp(network, address, proxyURL string) (s socket, err error) {
54 l, err := net.Listen(network, address)
64 // If we don't need the proxy - then we should return default net.Dialer,
65 // otherwise, let's try to parse the proxyURL and return proxy.Dialer
66 if len(proxyURL) != 0 {
67 // TODO: The error should be propagated, as proxy may be in use for
68 // security or privacy reasons. Also just pass proxy.Dialer in from
70 if dialer, err := getProxyDialer(proxyURL); err == nil {
71 return tcpSocket{l, func(ctx context.Context, addr string) (conn net.Conn, err error) {
72 defer perf.ScopeTimerErr(&err)()
73 return dialer.Dial(network, addr)
77 dialer := net.Dialer{}
78 return tcpSocket{l, func(ctx context.Context, addr string) (conn net.Conn, err error) {
79 defer perf.ScopeTimerErr(&err)()
80 return dialer.DialContext(ctx, network, addr)
84 type tcpSocket struct {
86 d func(ctx context.Context, addr string) (net.Conn, error)
89 func (me tcpSocket) dial(ctx context.Context, addr string) (net.Conn, error) {
90 return me.d(ctx, addr)
93 func setPort(addr string, port int) string {
94 host, _, err := net.SplitHostPort(addr)
98 return net.JoinHostPort(host, strconv.FormatInt(int64(port), 10))
101 func listenAll(networks []string, getHost func(string) string, port int, proxyURL string, f firewallCallback) ([]socket, error) {
102 if len(networks) == 0 {
105 var nahs []networkAndHost
106 for _, n := range networks {
107 nahs = append(nahs, networkAndHost{n, getHost(n)})
110 ss, retry, err := listenAllRetry(nahs, port, proxyURL, f)
117 type networkAndHost struct {
122 func listenAllRetry(nahs []networkAndHost, port int, proxyURL string, f firewallCallback) (ss []socket, retry bool, err error) {
123 ss = make([]socket, 1, len(nahs))
124 portStr := strconv.FormatInt(int64(port), 10)
125 ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), proxyURL, f)
127 return nil, false, errors.Wrap(err, "first listen")
130 if err != nil || retry {
131 for _, s := range ss {
137 portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
138 for _, nah := range nahs[1:] {
139 s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), proxyURL, f)
142 missinggo.IsAddrInUse(err) && port == 0,
143 errors.Wrap(err, "subsequent listen")
150 type firewallCallback func(net.Addr) bool
152 func listenUtp(network, addr, proxyURL string, fc firewallCallback) (s socket, err error) {
153 us, err := NewUtpSocket(network, addr, fc)
158 // If we don't need the proxy - then we should return default net.Dialer,
159 // otherwise, let's try to parse the proxyURL and return proxy.Dialer
160 if len(proxyURL) != 0 {
161 if dialer, err := getProxyDialer(proxyURL); err == nil {
162 return utpSocketSocket{us, network, dialer}, nil
166 return utpSocketSocket{us, network, nil}, nil
169 type utpSocketSocket struct {
175 func (me utpSocketSocket) dial(ctx context.Context, addr string) (conn net.Conn, err error) {
176 defer perf.ScopeTimerErr(&err)()
178 return me.d.Dial(me.network, addr)
181 return me.utpSocket.DialContext(ctx, me.network, addr)