]> Pileus Git - ~andy/lamechat/blobdiff - net.c
Support pre-formatted text.
[~andy/lamechat] / net.c
diff --git a/net.c b/net.c
index d7937e4ddc396bd20a2fc8d86d896f9bdbcc8b66..a1c11c8446e90e4ec36a0839a70acd65d75fa061 100644 (file)
--- a/net.c
+++ b/net.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 
+#ifdef USE_OPENSSL
 #include <openssl/bio.h>
 #include <openssl/ssl.h>
+#endif
+#ifdef USE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#endif
 
 #include "util.h"
 #include "net.h"
 
+/* Crypto types */
+struct crypto_t {
+#ifdef USE_OPENSSL
+       SSL_CTX *ctx;
+       SSL     *ssl;
+       BIO     *in;
+       BIO     *out;
+#endif
+#ifdef USE_GNUTLS
+       gnutls_session_t tls;
+#endif
+};
+
+/* Local data */
+#ifdef USE_GNUTLS
+static gnutls_certificate_credentials_t xcred;
+#endif
+
 /* Local functions */
 static int flush(net_t *net)
 {
+#ifdef USE_OPENSSL
        static char buf[NET_BUFFER];
+#endif
        int len;
 
        /* State machine */
@@ -56,9 +83,10 @@ static int flush(net_t *net)
                }
        }
        if (net->state == NET_ENCRYPTED) {
+#ifdef USE_OPENSSL
                 if (net->out_len > 0) {
                        debug("net: flush crypto");
-                       len = SSL_write(net->ssl,
+                       len = SSL_write(net->crypto->ssl,
                                        &net->out_buf[net->out_pos],
                                        net->out_len-net->out_pos);
                        if (len > 0)
@@ -71,6 +99,20 @@ static int flush(net_t *net)
 
                while ((len = BIO_read(net->out, buf, sizeof(buf))) > 0)
                        send(net->poll.fd, buf, len, 0);
+#endif
+#ifdef USE_GNUTLS
+                if (net->out_len > 0) {
+                       len = gnutls_record_send(net->crypto->tls,
+                                          &net->out_buf[net->out_pos],
+                                          net->out_len-net->out_pos);
+                       if (len > 0)
+                               net->out_pos += len;
+                       if (net->out_pos == net->out_len) {
+                               net->out_pos = 0;
+                               net->out_len = 0;
+                       }
+               }
+#endif
        }
 
        return 1;
@@ -93,6 +135,9 @@ static void on_poll(void *_net)
                }
 
                /* State machine */
+               if (net->state == NET_CLOSED) {
+                       done = 1;
+               }
                if (net->state == NET_CONNECT) {
                        debug("net: connect");
                        net->state = NET_READY;
@@ -113,6 +158,7 @@ static void on_poll(void *_net)
                }
                if (net->state == NET_HANDSHAKE) {
                        debug("net: handshake");
+#ifdef USE_OPENSSL
                        len = recv(net->poll.fd, buf, sizeof(buf), 0);
                        if (len < 0)
                                done = 1;
@@ -121,27 +167,59 @@ static void on_poll(void *_net)
                        if (len > 0)
                                BIO_write(net->in, buf, len);
 
-                       SSL_do_handshake(net->ssl);
+                       SSL_do_handshake(net->crypto->ssl);
 
                        while ((len = BIO_read(net->out, buf, sizeof(buf))) > 0)
                                send(net->poll.fd, buf, len, 0);
 
-                       if (SSL_is_init_finished(net->ssl))
+                       if (SSL_is_init_finished(net->crypto->ssl))
+                               net->state = NET_ENCRYPTED;
+#endif
+#ifdef USE_GNUTLS
+                       err = gnutls_handshake(net->crypto->tls);
+                       if (err == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) {
+                               gnutls_datum_t out;
+                               int type = gnutls_certificate_type_get(net->crypto->tls);
+                               unsigned status = gnutls_session_get_verify_cert_status(net->crypto->tls);
+                               gnutls_certificate_verification_status_print(status, type, &out, 0);
+                               debug("Cert verify failed: %s", out.data);
+                               gnutls_free(out.data);
+                               net_close(net);
+                       } else if (err < 0) {
+                               debug("Handshake failed: %s", gnutls_strerror(err));
+                               net_close(net);
+                       } else {
+                               char *desc = gnutls_session_get_desc(net->crypto->tls);
+                               debug("Session info: %s", desc);
+                               gnutls_free(desc);
                                net->state = NET_ENCRYPTED;
+                       }
+#endif
                        flush(net);
                }
                if (net->state == NET_ENCRYPTED) {
                        debug("net: encrypted");
+#ifdef USE_OPENSSL
                        len = recv(net->poll.fd, buf, sizeof(buf), 0);
                        if (len < 0)
                                done = 1;
                        if (len == 0)
                                net_close(net);
                        if (len > 0)
-                               BIO_write(net->in, buf, len);
+                               BIO_write(net->crypto->in, buf, len);
 
-                       while ((len = SSL_read(net->ssl, buf, sizeof(buf))) > 0)
+                       while ((len = SSL_read(net->crypto->ssl, buf, sizeof(buf))) > 0)
+                               net->recv(net->data, buf, len);
+#endif
+#ifdef USE_GNUTLS
+                       len = gnutls_record_recv(net->crypto->tls, buf, sizeof(buf));
+                       if (len < 0)
+                               done = 1;
+                       if (len == 0)
+                               net_close(net);
+                       if (len > 0)
                                net->recv(net->data, buf, len);
+#endif
                }
        }
 }
