]> Pileus Git - ~andy/lamechat/commitdiff
XMPP OpenSSL
authorAndy Spencer <andy753421@gmail.com>
Tue, 12 Sep 2017 09:49:36 +0000 (09:49 +0000)
committerAndy Spencer <andy753421@gmail.com>
Wed, 13 Sep 2017 09:02:12 +0000 (09:02 +0000)
chat.c
irc.c
irc.h [new file with mode: 0644]
main.c
makefile
net.c [new file with mode: 0644]
net.h [new file with mode: 0644]
util.c
util.h
xmpp.c

diff --git a/chat.c b/chat.c
index 4ac8f16d833831ea3c0b0d077ca10347da994862..b4bfaa4e6eddae747fd9cc76cd7aca722124c771 100644 (file)
--- a/chat.c
+++ b/chat.c
@@ -36,7 +36,7 @@ static buf_t log_buf;
 /* View init */
 void chat_init(void)
 {
-       chat_log = log_buf.data;
+       chat_log = (log_t*)log_buf.data;
        chat_len = 0;
 
        irc_init();
@@ -52,7 +52,7 @@ void chat_config(const char *group, const char *name, const char *key, const cha
 void chat_recv(const char *channel, const char *from, const char *msg)
 {
        append(&log_buf, NULL, sizeof(log_t));
-       chat_log = log_buf.data;
+       chat_log = (log_t*)log_buf.data;
 
        log_t *log = &chat_log[chat_len];
        log->when = time(NULL);
@@ -67,7 +67,7 @@ void chat_recv(const char *channel, const char *from, const char *msg)
 void chat_send(const char *channel, const char *msg)
 {
        append(&log_buf, NULL, sizeof(log_t));
-       chat_log = log_buf.data;
+       chat_log = (log_t*)log_buf.data;
 
        log_t *log = &chat_log[chat_len];
        log->when = time(NULL);
diff --git a/irc.c b/irc.c
index ec5a27267ae9f9a7d9971c82c7f7d07566076924..df6f449c004a8e701031486415e13f14b058af4e 100644 (file)
--- a/irc.c
+++ b/irc.c
@@ -218,7 +218,7 @@ static void recv_line(irc_server_t *srv, const char *line)
                chat_recv(dst, from, msg);
 }
 
-static void on_poll(void *_srv, int fd)
+static void on_poll(void *_srv)
 {
        static char buf[BUF_LEN];
        static char hostname[512];
@@ -227,7 +227,7 @@ static void on_poll(void *_srv, int fd)
        int len;
 
        /* Handle Input */
-       len = recv(fd, buf, sizeof(buf), 0);
+       len = recv(srv->poll.fd, buf, sizeof(buf), 0);
        if (len < 0) {
                debug("recv: error: %s -- %s", srv->name, strerror(errno));
        }
@@ -255,7 +255,7 @@ static void on_poll(void *_srv, int fd)
 
        /* Handle Output */
        if (srv->out_len > 0) {
-               len = send(fd, &srv->out_buf[srv->out_pos],
+               len = send(srv->poll.fd, &srv->out_buf[srv->out_pos],
                               srv->out_len-srv->out_pos, 0);
                if (len > 0)
                        srv->out_pos += len;
@@ -268,7 +268,7 @@ static void on_poll(void *_srv, int fd)
        /* Handle Errors */
        int err = 0;
        socklen_t elen = sizeof(err);
-       if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &elen))
+       if (getsockopt(srv->poll.fd, SOL_SOCKET, SO_ERROR, &err, &elen))
                error("Error getting socket opt");
        if (err) {
                debug("disconnect: %s -- %s", srv->name, strerror(err));
diff --git a/irc.h b/irc.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/main.c b/main.c
index 364e068db2962074ae9122df6c70d5d9cb7ee60a..d6833521fb023e41a4ef01170f78db0dda5a8bcb 100644 (file)
--- a/main.c
+++ b/main.c
@@ -20,6 +20,7 @@
 #include <signal.h>
 
 #include "util.h"
+#include "net.h"
 #include "args.h"
 #include "conf.h"
 #include "chat.h"
@@ -47,6 +48,7 @@ int main(int argc, char **argv)
 
        /* Initialize */
        util_init();
+       net_init();
 
        args_setup(argc, argv);
        conf_setup(".lamechatrc", on_config);
index 0a99140d71b6fbb6b5cb9851fee74a6ecf4a3348..c64feaf83f2c43426e561730c6f6ade9fe893e3f 100644 (file)
--- a/makefile
+++ b/makefile
@@ -11,11 +11,11 @@ MANPREFIX ?= $(PREFIX)/share/man
 # Compiler
 GCC       ?= gcc
 CFLAGS    ?= -Wall --std=c99
-LDFLAGS   ?= -lncursesw -lexpat -lcrypto
+LDFLAGS   ?= -lncursesw -lexpat -lcrypto -lssl
 
 # Sources
 PROG      ?= lamechat
-SOURCES   ?= main util args conf view chat irc xmpp
+SOURCES   ?= main util args conf view chat net irc xmpp 
 
 # Targets
 all: $(PROG)
diff --git a/net.c b/net.c
new file mode 100644 (file)
index 0000000..cf33bc1
--- /dev/null
+++ b/net.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 Andy Spencer <andy753421@gmail.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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <expat.h>
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+
+#include "util.h"
+#include "net.h"
+
+/* Local functions */
+static void on_poll(void *_net)
+{
+       static char buf[NET_BUFFER];
+       net_t *net = _net;
+       int len;
+
+       /* Handle Errors */
+       int err = 0;
+       socklen_t elen = sizeof(err);
+       if (getsockopt(net->poll.fd, SOL_SOCKET, SO_ERROR, &err, &elen))
+               error("Error getting socket opt");
+       if (err)
+               net_close(net);
+
+       /* State machine */
+       if (net->state == NET_CONNECT) {
+               debug("net: connect");
+               net->state = NET_READY;
+       }
+       if (net->state == NET_READY) {
+               debug("net: ready");
+               len = recv(net->poll.fd, buf, sizeof(buf), 0);
+               if (len == 0)
+                       net_close(net);
+               net->recv(net->data, buf, len);
+       }
+       if (net->state == NET_READY) {
+               if (net->out_len > 0) {
+                       debug("net: output plain");
+                       len = send(net->poll.fd, &net->out_buf[net->out_pos],
+                                      net->out_len-net->out_pos, 0);
+                       if (len > 0)
+                               net->out_pos += len;
+                       if (net->out_pos == net->out_len) {
+                               net->out_pos = 0;
+                               net->out_len = 0;
+                       }
+               }
+       }
+       if (net->state == NET_HANDSHAKE) {
+               debug("net: handshake");
+               len = recv(net->poll.fd, buf, sizeof(buf), 0);
+               if (len == 0)
+                       net_close(net);
+               if (len > 0)
+                       BIO_write(net->in, buf, len);
+
+               SSL_do_handshake(net->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))
+                       net->state = NET_ENCRYPTED;
+       }
+       if (net->state == NET_ENCRYPTED) {
+               debug("net: encrypted");
+               len = recv(net->poll.fd, buf, sizeof(buf), 0);
+               if (len == 0)
+                       net_close(net);
+               if (len > 0)
+                       BIO_write(net->in, buf, len);
+
+               while ((len = SSL_read(net->ssl, buf, sizeof(buf))) > 0)
+                       net->recv(net->data, buf, len);
+       }
+       if (net->state == NET_ENCRYPTED) {
+                if (net->out_len > 0) {
+                       debug("net: output crypto");
+                        len = SSL_write(net->ssl,
+                                        &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;
+                        }
+                }
+
+               while ((len = BIO_read(net->out, buf, sizeof(buf))) > 0)
+                       send(net->poll.fd, buf, len, 0);
+       }
+
+       /* Enable output poll */
+       if ((net->out_len > 0) &&
+           (net->state == NET_READY ||
+            net->state == NET_ENCRYPTED))
+               poll_ctl(&net->poll, 1, 1, 1);
+       else
+               poll_ctl(&net->poll, 1, 0, 1);
+}
+
+void net_encrypt(net_t *net)
+{
+       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);
+
+       net->state = NET_HANDSHAKE;
+}
+
+void net_close(net_t *net)
+{
+       debug("net_close: %s:%d",
+               net->host, net->port);
+       net->state = NET_CLOSED;
+       poll_del(&net->poll);
+}
+
+void net_open(net_t *net, const char *host, int port)
+{
+       int sock, flags;
+       struct addrinfo *addrs = NULL;
+       struct addrinfo hints = {};
+       char service[16];
+
+       net->host = strcopy(host);
+       net->port = port;
+
+       snprintf(service, sizeof(service), "%d", net->port);
+       hints.ai_family   = AF_INET;
+       hints.ai_socktype = SOCK_STREAM;
+
+       /* Setup address */
+       if (getaddrinfo(net->host, service, &hints, &addrs))
+               error("Error getting net address info");
+
+       if ((sock = socket(addrs->ai_family,
+                          addrs->ai_socktype,
+                          addrs->ai_protocol)) < 0)
+               error("Error opening net socket");
+
+       if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
+               error("Error getting net socket flags");
+
+       if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
+               error("Error setting net socket non-blocking");
+
+       if (connect(sock, addrs->ai_addr, addrs->ai_addrlen) < 0)
+               if (errno != EINPROGRESS)
+                       error("Error connecting socket");
+
+       freeaddrinfo(addrs);
+
+       /* Setup server */
+       net->state = NET_CONNECT;
+       poll_add(&net->poll, sock, on_poll, net);
+}
+
+int net_send(net_t *net, const char *buf, int len)
+{
+       if (net->out_len)
+               return 0;
+
+       debug("net: send");
+
+       if (len <= 0)
+               return 0;
+       if (len > NET_BUFFER)
+               len = NET_BUFFER;
+       memcpy(net->out_buf, buf, len);
+
+       net->out_len = len;
+       net->out_pos = 0;
+       return 1;
+}
+
+int net_print(net_t *net, const char *fmt, ...)
+{
+       int len;
+       va_list ap;
+       if (net->out_len)
+               return 0;
+
+       debug("net: print");
+
+       va_start(ap, fmt);
+       len = vsnprintf(net->out_buf, NET_BUFFER, fmt, ap);
+       va_end(ap);
+       if (len <= 0)
+               return 0;
+       if (len > NET_BUFFER)
+               len = NET_BUFFER;
+
+       net->out_len = len;
+       net->out_pos = 0;
+       return 1;
+}
+
+void net_init(void)
+{
+       SSL_library_init();
+       SSL_load_error_strings();
+       ERR_load_BIO_strings();
+       OpenSSL_add_all_algorithms();
+}
diff --git a/net.h b/net.h
new file mode 100644 (file)
index 0000000..6b97798
--- /dev/null
+++ b/net.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 Andy Spencer <andy753421@gmail.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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Networking Constants */
+#define NET_BUFFER 1024
+
+/* OpenSSL Types */
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct ssl_st     SSL;
+typedef struct bio_st     BIO;
+
+/* Networking Callbacks */
+typedef void (*recv_t)(void *data, char *buf, int len);
+typedef void (*err_t)(void *data, int errno);
+
+/* Networking Types */
+typedef enum {
+       NET_CLOSED,
+       NET_CONNECT,
+       NET_READY,
+       NET_HANDSHAKE,
+       NET_ENCRYPTED,
+} nstate_t;
+
+typedef struct {
+       char    *host;
+       int      port;
+       recv_t   recv;
+       err_t    err;
+       void    *data;
+
+       SSL_CTX *ctx;
+       SSL     *ssl;
+       BIO     *in;
+       BIO     *out;
+
+       poll_t   poll;
+       nstate_t state;
+
+       char     out_buf[NET_BUFFER];
+       int      out_pos;
+       int      out_len;
+} net_t;
+
+/* Networking Functions */
+void net_init(void);
+void net_open(net_t *net, const char *host, int port);
+void net_encrypt(net_t *net);
+int  net_send(net_t *net, const char *buf, int len);
+int  net_print(net_t *net, const char *fmt, ...);
+int  net_poll(net_t *net);
+void net_close(net_t *net);
diff --git a/util.c b/util.c
index 29edf581d5fcbd6f2f9aa038fde077e2f8d8660e..2e27cd417eb6db00b9bb1aa1a9973a52702b141b 100644 (file)
--- a/util.c
+++ b/util.c
@@ -120,7 +120,7 @@ void *alloc0(int size)
        return data;
 }
 
-void append(buf_t *buf, const void *data, int len)
+void append(buf_t *buf, const char *data, int len)
 {
        if (buf->len + len + 1 > buf->max) {
                buf->max += (((buf->len+len)/4096)+1)*4096;
@@ -239,7 +239,7 @@ int poll_run(int timeout)
                 //        event.events & EPOLLWAKEUP  ? " wakeup"  : "",
                 //        event.events & EPOLLONESHOT ? " oneshot" : "",
                 //        event.events & EPOLLET      ? " et"      : "");
-               poll->cb(poll->data, poll->fd);
+               poll->cb(poll->data);
                return running;
        }
        return running;
diff --git a/util.h b/util.h
index ef0851459c3796923bbb4eccb208fbe8a6fc4e32..6038c69207ceccb64ea1c31211a0e08158763ce7 100644 (file)
--- a/util.h
+++ b/util.h
 #define new0(type) alloc0(sizeof(type))
 
 /* Types */
-typedef void (*cb_t)(void *data, int fd);
+typedef void (*cb_t)(void *data);
 
 typedef struct {
-       void *data;
+       char *data;
        int   len;
        int   max;
 } buf_t;
@@ -50,7 +50,7 @@ int match(const char *a, const char *b);
 
 /* Memory functions */
 void *alloc0(int size);
-void append(buf_t *buf, const void *data, int len);
+void append(buf_t *buf, const char *data, int len);
 void release(buf_t *buf);
 
 /* File functions */
diff --git a/xmpp.c b/xmpp.c
index 4dbcd170e2d9c45b66c4097827363259b3ee2ebe..344c9d4e5c697dff47b857c59bc290a27554ddd7 100644 (file)
--- a/xmpp.c
+++ b/xmpp.c
@@ -28,6 +28,7 @@
 
 #include "util.h"
 #include "conf.h"
+#include "net.h"
 
 /* XMPP Constants */
 #define BUF_LEN 1024
 /* XMPP types */
 typedef enum {
        XMPP_DEAD,
-       XMPP_STREAM,
-       XMPP_STARTTLS,
-       XMPP_PROCEED,
+       XMPP_SEND_STREAM,
+       XMPP_RECV_FEATURES,
+       XMPP_SEND_STARTTLS,
+       XMPP_RECV_PROCEED,
+       XMPP_ENCRYPT,
        XMPP_READY,
 } xmpp_state_t;
 
@@ -50,18 +53,11 @@ typedef struct xmpp_server_t {
        const char  *jid;
        const char  *pass;
 
+       net_t        net;
        XML_Parser   expat;
        xmpp_state_t state;
-       poll_t       poll;
-
-       char        in_buf[BUF_LEN];
-       int         in_len;
-       char        out_buf[BUF_LEN];
-       int         out_pos;
-       int         out_len;
-
-       buf_t       buf;
-       int         indent;
+       buf_t        buf;
+       int          indent;
 
        struct xmpp_server_t *next;
 } xmpp_server_t;
@@ -113,31 +109,6 @@ static xmpp_channel_t *find_channel(const char *name, int create)
        return cur;
 }
 
-static int send_xml(xmpp_server_t *srv, const char *fmt, ...)
-{
-       int len, sent;
-       va_list ap;
-       if (srv->out_len)
-               return 0;
-
-       va_start(ap, fmt);
-       len = vsnprintf(srv->out_buf, BUF_LEN, fmt, ap);
-       va_end(ap);
-       if (len <= 0)
-               return 0;
-       if (len > BUF_LEN)
-               len = BUF_LEN;
-
-       //debug("xmpp send: [%.*s]", len, srv->out_buf);
-       sent = send(srv->poll.fd, srv->out_buf, len, 0);
-       if (sent == len)
-               return 1;
-
-       srv->out_len = len;
-       srv->out_pos = sent;
-       return 0;
-}
-
 static void on_start(void *_srv, const char *tag, const char **attrs)
 {
        xmpp_server_t *srv = _srv;
@@ -146,17 +117,26 @@ static void on_start(void *_srv, const char *tag, const char **attrs)
        debug("%*s<%s>",
                srv->indent*4, "", tag);
        for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
-               debug("%*s -- %s=\"%s\"%s",
-                       srv->indent*4+6, "",
+               debug("%*s%s=\"%s\"%s",
+                       srv->indent*4+8, "",
                        attrs[i+0], attrs[i+1],
                        attrs[i+2] ? "" : ">");
        }
        srv->indent++;
 
        /* Start TLS */
-       if (srv->state == XMPP_STARTTLS)
-               if (match(tag, "starttls"))
-                       srv->state = XMPP_PROCEED;
+       if (srv->state == XMPP_RECV_FEATURES) {
+               if (match(tag, "starttls")) {
+                       debug("xmpp: features -> starttls");
+                       srv->state = XMPP_SEND_STARTTLS;
+               }
+       }
+       if (srv->state == XMPP_RECV_PROCEED) {
+               if (match(tag, "proceed")) {
+                       debug("xmpp: proceed -> encrypt");
+                       srv->state = XMPP_ENCRYPT;
+               }
+       }
 }
 
 static void on_end(void *_srv, const char *tag)
@@ -164,118 +144,80 @@ static void on_end(void *_srv, const char *tag)
        xmpp_server_t *srv = _srv;
 
        /* Debug print */
+       if (srv->buf.len) {
+               debug("%*s \"%s\"",
+                       srv->indent*4, "",
+                       (char*)srv->buf.data);
+               srv->buf.len = 0;
+       }
        srv->indent--;
 }
 
 static void on_data(void *_srv, const char *data, int len)
 {
+       xmpp_server_t *srv = _srv;
+
+       /* Debug print */
+       append(&srv->buf, data, len);
 }
 
-static void on_poll(void *_srv, int fd)
+static void on_recv(void *_srv, char *buf, int len)
 {
        xmpp_server_t *srv = _srv;
-       static char buf[BUF_LEN];
-       int len;
-
-       /* Handle Input */
-       len = recv(srv->poll.fd, buf, sizeof(buf), 0);
-       if ((len < 0) && (errno != EAGAIN))
-               debug("xmpp recv: error, %s -- %s", srv->name, strerror(errno));
-       if (len == 0) {
-               debug("xmpp recv: close, %s", srv->name);
-               srv->state = XMPP_DEAD;
-               poll_del(&srv->poll);
-               return;
-       }
-       if (len > 0) {
-               //debug("xmpp recv: ready, %s -- [%.*s]", srv->name, len, buf);
-               XML_Parse(srv->expat, buf, len, 0);
-       }
-
-       /* Handle Output */
-       if (srv->out_len > 0) {
-               len = send(fd, &srv->out_buf[srv->out_pos],
-                              srv->out_len-srv->out_pos, 0);
-               if (len > 0)
-                       srv->out_pos += len;
-               if (srv->out_pos == srv->out_len) {
-                       srv->out_pos = 0;
-                       srv->out_len = 0;
-               }
-       }
 
-       /* Handle Errors */
-       int err = 0;
-       socklen_t elen = sizeof(err);
-       if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &elen))
-               error("Error getting socket opt");
-       if (err) {
-               debug("disconnect: %s -- %s", srv->name, strerror(err));
-               srv->state = XMPP_DEAD;
-               poll_del(&srv->poll);
-               return;
-       }
+       /* Parse input */
+       if (len > 0)
+               XML_Parse(srv->expat, buf, len, 0);
 
        /* State machine */
