From 4e229ec4ce843675c9b4420c09e361d52e4c5b53 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sat, 31 Mar 2018 18:03:49 +0200 Subject: Client listener in place, not functional yet except for ability to accept connections --- blabouncer.c | 197 ++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 140 insertions(+), 57 deletions(-) diff --git a/blabouncer.c b/blabouncer.c index 49e8f4b..ccbfa13 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -15,6 +15,9 @@ #define MAXDATASIZE 513 // max number of bytes we can get at once (RFC2812 says 512, plus one for null terminator) #define STDIN 0 // stdin is fd 0 +#define BACKLOG 10 // maximum length to which the queue of pending connections for sockfd may grow + +#define BOUNCERLISTENPORT "1234" // TODO: change this to a config option! // getstdin() return codes #define OK 0 @@ -38,7 +41,7 @@ static int getstdin(char *prompt, char *buff, size_t sz) { // If it was too long, there'll be no newline. In that case, we flush // to end of line so that excess doesn't affect the next call. - if (buff[strlen(buff)-1] != '\n') { // strlen of the actually entered line, not the original array size + if (buff[strlen(buff) - 1] != '\n') { // strlen of the actually entered line, not the original array size extra = 0; while (((ch = getchar()) != '\n') && (ch != EOF)) { extra = 1; @@ -47,7 +50,7 @@ static int getstdin(char *prompt, char *buff, size_t sz) { } // Otherwise remove newline and give string back to caller. - buff[strlen(buff)-1] = '\0'; + buff[strlen(buff) - 1] = '\0'; return OK; } @@ -65,8 +68,7 @@ void appendcrlf(char *string) { } // get sockaddr, IPv4 or IPv6: -void *get_in_addr(struct sockaddr *sa) -{ +void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); } @@ -74,7 +76,122 @@ void *get_in_addr(struct sockaddr *sa) return &(((struct sockaddr_in6*)sa)->sin6_addr); } -void dochat(int *sockfd, char *buf) { +int createserversocket(char *host, char *port) { + int sockfd; + struct addrinfo hints, *servinfo, *p; + int rv;// return value for getaddrinfo (for error message) + char s[INET6_ADDRSTRLEN]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((rv = getaddrinfo(host, port, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return 1; + } + + // loop through all the results and connect to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + perror("bouncer-server: socket"); + continue; + } + + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("bouncer-server: connect"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "bouncer-server: failed to connect\n"); + return 2; + } + + inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s); + printf("bouncer-server: connecting to %s\n", s); + + freeaddrinfo(servinfo); // all done with this structure + + return sockfd; +} + +int createclientsocket(char *listenport) { + listenport = BOUNCERLISTENPORT; + + int listener; // listening socket descriptor + + int rv; // return value for getaddrinfo (for error message) + + struct addrinfo hints, *ai, *p; + + // get us a socket and bind it + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if ((rv = getaddrinfo(NULL, listenport, &hints, &ai)) != 0) { + fprintf(stderr, "selectserver: %s\n", gai_strerror(rv)); + exit(1); + } + + // Try for IPv6 + for (p = ai; p != NULL; p = p->ai_next) { + if (p->ai_family == AF_INET6) { + listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (listener != -1) { + // success, got IPv6! + printf("success, got IPv6! ai_family: %d\n", p->ai_family); + break; + } + } + } + + // Try for IPv4 if IPv6 failed + if (listener < 0) { + for (p = ai; p != NULL; p = p->ai_next) { + if (p->ai_family == AF_INET) { + listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (listener != -1) { + // moderate success, got IPv4! + printf("moderate success, got IPv4! ai_family: %d\n", p->ai_family); + break; + } + } + } + } + + if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) { + // failed to bind + close(listener); + printf("bouncer-client: failed to bind, exiting...\n"); + exit(1); + } + + // if we got here, it means we didn't get bound + if (p == NULL) { + fprintf(stderr, "selectserver: failed to bind\n"); + exit(2); + } + + freeaddrinfo(ai); // all done with this + + // listen + if (listen(listener, BACKLOG) == -1) { + perror("listen"); + exit(1); + } + + return listener; +} + +void dochat(int *serversockfd, int *clientsockfd) { + char serverbuf[MAXDATASIZE]; int numbytes; // Number of bytes received from remote server char outgoingmsg[MAXDATASIZE]; // String to send to server int outgoingmsgrc; // Return code from getstdin() for outgoing message @@ -85,18 +202,20 @@ void dochat(int *sockfd, char *buf) { FD_ZERO(&rfds); // clear entries from fd set FD_SET(STDIN, &rfds); // add STDIN (fd 0) to read fds to monitor - FD_SET(*sockfd, &rfds); // add our network socket '' + FD_SET(*serversockfd, &rfds); // add our network socket to monitor - if (select(*sockfd + 1, &rfds, NULL, NULL, NULL) < 0) { // network socket + 1, rfds, no writes, no exceptions/errors, no timeout + // TODO - Change this to use number of fds + 1 instead of sockfd + 1 (I don't think they will be the same with multiple sockets/connections) + if (select(*serversockfd + 1, &rfds, NULL, NULL, NULL) < 0) { // network socket + 1, rfds, no writes, no exceptions/errors, no timeout printf("receive error, exiting!?\n"); perror("select"); - // TODO exit here? + // TODO exit here? let's try it. + exit(1); } - if (FD_ISSET(*sockfd, &rfds)) { + if (FD_ISSET(*serversockfd, &rfds)) { printf("reading socket!\n"); - if ((numbytes = recv(*sockfd, buf, MAXDATASIZE - 1, 0)) == -1) { + if ((numbytes = recv(*serversockfd, serverbuf, MAXDATASIZE - 1, 0)) == -1) { printf("receive error (-1), exiting...\n"); perror("recv"); exit(1); @@ -105,9 +224,9 @@ void dochat(int *sockfd, char *buf) { perror("recv"); exit(1); } - buf[numbytes] = '\0'; + serverbuf[numbytes] = '\0'; - printf("RECEIVED: %s\n",buf); + printf("RECEIVED: %s\n", serverbuf); } if (FD_ISSET(STDIN, &rfds)) { @@ -123,7 +242,7 @@ void dochat(int *sockfd, char *buf) { appendcrlf(outgoingmsg); - if (send(*sockfd, outgoingmsg, strlen(outgoingmsg), 0) == -1) { + if (send(*serversockfd, outgoingmsg, strlen(outgoingmsg), 0) == -1) { printf("send error, exiting...\n"); perror("send"); } @@ -132,63 +251,27 @@ void dochat(int *sockfd, char *buf) { } int main(int argc, char *argv[]) { - // TODO: see if any of this can be shared (i.e. 1. avoid code duplication, and 2. see if variables can be shared between client/server sockets) - - // I will try to keep to the notion of "server" meaning the real IRCd, "bouncer" meaning the bouncer, and "client" meaning the real IRC client - - // BOUNCER-TO-SERVER socket things - - int sockfd; - char buf[MAXDATASIZE]; - struct addrinfo hints, *servinfo, *p; - int rv; - char s[INET6_ADDRSTRLEN]; - if (argc != 3) { fprintf(stderr,"usage: %s [hostname] [port]\n", argv[0]); exit(1); } - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); - return 1; - } - - // loop through all the results and connect to the first we can - for (p = servinfo; p != NULL; p = p->ai_next) { - if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { - perror("bouncer-server: socket"); - continue; - } - - if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { - close(sockfd); - perror("bouncer-server: connect"); - continue; - } + // TODO: see if any of this can be shared (i.e. 1. avoid code duplication, and 2. see if variables can be shared between client/server sockets) - break; - } + // TODO: track fdmax - if (p == NULL) { - fprintf(stderr, "bouncer-server: failed to connect\n"); - return 2; - } + // I will try to keep to the notation of "server" meaning the real IRCd, "bouncer" meaning the bouncer, and "client" meaning the real IRC client - inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s); - printf("bouncer-server: connecting to %s\n", s); + // BOUNCER-TO-SERVER socket things - freeaddrinfo(servinfo); // all done with this structure + int serversockfd = createserversocket(argv[1], argv[2]); + int clientsockfd = createclientsocket(BOUNCERLISTENPORT); - dochat(&sockfd, buf); + dochat(&serversockfd, &clientsockfd); printf("dochat() complete, closing socket...\n"); - close(sockfd); + close(serversockfd); return 0; } -- cgit v1.2.3