|
@@ -6,13 +6,20 @@
|
6
|
6
|
package irc
|
7
|
7
|
|
8
|
8
|
import (
|
|
9
|
+ "errors"
|
9
|
10
|
"fmt"
|
10
|
11
|
"net"
|
|
12
|
+ "strings"
|
11
|
13
|
|
12
|
14
|
"github.com/oragono/oragono/irc/modes"
|
13
|
15
|
"github.com/oragono/oragono/irc/utils"
|
14
|
16
|
)
|
15
|
17
|
|
|
18
|
+var (
|
|
19
|
+ errBadGatewayAddress = errors.New("PROXY/WEBIRC commands are not accepted from this IP address")
|
|
20
|
+ errBadProxyLine = errors.New("Invalid PROXY/WEBIRC command")
|
|
21
|
+)
|
|
22
|
+
|
16
|
23
|
type webircConfig struct {
|
17
|
24
|
PasswordString string `yaml:"password"`
|
18
|
25
|
Password []byte `yaml:"password-bytes"`
|
|
@@ -57,22 +64,29 @@ func isGatewayAllowed(addr net.Addr, gatewaySpec string) bool {
|
57
|
64
|
}
|
58
|
65
|
|
59
|
66
|
// ApplyProxiedIP applies the given IP to the client.
|
60
|
|
-func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (exiting bool) {
|
|
67
|
+func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (success bool) {
|
61
|
68
|
// ensure IP is sane
|
62
|
69
|
parsedProxiedIP := net.ParseIP(proxiedIP)
|
63
|
70
|
if parsedProxiedIP == nil {
|
64
|
71
|
client.Quit(fmt.Sprintf(client.t("Proxied IP address is not valid: [%s]"), proxiedIP))
|
65
|
|
- return true
|
|
72
|
+ return false
|
|
73
|
+ }
|
|
74
|
+
|
|
75
|
+ // undo any mapping of v4 addresses into the v6 space: https://stackoverflow.com/a/1618259
|
|
76
|
+ // this is how a typical stunnel4 deployment on Linux will handle dual-stack
|
|
77
|
+ unmappedIP := parsedProxiedIP.To4()
|
|
78
|
+ if unmappedIP != nil {
|
|
79
|
+ parsedProxiedIP = unmappedIP
|
66
|
80
|
}
|
67
|
81
|
|
68
|
82
|
isBanned, banMsg := client.server.checkBans(parsedProxiedIP)
|
69
|
83
|
if isBanned {
|
70
|
84
|
client.Quit(banMsg)
|
71
|
|
- return true
|
|
85
|
+ return false
|
72
|
86
|
}
|
73
|
87
|
|
74
|
88
|
// given IP is sane! override the client's current IP
|
75
|
|
- rawHostname := utils.LookupHostname(proxiedIP)
|
|
89
|
+ rawHostname := utils.LookupHostname(parsedProxiedIP.String())
|
76
|
90
|
client.stateMutex.Lock()
|
77
|
91
|
client.proxiedIP = parsedProxiedIP
|
78
|
92
|
client.rawHostname = rawHostname
|
|
@@ -83,5 +97,37 @@ func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (exiting bool)
|
83
|
97
|
client.certfp = ""
|
84
|
98
|
client.SetMode(modes.TLS, tls)
|
85
|
99
|
|
86
|
|
- return false
|
|
100
|
+ return true
|
|
101
|
+}
|
|
102
|
+
|
|
103
|
+// handle the PROXY command: http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
104
|
+// PROXY must be sent as the first message in the session and has the syntax:
|
|
105
|
+// PROXY TCP[46] SOURCEIP DESTIP SOURCEPORT DESTPORT\r\n
|
|
106
|
+// unfortunately, an ipv6 SOURCEIP can start with a double colon; in this case,
|
|
107
|
+// the message is invalid IRC and can't be parsed normally, hence the special handling.
|
|
108
|
+func handleProxyCommand(server *Server, client *Client, line string) (err error) {
|
|
109
|
+ defer func() {
|
|
110
|
+ if err != nil {
|
|
111
|
+ client.Quit(client.t("Bad or unauthorized PROXY command"))
|
|
112
|
+ }
|
|
113
|
+ }()
|
|
114
|
+
|
|
115
|
+ params := strings.Fields(line)
|
|
116
|
+ if len(params) != 6 {
|
|
117
|
+ return errBadProxyLine
|
|
118
|
+ }
|
|
119
|
+
|
|
120
|
+ for _, gateway := range server.ProxyAllowedFrom() {
|
|
121
|
+ if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
|
|
122
|
+ // assume PROXY connections are always secure
|
|
123
|
+ if client.ApplyProxiedIP(params[2], true) {
|
|
124
|
+ return nil
|
|
125
|
+ } else {
|
|
126
|
+ return errBadProxyLine
|
|
127
|
+ }
|
|
128
|
+ }
|
|
129
|
+ }
|
|
130
|
+
|
|
131
|
+ // real source IP is not authorized to issue PROXY:
|
|
132
|
+ return errBadGatewayAddress
|
87
|
133
|
}
|