I'm using a modification of the C code in https://www.tcpdump.org/sniffex.c to print information on TCP packets passing through an interface, using libpcap.
This is a sample of the callback code I'm trying to use to check if the received packet has the source field equal to the IP address of the current interface (so to only analyse outgoing packets). It is a rather extensive program, so I decided to just include the problematic part:
// retrieve IP address of interface
char * dev_name = "eth0";
struct ifreq ifr;
int fd;
char *dev_ip;
fd = socket(AF_INET, SOCK_DGRAM, 0);
// type of address to retrieve (IPv4)
ifr.ifr_addr.sa_family = AF_INET;
// copy the interface name in the ifreq structure
strncpy(ifr.ifr_name , dev_name , IFNAMSIZ-1);
ioctl(fd, SIOCGIFADDR, &ifr);
close(fd);
dev_ip = inet_ntoa(( (struct sockaddr_in *)&ifr.ifr_addr )->sin_addr);
printf("IPv4 address: %s\n", dev_ip);
printf("inet_ntoa: %s\n",inet_ntoa(ip->ip_src));
if (strcmp(dev_ip,inet_ntoa(ip->ip_src)) == 0)
printf("EQUAL!\n");
However, as you can see in the following screenshot, even if the source IP (inet_ntoa) and the IP address of the interface (IPv4 address) are different, their values are always equal according to the program.
What could the problem be?
inet_ntoa returns a pointer to a string that has been constructed in static memory that is internal to inet_ntoa. It re-uses that same static memory every time it is called. When you do this:
dev_ip = inet_ntoa(...);
dev_ip is set to point to that internal static buffer. At that point the static buffer contains a string that represents the interface address, so your:
printf("IPv4 address: %s\n", dev_ip);
shows the expected result. But then you do this:
printf("inet_ntoa: %s\n",inet_ntoa(ip->ip_src));
and that overwrites inet_ntoa's internal buffer with a string representing the packet's address.
But remember that dev_ip is still pointing to that internal buffer. So when you do this:
if (strcmp(dev_ip,inet_ntoa(ip->ip_src)) == 0)
(which BTW unnecessarily overwrites the internal buffer again with the packet's address, which was already there) the two arguments that are passed to strcmp are both pointers to inet_nota's internal buffer, and therefore strcmp will always find that the target strings match -- because both arguments are pointing to the same string.
To fix, either make a local copy of the string generated by inet_ntoa immediately after you call it, by copying into a local buffer or by doing something like:
dev_ip = strdup(inet_ntoa(...));
(and remember to free(dev_ip) when you no longer need that string) or, even better, use the thread-safe variant of inet_ntoa called inet_ntoa_r if your platform has that function. inet_ntoa_r does not use (and re-use) an internal buffer. It requires its caller to provide the buffer where the string will be placed.
Related
You can get a connection state using the netstat command (a connection state is something like ESTABLISHED or TIME_WAIT and so on).
But can you get the connection state of a socket programmatically?
You can't query a SOCKET itself for the kind of state you are wanting.
But, you can query the socket for its type (TCP vs UDP) and IP/Port pairs assigned to it (ie, via getsockopt() with SO_BSP_STATE, or via getsockopt(SO_TYPE) +getsockname()+getpeername()), and then you can enumerate Windows' TCP/UDP tables until you find an entry that matches those same details, then you will have the state from that entry.
Have a look at the following enumeration functions:
TCP:
GetTcpTable()/GetTcpTable2() (IPv4 only)
GetTcp6Table()/GetTcp6Table2() (IPv6 only)
GetExtendedTcpTable() (both IPv4 and IPv6)
UDP:
GetUdpTable() (IPv4 only)
GetUdp6Table() (IPv6 only)
GetExtendedUdpTable() (both IPv4 and IPv6)
On Windows, you can use getsockopt() with the SO_BSP_STATE option to get at least some information about the state of a socket:
The SO_BSP_STATE socket option returns the local address, local port,
remote address, remote port, socket type, and protocol used by a
socket.
To perform this operation, call the getsockopt function with the
following parameters.
Socket option value The constant that represents this socket option is
0x1009.
Syntax
C++
int getsockopt(
(SOCKET) s, // descriptor identifying a socket
(int) SOL_SOCKET, // level
(int) SO_BSP_STATE, // optname
(char *) optval, // output buffer,
(int) *optlen, // size of output buffer );
Parameters
s [in]
A descriptor identifying the socket.
level [in]
The level at which the option is defined. Use SOL_SOCKET for this
operation.
optname [in]
The socket option for which the value is to be retrieved. Use
SO_BSP_STATE for this operation.
optval [out]
A pointer to the buffer in which the value for the requested option is
to be returned. This parameter should point to buffer equal to or
larger than the size of a CSADDR_INFO structure.
optlen [in, out]
A pointer to the size, in bytes, of the optval buffer. This size must
be equal to or larger than the size of a CSADDR_INFO structure.
Return value
If the operation completes successfully, getsockopt
returns zero.
If the operation fails, a value of SOCKET_ERROR is returned and a
specific error code can be retrieved by calling WSAGetLastError.
A CSADDR_INFO structure is defined as
typedef struct _CSADDR_INFO {
SOCKET_ADDRESS LocalAddr;
SOCKET_ADDRESS RemoteAddr;
INT iSocketType;
INT iProtocol;
} CSADDR_INFO, *PCSADDR_INFO, *LPCSADDR_INFO;
Okay first here is the code:
int recvMast_sock;
struct sockaddr_in serv_addr, cli_addr;
socklen_t cli_len;
if ((recvMast_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
critErr("listen:socket=");
}
fillSockaddrAny(&serv_addr, UDP_NODE_LISTEN_PORT);// fills the sockaddr_in works fine elsewhere
if ((bind(recvMast_sock, (struct sockaddr*) &serv_addr, sizeof serv_addr)) < 0)
{
critErr("listen:bind recv_mast_sock:");
}
recvReturn_i = recvfrom(recvMast_sock, &recvBuff[0], (size_t)1, 0, (struct sockaddr*) &cli_addr, &cli_len);
if(recvReturn_i <0)
printf("recv error%d\n",errno);
critErr is a function to handle errors which also includes a print of the error and an exit.
This runs in a thread, if this is of any relevance. If I compile and run this on a Zedboard (ZYNQ-7000 SoC) which has an ARM Cortex A9 and Linaro Linux (based on precise Ubuntu). It prints error 22 but still has the received value in recvBuff[0].
Running this in my VM with xubuntu it works fine.
Error 22 equals EINVAL which is described as Invalid argument.
In the manpage of recvfrom(2) it states EINVAL means that the MSG_OOB flag is set but I don't use any flags (passing 0).
Before leaving on friday I started an apt-get upgrade because I hope it is a faulty library or something like this. I can check back at monday but maybe someone here has another idea what is wrong.
You need to initialize cli_len before passing it to recvfrom():
cli_len = sizeof(cli_addr);
You are not initializing it, so it has a random value. If that value happens to be < sizeof(cli_addr), recvfrom() can fail with EINVAL, or at least truncate the address, because it thinks cli_addr is not large enough to receive the client address. If the value is vastly larger than sizeof(cli_addr), recvfrom() might consider the buffer to be outside of the valid memory range.
You have to tell recvfrom() how large cli_addr actually is. This is clearly stated in the documentation:
The argument addrlen is a value-result argument, which the caller should initialize before the call to the size of the buffer associated with src_addr, and modified on return to indicate the actual size of the source address. The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.
So you have to initialize cli_len with the total size of cli_addr before calling recvfrom(), then recvfrom() updates cli_len with the size of the address that was actually written into cli_addr. cli_addr can be larger than the address, for instance when using a sockaddr_storage structure to accept either IPv4 or IPv6 addresses on a dual-stack socket. In the example in the question, an IPv4 socket is being used, so cli_len must be initialized to a value >= sizeof(sockaddr_in).
This was not caused by the OS or the architecture. The function was not called on the x86-system because of a blocked mutex. So I didn't got the error there.
The problem was that I passed the socket to this function from 'main' (which i did not state in the question because I thought it was irrelevant, my bad...)
In 'main' I used it and used it in this function. Even though it was mutually exclusive, there was this error.
Remy's answer was also relevant but not a solution to the problem. Not setting cli_len beforehand just leads to a cut of sockaddr if its too small. No error was generated for that.
I need to get the local port used by a (client) socket.
It was my understanding that Windows Sockets performs an implicit bind function call, therefore getsockname() after sendto() should provide the assigned port. However, it always sets 0 as the port number. Am I missing something?
ex:
if (sendto(sockfd, ...) != SOCKET_ERROR)
printf("Sent\n");
if (getsockname(sockfd, (struct sockaddr*)&sin, &sinlen) != SOCKET_ERROR)
printf("port = %u\n", ntohs(sin.sin_port);
else
printf("Error");
//result: Sent, port = 0
Problem solved with a restart of the computer. Still unknown as to the actual cause, but at this point I'm just happy it's working.
If anyone has an idea for fixing the issue without a restart (for future readers), feel free to post.
The only ambiguity I can see in your example code is what size you assigned to sinlen before calling. (you do not show it) If you are using winsock, it should be defined, and assigned int sinlen = sizeof(sin);
I used this code on my system, and it returns a non-zero value for the port I am connecting through:
struct sockaddr_in sin;
int len = sizeof(sin);
if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1)
//handle error
else
printf("port number %d\n", ntohs(sin.sin_port));
By the way, The ntohs function function returns the value in host byte order. If [ sin.sin_port ] is already in host byte order, then this function will reverse it. It is up to [your] application to determine if the byte order must be reversed. [text in brackets are my emphasis]
In answer to comment question ( getsockname() ):
The function prototype for getsockname():
int getsockname(
_In_ SOCKET s,
_Out_ struct sockaddr *name,
_Inout_ int *namelen //int, not socklen_t
);
For more discussion on socklen_t
Edit (address possible approach to re-setting sockets without rebooting PC.)
If winsock API calls cease to work predictably, you can re-start sockets without rebooting the PC by using WSAStartup and WSACleanup (see code example at bottom of link for WSAStartup)
You say you want to know the LOCAL port, but your line
sendto(sockfd, ...)
implies sockfd is the REMOTE descriptor. Your later code may therefore give you info about the REMOTE port, not the LOCAL one. 'sockets' are not both ends, meaning one connection. A socket is one end, meaning the IP and port number of one end of the connection. The first parameter of your getsockname() is not a reference or a pointer, it is therefore not an output from the function, but an input. You're telling the function to use the same socket descriptor that you just sent to, ie. the remote one.
Formatting error. ntohs() returns unsigned short so the format should be %hu, not %u or %d. If you grab too many bytes they are not the port.
Answer. After using sendto() try using gethostname() then getaddrinfo() on the name that comes back. Note: the addrinfo structures you get back will give you struct sockaddr pointers which you will need to re-cast to struct sockaddr_in pointers to access the local port number.
To find the local port number the kernel dreamed up when you issued a sendto() function perhaps you could write a routine to parse the output from the (gnu linux) commands 'ss' or 'netstat'. (Not sure if these are POSIX compatible.) Or maybe you could access /proc/net if you have the privilege.
My FTP program written in C only works when the server address is an IP address. But the connection fails when the server address is a Fully Qualified Domain Name (FQDN).
When ftp_host is FQDN, the ftp connection fails to open. Please help.
I'd strongly recommend using the getaddrinfo() function, as gethostbyname() is obsolete. This also has the advantage that it's easy to convert to IPv6, either now or later.
I'm assuming you only care about IPv4 addresses, so here's an example of a function which will take a hostname as its parameter and fill in a struct sockaddr_in for you:
int get_ftp_addr(const char *hostname, struct sockaddr_in *addr)
{
char host_buffer[256];
struct addrinfo hints;
struct addrinfo *result;
struct sockaddr_in *res_addr;
int error = -1;
char *colon;
snprintf(host_buffer, sizeof(host_buffer), "%s", hostname);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
colon = strchr(host_buffer, ':');
if (colon) {
*colon = '\0';
error = getaddrinfo(host_buffer, colon + 1, &hints, &result);
} else {
error = getaddrinfo(host_buffer, "ftp", &hints, &result);
}
if (error != 0 || !result) {
return error;
}
res_addr = (struct sockaddr_in*)(result->ai_addr);
memcpy(addr, res_addr, sizeof(struct sockaddr_in));
freeaddrinfo(result);
return 0;
}
Note that I only take a copy of the string to avoid modifying the version in the caller - don't feel obliged to keep that part if you don't want to, but personally I think it makes a cleaner interface.
This function will accept both IP addresses in dotted quad notation and fully-qualified hostnames, as getaddrinfo() accepts both of those. If a port is specified with a colon it will be used, otherwise the default FTP port will be used.
A zero return value indicates success. A positive return can be passed into gai_strerror() to get a string error code, or you can check the getaddrinfo() man page for the possible error codes. A return of -1 indicates a successful result from getaddrinfo() but no result structure - I don't think this can happen, but I don't like leaving any possible return code unhandled.
There are several caveats here, the two most important are:
This code only support IPv4 as it stands, although getaddrinfo() makes it really easy to support IPv6 as well. If you want to support both then change the AF_INET in the hints structure to AF_UNSPEC and you'll get all address families. You'll need to iterate over addresses, though, and filter out only the IPv4 and IPv6 ones (see my next point).
A DNS lookup can potentially look up to multiple IP addresses - this is quite common with large sites such as Google as they use this feature for load-balancing across hosts, and also redundancy. Ideally you code should iterate across all the returned addresses and try connecting to each one until one works. For an FTP client this is perhaps overkill, but I think it's important to be aware of it.
If you want to support IPv6, or support multi-A-records (i.e. multiple addresses back from the DNS query), then you need to follow the ai_next pointer in the struct addrinfo structure - something like this:
struct addrinfo *res;
/* Assume result is initialised as above via getaddrinfo() */
for (res = result; res != NULL; res = res->ai_next) {
...
}
I am reading Beej's "Guide to network programming".
In one of his intro examples he talks about getting the IP address for a hostname (like google.com or yahoo.com for instance).
Here is the code.
/*
** showip.c -- show IP addresses for a host given on the command line
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: showip hostname\n");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 2;
}
printf("IP addresses for %s:\n\n", argv[1]);
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
freeaddrinfo(res); // free the linked list
return 0;
}
The part that confuses me is the for loop.
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
Would anyone mind going through psuedo-step-by-step at whats going on or what these things are? Is it iterating through a linked list?.. I have a general idea of what the struct addrinfo are but what the heck is struct *res and struct *p or void *addr and *char ipversion.
First thing's first, do you know what a linked list is? If you understand that, you'll recognise what that for loop is going. p is a pointer to a structure that also references (links) the next structure in the list. So you're looping through a list of those structures, which are addrinfo structs. 4
Now, the thing you need to know about network packets is that they're made up of a header. Specifically the Ethernet frame. This is the hardware to hardware protocol. It let's you get things around on a physical, bounded network but knows nothing about routing across physical network boundaries.
Next up comes tcp or possibly another transport layer protocol, which sits somewhere inbetween the two levels. TCP versus UDP versus X is about how you manage the packets - for example TCP requires packets be reassembled in order, whereas UDP is a "broadcast"-type protocol.
Finally, you have the internet protocol suite (IPv4, IPv6). These are higher level protocols that control the broader sense of routing, so they know about the internet at large, but less about the steps needed to get there.
A great explanation of this is the handy diagram on this page. To complete the picture, BGP is how routers know how to move stuff around.
tcp/udp fit into this picture by being a part of (enscapulated in) the protocol in question (IPv4 for example)
So ethernet frames contain other protocols most notably IPv4, which contain the information routers need to get it out across the internet (across multiple physical networks). The internet protocol specifies where you want to go, from where you are. So a typical's IPv4 body remains unchanged across its whole transit, but every time it traverses physical networks it gets wrapped up in a different ethernet packet.
Now, in the ethernet header there is a field for finding out what the "ethernet body" contains. This line:
if (p->ai_family == AF_INET) {
Does. AF_INET is a constant that matches the value tcp uses to identify the packet body as IPv4. So, if you're looking at an IPv4 header, this loop then goes on to read that information.
The else clause is technically wrong, because not being IPv4 doesn't automatically make it IPv6. You could change it to test for IPv6 like this:
else if (p->ai_family == AF_INET6) {
Which you might want to do, just in case you pick up something else.
Now it's worth explaining this bit of magic:
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
This basically takes the network, or raw, form of the data which appears as a sequence of bytes, and casts it (coverts it) into fields in a struct. Because you know how big the fields are going to be, this is a very quick and easy way to extract out what you need.
The last thing that needs explanation is this:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
There are other ways of achieving this, specifically ntohs().
Basically network data is transmitted in big endian encoding, and in order to read it, you need (potentially) to convert the data to the encoding of your system. It could be big endian, or it could be little, it depends on your system for the most part. Have a read of the wikipedia article on endianness.
Summary: what you're looking at here is a combination of computer science structures, how networks work and C code.
Well, it's not that complicated. getaddrinfo returns a linked list of addrinfo structs (struct addrinfo **res in the manpage) where each of these structs contains information about one address available to the given interface (const char *node in the manpage).
Now, every struct is being inspected and information about the struct is being printed out. To print out either IPv4 or IPv6, the variable ipver is set accordingly. Before printing out the information, the address has to be converted from a binary form to a string. This is done by inet_ntop (*n*umber to *p*ointer).
The resulting string of inet_ntop (ipstr) and ipver are now printed out to console. Printing ipver, however, is not neccessary since you would recognize the address type from the ipstr: an IPv4 address (as we all know) gets written 192.168.1.10 whereas IPv6 addresses use colons to separate the address elements: 2001:0db8:85a3:0000:0000:8a2e:0370:7334.
Yes, res points to a linked list of addrinfo structures that represent the different IP addresses of a host. The MSDN documentation on the getaddrinfo function is pretty good. I don't know what platform you're running on, but it shouldn't be much different on other platforms.