Преглед изворни кода

review fixes; add submatch support to glob

tags/v2.1.0-rc1
Shivaram Lingamneni пре 4 година
родитељ
комит
c92192ef48
12 измењених фајлова са 97 додато и 75 уклоњено
  1. 5
    4
      conventional.yaml
  2. 1
    1
      irc/client.go
  3. 6
    7
      irc/config.go
  4. 20
    21
      irc/ircconn.go
  5. 10
    10
      irc/listeners.go
  6. 12
    12
      irc/server.go
  7. 3
    3
      irc/socket.go
  8. 11
    10
      irc/utils/glob.go
  9. 11
    0
      irc/utils/glob_test.go
  10. 10
    0
      irc/utils/net_test.go
  11. 3
    3
      irc/utils/proxy.go
  12. 5
    4
      oragono.yaml

+ 5
- 4
conventional.yaml Прегледај датотеку

@@ -89,10 +89,11 @@ server:
89 89
         preload: false
90 90
 
91 91
     websockets:
92
-        # sets the Origin headers that will be accepted for websocket connections.
93
-        # an empty list means any value (or no value) is allowed. the main use of this
94
-        # is to prevent malicious third-party Javascript from co-opting non-malicious
95
-        # clients (i.e., mainstream browsers) to DDoS your server.
92
+        # Restrict the origin of WebSocket connections by matching the "Origin" HTTP
93
+        # header. This settings makes oragono reject every WebSocket connection,
94
+        # except when it originates from one of the hosts in this list. Use this to
95
+        # prevent malicious websites from making their visitors connect to oragono
96
+        # without their knowledge. An empty list means that there are no restrictions.
96 97
         allowed-origins:
97 98
             # - "https://oragono.io"
98 99
             # - "https://*.oragono.io"

+ 1
- 1
irc/client.go Прегледај датотеку

