20
"errors"
21
"fmt"
22
"io"
23
+ "io/fs"
24
"net"
25
"net/netip"
26
"os"
149
func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) {
150
var ln any
151
var err error
152
+ var address string
153
+ var unixFileMode fs.FileMode
154
152
- address := na.JoinHostPort(portOffset)
155
+ // split unix socket addr early so lnKey
156
+ // is independent of permissions bits
157
+ if na.IsUnixNetwork() {
158
+ var err error
159
+ address, unixFileMode, err = splitUnixSocketPermissionsBits(na.Host)
160
+ if err != nil {
161
+ return nil, err
162
+ }
163
+ } else {
164
+ address = na.JoinHostPort(portOffset)
165
+ }
166
154
- // if this is a unix socket, see if we already have it open
167
+ // if this is a unix socket, see if we already have it open,
168
+ // force socket permissions on it and return early
169
if socket, err := reuseUnixSocket(na.Network, address); socket != nil || err != nil {
170
+ if err := os.Chmod(address, unixFileMode); err != nil {
171
+ return nil, fmt.Errorf("unable to set permissions (%s) on %s: %v", unixFileMode, address, err)
172
+ }
173
return socket, err
174
}
175
210
unixSockets[lnKey] = unix
211
}
212
213
+ if IsUnixNetwork(na.Network) {
214
+ if err := os.Chmod(address, unixFileMode); err != nil {
215
+ return nil, fmt.Errorf("unable to set permissions (%s) on %s: %v", unixFileMode, address, err)
216
+ }
217
+ }
218
+
219
return ln, nil
220
}
221
311
return strings.HasPrefix(netw, "unix")
312
}
313
314
+// Takes a unix socket address in the unusual "path|bits" format
315
+// (e.g. /run/caddy.sock|0222) and tries to split it into
316
+// socket path (host) and permissions bits (port). Colons (":")
317
+// can't be used as separator, as socket paths on Windows may
318
+// include a drive letter (e.g. `unix/c:\absolute\path.sock`).
319
+// Permission bits will default to 0200 if none are specified.
320
+// Throws an error, if the first carrying bit does not
321
+// include write perms (e.g. `0422` or `022`).
322
+// Symbolic permission representation (e.g. `u=w,g=w,o=w`)
323
+// is not supported and will throw an error for now!
324
+func splitUnixSocketPermissionsBits(addr string) (path string, fileMode fs.FileMode, err error) {
325
+ addrSplit := strings.SplitN(addr, "|", 2)
326
+
327
+ if len(addrSplit) == 2 {
328
+ // parse octal permission bit string as uint32
329
+ fileModeUInt64, err := strconv.ParseUint(addrSplit[1], 8, 32)
330
+ if err != nil {
331
+ return "", 0, fmt.Errorf("could not parse octal permission bits in %s: %v", addr, err)
332
+ }
333
+ fileMode = fs.FileMode(fileModeUInt64)
334
+
335
+ // FileMode.String() returns a string like `-rwxr-xr--` for `u=rwx,g=rx,o=r` (`0754`)
336
+ if string(fileMode.String()[2]) != "w" {
337
+ return "", 0, fmt.Errorf("owner of the socket requires '-w-' (write, octal: '2') permissions at least; got '%s' in %s", fileMode.String()[1:4], addr)
338
+ }
339
+
340
+ return addrSplit[0], fileMode, nil
341
+ }
342
+
343
+ // default to 0200 (symbolic: `u=w,g=,o=`)
344
+ // if no permission bits are specified
345
+ return addr, 0200, nil
346
+}
347
+
348
// ParseNetworkAddress parses addr into its individual
349
// components. The input string is expected to be of
350
// the form "network/host:port-range" where any part is
369
network = defaultNetwork
370
}
371
if IsUnixNetwork(network) {
372
+ _, _, err := splitUnixSocketPermissionsBits(host)
373
return NetworkAddress{
374
Network: network,
375
Host: host,
318
- }, nil
376
+ }, err
377
}
378
var start, end uint64
379
if port == "" {