This commit is contained in:
Kurt
2021-02-22 15:58:19 +01:00
parent 43cea876bb
commit 01f82d2afb
49 changed files with 3643 additions and 847 deletions
+155
View File
@@ -0,0 +1,155 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "base64.h"
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* base64_encode - Base64 encode
* @src: Data to be encoded
* @len: Length of the data to be encoded
* @out_len: Pointer to output length variable, or %NULL if not used
* Returns: Allocated buffer of out_len bytes of encoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer. Returned buffer is
* nul terminated to make it easier to use as a C string. The nul terminator is
* not included in out_len.
*/
unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len)
{
unsigned char *out, *pos;
const unsigned char *end, *in;
size_t olen;
int line_len;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len)
return NULL; /* integer overflow */
out = (unsigned char *)malloc(olen);
if (out == NULL)
return NULL;
end = src + len;
in = src;
pos = out;
line_len = 0;
while (end - in >= 3) {
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
line_len += 4;
if (line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
}
if (end - in) {
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1) {
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
} else {
*pos++ = base64_table[((in[0] & 0x03) << 4) |
(in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
line_len += 4;
}
if (line_len)
*pos++ = '\n';
*pos = '\0';
if (out_len)
*out_len = pos - out;
return out;
}
/**
* base64_decode - Base64 decode
* @src: Data to be decoded
* @len: Length of the data to be decoded
* @out_len: Pointer to output length variable
* Returns: Allocated buffer of out_len bytes of decoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer.
*/
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len)
{
unsigned char dtable[256], *out, *pos, block[4], tmp;
size_t i, count, olen;
int pad = 0;
memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++)
dtable[base64_table[i]] = (unsigned char) i;
dtable['='] = 0;
count = 0;
for (i = 0; i < len; i++) {
if (dtable[src[i]] != 0x80)
count++;
}
if (count == 0 || count % 4)
return NULL;
olen = count / 4 * 3;
pos = out = (unsigned char*)malloc(olen);
if (out == NULL)
return NULL;
count = 0;
for (i = 0; i < len; i++) {
tmp = dtable[src[i]];
if (tmp == 0x80)
continue;
if (src[i] == '=')
pad++;
block[count] = tmp;
count++;
if (count == 4) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
if (pad) {
if (pad == 1)
pos--;
else if (pad == 2)
pos -= 2;
else {
/* Invalid padding */
free(out);
return NULL;
}
break;
}
}
}
*out_len = pos - out;
return out;
}
+19
View File
@@ -0,0 +1,19 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef BASE64_H
#define BASE64_H
#include <sys/types.h>
unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len);
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len);
#endif /* BASE64_H */
+73
View File
@@ -0,0 +1,73 @@
/*
Copyright (C) 2016 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"
#include "sha1.h"
#include "base64.h"
/**
* Gets the field Sec-WebSocket-Accept on response, by
* an previously informed key.
* @param wsKey Sec-WebSocket-Key
* @param dest source to be stored the value.
*/
int getHSaccept(char *wsKey, unsigned char **dest)
{
SHA1Context ctx;
char *str = (char *)malloc( sizeof(char) * (WS_KEY_LEN + WS_MS_LEN + 1) );
unsigned char hash[SHA1HashSize];
strcpy(str, wsKey);
strcat(str, MAGIC_STRING);
SHA1Reset(&ctx);
SHA1Input(&ctx, (const uint8_t *)str, WS_KEYMS_LEN);
SHA1Result(&ctx, hash);
*dest = base64_encode(hash, SHA1HashSize, NULL);
*(*dest + strlen((const char *)*dest) - 1) = '\0';
free(str);
return (0);
}
/**
* Gets the complete response to accomplish a succesfully
* handshake.
* @param hsrequest Client request.
* @param hsresponse Server response.
*/
int getHSresponse(char *hsrequest, char **hsresponse)
{
char *s;
unsigned char *accept;
for (s = strtok(hsrequest, "\r\n"); s != NULL; s = strtok(NULL, "\r\n") )
if (strstr(s, WS_HS_REQ) != NULL)
break;
s = strtok(s, " ");
s = strtok(NULL, " ");
getHSaccept(s, &accept);
*hsresponse = (char*)malloc(sizeof(char) * WS_HS_ACCLEN);
strcpy(*hsresponse, WS_HS_ACCEPT);
strcat(*hsresponse, (const char *)accept);
strcat(*hsresponse, "\r\n\r\n");
free(accept);
return (0);
}
+389
View File
@@ -0,0 +1,389 @@
/*
* sha1.c
*
* Description:
* This file implements the Secure Hashing Algorithm 1 as
* defined in FIPS PUB 180-1 published April 17, 1995.
*
* The SHA-1, produces a 160-bit message digest for a given
* data stream. It should take about 2**n steps to find a
* message with the same digest as a given message and
* 2**(n/2) to find any two messages with the same digest,
* when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code
* uses <stdint.h> (included via "sha1.h" to define 32 and 8
* bit unsigned integer types. If your C compiler does not
* support 32 bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. Although SHA-1 allows a message digest to be generated
* for messages of any number of bits less than 2^64, this
* implementation only works with messages with a length that is
* a multiple of the size of an 8-bit character.
*
*/
#include "sha1.h"
/*
* Define the SHA1 circular left shift macro
*/
#define SHA1CircularShift(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
/* Local Function Prototyptes */
void SHA1PadMessage(SHA1Context *);
void SHA1ProcessMessageBlock(SHA1Context *);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new SHA1 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Reset(SHA1Context *context)
{
if (!context)
{
return shaNull;
}
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = 0x67452301;
context->Intermediate_Hash[1] = 0xEFCDAB89;
context->Intermediate_Hash[2] = 0x98BADCFE;
context->Intermediate_Hash[3] = 0x10325476;
context->Intermediate_Hash[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
return shaSuccess;
}
/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 19th element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Result( SHA1Context *context,
uint8_t Message_Digest[SHA1HashSize])
{
int i;
if (!context || !Message_Digest)
{
return shaNull;
}
if (context->Corrupted)
{
return context->Corrupted;
}
if (!context->Computed)
{
SHA1PadMessage(context);
for(i=0; i<64; ++i)
{
/* message may be sensitive, clear it out */
context->Message_Block[i] = 0;
}
context->Length_Low = 0; /* and clear length */
context->Length_High = 0;
context->Computed = 1;
}
for(i = 0; i < SHA1HashSize; ++i)
{
Message_Digest[i] = context->Intermediate_Hash[i>>2]
>> 8 * ( 3 - ( i & 0x03 ) );
}
return shaSuccess;
}
/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/
int SHA1Input( SHA1Context *context,
const uint8_t *message_array,
unsigned length)
{
if (!length)
{
return shaSuccess;
}
if (!context || !message_array)
{
return shaNull;
}
if (context->Computed)
{
context->Corrupted = shaStateError;
return shaStateError;
}
if (context->Corrupted)
{
return context->Corrupted;
}
while(length-- && !context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] =
(*message_array & 0xFF);
context->Length_Low += 8;
if (context->Length_Low == 0)
{
context->Length_High++;
if (context->Length_High == 0)
{
/* Message is too long */
context->Corrupted = 1;
}
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
return shaSuccess;
}
/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the publication.
*
*
*/
void SHA1ProcessMessageBlock(SHA1Context *context)
{
const uint32_t K[] = { /* Constants defined in SHA-1 */
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
uint32_t temp; /* Temporary word value */
uint32_t W[80]; /* Word sequence */
uint32_t A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for(t = 0; t < 16; t++)
{
W[t] = context->Message_Block[t * 4] << 24;
W[t] |= context->Message_Block[t * 4 + 1] << 16;
W[t] |= context->Message_Block[t * 4 + 2] << 8;
W[t] |= context->Message_Block[t * 4 + 3];
}
for(t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Message_Block_Index = 0;
}
/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call the ProcessMessageBlock function
* provided appropriately. When it returns, it can be assumed that
* the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
* ProcessMessageBlock: [in]
* The appropriate SHA*ProcessMessageBlock function
* Returns:
* Nothing.
*
*/
void SHA1PadMessage(SHA1Context *context)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 64)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = context->Length_High >> 24;
context->Message_Block[57] = context->Length_High >> 16;
context->Message_Block[58] = context->Length_High >> 8;
context->Message_Block[59] = context->Length_High;
context->Message_Block[60] = context->Length_Low >> 24;
context->Message_Block[61] = context->Length_Low >> 16;
context->Message_Block[62] = context->Length_Low >> 8;
context->Message_Block[63] = context->Length_Low;
SHA1ProcessMessageBlock(context);
}
+73
View File
@@ -0,0 +1,73 @@
/*
* sha1.h
*
* Description:
* This is the header file for code which implements the Secure
* Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
* April 17, 1995.
*
* Many of the variable names in this code, especially the
* single character names, were used because those were the names
* used in the publication.
*
* Please read the file sha1.c for more information.
*
*/
#ifndef _SHA1_H_
#define _SHA1_H_
#include <stdint.h>
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typdef the following:
* name meaning
* uint32_t unsigned 32 bit integer
* uint8_t unsigned 8 bit integer (i.e., unsigned char)
* int_least16_t integer of >= 16 bits
*
*/
#ifndef _SHA_enum_
#define _SHA_enum_
enum
{
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError /* called Input after Result */
};
#endif
#define SHA1HashSize 20
/*
* This structure will hold context information for the SHA-1
* hashing operation
*/
typedef struct SHA1Context
{
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
uint32_t Length_Low; /* Message length in bits */
uint32_t Length_High; /* Message length in bits */
/* Index into message block array */
int_least16_t Message_Block_Index;
uint8_t Message_Block[64]; /* 512-bit message blocks */
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the message digest corrupted? */
} SHA1Context;
/*
* Function Prototypes
*/
int SHA1Reset( SHA1Context *);
int SHA1Input( SHA1Context *,
const uint8_t *,
unsigned int);
int SHA1Result( SHA1Context *,
uint8_t Message_Digest[SHA1HashSize]);
#endif
+339
View File
@@ -0,0 +1,339 @@
/*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* websocketserver.c ... sends data to a web browser
*
* if a web browser is logged into this WebSocketServer then
* we send the data to this browser.
* Its the job of the browser to make anything with these data.
*
* This WebSocketServer is a multi threaded implementation and
* opens a new thread for each client
*
* ! THIS implementation of a WebSocketServer DOES NOT require a Webserver (like Apache)
* because it handles all Websocket tasks by itself !
*
* usage:
* ======
*
* ws_init() ... call once after program start to initialize the websocket server
*
* ws_send(unsigned char *pdata, int len) ...
* send pdata (max. lenght MESSAGE_LENGTH) to all connected clients.
* this function is thread safe. It stores the data in a buffer.
* This buffer is sent to the clients in the websocket-thread in the background.
*
*
*/
#include "../hsmodem.h"
void init_ws_locks();
void ws_alive();
#ifdef _WIN32_
void wsproc(void* param);
#else
void* wsproc(void* param);
#endif
#define MAXIPLEN 16
WS_SOCK actsock[MAX_CLIENTS];
char clilist[MAX_CLIENTS][MAXIPLEN];
/*void test_websocket()
{
char* msg = "ABCD1234";
static int t = 0;
if (++t > 100)
{
t = 0;
printf("send ws: %s\n", msg);
ws_send((unsigned char *)msg, strlen(msg));
}
}*/
// initialise the WebSocketServer
// run the WebSocketServer in a new thread
void ws_init()
{
printf("starting websocket server\n");
init_ws_locks();
for(int i=0; i<MAX_CLIENTS; i++)
{
actsock[i].socket = -1;
actsock[i].send = 0;
}
start_timer(1000, ws_alive);
#ifdef WIN32
_beginthread(wsproc, 0, NULL);
#else
pthread_t ws_pid;
int ret = pthread_create(&ws_pid,NULL,wsproc, NULL);
if(ret)
printf("websocket: process NOT started !!!\n\r");
#endif
}
// Websocket thread
#ifdef WIN32
void wsproc(void* param)
{
#else
void* wsproc(void* param)
{
pthread_detach(pthread_self());
#endif
printf("websocket server running\n");
struct ws_events evs;
evs.onopen = &onopen;
evs.onclose = &onclose;
evs.onmessage = &onmessage;
evs.onwork = &onwork;
ws_socket(&evs, TcpDataPort_WebSocket);
#ifndef WIN32
pthread_exit(NULL); // self terminate this thread
return NULL;
#endif
}
#ifdef WIN32
CRITICAL_SECTION ws_crit_sec;
#define WS_LOCK EnterCriticalSection(&ws_crit_sec)
void WS_UNLOCK()
{
if (&ws_crit_sec != NULL)
LeaveCriticalSection(&ws_crit_sec);
}
#else
pthread_mutex_t ws_crit_sec;
#define WS_LOCK pthread_mutex_lock(&ws_crit_sec)
void WS_UNLOCK() { pthread_mutex_unlock(&ws_crit_sec); }
#endif
void init_ws_locks()
{
// init pipes only once
static int f = 1;
if (f)
{
f = 0;
#ifdef WIN32
InitializeCriticalSection(&ws_crit_sec);
#else
pthread_mutex_init(&ws_crit_sec, NULL);
#endif
}
}
/*
* save the data in an array and set a flag.
* the transmission to the web browser is done by the threads
* handling all logged-in web browsers.
*
* this function is thread safe by a LOCK
*/
// insert the new message into the buffer of each active client
void ws_send(unsigned char *pdata, int len)
{
WS_LOCK;
for(int i=0; i<MAX_CLIENTS; i++)
{
if(actsock[i].socket != -1)
{
// insert data
if(actsock[i].send == 0)
{
if ((unsigned int)len < sizeof(actsock[i].msg))
memcpy(actsock[i].msg, pdata, len);
else
memcpy(actsock[i].msg, "ws check data size", 18);
actsock[i].msglen = len;
actsock[i].send = 1;
}
}
}
WS_UNLOCK();
}
unsigned char *ws_build_txframe(int i, int *plength)
{
WS_LOCK;
// calculate total length
// add 3 for each element for the first byte which is the element type followed by the length
int geslen = 0;
if(actsock[i].send == 1) geslen += (actsock[i].msglen + 3);
if(geslen < 10)
{
WS_UNLOCK();
return NULL;
}
// assign TX buffer
unsigned char *txdata = (unsigned char*)malloc(geslen);
if(txdata == NULL)
{
WS_UNLOCK();
return NULL;
}
// copy data into TX buffer and set the type byte
int idx = 0;
if(actsock[i].send == 1)
{
txdata[idx++] = 0; // type information
txdata[idx++] = actsock[i].msglen >> 8;
txdata[idx++] = actsock[i].msglen & 0xff;
memcpy(txdata+idx,actsock[i].msg,actsock[i].msglen);
idx += actsock[i].msglen;
}
*plength = idx;
WS_UNLOCK();
return txdata;
}
// insert a socket into the socket-list
void insert_socket(int fd, char *cli)
{
WS_LOCK;
for(int i=0; i<MAX_CLIENTS; i++)
{
if(actsock[i].socket == -1)
{
actsock[i].socket = fd;
actsock[i].send = 0;
actsock[i].alive = 10;
strncpy(clilist[i],cli,MAXIPLEN);
clilist[i][MAXIPLEN-1] = 0;
printf("accepted client %d %d\n",i,fd);
WS_UNLOCK();
return;
}
}
WS_UNLOCK();
printf("all sockets in use !!!\n");
}
// remove a socket from the socket-list
void remove_socket(int fd)
{
WS_LOCK;
for(int i=0; i<MAX_CLIENTS; i++)
{
if(actsock[i].socket == fd)
{
printf("remove client %d %d\n", i, fd);
/*
!!! DO NOT close it here, this is the wrong thread !!!
close(fd);
*/
actsock[i].socket = -1;
clilist[i][0] = 0;
}
}
WS_UNLOCK();
}
int get_socket_idx(int fd)
{
WS_LOCK;
for(int i=0; i<MAX_CLIENTS; i++)
{
if(actsock[i].socket == fd)
{
WS_UNLOCK();
return i;
}
}
WS_UNLOCK();
return -1;
}
// get IP corresponding to a socket
char *getSocketIP(int fd)
{
WS_LOCK;
for(int i=0; i<MAX_CLIENTS; i++)
{
if(actsock[i].socket == fd)
{
WS_UNLOCK();
return clilist[i];
}
}
WS_UNLOCK();
return NULL;
}
// called by timer every 1s
void ws_alive()
{
for (int i = 0; i < MAX_CLIENTS; i++)
{
if (actsock[i].socket != -1 && actsock[i].alive > 0)
{
//printf("%d %d\n", i, actsock[i].alive);
actsock[i].alive--;
if (actsock[i].alive == 0)
{
// remove inactive client
remove_socket(actsock[i].socket);
}
}
}
}
// remove a socket from the socket-list
int get_alive(int fd)
{
int a = 0;
WS_LOCK;
for (int i = 0; i < MAX_CLIENTS; i++)
{
if (actsock[i].socket == fd)
{
a = actsock[i].alive;
WS_UNLOCK();
return a;
}
}
WS_UNLOCK();
return 0;
}
+87
View File
@@ -0,0 +1,87 @@
#define MESSAGE_LENGTH 30000
#define MAX_CLIENTS 20 // if changed: change also fifo.h !!!!!!!!!
#define WS_KEY_LEN 24
#define WS_MS_LEN 36
#define WS_KEYMS_LEN (WS_KEY_LEN + WS_MS_LEN)
#define MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_HS_REQ "Sec-WebSocket-Key"
#define WS_HS_ACCLEN 130
#define WS_HS_ACCEPT \
"HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade: websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: " \
/* Frame definitions. */
#define WS_FIN 128
/* Frame types. */
#define WS_FR_OP_TXT 1
#define WS_FR_OP_BINARY 2
#define WS_FR_OP_CLSE 8
#define WS_FR_OP_UNSUPPORTED 0xF
extern int TcpDataPort_WebSocket;
// list of sockets, -1=inactive
typedef struct {
int socket; // socket id
unsigned char msg[MESSAGE_LENGTH]; // message to send to the browser
int msglen;
int send; // 0=nothing to send, 1=send now
struct sockaddr_in fromSock;
int alive = 0;
} WS_SOCK;
// Events
struct ws_events
{
/* void onopen(int fd); */
void (*onopen)(int);
/* void onclose(int fd); */
void (*onclose)(int);
/* void onmessage(int fd, unsigned char *message); */
void (*onmessage)(int, unsigned char *);
/* int onwork(int fd); do something short, worker function, called by the thread's main loop */
int (*onwork)(int fd, unsigned char *cnt0, unsigned char *cnt1);
};
typedef struct {
uint32_t command;
uint32_t para;
uint32_t client;
char spara[100];
} USERMSG;
int getHSaccept(char *wsKey, unsigned char **dest);
int getHSresponse(char *hsrequest, char **hsresponse);
char* ws_getaddress(int fd);
int ws_sendframe_binary(int fd, unsigned char *msg, uint64_t length);
int ws_socket(struct ws_events *evs, int port);
void ws_send(unsigned char* pdata, int len);
void ws_init();
int get_useranz();
void onopen(int fd);
void onclose(int fd);
void onmessage(int fd, unsigned char *message);
int onwork(int fd, unsigned char *cnt0, unsigned char *cnt1);
void insert_socket(int fd, char *cli);
void remove_socket(int fd);
char *getSocketIP(int fd);
unsigned char *ws_build_txframe(int i, int *plength);
int get_socket_idx(int fd);
int isLocal(int idx);
void test_websocket();
int get_alive(int fd);
extern WS_SOCK actsock[MAX_CLIENTS];
extern char myIP[20];
+434
View File
@@ -0,0 +1,434 @@
/*
* 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;
}
+101
View File
@@ -0,0 +1,101 @@
/*
* 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"
extern int useCAT;
int connections = 0;
// a new browser connected
void onopen(int fd)
{
char *cli;
cli = ws_getaddress(fd);
if(cli != NULL)
{
insert_socket(fd,cli);
printf("Connection opened, client: %d | addr: %s\n", fd, cli);
connections++;
printf("%d users logged in\n",connections);
free(cli);
}
}
// a browser disconnected
void onclose(int fd)
{
remove_socket(fd);
#ifdef WIN32
_close(fd);
#else
close(fd);
#endif
printf("Connection closed, client: %d\n", fd);
connections--;
printf("%d users logged in\n",connections);
}
// if avaiable, send data to a browser
int onwork(int fd, unsigned char *cnt0, unsigned char *cnt1)
{
int ret = 0;
for(int i=0; i<MAX_CLIENTS; i++)
{
if(actsock[i].socket == fd)
{
// send all available data in one frame
// (sending multiple frames resulted in data loss)
int len;
unsigned char *p = ws_build_txframe(i,&len);
if(p != NULL)
{
ret = ws_sendframe_binary(fd, p, len);
free(p);
actsock[i].send = 0;
}
return ret;
}
}
return 0;
}
// received a Websocket Message from a browser
void onmessage(int fd, unsigned char *msg)
{
USERMSG tx_usermsg;
char *cli = ws_getaddress(fd);
if(cli != NULL)
{
tx_usermsg.client = get_socket_idx(fd);
tx_usermsg.command = 0;
//printf("Browser messages: %s, from: %s/%d\n", msg, cli, fd);
actsock[tx_usermsg.client].alive = 20;
free(cli);
}
}