invalid conversion from int to socklen - linux

Below is my code for Linux.
I am implementing a client/server application and below is the server .cpp file.
int main()
{
int serverFd, clientFd, serverLen, clientLen;
struct sockaddr_un serverAddress;/* Server address */
struct sockaddr_un clientAddress; /* Client address */
struct sockaddr* serverSockAddrPtr; /* Ptr to server address */
struct sockaddr* clientSockAddrPtr; /* Ptr to client address */
/* Ignore death-of-child signals to prevent zombies */
signal (SIGCHLD, SIG_IGN);
serverSockAddrPtr = (struct sockaddr*) &serverAddress;
serverLen = sizeof (serverAddress);
clientSockAddrPtr = (struct sockaddr*) &clientAddress;
clientLen = sizeof (clientAddress);
/* Create a socket, bidirectional, default protocol */
serverFd = socket (AF_LOCAL, SOCK_STREAM, DEFAULT_PROTOCOL);
serverAddress.sun_family = AF_LOCAL; /* Set domain type */
strcpy (serverAddress.sun_path, "css"); /* Set name */
unlink ("css"); /* Remove file if it already exists */
bind (serverFd, serverSockAddrPtr, serverLen); /* Create file */
listen (serverFd, 5); /* Maximum pending connection length */
readData();
while (1) /* Loop forever */
{
/* Accept a client connection */
clientFd = accept (serverFd, clientSockAddrPtr, &clientLen);
if (fork () == 0) /* Create child to send recipe */
{
printf ("");
printf ("\nRunner server program. . .\n\n");
printf ("Country Directory Server Started!\n");
close (clientFd); /* Close the socket */
exit (/* EXIT_SUCCESS */ 0); /* Terminate */
}
else
close (clientFd); /* Close the client descriptor */
}
}
When i tried to compile it displays an error message which shows.
server.cpp:237:67: error: invalid conversion from ‘int*’ to ‘socklen_t*’
server.cpp:237:67: error: initializing argument 3 of ‘int accept(int, sockaddr*, socklen_t*)’
It points to this line
clientFd = accept (serverFd, clientSockAddrPtr, &clientLen);
I do not actually know how to solve this problem.
Thanks in advance to those who helped! :)

Define clientLen as socklen_t:
socklen_t clientLen;
instead of
int clientLen;

Change,
clientFd = accept (serverFd, clientSockAddrPtr, &clientLen);
to
clientFd = accept (serverFd, clientSockAddrPtr,(socklen_t*)&clientLen);

Related

Non-blocking Linux server socket

I want to create a server socket that always prints to screen "tick" , and if one client sends data to this server, the server will print that data. I use non-blocking socket, but the it does not work, server prints to screen "tick" but cannot receives data from client.
Server
int main(int argc, char *argv[]) {
int server_sockfd, client_sockfd;
sockaddr_un server_address;
sockaddr_un client_address;
int client_len;
int res;
/* remove old socket and create unnamed socket */
unlink("server_socket");
server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
/* non-blocking socket */
fcntl(server_sockfd, F_SETFL, O_NONBLOCK);
/* name the socket */
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "server_socket");
bind(server_sockfd, (sockaddr*)&server_address, sizeof(server_address));
/* listen client */
printf("server_waiting\n");
listen(server_sockfd, 5);
client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd, (sockaddr*)&client_address, (socklen_t*)&client_len);
while(1) {
char ch;
res = recv(client_sockfd, &ch, 1, 0);
if (res == -1) {
printf("tick\n");
}
else {
printf("received: %c\n", ch);
}
}
}
client
int main(int argc, char *argv[]) {
int sock_fd;
struct sockaddr_un address;
int result;
char ch = 'A';
/* create socket for client */
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
/* name of socket as agreed with server */
address.sun_family = AF_UNIX;
strcpy(address.sun_path, "server_socket");
result = connect(sock_fd, (sockaddr*) &address, sizeof(address));
if (result == -1) {
perror("fail\n");
exit(1);
}
/* write via socket */
send(sock_fd, &ch, 1, 0);
close(sock_fd);
exit(0);
}
You are setting the listing socket to be non-blocking instead of the accepted socket.
Following your code logic, you DO want to wait on the accept call, but not the recv call
Instead of
/* non-blocking socket */
fcntl(server_sockfd, F_SETFL, O_NONBLOCK);
Delete it and instead add the fcntl call to the socket you are getting back from the accept call, like
client_sockfd = accept(....);
int flags = fcntl(client_sockfd, F_GETFL, 0);
fcntl(client_sockfd, F_SETFL, flags | O_NONBLOCK);
accept and fcntl can fail so you should check for failures in production code.

