add websocket client to the library

This commit is contained in:
lxsang 2019-12-22 00:11:26 +01:00
parent 4c9808da78
commit 4fb4674045
6 changed files with 241 additions and 50 deletions

View File

@ -56,6 +56,5 @@ make distcheck
### To do ### To do
- variable size dictionary - max upload file size should be configurable
- refactoring libantd API, remove unused functions
- remove static strings, replace it by configurations - remove static strings, replace it by configurations

View File

@ -17,6 +17,8 @@ maxcon=200
backlog=5000 backlog=5000
; number of workers ; number of workers
workers = 4 workers = 4
; max upload file size in bytes
max_upload_size = 10000000
; if SSL is enable on one port, one should specify ; if SSL is enable on one port, one should specify
; the SSL cert and key files ; the SSL cert and key files
;Example: self signed key ;Example: self signed key

View File

@ -142,6 +142,10 @@ static int config_handler(void *conf, const char *section, const char *name,
{ {
pconfig->tmpdir = strdup(value); pconfig->tmpdir = strdup(value);
} }
else if (MATCH("SERVER", "max_upload_size"))
{
pconfig->max_upload_size = atoi(value);
}
else if (MATCH("SERVER", "maxcon")) else if (MATCH("SERVER", "maxcon"))
{ {
pconfig->maxcon = atoi(value); pconfig->maxcon = atoi(value);
@ -267,6 +271,7 @@ void load_config(const char *file)
server_config.backlog = 1000; server_config.backlog = 1000;
server_config.handlers = dict(); server_config.handlers = dict();
server_config.maxcon = 100; server_config.maxcon = 100;
server_config.max_upload_size = 10000000; //10Mb
server_config.connection = 0; server_config.connection = 0;
server_config.errorfp = NULL; server_config.errorfp = NULL;
server_config.logfp = NULL; server_config.logfp = NULL;

View File

@ -78,6 +78,7 @@ typedef struct {
int maxcon; int maxcon;
int connection; int connection;
int n_workers; int n_workers;
int max_upload_size;
FILE* errorfp; FILE* errorfp;
// #ifdef DEBUG // #ifdef DEBUG
FILE* logfp; FILE* logfp;

249
lib/ws.c
View File

@ -1,3 +1,7 @@
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#include "ws.h" #include "ws.h"
static void ws_gen_mask_key(ws_msg_header_t * header) static void ws_gen_mask_key(ws_msg_header_t * header)
{ {
@ -69,13 +73,13 @@ ws_msg_header_t * ws_read_header(void* client)
switch(header->opcode){ switch(header->opcode){
case WS_CLOSE: // client requests to close the connection case WS_CLOSE: // client requests to close the connection
// send back a close message // send back a close message
ws_close(client,1000); ws_send_close(client,1000,header->mask?0:1);
//goto fail; //goto fail;
break; break;
case WS_PING: // client send a ping case WS_PING: // client send a ping
// send back a pong message // send back a pong message
pong(client,header->plen); ws_pong(client,header, header->mask?0:1 );
break; break;
default: break; default: break;
@ -208,7 +212,7 @@ void ws_send_file(void* client, const char* file, int mask)
ptr = fopen(file,"rb"); ptr = fopen(file,"rb");
if(!ptr) if(!ptr)
{ {
ws_close(client,1011); ws_send_close(client,1011,mask);
return; return;
} }
@ -242,18 +246,19 @@ void ws_send_file(void* client, const char* file, int mask)
* Not tested yet * Not tested yet
* but should work * but should work
*/ */
void pong(void* client, int len) void ws_pong(void* client, ws_msg_header_t* oheader, int mask)
{ {
//printf("PONG\n");
ws_msg_header_t pheader; ws_msg_header_t pheader;
pheader.fin = 1; pheader.fin = 1;
pheader.opcode = WS_PONG; pheader.opcode = WS_PONG;
pheader.plen = len; pheader.plen = oheader->plen;
pheader.mask = 0; pheader.mask = mask;
uint8_t *data = (uint8_t*)malloc(len); uint8_t *data = (uint8_t*)malloc(oheader->plen);
if(!data) return; if(!data) return;
if(antd_recv(client,data, len) < 0)
if(ws_read_data(client, oheader, pheader.plen,data) == -1)
{ {
ERROR("Cannot read ping data %d", pheader.plen);
free(data); free(data);
return; return;
} }
@ -262,6 +267,15 @@ void pong(void* client, int len)
//_send_header(client, pheader); //_send_header(client, pheader);
//send(client, data, len, 0); //send(client, data, len, 0);
} }
void ws_ping(void* client, const char* echo, int mask)
{
ws_msg_header_t pheader;
pheader.fin = 1;
pheader.opcode = WS_PING;
pheader.plen = strlen(echo);
pheader.mask = mask;
ws_send_frame(client,(uint8_t*)echo,pheader);
}
/* /*
* Not tested yet, but should work * Not tested yet, but should work
*/ */
@ -295,7 +309,7 @@ int ip_from_hostname(const char * hostname , char* ip)
if ( (he = gethostbyname( hostname ) ) == NULL) if ( (he = gethostbyname( hostname ) ) == NULL)
{ {
// get the host info // get the host info
herror("gethostbyname"); ERROR("gethostbyname:%s",strerror(errno));
return -1; return -1;
} }
addr_list = (struct in_addr **) he->h_addr_list; addr_list = (struct in_addr **) he->h_addr_list;
@ -323,14 +337,14 @@ int request_socket(const char* ip, int port)
timeout.tv_usec = 0;//3 s timeout.tv_usec = 0;//3 s
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
{ {
perror("Socket"); ERROR("Socket: %s", strerror(errno));
return -1; return -1;
} }
if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0) if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
perror("setsockopt failed\n"); ERROR("setsockopt failed:%s", strerror(errno));
if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0) if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
perror("setsockopt failed\n"); ERROR("setsockopt failed:%s",strerror(errno));
/*struct linger lingerStruct; /*struct linger lingerStruct;
lingerStruct.l_onoff = 0; // turn lingering off for sockets lingerStruct.l_onoff = 0; // turn lingering off for sockets
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct));*/ setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct));*/
@ -347,43 +361,192 @@ int request_socket(const char* ip, int port)
if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 ) if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 )
{ {
close(sockfd); close(sockfd);
perror("Connect"); ERROR("Connect:%s",strerror(errno));
return -1; return -1;
} }
return sockfd; return sockfd;
} }
//TODO: The ping request void ws_client_close(ws_client_t* wsclient)
/* {
this is for the client side, not use for now int usessl = wsclient->antdsock->port_config->usessl;
int ws_open_hand_shake(const char* host, int port, const char* resource) port_config_t *ptr = wsclient->antdsock->port_config;
antd_close(wsclient->antdsock);
if(ptr)
free(ptr);
#ifdef USE_OPENSSL
if(usessl)
{
if(wsclient->ssl_ctx)
SSL_CTX_free(wsclient->ssl_ctx);
FIPS_mode_set(0);
// DEPRECATED: CONF_modules_unload(1);
EVP_cleanup();
EVP_PBE_cleanup();
// DEPRECATED:ENGINE_cleanup();
CRYPTO_cleanup_all_ex_data();
// DEPRECATED: ERR_remove_state(0);
ERR_free_strings();
}
#else
UNUSED(usessl);
#endif
}
//this is for the client side, not use for now
int ws_client_connect(ws_client_t* wsclient)
{ {
char ip[100]; char ip[100];
char buff[MAX_BUFF]; int stat = ip_from_hostname(wsclient->host,ip);
char* rq = NULL; if(stat == -1)
int size; return -1;
// request socket int sock = request_socket(ip, wsclient->antdsock->port_config->port);
ip_from_hostname(host ,ip); if(sock <= 0)
int sock = request_socket(ip, port);
if(sock <= 0) return -1;
// now send ws request handshake
rq = __s(CLIENT_RQ,resource,host);
// printf("%s\n",rq);
size = send(sock, rq, strlen(rq),0);
if(size != strlen(rq))
{ {
printf("Cannot send request \n"); ERROR("Cannot request socket");
close(sock); return -1;
}
// will be free
wsclient->antdsock->ip = strdup(ip);
wsclient->antdsock->sock = sock;
wsclient->antdsock->status = 0;
wsclient->antdsock->last_io = time(NULL);
wsclient->antdsock->port_config->sock = -1;
wsclient->antdsock->port_config->rules = NULL;
wsclient->antdsock->port_config->htdocs = NULL;
#ifdef USE_OPENSSL
if(wsclient->antdsock->port_config->usessl)
{
SSL_library_init();
SSL_load_error_strings();
ERR_load_crypto_strings();
OpenSSL_add_ssl_algorithms();
const SSL_METHOD *method;
unsigned long ssl_err = 0;
method = SSLv23_client_method();
ssl_err = ERR_get_error();
if(!method)
{
ERROR("SSLv23_method: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
wsclient->ssl_ctx = SSL_CTX_new(method);
ssl_err = ERR_get_error();
if (!wsclient->ssl_ctx) {
ERROR("SSL_CTX_new: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
// configure the context
#if defined(SSL_CTX_set_ecdh_auto)
SSL_CTX_set_ecdh_auto(wsclient->ssl_ctx, 1);
#else
SSL_CTX_set_tmp_ecdh(wsclient->ssl_ctx, EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
#endif
SSL_CTX_set_options(wsclient->ssl_ctx, SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_SSLv2|SSL_OP_NO_TICKET);
// set the cipher suit
const char* suit = wsclient->ciphersuit?wsclient->ciphersuit:PREFERRED_WS_CIPHERS;
//const char* suit = "AES128-SHA";
if (SSL_CTX_set_cipher_list(wsclient->ssl_ctx, suit) != 1)
{
ssl_err = ERR_get_error();
// TODO Close the context
ERROR("SSL_CTX_set_cipher_list: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
if(wsclient->sslcert && wsclient->sslkey)
{
if (SSL_CTX_use_certificate_file(wsclient->ssl_ctx,wsclient->sslcert, SSL_FILETYPE_PEM) <= 0) {
ssl_err = ERR_get_error();
ERROR("SSL_CTX_use_certificate_file: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
if(wsclient->sslpasswd)
SSL_CTX_set_default_passwd_cb_userdata(wsclient->ssl_ctx,(void*)wsclient->sslpasswd);
if (SSL_CTX_use_PrivateKey_file(wsclient->ssl_ctx,wsclient->sslkey, SSL_FILETYPE_PEM) <= 0) {
ssl_err = ERR_get_error();
ERROR("SSL_CTX_use_PrivateKey_file: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
if (SSL_CTX_check_private_key(wsclient->ssl_ctx) == 0) {
ssl_err = ERR_get_error();
ERROR("SSL_CTX_check_private_key: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
}
//
// validate
if(wsclient->verify_location)
{
SSL_CTX_set_verify(wsclient->ssl_ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(wsclient->ssl_ctx, 5);
if(!SSL_CTX_load_verify_locations(wsclient->ssl_ctx, wsclient->verify_location, NULL))
{
ssl_err = ERR_get_error();
// TODO Close the context
ERROR("SSL_CTX_load_verify_locations: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
}
else
{
SSL_CTX_set_verify(wsclient->ssl_ctx, SSL_VERIFY_NONE, NULL);
}
wsclient->antdsock->ssl = (void*)SSL_new(wsclient->ssl_ctx);
if(!wsclient->antdsock->ssl)
{
ssl_err = ERR_get_error();
ERROR("SSL_new: %s", ERR_error_string(ssl_err, NULL));
return -1;
}
SSL_set_fd((SSL*)wsclient->antdsock->ssl, wsclient->antdsock->sock);
int stat, ret;
ERR_clear_error();
while( (ret = SSL_connect(wsclient->antdsock->ssl)) <= 0)
{
stat = SSL_get_error(wsclient->antdsock->ssl, ret);
switch (stat)
{
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_NONE:
continue;
default:
ERR_print_errors_fp(stderr);
ERROR("Error performing SSL handshake %d", stat);
return -1;
}
}
}
#endif
return 0;
}
int ws_open_handshake(ws_client_t* client)
{
char buf[MAX_BUFF];
// now send ws request handshake
sprintf(buf, CLIENT_RQ,client->resource,client->host);
//printf("Send %s\n", buf);
int size = antd_send(client->antdsock, buf, strlen(buf));
if(size != (int)strlen(buf))
{
ERROR("Cannot send request \n");
return -1; return -1;
} }
// now verify if server accept the socket // now verify if server accept the socket
size = read_buf(sock,buff,MAX_BUFF); size = read_buf(client->antdsock,buf,MAX_BUFF);
char* token; char* token;
int done = 0; int done = 0;
while (size > 0 && strcmp("\r\n",buff)) while (size > 0 && strcmp("\r\n",buf))
{ {
char* line = strdup(buff); char* line = buf;
//printf("LINE %s\n", line);
token = strsep(&line,":"); token = strsep(&line,":");
trim(token,' '); trim(token,' ');
if(token != NULL &&strcasecmp(token,"Sec-WebSocket-Accept") == 0) if(token != NULL &&strcasecmp(token,"Sec-WebSocket-Accept") == 0)
@ -392,25 +555,23 @@ int ws_open_hand_shake(const char* host, int port, const char* resource)
trim(token,' '); trim(token,' ');
trim(token,'\n'); trim(token,'\n');
trim(token,'\r'); trim(token,'\r');
//printf("Key found %s \n", token);
if(strcasecmp(token, SERVER_WS_KEY) == 0) if(strcasecmp(token, SERVER_WS_KEY) == 0)
{ {
// printf("Handshake sucessfull\n"); //LOG("Handshake sucessfull\n");
done = 1; done = 1;
} else } else
{ {
printf("Wrong key %s vs %s\n",token,SERVER_WS_KEY); ERROR("WS handshake, Wrong key %s vs %s",token,SERVER_WS_KEY);
close(sock);
return -1; return -1;
} }
} }
//if(line) free(line); //if(line) free(line);
size = read_buf(sock,buff,MAX_BUFF); size = read_buf(client->antdsock,buf,MAX_BUFF);
} }
if(done) return sock; if(done)
//printf("No server key found \n"); return 0;
return -1; return -1;
}*/ }
char* get_ip_address() char* get_ip_address()
{ {
struct ifaddrs* addrs; struct ifaddrs* addrs;

View File

@ -26,8 +26,10 @@
#define ws_f(c,f) (ws_send_file(c,f,0)) #define ws_f(c,f) (ws_send_file(c,f,0))
#define ws_close(c,r) (ws_send_close(c,r,0)) #define ws_close(c,r) (ws_send_close(c,r,0))
#define MAX_BUFF 1024 #define MAX_BUFF 1024
//#define CLIENT_RQ "GET /%s HTTP/1.1\r\nHost: %s\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n"
//#define SERVER_WS_KEY "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" #define PREFERRED_WS_CIPHERS "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS"
#define CLIENT_RQ "GET /%s HTTP/1.1\r\nHost: %s\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n"
#define SERVER_WS_KEY "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
typedef struct{ typedef struct{
uint8_t fin; uint8_t fin;
@ -37,9 +39,25 @@ typedef struct{
uint8_t mask_key[4]; uint8_t mask_key[4];
} ws_msg_header_t; } ws_msg_header_t;
typedef struct{
const char* host;
const char* resource;
antd_client_t* antdsock;
// ssl
const char* sslcert;
const char* sslkey;
const char* sslpasswd;
const char* ciphersuit;
const char* verify_location;
void * ssl_ctx;
} ws_client_t;
ws_msg_header_t * ws_read_header(void*); ws_msg_header_t * ws_read_header(void*);
void ws_send_frame(void* , uint8_t* , ws_msg_header_t ); void ws_send_frame(void* , uint8_t* , ws_msg_header_t );
void pong(void* client, int len); void ws_pong(void* client, ws_msg_header_t*, int mask);
void ws_ping(void* client, const char* echo, int mask);
void ws_send_text(void* client, const char* data,int mask); void ws_send_text(void* client, const char* data,int mask);
void ws_send_close(void* client, unsigned int status, int mask); void ws_send_close(void* client, unsigned int status, int mask);
@ -49,7 +67,12 @@ void ws_send_binary(void* client, uint8_t* data, int l, int mask);
int ws_read_data(void* , ws_msg_header_t*, int, uint8_t*); int ws_read_data(void* , ws_msg_header_t*, int, uint8_t*);
int request_socket(const char* ip, int port); int request_socket(const char* ip, int port);
int ip_from_hostname(const char * hostname , char* ip); int ip_from_hostname(const char * hostname , char* ip);
int sock_read_buf(void* sock, char*buf,int size);
//int ws_open_hand_shake(const char* host, int port, const char* resource); //int ws_open_hand_shake(const char* host, int port, const char* resource);
char* get_ip_address(); char* get_ip_address();
// client
void ws_client_close(ws_client_t* wsclient);
int ws_client_connect(ws_client_t* wsclient);
int ws_open_handshake(ws_client_t* client);
#endif #endif