C procedure entry point inet_ntop WS2_32.dll on XP? - c

I am getting an error on windows xp "The procedure entry point inet_ntop could not be located in the dynamic link library WS2_32.dll" and after some Googling I found that inet_ntop is not available in XP, so I made a Macro to use inet_ntoa instead. But it doesn't seem to be working, I still get the same error... Am I missing something?
char *get_ip(char *host)
{
struct hostent *hent;
int iplen = 39;
long errorcode;
char *ip = (char *)malloc(iplen + 1);
memset(ip, 0, iplen + 1);
if ((hent = gethostbyname(host)) == NULL)
{
perror("Could not get the IP address");
exit(1);
}
#if (_WIN32_WINNT >= 0x600)
if (inet_ntop(AF_INET, (void *)hent->h_addr_list[0], ip, iplen) == NULL)
{
perror("Could not resolve the host");
exit(1);
}
#else
ip = inet_ntoa(*((struct in_addr *)hent->h_addr_list[0]));
if (ip == NULL)
{
perror("Could not resolve the host");
exit(1);
}
#endif
return ip;
}

Your code needs to switch behaviour at runtime. Instead it uses conditional compilation which determines behaviour at compile time. Your #if code is evaluated at compile time. Not what you intended I expect. Only one of those branches is actually compiled. Again, not what you expected I am sure.
You need to use run time linking (LoadLibrary and GetProcAddress) and check the version of the OS at runtime to determine behaviour.

Related

IOCTL call works on some architecture

