I was going through the classic book Unix Network Programming, when I stumbled upon this program (Section 6.8, page 179-180)
#include "unp.h"
int
main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
maxfd = listenfd; /* initialize */
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for ( ; ; ) {
rset = allset; /* structure assignment */
nready = Select(maxfd+1, &rset, NULL, NULL, NULL);
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE)
err_quit("too many clients");
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
/*4connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
The author mentions that this program is not safe against DOS attack. Quoting from the book,
"Unfortunately, there is a problem with the server that we just showed. Consider what happens if a malicious client connects to the server, sends one byte of data (other than a newline), and then goes to sleep. The server will call read (system call), which will read the the single byte of data from the client and then block in the next call to read, waiting for more data from this client. The server is then blocked by this one client, and will not service any other clients until malicious client either sends a newline or terminates"
I am not sure if I understand this correctly. Why will the read system call be called the second time for this malicious client, since it only sent 1 byte of data, that gets notified by the first call to select. The subsequent calls to select will never have this malicious file descriptor set as there is no activity. Am I missing something here?
My guess here is that there is a typo in the code, instead of Read, it should be some version of Readline method mentioned at other places in the book.
Note: The code contains Read and Select (with capital R and S), which are nothing but error handled wrappers of read and select system call
Yes, it seems likely that it was intended to be Readline.
In the downloadable source code that file is tcpcliserv/tcpservselect01.c and there is a corresponding .lc file (with line number annotations) which uses Readline instead of Read, and it was Readline in the second edition of the book (source code). About the only way to make sense of the parenthetic comment "(other than a newline)" is to assume that the intended read function reads up to a newline.
Oddly, it hasn't been reported in the errata. Maybe you should do so.
I think that the problem that he was pointing out was that, as you noted in your NOTE, this code uses Read which is a wrapper of read. My guess, since I'm not about to dig out my copy of the book right now, is that Read will try to call read a second time to finish receiving the data that is never coming.
Related
I am using UNIX domain datagram sockets to send records from multiple clients to a single server in a multithreaded program. Everything is done within one process; I'm sending records from multiple threads to a single thread that acts as the server. All threads are assigned to separate cores using their affinity masks.
My problem is when I use select() to retrieve records from client sockets that have records in the socket buffer. I am using the same basic setup I used with a single client socket (and it worked in that context), but now it hangs (apparently it blocks) when I call recvfrom. That's surprising because the select() function has already identified the socket as available for reading.
int select_clientsockets(int64_t srvrfd, int64_t * claddr, int fds_array[], int fd_count, void * recvbuf){
int fds_ready;
int abc;
int64_t cli_addr;
FD_ZERO(&fdset);
FD_SET(0,&fdset);
socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
fds_ready = select(3, &fdset, NULL, NULL, 0);
for (int i = 0; i < fd_count; i++){
fds_array[i] = 0;
if (FD_ISSET(i, &fdset)) {
fds_array[i] = 1;
cli_addr = claddr[i];
server_receive(srvrfd, recvbuf, 720, cli_addr);}
}
return 0;
}
The select function calls server_receive on clients where select says data are available:
int64_t server_receive(int64_t sfd, void * buf, int64_t msgLen, int64_t claddr)
{
socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
int numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) claddr, len);
if (numBytes == -1)
return 0;
return numBytes;
}
The client socket address is taken from the 3-element array "claddr" (for 3 client sockets) where the corresponding position for each client socket is filled in when the socket is created. At socket creation I also call FD_SET to set the client address into the fd_set. I think I should get the client socket address from fd_set instead, BUT they're both the same pointer value so I don't know why that would make a difference. For internet domain datagram sockets we can use getpeername() but I don't know if there is an analogous function for UNIX domain sockets -- or even if that's the problem.
Thanks very much for any help with this.
UPDATE:
Client fds are added to the global fdset struct on socket creation:
int64_t * create_socket_client(struct sockaddr_un claddr, int64_t retvals[])
{
int sfd, j;
size_t msgLen;
ssize_t numBytes;
char resp[BUF_SIZE];
retvals[0] = 0;
retvals[1] = 0;
sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sfd == -1)
return retvals;
memset(&claddr, 0, sizeof(struct sockaddr_un));
claddr.sun_family = AF_UNIX;
snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/ud_ucase_cl.%ld", (long) getpid());
FD_SET(sfd,&fdset);
retvals[0] = sfd;
retvals[1] = (int64_t)&claddr;
return retvals;
}
FD_ZERO(&fdset);
FD_SET(0,&fdset);
socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
fds_ready = select(3, &fdset, NULL, NULL, 0);
for (int i = 0; i < fd_count; i++){
fds_array[i] = 0;
if (FD_ISSET(i, &fdset)) {
Your code empties fdset then adds only 0 to fdset. So when you call select and pass it fdset, you are asking it only to check socket 0 for readiness.
You later check if sockets 0 to one less than fd_count are in fdset, but only zero could possibly be because it's the only one you asked about.
Where is the list of sockets you want to check for readiness?
I'm trying to write a program which either reads from a socket or from stdin. If socket then print to stdout (to user), otherwise print to socket. So far I'm using poll to awake program when there's activity on either, my problem is that after the initial connection, poll always reports activity on the socket, even though client hasn't written anything else.
Is there any way to distinguish between "someone has connected to the socket" and "someone put a message on the channel"? Looking through man pages for poll, select and others I don't really see an appropriate flag.
If there is no appropriate flag, what would be a way to accomplish what I'm trying to do ?
What I currently have is something like below. When I enter something in stdin, poll returns and the loop determines the activity was on the socket (wrong).
edit: There's other issues at well but this is what I'm struggling with right now.
/* blocking accept for first and only connection */
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
/* poll stdin and newsockfd */
struct pollfd fds[2];
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].fd = newsockfd;
fds[1].events = POLLIN;
ioctl(newsockfd, FIONBIO, (char *)&on); /* int on = 1 */
while (1) {
int rc = poll(fds, 2, -1);
if (rc <= 0)
exit(1);
for (int i = 0; i < 2; i++) {
if (fds[i].revents == 0)
continue;
if (fds[i].revents != POLLIN)
exit(1);
if (fds[i].fd = newsockfd) {
n = read(newsockfd, buffer, 255);
printf("read %d chars from newsockfd: %s\n", n, buffer);
} else if (fds[i].fd = STDIN_FILENO) {
read(STDIN_FILENO, buffer, 255);
n = dprintf(newsockfd, "%s", buffer);
printf("wrote %d chars to newsockfd: %s\n", n, buffer);
}
}
}
I am developing a multi-client Unix Domain Socket to transfer data through multiple processes. I found some code that implements chat between every client and stuff but I want that once a client send something to the server, the server reply back and the client disconnect.
Having that said, I don't want while(fgets()) but I want (on client side):
int main() {
int sockfd;
struct sockaddr_un remote;
fd_set readfds;
char buf[1024];
char buf2[1024];
int len;
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SOCK_PATH);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if(connect(sockfd, (struct sockaddr*)&remote, len) == -1)
/* handle error */
FD_ZERO(&readfds);
FD_SET(0, &readfds);
FD_SET(sockfd, &readfds);
if(select(sockfd+1, &readfds, NULL, NULL, NULL) == -1)
/* handle error */
if(FD_ISSET(0, &readfds)) {
fgets(buf, 1024, stdin);
if(write(sockfd, buf, 1024) <= 0)
/* handle error */
}
if(FD_ISSET(sockfd, &readfds)) {
if(read(sockfd, &buf2, 1024) <= 0)
/* handle error */
}
printf("%s\n", buf2);
close(sockfd);
}
In this order, it works if I do everything after connect() twice (with a loop) but I want to do it only once. Without this loop, my server (which is a daemon) crash and I don't know why.
Furthermore, I added printf() from the code above to understand how it works:
(...)
printf("before select\n");
fflush(stdout);
if(select(sockfd+1, &readfds, NULL, NULL, NULL) == -1)
/* handle error */
printf("before select\n");
fflush(stdout);
if(FD_ISSET(0, &readfds)) {
fgets(buf, 1024, stdin);
if(write(sockfd, buf, 1024) <= 0)
/* handle error */
}
(...)
And I have this output:
before select
"input to fgets"
after select
And I don't understand why I have the input BEFORE "after select". It doesn't make any sense to me since I call fgets() after printf().
I hope this is understandable enough.
What's wrong with my code ? Did I miss something ?
The first time through, you call select() before the server has responded. The result is that sockfd won't be ready for reading.
In your case, the client might not need select() on the sockfd. You know that if you wrote something to the server you want to wait for the reply, right?
I'm attempting to write a TCP socket interface for my program and I'm pulling my hair out with an accept() error (I think). For this I've created some boiled down test code.
First I do a little set up
int server_socket = 0;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
int accepted_connection = 0;
struct sockaddr_in server_address;
server_address.sin_port = htons(9001);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
struct sockaddr_in client_address;
client_address.sin_port = 0;
client_address.sin_family = AF_INET;
char * server_socket_read_buffer[100] = {0};
int server_socket_read_length = 0;
All pretty simple stuff. Just allocate some variables. Next I bind and listen
if (bind(server_socket,(struct sockaddr *)&server_address, sizeof(server_address)) < 0)
{
perror("Bind() on server_socket has failed\n");
}
if (listen(server_socket, 10) < 0)
{
perror("Listen() on server_socket has failed\n");
}
Next is the part where I believe I have my problem
printf("Attempting accept!\n");
if (accepted_connection = accept(server_socket, (struct sockaddr *)NULL, NULL) < 0)
{
perror("Accept failed\n");
}
sleep(10);
if (server_socket_read_length = read(accepted_connection, &server_socket_read_buffer, server_socket_read_length) < 0)
{
perror("Read failed\n");
}
printf("Read %d bytes from socket\n", server_socket_read_length);
for (int i = 0; i<server_socket_read_length;i++)
{
printf("%x\n",server_socket_read_buffer[i]);
}
This compiles and runs. When I use nc with the command 'nc 127.0.0.1 9001' I get a connection, but no data is read. In particular I get 0 bytes of data. I thought this might be due to the NULLs in the accept line, but changing those to a proper struct and length prevent my code from compiling.
If anyone can shed some light on what I'm doing wrong I would be very grateful.
There are a couple of errors:
INADDR_ANY is in host byte order and needs to be converted to network one like htonl(INADDR_ANY). But it does not matter since constant INADDR_ANY is defined as 0.
This
char * server_socket_read_buffer[100]
should be
char server_socket_read_buffer[100]
This
read(accepted_connection, &server_socket_read_buffer, server_socket_read_length)
should be
read(accepted_connection, server_socket_read_buffer, sizeof server_socket_read_buffer)
You are passing in server_socket_read_length = 0 which causes a maximum read length of zero. Pass the buffer size. The declaration of server_socket_read_buffer is incorrect as well. Probably you should allocate a bigger buffer (like 4KB) on the heap.
Also remove the sleep.
The rest is probably working because nc obtains a connection and you are able to accept and read without error.
So after more struggle I found my final answer. The block
if (accepted_connection = accept(server_socket, (struct sockaddr *)NULL, NULL) < 0)
{
//code
}
Doesn't work. My read later on was blocking because accepted_connection wasn't a valid socket. Changing to
(accepted_connection = accept(server_socket, (struct sockaddr *)NULL, NULL);
if accepted_connection < 0)
{
//code
}
resolved my issue. As far as I can gather the file descriptor wasn't being created inline with the if() and reading data from an integer isn't very helpful.
Thanks for the input everyone.
I need this server to be able to listen for and establish new connections with clients while simultaneously writing to existing connections.. ie. Asynchronous non-blocking i/o. I've been told to use poll() but after spending an inordinate amount of time simply trying to grasp socket programming, I'm still unsure how implement the poll() function.
int sockfd;
int main(int argc, char *argv[])
{
int newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
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)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
while(1){
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
// READ READ READ READ READ READ READ READ READ READ READ READ READ READ READ READ
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
// WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
}
return 0;
}
My understanding is that I need to build something like this:
// Set up array of file descriptors for polling
struct pollfd ufds[2];
ufds[0].fd = sockfd;
ufds[0].events = POLLIN;
ufds[1].fd = newsockfd;
ufds[1].events = POLLOUT;
and use poll(ufds,2,2000); inside the loop to check whether sockfd or newsockfd have any activity, in which case I use the appropriate read or write.. If anybody could give me some guidance I'd be very appreciative.
The kernel will fill in the events that occurred in the revents field of your struct pollfd array.
From the manual page:
The field revents is an output parameter, filled by the kernel with the events that actually occurred. The bits returned in revents can include any of those specified in events, or one of the values POLLERR, POLLHUP, or POLLNVAL. (These three bits are meaningless in the events field, and will be set in the revents field whenever the corresponding condition is true.)
If you want event notifications for accepted connections, then you need to either reserve space in advance or resize the struct pollfd array for every connection.
You'll need some way to differentiate the listening socket. You could store it in index zero of your array.
int i, n;
n = poll(ufds, num_fds_in_array, timeout_value);
/* errors or timeout? */
if (n < 1)
;
for (i = 0; i < num_fds_in_array; i++) {
/* were there any events for this socket? */
if (!ufds[i].revents)
continue;
/* is it our listening socket? */
if (!i) {
if (ufds[0].revents & POLLIN)
/* call accept() and add the new socket to ufds */
else
/* error */
continue;
}
/* is there incoming data on the socket? */
if (ufds[i].revents & POLLIN)
/* call recv() on the socket and decide what to do from there */
}
The POLLOUT flag is used to signal when the sending data on the socket will not block the caller.
For non-blocking I/O, I'd use a more powerful API since it requires more bookkeeping to do reliably. See the next paragraph.
Unfortunately, there's no room for auxiliary per-connection data to store state when using poll. There are alternatives available depending on your platform, e. g. epoll for Linux, kqueue for *BSD, and a handful of options for Windows. If you want to use poll with context data, you'd have to use a data structure that can be searched using the file descriptor or array index.
Why don't u use libevent? It totally asynchronous and non-blocking.
http://libevent.org/