Procházet zdrojové kódy

fix PROXY protocol support for IPv6

1. Handle PROXY lines with IPv6 addresses starting with ::
(similar to WEBIRC in issue #211)

2. Strip v6 mapping from v4 addresses when handling proxied IPs.
tags/v0.12.0
Shivaram Lingamneni před 5 roky
rodič
revize
10d4f77638
5 změnil soubory, kde provedl 68 přidání a 39 odebrání
  1. 16
    1
      irc/client.go
  2. 0
    5
      irc/commands.go
  3. 51
    5
      irc/gateways.go
  4. 1
    21
      irc/handlers.go
  5. 0
    7
      irc/help.go

+ 16
- 1
irc/client.go Zobrazit soubor

@@ -246,6 +246,8 @@ func (client *Client) run() {
246 246
 	// (may be overridden by a later PROXY command from stunnel)
247 247
 	client.rawHostname = utils.AddrLookupHostname(client.socket.conn.RemoteAddr())
248 248
 
249
+	firstLine := true
250
+
249 251
 	for {
250 252
 		maxlenTags, maxlenRest := client.recomputeMaxlens()
251 253
 
@@ -259,7 +261,20 @@ func (client *Client) run() {
259 261
 			break
260 262
 		}
261 263
 
262
-		client.server.logger.Debug("userinput ", client.nick, "<- ", line)
264
+		client.server.logger.Debug("userinput", client.nick, "<- ", line)
265
+
266
+		// special-cased handling of PROXY protocol, see `handleProxyCommand` for details:
267
+		if firstLine {
268
+			firstLine = false
269
+			if strings.HasPrefix(line, "PROXY") {
270
+				err = handleProxyCommand(client.server, client, line)
271
+				if err != nil {
272
+					break
273
+				} else {
274
+					continue
275
+				}
276
+			}
277
+		}
263 278
 
264 279
 		msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
265 280
 		if err == ircmsg.ErrorLineIsEmpty {

+ 0
- 5
irc/commands.go Zobrazit soubor

@@ -215,11 +215,6 @@ func init() {
215 215
 			handler:   privmsgHandler,
216 216
 			minParams: 2,
217 217
 		},
218
-		"PROXY": {
219
-			handler:      proxyHandler,
220
-			usablePreReg: true,
221
-			minParams:    5,
222
-		},
223 218
 		"RENAME": {
224 219
 			handler:   renameHandler,
225 220
 			minParams: 2,

+ 51
- 5
irc/gateways.go Zobrazit soubor

@@ -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
 }

+ 1
- 21
irc/handlers.go Zobrazit soubor

@@ -1910,26 +1910,6 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
1910 1910
 	return false
1911 1911
 }
1912 1912
 
1913
-// PROXY TCP4/6 SOURCEIP DESTIP SOURCEPORT DESTPORT
1914
-// http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
1915
-func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
1916
-	// only allow unregistered clients to use this command
1917
-	if client.Registered() || client.proxiedIP != nil {
1918
-		return false
1919
-	}
1920
-
1921
-	for _, gateway := range server.ProxyAllowedFrom() {
1922
-		if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
1923
-			proxiedIP := msg.Params[1]
1924
-
1925
-			// assume PROXY connections are always secure
1926
-			return client.ApplyProxiedIP(proxiedIP, true)
1927
-		}
1928
-	}
1929
-	client.Quit(client.t("PROXY command is not usable from your address"))
1930
-	return true
1931
-}
1932
-
1933 1913
 // QUIT [<reason>]
1934 1914
 func quitHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
1935 1915
 	reason := "Quit"
@@ -2416,7 +2396,7 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2416 2396
 				if strings.HasPrefix(proxiedIP, "[") && strings.HasSuffix(proxiedIP, "]") {
2417 2397
 					proxiedIP = proxiedIP[1 : len(proxiedIP)-1]
2418 2398
 				}
2419
-				return client.ApplyProxiedIP(proxiedIP, secure)
2399
+				return !client.ApplyProxiedIP(proxiedIP, secure)
2420 2400
 			}
2421 2401
 		}
2422 2402
 	}

+ 0
- 7
irc/help.go Zobrazit soubor

@@ -385,13 +385,6 @@ Replies to a PING. Used to check link connectivity.`,
385 385
 		text: `PRIVMSG <target>{,<target>} <text to be sent>
386 386
 
387 387
 Sends the text to the given targets as a PRIVMSG.`,
388
-	},
389
-	"proxy": {
390
-		oper: true, // not really, but it's restricted anyways
391
-		text: `PROXY TCP4/6 <sourceip> <destip> <sourceport> <destport>
392
-
393
-Used by haproxy's PROXY v1 protocol, to allow for alternate TLS support:
394
-http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt`,
395 388
 	},
396 389
 	"rename": {
397 390
 		text: `RENAME <channel> <newname> [<reason>]

Načítá se…
Zrušit
Uložit