@@ -277,7 +277,7 @@ func (server *Server) RunClient(conn IRCConn) {
277 277
 	if isBanned {
278 278
 		// this might not show up properly on some clients,
279 279
 		// but our objective here is just to close the connection out before it has a load impact on us
280
-		conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
280
+		conn.WriteLine([]byte(fmt.Sprintf(errorMsg, banMsg)))
281 281
 		conn.Close()
282 282
 		return
283 283
 	}

+ 6
- 7
irc/config.go Прегледај датотеку

@@ -772,7 +772,7 @@ func (conf *Config) prepareListeners() (err error) {
772 772
 	conf.Server.trueListeners = make(map[string]utils.ListenerConfig)
773 773
 	for addr, block := range conf.Server.Listeners {
774 774
 		var lconf utils.ListenerConfig
775
-		lconf.ProxyDeadline = time.Minute
775
+		lconf.ProxyDeadline = RegisterTimeout
776 776
 		lconf.Tor = block.Tor
777 777
 		lconf.STSOnly = block.STSOnly
778 778
 		if lconf.STSOnly && !conf.Server.STS.Enabled {
@@ -1217,20 +1217,19 @@ func (config *Config) Diff(oldConfig *Config) (addedCaps, removedCaps *caps.Set)
1217 1217
 }
1218 1218
 
1219 1219
 func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard, folded *regexp.Regexp, err error) {
1220
+	if strings.Count(guestFormat, "?") != 0 || strings.Count(guestFormat, "*") != 1 {
1221
+		err = errors.New("guest format must contain 1 '*' and no '?'s")
1222
+		return
1223
+	}
1224
+
1220 1225
 	standard, err = utils.CompileGlob(guestFormat)
1221 1226
 	if err != nil {
1222 1227
 		return
1223 1228
 	}
1224 1229
 
1225 1230
 	starIndex := strings.IndexByte(guestFormat, '*')
1226
-	if starIndex == -1 {
1227
-		return nil, nil, errors.New("guest format must contain exactly one *")
1228
-	}
1229 1231
 	initial := guestFormat[:starIndex]
1230 1232
 	final := guestFormat[starIndex+1:]
1231
-	if strings.IndexByte(final, '*') != -1 {
1232
-		return nil, nil, errors.New("guest format must contain exactly one *")
1233
-	}
1234 1233
 	initialFolded, err := casefoldWithSetting(initial, casemapping)
1235 1234
 	if err != nil {
1236 1235
 		return

+ 20
- 21
irc/ircconn.go Прегледај датотеку

@@ -22,13 +22,16 @@ var (
22 22
 
23 23
 // IRCConn abstracts away the distinction between a regular
24 24
 // net.Conn (which includes both raw TCP and TLS) and a websocket.
25
-// it doesn't expose Read and Write because websockets are message-oriented,
26
-// not stream-oriented.
25
+// it doesn't expose the net.Conn, io.Reader, or io.Writer interfaces
26
+// because websockets are message-oriented, not stream-oriented, and
27
+// therefore this abstraction is message-oriented as well.
27 28
 type IRCConn interface {
28
-	UnderlyingConn() *utils.ProxiedConnection
29
+	UnderlyingConn() *utils.WrappedConn
29 30
 
30
-	Write([]byte) error
31
-	WriteBuffers([][]byte) error
31
+	// these take an IRC line or lines, correctly terminated with CRLF:
32
+	WriteLine([]byte) error
33
+	WriteLines([][]byte) error
34
+	// this returns an IRC line without the terminating CRLF:
32 35
 	ReadLine() (line []byte, err error)
33 36
 
34 37
 	Close() error
@@ -36,26 +39,26 @@ type IRCConn interface {
36 39
 
37 40
 // IRCStreamConn is an IRCConn over a regular stream connection.
38 41
 type IRCStreamConn struct {
39
-	conn   *utils.ProxiedConnection
42
+	conn   *utils.WrappedConn
40 43
 	reader *bufio.Reader
41 44
 }
42 45
 
43
-func NewIRCStreamConn(conn *utils.ProxiedConnection) *IRCStreamConn {
46
+func NewIRCStreamConn(conn *utils.WrappedConn) *IRCStreamConn {
44 47
 	return &IRCStreamConn{
45 48
 		conn: conn,
46 49
 	}
47 50
 }
48 51
 
49
-func (cc *IRCStreamConn) UnderlyingConn() *utils.ProxiedConnection {
52
+func (cc *IRCStreamConn) UnderlyingConn() *utils.WrappedConn {
50 53
 	return cc.conn
51 54
 }
52 55
 
53
-func (cc *IRCStreamConn) Write(buf []byte) (err error) {
56
+func (cc *IRCStreamConn) WriteLine(buf []byte) (err error) {
54 57
 	_, err = cc.conn.Write(buf)
55 58
 	return
56 59
 }
57 60
 
58
-func (cc *IRCStreamConn) WriteBuffers(buffers [][]byte) (err error) {
61
+func (cc *IRCStreamConn) WriteLines(buffers [][]byte) (err error) {
59 62
 	// on Linux, with a plaintext TCP or Unix domain socket,
60 63
 	// the Go runtime will optimize this into a single writev(2) call:
61 64
 	_, err = (*net.Buffers)(&buffers).WriteTo(cc.conn)
@@ -90,17 +93,13 @@ func NewIRCWSConn(conn *websocket.Conn) IRCWSConn {
90 93
 	return IRCWSConn{conn: conn}
91 94
 }
92 95
 
93
-func (wc IRCWSConn) UnderlyingConn() *utils.ProxiedConnection {
94
-	pConn, ok := wc.conn.UnderlyingConn().(*utils.ProxiedConnection)
95
-	if ok {
96
-		return pConn
97
-	} else {
98
-		// this can't happen
99
-		return nil
100
-	}
96
+func (wc IRCWSConn) UnderlyingConn() *utils.WrappedConn {
97
+	// just assume that the type is OK
98
+	wConn, _ := wc.conn.UnderlyingConn().(*utils.WrappedConn)
99
+	return wConn
101 100
 }
102 101
 
103
-func (wc IRCWSConn) Write(buf []byte) (err error) {
102
+func (wc IRCWSConn) WriteLine(buf []byte) (err error) {
104 103
 	buf = bytes.TrimSuffix(buf, crlf)
105 104
 	// there's not much we can do about this;
106 105
 	// silently drop the message
@@ -110,9 +109,9 @@ func (wc IRCWSConn) Write(buf []byte) (err error) {
110 109
 	return wc.conn.WriteMessage(websocket.TextMessage, buf)
111 110
 }
112 111
 
113
-func (wc IRCWSConn) WriteBuffers(buffers [][]byte) (err error) {
112
+func (wc IRCWSConn) WriteLines(buffers [][]byte) (err error) {
114 113
 	for _, buf := range buffers {
115
-		err = wc.Write(buf)
114
+		err = wc.WriteLine(buf)
116 115
 		if err != nil {
117 116
 			return
118 117
 		}

+ 10
- 10
irc/listeners.go Прегледај датотеку

@@ -89,7 +89,7 @@ func (nl *NetListener) Stop() error {
89 89
 }
90 90
 
91 91
 // ensure that any IP we got from the PROXY line is trustworthy (otherwise, clear it)
92
-func validateProxiedIP(conn *utils.ProxiedConnection, config *Config) {
92
+func validateProxiedIP(conn *utils.WrappedConn, config *Config) {
93 93
 	if !utils.IPInNets(utils.AddrToIP(conn.RemoteAddr()), config.Server.proxyAllowedFromNets) {
94 94
 		conn.ProxiedIP = nil
95 95
 	}
@@ -101,12 +101,12 @@ func (nl *NetListener) serve() {
101 101
 
102 102
 		if err == nil {
103 103
 			// hand off the connection
104
-			pConn, ok := conn.(*utils.ProxiedConnection)
104
+			wConn, ok := conn.(*utils.WrappedConn)
105 105
 			if ok {
106
-				if pConn.ProxiedIP != nil {
107
-					validateProxiedIP(pConn, nl.server.Config())
106
+				if wConn.ProxiedIP != nil {
107
+					validateProxiedIP(wConn, nl.server.Config())
108 108
 				}
109
-				go nl.server.RunClient(NewIRCStreamConn(pConn))
109
+				go nl.server.RunClient(NewIRCStreamConn(wConn))
110 110
 			} else {
111 111
 				nl.server.logger.Error("internal", "invalid connection type", nl.addr)
112 112
 			}
@@ -186,19 +186,19 @@ func (wl *WSListener) handle(w http.ResponseWriter, r *http.Request) {
186 186
 		return
187 187
 	}
188 188
 
189
-	pConn, ok := conn.UnderlyingConn().(*utils.ProxiedConnection)
189
+	wConn, ok := conn.UnderlyingConn().(*utils.WrappedConn)
190 190
 	if !ok {
191 191
 		wl.server.logger.Error("internal", "non-proxied connection on websocket", wl.addr)
192 192
 		conn.Close()
193 193
 		return
194 194
 	}
195
-	if pConn.ProxiedIP != nil {
196
-		validateProxiedIP(pConn, config)
195
+	if wConn.ProxiedIP != nil {
196
+		validateProxiedIP(wConn, config)
197 197
 	} else {
198 198
 		// if there was no PROXY protocol IP, use the validated X-Forwarded-For IP instead,
199 199
 		// unless it is redundant
200
-		if proxiedIP != nil && !proxiedIP.Equal(utils.AddrToIP(pConn.RemoteAddr())) {
201
-			pConn.ProxiedIP = proxiedIP
200
+		if proxiedIP != nil && !proxiedIP.Equal(utils.AddrToIP(wConn.RemoteAddr())) {
201
+			wConn.ProxiedIP = proxiedIP
202 202
 		}
203 203
 	}
204 204
 

+ 12
- 12
irc/server.go Прегледај датотеку

@@ -736,16 +736,16 @@ func (server *Server) setupListeners(config *Config) (err error) {
736 736
 		newConfig, stillConfigured := config.Server.trueListeners[addr]
737 737
 
738 738
 		if stillConfigured {
739
-			err := currentListener.Reload(newConfig)
739
+			reloadErr := currentListener.Reload(newConfig)
740 740
 			// attempt to stop and replace the listener if the reload failed
741
-			if err != nil {
741
+			if reloadErr != nil {
742 742
 				currentListener.Stop()
743
-				newListener, err := NewListener(server, addr, newConfig, config.Server.UnixBindMode)
744
-				if err != nil {
745
-					delete(server.listeners, addr)
746
-					return err
747
-				} else {
743
+				newListener, newErr := NewListener(server, addr, newConfig, config.Server.UnixBindMode)
744
+				if newErr == nil {
748 745
 					server.listeners[addr] = newListener
746
+				} else {
747
+					delete(server.listeners, addr)
748
+					return newErr
749 749
 				}
750 750
 			}
751 751
 			logListener(addr, newConfig)
@@ -765,13 +765,13 @@ func (server *Server) setupListeners(config *Config) (err error) {
765 765
 		_, exists := server.listeners[newAddr]
766 766
 		if !exists {
767 767
 			// make a new listener
768
-			newListener, listenerErr := NewListener(server, newAddr, newConfig, config.Server.UnixBindMode)
769
-			if listenerErr != nil {
770
-				server.logger.Error("server", "couldn't listen on", newAddr, listenerErr.Error())
771
-				err = listenerErr
772
-			} else {
768
+			newListener, newErr := NewListener(server, newAddr, newConfig, config.Server.UnixBindMode)
769
+			if newErr == nil {
773 770
 				server.listeners[newAddr] = newListener
774 771
 				logListener(newAddr, newConfig)
772
+			} else {
773
+				server.logger.Error("server", "couldn't listen on", newAddr, newErr.Error())
774
+				err = newErr
775 775
 			}
776 776
 		}
777 777
 	}

+ 3
- 3
irc/socket.go Прегледај датотеку

@@ -144,7 +144,7 @@ func (socket *Socket) BlockingWrite(data []byte) (err error) {
144 144
 		return io.EOF
145 145
 	}
146 146
 
147
-	err = socket.conn.Write(data)
147
+	err = socket.conn.WriteLine(data)
148 148
 	if err != nil {
149 149
 		socket.finalize()
150 150
 	}
@@ -216,7 +216,7 @@ func (socket *Socket) performWrite() (closed bool) {
216 216
 
217 217
 	var err error
218 218
 	if 0 < len(buffers) {
219
-		err = socket.conn.WriteBuffers(buffers)
219
+		err = socket.conn.WriteLines(buffers)
220 220
 	}
221 221
 
222 222
 	closed = closed || err != nil
@@ -244,7 +244,7 @@ func (socket *Socket) finalize() {
244 244
 	}
245 245
 
246 246
 	if len(finalData) != 0 {
247
-		socket.conn.Write(finalData)
247
+		socket.conn.WriteLine(finalData)
248 248
 	}
249 249
 
250 250
 	// close the connection

+ 11
- 10
irc/utils/glob.go Прегледај датотеку

@@ -6,7 +6,7 @@ package utils
6 6
 import (
7 7
 	"bytes"
8 8
 	"regexp"
9
-	"strings"
9
+	"regexp/syntax"
10 10
 )
11 11
 
12 12
 // yet another glob implementation in Go
@@ -14,15 +14,16 @@ import (
14 14
 func CompileGlob(glob string) (result *regexp.Regexp, err error) {
15 15
 	var buf bytes.Buffer
16 16
 	buf.WriteByte('^')
17
-	for {
18
-		i := strings.IndexByte(glob, '*')
19
-		if i == -1 {
20
-			buf.WriteString(regexp.QuoteMeta(glob))
21
-			break
22
-		} else {
23
-			buf.WriteString(regexp.QuoteMeta(glob[:i]))
24
-			buf.WriteString(".*")
25
-			glob = glob[i+1:]
17
+	for _, r := range glob {
18
+		switch r {
19
+		case '*':
20
+			buf.WriteString("(.*)")
21
+		case '?':
22
+			buf.WriteString("(.)")
23
+		case 0xFFFD:
24
+			return nil, &syntax.Error{Code: syntax.ErrInvalidUTF8, Expr: glob}
25
+		default:
26
+			buf.WriteString(regexp.QuoteMeta(string(r)))
26 27
 		}
27 28
 	}
28 29
 	buf.WriteByte('$')

+ 11
- 0
irc/utils/glob_test.go Прегледај датотеку

@@ -29,9 +29,20 @@ func TestGlob(t *testing.T) {
29 29
 	assertMatches("*://*.oragono.io", "https://testnet.oragono.io", true, t)
30 30
 	assertMatches("*://*.oragono.io", "https://oragono.io", false, t)
31 31
 	assertMatches("*://*.oragono.io", "https://githubusercontent.com", false, t)
32
+	assertMatches("*://*.oragono.io", "https://testnet.oragono.io.example.com", false, t)
32 33
 
33 34
 	assertMatches("", "", true, t)
34 35
 	assertMatches("", "x", false, t)
35 36
 	assertMatches("*", "", true, t)
36 37
 	assertMatches("*", "x", true, t)
38
+
39
+	assertMatches("c?b", "cab", true, t)
40
+	assertMatches("c?b", "cub", true, t)
41
+	assertMatches("c?b", "cb", false, t)
42
+	assertMatches("c?b", "cube", false, t)
43
+	assertMatches("?*", "cube", true, t)
44
+	assertMatches("?*", "", false, t)
45
+
46
+	assertMatches("S*e", "Skåne", true, t)
47
+	assertMatches("Sk?ne", "Skåne", true, t)
37 48
 }

+ 10
- 0
irc/utils/net_test.go Прегледај датотеку

@@ -190,8 +190,18 @@ func TestXForwardedFor(t *testing.T) {
190 190
 	checkXFF("10.0.0.4:28432", "10.0.0.3", "10.0.0.3", t)
191 191
 
192 192
 	checkXFF("10.0.0.4:28432", "1.1.1.1, 8.8.4.4", "8.8.4.4", t)
193
+	checkXFF("10.0.0.4:28432", "1.1.1.1,8.8.4.4", "8.8.4.4", t)
193 194
 	checkXFF("10.0.0.4:28432", "8.8.4.4, 1.1.1.1, 10.0.0.3", "1.1.1.1", t)
194 195
 	checkXFF("10.0.0.4:28432", "10.0.0.1, 10.0.0.2, 10.0.0.3", "10.0.0.1", t)
196
+	checkXFF("10.0.0.4:28432", "10.0.0.1, 10.0.0.2,10.0.0.3", "10.0.0.1", t)
195 197
 
196 198
 	checkXFF("@", "8.8.4.4, 1.1.1.1, 10.0.0.3", "1.1.1.1", t)
199
+
200
+	// invalid IP tests:
201
+	checkXFF("8.8.4.4:9999", "not_an_ip", "8.8.4.4", t)
202
+	checkXFF("10.0.0.4:28432", "not_an_ip", "10.0.0.4", t)
203
+	checkXFF("10.0.0.4:28432", "not_an_ip, 1.1.1.1, 10.0.0.3", "1.1.1.1", t)
204
+
205
+	checkXFF("10.0.0.4:28432", "8.8.4.4, not_an_ip, 10.0.0.3", "10.0.0.3", t)
206
+	checkXFF("10.0.0.4:28432", "8.8.4.4, not_an_ip", "10.0.0.4", t)
197 207
 }

+ 3
- 3
irc/utils/proxy.go Прегледај датотеку

@@ -86,10 +86,10 @@ func ParseProxyLine(line string) (ip net.IP, err error) {
86 86
 	return ip.To16(), nil
87 87
 }
88 88
 
89
-/// ProxiedConnection is a net.Conn with some additional data stapled to it;
89
+/// WrappedConn is a net.Conn with some additional data stapled to it;
90 90
 // the proxied IP, if one was read via the PROXY protocol, and the listener
91 91
 // configuration.
92
-type ProxiedConnection struct {
92
+type WrappedConn struct {
93 93
 	net.Conn
94 94
 	ProxiedIP net.IP
95 95
 	Config    ListenerConfig
@@ -154,7 +154,7 @@ func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
154 154
 		conn = tls.Server(conn, config.TLSConfig)
155 155
 	}
156 156
 
157
-	return &ProxiedConnection{
157
+	return &WrappedConn{
158 158
 		Conn:      conn,
159 159
 		ProxiedIP: proxiedIP,
160 160
 		Config:    config,

+ 5
- 4
oragono.yaml Прегледај датотеку

@@ -110,10 +110,11 @@ server:
110 110
         preload: false
111 111
 
112 112
     websockets:
113
-        # sets the Origin headers that will be accepted for websocket connections.
114
-        # an empty list means any value (or no value) is allowed. the main use of this
115
-        # is to prevent malicious third-party Javascript from co-opting non-malicious
116
-        # clients (i.e., mainstream browsers) to DDoS your server.
113
+        # Restrict the origin of WebSocket connections by matching the "Origin" HTTP
114
+        # header. This settings makes oragono reject every WebSocket connection,
115
+        # except when it originates from one of the hosts in this list. Use this to
116
+        # prevent malicious websites from making their visitors connect to oragono
117
+        # without their knowledge. An empty list means that there are no restrictions.
117 118
         allowed-origins:
118 119
             # - "https://oragono.io"
119 120
             # - "https://*.oragono.io"

Loading…
Откажи
Сачувај