// 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 #include #include #include #include #include #include #include #include #include #include #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; }