Skip to content

Instantly share code, notes, and snippets.

@syzdek
Last active December 15, 2015 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save syzdek/5300065 to your computer and use it in GitHub Desktop.
Save syzdek/5300065 to your computer and use it in GitHub Desktop.
/*
* simple Iv4/IPv6 TCP server which echos any data sent to the open port
*/
#include <stdio.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <netdb.h>
#include <string.h>
#include <arpa/inet.h>
#define MSG_REPEAT ">> "
#define MSG_REFUSED "max clients reached\ngood bye.\n"
#define MSG_SHUTDOWN "shutting down server now,\ngood bye.\n"
static int should_exit = 0;
void catch_signal(int sig);
void close_fd(struct pollfd fds[], int maxconn);
int main(int argc, char * argv[]);
void close_fd(struct pollfd fds[], int maxconn)
{
int c;
close(fds[0].fd);
for(c = 1; c < maxconn; c++)
if (fds[c].fd != -1)
{
write(fds[c].fd, MSG_SHUTDOWN, strlen(MSG_SHUTDOWN));
close(fds[c].fd);
};
return;
}
void catch_signal(int sig)
{
should_exit = 1;
signal(sig, catch_signal);
return;
}
int main(int argc, char * argv[])
{
int s;
int c;
int conn;
int opt;
int err;
int maxconn;
struct pollfd fds[100];
struct sockaddr_storage sin;
socklen_t sins_len;
ssize_t len;
char buff[2048];
char chost[11][INET6_ADDRSTRLEN+1];
char cserv[11][INET6_ADDRSTRLEN+1];
char host[INET6_ADDRSTRLEN+1];
char serv[INET6_ADDRSTRLEN+1];
struct addrinfo hints;
struct addrinfo * servinfo;
if ((argc < 3) || (argc > 4))
{
fprintf(stderr, "Usage: %s <ip> <port> [ <maxconn> ]\n", argv[0]);
return(1);
};
maxconn = 1;
if (argc > 3)
maxconn = (int)atol(argv[3]);
if (maxconn > 99)
maxconn = 99;
// converts human readable IP/port to network byte order
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // use AF_UNSPEC, AF_INET, or AF_INET6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST;
if ((err = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err));
return(1);
};
printf("catching signals...\n");
signal(SIGHUP, catch_signal);
signal(SIGINT, catch_signal);
signal(SIGQUIT, catch_signal);
signal(SIGKILL, catch_signal);
signal(SIGPIPE, SIG_IGN);
signal(SIGALRM, SIG_IGN);
signal(SIGTERM, catch_signal);
signal(SIGUSR1, SIG_IGN);
signal(SIGUSR2, SIG_IGN);
printf("creating socket...\n");
if ((s = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1)
{
perror("socket()");
return(1);
};
printf("configuring socket...\n");
#ifdef SO_NOSIGPIPE
opt = 1; setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(int));
#endif
opt = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int));
opt = 1; setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&opt, sizeof(int));
if (servinfo->ai_family == AF_INET6)
{
opt = 0;
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&opt, sizeof(int));
};
printf("binding socket...\n");
if (bind(s, (struct sockaddr *)servinfo->ai_addr, servinfo->ai_addrlen) == -1)
{
perror("bind()");
close(s);
return(2);
};
freeaddrinfo(servinfo);
printf("start listening..\n");
if (listen(s, 10) == -1)
{
perror("listen()");
close(s);
return(1);
};
printf("configuring polling parameters...\n");
memset(fds, 0, sizeof(fds));
fds[0].fd = s;
fds[0].events = POLLIN;
for(c = 1; c < (maxconn+1); c++)
{
fds[c].fd = -1;
fds[c].events = POLLERR | POLLHUP | POLLIN;
};
printf("entering socket loop...\n");
while(!(should_exit))
{
// wait for activity
if (poll(fds, (maxconn+1), 300000) == -1)
{
perror("poll()");
close_fd(fds, maxconn+1);
return(1);
};
// service existing connection
for(c = 1; (c < (maxconn+1)); c++)
{
if (fds[c].fd != -1)
{
if ((fds[c].revents & POLLHUP))
{
printf("closing: [%s]:%s\n", chost[c], cserv[c]);
close(fds[c].fd);
fds[c].fd = -1;
}
else if ((fds[c].revents & POLLIN))
{
printf("reading: [%s]:%s\n", chost[c], cserv[c]);
if ((len = read(fds[c].fd, buff, 2048)) == -1)
{
perror("read()");
close_fd(fds, maxconn+1);
return(1);
};
if (!(strncasecmp(buff, "shutdown", 8)))
should_exit = 1;
printf("writing: [%s]:%s\n", chost[c], cserv[c]);
write(fds[c].fd, MSG_REPEAT, strlen(MSG_REPEAT));
write(fds[c].fd, buff, len);
};
};
};
// check for new connection
sins_len = sizeof(sin);
if (!(fds[0].revents & POLLIN))
continue;
if ((conn = accept(s, (struct sockaddr *)&sin, &sins_len)) == -1)
{
perror("accept()");
close_fd(fds, maxconn+1);
return(1);
};
getnameinfo((struct sockaddr *)&sin, sins_len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST|NI_NUMERICSERV);
// reject connection if one already exists
for(c = 1; ((fds[c].fd != -1) && (c < (maxconn+1))); c++);
if (c >= (maxconn+1))
{
printf("reject: [%s]:%s\n", host, serv);
write(conn, MSG_REFUSED, strlen(MSG_REFUSED));
close(conn);
continue;
};
// copy client information
printf("accept: [%s]:%s\n", host, serv);
strncpy(chost[c], host, sizeof(host));
strncpy(cserv[c], serv, sizeof(serv));
fds[c].fd = conn;
};
printf("closing sockets...\n");
close_fd(fds, maxconn+1);
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment