38 #include <arpa/inet.h>
39 #include <netinet/in.h>
43 #include <sys/socket.h>
44 #include <sys/eventfd.h>
45 #include <event2/event.h>
46 #include <event2/bufferevent.h>
47 #include <event2/bufferevent_ssl.h>
48 #include <event2/thread.h>
49 #include <event2/listener.h>
50 #include <openssl/ssl.h>
51 #include <openssl/rand.h>
52 #include <openssl/conf.h>
53 #include <openssl/engine.h>
54 #include <openssl/err.h>
56 #include "qlibc/qlibc.h"
63 typedef struct ad_hook_s ad_hook_t;
74 static void notify_cb(
struct bufferevent *buffer,
void *userdata);
75 static void *server_loop(
void *instance);
77 static void libevent_log_cb(
int severity,
const char *msg);
78 static int set_undefined_options(
ad_server_t *server);
79 static SSL_CTX *init_ssl(
const char *cert_path,
const char *pkey_path);
80 static void listener_cb(
struct evconnlistener *listener,
81 evutil_socket_t evsocket,
struct sockaddr *sockaddr,
82 int socklen,
void *userdata);
86 static void conn_read_cb(
struct bufferevent *buffer,
void *userdata) ;
87 static void conn_write_cb(
struct bufferevent *buffer,
void *userdata);
88 static void conn_event_cb(
struct bufferevent *buffer,
short what,
void *userdata);
89 static void conn_cb(
ad_conn_t *conn,
int event);
90 static int call_hooks(
short event,
ad_conn_t *conn);
92 static void *get_userdata(
ad_conn_t *conn,
int index);
97 static bool initialized =
false;
137 if (server == NULL) {
142 server->
options = qhashtbl(0, 0);
143 server->
stats = qhashtbl(100, QHASHTBL_THREADSAFE);
144 server->
hooks = qlist(0);
150 DEBUG(
"Created a server object.");
160 DEBUG(
"Starting a server.");
163 set_undefined_options(server);
167 event_set_log_callback(libevent_log_cb);
169 event_enable_debug_mode();
176 struct sockaddr *sockaddr = NULL;
177 size_t sockaddr_len = 0;
178 if (addr[0] ==
'/') {
179 struct sockaddr_un unixaddr;
180 bzero((
void *) &unixaddr,
sizeof(
struct sockaddr_un));
181 if (strlen(addr) >=
sizeof(unixaddr.sun_path)) {
183 DEBUG(
"Too long unix socket name. '%s'", addr);
186 unixaddr.sun_family = AF_UNIX;
187 strcpy(unixaddr.sun_path, addr);
188 sockaddr = (
struct sockaddr *) &unixaddr;
189 sockaddr_len =
sizeof(unixaddr);
190 }
else if (strstr(addr,
":")) {
191 struct sockaddr_in6 ipv6addr;
192 bzero((
void *) &ipv6addr,
sizeof(
struct sockaddr_in6));
193 ipv6addr.sin6_family = AF_INET6;
194 ipv6addr.sin6_port = htons(port);
195 evutil_inet_pton(AF_INET6, addr, &ipv6addr.sin6_addr);
196 sockaddr = (
struct sockaddr *) &ipv6addr;
197 sockaddr_len =
sizeof(ipv6addr);
199 struct sockaddr_in ipv4addr;
200 bzero((
void *) &ipv4addr,
sizeof(
struct sockaddr_in));
201 ipv4addr.sin_family = AF_INET;
202 ipv4addr.sin_port = htons(port);
203 ipv4addr.sin_addr.s_addr =
204 (IS_EMPTY_STR(addr)) ? INADDR_ANY : inet_addr(addr);
205 sockaddr = (
struct sockaddr *) &ipv4addr;
206 sockaddr_len =
sizeof(ipv4addr);
213 server->
sslctx = init_ssl(cert_path, pkey_path);
214 if (server->
sslctx == NULL) {
215 ERROR(
"Couldn't load certificate file(%s) or private key file(%s).",
216 cert_path, pkey_path);
219 DEBUG(
"SSL Initialized.");
224 server->
evbase = event_base_new();
226 ERROR(
"Failed to create a new event base.");
232 int notifyfd = eventfd(0, 0);
233 server->
notify_buffer = bufferevent_socket_new(server->
evbase, notifyfd, BEV_OPT_CLOSE_ON_FREE);
234 bufferevent_setcb(server->
notify_buffer, NULL, notify_cb, NULL, server);
237 server->
listener = evconnlistener_new_bind(
238 server->
evbase, listener_cb, (
void *)server,
239 LEV_OPT_THREADSAFE | LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
241 sockaddr, sockaddr_len);
243 ERROR(
"Failed to bind on %s:%d", addr, port);
249 INFO(
"Listening on %s:%d%s", addr, port, ((server->
sslctx) ?
" (SSL)" :
""));
253 DEBUG(
"Launching server as a thread.")
254 server->
thread = NEW_OBJECT(pthread_t);
255 pthread_create(server->
thread, NULL, &server_loop, (
void *)server);
258 int *retval = server_loop(server);
259 exitstatus = *retval;
262 close_server(server);
279 DEBUG(
"Send loopexit notification.");
280 notify_loopexit(server);
284 close_server(server);
295 if (server == NULL)
return;
298 if (thread && server->
thread) {
299 notify_loopexit(server);
301 close_server(server);
305 event_base_free(server->
evbase);
309 SSL_CTX_free(server->
sslctx);
321 qlist_t *tbl = server->
hooks;
323 while ((hook = tbl->popfirst(tbl, NULL))) {
324 if (hook->method) free(hook->method);
330 DEBUG(
"Server terminated.");
352 CRYPTO_cleanup_all_ex_data();
353 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
377 return (value) ? atoi(value) : 0;
396 const char *pkey_path) {
398 SSL_CTX *sslctx = SSL_CTX_new(SSLv23_server_method());
399 if (! SSL_CTX_use_certificate_file(sslctx, cert_path, SSL_FILETYPE_PEM) ||
400 ! SSL_CTX_use_PrivateKey_file(sslctx, pkey_path, SSL_FILETYPE_PEM)) {
402 ERROR(
"Couldn't load certificate file(%s) or private key file(%s).",
403 cert_path, pkey_path);
428 SSL_CTX_free(server->
sslctx);
453 return server->
stats;
468 bzero((
void *)&hook,
sizeof(ad_hook_t));
469 hook.method = (method) ? strdup(method) : NULL;
471 hook.userdata = userdata;
473 server->
hooks->addlast(server->
hooks, (
void *)&hook,
sizeof(ad_hook_t));
482 return set_userdata(conn, 0, userdata, free_cb);
491 return get_userdata(conn, 0);
505 return set_userdata(conn, 1, extra, free_cb);
512 return get_userdata(conn, 1);
524 char *prev = conn->
method;
528 conn->
method = strdup(method);
535 #ifndef _DOXYGEN_SKIP
544 return bufferevent_write(server->
notify_buffer, &x,
sizeof(uint64_t));
547 static void notify_cb(
struct bufferevent *buffer,
void *userdata) {
549 event_base_loopexit(server->
evbase, NULL);
550 DEBUG(
"Existing loop.");
553 static void *server_loop(
void *instance) {
556 int *retval = NEW_OBJECT(
int);
558 event_base_loop(server->
evbase, 0);
559 DEBUG(
"Loop finished");
560 *retval = (event_base_got_break(server->
evbase)) ? -1 : 0;
566 DEBUG(
"Closing server.");
574 evconnlistener_free(server->
listener);
580 DEBUG(
"Waiting server's last loop to finish.");
581 pthread_join(*(server->
thread), &retval);
586 INFO(
"Server closed.");
589 static void libevent_log_cb(
int severity,
const char *msg) {
591 case _EVENT_LOG_MSG : {
595 case _EVENT_LOG_WARN : {
599 case _EVENT_LOG_ERR : {
611 static int set_undefined_options(
ad_server_t *server) {
614 for (
int i = 0; ! IS_EMPTY_STR(default_options[i][0]); i++) {
624 static SSL_CTX *init_ssl(
const char *cert_path,
const char *pkey_path) {
625 SSL_CTX *sslctx = SSL_CTX_new(SSLv23_server_method());
626 if (! SSL_CTX_use_certificate_file(sslctx, cert_path, SSL_FILETYPE_PEM) ||
627 ! SSL_CTX_use_PrivateKey_file(sslctx, pkey_path, SSL_FILETYPE_PEM)) {
633 static void listener_cb(
struct evconnlistener *listener, evutil_socket_t socket,
634 struct sockaddr *sockaddr,
int socklen,
void *userdata) {
635 DEBUG(
"New connection.");
639 struct bufferevent *buffer = NULL;
641 buffer = bufferevent_openssl_socket_new(server->
evbase, socket,
643 BUFFEREVENT_SSL_ACCEPTING,
644 BEV_OPT_CLOSE_ON_FREE);
646 buffer = bufferevent_socket_new(server->
evbase, socket, BEV_OPT_CLOSE_ON_FREE);
648 if (buffer == NULL)
goto error;
654 bzero((
void *)&tm,
sizeof(
struct timeval));
656 bufferevent_set_timeouts(buffer, &tm, NULL);
660 void *conn = conn_new(server, buffer);
661 if (! conn)
goto error;
666 if (buffer) bufferevent_free(buffer);
667 ERROR(
"Failed to create a connection handler.");
668 event_base_loopbreak(server->
evbase);
673 if (server == NULL || buffer == NULL) {
679 if (conn == NULL)
return NULL;
684 conn->
in = bufferevent_get_input(buffer);
685 conn->
out = bufferevent_get_output(buffer);
689 bufferevent_setcb(buffer, conn_read_cb, conn_write_cb, conn_event_cb, (
void *)conn);
690 bufferevent_setwatermark(buffer, EV_WRITE, 0, 0);
691 bufferevent_enable(buffer, EV_WRITE);
692 bufferevent_enable(buffer, EV_READ);
700 static void conn_reset(
ad_conn_t *conn) {
708 WARN(
"Found unreleased userdata.");
728 int sslerr = bufferevent_get_openssl_error(conn->
buffer);
731 ERR_error_string_n(sslerr, errmsg,
sizeof(errmsg));
732 ERROR(
"SSL %s (err:%d)", errmsg, sslerr);
735 bufferevent_free(conn->
buffer);
741 #define DRAIN_EVBUFFER(b) evbuffer_drain(b, evbuffer_get_length(b))
742 static void conn_read_cb(
struct bufferevent *buffer,
void *userdata) {
748 static void conn_write_cb(
struct bufferevent *buffer,
void *userdata) {
754 static void conn_event_cb(
struct bufferevent *buffer,
short what,
void *userdata) {
755 DEBUG(
"event_cb 0x%x", what);
758 if (what & BEV_EVENT_EOF || what & BEV_EVENT_ERROR || what & BEV_EVENT_TIMEOUT) {
764 static void conn_cb(
ad_conn_t *conn,
int event) {
765 DEBUG(
"conn_cb: status:0x%x, event:0x%x", conn->
status, event)
767 int status = call_hooks(event, conn);
782 DEBUG(
"Draining in-buffer. %d", conn->
status);
783 DRAIN_EVBUFFER(conn->
in);
788 if (evbuffer_get_length(conn->
out) <= 0) {
790 call_hooks(newevent, conn);
792 DEBUG(
"Connection closed.");
798 static int call_hooks(
short event,
ad_conn_t *conn) {
799 DEBUG(
"call_hooks: event 0x%x", event);
803 bzero((
void *)&obj,
sizeof(qdlobj_t));
804 while (hooks->getnext(hooks, &obj,
false) ==
true) {
805 ad_hook_t *hook = (ad_hook_t *)obj.data;
807 if (hook->method && conn->
method && strcmp(hook->method, conn->
method)) {
810 int status = hook->cb(event, conn, hook->
userdata);
811 if (status !=
AD_OK) {
821 conn->
userdata[index] = (
void *)userdata;
826 static void *get_userdata(
ad_conn_t *conn,
int index) {
830 #endif // _DOXYGEN_SKIP
void ad_server_register_hook(ad_server_t *server, ad_callback cb, void *userdata)
Register user hook.
struct bufferevent * notify_buffer
int ad_server_start(ad_server_t *server)
Start server.
SSL_CTX * ad_server_get_ssl_ctx(ad_server_t *server)
Get OpenSSL SSL_CTX object.
SSL_CTX * ad_server_ssl_ctx_create_simple(const char *cert_path, const char *pkey_path)
Helper method for creating minimal OpenSSL SSL_CTX object.
void ad_server_free(ad_server_t *server)
Release server object and all the resources.
struct bufferevent * buffer
#define AD_NUM_USERDATA
Defaults.
#define AD_EVENT_SHUTDOWN
char * ad_server_get_option(ad_server_t *server, const char *key)
Retrieve server option.
#define AD_SERVER_OPTIONS
Server option names and default values.
void ad_server_set_option(ad_server_t *server, const char *key, const char *value)
Set server option.
struct evconnlistener * listener
int ad_server_get_option_int(ad_server_t *server, const char *key)
Retrieve server option in integer format.
ad_userdata_free_cb userdata_free_cb[2]
ad_server_t * ad_server_new(void)
Create a server object.
void(* ad_userdata_free_cb)(ad_conn_t *conn, void *userdata)
char * ad_conn_set_method(ad_conn_t *conn, char *method)
Set method name on this connection.
#define AD_EVENT_INIT
Event types.
void * ad_conn_set_userdata(ad_conn_t *conn, const void *userdata, ad_userdata_free_cb free_cb)
Attach userdata into the connection.
void * ad_conn_set_extra(ad_conn_t *conn, const void *extra, ad_userdata_free_cb free_cb)
Set extra userdata into the connection.
void ad_server_register_hook_on_method(ad_server_t *server, const char *method, ad_callback cb, void *userdata)
Register user hook on method name.
void ad_server_set_ssl_ctx(ad_server_t *server, SSL_CTX *sslctx)
Attach OpenSSL SSL_CTX to the server.
qhashtbl_t * ad_server_get_stats(ad_server_t *server, const char *key)
Return internal statistic counter map.
enum ad_log_e ad_log_level(enum ad_log_e log_level)
Set debug output level.
int(* ad_callback)(short event, ad_conn_t *conn, void *userdata)
User callback(hook) prototype.
void * ad_conn_get_extra(ad_conn_t *conn)
Get extra userdata attached in this connection.
struct event_base * evbase
void ad_server_global_free(void)
Clean up all the global objects.
void ad_server_stop(ad_server_t *server)
Stop server.
void * ad_conn_get_userdata(ad_conn_t *conn)
Get userdata attached in the connection.