Added support for SSL when connecting to server.
authorThadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
Sat, 6 Jun 2009 22:32:15 +0000 (19:32 -0300)
committerThadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
Sat, 6 Jun 2009 22:32:15 +0000 (19:32 -0300)
Makefile.am
popproxy.c
ssl.c [new file with mode: 0644]
ssl.h [new file with mode: 0644]

index 1c20643..f8041f6 100644 (file)
@@ -1,3 +1,3 @@
 bin_PROGRAMS = popproxy
-popproxy_SOURCES = popproxy.c log.c log.h nethook.c nethook.h proto_detect.c proto_detect.h jabber.c jabber.h jabber_server.c iksemel_extra.c iksemel_extra.h null.c null.h
+popproxy_SOURCES = popproxy.c log.c log.h nethook.c nethook.h proto_detect.c proto_detect.h jabber.c jabber.h jabber_server.c iksemel_extra.c iksemel_extra.h null.c null.h ssl.c ssl.h
 sysconf_DATA = popproxy.conf
index eef400a..f2a7f30 100644 (file)
@@ -26,6 +26,7 @@
 #include "log.h"
 #include "nethook.h"
 #include "null.h"
+#include "ssl.h"
 
 #define CONFFILE SYSCONFDIR "/popproxy.conf"
 
@@ -38,7 +39,7 @@ void new_client (GServer* server, GConn* conn, gpointer data)
       return;
     }
   g_message ("Received connection from %s.", conn->hostname);
-  hook = null_hook_new (conn, data);
+  hook = ssl_hook_new (conn, data);
   gnet_conn_read (conn);
 }
 
