why gethostbyaddr doesn't work for www.google.com - linux

/* As usual, make the appropriate includes and declare the variables. */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *host, **names, **addrs;
struct hostent *hostinfo;
/* Set the host in question to the argument supplied with the getname call,
or default to the user's machine. */
if(argc == 1) {
char myname[256];
gethostname(myname, 255);
host = myname;
}
else
host = argv[1];
/* Make the call to gethostbyname and report an error if no information is found. */
hostinfo = gethostbyname(host);
if(!hostinfo) {
fprintf(stderr, "cannot get info for host: %s\n", host);
exit(1);
}
/* Display the hostname and any aliases it may have. */
printf("results for host %s:\n", host);
printf("Name: %s\n", hostinfo -> h_name);
printf("Aliases:");
names = hostinfo -> h_aliases;
while(*names) {
printf(" %s", *names);
names++;
}
printf("\n");
/* Warn and exit if the host in question isn't an IP host. */
if(hostinfo -> h_addrtype != AF_INET) {
fprintf(stderr, "not an IP host!\n");
exit(1);
}
/* Otherwise, display the IP address(es). */
addrs = hostinfo -> h_addr_list;
while(*addrs) {
char *ip_str = inet_ntoa(*(struct in_addr *)*addrs);
printf(" %s", ip_str);
/* <BEGIN> Get the host name by IP address */
struct in_addr addr = {0};
if (!inet_aton(ip_str, &addr)) {
printf("Cannot parse IP %s\n", ip_str);
exit(1);
}
struct hostent *remoteHost = gethostbyaddr((void*)&addr, sizeof(addr), AF_INET);
if (remoteHost == NULL) {
printf("\nInvalid remoteHost\n");
exit(1);
}
printf("\nOfficial name: %s\n", remoteHost->h_name);
char **pAlias;
for(pAlias=remoteHost->h_aliases; *pAlias != 0; pAlias++) {
printf("\tAlternate name: %s\n", *pAlias);
}
/* <End> */
addrs++;
}
printf("\n");
exit(0);
}
/* Test Run for www.yahoo */
user#ubuntu:~/Documents/C$ ./getname2way www.yahoo.com
results for host www.yahoo.com:
Name: any-fp.wa1.b.yahoo.com
Aliases: www.yahoo.com fp.wg1.b.yahoo.com
209.191.122.70
Official name: ir1.fp.vip.mud.yahoo.com
/* Test Run for www.google.com */
user#ubuntu:~/Documents/C$ ./getname2way www.google.com
results for host www.google.com:
Name: www.l.google.com
Aliases: www.google.com
74.125.225.83
Invalid remoteHost
Why the code doesn't work for www.google.com?

The IP address 74.125.225.83 does not have a reverse PTR record. You can always test on the command line with nslookup or dig.
can't find 74.125.225.83: Non-existent domain
http://en.wikipedia.org/wiki/Reverse_DNS_lookup

Related

IPv6 example program fails on connect()

