Skip to content
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

Unable to use recvfrom() on TCP socket #1144

Closed
thib-ack opened this issue Oct 27, 2019 · 4 comments · Fixed by #1145
Closed

Unable to use recvfrom() on TCP socket #1144

thib-ack opened this issue Oct 27, 2019 · 4 comments · Fixed by #1145
Assignees

Comments

@thib-ack
Copy link
Contributor

thib-ack commented Oct 27, 2019

Hello,

I try to use the socket::recvfrom() function on a TCP socket and I get an error ENOTCONN.

I agree I could use the socket::recv() function but the docs says using recvfrom is allowed
https://linux.die.net/man/2/recvfrom

The recvfrom() [...] may be used to receive data on a socket whether or not it is connection-oriented.

I believe the issue comes from the function sockaddr_storage_to_addr() :
https://github.com/nix-rust/nix/blob/master/src/sys/socket/mod.rs#L1243

    if len < mem::size_of_val(&addr.ss_family) {
        return Err(Error::Sys(Errno::ENOTCONN));
    }

Here is an example of the problem. If you connect to this server with a tcp client (telnet for ex). You will get errors: Read err ENOTCONN: Transport endpoint is not connected

use nix::sys::socket;
use nix::sys::socket::{AddressFamily, SockType, SockFlag, InetAddr, SockAddr, IpAddr};

fn main() {
    let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 6678);

    let tcp_listener = socket::socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
    socket::bind(tcp_listener, &SockAddr::new_inet(localhost)).unwrap();
    socket::listen(tcp_listener, 1).unwrap();

    loop {
        let client_socket = socket::accept(tcp_listener).unwrap();

        loop {
            let mut buf: [u8; 256] = [0; 256];

            match socket::recvfrom(client_socket, &mut buf[..]) {
                Ok((len, from)) => {
                    buf[len] = 0;
                    let message = String::from_utf8_lossy(&buf);
                    println!("Message from {} of len {}: {}", from, len, message);
                },
                Err(e) => {
                    println!("Read err {}", e);
                }
            }
        }
    }
}

On the other hand, with this C code, it works as expected

#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>

#define READ_SIZE 200

int main() {
    struct sockaddr_in localhost;
    memset(&localhost, 0, sizeof(struct sockaddr_in));
    localhost.sin_family = AF_INET;
    localhost.sin_port = htons(6677);
    inet_pton(AF_INET, "127.0.0.1", &(localhost.sin_addr));

    int tcp_listener = socket(AF_INET, SOCK_STREAM, 0);

    bind(tcp_listener, (struct sockaddr *) &localhost, sizeof(struct sockaddr_in));
    listen(tcp_listener, 1);

    while (1) {

        int client_socket = accept(tcp_listener, NULL, NULL);

        while(1) {
            char read_buffer[READ_SIZE + 1];
            int bytes_read = recvfrom(client_socket, read_buffer, READ_SIZE, 0, NULL, NULL);
            read_buffer[bytes_read] = '\0';
            printf("Message of len %d: %s\n", bytes_read, read_buffer);
        }
    }

    return 0;
}

Thanks,
Thibaut

@asomers
Copy link
Member

asomers commented Oct 27, 2019

You're correct, and it's somewhat shocking that nobody has noticed that bug for more than four years. Maybe most people don't use recvfrom that way because the standard library works well enough? In any case, I'll fix it.

@asomers asomers self-assigned this Oct 27, 2019
asomers added a commit to asomers/nix that referenced this issue Oct 27, 2019
recvfrom(2) only returns the sender's address for protocols that provide
it.  Usually, that means it returns the sender's address for datagram
sockets but not for stream sockets.

Fixes nix-rust#1144
asomers added a commit to asomers/nix that referenced this issue Oct 27, 2019
recvfrom(2) only returns the sender's address for protocols that provide
it.  Usually, that means it returns the sender's address for datagram
sockets but not for stream sockets.

Fixes nix-rust#1144
@thib-ack
Copy link
Contributor Author

Amazing, thank you !

@asomers
Copy link
Member

asomers commented Oct 28, 2019

Could you please check PR #1145 and tell me if that change works for you?

@thib-ack
Copy link
Contributor Author

thib-ack commented Oct 28, 2019

Hi,

It works like a charm with your fix 👍
The returned Option<SockAddr> behave as expected, with Some(SockAddr) in case of UDP socket and None in case of TCP socket.

Thanks,

bors bot added a commit that referenced this issue Oct 29, 2019
1145: Fix sys::socket::recvfrom for TCP sockets r=asomers a=asomers

recvfrom(2) only returns the sender's address for protocols that provide
it.  Usually, that means it returns the sender's address for datagram
sockets but not for stream sockets.

Fixes #1144

Co-authored-by: Alan Somers <asomers@gmail.com>
@bors bors bot closed this as completed in e10e537 Oct 29, 2019
@bors bors bot closed this as completed in #1145 Oct 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants