Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

recvfrom fails where recv works

138 views
Skip to first unread message

Corinna Vinschen

unread,
Sep 22, 2009, 12:10:59 PM9/22/09
to
Hi,


a guy on the Cygwin mailing list found a weird behaviour in a specific
scenario in which calls to recv and WSARecv work fine, but recvfrom and
WSARecvFrom both fail. For the original report, see
http://cygwin.com/ml/cygwin/2009-09/msg00383.html and
http://cygwin.com/ml/cygwin/2009-09/msg00536.html

The simplified scenario is as follows. Consider a process which creates
a socket. Then it starts a child process with handle inheritance, which
connects the socket to a remote machine. When the child process signals
that the connection has been established, the parent process calls send
and recv to send and receive a packet of data.

This works fine if the parent process uses the recv or WSARecv function
to receive the packet.

However, if the process uses recvfrom or WSARecvFrom, the call fails
with WSAEINVAL. It does *not* matter if the name and namelen parameter
are pointing to valid data, or if they are both set to NULL.

This is puzzeling for two reasons. First of all, recvfrom or
WSARecvFrom both work fine if the connect call has been performed by the
parent process itself. Second, recv(s, buf, len, flags) is equivalent
to recvfrom(s, buf, len, flags, NULL, NULL). Same goes for WSARecv vs.
WSARecvFrom.

So, why does recv/WSARecv work but recvfrom/WSARecvFrom doesn't? That
doesn't make sense. Per MSDN, WSAEINVAL means "The socket has not been
bound" in this context. It is as if the recvfrom function tests if the
socket is bound locally, but in the parent process WinSock doesn't know
about that and fails, while the same test is omitted in the recv
function, so the call succeeds.

Note that the equivalent problem does not occur with send/WSASend vs.
sendto/WSASendTo. Only the recv family of functions is affected.

The question is, is this a bug in WinSock or are my expectations flawed?
Last but not least, is there a workaround which allows to use recvfrom
or WSARecvFrom also in this scenario?

Here's a self-contained Win32/WinSock-based testcase, which does not
rely on Cygwin. I built it as a MingW application, like this:

> gcc -mno-cygwin foo.c -o foo -lws2_32

but it should also be no problem to build it with VC++ since it's quite
simple.

For the sake of having a really simple testcase, the child process
signals that the connection worked by its return code to the parent
process. For the actual scenario it does not matter whether or not the
child still runs at the time recv/recvfrom is called.

Assume you compiled it as "foo", as I did, you can test both variations,
once with recv, once with recvfrom:

> foo <== uses recv
connection ok!
768 > 0 <== received 768 bytes, error code 0

> foo from <== uses recvfrom
connection ok!
-1 > 10022 <== recvfrom returned -1, error code WSAEINVAL

=== SNIP ===
#include <stdio.h>
#include <windows.h>
#include <winsock2.h>

int
main (int argc, char **argv)
{
WSADATA wsadata;
int sock_fd;
char cmdline[512];
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD childret;

WSAStartup ((2<<8) | 2, &wsadata);

if (argc > 1 && strcmp (argv[1], "from") != 0)
{
/* child */
sock_fd = atoi (argv[1]);
struct sockaddr_in sa;
sa.sin_addr.s_addr = inet_addr ("74.125.45.100"); // google ip address
sa.sin_port = htons (80);
sa.sin_family = AF_INET;
return connect (sock_fd, (struct sockaddr *) &sa, sizeof sa) ? 1 : 0;
}

sock_fd = socket (AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
fprintf (stderr, "socket failed: %lu\n", WSAGetLastError ());
return 1;
}

sprintf (cmdline, "%s %d", argv[0], sock_fd);
memset (&si, 0, sizeof si);
si.cb = sizeof si;
if (!CreateProcess (argv[0], cmdline, NULL, NULL, TRUE, 0, NULL, NULL,
&si, &pi))
{
fprintf (stderr, "CreateProcess failed: %lu\n", GetLastError ());
return 1;
}

WaitForSingleObject (pi.hProcess, INFINITE);
GetExitCodeProcess (pi.hProcess, &childret);

CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);

if (childret == 0)
{
char httpresult[4096];
int ret;

printf ("connection ok !\r\n");
ret =
send (sock_fd, "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n",
strlen ("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n"), 0);
if (ret < 0)
fprintf (stderr, "send failed: %lu\n", WSAGetLastError ());
else
{
WSASetLastError (0);
if (argc > 1 && strcmp (argv[1], "from") == 0)
ret = recvfrom (sock_fd, httpresult, 4096, 0, NULL, NULL);
else
ret = recv (sock_fd, httpresult, 4096, 0);
printf ("%i > %lu\n", ret, WSAGetLastError ());
}
}
else
printf ("connection could not be etablished\n");

WSACleanup ();
return 0;
}
=== SNAP ===


Thanks in advance,
Corinna

--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat

0 new messages