3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-26 05:49:25 +01:00

support unix domain sockets

This commit is contained in:
Shivaram Lingamneni 2018-02-01 15:53:49 -05:00
parent bec39ee8cb
commit 2a7f055ef3
5 changed files with 91 additions and 40 deletions

View File

@ -33,6 +33,7 @@ const (
var ( var (
// ErrNickAlreadySet is a weird error that's sent when the server's consistency has been compromised. // ErrNickAlreadySet is a weird error that's sent when the server's consistency has been compromised.
ErrNickAlreadySet = errors.New("Nickname is already set") ErrNickAlreadySet = errors.New("Nickname is already set")
LoopbackIP = net.ParseIP("127.0.0.1")
) )
// Client is an IRC client. // Client is an IRC client.
@ -64,7 +65,7 @@ type Client struct {
nickMaskCasefolded string nickMaskCasefolded string
nickMaskString string // cache for nickmask string since it's used with lots of replies nickMaskString string // cache for nickmask string since it's used with lots of replies
operName string operName string
proxiedIP string // actual remote IP if using the PROXY protocol proxiedIP net.IP // actual remote IP if using the PROXY protocol
quitMessage string quitMessage string
rawHostname string rawHostname string
realname string realname string
@ -111,7 +112,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
// error is not useful to us here anyways so we can ignore it // error is not useful to us here anyways so we can ignore it
client.certfp, _ = client.socket.CertFP() client.certfp, _ = client.socket.CertFP()
} }
if server.checkIdent { if server.checkIdent && !utils.AddrIsUnix(conn.RemoteAddr()) {
_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String()) _, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
serverPort, _ := strconv.Atoi(serverPortString) serverPort, _ := strconv.Atoi(serverPortString)
if err != nil { if err != nil {
@ -146,19 +147,18 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
// IP returns the IP address of this client. // IP returns the IP address of this client.
func (client *Client) IP() net.IP { func (client *Client) IP() net.IP {
if client.proxiedIP != "" { if client.proxiedIP != nil {
return net.ParseIP(client.proxiedIP) return client.proxiedIP
} }
if ip := utils.AddrToIP(client.socket.conn.RemoteAddr()); ip != nil {
return net.ParseIP(utils.IPString(client.socket.conn.RemoteAddr())) return ip
}
// unix domain socket that hasn't issued PROXY/WEBIRC yet. YOLO
return LoopbackIP
} }
// IPString returns the IP address of this client as a string. // IPString returns the IP address of this client as a string.
func (client *Client) IPString() string { func (client *Client) IPString() string {
if client.proxiedIP != "" {
return client.proxiedIP
}
ip := client.IP().String() ip := client.IP().String()
if 0 < len(ip) && ip[0] == ':' { if 0 < len(ip) && ip[0] == ':' {
ip = "0" + ip ip = "0" + ip
@ -581,7 +581,7 @@ func (client *Client) AllNickmasks() []string {
masks = append(masks, mask) masks = append(masks, mask)
} }
mask2, err := Casefold(fmt.Sprintf("%s!%s@%s", client.nick, client.username, utils.IPString(client.socket.conn.RemoteAddr()))) mask2, err := Casefold(fmt.Sprintf("%s!%s@%s", client.nick, client.username, client.IPString()))
if err == nil && mask2 != mask { if err == nil && mask2 != mask {
masks = append(masks, mask2) masks = append(masks, mask2)
} }

View File

@ -38,10 +38,34 @@ func (wc *webircConfig) Populate() (err error) {
return err return err
} }
func isGatewayAllowed(addr net.Addr, gatewaySpec string) bool {
// "localhost" includes any loopback IP or unix domain socket
if gatewaySpec == "localhost" {
return utils.AddrIsLocal(addr)
}
ip := utils.AddrToIP(addr)
if ip == nil {
return false
}
// exact IP match
if ip.String() == gatewaySpec {
return true
}
// CIDR match
_, gatewayNet, err := net.ParseCIDR(gatewaySpec)
if err != nil {
return false
}
return gatewayNet.Contains(ip)
}
// WEBIRC <password> <gateway> <hostname> <ip> [:flag1 flag2=x flag3] // WEBIRC <password> <gateway> <hostname> <ip> [:flag1 flag2=x flag3]
func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// only allow unregistered clients to use this command // only allow unregistered clients to use this command
if client.registered || client.proxiedIP != "" { if client.registered || client.proxiedIP != nil {
return false return false
} }
@ -68,11 +92,9 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
} }
} }
clientAddress := utils.IPString(client.socket.conn.RemoteAddr())
clientHostname := client.hostname
for _, info := range server.WebIRCConfig() { for _, info := range server.WebIRCConfig() {
for _, address := range info.Hosts { for _, gateway := range info.Hosts {
if clientHostname == address || clientAddress == address { if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
// confirm password and/or fingerprint // confirm password and/or fingerprint
givenPassword := msg.Params[0] givenPassword := msg.Params[0]
if 0 < len(info.Password) && passwd.ComparePasswordString(info.Password, givenPassword) != nil { if 0 < len(info.Password) && passwd.ComparePasswordString(info.Password, givenPassword) != nil {
@ -96,14 +118,12 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt // http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// only allow unregistered clients to use this command // only allow unregistered clients to use this command
if client.registered || client.proxiedIP != "" { if client.registered || client.proxiedIP != nil {
return false return false
} }
clientAddress := utils.IPString(client.socket.conn.RemoteAddr()) for _, gateway := range server.ProxyAllowedFrom() {
clientHostname := client.hostname if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
for _, address := range server.ProxyAllowedFrom() {
if clientHostname == address || clientAddress == address {
proxiedIP := msg.Params[1] proxiedIP := msg.Params[1]
// assume PROXY connections are always secure // assume PROXY connections are always secure
@ -130,7 +150,7 @@ func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (exiting bool)
} }
// given IP is sane! override the client's current IP // given IP is sane! override the client's current IP
client.proxiedIP = proxiedIP client.proxiedIP = parsedProxiedIP
client.rawHostname = utils.LookupHostname(proxiedIP) client.rawHostname = utils.LookupHostname(proxiedIP)
client.hostname = client.rawHostname client.hostname = client.rawHostname

View File

@ -267,19 +267,15 @@ func (server *Server) Run() {
func (server *Server) acceptClient(conn clientConn) { func (server *Server) acceptClient(conn clientConn) {
// check IP address // check IP address
ipaddr := net.ParseIP(utils.IPString(conn.Conn.RemoteAddr())) ipaddr := utils.AddrToIP(conn.Conn.RemoteAddr())
if ipaddr == nil { if ipaddr != nil {
conn.Conn.Write([]byte(couldNotParseIPMsg)) isBanned, banMsg := server.checkBans(ipaddr)
conn.Conn.Close() if isBanned {
return // 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
} conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
conn.Conn.Close()
isBanned, banMsg := server.checkBans(ipaddr) return
if isBanned { }
// 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
conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
conn.Conn.Close()
return
} }
server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr)) server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
@ -336,7 +332,23 @@ func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
// createListener starts the given listeners. // createListener starts the given listeners.
func (server *Server) createListener(addr string, tlsConfig *tls.Config) *ListenerWrapper { func (server *Server) createListener(addr string, tlsConfig *tls.Config) *ListenerWrapper {
// make listener // make listener
listener, err := net.Listen("tcp", addr) var listener net.Listener
var err error
optional_unix_prefix := "unix:"
optional_prefix_len := len(optional_unix_prefix)
if len(addr) >= optional_prefix_len && strings.ToLower(addr[0:optional_prefix_len]) == optional_unix_prefix {
addr = addr[optional_prefix_len:]
if len(addr) == 0 || addr[0] != '/' {
log.Fatal("Bad unix socket address", addr)
}
}
if len(addr) > 0 && addr[0] == '/' {
// https://stackoverflow.com/a/34881585
os.Remove(addr)
listener, err = net.Listen("unix", addr)
} else {
listener, err = net.Listen("tcp", addr)
}
if err != nil { if err != nil {
log.Fatal(server, "listen error: ", err) log.Fatal(server, "listen error: ", err)
} }

View File

@ -22,6 +22,9 @@ func IPString(addr net.Addr) string {
// AddrLookupHostname returns the hostname (if possible) or address for the given `net.Addr`. // AddrLookupHostname returns the hostname (if possible) or address for the given `net.Addr`.
func AddrLookupHostname(addr net.Addr) string { func AddrLookupHostname(addr net.Addr) string {
if AddrIsUnix(addr) {
return "localhost"
}
return LookupHostname(IPString(addr)) return LookupHostname(IPString(addr))
} }
@ -30,10 +33,22 @@ func AddrIsLocal(addr net.Addr) bool {
if tcpaddr, ok := addr.(*net.TCPAddr); ok { if tcpaddr, ok := addr.(*net.TCPAddr); ok {
return tcpaddr.IP.IsLoopback() return tcpaddr.IP.IsLoopback()
} }
if _, ok := addr.(*net.UnixAddr); ok { _, ok := addr.(*net.UnixAddr)
return true return ok
}
// AddrToIP returns the IP address for a net.Addr, or nil if it's a unix domain socket.
func AddrToIP(addr net.Addr) net.IP {
if tcpaddr, ok := addr.(*net.TCPAddr); ok {
return tcpaddr.IP
} }
return false return nil
}
// AddrIsUnix returns whether the address is a unix domain socket.
func AddrIsUnix(addr net.Addr) bool {
_, ok := addr.(*net.UnixAddr)
return ok
} }
// LookupHostname returns the hostname for `addr` if it has one. Otherwise, just returns `addr`. // LookupHostname returns the hostname for `addr` if it has one. Otherwise, just returns `addr`.

View File

@ -16,6 +16,8 @@ server:
- "127.0.0.1:6668" - "127.0.0.1:6668"
- "[::1]:6668" - "[::1]:6668"
- ":6697" # ssl port - ":6697" # ssl port
# unix domain socket for proxying:
# - "/tmp/oragono_sock"
# tls listeners # tls listeners
tls-listeners: tls-listeners:
@ -59,11 +61,12 @@ server:
#motd-formatting: true #motd-formatting: true
# addresses/hostnames the PROXY command can be used from # addresses/hostnames the PROXY command can be used from
# this should be restricted to 127.0.0.1 and localhost at most # this should be restricted to 127.0.0.1/8 and localhost at most
# you should also add these addresses to the connection limits and throttling exemption lists # you should also add these addresses to the connection limits and throttling exemption lists
proxy-allowed-from: proxy-allowed-from:
# - localhost # - localhost
# - "127.0.0.1" # - "127.0.0.1"
# - "127.0.0.1/8"
# controls the use of the WEBIRC command (by IRC<->web interfaces, bouncers and similar) # controls the use of the WEBIRC command (by IRC<->web interfaces, bouncers and similar)
webirc: webirc:
@ -79,6 +82,7 @@ server:
hosts: hosts:
# - localhost # - localhost
# - "127.0.0.1" # - "127.0.0.1"
# - "127.0.0.1/8"
# - "0::1" # - "0::1"
# maximum length of clients' sendQ in bytes # maximum length of clients' sendQ in bytes