-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathgateway.go
120 lines (103 loc) · 3.14 KB
/
gateway.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
109
110
111
112
113
114
115
116
117
118
119
120
package main
import (
"bufio"
"fmt"
"net"
"strings"
"github.com/kofoworola/tunnelify/config"
"github.com/kofoworola/tunnelify/handler"
"github.com/kofoworola/tunnelify/logging"
)
const defaultBufferSize = 2048
type listenerGateway struct {
net.Listener
connChan chan net.Conn
listenerErr error
config *config.Config
logger *logging.Logger
}
func NewGateway(cfg *config.Config) (*listenerGateway, error) {
logger, err := logging.NewLogger(cfg)
if err != nil {
return nil, fmt.Errorf("error creating logger: %w", err)
}
listener, err := net.Listen("tcp", ":"+cfg.Port)
if err != nil {
return nil, fmt.Errorf("error creating listener for %s: %w", cfg.Port, err)
}
return &listenerGateway{
Listener: listener,
connChan: make(chan net.Conn, 1),
config: cfg,
logger: logger,
}, nil
}
func (l *listenerGateway) Accept() (net.Conn, error) {
if l.listenerErr != nil {
return nil, l.listenerErr
}
c := <-l.connChan
l.logger.Debug("connection forwarded to liveness server")
return c, nil
}
// Start listnes for new connections from the core listener,
// then determines how to handle it. Either passing it to the proxy handler,
// the tunnel handler, or sending it to the connection channel for it's own Accept() method.
func (l *listenerGateway) Start() error {
// listen to new connections
for {
c, err := l.Listener.Accept()
if err != nil {
l.listenerErr = err
l.logger.LogError("error accepting a new connection", err)
break
}
l.logger.Debug("received new connection")
var h handler.ConnectionHandler
// read first line of the connection and use an appropriate handler
r := bufio.NewReaderSize(c, defaultBufferSize)
reqLine, err := r.ReadBytes('\n')
if err != nil {
l.logger.LogError("error reading request line from connection", err)
continue
}
// use the length of the first line to determine the content of the buffer
// and fetch that to prepend to the connection
bufferContent := make([]byte, defaultBufferSize-len(reqLine))
n, err := r.Read(bufferContent)
if err != nil {
l.logger.LogError("error reading request from connection", err)
continue
}
// make sure the length of what was read is the same as the length of the bufferContent
// if not trim it
if n < len(bufferContent) {
bufferContent = bufferContent[:n]
}
// check the reqline for the handler to use
reqDetails := strings.Split(string(reqLine), " ")
if len(reqDetails) != 3 {
l.logger.LogError("invalid request start line", nil)
continue
}
logger := l.logger.With("action", reqDetails[0])
cr := NewConnectionReader(c, reqLine, bufferContent)
if reqDetails[0] == "CONNECT" {
h = handler.NewTunnelHandler(l.config, cr, reqDetails[1], strings.TrimSpace(reqDetails[2]), c.Close)
} else if reqDetails[0] != "CONNECT" && !strings.HasPrefix(reqDetails[1], "/") {
h = handler.NewProxyHandler(cr, c.RemoteAddr().String(), l.config, c.Close)
} else {
l.connChan <- cr
}
if h != nil {
// check if allowed
if !l.config.ShouldAllowIP(c.RemoteAddr().String()) {
handler.WriteResponse(c, "HTTP/1.1", "403 Forbidden", nil)
c.Close()
continue
}
go h.Handle(logger)
}
}
return nil
}