@@ -159,10 +237,19 @@ const char *get_hostname(void)
 
 void net_init(void)
 {
+#ifdef USE_OPENSSL
        SSL_library_init();
        SSL_load_error_strings();
        ERR_load_BIO_strings();
        OpenSSL_add_all_algorithms();
+#endif
+#ifdef USE_GNUTLS
+       gnutls_global_init();
+       gnutls_certificate_allocate_credentials(&xcred);
+       gnutls_certificate_set_x509_trust_file(xcred,
+                       "/etc/ssl/certs/ca-certificates.crt",
+                       GNUTLS_X509_FMT_PEM);
+#endif
 }
 
 void net_open(net_t *net, const char *host, int port)
@@ -218,21 +305,37 @@ void net_open(net_t *net, const char *host, int port)
        poll_add(&net->poll, sock, on_poll, net);
 }
 
-void net_encrypt(net_t *net)
+void net_encrypt(net_t *net, int flags)
 {
        debug("net: encrypt");
 
-       net->ctx = SSL_CTX_new(TLSv1_2_client_method());
-       net->ssl = SSL_new(net->ctx);
-
-       net->in  = BIO_new(BIO_s_mem());
-       net->out = BIO_new(BIO_s_mem());
-
-       BIO_set_mem_eof_return(net->in,  -1);
-       BIO_set_mem_eof_return(net->out, -1);
-
-       SSL_set_bio(net->ssl, net->in, net->out);
-       SSL_set_connect_state(net->ssl);
+#if defined(USE_OPENSSL)
+       net->crypto = new0(crypto_t);
+       net->crypto->ctx = SSL_CTX_new(TLSv1_2_client_method());
+       net->crypto->ssl = SSL_new(net->ctx);
+
+       net->crypto->in  = BIO_new(BIO_s_mem());
+       net->crypto->out = BIO_new(BIO_s_mem());
+
+       BIO_set_mem_eof_return(net->crypto->in,  -1);
+       BIO_set_mem_eof_return(net->crypto->out, -1);
+
+       SSL_set_bio(net->crypto->ssl, net->crypto->in, net->crypto->out);
+       SSL_set_connect_state(net->crypto->ssl);
+#elif defined(USE_GNUTLS)
+       net->crypto = new0(crypto_t);
+       gnutls_init(&net->crypto->tls, GNUTLS_CLIENT);
+       gnutls_set_default_priority(net->crypto->tls);
+       gnutls_server_name_set(net->crypto->tls, GNUTLS_NAME_DNS,
+                              net->host, strlen(net->host));
+       gnutls_credentials_set(net->crypto->tls, GNUTLS_CRD_CERTIFICATE, xcred);
+        gnutls_handshake_set_timeout(net->crypto->tls, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+        gnutls_transport_set_int(net->crypto->tls, net->poll.fd);
+        if (!(flags & NET_NOVERIFY))
+               gnutls_session_set_verify_cert(net->crypto->tls, net->host, 0);
+#else
+       error("Encryption is not supported");
+#endif
 
        net->state = NET_ENCRYPT;
 }