소스 검색

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
         preload: false
89
         preload: false
90
 
90
 
91
     websockets:
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
         allowed-origins:
97
         allowed-origins:
97
             # - "https://oragono.io"
98
             # - "https://oragono.io"
98
             # - "https://*.oragono.io"
99
             # - "https://*.oragono.io"

+ 1
- 1
irc/client.go 파일 보기

277
 	if isBanned {
277
 	if isBanned {
278
 		// this might not show up properly on some clients,
278
 		// this might not show up properly on some clients,
279
 		// but our objective here is just to close the connection out before it has a load impact on us
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
 		conn.Close()
281
 		conn.Close()
282
 		return
282
 		return
283
 	}
283
 	}

+ 6
- 7
irc/config.go 파일 보기

772
 	conf.Server.trueListeners = make(map[string]utils.ListenerConfig)
772
 	conf.Server.trueListeners = make(map[string]utils.ListenerConfig)
773
 	for addr, block := range conf.Server.Listeners {
773
 	for addr, block := range conf.Server.Listeners {
774
 		var lconf utils.ListenerConfig
774
 		var lconf utils.ListenerConfig
775
-		lconf.ProxyDeadline = time.Minute
775
+		lconf.ProxyDeadline = RegisterTimeout
776
 		lconf.Tor = block.Tor
776
 		lconf.Tor = block.Tor
777
 		lconf.STSOnly = block.STSOnly
777
 		lconf.STSOnly = block.STSOnly
778
 		if lconf.STSOnly && !conf.Server.STS.Enabled {
778
 		if lconf.STSOnly && !conf.Server.STS.Enabled {
1217
 }
1217
 }
1218
 
1218
 
1219
 func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard, folded *regexp.Regexp, err error) {
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
 	standard, err = utils.CompileGlob(guestFormat)
1225
 	standard, err = utils.CompileGlob(guestFormat)
1221
 	if err != nil {
1226
 	if err != nil {
1222
 		return
1227
 		return
1223
 	}
1228
 	}
1224
 
1229
 
1225
 	starIndex := strings.IndexByte(guestFormat, '*')
1230
 	starIndex := strings.IndexByte(guestFormat, '*')
1226
-	if starIndex == -1 {
1227
-		return nil, nil, errors.New("guest format must contain exactly one *")
1228
-	}
1229
 	initial := guestFormat[:starIndex]
1231
 	initial := guestFormat[:starIndex]
1230
 	final := guestFormat[starIndex+1:]
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
 	initialFolded, err := casefoldWithSetting(initial, casemapping)
1233
 	initialFolded, err := casefoldWithSetting(initial, casemapping)
1235
 	if err != nil {
1234
 	if err != nil {
1236
 		return
1235
 		return

+ 20
- 21
irc/ircconn.go 파일 보기

22
 
22
 
23
 // IRCConn abstracts away the distinction between a regular
23
 // IRCConn abstracts away the distinction between a regular
24
 // net.Conn (which includes both raw TCP and TLS) and a websocket.
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
 type IRCConn interface {
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
 	ReadLine() (line []byte, err error)
35
 	ReadLine() (line []byte, err error)
33
 
36
 
34
 	Close() error
37
 	Close() error
36
 
39
 
37
 // IRCStreamConn is an IRCConn over a regular stream connection.
40
 // IRCStreamConn is an IRCConn over a regular stream connection.
38
 type IRCStreamConn struct {
41
 type IRCStreamConn struct {
39
-	conn   *utils.ProxiedConnection
42
+	conn   *utils.WrappedConn
40
 	reader *bufio.Reader
43
 	reader *bufio.Reader
41
 }
44
 }
42
 
45
 
43
-func NewIRCStreamConn(conn *utils.ProxiedConnection) *IRCStreamConn {
46
+func NewIRCStreamConn(conn *utils.WrappedConn) *IRCStreamConn {
44
 	return &IRCStreamConn{
47
 	return &IRCStreamConn{
45
 		conn: conn,
48
 		conn: conn,
46
 	}
49
 	}
47
 }
50
 }
48
 
51
 
49
-func (cc *IRCStreamConn) UnderlyingConn() *utils.ProxiedConnection {
52
+func (cc *IRCStreamConn) UnderlyingConn() *utils.WrappedConn {
50
 	return cc.conn
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
 	_, err = cc.conn.Write(buf)
57
 	_, err = cc.conn.Write(buf)
55
 	return
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
 	// on Linux, with a plaintext TCP or Unix domain socket,
62
 	// on Linux, with a plaintext TCP or Unix domain socket,
60
 	// the Go runtime will optimize this into a single writev(2) call:
63
 	// the Go runtime will optimize this into a single writev(2) call:
61
 	_, err = (*net.Buffers)(&buffers).WriteTo(cc.conn)
64
 	_, err = (*net.Buffers)(&buffers).WriteTo(cc.conn)
90
 	return IRCWSConn{conn: conn}
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
 	buf = bytes.TrimSuffix(buf, crlf)
103
 	buf = bytes.TrimSuffix(buf, crlf)
105
 	// there's not much we can do about this;
104
 	// there's not much we can do about this;
106
 	// silently drop the message
105
 	// silently drop the message
110
 	return wc.conn.WriteMessage(websocket.TextMessage, buf)
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
 	for _, buf := range buffers {
113
 	for _, buf := range buffers {
115
-		err = wc.Write(buf)
114
+		err = wc.WriteLine(buf)
116
 		if err != nil {
115
 		if err != nil {
117
 			return
116
 			return
118
 		}
117
 		}

+ 10
- 10
irc/listeners.go 파일 보기

89
 }
89
 }
90
 
90
 
91
 // ensure that any IP we got from the PROXY line is trustworthy (otherwise, clear it)
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
 	if !utils.IPInNets(utils.AddrToIP(conn.RemoteAddr()), config.Server.proxyAllowedFromNets) {
93
 	if !utils.IPInNets(utils.AddrToIP(conn.RemoteAddr()), config.Server.proxyAllowedFromNets) {
94
 		conn.ProxiedIP = nil
94
 		conn.ProxiedIP = nil
95
 	}
95
 	}
101
 
101
 
102
 		if err == nil {
102
 		if err == nil {
103
 			// hand off the connection
103
 			// hand off the connection
104
-			pConn, ok := conn.(*utils.ProxiedConnection)
104
+			wConn, ok := conn.(*utils.WrappedConn)
105
 			if ok {
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
 			} else {
110
 			} else {
111
 				nl.server.logger.Error("internal", "invalid connection type", nl.addr)
111
 				nl.server.logger.Error("internal", "invalid connection type", nl.addr)
112
 			}
112
 			}
186
 		return
186
 		return
187
 	}
187
 	}
188
 
188
 
189
-	pConn, ok := conn.UnderlyingConn().(*utils.ProxiedConnection)
189
+	wConn, ok := conn.UnderlyingConn().(*utils.WrappedConn)
190
 	if !ok {
190
 	if !ok {
191
 		wl.server.logger.Error("internal", "non-proxied connection on websocket", wl.addr)
191
 		wl.server.logger.Error("internal", "non-proxied connection on websocket", wl.addr)
192
 		conn.Close()
192
 		conn.Close()
193
 		return
193
 		return
194
 	}
194
 	}
195
-	if pConn.ProxiedIP != nil {
196
-		validateProxiedIP(pConn, config)
195
+	if wConn.ProxiedIP != nil {
196
+		validateProxiedIP(wConn, config)
197
 	} else {
197
 	} else {
198
 		// if there was no PROXY protocol IP, use the validated X-Forwarded-For IP instead,
198
 		// if there was no PROXY protocol IP, use the validated X-Forwarded-For IP instead,
199
 		// unless it is redundant
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
 		newConfig, stillConfigured := config.Server.trueListeners[addr]
736
 		newConfig, stillConfigured := config.Server.trueListeners[addr]
737
 
737
 
738
 		if stillConfigured {
738
 		if stillConfigured {
739
-			err := currentListener.Reload(newConfig)
739
+			reloadErr := currentListener.Reload(newConfig)
740
 			// attempt to stop and replace the listener if the reload failed
740
 			// attempt to stop and replace the listener if the reload failed
741
-			if err != nil {
741
+			if reloadErr != nil {
742
 				currentListener.Stop()
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
 					server.listeners[addr] = newListener
745
 					server.listeners[addr] = newListener
746
+				} else {
747
+					delete(server.listeners, addr)
748
+					return newErr
749
 				}
749
 				}
750
 			}
750
 			}
751
 			logListener(addr, newConfig)
751
 			logListener(addr, newConfig)
765
 		_, exists := server.listeners[newAddr]
765
 		_, exists := server.listeners[newAddr]
766
 		if !exists {
766
 		if !exists {
767
 			// make a new listener
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
 				server.listeners[newAddr] = newListener
770
 				server.listeners[newAddr] = newListener
774
 				logListener(newAddr, newConfig)
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
 		return io.EOF
144
 		return io.EOF
145
 	}
145
 	}
146
 
146
 
147
-	err = socket.conn.Write(data)
147
+	err = socket.conn.WriteLine(data)
148
 	if err != nil {
148
 	if err != nil {
149
 		socket.finalize()
149
 		socket.finalize()
150
 	}
150
 	}
216
 
216
 
217
 	var err error
217
 	var err error
218
 	if 0 < len(buffers) {
218
 	if 0 < len(buffers) {
219
-		err = socket.conn.WriteBuffers(buffers)
219
+		err = socket.conn.WriteLines(buffers)
220
 	}
220
 	}
221
 
221
 
222
 	closed = closed || err != nil
222
 	closed = closed || err != nil
244
 	}
244
 	}
245
 
245
 
246
 	if len(finalData) != 0 {
246
 	if len(finalData) != 0 {
247
-		socket.conn.Write(finalData)
247
+		socket.conn.WriteLine(finalData)
248
 	}
248
 	}
249
 
249
 
250
 	// close the connection
250
 	// close the connection

+ 11
- 10
irc/utils/glob.go 파일 보기

6
 import (
6
 import (
7
 	"bytes"
7
 	"bytes"
8
 	"regexp"
8
 	"regexp"
9
-	"strings"
9
+	"regexp/syntax"
10
 )
10
 )
11
 
11
 
12
 // yet another glob implementation in Go
12
 // yet another glob implementation in Go
14
 func CompileGlob(glob string) (result *regexp.Regexp, err error) {
14
 func CompileGlob(glob string) (result *regexp.Regexp, err error) {
15
 	var buf bytes.Buffer
15
 	var buf bytes.Buffer
16
 	buf.WriteByte('^')
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
 	buf.WriteByte('$')
29
 	buf.WriteByte('$')

+ 11
- 0
irc/utils/glob_test.go 파일 보기

29
 	assertMatches("*://*.oragono.io", "https://testnet.oragono.io", true, t)
29
 	assertMatches("*://*.oragono.io", "https://testnet.oragono.io", true, t)
30
 	assertMatches("*://*.oragono.io", "https://oragono.io", false, t)
30
 	assertMatches("*://*.oragono.io", "https://oragono.io", false, t)
31
 	assertMatches("*://*.oragono.io", "https://githubusercontent.com", false, t)
31
 	assertMatches("*://*.oragono.io", "https://githubusercontent.com", false, t)
32
+	assertMatches("*://*.oragono.io", "https://testnet.oragono.io.example.com", false, t)
32
 
33
 
33
 	assertMatches("", "", true, t)
34
 	assertMatches("", "", true, t)
34
 	assertMatches("", "x", false, t)
35
 	assertMatches("", "x", false, t)
35
 	assertMatches("*", "", true, t)
36
 	assertMatches("*", "", true, t)
36
 	assertMatches("*", "x", true, t)
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
 	checkXFF("10.0.0.4:28432", "10.0.0.3", "10.0.0.3", t)
190
 	checkXFF("10.0.0.4:28432", "10.0.0.3", "10.0.0.3", t)
191
 
191
 
192
 	checkXFF("10.0.0.4:28432", "1.1.1.1, 8.8.4.4", "8.8.4.4", t)
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
 	checkXFF("10.0.0.4:28432", "8.8.4.4, 1.1.1.1, 10.0.0.3", "1.1.1.1", t)
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
 	checkXFF("10.0.0.4:28432", "10.0.0.1, 10.0.0.2, 10.0.0.3", "10.0.0.1", t)
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
 	checkXFF("@", "8.8.4.4, 1.1.1.1, 10.0.0.3", "1.1.1.1", t)
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
 	return ip.To16(), nil
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
 // the proxied IP, if one was read via the PROXY protocol, and the listener
90
 // the proxied IP, if one was read via the PROXY protocol, and the listener
91
 // configuration.
91
 // configuration.
92
-type ProxiedConnection struct {
92
+type WrappedConn struct {
93
 	net.Conn
93
 	net.Conn
94
 	ProxiedIP net.IP
94
 	ProxiedIP net.IP
95
 	Config    ListenerConfig
95
 	Config    ListenerConfig
154
 		conn = tls.Server(conn, config.TLSConfig)
154
 		conn = tls.Server(conn, config.TLSConfig)
155
 	}
155
 	}
156
 
156
 
157
-	return &ProxiedConnection{
157
+	return &WrappedConn{
158
 		Conn:      conn,
158
 		Conn:      conn,
159
 		ProxiedIP: proxiedIP,
159
 		ProxiedIP: proxiedIP,
160
 		Config:    config,
160
 		Config:    config,

+ 5
- 4
oragono.yaml 파일 보기

110
         preload: false
110
         preload: false
111
 
111
 
112
     websockets:
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
         allowed-origins:
118
         allowed-origins:
118
             # - "https://oragono.io"
119
             # - "https://oragono.io"
119
             # - "https://*.oragono.io"
120
             # - "https://*.oragono.io"

Loading…
취소
저장