Added OpenSSL support.

Added win32-specific srv lookup support.
Extend session timeout to 15 seconds.
This commit is contained in:
James Canete
2007-09-05 20:01:24 +00:00
parent c3b8d03096
commit f45380bb23
10 changed files with 463 additions and 24 deletions

View File

@@ -27,10 +27,11 @@
/* FIXME: these should be configurable */
#define FEATURES_TIMEOUT 2000 /* 2 seconds */
#define BIND_TIMEOUT 2000 /* 2 seconds */
#define SESSION_TIMEOUT 2000 /* 2 seconds */
#define SESSION_TIMEOUT 15000 /* 15 seconds */
#define LEGACY_TIMEOUT 2000 /* 2 seconds */
static void _auth(xmpp_conn_t * const conn);
static void _handle_open_tls(xmpp_conn_t * const conn);
static void _handle_open_sasl(xmpp_conn_t * const conn);
static int _handle_missing_legacy(xmpp_conn_t * const conn,
void * const userdata);
@@ -89,8 +90,13 @@ static int _handle_error(xmpp_conn_t * const conn,
if (conn->stream_error) {
child = xmpp_stanza_get_children(stanza);
do {
if (child && strcmp(xmpp_stanza_get_ns(child),
XMPP_NS_STREAMS_IETF) == 0) {
char *ns = NULL;
if (child) {
ns = xmpp_stanza_get_ns(child);
}
if (ns && strcmp(ns, XMPP_NS_STREAMS_IETF) == 0) {
name = xmpp_stanza_get_name(child);
if (strcmp(name, "text") == 0) {
if (conn->stream_error->text)
@@ -203,7 +209,7 @@ static int _handle_features(xmpp_conn_t * const conn,
/* TODO: Implement TLS */
_auth(conn);
return 0;
}
@@ -222,6 +228,36 @@ static char *_get_authid(xmpp_conn_t * const conn)
return authid;
}
static int _handle_proceedtls_default(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza,
void * const userdata)
{
char *name;
name = xmpp_stanza_get_name(stanza);
xmpp_debug(conn->ctx, "xmpp",
"handle proceedtls called for %s", name);
if (strcmp(name, "proceed") == 0) {
xmpp_debug(conn->ctx, "xmpp", "proceeding with TLS");
parser_prepare_reset(conn, _handle_open_tls);
conn->tls = tls_new(conn->ctx, conn->sock);
if (!tls_start(conn->tls))
{
xmpp_debug(conn->ctx, "xmpp", "Couldn't start TLS!");
}
else
{
conn_open_stream(conn);
}
}
return 0;
}
static int _handle_digestmd5_default(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza,
void * const userdata)
@@ -393,6 +429,20 @@ static int _handle_plain(xmpp_conn_t * const conn,
return ret;
}
static xmpp_stanza_t *_make_starttls(xmpp_conn_t * const conn)
{
xmpp_stanza_t *starttls;
/* build start stanza */
starttls = xmpp_stanza_new(conn->ctx);
if (starttls) {
xmpp_stanza_set_name(starttls, "starttls");
xmpp_stanza_set_ns(starttls, XMPP_NS_TLS);
}
return starttls;
}
static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
const char * const mechanism)
{
@@ -418,7 +468,25 @@ static void _auth(xmpp_conn_t * const conn)
xmpp_stanza_t *auth, *authdata, *query, *child, *iq;
char *str, *authid;
if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
if (conn->tls_support)
{
auth = _make_starttls(conn);
if (!auth) {
disconnect_mem_error(conn);
return;
}
handler_add(conn, _handle_proceedtls_default,
XMPP_NS_TLS, NULL, NULL, NULL);
xmpp_send(conn, auth);
xmpp_stanza_release(auth);
/* TLS was tried, unset flag */
conn->tls_support = 0;
}
else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
auth = _make_sasl_auth(conn, "DIGEST-MD5");
if (!auth) {
disconnect_mem_error(conn);
@@ -592,6 +660,16 @@ void auth_handle_open(xmpp_conn_t * const conn)
FEATURES_TIMEOUT, NULL);
}
/* called when stream:stream tag received after TLS connection */
static void _handle_open_tls(xmpp_conn_t * const conn)
{
xmpp_debug(conn->ctx, "xmpp", "TLS successful, proceeding with SASL");
/* go straight to SASL auth */
_auth(conn);
}
/* called when stream:stream tag received after SASL auth */
static void _handle_open_sasl(xmpp_conn_t * const conn)
{

View File

@@ -24,6 +24,7 @@
#include "strophe.h"
#include "sock.h"
#include "tls.h"
#include "hash.h"
#include "util.h"
@@ -157,6 +158,7 @@ struct _xmpp_conn_t {
int error;
xmpp_stream_error_t *stream_error;
sock_t sock;
tls_t *tls;
int tls_support;
int sasl_support; /* if true, field is a bitfield of supported

View File

@@ -41,6 +41,7 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
conn->type = XMPP_UNKNOWN;
conn->sock = -1;
conn->tls = NULL;
conn->timeout_stamp = 0;
conn->error = 0;
conn->stream_error = NULL;
@@ -241,6 +242,9 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
xmpp_conn_handler callback,
void * const userdata)
{
char connectdomain[2048];
int connectport;
conn->type = XMPP_CLIENT;
if (domain) {
@@ -250,8 +254,8 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
}
if (!conn->domain) return -1;
/* TODO: look up SRV record for actual host and port */
conn->sock = sock_connect(conn->domain, 5222);
sock_srv_lookup("xmpp-client", "tcp", conn->domain, connectdomain, 2048, &connectport);
conn->sock = sock_connect(connectdomain, connectport);
if (conn->sock < 0) return -1;
/* setup handler */
@@ -265,7 +269,7 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
conn->state = XMPP_STATE_CONNECTING;
conn->timeout_stamp = time_stamp();
xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", conn->domain);
xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", connectdomain);
return 0;
}
@@ -285,6 +289,11 @@ void conn_disconnect(xmpp_conn_t * const conn)
{
xmpp_debug(conn->ctx, "xmpp", "Closing socket.");
conn->state = XMPP_STATE_DISCONNECTED;
if (conn->tls) {
tls_stop(conn->tls);
tls_free(conn->tls);
conn->tls = NULL;
}
sock_close(conn->sock);
/* fire off connection handler */

View File

@@ -26,11 +26,13 @@
void xmpp_initialize(void)
{
sock_initialize();
tls_initialize();
}
void xmpp_shutdown(void)
{
sock_shutdown();
tls_shutdown();
}
/** version **/

View File

@@ -61,16 +61,31 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
sq = conn->send_queue_head;
while (sq) {
towrite = sq->len - sq->written;
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
if (ret < 0 && !sock_is_recoverable(sock_error())) {
/* an error occured */
conn->error = sock_error();
break;
} else if (ret < towrite) {
/* not all data could be sent now */
if (ret >= 0) sq->written += ret;
break;
if (conn->tls) {
ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
/* an error occured */
conn->error = tls_error(conn->tls);
break;
} else if (ret < towrite) {
/* not all data could be sent now */
if (ret >= 0) sq->written += ret;
break;
}
} else {
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
if (ret < 0 && !sock_is_recoverable(sock_error())) {
/* an error occured */
conn->error = sock_error();
break;
} else if (ret < towrite) {
/* not all data could be sent now */
if (ret >= 0) sq->written += ret;
break;
}
}
/* all data for this queue item written, delete and move on */
@@ -161,7 +176,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
/* no events happened */
if (ret == 0) return;
/* process events */
connitem = ctx->connlist;
while (connitem) {
@@ -191,7 +206,12 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
break;
case XMPP_STATE_CONNECTED:
if (FD_ISSET(conn->sock, &rfds)) {
ret = sock_read(conn->sock, buf, 4096);
if (conn->tls) {
ret = tls_read(conn->tls, buf, 4096);
} else {
ret = sock_read(conn->sock, buf, 4096);
}
if (ret > 0) {
ret = XML_Parse(conn->parser, buf, ret, 0);
if (!ret) {
@@ -201,10 +221,19 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
conn_disconnect(conn);
}
} else {
/* return of 0 means socket closed by server */
xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
conn->error = ECONNRESET;
conn_disconnect(conn);
if (conn->tls) {
if (!tls_is_recoverable(tls_error(conn->tls)))
{
xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error.");
conn->error = tls_error(conn->tls);
conn_disconnect(conn);
}
} else {
/* return of 0 means socket closed by server */
xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
conn->error = ECONNRESET;
conn_disconnect(conn);
}
}
}

View File

@@ -19,6 +19,7 @@
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windns.h>
#define snprintf _snprintf
#else
#include <errno.h>
@@ -163,3 +164,54 @@ int sock_connect_error(const sock_t sock)
if (ret < 0) return ret;
return error;
}
void sock_srv_lookup(const char *service, const char *proto, const char *domain, char *resulttarget, int resulttargetlength, int *resultport)
{
int set = 0;
char fulldomain[2048];
snprintf(fulldomain, 2048, "_%s._%s.%s", service, proto, domain);
#ifdef _WIN32
{
HINSTANCE hdnsapi = NULL;
DNS_STATUS (WINAPI * pDnsQuery_A)(PCSTR, WORD, DWORD, PIP4_ARRAY, PDNS_RECORD*, PVOID*);
void (WINAPI * pDnsRecordListFree)(PDNS_RECORD, DNS_FREE_TYPE);
if (hdnsapi = LoadLibrary("dnsapi.dll")) {
pDnsQuery_A = GetProcAddress(hdnsapi, "DnsQuery_A");
pDnsRecordListFree = GetProcAddress(hdnsapi, "DnsRecordListFree");
if (pDnsQuery_A && pDnsRecordListFree) {
PDNS_RECORD dnsrecords = NULL;
if (pDnsQuery_A(fulldomain, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &dnsrecords, NULL) == 0) {
PDNS_RECORD current = dnsrecords;
while (current) {
if (current->wType == DNS_TYPE_SRV) {
snprintf(resulttarget, resulttargetlength, current->Data.Srv.pNameTarget);
*resultport = current->Data.Srv.wPort;
set = 1;
current = NULL;
} else {
current = current->pNext;
}
}
}
pDnsRecordListFree(dnsrecords, DnsFreeRecordList);
}
/*UnloadLibrary(hdnsapi);*/
}
}
#else
#endif
if (!set)
{
snprintf(resulttarget, resulttargetlength, domain);
*resultport = 5222;
}
}

View File

@@ -40,4 +40,8 @@ int sock_is_recoverable(const int error);
/* checks for an error after connect, return 0 if connect successful */
int sock_connect_error(const sock_t sock);
void sock_srv_lookup(const char *service, const char *proto,
const char *domain, char *resulttarget,
int resulttargetlength, int *resultport);
#endif /* __LIBSTROPHE_SOCK_H__ */

View File

@@ -335,6 +335,47 @@ int xmpp_stanza_set_attributes(xmpp_stanza_t * const stanza,
return XMPP_EOK;
}
int xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza)
{
if (stanza->attributes == NULL) {
return 0;
}
return hash_num_keys(stanza->attributes);
}
int xmpp_stanza_get_attributes(xmpp_stanza_t * const stanza,
char **attr, int attrlen)
{
hash_iterator_t *iter;
char *key;
int num = 0;
if (stanza->attributes == NULL) {
return 0;
}
iter = hash_iter_new(stanza->attributes);
while ((key = hash_iter_next(iter)) != NULL && attrlen) {
attr[num++] = key;
attrlen--;
if (attrlen == 0) {
hash_iter_release(iter);
return num;
}
attr[num++] = hash_get(stanza->attributes, key);
attrlen--;
if (attrlen == 0) {
hash_iter_release(iter);
return num;
}
}
hash_iter_release(iter);
return num;
}
int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
const char * const key,
const char * const value)

View File

@@ -15,7 +15,6 @@
#ifndef __LIBSTROPHE_TLS_H__
#define __LIBSTROPHE_TLS_H__
#include <gnutls/gnutls.h>
#include "common.h"
#include "sock.h"

223
src/tls_openssl.c Normal file
View File

@@ -0,0 +1,223 @@
/* tls.c
** libstrophe XMPP client library -- TLS abstraction openssl impl.
**
** Copyright (C) 2005 OGG, LCC. All rights reserved.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
*/
#include <openssl/ssl.h>
#include <openssl/applink.c>
#include "common.h"
#include "tls.h"
#include "sock.h"
struct _tls {
xmpp_ctx_t *ctx;
sock_t sock;
SSL_CTX *ssl_ctx;
SSL *ssl;
int lasterror;
};
void tls_initialize(void)
{
#ifdef _WIN32
RAND_screen ();
#else
{
char filename[512];
char *file;
filename[0] = '\0';
file = RAND_file_name(filename, 512);
if (file && strlen(file) != 0) {
RAND_load_file(filename, /*It's over */9000);
}
}
#endif
SSL_library_init();
SSL_load_error_strings();
return;
}
void tls_shutdown(void)
{
return;
}
void tls_logerror(tls_t *tls)
{
char *texterror = NULL;
switch(tls->lasterror) {
case SSL_ERROR_NONE:
texterror = "No error.";
break;
case SSL_ERROR_ZERO_RETURN:
texterror = "Connection closed.";
break;
case SSL_ERROR_WANT_READ:
texterror = "Data waiting to read.";
break;
case SSL_ERROR_WANT_WRITE:
texterror = "Data waiting to write.";
break;
case SSL_ERROR_WANT_CONNECT:
texterror = "Not yet connected.";
break;
case SSL_ERROR_WANT_ACCEPT:
texterror = "Not yet accepted.";
break;
case SSL_ERROR_WANT_X509_LOOKUP:
texterror = "No certification.";
break;
case SSL_ERROR_SYSCALL:
texterror = "I/O error.";
break;
case SSL_ERROR_SSL:
texterror = "SSL library internal error";
break;
default:
texterror = "Unknown error";
break;
}
xmpp_debug(tls->ctx, "xmpp", "SSL error: %s", texterror);
}
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
{
tls_t *tls = xmpp_alloc(ctx, sizeof(*tls));
if (tls) {
int ret;
memset(tls, 0, sizeof(*tls));
tls->ctx = ctx;
tls->sock = sock;
tls->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_client_cert_cb(tls->ssl_ctx, NULL);
SSL_CTX_set_mode (tls->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_verify (tls->ssl_ctx, SSL_VERIFY_NONE, NULL);
tls->ssl = SSL_new(tls->ssl_ctx);
ret = SSL_set_fd(tls->ssl, sock);
if (ret <= 0) {
tls->lasterror = SSL_get_error(tls->ssl, ret);
tls_error(tls);
tls_free(tls);
tls = NULL;
}
}
return tls;
}
void tls_free(tls_t *tls)
{
SSL_CTX_free(tls->ssl_ctx);
xmpp_free(tls->ctx, tls);
return;
}
int tls_set_credentials(tls_t *tls, const char *cafilename)
{
return -1;
}
int tls_start(tls_t *tls)
{
int ret = -1;
/* Since we're non-blocking, loop the connect call until it
succeeds or fails */
while (ret == -1) {
ret = SSL_connect(tls->ssl);
/* wait for something to happen on the sock before looping back */
if (ret == -1) {
fd_set fds;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1000;
FD_ZERO(&fds);
FD_SET(tls->sock, &fds);
select(tls->sock + 1, &fds, &fds, NULL, &tv);
}
}
if (ret <= 0) {
tls->lasterror = SSL_get_error(tls->ssl, ret);
tls_logerror(tls);
return 0;
}
return 1;
}
int tls_stop(tls_t *tls)
{
int ret;
ret = SSL_shutdown(tls->ssl);
if (ret <= 0) {
tls->lasterror = SSL_get_error(tls->ssl, ret);
tls_logerror(tls);
return 0;
}
return 1;
}
int tls_error(tls_t *tls)
{
return tls->lasterror;
}
int tls_is_recoverable(int error)
{
return (error == SSL_ERROR_NONE || error == SSL_ERROR_WANT_READ
|| error == SSL_ERROR_WANT_WRITE
|| error == SSL_ERROR_WANT_CONNECT
|| error == SSL_ERROR_WANT_ACCEPT);
}
int tls_read(tls_t *tls, void * const buff, const size_t len)
{
int ret = SSL_read(tls->ssl, buff, len);
if (ret <= 0) {
tls->lasterror = SSL_get_error(tls->ssl, ret);
tls_logerror(tls);
}
return ret;
}
int tls_write(tls_t *tls, const void * const buff, const size_t len)
{
int ret = SSL_write(tls->ssl, buff, len);
if (ret <= 0) {
tls->lasterror = SSL_get_error(tls->ssl, ret);
tls_logerror(tls);
}
return ret;
}