From 9192ee09a3604952ad24b7ae429db07e7e782912 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Wed, 19 Jan 2022 17:31:04 +0300 Subject: [PATCH] add serialsock --- NES_webiface/.gitignore | 23 + NES_webiface/CMakeLists.txt | 71 ++ NES_webiface/HOWTO.cert | 5 + NES_webiface/README.md | 3 + NES_webiface/auth.c | 682 ++++++++++++++++++++ NES_webiface/auth.h | 70 ++ NES_webiface/cert.key | 28 + NES_webiface/cert.pem | 21 + NES_webiface/cmdlnopts.c | 150 +++++ NES_webiface/cmdlnopts.h | 69 ++ NES_webiface/main.c | 250 +++++++ NES_webiface/static/admin.html | 156 +++++ NES_webiface/static/auth.js | 96 +++ NES_webiface/static/index.html | 16 + NES_webiface/static/pass.html | 92 +++ NES_webiface/static/pass.js | 56 ++ NES_webiface/static/requests.js | 43 ++ NES_webiface/websockets.c | 88 +++ NES_webiface/websockets.h | 26 + README | 6 + Socket_CAN/99-socketcan.rules | 1 + Socket_CAN/HOWTO.cert | 5 + Socket_CAN/Makefile | 63 ++ Socket_CAN/Makefile.bk | 62 ++ Socket_CAN/README_ports | 7 + Socket_CAN/Readme.md | 15 + Socket_CAN/SocketCAN.cflags | 1 + Socket_CAN/SocketCAN.config | 6 + Socket_CAN/SocketCAN.creator | 1 + Socket_CAN/SocketCAN.creator.user | 159 +++++ Socket_CAN/SocketCAN.cxxflags | 1 + Socket_CAN/SocketCAN.files | 16 + Socket_CAN/SocketCAN.includes | 1 + Socket_CAN/ca/ca/ca_cert.pem | 32 + Socket_CAN/ca/ca/ca_cert.srl | 1 + Socket_CAN/ca/ca/private/ca_key.pem | 52 ++ Socket_CAN/ca/client/client.csr | 27 + Socket_CAN/ca/client/client_cert.pem | 30 + Socket_CAN/ca/client/private/client_key.pem | 51 ++ Socket_CAN/ca/gen.sh | 49 ++ Socket_CAN/ca/server/private/server_key.pem | 51 ++ Socket_CAN/ca/server/server.csr | 27 + Socket_CAN/ca/server/server_cert.pem | 30 + Socket_CAN/can4linux.h | 211 ++++++ Socket_CAN/can_io.c | 545 ++++++++++++++++ Socket_CAN/can_io.h | 29 + Socket_CAN/canbus.h | 39 ++ Socket_CAN/cansock.c | 76 +++ Socket_CAN/cert.key | 28 + Socket_CAN/cert.pem | 21 + Socket_CAN/client.c | 43 ++ Socket_CAN/client.h | 24 + Socket_CAN/cmdlnopts.c | 89 +++ Socket_CAN/cmdlnopts.h | 54 ++ Socket_CAN/daemon.c | 99 +++ Socket_CAN/daemon.h | 25 + Socket_CAN/diff/clientscript | 5 + Socket_CAN/diff/serverscript | 6 + Socket_CAN/log.log | 121 ++++ Socket_CAN/server.c | 74 +++ Socket_CAN/server.h | 29 + Socket_CAN/soccanclient | Bin 0 -> 26864 bytes Socket_CAN/soccanserver | Bin 0 -> 33008 bytes Socket_CAN/sslsock.c | 322 +++++++++ Socket_CAN/sslsock.h | 35 + serialsock/Makefile | 45 ++ serialsock/Readme | 1 + serialsock/canserver.cflags | 1 + serialsock/canserver.config | 6 + serialsock/canserver.creator | 1 + serialsock/canserver.cxxflags | 1 + serialsock/canserver.files | 5 + serialsock/canserver.includes | 1 + serialsock/cmdlnopts.c | 95 +++ serialsock/cmdlnopts.h | 42 ++ serialsock/main.c | 115 ++++ serialsock/sersock.c | 386 +++++++++++ serialsock/sersock.h | 31 + 78 files changed, 5244 insertions(+) create mode 100644 NES_webiface/.gitignore create mode 100644 NES_webiface/CMakeLists.txt create mode 100644 NES_webiface/HOWTO.cert create mode 100644 NES_webiface/README.md create mode 100644 NES_webiface/auth.c create mode 100644 NES_webiface/auth.h create mode 100644 NES_webiface/cert.key create mode 100644 NES_webiface/cert.pem create mode 100644 NES_webiface/cmdlnopts.c create mode 100644 NES_webiface/cmdlnopts.h create mode 100644 NES_webiface/main.c create mode 100644 NES_webiface/static/admin.html create mode 100644 NES_webiface/static/auth.js create mode 100644 NES_webiface/static/index.html create mode 100644 NES_webiface/static/pass.html create mode 100644 NES_webiface/static/pass.js create mode 100644 NES_webiface/static/requests.js create mode 100644 NES_webiface/websockets.c create mode 100644 NES_webiface/websockets.h create mode 100644 Socket_CAN/99-socketcan.rules create mode 100644 Socket_CAN/HOWTO.cert create mode 100644 Socket_CAN/Makefile create mode 100644 Socket_CAN/Makefile.bk create mode 100644 Socket_CAN/README_ports create mode 100644 Socket_CAN/Readme.md create mode 100644 Socket_CAN/SocketCAN.cflags create mode 100644 Socket_CAN/SocketCAN.config create mode 100644 Socket_CAN/SocketCAN.creator create mode 100644 Socket_CAN/SocketCAN.creator.user create mode 100644 Socket_CAN/SocketCAN.cxxflags create mode 100644 Socket_CAN/SocketCAN.files create mode 100644 Socket_CAN/SocketCAN.includes create mode 100644 Socket_CAN/ca/ca/ca_cert.pem create mode 100644 Socket_CAN/ca/ca/ca_cert.srl create mode 100644 Socket_CAN/ca/ca/private/ca_key.pem create mode 100644 Socket_CAN/ca/client/client.csr create mode 100644 Socket_CAN/ca/client/client_cert.pem create mode 100644 Socket_CAN/ca/client/private/client_key.pem create mode 100755 Socket_CAN/ca/gen.sh create mode 100644 Socket_CAN/ca/server/private/server_key.pem create mode 100644 Socket_CAN/ca/server/server.csr create mode 100644 Socket_CAN/ca/server/server_cert.pem create mode 100644 Socket_CAN/can4linux.h create mode 100644 Socket_CAN/can_io.c create mode 100644 Socket_CAN/can_io.h create mode 100644 Socket_CAN/canbus.h create mode 100644 Socket_CAN/cansock.c create mode 100644 Socket_CAN/cert.key create mode 100644 Socket_CAN/cert.pem create mode 100644 Socket_CAN/client.c create mode 100644 Socket_CAN/client.h create mode 100644 Socket_CAN/cmdlnopts.c create mode 100644 Socket_CAN/cmdlnopts.h create mode 100644 Socket_CAN/daemon.c create mode 100644 Socket_CAN/daemon.h create mode 100755 Socket_CAN/diff/clientscript create mode 100755 Socket_CAN/diff/serverscript create mode 100644 Socket_CAN/log.log create mode 100644 Socket_CAN/server.c create mode 100644 Socket_CAN/server.h create mode 100755 Socket_CAN/soccanclient create mode 100755 Socket_CAN/soccanserver create mode 100644 Socket_CAN/sslsock.c create mode 100644 Socket_CAN/sslsock.h create mode 100644 serialsock/Makefile create mode 100644 serialsock/Readme create mode 100644 serialsock/canserver.cflags create mode 100644 serialsock/canserver.config create mode 100644 serialsock/canserver.creator create mode 100644 serialsock/canserver.cxxflags create mode 100644 serialsock/canserver.files create mode 100644 serialsock/canserver.includes create mode 100644 serialsock/cmdlnopts.c create mode 100644 serialsock/cmdlnopts.h create mode 100644 serialsock/main.c create mode 100644 serialsock/sersock.c create mode 100644 serialsock/sersock.h diff --git a/NES_webiface/.gitignore b/NES_webiface/.gitignore new file mode 100644 index 0000000..60663b5 --- /dev/null +++ b/NES_webiface/.gitignore @@ -0,0 +1,23 @@ +# Prerequisites +*.d + +# Object files +*.o + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.so +*.so.* + +# qt-creator +*.config +*.cflags +*.cxxflags +*.creator* +*.files +*.includes diff --git a/NES_webiface/CMakeLists.txt b/NES_webiface/CMakeLists.txt new file mode 100644 index 0000000..730c442 --- /dev/null +++ b/NES_webiface/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.0) +set(PROJ NES_web) +set(MINOR_VERSION "1") +set(MID_VERSION "0") +set(MAJOR_VERSION "0") +set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") + +project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C) + +message("VER: ${VERSION}") + +# default flags +set(CMAKE_C_FLAGS_RELEASE "") +set(CMAKE_C_FLAGS_DEBUG "") +set(CMAKE_C_FLAGS "-O2 -std=gnu99") + +set(CMAKE_COLOR_MAKEFILE ON) + +# here is one of two variants: all .c in directory or .c files in list +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) + +# cmake -DDEBUG=1 -> debugging +if(DEFINED EBUG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wall -Werror -W") + set(CMAKE_BUILD_TYPE DEBUG) + set(CMAKE_VERBOSE_MAKEFILE "ON") + add_definitions(-DEBUG) +else() + set(CMAKE_BUILD_TYPE RELEASE) +endif() + +#pthreads +find_package(Threads REQUIRED) + +###### pkgconfig ###### +# pkg-config modules (for pkg-check-modules) +set(MODULES usefull_macros sqlite3) + +# find packages: +find_package(PkgConfig REQUIRED) +pkg_check_modules(${PROJ} REQUIRED ${MODULES}) + +###### additional flags ###### +list(APPEND ${PROJ}_LIBRARIES "-lonion -lcrypt") + +# change wrong behaviour with install prefix +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local") +else() + message("Change default install path to /usr/local") + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}") + +# exe file +add_executable(${PROJ} ${SOURCES}) +# -I +include_directories(${${PROJ}_INCLUDE_DIRS}) +# -L +link_directories(${${PROJ}_LIBRARY_DIRS}) +# -D +add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" + -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" + -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" + -DMAJOR_VERSION=\"${MAJOR_VESION}\") + +# -l +target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} -lm) + +# Installation of the program +INSTALL(TARGETS ${PROJ} DESTINATION "bin") diff --git a/NES_webiface/HOWTO.cert b/NES_webiface/HOWTO.cert new file mode 100644 index 0000000..8b03d23 --- /dev/null +++ b/NES_webiface/HOWTO.cert @@ -0,0 +1,5 @@ +create: +openssl req -newkey rsa:2048 -nodes -keyout cert.key -x509 -days 365 -out cert.pem + +review: +openssl x509 -text -noout -in cert.pem diff --git a/NES_webiface/README.md b/NES_webiface/README.md new file mode 100644 index 0000000..1c5237e --- /dev/null +++ b/NES_webiface/README.md @@ -0,0 +1,3 @@ +# NES_web + +Web-interface for NES (Nesmith Echelle Spectrograph) management. Based on pusirobot and liboninon. \ No newline at end of file diff --git a/NES_webiface/auth.c b/NES_webiface/auth.c new file mode 100644 index 0000000..bc1ff31 --- /dev/null +++ b/NES_webiface/auth.c @@ -0,0 +1,682 @@ +/* + * This file is part of the NES_web project. + * Copyright 2020 Edward V. Emelianov . + * + * 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 +#include +#include +#include +#include + +#include "auth.h" + +extern char *onion_sessions_generate_id(); +extern void onion_random_init(); + +typedef struct{ + sqlite3 *db; // session database + char *name; // database filename + sqlite3_stmt *add; // template to add/modify session + sqlite3_stmt *getsid; // get data by session ID + sqlite3_stmt *getsockid;// get data by socked ID + sqlite3_stmt *delold; // delete all old sessions (with atime < XX) + sqlite3_stmt *del; // delete session + pthread_mutex_t mutex; // locking mutex +} _sessionDB; + +typedef struct{ + sqlite3 *db; // auth database + char *name; // database filename + sqlite3_stmt *add; // add user data + sqlite3_stmt *get; // get user data by username + sqlite3_stmt *del; // delete user data + pthread_mutex_t mutex; // locking mutex +} _authDB; + +static _sessionDB *sessionDB = NULL; +static _authDB *authDB = NULL; + +/** + * @brief getQdata - find data in POST or GET parts + * @param req - request + * @param key - searching key + * @return value or NULL + */ +const char *getQdata(onion_request *req, const char *key){ + if(!req || !key) return NULL; + printf("key %s, ", key); + const char *data = onion_request_get_query(req, key); + printf("GET: '%s' ", data); + if(!data){ + data = onion_request_get_post(req, key); + if(data && *data == 0) data = NULL; + printf("POST: '%s'", data); + } + printf("\n"); + fflush(stdout); + return data; +} + +/** + * @brief sqprep - prepare SQLite statement + * @param db - database + * @param str - text string for statement + * @param stmt - the statement + * @return 0 if all OK + */ +static int sqprep(sqlite3 *db, const char *str, sqlite3_stmt **stmt){ + int rc = sqlite3_prepare_v2(db, str, (int)strlen(str), stmt, NULL); + if(rc != SQLITE_OK){ + WARNX("Can't prepare statement to save (%d)", rc); + return 1; + } + return 0; +} + +/** + * @brief addtext - bind text phrase to statement + * @param stmt - the statement + * @param narg - argument number for binding + * @param str - the text itself + * @return 0 if all OK + */ +static int addtext(sqlite3_stmt *stmt, int narg, const char *str){ + if(str == NULL) str = ""; + int rc = sqlite3_bind_text(stmt, narg, str, -1, SQLITE_TRANSIENT); + if(rc != SQLITE_OK){ + WARNX("Can't bind %s: %d", str, rc); + return 1; + } + return 0; +} + + +/** + * @brief addWSkey - add new websocket ID to session & send this ID to user + * @param res - web response + * @param session - session information to modify + */ +void addWSkey(onion_response *res, sessinfo *session){ + if(!res || !session) return; + do{ + FREE(session->sockID); + session->sockID = onion_sessions_generate_id(); + DBG("Try to modify session %s", session->sessID); + if(!addSession(session, 1)){ + DBG("Modify session: ID=%s, sockid=%s, atime=%" PRId64 ", user=%s, data=%s\n", + session->sessID, session->sockID, session->atime, session->username, session->data); + break; + } + }while(1); + onion_response_write0(res, session->sockID); +} + +/** + * @brief qookieSession - find session ID from SESSION_COOKIE_NAME and search session in DB + * @param req - web request + * @return session data (allocated here) or NULL if session not found + */ +sessinfo *qookieSession(onion_request *req){ + if(!req) return NULL; + /*const char *path = onion_request_get_path(req); + printf("Ask path %s\n", path);*/ + onion_dict *cookies = onion_request_get_cookies_dict(req); + const char *skey = onion_dict_get(cookies, SESSION_COOKIE_NAME); + if(!skey){ + WARNX("No session cookie found\n"); + return NULL; + } + return getSession(skey); +} + +onion_connection_status auth(_U_ onion_handler *h, onion_request *req, onion_response *res){ + if(!req || !res) return OCS_CLOSE_CONNECTION; + if(onion_request_get_flags(req) & OR_HEAD) { + onion_response_write_headers(res); + return OCS_PROCESSED; + } + const char *host = onion_request_get_client_description(req); + char json[2048]; // json buffer for UA & IP + const char *UA = onion_request_get_header(req, "User-Agent"); + snprintf(json, 2048, "{\"User-Agent\": \"%s\", \"User-IP\": \"%s\"}", UA, host); + DBG("Client: %s, UA: %s\n", host, UA); + const char *logout = getQdata(req, "LogOut"); + DBG("logout=%s\n", logout); + sessinfo *session = qookieSession(req); + if(!session) DBG("No cookie, need to create\n"); + else if(!logout){ + onion_response_write0(res, AUTH_ANS_AUTHOK); + goto closeconn; + } + const char *username = NULL, *passwd = NULL; + if(logout){ + DBG("User logged out\n"); + if(session){ + if(deleteSession(session->sessID)) + WARNX("Can't delete session with ID=%s from database", session->sessID); + } + onion_response_write0(res, AUTH_ANS_LOGOUT); + onion_response_add_cookie(res, SESSION_COOKIE_NAME, "clear", 0, "/", NULL, OC_HTTP_ONLY|OC_SECURE); + goto closeconn; + }else{ // log in + freeSessInfo(&session); + username = getQdata(req, "login"); + if(!username){ + ONION_WARNING("no login field -> need auth"); + onion_response_write0(res, AUTH_ANS_NEEDAUTH); + return OCS_CLOSE_CONNECTION; + } + passwd = getQdata(req, "passwd"); + if(!passwd){ + ONION_WARNING("Trying to enter authenticated area without password"); + onion_response_write0(res, AUTH_ANS_NOPASSWD); + return OCS_FORBIDDEN; + } + } + userinfo *U = getUserData(username); + if(!U){ + WARNX("User %s not found", username); + return OCS_FORBIDDEN; + } + char *pass = strdup(crypt(passwd, "$6$")); + if(!pass){ + WARN("Error in ctypt or strdup"); + freeUserInfo(&U); + return OCS_FORBIDDEN; + } + int comp = strcmp(pass, U->password); + freeUserInfo(&U); + FREE(pass); + if(comp){ + WARNX("User %s give wrong password", username); + return OCS_FORBIDDEN; + } + session = MALLOC(sessinfo, 1); + session->atime = time(NULL); + session->username = strdup(username); +/* + onion_dict *data = onion_dict_new(); + onion_dict_add(data, "UA", UA, 0); + onion_dict_add(data, "IP", host, 0); + onion_block *bl = onion_dict_to_json(data); + if(bl){ + const char *json = onion_block_data(bl); + if(json) session->data = strdup(json); + }*/ + session->data = strdup(json); + do{ + FREE(session->sessID); + session->sessID = onion_sessions_generate_id(); + DBG("Try to add session %s", session->sessID); + if(!addSession(session, 0)){ + DBG("New session: ID=%s, atime=%" PRId64 ", user=%s, data=%s\n", + session->sessID, session->atime, session->username, session->data); + break; + } + sleep(2); + }while(1); + onion_response_add_cookie(res, SESSION_COOKIE_NAME, session->sessID, 366*86400, "/", NULL, OC_HTTP_ONLY|OC_SECURE); + onion_response_write0(res, AUTH_ANS_AUTHOK); +closeconn: + freeSessInfo(&session); + return OCS_CLOSE_CONNECTION; +} + +/** + * @brief opendb - open SQLite database + * @param name - database filename + * @param db - database itself + * @return 0 if all OK + */ +static int opendb(const char *name, sqlite3 **db){ + if(!name || !db) return 2; + if(SQLITE_OK != sqlite3_open(name, db)){ + ONION_ERROR("Can't open database: %s", sqlite3_errmsg(*db)); + sqlite3_close(*db); + return 1; + } + return 0; +} + +/** + * @brief close_sessDB - close session database & free memory + */ +static void close_sessDB(){ + if(!sessionDB) return; + pthread_mutex_lock(&sessionDB->mutex); + sqlite3_finalize(sessionDB->add); + sqlite3_finalize(sessionDB->del); + sqlite3_finalize(sessionDB->getsid); + sqlite3_finalize(sessionDB->getsockid); + sqlite3_close(sessionDB->db); + pthread_mutex_unlock(&sessionDB->mutex); + pthread_mutex_destroy(&sessionDB->mutex); + FREE(sessionDB->name); + FREE(sessionDB); +} +/** + * @brief close_authDB - close user database & free memory + */ +static void close_authDB(){ + if(!authDB) return; + pthread_mutex_lock(&authDB->mutex); + sqlite3_finalize(authDB->add); + sqlite3_finalize(authDB->del); + sqlite3_finalize(authDB->get); + sqlite3_close(authDB->db); + pthread_mutex_unlock(&authDB->mutex); + pthread_mutex_destroy(&authDB->mutex); + FREE(authDB->name); + FREE(authDB); +} + +/** + * @brief closeSQLite - close all databases + */ +void closeSQLite(){ + close_authDB(); + close_sessDB(); +} + +/** + * @brief initSQLite - init SQL databases + * @param auth_filename - database with auth data (username, password hash, access level) + * @param sess_filename - session database (session ID, websosket ID, access time, data {UA, IP, username, access level - JSON}) + * @return error code or 0 if OK + */ +int initSQLite(const char *auth_filename, const char *sess_filename){ + if(!auth_filename || !sess_filename) return 5; + int rc; + char *zErrMsg = NULL; + onion_random_init(); + closeSQLite(); + /******************************* users database *******************************/ + authDB = MALLOC(_authDB, 1); + if(opendb(auth_filename, &authDB->db)) return 2; + authDB->name = strdup(auth_filename); + rc = sqlite3_exec(authDB->db, "CREATE TABLE passwd (user TEXT PRIMARY KEY NOT NULL, passhash TEXT, level INT, comment TEXT)", NULL, NULL, &zErrMsg); + if(rc != SQLITE_OK){ + if(strcmp(zErrMsg, "table passwd already exists")){ + WARNX("SQL CREATE error %d: %s", rc, zErrMsg); + goto ret; + } + }else{ + fprintf(stderr, "Add default user\n"); + // default admin: toor, password: p@ssw0rd + rc = sqlite3_exec(authDB->db, "INSERT INTO passwd VALUES (\"toor\", \"$6$$F55h0JlnNG19zS6YHUDX6h4zksblEBCFRqzCoCt6Y3VqE7zBeM/ifdGbUNMK.dLIwu9jq3fCmLBYEpzEpNCej0\", 0, \"\")", NULL, NULL, &zErrMsg); + if(rc != SQLITE_OK){ + fprintf(stderr, "SQL INSERT error %d: %s\n", rc, zErrMsg); + sqlite3_free(zErrMsg); + goto ret; + } + } + // get user information + if(sqprep(authDB->db, "SELECT passhash, level, comment FROM passwd WHERE user=?", + &authDB->get)) goto ret; + // add or modify user + if(sqprep(authDB->db, "INSERT OR REPLACE INTO passwd (user, passhash, level, comment) VALUES (?, ?, ?, ?)", + &authDB->add)) goto ret; + // delete user + if(sqprep(authDB->db, "DELETE FROM passwd WHERE user=?", &authDB->del)) goto ret; + if(pthread_mutex_init(&authDB->mutex, NULL)){ + WARN("Can't init authDB mutex"); + goto ret; + } + /****************************** session database ******************************/ + sessionDB = MALLOC(_sessionDB, 1); + if(opendb(sess_filename, &sessionDB->db)) goto ret; + sessionDB->name = strdup(sess_filename); + rc = sqlite3_exec(sessionDB->db, "CREATE TABLE sessions (sessID TEXT PRIMARY KEY NOT NULL, sockID TEXT, atime INT, username TEXT, data TEXT)", NULL, NULL, &zErrMsg); + if(rc != SQLITE_OK){ + if(strcmp(zErrMsg, "table sessions already exists")){ + WARNX("SQL CREATE error %d: %s", rc, zErrMsg); + sqlite3_free(zErrMsg); + goto ret; + } + } + // get session information + if(sqprep(sessionDB->db, "SELECT sockID, atime, username, data FROM sessions WHERE sessID=?", + &sessionDB->getsid)) goto ret; + if(sqprep(sessionDB->db, "SELECT sessID, atime, username, data FROM sessions WHERE sockID=?", + &sessionDB->getsockid)) goto ret; + // add or modify session + if(sqprep(sessionDB->db, "INSERT OR REPLACE INTO sessions (sessID, sockID, atime, username, data) VALUES (?, ?, ?, ?, ?)", + &sessionDB->add)) goto ret; + // delete session + if(sqprep(sessionDB->db, "DELETE FROM sessions WHERE sessID=?", + &sessionDB->del)) goto ret; + if(sqprep(sessionDB->db, "DELETE FROM sessions WHERE atimedelold)) goto ret; + return 0; +ret: + closeSQLite(); + return -1; +} + +/** + * @brief getUserData - find all records in authDB for given username + * @param username - user name + * @return userinfo structure allocated here or NULL if user not found + */ +userinfo *getUserData(const char *username){ + if(!username) return NULL; + userinfo *U = NULL; + if(!authDB){ + WARNX("User database not initialized"); + return NULL; + } + pthread_mutex_lock(&authDB->mutex); + sqlite3_reset(authDB->get); + if(addtext(authDB->get, 1, username)) return NULL; + if(SQLITE_ROW != sqlite3_step(authDB->get)){ + WARNX("User %s not found", username); + goto ret; + } + const char *pass = (const char *) sqlite3_column_text(authDB->get, 0); + int levl = sqlite3_column_int(authDB->get, 1); + const char *comment = (const char *) sqlite3_column_text(authDB->get, 2); + U = (userinfo *) malloc(sizeof(userinfo)); + if(!U) goto ret; + U->username = strdup(username); + U->password = strdup(pass); + U->level = levl; + U->comment = strdup(comment); +ret: + pthread_mutex_unlock(&authDB->mutex); + return U; +} + +/** + * @brief freeUserInfo - free memory for struct userinfo + * @param x - pointer to userinfo pointer + */ +void freeUserInfo(userinfo **x){ + if(!x || !*x) return; + userinfo *U = *x; + free(U->username); + free(U->password); + free(U->comment); + free(U); + *x = NULL; +} + +// callback for showAllUsers() +static int selallcb(_U_ void *notused, int argc, char **argv, _U_ char **no){ + if(argc != 4){ + fprintf(stderr, "Wrong argc: %d\n", argc); + return 1; + } + printf("%s\t%s\t%s\t%s\n", argv[0], argv[2], argv[1], argv[3]); + return 0; +} +/** + * @brief showAllUsers - printout user database + */ +void showAllUsers(){ + char *zErrMsg = NULL; + green("\nUSER\tLevel\tPassHash\t\t\tComment\n"); + pthread_mutex_lock(&authDB->mutex); + int rc = sqlite3_exec(authDB->db, "SELECT * FROM passwd GROUP BY user", selallcb, NULL, &zErrMsg); + if(rc != SQLITE_OK){ + WARNX("SQL CREATE error %d: %s", rc, zErrMsg); + sqlite3_free(zErrMsg); + } + pthread_mutex_unlock(&authDB->mutex); +} + +/** + * @brief deleteUser - delete user record from database + * @param username - user name + * @return 0 if all OK + */ +int deleteUser(const char *username){ + if(!username) return 1; + userinfo *u = getUserData(username); + if(!u) return 1; + FREE(u); + pthread_mutex_lock(&authDB->mutex); + sqlite3_reset(authDB->del); + if(addtext(authDB->del, 1, username)) goto reterr; + if(SQLITE_DONE != sqlite3_step(authDB->del)) goto reterr; + else{ + printf("User %s deleted\n", username); + pthread_mutex_unlock(&authDB->mutex); + return 0; + } +reterr: + pthread_mutex_unlock(&authDB->mutex); + WARNX("Can't delete user %s", username); + return 2; +} + +/** + * @brief addUser - add user to database + * @param User - struct with user data + * @return 0 if OK + */ +int addUser(userinfo *User){ + if(!User) return 1; + if(strlen(User->username) < 1){ + WARNX("No username"); + return 1; + } + pthread_mutex_lock(&authDB->mutex); + sqlite3_reset(authDB->add); + if(addtext(authDB->add, 1, User->username)) goto reterr; + if(addtext(authDB->add, 2, User->password)) goto reterr; + if(SQLITE_OK != sqlite3_bind_int(authDB->add, 3, User->level)) goto reterr; + addtext(authDB->add, 4, User->comment); + if(SQLITE_DONE == sqlite3_step(authDB->add)){ + green("Add to database:\n"); + printf("\tuser %s, passHash %s, level %d, comment '%s'\n", + User->username, User->password, User->level, User->comment); + pthread_mutex_unlock(&authDB->mutex); + return 0; + } +reterr: + pthread_mutex_unlock(&authDB->mutex); + WARNX("Can't insert user %s", User->username); + return 1; +} + +/** + * @brief getSession - find session by session ID or socket ID + * @param ID - session or socket ID + * @return NULL if not found or session structure (allocated here) + */ +sessinfo *getSession(const char *ID){ + if(!ID) return NULL; + sqlite3_stmt *stmt = sessionDB->getsid; + sessinfo *s = NULL; + pthread_mutex_lock(&sessionDB->mutex); + sqlite3_reset(stmt); + if(addtext(stmt, 1, ID)) goto ret; + const char *sessID = NULL, *sockID = NULL; + if(SQLITE_ROW != sqlite3_step(stmt)){ + stmt = sessionDB->getsockid; + sqlite3_reset(stmt); + if(addtext(stmt, 1, ID)) goto ret; + if(SQLITE_ROW != sqlite3_step(stmt)){ + WARNX("Session %s not found\n", ID); + goto ret; + }else{ + sockID = ID; + sessID = (const char *) sqlite3_column_text(stmt, 0); + } + }else{ + sessID = ID; + sockID = (const char *) sqlite3_column_text(stmt, 0); + } + // 0-ID, 1-atime, 2-username, 3-data + int64_t atime = sqlite3_column_int64(stmt, 1); + const char *username = (const char *)sqlite3_column_text(stmt, 2); + const char *data = (const char *)sqlite3_column_text(stmt, 3); + s = (sessinfo*) malloc(sizeof(sessinfo)); + if(!s) goto ret; + s->sessID = strdup(sessID); + s->sockID = strdup(sockID); + s->atime = atime; + s->username = strdup(username); + s->data = strdup(data); +ret: + pthread_mutex_unlock(&sessionDB->mutex); + return s; +} + +/** + * @brief addSession - add new session or modify old + * @param s - structure with session information + * @param modify != 0 to modify existant session + * @return 0 if all OK + */ +int addSession(sessinfo *s, int modify){ + if(!s) return 1; + sessinfo *byID = getSession(s->sessID); + sessinfo *bysID = getSession(s->sockID); + if(byID || bysID){ // found session with same IDs + if(modify){ + if(bysID && strcmp(bysID->sessID, s->sessID)){ + freeSessInfo(&byID); + freeSessInfo(&bysID); + WARNX("Found another session with the same sockID"); + return 2; + } + }else{ + if(byID) WARNX("Found another session with the same sessID"); + if(bysID) WARNX("Found another session with the same sockID"); + freeSessInfo(&byID); + freeSessInfo(&bysID); + return 3; + } + } + pthread_mutex_lock(&sessionDB->mutex); + // 1-sessID, 2-sockID, 3-atime, 4-username, 5-data + sqlite3_reset(sessionDB->add); + if(addtext(sessionDB->add, 1, s->sessID)) goto reterr; + if(addtext(sessionDB->add, 2, s->sockID)) goto reterr; + if(SQLITE_OK != sqlite3_bind_int64(sessionDB->add, 3, s->atime)) goto reterr; + if(addtext(sessionDB->add, 4, s->username)) goto reterr; + if(addtext(sessionDB->add, 5, s->data)) goto reterr; + if(SQLITE_DONE == sqlite3_step(sessionDB->add)){ + pthread_mutex_unlock(&sessionDB->mutex); + return 0; + } +reterr: + pthread_mutex_unlock(&sessionDB->mutex); + WARNX("Can't insert session %s\n", s->sessID); + return 1; +} + +/** + * @brief deleteSession - delete session by SessID + * @param sessID - session ID + * @return 0 if all OK + */ +int deleteSession(const char *sessID){ + if(!sessID) return 1; + pthread_mutex_lock(&sessionDB->mutex); + sqlite3_reset(sessionDB->del); + if(addtext(sessionDB->del, 1, sessID)) goto reterr; + if(SQLITE_DONE != sqlite3_step(sessionDB->del)) goto reterr; + else{ + printf("Session with id %s deleted\n", sessID); + pthread_mutex_unlock(&sessionDB->mutex); + return 0; + } +reterr: + pthread_mutex_unlock(&sessionDB->mutex); + WARNX("Can't delete session with ID %s\n", sessID); + return 1; +} + +/** + * @brief deleteSession - delete session by SessID + * @param minatime - minimal access time (all sessions with atimemutex); + sqlite3_reset(sessionDB->delold); + if(SQLITE_OK != sqlite3_bind_int64(sessionDB->delold, 1, minatime)) goto reterr; + if(SQLITE_DONE != sqlite3_step(sessionDB->delold)) goto reterr; + else{ + pthread_mutex_unlock(&sessionDB->mutex); + return 0; + } +reterr: + pthread_mutex_unlock(&sessionDB->mutex); + WARNX("Can't delete sessions with atime < %" PRId64 " \n", minatime); + return 1; +} + +// callback for showAllSessions +static int selallscb(void _U_ *notused, int argc, char **argv, char _U_ **no){ + if(argc != 5){ + fprintf(stderr, "Wrong argc: %d\n", argc); + return 1; + } + printf("%s\t%s\t%s\t%s\t%s\n", argv[3], argv[2], argv[0], argv[1], argv[4]); + return 0; +} +void showAllSessions(){ + char *zErrMsg = NULL; + green("Username\tAtime\tSession ID\t\tSocket ID\t\tData\n"); + int rc = sqlite3_exec(sessionDB->db, "SELECT * FROM sessions ORDER BY username", selallscb, NULL, &zErrMsg); + if(rc != SQLITE_OK){ + WARNX("SQL CREATE error %d: %s\n", rc, zErrMsg); + sqlite3_free(zErrMsg); + } +} + +/** + * @brief freeSessInfo - free session info structure + * @param si - address of pointer to SI + */ +void freeSessInfo(sessinfo **si){ + if(!si || !*si) return; + sessinfo *s = *si; + free(s->data); + free(s->sessID); + free(s->sockID); + free(s->username); + free(*si); + *si = NULL; +} + +/** + * @brief vacuum - rebuild databases to free all deleted records + */ +void vacuumSQLite(){ + char *zErrMsg = NULL; + int rc = sqlite3_exec(authDB->db, "vacuum", NULL, NULL, &zErrMsg); + if(rc != SQLITE_OK){ + WARNX("Error in vacuum %s (%d): %s\n", authDB->name, rc, zErrMsg); + sqlite3_free(zErrMsg); + } + rc = sqlite3_exec(sessionDB->db, "vacuum", NULL, NULL, &zErrMsg); + if(rc != SQLITE_OK){ + WARNX("Error in vacuum %s (%d): %s\n", sessionDB->name, rc, zErrMsg); + sqlite3_free(zErrMsg); + } + green("Both databases are rebuilt\n"); +} + diff --git a/NES_webiface/auth.h b/NES_webiface/auth.h new file mode 100644 index 0000000..f5845ea --- /dev/null +++ b/NES_webiface/auth.h @@ -0,0 +1,70 @@ +/* + * This file is part of the NES_web project. + * Copyright 2020 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef AUTH_H__ +#define AUTH_H__ + +#include + +#define SESSION_COOKIE_NAME "Acookie" + +// standard answers to client +#define AUTH_ANS_NEEDAUTH "NeedAuth" +#define AUTH_ANS_AUTHOK "AuthOK" +#define AUTH_ANS_LOGOUT "LogOut" +#define AUTH_ANS_NOPASSWD "NoPassword" + +typedef struct{ + char *username; // user name + char *password; // password hash (SHA512) + int level; // user access level + char *comment; // optional comment +} userinfo; + +typedef struct{ + char *sessID; // session ID + char *sockID; // websoket ID (cleared when disconnected or after 1 minute after `atime`) + int64_t atime; // last access time (UNIX time) - all ID data cleared after 1yr of inactivity + char *username; // username + char *data; // JSON data with UA, IP etc +} sessinfo; + +const char *getQdata(onion_request *req, const char *key); +onion_connection_status auth(onion_handler *h, onion_request *req, onion_response *res); +void addWSkey(onion_response *res, sessinfo *session); + +int initSQLite(const char *auth_filename, const char *sess_filename); +void closeSQLite(); +void vacuumSQLite(); + +userinfo *getUserData(const char *username); +void freeUserInfo(userinfo **x); +void showAllUsers(); +int deleteUser(const char *username); +int addUser(userinfo *User); + +sessinfo *getSession(const char *ID); +sessinfo *qookieSession(onion_request *req); +int addSession(sessinfo *s, int modify); +int deleteSession(const char *sessID); +int deleteOldSessions(int64_t minatime); +void showAllSessions(); +void freeSessInfo(sessinfo **si); + +#endif // AUTH_H__ diff --git a/NES_webiface/cert.key b/NES_webiface/cert.key new file mode 100644 index 0000000..20c7b37 --- /dev/null +++ b/NES_webiface/cert.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC68f94gnFpcepC ++kpD4bxY7sQAXYbyp5ahAWun4scbLJPjJe56/CE47yvTpdU8KrVfHLYmP8KcF4pM +w0ZUNMCEenkQkb+eWjIviw3wzgesqLSssM+GZzlg92V2xkYlS/hEolUh0Z+Gl1Se +zl9f1S/EhL6RTfQx5ZuGlppHknqtbP0g4QNkxPXGj/qXqmaWLOxb8fX30eNf/6SF +vl1o45xwFE1SSGQnyh8KKZUWAPH2aZWpwwgB8gv4XwaVWo7DirbqOlUhhkylhhl0 +cA7dHFgODTLluRstYr9M5u3WdoJzTGZTvg2ZV1vmfpQMVbat49TVJYZI3iLKoBHQ +IzeSU/lvAgMBAAECggEAUlc40RmTXoBgUHPxtgh9byZrikWnpMWQIQaBJodKb3uo +/8m7Ssw2zd76jNRkIYYmMOhyilJXI21y6vCvz3MUwMU5AcVQgyzzIeG7mC8HTlNY +kR+nqGla6ozNUg1u5AqcJY7itGyiOSP6j6ASfiFmUsatMU8GmduqLxOyjIfGJRA1 +hnNAqYsVv2LbhbUQJTBozYMz2XYfiq/GbzL2NC1tv/xWoSL4zELjDuiGyZcT8oyV +u+dOf0x/VXOLvHqDrA8HuFbUrBjK5z0QW9iYReti/oxuhGrlp2rJLuzivKr4l+v8 +qA0momNxhFFgyyx2Mb5zxp9TwA2x7q5Cg3vOCje3MQKBgQDe6MTywxuO4MVpwDSl +yot8vzUlI7Bv8BtdW18JUc5y1SIyGHlW1cuaQnZW6hbor6UxAoWi7oIjaFX+JQVD +3NLEnjuUF64/Y+k8wtvK0fFeZ2nvx2UNHF50JV0tQZtHo8znwKI/L8ZBSGJcBwh2 +BGXtJtK9AY6C5xv5U03xRukw/QKBgQDWsn0qd6mV6iGqLth6sjDOJt/RFrvcikXb +lMhkCyGPuR2lQsUDbgWMcb/td3CqR5iZAksDbXrPqQBZVm9LO7DfDsL9GxtfgMrt +0dWk0Baod/n4NQBn8A6qGpbheFZwOhNGayYI17IqtRJmfLJuR7gRvm/IfGKso24X +TTA3kpSl2wKBgEBv52cJ8bB213p/fniiuXnhSDqpO3rQXQi6vhlSlaxqYk069/Cb +MxUvu0faua6f/8/QG9OCwQn9QkaKayA3+JGv8CcaRVu7xRO0fJb/45dXq68N4+9L +UR6gInRPr9SgzD3+WKiNZfE/PHe/7Lk5AkHw5CCRD6JVrqd/ZlumFQj9AoGBAKae +fM7xcRYcTyYRFwYZthC3UKmnOAJO+SoRTHd/v/sXUe+IYvdncjztpmK3eCNeTwoo +Imk1lMMGSHQMxXCgkYJ6pU7is5qpjFOGroQqzfrOqZs8HuWLAwZ2fjPbPVH5cC4N +R8ZDB01nmzEYgy1c0XhLz9rK1ZVffDfvOoVWZ7BTAoGAdQUkTWTTHU+Xwc9HshJy +qDFAFu5SvvO2dn3OvbGVUVc+I3M1P+HMeKS3HnomFLtRRDUGEbqZtPfrxBRnduW9 +cEtgO+smyq86vs1p/ohmUESB2ablkvOgwXTXus+NsmUt3uVDq+0lFTfog/DX49+O +EiTvSXB1U92Opv/kh8ZhQak= +-----END PRIVATE KEY----- diff --git a/NES_webiface/cert.pem b/NES_webiface/cert.pem new file mode 100644 index 0000000..0c39286 --- /dev/null +++ b/NES_webiface/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUU3mUrQDJVG5A00d+WvVFfqScrqIwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA3MTMxNjE0MTlaFw0yMTA3 +MTMxNjE0MTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQC68f94gnFpcepC+kpD4bxY7sQAXYbyp5ahAWun4scb +LJPjJe56/CE47yvTpdU8KrVfHLYmP8KcF4pMw0ZUNMCEenkQkb+eWjIviw3wzges +qLSssM+GZzlg92V2xkYlS/hEolUh0Z+Gl1Sezl9f1S/EhL6RTfQx5ZuGlppHknqt +bP0g4QNkxPXGj/qXqmaWLOxb8fX30eNf/6SFvl1o45xwFE1SSGQnyh8KKZUWAPH2 +aZWpwwgB8gv4XwaVWo7DirbqOlUhhkylhhl0cA7dHFgODTLluRstYr9M5u3WdoJz +TGZTvg2ZV1vmfpQMVbat49TVJYZI3iLKoBHQIzeSU/lvAgMBAAGjUzBRMB0GA1Ud +DgQWBBQiHARml0EswF90cHiLYLKxpgj3DjAfBgNVHSMEGDAWgBQiHARml0EswF90 +cHiLYLKxpgj3DjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB4 +mMeSGGA4GAfJnxIwRJWGhj2KoweOh9UOVUtvrEJm3LoURbElo1pV3Q3vzt79wLn5 +JPdXyN/MexYhtq7cwKQps3E4FElmIGUbR4peO/M1z3wJLaXSbaZqeo7ogSW6vzZL +xIHuxLtchNOi4rkSmOhzJqJ23cLULIF7Z94nwxXRxqKaIW8hXB7mlV9cDztpn2sz +K58M9VYmlfZetoHDzmsymM2DTmmoqR28Ykz+wK+G4U6W1+s7sY3Q+mG91lwvpzTf +yvz6dEtqi8ktBdu86E0yHfX04oDyKtl40d0LDzQT5JWhKQFiC25W5tE2p8QLbtHz +D5vg23HjTgRHZ80+3e4J +-----END CERTIFICATE----- diff --git a/NES_webiface/cmdlnopts.c b/NES_webiface/cmdlnopts.c new file mode 100644 index 0000000..d0ba7ed --- /dev/null +++ b/NES_webiface/cmdlnopts.c @@ -0,0 +1,150 @@ +/* + * This file is part of the NES_web project. + * Copyright 2020 Edward V. Emelianov . + * + * 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 "cmdlnopts.h" + +/* + * here are global parameters initialisation + */ +static int help; +// global parameters (init with default): +glob_pars G = { + .port = "8080", + .wsport = "8081", + .certfile = "cert.pem", + .keyfile = "cert.key", +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +static myoption cmdlnopts[] = { +// common options + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"vacuum", NO_ARGS, NULL, 'v', arg_int, APTR(&G.vacuum), _("make \"vacuum\" operation with databases")}, + {"dumpusers", NO_ARGS, NULL, 'U', arg_int, APTR(&G.dumpUserDB),_("dump users database")}, + {"dumpsess", NO_ARGS, NULL, 'S', arg_int, APTR(&G.dumpSessDB),_("dump session database")}, + {"server", NO_ARGS, NULL, 'r', arg_int, APTR(&G.runServer), _("run server process")}, + {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("server port to listen")}, + {"wsport", NEED_ARG, NULL, 'P', arg_string, APTR(&G.wsport), _("websocket port to listen (!= server port!)")}, + {"certfile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.certfile), _("file with SSL certificate")}, + {"keyfile", NEED_ARG, NULL, 'k', arg_string, APTR(&G.keyfile), _("file with SSL key")}, + {"usersdb", NEED_ARG, NULL, 'u', arg_string, APTR(&G.usersdb), _("users database filename")}, + {"sessiondb",NEED_ARG, NULL, 's', arg_string, APTR(&G.sessiondb), _("session database filename")}, + {"userdel", MULT_PAR, NULL, 'd', arg_string, APTR(&G.userdel), _("user to delete (maybe several)")}, + {"useradd", NO_ARGS, NULL, 'a', arg_int, APTR(&G.useradd), _("add user[s] interactively")}, + {"sdatime", NEED_ARG, NULL, 'A', arg_longlong,APTR(&G.delatime), _("minimal atime to delete sessions from DB (-1 for >year)")}, + {"sessdel", NEED_ARG, NULL, 'l', arg_string, APTR(&G.delsession),_("delete session by sessID or sockID")}, + {"logfile", NEED_ARG, NULL, 'L', arg_string, APTR(&G.logfilename),_("log file name")}, + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + int i; + size_t hlen = 1024; + char helpstring[1024], *hptr = helpstring; + snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n"); + // format of help: "Usage: progname [args]\n" + change_helpstring(helpstring); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + G.rest_pars_num = argc; + G.rest_pars = MALLOC(char *, argc); + for (i = 0; i < argc; i++) + G.rest_pars[i] = strdup(argv[i]); + } + return &G; +} + +// array with all opened logs - for error/warning messages +static Cl_log *errlogs = NULL; +static int errlogsnum = 0; + +/** + * @brief Cl_createlog - create log file: init mutex, test file open ability + * @param log - log structure + * @return 0 if all OK + */ +int Cl_createlog(Cl_log *log){ + if(!log || !log->logpath || log->loglevel >= LOGLEVEL_NONE) return 1; + FILE *logfd = fopen(log->logpath, "a"); + if(!logfd){ + WARN("Can't open log file"); + return 2; + } + fclose(logfd); + if(pthread_mutex_init(&log->mutex, NULL)){ + WARN("Can't init log mutes"); + return 3; + } + errlogs = realloc(errlogs, (++errlogsnum) *sizeof(Cl_log)); + if(!errlogs) errlogsnum = 0; + else memcpy(&errlogs[errlogsnum-1], log, sizeof(Cl_log)); + return 0; +} + +/** + * @brief Cl_putlog - put message to log file with/without timestamp + * @param timest - ==1 to put timestamp + * @param log - pointer to log structure + * @param lvl - message loglevel (if lvl > loglevel, message won't be printed) + * @param fmt - format and the rest part of message + * @return amount of symbols saved in file + */ +int Cl_putlogt(int timest, Cl_log *log, Cl_loglevel lvl, const char *fmt, ...){ + if(!log || !log->logpath || log->loglevel >= LOGLEVEL_NONE) return 0; + if(lvl > log->loglevel) return 0; + if(pthread_mutex_lock(&log->mutex)){ + WARN("Can't lock log mutex"); + return 0; + } + int i = 0; + FILE *logfd = fopen(log->logpath, "a"); + if(!logfd) goto rtn; + if(timest){ + char strtm[128]; + time_t t = time(NULL); + struct tm *curtm = localtime(&t); + strftime(strtm, 128, "%Y/%m/%d-%H:%M", curtm); + i = fprintf(logfd, "%s\t", strtm); + } + va_list ar; + va_start(ar, fmt); + i += vfprintf(logfd, fmt, ar); + va_end(ar); + fclose(logfd); +rtn: + pthread_mutex_unlock(&log->mutex); + return i; +} diff --git a/NES_webiface/cmdlnopts.h b/NES_webiface/cmdlnopts.h new file mode 100644 index 0000000..9929efe --- /dev/null +++ b/NES_webiface/cmdlnopts.h @@ -0,0 +1,69 @@ +/* + * This file is part of the NES_web project. + * Copyright 2020 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef CMDLNOPTS_H__ +#define CMDLNOPTS_H__ + +#include + +/* + * here are some typedef's for global data + */ +typedef struct{ + int vacuum; // make "vacuum" operation + int dumpUserDB; // dump users database + int dumpSessDB; // dump session database + int runServer; // run as server + int useradd; // add user[s] + char *port; // server port to listen + char *wsport; // websocket port to listen (!= port !!!) + char *certfile; // file with SSL certificate + char *keyfile; // file with SSL key + char *usersdb; // users database name + char *sessiondb; // session database name + char **userdel; // user names to delete + long long delatime; // minimal atime to delete sessions from DB + char *delsession; // delete session by sessID or sockID + char *logfilename; // name of log file + int rest_pars_num; // number of rest parameters + char** rest_pars; // the rest parameters: array of char* +} glob_pars; + +extern glob_pars G; + +glob_pars *parse_args(int argc, char **argv); + +typedef enum{ + LOGLEVEL_DBG, // all messages + LOGLEVEL_MSG, // all without debug + LOGLEVEL_WARN, // only warnings and errors + LOGLEVEL_ERR, // only errors + LOGLEVEL_NONE // no logs +} Cl_loglevel; + +typedef struct{ + char *logpath; // full path to logfile + Cl_loglevel loglevel; // loglevel + pthread_mutex_t mutex; // log mutex +} Cl_log; + +int Cl_createlog(Cl_log *log); +int Cl_putlogt(int timest, Cl_log *log, Cl_loglevel lvl, const char *fmt, ...); + +#endif // CMDLNOPTS_H__ diff --git a/NES_webiface/main.c b/NES_webiface/main.c new file mode 100644 index 0000000..ba33bc7 --- /dev/null +++ b/NES_webiface/main.c @@ -0,0 +1,250 @@ +/* + * This file is part of the NES_web project. + * Copyright 2020 Edward V. Emelianov . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "auth.h" +#include "cmdlnopts.h" +#include "websockets.h" + +// temporary +#define putlog(...) + +onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_response *res){ + sessinfo *session = qookieSession(req); + if(!session){ + onion_response_write0(res, "NeedAuth"); + ONION_WARNING("try to run command without auth"); + return OCS_FORBIDDEN; + } + const char *path = onion_request_get_path(req); + if(path && *path == 0) path = NULL; + else printf("GET ask path %s\n", path); + const char *param = getQdata(req, "param"); + if(param) printf("User set param=%s\n", param); + if(getQdata(req, "getWSkey")){ + addWSkey(res, session); + } + freeSessInfo(&session); + return OCS_CLOSE_CONNECTION; +} + +static onion *os = NULL, *ow = NULL; +void signals(int signo){ + closeSQLite(); + if(os) onion_free(os); + if(ow) onion_free(ow); + exit(signo); +} + +// POST/GET server +static void *runPostGet(_U_ void *data){ + os = onion_new(O_THREADED); + if(!(onion_flags(os) & O_SSL_AVAILABLE)){ + ONION_ERROR("SSL support is not available"); + signals(1); + } + int error = onion_set_certificate(os, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile); + if(error){ + ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile); + signals(1); + } + onion_set_port(os, G.port); + onion_url *url = onion_root_url(os); + onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static")); + onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL); + onion_url_add(url, "^auth/", auth); + onion_url_add(url, "^get/", get); + error = onion_listen(os); + if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno)); + onion_free(os); + return NULL; +} + +// Websocket server +static void *runWS(_U_ void *data){ + ow = onion_new(O_THREADED); + if(!(onion_flags(ow) & O_SSL_AVAILABLE)){ + ONION_ERROR("SSL support is not available"); + signals(1); + } + int error = onion_set_certificate(ow, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile); + if(error){ + ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile); + signals(1); + } + onion_set_port(ow, G.wsport); + onion_url *url = onion_root_url(ow); + onion_url_add(url, "", websocket_run); + DBG("Listen websocket"); + error = onion_listen(ow); + if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno)); + onion_free(ow); + return NULL; +} + +static void runServer(){ + // if(G.logfilename) Cl_createlog(); + signal(SIGTERM, signals); + signal(SIGINT, signals); + signal(SIGQUIT, signals); + signal(SIGTSTP, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + pthread_t pg_thread, ws_thread; + if(pthread_create(&pg_thread, NULL, runPostGet, NULL)){ + ERR("pthread_create()"); + } + if(pthread_create(&ws_thread, NULL, runWS, NULL)){ + ERR("pthread_create()"); + } + do{ + if(pthread_kill(pg_thread, 0) == ESRCH){ // POST/GET died + WARNX("POST/GET server thread died"); + putlog("POST/GET server thread died"); + pthread_join(pg_thread, NULL); + if(pthread_create(&pg_thread, NULL, runPostGet, NULL)){ + putlog("pthread_create() failed"); + ERR("pthread_create()"); + } + } + if((pthread_kill(pg_thread, 0) == ESRCH) || (pthread_kill(ws_thread, 0) == ESRCH)){ // died + WARNX("Websocket server thread died"); + putlog("Websocket server thread died"); + pthread_join(ws_thread, NULL); + if(pthread_create(&ws_thread, NULL, runWS, NULL)){ + putlog("pthread_create() failed"); + ERR("pthread_create()"); + } + } +#if 0 + usleep(1000); // sleep a little or thread's won't be able to lock mutex + if(dtime() - tgot < T_INTERVAL) continue; + tgot = dtime(); + /* + * INSERT CODE HERE + * Gather data (poll_device) + */ + // copy temporary buffers to main + pthread_mutex_lock(&mutex); + /* + * INSERT CODE HERE + * fill global data buffers + */ + pthread_mutex_unlock(&mutex); +#endif + }while(1); + putlog("Unreaceable code reached!"); + ERRX("Unreaceable code reached!"); +} + +static char *getl(char *msg, int noempty){ + static char *inpstr = NULL; + do{ + FREE(inpstr); + printf("Enter %s: ", msg); + size_t n = 0; + if(getline(&inpstr, &n, stdin) < 0) FREE(inpstr); + else{ + char *nl = strchr(inpstr, '\n'); + if(nl) *nl = 0; + if(strlen(inpstr) < 1 && noempty){ + WARNX("Enter non-empty string"); + }else break; + } + }while(1); + return inpstr; +} + +static void adduser(){ + userinfo *User = MALLOC(userinfo, 1); + char *str; + do{ + str = getl("username", 1); + printf("Username: %s\n", str); + User->username = strdup(str); + do{ + str = getl("password", 1); + if(strlen(str) < 4){ + WARNX("Bad password: not less than 4 symbols"); + }else break; + }while(1); + User->password = strdup(crypt(str, "$6$")); + printf("PassHash: %s\n", User->password); + str = getl("access level", 1); + User->level = atoi(str); + printf("Level: %d\n", User->level); + str = getl("comment", 0); + if(strlen(str)){ + User->comment = strdup(str); + printf("Comment: %s", User->comment); + } + if(addUser(User)) continue; + if(!(str = getl("next? (y/n)", 0)) || 0 != strcmp(str, "y")) break; + }while(1); + freeUserInfo(&User); +} + +extern char *onion_sessions_generate_id(); + +int main(int argc, char **argv){ + initial_setup(); + parse_args(argc, argv); + if(!G.usersdb) ERRX("You should point users database file name"); + if(!G.sessiondb) ERRX("You should point session database file name"); + if(initSQLite(G.usersdb, G.sessiondb)) return 1; + if(G.vacuum){ + printf("VAAAA\n"); + vacuumSQLite(); + } + if(G.dumpUserDB) showAllUsers(); + if(G.userdel){ + for(int i = 0; G.userdel[i]; ++i){ + if(!deleteUser(G.userdel[i])) green("User %s deleted\n", G.userdel[i]); + } + } + if(G.useradd) adduser(); + if(G.dumpSessDB) showAllSessions(); + if(G.delsession){ + if(!deleteSession(G.delsession)) + green("Session with ID %s deleted\n", G.delsession); + } + if(G.delatime){ + if(G.delatime < 0) G.delatime = time(NULL) - 31556926LL; + if(!deleteOldSessions((int64_t)G.delatime)) + green("All sessions with atime<%lld deleted\n", G.delatime); + } + if(G.runServer){ + if(strcmp(G.port, G.wsport) == 0) ERRX("Server port ans websocket port should be different!"); + runServer(); + } + closeSQLite(); + return 0; +} diff --git a/NES_webiface/static/admin.html b/NES_webiface/static/admin.html new file mode 100644 index 0000000..75c6670 --- /dev/null +++ b/NES_webiface/static/admin.html @@ -0,0 +1,156 @@ + + + + + +Управление пользователями + + +
+
Вход
+
Новый пользователь
+
+ +
+ + diff --git a/NES_webiface/static/auth.js b/NES_webiface/static/auth.js new file mode 100644 index 0000000..7aabd33 --- /dev/null +++ b/NES_webiface/static/auth.js @@ -0,0 +1,96 @@ +auth = function(){ + var wsKey = ""; + function _ilogin(){ + $("inout").innerHTML = "Log in"; + $("inout").onclick = auth.login; + } + function _ilogout(){ + $("shadow").style.display = "none"; + $("inout").innerHTML = "Log out"; + $("inout").onclick = auth.logout; + } + function _wsk(request){ + wsKey = request.responseText; + if(wsKey) console.log("Web key received: " + wsKey); + wsinit(); + } + function reqAuth(request){ + var txt = request.responseText; + dbg("received " + txt); + if(txt == "AuthOK"){ // cookies received + sendrequest("get/?getWSkey", _wsk); + _ilogout(); + }else if(txt == "NeedAuth"){ + _ilogin(); + }else{ + parseErr(txt); + } + } + function init1(){ + sendrequest("auth/?check=1", reqAuth); + var l = document.createElement('a'); + l.id = "inout"; + l.href = "#"; + var s1 = document.createElement('style'); + s1.type = 'text/css'; + s1.innerHTML = ".inout{position:absolute;top:0;right:0;background-color:green;" + document.body.appendChild(s1); + l.className = "inout"; + document.body.appendChild(l); + var d = document.createElement('div'); + d.id = "shadow"; + var s = document.createElement('style'); + s.type = 'text/css'; + s.innerHTML = '.shadow{position:absolute;text-align:center;vertical-align:center;top:0;display:none;left:0;width:100%;height:100%;background-color:lightGrey;opacity:0.9;}'; + document.body.appendChild(s); + d.innerHTML = "
Login:
Password:
"; + d.className = "shadow"; + document.body.appendChild(d); + } + function login1(){ + $("shadow").style.display = "block"; + } + function logout1(){ + sendrequest("auth/?LogOut=1", _ilogin); + } + function sendlogpass(){ + $("shadow").style.display = "none"; + var l = $("login").value, p = $("passwd").value; + if(!l || !p){ + parseErr("give login and password"); + return; + } + var str = "auth/?login=" + l + "&passwd=" + p; + sendrequest(str, reqAuth); + } + // websockets + var ws; + function wsinit(){ + delete(ws); + ws = new WebSocket('wss://localhost:8081'); + ws.onopen = function(){ws.send("Akey="+wsKey);}; // send key after init + ws.onclose = function(evt){ + var text = "WebSocket closed: "; + if(evt.wasClean) text += "by remote side"; + else text += "connection lost" + $('wsmsgs').innerHTML = text; + }; + ws.onmessage = function(evt){ + $('wsmsgs').innerHTML = evt.data; + } + ws.onerror = function(err){ + parseErr("WebSocket error " + err.message); + } + } + function wssend(txt){ + ws.send(txt); + } + return{ + init: init1, + login: login1, + logout: logout1, + send: sendlogpass, + wssend: wssend, + wsinit: wsinit + }; +}(); diff --git a/NES_webiface/static/index.html b/NES_webiface/static/index.html new file mode 100644 index 0000000..63170f0 --- /dev/null +++ b/NES_webiface/static/index.html @@ -0,0 +1,16 @@ + + + Index + + + + +

Text +

More text +

+

+
+

+ + + diff --git a/NES_webiface/static/pass.html b/NES_webiface/static/pass.html new file mode 100644 index 0000000..801a152 --- /dev/null +++ b/NES_webiface/static/pass.html @@ -0,0 +1,92 @@ + + +Авторизация + + + +

+

Пожалуйста введите регистрационную информацию для получения доступа к сервисам

+
+
Имя:
+
Пароль:

+
+
+
+
+ + diff --git a/NES_webiface/static/pass.js b/NES_webiface/static/pass.js new file mode 100644 index 0000000..6a592e6 --- /dev/null +++ b/NES_webiface/static/pass.js @@ -0,0 +1,56 @@ +// move this file to the root html directory +// change const's EXURL & PASSURL +var KEY; +const PASSURL="https://ishtar.sao.ru/pass"; +const EXURL = "https://ishtar.sao.ru/cgi-bin/auth"; +function $(id){ + return document.getElementById(id); +} +function checkcookie(){ + var txt = document.cookie; + if(txt.length==0 || txt.indexOf('KEY')<0){ + $("inout").innerHTML = "Войти"; + return 0; + } + else{ + $("inout").innerHTML = "Выйти"; + return 1; + } +} +function getcookie(){ +/* без аргументов - для текущей страницы, + каждый аргумент - доп. "печенька" +*/ + var i, newurl = PASSURL+"?URL="+document.location.href; + for(i = 0; i < getcookie.arguments.length; i++) + newurl += "&URL=" + getcookie.arguments[i]; + if(!checkcookie()) + document.location.href = newurl; +} +function onEX(){ + var d = new Date(); + d.setTime(d.getTime() - 1000); + var str = "KEY=; expires="+d.toGMTString()+"; path="+document.location.pathname; + document.cookie = str; + window.location.reload(); +} +function exit(){ + var request = new XMLHttpRequest(); + request.open("POST", EXURL, true); + request.setRequestHeader("Accept-Charset", "koi8-r"); + request.setRequestHeader("Cookie", document.cookie); + request.overrideMimeType("multipart/form-data; charset=koi8-r"); + request.onreadystatechange=function(){ + if (request.readyState == 4){ + if (request.status == 200){ + onEX(); + } + else alert("Ошибка соединения"); + } + } + request.send("") +} +function inout(){ + if(checkcookie()) exit(); + else getcookie(); +} diff --git a/NES_webiface/static/requests.js b/NES_webiface/static/requests.js new file mode 100644 index 0000000..d42c0ad --- /dev/null +++ b/NES_webiface/static/requests.js @@ -0,0 +1,43 @@ +const Debug = true; + +var elementsCache = {}; +function $(id) { + if (elementsCache[id] === undefined) + elementsCache[id] = document.getElementById(id); + return elementsCache[id]; +} + +function dbg(text){ + if(Debug) console.log("Debug message: " + text); +} + +function sendrequest(req_STR, onOK, postdata){ + var request = new XMLHttpRequest(), timeout_id; + dbg("send request " + req_STR); + var method = postdata ? "POST" : "GET"; + request.open(method, req_STR, true); + //request.setRequestHeader("Accept-Charset", "koi8-r"); + //request.overrideMimeType("multipart/form-data; charset=koi8-r"); + request.onreadystatechange=function(){ + if(request.readyState == 4){ + if(request.status == 200){ + clearTimeout(timeout_id); + if(onOK) onOK(request); + } + else{ + clearTimeout(timeout_id); + parseErr("request sending error"); + } + } + } + request.send(postdata); + timeout_id = setTimeout(function(){request.onreadystatechange=null; request.abort(); parseErr("request timeout");}, 5000); +} + +function parseErr(txt){ + console.log("Error: " + txt); + var msgDiv = $('errmsg'); + if(!msgDiv) return; + msgDiv.innerHTML = "Error: " + txt; + setTimeout(function(){msgDiv.innerHTML = "";}, 3500); +} diff --git a/NES_webiface/websockets.c b/NES_webiface/websockets.c new file mode 100644 index 0000000..af24e8d --- /dev/null +++ b/NES_webiface/websockets.c @@ -0,0 +1,88 @@ +/* + * This file is part of the NES_web project. + * Copyright 2020 Edward V. Emelianov . + * + * 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 "auth.h" +#include "websockets.h" + +#include +#include +#include +#include +#include + +#define BUFLEN 255 + +// bit-fields of `data` field (websocket_cont) +#define WS_FLAG_NOTAUTHORIZED 1 + +static onion_connection_status websocket_cont(void *data, onion_websocket *ws, ssize_t dlen){ + FNAME(); + uint32_t flags = *((uint32_t*)data); + char tmp[BUFLEN+1]; + if(dlen > BUFLEN) dlen = BUFLEN; + + int len = onion_websocket_read(ws, tmp, dlen); + if(len <= 0){ + ONION_ERROR("Error reading data: %d: %s (%d)", errno, strerror(errno), dlen); + return OCS_NEED_MORE_DATA; + } + tmp[len] = 0; + //ONION_INFO("Read from websocket: %s (len=%d)", tmp, len); + DBG("WS: got %s", tmp); + if(flags & WS_FLAG_NOTAUTHORIZED){ // not authorized over websocket + sessinfo *session = NULL; + if(strncmp(tmp, "Akey=", 5) == 0){ // got authorized key - check it + char *key = tmp + 5; + session = getSession(key); + /* here we should make a proper check, but for now do simplest */ + } + if(!session){ + onion_websocket_printf(ws, AUTH_ANS_NEEDAUTH); + WARNX("Wrong websocket session ID"); + return OCS_FORBIDDEN; + } + flags &= ~WS_FLAG_NOTAUTHORIZED; // clear non-authorized flag + return OCS_NEED_MORE_DATA; + } + char *eq = strchr(tmp, '='); + if(eq){ + *eq++ = 0; + onion_websocket_printf(ws, "parameter: '%s', its value: '%s'", tmp, eq); + }else{ + onion_websocket_printf(ws, "Echo: %s", tmp); + } + return OCS_NEED_MORE_DATA; +} + +onion_connection_status websocket_run(_U_ void *data, onion_request *req, onion_response *res){ + FNAME(); + onion_websocket *ws = onion_websocket_new(req, res); + if (!ws){ + green("PROC\n"); + DBG("Processed"); + return OCS_PROCESSED; + } + DBG("WS ready"); + const char *host = onion_request_get_client_description(req); + const char *UA = onion_request_get_header(req, "User-Agent"); + green("Got WS connection from %s (UA: %s)\n", host, UA); + uint32_t *flags = calloc(1, 4); + onion_websocket_set_userdata(ws, (void*)flags, free); + onion_websocket_set_callback(ws, websocket_cont); + return OCS_WEBSOCKET; +} diff --git a/NES_webiface/websockets.h b/NES_webiface/websockets.h new file mode 100644 index 0000000..ade1134 --- /dev/null +++ b/NES_webiface/websockets.h @@ -0,0 +1,26 @@ +/* + * This file is part of the NES_web project. + * Copyright 2020 Edward V. Emelianov . + * + * 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 . + */ +#pragma once +#ifndef WEBSOCKETS_H__ +#define WEBSOCKETS_H__ +#include +#include + +onion_connection_status websocket_run(void *data, onion_request *req, onion_response *res); + +#endif // WEBSOCKETS_H__ diff --git a/README b/README index 6b39c30..3466188 100644 --- a/README +++ b/README @@ -9,6 +9,8 @@ fifo_lifo - simple stack-like queues netdaemon - snippet for network management of serial devices +serialsock - socket server for serial devices + simple_list - 1-directional list with functions: add element; delete list stellarium_emul - snippet for stellarium telescope remote control @@ -23,3 +25,7 @@ usefull_macros - a lot of different macros & functions * MMAP files into memory USBrelay - simplest tool to manage USB-HID chinese relays + + +translate c into asm: gcc -S -fverbose-asm file.c + diff --git a/Socket_CAN/99-socketcan.rules b/Socket_CAN/99-socketcan.rules new file mode 100644 index 0000000..8cdc4f2 --- /dev/null +++ b/Socket_CAN/99-socketcan.rules @@ -0,0 +1 @@ +SUBSYSTEMS=="net", ACTION=="add", ENV{INTERFACE}=="can0", RUN+="/etc/udev/rules.d/socketcan0" diff --git a/Socket_CAN/HOWTO.cert b/Socket_CAN/HOWTO.cert new file mode 100644 index 0000000..95c27e8 --- /dev/null +++ b/Socket_CAN/HOWTO.cert @@ -0,0 +1,5 @@ +create: +openssl req -newkey rsa:2048 -nodes -keyout cert.key -x509 -days 10100 -out cert.pem + +review: +openssl x509 -text -noout -in cert.pem diff --git a/Socket_CAN/Makefile b/Socket_CAN/Makefile new file mode 100644 index 0000000..b8ec511 --- /dev/null +++ b/Socket_CAN/Makefile @@ -0,0 +1,63 @@ +# run `make DEF=...` to add extra defines +CLIENT := soccanclient +SERVER := soccanserver +LDFLAGS += -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lusefull_macros -lssl -lcrypto +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +SOBJDIR := mkserver +COBJDIR := mkclient +CFLAGS += -O2 -Wno-trampolines -std=gnu99 -pthread +COMMSRCS := sslsock.c daemon.c cmdlnopts.c +SSRC := server.c can_io.c $(COMMSRCS) +CSRC := client.c $(COMMSRCS) +SOBJS := $(addprefix $(SOBJDIR)/, $(SSRC:%.c=%.o)) +COBJS := $(addprefix $(COBJDIR)/, $(CSRC:%.c=%.o)) +SDEPS := $(SOBJS:.o=.d) +CDEPS := $(COBJS:.o=.d) +CC = gcc +#CXX = g++ + + +all : $(SOBJDIR) $(COBJDIR) $(CLIENT) $(SERVER) + +debug: DEFINES += -DEBUG -Werror -Wall -Wextra +debug: all + +$(CLIENT) : DEFINES += -DCLIENT +$(CLIENT) : $(COBJS) + @echo -e "\t\tLD $(CLIENT)" + $(CC) $(LDFLAGS) $(COBJS) -o $(CLIENT) + + +$(SERVER) : DEFINES += -DSERVER +$(SERVER) : $(SOBJS) + @echo -e "\t\tLD $(SERVER)" + $(CC) $(LDFLAGS) $(SOBJS) -o $(SERVER) + +$(SOBJDIR): + mkdir $(SOBJDIR) + +$(COBJDIR): + mkdir $(COBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(SDEPS) $(CDEPS) +endif + +$(SOBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< + +$(COBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< + +clean: + @echo -e "\t\tCLEAN" + @rm -f $(SOBJS) $(SDEPS) $(COBJS) $(CDEPS) 2>/dev/null || true + @rmdir $(SOBJDIR) $(COBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/Socket_CAN/Makefile.bk b/Socket_CAN/Makefile.bk new file mode 100644 index 0000000..aa0770b --- /dev/null +++ b/Socket_CAN/Makefile.bk @@ -0,0 +1,62 @@ +# run `make DEF=...` to add extra defines +CLIENT := soccanclient +SERVER := soccanserver +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lusefull_macros +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +OBJDIR := mk +CFLAGS += -O2 -Wno-trampolines -std=gnu99 +SSRC := server.c +CSRC := client.c +COMMSRCS := sslsock.c daemon.c cmdlnopts.c +SOBJS := $(addprefix $(OBJDIR)/, $(SSRC:%.c=%.o)) +COBJS := $(addprefix $(OBJDIR)/, $(CSRC:%.c=%.o)) +COMMOBJS := $(addprefix $(OBJDIR)/, $(COMMSRCS:%.c=%.o)) +OBJS := $(SOBJS) $(COBJS) $(COMMOBJS) +DEPS := $(OBJS:.o=.d) +CC = gcc +#CXX = g++ + + +all : $(OBJDIR) $(CLIENT) $(SERVER) + +debug: DEFINES += -DEBUG -Werror -Wall -Wextra +debug: all + +$(CLIENT) : DEFINES += -DCLIENT +$(CLIENT) : $(COBJS) + @rm $(COMMOBJS) 2>/dev/null || true + @make DEF="$(DEFINES)" $(COMMOBJS) + @echo -e "\t\tLD $(CLIENT)" + $(CC) $(LDFLAGS) $(COBJS) $(COMMOBJS) -o $(CLIENT) + + +$(SERVER) : DEFINES += -DSERVER +$(SERVER) : $(SOBJS) + @rm $(COMMOBJS) 2>/dev/null || true + @make DEF="$(DEFINES)" $(COMMOBJS) + @echo -e "\t\tLD $(SERVER)" + $(CC) $(LDFLAGS) $(SOBJS) $(COMMOBJS) -o $(SERVER) + @rm $(COMMOBJS) + + +$(OBJDIR): + mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< + +clean: + @echo -e "\t\tCLEAN" + @rm -f $(OBJS) $(DEPS) + @rmdir $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/Socket_CAN/README_ports b/Socket_CAN/README_ports new file mode 100644 index 0000000..cde00ab --- /dev/null +++ b/Socket_CAN/README_ports @@ -0,0 +1,7 @@ +server: +socat pty,link=/tmp/ttyX0,waitslave tcp:127.0.0.1:2002 & +ssh -L 2002:robotel1:2000 -N -f robotel1 & + +client: + +socat TCP-LISTEN:2000 PTY,link=/tmp/ttyX0,raw,crnl & diff --git a/Socket_CAN/Readme.md b/Socket_CAN/Readme.md new file mode 100644 index 0000000..3ea65e4 --- /dev/null +++ b/Socket_CAN/Readme.md @@ -0,0 +1,15 @@ +CANbus messages retranslation from BTA bus into Dome bus + +RUN server: + + ./soccanserver -i localhost -l log.log -a ca/ca/ca_cert.pem -c ca/server/server_cert.pem -k ca/server/private/server_key.pem + + +RUN client: + + ./soccanclient -i localhost -a ca/ca/ca_cert.pem -c ca/client/client_cert.pem -k ca/client/private/client_key.pem + + + +Compile i686: +export LDFLAGS=-m32 diff --git a/Socket_CAN/SocketCAN.cflags b/Socket_CAN/SocketCAN.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/Socket_CAN/SocketCAN.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/Socket_CAN/SocketCAN.config b/Socket_CAN/SocketCAN.config new file mode 100644 index 0000000..406d47a --- /dev/null +++ b/Socket_CAN/SocketCAN.config @@ -0,0 +1,6 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 +#define EBUG +#define SERVER +#define CLIENT +#define _GNU_SOURCE diff --git a/Socket_CAN/SocketCAN.creator b/Socket_CAN/SocketCAN.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/Socket_CAN/SocketCAN.creator @@ -0,0 +1 @@ +[General] diff --git a/Socket_CAN/SocketCAN.creator.user b/Socket_CAN/SocketCAN.creator.user new file mode 100644 index 0000000..d64294a --- /dev/null +++ b/Socket_CAN/SocketCAN.creator.user @@ -0,0 +1,159 @@ + + + + + + EnvironmentId + {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + false + false + 1 + true + true + 0 + 8 + true + false + 2 + true + true + true + *.md, *.MD, Makefile + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + true + Builtin.DefaultTidyAndClazy + 4 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {91347f2c-5221-46a7-80b1-0a054ca02f79} + 0 + 0 + 0 + + /home/eddy/Docs/SAO/ELECTRONICS/CAN_controller/Socket_CAN + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Default + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/Socket_CAN/SocketCAN.cxxflags b/Socket_CAN/SocketCAN.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/Socket_CAN/SocketCAN.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/Socket_CAN/SocketCAN.files b/Socket_CAN/SocketCAN.files new file mode 100644 index 0000000..2a21b8b --- /dev/null +++ b/Socket_CAN/SocketCAN.files @@ -0,0 +1,16 @@ +Makefile +can4linux.h +can_io.c +can_io.h +canbus.h +cansock.c +client.c +client.h +cmdlnopts.c +cmdlnopts.h +daemon.c +daemon.h +server.c +server.h +sslsock.c +sslsock.h diff --git a/Socket_CAN/SocketCAN.includes b/Socket_CAN/SocketCAN.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/Socket_CAN/SocketCAN.includes @@ -0,0 +1 @@ +. diff --git a/Socket_CAN/ca/ca/ca_cert.pem b/Socket_CAN/ca/ca/ca_cert.pem new file mode 100644 index 0000000..5030c42 --- /dev/null +++ b/Socket_CAN/ca/ca/ca_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIUV/DCy65P7x+mJpXLG5/WIqplrN0wDQYJKoZIhvcNAQEL +BQAwUDELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNVBAcMBkJ1a292 +bzEQMA4GA1UECgwHU0FPIFJBUzEPMA0GA1UEAwwGc2FvLnJ1MCAXDTIxMTIyMDA4 +MTczOVoYDzIxMjExMjIwMDgxNzM5WjBQMQswCQYDVQQGEwJSVTENMAsGA1UECAwE +S0NoUjEPMA0GA1UEBwwGQnVrb3ZvMRAwDgYDVQQKDAdTQU8gUkFTMQ8wDQYDVQQD +DAZzYW8ucnUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDj+eCtYWLL +GHXbkv3yidLuNEL9CYoJjcEYs1lPKSGypYHUHTQRu+12bRinDCkFBlL9LxJl7PeG +/wek1DXUG6OvOKdB6B4R227YJOUfKSSq15yu5Zwumimki8Z7xCXJdsCWPxoo12aC +u6DUdofaoO/nWhcEnZV98l4H4vZUBCj8wVah5DwLOdvom1duw6oJr6kG7zKFjnpI +0OCbRSUOULJId8hVyXXpUb9TYjPjo/6Btp8XNbnAJ4Q5hk/PYqHidroKJQydFeHI +YC7QPA9EtLNMtCfIlhjjhaAYKTgU3s18BcNuVP90ZJat9lu08qUYHsT6/GoxcRUp +SGQ+Bh4oikBinPUDMUBMlMcWlU7LLSS130mZrFxVdbKIG0sDgsfk/ivjvEFHx52g +CAmp26wzG3u+r0aVFLbyqGwlNgoiggMYfqyJrrhgFvtumuExqts9CtCdbKWlfWx0 +9g27+UuRgWC/+KFU+MVdzd6Ezq+518r3Sx9C20ANS7r+XmFaYkYkv6pCfkMUHOMX +hUvyGpkmqq67zm6MHQi0A/p68sQjcKvSah1zVe/VhNNqkK3GNoiYfH0VKLtad+TJ +tyH+sCe06pGr7sfXxN8AfrHnHrM8mqeERpnwRqmHoZC5let/vM+wKYh2+n0uJ/nT +lJZFRiESAUwfD4orijqgz8tMSOR6nWBKlwIDAQABo1MwUTAdBgNVHQ4EFgQUhe1A +CZbaeGI06X/DOLvvpnFlujwwHwYDVR0jBBgwFoAUhe1ACZbaeGI06X/DOLvvpnFl +ujwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAPDZVdlVYA2zo +j0ZmILvf8LvSWMst7FXVSb+9RMHTwTJmxoN6veIuWoaqv95bN+08At30Ri6U+/p/ +C4YK+JNU/eTKrr0PPt0+Y/eZJbrk2uU1rkCzlJXZacocZpWHYH6y//dRI7ygJigv +mI4ksyQl+Csu4rjYS7BozLJj8r0GwatJaCjq/CAmw1khU/cKWHKFEdircE5puJ5s +dvqc41UoIhS+XGk4ld5gbbk8CjcqEymR3vzYiyCHJK2GG7/mSx9ULpOLXAbfQox4 +IRIfKvkl+/mwqez9pLO6wbY7B8+pexmRzC1gn8ZN8zPgmkJTq/0nQG4wFlkerGOu +Un/E+sEcx++nT38mc77rKsyMQez28PKB1yjkdERYzGFmZtbw2yhHkFZ7z/VCHdDq +IoJTOn9nVKimc2JId3rMSvE9Sfga8+GCmzeJz1MaeR+E5g00B0FlF0KVvFsldsdq +Y221sfKA7aZyiPzad2LGLBGno+inZHykvN+KKmZLA+Qz7D/TQzix5XoBbkCQeVua +5MZdQv7zft1YmbMNr2CFnI5Clibcet/cN5UNlQqBWj76gf6H76eWzMPTiJa5Ago1 +YxGsfSBVdfrzoP48heVEHs8OD4IlbHYtaMLTVFjPNDDDoorOU+ei1vLfyIGMFDFG +jfORPJGT7YvQ0nFHf9V3Bw2ynuaSUzU= +-----END CERTIFICATE----- diff --git a/Socket_CAN/ca/ca/ca_cert.srl b/Socket_CAN/ca/ca/ca_cert.srl new file mode 100644 index 0000000..431283e --- /dev/null +++ b/Socket_CAN/ca/ca/ca_cert.srl @@ -0,0 +1 @@ +1D339878EE448B717AB9855CC9DFBBC66C06FF8D diff --git a/Socket_CAN/ca/ca/private/ca_key.pem b/Socket_CAN/ca/ca/private/ca_key.pem new file mode 100644 index 0000000..8885a34 --- /dev/null +++ b/Socket_CAN/ca/ca/private/ca_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +XiPSjQhMjqLaniianqcVkhCKCicsk0c= +-----END PRIVATE KEY----- diff --git a/Socket_CAN/ca/client/client.csr b/Socket_CAN/ca/client/client.csr new file mode 100644 index 0000000..eeaa14b --- /dev/null +++ b/Socket_CAN/ca/client/client.csr @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEhDCCAmwCAQAwPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNV +BAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBALwxkEemASoXwJu8qEIFhnOqrn50Gj6xI8O4/2RyyB35nisR +3VB24a5cdBvXzCq7PfuVzCtHCnKbLxPJhA5HJAB4MHOyXH97NKp0Ihpp/8A3FWJq +/oNF0QpnBlSKVlQ/5Omh7kWzzqNlQHIIJpgyZZJlePi1RBjCK0h+HZMg+/1jutvt +FRGXOcbRLke8Vm5NSmdPBAYLCzaoByCCqefhxmdiSnUC6TgPqiGgSxQ7SSMB/sdb +ADd9e8pEZRGWMyU2z6X8GtuyRkIKC4txdPYdAxBNSe32nHDOx4RE0GsjSzqCQ2di ++mzh4mbeQGJLQprqNRLbR1moppumBM9qatL9bDr+NIOJVj9y2RqmEbuXLzV8f0vM +v61k89ZlQJro5y+4AffwpKXDrHqXaALhUvD80xZDvTRRZrBTC5cLvCC/Qx5Lq8hI +/Yp/N8E4ZLIJGL0PJ0gKiuUQabLGNydqLxLuDp4nwPvRQdsB/MVErlw1ojA9L0wN +WvxTJl5tCflcIH9imCceuIhGP9+TGuJdknZPk1pl97wfQjWGunbkHWzw20bQXL1f +sqZOYHIaRjX/TRsIo8A6xOo9tM+J8tlkr6Vbf177xv+yxblMOhMnbi77efSKDImj +mqrAm7EzxzOximrAxRjJEANtgfoFCY205cwM8uepnJYoTg4ZY4YLuHX9lInvAgMB +AAGgADANBgkqhkiG9w0BAQsFAAOCAgEAsiA2FfbdcQCFlYhgD2vZZMqOvJD3IfT+ +2Jt1QTfLBiPtqCIa20A6lHCaRQ8FxQ3IbpJpSlpJ9Abj1UqOkNFHKCHIfIbkITr9 +uG/UlWu3dQ0FnmxpM5i6Vm4yEgZymGbNDObmCP50Ai1mtGpFjb3lYIv8sgX/e9+B +d/evTvzH+QjSWGzABLe7uMv94wkjWm+LFSVT9XIQ8FzkoIz17QQTcg8q5ku5qveW +SYmOS3rOxWzh+kYJG8+qkLw9ga6T+/lydzqb6gr7iW2pEUSFCWpwb9v7yFk8uZ6L +NZe3YW6e601uqzPpvH0/aQSIbnh//vVTHxFitA+lCENYgIaz8/TAT3/VQhCiM3OZ +cTEZWIPRtlV84c9kIvxfYTthmS3vnFPSMvI5STUmFA1MSvuxOx1c5BUtdlMv/58x +WL8btQB1oRqt6KuMKnwsUA8nv84MRzkH0tCatmcuAIy2/NXrsbo/F794jwCFJFWp +nEoKFnCIhpFvAYTV70BV0jkAQ3iLV4RpU6LdqdVtzDQbjbFd/bWIjT/A7/xEoMI7 +jaJfPE/1JaDnzFD20WTSXU1SrOcFlDRURrfYRrWMxaqCHBQbp+OwTaa3ut928tZk +Gs/m+Ov6hMNt54tbBmc2Yc42rbBStBbUKjO4YpwMbFm/dyjFZ4dGx/K54XMve7R6 +dFl5goynSwo= +-----END CERTIFICATE REQUEST----- diff --git a/Socket_CAN/ca/client/client_cert.pem b/Socket_CAN/ca/client/client_cert.pem new file mode 100644 index 0000000..07fd6c8 --- /dev/null +++ b/Socket_CAN/ca/client/client_cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFGDCCAwACFB0zmHjuRItxermFXMnfu8ZsBv+NMA0GCSqGSIb3DQEBCwUAMFAx +CzAJBgNVBAYTAlJVMQ0wCwYDVQQIDARLQ2hSMQ8wDQYDVQQHDAZCdWtvdm8xEDAO +BgNVBAoMB1NBTyBSQVMxDzANBgNVBAMMBnNhby5ydTAgFw0yMTEyMjAwODE3NDBa +GA8yMTIxMTIyMDA4MTc0MFowPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIx +DzANBgNVBAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBALwxkEemASoXwJu8qEIFhnOqrn50Gj6xI8O4/2Ry +yB35nisR3VB24a5cdBvXzCq7PfuVzCtHCnKbLxPJhA5HJAB4MHOyXH97NKp0Ihpp +/8A3FWJq/oNF0QpnBlSKVlQ/5Omh7kWzzqNlQHIIJpgyZZJlePi1RBjCK0h+HZMg ++/1jutvtFRGXOcbRLke8Vm5NSmdPBAYLCzaoByCCqefhxmdiSnUC6TgPqiGgSxQ7 +SSMB/sdbADd9e8pEZRGWMyU2z6X8GtuyRkIKC4txdPYdAxBNSe32nHDOx4RE0Gsj +SzqCQ2di+mzh4mbeQGJLQprqNRLbR1moppumBM9qatL9bDr+NIOJVj9y2RqmEbuX +LzV8f0vMv61k89ZlQJro5y+4AffwpKXDrHqXaALhUvD80xZDvTRRZrBTC5cLvCC/ +Qx5Lq8hI/Yp/N8E4ZLIJGL0PJ0gKiuUQabLGNydqLxLuDp4nwPvRQdsB/MVErlw1 +ojA9L0wNWvxTJl5tCflcIH9imCceuIhGP9+TGuJdknZPk1pl97wfQjWGunbkHWzw +20bQXL1fsqZOYHIaRjX/TRsIo8A6xOo9tM+J8tlkr6Vbf177xv+yxblMOhMnbi77 +efSKDImjmqrAm7EzxzOximrAxRjJEANtgfoFCY205cwM8uepnJYoTg4ZY4YLuHX9 +lInvAgMBAAEwDQYJKoZIhvcNAQELBQADggIBALG6oAI97WWjGKVkfZZMDCztpkJw +GQOPArtPqz+rmVPC2YsqAK891TbiJ+vfniQ4u17+oJp+UeeTfBkRsLyHpdIWjqpZ +OG5Q3+fIMvlWKv1Vds76gY6fbnGPdI3rFZASl4qhN85K3a8T7AsB4pkBjJg0W/sO +sgDU3s+8XUsuSQtIhgXS4Rl2upEjrJ1/9/3HOwcsHX7Qe2VgInN8r2vLXz0Tn86A +yB0ZqmhqjTaA6aObd3ncx2m4UD9IU2h1IoDRpmJP9Rfeyovdp1IGS5FQEsrJkxXS +gYCgzsWq1X6u3NF9Rtu4b8tY7BGK11lvds2J8QCJZomW6A+WSNK20ksR044SlSy6 +RvNioaDIkL0mKhMu87DJnB2phDTmF/6SFVQtNt9MWtOuAUujrn9FiNVeBxqnkzP7 +Fq4zMgmZVjKydA5BiR1cXCH/0C+jOPmaGUSvxVLX9B3U81Tj2jBBLh5C4QHy/D8f +DPFN5WOrpbrfH9HOstC0Kk4bLHq4xsFtwEz7N/8aL9ahutejVlaKaviaBA3sJ+uv +oz0cEa8Bn4owDDnGwRPeimxNHd+C8dulUoZQ/ukT8ah6cuo35ykYdOiOgqewTsPT +2J3hJa1DqD9zkH2BzLatHEdZkXpNtgF8RRpDH3m1ZsS4a4ykd03oYEoh3XOFVbwi +myCZgwzp8pk0xfV1 +-----END CERTIFICATE----- diff --git a/Socket_CAN/ca/client/private/client_key.pem b/Socket_CAN/ca/client/private/client_key.pem new file mode 100644 index 0000000..ad1034d --- /dev/null +++ b/Socket_CAN/ca/client/private/client_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/Socket_CAN/ca/gen.sh b/Socket_CAN/ca/gen.sh new file mode 100755 index 0000000..e3fee03 --- /dev/null +++ b/Socket_CAN/ca/gen.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +# https://gist.github.com/zapstar/4b51d7cfa74c7e709fcdaace19233443 +mkdir -p ca/private +chmod 700 ca/private +# NOTE: I'm using -nodes, this means that once anybody gets +# their hands on this particular key, they can become this CA. +openssl req \ + -x509 \ + -nodes \ + -days 36524 \ + -newkey rsa:4096 \ + -keyout ca/private/ca_key.pem \ + -out ca/ca_cert.pem \ + -subj "/C=RU/ST=KChR/L=Bukovo/O=SAO RAS/CN=sao.ru" + +# Create server private key and certificate request +mkdir -p server/private +chmod 700 ca/private +openssl genrsa -out server/private/server_key.pem 4096 +openssl req -new \ + -key server/private/server_key.pem \ + -out server/server.csr \ + -subj "/C=RU/ST=KChR/L=Bukovo/O=SAO RAS" + +# Create client private key and certificate request +mkdir -p client/private +chmod 700 client/private +openssl genrsa -out client/private/client_key.pem 4096 +openssl req -new \ + -key client/private/client_key.pem \ + -out client/client.csr \ + -subj "/C=RU/ST=KChR/L=Bukovo/O=SAO RAS" + +# Generate certificates +openssl x509 -req -days 36524 -in server/server.csr \ + -CA ca/ca_cert.pem -CAkey ca/private/ca_key.pem \ + -CAcreateserial -out server/server_cert.pem +openssl x509 -req -days 36524 -in client/client.csr \ + -CA ca/ca_cert.pem -CAkey ca/private/ca_key.pem \ + -CAcreateserial -out client/client_cert.pem + +# Now test both the server and the client +# On one shell, run the following +# openssl s_server -CAfile ca/ca_cert.pem -cert server/server_cert.pem -key server/private/server_key.pem -Verify 1 +# On another shell, run the following +# openssl s_client -CAfile ca/ca_cert.pem -cert client/client_cert.pem -key client/private/client_key.pem +# Once the negotiation is complete, any line you type is sent over to the other side. +# By line, I mean some text followed by a keyboard return press. diff --git a/Socket_CAN/ca/server/private/server_key.pem b/Socket_CAN/ca/server/private/server_key.pem new file mode 100644 index 0000000..ebc0255 --- /dev/null +++ b/Socket_CAN/ca/server/private/server_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/Socket_CAN/ca/server/server.csr b/Socket_CAN/ca/server/server.csr new file mode 100644 index 0000000..e0d2d20 --- /dev/null +++ b/Socket_CAN/ca/server/server.csr @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEhDCCAmwCAQAwPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNV +BAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAM/Iqcldh+6UfwTJySrY7WpzFlUTq2c0nwTyvRDDP1+R9Ryt +zyOwnkKMRvU4Bs40iftpacwlU4JfQGGeQn1g7507rRlalIxt11D2kB+hHh7V8pS1 +3Y5Y1yOTZlhExdKrs1A3EpaEl4wurF2Rxk6fQMfGoi18Tx/jvjy5diT2LTWv+KOb +xFIJv7o0ikLdvdLFdqobWn4xaYNjP6M7+3IlWCU8N0ickAprKBHOf9jFgE3ABjNu +MYUY1gX9dJbkEbhjrvIcbjC5MmknMoR1HHbG8MHLgEbcbu0ZFgj7pu01QfiByEbL +gDT1xl4pZ8S+6d4q7lPmkwmJzwGJr5ZmCqyT7A4i01AqVRHbezZvR0oV4QusErhK +xvGh7123H/OrSvfwIxEIbWEAIzvWaxV14ctLKOiOw+mENltQxFWziaiGNHXM38SH +QNSmTHJUWC08IUqslR5r1Ys4h15vdJ+ZMVpw1ijNIW0C9zgdJu/ERkaCnVSW5rl3 +gicou4NobomKscHfnyYTiPUWZ+GccnNR35xlSbGSmjzPwYtvvX0UOn8wSp2oCeW6 +PQuWJDpVOlTFgQwJNVT6T9J2XW/wtlNWyZGjM3tATqAlpXTrrapJzTlLuEGpkebK +IMTLnDa/DXemy1A7ehjyKMXfXC4pzwEsR12oPfZ1qyO+lQJPYV0Ur+onSNlZAgMB +AAGgADANBgkqhkiG9w0BAQsFAAOCAgEAAD2CuiczzQnU0+W2lYGuxueCbIkD6ZW0 +li48K4iu5kp2Axv4H/d9o61nHn8E+fqkZLNB10eIut/45yRXUA6cWjBTtMZR1Gcz +BFXnaau1agCnFEqqanU7uBJpflQD+soieICjtQGyrqCTSJlaZy9V/iv5lYCvCn8T +mP9yiUkoAKOnAf/XHLkyBrt7IW2ovT5raMcBj0z/5GNNKouhu2kdWEk/BH9zMvYY +uccf2+YMbtdHDUG+0qlSVAYOyDONbJgHAFfROVdlfN2mVDMkUSnSg9/IE4aevP9s +LuHBzCJzodRqpMuIgV+GtTETzWcZvnaXKLmOvKCnLQtxP2OZYrF4EwxK2SdDPCW+ +YQYu51YF/tBumZbCWrGThg5YYGJfghTaINr8x51j1lQNBTGh1gi3I5633eGtIlpW +HUaY49KSQ+yBefmHMWesC6dbSbHfqvfbugidVAk8eFNS3weo26AJsv3dPNKani/+ +c0bOeEJ4tfIQUEpTRlPC5MWqEk0/wz9XC1hPAYHV4YZExQ+kBQ2xyEa0w/9yLuWG +5sZjd/k/XBnz/V6rrx+jPeqikaaScCGR8YpCwglWEmn6K1Mp6ciSNAFqGTeVnhwD +U0E3INACyi984vDzL886zWt7J1zT0t6IpIaDnxDpmoqvJDK5+xVaw4059HeXwaYb +5zpySGMozeg= +-----END CERTIFICATE REQUEST----- diff --git a/Socket_CAN/ca/server/server_cert.pem b/Socket_CAN/ca/server/server_cert.pem new file mode 100644 index 0000000..76a1e88 --- /dev/null +++ b/Socket_CAN/ca/server/server_cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFGDCCAwACFB0zmHjuRItxermFXMnfu8ZsBv+MMA0GCSqGSIb3DQEBCwUAMFAx +CzAJBgNVBAYTAlJVMQ0wCwYDVQQIDARLQ2hSMQ8wDQYDVQQHDAZCdWtvdm8xEDAO +BgNVBAoMB1NBTyBSQVMxDzANBgNVBAMMBnNhby5ydTAgFw0yMTEyMjAwODE3NDBa +GA8yMTIxMTIyMDA4MTc0MFowPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIx +DzANBgNVBAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAM/Iqcldh+6UfwTJySrY7WpzFlUTq2c0nwTyvRDD +P1+R9RytzyOwnkKMRvU4Bs40iftpacwlU4JfQGGeQn1g7507rRlalIxt11D2kB+h +Hh7V8pS13Y5Y1yOTZlhExdKrs1A3EpaEl4wurF2Rxk6fQMfGoi18Tx/jvjy5diT2 +LTWv+KObxFIJv7o0ikLdvdLFdqobWn4xaYNjP6M7+3IlWCU8N0ickAprKBHOf9jF +gE3ABjNuMYUY1gX9dJbkEbhjrvIcbjC5MmknMoR1HHbG8MHLgEbcbu0ZFgj7pu01 +QfiByEbLgDT1xl4pZ8S+6d4q7lPmkwmJzwGJr5ZmCqyT7A4i01AqVRHbezZvR0oV +4QusErhKxvGh7123H/OrSvfwIxEIbWEAIzvWaxV14ctLKOiOw+mENltQxFWziaiG +NHXM38SHQNSmTHJUWC08IUqslR5r1Ys4h15vdJ+ZMVpw1ijNIW0C9zgdJu/ERkaC +nVSW5rl3gicou4NobomKscHfnyYTiPUWZ+GccnNR35xlSbGSmjzPwYtvvX0UOn8w +Sp2oCeW6PQuWJDpVOlTFgQwJNVT6T9J2XW/wtlNWyZGjM3tATqAlpXTrrapJzTlL +uEGpkebKIMTLnDa/DXemy1A7ehjyKMXfXC4pzwEsR12oPfZ1qyO+lQJPYV0Ur+on +SNlZAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAJH7lEF/tpLN0uOz7pnUdTT2A3IF +0cehqlhv2E8pXLQ7lydAwMuSh582/jCPx9f/aAz11MKfcUwdSBJvdinLvOpM0qCN +0i2aCxHGXnC9kVRsukCkKbF2/+hqoM/Cfmi995f1eEgrk/QkR34HC/2745mh//3S +i69jzwbEGDM7YuGisY25OYSyT37SHR56dU/j1SRw9JgfIEC4DPzS38oqWQfEJjEk +VCg2XrPwUmRk6eLqC80pBL4tmItT1RTERN/0vBNVivHZrseEGWrtMsq52mDEPuVf +mijF6ImVkonKi7ouIrTE/4no3TpNjjUTlMNYumedO38q30/4A0+wjHvsP42b9e5V +bahjejgwlhhbFaEkUpKk9ApcM6Pqq8kURoWgzcmjrUYuXryWAPKWTwE5Y939CgsV +DJXbSkITl61jZQWyAzl5kEUPVguBwJjOyL2zsxbLcHuvIx51RDEa0xl9sKp4kCJR +FoLyexymVyMw8yPenPL0jF7LvY1ua6+60avE7tNnJ0DdWt0DIjEUtqmuQEfBUP0M +n0q/fO9+ZOqfOhuNLPKlw2UFKCw2xd6SU6qm0BFebLiIBkvU4GQ3IWwNGZWw+66J +UzDRHzWiVNED+IBrdGE43+1TdROkKz0HCiIrAnE7gjWeaq2ShGmjaabyYOFE0FoJ +Qx01GFwRqiRjVnId +-----END CERTIFICATE----- diff --git a/Socket_CAN/can4linux.h b/Socket_CAN/can4linux.h new file mode 100644 index 0000000..96e3427 --- /dev/null +++ b/Socket_CAN/can4linux.h @@ -0,0 +1,211 @@ +/* + * can4linux.h - can4linux CAN driver module + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2001 port GmbH Halle/Saale + *------------------------------------------------------------------ + * $Header: /z2/cvsroot/products/0530/software/can4linux/src/can4linux.h,v 1.5 2004/05/14 10:02:54 oe Exp $ + * + *-------------------------------------------------------------------------- + * + * + * modification history + * -------------------- + * $Log: can4linux.h,v $ + * Revision 1.5 2004/05/14 10:02:54 oe + * - started supporting CPC-Card + * - version number in can4linux.h available + * - only one structure type for Config_par_t Command_par_t + * - new ioctl command CMD_CLEARBUFFERS + * + * Revision 1.4 2003/08/27 17:49:04 oe + * - New CanStatusPar structure + * + * Revision 1.3 2002/08/20 05:57:22 oe + * - new write() handling, now not ovrwriting buffer content if buffer fill + * - ioctl() get status returns buffer information + * + * Revision 1.2 2002/08/08 17:50:46 oe + * - MSG_ERR_MASK extended + * + * Revision 1.1 2002/01/10 19:13:19 oe + * - application header file changed name can.h -> can4linux.h + * + * Revision 1.2 2001/09/14 14:58:09 oe + * first free release + * + * Revision 1.1.1.1 2001/06/11 18:30:54 oe + * minimal version can4linux embedded, compile time Konfigurierbar + * + * + * + * + *-------------------------------------------------------------------------- + */ + + +/** +* \file can.h +* \author Heinz-JЭrgen Oertel, port GmbH +* $Revision: 1.5 $ +* $Date: 2004/05/14 10:02:54 $ +* +* can4linux interface definitions +* +* +* +*/ + + +#ifndef __CAN_H +#define __CAN_H + + +#define CAN4LINUXVERSION 0x0301 /*(Version 3.1)*/ + +#ifndef __KERNEL__ +#include +#endif + /*---------- the can message structure */ + +#define CAN_MSG_LENGTH 8 /**< maximum length of a CAN frame */ + + +#define MSG_RTR (1<<0) /**< RTR Message */ +#define MSG_OVR (1<<1) /**< CAN controller Msg overflow error */ +#define MSG_EXT (1<<2) /**< extended message format */ +#define MSG_PASSIVE (1<<4) /**< controller in error passive */ +#define MSG_BUSOFF (1<<5) /**< controller Bus Off */ +#define MSG_ (1<<6) /**< */ +#define MSG_BOVR (1<<7) /**< receive/transmit buffer overflow */ +/** +* mask used for detecting CAN errors in the canmsg_t flags field +*/ +#define MSG_ERR_MASK (MSG_OVR + MSG_PASSIVE + MSG_BUSOFF + MSG_BOVR) + +/** +* The CAN message structure. +* Used for all data transfers between the application and the driver +* using read() or write(). +*/ +typedef struct { + /** flags, indicating or controlling special message properties */ + int flags; + int cob; /**< CAN object number, used in Full CAN */ + unsigned long id; /**< CAN message ID, 4 bytes */ + struct timeval timestamp; /**< time stamp for received messages */ + short int length; /**< number of bytes in the CAN message */ + unsigned char data[CAN_MSG_LENGTH]; /**< data, 0...8 bytes */ +} canmsg_t; + + + +/** +---------- IOCTL requests */ + +#define COMMAND 0 /**< IOCTL command request */ +#define CONFIG 1 /**< IOCTL configuration request */ +#define SEND 2 /**< IOCTL request */ +#define RECEIVE 3 /**< IOCTL request */ +#define CONFIGURERTR 4 /**< IOCTL request */ +#define STATUS 5 /**< IOCTL status request */ + +/*---------- CAN ioctl parameter types */ +/** + IOCTL Command request parameter structure */ +struct Command_par { + int cmd; /**< special driver command */ + int target; /**< special configuration target */ + unsigned long val1; /**< 1. parameter for the target */ + unsigned long val2; /**< 2. parameter for the target */ + int error; /**< return value */ + unsigned long retval; /**< return value */ +}; + + +typedef struct Command_par Command_par_t ; +/** + PSW made them all the same + IOCTL Configuration request parameter structure */ +typedef struct Command_par Config_par_t ; + + +/** + IOCTL generic CAN controller status request parameter structure */ +typedef struct CanStatusPar { + unsigned int baud; /**< actual bit rate */ + unsigned int status; /**< CAN controller status register */ + unsigned int error_warning_limit; /**< the error warning limit */ + unsigned int rx_errors; /**< content of RX error counter */ + unsigned int tx_errors; /**< content of TX error counter */ + unsigned int error_code; /**< content of error code register */ + unsigned int rx_buffer_size; /**< size of rx buffer */ + unsigned int rx_buffer_used; /**< number of messages */ + unsigned int tx_buffer_size; /**< size of tx buffer */ + unsigned int tx_buffer_used; /**< number of messages */ + unsigned long retval; /**< return value */ + unsigned int type; /**< CAN controller / driver type */ +} CanStatusPar_t; + +/** + IOCTL CanStatusPar.type CAN controller hardware chips */ +#define CAN_TYPE_UNSPEC 0 +#define CAN_TYPE_SJA1000 1 +#define CAN_TYPE_FlexCAN 2 +#define CAN_TYPE_TouCAN 3 +#define CAN_TYPE_82527 4 +#define CAN_TYPE_TwinCAN 5 + + +/** + IOCTL Send request parameter structure */ +typedef struct Send_par { + canmsg_t *Tx; /**< CAN message struct */ + int error; /**< return value for errno */ + unsigned long retval; /**< return value */ +} Send_par_t ; + +/** + IOCTL Receive request parameter structure */ +typedef struct Receive_par { + canmsg_t *Rx; /**< CAN message struct */ + int error; /**< return value for errno */ + unsigned long retval; /**< return value */ +} Receive_par_t ; + +/** +IOCTL ConfigureRTR request parameter structure */ +typedef struct ConfigureRTR_par { + unsigned message; /**< CAN message ID */ + canmsg_t *Tx; /**< CAN message struct */ + int error; /**< return value for errno */ + unsigned long retval; /**< return value */ +} ConfigureRTR_par_t ; + +/** +---------- IOCTL Command subcommands and there targets */ + +# define CMD_START 1 +# define CMD_STOP 2 +# define CMD_RESET 3 +# define CMD_CLEARBUFFERS 4 + + + + +/** +---------- IOCTL Configure targets */ + +# define CONF_ACC 0 /* mask and code */ +# define CONF_ACCM 1 /* mask only */ +# define CONF_ACCC 2 /* code only */ +# define CONF_TIMING 3 /* bit timing */ +# define CONF_OMODE 4 /* output control register */ +# define CONF_FILTER 5 +# define CONF_FENABLE 6 +# define CONF_FDISABLE 7 + +#endif /* __CAN_H */ diff --git a/Socket_CAN/can_io.c b/Socket_CAN/can_io.c new file mode 100644 index 0000000..b819f7a --- /dev/null +++ b/Socket_CAN/can_io.c @@ -0,0 +1,545 @@ +/* CAN I/O library (to use as a process) + * usage: + * first: fork() + start_can_io(NULL) - start CAN Rx-buffering process + * then: fork() + Control_1(....) - start process that uses recv/send functions + * ........................... + * then: fork() + Control_N(....) + * + * note: use init_can_io() at the begining of every Control process + * BUT DON't USE it in main() before Control process start + * ^^^^^^^^^^^^^ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "can4linux.h" +#include "can_io.h" + +char can_dev[40] = "/dev/can0"; +int can_fd=-1; +char can_lck[40] = "/tmp/dev_can0.lock"; +int can_lk=-1; +static int server_mode=0; +static int my_uid; + +#define CAN_SHM_SIZE ((sizeof(int)*3)+CAN_CTLR_SIZE+(CAN_RX_SIZE*sizeof(canmsg_t))) + +union ShMkey { + char name[5]; + key_t code; +} can_shm_key; +int can_shm_id=-1; +char *can_shm_addr = NULL; +#define can_pid (*(((int *)can_shm_addr)+0)) /* PID of CAN I/O process */ +#define can_open (*(((int *)can_shm_addr)+1)) /* file descr.of CAN-driver */ +#define rx_buff_pntr (*(((int *)can_shm_addr)+2)) /* from 0 till CAN_RX_SIZE-1 */ +void *can_ctrl_addr = NULL; /* shm area reserved for control process purpose*/ +canmsg_t *rx_buff; /* rx ring buffer: CAN_RX_SIZE*sizeof(canmsg_t)*/ + +struct CMD_Queue { /* описание очереди (канала) команд */ + union { + char name[5]; /* ключ идентефикации очереди */ + key_t code; + } key; + int mode; /* режим доступа (rwxrwxrwx) */ + int side; /* тип подсоединения: Клиент/Сервер (Sender/Receiver)*/ + int id; /* дескриптор подсоединения */ + unsigned int acckey; /* ключ доступа (для передачи Клиент->Сервер) */ +}; +/* канал команд используем для передачи CAN-фреймов */ +static struct CMD_Queue canout = {{{'C','A','N',0,0}},0200,0,-1,0}; + +/* структура сообщения */ +struct my_msgbuf { + long mtype; /* type of message */ + unsigned long acckey; /* ключ доступа клиента */ + unsigned long src_pid; /* номер процесса источника */ + unsigned long src_ip; /* IP-адр. источника, =0 - локальная команда */ + char mtext[100]; /* message text */ +}; + +static void can_abort(int sig); + +void set_server_mode(int mode) {server_mode=mode;} +int can_server() {return(server_mode);} +int can_card() {return(can_fd>0);} +int can_gate() {return(0);} +double can_gate_time_offset() {return(0.0);} + +unsigned long get_acckey() {return(0);} + +static int shm_created=0; + +/* to use _AFTER_ process forking */ +void *init_can_io() { /* returns shared area addr. for client control process*/ + int i; + int new_shm=0; + char *p, msg[100]; + + my_uid=geteuid(); + if(can_shm_addr==NULL) { + if((p=strrchr(can_dev,'/'))!=NULL) { + memcpy(&can_lck[9], p+1, 4); + memcpy(can_shm_key.name, p+1, 4); + can_shm_key.name[4]='\0'; + } else { + fprintf(stderr,"Wrong CAN device name: %s\n", can_dev); + return NULL; + } + can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, 0644); + if(can_shm_id<0 && errno==EACCES) + can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, 0444); + if(can_shm_id<0 && errno==ENOENT && server_mode) { + can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, IPC_CREAT|IPC_EXCL|0644); + new_shm = shm_created = 1; + } + if(can_shm_id<0) { + can_prtime(stderr); + if(new_shm) + sprintf(msg,"Can't create shm CAN buffer '%s'",can_shm_key.name); + else if(server_mode) + sprintf(msg,"CAN-I/O: Can't find shm segment for CAN buffer '%s'",can_shm_key.name); + else + sprintf(msg,"Can't find shm segment for CAN buffer '%s' (maybe no CAN-I/O process?)",can_shm_key.name); + perror(msg); + return NULL; + } + can_shm_addr = shmat(can_shm_id, NULL, 0); + if (can_shm_addr == (void*)-1 && errno==EACCES) + can_shm_addr = shmat(can_shm_id, NULL, SHM_RDONLY); + if (can_shm_addr == (void*)-1) { + sprintf(msg,"Can't attach shm CAN buffer '%s'",can_shm_key.name); + perror(msg); + shmctl(can_shm_id, IPC_RMID, NULL); + return NULL; + } + } + can_ctrl_addr = (canmsg_t *)(can_shm_addr+sizeof(int)*3); + rx_buff = (canmsg_t *)(can_ctrl_addr+CAN_CTLR_SIZE); + + if(can_fd<0 && canout.id<0) { + if(server_mode) { + if(( can_fd = open(can_dev, O_RDWR )) < 0 ) { + sprintf(msg,"CAN-I/O: Error opening CAN device %s", can_dev); + can_prtime(stderr); + perror(msg); + shmctl(can_shm_id, IPC_RMID, NULL); + return NULL; + } + } else { + if((canout.id = msgget(canout.key.code, canout.mode)) < 0) { + sprintf(msg,"Error opening CAN output queue '%s'(maybe no CANqueue server process?) ",canout.key.name); + perror(msg); + } + } + } + if(can_lk>0) close(can_lk); + if(( can_lk = open(can_lck, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH )) < 0 ) { + sprintf(msg,"Error opening CAN device lock-file %s", can_lck); + perror(msg); + shmctl(can_shm_id, IPC_RMID, NULL); + close(can_fd); + return NULL; + } + fchmod(can_lk, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if(new_shm) { + struct timeval tmv; + struct timezone tz; + gettimeofday(&tmv,&tz); + if(flock(can_lk, LOCK_EX)<0) perror("locking CAN"); + can_pid = 0; + can_open = -1; + rx_buff_pntr = 0; + for(i=0; i 0) { + /* work around the timestamp bug in old driver version */ + while((double)rx.timestamp.tv_sec+(double)rx.timestamp.tv_usec/1e6 < (double)tm.tv_sec+(double)tm.tv_usec/1e6) { + rx.timestamp.tv_usec += 10000; + if(rx.timestamp.tv_usec > 1000000) { + rx.timestamp.tv_sec++; + rx.timestamp.tv_usec -= 1000000; + } + } + if(flock(can_lk, LOCK_EX)<0) perror("locking CAN"); + rx_buff[rx_buff_pntr] = rx; + rx_buff_pntr = (rx_buff_pntr + 1) % CAN_RX_SIZE; + if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN"); +//fprintf(stderr,"%d read(id=%02x,len=%d)\n",rx_buff_pntr,rx.id,rx.length);fflush(stderr); + /*fprintf(stderr,"reading CAN: 1 frame\n");*/ + } else { + fprintf(stderr,"reading CAN: nothing\n");fflush(stderr); + } +// } while(n>0); + } +} + +/* put CAN-frame to recv-buffer */ +void can_put_buff_frame(double rtime, int id, int length, unsigned char data[]) { + int i; + canmsg_t rx; + int sec = (int)rtime; + if(!server_mode) return; + if(length<0) length=0; + if(length>8) length=8; + rx.id=id; + rx.length=length; + for(i=0; i0 && can_open>0); +} + + +/* Все нормально с CAN-I/O процессом */ +/* (но надо быть супер-юзером!) */ +int can_io_ok() { + return(can_io_shm_ok() && (my_uid!=0||kill(can_pid, 0)==0)); +} + + +/* Возможна работа c CAN для клиента */ +int can_ok() { + return(can_io_shm_ok()); +} + + +/* wait for CAN-frame */ +int can_wait(int fd, double tout) +{ + int nfd,width; + struct timeval tv; + fd_set readfds; + + if(fd==0 && tout>=0.01) { + double dt = can_dsleep(tout); + if(dt>0.) can_dsleep(dt); + return(0); + } + if(fd<0) fd=can_fd; + if(fd>0) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + width = fd+1; + } else + width = 0; + tv.tv_sec = (int)tout; + tv.tv_usec = (int)((tout - tv.tv_sec)*1000000.+0.9); +slipping: + if(fd>0 && can_fd>0) + nfd = select(width, &readfds, (fd_set *)NULL, (fd_set *)NULL, &tv); + else + nfd = select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, &tv); + if(nfd < 0) { + if(errno == EINTR) + goto slipping; + perror("Error in can_wait(){ select() }"); + return(-1); + } else if(nfd == 0) /* timeout! */ + return(0); + if(fd>0 && FD_ISSET(fd, &readfds)) /* Rx frame! */ + return(1); + return(0); +} + +/* cleanup recv-buffer in client process */ +void can_clean_recv(int *pbuf, double *rtime) { + struct timeval tmv; + struct timezone tz; + gettimeofday(&tmv,&tz); + *pbuf = rx_buff_pntr; + *rtime = tmv.tv_sec + (double)tmv.tv_usec/1000000.; +} + +/* find next rx-frame in recv-buffer for client process */ +int can_recv_frame(int *pbuf, double *rtime, + int *id, int *length, unsigned char data[]) { + return(can_get_buff_frame(pbuf, rtime, id, length, data)); +} +int can_get_buff_frame(int *pbuf, double *rtime, + int *id, int *length, unsigned char data[]) { + while(*pbuf != rx_buff_pntr) { + canmsg_t *rx = &rx_buff[*pbuf]; + struct timeval *tv = &rx->timestamp; + double t_rx; + + if(flock(can_lk, LOCK_EX)<0) perror("locking CAN"); + + t_rx = tv->tv_sec + (double)tv->tv_usec/1000000.; + if(t_rx+1. >= *rtime) { + int i; + *id = rx->id; + *length = rx->length; + for(i = 0; i < *length; i++) + data[i] = rx->data[i]; + *rtime = t_rx; + *pbuf = (*pbuf + 1) % CAN_RX_SIZE; + + if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN"); + return(1); + } + *pbuf = (*pbuf + 1) % CAN_RX_SIZE; + + if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN"); + } + return(0); +} + +/* send tx-frame from client process */ +/* to CAN-driver or to output queue */ +int can_send_frame(int id, int length, unsigned char data[]) { + int i, ret=1; + if(can_fd<0 && canout.id<0) + return(0); + if(length>8) length=8; + if(length<0) length=0; + if(can_fd>=0) { + canmsg_t tx; + tx.id=id; + tx.cob=0; + tx.flags=0; + tx.length=length; + for(i=0;i=0) { + struct my_msgbuf mbuf; + mbuf.src_pid = getpid(); + mbuf.src_ip = 0; + mbuf.acckey = canout.acckey; + mbuf.mtype = id; + for(i=0;i=0) close(can_fd); + can_prtime(stderr); + fprintf(stderr,"%s process stop!\n",ss); + fflush(stderr); + close(can_lk); + shmdt(can_shm_addr); + shmctl(can_shm_id, IPC_STAT, &buf); + if(buf.shm_nattch==0) + shmctl(can_shm_id, IPC_RMID, NULL); + exit(sig); + } +} + +char *time2asc(double t) +{ + static char stmp[10][20]; + static int itmp=0; + char *lin = stmp[itmp]; + int h, min; + double sec; + h = (int)(t/3600.); + min = (int)((t - (double)h*3600.)/60.); + sec = t - (double)h*3600. - (double)min*60.; + h %= 24; + sprintf(lin, "%02d:%02d:%09.6f", h,min,sec); + itmp = (itmp+1)%10; + return lin; +} + +double can_dsleep(double dt) { + struct timespec ts,tsr; + ts.tv_sec = (time_t)dt; + ts.tv_nsec = (long)((dt-ts.tv_sec)*1e9); + nanosleep(&ts,&tsr); + return((double)ts.tv_sec + (double)ts.tv_nsec/1e9); +} + +double can_dtime() { + struct timeval ct; + struct timezone tz; + gettimeofday(&ct, &tz); + return ((double)ct.tv_sec + (double)ct.tv_usec/1e6); +} + +char *can_atime() {return(time2asc(can_dtime()));} + +void can_prtime(FILE *fd) { + static double otime=0.0; + double ntime=can_dtime(); + time_t itime = (int)ntime; + if(otime==0.0) tzset(); + ntime -= (double)timezone; + if((((int)ntime)%(24*3600) < ((int)otime)%(24*3600)) || otime==0.0) + fprintf(fd,"========================\n%s",ctime(&itime)); + fprintf(fd,"%s ",time2asc(ntime)); + otime=ntime; +} diff --git a/Socket_CAN/can_io.h b/Socket_CAN/can_io.h new file mode 100644 index 0000000..de3e2f9 --- /dev/null +++ b/Socket_CAN/can_io.h @@ -0,0 +1,29 @@ +#define CAN_CTLR_SIZE 300 /* size of client process shared area */ +#define CAN_RX_SIZE 1000 /* max. # frames in Rx-buffer */ + +int can_wait(int fd, double tout); +#define can_delay(Tout) can_wait(0, Tout) +void set_server_mode(int mode); +int can_server(); +int can_card(); +int can_gate(); +double can_gate_time_offset(); +unsigned long get_acckey(); +void *init_can_io(); +void *start_can_io(); +void can_put_buff_frame(double rtime, int id, int length, unsigned char data[]); +int can_io_ok(); +int can_io_shm_ok(); +int can_ok(); +void can_clean_recv(int *pbuf, double *rtime); +int can_get_buff_frame(int *pbuf, double *rtime, + int *id, int *length, unsigned char data[]); +int can_recv_frame(int *pbuf, double *rtime, + int *id, int *length, unsigned char data[]); +int can_send_frame(int id, int length, unsigned char data[]); +void can_exit(int sig); +char *time2asc(double t); +double can_dsleep(double dt); +double can_dtime(); +char *can_atime(); +void can_prtime(FILE *fd); diff --git a/Socket_CAN/canbus.h b/Socket_CAN/canbus.h new file mode 100644 index 0000000..80dfcbb --- /dev/null +++ b/Socket_CAN/canbus.h @@ -0,0 +1,39 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef CANBUS_H__ +#define CANBUS_H__ + +#include + +// CAN packet MAGICK +#define CANMAGICK (0xdeadbeef) +// BTA SEW mask +#define ID_MASK (0xD80) +// BTA SEW-dome frames +#define ID_DOME (0x580) + +typedef struct{ + uint32_t magick; + uint16_t ID; + uint8_t len; + uint8_t data[8]; +} can_packet; + +#endif // CANBUS_H__ diff --git a/Socket_CAN/cansock.c b/Socket_CAN/cansock.c new file mode 100644 index 0000000..84fc7c4 --- /dev/null +++ b/Socket_CAN/cansock.c @@ -0,0 +1,76 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + + +int readCAN(int sock, struct can_frame *frame){ + fd_set fds; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 1000; // wait not more than 1millisecond + FD_ZERO(&fds); + FD_SET(sock, &fds); + do{ + int rc = select(sock+1, &fds, NULL, NULL, &timeout); + if(rc < 0){ + if(errno != EINTR){ + WARN("select()"); + return 0; + } + continue; + } + break; + }while(1); + if(FD_ISSET(sock, &fds)){ + return read(sock, frame, sizeof(struct can_frame)); + } + return 0; +} + + + +int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); +if(sock < 0) ERR("Can't create socket"); +struct sockaddr_can addr; +struct ifreq ifr; +strcpy(ifr.ifr_name, "can0"); +if(ioctl(sock, SIOCGIFINDEX, &ifr) < 0) ERR("ioctl()"); +addr.can_family = AF_CAN; +addr.can_ifindex = ifr.ifr_ifindex; +if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) ERR("bind()"); +int nbytes = 0; +struct can_frame frame = {0}; +double dt = dtime(); +do{ + nbytes = readCAN(sock, &frame); + if(nbytes < 0) ERR("can raw socket read"); +}while(nbytes == 0 && dtime() - dt < 10.); +printf("got frame len=%d id=%d", frame.can_dlc, frame.can_id); +if(frame.can_dlc) printf(", data: "); +for(int i = 0; i < frame.can_dlc; ++i) + printf("0x%02x ", frame.data[i]); +printf("\n"); +frame.can_dlc = 6; +frame.can_id = 0xaa; +const uint8_t d[] = {1, 2, 3, 4, 5, 6, 0, 0}; +memcpy(&frame.data, d, 8); +int n = write(sock, &frame, sizeof(struct can_frame)); +if(sizeof(struct can_frame) != n){ + printf("n=%d\n", n); + WARN("write()"); +} +close(sock); diff --git a/Socket_CAN/cert.key b/Socket_CAN/cert.key new file mode 100644 index 0000000..c7cd3b1 --- /dev/null +++ b/Socket_CAN/cert.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCocrggX/uMcOfG +bVqeVZpqzX/RPl0YZf9R5ym0YhCeZPj4Ribl59vLsjFShR0PtwV3pCffZUazQJHs +yA/UxrIG3JN9sk5B3+SdBUdmFDXcjI/vTYAFjqa6cqQ7GqQENNSKnvIW2PWe+TS8 +KepPQKEqdXLb2k7MTTLx7sFeS3RyNHnQ9V1Ef7ZTjmYGrGe6QQA1irf3QgZ8c47Q +oIUQBEIj1JFyXhChU15BRwVEcAGDrcp8QCYjSba8ac+eBDmn/c8Ojrw3MIsgB38q +I18xXIfuf4jSN+hZjMktFr/IZwePOUFqQjSNkMlUsZvUdkFWfEoEamP3b5Pft1IC +okeg4+rDAgMBAAECggEAYHRoOKmdadrQ7Q97H0UXSeboNNHIDwuAus5qVA7/QyPA +aLIK3gT5F9euZHkynegIKm0GI+ZjKItlw7b/UbCBCmrNzeEG7LOevcbjDnGuMd8P +IFE5aHU5xXOV1P45QutZdRL0vt53LxO1/bTmpYD1iFF+dqO4EUZ3UI+NjEgaorW5 +x9kZwV6PE4Cnfcx8kdDNvBrQDs/AXJz3zNNsIU7cEFAVLvtkDnw5+aa0O52lLa+D +2vtpctxCyPrKtV5Z7Cgd+KkvXKDYbSgrld/Pb6Bhwj+cD39s5Jd/JRBnhpprHq5Y +X8nCs0rZP901rTbGPRcOAu1qUrgdtEyFyiAMnCBGWQKBgQDWQGD87w2E7cbNxZAp +nYBc4LizChtvFRu5hDmbblhvRt93XWh4H5wrJuKbFBM/O38KHy/9GCEVv8XwCkgd +DKhNlAPwMkszxYkVw4cmc3FwKEQhfX4KpzwfrWFbPFTGlCtt3EAP5g6wvXYJeWZZ +T+KXCOzWG5b+v38LLZzxctPp7QKBgQDJRYHKK4txrbXIfqSILk1k9C3+8H6lPFfk +S0gemPP7TuE2jHH/OsK36qtgH+i6pWrqZVKL9QmpFugiSvI/SP0DWk7t17iFnJ6s +bltn8gftASRnx8yFVZBYUO9TbmriS0ZRJG8Yz7f79jj2TGDunBVgeu560X6Syjyk +WpzEjEPRbwKBgFL2MNggNOrxK5cIVi9XFppgRgTF+COGV+r5IVlnXAUSu3s8BzTk +gJNRBlQobN+CSUoBE5L8YetLC/lL8eqVuSH5G6FJyEbuyYtM4CtqblWQsfkJ3+F+ +KlDV0SoD7YvLWhm7PG8rlSqo4mj+wjv5K/Nx+Etb+ZcBTc9lRS1VWmttAoGBAMSC +GnA7B5Bb26n/C8DyBBpW1TmdsOi++8knPyiwiTWKFBTgFsTsqARNGDlnrh/dNX/Y +oTmIaoAun0IsDkx/hJfrajiJb9zzx2/u50ubYOWjQdoZlrNvkNjJXIEGw1Bh0iuS ++O0ukSFtirveYp1UwwJJw+Eh/QRwc7i7x2eW0vf9AoGAVPIZuGkwafu4yfsKBKqx +Qvmgm7+iCywBz6jj70Zk1uS2+l/Kvc1sFD3vF9QS6LzxWgotMqqTR+JTLqMhw/SW +nTSoickIlkchWWnOF0tg1Gx9qHj9iTXufSwAqtYa18Srf/lQKB/HM24BSugGAxK/ +W+UrzBJIZnVyR/HkDSk5VWY= +-----END PRIVATE KEY----- diff --git a/Socket_CAN/cert.pem b/Socket_CAN/cert.pem new file mode 100644 index 0000000..e63771d --- /dev/null +++ b/Socket_CAN/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgIUBo309dQtnbIqVJIYQ0929yXmRB0wDQYJKoZIhvcNAQEL +BQAwPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNVBAcMBkJ1a292 +bzEQMA4GA1UECgwHU0FPIFJBUzAeFw0yMTEyMTYwODA3NDNaFw00OTA4MTEwODA3 +NDNaMD8xCzAJBgNVBAYTAlJVMQ0wCwYDVQQIDARLQ2hSMQ8wDQYDVQQHDAZCdWtv +dm8xEDAOBgNVBAoMB1NBTyBSQVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCocrggX/uMcOfGbVqeVZpqzX/RPl0YZf9R5ym0YhCeZPj4Ribl59vLsjFS +hR0PtwV3pCffZUazQJHsyA/UxrIG3JN9sk5B3+SdBUdmFDXcjI/vTYAFjqa6cqQ7 +GqQENNSKnvIW2PWe+TS8KepPQKEqdXLb2k7MTTLx7sFeS3RyNHnQ9V1Ef7ZTjmYG +rGe6QQA1irf3QgZ8c47QoIUQBEIj1JFyXhChU15BRwVEcAGDrcp8QCYjSba8ac+e +BDmn/c8Ojrw3MIsgB38qI18xXIfuf4jSN+hZjMktFr/IZwePOUFqQjSNkMlUsZvU +dkFWfEoEamP3b5Pft1ICokeg4+rDAgMBAAGjUzBRMB0GA1UdDgQWBBRqwlIH5zOT +171ywLEMmP1WfJ3ZdjAfBgNVHSMEGDAWgBRqwlIH5zOT171ywLEMmP1WfJ3ZdjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAwgUv4mlGxwqmyQsTg +LHVppuLl4TA1EMrUuvJfSBClfStnWiCDJmNMn7OMAuNVmpLZHz2gPbAz5fHRnm0N +Ey3nQNS0X18/jq4NCjaONoSjcRxHDbw0V0E2Kap+t9YZbzw83FWKcJM3WKMAL51p +xiUWQ9bP5E7CFY+j+kcty3HfSgoGcEqanTK94QQqlKxczI1kKkR/Bdow3jTk6PG7 +fMPU6c8FL3NkRacKGoc5VNq8A7HI5Hll4gu1fYxXVYnkKc45NrLsipdhInFNJOmx +O16CWm+S/YEJFVio6Eke9FhLehaHagY0oMhadaV4sxtTDXGosfoI/XorGsYE79sU +uqgK +-----END CERTIFICATE----- diff --git a/Socket_CAN/client.c b/Socket_CAN/client.c new file mode 100644 index 0000000..f74be2f --- /dev/null +++ b/Socket_CAN/client.c @@ -0,0 +1,43 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 +#include +#include + +#include "cmdlnopts.h" +#include "daemon.h" +#include "sslsock.h" + + +int main(int argc, char **argv){ + char *self = argv[0]; + initial_setup(); + parse_args(argc, argv); + // check args + if(!G.serverhost) ERR("Point server IP address"); +#ifdef EBUG + printf("Server: %s\n", G.serverhost); +#endif + return start_daemon(self); +} diff --git a/Socket_CAN/client.h b/Socket_CAN/client.h new file mode 100644 index 0000000..b40cd07 --- /dev/null +++ b/Socket_CAN/client.h @@ -0,0 +1,24 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ +#pragma once +#ifndef CLIENT_H__ +#define CLIENT_H__ + +#include "canbus.h" + +#endif // CLIENT_H__ diff --git a/Socket_CAN/cmdlnopts.c b/Socket_CAN/cmdlnopts.c new file mode 100644 index 0000000..25f8f8b --- /dev/null +++ b/Socket_CAN/cmdlnopts.c @@ -0,0 +1,89 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 "cmdlnopts.h" + + +/* + * here are global parameters initialisation + */ +static int help; +glob_pars G; + +// DEFAULTS +// default global parameters +static glob_pars const Gdefault = { + .pidfile = DEFAULT_PIDFILE, + .port = DEFAULT_PORT, + .cert = "cert.pem", + .key = "cert.key", +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +static myoption cmdlnopts[] = { +// common options + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs")}, + {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, + {"certificate",NEED_ARG,NULL, 'c', arg_string, APTR(&G.cert), _("path to SSL sertificate (default: cert.pem)")}, + {"key", NEED_ARG, NULL, 'k', arg_string, APTR(&G.key), _("path to SSL key (default: cert.key)")}, + {"ca", NEED_ARG, NULL, 'a', arg_string, APTR(&G.ca), _("path to SSL ca (default: ca.pem)")}, + {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("port to open (default: " DEFAULT_PORT ")")}, + {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), _("increase log verbose level (default: LOG_WARN)")}, +#ifdef SERVER +#endif +#ifdef CLIENT + {"server", NEED_ARG, NULL, 'i', arg_string, APTR(&G.serverhost), _("server IP address")}, +#endif + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + */ +void parse_args(int argc, char **argv){ + int i; + void *ptr; + ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); + size_t hlen = 1024; + char helpstring[1024], *hptr = helpstring; + snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n"); + // format of help: "Usage: progname [args]\n" + change_helpstring(helpstring); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + red("Ignored options:\n"); + for (i = 0; i < argc; i++) + printf("\t%s\n", argv[i]); + } +} + diff --git a/Socket_CAN/cmdlnopts.h b/Socket_CAN/cmdlnopts.h new file mode 100644 index 0000000..017d5e1 --- /dev/null +++ b/Socket_CAN/cmdlnopts.h @@ -0,0 +1,54 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef CMDLNOPTS_H__ +#define CMDLNOPTS_H__ + +// default PID filename: +#ifndef DEFAULT_PIDFILE +#define DEFAULT_PIDFILE "/tmp/soccan.pid" +#endif +#ifndef DEFAULT_PORT +#define DEFAULT_PORT "4444" +#endif + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *pidfile; // name of PID file + char *logfile; // logging to this file + char *cert; // sertificate + char *key; // key + char *ca; // ca + char *port; // port number + int verbose; // logfile verbose level +#ifdef SERVER +#endif +#ifdef CLIENT + char *serverhost; // server IP address + int speed; // connection speed +#endif +} glob_pars; + +extern glob_pars G; + +void parse_args(int argc, char **argv); + +#endif // CMDLNOPTS_H__ diff --git a/Socket_CAN/daemon.c b/Socket_CAN/daemon.c new file mode 100644 index 0000000..16bfdbd --- /dev/null +++ b/Socket_CAN/daemon.c @@ -0,0 +1,99 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 // prctl +#include // wait +#include +#include + +#include "cmdlnopts.h" +#include "daemon.h" +#include "sslsock.h" + +static pid_t childpid = 0; + +void signals(int sig){ + if(childpid == 0){ + LOGWARN("Child killed with sig=%d", sig); + exit(sig); // slave process + } + // master process + if(sig){ + signal(sig, SIG_IGN); + LOGERR("Exit with signal %d", sig); + }else LOGERR("Exit"); + if(G.pidfile) unlink(G.pidfile); + exit(sig); +} + +/** + * @brief start_daemon - daemonize + * @param self - self name of process + * @return error code or 0 + */ +int start_daemon(_U_ char *self){ + // check args + int port = atoi(G.port); + if(port < 1024 || port > 65535){ + LOGERR("Wrong port value: %d", port); + return 1; + } + FILE *f = fopen(G.cert, "r"); + if(!f) ERR("Can't open certificate file %s", G.cert); + fclose(f); + f = fopen(G.key, "r"); + if(!f) ERR("Can't open certificate key file %s", G.key); + fclose(f); +#ifdef EBUG + printf("cert: %s, key: %s\n", G.cert, G.key); +#endif + if(G.logfile){ + int lvl = LOGLEVEL_WARN + G.verbose; + DBG("level = %d", lvl); + if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY; + green("Log file %s @ level %d\n", G.logfile, lvl); + OPENLOG(G.logfile, lvl, 1); + } + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z +#ifdef SERVER + check4running(self, G.pidfile); +#endif + LOGMSG("Started"); +#ifndef EBUG + while(1){ + childpid = fork(); + if(childpid){ // master + LOGMSG("Created child with pid %d", childpid); + wait(NULL); + LOGWARN("Child %d died", childpid); + sleep(1); // wait a little before respawn + }else{ // slave + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + break; + } + } +#endif + // parent should never reach this part of code + return open_socket(); +} diff --git a/Socket_CAN/daemon.h b/Socket_CAN/daemon.h new file mode 100644 index 0000000..18a3155 --- /dev/null +++ b/Socket_CAN/daemon.h @@ -0,0 +1,25 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef DAEMON_H__ +#define DAEMON_H__ + +int start_daemon(char *self); + +#endif // DAEMON_H__ diff --git a/Socket_CAN/diff/clientscript b/Socket_CAN/diff/clientscript new file mode 100755 index 0000000..5e5591d --- /dev/null +++ b/Socket_CAN/diff/clientscript @@ -0,0 +1,5 @@ +#!/bin/bash + +for ((x=0; x<10; ++x)); do + socat TCP-LISTEN:2000 PTY,link=/tmp/ttyX0,raw,crnl +done \ No newline at end of file diff --git a/Socket_CAN/diff/serverscript b/Socket_CAN/diff/serverscript new file mode 100755 index 0000000..ecef101 --- /dev/null +++ b/Socket_CAN/diff/serverscript @@ -0,0 +1,6 @@ +#!/bin/bash + +#for ((x=0; x<10; ++x)); do + socat pty,link=/tmp/ttyX0,waitslave tcp:127.0.0.1:2002 & + ssh -L 2002:robotel1:2000 -N -f robotel1 +#done \ No newline at end of file diff --git a/Socket_CAN/log.log b/Socket_CAN/log.log new file mode 100644 index 0000000..d362162 --- /dev/null +++ b/Socket_CAN/log.log @@ -0,0 +1,121 @@ +[ERR] 2021/12/20-09:51:56 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-09:51:57 SSL_accept() +[WARN] 2021/12/20-09:51:57 Child killed with sig=9 +[ERR] 2021/12/20-09:54:18 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-09:54:19 SSL_accept() +[ERR] 2021/12/20-09:54:33 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-09:54:39 Child killed with sig=2 +[ERR] 2021/12/20-09:55:27 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-09:55:30 Child killed with sig=2 +[ERR] 2021/12/20-10:00:07 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-10:00:14 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-10:00:17 Child killed with sig=2 +[WARN] 2021/12/20-11:30:15 Could not set the CA file location +[WARN] 2021/12/20-11:30:15 Child killed with sig=9 +[ERR] 2021/12/20-11:31:48 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-11:31:48 SSL_accept() +[ERR] 2021/12/20-11:32:38 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-11:32:38 SSL_accept() +[WARN] 2021/12/20-11:33:54 Child killed with sig=2 +[ERR] 2021/12/20-11:33:57 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-11:33:57 SSL_accept() +[WARN] 2021/12/20-11:37:21 Child killed with sig=2 +[ERR] 2021/12/20-11:37:24 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-11:37:40 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-11:37:42 Child killed with sig=2 +[ERR] 2021/12/20-11:38:03 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-11:38:03 SSL_accept() +[WARN] 2021/12/20-11:38:14 Child killed with sig=2 +[ERR] 2021/12/20-14:44:30 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-14:44:30 SSL_accept() +[WARN] 2021/12/20-14:44:30 Child killed with sig=9 +[ERR] 2021/12/20-14:44:38 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-14:44:38 SSL_accept() +[WARN] 2021/12/20-14:44:38 Child killed with sig=9 +[ERR] 2021/12/20-14:44:53 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-14:45:16 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-14:46:13 Child killed with sig=2 +[ERR] 2021/12/20-14:46:20 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-14:47:49 Child killed with sig=2 +[ERR] 2021/12/20-14:47:52 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-14:50:22 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-14:50:24 Child killed with sig=2 +[ERR] 2021/12/20-14:50:28 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-14:52:08 Child killed with sig=2 +[ERR] 2021/12/20-14:52:28 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-14:53:09 Child killed with sig=2 +[ERR] 2021/12/20-14:53:11 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-14:53:12 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/20-14:54:40 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-14:55:40 Child killed with sig=2 +[ERR] 2021/12/20-14:55:42 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/20-14:56:01 Child killed with sig=2 +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:21 SSL_accept() +[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-09:17:21 Child killed with sig=9 +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:17:27 SSL_accept() +[WARN] 2021/12/21-09:17:27 Child killed with sig=9 +[ERR] 2021/12/21-09:17:27 SSL_accept() +[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:32:32 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-09:34:25 Child killed with sig=2 +[ERR] 2021/12/21-09:34:28 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-09:34:48 Child killed with sig=2 +[ERR] 2021/12/21-09:36:00 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:36:00 SSL_accept() +[WARN] 2021/12/21-09:36:00 Child killed with sig=9 +[ERR] 2021/12/21-09:44:24 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-09:55:33 Child killed with sig=2 +[ERR] 2021/12/21-09:55:38 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:55:38 SSL_accept() +[WARN] 2021/12/21-09:55:38 Child killed with sig=9 +[ERR] 2021/12/21-09:56:50 open_socket(): UNREACHABLE CODE REACHED! +[ERR] 2021/12/21-09:57:36 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-09:57:39 Child killed with sig=2 +[ERR] 2021/12/21-09:58:13 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-09:58:36 Child killed with sig=2 +[ERR] 2021/12/21-09:58:45 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-09:58:47 Child killed with sig=2 +[ERR] 2021/12/21-10:03:33 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-10:03:40 Child killed with sig=2 +[ERR] 2021/12/21-12:39:08 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-12:39:16 Child killed with sig=2 +[ERR] 2021/12/21-12:40:52 open_socket(): UNREACHABLE CODE REACHED! +[WARN] 2021/12/21-12:41:02 Child killed with sig=2 +[ERR] 2021/12/21-14:52:19 Can't init CAN bus, will try later +[WARN] 2021/12/21-14:52:27 Child killed with sig=2 +[ERR] 2021/12/21-14:54:28 Can't init CAN bus, will try later +[WARN] 2021/12/21-14:54:50 Child killed with sig=2 +[WARN] 2021/12/21-14:56:52 Child killed with sig=2 +[WARN] 2021/12/21-14:57:08 Max amount of connections: disconnect fd=6 +[WARN] 2021/12/21-14:57:12 Max amount of connections: disconnect fd=6 +[WARN] 2021/12/21-14:57:20 Child killed with sig=2 +[ERR] 2021/12/21-14:57:39 SSL error 5 +[WARN] 2021/12/21-14:57:40 Child killed with sig=2 +[ERR] 2021/12/21-16:27:13 SSL error 5 +[WARN] 2021/12/21-16:27:16 Child killed with sig=2 diff --git a/Socket_CAN/server.c b/Socket_CAN/server.c new file mode 100644 index 0000000..09a2d22 --- /dev/null +++ b/Socket_CAN/server.c @@ -0,0 +1,74 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 "can_io.h" +#include "daemon.h" +#include "cmdlnopts.h" +#include "server.h" +#include "sslsock.h" + +int can_inited = 0; + +static int try2initBTAcan(){ + static double t0 = 0., t1 = 0.; + can_inited = 0; + if(dtime() - t1 < 1.) return 0; // try not more then once per second + t1 = dtime(); + if(!init_can_io() || !can_ok()){ + if(dtime() - t0 > 30.){ // show errors not more than onse per 30s + LOGERR("Can't init CAN bus, will try later"); + WARNX("Can't init CAN bus"); + t0 = dtime(); + } + }else{ + can_inited = 1; + int rxpnt; double rxtime; + can_clean_recv(&rxpnt, &rxtime); + } + return can_inited; +} + +// read next message from CAN +// @parameter pk - packet (allocated outside) +// @return pointer to pk or NULL if none/error +can_packet *readBTAcan(can_packet *pk){ + if((!can_inited || !can_ok()) && !try2initBTAcan()) return NULL; + int rxpnt, idr, len; + double rxtime; + if(!can_recv_frame(&rxpnt, &rxtime, &idr, &len, pk->data)) return NULL; + if((idr & ID_MASK) == ID_DOME){ + pk->ID = (uint16_t)idr; + pk->len = (uint8_t)len; + pk->magick = CANMAGICK; + return pk; + } + return NULL; +} + +int main(int argc, char **argv){ + char *self = argv[0]; + initial_setup(); + parse_args(argc, argv); +#ifdef EBUG +#endif + try2initBTAcan(); + return start_daemon(self); +} diff --git a/Socket_CAN/server.h b/Socket_CAN/server.h new file mode 100644 index 0000000..b83ea6d --- /dev/null +++ b/Socket_CAN/server.h @@ -0,0 +1,29 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ +#pragma once +#ifndef SERVER_H__ +#define SERVER_H__ + +#include "canbus.h" + +can_packet *readBTAcan(can_packet *pk); + +extern int can_inited; + + +#endif // SERVER_H__ diff --git a/Socket_CAN/soccanclient b/Socket_CAN/soccanclient new file mode 100755 index 0000000000000000000000000000000000000000..2d080d94be0ce8298e02c6097271fec3246047ce GIT binary patch literal 26864 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 literal 0 HcmV?d00001 diff --git a/Socket_CAN/sslsock.c b/Socket_CAN/sslsock.c new file mode 100644 index 0000000..5b04bbd --- /dev/null +++ b/Socket_CAN/sslsock.c @@ -0,0 +1,322 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 // inet_ntop +#include +#include // addrinfo +#include +#include +#include +#include +#include +#include +#include // pthread_kill +#include +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "sslsock.h" +#ifdef SERVER +#include "server.h" +#else +#include "client.h" +#endif + +#ifdef SERVER +static int OpenConn(int port){ + int sd = socket(PF_INET, SOCK_STREAM, 0); + if(sd < 0){ + LOGERR("Can't open socket"); + ERRX("socket()"); + } + int enable = 1; + // allow reuse of descriptor + if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&enable, sizeof(int)) < 0){ + LOGERR("Can't apply SO_REUSEADDR to socket"); + ERRX("setsockopt()"); + } + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + if(bind(sd, (struct sockaddr*)&addr, sizeof(addr))){ + LOGWARN("Can't bind port %d", port); + ERRX("bind()"); + } + if(listen(sd, BACKLOG)){ + LOGWARN("Can't listen()"); + ERRX("listen()"); + } + return sd; +} + +// return 0 if client disconnected +static int handle_connection(SSL *ssl){ + char buf[1025]; + int bytes = SSL_read(ssl, buf, sizeof(buf)-1); + int sd = SSL_get_fd(ssl); + if(bytes < 1){ + int sslerr = SSL_get_error(ssl, bytes); + if(SSL_ERROR_WANT_READ == sslerr || + SSL_ERROR_WANT_WRITE == sslerr) return 1; // empty call + LOGERR("SSL error %d", sslerr); + WARNX("SSL error %d", sslerr); + return 0; + }else{ + buf[bytes] = '\0'; + printf("Client %d msg: \"%s\"\n", sd, buf); + LOGDBG("fd=%d, message=%s", sd, buf); + snprintf(buf, 1024, "Hello, yout FD=%d", sd); + SSL_write(ssl, buf, strlen(buf)); + } + return 1; +} +#endif + +#ifdef CLIENT +static int SSL_nbread(SSL *ssl, char *buf, int bufsz){ + struct pollfd fds = {0}; + int fd = SSL_get_fd(ssl); + fds.fd = fd; + fds.events = POLLIN; + if(poll(&fds, 1, 1) < 0){ // wait no more than 1ms + LOGWARN("SSL_nbread(): poll() failed"); + WARNX("poll()"); + return 0; + } + if(fds.revents == POLLIN){ + DBG("Got info in fd #%d", fd); + int bytes = SSL_read(ssl, buf, bufsz); + DBG("read %d bytes", bytes); + if(bytes == 0) return -1; + if(bytes < 0){ + int sslerr = SSL_get_error(ssl, bytes); + if(SSL_ERROR_WANT_READ == sslerr || + SSL_ERROR_WANT_WRITE == sslerr) return 0; // empty call + LOGERR("SSL error %d", sslerr); + WARNX("SSL error %d", sslerr); + return bytes; + } + return bytes; + } + return 0; +} + +static int OpenConn(int port){ + int sd; + struct hostent *host; + struct sockaddr_in addr; + if((host = gethostbyname(G.serverhost)) == NULL ){ + LOGWARN("gethostbyname(%s) error", G.serverhost); + ERRX("gethostbyname()"); + } + sd = socket(PF_INET, SOCK_STREAM, 0); + bzero(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = *(long*)(host->h_addr); + if(connect(sd, (struct sockaddr*)&addr, sizeof(addr))){ + close(sd); + LOGWARN("Can't connect to %s", G.serverhost); + ERRX("connect()"); + } + return sd; +} + +static void clientproc(SSL_CTX *ctx, int fd){ + FNAME(); + SSL *ssl; + char buf[1024]; + char acClientRequest[1024] = {0}; + int bytes; + ssl = SSL_new(ctx); + SSL_set_fd(ssl, fd); + if(-1 == SSL_connect(ssl)){ + LOGERR("SSL_connect()"); + ERRX("SSL_connect() error"); + } + int enable = 1; + if(ioctl(fd, FIONBIO, (void *)&enable) < 0){ + LOGERR("Can't make socket nonblocking"); + ERRX("ioctl()"); + } + double t0 = dtime(); + int N = 0; + while(1){ + if(dtime() - t0 > 3.){ + DBG("Sent test message"); + sprintf(acClientRequest, "Test connection #%d", ++N); + SSL_write(ssl, acClientRequest, strlen(acClientRequest)); + t0 = dtime(); + } + bytes = SSL_nbread(ssl, buf, sizeof(buf)); + if(bytes > 0){ + if(bytes == sizeof(can_packet) && ((can_packet*)buf)->magick == CANMAGICK){ // can packet + can_packet *pk = (can_packet*)buf; +#ifdef EBUG + green("Got CAN message for ID 0x%X: ", pk->ID); + for(int i = 0; i < pk->len; ++i) + printf("0x%X ", pk->data[i]); +#endif + }else{ // text message + buf[bytes] = 0; + printf("Received: \"%s\"\n", buf); + } + }else if(bytes < 0){ + LOGWARN("Server disconnected or other error"); + ERRX("Disconnected"); + } + } + SSL_free(ssl); +} +#endif + +static SSL_CTX* InitCTX(void){ + const SSL_METHOD *method; + SSL_CTX *ctx; + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + method = +#ifdef CLIENT + TLS_client_method(); +#else + TLS_server_method(); +#endif + ctx = SSL_CTX_new(method); + if(!ctx){ + LOGWARN("Can't create SSL context"); + ERRX("SSL_CTX_new()"); + } + if(SSL_CTX_load_verify_locations(ctx, G.ca, NULL) != 1){ + LOGWARN("Could not set the CA file location\n"); + ERRX("Could not set the CA file location\n"); + } +#ifdef SERVER + SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(G.ca)); +#endif + if(SSL_CTX_use_certificate_file(ctx, G.cert, SSL_FILETYPE_PEM) <= 0){ + LOGWARN("Can't use SSL certificate %s", G.cert); + ERRX("Can't use SSL certificate %s", G.cert); + } + if(SSL_CTX_use_PrivateKey_file(ctx, G.key, SSL_FILETYPE_PEM) <= 0){ + LOGWARN("Can't use SSL key %s", G.key); + ERRX("Can't use SSL key %s", G.key); + } + if(!SSL_CTX_check_private_key(ctx)){ + LOGWARN("Private key does not match the public certificate\n"); + ERRX("Private key does not match the public certificate\n"); + } + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); +#ifdef SERVER + SSL_CTX_set_verify(ctx, // Specify that we need to verify the client as well + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + NULL); +#else + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); +#endif + SSL_CTX_set_verify_depth(ctx, 1); // We accept only certificates signed only by the CA himself + return ctx; +} + +int open_socket(){ + int fd; + SSL_library_init(); + SSL_CTX *ctx = InitCTX(); + fd = OpenConn(atoi(G.port)); +#ifdef SERVER + int enable = 1; + if(ioctl(fd, FIONBIO, (void *)&enable) < 0){ + LOGERR("Can't make socket nonblocking"); + ERRX("ioctl()"); + } + int nfd = 1; // only one socket @start + struct pollfd poll_set[BACKLOG+1]; + memset(poll_set, 0, sizeof(poll_set)); + poll_set[0].fd = fd; + poll_set[0].events = POLLIN; + SSL *ssls[BACKLOG] = {0}; +double t0 = dtime(); +char buf[64]; int P = 0; + while(1){ + // check CAN bus and send data to all connected + can_packet canpack; + if(readBTAcan(&canpack)){ + DBG("GOT CAN packet"); + for(int i = nfd-1; i > -1; --i) + SSL_write(ssls[i], &canpack, sizeof(canpack)); + } + if(dtime() - t0 > 5. && nfd > 1){ + DBG("send ping"); + snprintf(buf, 63, "ping #%d", ++P); + for(int i = nfd-2; i > -1; --i) + SSL_write(ssls[i], buf, strlen(buf)); + t0 = dtime(); + } + poll(poll_set, nfd, 1); // max timeout - 1ms + // check main for accept() + if(poll_set[0].revents & POLLIN){ + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int client = accept(fd, (struct sockaddr*)&addr, &len); + DBG("Connection: %s: %d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + LOGMSG("Client %s connected to port %d (fd=%d)", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), client); + if(nfd == BACKLOG + 1){ + LOGWARN("Max amount of connections: disconnect fd=%d", client); + WARNX("Limit of connections reached"); + close(client); + }else{ + SSL *ssl = SSL_new(ctx); + SSL_set_fd(ssl, client); + if(-1 == SSL_accept(ssl)){ + LOGERR("SSL_accept()"); + WARNX("SSL_accept()"); + SSL_free(ssl); + }else{ + ssls[nfd-1] = ssl; + memset(&poll_set[nfd], 0, sizeof(struct pollfd)); + poll_set[nfd].fd = client; + poll_set[nfd].events = POLLIN; + ++nfd; + } + } + } + // scan connections + for(int fdidx = 1; fdidx < nfd; ++fdidx){ + if((poll_set[fdidx].revents & POLLIN) == 0) continue; + int fd = poll_set[fdidx].fd; + if(!handle_connection(ssls[fdidx-1])){ // socket closed + SSL_free(ssls[fdidx-1]); + DBG("Client fd=%d disconnected", fd); + LOGMSG("Client fd=%d disconnected", fd); + close(fd); + // move last FD to current position + poll_set[fdidx] = poll_set[nfd - 1]; + ssls[fdidx - 1] = ssls[nfd - 2]; + } + } + } +#else + clientproc(ctx, fd); +#endif + close(fd); + SSL_CTX_free(ctx); + return 0; +} diff --git a/Socket_CAN/sslsock.h b/Socket_CAN/sslsock.h new file mode 100644 index 0000000..c4239fa --- /dev/null +++ b/Socket_CAN/sslsock.h @@ -0,0 +1,35 @@ +/* + * This file is part of the SocketCAN project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef SSLSOCK_H__ +#define SSLSOCK_H__ + +#if ! defined CLIENT && ! defined SERVER +#error "Define CLIENT or SERVER before including this file" +#endif +#if defined CLIENT && defined SERVER +#error "Both CLIENT and SERVER defined" +#endif + +#define BACKLOG 10 + +int open_socket(); + + +#endif // SSLSOCK_H__ diff --git a/serialsock/Makefile b/serialsock/Makefile new file mode 100644 index 0000000..f6204de --- /dev/null +++ b/serialsock/Makefile @@ -0,0 +1,45 @@ +# run `make DEF=...` to add extra defines +PROGRAM := serialsock +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lusefull_macros +SRCS := $(wildcard *.c) +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +OBJDIR := mk +CFLAGS += -O3 -Wno-trampolines -std=gnu99 +OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) +DEPS := $(OBJS:.o=.d) +CC = gcc +#CXX = g++ + + +all : $(PROGRAM) + +debug: CFLAGS += -DEBUG -Werror -Wall -Wextra +debug: all + +$(OBJS): $(OBJDIR) + +$(PROGRAM) : $(OBJS) + @echo -e "\t\tLD $(PROGRAM)" + $(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM) + +$(OBJDIR): + mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< + +clean: + @echo -e "\t\tCLEAN" + @rm -f $(OBJS) $(DEPS) + @rmdir $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/serialsock/Readme b/serialsock/Readme new file mode 100644 index 0000000..5f42504 --- /dev/null +++ b/serialsock/Readme @@ -0,0 +1 @@ +Socket server for my USB-CAN diff --git a/serialsock/canserver.cflags b/serialsock/canserver.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/serialsock/canserver.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/serialsock/canserver.config b/serialsock/canserver.config new file mode 100644 index 0000000..c49fee0 --- /dev/null +++ b/serialsock/canserver.config @@ -0,0 +1,6 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 +#define EBUG +#define _GNU_SOURCE +#define _XOPEN_SOURCE=1111 + diff --git a/serialsock/canserver.creator b/serialsock/canserver.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/serialsock/canserver.creator @@ -0,0 +1 @@ +[General] diff --git a/serialsock/canserver.cxxflags b/serialsock/canserver.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/serialsock/canserver.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/serialsock/canserver.files b/serialsock/canserver.files new file mode 100644 index 0000000..a52c6f3 --- /dev/null +++ b/serialsock/canserver.files @@ -0,0 +1,5 @@ +cmdlnopts.c +cmdlnopts.h +main.c +sersock.c +sersock.h diff --git a/serialsock/canserver.includes b/serialsock/canserver.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/serialsock/canserver.includes @@ -0,0 +1 @@ +. diff --git a/serialsock/cmdlnopts.c b/serialsock/cmdlnopts.c new file mode 100644 index 0000000..1d2482f --- /dev/null +++ b/serialsock/cmdlnopts.c @@ -0,0 +1,95 @@ +/* + * This file is part of the canserver project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 + +#include "cmdlnopts.h" +#include "usefull_macros.h" + +// default PID filename: +#define DEFAULT_PIDFILE "/tmp/usbsock.pid" +#define DEFAULT_PORT "1234" +#define DEFAULT_SOCKPATH "\0canbus" + +static int help; +static glob_pars G = { + .pidfile = DEFAULT_PIDFILE, + .speed = 9600, + .port = DEFAULT_PORT, + .path = DEFAULT_SOCKPATH, + .logfile = NULL // don't save logs +}; +glob_pars *GP = &G; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +static myoption cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"devpath", NEED_ARG, NULL, 'd', arg_string, APTR(&G.devpath), _("serial device path")}, + {"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("serial device speed (default: 9600)")}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")}, + {"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, + {"client", NO_ARGS, NULL, 'c', arg_int, APTR(&G.client), _("run as client")}, + {"sockpath",NEED_ARG, NULL, 'f', arg_string, APTR(&G.path), _("socket path (start from \\0 for no files)")}, + {"port", NEED_ARG, NULL, 'P', arg_string, APTR(&G.port), _("port to connect (default: " DEFAULT_PORT ")")}, + {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), _("increase log verbose level (default: LOG_WARN) and messages (default: none)")}, + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +void parse_args(int argc, char **argv){ + int i; + size_t hlen = 1024; + char helpstring[1024], *hptr = helpstring; + snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n"); + // format of help: "Usage: progname [args]\n" + change_helpstring(helpstring); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + for(i = 0; i < argc; i++) + printf("Ignore parameter\t%s\n", argv[i]); + if(help) showhelp(-1, cmdlnopts); +} + +/** + * @brief verbose - print additional messages depending of G.verbose (add '\n' at end) + * @param levl - message level + * @param fmt - message + */ +void verbose(int levl, const char *fmt, ...){ + va_list ar; + if(levl > G.verbose) return; + //printf("%s: ", __progname); + va_start(ar, fmt); + vprintf(fmt, ar); + va_end(ar); + printf("\n"); +} diff --git a/serialsock/cmdlnopts.h b/serialsock/cmdlnopts.h new file mode 100644 index 0000000..fcd6556 --- /dev/null +++ b/serialsock/cmdlnopts.h @@ -0,0 +1,42 @@ +/* + * This file is part of the canserver project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef CMDLNOPTS_H__ +#define CMDLNOPTS_H__ + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *devpath; // path to serial device + char *pidfile; // name of PID file + char *logfile; // logging to this file + char *port; // network port + char *path; // path to socket file + int speed; // connection speed + int verbose; // verbose level: for messages & logging + int client; // ==1 if application runs in client mode +} glob_pars; + +extern glob_pars *GP; + +void parse_args(int argc, char **argv); +void verbose(int levl, const char *fmt, ...); + +#endif // CMDLNOPTS_H__ diff --git a/serialsock/main.c b/serialsock/main.c new file mode 100644 index 0000000..fb5849d --- /dev/null +++ b/serialsock/main.c @@ -0,0 +1,115 @@ +/* + * This file is part of the canserver project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "sersock.h" + +static TTY_descr *dev = NULL; +static pid_t childpid = 0; +int server = 1; + +void signals(int sig){ + if(childpid){ // slave process + DBG("Child killed with sig=%d", sig); + LOGWARN("Child killed with sig=%d", sig); + exit(sig); + } + // master process + DBG("Master process"); + if(!server) restore_console(); + else if(dev) close_tty(&dev); + if(sig){ + DBG("Exit with signal %d", sig); + signal(sig, SIG_IGN); + LOGERR("Exit with signal %d", sig); + }else LOGERR("Exit"); + if(GP->pidfile && server){ + DBG("Unlink pid file"); + unlink(GP->pidfile); + } + exit(sig); +} + +int main(int argc, char **argv){ + char *self = strdup(argv[0]); + initial_setup(); + parse_args(argc, argv); + if(GP->logfile){ + int lvl = LOGLEVEL_WARN + GP->verbose; + DBG("level = %d", lvl); + if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY; + verbose(1, "Log file %s @ level %d\n", GP->logfile, lvl); + OPENLOG(GP->logfile, lvl, 1); + if(!globlog) WARNX("Can't create log file"); + } + if(GP->client) server = 0; + else if(!GP->devpath){ + LOGERR("You should point serial device path"); + ERRX("You should point serial device path"); + } + int port = atoi(GP->port); + if(port < 1024 || port > 65535){ + LOGERR("Wrong port value: %d", port); + WARNX("Wrong port value: %d", port); + return 1; + } + if(server) check4running(self, GP->pidfile); + // signal reactions: + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z + LOGMSG("Started"); +#ifndef EBUG + if(server){ + unsigned int pause = 5; + while(1){ + childpid = fork(); + if(childpid){ // master + double t0 = dtime(); + LOGMSG("Created child with pid %d", childpid); + wait(NULL); + LOGWARN("Child %d died", childpid); + if(dtime() - t0 < 1.) pause += 5; + else pause = 1; + if(pause > 900) pause = 900; + sleep(pause); // wait a little before respawn + }else{ // slave + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + break; + } + } + } +#endif + return start_socket(server, GP->path, &dev); +} diff --git a/serialsock/sersock.c b/serialsock/sersock.c new file mode 100644 index 0000000..ba7618a --- /dev/null +++ b/serialsock/sersock.c @@ -0,0 +1,386 @@ +/* + * This file is part of the canserver project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 +#include +#include +#include +#include +#include +#include // unix socket +#include + +#include "cmdlnopts.h" +#include "sersock.h" + +/** + * wait for answer from socket + * @param sock - socket fd + * @return 0 in case of error or timeout, 1 in case of socket ready + */ +static int waittoread(int sock){ + fd_set fds; + struct timeval timeout; + int rc; + timeout.tv_sec = 0; + timeout.tv_usec = 100; + FD_ZERO(&fds); + FD_SET(sock, &fds); + do{ + rc = select(sock+1, &fds, NULL, NULL, &timeout); + if(rc < 0){ + if(errno != EINTR){ + WARN("select()"); + return 0; + } + continue; + } + break; + }while(1); + if(FD_ISSET(sock, &fds)){ + //DBG("FD_ISSET"); + return 1; + } + return 0; +} + +// work with single client, return FALSE if disconnected +static int handle_socket(int sock, TTY_descr *d){ + char buff[BUFLEN]; + ssize_t rd = read(sock, buff, BUFLEN-1);; + DBG("Got %zd bytes", rd); + if(rd <= 0){ // error or disconnect + DBG("Nothing to read from fd %d (ret: %zd)", sock, rd); + return FALSE; + } + // add trailing zero to be on the safe side + buff[rd] = 0; + DBG("GOT: %s", buff); + ssize_t blen = strlen(buff); + if(blen != write(d->comfd, buff, blen)){ + LOGWARN("write()"); + WARN("write()"); + } + if(buff[blen-1] == '\n') buff[blen-1] = 0; + LOGMSG("CLIENT_%d: %s", sock, buff); + return TRUE; +} + +/** + * check data from fd + * @param fd - file descriptor + * @return 0 in case of timeout, 1 in case of fd have data, -1 if error + */ +static int canberead(int fd){ + fd_set fds; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100; + FD_ZERO(&fds); + FD_SET(fd, &fds); + do{ + int rc = select(fd+1, &fds, NULL, NULL, &timeout); + if(rc < 0){ + if(errno != EINTR){ + LOGWARN("select()"); + WARN("select()"); + return -1; + } + continue; + } + break; + }while(1); + if(FD_ISSET(fd, &fds)){ + DBG("FD_ISSET"); + return 1; + } + return 0; +} + +/** + * @brief getserdata - read data (ending by '\n') from serial device + * @param D (i) - device + * @param len (o) - amount of butes read (-1 if disconnected) + * @return pointer to data buffer or NULL if none + */ +static char *getserdata(TTY_descr *D, int *len){ + static char serbuf[BUFLEN], *ptr = serbuf; + static size_t blen = BUFLEN - 1; + if(!D || D->comfd < 0) return NULL; + char *nl = NULL; + do{ + int s = canberead(D->comfd); + if(s == 0) break; + if(s < 0){ // interrupted? + if(len) *len = 0; + return NULL; + } + ssize_t l = read(D->comfd, ptr, blen); + if(l < 1){ // disconnected + DBG("device disconnected"); + if(len) *len = -1; + return NULL; + } + ptr[l] = 0; + DBG("GOT %zd bytes: '%s'", l, ptr); + nl = strchr(ptr, '\n'); + ptr += l; + blen -= l; + if(nl){ + DBG("Got newline"); + break; + } + }while(blen); + // recalculate newline from the beginning (what if old data stays there?) + nl = strchr(serbuf, '\n'); + if(blen && !nl){ + if(len) *len = 0; + return NULL; + } + // in case of overflow send buffer without trailing '\n' + int L; + if(nl) L = nl - serbuf + 1; // get line to '\n' + else L = strlen(serbuf); // get all buffer + if(len) *len = L; + memcpy(D->buf, serbuf, L); // copy all + trailing zero + D->buflen = L; + D->buf[L] = 0; + DBG("Put to buf %d bytes: '%s'", L, D->buf); + if(nl){ + L = ptr - nl - 1; // symbols after newline + if(L > 0){ // there's some data after '\n' -> put it into the beginning + memmove(serbuf, nl+1, L); + blen = BUFLEN - 1 - L; + ptr = serbuf + L; + *ptr = 0; + DBG("now serbuf is '%s'", serbuf); + }else{ + blen = BUFLEN - 1; + ptr = serbuf; + *ptr = 0; + } + }else{ + blen = BUFLEN - 1; + ptr = serbuf; + *ptr = 0; + } + return D->buf; +} + +static void server_(int sock, TTY_descr *d){ + if(listen(sock, MAXCLIENTS) == -1){ + WARN("listen"); + LOGWARN("listen"); + return; + } + int enable = 1; + if(ioctl(sock, FIONBIO, (void *)&enable) < 0){ // make socket nonblocking + LOGERR("Can't make socket nonblocking"); + ERRX("ioctl()"); + } + int nfd = 1; // only one socket @start + struct pollfd poll_set[MAXCLIENTS+1]; + bzero(poll_set, sizeof(poll_set)); + // ZERO - listening server socket + poll_set[0].fd = sock; + poll_set[0].events = POLLIN; + while(1){ + poll(poll_set, nfd, 1); // max timeout - 1ms + if(poll_set[0].revents & POLLIN){ // check main for accept() + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int client = accept(sock, (struct sockaddr*)&addr, &len); + DBG("New connection"); + LOGMSG("Connection, fd=%d", client); + if(nfd == MAXCLIENTS + 1){ + LOGWARN("Max amount of connections, disconnect fd=%d", client); + WARNX("Limit of connections reached"); + close(client); + }else{ + memset(&poll_set[nfd], 0, sizeof(struct pollfd)); + poll_set[nfd].fd = client; + poll_set[nfd].events = POLLIN; + ++nfd; + } + } + int l = -1; + char *serdata = getserdata(d, &l); + if(l < 0){ + LOGERR("Serial device disconnected"); + ERRX("Serial device disconnected"); + } + if(serdata){ + for(int i = 1; i < nfd; ++i) + if(l != write(poll_set[i].fd, serdata, l)){ + LOGWARN("write()"); + WARN("write()"); + } + if(serdata[l-1] == '\n') serdata[l-1] = 0; + LOGMSG("SERIAL: %s", serdata); + } + // scan connections + for(int fdidx = 1; fdidx < nfd; ++fdidx){ + if((poll_set[fdidx].revents & POLLIN) == 0) continue; + int fd = poll_set[fdidx].fd; + if(!handle_socket(fd, d)){ // socket closed + DBG("Client fd=%d disconnected", fd); + LOGMSG("Client fd=%d disconnected", fd); + close(fd); + // move last FD to current position + poll_set[fdidx] = poll_set[nfd - 1]; + --nfd; + } + } + } +} + +// read console char - for client +static int rc(){ + int rb; + struct timeval tv; + int retval; + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + tv.tv_sec = 0; tv.tv_usec = 100; + retval = select(1, &rfds, NULL, NULL, &tv); + if(!retval) rb = 0; + else { + if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar(); + else rb = 0; + } + return rb; +} + +/** + * @brief mygetline - silently and non-blocking getline + * @return zero-terminated string with '\n' at end (or without in case of overflow) + */ +static char *mygetline(){ + static char buf[BUFLEN+1]; + static int i = 0; + while(i < BUFLEN){ + char rd = rc(); + if(!rd) return NULL; + if(rd == 0x7f && i){ // backspace + buf[--i] = 0; + printf("\b \b"); + }else{ + buf[i++] = rd; + printf("%c", rd); + } + fflush(stdout); + if(rd == '\n') break; + } + buf[i] = 0; + i = 0; + return buf; +} + +static void client_(int sock){ + setup_con(); // convert console mode into non-canon + int Bufsiz = BUFLEN; + char *recvBuff = MALLOC(char, Bufsiz); + while(1){ + char *msg = mygetline(); + if(msg){ + ssize_t L = strlen(msg); + if(send(sock, msg, L, 0) != L){ + WARN("send"); + WARN("send"); + } + if(msg[L-1] == '\n') msg[L-1] = 0; + LOGMSG("TERMINAL: %s", msg); + } + if(!waittoread(sock)) continue; + int n = read(sock, recvBuff, Bufsiz-1); + if(n == 0){ + WARNX("Server disconnected"); + signals(0); + } + recvBuff[n] = 0; + printf("%s", recvBuff); + if(recvBuff[n-1] == '\n') recvBuff[n-1] = 0; + LOGMSG("SERIAL: %s", recvBuff); + } +} + +/** + * @brief openserialdev - open connection to serial device + * @param path (i) - path to device + * @param speed - connection speed + * @return pointer to device structure if all OK + */ +static TTY_descr *openserialdev(char *path, int speed){ + TTY_descr *d = new_tty(path, speed, BUFLEN); + DBG("Device created"); + if(!d || !(tty_open(d, 1))){ + WARN("Can't open device %s", path); + LOGWARN("Can't open device %s", path); + return NULL; + } + DBG("device opened"); + return d; +} + +int start_socket(int server, char *path, TTY_descr **dev){ + if(server){ + if(!dev) return 1; + if(!(*dev = openserialdev(GP->devpath, GP->speed))){ + LOGERR("Can't open serial device %s", GP->devpath); + ERR("Can't open serial device %s", GP->devpath); + } + } + int sock = -1; + int reuseaddr = 1; + struct sockaddr_un saddr = {0}; + saddr.sun_family = AF_UNIX; + strncpy(saddr.sun_path, path, 106); // if sun_path[0] == 0 we don't create a file + if((sock = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0){ // or SOCK_STREAM? + LOGERR("socket()"); + ERR("socket()"); + } + if(server){ + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){ + LOGWARN("setsockopt"); + WARN("setsockopt"); + } + if(bind(sock, &saddr, sizeof(saddr)) == -1){ + close(sock); + LOGERR("bind"); + ERR("bind"); + } + }else{ + if(connect(sock, &saddr, sizeof(saddr)) == -1){ + LOGERR("connect()"); + ERR("connect()"); + } + } + if(server) server_(sock, *dev); + else client_(sock); + close(sock); + signals(0); + return 0; +} + + diff --git a/serialsock/sersock.h b/serialsock/sersock.h new file mode 100644 index 0000000..3279561 --- /dev/null +++ b/serialsock/sersock.h @@ -0,0 +1,31 @@ +/* + * This file is part of the canserver project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef SERSOCK_H__ +#define SERSOCK_H__ + +#define BUFLEN (1024) +// Max amount of connections +#define MAXCLIENTS (30) + +#include + +int start_socket(int server, char *path, TTY_descr **dev); + +#endif // SERSOCK_H__