/* * This file is part of blabouncer (https://www.blatech.co.uk/l_bratch/blabouncer). * Copyright (C) 2019 Luke Bratch . * * Blabouncer is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * Blabouncer is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with blabouncer. If not, see . */ #include "sockets.h" // 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); } // Create socket to connect to real IRC server // Returns the socket descriptor on success, or -1 on error 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) { debugprint(DEBUG_CRIT, "createserversocket(): getaddrinfo(): %s\n", gai_strerror(rv)); freeaddrinfo(servinfo); 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) { debugprint(DEBUG_CRIT, "createserversocket(): socket(): %s\n", strerror(errno)); continue; } if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); debugprint(DEBUG_CRIT, "createserversocket(): connect(): %s\n", strerror(errno)); continue; } break; } if (p == NULL) { debugprint(DEBUG_CRIT, "createserversocket(): p == NULL, (%s)\n", strerror(errno)); freeaddrinfo(servinfo); return -1; } inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s); debugprint(DEBUG_SOME, "bouncer-server: connecting to '%s'\n", s); freeaddrinfo(servinfo); // All done with this structure return sockfd; } // Create listening socket to listen for bouncer client connections int createclientsocket(char *listenport) { int listener; // listening socket descriptor int rv; // return value for getaddrinfo (for error message) struct addrinfo hints, *ai, *p; int yes = 1; // for enabling socket options with setsockopt // 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, "bouncer-client: %s\n", gai_strerror(rv)); debugprint(DEBUG_CRIT, "bouncer-client: %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! debugprint(DEBUG_FULL, "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! debugprint(DEBUG_FULL, "moderate success, got IPv4! ai_family: '%d'\n", p->ai_family); break; } } } } // allow address re-use setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); // 1 as in non-zero as in enable if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) { // failed to bind close(listener); printf("bouncer-client: failed to bind, exiting...\n"); debugprint(DEBUG_CRIT, "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, "bouncer-client: failed to bind\n"); debugprint(DEBUG_CRIT, "bouncer-client: failed to bind\n"); exit(1); } freeaddrinfo(ai); // all done with this // listen if (listen(listener, BACKLOG) == -1) { perror("listen"); debugprint(DEBUG_CRIT, "bouncer-client failed to listen(), errno '%d'.\n", errno); exit(1); } return listener; } void init_openssl() { SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); } void cleanup_openssl() { EVP_cleanup(); } // Create OpenSSL context, type = 0 for IRC server-side (OpenSSL client) // or type = 1 for bouncer client-side (OpenSSL server) SSL_CTX *create_openssl_context(int type) { const SSL_METHOD *method; SSL_CTX *ctx; if (type == 0) { method = SSLv23_client_method(); } else { method = SSLv23_server_method(); } ctx = SSL_CTX_new(method); if (!ctx) { char* errstr = openssl_error_string(); debugprint(DEBUG_CRIT, "Unable to create SSL context, errno '%d', type '%d' - %s", errno, type, errstr); if (errstr != NULL) free(errstr); exit(EXIT_FAILURE); } return ctx; } // Configure OpenSSL context, with certfile and keyfile provided if // IRC server-side or set to NULL if bouncer client-side void configure_openssl_context(SSL_CTX *ctx, char *certfile, char *keyfile) { SSL_CTX_set_ecdh_auto(ctx, 1); /* Set the key and cert if set or return if not */ if (certfile == NULL || keyfile == NULL) { return; } if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); printf("Couldn't load certificate file '%s'. Hint: You can generate your own with OpenSSL. Once created, set its location in blabouncer.conf which by default is in ~/.blabouncer/.\n", certfile); debugprint(DEBUG_CRIT, "Couldn't load certificate file '%s'. Hint: You can generate your own with OpenSSL. Once created, set its location in blabouncer.conf which by default is in ~/.blabouncer/.\n", certfile); exit(EXIT_FAILURE); } if (SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) <= 0 ) { ERR_print_errors_fp(stderr); printf("Couldn't load key file '%s'. Hint: You can generate your own with OpenSSL. Once created, set its location in blabouncer.conf which by default is in ~/.blabouncer/.\n", keyfile); debugprint(DEBUG_CRIT, "Couldn't load key file '%s'. Hint: You can generate your own with OpenSSL. Once created, set its location in blabouncer.conf which by default is in ~/.blabouncer/.\n", keyfile); exit(EXIT_FAILURE); } } // Read from a socket, whether or not using TLS int sockread(SSL *fd, char *buf, int bufsize, int tls) { if (tls) { return SSL_read(fd, buf, bufsize); } else { // Cast the supposed SSL *fd to a long int if we're not using TLS return recv((long int)fd, buf, bufsize, 0); } } // Write to a socket, whether or not using TLS int socksend(SSL *fd, char *buf, int bufsize, int tls) { if (tls) { return SSL_write(fd, buf, bufsize); } else { // Cast the supposed SSL *fd to a long int if we're not using TLS return send((long int)fd, buf, bufsize, 0); } } char *openssl_error_string() { BIO *bio = BIO_new (BIO_s_mem ()); ERR_print_errors (bio); char *buf = NULL; size_t len = BIO_get_mem_data (bio, &buf); char *ret = (char *)calloc(1, 1 + len); if (ret) { memcpy(ret, buf, len); } BIO_free (bio); return ret; }