I'm writing a program in C and it needs to look up the IP & MAC address of an interface. I'm using IOCTL calls. Until recently I was still using custom data structure to store theses addresses, and everything was working perfectly.
Then I moved to using standards struct like
struct in_addr
struct ether_addr.
On my OS (Archlinux), it is still working properly,so that's good. But usually I run my program into a virtualized environment (Slitaz linux) in VirtualBox.I do this so I can run a virtualized network with GNS3. Since these modifications, impossible to get the IOCTL call working properly in Slitaz. When I call
printf("%s\n",strerror(errno));
I just get
No Such Device
If it wasn't working properly on both architecture, I would search more in-depth, but here i'm completely lost, it works fine on Arch but not on Slitaz. it WORKED in Slitaz BEFORE these change, I can still use the older version ( 2 branches in git, one for the old version and one for the current).
Thank you for your help.
Nikko
Here are the relevant part of code (I show for MAC oinyl since IP problem is the same):
/*
* Return a ifreq structure for this interface
* */
struct ifreq
get_ifreq ( const char * interface )
{
struct ifreq ifr;
size_t if_len;
if_len = strlen(interface);
if (if_len >= sizeof(ifr.ifr_name)){
fprintf(stderr,"Interface name too long to open descriptor.\nAbort.");
exit(EXIT_FAILURE);
}
strncpy(ifr.ifr_name,interface,if_len);
return ifr;
}
int
get_mac_address(const char * interface, struct ether_addr * ether) {
int fd ;
struct ifreq ifr = get_ifreq(interface);
if((fd = get_socketudp()) == -1) {
fprintf(stderr,"Unable to get mac address.\n");
return -1;
};
if(ioctl(fd,SIOCGIFHWADDR,&ifr) == -1) {
fprintf(stderr,"%s\n",strerror(fd));
fprintf(stderr,"Error while operating IOCTL (MAC resolving).\n");
close(fd);
return -1;
}
close(fd);
memcpy(ether,&ifr.ifr_hwaddr.sa_data,ETH_ALEN);
return 0;
}
And in the main.c, where I call this function :
char * interface = NULL;
/*----------------------------------------------------------------------
* Our OWN mac address & ip address
*-----------------------------------------------------------------------*/
struct ether_addr mac;
struct in_addr ip;
int
main ( int argc, char *argv[] )
{
char * operation = NULL;
char * hostA = NULL;
char * hostB = NULL;
int c = 0;
if (argc < 2) {
usage();
exit(EXIT_FAILURE);
}
while((c = getopt(argc,argv,"m:i:a:b:f:l:")) != -1){
switch(c){
case 'm':
operation = optarg;
if (strncmp(operation,"mitm",4) != 0 &&
strncmp(operation,"flood",5) != 0) {
fprintf(stderr,"Operation %s is unknown.Abort\n",operation);
abort();
}
break;
case 'i':
interface = optarg;
break;
case '?':
fprintf(stderr,"Option %c requires an argument",optopt);
abort();
}
}
/* Check options consistency */
if(operation == NULL) {
fprintf(stderr,"No Operations given. Abort.\n");
exit(EXIT_FAILURE);
} else if (interface == NULL) {
fprintf(stderr,"No interface given. Abort.\n");
exit(EXIT_FAILURE);
}
/* Store our own mac address */
if (get_mac_address(interface,mac) == -1) {
fprintf(stderr,"Abort.\n");
exit(EXIT_FAILURE);
}
SOLUTION
Thanks to the anser I changed my get_ifreq method to :
struct ifreq
get_ifreq ( const char * interface )
{
struct ifreq ifr;
size_t if_len;
memset(ifr.ifr_name,0x00,IFNAMSIZ);
if_len = strlen(interface);
if (if_len >= IFNAMSIZ){
fprintf(stderr,"Interface name too long to open descriptor.\nAbort.");
exit(EXIT_FAILURE);
}
strncpy(ifr.ifr_name,interface,if_len);
return ifr;
}
It seems likely that there is additional garbage in the ifreq structure that you're not clearing out here:
struct ifreq ifr;
if_len = strlen(interface);
strncpy(ifr.ifr_name,interface,if_len);
You declare the struct ifreq on the stack but don't initialize it so the bytes in that structure are potentially random garbage. You then copy exactly if_len bytes into it, but what about the bytes immediately following that. Assuming if_len is less than IFNAMSIZ, how will the kernel know to stop at if_len in interpreting the interface name?
I would clear the structure prior to the strncpy.

Get all IP-Addresses in C

I have the following problem. I have to write a Plugin for Pidgin in the Language C. I am completely new to C.
I found the following Code.
WORD wVersionRequested;
WSADATA wsaData;
char name[255];
char* ip;
PHOSTENT hostinfo;
wVersionRequested = MAKEWORD( 2, 0 );
if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) {
if( gethostname ( name, sizeof(name)) == 0) {
if((hostinfo = gethostbyname(name)) != NULL) {
ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
}
}
WSACleanup( );
}
I have the IP-Address
172.28.52.220
But because of my VMWare I also have the IP 10.0.1.3.
In my Plugin now the IP 10.0.1.3 is assigned to my variable.
i need the IP to find out in which location of my company I am. I need hte 172...
Now I could find in the winsock2.h that *hostinfo->h_addr_list contains the list of Ip addresses. How can I assign the 172. Address to my IP_Variable?
Thank you in advance for your help!
Edit:
Just to clarify: I don´t want to have my external IP address. I need my internals.
Here is an example I tested on on linux. I dont have access to a Windows system until tomorrow, but can test and update the answer if required.
It is comparable to the Windows version only without the WSAStartup call at the beginning.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
int main()
{
char hostnamebuff[100];
if(gethostname(hostnamebuff, 100) == 0)
{
struct hostent* hostinfo = gethostbyname(hostnamebuff);
printf("host name is %s\n", hostnamebuff);
if(hostinfo != NULL)
{
char** paddrlist = hostinfo->h_addr_list;
printf("host list is\n");
while(*paddrlist != NULL)
{
char addrbuff[INET6_ADDRSTRLEN];
if(inet_ntop(hostinfo->h_addrtype, *paddrlist, addrbuff, hostinfo->h_addrtype == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN))
{
printf("%s\n", addrbuff);
if(strncmp(addrbuff, "172.", 4) == 0)
{
printf("its a match\n");
break;
}
} else
{
printf("failed to convert an address\n");
}
paddrlist++;
}
} else
{
printf("failed on gethostbyname\n");
}
} else
{
printf("failed on gethostname errno=%d\n", errno);
}
}
The h_addr_list member of hostent is a NULL terminated array of pointers to char* (so its double pointer). My example shows how to traverse this. Hope it helps.
As for this distinctly smelly bit of code
ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
This is a highly unreadable way of getting the first entry in the address list.

zmq_getsockopt returns EINVAL on windows x64 when local address of ZMQ_FD option_val passed

