#include "irc-channels.h"
#include "irc-nicklist.h"
#include "modes.h"
+#include "line-split.h"
+
+void proxy_send(CLIENT_REC *client, char *d, int l)
+{
+#ifdef HAVE_OPENSSL
+ if(client->listen->use_ssl) {
+ SSL_write(client->ssl, d, l);
+ } else
+#endif
+ net_sendbuffer_send(client->handle, d, l);
+}
+
+int proxy_readline(CLIENT_REC *client, char **str)
+{
+#ifdef HAVE_OPENSSL
+ if(client->listen->use_ssl) {
+ char tmpbuf[2048];
+ int recvlen = 0;
+
+ recvlen = SSL_read(client->ssl, tmpbuf, sizeof(tmpbuf));
+ if(recvlen > 0) {
+ return line_split(tmpbuf, recvlen, str, &client->handle->readbuffer);
+ } else {
+ int err;
+ err = SSL_get_error(client->ssl, recvlen);
+ /* READ/WRITE are not really errors, they just indicate that atm
+ OpenSSL is waiting for more data */
+ if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+ return line_split(tmpbuf, 0, str, &client->handle->readbuffer);
+ }
+ return recvlen; /* if any other error occurs, this will quit the connection */
+ }
+ } else
+#endif
+ return net_sendbuffer_receive_line(client->handle, str, 1);
+}
void proxy_outdata(CLIENT_REC *client, const char *data, ...)
{
va_start(args, data);
str = g_strdup_vprintf(data, args);
- net_sendbuffer_send(client->handle, str, strlen(str));
+ proxy_send(client, str, strlen(str));
g_free(str);
va_end(args);
CLIENT_REC *rec = tmp->data;
if (rec->connected && rec->server == server)
- net_sendbuffer_send(rec->handle, str, len);
+ proxy_send(rec, str, len);
}
g_free(str);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client %s:%d disconnected", rec->host, rec->port);
+#ifdef HAVE_OPENSSL
+ if(rec->listen->use_ssl) {
+ SSL_free(rec->ssl);
+ }
+#endif
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
g_source_remove(rec->recv_tag);
"Proxy: Client %s:%d connected",
client->host, client->port);
client->connected = TRUE;
+#ifdef HAVE_OPENSSL
+ if(client->listen->use_ssl) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Client connected from %s using encryption %s and logged in!", client->host, SSL_get_cipher(client->ssl));
+ }
+#endif
+
proxy_dump_data(client);
}
}
g_return_if_fail(client != NULL);
while (g_slist_find(proxy_clients, client) != NULL) {
- ret = net_sendbuffer_receive_line(client->handle, &str, 1);
+ ret = proxy_readline(client, &str);
if (ret == -1) {
/* connection lost */
remove_client(client);
net_ip2host(&ip, host);
sendbuf = net_sendbuffer_create(handle, 0);
rec = g_new0(CLIENT_REC, 1);
+
+#ifdef HAVE_OPENSSL
+ if(listen->use_ssl) {
+ rec->ssl = SSL_new(listen->ssl_ctx);
+ SSL_set_fd(rec->ssl, g_io_channel_unix_get_fd(handle));
+ int sslerror = SSL_accept(rec->ssl); /* handle error! */
+ if(sslerror <= 0) {
+ /* The Handshake might take longer and the client might not be ready yet
+ so if such an error occurs, we just ignore it, SSL_read and SSL_write
+ should continue with the handshake. */
+ if(SSL_get_error(rec->ssl, sslerror) != SSL_ERROR_WANT_READ) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: An error occured while accepting SSL connection!");
+ g_free(rec);
+ return;
+ }
+ }
+ }
+#endif
+
rec->listen = listen;
rec->handle = sendbuf;
rec->host = g_strdup(host);
if (sscanf(signal+6, "%p", &client) == 1) {
/* send it to specific client only */
if (g_slist_find(proxy_clients, client) != NULL)
- net_sendbuffer_send(((CLIENT_REC *) client)->handle, next_line->str, next_line->len);
+ proxy_send((CLIENT_REC *) client, next_line->str, next_line->len);
g_free(event);
signal_stop();
return;
if (rec->want_ctcp == 1) {
/* only CTCP for the chatnet where client is connected to will be forwarded */
if (strstr(rec->proxy_address, server->connrec->chatnet) != NULL) {
- net_sendbuffer_send(rec->handle,
+ proxy_send(rec,
next_line->str, next_line->len);
signal_stop();
}
return NULL;
}
-static void add_listen(const char *ircnet, int port)
+static void add_listen(const char *ircnet, int port, char *sslcert)
{
LISTEN_REC *rec;
IPADDR ip4, ip6, *my_ip;
return;
}
+ if(sslcert != NULL) {
+#ifdef HAVE_OPENSSL
+ rec->use_ssl = TRUE;
+ rec->ssl_method = SSLv3_server_method(); /* let's start with 3 */
+ rec->ssl_ctx = SSL_CTX_new(rec->ssl_method);
+ if(rec->ssl_ctx == NULL) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: Error setting up SSL Context for port %d failed.",
+ rec->port);
+ g_free(rec->ircnet);
+ g_free(rec);
+ return;
+ }
+
+ if(SSL_CTX_use_certificate_file(rec->ssl_ctx, sslcert, SSL_FILETYPE_PEM) <= 0) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading certificate.");
+ SSL_CTX_free(rec->ssl_ctx);
+ g_free(rec->ircnet);
+ g_free(rec);
+ return;
+ }
+
+ if(SSL_CTX_use_PrivateKey_file(rec->ssl_ctx, sslcert, SSL_FILETYPE_PEM) <= 0) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading private key.");
+ SSL_CTX_free(rec->ssl_ctx);
+ g_free(rec->ircnet);
+ g_free(rec);
+ return;
+ }
+
+ if(!SSL_CTX_check_private_key(rec->ssl_ctx)) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading checking certificate agains private key.");
+ SSL_CTX_free(rec->ssl_ctx);
+ g_free(rec->ircnet);
+ g_free(rec);
+ return;
+ }
+
+#else
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: Specified SSL certificate/private key but irssi compiled WITHOUT OpenSSL!");
+#endif
+
+ }
+
rec->tag = g_input_add(rec->handle, G_INPUT_READ,
(GInputFunction) sig_listen, rec);
remove_client(rec->clients->data);
net_disconnect(rec->handle);
+#ifdef HAVE_OPENSSL
+ if(rec->use_ssl) {
+ SSL_CTX_free(rec->ssl_ctx);
+ }
+#endif
g_source_remove(rec->tag);
g_free(rec->ircnet);
g_free(rec);
LISTEN_REC *rec;
GSList *remove_listens = NULL;
GSList *add_listens = NULL;
- char **ports, **tmp, *ircnet, *port;
+ char **ports, **tmp, *ircnet, *port, *sslfile;
int portnum;
remove_listens = g_slist_copy(proxy_listens);
continue;
*port++ = '\0';
+
+ sslfile = strchr(port, ':');
+
+ if (sslfile != NULL) {
+ *sslfile++ = '\0';
+ }
+
portnum = atoi(port);
if (portnum <= 0)
continue;
while (add_listens != NULL) {
rec = add_listens->data;
- add_listen(rec->ircnet, rec->port);
+ add_listen(rec->ircnet, rec->port, sslfile);
g_free(rec);
add_listens = g_slist_remove(add_listens, add_listens->data);
}
settings_add_str("irssiproxy", "irssiproxy_bind", "");
settings_add_bool("irssiproxy", "irssiproxy", TRUE);
+#ifdef HAVE_OPENSSL
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+#endif
+
if (*settings_get_str("irssiproxy_password") == '\0') {
/* no password - bad idea! */
signal_emit("gui dialog", 2, "warning",
}
if (*settings_get_str("irssiproxy_ports") == '\0') {
signal_emit("gui dialog", 2, "warning",
- "No proxy ports specified. Use /SET "
+ "No proxy ports specified. Use /set "
+#ifdef HAVE_OPENSSL
+ "irssiproxy_ports <ircnet>=<port> <ircnet2>=<port2>:<sslcert> "
+ "... to set them. You can add :filename.pem to secure the proxy with SSL."
+ " (Should contain a cert and key in PEM format)");
+#else
"irssiproxy_ports <ircnet>=<port> <ircnet2>=<port2> "
"... to set them.");
+#endif
+
}
command_bind("irssiproxy", NULL, (SIGNAL_FUNC) cmd_irssiproxy);
proxy_listen_init();
}
settings_check();
- module_register("proxy", "irc");
+ module_register("proxy", "irc");
}
void irc_proxy_deinit(void)