-       if (srv->state == XMPP_STREAM) {
-               if (send_xml(srv,
-                    "<?xml version='1.0'?>"
-                    "<stream:stream"
-                    " from='%s'"
-                    " to='%s'"
-                    " version='1.0'"
-                    " xml:lang='en'"
-                    " xmlns='jabber:client'"
-                    " xmlns:stream='http://etherx.jabber.org/streams'>",
-                    srv->jid, srv->host))
-                       srv->state = XMPP_STARTTLS;
+output:
+       if (srv->state == XMPP_SEND_STREAM) {
+               if (net_print(&srv->net,
+                   "<?xml version='1.0'?>"
+                   "<stream:stream"
+                   " from='%s'"
+                   " to='%s'"
+                   " version='1.0'"
+                   " xml:lang='en'"
+                   " xmlns='jabber:client'"
+                   " xmlns:stream='http://etherx.jabber.org/streams'>",
+                   srv->jid, srv->host)) {
+                       debug("xmpp: stream -> features");
+                       srv->state = XMPP_RECV_FEATURES;
+               }
        }
-       if (srv->state == XMPP_PROCEED) {
-               if (send_xml(srv, "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
-                       debug("ready");
-                       srv->state = XMPP_READY;
+       if (srv->state == XMPP_SEND_STARTTLS) {
+               if (net_print(&srv->net,
+                   "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
+                       debug("xmpp: startls -> proceed");
+                       srv->state = XMPP_RECV_PROCEED;
                }
        }
-
-       /* Enable output poll */
-       if (srv->out_len) {
-               poll_ctl(&srv->poll, 1, 1, 1);
-       } else {
-               poll_ctl(&srv->poll, 1, 0, 1);
+       if (srv->state == XMPP_ENCRYPT) {
+               /* Encrypt connection */
+               net_encrypt(&srv->net);
+
+               /* Reset Expat */
+               if (!(XML_ParserReset(srv->expat, NULL)))
+                       error("Error resetting XML parser");
+               XML_SetUserData(srv->expat, srv);
+               XML_SetStartElementHandler(srv->expat, on_start);
+               XML_SetEndElementHandler(srv->expat, on_end);
+               XML_SetCharacterDataHandler(srv->expat, on_data);
+
+               /* Reset server */
+               debug("xmpp: encrypt -> stream");
+               srv->state = XMPP_SEND_STREAM;
+               goto output;
        }
 }
 
 static void xmpp_connect(xmpp_server_t *srv)
 {
-       int sock, flags;
-       struct addrinfo *addrs = NULL;
-       struct addrinfo hints = {};
-       char service[16];
-
-       snprintf(service, sizeof(service), "%d", srv->port);
-       hints.ai_family   = AF_INET;
-       hints.ai_socktype = SOCK_STREAM;
-
-       /* Setup address */
-       if (getaddrinfo(srv->host, service, &hints, &addrs))
-               error("Error getting xmpp address info");
-
-       if ((sock = socket(addrs->ai_family,
-                          addrs->ai_socktype,
-                          addrs->ai_protocol)) < 0)
-               error("Error opening xmpp socket");
-
-       if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
-               error("Error getting xmpp socket flags");
-
-       if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
-               error("Error setting xmpp socket non-blocking");
-
-       if (connect(sock, addrs->ai_addr, addrs->ai_addrlen) < 0)
-               if (errno != EINPROGRESS)
-                       error("Error connecting socket");
-
-       freeaddrinfo(addrs);
+       /* Net connect */
+       srv->net.recv = on_recv;
+       srv->net.data = srv;
+       net_open(&srv->net, srv->host, srv->port);
 
        /* Setup Expat */
        if (!(srv->expat = XML_ParserCreate(NULL)))
@@ -286,8 +228,7 @@ static void xmpp_connect(xmpp_server_t *srv)
        XML_SetCharacterDataHandler(srv->expat, on_data);
 
        /* Setup server */
-       srv->state = XMPP_STREAM;
-       poll_add(&srv->poll, sock, on_poll, srv);
+       srv->state = XMPP_SEND_STREAM;
 }
 
 /* XMPP functions */