On windows x64 passing the address of a local variable to zmq_getsockopt for ZMQ_FD consistently results in EINVAL. The code below is the smallest possible to reproduce the problem.
#include <zmq.h>
#include <stdio.h>
void zmq_perror(const char*);
int main(void)
{
const char *endpoint = "tcp://127.0.0.1:7100";
void *ctx = zmq_ctx_new();
if (ctx == NULL) { zmq_perror("zmq_ctx_new"); }
void *socket = zmq_socket(ctx, ZMQ_DEALER);
if (socket == NULL) { zmq_perror("zmq_socket"); }
int rc;
rc = zmq_connect(socket, endpoint);
if ( rc == -1 ) { zmq_perror("zmq_connect"); }
/*** This results in EINVAL ***/
int fd;
size_t fd_size = sizeof (fd);
rc = zmq_getsockopt(socket, ZMQ_FD, &fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
/*** This works without issue ***/
/*
int *fd = malloc(sizeof(int));
size_t fd_size = sizeof (fd);
rc = zmq_getsockopt(socket, ZMQ_FD, fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
*/
}
void zmq_perror(const char *f)
{
fprintf(stderr, "%s: %s\n", f, zmq_strerror(zmq_errno()));
abort();
}
Running the above using the first (manpage) form always produces:
zmq_getsockopt: Invalid argument
However the second, commented out form using malloc has no issues. This makes zero sense to me since passing the address of the local variable to zmq_getsockopt is perfectly legal.
This problem only manifests with 64 bit binaries on windows; 32 bit binaries on windows or 64 bit binaries on linux have no issue.
It also seems to only be an issue with the ZMQ_FD socket option. ZMQ_TYPE and ZMQ_SNDHWM worked without issue.
Is there some weird behavior related to ZMQ_FD on windows x64 that I'm not aware of?
Update
So I just noticed that my "working" code is actually erroneous.
sizeof(fd)
is taking the sizeof a pointer in the second form. In fact, it has nothing to do with malloc, as once I change it to sizeof(int) as it should be I get EINVAL again:
/* Fail */
int *fd = malloc(sizeof(int));
size_t fd_size = sizeof(int);
rc = zmq_getsockopt(socket, ZMQ_FD, fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
It turns out I apparently need to use a 64bit integer type with ZMQ_FD on windows x64
/* Success! */
uint64_t fd;
size_t fd_size = sizeof(uint64_t);
rc = zmq_getsockopt(socket, ZMQ_FD, &fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
This is very confusing since the api for zmq_getsockopt is int. Is this a bug? A windows eccentricity? Me being dense?
Relevant addendum:
zmq version: 3.2.3
compiler: cross compiled using mingw-w64, rubenvb-4.8.0 build for both 64bit and 32bit binaries
os: windows 7
The zmq_getsockopt man page says,
Option value type int on POSIX systems, SOCKET on Windows

client - searching all services using servbyport() function

I need to do simple client program, when I add port number and client will be serch all services for this port. Now is problem with segmentation fault in if statement.
How to return all services? In my program it will be return just one, I think.
my code:
int main (int argc, char *argv[])
{
int sockfd, n,pol, s;
int numer;
char recvline[MAXLINE +1];
char p;
struct sockaddr_in servaddr;
struct servent *sp;
if (argc != 3)
err_sys("Aby uruchomić podaj: klient <Adres IP> <port>");
s = atoi(argv[2]);
if((sp = getservbyport(s,NULL)) == NULL)
{
printf("port (s): %d \n", s);
printf("port (sp): %d \n", sp->s_port); //segmentation fault
err_sys("problem with port");
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0))<0)
err_sys("Blad utworzenia polaczenia");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = sp->s_port;
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr)<=0)
err_sys_kom("Blad konwersji do adresu IP dla %s", argv[1]);
printf("%s", sp->s_name);
pol = connect(sockfd, (SA*) &servaddr, sizeof(servaddr));
if (pol < 0)
{
err_sys_kom("Blad polaczenie z serwerem");
close(sockfd);
exit(-1);
}
else
str_cli(stdin, sockfd , 1);
exit(0);
}
EDIT (in response to new new problem - see comments below):
You probably need to get a list of protocols and work with those in a loop. The contents of the loop should roughly be:
Call getprotoent
If the result is NULL, exit the loop.
Else, dig out the protocol name from the returned structure.
Use that name as the second argument for getservbyport
Do what you want to with the result
EDIT (in response to new problem):
if((sp = getservbyport(s,NULL)) == NULL)
So your logic is to read sp ONLY IF sp is NULL. Obviously it will segfault.
It should be:
if((sp = getservbyport(s,NULL)) != NULL)
But then you will point out another new problem:
Why is sp NULL?
This could be because (as per the earlier version of my answer), you did an atoi on something which was not an integer. It could be because of any other reason. We can't say because we don't know what input you give.
This following part of the answer was in response to an old problem that the OP asked in the same question, and has since then chosen to edit over it:
First of all: Since you're using getservbyport, you really should read about services, if you haven't already.
Now on to the error:
getservbyport is of type:
struct servent *getservbyport(int port, const char *proto);
You are passing argv[2] which is of type char * instead of an int for port.
I believe the user inputs this as an argument in your program?
If you know that a char * points to a set of characters which look like an integer, like "1024", then you can convert it to an integer with atoi.
Do this instead, on the line with the error, when calling getservbyport, while making sure you've included stdlib.h:
getservbyport(atoi(argv[2]),NULL)
If argv[2] is NOT representable as an integer, you'll get undefined behavior, so maybe, you'll want to check this first.

