WIP Initial server support

This commit is contained in:
Dmitry Podgorny
2016-09-20 23:12:24 +03:00
parent b1b827cde6
commit b1698f7b5d
12 changed files with 384 additions and 4 deletions

1
.gitignore vendored
View File

@@ -35,6 +35,7 @@ examples/basic
examples/bot
examples/component
examples/roster
examples/server
examples/uuid
examples/vcard
test_stamp

View File

@@ -36,6 +36,7 @@ libstrophe_la_SOURCES = \
src/resolver.c \
src/sasl.c \
src/scram.c \
src/server.c \
src/sha1.c \
src/snprintf.c \
src/sock.c \
@@ -99,6 +100,7 @@ noinst_PROGRAMS = \
examples/bot \
examples/component \
examples/roster \
examples/server \
examples/uuid \
examples/vcard
@@ -117,6 +119,9 @@ examples_component_LDADD = $(STROPHE_LIBS)
examples_roster_SOURCES = examples/roster.c
examples_roster_CFLAGS = $(STROPHE_FLAGS)
examples_roster_LDADD = $(STROPHE_LIBS)
examples_server_SOURCES = examples/server.c
examples_server_CFLAGS = $(STROPHE_FLAGS)
examples_server_LDADD = $(STROPHE_LIBS)
examples_uuid_SOURCES = examples/uuid.c
examples_uuid_CFLAGS = $(STROPHE_FLAGS)
examples_uuid_LDADD = $(STROPHE_LIBS)

83
examples/server.c Normal file
View File

@@ -0,0 +1,83 @@
#include <stdio.h>
#include <string.h>
#include <strophe.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif
int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
void * const userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *success;
if (strcmp(xmpp_stanza_get_name(stanza), "auth") == 0) {
success = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(success, "success");
xmpp_stanza_set_ns(success, XMPP_NS_SASL);
xmpp_send(conn, success);
xmpp_stanza_release(success);
} else
xmpp_disconnect(conn);
return 1;
}
void server_handler(xmpp_server_t * const srv, xmpp_conn_t * const conn,
const xmpp_server_event_t event, const int error,
void * const userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
static char *attrs[] = {
"xmlns", XMPP_NS_CLIENT, "xmlns:stream", XMPP_NS_STREAMS,
"id", "0123456789", "from", "127.0.0.1", "version", "1.0",
"xml:lang", "en",
};
switch (event) {
case XMPP_SERVER_ACCEPT:
printf("Event XMPP_SERVER_ACCEPT\n");
break;
case XMPP_SERVER_OPEN_STREAM:
printf("Event XMPP_SERVER_OPEN_STREAM\n");
xmpp_handler_add(conn, message_handler, NULL, NULL, NULL, ctx);
xmpp_conn_open_stream(conn, attrs, ARRAY_SIZE(attrs));
xmpp_send_raw_string(conn,
"<stream:features>"
"<mechanisms xmlns=\"%s\"><mechanism>PLAIN</mechanism>"
"</mechanisms></stream:features>", XMPP_NS_SASL);
break;
case XMPP_SERVER_DISCONNECT:
printf("Event XMPP_SERVER_DISCONNECT\n");
xmpp_stop(ctx);
break;
default:
printf("Unknown event\n");
break;
}
}
int main()
{
xmpp_ctx_t *ctx;
xmpp_log_t *log;
xmpp_server_t *srv;
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
ctx = xmpp_ctx_new(NULL, log);
srv = xmpp_server_new(ctx);
xmpp_server_listen(srv, 0, server_handler, ctx);
xmpp_run(ctx);
xmpp_server_stop(srv);
xmpp_server_free(srv);
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}

View File

@@ -44,6 +44,7 @@ LOCAL_SRC_FILES := \
../src/resolver.c \
../src/sasl.c \
../src/scram.c \
../src/server.c \
../src/sha1.c \
../src/snprintf.c \
../src/sock.c \

View File

