sendmsg not working while sending file descriptor - c

I am using unix domain sockets to send open file descriptor between different processes. Unix domain sockets work fine but when i used sendmsg to send file descriptor something wierd happened. The function returns just after sendmsg and sendmsg not returning anything. And then recvmsg is giving Invalid Argument error. Here is my code.
Client (sending the file descriptor):
#include <stropts.h>
#include "accesories.c"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int send_err(int fd, int errcode, const char *msg);
int send_fd(int fd, int fd_to_send);
int main(int argc, char const *argv[])
{
int fd_to_send;
if((fd_to_send = open("vi",O_RDONLY)) < 0)
printf("vi open failed");
struct sockaddr_un address;
int socket_fd, nbytes;
char buffer[256];
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("socket() failed\n");
return 1;
}
/* start with a clean address structure */
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");
if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
{
printf("connect() failed\n");
return 1;
}
nbytes = snprintf(buffer, 256, "hello from a client");
write(socket_fd, buffer, nbytes);
nbytes = read(socket_fd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM SERVER: %s\n", buffer);
//sending the file descriptor
printf("From send_fd %d \n",send_fd(socket_fd,fd_to_send));
close(socket_fd);
exit(0);
}
int send_err(int fd, int errcode, const char *msg)
{
int n;
if ((n = strlen(msg)) > 0)
if (write(fd, msg, n) != n) /* send the error message */
return(-1);
if (errcode >= 0)
errcode = -1; /* must be negative */
if (send_fd(fd, errcode) < 0)
return(-1);
return(0);
}
int send_fd(int fd, int fd_to_send)
{
int temp;
struct iovec iov[1];
struct msghdr msg;
char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
iov[0].iov_base = buf;
iov[0].iov_len = 2;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (fd_to_send < 0) {
msg.msg_control = NULL;
msg.msg_controllen = 0;
buf[1] = -fd_to_send; /* nonzero status means error */
if (buf[1] == 0)
buf[1] = 1; /* -256, etc. would screw up protocol */
} else {
if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
return(-1);
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
cmptr->cmsg_len = CONTROLLEN;
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
*(int *)CMSG_DATA(cmptr) = fd_to_send; /* the fd to pass */
buf[1] = 0; /* zero status means OK */
}
buf[0] = 0; /* null byte flag to recv_fd() */
printf("before sendmsg \n");
if (temp = sendmsg(fd, &msg, 0) != 2)
{
printf("inside sendmsg condition %d\n",temp);
return(-1);
}
printf("after sendmsg %d\n",temp);
return(0);
}
Server (recv the file descriptor):
#include <stropts.h>
#include "accesories.c"
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#define MAXLINE 10
/* size of control buffer to send/recv one file descriptor */
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t));
ssize_t errcheckfunc(int a,const void *b, size_t c);
int connection_handler(int connection_fd);
int main(int argc, char const *argv[])
{
struct sockaddr_un address;
int socket_fd, connection_fd;
socklen_t address_length;
pid_t child;
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("socket() failed\n");
return 1;
}
unlink("./demo_socket");
/* start with a clean address structure */
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");
if(bind(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
{
printf("bind() failed\n");
return 1;
}
if(listen(socket_fd, 5) != 0)
{
printf("listen() failed\n");
return 1;
}
while((connection_fd = accept(socket_fd, (struct sockaddr *) &address,&address_length)) > -1)
{
connection_handler(connection_fd);
int fd_to_recv;
fd_to_recv = recv_fd(socket_fd,&errcheckfunc);
if(read(fd_to_recv,msgbuf,5) < 0)
printf("message read failed");
printf("message received:%s\n",msgbuf);
close(connection_fd);
}
close(socket_fd);
unlink("./demo_socket");
return 0;
}
int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t))
{
int newfd, nr, status;
char *ptr;
char buf[MAXLINE];
struct iovec iov[1];
struct msghdr msg;
status = -1;
for ( ; ; )
{
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
return(-1);
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
if ((nr = recvmsg(fd, &msg, 0)) < 0)
{
printf("recvmsg errrrror %d %d %s\n",nr,errno,strerror(errno));
//perror("recvmsg errrrror");
} else if (nr == 0)
{
perror("connection closed by server");
return(-1);
}
/*
* See if this is the final data with null & status. Null
* is next to last byte of buffer; status byte is last byte.
* Zero status means there is a file descriptor to receive.
*/
for (ptr = buf; ptr < &buf[nr]; )
{
if (*ptr++ == 0)
{
if (ptr != &buf[nr-1])
perror("message format error");
status = *ptr & 0xFF; /* prevent sign extension */
if (status == 0)
{
if (msg.msg_controllen != CONTROLLEN)
perror("status = 0 but no fd");
newfd = *(int *)CMSG_DATA(cmptr);
} else
{
newfd = -status;
}
nr -= 2;
}
}
if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)
return(-1);
if (status >= 0) /* final data has arrived */
return(newfd); /* descriptor, or -status */
}
}
ssize_t errcheckfunc(int a,const void *b, size_t c)
{
return 0;
}
int connection_handler(int connection_fd)
{
int nbytes;
char buffer[256];
nbytes = read(connection_fd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM CLIENT: %s\n", buffer);
nbytes = snprintf(buffer, 256, "hello from the server");
write(connection_fd, buffer, nbytes);
return 0;
}
output of client:
MESSAGE FROM SERVER: hello from the server
before sendmsg
Not even inside sendmsg condition and after sendmsg prints?

Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.
Your call to recvmsg() is trying to receive data from a listening socket rather than a connected socket, because your main loop is passing socket_fd to recv_fd() rather than connection_fd. – Matthew Slattery

Related

TCP sockets, send files, client server, server doest save a whole file, C language

I wrote a simple protocol in which Im able to exchange files / text messages between client and server. If the client send a text to server, server should simply echo it back. On the other hand, when client send a special command (for example, SEND_TXT_FILE) server should receive a file uploaded by client to the server.
I almost got it work. However, there's still problem with sending files. Sever does not save the whole file, it only creates it and disconnects.
Here's the protocol:
CLIENT ---------- text1 ----------> SERVER
CLIENT <---------- text1 ---------- SERVER
CLIENT ---------- text2 ----------> SERVER
CLIENT <---------- text3 ---------- SERVER
CLIENT ---------- SENDTXTFILE ----------> SERVER
CLIENT <---------- OK ---------- SERVER
CLIENT ---------- FILENAME ----------> SERVER
CLIENT <---------- OK ---------- SERVER
CLIENT ---------- file content ----------> SERVER
CLIENT <--------- FILE_UPLOADED --------- SERVER
CLIENT ---------- text3 ----------> SERVER
CLIENT <---------- text3 ---------- SERVER
How can I solve this?
server.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#define BUFFER_SIZE 1024
int send_all(int sockfd, const char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = send(sockfd, buf, len, 0);
if (n < 0)
return -1;
buf += n;
len -= n;
}
return 0;
}
int recv_all(int sockfd, char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = recv(sockfd, buf, len, 0);
if (n <= 0)
return n;
buf += n;
len -= n;
}
return 1;
}
int recv_txt_file(int sockfd, int len, const char *filename)
{
FILE *fp = fopen(filename, "wb");
int total = 0, b = 0;
char buffer[BUFFER_SIZE];
memset(buffer, '\0', BUFFER_SIZE);
if (fp != NULL)
{
while (recv_all(sockfd, buffer, len) != 1)
{
total += b;
fwrite(buffer, 1, b, fp);
}
printf("Received byte: %d\n",total);
if (b < 0)
perror("Receiving");
fclose(fp);
}
else
{
perror("File");
}
close(sockfd);
}
int main()
{
int port = 6666;
int server_fd, client_fd, read;
struct sockaddr_in server, client;
char buffer[BUFFER_SIZE], filename[BUFFER_SIZE];
char remote_ip[16];
int remote_port, res = 0;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("Could not create socket");
return 1;
}
int optval = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("Could not bind socket");
close(server_fd);
return 1;
}
if (listen(server_fd, 1) < 0)
{
perror("Could not listen on socket");
close(server_fd);
return 1;
}
printf("Server TCP is listening on port %d ... \n", port);
socklen_t client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *)&client, &client_len);
if (client_fd < 0)
{
perror("Could not establish new connection");
close(server_fd);
return 1;
}
remote_port = ntohs(client.sin_port);
inet_ntop(AF_INET, &client.sin_addr, remote_ip, sizeof(remote_ip));
printf("Client IP address: %s, port %d\n", remote_ip, remote_port);
while (1)
{
read = recv_all(client_fd, buffer, BUFFER_SIZE);
if (read <= 0)
{
if (read < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
break;
}
if ((res = strcmp(buffer, "SENDFILE_TXT\n") == 0))
{
printf("-------FROM CLIENT: %s-------\n", buffer);
memset(buffer, BUFFER_SIZE, '\0');
strcpy(buffer, "OK");
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
read = recv_all(client_fd, buffer, BUFFER_SIZE);
if (read <= 0)
{
if (read < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
break;
}
printf("-------FROM CLIENT: %s-------\n", buffer);
memset(filename, '\0', BUFFER_SIZE);
strcpy(filename, buffer);
memset(buffer, BUFFER_SIZE, '\0');
strcpy(buffer, "OK");
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
recv_txt_file(client_fd, BUFFER_SIZE, filename);
}
else
{
printf("FROM CLIENT: %.*s\n", BUFFER_SIZE, buffer);
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
}
}
close(client_fd);
close(server_fd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#define BUFFER_SIZE 1024
socklen_t hostname_to_ip_port(char *hostname, int port, struct sockaddr_storage *addr)
{
int sockfd;
struct addrinfo hints, *servinfo;
int rv;
char service[20];
sprintf(service, "%d", port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if ((rv = getaddrinfo(hostname, service, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 0;
}
socklen_t addrlen = servinfo->ai_addrlen;
memcpy(addr, servinfo->ai_addr, addrlen);
freeaddrinfo(servinfo);
return addrlen;
}
int send_all(int sockfd, const char *buf, size_t len)
{
ssize_t n;
while (len > 0)
{
n = send(sockfd, buf, len, 0);
if (n < 0)
return -1;
buf += n;
len -= n;
}
return 0;
}
int recv_all(int sockfd, char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = recv(sockfd, buf, len, 0);
if (n <= 0)
return n;
buf += n;
len -= n;
}
return 1;
}
long int get_file_size(char filename[])
{
// opening the file in read mode
FILE *fp = fopen(filename, "r");
// checking if the file exist or not
if (fp == NULL) {
printf("File Not Found!\n");
return -1;
}
fseek(fp, 0L, SEEK_END);
// calculating the size of the file
long int res = ftell(fp);
// closing the file
fclose(fp);
return res;
}
int send_txt_file(int sockfd, int len, const char *filename)
{
FILE *fp = fopen(filename, "r");
int b;
char buffer[BUFFER_SIZE];
memset(buffer, '\0', BUFFER_SIZE);
if (fp == NULL)
{
perror("Error while openning file");
return 0;
}
while ((b = fread(buffer, 1, BUFFER_SIZE, fp)) > 0){
send_all(sockfd, buffer, BUFFER_SIZE);
memset(buffer, '\0', BUFFER_SIZE);
}
fclose(fp);
return 1;
}
int main(int argc, char **argv)
{
char *hostname = "127.0.0.1";
int port = 6666;
char buffer[BUFFER_SIZE], fname[BUFFER_SIZE];
int sockfd, err, res;
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
server_addr_len = hostname_to_ip_port(hostname, port, &server_addr);
if (server_addr_len == 0)
return 1;
sockfd = socket(server_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
perror("Could not create socket");
return 1;
}
if (connect(sockfd, (struct sockaddr *)&server_addr, server_addr_len) < 0)
{
perror("Could not connect socket");
return 1;
}
while (1)
{
memset(buffer, BUFFER_SIZE, '\0');
memset(fname, BUFFER_SIZE, '\0');
printf("> ");
if (!fgets(buffer, BUFFER_SIZE, stdin))
break;
if (strstr(buffer, "SENDFILE_TXT") != NULL)
{
if (send_all(sockfd, buffer, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
memset(buffer, BUFFER_SIZE, '\0');
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
if ((res = strcmp(buffer, "OK") == 0))
{
printf("-------FROM SERVER: %s-------\n", buffer);
printf("Give filename> ");
memset(fname, BUFFER_SIZE, '\0');
if (!fgets(fname, BUFFER_SIZE, stdin))
break;
if (send_all(sockfd, fname, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
}
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
fname[strlen(fname)-1] = 0;
printf("----%s----\n", fname);
send_txt_file(sockfd, BUFFER_SIZE, fname);
printf("FROM SERVER: %.*s\n", BUFFER_SIZE, buffer);
}
else
{
if (send_all(sockfd, buffer, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
printf("FROM SERVER: %.*s\n", BUFFER_SIZE, buffer);
}
}
close(sockfd);
return 0;
}
The way you are using send_txt_file() and recv_txt_file() to transfer a file is not correct.
On the server side:
When a client connects, the server waits for the client to send exactly BUFFER_SIZE (1024) bytes for each command, no more, no less (which is a waste of bandwidth for short commands). When a SENDFILE_TXT command is received, the server reads the filename from the client (which will cause a buffer overflow if the filename is exactly BUFFER_SIZE bytes in length), and then calls recv_txt_file().
recv_txt_file() attempts to read from the client in a loop, reading in exactly BUFFER_SIZE chunks. However, the while loop being used is coded incorrectly. It is checking the return value of recv_all() for failure, not for success. The != check needs to be changed to == instead. And also, the b variable that is being used to increment total, and tell fwrite() how many bytes to write, is never set to any value other than 0. It needs to be set to BUFFER_SIZE instead, since that is how many bytes have actually been read if recv_all() returns 1.
However, even if the while loop were coded properly, the transfer would still not operate properly, because it requires the file to be sent in even multiples of BUFFER_SIZE. If the file is not an even multiple in size, recv_all() will end up waiting for data that the client does not send, until an error occurs on the socket.
Also, recv_txt_file() is closing the connection after the transfer is finished. It should not do that, as that will prevent the client from being able to send further commands after sending a file. The client is not closing its end of the connection after sending a file, so the server should not be closing its end after receiving a file.
On the client side:
When the client sends a SENDFILE_TXT command and gets an acknowledgement back, it calls send_txt_file(), which reads the file in a loop, sending it to the server in BUFFER_SIZE sized chunks. If the file size is not an even multiple of BUFFER_SIZE, the last block sent will just waste bandwidth and send random garbage to the server. Also, you are ignoring the return value of send_all() to break the loop if an error occurs on the socket.
The client should send the actual file size to the server before sending the file data. The server can then read that size value first so it knows when to stop reading.
That being said, try something more like the following:
server.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <stdint.h>
#define BUFFER_SIZE 1024
int send_all(int sockfd, const void *buf, int len)
{
ssize_t n;
const char *pbuf = (const char*) buf;
while (len > 0)
{
n = send(sockfd, pbuf, len, 0);
if (n < 0)
{
perror("Client write failed");
return n;
}
pbuf += n;
len -= n;
}
return 0;
}
int send_uint32(int sockfd, uint32_t value)
{
value = htonl(value);
if (send_all(sockfd, &value, sizeof(value)) < 0)
return -1;
return 0;
}
int send_str(int sockfd, const char *s)
{
uint32_t len = strlen(s);
int res = send_uint32(sockfd, len);
if (res == 0)
res = send_all(sockfd, s, len);
return res;
}
int recv_all(int sockfd, void * buf, int len)
{
ssize_t n;
char *pbuf = (char*) buf;
while (len > 0)
{
n = recv(sockfd, pbuf, len, 0);
if (n <= 0)
{
if (n < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
return n;
}
pbuf += n;
len -= n;
}
return 1;
}
int recv_uint32(int sockfd, uint32_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohl(*value);
return res;
}
int recv_uint64(int sockfd, uint64_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohll(*value); // <-- use any implementation of your choosing...
return res;
}
int recv_str(int sockfd, char **str)
{
uint32_t len;
int res = recv_uint32(sockfd, &len);
if (res <= 0)
return res;
*str = (char*) malloc(len + 1);
if (*str == NULL)
{
perror("Could not allocate memory");
return -1;
}
res = recv_all(sockfd, *str, len);
if (res <= 0)
free(*str);
else
(*str)[len] = '\0';
return res;
}
int recv_txt_file(int sockfd)
{
char *filename;
uint64_t filesize;
if (recv_str(sockfd, &filename) <= 0)
return -1;
int res = recv_uint64(sockfd, &filesize);
if (res <= 0)
{
free(filename);
return -1;
}
printf("-------FROM CLIENT: %s-------\n", filename);
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
perror("Could not create file");
free(filename);
send_str(sockfd, "NO");
return 0;
}
free(filename);
// optional: pre-size the new file to the specified filesize...
if (send_str(sockfd, "OK") < 0)
return -1;
char buffer[BUFFER_SIZE];
int b;
uint64_t total = 0;
while (filesize > 0)
{
b = (filesize > BUFFER_SIZE) ? BUFFER_SIZE : (int) filesize;
res = recv_all(sockfd, buffer, b);
if (res <= 0)
{
fclose(fp);
return -1;
}
if (fwrite(buffer, b, 1, fp) < 1)
{
perror("Could not write to file");
fclose(fp);
send_str(sockfd, "ERROR");
return -1;
}
total += b;
filesize -= b;
}
fclose(fp);
printf("Received bytes: %lu\n", total);
if (send_str(sockfd, "OK") < 0)
return -1;
return 1;
}
int main()
{
int port = 6666;
int server_fd, client_fd, read;
struct sockaddr_in server, client;
char filename[BUFFER_SIZE], *cmd;
char remote_ip[16];
int remote_port, res = 0;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("Could not create socket");
return 1;
}
int optval = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd, (struct sockaddr *) &server, sizeof(server)) < 0)
{
perror("Could not bind socket");
close(server_fd);
return 1;
}
if (listen(server_fd, 1) < 0)
{
perror("Could not listen on socket");
close(server_fd);
return 1;
}
printf("Server TCP is listening on port %d ... \n", port);
socklen_t client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len);
if (client_fd < 0)
{
perror("Could not establish new connection");
close(server_fd);
return 1;
}
remote_port = ntohs(client.sin_port);
inet_ntop(AF_INET, &client.sin_addr, remote_ip, sizeof(remote_ip));
printf("Client IP address: %s, port %d\n", remote_ip, remote_port);
while (recv_str(client_fd, &cmd) > 0)
{
printf("-------FROM CLIENT: %s-------\n", cmd);
if (strcmp(cmd, "SENDFILE_TXT") == 0)
{
if (recv_txt_file(client_fd) < 0)
break;
}
else
{
if (send_str(client_fd, cmd) < 0)
{
free(cmd);
break;
}
}
free(cmd);
}
close(client_fd);
close(server_fd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <stdint.h>
#define BUFFER_SIZE 1024
socklen_t hostname_to_ip_port(char *hostname, int port, struct sockaddr_storage *addr)
{
int sockfd;
struct addrinfo hints, *servinfo;
int rv;
char service[20];
sprintf(service, "%d", port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if ((rv = getaddrinfo(hostname, service, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 0;
}
socklen_t addrlen = servinfo->ai_addrlen;
memcpy(addr, servinfo->ai_addr, addrlen);
freeaddrinfo(servinfo);
return addrlen;
}
int send_all(int sockfd, const void *buf, size_t len)
{
ssize_t n;
const char *pbuf = (const char *) buf;
while (len > 0)
{
n = send(sockfd, pbuf, len, 0);
if (n < 0)
{
perror("Server write failed");
return n;
}
pbuf += n;
len -= n;
}
return 0;
}
int send_uint32(int sockfd, uint32_t value)
{
value = htonl(value);
return send_all(sockfd, &value, sizeof(value));
}
int send_uint64(int sockfd, uint64_t value)
{
value = htonll(value); // <-- use any implementation of your choosing...
return send_all(sockfd, &value, sizeof(value));
}
int send_str(int sockfd, const char *s)
{
uint32_t len = strlen(s);
int res = send_uint32(sockfd, len);
if (res == 0)
res = send_all(sockfd, s, len);
return res;
}
int recv_all(int sockfd, void *buf, int len)
{
ssize_t n;
char *pbuf = (char*) buf;
while (len > 0)
{
n = recv(sockfd, pbuf, len, 0);
if (n <= 0)
{
if (n < 0)
perror("Server read failed");
else
printf("Server disconnected\n");
return n;
}
pbuf += n;
len -= n;
}
return 1;
}
int recv_uint32(int sockfd, uint32_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohl(*value);
return res;
}
int recv_str(int sockfd, char **str)
{
uint32_t len;
int res = recv_uint32(sockfd, &len);
if (res <= 0)
return res;
*str = (char*) malloc(len + 1);
if (*str == NULL)
{
perror("Could not allocate memory");
return -1;
}
res = recv_all(sockfd, *str, len);
if (res <= 0)
free(*str);
else
(*str)[len] = '\0';
return res;
}
int send_txt_file(int sockfd, const char *filename)
{
char *resp;
int res;
long int filesize;
char buffer[BUFFER_SIZE];
int b;
FILE* fp = fopen(filename, "rb");
if (fp == NULL)
{
printf("Could not open file!\n");
return 0;
}
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (filesize < 0)
{
fclose(fp);
return 0;
}
if (send_str(sockfd, "SENDFILE_TXT") < 0)
{
fclose(fp);
return -1;
}
if (send_str(sockfd, filename) < 0)
{
fclose(fp);
return -1;
}
if (send_uint64(sockfd, filesize) < 0)
{
fclose(fp);
return -1;
}
res = recv_str(sockfd, &resp);
if (res <= 0)
return -1;
printf("-------FROM SERVER: %s-------\n", resp);
if (strcmp(resp, "OK") != 0)
{
free(resp);
fclose(fp);
return 0;
}
free(resp);
while (filesize > 0)
{
b = (filesize > BUFFER_SIZE) ? BUFFER_SIZE : (int) filesize;
b = fread(buffer, 1, b, fp);
if (b < 1)
{
fclose(fp);
return -1;
}
if (send_all(sockfd, buffer, b) < 0)
{
fclose(fp);
return -1;
}
filesize -= b;
}
fclose(fp);
res = recv_str(sockfd, &resp);
if (res <= 0)
return -1;
printf("-------FROM SERVER: %s-------\n", resp);
free(resp);
return 0;
}
int prompt(const char *text, char **input)
{
*input = NULL;
size_t size = 0;
printf("%s> ", text);
ssize_t len = getline(input, &size, stdin);
if (len < 0)
return len;
if ((*input)[len-1] == '\n')
{
--len;
(*input)[len] = '\0';
}
return len;
}
int main()
{
char *hostname = "127.0.0.1";
int port = 6666;
char *cmd, *resp, *fname;
size_t size;
ssize_t len;
int sockfd, res;
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
server_addr_len = hostname_to_ip_port(hostname, port, &server_addr);
if (server_addr_len == 0)
return 1;
sockfd = socket(server_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
perror("Could not create socket");
return 1;
}
if (connect(sockfd, (struct sockaddr *) &server_addr, server_addr_len) < 0)
{
perror("Could not connect socket");
return 1;
}
while (prompt("", &cmd) >= 0)
{
if (strcmp(cmd, "SENDFILE_TXT") == 0)
{
if (prompt("Give filename", &fname) < 0)
break;
if (send_txt_file(sockfd, fname) < 0)
break;
free(fname);
}
else
{
if (send_str(sockfd, cmd) < 0)
{
free(cmd);
close(sockfd);
return 1;
}
if (recv_str(sockfd, &resp) <= 0)
break;
printf("FROM SERVER: %s\n", resp);
free(resp);
}
free(cmd);
}
close(sockfd);
return 0;
}
regarding:
int total = 0, b = 0;
and
while(recv_all(sockfd, buffer, len) != 1)
{
total += b;
fwrite(buffer, 1, b, fp);
the total and b never move from their initial value of 0. So 0 bytes are written to the output file.

Read data from fd which was received from fusermount failed

I want to test fuse and copy some code from libfuse like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#define xlog(_fmt_, ...) \
printf("\033[0;33m[%s:%s:%d]\033[0m " _fmt_ "\n", __FILE__, __func__, \
__LINE__, ##__VA_ARGS__)
static int receive_fd(int fd) {
struct msghdr msg;
struct iovec iov;
char buf[1];
int rv;
size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
struct cmsghdr *cmsg;
iov.iov_base = buf;
iov.iov_len = 1;
memset(&msg, 0, sizeof(msg));
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = ccmsg;
msg.msg_controllen = sizeof(ccmsg);
while (((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR) {
}
if (rv == -1) {
xlog("recvmsg: %s", strerror(errno));
return -1;
}
if (!rv) {
return -1;
}
cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg->cmsg_type != SCM_RIGHTS) {
xlog("got control message of unknown type %d", cmsg->cmsg_type);
return -1;
}
return *(int *)CMSG_DATA(cmsg);
}
static int fuse_mount(const char *mountpoint) {
int fds[2];
int res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
if (res != 0) {
xlog("socketpair: %s", strerror(errno));
return -1;
}
pid_t pid = fork();
if (pid == -1) {
close(fds[0]);
close(fds[1]);
return -1;
}
if (pid == 0) {
const char *argv[32];
unsigned argc = 0;
argv[argc++] = "--";
argv[argc++] = mountpoint;
argv[argc++] = NULL;
char env[16];
snprintf(env, sizeof(env), "%i", fds[0]);
setenv("_FUSE_COMMFD", env, 1);
execvp("fusermount", (char **)argv);
xlog("exec: %s", strerror(errno));
exit(1);
}
int rv = receive_fd(fds[1]);
if (rv >= 0) {
fcntl(rv, F_SETFD, FD_CLOEXEC);
}
waitpid(pid, NULL, 0);
close(fds[0]);
close(fds[1]);
return rv;
}
#define MOUNT_POINT "/tmp/xx"
#define BUFFER_SIZE 1024
int main(int argc, char **argv) {
int fd = fuse_mount(MOUNT_POINT);
if (fd < 0) {
xlog("fuse mount fail");
return -1;
}
char buf[BUFFER_SIZE];
ssize_t n = read(fd, buf, sizeof(buf));
if (n < 0) {
xlog("read: %s", strerror(errno));
} else {
xlog("ok");
}
return 0;
}
It received the /dev/fuse filehandle from fusermount and read data from it.
The code run ok on most platforms, but when I run it at virtualbox, I got the error read: Invalid argument at line 107:
ssize_t n = read(fd, buf, sizeof(buf));
Then I check the code of libfuse, it set the buffer size like this:
se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE;
So I set a larger BUFFER_SIZE like 10240 in my code and it run ok. I feel confused because I think 1024 was large enough and the data size I read from fd was only 56.
My question is in system call:
ssize_t read(int fd, void *buf, size_t count);
Why the size of count would cause Invalid argument error?
Why it was ok when I set count to 1024 on normal platforms, but it was failed on virtualbox?
Thanks for #user253751 tip, I read the code fs/fuse/dev.c and found this:
static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
struct fuse_copy_state *cs, size_t nbytes) {
// ...
if (nbytes < max_t(size_t, FUSE_MIN_READ_BUFFER,
sizeof(struct fuse_in_header) +
sizeof(struct fuse_write_in) + fc->max_write))
return -EINVAL;
// ...
}
The nbytes check was added since linux v5.4, so if the kernel version newer than v5.3.18, the small nbytes would cause EINVAL error.

C: How do you read and unpack a message over a socket?

I am trying to receive an updated message over the socket and unpack the
data. I want to use the update_client function to update the server's
internal representation of the client co-ordinates and respond to the
client with the required value.
I think I should use recv to read the full message (of length UPDATE_CMD_LEN) into buffer.
Then unpack data having message in the following format:
U<id><fields of gpscoords>
Character 'U' (meaning update), followed by ID of the client (unsigned char)
and then the gpscoord structure in network format.
This is a rough idea of how to do it but I don't know how:
[1] Unpack ID
[2] Unpack coordinates with int update_client(unsigned char id, gpscoords *pos
which takes ID and pointer to position struct, returns -1 on error and 0 on success
The server should then respond to client using sendto function and reply
NO_RES on error and SUCC_RES on success.
void do_update(int sockfd, struct sockaddr_in *clientaddr) {
char msg[UPDATE_CMD_LEN];
recv(sockfd, msg, UPDATE_CMD_LEN, flags);
fprintf(stderr, "do_update not implemented!\n");
exit(-1);
}
client.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "gps.h"
#define CMD_LEN 10
#define LINE_LEN NAME_LEN + 15
#define FIREFOX "/usr/bin/firefox"
void make_route_url(char *url, gpscoords *pos, gpscoords *target) {
char pos_sign_n;
char pos_sign_e;
char tar_sign_n;
char tar_sign_e;
pos_sign_n = pos->north ? '+' : '-';
pos_sign_e = pos->east ? '+' : '-';
tar_sign_n = target->north ? '+' : '-';
tar_sign_e = target->east ? '+' : '-';
sprintf(url,
"http://map.project-osrm.org/?z=13&loc=%c%u.%u,%c%u.%u&loc=%c%u.%u,%c%u.%u",
pos_sign_n, pos->lat, pos->lat_frac,
pos_sign_e, pos->lon, pos->lon_frac,
tar_sign_n, target->lat, target->lat_frac,
tar_sign_e, target->lon, target->lon_frac);
}
void show_route(gpscoords *pos, gpscoords *target) {
// char url[75];
fprintf(stderr, "show_route not implemented!\n");
exit(-1);
}
int run_client(char *name, char *serverip) {
char *cmdline;
size_t line_len;
char cmd[11];
gpsinfo info;
gpscoords pos, targetpos;
char targetname[NAME_LEN];
bzero(targetname, NAME_LEN);
bzero(&info, sizeof(info));
bzero(&pos, sizeof(pos));
bzero(&targetpos, sizeof(targetpos));
if (register_client(serverip, name, &info) == -1) {
perror("Error registering client.");
return -1;
}
find_self(&pos);
if (update_position(&info, &pos) == -1) {
printf("Failed to send initial position to server.\n");
exit(-1);
}
for (;;) {
printf("Command: ");
line_len = 0;
cmdline = NULL;
getline(&cmdline, &line_len, stdin);
if (strncmp(cmdline, "update", 3) == 0) {
find_self(&pos);
if (update_position(&info, &pos) == -1) {
printf("Failed to update.\n");
}
} else if (strncmp(cmdline, "find", 3) == 0) {
sscanf(cmdline, "%10s %25s", cmd, targetname);
if (get_position(&info, targetname, &targetpos) == -1) {
printf("Couldn't find target.\n");
} else {
show_route(&pos, &targetpos);
}
} else if (strncmp(cmdline, "quit", 4) == 0) {
free(cmdline);
break;
}
free(cmdline);
}
if (unregister_client(&info) == -1) {
perror("Error unregistering client.");
return -1;
}
return 0;
}
int main(int argc, char **argv) {
if (argc < 2) {
printf("Usage: ./client <client name> <server_ip>\n");
return 0;
}
return run_client(argv[1], argv[2]);
}
socket.c
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "sockets.h"
int create_dg_socket(in_port_t port) {
// Create the socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
printf("create_dg_socket cannot create socket!");
return -1;
}
// Bind the socket port
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
printf("create_dg_socket cannot bind socket!");
close(sockfd);
return -1;
}
return sockfd;
}
int open_dg_socket(char *ipaddr, in_port_t port, struct sockaddr_in *addr) {
// Create the socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
return -1;
}
// Setup
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
addr->sin_addr.s_addr = inet_addr(ipaddr);
return sockfd;
}
int dg_sendrecv(int sockfd,
struct sockaddr_in *addr,
char *message, size_t out_len,
char *response, size_t in_len) {
if (sendto(sockfd, message, out_len, 0, (struct sockaddr *)&addr, sizeof(*addr)) == -1) {
printf("dg_sendrecv cannot send data!");
return -1;
}
int ret = recvfrom(sockfd, response, in_len, 0, NULL, NULL);
if (ret == -1) {
printf("dg_sendrecv cannot read data!");
return -1;
}
return ret;
}
/*
int dg_sendrecv(int sockfd,
struct sockaddr_in *addr,
char *message, size_t out_len,
char *response, size_t in_len) {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ipaddr);
return dg_sendrecv_addr(sockfd, &addr, message, out_len, response, in_len);
}
*/
server.c
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include "sockets.h"
#include "gps.h"
#include "server.h"
#define NUM_CLIENTS 5
char clients[NUM_CLIENTS][NAME_LEN];
gpscoords coords[NUM_CLIENTS];
void do_register(int sockfd, struct sockaddr_in *clientaddr) {
char msg[REGISTER_CMD_LEN];
char *name;
unsigned char id, i;
if (recv(sockfd, msg, REGISTER_CMD_LEN, 0) == -1) {
perror("Error receiving register command.");
exit(-1);
}
// first byte is server command, rest is name
name = &msg[1];
id = NO_RES;
for (i = 0; i < NUM_CLIENTS; ++i) {
if (clients[i][0] == '\0') {
strncpy(clients[i], name, NAME_LEN);
clients[i][NAME_LEN - 1] = '\0';
bzero(&coords[i], sizeof(coords[i]));
id = i + 1;
break;
}
}
if (sendto(sockfd,
&id, sizeof(id),
0,
(struct sockaddr *)clientaddr, sizeof(*clientaddr)) == -1) {
perror("Error replying to client.");
exit(-1);
}
}
void do_unregister(int sockfd, struct sockaddr_in *clientaddr) {
unsigned char msg[UNREGISTER_CMD_LEN];
unsigned char id;
char res = NO_RES;
if (recv(sockfd, &msg, UNREGISTER_CMD_LEN, 0) == -1) {
perror("Error receiving unregister command.");
exit(-1);
}
id = msg[1] - 1;
if (id < NUM_CLIENTS) {
clients[id][0] = '\0';
res = SUCC_RES;
}
if (sendto(sockfd,
&res, sizeof(res),
0,
(struct sockaddr *)clientaddr, sizeof(*clientaddr)) == -1) {
perror("Error responding to client.");
exit(-1);
}
}
int update_client(unsigned char id, gpscoords *pos) {
id -= 1;
if (id >= NUM_CLIENTS)
return -1;
memcpy(&coords[id], pos, sizeof(coords[id]));
return 0;
}
void do_update(int sockfd, struct sockaddr_in *clientaddr) {
/**
* Receive an update message over the socket and unpack the
* data. Then use the update_client function to update the server's
* internal representation of the client co-ordinates. Then respond to the
* client with the required value.
* Use recv to read the full message (of length UPDATE_CMD_LEN) into buffer.
* Unpack data, message of following format:
* U<id><fields of gpscoords>
* Character 'U' (meaning update), followed by ID of the client (unsigned char)
* and then the gpscoord structure in network format.
*
* 1. Unpack ID
* 2. Unpack coordinates with (int update_client(unsigned char id, gpscoords *pos)
* which takes ID and pointer to position struct, returns -1 on error and 0 on success
* Server should then respond to client using sendto function and reply
* NO_RES on error and SUCC_RES on success.
*/
char msg[UPDATE_CMD_LEN];
recv(sockfd, msg, UPDATE_CMD_LEN, flags);
fprintf(stderr, "do_update not implemented!\n");
exit(-1);
}
static gpscoords *get_client(char *name) {
for (int i = 0; i < NUM_CLIENTS; ++i) {
if (strcmp(name, clients[i]) == 0) {
return &coords[i];
break;
}
}
return NULL;
}
void do_get(int sockfd, struct sockaddr_in *clientaddr) {
char msg[GET_CMD_LEN];
char res[GET_RES_LEN];
char *name;
if (recv(sockfd, msg, GET_CMD_LEN, 0) == -1) {
perror("Error receiving get command.");
exit(-1);
}
name = &msg[1];
name[NAME_LEN - 1] = '\0';
gpscoords *pos = get_client(name);
if (pos == NULL) {
res[0] = NO_RES;
} else {
res[0] = SUCC_RES;
pack_gpscoords(pos, &res[1]);
}
if (sendto(sockfd,
&res, GET_RES_LEN,
0,
(struct sockaddr *)clientaddr, sizeof(*clientaddr)) == -1) {
perror("Error sending result to client.");
exit(-1);
}
}
int serve(int sockfd) {
char cmd;
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
for (;;) {
if (recvfrom(sockfd,
&cmd, sizeof(cmd),
MSG_PEEK,
(struct sockaddr *)&clientaddr, &clientaddrlen) == -1) {
exit(-1);
}
switch (cmd) {
case CMD_REGISTER:
do_register(sockfd, &clientaddr);
break;
case CMD_UNREGISTER:
do_unregister(sockfd, &clientaddr);
break;
case CMD_UPDATE:
do_update(sockfd, &clientaddr);
break;
case CMD_GET:
do_get(sockfd, &clientaddr);
break;
default:
break;
}
}
}
void init_info() {
int i;
for (i = 0; i < NUM_CLIENTS; ++i) {
clients[i][0] = '\0';
}
}
int main(int argc, char **argv) {
int sockfd = create_dg_socket(SERVER_PORT);
if (sockfd == -1) {
fprintf(stderr, "Error creating socket!\n");
return -1;
}
init_info();
printf("Server ready.\n");
serve(sockfd);
// Note: server never terminates, we're relying on the system to clean up
// our open socket file descriptor (and shut it down)
return 0;
}
If you are looking for a way to safe and read data from char arrays that maybe will help you:
char buffer[5+sizeof(gpscoords)];
int *int_pointer;
gpscoords *gps_pointer;
int id=1;
gpscoords gps="some gps contend";
int id_frombuffer;
gpscoords gps_frombuffer;
char c_frombuffer;
buffer[0]='U';
int_pointer=(int*)&buffer;
int_pointer+=1;
*int_pointer=id;
gps_pointer=(gpscoords*)&buffer;
gps_pointer+=5;
*gpspointer=gps;
//send buffer
c_frombuffer=buffer[0];//c_frombuffer now contains 'U'
int_pointer=(int*)&buffer;
int_pointer+=1;
id_frombuffer=*int_pointer;//id_frombuffer now contains 1
gps_pointer=(gpscoords*)&buffer;
gpscoords+=5;
gps_frombuffer=*pointer;//gps_frombuffer now contains "some gps contend"
As an example if your client code looks like that:
void sendupdate(unsigned char id,gpscoords gps)
{
char buffer[2+sizeof(gpscoords)];
gpscoords *gps_pointer;
buffer[0]='U';
buffer[1]=id;
gps_pointer=(gpscoords*)&buffer;
gps_pointer+=2;
*gpspointer=gps;
send(socket, buffer, 2+sizeof(gpscoords), flags);
}
your server function should look like that:
void do_update(int sockfd, struct sockaddr_in *clientaddr)
{
char buffer[2+sizeof(gpscoords)];
recv(sockfd, buffer, 2+sizeof(gpscoords), flags);
unsigned char id_frombuffer=buffer[1];
gpscoords *gpspointer=(gpscoords*)&buffer;
gpspointer+=2;
update_client(id_frombuffer, gpspointer);
}

error: too many arguments to function ‘strcasecmp’ "Head First C"

I'm trying to get the Internet Knock Knock Protocol Server from O'Reilly's Head First C working but when I try to compile it I receive the following Error.
ikkp_server.c:106:4: error: too many arguments to function ‘strcasecmp’
ikkp_server.c:111:6: error: too many arguments to function ‘strcasecmp’
those lines correspond to the lines,
if(strcasecmp("Who's there?", buf,12))
if(strcasecmp("Oscar who?", buf,10))
The full code is
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void error(char *msg){
fprintf(stderr, "\n\t\t%s: %s\n\n", msg, strerror(errno));
exit(errno);
}
int catch_signal(int sig, void (*handler)(int)){
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(sig, &action, NULL);
}
int read_in(int socket, char *buf, int len){
char *s = buf;
int slen = len;
int c = recv(socket, s, slen, 0);
while ((c > 0) && (s[c-1] != '\n')){
s += c; slen -= c;
c = recv(socket, s, slen, 0);
}
if ( c < 0)
return c;
else if (c == 0)
buf[0] = '\0';
else
s[c -1] = '\0';
return len - slen;
}
int open_listener_socket(){
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1)
error("Open Socket Error");
return s;
}
void bind_to_port(int socket, int port){
struct sockaddr_in name;
name.sin_family = PF_INET;
name.sin_port = (in_port_t)htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
int reuse = 1;
if(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int)) == -1)
error("Error reusing socket.");
int c = bind( socket, (struct sockaddr *) &name, sizeof(name));
if (c == -1)
error("Error binding socket.");
}
int say(int socket, char *s){
int result = send(socket, s, strlen(s), 0);
if (result == -1)
error("Can't send message");
return result;
}
int listener_d;
void handle_shutdown(int sig){
if (listener_d)
close(listener_d);
fprintf(stderr, "\n\t\tServer Closed.\n\n");
exit(0);
}
int main(){
//catch sig
if(catch_signal(SIGINT, handle_shutdown) == -1)
error("Signal Error");
//create listener socket..
listener_d = open_listener_socket();
//bind listener.
bind_to_port(listener_d, 3098);
if(listen(listener_d, 10) == -1)
error("Can't Listen.");
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
puts("Waiting for Connection.");
char buf[255];
while(1){
int connect_d = accept(listener_d, (struct sockaddr *)&client_addr, &address_size);
if( connect_d == -1)
error("Client Connect Error.");
//begin knock knock joke.
if(say(connect_d,"Internet Knock Knock Protocol Server\r\nVersion 1.0\r\n\nKnock! Knock!\r\n" ) != -1){
read_in(connect_d, buf, sizeof(buf));
if(strcasecmp("Who's there?", buf,12))
say(connect_d,"You should say 'Who's there?'\r\n");
else {
if(say(connect_d,"Oscar.") != -1){
read_in(connect_d, buf, sizeof(buf));
if(strcasecmp("Oscar who?", buf,10))
say(connect_d, "You should say 'Oscar who?'\r\n");
else
say(connect_d, "Oscar silly question, get a silly answer!\r\n");
}
}
}
close(connect_d);
}
return 0;
}
man strcasecmp says that it should work?
int strncasecmp(const char *s1, const char *s2, size_t n);
what am I missing?

Sockets behavior differently between BSD (Mac OS X and OpenBSD) and Linux (Ubuntu)

I wrote a man-in-the-middle/proxy server initially on my mac. Essentially the proxy creates a socket and waits for a connection, then connects to another application. This works flawlessly on in OS X and in OpenBSD; however, when porting the proxy to Ubuntu, it doesn't work as intended.
There is two instances of this proxy running, listening on two separate ports. When this is ran on Ubuntu, all the traffic goes through a single port. I also run into a problem when setting the socket to nonblocking (through fcntl) that sometimes it fails with "Invalid Argument"
I am using sys/socket.
Any pitfalls during this port that I am missing?
EDIT:
I believe there are two problems. One being the Invalid Argument, the other being that traffic is being pushed to different ports.
Service 1 binds to Proxy instance 1 which then binds back to the appropriate service on the black box, which kicks off service 2. However, for some reason on Ubuntu it connects to the Instance 1 which is listening on the incorrect port.
EDIT Solution to Invalid Argument for fcntl:
Found out why i was getting the invalid argument, sadly i'm still having the other issue.
fcntl(fd, cmd, arg)
cmd - F_SETFL(long)
I was passing in a pointer to an int instead of the long primitive.
EDIT:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
void cleanup(int sig)
{
syslog(LOG_INFO, "Cleaning up...");
exit(0);
}
void sigreap(int sig)
{
int status;
pid_t p;
while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
syslog(LOG_INFO, "sigreap: pid=%d, status=%d\n", (int) p, status);
}
/* doh! */
signal(SIGCHLD, sigreap);
}
void set_nonblock(int fd)
{
long fl;
int x;
fl = fcntl(fd, F_GETFL);
if (fl < 0) {
syslog(LOG_ERR, "fcntl F_GETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
fl |= O_NONBLOCK;
x = fcntl(fd, F_SETFL, fl);
if (x < 0) {
syslog(LOG_ERR, "fcntl F_SETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
}
int create_server_sock(char *addr, int port)
{
int addrlen, s, on = 1, x;
static struct sockaddr_in client_addr;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
perror("socket"), exit(1);
addrlen = sizeof(client_addr);
memset(&client_addr, '\0', addrlen);
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = INADDR_ANY; //inet_addr(addr);
client_addr.sin_port = htons(port);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
x = bind(s, (struct sockaddr *) &client_addr, addrlen);
if (x < 0)
perror("bind"), exit(1);
x = listen(s, 5);
if (x < 0)
perror("listen"), exit(1);
return s;
}
int open_remote_host(char *host, int port)
{
struct sockaddr_in rem_addr;
int len, s, x;
struct hostent *H;
int on = 1;
H = gethostbyname(host);
if (!H)
return (-2);
len = sizeof(rem_addr);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
return s;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
len = sizeof(rem_addr);
memset(&rem_addr, '\0', len);
rem_addr.sin_family = AF_INET;
memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length);
rem_addr.sin_port = htons(port);
x = connect(s, (struct sockaddr *) &rem_addr, len);
if (x < 0) {
close(s);
return x;
}
set_nonblock(s);
return s;
}
int get_hinfo_from_sockaddr(struct sockaddr_in addr, int len, char *fqdn)
{
struct hostent *hostinfo;
hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET);
if (!hostinfo) {
sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr));
return 0;
}
if (hostinfo && fqdn)
sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr));
return 0;
}
int wait_for_connection(int s)
{
int newsock;
socklen_t len;
static struct sockaddr_in peer;
len = sizeof(struct sockaddr);
newsock = accept(s, (struct sockaddr *) &peer, &len);
/* dump_sockaddr (peer, len); */
if (newsock < 0) {
if (errno != EINTR)
perror("accept");
}
get_hinfo_from_sockaddr(peer, len, client_hostname);
set_nonblock(newsock);
return (newsock);
}
static int print_bytes(char * buf, ssize_t length)
{
int i = 0, ascii_off = 0, hex_off = 0;
char * hex_bytes = (char *) calloc(32*2,1);
char * ascii_bytes = (char *) calloc(32*2,1);
for( i = 0; i < length; i++)
{
hex_off += sprintf(hex_bytes+hex_off,"%02X ",(unsigned char)buf[i]);
if(buf[i] >= '!' && buf[i] <= '~')
ascii_off += sprintf(ascii_bytes+ascii_off,"%c ",buf[i]);
else
ascii_off += sprintf(ascii_bytes+ascii_off,". ");
if( ((i+1) % 16 == 0) || i == length-1 )
{
fprintf(stderr,"%-48s\t%s\n",hex_bytes,ascii_bytes);
free(hex_bytes);
free(ascii_bytes);
hex_bytes = (char *) calloc(32*2,1);
ascii_bytes = (char *) calloc(32*2,1);
ascii_off = 0;
hex_off = 0;
if(i != length-1)
fprintf(stderr,"\t");
}
}
free(hex_bytes);
free(ascii_bytes);
return 0;
}
int mywrite(int fd, char *buf, int *len)
{
int x = write(fd, buf, *len);
print_bytes(buf,*len);
if (x < 0)
return x;
if (x == 0)
return x;
if (x != *len)
memmove(buf, buf+x, (*len)-x);
*len -= x;
return x;
}
void service_client(int fd1, int fd2, int injfd)
{
int maxfd;
cfd = fd1;
sfd = fd2;
char *sbuf;
char *cbuf;
int x, n;
int cbo = 0;
int sbo = 0;
int ibo = 0;
fd_set R;
int max_clients = 30;
int i = 0,s = 0, addrlen;
struct sockaddr_in address;
sbuf = malloc(BUF_SIZE);
cbuf = malloc(BUF_SIZE);
cntrlbuf = calloc(1,BUF_SIZE);
char * injbuf = malloc(BUF_SIZE);
maxfd = cfd > sfd ? cfd : sfd;
maxfd = injfd > maxfd ? injfd : maxfd;
maxfd++;
maxfd++;
struct inj_con * ptr;
while (1) {
struct timeval to;
if (cbo) {
process_packet(cbuf,&cbo,sfd);
}
if (sbo) {
process_packet(sbuf,&sbo,cfd);
}
if (ibo) {
process_packet(injbuf,&ibo,cfd);
}
if (cntrlo) {
fprintf(stderr,"\033[33;1mControl->(%d), len = 0x%x (%d):\033[0m\n\t",cfd,cntrlo,cntrlo);
if (mywrite(cfd, cntrlbuf, &cntrlo) < 0 && errno != EWOULDBLOCK) {
syslog(LOG_ERR, "write %d: %s", cfd, strerror(errno));
exit(1);
}
}
FD_ZERO(&R);
if (cbo < BUF_SIZE)
FD_SET(cfd, &R);
if (sbo < BUF_SIZE)
FD_SET(sfd, &R);
if (ibo < BUF_SIZE)
FD_SET(injfd, &R);
to.tv_sec = 0;
to.tv_usec = 1000;
x = select(max_clients+3, &R, 0, 0, &to);
if (x > 0 || cntrl_q->item_count > 0) {
if (FD_ISSET(injfd, &R)) {
int new_socket;
if((new_socket = accept(injfd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0)
{
perror("accept");
exit(1);
}
// Truncated
//
}
char * temp_pkt;
if (FD_ISSET(cfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(cfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from CLIENT (%d)", n, cfd);
if (n > 0) {
push_msg(s_q,temp_pkt,n);
} else {
free(temp_pkt);
close(cfd);
close(sfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if (FD_ISSET(sfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(sfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from SERVER (%d)\n", n, sfd);
if (n > 0) {
push_msg(c_q,temp_pkt,n);
} else {
free(temp_pkt);
close(sfd);
close(cfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if(cntrlo == 0 && cntrl_q->front != NULL)
{
struct msg * tmp = next_msg(cntrl_q);
if(tmp != NULL)
{
memcpy(cntrlbuf,tmp->msg,tmp->len);
cntrlo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(sbo == 0 && c_q->front != NULL)
{
struct msg * tmp = next_msg(c_q);
if(tmp != NULL)
{
memcpy(sbuf,tmp->msg,tmp->len);
sbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(cbo == 0 && s_q->front != NULL)
{
struct msg * tmp = next_msg(s_q);
if(tmp != NULL)
{
memcpy(cbuf,tmp->msg,tmp->len);
cbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(ibo == 0 && inj_q->front != NULL)
{
struct msg * tmp = next_msg(inj_q);
if(tmp != NULL)
{
memcpy(injbuf,tmp->msg,tmp->len);
ibo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
} else if (x < 0 && errno != EINTR) {
close(sfd);
close(cfd);
_exit(0);
}
}
}
static int create_injection_sock(int injectionport)
{
struct sockaddr_in serv_addr;
int portno = injectionport;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd <0)
{
perror("ERROR: opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
{
perror("ERROR: on bind");
exit(1);
}
if (listen(sockfd,5) < 0 )
{
perror("listen injection");
exit(1);
}
return sockfd;
}
int main(int argc, char *argv[])
{
if (!(5 == argc || 6 == argc)) {
fprintf(stderr, "usage: %s laddr lport rhost rport [injectionport]\n", argv[0]);
exit(1);
}
char *localaddr = strdup(argv[1]);
int localport = atoi(argv[2]);
char *remoteaddr = strdup(argv[3]);
int remoteport = atoi(argv[4]);
int injectionport;
if(argc == 6)
injectionport = atoi(argv[5]);
int client, server;
int master_sock;
int injection_sock = -1;
cntrl_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
inj_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
s_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
c_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
identities = (struct item_queue *) calloc(1,sizeof(struct item_queue));
assert(localaddr);
assert(localport > 0);
assert(remoteaddr);
assert(remoteport > 0);
if(argc == 6)
assert(injectionport > 0);
openlog(argv[0], LOG_PID, LOG_LOCAL4);
signal(SIGINT, cleanup);
signal(SIGCHLD, sigreap);
if(argc == 6)
injection_sock = create_injection_sock(injectionport);
master_sock = create_server_sock(localaddr, localport);
for (;;) {
if ((client = wait_for_connection(master_sock)) < 0)
continue;
if ((server = open_remote_host(remoteaddr, remoteport)) < 0)
continue;
if (!fork()) {
service_client(client, server, injection_sock);
}
close(client);
close(server);
}
}
Just a guess, but I think your problem is the way you're handling the injection socket. I don't know what you deleted in the // TRUNCATED code block, but basically what you're doing is this:
// Parent process -- bind & listen on the injection socket
injfd = socket(...);
bind(injfd, ...);
listen(injfd, ...);
...
while (1)
{
client = wait_for_connection();
...
if (!fork())
{
// Each child process
...
if (stuff && FD_ISSET(injfd, &R)) {
new_socket = accept(injfd);
...
}
...
}
...
}
In other words, all of your child processes are listening on the same injection socket and trying to accept connections. I'm not sure if this even well-defined behavior, but even in the best case, when a new connection arrives on the injection port, the process that is going to accept the connection could be random and uncontrollable. It could be any of the child processes or even the parent process.
In order to control that behavior, you need to decide who should be listening for connections on the injection socket. If only the parent should be listening, then only the parent should call accept() on it or pass it as an argument to select(). Likewise, if only a particular child should be listening, then only that child should call accept() or pass it to select(). All other processes that are ignoring that socket should close() it at their earliest convenience (e.g. immediately after fork() returns) to avoid leaking file descriptors.
Turns out that SO_REUSEADDR socket option was the issue. I removed me setting that socket option and everything worked out.
This Lead me to the solution.

Resources