SSB_HighSpeed_Modem/hsmodem/websocket/ws.cpp

435 lines
11 KiB
C++
Executable File

/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
websocket server: based on the work by: Davidson Francis <davidsondfgl@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "../hsmodem.h"
/* Registered events. */
struct ws_events events;
/**
* Gets the IP address relative to a
* file descriptor opened by the server.
* @param fd File descriptor target.
* @return Pointer the ip address.
*/
char* ws_getaddress(int fd)
{
struct sockaddr_in addr;
#ifdef WIN32
int addr_size;
#else
socklen_t addr_size;
#endif
char *client;
addr_size = sizeof(struct sockaddr_in);
if ( getpeername(fd, (struct sockaddr *)&addr, &addr_size) < 0 )
return NULL;
client = (char *)malloc(sizeof(char) * 20);
if(client == NULL)
return NULL;
strcpy(client, inet_ntoa(addr.sin_addr));
return (client);
}
/**
* Creates and send a WebSocket frame
* with some binary message.
* @param fd Target to be send.
* @param msg Message to be send.
*/
int ws_sendframe_binary(int fd, unsigned char *msg, uint64_t length)
{
unsigned char *response; /* Response data. */
unsigned char frame[10]; /* Frame. */
uint8_t idx_first_rData; /* Index data. */
int idx_response; /* Index response. */
int output; /* Bytes sent. */
/* Binary data. */
frame[0] = (WS_FIN | WS_FR_OP_BINARY);
/* Split the size between octects. */
if (length <= 125)
{
frame[1] = length & 0x7F;
idx_first_rData = 2;
}
/* Size between 126 and 65535 bytes. */
else if (length >= 126 && length <= 65535)
{
frame[1] = 126;
frame[2] = (length >> 8) & 255;
frame[3] = length & 255;
idx_first_rData = 4;
}
/* More than 65535 bytes. */
else
{
frame[1] = 127;
frame[2] = (unsigned char) ((length >> 56) & 255);
frame[3] = (unsigned char) ((length >> 48) & 255);
frame[4] = (unsigned char) ((length >> 40) & 255);
frame[5] = (unsigned char) ((length >> 32) & 255);
frame[6] = (unsigned char) ((length >> 24) & 255);
frame[7] = (unsigned char) ((length >> 16) & 255);
frame[8] = (unsigned char) ((length >> 8) & 255);
frame[9] = (unsigned char) (length & 255);
idx_first_rData = 10;
}
/* Add frame bytes. */
idx_response = 0;
response = (unsigned char *)malloc((size_t)( sizeof(unsigned char) * (idx_first_rData + length + 1)));
if(response != NULL)
{
for (int i = 0; i < idx_first_rData; i++)
{
response[i] = frame[i];
idx_response++;
}
/* Add data bytes. */
for (uint64_t i = 0; i < length; i++)
{
response[idx_response] = msg[i];
idx_response++;
}
output = send(fd, (char *)response, idx_response,0);
free(response);
return (output);
}
return 0;
}
/**
* Receives a text frame, parse and decodes it.
* @param frame WebSocket frame to be parsed.
* @param length Frame length.
* @param type Frame type.
*/
static unsigned char* ws_receiveframe(unsigned char *frame, size_t length, int *type)
{
unsigned char *msg; /* Decoded message. */
uint8_t mask; /* Payload is masked? */
uint8_t flength; /* Raw length. */
uint8_t idx_first_mask; /* Index masking key. */
uint8_t idx_first_data; /* Index data. */
size_t data_length; /* Data length. */
uint8_t masks[4]; /* Masking key. */
int i,j; /* Loop indexes. */
msg = NULL;
/* Checks the frame type and parse the frame. */
if (frame[0] == (WS_FIN | WS_FR_OP_TXT) )
{
*type = WS_FR_OP_TXT;
idx_first_mask = 2;
mask = frame[1];
flength = mask & 0x7F;
if (flength == 126)
idx_first_mask = 4;
else if (flength == 127)
idx_first_mask = 10;
idx_first_data = idx_first_mask + 4;
data_length = length - idx_first_data;
masks[0] = frame[idx_first_mask+0];
masks[1] = frame[idx_first_mask+1];
masks[2] = frame[idx_first_mask+2];
masks[3] = frame[idx_first_mask+3];
msg = (unsigned char *)malloc(sizeof(unsigned char) * (data_length+1) );
if(msg == NULL)
return NULL;
for (i = idx_first_data, j = 0; i < (int)length; i++, j++)
msg[j] = frame[i] ^ masks[j % 4];
msg[j] = '\0';
}
/* Close frame. */
else if (frame[0] == (WS_FIN | WS_FR_OP_CLSE) )
*type = WS_FR_OP_CLSE;
/* Not supported frame yet. */
else
*type = frame[0] & 0x0F;
return msg;
}
// nonblocking read with a 10ms timeout
int readsocket(int sock, unsigned char* buf, int maxlen)
{
int n;
// make the read unblocking
// but check with select if something is in the receive buffer
fd_set input;
FD_ZERO(&input);
FD_SET(sock, &input);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
n = select(sock + 1, &input, NULL, NULL, &timeout); // select will socket+1, blöd, aber ist so
if (n <= 0)
{
return n; // 0=no data, <0=error
}
if (!FD_ISSET(sock, &input))
{
return -1; // error
}
sleep_ms(10); // wait a bit to give a message the chance to be rxed completely
n = recv(sock, (char*)buf, maxlen, 0);
return n;
}
/**
* Establishes to connection with the client and trigger
* events when occurs one.
* @param vsock Client file descriptor.
* @note This will be run on a different thread.
*/
#ifdef WIN32
void ws_establishconnection(void* vsock)
{
#else
void* ws_establishconnection(void* vsock)
{
pthread_detach(pthread_self());
#endif
int sock;
size_t n; /* Number of bytes sent/received. */
unsigned char frm[MESSAGE_LENGTH]; /* Frame. */
unsigned char *msg; /* Message. */
char *response; /* Response frame. */
int handshaked; /* Handshake state. */
int type; /* Frame type. */
unsigned char cnt0=0,cnt1=0;
handshaked = 0;
sock = (int)(intptr_t)vsock;
while (keeprunning)
{
n = readsocket(sock, frm, sizeof(unsigned char) * MESSAGE_LENGTH);
/* Receives message until get some error. */
if (n >= 0)
{
if (n > 0)
{
// data received
/* If not handshaked yet. */
if (!handshaked)
{
getHSresponse((char*)frm, &response);
handshaked = 1;
/*printf("Handshaked, response: \n"
"------------------------------------\n"
"%s"
"------------------------------------\n"
,response);*/
n = send(sock, response, strlen(response),0);
events.onopen(sock);
free(response);
}
/* Decode/check type of frame. */
msg = ws_receiveframe(frm, n, &type);
if (msg == NULL)
continue;
/* Trigger events. */
if (type == WS_FR_OP_TXT)
events.onmessage(sock, msg);
else if (type == WS_FR_OP_CLSE)
{
if (msg != NULL) free(msg);
events.onclose(sock);
#ifdef WIN32
return;
#else
return vsock;
#endif
}
else
printf("type :%d\n", type);
if (msg != NULL) free(msg);
}
if (n == 0)
{
if (get_alive(sock) == 0)
{
events.onclose(sock);
#ifdef WIN32
return;
#else
return vsock;
#endif
}
// no data received, normal processing loop
int ret = events.onwork(sock, &cnt0, &cnt1);
if (ret == -1)
{
// other side closed the connection (write error)
events.onclose(sock);
#ifdef WIN32
return;
#else
return vsock;
#endif
}
sleep_ms(10); // do not eat up the CPU time
}
}
}
#ifdef WIN32
_close(sock);
return;
#else
close(sock);
return vsock;
#endif
}
/**
* Main loop for the server, runs in a thread
* @param evs Events structure.
* @param port Server port.
*/
int ws_socket(struct ws_events *evs, int port)
{
int sock; /* Current socket. */
int new_sock; /* New opened connection. */
struct sockaddr_in server; /* Server. */
struct sockaddr_in client; /* Client. */
int len; /* Length of sockaddr. */
if (evs == NULL || port <= 0 || port > 65535)
{
printf("An error has ocurred, please review your events or the desired port!\n");
return -1;
}
/* Copy events. */
memcpy(&events, evs, sizeof(struct ws_events));
#ifdef _WIN32_
WSADATA wsaData = { 0 };
int ires = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (ires != 0)
printf("WSAStartup failed: %d\n", ires);
#endif
/* Create socket. */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
printf("Could not create socket\n");
return -1;
}
/* Reuse previous address. */
const char val = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) < 0)
{
perror("setsockopt(SO_REUSEADDR) failed");
return -1;
}
/* Prepare the sockaddr_in structure. */
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
printf("Websocket Server: listen to port:%d\n",port);
server.sin_port = htons(port);
/* Bind. */
if( bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0 )
{
perror("Bind failed");
return -1;
}
/* Listen. */
listen(sock, MAX_CLIENTS);
/* Wait for incoming connections. */
printf("Waiting for incoming connections...\n");
len = sizeof(struct sockaddr_in);
/* Accept connections. */
while (keeprunning)
{
//if (extData_active)
{
/* Accept. */
#ifdef WIN32
new_sock = accept(sock, (struct sockaddr*)&client, &len);
printf("new socket: %d\n", new_sock);
#else
new_sock = accept(sock, (struct sockaddr*)&client, (socklen_t*)&len);
#endif
if (new_sock < 0)
{
perror("Error on accepting conections..");
exit(-1);
}
#ifdef WIN32
printf("start Thread\n");
_beginthread(ws_establishconnection, 0, (void*)(intptr_t)new_sock);
#else
pthread_t client_thread;
if (pthread_create(&client_thread, NULL, ws_establishconnection, (void*)(intptr_t)new_sock) < 0)
perror("Could not create the client thread!");
pthread_detach(client_thread); // automatically release all ressources as soon as the thread is done
#endif
}
/*else
{
sleep_ms(100);
}*/
}
return 0;
}