summaryrefslogtreecommitdiff
path: root/blabouncer.c
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2018-03-30 15:19:12 +0200
committerLuke Bratch <luke@bratch.co.uk>2018-03-30 15:19:12 +0200
commitc69f622585f9c695aaa1b912978723986ceb01bf (patch)
treee50e701bf0b323b43465322e44528db4bfef70f9 /blabouncer.c
Initial commit - currently just a very manual IRC client
Diffstat (limited to 'blabouncer.c')
-rw-r--r--blabouncer.c194
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;
+}