mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
server: changed interface definition and implementation
This commit is contained in:
parent
2813e80a98
commit
d37c2e2a35
@ -33,7 +33,7 @@ ipset=/example4.com/myset
|
|||||||
#### Config iptables on your linux gateway
|
#### Config iptables on your linux gateway
|
||||||
```bash
|
```bash
|
||||||
iptables -t nat -I PREROUTING -p tcp -m set --match-set myset dst -j REDIRECT --to-ports 1081
|
iptables -t nat -I PREROUTING -p tcp -m set --match-set myset dst -j REDIRECT --to-ports 1081
|
||||||
iptables -t nat -I OUTPUT -p tcp -m set --match-set myset dst -j REDIRECT --to-ports 1081
|
#iptables -t nat -I OUTPUT -p tcp -m set --match-set myset dst -j REDIRECT --to-ports 1081
|
||||||
```
|
```
|
||||||
|
|
||||||
#### When client requests network, the whole process:
|
#### When client requests network, the whole process:
|
||||||
|
@ -73,7 +73,7 @@ cidr=172.16.102.0/24
|
|||||||
#### Configure iptables on your linux gateway
|
#### Configure iptables on your linux gateway
|
||||||
```bash
|
```bash
|
||||||
iptables -t nat -I PREROUTING -p tcp -m set --match-set glider dst -j REDIRECT --to-ports 1081
|
iptables -t nat -I PREROUTING -p tcp -m set --match-set glider dst -j REDIRECT --to-ports 1081
|
||||||
iptables -t nat -I OUTPUT -p tcp -m set --match-set glider dst -j REDIRECT --to-ports 1081
|
#iptables -t nat -I OUTPUT -p tcp -m set --match-set glider dst -j REDIRECT --to-ports 1081
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Client DNS settings
|
#### Client DNS settings
|
||||||
|
2
main.go
2
main.go
@ -81,7 +81,7 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go local.ListenAndServe(nil)
|
go local.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
@ -35,7 +35,7 @@ func init() {
|
|||||||
proxy.RegisterServer("http", NewHTTPServer)
|
proxy.RegisterServer("http", NewHTTPServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTP returns a http proxy.
|
// NewHTTP returns a http proxy
|
||||||
func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) {
|
func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -63,27 +63,26 @@ func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) {
|
|||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPDialer returns a http proxy dialer.
|
// NewHTTPDialer returns a http proxy dialer
|
||||||
func NewHTTPDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
func NewHTTPDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
return NewHTTP(s, dialer)
|
return NewHTTP(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPServer returns a http proxy server.
|
// NewHTTPServer returns a http proxy server
|
||||||
func NewHTTPServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewHTTPServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewHTTP(s, dialer)
|
return NewHTTP(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe .
|
||||||
func (s *HTTP) ListenAndServe(c net.Conn) {
|
func (s *HTTP) ListenAndServe() {
|
||||||
if c == nil {
|
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("failed to listen on %s: %v", s.addr, err)
|
log.F("[http] failed to listen on %s: %v", s.addr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer l.Close()
|
defer l.Close()
|
||||||
|
|
||||||
log.F("listening TCP on %s", s.addr)
|
log.F("[http] listening TCP on %s", s.addr)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
c, err := l.Accept()
|
c, err := l.Accept()
|
||||||
@ -92,9 +91,6 @@ func (s *HTTP) ListenAndServe(c net.Conn) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.Serve(c)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
go s.Serve(c)
|
go s.Serve(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,20 +111,19 @@ func (s *HTTP) Serve(c net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.pretendAsWebServer {
|
if s.pretendAsWebServer {
|
||||||
fmt.Fprintf(c, "%s 404 Not Found\r\nServer: nginx\r\n\r\n", proto)
|
fmt.Fprintf(c, "%s 404 Not Found\r\nServer: nginx\r\n\r\n404 Not Found\r\n", proto)
|
||||||
log.F("[http pretender] being accessed as web server from %s", c.RemoteAddr().String())
|
log.F("[http pretender] being accessed as web server from %s", c.RemoteAddr().String())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == "CONNECT" {
|
if method == "CONNECT" {
|
||||||
s.servHTTPS(method, requestURI, proto, c)
|
s.servHTTPS(method, requestURI, proto, c)
|
||||||
//c.Write([]byte("HTTP/1.1 405\nAllow: GET, POST, HEAD, OPTION, PATCH\nServer: vsps/1.2\nContent-Type: \n\n"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reqHeader, err := reqTP.ReadMIMEHeader()
|
reqHeader, err := reqTP.ReadMIMEHeader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("read header error:%s", err)
|
log.F("[http] read header error:%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cleanHeaders(reqHeader)
|
cleanHeaders(reqHeader)
|
||||||
@ -237,7 +232,7 @@ func (s *HTTP) Addr() string {
|
|||||||
// NextDialer returns the next dialer
|
// NextDialer returns the next dialer
|
||||||
func (s *HTTP) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
func (s *HTTP) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||||
|
|
||||||
// Dial connects to the address addr on the network net via the proxy.
|
// Dial connects to the address addr on the network net via the proxy
|
||||||
func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
||||||
rc, err := s.dialer.Dial(network, s.addr)
|
rc, err := s.dialer.Dial(network, s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -274,12 +269,12 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
|||||||
return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code)
|
return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the given address via the proxy.
|
// DialUDP connects to the given address via the proxy
|
||||||
func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||||
return nil, nil, errors.New("http client does not support udp")
|
return nil, nil, errors.New("http client does not support udp")
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts.
|
// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts
|
||||||
func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) {
|
func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) {
|
||||||
line, err := tp.ReadLine()
|
line, err := tp.ReadLine()
|
||||||
// log.F("first line: %s", line)
|
// log.F("first line: %s", line)
|
||||||
@ -327,7 +322,5 @@ func writeHeaders(buf *bytes.Buffer, header textproto.MIMEHeader) {
|
|||||||
buf.Write([]byte("\r\n"))
|
buf.Write([]byte("\r\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//header ended
|
|
||||||
buf.Write([]byte("\r\n"))
|
buf.Write([]byte("\r\n"))
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/nadoo/glider/proxy/socks5"
|
"github.com/nadoo/glider/proxy/socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase.
|
// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase
|
||||||
var httpMethods = [...][]byte{
|
var httpMethods = [...][]byte{
|
||||||
[]byte("GET"),
|
[]byte("GET"),
|
||||||
[]byte("POST"),
|
[]byte("POST"),
|
||||||
@ -31,15 +31,13 @@ type MixedProxy struct {
|
|||||||
|
|
||||||
http *http.HTTP
|
http *http.HTTP
|
||||||
socks5 *socks5.SOCKS5
|
socks5 *socks5.SOCKS5
|
||||||
|
|
||||||
pretendAsWebServer bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proxy.RegisterServer("mixed", NewMixedProxyServer)
|
proxy.RegisterServer("mixed", NewMixedProxyServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMixedProxy returns a mixed proxy.
|
// NewMixedProxy returns a mixed proxy
|
||||||
func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) {
|
func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -47,16 +45,9 @@ func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pretend := u.Query().Get("pretend")
|
|
||||||
|
|
||||||
p := &MixedProxy{
|
p := &MixedProxy{
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
addr: u.Host,
|
addr: u.Host,
|
||||||
pretendAsWebServer: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
if pretend == "true" {
|
|
||||||
p.pretendAsWebServer = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.http, _ = http.NewHTTP(s, dialer)
|
p.http, _ = http.NewHTTP(s, dialer)
|
||||||
@ -65,15 +56,13 @@ func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMixedProxyServer returns a mixed proxy server.
|
// NewMixedProxyServer returns a mixed proxy server
|
||||||
func NewMixedProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewMixedProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewMixedProxy(s, dialer)
|
return NewMixedProxy(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe .
|
||||||
func (p *MixedProxy) ListenAndServe(c net.Conn) {
|
func (p *MixedProxy) ListenAndServe() {
|
||||||
|
|
||||||
if c == nil {
|
|
||||||
go p.socks5.ListenAndServeUDP()
|
go p.socks5.ListenAndServeUDP()
|
||||||
|
|
||||||
l, err := net.Listen("tcp", p.addr)
|
l, err := net.Listen("tcp", p.addr)
|
||||||
@ -82,6 +71,8 @@ func (p *MixedProxy) ListenAndServe(c net.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.F("[mixed] listening TCP on %s", p.addr)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
c, err := l.Accept()
|
c, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,9 +80,6 @@ func (p *MixedProxy) ListenAndServe(c net.Conn) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go p.Serve(c)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
go p.Serve(c)
|
go p.Serve(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +103,7 @@ func (p *MixedProxy) Serve(c net.Conn) {
|
|||||||
|
|
||||||
// check socks5, client send socksversion: 5 as the first byte
|
// check socks5, client send socksversion: 5 as the first byte
|
||||||
if head[0] == socks5.Version {
|
if head[0] == socks5.Version {
|
||||||
p.socks5.ServeTCP(cc)
|
p.socks5.Serve(cc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ func NewRedir6Server(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe .
|
||||||
func (s *RedirProxy) ListenAndServe(_ net.Conn) {
|
func (s *RedirProxy) ListenAndServe() {
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[redir] failed to listen on %s: %v", s.addr, err)
|
log.F("[redir] failed to listen on %s: %v", s.addr, err)
|
||||||
@ -114,6 +114,11 @@ func (s *RedirProxy) ListenAndServe(_ net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve .
|
||||||
|
func (s *RedirProxy) Serve(c net.Conn) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Get the original destination of a TCP connection.
|
// Get the original destination of a TCP connection.
|
||||||
func getOrigDst(conn net.Conn, ipv6 bool) (socks.Addr, error) {
|
func getOrigDst(conn net.Conn, ipv6 bool) (socks.Addr, error) {
|
||||||
c, ok := conn.(*net.TCPConn)
|
c, ok := conn.(*net.TCPConn)
|
||||||
|
@ -11,11 +11,14 @@ import (
|
|||||||
|
|
||||||
// Server interface
|
// Server interface
|
||||||
type Server interface {
|
type Server interface {
|
||||||
// ListenAndServe as proxy server, use only in server mode.
|
// ListenAndServe sets up a listener and serve on it
|
||||||
ListenAndServe(net.Conn)
|
ListenAndServe()
|
||||||
|
|
||||||
|
// Serve serves a connection
|
||||||
|
Serve(c net.Conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerCreator is a function to create proxy servers.
|
// ServerCreator is a function to create proxy servers
|
||||||
type ServerCreator func(s string, dialer Dialer) (Server, error)
|
type ServerCreator func(s string, dialer Dialer) (Server, error)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -27,8 +30,8 @@ func RegisterServer(name string, c ServerCreator) {
|
|||||||
serverMap[name] = c
|
serverMap[name] = c
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerFromURL calls the registered creator to create proxy servers.
|
// ServerFromURL calls the registered creator to create proxy servers
|
||||||
// dialer is the default upstream dialer so cannot be nil, we can use Default when calling this function.
|
// dialer is the default upstream dialer so cannot be nil, we can use Default when calling this function
|
||||||
func ServerFromURL(s string, dialer Dialer) (Server, error) {
|
func ServerFromURL(s string, dialer Dialer) (Server, error) {
|
||||||
if dialer == nil {
|
if dialer == nil {
|
||||||
return nil, errors.New("ServerFromURL: dialer cannot be nil")
|
return nil, errors.New("ServerFromURL: dialer cannot be nil")
|
||||||
|
@ -43,7 +43,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSOCKS5 returns a Proxy that makes SOCKS v5 connections to the given address
|
// NewSOCKS5 returns a Proxy that makes SOCKS v5 connections to the given address
|
||||||
// with an optional username and password. See RFC 1928.
|
// with an optional username and password. See RFC 1928
|
||||||
func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -65,25 +65,24 @@ func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
|||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSocks5Dialer returns a socks5 proxy dialer.
|
// NewSocks5Dialer returns a socks5 proxy dialer
|
||||||
func NewSocks5Dialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
func NewSocks5Dialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
return NewSOCKS5(s, dialer)
|
return NewSOCKS5(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSocks5Server returns a socks5 proxy server.
|
// NewSocks5Server returns a socks5 proxy server
|
||||||
func NewSocks5Server(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewSocks5Server(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewSOCKS5(s, dialer)
|
return NewSOCKS5(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe serves socks5 requests.
|
// ListenAndServe serves socks5 requests
|
||||||
func (s *SOCKS5) ListenAndServe(c net.Conn) {
|
func (s *SOCKS5) ListenAndServe() {
|
||||||
go s.ListenAndServeUDP()
|
go s.ListenAndServeUDP()
|
||||||
s.ListenAndServeTCP(c)
|
s.ListenAndServeTCP()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeTCP .
|
// ListenAndServeTCP .
|
||||||
func (s *SOCKS5) ListenAndServeTCP(c net.Conn) {
|
func (s *SOCKS5) ListenAndServeTCP() {
|
||||||
if c == nil {
|
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[socks5] failed to listen on %s: %v", s.addr, err)
|
log.F("[socks5] failed to listen on %s: %v", s.addr, err)
|
||||||
@ -99,15 +98,12 @@ func (s *SOCKS5) ListenAndServeTCP(c net.Conn) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.ServeTCP(c)
|
go s.Serve(c)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
go s.ServeTCP(c)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeTCP .
|
// Serve .
|
||||||
func (s *SOCKS5) ServeTCP(c net.Conn) {
|
func (s *SOCKS5) Serve(c net.Conn) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if c, ok := c.(*net.TCPConn); ok {
|
if c, ok := c.(*net.TCPConn); ok {
|
||||||
@ -152,7 +148,7 @@ func (s *SOCKS5) ServeTCP(c net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeUDP serves udp requests.
|
// ListenAndServeUDP serves udp requests
|
||||||
func (s *SOCKS5) ListenAndServeUDP() {
|
func (s *SOCKS5) ListenAndServeUDP() {
|
||||||
lc, err := net.ListenPacket("udp", s.addr)
|
lc, err := net.ListenPacket("udp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -248,7 +244,7 @@ func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the given address via the proxy.
|
// DialUDP connects to the given address via the proxy
|
||||||
func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||||
c, err := s.dialer.Dial("tcp", s.addr)
|
c, err := s.dialer.Dial("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -297,7 +293,7 @@ func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A
|
|||||||
|
|
||||||
// connect takes an existing connection to a socks5 proxy server,
|
// connect takes an existing connection to a socks5 proxy server,
|
||||||
// and commands the server to extend that connection to target,
|
// and commands the server to extend that connection to target,
|
||||||
// which must be a canonical address with a host and port.
|
// which must be a canonical address with a host and port
|
||||||
func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
||||||
host, portStr, err := net.SplitHostPort(target)
|
host, portStr, err := net.SplitHostPort(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -428,9 +424,9 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handshake fast-tracks SOCKS initialization to get target address to connect.
|
// Handshake fast-tracks SOCKS initialization to get target address to connect
|
||||||
func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
||||||
// Read RFC 1928 for request and reply structure and sizes.
|
// Read RFC 1928 for request and reply structure and sizes
|
||||||
buf := make([]byte, socks.MaxAddrLen)
|
buf := make([]byte, socks.MaxAddrLen)
|
||||||
// read VER, NMETHODS, METHODS
|
// read VER, NMETHODS, METHODS
|
||||||
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
||||||
|
@ -29,7 +29,7 @@ func init() {
|
|||||||
proxy.RegisterServer("ss", NewSSServer)
|
proxy.RegisterServer("ss", NewSSServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSS returns a shadowsocks proxy.
|
// NewSS returns a shadowsocks proxy
|
||||||
func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -55,25 +55,24 @@ func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSSDialer returns a ss proxy dialer.
|
// NewSSDialer returns a ss proxy dialer
|
||||||
func NewSSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
func NewSSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
return NewSS(s, dialer)
|
return NewSS(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSSServer returns a ss proxy server.
|
// NewSSServer returns a ss proxy server
|
||||||
func NewSSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewSSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewSS(s, dialer)
|
return NewSS(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe serves ss requests.
|
// ListenAndServe serves ss requests
|
||||||
func (s *SS) ListenAndServe(c net.Conn) {
|
func (s *SS) ListenAndServe() {
|
||||||
go s.ListenAndServeUDP()
|
go s.ListenAndServeUDP()
|
||||||
s.ListenAndServeTCP(c)
|
s.ListenAndServeTCP()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeTCP serves tcp ss requests.
|
// ListenAndServeTCP serves tcp ss requests
|
||||||
func (s *SS) ListenAndServeTCP(c net.Conn) {
|
func (s *SS) ListenAndServeTCP() {
|
||||||
if c == nil {
|
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[ss] failed to listen on %s: %v", s.addr, err)
|
log.F("[ss] failed to listen on %s: %v", s.addr, err)
|
||||||
@ -88,15 +87,13 @@ func (s *SS) ListenAndServeTCP(c net.Conn) {
|
|||||||
log.F("[ss] failed to accept: %v", err)
|
log.F("[ss] failed to accept: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go s.ServeTCP(c)
|
go s.Serve(c)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
go s.ServeTCP(c)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeTCP serves tcp ss requests.
|
}
|
||||||
func (s *SS) ServeTCP(c net.Conn) {
|
|
||||||
|
// Serve serves tcp ss requests
|
||||||
|
func (s *SS) Serve(c net.Conn) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if c, ok := c.(*net.TCPConn); ok {
|
if c, ok := c.(*net.TCPConn); ok {
|
||||||
@ -169,7 +166,7 @@ func (s *SS) ServeTCP(c net.Conn) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeUDP serves udp ss requests.
|
// ListenAndServeUDP serves udp ss requests
|
||||||
func (s *SS) ListenAndServeUDP() {
|
func (s *SS) ListenAndServeUDP() {
|
||||||
lc, err := net.ListenPacket("udp", s.addr)
|
lc, err := net.ListenPacket("udp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -271,7 +268,7 @@ func (s *SS) Dial(network, addr string) (net.Conn, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the given address via the proxy.
|
// DialUDP connects to the given address via the proxy
|
||||||
func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||||
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -22,7 +22,7 @@ func init() {
|
|||||||
proxy.RegisterServer("tcptun", NewTCPTunServer)
|
proxy.RegisterServer("tcptun", NewTCPTunServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTCPTun returns a tcptun proxy.
|
// NewTCPTun returns a tcptun proxy
|
||||||
func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) {
|
func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -42,13 +42,13 @@ func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTCPTunServer returns a udp tunnel server.
|
// NewTCPTunServer returns a udp tunnel server
|
||||||
func NewTCPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewTCPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewTCPTun(s, dialer)
|
return NewTCPTun(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe .
|
||||||
func (s *TCPTun) ListenAndServe(_ net.Conn) {
|
func (s *TCPTun) ListenAndServe() {
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("failed to listen on %s: %v", s.addr, err)
|
log.F("failed to listen on %s: %v", s.addr, err)
|
||||||
@ -64,7 +64,12 @@ func (s *TCPTun) ListenAndServe(_ net.Conn) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go s.Serve(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve .
|
||||||
|
func (s *TCPTun) Serve(c net.Conn) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if c, ok := c.(*net.TCPConn); ok {
|
if c, ok := c.(*net.TCPConn); ok {
|
||||||
@ -88,7 +93,4 @@ func (s *TCPTun) ListenAndServe(_ net.Conn) {
|
|||||||
}
|
}
|
||||||
log.F("relay error: %v", err)
|
log.F("relay error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TLS .
|
// TLS struct
|
||||||
type TLS struct {
|
type TLS struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
addr string
|
addr string
|
||||||
@ -24,15 +24,14 @@ type TLS struct {
|
|||||||
keyFile string
|
keyFile string
|
||||||
|
|
||||||
server proxy.Server
|
server proxy.Server
|
||||||
serverProto string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proxy.RegisterDialer("tls", NewTLSDialer)
|
proxy.RegisterDialer("tls", NewTLSDialer)
|
||||||
proxy.RegisterServer("tls", NewTLSTransport)
|
proxy.RegisterServer("tls", NewTLSServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTLS returns a tls proxy.
|
// NewTLS returns a tls proxy
|
||||||
func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -41,23 +40,24 @@ func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addr := u.Host
|
addr := u.Host
|
||||||
|
|
||||||
query := u.Query()
|
|
||||||
skipVerify := query.Get("skipVerify")
|
|
||||||
|
|
||||||
colonPos := strings.LastIndex(addr, ":")
|
colonPos := strings.LastIndex(addr, ":")
|
||||||
if colonPos == -1 {
|
if colonPos == -1 {
|
||||||
colonPos = len(addr)
|
colonPos = len(addr)
|
||||||
}
|
}
|
||||||
serverName := addr[:colonPos]
|
serverName := addr[:colonPos]
|
||||||
|
|
||||||
|
query := u.Query()
|
||||||
|
skipVerify := query.Get("skipVerify")
|
||||||
|
certFile := query.Get("cert")
|
||||||
|
keyFile := query.Get("key")
|
||||||
|
|
||||||
p := &TLS{
|
p := &TLS{
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
serverName: serverName,
|
serverName: serverName,
|
||||||
skipVerify: false,
|
skipVerify: false,
|
||||||
certFile: "",
|
certFile: certFile,
|
||||||
keyFile: "",
|
keyFile: keyFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipVerify == "true" {
|
if skipVerify == "true" {
|
||||||
@ -67,102 +67,71 @@ func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTLSServerTransport returns a tls transport layer before the real server
|
// NewTLSDialer returns a tls proxy dialer.
|
||||||
func NewTLSTransport(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewTLSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
|
return NewTLS(s, dialer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSServer returns a tls transport layer before the real server
|
||||||
|
func NewTLSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
transport := strings.Split(s, ",")
|
transport := strings.Split(s, ",")
|
||||||
|
|
||||||
// prepare transport listener
|
// prepare transport listener
|
||||||
if len(transport) != 2 {
|
// TODO: check here
|
||||||
err := fmt.Errorf("malformd listener: %s", s)
|
if len(transport) < 2 {
|
||||||
|
err := fmt.Errorf("[tls] malformd listener: %s", s)
|
||||||
log.F(err.Error())
|
log.F(err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.Parse(transport[0])
|
p, err := NewTLS(transport[0], dialer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("parse url err: %s", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cert=&key=
|
|
||||||
query := u.Query()
|
|
||||||
|
|
||||||
certFile := query.Get("cert")
|
|
||||||
keyFile := query.Get("key")
|
|
||||||
|
|
||||||
addr := u.Host
|
|
||||||
colonPos := strings.LastIndex(addr, ":")
|
|
||||||
if colonPos == -1 {
|
|
||||||
colonPos = len(addr)
|
|
||||||
}
|
|
||||||
serverName := addr[:colonPos]
|
|
||||||
|
|
||||||
p := &TLS{
|
|
||||||
dialer: dialer,
|
|
||||||
addr: addr,
|
|
||||||
serverName: serverName,
|
|
||||||
skipVerify: false,
|
|
||||||
certFile: certFile,
|
|
||||||
keyFile: keyFile,
|
|
||||||
serverProto: transport[1],
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare layer 7 server
|
|
||||||
p.server, err = proxy.ServerFromURL(transport[1], dialer)
|
p.server, err = proxy.ServerFromURL(transport[1], dialer)
|
||||||
|
|
||||||
return p, nil
|
return p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TLS) ListenAndServe(c net.Conn) {
|
// ListenAndServe .
|
||||||
// c for TCP_FAST_OPEN
|
func (s *TLS) ListenAndServe() {
|
||||||
|
|
||||||
var tlsConfig *stdtls.Config
|
|
||||||
|
|
||||||
var ticketKey [32]byte
|
|
||||||
copy(ticketKey[:], "f8710951c1f6d0d95a95eed5e99b51f1")
|
|
||||||
|
|
||||||
if s.certFile != "" && s.keyFile != "" {
|
|
||||||
cert, err := stdtls.LoadX509KeyPair(s.certFile, s.keyFile)
|
cert, err := stdtls.LoadX509KeyPair(s.certFile, s.keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("unabled load cert: %s, key %s", s.certFile, s.keyFile)
|
log.F("[tls] unabled load cert: %s, key %s", s.certFile, s.keyFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig = &stdtls.Config{
|
tlsConfig := &stdtls.Config{
|
||||||
Certificates: []stdtls.Certificate{cert},
|
Certificates: []stdtls.Certificate{cert},
|
||||||
MinVersion: stdtls.VersionTLS10,
|
MinVersion: stdtls.VersionTLS10,
|
||||||
MaxVersion: stdtls.VersionTLS12,
|
MaxVersion: stdtls.VersionTLS12,
|
||||||
SessionTicketKey: ticketKey,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tlsConfig = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := stdtls.Listen("tcp", s.addr, tlsConfig)
|
l, err := stdtls.Listen("tcp", s.addr, tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("failed to listen on tls %s: %v", s.addr, err)
|
log.F("[tls] failed to listen on tls %s: %v", s.addr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer l.Close()
|
defer l.Close()
|
||||||
|
|
||||||
log.F("listening TCP on %s with TLS", s.addr)
|
log.F("[tls] listening TCP on %s with TLS", s.addr)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
c, err := l.Accept()
|
c, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[https] failed to accept: %v", err)
|
log.F("[tls] failed to accept: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's callee's response to decide process request in sync/async mode.
|
go s.Serve(c)
|
||||||
s.server.ListenAndServe(c)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTLSDialer returns a tls proxy dialer.
|
// Serve .
|
||||||
func NewTLSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
func (s *TLS) Serve(c net.Conn) {
|
||||||
return NewTLS(s, dialer)
|
// TODO: check here
|
||||||
|
s.server.Serve(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Addr returns forwarder's address
|
// Addr returns forwarder's address
|
||||||
|
@ -51,7 +51,7 @@ func NewTProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe .
|
||||||
func (s *TProxy) ListenAndServe(_ net.Conn) {
|
func (s *TProxy) ListenAndServe() {
|
||||||
// go s.ListenAndServeTCP()
|
// go s.ListenAndServeTCP()
|
||||||
s.ListenAndServeUDP()
|
s.ListenAndServeUDP()
|
||||||
}
|
}
|
||||||
@ -114,6 +114,9 @@ func (s *TProxy) ListenAndServeUDP() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve .
|
||||||
|
func (s *TProxy) Serve(c net.Conn) {}
|
||||||
|
|
||||||
// ReadFromUDP reads a UDP packet from c, copying the payload into b.
|
// ReadFromUDP reads a UDP packet from c, copying the payload into b.
|
||||||
// It returns the number of bytes copied into b and the return address
|
// It returns the number of bytes copied into b and the return address
|
||||||
// that was on the packet.
|
// that was on the packet.
|
||||||
|
@ -24,7 +24,7 @@ func init() {
|
|||||||
proxy.RegisterServer("udptun", NewUDPTunServer)
|
proxy.RegisterServer("udptun", NewUDPTunServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUDPTun returns a UDPTun proxy.
|
// NewUDPTun returns a UDPTun proxy
|
||||||
func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) {
|
func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -44,13 +44,13 @@ func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUDPTunServer returns a udp tunnel server.
|
// NewUDPTunServer returns a udp tunnel server
|
||||||
func NewUDPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewUDPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewUDPTun(s, dialer)
|
return NewUDPTun(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe .
|
||||||
func (s *UDPTun) ListenAndServe(_ net.Conn) {
|
func (s *UDPTun) ListenAndServe() {
|
||||||
c, err := net.ListenPacket("udp", s.addr)
|
c, err := net.ListenPacket("udp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[udptun] failed to listen on %s: %v", s.addr, err)
|
log.F("[udptun] failed to listen on %s: %v", s.addr, err)
|
||||||
@ -104,3 +104,8 @@ func (s *UDPTun) ListenAndServe(_ net.Conn) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve .
|
||||||
|
func (s *UDPTun) Serve(c net.Conn) {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -24,7 +24,7 @@ func init() {
|
|||||||
proxy.RegisterServer("uottun", NewUoTTunServer)
|
proxy.RegisterServer("uottun", NewUoTTunServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUoTTun returns a UoTTun proxy.
|
// NewUoTTun returns a UoTTun proxy
|
||||||
func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -44,13 +44,13 @@ func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUoTTunServer returns a uot tunnel server.
|
// NewUoTTunServer returns a uot tunnel server
|
||||||
func NewUoTTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewUoTTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewUoTTun(s, dialer)
|
return NewUoTTun(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe .
|
||||||
func (s *UoTTun) ListenAndServe(_ net.Conn) {
|
func (s *UoTTun) ListenAndServe() {
|
||||||
c, err := net.ListenPacket("udp", s.addr)
|
c, err := net.ListenPacket("udp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[uottun] failed to listen on %s: %v", s.addr, err)
|
log.F("[uottun] failed to listen on %s: %v", s.addr, err)
|
||||||
@ -102,3 +102,9 @@ func (s *UoTTun) ListenAndServe(_ net.Conn) {
|
|||||||
log.F("[uottun] %s <-> %s", clientAddr, s.raddr)
|
log.F("[uottun] %s <-> %s", clientAddr, s.raddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve .
|
||||||
|
func (s *UoTTun) Serve(c net.Conn) {
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user