Discussion:
accept() and getpeername() return 0.0.0.0
(too old to reply)
Mike King
2007-02-27 03:55:09 UTC
Permalink
I think I'm dealing with a bug in a library or some weird problem. The
following code compiles and works as I'd expect on Linux and cygwin, but on
the machine that matters (HP PA64, HPUX 11.11) it identifies the peer as
0.0.0.0 in both the accept() and getpeername() return vals, also identifies
the peer port as 0. I'm using gcc 4.1.2, downloaded from HP's site,
compiling with 'gcc sockprob.c -o sockprob', then running it with
'./sockprob.c MY.I.P.ADD 7890', then connect to the server socket with
'telnet MY.I.P.ADD 7890'. Cygwin and Linux show the ip and port of the
connector, but HPUX is showing 0.0.0.0. I run a 'netstat -n' and it shows
the correct ip/port for the server and client. I just don't get it. Does
anyone have any advice? Anyone seen stuff like this with gcc on PA64?

Thanks and sorry in advance. I'm not a newsgroup frequent flyer - I just
got flamed for posting the same question on comp.lang.c. If you know of a
good place to ask then please tell me.

-M

#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char* ipstring;
int port1;

void parseargs(int, char**);

int main(int argc, char* args[])
{ struct sockaddr_in adr1; /* address for the server */
int ss1, as1; /* file descriptors for server socket, active
socket */

struct sockaddr_in from1; /* address for peer */
socklen_t from1_len;

parseargs(argc, args);

if((ss1 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{ perror("socket failed");
return 1;
}

memset(&adr1, 0, sizeof(adr1));
adr1.sin_family = AF_INET;
adr1.sin_port = htons(port1);

if(!inet_aton(ipstring, (struct in_addr*)&(adr1.sin_addr)))
{ perror("inet_aton failed");
return 1;
}

if(bind(ss1, (struct sockaddr*) &adr1, sizeof(adr1)))
{ perror("bind failed");
return 1;
}

if(listen(ss1, 1) < 0)
{ perror("listen failed");
return 1;
}

from1_len = sizeof(from1);
memset(&from1, 0, sizeof(from1));
from1.sin_family = AF_INET;

if((as1 = accept(ss1, (struct sockaddr*) &from1, &from1_len)) == -1)
{ perror("Error accepting");
return 1;
}

printf("peer address from accept() is %s %d\n",
inet_ntoa(from1.sin_addr), ntohs(from1.sin_port));


if(getpeername(as1, (struct sockaddr*) &from1, &from1_len))
{ perror("getpeername failed");
return 1;
}

printf("peer add/port from getpeername() is %s %d\n",
inet_ntoa(from1.sin_addr), ntohs(from1.sin_port));

return 0;
}

void parseargs(int argc, char* args[])
{
/* command line is sockprob ip port */
ipstring = args[1];
sscanf(args[2], "%d", &port1);
return;
}
Vishwas Pai
2007-02-27 05:08:08 UTC
Permalink
Post by Mike King
from1_len = sizeof(from1);
memset(&from1, 0, sizeof(from1));
from1.sin_family = AF_INET;
if((as1 = accept(ss1, (struct sockaddr*) &from1, &from1_len)) == -1)
{ perror("Error accepting");
return 1;
}
What is the value of "from1_len" after the accept call ^ ?

Also, could you compile the same with HP C compiler
and see if there is any difference ?
Post by Mike King
printf("peer address from accept() is %s %d\n",
inet_ntoa(from1.sin_addr), ntohs(from1.sin_port));
If "from1_len" is zero (or invalid) above, you need to
re-initialize it here.
Post by Mike King
if(getpeername(as1, (struct sockaddr*) &from1, &from1_len))
{ perror("getpeername failed");
return 1;
}
--vishwas.
Mike King
2007-02-27 04:35:27 UTC
Permalink
Thanks for the reply - from1_len is 16 before and 16 after the accept()
call. I did a printf for each of the 16 bytes and it's all 0 except the
second byte (socket family) which is 2, which is what the sys/socket.h
header says for AF_INET.

Unfortunately, I don't have the full ansi cc product at my disposal, but if
you think that's the solution then I'll pursue that.

Thanks,
Mike
Post by Vishwas Pai
Post by Mike King
from1_len = sizeof(from1);
memset(&from1, 0, sizeof(from1));
from1.sin_family = AF_INET;
if((as1 = accept(ss1, (struct sockaddr*) &from1, &from1_len)) == -1)
{ perror("Error accepting");
return 1;
}
What is the value of "from1_len" after the accept call ^ ?
Also, could you compile the same with HP C compiler
and see if there is any difference ?
Post by Mike King
printf("peer address from accept() is %s %d\n",
inet_ntoa(from1.sin_addr), ntohs(from1.sin_port));
If "from1_len" is zero (or invalid) above, you need to
re-initialize it here.
Post by Mike King
if(getpeername(as1, (struct sockaddr*) &from1, &from1_len))
{ perror("getpeername failed");
return 1;
}
--vishwas.
Vishwas Pai
2007-02-27 09:23:37 UTC
Permalink
Post by Mike King
Thanks for the reply - from1_len is 16 before and 16 after the accept()
call. I did a printf for each of the 16 bytes and it's all 0 except the
second byte (socket family) which is 2, which is what the sys/socket.h
header says for AF_INET.
Unfortunately, I don't have the full ansi cc product at my disposal, but if
you think that's the solution then I'll pursue that.
I don't think you need to test this with HP CC.

You can try installing the latest transport patches
(PHNE_35183 ,which has few dependencies, is the latest
transport patch for 11.11).

--vishwas.
Rick Jones
2007-02-27 19:42:43 UTC
Permalink
A 32-bit compilation of the program appears to work on my PA-RISC
11.11 system:

$ ./tryme 127.0.0.1 12345
peer address from accept() is 127.0.0.1 55063
peer add/port from getpeername() is 127.0.0.1 55063

I do though get the following warnings in the compile:

cc -o tryme tryme.c
cc: "tryme.c", line 54: warning 604: Pointers are not assignment-compatible.
cc: "tryme.c", line 54: warning 563: Argument #3 is not the correct type.
cc: "tryme.c", line 63: warning 604: Pointers are not assignment-compatible.
cc: "tryme.c", line 63: warning 563: Argument #3 is not the correct type.


However a 64-bit compilation does not:

$ ./tryme 127.0.0.1 12345
peer address from accept() is 0.0.0.0 0
peer add/port from getpeername() is 0.0.0.0 0


The 64-bit compilation gave me the following warnings:

cc +DD64 -o tryme tryme.c
cc: "tryme.c", line 54: warning 728: Argument #3 converts long* to int*.
cc: "tryme.c", line 63: warning 728: Argument #3 converts long* to int*.

I believe this all stems from the use of socklen_t, which IMO HP-UX
11.11 has rather well and truly botched. By default there is a
socklen_t available, but the "default" socket calls do not use it,
they use an int. Only the XOPEN (IIRC) socket calls use the
socklen_t, and you only get those calls when setting the apropriate
XOPEN define.

That is all well and good save for the observation that socklen_t is
(IIRC) defined as a size_t, which is then defined as a long. In
64-bit a long is 8 bytes and an int is 4 bytes (hence that warning -
64-bit HP-UX is "LP64" - Longs and Pointers 64-bit - Ints remain
32-bit) and the nature of the things (I'm guessing) is such that the
pointer is to the high-order bytes, which the call will take as the
int and so will believe that the value you are passing-in is actually
0 rather than 16. So, it says you have no space for the from and so
it puts nothing there.

If in your source I replace "socklen_t" with "int" the 64-bit
compilation warnings go away:

cc +DD64 -o tryme tryme.c

and the code behaves correctly:

$ ./tryme 127.0.0.1 12345
peer address from accept() is 127.0.0.1 55080
peer add/port from getpeername() is 127.0.0.1 55080

(the 32-bit compilation warnings go away too)

hth,

rick jones

PS - there is a bunch of backflipping in the netperf configure script
to decide on the "right" value to use for socklen_t to take care of
compilation warnings. You might examine that for ideas if this code
is to be compiled on multiple platforms. http://www.netperf.org/
--
web2.0 n, the dot.com reunion tour...
these opinions are mine, all mine; HP might not want them anyway... :)
feel free to post, OR email to rick.jones2 in hp.com but NOT BOTH...
Mike King
2007-02-28 01:24:47 UTC
Permalink
You nailed it. I replaced socklen_t with int and it works. Funny that with
gcc it actually introduced a compile warning about passing int* when
socklen_t* was expected. So I changed it back to socklen_t, then put a

'from1_len = from1_len << 32;'

line in there to shift the value into the 4 high bytes. It worked.

Thank you very much for your time - you and Vishwas both. I'm also going to
install the arpa patch at our next scheduled downtime.

Sincerely,
Mike
Post by Rick Jones
A 32-bit compilation of the program appears to work on my PA-RISC
$ ./tryme 127.0.0.1 12345
peer address from accept() is 127.0.0.1 55063
peer add/port from getpeername() is 127.0.0.1 55063
cc -o tryme tryme.c
cc: "tryme.c", line 54: warning 604: Pointers are not
assignment-compatible.
cc: "tryme.c", line 54: warning 563: Argument #3 is not the correct type.
cc: "tryme.c", line 63: warning 604: Pointers are not
assignment-compatible.
cc: "tryme.c", line 63: warning 563: Argument #3 is not the correct type.
$ ./tryme 127.0.0.1 12345
peer address from accept() is 0.0.0.0 0
peer add/port from getpeername() is 0.0.0.0 0
cc +DD64 -o tryme tryme.c
cc: "tryme.c", line 54: warning 728: Argument #3 converts long* to int*.
cc: "tryme.c", line 63: warning 728: Argument #3 converts long* to int*.
I believe this all stems from the use of socklen_t, which IMO HP-UX
11.11 has rather well and truly botched. By default there is a
socklen_t available, but the "default" socket calls do not use it,
they use an int. Only the XOPEN (IIRC) socket calls use the
socklen_t, and you only get those calls when setting the apropriate
XOPEN define.
That is all well and good save for the observation that socklen_t is
(IIRC) defined as a size_t, which is then defined as a long. In
64-bit a long is 8 bytes and an int is 4 bytes (hence that warning -
64-bit HP-UX is "LP64" - Longs and Pointers 64-bit - Ints remain
32-bit) and the nature of the things (I'm guessing) is such that the
pointer is to the high-order bytes, which the call will take as the
int and so will believe that the value you are passing-in is actually
0 rather than 16. So, it says you have no space for the from and so
it puts nothing there.
If in your source I replace "socklen_t" with "int" the 64-bit
cc +DD64 -o tryme tryme.c
$ ./tryme 127.0.0.1 12345
peer address from accept() is 127.0.0.1 55080
peer add/port from getpeername() is 127.0.0.1 55080
(the 32-bit compilation warnings go away too)
hth,
rick jones
PS - there is a bunch of backflipping in the netperf configure script
to decide on the "right" value to use for socklen_t to take care of
compilation warnings. You might examine that for ideas if this code
is to be compiled on multiple platforms. http://www.netperf.org/
--
web2.0 n, the dot.com reunion tour...
these opinions are mine, all mine; HP might not want them anyway... :)
feel free to post, OR email to rick.jones2 in hp.com but NOT BOTH...
Rick Jones
2007-02-28 02:53:45 UTC
Permalink
Post by Mike King
You nailed it. I replaced socklen_t with int and it works. Funny
that with gcc it actually introduced a compile warning about passing
int* when socklen_t* was expected.
Odd - unless gcc was doing something funny, an int * should have been
expected.
Post by Mike King
So I changed it back to socklen_t, then put a
'from1_len = from1_len << 32;'
line in there to shift the value into the 4 high bytes. It worked.
Stick with using an int if you can - or switch to the XOPEN socket
style. I'd suggest staying far away from the shift experiment :)

rick jones
--
oxymoron n, Hummer H2 with California Save Our Coasts and Oceans plates
these opinions are mine, all mine; HP might not want them anyway... :)
feel free to post, OR email to rick.jones2 in hp.com but NOT BOTH...
Mike King
2007-02-28 02:29:17 UTC
Permalink
Don't worry, I'm not going to get creative with shifting - I just did that
to confirm your theory. I think gcc is using it's own headers that came
with it because the man pages show that int* is expected as you say. The
gcc headers must have gratuitously changed the int* to socklen_t*, hence the
compile warning. I supposed that's what I get for using a non-native
compiler.

Mike
Post by Rick Jones
Post by Mike King
You nailed it. I replaced socklen_t with int and it works. Funny
that with gcc it actually introduced a compile warning about passing
int* when socklen_t* was expected.
Odd - unless gcc was doing something funny, an int * should have been
expected.
Post by Mike King
So I changed it back to socklen_t, then put a
'from1_len = from1_len << 32;'
line in there to shift the value into the 4 high bytes. It worked.
Stick with using an int if you can - or switch to the XOPEN socket
style. I'd suggest staying far away from the shift experiment :)
rick jones
--
oxymoron n, Hummer H2 with California Save Our Coasts and Oceans plates
these opinions are mine, all mine; HP might not want them anyway... :)
feel free to post, OR email to rick.jones2 in hp.com but NOT BOTH...
Loading...