nbeng

a non-blocking client/server engine
git clone git://git.2f30.org/nbeng.git
Log | Files | Refs | README

commit ba767a66b0881490fa34cb6fd810260e3d13d9df
parent ded92c4fae4f49ac28aec433adc69cbe63b0ab74
Author: dsp <dsp@2f30.org>
Date:   Mon Jul  2 17:32:35 +0300

applied lostd patch in style and abstraction

Diffstat:
nbeng.c | 336++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 195 insertions(+), 141 deletions(-)
diff --git a/nbeng.c b/nbeng.c @@ -1,51 +1,57 @@ /* - * NonBlocking network engine for tcp/udp/ssl connections + * Non-blocking network engine for tcp/udp/ssl connections * The design uses the same socket for sending and receiving - * data. The relevant data are stored in a connection context - * which is passed around the funcs for dealing with it. - * Also provided are 2 sample reading and writing functions. - * CopyLeft: (DsP <dsp@2f30.org>, lostd <lostd@2f30.org>) - * cheers go to photorec for helping recover this file :) + * data. The relevant data are stored in a connection context + * which is passed around the functions for dealing with it. + * Also provided are sample reading and writing functions. + * Copyleft: + * DsP <dsp@2f30.org> + * Lazaros Koromilas <lostd@2f30.org> + * Cheers go to PhotoRec for helping recover this file :) */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> + #include <sys/types.h> #include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> + #include <netdb.h> -#include <ctype.h> -#include <sys/time.h> -#include <fcntl.h> -#include <err.h> -#include <errno.h> -/*connection types*/ -#define UDPCON 1 -#define TCPCON 2 -#define SSLCON 3 +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> -/* flag to know when to quit the prog */ -unsigned int quitflag=0; +/* global flags */ +unsigned int tflag = 0; +unsigned int sflag = 0; +unsigned int qflag = 0; /* connection context */ typedef struct concontxt_t { +#define UDPCON 1 +#define TCPCON 2 +#define SSLCON 3 unsigned int contype; /* connection type */ int confd; /* net connection fd */ int clifds[FD_SETSIZE]; /* for tcp connected clients */ fd_set lset; /* the set that we select() on */ struct addrinfo *clinfo; /* info on where we connect */ struct addrinfo *srvinfo; /* info for the local part */ +#define INBUFSIZ 512 + char *buf; /* buffer used for input/output */ + unsigned int buflen; + int wfd, lfd; /* local file descriptors */ } concontxt; -static void -usage(const char *s) +void +usage(int ret) { - fprintf(stderr, "usage: %s [-ht] rhost rport\n" - " -t\ttcp session\n" - " -h\tThis help screen\n", s); + fprintf(stderr, "usage: nbeng [-ht] rhost rport\n" + "\t-t Use TCP for transport\n" + "\t-h This help screen\n"); + if (ret) + exit(1); } static void @@ -73,32 +79,32 @@ set_blocking(int fd) } /* - * This is the main function that prepares a socket for - * a) connection to the other endpoint. - * b) being able to receive data. - * when it is ready it sets the fields in the context. - * the flag for the connection type lives in the context. + * This is the main function that prepares a socket for: + * a) connection to the other endpoint. + * b) being able to receive data. + * When it is ready it sets the fields in the context. + * The flag for the connection type lives in the context. */ static void -prepare_socket(concontxt *c, char *to, char *port) +prepare_socket(concontxt *con, char *host, char *port) { struct addrinfo cli_hints, *cli_servinfo, srv_hints, *srv_servinfo, *p0; int rv, cli_sockfd, optval = 1, i; /* all client fds are available */ for (i = 0; i < FD_SETSIZE; i++) - c->clifds[i] = -1; + con->clifds[i] = -1; - if ((c == NULL) || (to == NULL) || (port == NULL)) + if ((con == NULL) || (host == NULL) || (port == NULL)) errx(1, "prepare_socket was passed a null arg"); - memset(&cli_hints, 0, sizeof(cli_hints)); - memset(&srv_hints, 0, sizeof(srv_hints)); + memset(&cli_hints, 0, sizeof (cli_hints)); + memset(&srv_hints, 0, sizeof (srv_hints)); cli_hints.ai_family = srv_hints.ai_family = AF_INET; srv_hints.ai_flags = AI_PASSIVE; cli_hints.ai_socktype = srv_hints.ai_socktype = - ((c->contype == TCPCON) || (c->contype == SSLCON)) + ((con->contype == TCPCON) || (con->contype == SSLCON)) ? SOCK_STREAM : SOCK_DGRAM; - rv = getaddrinfo(to, port, &cli_hints, &cli_servinfo); + rv = getaddrinfo(host, port, &cli_hints, &cli_servinfo); if (rv) errx(1, "getaddrinfo: %s", gai_strerror(rv)); rv = getaddrinfo(NULL, port, &srv_hints, &srv_servinfo); @@ -111,7 +117,7 @@ prepare_socket(concontxt *c, char *to, char *port) if (cli_sockfd < 0) continue; /* until the socket is ready */ rv = setsockopt(cli_sockfd, SOL_SOCKET, - SO_REUSEADDR, &optval, sizeof(optval)); + SO_REUSEADDR, &optval, sizeof (optval)); if (rv < 0) { close(cli_sockfd); warn("setsockopt"); @@ -133,167 +139,213 @@ prepare_socket(concontxt *c, char *to, char *port) if (!p0) errx(1, "failed to bind socket"); /* if it's a tcp connection we have to listen() */ - if (c->contype != UDPCON) - listen(cli_sockfd,5); + if (con->contype != UDPCON) + listen(cli_sockfd, 5); /* all was ok, so we register the socket to the context */ - c->confd = cli_sockfd; - c->clinfo = cli_servinfo; + con->confd = cli_sockfd; + con->clinfo = cli_servinfo; +} + +/* + * Reads data from standard input and saves them in the context. + * Sets the qflag on "Q". + */ +static void +readstdin(concontxt *con) +{ + char buf[INBUFSIZ]; + ssize_t n; + + /* handle input data */ + n = read(con->wfd, buf, INBUFSIZ); + buf[n] = '\0'; + + /* user quits? */ + if (n == 0 || strncmp(buf, "Q\n", 3) == 0) { + qflag = 1; + return; + } + printf("n=%zd buf=%s", n, buf); + + /* copy to context */ + if ((con->buf = (char *)malloc(n)) == NULL) { + warn("malloc"); + goto freex; + } + memcpy(con->buf, buf, n); + con->buflen = n; + return; + +freex: + if (con->buf != NULL) + free(con->buf); + con->buf = NULL; + con->buflen = 0; + return; } /* - * this sample func reads data from input, null-terminates them - * and sends them over the prepared socket stored in the context. - * If it gets a "Q" it quits. + * Sends data available in the context to all peers. */ static void -stdinputdata(concontxt *con) -{ char *inbuf = NULL, *linbuf = NULL; - ssize_t inbufln, sent; - int tcpfd, i; - - /* deal with input data */ - inbuf = fgetln(stdin, &inbufln); - if (inbuf[inbufln - 1] == '\n') - inbuf[inbufln - 1] = '\0'; - else { - /* EOF without EOL, - copy and add the NULL */ - if ((linbuf = malloc(inbufln + 1)) - == NULL) { - warnx("linbuf alloc failed"); - goto freex; - } - memcpy(linbuf, inbuf, inbufln); - linbuf[inbufln] = '\0'; - inbuf = linbuf; - } - if ((strncmp(inbuf, "Q", 2) == 0)) - goto endit; - printf("got msg: %s len %d, to : %x sending it\n", inbuf,inbufln,con->clinfo->ai_addr); +writesock(concontxt *con) +{ + ssize_t sent; + int newfd; + int i; + + /* nothing to do */ + if (con->buf == NULL) + return; + if (con->contype == UDPCON) { - sent = sendto(con->confd, inbuf, inbufln, 0, + sent = sendto(con->confd, con->buf, con->buflen, 0, con->clinfo->ai_addr, con->clinfo->ai_addrlen); - if(sent < 0) - warn("sendto failed"); + if (sent < 0) + warn("sendto"); } else { - tcpfd = connect(con->confd, + newfd = connect(con->confd, con->clinfo->ai_addr, con->clinfo->ai_addrlen); - if (tcpfd < 0) { - warn("tcp connect"); + printf("newfd=%d\n", newfd); + if (newfd < 0) { + warn("connect"); goto freex; } - /* XXX: check if we hit the setsiz limit */ for (i = 0; i < FD_SETSIZE; i++) { if (con->clifds[i] == -1) { - con->clifds[i] = tcpfd; + con->clifds[i] = newfd; break; } } } - fflush(stdout); - /* free the buf */ -freex: - if(linbuf != NULL) - free(linbuf); - return; /*here we normally go out*/ -endit: /* let the main process know we want out */ - if(linbuf != NULL) - free(linbuf); - quitflag=1; + +freex: + if (con->buf != NULL) + free(con->buf); + con->buf = NULL; + con->buflen = 0; + return; } /* - * This sample receive func, reads up to 512 bytes of input - * from the socket stored in the context. + * Reads data from the socket and saves them in the context. */ static void -srvinputdata(concontxt *con) +readsock(concontxt *con) { - ssize_t bytes=0; - int ret; + char buf[INBUFSIZ]; + ssize_t n; + int r; char host[NI_MAXHOST]; - char buf[512]; socklen_t addr_len; struct sockaddr_storage their_addr; - addr_len = sizeof(their_addr); + + addr_len = sizeof (their_addr); if (con->contype == UDPCON) { - bytes = recvfrom(con->confd, buf, - sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *)&their_addr, - &addr_len); + n = recvfrom(con->confd, buf, sizeof (buf), MSG_DONTWAIT, + (struct sockaddr *)&their_addr, &addr_len); } else { /* code for tcp */ + n = 0; } - if (bytes > 0) { - ret = getnameinfo((struct sockaddr *)&their_addr, - addr_len, host, - sizeof(host), NULL, 0, 0); - if (ret < 0) { - warn("getnameinfo"); - snprintf(host, sizeof(host), "unknown"); + buf[n] = '\0'; + + /* copy to context */ + if ((con->buf = (char *)malloc(n)) == NULL) { + warn("malloc"); + goto freex; + } + memcpy(con->buf, buf, n); + con->buflen = n; + + if (n > 0) { + r = getnameinfo((struct sockaddr *)&their_addr, addr_len, + host, sizeof (host), NULL, 0, 0); + if (r < 0) { + warn("getnameinfo"); + snprintf(host, sizeof (host), "unknown"); } - printf("Received %zd bytes from %s that read: %s \n", bytes, host, buf); } + printf("host=%s n=%zd buf=%s", host, n, buf); + return; + +freex: + if (con->buf != NULL) + free(con->buf); + con->buf = NULL; + con->buflen = 0; + return; } +/* + * Writes data available in the context to standard output. + */ +static void +writestdout(concontxt *con) +{ + write(con->lfd, con->buf, con->buflen); + + if (con->buf != NULL) + free(con->buf); + con->buf = NULL; + con->buflen = 0; + return; +} int main(int argc, char *argv[]) { - /* our input is the stdin for now, we record lines and send them */ - int recfd = STDIN_FILENO; - char *prog = *argv; - int c,i; - fd_set rsocks; + int c, i; int highsock, readsocks; struct timeval timeout; - concontxt *con = (concontxt *) malloc(sizeof (concontxt)); - if (con == NULL) - errx(1,"malloc"); - /* our default is the udp connection type */ - con->contype = UDPCON; + char *host, *port; + concontxt *con; + while ((c = getopt(argc, argv, "ht")) != -1) { switch (c) { case 'h': - usage(prog); - goto freex; + usage(1); break; case 't': - con->contype = TCPCON; + tflag = 1; break; - case '?': default: - goto freex; + usage(1); } } argc -= optind; argv += optind; - if (argc != 2) { - usage(prog); - goto freex; - } - prepare_socket(con, argv[0], argv[1]); - highsock = con->confd; - /*FD_ZERO(&(con->lset)); - FD_SET(recfd, &(con->lset)); - FD_SET(con->confd, &(con->lset)); - timeout.tv_sec = 1; - timeout.tv_usec = 0; */ + if (argc != 2) + usage(1); + host = argv[0]; + port = argv[1]; + + con = (concontxt *) malloc(sizeof (concontxt)); + if (con == NULL) + errx(1,"malloc"); + /* defaults */ + con->wfd = fileno(stdin); + con->lfd = fileno(stdout); + con->contype = (tflag || sflag) ? TCPCON : UDPCON; + + prepare_socket(con, host, port); + timeout.tv_sec = 1; timeout.tv_usec = 0; - /* these are the sockets for reading (stdin + connection fd) */ + /* file descriptors for reading are stdin and confd */ while (1) { FD_ZERO(&(con->lset)); - FD_SET(recfd, &(con->lset)); + FD_SET(con->wfd, &(con->lset)); FD_SET(con->confd, &(con->lset)); + highsock = con->confd; /* add all connected clients */ for (i = 0; i < FD_SETSIZE && con->clifds[i] != -1; i++) { FD_SET(con->clifds[i], &(con->lset)); } - if (quitflag) + if (qflag) goto freex; readsocks = select(highsock + 1, &(con->lset), (fd_set *)0, (fd_set *)0, &timeout); @@ -306,11 +358,13 @@ main(int argc, char *argv[]) printf("."); fflush(stdout); } else { - if (FD_ISSET(recfd, &(con->lset))) { - stdinputdata(con); + if (FD_ISSET(con->wfd, &(con->lset))) { + readstdin(con); + writesock(con); } if (FD_ISSET(con->confd, &(con->lset))) { - srvinputdata(con); + readsock(con); + writestdout(con); } } }