From c3c2acea53fb397cef079aaed393a9adca8a00ae Mon Sep 17 00:00:00 2001 From: cisoakk Date: Mon, 22 Apr 2024 00:34:24 +0300 Subject: [PATCH 1/6] A LUT for peer reference in relay example has been sketched up --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dc91f36..27c0157 100644 --- a/Makefile +++ b/Makefile @@ -106,13 +106,17 @@ $(LIB_WS): $(WS_OBJ) $(Q)$(AR) $(ARFLAGS) $(LIB_WS) $^ # Examples -examples: examples/echo/echo examples/ping/ping +examples: examples/echo/echo examples/ping/ping examples/relay/relay examples/echo/echo: examples/echo/echo.o $(LIB_WS) @echo " LINK $@" $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) examples/ping/ping: examples/ping/ping.o $(LIB_WS) @echo " LINK $@" $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) +examples/relay/relay: examples/relay/relay.o $(LIB_WS) + @echo " LINK $@" + $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) + # Autobahn tests tests: examples @@ -194,5 +198,6 @@ clean: @rm -f $(TOYWS)/toyws.o $(TOYWS)/tws_test.o $(TOYWS)toyws_test @rm -f examples/echo/{echo,echo.o} @rm -f examples/ping/{ping,ping.o} + @rm -f examples/relay/{relay,relay.o} @$(MAKE) clean -C tests/ @$(MAKE) clean -C tests/fuzzy From 1a53d20ec840e41e042f8532e721390b4392f0d2 Mon Sep 17 00:00:00 2001 From: cisoakk Date: Mon, 22 Apr 2024 23:38:07 +0300 Subject: [PATCH 2/6] A json parser for the peers file --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 27c0157..7907851 100644 --- a/Makefile +++ b/Makefile @@ -113,9 +113,9 @@ examples/echo/echo: examples/echo/echo.o $(LIB_WS) examples/ping/ping: examples/ping/ping.o $(LIB_WS) @echo " LINK $@" $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) -examples/relay/relay: examples/relay/relay.o $(LIB_WS) +examples/relay/relay: examples/relay/relay.o examples/relay/waitlist.o examples/relay/peers_lut.o examples/relay/json_pars.o $(LIB_WS) @echo " LINK $@" - $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) + $(Q)$(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) # Autobahn tests From f9a5ebc615cc17a6ff42086c64e7fca0c284bc49 Mon Sep 17 00:00:00 2001 From: cisoakk Date: Mon, 22 Apr 2024 23:41:45 +0300 Subject: [PATCH 3/6] The relay example source dir --- examples/relay/CMakeLists.txt | 17 ++ examples/relay/echo.html | 137 ++++++++++ examples/relay/jsmn.h | 473 ++++++++++++++++++++++++++++++++++ examples/relay/json_pars.c | 126 +++++++++ examples/relay/json_pars.h | 9 + examples/relay/peers.json | 18 ++ examples/relay/peers_lut.c | 189 ++++++++++++++ examples/relay/peers_lut.h | 13 + examples/relay/relay.c | 156 +++++++++++ examples/relay/waitlist.c | 115 +++++++++ examples/relay/waitlist.h | 10 + 11 files changed, 1263 insertions(+) create mode 100644 examples/relay/CMakeLists.txt create mode 100644 examples/relay/echo.html create mode 100755 examples/relay/jsmn.h create mode 100644 examples/relay/json_pars.c create mode 100644 examples/relay/json_pars.h create mode 100644 examples/relay/peers.json create mode 100644 examples/relay/peers_lut.c create mode 100644 examples/relay/peers_lut.h create mode 100644 examples/relay/relay.c create mode 100644 examples/relay/waitlist.c create mode 100644 examples/relay/waitlist.h diff --git a/examples/relay/CMakeLists.txt b/examples/relay/CMakeLists.txt new file mode 100644 index 0000000..b35126f --- /dev/null +++ b/examples/relay/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 Davidson Francis +# +# 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 + +add_executable(relay relay.c) +target_link_libraries(relay ws) diff --git a/examples/relay/echo.html b/examples/relay/echo.html new file mode 100644 index 0000000..dcac1cc --- /dev/null +++ b/examples/relay/echo.html @@ -0,0 +1,137 @@ + + + + + + + + + + diff --git a/examples/relay/jsmn.h b/examples/relay/jsmn.h new file mode 100755 index 0000000..edab6a6 --- /dev/null +++ b/examples/relay/jsmn.h @@ -0,0 +1,473 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#define JSMN_PARENT_LINKS + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/examples/relay/json_pars.c b/examples/relay/json_pars.c new file mode 100644 index 0000000..db71222 --- /dev/null +++ b/examples/relay/json_pars.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include + +#define JSMN_STATIC +#include "jsmn.h" +#include "json_pars.h" + + +static int check_symb(char s) +{ /* + 65-70 A-F + 97-102 a-f + 48-57 0-9 + */ + if ( (s>=65 && s<=70) || (s>=97 && s<=102) || (s>=48 && s<=57) ) + return 1; + + return 0; +} + +static int is_uuid(const char* uu) +{ + int len=0; + while(0!=uu[len]) + { + if( (len>=0 && len<8) || (len>=9 && len<13) || (len>=14 && len<18) || (len>=19 && len<23) || (len>=24 && len<36)) + { + if(0==check_symb(uu[len])) + { + return 0; + } + } + else + if('-'!=uu[len]) + return 0; + + if(len > 35) + return 0; + + len++; + } + + if(len < 36) + return 0; + + return 1; +} + + +char* alloc_peer_buff(const char* peer_file) +{ + FILE* fp = fopen(peer_file,"rb"); + size_t tmp = 0; + char* txt = 0; + + if(fp) + { + fseek(fp,0,SEEK_END); + tmp = ftell(fp); + fseek(fp,0,SEEK_SET); + + txt = malloc(tmp+1); + txt[tmp]=0; + tmp = fread(txt,1,tmp,fp); + fclose(fp); + } + + return txt; +} + + +int get_pairs(char* peer_bfr, pairs fncptr) +{ + const char* provider=0; + const char* user=0; + + char tmp=0; + + jsmn_parser parser; + jsmntok_t tokens[MAX_CLIENTS*3]={0}; + jsmn_init(&parser); + int ret = jsmn_parse(&parser,peer_bfr,strlen(peer_bfr),tokens,MAX_CLIENTS*3); + + if(0>ret) + return ret; + + for(int i=0;i +#include +#include +#include +#include +#include "peers_lut.h" + +/* + * This is a bastardised version of the hashtable used to store crosslinks + * between clients connected through the relay, with client pointers playing + * the role of hashes. The crosslink is an object which references two + * connected clients by their authentication uuids, passed to the relay as + * the very first message after the client connects. The table uses binary + * search to find a hash (a pointer in this case). + */ + +#define ADD_UUID_PAIR 1 +#define ADD_CLIENT 2 +#define GET_PEER 3 +#define REMOVE_CLIENT 4 +#define DUMP 666 + + +struct crosslink +{ + ws_cli_conn_t *clnt; + const char *uuid_own; + const char *uuid_peer; + ws_cli_conn_t *peer; +}; + +static void loc_lock() +{} + +static void loc_unlock() +{} + +static int compare(const void * s1, const void * s2) +{ + const struct crosslink* v1=(const struct crosslink*)s1; + const struct crosslink* v2=(const struct crosslink*)s2; + + if(v1->clnt < v2->clnt) + return -1; + if(v1->clnt > v2->clnt) + return 1; + return 0; +} + +int binary_search(const struct crosslink* A, size_t n, const ws_cli_conn_t* T) +{ + int L = 0; + int R = n - 1; + int m; + + while (L <= R) + { + m = (L + R) >> 1; + if( A[m].clnt < T ) + L = m + 1; + else if (A[m].clnt > T ) + R = m - 1; + else + return m; + } + return -1; +} + +static int hashtable(struct crosslink* clnk, int todo) +{ + static struct crosslink ht[MAX_CLIENTS]; + int ret = -1; + int tmp = 0; + + loc_lock(); + + switch(todo) + { + case ADD_UUID_PAIR: + + tmp = 0; + + for(int i=0;iuuid_own; + ht[i].uuid_peer = clnk->uuid_peer; + tmp++; + } + else + { + ht[i].uuid_own = clnk->uuid_peer; + ht[i].uuid_peer = clnk->uuid_own; + ret = i; + break; + } + } + } + + break; + + case ADD_CLIENT: + + tmp = 0; + + for(int i=0;iuuid_own,ht[i].uuid_own)) + { + ht[i].clnt = clnk->clnt; + tmp++; + } + else + if(0!=ht[i].uuid_peer && 0==strcmp(clnk->uuid_own ,ht[i].uuid_peer)) + { + ht[i].peer = clnk->clnt; + tmp++; + } + + if(2==tmp) + { /* Since the binary search is used for looking-up, the array must be sorted */ + qsort(ht,MAX_CLIENTS,sizeof(struct crosslink),compare); + ret = i; + break; + } + } + + break; + + case GET_PEER: + + tmp = binary_search(ht,MAX_CLIENTS,clnk->clnt); + + if(0<=tmp) + clnk->peer = ht[tmp].peer; + + break; + + case DUMP: + printf("\n"); + for(int i=0;i + * + * 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 + */ + +#include +#include +#include +#include +#include + +#include "peers_lut.h" +#include "waitlist.h" +#include "json_pars.h" + +/** + * @dir examples/ + * @brief wsServer examples folder + */ + +/* + * @dir examples/echo + * @brief Echo example directory. + * @file echo.c + * @brief Simple echo example. + */ + +/** + * @brief Called when a client connects to the server. + * + * @param client Client connection. The @p client parameter is used + * in order to send messages and retrieve informations about the + * client. + */ +void onopen(ws_cli_conn_t *client) +{ + char *cli, *port; + cli = ws_getaddress(client); + port = ws_getport(client); +#ifndef DISABLE_VERBOSE + printf("Connection opened, addr: %s, port: %s\n", cli, port); +#endif + add_applier(client,2000); +} + +/** + * @brief Called when a client disconnects to the server. + * + * @param client Client connection. The @p client parameter is used + * in order to send messages and retrieve informations about the + * client. + */ +void onclose(ws_cli_conn_t *client) +{ + char *cli; + cli = ws_getaddress(client); +#ifndef DISABLE_VERBOSE + printf("Connection closed, addr: %s\n", cli); +#endif +} + +/** + * @brief Called when a client connects to the server. + * + * @param client Client connection. The @p client parameter is used + * in order to send messages and retrieve informations about the + * client. + * + * @param msg Received message, this message can be a text + * or binary message. + * + * @param size Message size (in bytes). + * + * @param type Message type. + */ +void onmessage(ws_cli_conn_t *client, + const unsigned char *msg, uint64_t size, int type) +{ +#ifndef DISABLE_VERBOSE + char *cli; + cli = ws_getaddress(client); + printf("I receive a message: %s (size: %" PRId64 ", type: %d), from: %s\n", + msg, size, type, cli); +#endif + + /** + * Mimicks the same frame type received and re-send it again + * + * Please note that we could just use a ws_sendframe_txt() + * or ws_sendframe_bin() here, but we're just being safe + * and re-sending the very same frame type and content + * again. + * + * Alternative functions: + * ws_sendframe() + * ws_sendframe_txt() + * ws_sendframe_txt_bcast() + * ws_sendframe_bin() + * ws_sendframe_bin_bcast() + */ + + ws_sendframe_bcast(8080, (char *)msg, size, type); +} + +/** + * @brief Main routine. + * + * @note After invoking @ref ws_socket, this routine never returns, + * unless if invoked from a different thread. + */ +int main(void) +{ + char* jsn = alloc_peer_buff("./peers.json"); + + if(jsn) + { + get_pairs(jsn,add_pair); + } + + ws_socket(&(struct ws_server){ + /* + * Bind host: + * localhost -> localhost/127.0.0.1 + * 0.0.0.0 -> global IPv4 + * :: -> global IPv4+IPv6 (DualStack) + */ + .host = "0.0.0.0", + .port = 8080, + .thread_loop = 0, + .timeout_ms = 1000, + .evs.onopen = &onopen, + .evs.onclose = &onclose, + .evs.onmessage = &onmessage + }); + + /* + * If you want to execute code past ws_socket(), set + * .thread_loop to '1'. + */ + + free(jsn); + + return (0); +} diff --git a/examples/relay/waitlist.c b/examples/relay/waitlist.c new file mode 100644 index 0000000..fa3bbf8 --- /dev/null +++ b/examples/relay/waitlist.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include + +#include "waitlist.h" + +#define ADD_APPLIER 1 +#define REMOVE_BELATED 2 +#define CONFIRM_APPLIER 3 +#define MAX_APPLIERS MAX_CLIENTS + +struct applier +{ + ws_cli_conn_t *clnt; + uint32_t deadline; +}; + +uint32_t get_ticks() +{ + struct timeval tv; + gettimeofday(&tv,0); + + return tv.tv_sec*1000 + tv.tv_usec/1000; +} + +static int waitlist(struct applier* app, int todo) +{ + static struct applier ht[MAX_APPLIERS]; + int ret = -1; + uint32_t tmp = get_ticks(); + + switch(todo) + { + case ADD_APPLIER: + + for(int i=0;iclnt==ht[i].clnt) + { + ht[i].clnt = 0; + ht[i].deadline = 0; + ret = i; + break; + } + } + + break; + default:break; + } + + return ret; + +} + +int add_applier(ws_cli_conn_t* cl, uint32_t tmout_ms) +{ + struct applier appl; + + appl.deadline = get_ticks() + tmout_ms; + appl.clnt = cl; + + return waitlist(&appl,ADD_APPLIER); +} + +ws_cli_conn_t* remove_belated() +{ + struct applier appl; + + if(0 Date: Sat, 11 May 2024 16:07:42 +0300 Subject: [PATCH 4/6] The first working variant of the relay --- Makefile | 7 +- examples/relay/json_pars.c | 14 +- examples/relay/{echo.html => peer.html} | 4 +- examples/relay/peers_lut.c | 124 ++++++++++++++--- examples/relay/peers_lut.h | 7 +- examples/relay/relay.c | 173 ++++++++++++++++-------- examples/relay/waitlist.c | 121 +++++++++++------ examples/relay/waitlist.h | 2 + 8 files changed, 325 insertions(+), 127 deletions(-) rename examples/relay/{echo.html => peer.html} (96%) diff --git a/Makefile b/Makefile index 7907851..a307943 100644 --- a/Makefile +++ b/Makefile @@ -113,9 +113,9 @@ examples/echo/echo: examples/echo/echo.o $(LIB_WS) examples/ping/ping: examples/ping/ping.o $(LIB_WS) @echo " LINK $@" $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) -examples/relay/relay: examples/relay/relay.o examples/relay/waitlist.o examples/relay/peers_lut.o examples/relay/json_pars.o $(LIB_WS) +examples/relay/relay: examples/relay/relay.o examples/relay/waitlist.o examples/relay/peers_lut.o examples/relay/json_pars.o $(LIB_WS) @echo " LINK $@" - $(Q)$(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) + $(Q)$(CC) $(CFLAGS) -pthread $^ -o $@ $(LDLIBS) # Autobahn tests @@ -198,6 +198,7 @@ clean: @rm -f $(TOYWS)/toyws.o $(TOYWS)/tws_test.o $(TOYWS)toyws_test @rm -f examples/echo/{echo,echo.o} @rm -f examples/ping/{ping,ping.o} - @rm -f examples/relay/{relay,relay.o} + @rm -f examples/relay/relay + @rm -f examples/relay/*.o @$(MAKE) clean -C tests/ @$(MAKE) clean -C tests/fuzzy diff --git a/examples/relay/json_pars.c b/examples/relay/json_pars.c index db71222..42edabf 100644 --- a/examples/relay/json_pars.c +++ b/examples/relay/json_pars.c @@ -24,7 +24,7 @@ static int check_symb(char s) static int is_uuid(const char* uu) { int len=0; - while(0!=uu[len]) + while(uu && 0!=uu[len]) { if( (len>=0 && len<8) || (len>=9 && len<13) || (len>=14 && len<18) || (len>=19 && len<23) || (len>=24 && len<36)) { @@ -61,10 +61,13 @@ char* alloc_peer_buff(const char* peer_file) fseek(fp,0,SEEK_END); tmp = ftell(fp); fseek(fp,0,SEEK_SET); - + + if(tmp>0) + { txt = malloc(tmp+1); txt[tmp]=0; tmp = fread(txt,1,tmp,fp); + } fclose(fp); } @@ -86,6 +89,8 @@ int get_pairs(char* peer_bfr, pairs fncptr) if(0>ret) return ret; + + ret = 0; for(int i=0;iwsServer

