Browse Source

support unix domain sockets

tags/v0.11.0-beta
Shivaram Lingamneni 6 years ago
parent
commit
2a7f055ef3
5 changed files with 91 additions and 40 deletions
  1. 11
    11
      irc/client.go
  2. 31
    11
      irc/gateways.go
  3. 26
    14
      irc/server.go
  4. 18
    3
      irc/utils/net.go
  5. 5
    1
      oragono.yaml

+ 11
- 11
irc/client.go View File

33
 var (
33
 var (
34
 	// ErrNickAlreadySet is a weird error that's sent when the server's consistency has been compromised.
34
 	// ErrNickAlreadySet is a weird error that's sent when the server's consistency has been compromised.
35
 	ErrNickAlreadySet = errors.New("Nickname is already set")
35
 	ErrNickAlreadySet = errors.New("Nickname is already set")
36
+	LoopbackIP        = net.ParseIP("127.0.0.1")
36
 )
37
 )
37
 
38
 
38
 // Client is an IRC client.
39
 // Client is an IRC client.
64
 	nickMaskCasefolded string
65
 	nickMaskCasefolded string
65
 	nickMaskString     string // cache for nickmask string since it's used with lots of replies
66
 	nickMaskString     string // cache for nickmask string since it's used with lots of replies
66
 	operName           string
67
 	operName           string
67
-	proxiedIP          string // actual remote IP if using the PROXY protocol
68
+	proxiedIP          net.IP // actual remote IP if using the PROXY protocol
68
 	quitMessage        string
69
 	quitMessage        string
69
 	rawHostname        string
70
 	rawHostname        string
70
 	realname           string
71
 	realname           string
111
 		// error is not useful to us here anyways so we can ignore it
112
 		// error is not useful to us here anyways so we can ignore it
112
 		client.certfp, _ = client.socket.CertFP()
113
 		client.certfp, _ = client.socket.CertFP()
113
 	}
114
 	}
114
-	if server.checkIdent {
115
+	if server.checkIdent && !utils.AddrIsUnix(conn.RemoteAddr()) {
115
 		_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
116
 		_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
116
 		serverPort, _ := strconv.Atoi(serverPortString)
117
 		serverPort, _ := strconv.Atoi(serverPortString)
117
 		if err != nil {
118
 		if err != nil {
146
 
147
 
147
 // IP returns the IP address of this client.
148
 // IP returns the IP address of this client.
148
 func (client *Client) IP() net.IP {
149
 func (client *Client) IP() net.IP {
149
-	if client.proxiedIP != "" {
150
-		return net.ParseIP(client.proxiedIP)
150
+	if client.proxiedIP != nil {
151
+		return client.proxiedIP
151
 	}
152
 	}
152
-
153
-	return net.ParseIP(utils.IPString(client.socket.conn.RemoteAddr()))
153
+	if ip := utils.AddrToIP(client.socket.conn.RemoteAddr()); ip != nil {
154
+		return ip
155
+	}
156
+	// unix domain socket that hasn't issued PROXY/WEBIRC yet. YOLO
157
+	return LoopbackIP
154
 }
158
 }
155
 
159
 
156
 // IPString returns the IP address of this client as a string.
160
 // IPString returns the IP address of this client as a string.
157
 func (client *Client) IPString() string {
161
 func (client *Client) IPString() string {
158
-	if client.proxiedIP != "" {
159
-		return client.proxiedIP
160
-	}
161
-
162
 	ip := client.IP().String()
162
 	ip := client.IP().String()
163
 	if 0 < len(ip) && ip[0] == ':' {
163
 	if 0 < len(ip) && ip[0] == ':' {
164
 		ip = "0" + ip
164
 		ip = "0" + ip
581
 		masks = append(masks, mask)
581
 		masks = append(masks, mask)
582
 	}
582
 	}
583
 
583
 
584
-	mask2, err := Casefold(fmt.Sprintf("%s!%s@%s", client.nick, client.username, utils.IPString(client.socket.conn.RemoteAddr())))
584
+	mask2, err := Casefold(fmt.Sprintf("%s!%s@%s", client.nick, client.username, client.IPString()))
585
 	if err == nil && mask2 != mask {
585
 	if err == nil && mask2 != mask {
586
 		masks = append(masks, mask2)
586
 		masks = append(masks, mask2)
587
 	}
587
 	}

+ 31
- 11
irc/gateways.go View File

38
 	return err
38
 	return err
39
 }
39
 }
40
 
40
 
41
+func isGatewayAllowed(addr net.Addr, gatewaySpec string) bool {
42
+	// "localhost" includes any loopback IP or unix domain socket
43
+	if gatewaySpec == "localhost" {
44
+		return utils.AddrIsLocal(addr)
45
+	}
46
+
47
+	ip := utils.AddrToIP(addr)
48
+	if ip == nil {
49
+		return false
50
+	}
51
+
52
+	// exact IP match
53
+	if ip.String() == gatewaySpec {
54
+		return true
55
+	}
56
+
57
+	// CIDR match
58
+	_, gatewayNet, err := net.ParseCIDR(gatewaySpec)
59
+	if err != nil {
60
+		return false
61
+	}
62
+	return gatewayNet.Contains(ip)
63
+}
64
+
41
 // WEBIRC <password> <gateway> <hostname> <ip> [:flag1 flag2=x flag3]
65
 // WEBIRC <password> <gateway> <hostname> <ip> [:flag1 flag2=x flag3]
42
 func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
66
 func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
43
 	// only allow unregistered clients to use this command
67
 	// only allow unregistered clients to use this command
44
-	if client.registered || client.proxiedIP != "" {
68
+	if client.registered || client.proxiedIP != nil {
45
 		return false
69
 		return false
46
 	}
70
 	}
47
 
71
 
68
 		}
92
 		}
69
 	}
93
 	}