diff --git a/ssl.c b/ssl.c
new file mode 100644 (file)
index 0000000..2a45657
--- /dev/null
+++ b/ssl.c
@@ -0,0 +1,244 @@
+/*
+** Copyright (C) 2006 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
+** Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
+**  
+** This program 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; either version 2 of the License, or
+** (at your option) any later version.
+**  
+** This program 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 this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**  
+*/
+
+#include <gnutls/gnutls.h>
+#include <gnet.h>
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include "ssl.h"
+
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct ssl_data
+{
+  char *server;
+  gnutls_session_t session;
+  GString *buffer;
+  gboolean handshaking;
+};
+
+static struct ssl_data *
+ssl_data_new (char *server)
+{
+  struct ssl_data *ssl;
+  int kx_prio[] = {GNUTLS_KX_RSA, 0};
+  gnutls_certificate_credentials cred;
+  gnutls_certificate_allocate_credentials (&cred);
+  ssl = g_slice_new (struct ssl_data);
+  ssl->server = g_strdup (server);
+  gnutls_init (&ssl->session, GNUTLS_CLIENT);
+  gnutls_set_default_priority (ssl->session);
+  gnutls_set_default_priority (ssl->session);
+  gnutls_kx_set_priority (ssl->session, kx_prio);
+  gnutls_credentials_set (ssl->session, GNUTLS_CRD_CERTIFICATE, cred);
+  ssl->buffer = g_string_sized_new (4096);
+  ssl->handshaking = FALSE;
+  return ssl;
+}
+
+static void
+ssl_data_destroy (struct ssl_data *ssl)
+{
+  gnutls_deinit (ssl->session);
+  g_free (ssl->server);
+  g_string_free (ssl->buffer, TRUE);
+  g_slice_free (struct ssl_data, ssl);
+}
+
+static ssize_t
+ssl_push (gnutls_transport_ptr_t ptr, const void *buffer, size_t len)
+{
+  net_hook_t *hook = ptr;
+  struct ssl_data *ssl = hook->data;
+  int r;
+  if (ssl->handshaking == TRUE)
+    {
+      g_io_channel_write_chars (hook->conn->iochannel, buffer, len,
+                                &r, NULL);
+      return r;
+    }
+  gnet_conn_write (hook->conn, (void *) buffer, len);
+  return len;
+}
+
+static ssize_t
+ssl_pull (gnutls_transport_ptr_t ptr, void *buffer, size_t len)
+{
+  net_hook_t *hook = ptr;
+  struct ssl_data *ssl = hook->data;
+  int r;
+  if (ssl->handshaking == TRUE)
+    {
+      g_io_channel_read_chars (hook->conn->iochannel, buffer, len,
+                               &r, NULL);
+      return r;
+    }
+  if (len > ssl->buffer->len)
+    {
+      r = ssl->buffer->len;
+      memcpy (buffer, ssl->buffer->str, r);
+      g_string_truncate (ssl->buffer, 0);
+    }
+  else
+    {
+      r = len;
+      memcpy (buffer, ssl->buffer->str, r);
+      g_string_erase (ssl->buffer, 0, r);
+    }
+  if (r == 0)
+    {
+      gnutls_transport_set_errno (ssl->session, EAGAIN);
+      return -1;
+    }
+  return r;
+}
+
+static void
+ssl_server_connect (net_hook_t *hook)
+{
+  struct ssl_data *ssl = hook->data;
+  int error;
+  gnutls_transport_set_ptr (ssl->session, (gnutls_transport_ptr_t) hook);
+  gnutls_transport_set_push_function (ssl->session, ssl_push);
+  gnutls_transport_set_pull_function (ssl->session, ssl_pull);
+  ssl->handshaking = TRUE;
+  if ((error = gnutls_handshake (ssl->session)) < 0)
+    {
+      g_message ("%satal error while doing TLS handshaking.\n",
+                 gnutls_error_is_fatal (error) ? "F" : "Nonf");
+      g_message ("%s\n", gnutls_strerror (error));
+    }
+  ssl->handshaking = FALSE;
+}
+
+static void
+ssl_server_close (net_hook_t *hook)
+{
+  struct ssl_data *ssl = hook->data;
+  if (hook->peer)
+    {
+      hook->peer->peer = NULL;
+      gnet_conn_disconnect (hook->peer->conn);
+    }
+  gnet_conn_delete (hook->conn);
+  if (ssl != NULL)
+    {
+      gnutls_bye (ssl->session, GNUTLS_SHUT_RDWR);
+      ssl_data_destroy (ssl);
+    }
+  g_slice_free (net_hook_t, hook);
+}
+
+static void
+ssl_server_write (net_hook_t *hook)
+{
+}
+
+static void
+ssl_server_read (net_hook_t *hook, gchar *buffer, size_t len)
+{
+  struct ssl_data *ssl = hook->data;
+  int r;
+  g_string_append_len (ssl->buffer, buffer, len);
+  do
+    {
+      r = gnutls_record_recv (ssl->session, buffer, len);
+      if (r > 0)
+        gnet_conn_write (hook->peer->conn, buffer, r);
+    } while (r > 0);
+}
+
+static void
+ssl_server_error (net_hook_t *hook)
+{
+  g_message ("Error in POP3 client connection.");
+}
+
+static net_hook_t *
+ssl_server_hook_new (net_hook_t *client_hook, char *server)
+{
+  net_hook_t *hook;
+  hook = g_slice_new (net_hook_t);
+  hook->conn = gnet_conn_new (server, 995, nethook_event, hook);
+  hook->peer = client_hook;
+  hook->server = TRUE;
+  hook->connect = ssl_server_connect;
+  hook->close = ssl_server_close;
+  hook->write = ssl_server_write;
+  hook->read = ssl_server_read;
+  hook->data = ssl_data_new (server);
+  gnet_conn_connect (hook->conn);
+  gnet_conn_read (hook->conn);
+  return hook;
+}
+
+static void
+ssl_connect (net_hook_t *hook)
+{
+}
+
+static void
+ssl_close (net_hook_t *hook)
+{
+}
+
+static void
+ssl_write (net_hook_t *hook)
+{
+}
+
+static void
+ssl_read (net_hook_t *hook, gchar *buffer, size_t len)
+{
+  struct ssl_data *ssl = hook->peer->data;
+  gnutls_record_send (ssl->session, buffer, len);
+}
+
+static void
+ssl_error (net_hook_t *hook)
+{
+}
+
+net_hook_t *
+ssl_hook_new (GConn *conn, char *server)
+{
+  net_hook_t *hook;
+  hook = g_slice_new (net_hook_t);
+  hook->conn = conn;
+  hook->peer = NULL;
+  hook->server = FALSE;
+  hook->connect = ssl_connect;
+  hook->close = ssl_close;
+  hook->write = ssl_write;
+  hook->read = ssl_read;
+  hook->data = NULL;
+  hook->peer = ssl_server_hook_new (hook, server);
+  gnet_conn_set_callback (hook->conn, nethook_event, hook);
+  return hook;
+}
+
+void
+ssl_destroy (net_hook_t *hook)
+{
+  g_slice_free (net_hook_t, hook);
+}
diff --git a/ssl.h b/ssl.h
new file mode 100644 (file)
index 0000000..c292008
--- /dev/null
+++ b/ssl.h
@@ -0,0 +1,30 @@
+/*
+** Copyright (C) 2006 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
+** Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
+**  
+** This program 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; either version 2 of the License, or
+** (at your option) any later version.
+**  
+** This program 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 this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**  
+*/
+
+#ifndef POPPROXY_SSL_H
+#define POPPROXY_SSL_H
+
+#include <gnet.h>
+#include "nethook.h"
+
+net_hook_t* ssl_hook_new (GConn*, char*);
+void ssl_destroy (net_hook_t*);
+
+#endif