bug: unix_addr check is useless
- [ x] I have looked at the documentation here first?
- [ x] I have looked at the examples provided that may showcase my question here?
Package version eg. v9, v10:
v10.23.0
Issue, Question or Enhancement:
The unix_addr check is useless. I looked into the src:
https://github.com/go-playground/validator/blob/6c3307e6c64040ebc0efffe9366927c68146ffba/baked_in.go#L2564C16-L2564C31
func isUnixAddrResolvable(fl FieldLevel) bool {
_, err := net.ResolveUnixAddr("unix", fl.Field().String())
return err == nil
}
and in stdlib net:
https://cs.opensource.google/go/go/+/refs/tags/go1.23.4:src/net/unixsock.go;l=57
func ResolveUnixAddr(network, address string) (*UnixAddr, error) {
switch network {
case "unix", "unixgram", "unixpacket":
return &UnixAddr{Name: address, Net: network}, nil
default:
return nil, UnknownNetworkError(network)
}
}
effectively this will never throw an error, since you're always passing "unix".
In fact, ResolveUnixAddr is itself useless to check if the socket actually exists.
You need something that checks if the file exists and if that file is a socket:
func IsSocket(filePath string) (err error) {
stats, err := os.Stat(filePath)
if err != nil {
err = fmt.Errorf("check file existence: %w", err)
return
}
if stats.Mode().Type() != fs.ModeSocket {
err = fmt.Errorf("not socket file: %v", filePath)
return
}
return
}
I'm not entirely sure about the original design intention for this validator. It might have been intended to validate input values exclusively for the 'Unix' network. However, it would be beneficial to extend its functionality to also handle unixgram and unixpacket networks.
We always welcome pull requests with improvements or fixes.
I suggest creating a new socket validator and implementing it with the approach you provided.
A "socket" does not inherently mean a UDS/IPC socket. tcp,127.0.0.1:1234 is a "socket".
If anything, it explicitly should be called "ipc" ("Inter-Process Communication") or "uds" (UNIX Domain Socket).
socket as a validator name should be applicable to all of the above cases (see also #1327).
I went through the issue. @nf-brentsaner is right about the fact that socket can mean many things(tcp, uds, etc). So it probably will not be a good idea to name the validator as 'socket' if it just validates UDS. I think we have a few options here
- We could just build an UDS validator and name it as UDS or IPC
- We could try building a proper Socket validator ( supporting tcp,uds etc). But that will involve adding support for quite a lot of socket types.
- We could try incrementally adding validation for each socket types for example an UDS validator a TCP validator and so on.
I propose we build out the socket validations for each type one by one. Once we are confident we have considered all cases we can debate about if we merge all of them into a 'socket' validator. @nodivbyzero @deankarn Kindly look into this.
I propose we build out the socket validations for each type one by one. Once we are confident we have considered all cases we can debate about if we merge all of them into a 'socket' validator. @nodivbyzero @deankarn Kindly look into this.
I think this makes sense, and when all use cases are covered then can register a socket alias validation, just like how color one works.
Okay so I was working to create a new UDS validate tag. I ran into some interesting questions.
// IsSocket checks if the given file path exists and is a Unix domain socket
func IsSocket(filePath string) error {
stats, err := os.Stat(filePath)
if err != nil {
return fmt.Errorf("check file existence: %w", err)
}
if stats.Mode().Type() != fs.ModeSocket {
return fmt.Errorf("not socket file: %v", filePath)
}
return nil
}
This checks if the file exists at that path and is a valid socket file.
But this fails for certain scenarios like
- Some servers create the socket at runtime. So although there is a valid path since no file exists this code will say its an invalid UDS because the file does not exist yet
- I am not sure how to validate abstract sockets using this approach (example - "@socket1")
However I still think we should stick with the original approach of checking if the file exists and it it is a socket. The reason is if we build a rule based validator that checks if the file path is a valid socket file path, that may result in false positives.
For example let us say I give the path as "/tmp/socket1.sock". This is a valid file path but assume I do not actually create a file at this location.
The rule based validator will treat this as valid since its a valid file path but if for some reason I never end up creating the file at this path then this would be a false positive.
@deankarn @nodivbyzero kindly look into this.
- The
filepathvalidator could be used instead in this case. Assuming it's not an abstract socket, of course. Or anoptionaloption in the tag, which wouldn't fail if the socket path didn't exist at invocation. - This is a much trickier answer; the syntax works for me since that's generally what abstract sockets use. Checking their existence would like wise be easy. The
optionaloption above, however, would likely need to lead to validation against the name itself. (I think as long as it's a POSIX "portable filename" match, it's a valid abstract socket name.)
@nf-brentsaner Thanks for the insight. I absolutely agree with you. Based on our discussion I propose we add 2 validation tags
- uds_path -> This just checks if the file path is a valid UDS socket path (both normal and abstract) using certain rules. This does not actually check if the file exists. For abstract paths it will just check if it is a POSIX "portable filename".
- uds_exists -> This checks if the file actually exists at the given file path and if it is a socket. Obviously 'uds_exists' will not work with abstract sockets. It is meant only for filesystem paths.
Also we could use filepath to validate if its a valid filepath but we also need to check if the file path is less than equal to 107 characters in linux and 104 characters on MAC OS X 10.9
Source: https://unix.stackexchange.com/questions/367008/why-is-socket-path-length-limited-to-a-hundred-chars
@deankarn @nodivbyzero Kindly let me know if I missed anything.
Hi! I am looking to get my first open source contribution, and I would like to help in this library. It is okey if I craft an MR with the 2 validation tags that @JunaidIslam2105 proposed?
cc: @deankarn as maintainer. (I don't know who to ping)
By all means @kitosforos , PR's are always welcome
@kitosforos Nice!
For some starter hints, at least on Linux the uds_exists is fairly easy but with some caveats.
For abstract sockets, you'd need to read /proc/net/unix.
It can be read as a regular text file, each socket is defined per-line, and (this is the important bit) it also includes the aforementioned "abstract sockets". Its format is described in proc_pid_net(5). This is actually the only way I can think of to test abstract socket existence, aside from actually trying to connect to it.
Only Linux, to my knowledge, has abstract sockets, but they're essentially sockets that only exist in memory/in-kernel - they have no corresponding filesystem path, just a reference name. Their name technically begins with a NUL (0x00), but that's usually replaced with @ in textual/visual/human representation.
For example, an abstract socket in /proc/net/unix looks like this (in this case, this one was created by iscsid, some fields were changed):
01234567890abcdef: 00000000 00000000 00010000 0001 01 01234 @ISCSID_UIP_ABSTRACT_NAMESPACE
You unfortunately can't entirely rely on ONLY parsing this file, as processes can also use relative paths for sockets or may be chrooted. For example, for e.g. Postfix on an AlmaLinux 9.6 system, its postfix sockets have lines like:
01234567890abcdef: 00000000 00000000 00010000 0001 01 01234 private/smtp
Now, its mere existence in this file indicates that the socket DOES exist, but the actual socket file in this case is /var/spool/postfix/private/smtp:
# stat /var/spool/postfix/private/smtp
File: /var/spool/postfix/private/smtp
Size: 0 Blocks: 0 IO Block: 4096 socket
Device: fd00h/64768d Inode: 202450695 Links: 1
Access: (0666/srw-rw-rw-) Uid: ( 89/ postfix) Gid: ( 89/ postfix)
Context: system_u:object_r:postfix_private_t:s0
Access: 2025-10-09 06:20:32.183056177 +0000
Modify: 2025-10-04 04:50:00.473000000 +0000
Change: 2025-10-04 04:50:00.473000000 +0000
Birth: 2025-10-04 04:50:00.473000000 +0000
(And no, the inode field in /proc/net/unix will not correspond to the Inode in the stat, and that's expected).
All of that complicates validation, so I'd recommend the following basic logic:
// ...
if runtime.GOOS == "linux" && strings.HasPrefix(sockpath, "@") {
// read/parse /proc/net/unix and look for sockpath in the last field for each line
} else {
// check the actual path on the filesystem using e.g. `filepath` validator
}
// ...
which should properly work in "expected" ways in all cases.
@nf-brentsaner thanks for the context! Appreciate the effort 🙇🏼♂️
However, I was thinking about this. I am currently developing on a Mac, so it would be difficult for me to test this implementation. I think I have to step back and find another issue.
I am currently developing on a Mac, so it would be difficult for me to test this implementation. I think I have to step back and find another issue.
@kitosforos no need!
https://mac.getutm.app/ (it's free if you download it from their site, 10USD in the App Store otherwise) they have a ton of ready-to-go VM images here: https://mac.getutm.app/gallery/
there's also Parallels Desktop, which is very much NOT free, but has a free trial.
https://www.parallels.com/products/desktop/buy/?full