Why select do not tell me that a client wants to connect?

I've made a simple tcp server that I can test with telnet program.
When running it on windows, it works as expected, when running it on linux, the behavior is strange:
telnet clients understand that they are connected to the server,
the server do not see clients (select return always 0),
when I kill the server, the clients detect the disconnection.
I think I missed something in accept, listen or select.
What did I missed?
Thanks.
Here's the program source:
#include "headers.h"
#define DEFAULT_PORT 24891
/**
* test_server [ip port]
*/
int main(int argc, char *argv[])
{
sockaddr_in sin;
socket_t sock;
/* listening socket creation */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sock)
{ die("socket()"); }
/* binding */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(DEFAULT_PORT);
if (3 == argc)
{
sin.sin_addr.s_addr = inet_addr(argv[1]);
sin.sin_port = htons(strtol(argv[2], NULL, 0));
}
if (-1 == bind(sock, (sockaddr*) &sin, sizeof(sin)))
{ die("bind()"); }
/* Listening */
if (-1 == listen(sock, SOMAXCONN))
{ die("listen()"); }
while (1)
{
timeval timeout = { 1, 0 };
fd_set in_set;
FD_ZERO(&in_set);
FD_SET(sock, &in_set);
// select the set
int cnt = select(1, &in_set, NULL, NULL, &timeout);
if (cnt > 0)
{
// ask if an event occurs on listening socket
if (FD_ISSET(sock, &in_set))
{
/* a new client wants to connect */
socket_t csock = accept(sock, NULL, NULL);
send(csock, "hello\r\n", 7, 0);
printf("new client!\n");
close(csock);
}
}
else if (cnt < 0)
{ die("select"); }
}
/* closing listen socket */
close(sock);
printf("socket closed\n");
return 0;
}
You simply call select incorrectly. The first parameter needs to be the highest numbered fd from the fdset, plus one. See man page:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
....
nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
The code may work, or may not, that depends on the fd returned by "socket()".
In your case the value of "nfds" needs to be "sock + 1", generally you need to track the highest numbered fd when doing a select on multiple fd's.

How to know where does the sock->ops assigned in kernel?

In the code of kernel linux/net/socket.c
sock->ops->bind()
I can't find where the sock or sock->ops has been assigned in UDP program.
Thanks !
This is not done directly as you are expecting. If you look at the code carefully, you will find that the function sock_from_file(struct file *file, int *err) is used to assign file->f_op to socket_file_ops
It is called twice in the code
In the sockfd_lookup(int fd, int *err) function as sock = sock_from_file(file, err);
In the sockfd_lookup_light(int fd, int *err, int *fput_needed) function as sock = sock_from_file(f.file, err);
This file is then assigned to sock->file inside the sock_alloc_file(struct socket *sock, int flags, const char *dname) function.
The proto_ops structure is overridden in each socket implementation. If you search lxr for inet_bind you can see it.
https://elixir.bootlin.com/linux/latest/source/net/ipv4/af_inet.c#L435
int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sock *sk = sock->sk;
u32 flags = BIND_WITH_LOCK;
int err;
/* If the socket has its own bind function then use it. (RAW) */
if (sk->sk_prot->bind) {
return sk->sk_prot->bind(sk, uaddr, addr_len);
}
if (addr_len < sizeof(struct sockaddr_in))
return -EINVAL;
/* BPF prog is run before any checks are done so that if the prog
* changes context in a wrong way it will be caught.
*/
err = BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr,
CGROUP_INET4_BIND, &flags);
if (err)
return err;
return __inet_bind(sk, uaddr, addr_len, flags);
}
EXPORT_SYMBOL(inet_bind);
That is then set in the proto_ops structure here -
https://elixir.bootlin.com/linux/latest/source/net/ipv4/af_inet.c#L1019
const struct proto_ops inet_stream_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
.bind = inet_bind,
// ...

How to make the copy of the packet?

