public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
* Dup'd sockets lose error information
@ 2014-04-23 14:25 qq qq
  2014-04-23 15:38 ` Corinna Vinschen
  0 siblings, 1 reply; 4+ messages in thread
From: qq qq @ 2014-04-23 14:25 UTC (permalink / raw)
  To: cygwin

The following code is a simplified app that was used to test-connect to local ports 55000+ (none of which were actually listening) and received false-positive "connected" results because Cygwin's dup()
for socket causes SO_ERROR to be lost.  Since FD_SETSIZE is only 64 on Cygwin, the app uses dup()'s to lower the descriptors as it checks them for completion.  There is no such problem on Linux.
Also, strangely that Cygwin does not accept sin_addr as 0 to connect locally (and either localhost or local host IP must be stuffed in there, otherwise resulting in the "Cannot assign requested address" error).

$ cat connect.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>

#ifndef MAX_SOCK
#define MAX_SOCK 100
#endif

#ifdef LINUX
#undef  FD_SETSIZE
#define FD_SETSIZE 16
#endif


int main()
{
   static struct {
       struct sockaddr_in sin;
       int                sock;
   } s[MAX_SOCK];
   int i;
   
   for (i = 0;  i < MAX_SOCK;  ++i) {
       s[i].sock = socket(AF_INET, SOCK_STREAM, 0);
       fcntl(s[i].sock, F_SETFL, O_NONBLOCK);
       memset(&s[i].sin, 0, sizeof(s[i].sin));
       s[i].sin.sin_family = AF_INET;
#ifdef BUG2
       s[i].sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
#endif
       s[i].sin.sin_port = htons(55000 + i);
       connect(s[i].sock, (struct sockaddr*) &s[i].sin, sizeof(s[i].sin));
       printf("connecting #%d(%d) to :%hu\n", i, s[i].sock, ntohs(s[i].sin.sin_port));
   }

   for (;;) {
      fd_set wfds, efds;
      int m = 0, v = 0;

      for (i = 0;  i < MAX_SOCK;  ++i) {
          if (s[i].sock < 0)
              continue;

          if (s[i].sock >= FD_SETSIZE) {
              int fd = dup(s[i].sock);
              if (fd < 0)
                  continue;
              if (fd >= FD_SETSIZE) {
                  close(fd);
                  continue;
              }
              close(s[i].sock);
              printf("%d -> %d\n", s[i].sock, fd);
              s[i].sock = fd;
          }

          if (!m) {
              FD_ZERO(&wfds);
              FD_ZERO(&efds);
          }
          
          FD_SET(s[i].sock, &wfds);
          FD_SET(s[i].sock, &efds);
          ++m;
          if (v < s[i].sock)
              v = s[i].sock;
       }
       if (!m)
          break;

       if (select(v + 1, 0, &wfds, &efds, 0) < 0) {
          perror("select");
          return 1;
       }

       for (i = 0;  i < MAX_SOCK;  ++i) {
          int err;
          socklen_t len;
          
          if (s[i].sock < 0  ||  FD_SETSIZE <= s[i].sock)
              continue;
              
          if (FD_ISSET(s[i].sock, &efds))
              v = 1;
          else if (FD_ISSET(s[i].sock, &wfds))
              v = 0;
          else
              continue;
              
          len = sizeof(err);
          if (getsockopt(s[i].sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
              perror("getsockopt");
              return 1;
          }
          if (!err)
              printf("#%d connected to :%hu%s\n", i, ntohs(s[i].sin.sin_port), v ? " w/exception" : "!");
          else
              printf("#%d to :%hu: %s\n", i, ntohs(s[i].sin.sin_port), strerror(err));
          close(s[i].sock);
          s[i].sock = -1;
       }
   }
   return 0;
}

Linux:
$ gcc -Wall -DLINUX connect.c
$ ./a.out
(shows all connection refused with or without -DBUG2 given to the compiler)

Cygwin:
$ gcc -Wall -DBUG2 connect.c
$ ./a.exe
(only shows "connection refused" for fds less than FD_SETSIZE==64, others will be "successfully connected")

$ gcc -Wall connect.c
(shows "cannot assign requested address" errors for fds < 64, all others "successfully connected")

Keep in mind the socket fds are offset by 3 open descriptors used by stdio.

Setting MAX_SOCK to 2 and FD_SETSIZE to 1 on Cygwin clearly shows that it's the dup() call that messes up the socket error state:
the first socket will get the proper "connection refused", and the other -- will be erroneously reported back as "connected" (no error returned).

--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dup'd sockets lose error information
  2014-04-23 14:25 Dup'd sockets lose error information qq qq
@ 2014-04-23 15:38 ` Corinna Vinschen
  2014-04-24 14:14   ` Corinna Vinschen
  0 siblings, 1 reply; 4+ messages in thread
From: Corinna Vinschen @ 2014-04-23 15:38 UTC (permalink / raw)
  To: cygwin

[-- Attachment #1: Type: text/plain, Size: 1065 bytes --]

On Apr 23 16:25, qq qq wrote:
> The following code is a simplified app that was used to test-connect
> to local ports 55000+ (none of which were actually listening) and
> received false-positive "connected" results because Cygwin's dup()
> for socket causes SO_ERROR to be lost.  Since FD_SETSIZE is only 64 on
> Cygwin, the app uses dup()'s to lower the descriptors as it checks
> them for completion.  There is no such problem on Linux.
> Also, strangely that Cygwin does not accept sin_addr as 0 to connect
> locally (and either localhost or local host IP must be stuffed in
> there, otherwise resulting in the "Cannot assign requested address"
> error).

This is Winsock at work.  Cygwin doesn't check the AF_INET address
when calling Winsock's connect, so Winsock's connect itself seems to
have this issue.

As for the SO_ERROR value, I have to check.  Thanks for the testcase.


Corinna

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dup'd sockets lose error information
  2014-04-23 15:38 ` Corinna Vinschen
