diff options
author | Luke Bratch <luke@bratch.co.uk> | 2018-03-30 15:19:12 +0200 |
---|---|---|
committer | Luke Bratch <luke@bratch.co.uk> | 2018-03-30 15:19:12 +0200 |
commit | c69f622585f9c695aaa1b912978723986ceb01bf (patch) | |
tree | e50e701bf0b323b43465322e44528db4bfef70f9 /blabouncer.c |
Initial commit - currently just a very manual IRC client
Diffstat (limited to 'blabouncer.c')
-rw-r--r-- | blabouncer.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/blabouncer.c b/blabouncer.c new file mode 100644 index 0000000..679c775 --- /dev/null +++ b/blabouncer.c @@ -0,0 +1,194 @@ +// uses socket demo code from https://beej.us/guide/bgnet/html/single/bgnet.html +// and getstdin() uses getLine() from https://stackoverflow.com/questions/4023895/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/select.h> + +#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 + +// getstdin() return codes +#define OK 0 +#define NO_INPUT 1 +#define TOO_LONG 2 + +// Get stdin line with buffer overrun protection +static int getstdin(char *prompt, char *buff, size_t sz) { + int ch, extra; + + // Print optional prompt + if (prompt != NULL) { + printf ("%s", prompt); + fflush (stdout); + } + + // Get the intput from stdin + if (fgets (buff, sz, stdin) == NULL) { + return NO_INPUT; + } + + // 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 + extra = 0; + while (((ch = getchar()) != '\n') && (ch != EOF)) { + extra = 1; + } + return (extra == 1) ? TOO_LONG : OK; + } + + // Otherwise remove newline and give string back to caller. + buff[strlen(buff)-1] = '\0'; + return OK; +} + +// Append CR-LF to the end of a string +void appendcrlf(char *string) { + // Make sure it doesn't already end with CR or LF + while (string[strlen(string) - 1] == '\r' || string[strlen(string) - 1] == '\r') { + string[strlen(string) - 1] = '\0'; + } + + int startlen = strlen(string); + string[startlen] = '\r'; + string[startlen + 1] = '\n'; + string[startlen + 2] = '\0'; +} + +// get sockaddr, IPv4 or IPv6: +void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +void dochat(int *sockfd, char *buf) { + 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 + + fd_set rfds; + + while (1) { + 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 '' + + if(select(*sockfd + 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? + } + + if(FD_ISSET(*sockfd, &rfds)) { + printf("reading socket!\n"); + + if ((numbytes = recv(*sockfd, buf, MAXDATASIZE - 1, 0)) == -1) { + printf("receive error (-1), exiting...\n"); + perror("recv"); + exit(1); + } else if (numbytes == 0) { + printf("socket closed (or no data received) (0), exiting...\n"); + perror("recv"); + exit(1); + } + buf[numbytes] = '\0'; + + printf("RECEIVED: %s\n",buf); + } + + if(FD_ISSET(STDIN, &rfds)) { + printf("reading stdin!\n"); + + outgoingmsgrc = getstdin(NULL, outgoingmsg, sizeof(outgoingmsg)); + + if (outgoingmsgrc == NO_INPUT) { + printf("\nError! No input.\n"); + } else if (outgoingmsgrc == TOO_LONG) { + printf ("Error! Too long. Would have allowed up to: [%s]\n", outgoingmsg); + } + + appendcrlf(outgoingmsg); + + if (send(*sockfd, outgoingmsg, strlen(outgoingmsg), 0) == -1) { + printf("send error, exiting...\n"); + perror("send"); + } + } + } +} + +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; + } + + 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 + + dochat(&sockfd, buf); + + printf("dochat() complete, closing socket...\n"); + + close(sockfd); + + return 0; +} |