sockaddr_in and bind()

Theo Markettos theo at markettos.org.uk
Thu Jan 22 15:01:26 PST 2004


I think I've tracked down the bug that's been bugging me for a while.  The
problem is that a certain bit of code that opens a socket for incoming
connections fails under UnixLib, but not under Unix or TCPIPLibs.  I think
I've tracked it down to the fact that UnixLib doesn't clear the zero part of
sockaddr_ins, nor does it adjust the Internet 5 length field correctly.  It
seems like Internet 5.06 is falling over if the zero part of a sockaddr_in
is non-zero when binding to a local address.

I'm not sure what the best solution is.  TCPIPLibs doesn't appear to do any
special processing in its socket calls - AFAICS from the binary it just
calls the SWIs directly.  I smell something fishy here because there doesn't
seem to be any difference between the way the two libraries operate, though
getting a zero initialised sockaddr_in may just be a side effect of
something else in TCPIPLibs.

My test code is below.  I'm using the gcc 3.3test2 snapshot from a few weeks
ago and building with Norcroft (I've rebuilt UnixLib with Norcroft). 
Command lines used to build it:

UnixLib:
cc    -I@ -I^ -I^.charset -throwback -jUnix: -D__FAVOR_BSD
-D__UNIXLIB_INTERNALS -lUnix:o.unixlib -c bindtest.c
cc  -o bindtest -I@ -I^ -I^.charset -throwback -jUnix: -D__FAVOR_BSD
-D__UNIXLIB_INTERNALS -lUnix:o.unixlib bindtest.o 

TCPIPLibs:
cc -o bindtestn -ITCPIPLibs: -DINADDR_LOOPBACK=0x7f000001
TCPIPLibs:o.socklib5 bindtest.c TCPIPLibs:o.inetlib

without the memset line the UnixLib code outputs:
*bindtest
Open socket 3
Real socket 12
setsockopt
bound 0 at 100007f port d204
a, 2, 4, d2, 7f, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
listening
successful

whilst with it:
*bindtest
Open socket 3
Real socket 13
setsockopt
bound -1 at 100007f port d204
a, 2, 4, d2, 7f, 0, 0, 1, 0, 0, 0, 0, a8, b0, c1, 
Error 49

Without it, TCPIPLibs gives:
*bindtestn
Open socket 12
setsockopt
bound 0 at 100007f port d204
a, 2, 4, d2, 7f, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
listening
successful

(I use Director to check that the socket hasn't been left open between
calls, which it hasn't.  The same happens the other way round)


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>

#include <errno.h>

#ifdef __UNIXLIB_INTERNALS
#include <unixlib/unix.h>
#include <sys/socket.h>
#include <unixlib/fd.h>
#include <unixlib/local.h>
extern struct proc *__u;	/* current process */
#endif

void sk_newlistener(char *srcaddr, int port, int local_host_only)
{
    int s;
    struct sockaddr_in a;
    int err;
    int retcode;
    int on = 1;

/* Include this and it works on UnixLib */
    //memset(&a,'\0',sizeof(struct sockaddr_in));
    s = socket(AF_INET, SOCK_STREAM, 0);
    printf("Open socket %d\n",s);
#ifdef __UNIXLIB_INTERNALS
    printf("Real socket %d\n",__u->fd[s].handle);
#endif
    if (s < 0) return ;
    printf("setsockopt\n");
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
    {
	int got_addr = 0;
	a.sin_family = AF_INET;

	if (srcaddr) {
	    a.sin_addr.s_addr = inet_addr(srcaddr);
	    if (a.sin_addr.s_addr != INADDR_NONE) {
		/* Override localhost_only with specified listen addr. */
		got_addr = 1;
	    }
	}

	if (!got_addr) {
	    if (local_host_only)
		a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	    else
		a.sin_addr.s_addr = htonl(INADDR_ANY);
	}

	a.sin_port = htons((short)port);
	a.sin_len = 0xa;
	retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
	printf("bound %d at %x port %x\n",retcode,a.sin_addr.s_addr,a.sin_port);
	{
	  int i=0;
	  for (i=0; i<15; i++) printf("%x, ",((char *) &a)[i]);
	  printf("\n");
	}
    }

    if (retcode >= 0) {
	err = 0;
    } else {
        printf("Error %d\n",errno);
	err = errno;
    }

    if (err) {
	return ;
    }

    printf("listening\n");
    if (listen(s, SOMAXCONN) < 0) {
        printf("gave up listening\n");
	return ;
    }

    printf("successful\n");
    return ;
}

int main(void)
{
  sk_newlistener((char *) 0, 1234, 1);

}

In fact I can verify this in BASIC:
>DIM m% 100
>SYS "Socket_Creat",2,1,0 TO s%
>!m%=1
>SYS "Socket_Setsockopt",s%,&ffff,4,m%,4
>!m%=&a
>m%?1=2
>m%?2=4
>m%?3=&a0
>m%?4=&7f
>m%?5=0
>m%?6=0
>m%?7=1
>m%!8=0
>m%?12=&18
>m%?13=&b0
>m%?14=&c1
>SYS "XSocket_Bind",s%,m%,16 TO v%
>P.!v%
        49
>SYS "OS_Write0",v%+4
Resource temporarily unavailable
>m%!12=0
>SYS "Socket_Bind",s%,m%,16
>



Any thoughts, or should I take this to csa.programmer?

Thanks,
Theo




More information about the gcc mailing list