Last active
December 15, 2015 17:59
-
-
Save syzdek/5300065 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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