calls to socket() fail when calling getaddrinfo() with IPPROTO_TCP

Corinna Vinschen corinna-cygwin@cygwin.com
Fri Jul 30 09:34:34 GMT 2021


Hi John,

On Jul 29 22:41, John Scott via Cygwin wrote:
> Hi,
> 
> I was wondering why my daytime server doesn't work when built for
> Cygwin, and I have been able to narrow it down to this reproducible
> test case:
> [...]
> This code fails with "Failed to create socket: Invalid argument". Does
> anyone have an idea why this happens, given that the arguments to
> socket() come directly from the call to getaddrinfo()?

Welcome to the Windows implementation of getaddrinfo.

Assuming you call getaddrinfo (NULL, "daytime", NULL, &result), you get
the following return from Linux:

  family: 2 socktype 1 protocol 6	AF_INET,  STREAM, TCP
  family: 2 socktype 2 protocol 17	AF_INET,  DGRAM,  UCP
  family: 10 socktype 1 protocol 6	AF_INET6, STREAM, TCP
  family: 10 socktype 2 protocol 17	AF_INET6, DGRAM,  UCP

The same call on Windows returns:

  family: 23 socktype 0 protocol 0	AF_INET6, any, any
  family: 2 socktype 0 protocol 0	AF_INET,  any, any

If the service supports both, TCP and UDP, then socktype and protocol
are always 0 on Windows.  The restriction from the hints parameter
*only* restricts the output for that very field!

I.e., your hints with .ai_protocol = IPPROTO_TCP only restricts the
output of the ai_protocol field, not the output of the ai_socktype
field:

  family: 23 socktype 0 protocol 6	AF_INET6, any, TCP
  family: 2 socktype 0 protocol 6	AF_INET,  any, TCP

On Linux you get the less surprising result

  family: 2 socktype 1 protocol 6	AF_INET,  STREAM, TCP
  family: 10 socktype 1 protocol 6	AF_INET6, STREAM, TCP

> Remarkably,
> changing the service from "daytime" to "http" seems to fix it, which
> seems quite strange.

Yeah, that's a bad joke as well.  The reason is that the http service is
defined for TCP only.  Not for UDP.  As a result, Windows' getaddrinfo
suddenly returns a valid ai_socktype field:

  family: 23 socktype 1 protocol 6	AF_INET6, STREAM, TCP
  family: 2 socktype 1 protocol 6	AF_INET,  STREAM, TCP

Cygwin implements a shallow (~300 lines) wrapper over the WinSock
GetAddrInfoW function and otherwise relies on the values returned by the
OS.  However, it already duplicates the returned list to self-allocated
memory, which is required for fork(2) semantics.  It should be possible
to improve the wrapper to duplicate entries with socktype and protocol
0-entries, but that would be in the next Cygwin version earliest.

Back to your problem.  For the time being, you can easily "fix" your
code by changing the hints:

- 	int s = getaddrinfo(NULL, "daytime", &(const struct addrinfo){.ai_flags = AI_PASSIVE, .ai_protocol = IPPROTO_TCP}, &res);
+ 	int s = getaddrinfo(NULL, "daytime", &(const struct addrinfo){.ai_flags = AI_PASSIVE, .ai_socktype = SOCK_STREAM}, &res);

This returns

  family: 23 socktype 1 protocol 0      AF_INET6, STREAM, any
  family: 2 socktype 1 protocol 0       AF_INET,  STREAM, any

The content of the protocol parameter doesn't really matter to socket(2),
so it will work on Cygwin as well as on Linux and others.
  

HTH,
Corinna


More information about the Cygwin mailing list