11 "golang.org/x/net/proxy"
13 "github.com/anacrolix/missinggo"
16 type dialer interface {
17 dial(_ context.Context, addr string) (net.Conn, error)
20 type socket interface {
25 func getProxyDialer(proxyURL string) (proxy.Dialer, error) {
26 fixedURL, err := url.Parse(proxyURL)
31 return proxy.FromURL(fixedURL, proxy.Direct)
34 func listen(network, addr, proxyURL string) (socket, error) {
35 if isTcpNetwork(network) {
36 return listenTcp(network, addr, proxyURL)
37 } else if isUtpNetwork(network) {
38 return listenUtp(network, addr, proxyURL)
40 panic(fmt.Sprintf("unknown network %q", network))
44 func isTcpNetwork(s string) bool {
45 return strings.Contains(s, "tcp")
48 func isUtpNetwork(s string) bool {
49 return strings.Contains(s, "utp") || strings.Contains(s, "udp")
52 func listenTcp(network, address, proxyURL string) (s socket, err error) {
53 l, err := net.Listen(network, address)
58 // If we don't need the proxy - then we should return default net.Dialer,
59 // otherwise, let's try to parse the proxyURL and return proxy.Dialer
60 if len(proxyURL) != 0 {
61 if dialer, err := getProxyDialer(proxyURL); err == nil {
62 return tcpSocket{l, dialer}, nil
66 return tcpSocket{l, nil}, nil
69 type tcpSocket struct {
74 func (me tcpSocket) dial(ctx context.Context, addr string) (net.Conn, error) {
76 return me.d.Dial(me.Addr().Network(), addr)
79 return net.Dial(me.Addr().Network(), addr)
82 func setPort(addr string, port int) string {
83 host, _, err := net.SplitHostPort(addr)
87 return net.JoinHostPort(host, strconv.FormatInt(int64(port), 10))
90 func listenAll(networks []string, getHost func(string) string, port int, proxyURL string) ([]socket, error) {
91 if len(networks) == 0 {
94 var nahs []networkAndHost
95 for _, n := range networks {
96 nahs = append(nahs, networkAndHost{n, getHost(n)})
99 ss, retry, err := listenAllRetry(nahs, port, proxyURL)
106 type networkAndHost struct {
111 func listenAllRetry(nahs []networkAndHost, port int, proxyURL string) (ss []socket, retry bool, err error) {
112 ss = make([]socket, 1, len(nahs))
113 portStr := strconv.FormatInt(int64(port), 10)
114 ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), proxyURL)
116 return nil, false, fmt.Errorf("first listen: %s", err)
119 if err != nil || retry {
120 for _, s := range ss {
126 portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
127 for _, nah := range nahs[1:] {
128 s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), proxyURL)
131 missinggo.IsAddrInUse(err) && port == 0,
132 fmt.Errorf("subsequent listen: %s", err)
139 func listenUtp(network, addr, proxyURL string) (s socket, err error) {
140 us, err := NewUtpSocket(network, addr)
145 // If we don't need the proxy - then we should return default net.Dialer,
146 // otherwise, let's try to parse the proxyURL and return proxy.Dialer
147 if len(proxyURL) != 0 {
148 if dialer, err := getProxyDialer(proxyURL); err == nil {
149 return utpSocketSocket{us, network, dialer}, nil
153 return utpSocketSocket{us, network, nil}, nil
156 type utpSocketSocket struct {
162 func (me utpSocketSocket) dial(ctx context.Context, addr string) (net.Conn, error) {
164 return me.d.Dial(me.network, addr)
167 return me.utpSocket.DialContext(ctx, me.network, addr)