We had a problem with a client system where some HA4i jobs would end abnormally due to a request to the TCP/IP port being incorrectly formed. We need to be able to find out what IP address was sending the request so that we could determine if the request was a legitimate request or a bad actor trying to get into the system using our responder jobs.
We added code to the responder jobs to try to trap the IP address the request was being sent from, a quick review of the available API’s pushed us towards using the inet_ntop() API and using the address structure that could be filled in via the accept() API. We developed a quick update to the responder jobs that would capture information and send that out to a message queue for analysis. When we checked back in we found that the address was only ever returning 0.0.0.0? This was not correct and we could not figure out why that address was being returned, nothing in the manuals indicated we had done things incorrectly.
We started to debug the API’s and found that the structure was being filled in correctly but the inet_ntop() API was not returning the IP address? We went through the documentation a number of times and felt we were using the correct structures, so something must be wrong with the API’s? We raised a PMR with IBM and while IBM does not help with coding issues we did manage to get their help on why the API was not returning the address correctly. The documentation does not state which address structure needs to be passed into the call to inet_ntop() API, just that it needs to be passed a valid pointer to the source address.
(Input) The pointer to a buffer that contains the numeric form of an IPv4 address if the af parameter is AF_INET, or the numeric form of an IPv6 address if the af parameter is AF_INET6.
We needed to pass it a pointer to the address to be converted? We compared the address buffers we were passing in (the one suggested by the documentation and the one suggested by IBM).
struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
struct sockaddr_in {
uint8_t sin_len;
sa_family_t sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
As you can see they are the same size but the structaddr_in has a u_short prior to where the address is stored. When you pass in the pointer to inet_ntop() using the structaddr.sa_data alignment, the IP address returned is xx.xx.YY.YY. We found out that YY.YY is the start of the IP address and xx.xx relates to the u_short alignment (port), this proves that using structaddr_in.sin_addr is correct, using the structaddr.sa_data is incorrect.
Reading the accept() API it also mentions using a structure structaddr_storage being passed in, we tried this and then used the _ss_align offset, it returned the 0.0.0.0 address and looking at the structure alignment you can see why.
I think the IBM documentation needs to be improved, you should use the structaddr_in offsets and pass in the sin_addr offset to inet_ntop(), anything else will not work. I have added the code below that we used to test the various structures below, it is just test code but it does show how we used the structaddr_in offsets for the call to inet_ntop() that resulted in the correct IP address being returned.
This is the server code, copy into a C source file and compile to a program and then do a call to the program, no parameters required. ie call library/pgm
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#define _1KB 1024
#define _64K _1KB * 64
#define _CHAR_64K "65536"
int main(int argc, char **argv) {
int Server_Port = 3005; // listening port
int listen_sd, accept_sd = 0; // listen socket and accept socket
int rc = 0; // return code
int on = 1; // flag
char ip_addr[17]; // ip address
socklen_t addr_len; // address struct len
struct sockaddr_in addr; // address struct
struct sockaddr_in addr_in; // socket address
memset(&addr_in, 0, sizeof(addr_in));
addr_len = sizeof(addr_in);
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sd < 0) {
printf("%s",strerror(errno));
close(accept_sd);
exit(-1);
}
setsockopt(listen_sd,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
setsockopt(listen_sd,SOL_SOCKET,SO_RCVBUF,_CHAR_64K,sizeof(_CHAR_64K));
setsockopt(listen_sd,SOL_SOCKET,SO_SNDBUF,_CHAR_64K,sizeof(_CHAR_64K));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(Server_Port);
rc = bind(listen_sd,(struct sockaddr *) &addr, sizeof(addr));
rc = listen(listen_sd, 5);
if(rc < 0) {
printf("listen error : %s",strerror(errno));
close(listen_sd);
exit(-1);
}
printf("Ready for client connect(). \n");
accept_sd = accept(listen_sd,(struct sockaddr *)&addr_in, &addr_len);
inet_ntop(AF_INET,(struct sockaddr_in *)&addr_in.sin_addr,ip_addr,16);
printf("Accept address %.16s",ip_addr);
close(listen_sd);
close(accept_sd);
exit(0);
}
Next is the client code, again copy the code to a C source file, compile and put onto a remote system (you can run on the same system but then you are into running multiple sessions etc. and its not really proving much, its the same address) Once its compiled on on the target system you can run the request to call the target server ie call library/pgm ‘remote IP address, or remote address that can be resolved’
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>
#include <resolv.h>
// function Get_Host_Addr()
// Purpose: get the Host address.
// @parms
// string server name
// struct socket address
// int server port
// returns 1 on sucess
int Get_Host_Addr(char *server,
struct sockaddr_in *addr,
int Server_Port) {
struct hostent *hostp; // host struct pointer
addr->sin_family = AF_INET;
addr->sin_port = htons(Server_Port);
if((addr->sin_addr.s_addr = inet_addr(server)) == (unsigned long) INADDR_NONE) {
hostp = gethostbyname(server);
if(hostp == (struct hostent *)NULL) {
printf("%s",hstrerror(h_errno));
return -1;
}
memcpy(&addr->sin_addr,hostp->h_addr,sizeof(addr->sin_addr));
}
return 1;
}
int main(int argc, char** argv) {
int i = 0; // counter
int sd = 0; // socket descriptor
int rc = 0; // return code
int Server_Port = 3005; // listening port
char server[41]; // server address
struct sockaddr_in addr; // address structure
if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket() failed");
exit(-1);
}
memset(&addr,0x00,sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(Server_Port);
for(i = 0; i < 40; i++) {
server[i] = (argv[1][i] == ' ' ) ? '\0' : argv[1][i];
if(argv[1][i] == ' ') {
break;
}
}
server[i] = '\0';
Get_Host_Addr(server,&addr,Server_Port);
if((rc = connect(sd,(struct sockaddr *) &addr,sizeof(addr))) < 0) {
perror("connect() failed");
}
close(sd);
exit(0);
}
After the call from the remote client, check the server side and you should see something similar to the following. The IP address we ran the client was ‘10.10.10.82’ which as you can see is exactly what we saw… just press enter to end the server program.

Thought we would share this just in case others wanted to to try out the inet_ntop() API and needed a helping hand. Plus we tend to refer back to the blog when looking at how to code something up so its a reminder for us in the future 🙂
Chris..