-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SocketAddr from UnixDatagram recv_from missing the last character (on OS X) #69061
Comments
Wow, can't believe this made it 3.5 years without being noticed! |
And I think this problem also exists in |
I can reproduce this, but weirdly not when sending from Rust code, just Python. |
The problem appears to be that Python sets the socklen_t to just the size up to the last byte of the addr not including the null terminator, and that number gets sent to recvfrom on OSX. I guess we should check if the last byte is a null before ignoring it. |
Hmm, so it is Python's problem? Because in C, |
Test with C client: #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stddef.h>
int main(int argc, char *argv[])
{
int ret = 0;
int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (fd < 0)
{
perror("socket");
return EXIT_FAILURE;
}
struct sockaddr_un baddr;
memset(&baddr, 0, sizeof(baddr));
const char *BIND_PATH = "/tmp/shadowsocks-manager-c.sock";
baddr.sun_family = AF_UNIX;
strcpy(baddr.sun_path, BIND_PATH);
unlink(BIND_PATH);
if ((ret = bind(fd, (struct sockaddr *)&baddr, sizeof(baddr))) < 0)
{
perror("bind");
close(fd);
return EXIT_FAILURE;
}
const char buf[] = "ping";
struct sockaddr_un caddr;
memset(&caddr, 0, sizeof(caddr));
caddr.sun_family = AF_UNIX;
const char *SVR_PATH = "/tmp/shadowsocks-manager.sock";
strcpy(caddr.sun_path, SVR_PATH);
caddr.sun_len = SUN_LEN(&caddr);
socklen_t caddr_len = offsetof(struct sockaddr_un, sun_path) + strlen(caddr.sun_path) + 1;
ssize_t rcount = sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&caddr, caddr_len);
if (rcount < 0)
{
perror("sendto");
close(fd);
return EXIT_FAILURE;
}
return 0;
} And Rust server output:
|
socklen_t caddr_len = offsetof(struct sockaddr_un, sun_path) + strlen(caddr.sun_path) + 1; Could be replaced by socklen_t caddr_len = SUN_LEN(&caddr); or socklen_t caddr_len = caddr.sun_len; They have the same value. |
This is also an issue on FreeBSD and NetBSD. (See the first ~15 lines of the output of this test program ran on FreeBSD, macOS and Linux.) Both Linux and FreeBSD supports socket paths of length On Linux I think that std should support |
I think the relevant bit of unix(7) may be this:
So I think the correct behavior is to only chop off the last byte of sun_path if it is actually a zero byte. |
It have been quite a long time, what's the status of this issue? @sfackler |
I propose 2 simple fixes to rust/library/std/src/os/unix/net/addr.rs Lines 199 to 214 in 24bdc6d
fn address(&self) -> AddressKind<'_> {
let len = self.len as usize - sun_path_offset(&self.addr);
let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
// macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
if len == 0
|| (cfg!(not(any(target_os = "linux", target_os = "android")))
&& self.addr.sun_path[0] == 0)
{
AddressKind::Unnamed
} else if self.addr.sun_path[0] == 0 {
AddressKind::Abstract(&path[1..len])
} else {
cfg_if! {
if #[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd"))] {
// BSD-like systems may not includes the last '\0' in length,
// because they have a sun_len as the length of sun_path
let sun_len = self.addr.sun_len;
} else {
// Trim the last '\0'
let mut sun_len = len;
while sun_len > 0 && path[sun_len] == 0 {
sun_len -= 1;
}
}
}
AddressKind::Pathname(OsStr::from_bytes(&path[..sun_len]).as_ref())
}
}
// Trim the last '\0'
let mut sun_len = len;
while sun_len > 0 && path[sun_len] == 0 {
sun_len -= 1;
} I personally prefer the first solution. What do you think? I could make a PR for this. |
If |
So @tormol prefers the 1st solution? |
SocketAddr of Unix Sockets use sun_len as path's length in BSD-like OSes - fixes rust-lang#69061
Made a PR to cpython instead: python/cpython#26866 |
I tried this code:
Send request with a Python test script:
I expected to see this happen:
Instead, this happened:
Missing the last character.
Meta
rustc --version --verbose
:Problem
On OS X,
sockaddr_un
is defined aswhich has one more byte
sun_len
beforesun_path
than Linux's definition:This issue also exists in Tokio (Mio), too. tokio-rs/tokio#2230
The text was updated successfully, but these errors were encountered: