This repository has been archived by the owner on Oct 17, 2022. It is now read-only.
/
tcp_dialer.go
108 lines (103 loc) · 3.34 KB
/
tcp_dialer.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
// Copyright 2022 DomesticMoth
//
// This file is part of Ytl.
//
// Ytl is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Ytl is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package dialers
import (
"context"
"github.com/DomesticMoth/ytl/addr"
"golang.org/x/net/proxy"
"net"
"net/url"
"syscall"
"time"
)
// Implemets options for connecting to tcp/ip address
// with some extra features
type TcpDialer struct {
Timeout time.Duration `default:"2m"`
KeepAlive time.Duration `default:"15s"`
Control func(network, address string, c syscall.RawConn) error
}
// Dial connects to the address by url with optional using proxy (if not nil).
// It also drops ygg over ygg connections.
func (d *TcpDialer) Dial(uri url.URL, proxy *url.URL) (net.Conn, error) {
return d.DialContext(context.Background(), uri, proxy)
}
// Dial connects to the address by url with optional using proxy (if not nil).
// It also drops ygg over ygg connections.
// It also accepts a context that allows you to
// cancel the process of settling ahead of time.
func (d *TcpDialer) DialContext(ctx context.Context, uri url.URL, proxy_uri *url.URL) (net.Conn, error) {
// Clean code? Cyclomatic complexity?
// I dont know these buzzwords
use_proxy := false
if proxy_uri != nil {
use_proxy = proxy_uri.Scheme == "socks" || proxy_uri.Scheme == "socks5" || proxy_uri.Scheme == "socks5h"
}
if use_proxy {
dialerdst, err := net.ResolveTCPAddr("tcp", proxy_uri.Host)
if err != nil {
return nil, err
}
if err = addr.CheckAddr(dialerdst.IP); err != nil {
return nil, err
}
auth := &proxy.Auth{}
if proxy_uri.User != nil {
auth.User = proxy_uri.User.Username()
auth.Password, _ = proxy_uri.User.Password()
}
innerDialer, err := proxy.SOCKS5("tcp", dialerdst.String(), auth, proxy.Direct)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(ctx, d.Timeout)
conn, err := innerDialer.(proxy.ContextDialer).DialContext(ctx, "tcp", uri.Host)
cancel()
if err != nil {
return nil, err
}
laddr, _, _ := net.SplitHostPort(conn.LocalAddr().String())
raddr, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
if err = addr.CheckAddr(net.ParseIP(laddr)); err != nil {
conn.Close()
return nil, err
}
if err = addr.CheckAddr(net.ParseIP(raddr)); err != nil {
conn.Close()
return nil, err
}
return conn, err
} else {
dst, err := net.ResolveTCPAddr("tcp", uri.Host)
if err != nil {
return nil, err
}
if err = addr.CheckAddr(dst.IP); err != nil {
return nil, err
}
innerDialer := net.Dialer{
Timeout: d.Timeout,
KeepAlive: d.KeepAlive,
Control: d.Control,
}
ctx, cancel := context.WithTimeout(ctx, d.Timeout)
conn, err := innerDialer.DialContext(ctx, "tcp", dst.String())
cancel()
return conn, err
}
}