IPv6 example program fails on connect()
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char * es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
struct sockaddr * getadr(char * name)
{
struct addrinfo * p;
int r;
struct sockaddr_in6 * sap;
unsigned long long addrl, addrh;
printf("getadr: begin\n");
r = getaddrinfo(name, NULL, NULL, & p);
if (r) error(gai_strerror(r));
sap = NULL;
while (p && !sap) {
/* traverse the available addresses */
if (p - > ai_family == AF_INET6 && p - > ai_socktype == SOCK_STREAM) {
/* get the IPv6 address */
sap = (struct sockaddr_in6 * ) p - > ai_addr;
}
p = p - > ai_next;
}
if (!sap) error("No address found");
addrh = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[0]) << 32 |
(unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[1]);
addrl = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[2]) << 32 |
(unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[3]);
printf("Address: %llx:%llx:%llx:%llx:%llx:%llx:%llx:%llx\n",
addrh >> 48 & 0xffff, addrh >> 32 & 0xffff, addrh >> 16 & 0xffff, addrh & 0xffff,
addrl >> 48 & 0xffff, addrl >> 32 & 0xffff, addrl >> 16 & 0xffff, addrl & 0xffff);
printf("getadr: end\n");
return ((struct sockaddr * ) sap);
}
int main(int argc, char * argv[]) {
int sockfd = 0, n = 0;
char buff[1024];
struct sockaddr_in6 serv_addr;
int r;
struct sockaddr * sap;
if (argc != 3) {
printf("Usage: socket <server> <page>\n");
exit(1);
}
if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
error("Could not create socket");
memset( & serv_addr, '0', sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(80);
printf("before address resolve\n");
if (isdigit(argv[1][0])) {
r = inet_pton(AF_INET6, argv[1], & serv_addr.sin6_addr);
if (r <= 0) error("inet_pton error occured");
} else {
sap = getadr(argv[1]);
memcpy( & serv_addr, sap, sizeof(struct sockaddr));
}
printf("after address resolve\n");
r = connect(sockfd, (struct sockaddr * ) & serv_addr, sizeof(serv_addr));
if (r < 0) error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n\r\n", "www.example.com" /*argv[1]*/ );
write(sockfd, buff, strlen(buff));
do {
r = read(sockfd, buff, sizeof(buff));
if (r > 0) {
buff[r] = 0;
printf("%s", buff);
}
} while (r);
return 0;
}
I arranged the server argument to be evaluated by inet_pton() if numeric, otherwise, it goes through getaddrinfo(). inet_pton() sets up the address and it works. getaddrinfo() does not, apparently, it dies in connect (hangs up). The example program is a simple web page fetch and print (not https). I used the www.example.com server to test it.
Note in the following example run that I use the same address getaddrinfo()gives me in a numeric example, then that works fine.
What am I doing wrong here?
The compile is simply gcc socket.c -o socket.
$ socket6 www.example.com /
before address resolve
getadr: begin
Address: 2606:2800:220:1:248:1893:25c8:1946
getadr: end
after address resolve
^C
(hangs up in connect, CTL-C out of it)
$ socket6 2606:2800:220:1:248:1893:25c8:1946 /
before address resolve
after address resolve
after connect
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
...
(prints the rest of the page)
I did find a similar post where they recommend adding the interface name to the server name, like www.example.com%enp2s0, but that was just rejected as invalid by getaddrinfo().
The main problem is that you are not populating serv_addr correctly.
When calling getadr(), your call to memcpy() for the result does not copy enough bytes for a complete sockaddr_in6 (sizeof(sockaddr) is less thansizeof(sockaddr_in6)). Also, you are not askinggetaddrinfo()to output a port number, so thesin6_port` of the result will not be 80 as you are expecting.
That is why connect() fails when you use getaddrinfo() instead of inet_pton().
There are other problems with your code, too.
When using memset(), you need to use an integer 0 rather than a character '0'. They are not the same value. Unused sockaddr_in6 fields need to be zeroed out properly.
Your getadr() function leaks memory, and it is inefficient in general. Use the hints parameter of getaddrinfo() to limit the results it outputs so you don't have to go hunting for them. And you need to free the output with freeaddrinfo() when you are done using it.
isdigit() is not the correct way to differentiate a numeric IP from a hostname. And besides, you don't really need to do this differentiation manually, as getaddrinfo() can parse a numeric IP string. But if you do differentiate manually, call inet_pton() unconditionally and then call getaddrinfo() if inet_pton() fails.
With that said, try something more like this instead:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
void getadr(const char* name, struct in6_addr *addr)
{
struct addrinfo hints, *p;
int r;
struct sockaddr_in6 *sap;
char addrstr[INET6_ADDRSTRLEN];
printf("getadr: begin\n");
/*
r = inet_pton(AF_INET6, name, addr);
if (r == 1)
{
printf("getadr: end\n")
return;
}
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(name, NULL, &hints, &p);
if (r != 0)
error(gai_strerror(r));
sap = (struct sockaddr_in6*)p->ai_addr;
memcpy(addr, &(sap->sin6_addr), sizeof(*addr));
freeaddrinfo(p);
printf("Address: %s\n", inet_ntop(AF_INET6, addr, addrstr, sizeof(addrstr)));
printf("getadr: end\n");
}
int main(int argc, char *argv[])
{
int sockfd, r;
char buff[1024];
struct sockaddr_in6 serv_addr;
if (argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("Could not create socket");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(80);
printf("before address resolve\n");
getadr(argv[1], &(serv_addr.sin6_addr));
printf("after address resolve\n");
r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (r < 0)
error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
write(sockfd, buff, strlen(buff));
do
{
r = read(sockfd, buff, sizeof(buff));
if (r <= 0) break;
printf("%.*s", r, buff);
}
while (true);
close(sockfd);
return 0;
}
That said, getaddrinfo() outputs a linked list of IP addresses. It is possible for a hostname to resolve to multiple IPs, not all of which may be reachable from your machine. You should loop through the entire list connect()'ing to each IP until one of them succeeds or the list is exhausted. For example:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
struct addrinfo* getadrs(const char* name, const char* port)
{
struct addrinfo hints, *p;
int r;
printf("getadr: begin\n");
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6 /*AF_UNSPEC*/;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(name, port, &hints, &p);
if (r != 0)
error(gai_strerror(r));
printf("getadr: end\n");
return p;
}
void* adrptr(struct sockaddr* addr)
{
switch (addr->sa_family)
{
case AF_INET:
return &(((struct sockaddr_in*)addr)->sin_addr);
case AF_INET6:
return &(((struct sockaddr_in6*)addr)->sin6_addr);
}
return NULL;
}
int main(int argc, char *argv[])
{
int sockfd = -1, r;
char buff[1024], addrstr[INET6_ADDRSTRLEN];
struct addrinfo *serv_addrs, *addr;
if (argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
printf("before address resolve\n");
serv_addrs = getadrs(argv[1], "80");
printf("after address resolve\n");
for(addr = serv_addrs; addr != NULL; addr = addr->ai_next)
{
sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sockfd < 0)
error("Could not create socket");
printf("Address: %s\n", inet_ntop(addr->ai_family, adrptr(addr->ai_addr), addrstr, sizeof(addrstr)));
r = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
if (r == 0) break;
close(sockfd);
sockfd = -1;
}
if (sockfd < 0)
error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
write(sockfd, buff, strlen(buff));
do
{
r = read(sockfd, buff, sizeof(buff));
if (r <= 0) break;
printf("%.*s", r, buff);
}
while (true);
close(sockfd);
return 0;
}
Refactored code:
/*
* Socket.c program taken from the sockets example for linux
* and refactored for IPv6.
*/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd = 0, n = 0;
char buff[1024];
struct sockaddr_in6 serv_addr;
int r;
struct sockaddr* sap;
struct addrinfo hints, *p;
if(argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
if((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
error("Could not create socket");
/* resolve address */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(argv[1], "80", &hints, &p);
if (r != 0) error(gai_strerror(r));
memcpy(&serv_addr, (struct sockaddr_in6*)p->ai_addr, sizeof(struct sockaddr_in6));
freeaddrinfo(p);
/* connect to server */
r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (r < 0) error("Connect Failed");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n\r\n", "www.example.com" /*argv[1]*/);
write(sockfd, buff, strlen(buff));
do {
r = read(sockfd, buff, sizeof(buff));
if (r > 0) {
buff[r] = 0;
printf("%s", buff);
}
} while (r);
return 0;
}
The run is now:
$ addr www.example.com
Addresses for host: www.example.com
Address: type: AF_INET6 Socket type: SOCK_STREAM Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET6 Socket type: SOCK_DGRAM Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET6 Socket type: SOCK_RAW Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET Socket type: SOCK_STREAM Address: 93.184.216.34
Address: type: AF_INET Socket type: SOCK_DGRAM Address: 93.184.216.34
Address: type: AF_INET Socket type: SOCK_RAW Address: 93.184.216.34
$ socket6 www.example.com /
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 27 Oct 2019 17:50:10 GMT
...
(full page)
$ socket6 2606:2800:220:1:248:1893:25c8:1946 /
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 27 Oct 2019 17:50:21 GMT
...
(full page)
I'll have to put some thought into what you said about not all of the far addresses being valid, I wasn't aware of that.

getaddrinfo, Segmentation Fault

I am getting Segmentation Fault with the codes below.
What I need the program to do is, when an invalid/unknown name or service is entered as an argument, it displays an error only for that particular service and continues to work on the rest of the provided services.
Right now, the program works if I include an invalid service anywhere in a line of services (e.g ./dnslookup www.nhawurha.com www.google.com OR www.google.com www.nhawurha.com)
But it gives me a Segmentation Fault after printing the error if only the invalid service is used as the sole argument (e.g ./dnslookup www.nhawurha.com)
Any form of help would be much appreciated, thanks!
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#define BUFLEN 1500
int main(int argc, char *argv[]) {
struct addrinfo hints, *ai, *result;
char ipaddrv4[BUFLEN];
char ipaddrv6[BUFLEN];
int error;
if (argc < 2) {
fprintf(stderr, "Missing <hostname> after %s \n", argv[0]);
return 0;
}
for (int j = 1; j < argc; j++) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC; /* IPv4, IPv6, or anything */
hints.ai_socktype = SOCK_DGRAM; /* Dummy socket type */
error = getaddrinfo(argv[j], NULL, &hints, &result);
if (error) {
fprintf(stderr, "ERROR (%s: %s)\n", argv[j], gai_strerror(error));
continue;
}
for (ai = result; ai != NULL; ai = ai->ai_next) {
if (ai->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *) ai->ai_addr;
void *addr = &(ipv4->sin_addr);
inet_ntop(AF_INET, addr, ipaddrv4, BUFLEN);
printf("%s IPv4 %s\n", argv[j], ipaddrv4);
}
else if (ai->ai_family == AF_INET6) {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) ai->ai_addr;
void *addr = &(ipv6->sin6_addr);
inet_ntop(AF_INET6, addr, ipaddrv6, BUFLEN);
printf("%s IPv6 %s\n", argv[j], ipaddrv6);
}
else {
continue;
}
}
}
freeaddrinfo(result);
return 0;
}
Have a look at the 'result' parameter - if your one and only lookup fails, result will be uninitialised and freeaddrinfo will segfault. Try initialising it to NULL first.
There is a second problem if you have more than one lookup - a memory leak because you don't call freeaddrinfo on each result.
So I think your logic should be more like:
for each command line arg
if lookup succeeds
print result
free result
else
print error
See man page for getaddrinfo

Lazarus: How to list all the available network connection on a system?

I am writing a program on a Linux system using Lazarus IDE. The program is supposed to connect to the Internet or Intranet. So, I want to display to the user list of all the available network connections that they can use to connect to the Internet or Intranet like wifi, if there are two active network cards on the system, then this program should display their available connections.
At the moment, I don't know where to start or what tool(s) to use.
Any hints, clues or advice will be greatly appreciated.
You can use ifconfig to list all available network interfaces and their status.
Edit: For doing it programmatically you have to use function ioctl with SIOCGIFCONF.
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int sockfd, len, lastlen;
char *ptr, *buf;
struct ifconf ifc;
struct ifreq *ifr;
char ifname[IFNAMSIZ + 1];
char str[INET6_ADDRSTRLEN];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
lastlen = 0;
len = 100 * sizeof(struct ifreq); /* initial buffer size guess */
for ( ; ; )
{
buf = malloc(len);
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)
{
if (errno != EINVAL || lastlen != 0)
exit(-1);
}
else
{
if (ifc.ifc_len == lastlen)
break; /* success, len has not changed */
lastlen = ifc.ifc_len;
}
len += 10 * sizeof(struct ifreq); /* increment */
free(buf);
}
printf("LEN: %d\n", ifc.ifc_len);
for (ptr = buf; ptr < buf + ifc.ifc_len; )
{
ifr = (struct ifreq *) ptr;
ptr += sizeof(struct ifreq); /* for next one in buffer */
memcpy(ifname, ifr->ifr_name, IFNAMSIZ);
printf("Interface name: %s\n", ifname);
const char *res;
switch (ifr->ifr_addr.sa_family)
{
case AF_INET6:
res = inet_ntop(ifr->ifr_addr.sa_family, &(((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr), str, INET6_ADDRSTRLEN);
break;
case AF_INET:
res = inet_ntop(ifr->ifr_addr.sa_family, &(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr), str, INET_ADDRSTRLEN);
break;
default:
printf("OTHER\n");
str[0] = 0;
res = 0;
}
if (res != 0)
{
printf("IP Address: %s\n", str);
}
else
{
printf("ERROR\n");
}
}
return 0;
}
ioctl SIOCGIFCONF will return, if success, a struct ifconf which has a pointer to an array of struct ifreq.
These structs are defined in net/if.h
Using this code, from ifc.ifc_req you can get all interfaces, please look at the declaration of struct ifreq in order to determine the length and type of each array element. I think from here you can continue alone, if not please let me know.
The following code does work on my Linux system. It outputs all the available connection point through which you can connect to the Internet or intranet. I modified the code to print out its name and ip address.
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
// you may need to include other headers
int main()
{
struct ifaddrs* interfaces = NULL;
struct ifaddrs* temp_addr = NULL;
int success;
char *name;
char *address;
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(&interfaces);
if (success == 0)
{
// Loop through linked list of interfaces
temp_addr = interfaces;
while (temp_addr != NULL)
{
if (temp_addr->ifa_addr->sa_family == AF_INET) // internetwork only
{
name = temp_addr->ifa_name;
address = inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr);
printf("%s %s\n",name,address);
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
}

Implementing DNAT in OUTPUT chain using netfilter framework

I am trying to implement DNAT in OUTPUT chain but the packets do not arrive to the target destination. What I am trying to do, is e.g. if the message is sent to 192.168.56.17, I change it to 192.168.56.1 and this ip is in my network, so it should be sent.
The code is:
#include <linux/module.h> //needed for every module
#include <linux/kernel.h>
#include <linux/types.h> //u_int && co
#include <linux/skbuff.h> //struct sk_buff
#include <linux/in.h> //basic internet shiat
#include <linux/ip.h> //protocol headers
#include <linux/tcp.h>
#include <linux/netfilter.h> //need this for register_
#include <linux/netfilter_ipv4.h> //..
#include <linux/netdevice.h> //struct net_device
#define NR_OF_VLANS 3
#define MAX_UNIQUE_ADDRS 16
#define IFACE0 "vboxnet0"
#define IFACE1 "eth0"
#define IFACE2 "eth0.1"
#define INIT_ADDR 0x0038A8C0
MODULE_AUTHOR("tomak");
MODULE_DESCRIPTION("dnat");
static struct nf_hook_ops NF_hook_out;
static int change_ip_out(struct iphdr* iph)
{
//examine last byte of dest ip addr
__be32 daddr = iph->daddr;
//Note: big endian => we need first byte of the structure
__be32 offset = daddr - INIT_ADDR;
//printk(KERN_INFO "be32 offset %pI4\n", &(offset));
__u32 uOffset = be32_to_cpu(offset);
//printk(KERN_INFO "offset: %d", uOffset);
__u32 uRemainder = uOffset % MAX_UNIQUE_ADDRS ;
__be32 remainder = cpu_to_be32(uRemainder);
//printk(KERN_INFO "remainder is: %pI4\n", &remainder);
int division = (int)(uOffset / MAX_UNIQUE_ADDRS);
//change ip and put on right iface
iph->daddr = INIT_ADDR + remainder;
printk(KERN_INFO "OUT changed daddr to %pI4\n", &(iph->daddr));
if (division == 0) {
return NF_ACCEPT;
}
return NF_REPEAT;
}
u_int hook_fcn_out( u_int hooknum, //the hook number (linux/netfilter_ipv4.h)
struct sk_buff **skpp, //pointer to a pointer with an sk_buff(mad ****) (linux/skbuff.h)
const struct net_device *in, //only valid for recieved
const struct net_device *out, //only valid for outgoing (linux/netdevice.h)
int (*okfn)(struct sk_buff *)) //called from net/core/netfilter.c ??
{
int result;
int i = 0;
int max_addrs = MAX_UNIQUE_ADDRS * NR_OF_VLANS;
__be32 test_addr[max_addrs]; //addresses to check
struct iphdr* iph = ip_hdr(skpp); //getting ip header
//addresses for detection
__be32 addr = INIT_ADDR;
for(i = 0; i < max_addrs; i++){
test_addr[i] = addr;
addr = addr + 0x01000000; //+1
}
//detecting ips
for(i = 0; i < max_addrs; i++){
if(memcmp(&(iph->daddr),&test_addr[i], sizeof(test_addr[0])) == 0) {
printk(KERN_INFO "OUT detected message to address %pI4\n", &(iph->daddr));
printk(KERN_INFO "OUT message detected to interface %s\n", out->name);
result = change_ip_out(iph);
return result;
}
}
/* printk(KERN_INFO "Detected output message to daddr: %pI4\n", &(iph->daddr));
printk(KERN_INFO "Message detected for interface: %s\n", out->name); */
return NF_ACCEPT;
}
int init_module(void)
{
printk(KERN_DEBUG "nat_up\n");
NF_hook_out.hook = hook_fcn_out;
NF_hook_out.hooknum = NF_INET_LOCAL_OUT;
NF_hook_out.pf = PF_INET;
NF_hook_out.priority = NF_IP_PRI_NAT_DST;
//register hook functions
nf_register_hook(&NF_hook_out);
return 0;
}
void cleanup_module(void)
{
printk(KERN_DEBUG "nat_down\n");
nf_unregister_hook(&NF_hook_out);
}
Does anyone has any idea where might be the problem. My guess was priority, but even NF_INET_PRI_FIRST did not work.
Thanks a lot for your comments and help.
Tomas

How getservbyname can get the server port information if it is run on a client machine?

The following client program trys to connect to a server and finds the current time and date on that server.
/* Start with the usual includes and declarations. */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *host;
int sockfd;
int len, result;
struct sockaddr_in address;
struct hostent *hostinfo;
struct servent *servinfo;
char buffer[128];
if(argc == 1)
host = "localhost";
else
host = argv[1];
/* Find the host address and report an error if none is found. */
hostinfo = gethostbyname(host);
if(!hostinfo) {
fprintf(stderr, "no host: %s\n", host);
exit(1);
}
/* Check that the daytime service exists on the host. */
servinfo = getservbyname("daytime", "tcp");
if(!servinfo) {
fprintf(stderr,"no daytime service\n");
exit(1);
}
printf("daytime port is %d\n", ntohs(servinfo -> s_port));
/* Create a socket. */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
/* Construct the address for use with connect... */
address.sin_family = AF_INET;
address.sin_port = servinfo -> s_port;
address.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list;
len = sizeof(address);
/* ...then connect and get the information. */
result = connect(sockfd, (struct sockaddr *)&address, len);
if(result == -1) {
perror("oops: getdate");
exit(1);
}
result = read(sockfd, buffer, sizeof(buffer));
buffer[result] = '\0';
printf("read %d bytes: %s", result, buffer);
close(sockfd);
exit(0);
}
Question:
We run the above program on a client machine, how the function getservbyname can get the
server information without a reference to the server machine in the parameter list?
It examines /etc/services for an entry with the given service name and protocol.
$ grep "^daytime\s.*/tcp" /etc/services
daytime 13/tcp
getservbyname simply looks in /etc/services to find the "daytime" service using the "tcp" protocol.
It's just a convenience, to save you from parsing that file.
Edit (clarification)
Each of these protocols has a friendly name ("daytime", "http", etc) and a useful name (the port number - 13, 80 etc). /etc/services holds this mapping, nothing more.

Resources