I want to make a copy of the packet (and send it to queue that is made by me) at the Net Filter hook.
Will skb_copy work for me? i also have to add the seq no before the packet,skb_reserve will do that?
I have written the following code to capture packet
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
if (strcmp(in->name, drop_if) == 0) {
printk("Dropped packet on %s...\n", drop_if);
return NF_DROP;
} else {
return NF_ACCEPT;
}
}
/* Initialisation routine */
int init_module()
{
/* Fill in our hook structure */
nfho.hook = hook_func; /* Handler function */
nfho.hooknum = NF_IP_PRE_ROUTING; /* First hook for IPv4 */
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* Make our function first */
nf_register_hook(&nfho);
return 0;
}
I do agree with Rachit Jain, unless you have a valid reason to do this in Kernel space, I do suggest you use libpcap to do it in user-space.
Anyhow, if you just wanna copy the packet and then amend some data, I suggest you allocate a new skb with enough space to copy the data you already have in the skb you received + enough space to add a header.
Here's a code that I once used, it doesn't do any copying from an already existing skb but it can be useful to you. I am crafting a special kind of ICMP echo message here
int sendICMPEcho(unsigned char *msg, unsigned int length,
__be32 source, __be32 dest)
{
struct ethhdr *eth;
struct iphdr *iph;
struct icmphdr *icmph;
struct sk_buff *newPacket;
unsigned char *data;
unsigned int skbSize = length + sizeof(struct icmphdr)
+ sizeof(struct iphdr)
+ sizeof(struct ethhdr);
/* Allocate the skb */
newPacket = alloc_skb(skbSize, GFP_ATOMIC);
if(newPacket == NULL)
return SEND_FAIL_MEMORY;
/* Reserve the headers area */
skb_reserve(newPacket, sizeof(struct icmphdr)
+ sizeof(struct iphdr)
+ sizeof(struct ethhdr));
/* Extend the data area from 0 to the message length */
data = skb_put(newPacket, length);
/* Copy the data from the message buffer to the newPacket */
memcpy(data, msg, length);
/************** ICMP HEADER***************/
/* skb_push - pushing the icmp header in the packet data */
icmph = (struct icmphdr *) skb_push(newPacket,
sizeof(struct icmphdr));
/*set ICMP header here */
icmph->type = ICMP_ECHO;
icmph->code = 100; /* Our magic number */
icmph->un.echo.id = 0;
icmph->un.echo.sequence = htons(sendCounter);
icmph->checksum= 0;
icmph->checksum = in_cksum((unsigned short *)icmph,
sizeof(struct icmphdr) + length);
/************** END ICMP HEADER**************/
/************** IP HEADER ***************/
iph = (struct iphdr *) skb_push(newPacket,
sizeof(struct iphdr));
/* set IP header here */
iph->ihl = 5;/* 5 * 32(bits) */
iph->version = 4;
iph->tos = 255; /* Just a magic number - remove it */
iph->tot_len = htons( sizeof(struct iphdr)
+ sizeof(struct icmphdr)
+ length);
iph->id = 0;
iph->frag_off = 0; /* No fragementation */
iph->ttl = 65;
iph->protocol = IPPROTO_ICMP;
iph->saddr = source;
iph->daddr = dest;
iph->check = 0;
iph->check = in_cksum((unsigned short *)iph, sizeof(struct iphdr));
/************** END IP HEADER ***************/
/*WARNING: THE CODE BELOW SHOULD BE REPLACED BY SOMETHING MORE ROBUST
THAT USES THE KERNEL ROUTING!
AND USES IP_LOCAL_OUT INSTEAD OF WHAT WE ARE DOING */
/* Set up the net-device for the new packet */
/* In my code, there was a function findDeviceByIp that does the routing and return which net_device to use for transmission*/
newPacket->dev = findDeviceByIP(source);
if(newPacket->dev == NULL)
{
kfree_skb(newPacket);
return SEND_DEV_FAIL;
}
/************** ETH HEADER ***************/
eth = (struct ethhdr *) skb_push(newPacket, sizeof(struct ethhdr));
if(strcmp(newPacket->dev->name, "wlan0") == 0)
memcpy(eth->h_dest, wifiMAC, 6);
else if(strcmp(newPacket->dev->name, "eth0") == 0)
memcpy(eth->h_dest, etherMAC, 6);
else
{
kfree_skb(newPacket);
return SEND_FAIL_SEND;
}
memcpy(eth->h_source, newPacket->dev->dev_addr, 6);
eth->h_proto = htons(ETH_P_IP);
/************** END ETH HEADER ***************/
dev_queue_xmit(newPacket);/* Transmite the packet */
/* END OF THE WARNING AREA */
++sendCounter;
return SEND_SUCCESS;
}
There are many helper functions provided by linux kernel to work on skb's. it depends on the usecase which one you want to use.
skb_clone ==>copies the skbuff header and increments the reference counter for data buffer. If you are only interested in modifying the skbuff header then you can use skb_clone
skb_copy ==> copies skbuff header, data buffer as well as fragments. Use when you are interested in modifying the data in main buffer as well as in fragments buffer
pskb_copy ==> copies skbuff header + only the data buffer but not the fragments, thus if you want to modify skb except fragment buffer then you can use this one. which takes headroom also as arguement.
Better read the helper function provided by linux kernel(net/core/skbuff.c) to do the skb operations efficiently and to avoid any pitfalls.