How to differentiate ethernet from the others?

It was suggested in an answer here, Get the IP address of the machine, one could use getifaddrs() to obtain the IP address of the machine the program was running on, which worked great :D:D
However, running the same program on two different systems, one displayed
SERVER_ADDRESS lo 127.0.0.1
SERVER_ADDRESS eth0 129.xxx.xxx.xxx
SERVER_ADDRESS virbr0 192.zzz.zzz.1
while the other displayed
SERVER_ADDRESS lo0 127.0.0.1
SERVER_ADDRESS en0 192.yyy.yyy.yyy
I was going to use strcmp to differentiate ethernet, but now I realized it doesn't work across systems since different strings may be printed out.
Is there a function (or better way) to check whether or not an ifa_name is ethernet or not?
To (again) state this explicitly: All addresses of the 127.0.0.0/255.0.0.0 net (range) as there are the addresses from 127.0.0.0 to 127.255.255.255, are defined to be treated as local loopback addresses. Data addressed to such will not leave the local machine.
Anyhow as you are already using getifaddrs() live is easy .. - just test the member ifa_flags of the structure(s) struct ifaddrs returned for IFF_LOOPBACK like so:
#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h> /* for IFF_LOOPBACK */
...
struct ifaddrs * pIfAddrs = NULL;
if (!getifaddrs(&pIfAddrs)) {
/* Test if the first interface is looping back to the local host. */
int iIsLoopBack = (0 != (pIfAddrs->ifa_flags & IFF_LOOPBACK));
...
}
...
/* clean up */
if (pIfAddrs) {
freeifaddrs(pIfAddrs);
pIfAddrs = NULL;
}
...
You're likely to run into more than just that issue.. say for example there are multiple NICs, vLANs, WANS that look like LANS, and vice-versa, etc.
What is known? 127.X.X.X Throw out that result and you've got your non-loopbacks.
If you want to know if the address is private or not.. you'll then have to go down this road.
/* Output:
Name: 'eth0' Addr: 'xxx.xxx.xxx.xxx'
Name: 'eth0:0' Addr: 'xxx.xxx.xxx.xxx'
*/
struct ifaddrs *ifaddr;
char ip[255];
if (getifaddrs(&ifaddr) == -1)
{
//sprintf(ip[0], "%s", strerror(errno));
}
else
{
struct ifaddrs *ifa;
int i = 0, family;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
{
continue;
}
family = ifa->ifa_addr->sa_family;
if (family == AF_INET || family == AF_INET6)
{
if(!(ifa->ifa_flags & IFF_LOOPBACK) && (family == AF_INET) && getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
{
printf("Name: '%s' Addr: '%s'\n", ifa->ifa_name, ip);
}
}
}
}
freeifaddrs(ifaddr);

Resources