sendto() Failing: errno = EINVAL on Linux, errno = 0 on SunOS

This is a discussion on sendto() Failing: errno = EINVAL on Linux, errno = 0 on SunOS within the C Programming forums, part of the General Programming Boards category; I'm writing a simple UDP server, and right now as placeholder code I'm simply echoing back input. But sendto() is ...

  1. #1
    Registered User
    Join Date
    Oct 2010
    Posts
    11

    sendto() Failing: errno = EINVAL on Linux, errno = 0 on SunOS

    I'm writing a simple UDP server, and right now as placeholder code I'm simply echoing back input. But sendto() is returning -1 and failing to send. On Debian GNU/Linux with GCC 4.4, this happens once and errno is set to EINVAL, then subsequent messages are sent successfully without errors. On SunOS 5.10 (a.k.a. Solaris 10) with GCC 3.4.6, sendto() fails to send, returns -1, and sets errno to 0 on every attempt. I've also tried to do this on Windows, but Winsock is a pain to work with and is throwing some other weird poorly documented error on sendto(), so I gave up on that.

    Here is the full function in which this error occurs; the sendto() call is near the end.
    Code:
    void *server_thread_main(void *argv) {
    	
    	struct protoent *udp_protocol;
    	int socket_in;
    	struct sockaddr_in local_addr, remote_addr;
    	char recv_buf[128];
    	socklen_t recv_len;
    	uint16_t local_port = 9000;
    	int recv_buf_len;
    	
    	if ((udp_protocol = getprotobyname("udp")) == NULL)
    		server_error("Unable to read UDP protocol information from database.");
    	
    	if ((socket_in = socket(AF_INET, SOCK_DGRAM, udp_protocol->p_proto)) < 0)
    		server_error("Unable to create socket.");
    	
    	// ARPANET family: Internet.
    	local_addr.sin_family = AF_INET;
    	// Accept datagrams coming to any address.
    	local_addr.sin_addr.s_addr = INADDR_ANY;
    	// Accept datagrams coming to specified port (in network byte order!).
    	local_addr.sin_port = htons(local_port);
    	
    	if (bind(socket_in, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
    		server_error("Unable to bind to address.");
    	
    	while (true) {
    		
    		recv_buf_len = recvfrom(socket_in, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) &remote_addr, &recv_len);
    		if (recv_buf_len < 0)
    			server_error("Cannot read socket.");
    		
    		// Someone sent a datagram. We r dumb. Should do better than this.
    		puts("Received datagram.");
    		
    		// We copycats. Very annoying and dumb. This should be different thread, but we dumb.
    		// THIS FAILS.
    		if (sendto(socket_in, recv_buf, recv_buf_len, 0, (struct sockaddr *) &remote_addr, recv_len) < 0)
    			server_error("Cannot send to client.");
    		printf("recv_buf: '%s'\nrecv_len: %d\nrecv_buf_len: %d\n", recv_buf, recv_len, recv_buf_len);
    		printf("sizeof(struct sockaddr_storage): %ld\n", sizeof(struct sockaddr_storage));
    		
    	}
    	
    }
    I run this program and send it the message "test" through netcat. This is the output of my program on my Debian system:
    Code:
    Received datagram.
    ERROR:  Cannot send to client.
            Invalid argument (22)
    recv_buf: 'test
    '
    recv_len: 16
    recv_buf_len: 5
    sizeof(struct sockaddr_storage): 128
    This is the output on the SunOS system:
    Code:
    Received datagram.
    ERROR:  Cannot send to client.
            Error 0 (0)
    recv_buf: 'test
    '
    recv_len: 0
    recv_buf_len: 5
    sizeof(struct sockaddr_storage): 256
    I've spent days trying to figure out this odd error, searching online, trying different arguments, etc. I've even tried looking in the Linux kernel source to try to find what could possibly be setting this errno, but to no avail. Does anyone have any idea why this function call is failing? Any help would be appreciated.

  2. #2
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    I f you look in the networking section you'll discover I've been going through the same dilemma on a project I'm writing... Winsock or Sockets both need you to keep your buffers --address and data-- valid until the data is actually sent.

    The thing is that sendto does not return when the data has actually crossed wires. It returns after placing pointers to the data in a network buffer that will actually send the data the next moment your system multitasks... This is important: It does not make copies of your address and data, it merely (stupidly?) stores the pointers you hand it. In my case (in the other thread) I got messed over on this because I was sending a datagram then deleting the address, naively thinking that sendto had actually transmitted the data.

    In your case it appears your system never gets to multitask and simply loops back to receive, invalidating the address and data buffers before the data is sent.

    I don't know if select() will help you in this case but you may want to put it before your recvfrom call looking for incoming data.... I can't promise but if you don't call recv before there is actual data, perhaps the background networking will have time to clear it's buffers.

  3. #3
    Registered User
    Join Date
    Oct 2010
    Posts
    11
    Thanks for the heads up on that behavior of sendto with buffers; time-permitting, I'll look into that myself and throw in a workaround for that.

    But on my GNU/Linux system, sendto() fails on the first attempt (not just fails to send and returns okay, it returns -1 and sets errno to EINVAL), then succeeds on all following attempts. So it's not a problem in the loop.

    EDIT:

    I printed out remote_addr between recvfrom() and sendto() and found that remote_addr.sin_addr.s_addr and remote_addr.sin_port were both set to 0 after the first recvfrom() and correct after each following recvfrom(). I tried adding sleep() calls before and after recvfrom() with no effect. So here is my new while loop:

    Code:
    	while (true) {
    		
    		recv_buf_len = recvfrom(socket_in, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) &remote_addr, &recv_len);
    		if (recv_buf_len < 0)
    			server_error("Cannot read socket.");
    		
    		// Someone sent a datagram. We r dumb. Should do better than this.
    		printf("Received datagram from \"udp://%s:%u\": \"%s\".\n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port), recv_buf);
    		
    		// We copycats. Very annoying and dumb. This should be different thread, but we dumb.
    		// THIS FAILS.
    		if (sendto(socket_in, recv_buf, recv_buf_len, 0, (struct sockaddr *) &remote_addr, recv_len) < 0)
    			server_error("Cannot send to client.");
    //		printf("recv_buf: '%s'\nrecv_len: %d\nrecv_buf_len: %d\n", recv_buf, recv_len, recv_buf_len);
    //		printf("sizeof(struct sockaddr_storage): %ld\n", sizeof(struct sockaddr_storage));
    		
    	}
    And output on GNU/Linux:
    Code:
    Received datagram from "udp://0.0.0.0:0": "foo
    ".
    ERROR:  Cannot send to client.
            Invalid argument (22)
    Received datagram from "udp://127.0.0.1:38122": "bar
    ".
    And output on SunOS:
    Code:
    Received datagram from "udp://0.0.0.0:0": "foo
    ".
    ERROR:  Cannot send to client.
            Error 0 (0)
    Received datagram from "udp://0.0.0.0:0": "bar
    ".
    ERROR:  Cannot send to client.
            Error 0 (0)
    Last edited by PehJota; 10-06-2010 at 03:05 AM.

  4. #4
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    I'm guessing you've vetted the various parameters of recvfrom and sendto to make sure you're passing them correctly....

    Have you tested sendto with known data... Sending to a specific address, with a known message? This would tell you something not entirely obvious... it could be that sendto is failing because of errors in the recvfrom call... You have them very closely linked so it should not suprise you that one can mess up the other...

    Also you probably should try select() before recv... it's not that hard to set up...

    Code:
    // just above the loop...
        FD_SET    st;           
        st.fd_count    = 1;                   // 1 socket
        st.fd_array[0] = hSocket;       //  socket handle
     
    // inside the loop....
            select(1,&st,NULL,NULL,NULL);
            recvfrom...
    Select is a blocking call, same as recvfrom but it doesn't tie up the socket. It's just monitoring socket status. IOW... you may not be sending because recv is tying up the port.

    Also the last parameter in recvfrom &recv_len needs to be preset to the size of your sockaddr struct before each call to recvfrom. It may be altered by recvfrom so in sendto I would just use sizeof() to set the sockaddr size.
    Last edited by CommonTater; 10-06-2010 at 04:20 AM.

  5. #5
    a_capitalist_story
    Join Date
    Dec 2007
    Posts
    2,650
    errno is set by many functions. If you need to use/check errno, you should save it to a separate variable immediately after the error you're experiencing and before making any further system calls, otherwise it can/will change on you.

    From the man page:
    The value of errno shall be defined only after a call to a function for which it is explicitly stated to be set and until it is changed by the next function call or if the application assigns it a value.

  6. #6
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,494
    > socklen_t recv_len;
    This isnīt initialised on the first call to recvfrom

    recvfrom
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  7. #7
    Registered User
    Join Date
    Oct 2010
    Posts
    11
    Quote Originally Posted by CommonTater View Post
    Also the last parameter in recvfrom &recv_len needs to be preset to the size of your sockaddr struct before each call to recvfrom. It may be altered by recvfrom so in sendto I would just use sizeof() to set the sockaddr size.
    Quote Originally Posted by Salem View Post
    > socklen_t recv_len;
    This isnīt initialised on the first call to recvfrom

    recvfrom
    Yes, that was my problem. I fixed that, and now my code is working on GNU/Linux, SunOS, and MS Windows. Thanks!

    Quote Originally Posted by rags_to_riches View Post
    errno is set by many functions. If you need to use/check errno, you should save it to a separate variable immediately after the error you're experiencing and before making any further system calls, otherwise it can/will change on you.
    I'm getting a value from errno in my server_error() function, and no other library functions are called between the error and the errno retrieval. And SUS/POSIX requires errno to be thread-safe, and most/all Unix-like platforms respect that (otherwise, getting errno in a multithreaded application even immediately after the error would be unsafe). So I think my use of errno (as follows) is then safe, correct?
    Code:
    void server_error(const char *message) {
    	
    #if defined PLATFORM_UNIX
    	fprintf(stderr, "ERROR:  %s\n        %s (%d)\n", message, strerror(errno), errno);
    #elif defined PLATFORM_WINDOWS
    	fprintf(stderr, "ERROR:  %s\n        %d\n", message, WSAGetLastError());
    #endif
    	
    	/*
    		Why does Windows not use errno as standards dictate? The MSDN says it's not thread-safe.
    		But the Single Unix Specification (and thus POSIX) says that errno is in fact required to
    		be thread-safe. Oops.
    	*/
    	
    //	server_exit(1);
    	
    }
    Last edited by PehJota; 10-06-2010 at 01:11 PM.

  8. #8
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,239
    Quote Originally Posted by rags_to_riches View Post
    errno is set by many functions. If you need to use/check errno, you should save it to a separate variable immediately after the error you're experiencing and before making any further system calls, otherwise it can/will change on you.
    True, but no function will ever set errno to 0. The only way errno is 0 is if it was 0 before the call already and the function did not set it.

    Also, the sendto() most decidedly does NOT require that you keep the buffer around until some indeterminable point in the future. After sendto() has returned, the data is safely in some buffer somewhere for transmission.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by brewbuck View Post
    Also, the sendto() most decidedly does NOT require that you keep the buffer around until some indeterminable point in the future. After sendto() has returned, the data is safely in some buffer somewhere for transmission.
    Brewbuck... that's what I thought too... but not so.

    Your data pointer and sockaddr pointers do have to be maintained until the data is actually sent.

    See my thread in the networking forum, where I tripped over exactly this issue. My data buffer, being composed in global memory was safe enough but the sockaddr at the pointer passed into the function was being deleted by a garbage collector.

    My messages were not being sent because I naively thought sendto() would not return until the data was either safely buffered or actually sent, so I went on to houseclean the memory right after sending. The messages ended up not being sent...

    I trapped the exact condition by commenting out the code releasing the memory and suddenly messages were being sent. Reactivate the code... no send. Comment it out... sent. And... no error messages. sendto was satisfied it's job was done... even though the operation failed.

    I'm guessing this isn't a well known problem because it would be fairly rare that someone would send a datagram and microseconds later delete the sockaddr. I'm guessing the general practice would be more like the rest of my project... the sockaddr is a variable that sits in memory until re-used, so it remains valid until the next call to sendto().

    You might say "the operation was a complete success but the patient died anyway."

  10. #10
    a_capitalist_story
    Join Date
    Dec 2007
    Posts
    2,650
    Quote Originally Posted by brewbuck View Post
    True, but no function will ever set errno to 0. The only way errno is 0 is if it was 0 before the call already and the function did not set it.
    All right! I did not know that...I love it when I learn something new

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. A table for errno values by linux sockets?
    By hardi in forum Networking/Device Communication
    Replies: 2
    Last Post: 12-20-2006, 01:10 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21