Linux C Socket: Blocked on recv call

In my application i have created a thread for a simple http server, then from within my application i tried to connect to http server but control is blocked/hanged on recv call.
But if try to connect to my application's http server using linux GET command, I will be connected to http server successfully.
As per my understanding by searching the google i found that this is not the right approach.
But if i want to do this, in what should i create the sockets so that i can connect o my http server from within the application.
Below is how my http server socket created
pthread_create(&pt_server, NULL, http_srvr, NULL);
//http server handler
void *http_server()
{
int sockfd, new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
socklen_t sin_size;
struct sigaction sa;
int yes=1;
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(HTTP_PORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
perror("bind");
exit(1);
}
printf("Listening to sockets\n");
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1)
{
perror("accept");
continue;
}
printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
handle_connection(new_fd);
}
}
And following is how i am doing http POST to my http server
/* create socket */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return ERRSOCK;
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
/* connect to server */
if (connect(s, &server, sizeof(server)) < 0)
ret=ERRCONN;
else {
if (pfd) *pfd=s;
/* create header */
if (proxy) {
sprintf(header,
"%s http://%.128s:%d/%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
http_server1,
http_port,
url,
http_user_agent,
additional_header
);
} else {
sprintf(header,
"%s /%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
url,
http_user_agent,
additional_header
);
}
hlg=strlen(header);
/* send header */
if (send(s,header,hlg,0)!=hlg)
ret= ERRWRHD;
/* send data */
else if (length && data && (send(s,data,length,0)!=length) )
ret= ERRWRDT;
else {
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
and following are the contents of http_read_line, and in this function recv call blocked
static int http_read_line (fd,buffer,max)
int fd; /* file descriptor to read from */
char *buffer; /* placeholder for data */
int max; /* max number of bytes to read */
{ /* not efficient on long lines (multiple unbuffered 1 char reads) */
int n=0;
while (n<max) {
if (recv(fd,buffer,1,0)!=1) {
n= -n;
break;
}
n++;
if (*buffer=='\015') continue; /* ignore CR */
if (*buffer=='\012') break; /* LF is the separator */
buffer++;
}
*buffer=0;
return n;
}
You need to either send an HTTP 1.0 header, or else read about content-length in HTTP 1.1. You are reading the stream to EOS when the server is under no obligation to close the connection, so you block. The Content-Length header tells you how much data is in the body: you should only try to read that many bytes.
If you specify HTTP 1.0 (and no fancy headers) the server will close the connection after sending the response.
You have told "In my application i have created a thread for a simple http server, then from within my application
i tried to connect to http server but control is blocked/hanged on recv call."
That means the recv is never returning 0. Now when the recv function will
return a 0? ->When it gets a TCP FIN segment. It seems that your server is never
sending a TCP FIN segment to the client.
The reason that is most likely here is that, your client code needs modification.
You are sending data from from the client, but you are never sending the FIN,
so I assume that your server function is continuing forever and it had not
sent the FIN. This made the recv wait for ever.
In the current code perhaps the fix is to add a line
else {
/*Send the FIN segment, but we can still read the socket*/
shutdown(s, SHUT_WR);
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
In this case the shutdown function sends the TCP FIN and the server function can return and possibly then it would do a proper close.
And on a proper close, the FIN from the server will be received by the client. This would make the recv return 0, instead of getting blocked for ever.
Now if you want to continue any further data transfer from the client, you need to again connect or may be you need to have some different algorithm.
I hope my explanation may help fix the current problem.

Resources