#include "../public/nsapi.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <stdbool.h>
#include <pthread.h>
#include <ucx/map.h>
#include "../util/atomic.h"
#include "httplistener.h"
#include "netsite.h"
#include "session.h"
#include "configmanager.h"
#include "log.h"
UcxMap *listener_map =
NULL;
int start_all_listener() {
ServerConfiguration *conf = cfgmgr_get_server_config();
UcxList *ls = conf->listeners;
while(ls) {
HttpListener *listener = ls->data;
http_listener_start(listener);
ls = ls->next;
}
return 0;
}
HttpListener* http_listener_create(ListenerConfig *conf) {
if(listener_map ==
NULL) {
listener_map = ucx_map_new(
16);
}
HttpListener *fl = ucx_map_sstr_get(listener_map, conf->name);
if(fl ==
NULL) {
return http_listener_new(conf);
}
HttpListener* newls = calloc(
1,
sizeof(HttpListener));
if(newls ==
NULL) {
}
newls->name = conf->name;
newls->cfg = conf->cfg;
newls->nacceptors = conf->nacceptors;
newls->default_vs.vs_name = conf->vs.ptr;
newls->port = fl->port;
newls->server_socket = fl->server_socket;
newls->running =
1;
newls->threadpool =
NULL;
newls->ref =
2;
newls->session_handler = fl->session_handler;
if(conf->threadpool.ptr !=
NULL) {
newls->threadpool = get_threadpool(conf->threadpool);
}
if(newls->threadpool ==
NULL) {
newls->threadpool = get_default_threadpool();
}
newls->acceptors = calloc(newls->nacceptors,
sizeof(
void*));
for (
int i=
0;i<newls->nacceptors;i++) {
newls->acceptors[i] = acceptor_new(newls);
}
fl->next = newls;
ucx_map_sstr_put(listener_map, newls->name, newls);
for (
int i=
0;i<newls->nacceptors;i++) {
acceptor_start(newls->acceptors[i]);
}
if(newls->port != conf->port) {
}
return newls;
}
HttpListener* http_listener_new(ListenerConfig *conf) {
if(listener_map ==
NULL) {
listener_map = ucx_map_new(
16);
}
HttpListener *fl = ucx_map_sstr_get(listener_map, conf->name);
if(fl !=
NULL) {
return fl;
}
HttpListener *listener = malloc(
sizeof(HttpListener));
listener->running =
0;
listener->cfg = conf->cfg;
listener->name = conf->name;
listener->default_vs.vs_name = conf->vs.ptr;
listener->threadpool =
NULL;
if(conf->threadpool.ptr !=
NULL) {
listener->threadpool = get_threadpool(conf->threadpool);
}
if(listener->threadpool ==
NULL) {
listener->threadpool = get_default_threadpool();
}
if(conf->blockingio) {
listener->session_handler = create_basic_session_handler();
}
else {
listener->session_handler = create_event_session_handler();
}
listener->nacceptors = conf->nacceptors;
listener->port = conf->port;
listener->ref =
1;
listener->next =
NULL;
listener->ssl =
NULL;
int error =
0;
if(conf->ssl) {
listener->ssl = malloc(
sizeof(HttpSSL));
SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_set_options(
ctx,
SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv3);
if(conf->disable_proto.ptr) {
ssize_t n =
0;
sstr_t *plist = sstrsplit(conf->disable_proto,
S(
","), &n);
if(plist) {
for(
int i=
0;i<n;i++) {
sstr_t proto = plist[i];
log_ereport(
LOG_VERBOSE,
"Listener %s: Disable protocol %s",
listener->name.ptr,
proto.ptr);
if(!sstrcasecmp(sstrtrim(proto),
S(
"SSLv2"))) {
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
}
else if(!sstrcasecmp(sstrtrim(proto),
S(
"SSLv3"))) {
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
}
else if(!sstrcasecmp(sstrtrim(proto),
S(
"TLSv1"))) {
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
}
else if(!sstrcasecmp(sstrtrim(proto),
S(
"TLSv1.1"))) {
#ifdef SSL_OP_NO_TLSv1_1
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
#else
log_ereport(
LOG_WARN,
"Listener: %s: TLSv1.1 already not supported",
listener->name.ptr);
#endif
}
else if(sstrcasecmp(sstrtrim(proto),
S(
"TLSv1.2"))) {
#ifdef SSL_OP_NO_TLSv1_2
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
#else
log_ereport(
LOG_WARN,
"Listener: %s: TLSv1.2 already not supported",
listener->name.ptr);
#endif
}
else if(sstrcasecmp(sstrtrim(proto),
S(
"TLSv1.3"))) {
#ifdef SSL_OP_NO_TLSv1_3
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_3);
#else
log_ereport(
LOG_WARN,
"Listener: %s: TLSv1.3 already not supported",
listener->name.ptr);
#endif
}
else {
error =
1;
log_ereport(
LOG_MISCONFIG,
"Listener: %s: Unknown protocol %s",
listener->name.ptr,
proto.ptr);
}
free(proto.ptr);
}
free(plist);
}
}
if(error) {
return NULL;
}
sstr_t file;
int ret;
char errbuf[
512];
if(!conf->chainfile.ptr) {
file = sstrdup(conf->certfile);
ret = SSL_CTX_use_certificate_file(ctx, file.ptr,
SSL_FILETYPE_PEM);
free(file.ptr);
if(!ret) {
ERR_error_string(ERR_get_error(), errbuf);
log_ereport(
LOG_MISCONFIG,
"Cannot load ssl chain file: %s", errbuf);
return NULL;
}
}
else {
file = sstrdup(conf->chainfile);
ret = SSL_CTX_use_certificate_chain_file(ctx, file.ptr);
free(file.ptr);
if(!ret) {
ERR_error_string(ERR_get_error(), errbuf);
log_ereport(
LOG_MISCONFIG,
"Cannot load ssl cert file: %s", errbuf);
return NULL;
}
}
file = sstrdup(conf->privkeyfile);
ret = SSL_CTX_use_PrivateKey_file(ctx, file.ptr,
SSL_FILETYPE_PEM);
free(file.ptr);
if(!ret) {
ERR_error_string(ERR_get_error(), errbuf);
log_ereport(
LOG_MISCONFIG,
"Cannot load ssl key file: %s", errbuf);
return NULL;
}
listener->ssl->sslctx = ctx;
}
ucx_map_sstr_put(listener_map, listener->name, listener);
struct sockaddr_in servaddr;
memset(&servaddr,
0,
sizeof(servaddr));
servaddr.sin_family =
AF_INET;
servaddr.sin_addr.s_addr = htonl(
INADDR_ANY);
servaddr.sin_port = htons(conf->port);
if((listener->server_socket = socket(
AF_INET,
SOCK_STREAM,
0)) == -
1) {
perror(
"Error: http_listener_new: socket");
return NULL;
}
int o =
1;
setsockopt(
listener->server_socket,
SOL_SOCKET,
SO_REUSEADDR,
&o,
sizeof(
int));
if(bind(listener->server_socket, (
struct sockaddr*)&servaddr,
sizeof(servaddr))){
log_ereport(
LOG_FAILURE,
"http_listener_new: bind failed. Port: %d", conf->port);
return NULL;
}
listener->acceptors = calloc(listener->nacceptors,
sizeof(
void*));
for (
int i=
0;i<listener->nacceptors;i++) {
listener->acceptors[i] = acceptor_new(listener);
}
return listener;
}
int http_listener_start(HttpListener *listener) {
if(listener->running) {
return 0;
}
log_ereport(
LOG_INFORM,
"start listener on port %d", listener->port);
if (listen(listener->server_socket,
256) == -
1) {
log_ereport(
LOG_FAILURE,
"http_listener_start: listen failed");
return -
1;
}
for (
int i=
0;i<listener->nacceptors;i++) {
acceptor_start(listener->acceptors[i]);
}
return 0;
}
void http_listener_ref(HttpListener *listener) {
ws_atomic_inc32(&listener->ref);
}
void http_listener_unref(HttpListener *listener) {
uint32_t ref = ws_atomic_dec32(&listener->ref);
if(ref ==
0) {
free(listener->acceptors);
free(listener);
}
}
Acceptor* acceptor_new(HttpListener *listener) {
Acceptor *acceptor = malloc(
sizeof(Acceptor));
acceptor->listener = listener;
return acceptor;
}
void acceptor_start(Acceptor *a) {
if(pthread_create(
&a->tid,
NULL,
(
void*(*)(
void*))acceptor_thread,
a) !=
0)
{
perror(
"Error: acceptor_start: pthread_create");
}
}
void* acceptor_thread(Acceptor *acceptor) {
WS_ASSERT(acceptor);
WS_ASSERT(acceptor->listener);
WS_ASSERT(acceptor->listener->session_handler);
WS_ASSERT(acceptor->listener->session_handler->enqueue_connection);
HttpListener *listener = acceptor->listener;
for (;;) {
struct sockaddr_in ca;
socklen_t length =
sizeof(ca);
int clientfd;
clientfd = accept(
listener->server_socket,
(
struct sockaddr*)&ca,
&length);
if (clientfd == -
1) {
perror(
"Error: acceptor_thread: accept");
continue;
}
HttpListener *ls = listener;
int acceptor_exit =
0;
while(ls->next) {
ls = ls->next;
acceptor_exit =
1;
}
Connection *conn = malloc(
sizeof(Connection));
conn->address = ca;
conn->fd = clientfd;
conn->listener = ls;
conn->ssl_accepted =
0;
if(ls->ssl) {
int flags;
if((flags = fcntl(conn->fd,
F_GETFL,
0)) == -
1) {
flags =
0;
}
if(fcntl(conn->fd,
F_SETFL, flags |
O_NONBLOCK)) {
perror(
"Error: acceptor_thread: fcntl");
}
SSL *ssl = SSL_new(ls->ssl->sslctx);
SSL_set_fd(ssl, clientfd);
conn->ssl = ssl;
conn->read = connection_ssl_read;
conn->write = connection_ssl_write;
conn->close = connection_ssl_close;
}
else {
conn->ssl =
NULL;
conn->read = connection_read;
conn->write = connection_write;
conn->close = connection_close;
}
if(conn) {
cfg_ref(ls->cfg);
ls->session_handler->enqueue_connection(
ls->session_handler,
conn);
}
if(acceptor_exit) {
break;
}
}
http_listener_unref(listener->next);
http_listener_unref(listener);
return NULL;
}