summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2018-03-31 18:03:49 +0200
committerLuke Bratch <luke@bratch.co.uk>2018-03-31 18:03:49 +0200
commit4e229ec4ce843675c9b4420c09e361d52e4c5b53 (patch)
treec138f16e59b03509330a5d2e418e8c8d369d198e
parentcb149d41a370ea646f426e60a125eeccfbea40a4 (diff)
Client listener in place, not functional yet except for ability to accept connections
-rw-r--r--blabouncer.c197
1 files 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;
}