diff --git a/Makefile b/Makefile index c6374ad..ec3f32a 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CFLAGS := -I $(INCLUDE_DIR) -I $(PRIVATE_INCLUDE_DIR) -Wall -Wextra CFLAGS_DBG := $(CFLAGS) -g -O0 CFLAGS_REL := $(CFLAGS) -O2 -LDFLAGS := +LDFLAGS := -lssl -lcrypto LDFLAGS_REL := $(LDFLAGS) -s APPLICATION := bbirc @@ -19,7 +19,7 @@ SRCS := $(wildcard $(SRC_DIR)/*.c) DBG_OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/dbg_%.o,$(SRCS)) REL_OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/rel_%.o,$(SRCS)) -.PHONY: all debug release clean +.PHONY: all debug release both clean all: release diff --git a/include/IRC/IRC.h b/include/IRC/IRC.h index 8e6f00e..1f262b3 100644 --- a/include/IRC/IRC.h +++ b/include/IRC/IRC.h @@ -13,8 +13,12 @@ void IRC_NOTICE(char* to, char* msg, irc_client_t *irc); void IRC_NICK(char *nick, irc_client_t *irc); void IRC_USER(char *ident, char* realname, irc_client_t *irc); +void IRC_CTCPSend(char *to, char *cmd, irc_client_t *irc); + // registration -void IRC_Register(char *nick, char *username, char *realname, irc_client_t *irc); +void IRC_SetConnectionParameters(char* host, int port, irc_connection_type_t conntype, irc_client_t *irc); +void IRC_SetIdentityParameters(char* nickname, char* username, char* realname, irc_client_t *irc); +void IRC_Register(irc_client_t *irc); void IRC_SetSASLParameters(irc_sasl_mechanism_t, char* username, char* password, irc_client_t *irc); void IRC_RequestSASLAuthentication(irc_client_t *irc); @@ -26,7 +30,12 @@ void IRC_EndCapabilityNegotiation(irc_client_t *irc); void IRC_NeedCapabilities(char* caps, irc_client_t *irc); - int IRC_ParseCapabilities(char* caps, irc_capability_t *capabilities, int capsLength); +// raw socket things +void IRC_Connect(irc_client_t *irc); +ssize_t IRC_Receive(irc_client_t *irc, char* toBuf, size_t bufSize); +void IRC_Send(irc_client_t *irc, char* toSend); +void IRC_Close(irc_client_t *irc); + #endif \ No newline at end of file diff --git a/include/IRC/IRC_structs.h b/include/IRC/IRC_structs.h index 05c05f9..b58afb7 100644 --- a/include/IRC/IRC_structs.h +++ b/include/IRC/IRC_structs.h @@ -1,7 +1,9 @@ #ifndef IRC_STRUCTS_H #define IRC_STRUCTS_H +#include "netcode.h" #include +#include typedef enum { @@ -34,7 +36,7 @@ typedef struct typedef struct { char name[64]; - char topic[512]; + char topic[513]; irc_chanuser_t* users; } irc_channel_t; @@ -68,22 +70,40 @@ typedef struct typedef struct { - int sockfd; + char nick[32]; + char altnick[32]; + char altnick2[32]; + char username[16]; + char realname[128]; +} irc_identity_t; +typedef enum +{ + IRC_CONNECTION_PLAINTEXT, + IRC_CONNECTION_TLS +} irc_connection_type_t; + +typedef struct +{ + char host[128]; + int port; + irc_connection_type_t connType; +} irc_connection_info_t; + +typedef struct +{ + irc_connection_info_t connectionInfo; + sslSockfd_t connection; irc_hostmask_t ownHostmask; - char supportedCapabilities[512]; - char requestedCapabilities[512]; + char supportedCapabilities[513]; + char requestedCapabilities[513]; irc_capability_t acked[24]; /* Authentication parameters */ irc_auth_t saslAuth; -} irc_client_t; -typedef struct -{ - char nick[32]; - char ident[16]; - char realname[64]; -} irc_identity_t; + /* Identity */ + irc_identity_t identity; +} irc_client_t; typedef struct { const char *name; diff --git a/include/netcode.h b/include/netcode.h index c4bafb2..c58f186 100644 --- a/include/netcode.h +++ b/include/netcode.h @@ -1,9 +1,24 @@ #ifndef NETCODE_H #define NETCODE_H +#include + + +typedef struct +{ + SSL *ssl; + int sockfd; + SSL_CTX *ctx; +} sslSockfd_t; + // Returns a socket file descriptor. -int NET_Connect(const char* host, int port); -void NET_Send(int sockfd, const char* toSend); -void NET_Close(int sockfd); +sslSockfd_t NET_Connect(const char* host, int port); +void NET_Send(sslSockfd_t info, const char* toSend); +void NET_Close(sslSockfd_t info); + +// TLS versions of the same thing +sslSockfd_t NET_TLS_Connect(const char* host, int port); +void NET_TLS_Send(sslSockfd_t info, const char* toSend); +void NET_TLS_Close(sslSockfd_t info); #endif \ No newline at end of file diff --git a/src/IRC.c b/src/IRC.c index 046cadc..751a1ed 100644 --- a/src/IRC.c +++ b/src/IRC.c @@ -88,7 +88,7 @@ int IRC_ParseCapabilities(char *caps, irc_capability_t *capabilities, int capsLe caps[len-1] = '\0'; len--; } - char capBak[512]; + char capBak[513]; strncpy(capBak, caps, sizeof(capBak) - 1); // copy the capline so we don't destroy our parameter capBak[sizeof(capBak) - 1] = 0; @@ -144,7 +144,7 @@ static void handlePing(irc_message_t *msg, irc_client_t *irc) { char buf[128]; // most pingpongs are usually short, with the servername being short too snprintf(buf, sizeof buf, "PONG :%s\r\n", msg->argv[0]); - NET_Send(irc->sockfd, buf); + IRC_Send(irc, buf); } static void handleCapabilities(irc_message_t *msg, irc_client_t *irc) @@ -162,7 +162,7 @@ static void handleCapabilities(irc_message_t *msg, irc_client_t *irc) int reqCapCount = IRC_ParseCapabilities(irc->requestedCapabilities, reqCaps, sizeof(reqCaps) / sizeof(irc_capability_t)); - char buf[512]; + char buf[513]; int bytesWritten = 0; for(int i = 0; i < capCount; i++) @@ -184,7 +184,6 @@ static void handleCapabilities(irc_message_t *msg, irc_client_t *irc) for(int i = 0; i < ackedCount; i++) { - printf("%s = %s\n", irc->acked[i].name, irc->acked[i].args ? irc->acked[i].args : "(none)"); if(strcmp(irc->acked[i].name, "sasl") == 0) { IRC_RequestSASLAuthentication(irc); @@ -220,16 +219,16 @@ static void handleAuthentication(irc_message_t *msg, irc_client_t *irc) memcpy(sasl_data + idx, pass, pass_len); idx += pass_len; - char base64Out[512]; + char base64Out[513]; base64_encode(sasl_data, idx, base64Out, sizeof(base64Out)); - char buf[512]; + char buf[513]; snprintf(buf, sizeof buf, "AUTHENTICATE %s\r\n", base64Out); - NET_Send(irc->sockfd, buf); + IRC_Send(irc, buf); break; } case SASL_MECHANISM_EXTERNAL: { - NET_Send(irc->sockfd, "AUTHENTICATE +\r\n"); + IRC_Send(irc, "AUTHENTICATE +\r\n"); break; } } @@ -239,6 +238,9 @@ static void handleAuthentication(irc_message_t *msg, irc_client_t *irc) static void handleSaslSuccess(irc_message_t *msg, irc_client_t *irc) { + // silence msg warning + (void)msg; + IRC_EndCapabilityNegotiation(irc); } @@ -268,41 +270,41 @@ void IRC_ProcessMessage(irc_message_t *msg, irc_client_t *irc) void IRC_PRIVMSG(char *to, char *msg, irc_client_t *irc) { - char buf[512]; + char buf[513]; snprintf(buf, sizeof buf, "PRIVMSG %s :%s\r\n", to, msg); - NET_Send(irc->sockfd, buf); + IRC_Send(irc, buf); } void IRC_NOTICE(char *to, char *msg, irc_client_t *irc) { - char buf[512]; + char buf[513]; snprintf(buf, sizeof buf, "NOTICE %s :%s\r\n", to, msg); - NET_Send(irc->sockfd, buf); + IRC_Send(irc, buf); } void IRC_NICK(char *newNick, irc_client_t *irc) { - char buf[512]; + char buf[513]; snprintf(buf, sizeof buf, "NICK %s\r\n", newNick); - NET_Send(irc->sockfd, buf); + IRC_Send(irc, buf); } void IRC_USER(char *ident, char *realname, irc_client_t *irc) { - char buf[512]; + char buf[513]; snprintf(buf, sizeof buf, "USER %s 0 * :%s\r\n", ident, realname); - NET_Send(irc->sockfd, buf); + IRC_Send(irc, buf); } -void IRC_Register(char *nick, char *username, char *realname, irc_client_t *irc) +void IRC_Register(irc_client_t *irc) { if(irc->saslAuth.mechanism != SASL_MECHANISM_NONE) { IRC_NeedCapabilities("sasl", irc); IRC_StartCapabilityNegotiation(irc); } - IRC_NICK(nick, irc); - IRC_USER(username, realname, irc); + IRC_NICK(irc->identity.nick, irc); + IRC_USER(irc->identity.username, irc->identity.realname, irc); } void IRC_SetSASLParameters(irc_sasl_mechanism_t mechanism, char *username, char *password, irc_client_t *irc) @@ -323,14 +325,14 @@ void IRC_SetSASLParameters(irc_sasl_mechanism_t mechanism, char *username, char void IRC_StartCapabilityNegotiation(irc_client_t *irc) { - NET_Send(irc->sockfd, "CAP LS 302\r\n"); + IRC_Send(irc, "CAP LS 302\r\n"); } void IRC_RequestCapabilities(char *caps, irc_client_t *irc) { - char buf[512]; + char buf[513]; snprintf(buf, sizeof buf, "CAP REQ :%s\r\n", caps); - NET_Send(irc->sockfd, buf); + IRC_Send(irc, buf); } void IRC_NeedCapabilities(char *caps, irc_client_t *irc) @@ -340,7 +342,7 @@ void IRC_NeedCapabilities(char *caps, irc_client_t *irc) void IRC_EndCapabilityNegotiation(irc_client_t *irc) { - NET_Send(irc->sockfd, "CAP END\r\n"); + IRC_Send(irc, "CAP END\r\n"); } void IRC_RequestSASLAuthentication(irc_client_t *irc) @@ -351,10 +353,103 @@ void IRC_RequestSASLAuthentication(irc_client_t *irc) // why? break; case SASL_MECHANISM_PLAIN: - NET_Send(irc->sockfd, "AUTHENTICATE PLAIN\r\n"); + IRC_Send(irc, "AUTHENTICATE PLAIN\r\n"); break; case SASL_MECHANISM_EXTERNAL: - NET_Send(irc->sockfd, "AUTHENTICATE EXTERNAL\r\n"); + IRC_Send(irc, "AUTHENTICATE EXTERNAL\r\n"); break; } +} + +void IRC_CTCPSend(char *to, char *cmd, irc_client_t *irc) +{ + char buf[513]; + snprintf(buf, sizeof buf, "\x01%s\x01", cmd); + IRC_PRIVMSG(to, buf, irc); +} + +void IRC_Connect(irc_client_t *irc) +{ + switch(irc->connectionInfo.connType) + { + case IRC_CONNECTION_PLAINTEXT: + irc->connection = NET_Connect(irc->connectionInfo.host, irc->connectionInfo.port); + break; + case IRC_CONNECTION_TLS: + irc->connection = NET_TLS_Connect(irc->connectionInfo.host, irc->connectionInfo.port); + break; + + } + + if (irc->connection.sockfd < 0) { + fprintf(stderr, "Connection failed\n"); + exit(1); + } +} + +ssize_t IRC_Receive(irc_client_t *irc, char *toBuf, size_t bufSize) +{ + switch(irc->connectionInfo.connType) + { + case IRC_CONNECTION_PLAINTEXT: + { + return recv(irc->connection.sockfd, toBuf, bufSize, 0); + break; + } + case IRC_CONNECTION_TLS: + { + ssize_t n = SSL_read(irc->connection.ssl, toBuf, bufSize); + if (n <= 0) { + int err = SSL_get_error(irc->connection.ssl, n); + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + return -2; + } + return -1; + } + return n; + } + } + return -1; +} + +void IRC_Send(irc_client_t *irc, char *toSend) +{ + switch(irc->connectionInfo.connType) + { + case IRC_CONNECTION_PLAINTEXT: + NET_Send(irc->connection, toSend); + break; + case IRC_CONNECTION_TLS: + NET_TLS_Send(irc->connection, toSend); + break; + + } +} + +void IRC_Close(irc_client_t *irc) +{ + switch(irc->connectionInfo.connType) + { + case IRC_CONNECTION_PLAINTEXT: + NET_Close(irc->connection); + break; + case IRC_CONNECTION_TLS: + NET_TLS_Close(irc->connection); + break; + + } +} + +void IRC_SetConnectionParameters(char *host, int port, irc_connection_type_t conntype, irc_client_t *irc) +{ + snprintf(irc->connectionInfo.host, sizeof irc->connectionInfo.host, "%s", host); + irc->connectionInfo.port = port; + irc->connectionInfo.connType = conntype; +} + +void IRC_SetIdentityParameters(char *nickname, char *username, char *realname, irc_client_t *irc) +{ + snprintf(irc->identity.nick, sizeof irc->identity.nick, "%s", nickname); + snprintf(irc->identity.username, sizeof irc->identity.username, "%s", username); + snprintf(irc->identity.realname, sizeof irc->identity.realname, "%s", realname); } \ No newline at end of file diff --git a/src/main.c b/src/main.c index c78b911..0cb39bc 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include "IRC/IRC.h" #include "IRC/IRC_structs.h" @@ -16,9 +18,12 @@ #include "credentials.h" + int main(int argc, char **argv) { - int sockfd = NET_Connect("irc.libera.chat", 6667); + SSL_library_init(); + OpenSSL_add_ssl_algorithms(); + SSL_load_error_strings(); fd_set readfds; static char linebuf[1026]; @@ -27,37 +32,38 @@ int main(int argc, char **argv) char sendline[513]; irc_client_t irc; - irc.sockfd = sockfd; - if(sockfd >= 0) - { - IRC_SetSASLParameters(SASL_MECHANISM_PLAIN, CREDENTIALS_USERNAME, CREDENTIALS_PASSWORD, &irc); - IRC_Register("BareBonesDude", "bbirc", "A Barebones IRC (https://gitea.codersquack.nl/thorium1256/bbirc) test", &irc); - } - else - { - return 1; - } + IRC_SetConnectionParameters("irc.libera.chat", 6697, IRC_CONNECTION_TLS, &irc); + IRC_Connect(&irc); + IRC_SetIdentityParameters("BareBonesDude", "bbirc", "A BareBones IRC (https://gitea.codersquack.nl/thorium1256/bbirc) test...", &irc); + IRC_SetSASLParameters(SASL_MECHANISM_PLAIN, CREDENTIALS_USERNAME, CREDENTIALS_PASSWORD, &irc); + IRC_Register( &irc); + while(1) { FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); - FD_SET(sockfd, &readfds); + FD_SET(irc.connection.sockfd, &readfds); - int maxfd = (sockfd > 0) ? sockfd + 1 : 1; + int maxfd = (irc.connection.sockfd > 0) ? irc.connection.sockfd + 1 : 1; select(maxfd, &readfds, NULL, NULL, NULL); if(FD_ISSET(STDIN_FILENO, &readfds)) { fgets(sendline, sizeof(sendline), stdin); - NET_Send(sockfd, sendline); + IRC_Send(&irc, sendline); } - if(FD_ISSET(sockfd, &readfds)) + if(FD_ISSET(irc.connection.sockfd, &readfds)) { - ssize_t n = recv(sockfd, recvline, sizeof(recvline), 0); + // ssize_t n = recv(irc.connection.sockfd, recvline, sizeof(recvline), 0); + ssize_t n = IRC_Receive(&irc, recvline, sizeof(recvline)); + + if (n == -2) { + continue; + } if(n <= 0) break; recvline[n] = NUL; @@ -114,7 +120,7 @@ int main(int argc, char **argv) printf("Connection closed by foreign host.\n"); - NET_Close(sockfd); + IRC_Close(&irc); return 0; } \ No newline at end of file diff --git a/src/netcode.c b/src/netcode.c index 1f07faf..3bbb3ff 100644 --- a/src/netcode.c +++ b/src/netcode.c @@ -1,5 +1,8 @@ #include "netcode.h" +#include "print.h" +#include +#include #include #include #include @@ -8,9 +11,12 @@ #include #include #include +#include +#include -int NET_Connect(const char *host, int port) +sslSockfd_t NET_Connect(const char *host, int port) { + sslSockfd_t sock = {NULL, -1, NULL}; int status; struct addrinfo hints, *res; @@ -21,7 +27,7 @@ int NET_Connect(const char *host, int port) // save us from user or programmer stupidity if(!host) { - return -1; + return sock; } char portNumber[6]; @@ -32,11 +38,10 @@ int NET_Connect(const char *host, int port) if ((status = getaddrinfo(host, portNumber, &hints, &res)) == -1) { fprintf(stderr, "Unable to lookup %s: %s\n", host, gai_strerror(status)); - return -1; + return sock; } char ipstr[INET6_ADDRSTRLEN]; - int sockfd = -1; for(struct addrinfo *p = res; p != NULL; p = p->ai_next) { @@ -56,9 +61,9 @@ int NET_Connect(const char *host, int port) } inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); - printf("Connecting to %s (%s)...\n", host, ipstr); + printf("Connecting to %s:%d (%s:%d)...\n", host, port, ipstr, port); - if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) + if((sock.sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { char perrorMsg[sizeof("Failed to connect to ") + sizeof ipstr]; snprintf(perrorMsg, sizeof perrorMsg, "Failed to connect to %s", ipstr); @@ -66,9 +71,9 @@ int NET_Connect(const char *host, int port) continue; } - if((connect(sockfd, p->ai_addr, p->ai_addrlen)) == -1) + if((connect(sock.sockfd, p->ai_addr, p->ai_addrlen)) == -1) { - close(sockfd); + close(sock.sockfd); char perrorMsg[sizeof("Failed to connect to ") + sizeof ipstr]; snprintf(perrorMsg, sizeof perrorMsg, "Failed to connect to %s", ipstr); perror(perrorMsg); @@ -79,17 +84,10 @@ int NET_Connect(const char *host, int port) break; } - if(sockfd >= 0) - { - return sockfd; - } - else - { - return -1; - } + return sock; } -void NET_Send(int sockfd, const char *toSend) +void NET_Send(sslSockfd_t info, const char *toSend) { size_t bytesSent = 0; size_t sendLen = strlen(toSend); @@ -98,7 +96,7 @@ void NET_Send(int sockfd, const char *toSend) while(bytesSent < sendLen) { - ssize_t sent = send(sockfd, toSend, sendLen - bytesSent, 0); + ssize_t sent = send(info.sockfd, toSend, sendLen - bytesSent, 0); if(sent == -1) { perror("NET_Send"); @@ -107,7 +105,7 @@ void NET_Send(int sockfd, const char *toSend) if(sent == 0) { - fprintf(stderr, "NET_Send: connection closed"); + fprintf(stderr, "NET_Send: connection closed\n"); return; } @@ -115,11 +113,138 @@ void NET_Send(int sockfd, const char *toSend) } } -void NET_Close(int sockfd) +void NET_Close(sslSockfd_t info) { - if(sockfd >= 0) + if(info.sockfd >= 0) { - shutdown(sockfd, SHUT_RDWR); - close(sockfd); + shutdown(info.sockfd, SHUT_RDWR); + close(info.sockfd); } -} \ No newline at end of file +} + +sslSockfd_t NET_TLS_Connect(const char *host, int port) +{ + sslSockfd_t result; + + result = NET_Connect(host, port); + if(result.sockfd < 0) return result; + + int flags = fcntl(result.sockfd, F_GETFL, 0); + fcntl(result.sockfd, F_SETFL, flags | O_NONBLOCK); + + + // const char *const CIPHER_LIST = + // "ECDHE-ECDSA-AES128-GCM-SHA256:" + // "ECDHE-RSA-AES128-GCM-SHA256:" + // "ECDHE-ECDSA-AES256-GCM-SHA384:" + // "ECDHE-RSA-AES256-GCM-SHA384:" + // "ECDHE-ECDSA-CHACHA20-POLY1305:" + // "ECDHE-RSA-CHACHA20-POLY1305:" + // "DHE-RSA-AES128-GCM-SHA256:" + // "DHE-RSA-AES256-GCM-SHA384:" + // "DHE-RSA-CHACHA20-POLY1305"; + + // if (SSL_CTX_set_cipher_list(result.ctx, CIPHER_LIST) != 1) { + // fprintf(stderr, "Failed to set cipher list\n"); + // exit(1); + // } + + result.ctx = SSL_CTX_new(TLS_client_method()); +if (!result.ctx) return result; + + // SSL_CTX_set_verify(result.ctx, SSL_VERIFY_NONE, NULL); + // SSL_CTX_set_min_proto_version(result.ctx, TLS1_2_VERSION); + // SSL_CTX_set_max_proto_version(result.ctx, TLS1_2_VERSION); + +// SSL_CTX_set_options(result.ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION); +// SSL_CTX_set_mode(result.ctx, SSL_MODE_AUTO_RETRY); + + result.ssl = SSL_new(result.ctx); + if(!result.ssl) + { + SSL_CTX_free(result.ctx); + return result; + } + + // SSL_set_tlsext_host_name(result.ssl, host); + + SSL_set_fd(result.ssl, result.sockfd); + SSL_set_connect_state(result.ssl); + + int ret; + while((ret = SSL_connect(result.ssl)) != 1) + { + int err = SSL_get_error(result.ssl, ret); + if(err == SSL_ERROR_WANT_READ) + { + fd_set fds; + FD_ZERO(&fds); + FD_SET(result.sockfd, &fds); + select(result.sockfd + 1, &fds, NULL, NULL, NULL); + continue; + } + else if (err == SSL_ERROR_WANT_WRITE) + { + fd_set fds; + FD_ZERO(&fds); + FD_SET(result.sockfd, &fds); + select(result.sockfd + 1, NULL, &fds, NULL, NULL); + continue; + } + else + { + SSL_free(result.ssl); + SSL_CTX_free(result.ctx); + result.ssl = NULL; + result.ctx = NULL; + result.sockfd = -1; + return result; + } + } + + return result; +} + +void NET_TLS_Send(sslSockfd_t info, const char* toSend) +{ + size_t bytesSent = 0; + size_t sendLen = strlen(toSend); + + printf("> %s", toSend); + + while(bytesSent < sendLen) + { + ssize_t sent = SSL_write(info.ssl, toSend + bytesSent, sendLen - bytesSent); + if(sent <= 0) + { + int err = SSL_get_error(info.ssl, sent); + if (err == SSL_ERROR_WANT_WRITE) { + usleep(1000); + continue; + } else if (err == SSL_ERROR_WANT_READ) { + usleep(1000); + continue; + } else { + unsigned long errcode = ERR_get_error(); + char errbuf[256]; + ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); + fprintf(stderr, "NET_TLS_Send: SSL_write error: %s\n", errbuf); + return; + } + } + bytesSent += sent; + } +} + +void NET_TLS_Close(sslSockfd_t info) +{ + if (info.ssl) { + SSL_shutdown(info.ssl); + SSL_free(info.ssl); + SSL_CTX_free(info.ctx); + } + if (info.sockfd >= 0) { + close(info.sockfd); + } +} +