@ 2014-04-24 14:14   ` Corinna Vinschen
  2014-05-05  9:08     ` Corinna Vinschen
  0 siblings, 1 reply; 4+ messages in thread
From: Corinna Vinschen @ 2014-04-24 14:14 UTC (permalink / raw)
  To: cygwin

[-- Attachment #1: Type: text/plain, Size: 2628 bytes --]

On Apr 23 17:38, Corinna Vinschen wrote:
> On Apr 23 16:25, qq qq wrote:
> > The following code is a simplified app that was used to test-connect
> > to local ports 55000+ (none of which were actually listening) and
> > received false-positive "connected" results because Cygwin's dup()
> > for socket causes SO_ERROR to be lost.  Since FD_SETSIZE is only 64 on
> > Cygwin, the app uses dup()'s to lower the descriptors as it checks
> > them for completion.  There is no such problem on Linux.
> > Also, strangely that Cygwin does not accept sin_addr as 0 to connect
> > locally (and either localhost or local host IP must be stuffed in
> > there, otherwise resulting in the "Cannot assign requested address"
> > error).
> 
> This is Winsock at work.  Cygwin doesn't check the AF_INET address
> when calling Winsock's connect, so Winsock's connect itself seems to
> have this issue.
> 
> As for the SO_ERROR value, I have to check.  Thanks for the testcase.

I've applied a patch which should solve both problems.

Cygwin is using socket events to implement select.  It turned out that,
when using the original socket, the socket event as well as the socket's
SO_ERROR value were set correctly.  However, when calling select with
the dup'ed socket handle, the socket event contained the correct error
code, but the SO_ERROR value wasn't set.  I added code to write back the
error code from the socket event entry to the SO_ERROR socket option and
that seems to do the trick.

As for connecting or sending to INADDR_ANY or in6addr_any, this simply
doesn't work with Winsock.  It just returns WSAEADDRNOTAVAIL.  I now
added a conversion from INADDR_ANY and in6addr_any to INADDR_LOOPBACK
and in6addr_loopback in calls to connect, sendto, and sendmsg to emulate
the Linux behaviour.

Apart from that it turned out that 64 bit Cygwin was suffering from a
definition problem of a couple of Winsock types used in the Cygwin
socket code.  As the (hopefully) only user-visible effect, this led
to a broken address output in /proc/net/if_inet6.  This is fixed now
as well.

As for FD_SETSIZE, it's defined as 64 by default, but the user code
can override the value by defining FD_SETSIZE to another value prior
to including the standard headers.  The underlying code does not
restrict the file descriptor values to < FD_SETSIZE.

Please give the today's developer snapshot from 
http://cygwin.com/snapshots/ a try.


Thanks,
Corinna

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dup'd sockets lose error information
  2014-04-24 14:14   ` Corinna Vinschen
@ 2014-05-05  9:08     ` Corinna Vinschen
  0 siblings, 0 replies; 4+ messages in thread
From: Corinna Vinschen @ 2014-05-05  9:08 UTC (permalink / raw)
  To: cygwin

[-- Attachment #1: Type: text/plain, Size: 2826 bytes --]

qq qq?

Ping?

On Apr 24 16:14, Corinna Vinschen wrote:
> On Apr 23 17:38, Corinna Vinschen wrote:
> > On Apr 23 16:25, qq qq wrote:
> > > The following code is a simplified app that was used to test-connect
> > > to local ports 55000+ (none of which were actually listening) and
> > > received false-positive "connected" results because Cygwin's dup()
> > > for socket causes SO_ERROR to be lost.  Since FD_SETSIZE is only 64 on
> > > Cygwin, the app uses dup()'s to lower the descriptors as it checks
> > > them for completion.  There is no such problem on Linux.
> > > Also, strangely that Cygwin does not accept sin_addr as 0 to connect
> > > locally (and either localhost or local host IP must be stuffed in
> > > there, otherwise resulting in the "Cannot assign requested address"
> > > error).
> > 
> > This is Winsock at work.  Cygwin doesn't check the AF_INET address
> > when calling Winsock's connect, so Winsock's connect itself seems to
> > have this issue.
> > 
> > As for the SO_ERROR value, I have to check.  Thanks for the testcase.
> 
> I've applied a patch which should solve both problems.
> 
> Cygwin is using socket events to implement select.  It turned out that,
> when using the original socket, the socket event as well as the socket's
> SO_ERROR value were set correctly.  However, when calling select with
> the dup'ed socket handle, the socket event contained the correct error
> code, but the SO_ERROR value wasn't set.  I added code to write back the
> error code from the socket event entry to the SO_ERROR socket option and
> that seems to do the trick.
> 
> As for connecting or sending to INADDR_ANY or in6addr_any, this simply
> doesn't work with Winsock.  It just returns WSAEADDRNOTAVAIL.  I now
> added a conversion from INADDR_ANY and in6addr_any to INADDR_LOOPBACK
> and in6addr_loopback in calls to connect, sendto, and sendmsg to emulate
> the Linux behaviour.
> 
> Apart from that it turned out that 64 bit Cygwin was suffering from a
> definition problem of a couple of Winsock types used in the Cygwin
> socket code.  As the (hopefully) only user-visible effect, this led
> to a broken address output in /proc/net/if_inet6.  This is fixed now
> as well.
> 
> As for FD_SETSIZE, it's defined as 64 by default, but the user code
> can override the value by defining FD_SETSIZE to another value prior
> to including the standard headers.  The underlying code does not
> restrict the file descriptor values to < FD_SETSIZE.
> 
> Please give the today's developer snapshot from 
> http://cygwin.com/snapshots/ a try.

Any feedback?  Did you test my patch?


Thanks,
Corinna

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-05-05  9:08 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-23 14:25 Dup'd sockets lose error information qq qq
2014-04-23 15:38 ` Corinna Vinschen
2014-04-24 14:14   ` Corinna Vinschen
2014-05-05  9:08     ` Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).