Message: + placeholder="Type your message (ENTER to send)" size="36" >

- + diff --git a/examples/relay/peers_lut.c b/examples/relay/peers_lut.c index eb72b9a..083f48a 100644 --- a/examples/relay/peers_lut.c +++ b/examples/relay/peers_lut.c @@ -6,18 +6,20 @@ #include "peers_lut.h" /* - * This is a bastardised version of the hashtable used to store crosslinks - * between clients connected through the relay, with client pointers playing - * the role of hashes. The crosslink is an object which references two - * connected clients by their authentication uuids, passed to the relay as + * This is a bastardised version of the hashtable used to store crosslinks + * between clients connected through the relay, with client pointers playing + * the role of hashes. The crosslink is an object which references two + * connected clients by their authentication uuids, passed to the relay as * the very first message after the client connects. The table uses binary * search to find a hash (a pointer in this case). */ -#define ADD_UUID_PAIR 1 -#define ADD_CLIENT 2 -#define GET_PEER 3 -#define REMOVE_CLIENT 4 +#define ADD_UUID_PAIR 1 +#define ADD_CLIENT 2 +#define GET_PEER 3 +#define REMOVE_CLIENT 4 +#define GET_CLIENT_UUID 5 +#define FIND_UUID 6 #define DUMP 666 @@ -105,21 +107,28 @@ static int hashtable(struct crosslink* clnk, int todo) case ADD_CLIENT: tmp = 0; + int i = 0; - for(int i=0;iuuid_own,ht[i].uuid_own)) { + if(ht[i].clnt) + break; // There is already a client connected with this uiid. + ht[i].clnt = clnk->clnt; tmp++; } else if(0!=ht[i].uuid_peer && 0==strcmp(clnk->uuid_own ,ht[i].uuid_peer)) { + if(ht[i].peer) + break; // There is already a client connected with this uiid. + ht[i].peer = clnk->clnt; tmp++; } - + if(2==tmp) { /* Since the binary search is used for looking-up, the array must be sorted */ qsort(ht,MAX_CLIENTS,sizeof(struct crosslink),compare); @@ -127,7 +136,7 @@ static int hashtable(struct crosslink* clnk, int todo) break; } } - + break; case GET_PEER: @@ -138,6 +147,52 @@ static int hashtable(struct crosslink* clnk, int todo) clnk->peer = ht[tmp].peer; break; + + case GET_CLIENT_UUID: + + tmp = binary_search(ht,MAX_CLIENTS,clnk->clnt); + + if(0<=tmp) + clnk->uuid_own = ht[tmp].uuid_own; + + break; + + case FIND_UUID: + + for(int i=0;iuuid_own)) + { + clnk->clnt = ht[i].clnt; + break; + } + } + + break; + + case REMOVE_CLIENT: + + tmp = 0; + + for(int i=0;iclnt == ht[i].clnt) + { + ht[i].clnt=0; + tmp++; + } + + if(clnk->clnt == ht[i].peer) + { + ht[i].peer=0; + tmp++; + } + + if(2==tmp) + break; + } + + break; case DUMP: printf("\n"); @@ -159,31 +214,60 @@ int add_pair(const char* provider, const char* user) clnk.uuid_own = provider; clnk.uuid_peer = user; - + return hashtable(&clnk,ADD_UUID_PAIR); } -int add_client(ws_cli_conn_t* cl, const char* uuid) +int add_client(ws_cli_conn_t* cl, const unsigned char * uuid) { - struct crosslink clnk; + struct crosslink clnk; - clnk.uuid_own = uuid; + clnk.uuid_own = (const char *)uuid; clnk.clnt = cl; - - return hashtable(&clnk,ADD_CLIENT); + + if(uuid) + return hashtable(&clnk,ADD_CLIENT); + + return -1; } ws_cli_conn_t* get_peer(ws_cli_conn_t* cl) { - struct crosslink clnk={0,0}; + struct crosslink clnk={0,0,0,0}; clnk.clnt = cl; hashtable(&clnk, GET_PEER); return clnk.peer; } +int get_client_auth_status(ws_cli_conn_t* cl) +{ + struct crosslink clnk={0,0,0,0}; + + clnk.clnt = cl; + hashtable(&clnk, GET_CLIENT_UUID); + return 0!=clnk.uuid_own; +} + +int known_uuid(char* id) +{ + struct crosslink clnk={0,0,0,0}; + + clnk.uuid_own = id; + hashtable(&clnk, FIND_UUID); + return 0!=clnk.clnt; +} + +void remove_client(ws_cli_conn_t* cl) +{ + struct crosslink clnk={cl,0,0,0}; + + hashtable(&clnk, FIND_UUID); +} + + int lut_dump() { - struct crosslink clnk; - return hashtable(&clnk,DUMP); + struct crosslink clnk; + return hashtable(&clnk,DUMP); } diff --git a/examples/relay/peers_lut.h b/examples/relay/peers_lut.h index 22aaee1..cd6fe43 100644 --- a/examples/relay/peers_lut.h +++ b/examples/relay/peers_lut.h @@ -3,11 +3,16 @@ int add_pair(const char* provider, const char* user); -int add_client(ws_cli_conn_t* cl, const char* guid); +int add_client(ws_cli_conn_t* cl, const unsigned char * uuid); ws_cli_conn_t* get_peer(ws_cli_conn_t* cl); int lut_dump(); +int get_client_auth_status(ws_cli_conn_t* cl); + +int known_uuid(char* id); + +void remove_client(ws_cli_conn_t* cl); #endif diff --git a/examples/relay/relay.c b/examples/relay/relay.c index b2c17f6..a3ceb66 100644 --- a/examples/relay/relay.c +++ b/examples/relay/relay.c @@ -1,6 +1,4 @@ /* - * Copyright (C) 2016-2023 Davidson Francis - * * 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 @@ -14,7 +12,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see */ - +#define _GNU_SOURCE #include #include #include @@ -25,6 +23,14 @@ #include "waitlist.h" #include "json_pars.h" +#include + + +pthread_mutex_t auth_sync; + + +ws_cli_conn_t* check_auth(ws_cli_conn_t*, const unsigned char* , int* ); + /** * @dir examples/ * @brief wsServer examples folder @@ -46,13 +52,16 @@ */ void onopen(ws_cli_conn_t *client) { - char *cli, *port; - cli = ws_getaddress(client); - port = ws_getport(client); + char *cli, *port; + cli = ws_getaddress(client); + port = ws_getport(client); #ifndef DISABLE_VERBOSE - printf("Connection opened, addr: %s, port: %s\n", cli, port); + printf("Connection opened, addr: %s, port: %s\n", cli, port); #endif + + pthread_mutex_lock(&auth_sync); add_applier(client,2000); + pthread_mutex_unlock(&auth_sync); } /** @@ -64,11 +73,18 @@ void onopen(ws_cli_conn_t *client) */ void onclose(ws_cli_conn_t *client) { - char *cli; - cli = ws_getaddress(client); + char *cli; + cli = ws_getaddress(client); #ifndef DISABLE_VERBOSE - printf("Connection closed, addr: %s\n", cli); + printf("Connection closed, addr: %s\n", cli); #endif + + pthread_mutex_lock(&auth_sync); + + remove_client(client); + delete_applier(client); + + pthread_mutex_unlock(&auth_sync); } /** @@ -86,34 +102,57 @@ void onclose(ws_cli_conn_t *client) * @param type Message type. */ void onmessage(ws_cli_conn_t *client, - const unsigned char *msg, uint64_t size, int type) + const unsigned char *msg, uint64_t size, int type) { + ws_cli_conn_t * peer = 0; + int bad=0; + #ifndef DISABLE_VERBOSE char *cli; - cli = ws_getaddress(client); - printf("I receive a message: %s (size: %" PRId64 ", type: %d), from: %s\n", - msg, size, type, cli); + cli = ws_getaddress(client); + printf("I receive a message: %s (size: %" PRId64 ", type: %d), from: %s\n", + msg, size, type, cli); #endif - /** - * Mimicks the same frame type received and re-send it again - * - * Please note that we could just use a ws_sendframe_txt() - * or ws_sendframe_bin() here, but we're just being safe - * and re-sending the very same frame type and content - * again. - * - * Alternative functions: - * ws_sendframe() - * ws_sendframe_txt() - * ws_sendframe_txt_bcast() - * ws_sendframe_bin() - * ws_sendframe_bin_bcast() - */ - - ws_sendframe_bcast(8080, (char *)msg, size, type); + peer = check_auth(client,msg,&bad); + + if(peer) + ws_sendframe(peer, (char *)msg, size, type); + + if(bad) + ws_close_client(client); + +} + +ws_cli_conn_t* check_auth(ws_cli_conn_t *client, const unsigned char *msg, int* err) +{ + ws_cli_conn_t * peer = 0; + int bad=0; + + pthread_mutex_lock(&auth_sync); + + peer = get_peer(client); + + if(!peer) /* The client isn't paired */ + { + if(!get_client_auth_status(client))/* There is no such authenticated client */ + { + if(add_client(client,msg) < 0) /* The first message should be the client UUID */ + { + bad = 1; /* We could not add the client: wrong UUID */ + } + } + + delete_applier(client); /* Remove the client from the authenthication waitlist */ + } + + pthread_mutex_unlock(&auth_sync); + *err = bad; + + return peer; } + /** * @brief Main routine. * @@ -123,34 +162,54 @@ void onmessage(ws_cli_conn_t *client, int main(void) { char* jsn = alloc_peer_buff("./peers.json"); + pthread_mutex_init(&auth_sync,0); + ws_cli_conn_t* clnt=0; - if(jsn) + if(0==jsn || 0>=get_pairs(jsn,add_pair)) { - get_pairs(jsn,add_pair); + printf("The peer file is absent or corrupt\n"); + goto end; + } + + ws_socket(&(struct ws_server){ + /* + * Bind host: + * localhost -> localhost/127.0.0.1 + * 0.0.0.0 -> global IPv4 + * :: -> global IPv4+IPv6 (DualStack) + */ + .host = "0.0.0.0", + .port = 8080, + .thread_loop = 1, + .timeout_ms = 1000, + .evs.onopen = &onopen, + .evs.onclose = &onclose, + .evs.onmessage = &onmessage + }); + + while (1) + { + pthread_mutex_lock(&auth_sync); + + clnt = remove_belated(); + + if(clnt) + remove_client(clnt); + + pthread_mutex_unlock(&auth_sync); + + if(clnt) + ws_close_client(clnt); + + clnt = 0; + + usleep(5000); } - ws_socket(&(struct ws_server){ - /* - * Bind host: - * localhost -> localhost/127.0.0.1 - * 0.0.0.0 -> global IPv4 - * :: -> global IPv4+IPv6 (DualStack) - */ - .host = "0.0.0.0", - .port = 8080, - .thread_loop = 0, - .timeout_ms = 1000, - .evs.onopen = &onopen, - .evs.onclose = &onclose, - .evs.onmessage = &onmessage - }); - - /* - * If you want to execute code past ws_socket(), set - * .thread_loop to '1'. - */ - - free(jsn); - - return (0); +end: + if(jsn) + free(jsn); + + pthread_mutex_destroy(&auth_sync); + return (0); } diff --git a/examples/relay/waitlist.c b/examples/relay/waitlist.c index fa3bbf8..a790f48 100644 --- a/examples/relay/waitlist.c +++ b/examples/relay/waitlist.c @@ -9,7 +9,7 @@ #define ADD_APPLIER 1 #define REMOVE_BELATED 2 -#define CONFIRM_APPLIER 3 +#define DELETE_APPLIER 3 #define MAX_APPLIERS MAX_CLIENTS struct applier @@ -18,6 +18,15 @@ struct applier uint32_t deadline; }; +struct buff_state +{ + int* belated; + int* empty; + struct applier* bfr; + size_t bf_sz; + ws_cli_conn_t *clnt; +}; + uint32_t get_ticks() { struct timeval tv; @@ -26,63 +35,95 @@ uint32_t get_ticks() return tv.tv_sec*1000 + tv.tv_usec/1000; } +static int get_buffer_state(struct buff_state* bst) +{ + int ret = -1; + *bst->belated = -1; + *bst->empty = -1; + + uint32_t tmp = get_ticks(); + + for(size_t i=0;ibf_sz;i++) + { + if(bst->clnt && bst->clnt==bst->bfr[i].clnt) + { + ret = i; + } + + if(bst->bfr[i].deadline<=tmp) + { + *bst->belated = i; + } + + if(!bst->bfr[i].clnt) + { + *bst->empty = i; + } + } + + return ret; +} + + static int waitlist(struct applier* app, int todo) { static struct applier ht[MAX_APPLIERS]; + static int belated = -1; + static int empty = -1; int ret = -1; - uint32_t tmp = get_ticks(); + struct buff_state bs={&belated,&empty,ht,MAX_APPLIERS,app->clnt}; + switch(todo) { case ADD_APPLIER: - for(int i=0;i=0) + { + ret = empty; + ht[empty] = *app; } + empty = -1; break; case REMOVE_BELATED: - - for(int i=0;i=0) + { + ret = belated; + *app = ht[belated]; + ht[belated].clnt = 0; + ht[belated].deadline = 0; } + belated = -1; + break; - case CONFIRM_APPLIER: - - for(int i=0;iclnt==ht[i].clnt) - { - ht[i].clnt = 0; - ht[i].deadline = 0; - ret = i; - break; - } + case DELETE_APPLIER: + + ret = get_buffer_state(&bs); + + if(ret>=0) + { + ht[ret].clnt = 0; + ht[ret].deadline = 0; + break; } - - break; + + break; + default:break; - } + } return ret; - } int add_applier(ws_cli_conn_t* cl, uint32_t tmout_ms) @@ -97,19 +138,19 @@ int add_applier(ws_cli_conn_t* cl, uint32_t tmout_ms) ws_cli_conn_t* remove_belated() { - struct applier appl; + struct applier appl={0,0}; - if(0=0) return appl.clnt; return 0; } -int confirm_applier(ws_cli_conn_t* cl) +int delete_applier(ws_cli_conn_t* cl) { struct applier appl; appl.clnt = cl; - return waitlist(&appl,CONFIRM_APPLIER); + return waitlist(&appl,DELETE_APPLIER); } diff --git a/examples/relay/waitlist.h b/examples/relay/waitlist.h index b5bd91b..87ad5c9 100644 --- a/examples/relay/waitlist.h +++ b/examples/relay/waitlist.h @@ -7,4 +7,6 @@ ws_cli_conn_t* remove_belated(); int confirm_applier(ws_cli_conn_t* cl); +int delete_applier(ws_cli_conn_t* cl); + #endif From f97dbcfa578fd56c59facee55451c8c5386d133e Mon Sep 17 00:00:00 2001 From: cisoakk Date: Wed, 15 May 2024 12:25:29 +0300 Subject: [PATCH 5/6] A copy-past error in remove_client() has been corrected --- examples/relay/peers_lut.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/examples/relay/peers_lut.c b/examples/relay/peers_lut.c index 083f48a..91b600b 100644 --- a/examples/relay/peers_lut.c +++ b/examples/relay/peers_lut.c @@ -31,12 +31,6 @@ struct crosslink ws_cli_conn_t *peer; }; -static void loc_lock() -{} - -static void loc_unlock() -{} - static int compare(const void * s1, const void * s2) { const struct crosslink* v1=(const struct crosslink*)s1; @@ -49,7 +43,7 @@ static int compare(const void * s1, const void * s2) return 0; } -int binary_search(const struct crosslink* A, size_t n, const ws_cli_conn_t* T) +static int binary_search(const struct crosslink* A, size_t n, const ws_cli_conn_t* T) { int L = 0; int R = n - 1; @@ -74,8 +68,6 @@ static int hashtable(struct crosslink* clnk, int todo) int ret = -1; int tmp = 0; - loc_lock(); - switch(todo) { case ADD_UUID_PAIR: @@ -204,7 +196,6 @@ static int hashtable(struct crosslink* clnk, int todo) break; } - loc_unlock(); return ret; } @@ -262,7 +253,7 @@ void remove_client(ws_cli_conn_t* cl) { struct crosslink clnk={cl,0,0,0}; - hashtable(&clnk, FIND_UUID); + hashtable(&clnk, REMOVE_CLIENT); } From 9bf93c8b8ab2fd0eb0fb245358558c203ce6b0f2 Mon Sep 17 00:00:00 2001 From: cisoakk Date: Wed, 15 May 2024 14:28:45 +0300 Subject: [PATCH 6/6] Some final cleanup and commenting --- examples/README.md | 4 ++ examples/relay/json_pars.c | 16 +++--- examples/relay/peers_lut.c | 56 ++++++++++++++------ examples/relay/peers_lut.h | 4 ++ examples/relay/relay.c | 106 ++++++++++++++++++++++++------------- examples/relay/waitlist.c | 6 +-- 6 files changed, 126 insertions(+), 66 deletions(-) diff --git a/examples/README.md b/examples/README.md index e1f715f..52d652e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,6 +10,10 @@ to all connected clients. - [vtouchpad](vtouchpad): A 'virtual touchpad' that remotely controls a computer's mouse. +- [relay](relay): A simple relay server that connects peers on a one-to-one +basis and relays messages between them. UUIDs are used to authenticate peers. + + If you have other examples and/or small demos that might be useful for illustrating wsServer's functionality, feel free to submit a PR. diff --git a/examples/relay/json_pars.c b/examples/relay/json_pars.c index 42edabf..3486dd2 100644 --- a/examples/relay/json_pars.c +++ b/examples/relay/json_pars.c @@ -62,12 +62,12 @@ char* alloc_peer_buff(const char* peer_file) tmp = ftell(fp); fseek(fp,0,SEEK_SET); - if(tmp>0) - { - txt = malloc(tmp+1); - txt[tmp]=0; - tmp = fread(txt,1,tmp,fp); - } + if(tmp>0) + { + txt = malloc(tmp+1); + txt[tmp]=0; + tmp = fread(txt,1,tmp,fp); + } fclose(fp); } @@ -89,7 +89,7 @@ int get_pairs(char* peer_bfr, pairs fncptr) if(0>ret) return ret; - + ret = 0; for(int i=0;iuuid_own,ht[i].uuid_own)) { - if(ht[i].clnt) - break; // There is already a client connected with this uiid. - + if(ht[i].clnt) + break; // There is already a client connected with this uiid. + ht[i].clnt = clnk->clnt; tmp++; } else if(0!=ht[i].uuid_peer && 0==strcmp(clnk->uuid_own ,ht[i].uuid_peer)) { - if(ht[i].peer) - break; // There is already a client connected with this uiid. - + if(ht[i].peer) + break; // There is already a client connected with this uiid. + ht[i].peer = clnk->clnt; tmp++; } - + if(2==tmp) { /* Since the binary search is used for looking-up, the array must be sorted */ qsort(ht,MAX_CLIENTS,sizeof(struct crosslink),compare); @@ -128,7 +132,7 @@ static int hashtable(struct crosslink* clnk, int todo) break; } } - + break; case GET_PEER: @@ -162,11 +166,11 @@ static int hashtable(struct crosslink* clnk, int todo) break; - case REMOVE_CLIENT: - - tmp = 0; - - for(int i=0;iclnt == ht[i].clnt) { @@ -181,11 +185,22 @@ static int hashtable(struct crosslink* clnk, int todo) } if(2==tmp) - break; + break; + } + + break; + + case FOREACH: + + for(int i=0;i #include #include +#include #include #include "peers_lut.h" @@ -25,11 +26,13 @@ #include +/* A sync object to guard access to the peers LUT and the waitlist */ +static pthread_mutex_t auth_sync; -pthread_mutex_t auth_sync; +static int term = 0; -ws_cli_conn_t* check_auth(ws_cli_conn_t*, const unsigned char* , int* ); +static ws_cli_conn_t* check_auth(ws_cli_conn_t*, const unsigned char* , int* ); /** * @dir examples/ @@ -37,14 +40,15 @@ ws_cli_conn_t* check_auth(ws_cli_conn_t*, const unsigned char* , int* ); */ /* - * @dir examples/echo - * @brief Echo example directory. - * @file echo.c - * @brief Simple echo example. + * @dir examples/relay + * @brief Relay example directory. + * @file relay.c + * @brief Simple relay example. */ /** - * @brief Called when a client connects to the server. + * @brief Called when a client connects to the relay. It adds the + * newly connected client to the authentication waitlist. * * @param client Client connection. The @p client parameter is used * in order to send messages and retrieve informations about the @@ -65,7 +69,7 @@ void onopen(ws_cli_conn_t *client) } /** - * @brief Called when a client disconnects to the server. + * @brief Called when a client disconnects from the relay. * * @param client Client connection. The @p client parameter is used * in order to send messages and retrieve informations about the @@ -88,14 +92,16 @@ void onclose(ws_cli_conn_t *client) } /** - * @brief Called when a client connects to the server. + * @brief Called when a client send a message to the relay. + * If the client is authenticated and has a peer connected, + * relay will proxy the message to the peer, otherwise the + * message will be dropped. Pings and pongs are not proxied. * * @param client Client connection. The @p client parameter is used * in order to send messages and retrieve informations about the * client. * - * @param msg Received message, this message can be a text - * or binary message. + * @param msg Received message, this message can be any message. * * @param size Message size (in bytes). * @@ -116,7 +122,7 @@ void onmessage(ws_cli_conn_t *client, peer = check_auth(client,msg,&bad); - if(peer) + if(peer && WS_FR_OP_PING!=type && WS_FR_OP_PONG!=type) ws_sendframe(peer, (char *)msg, size, type); if(bad) @@ -124,7 +130,7 @@ void onmessage(ws_cli_conn_t *client, } -ws_cli_conn_t* check_auth(ws_cli_conn_t *client, const unsigned char *msg, int* err) +static ws_cli_conn_t* check_auth(ws_cli_conn_t *client, const unsigned char *msg, int* err) { ws_cli_conn_t * peer = 0; int bad=0; @@ -139,7 +145,7 @@ ws_cli_conn_t* check_auth(ws_cli_conn_t *client, const unsigned char *msg, int* { if(add_client(client,msg) < 0) /* The first message should be the client UUID */ { - bad = 1; /* We could not add the client: wrong UUID */ + bad = 1; /* We could not add the client: wrong UUID */ } } @@ -152,6 +158,38 @@ ws_cli_conn_t* check_auth(ws_cli_conn_t *client, const unsigned char *msg, int* return peer; } +static void endsignal(int sig) +{ + if(SIGINT == sig) + term = 1; +} + +static void close_all() +{ + pthread_mutex_lock(&auth_sync); + + foreach(ws_close_client); + + pthread_mutex_unlock(&auth_sync); +} + +static void check_belated() +{ + ws_cli_conn_t* clnt=0; + + pthread_mutex_lock(&auth_sync); + + clnt = remove_belated(); + + if(clnt) + remove_client(clnt); + + pthread_mutex_unlock(&auth_sync); + + if(clnt) + ws_close_client(clnt); +} + /** * @brief Main routine. @@ -163,21 +201,23 @@ int main(void) { char* jsn = alloc_peer_buff("./peers.json"); pthread_mutex_init(&auth_sync,0); - ws_cli_conn_t* clnt=0; + if(0==jsn || 0>=get_pairs(jsn,add_pair)) { printf("The peer file is absent or corrupt\n"); goto end; } + + signal( SIGINT , endsignal ); ws_socket(&(struct ws_server){ /* - * Bind host: - * localhost -> localhost/127.0.0.1 - * 0.0.0.0 -> global IPv4 - * :: -> global IPv4+IPv6 (DualStack) - */ + * Bind host: + * localhost -> localhost/127.0.0.1 + * 0.0.0.0 -> global IPv4 + * :: -> global IPv4+IPv6 (DualStack) + */ .host = "0.0.0.0", .port = 8080, .thread_loop = 1, @@ -187,24 +227,14 @@ int main(void) .evs.onmessage = &onmessage }); - while (1) - { - pthread_mutex_lock(&auth_sync); - - clnt = remove_belated(); - - if(clnt) - remove_client(clnt); - - pthread_mutex_unlock(&auth_sync); - - if(clnt) - ws_close_client(clnt); - - clnt = 0; - - usleep(5000); - } + while (!term) + { + check_belated(); + usleep(5000); + } + + close_all(); + usleep(500000); end: if(jsn) diff --git a/examples/relay/waitlist.c b/examples/relay/waitlist.c index a790f48..e65826f 100644 --- a/examples/relay/waitlist.c +++ b/examples/relay/waitlist.c @@ -117,9 +117,9 @@ static int waitlist(struct applier* app, int todo) ht[ret].deadline = 0; break; } - - break; - + + break; + default:break; }