70
 
94
 
71
-	clientAddress := utils.IPString(client.socket.conn.RemoteAddr())
72
-	clientHostname := client.hostname
73
 	for _, info := range server.WebIRCConfig() {
95
 	for _, info := range server.WebIRCConfig() {
74
-		for _, address := range info.Hosts {
75
-			if clientHostname == address || clientAddress == address {
96
+		for _, gateway := range info.Hosts {
97
+			if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
76
 				// confirm password and/or fingerprint
98
 				// confirm password and/or fingerprint
77
 				givenPassword := msg.Params[0]
99
 				givenPassword := msg.Params[0]
78
 				if 0 < len(info.Password) && passwd.ComparePasswordString(info.Password, givenPassword) != nil {
100
 				if 0 < len(info.Password) && passwd.ComparePasswordString(info.Password, givenPassword) != nil {
96
 // http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
118
 // http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
97
 func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
119
 func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
98
 	// only allow unregistered clients to use this command
120
 	// only allow unregistered clients to use this command
99
-	if client.registered || client.proxiedIP != "" {
121
+	if client.registered || client.proxiedIP != nil {
100
 		return false
122
 		return false
101
 	}
123
 	}
102
 
124
 
103
-	clientAddress := utils.IPString(client.socket.conn.RemoteAddr())
104
-	clientHostname := client.hostname
105
-	for _, address := range server.ProxyAllowedFrom() {
106
-		if clientHostname == address || clientAddress == address {
125
+	for _, gateway := range server.ProxyAllowedFrom() {
126
+		if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
107
 			proxiedIP := msg.Params[1]
127
 			proxiedIP := msg.Params[1]
108
 
128
 
109
 			// assume PROXY connections are always secure
129
 			// assume PROXY connections are always secure
130
 	}
150
 	}
131
 
151
 
132
 	// given IP is sane! override the client's current IP
152
 	// given IP is sane! override the client's current IP
133
-	client.proxiedIP = proxiedIP
153
+	client.proxiedIP = parsedProxiedIP
134
 	client.rawHostname = utils.LookupHostname(proxiedIP)
154
 	client.rawHostname = utils.LookupHostname(proxiedIP)
135
 	client.hostname = client.rawHostname
155
 	client.hostname = client.rawHostname
136
 
156
 

+ 26
- 14
irc/server.go View File

267
 
267
 
268
 func (server *Server) acceptClient(conn clientConn) {
268
 func (server *Server) acceptClient(conn clientConn) {
269
 	// check IP address
269
 	// check IP address
270
-	ipaddr := net.ParseIP(utils.IPString(conn.Conn.RemoteAddr()))
271
-	if ipaddr == nil {
272
-		conn.Conn.Write([]byte(couldNotParseIPMsg))
273
-		conn.Conn.Close()
274
-		return
275
-	}
276
-
277
-	isBanned, banMsg := server.checkBans(ipaddr)
278
-	if isBanned {
279
-		// this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us
280
-		conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
281
-		conn.Conn.Close()
282
-		return
270
+	ipaddr := utils.AddrToIP(conn.Conn.RemoteAddr())
271
+	if ipaddr != nil {
272
+		isBanned, banMsg := server.checkBans(ipaddr)
273
+		if isBanned {
274
+			// this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us
275
+			conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
276
+			conn.Conn.Close()
277
+			return
278
+		}
283
 	}
279
 	}
284
 
280
 
285
 	server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
281
 	server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
336
 // createListener starts the given listeners.
332
 // createListener starts the given listeners.
337
 func (server *Server) createListener(addr string, tlsConfig *tls.Config) *ListenerWrapper {
333
 func (server *Server) createListener(addr string, tlsConfig *tls.Config) *ListenerWrapper {
338
 	// make listener
334
 	// make listener
339
-	listener, err := net.Listen("tcp", addr)
335
+	var listener net.Listener
336
+	var err error
337
+	optional_unix_prefix := "unix:"
338
+	optional_prefix_len := len(optional_unix_prefix)
339
+	if len(addr) >= optional_prefix_len && strings.ToLower(addr[0:optional_prefix_len]) == optional_unix_prefix {
340
+		addr = addr[optional_prefix_len:]
341
+		if len(addr) == 0 || addr[0] != '/' {
342
+			log.Fatal("Bad unix socket address", addr)
343
+		}
344
+	}
345
+	if len(addr) > 0 && addr[0] == '/' {
346
+		// https://stackoverflow.com/a/34881585
347
+		os.Remove(addr)
348
+		listener, err = net.Listen("unix", addr)
349
+	} else {
350
+		listener, err = net.Listen("tcp", addr)
351
+	}
340
 	if err != nil {
352
 	if err != nil {
341
 		log.Fatal(server, "listen error: ", err)
353
 		log.Fatal(server, "listen error: ", err)
342
 	}
354
 	}

+ 18
- 3
irc/utils/net.go View File

22
 
22
 
23
 // AddrLookupHostname returns the hostname (if possible) or address for the given `net.Addr`.
23
 // AddrLookupHostname returns the hostname (if possible) or address for the given `net.Addr`.
24
 func AddrLookupHostname(addr net.Addr) string {
24
 func AddrLookupHostname(addr net.Addr) string {
25
+	if AddrIsUnix(addr) {
26
+		return "localhost"
27
+	}
25
 	return LookupHostname(IPString(addr))
28
 	return LookupHostname(IPString(addr))
26
 }
29
 }
27
 
30
 
30
 	if tcpaddr, ok := addr.(*net.TCPAddr); ok {
33
 	if tcpaddr, ok := addr.(*net.TCPAddr); ok {
31
 		return tcpaddr.IP.IsLoopback()
34
 		return tcpaddr.IP.IsLoopback()
32
 	}
35
 	}
33
-	if _, ok := addr.(*net.UnixAddr); ok {
34
-		return true
36
+	_, ok := addr.(*net.UnixAddr)
37
+	return ok
38
+}
39
+
40
+// AddrToIP returns the IP address for a net.Addr, or nil if it's a unix domain socket.
41
+func AddrToIP(addr net.Addr) net.IP {
42
+	if tcpaddr, ok := addr.(*net.TCPAddr); ok {
43
+		return tcpaddr.IP
35
 	}
44
 	}
36
-	return false
45
+	return nil
46
+}
47
+
48
+// AddrIsUnix returns whether the address is a unix domain socket.
49
+func AddrIsUnix(addr net.Addr) bool {
50
+	_, ok := addr.(*net.UnixAddr)
51
+	return ok
37
 }
52
 }
38
 
53
 
39
 // LookupHostname returns the hostname for `addr` if it has one. Otherwise, just returns `addr`.
54
 // LookupHostname returns the hostname for `addr` if it has one. Otherwise, just returns `addr`.

+ 5
- 1
oragono.yaml View File

16
         - "127.0.0.1:6668"
16
         - "127.0.0.1:6668"
17
         - "[::1]:6668"
17
         - "[::1]:6668"
18
         - ":6697" # ssl port
18
         - ":6697" # ssl port
19
+        # unix domain socket for proxying:
20
+        # - "/tmp/oragono_sock"
19
 
21
 
20
     # tls listeners
22
     # tls listeners
21
     tls-listeners:
23
     tls-listeners:
59
     #motd-formatting: true
61
     #motd-formatting: true
60
 
62
 
61
     # addresses/hostnames the PROXY command can be used from
63
     # addresses/hostnames the PROXY command can be used from
62
-    # this should be restricted to 127.0.0.1 and localhost at most
64
+    # this should be restricted to 127.0.0.1/8 and localhost at most
63
     # you should also add these addresses to the connection limits and throttling exemption lists
65
     # you should also add these addresses to the connection limits and throttling exemption lists
64
     proxy-allowed-from:
66
     proxy-allowed-from:
65
         # - localhost
67
         # - localhost
66
         # - "127.0.0.1"
68
         # - "127.0.0.1"
69
+        # - "127.0.0.1/8"
67
 
70
 
68
     # controls the use of the WEBIRC command (by IRC<->web interfaces, bouncers and similar)
71
     # controls the use of the WEBIRC command (by IRC<->web interfaces, bouncers and similar)
69
     webirc:
72
     webirc:
79
             hosts:
82
             hosts:
80
                 # - localhost
83
                 # - localhost
81
                 # - "127.0.0.1"
84
                 # - "127.0.0.1"
85
+                # - "127.0.0.1/8"
82
                 # - "0::1"
86
                 # - "0::1"
83
 
87
 
84
     # maximum length of clients' sendQ in bytes
88
     # maximum length of clients' sendQ in bytes

Loading…
Cancel
Save