WIP Initial server support
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,6 +35,7 @@ examples/basic
|
||||
examples/bot
|
||||
examples/component
|
||||
examples/roster
|
||||
examples/server
|
||||
examples/uuid
|
||||
examples/vcard
|
||||
test_stamp
|
||||
|
||||
@@ -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
83
examples/server.c
Normal 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;
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
24
src/common.h
24
src/common.h
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
37
src/event.c
37
src/event.c
@@ -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
162
src/server.c
Normal 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);
|
||||
}
|
||||
}
|
||||
37
src/sock.c
37
src/sock.c
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
29
strophe.h
29
strophe.h
@@ -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 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user