@@ -43,6 +43,11 @@ typedef struct _xmpp_connlist_t {
struct _xmpp_connlist_t *next;
} xmpp_connlist_t;
typedef struct _xmpp_serverlist_t {
xmpp_server_t *server;
struct _xmpp_serverlist_t *next;
} xmpp_serverlist_t;
struct _xmpp_ctx_t {
const xmpp_mem_t *mem;
const xmpp_log_t *log;
@@ -50,6 +55,7 @@ struct _xmpp_ctx_t {
xmpp_rand_t *rand;
xmpp_loop_status_t loop_status;
xmpp_connlist_t *connlist;
xmpp_serverlist_t *serverlist;
unsigned long timeout;
};
@@ -225,6 +231,24 @@ int conn_tls_start(xmpp_conn_t * const conn);
void conn_prepare_reset(xmpp_conn_t * const conn, xmpp_open_handler handler);
void conn_parser_reset(xmpp_conn_t * const conn);
typedef enum {
XMPP_STATE_STOPPED,
XMPP_STATE_LISTENING
} xmpp_server_state_t;
struct _xmpp_server_t {
xmpp_ctx_t *ctx;
xmpp_server_state_t state;
sock_t sock;
unsigned short port;
xmpp_server_handler callback;
void *userdata;
/* incomming connections list */
};
void server_accept(xmpp_server_t * const srv);
void server_handle_open(xmpp_conn_t * const conn);
typedef enum {
XMPP_STANZA_UNKNOWN,

View File

@@ -625,14 +625,15 @@ int xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes,
{
char *tag;
if (!conn->is_raw)
if (!conn->is_raw && conn->type != XMPP_INCOMING)
return XMPP_EINVOP;
tag = _conn_build_stream_tag(conn, attributes, attributes_len);
if (!tag)
return XMPP_EMEM;
conn_prepare_reset(conn, auth_handle_open_raw);
if (conn->type != XMPP_INCOMING)
conn_prepare_reset(conn, auth_handle_open_raw);
xmpp_send_raw_string(conn, "<?xml version=\"1.0\"?>%s", tag);
xmpp_free(conn->ctx, tag);

View File

@@ -415,6 +415,7 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
ctx->log = log;
ctx->connlist = NULL;
ctx->serverlist = NULL;
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
ctx->rand = xmpp_rand_new(ctx);
ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;

View File

@@ -67,7 +67,9 @@
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
{
xmpp_connlist_t *connitem;
xmpp_serverlist_t *serveritem;
xmpp_conn_t *conn;
xmpp_server_t *srv;
fd_set rfds, wfds;
sock_t max = 0;
int ret;
@@ -206,6 +208,23 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
connitem = connitem->next;
}
serveritem = ctx->serverlist;
while (serveritem) {
srv = serveritem->server;
switch (srv->state) {
case XMPP_STATE_LISTENING:
FD_SET(srv->sock, &rfds);
if (srv->sock > max) max = srv->sock;
break;
case XMPP_STATE_STOPPED:
/* do nothing */
default:
break;
}
serveritem = serveritem->next;
}
/* check for events */
if (max > 0)
ret = select(max + 1, &rfds, &wfds, NULL, &tv);
@@ -293,6 +312,24 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
connitem = connitem->next;
}
serveritem = ctx->serverlist;
while (serveritem) {
srv = serveritem->server;
switch (srv->state) {
case XMPP_STATE_LISTENING:
if (FD_ISSET(srv->sock, &rfds)) {
server_accept(srv);
}
break;
case XMPP_STATE_STOPPED:
/* do nothing */
default:
break;
}
serveritem = serveritem->next;
}
/* fire any ready handlers */
handler_fire_timed(ctx);
}

162
src/server.c Normal file
View File

@@ -0,0 +1,162 @@
/* server.c
* strophe XMPP client library -- server object functions
*
* Copyright (C) 2016 Dmitry Podgorny <pasis.ua@gmail.com>
*
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
*/
/** @file
* Server management.
*/
/** @defgroup Server Server management
*/
#include <string.h>
#include "common.h"
#include "strophe.h"
static xmpp_server_t *conn2srv(xmpp_conn_t *conn);
static void _server_conn_handler(xmpp_conn_t * const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t * const stream_error,
void * const userdata);
xmpp_server_t *xmpp_server_new(xmpp_ctx_t * const ctx)
{
xmpp_server_t *srv;
xmpp_serverlist_t *item;
srv = xmpp_alloc(ctx, sizeof(*srv));
item = xmpp_alloc(ctx, sizeof(*item));
if (srv == NULL || item == NULL) {
xmpp_error(ctx, "xmpp", "Failed to allocate memory.");
xmpp_free(ctx, srv);
xmpp_free(ctx, item);
srv = NULL;
item = NULL;
}
if (srv != NULL) {
memset(srv, 0, sizeof(*srv));
srv->state = XMPP_STATE_STOPPED;
srv->ctx = ctx;
srv->sock = -1;
item->server = srv;
item->next = ctx->serverlist;
ctx->serverlist = item;
}
return srv;
}
void xmpp_server_free(xmpp_server_t * const srv)
{
xmpp_ctx_t *ctx = srv->ctx;
xmpp_serverlist_t *item, *prev = NULL;
item = ctx->serverlist;
while (item != NULL) {
if (item->server == srv) {
if (prev == NULL) ctx->serverlist = item->next;
if (prev != NULL) prev->next = item->next;
xmpp_free(ctx, item);
}
prev = item;
item = item->next;
}
xmpp_free(ctx, srv);
}
int xmpp_server_listen(xmpp_server_t * const srv, unsigned short port,
xmpp_server_handler callback, void * const userdata)
{
int rc = 0;
if (port == 0) port = XMPP_PORT_CLIENT;
srv->callback = callback;
srv->userdata = userdata;
srv->port = port;
srv->sock = sock_listen(port);
if (srv->sock >= 0) {
srv->state = XMPP_STATE_LISTENING;
xmpp_debug(srv->ctx, "xmpp", "Listening on port %u.", port);
} else
rc = XMPP_EINT;
return rc;
}
void xmpp_server_stop(xmpp_server_t * const srv)
{
srv->state = XMPP_STATE_STOPPED;
sock_stop_listen(srv->sock);
xmpp_debug(srv->ctx, "xmpp", "Server stopped on %u.", srv->port);
}
void server_accept(xmpp_server_t * const srv)
{
xmpp_conn_t *conn;
sock_t fd;
fd = sock_accept(srv->sock);
if (fd >= 0) {
xmpp_debug(srv->ctx, "xmpp", "New incoming connection on port %u.",
srv->port);
conn = xmpp_conn_new(srv->ctx);
conn->type = XMPP_INCOMING;
conn->state = XMPP_STATE_CONNECTED;
conn->sock = fd;
conn->conn_handler = _server_conn_handler;
conn->userdata = (void *)srv;
conn->authenticated = 1; /* don't ignore handlers */
conn_prepare_reset(conn, server_handle_open);
srv->callback(srv, conn, XMPP_SERVER_ACCEPT, 0, srv->userdata);
}
}
void server_handle_open(xmpp_conn_t * const conn)
{
xmpp_server_t *srv = conn2srv(conn);
/* XXX need to reset parser and re-open stream after authentication */
srv->callback(srv, conn, XMPP_SERVER_OPEN_STREAM, 0, srv->userdata);
}
static xmpp_server_t *conn2srv(xmpp_conn_t *conn)
{
xmpp_server_t *srv;
/* XXX dirty hack */
if (conn->ctx->serverlist != NULL)
srv = conn->ctx->serverlist->server;
else
srv = NULL;
return srv;
}
static void _server_conn_handler(xmpp_conn_t * const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t * const stream_error,
void * const userdata)
{
xmpp_server_t *srv = (xmpp_server_t *)userdata;
if (status == XMPP_CONN_DISCONNECT) {
/* remove conn from the list */
/* XXX when client sends </stream> first, server should close stream too */
srv->callback(srv, conn, XMPP_SERVER_DISCONNECT, error, srv->userdata);
}
}

View File

@@ -109,6 +109,43 @@ sock_t sock_connect(const char * const host, const unsigned short port)
return sock;
}
sock_t sock_listen(const unsigned short port)
{
struct sockaddr_in addr;
sock_t sock;
int rc;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) return -1;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
rc = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (rc == 0) {
rc = listen(sock, 100);
sock_set_nonblocking(sock);
}
if (rc != 0) close(sock);
return rc == 0 ? sock : -1;
}
int sock_stop_listen(const sock_t sock)
{
return sock_close(sock);
}
sock_t sock_accept(const sock_t sock)
{
sock_t fd;
fd = accept(sock, NULL, NULL);
return fd;
}
int sock_set_keepalive(const sock_t sock, int timeout, int interval)
{
int ret;

View File

@@ -32,6 +32,9 @@ int sock_error(void);
sock_t sock_connect(const char * const host, const unsigned short port);
int sock_close(const sock_t sock);
sock_t sock_listen(const unsigned short port);
int sock_stop_listen(const sock_t sock);
sock_t sock_accept(const sock_t sock);
int sock_set_blocking(const sock_t sock);
int sock_set_nonblocking(const sock_t sock);

View File

@@ -139,7 +139,8 @@ typedef enum {
typedef enum {
XMPP_UNKNOWN,
XMPP_CLIENT,
XMPP_COMPONENT
XMPP_COMPONENT,
XMPP_INCOMING
} xmpp_conn_type_t;
typedef void (*xmpp_log_handler)(void * const userdata,
@@ -211,7 +212,7 @@ typedef struct {
xmpp_stanza_t *stanza;
} xmpp_stream_error_t;
typedef void (*xmpp_conn_handler)(xmpp_conn_t * const conn,
typedef void (*xmpp_conn_handler)(xmpp_conn_t * const conn,
const xmpp_conn_event_t event,
const int error,
xmpp_stream_error_t * const stream_error,
@@ -263,6 +264,30 @@ void xmpp_send_raw_string(xmpp_conn_t * const conn,
void xmpp_send_raw(xmpp_conn_t * const conn,
const char * const data, const size_t len);
/* server */
/* opaque server object */
typedef struct _xmpp_server_t xmpp_server_t;
/* server callback */
typedef enum {
XMPP_SERVER_ACCEPT,
XMPP_SERVER_OPEN_STREAM,
XMPP_SERVER_DISCONNECT,
XMPP_SERVER_FAIL
} xmpp_server_event_t;
typedef void (*xmpp_server_handler)(xmpp_server_t * const srv,
xmpp_conn_t * const conn,
const xmpp_server_event_t event,
const int error,
void * const userdata);
xmpp_server_t *xmpp_server_new(xmpp_ctx_t * const ctx);
void xmpp_server_free(xmpp_server_t * const srv);
int xmpp_server_listen(xmpp_server_t * const srv, unsigned short port,
xmpp_server_handler callback, void * const userdata);
void xmpp_server_stop(xmpp_server_t * const srv);
/* handlers */