|
|
|
@ -7,11 +7,8 @@ import (
|
|
|
|
|
"io" |
|
|
|
|
"net" |
|
|
|
|
"os" |
|
|
|
|
"os/user" |
|
|
|
|
"path/filepath" |
|
|
|
|
"regexp" |
|
|
|
|
"sort" |
|
|
|
|
"strconv" |
|
|
|
|
"strings" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
@ -348,89 +345,13 @@ type Config struct {
|
|
|
|
|
WatchPlans []*watch.WatchPlan `mapstructure:"-" json:"-"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// UnixSocket contains the parameters for a Unix socket interface
|
|
|
|
|
type UnixSocket struct { |
|
|
|
|
// Path to the socket on-disk
|
|
|
|
|
Path string |
|
|
|
|
|
|
|
|
|
// uid of the owner of the socket
|
|
|
|
|
Uid int |
|
|
|
|
|
|
|
|
|
// gid of the group of the socket
|
|
|
|
|
Gid int |
|
|
|
|
|
|
|
|
|
// Permissions for the socket file
|
|
|
|
|
Permissions os.FileMode |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func populateUnixSocket(addr string) (*UnixSocket, error) { |
|
|
|
|
// unixSocketAddr tests if a given address describes a domain socket,
|
|
|
|
|
// and returns the relevant path part of the string if it is.
|
|
|
|
|
func unixSocketAddr(addr string) (string, bool) { |
|
|
|
|
if !strings.HasPrefix(addr, "unix://") { |
|
|
|
|
return nil, fmt.Errorf("Failed to parse Unix address, format is unix://[path];[user];[group];[mode]: %v", addr) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
splitAddr := strings.Split(strings.TrimPrefix(addr, "unix://"), ";") |
|
|
|
|
if len(splitAddr) != 4 { |
|
|
|
|
return nil, fmt.Errorf("Failed to parse Unix address, format is unix://[path];[user];[group];[mode]: %v", addr) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ret := &UnixSocket{Path: splitAddr[0]} |
|
|
|
|
|
|
|
|
|
var userVal *user.User |
|
|
|
|
var err error |
|
|
|
|
|
|
|
|
|
regex := regexp.MustCompile("[\\d]+") |
|
|
|
|
if regex.MatchString(splitAddr[1]) { |
|
|
|
|
userVal, err = user.LookupId(splitAddr[1]) |
|
|
|
|
} else { |
|
|
|
|
userVal, err = user.Lookup(splitAddr[1]) |
|
|
|
|
} |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, fmt.Errorf("Invalid user given for Unix socket ownership: %v", splitAddr[1]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if uid64, err := strconv.ParseInt(userVal.Uid, 10, 32); err != nil { |
|
|
|
|
return nil, fmt.Errorf("Failed to parse given user ID of %v into integer", userVal.Uid) |
|
|
|
|
} else { |
|
|
|
|
ret.Uid = int(uid64) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Go doesn't currently have a way to look up gid from group name,
|
|
|
|
|
// so require a numeric gid; see
|
|
|
|
|
// https://codereview.appspot.com/101310044
|
|
|
|
|
if gid64, err := strconv.ParseInt(splitAddr[2], 10, 32); err != nil { |
|
|
|
|
return nil, fmt.Errorf("Socket group must be given as numeric gid. Failed to parse given group ID of %v into integer", splitAddr[2]) |
|
|
|
|
} else { |
|
|
|
|
ret.Gid = int(gid64) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if mode, err := strconv.ParseUint(splitAddr[3], 8, 32); err != nil { |
|
|
|
|
return nil, fmt.Errorf("Failed to parse given mode of %v into integer", splitAddr[3]) |
|
|
|
|
} else { |
|
|
|
|
if mode > 0777 { |
|
|
|
|
return nil, fmt.Errorf("Given mode is invalid; must be an octal number between 0 and 777") |
|
|
|
|
} else { |
|
|
|
|
ret.Permissions = os.FileMode(mode) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ret, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func adjustUnixSocketPermissions(addr string) error { |
|
|
|
|
sock, err := populateUnixSocket(addr) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
return "", false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err = os.Chown(sock.Path, sock.Uid, sock.Gid); err != nil { |
|
|
|
|
return fmt.Errorf("Error attempting to change socket permissions to userid %v and groupid %v: %v", sock.Uid, sock.Gid, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err = os.Chmod(sock.Path, sock.Permissions); err != nil { |
|
|
|
|
return fmt.Errorf("Error attempting to change socket permissions to mode %v: %v", sock.Permissions, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
return strings.TrimPrefix(addr, "unix://"), true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type dirEnts []os.FileInfo |
|
|
|
@ -485,31 +406,14 @@ func (c *Config) ClientListener(override string, port int) (net.Addr, error) {
|
|
|
|
|
addr = c.ClientAddr |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch { |
|
|
|
|
case strings.HasPrefix(addr, "unix://"): |
|
|
|
|
sock, err := populateUnixSocket(addr) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return &net.UnixAddr{Name: sock.Path, Net: "unix"}, nil |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
ip := net.ParseIP(addr) |
|
|
|
|
if ip == nil { |
|
|
|
|
return nil, fmt.Errorf("Failed to parse IP: %v", addr) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ip.IsUnspecified() { |
|
|
|
|
ip = net.ParseIP("127.0.0.1") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ip == nil { |
|
|
|
|
return nil, fmt.Errorf("Failed to parse IP 127.0.0.1") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return &net.TCPAddr{IP: ip, Port: port}, nil |
|
|
|
|
if path, ok := unixSocketAddr(addr); ok { |
|
|
|
|
return &net.UnixAddr{Name: path, Net: "unix"}, nil |
|
|
|
|
} |
|
|
|
|
ip := net.ParseIP(addr) |
|
|
|
|
if ip == nil { |
|
|
|
|
return nil, fmt.Errorf("Failed to parse IP: %v", addr) |
|
|
|
|
} |
|
|
|
|
return &net.TCPAddr{IP: ip, Port: port}, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// DecodeConfig reads the configuration from the given reader in JSON
|
|
|
|
|