commit 8d7562cd14f446735609a0de38564ee09fbf6af3 Author: cyanide-burnout <cyanide.burnout@gmail.com> Date: Wed Mar 23 20:57:51 2016 +0400 Initial import diff --git a/CronosAgent.c b/CronosAgent.c new file mode 100644 index 0000000..dd286ce --- /dev/null +++ b/CronosAgent.c @@ -0,0 +1,696 @@ +#include <stddef.h> +#include <stdint.h> + +#include <stdlib.h> +#include <getopt.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#include <netdb.h> +#include <arpa/inet.h> + +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <openssl/sha.h> + +#ifdef __linux__ +#include <endian.h> +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <sys/signalfd.h> +#endif + +#ifdef __MACH__ +#include <sys/event.h> +#include <mach/mach.h> +#include <mach/clock.h> +#include <machine/endian.h> +#endif + +#include "Rewind.h" +#include "Version.h" + +// #include "ASNTools.h" +#ifndef ASNTOOLS_H +#define ASN_SEQUENCE 0x10 +#define ASN_UNIVERSAL 0x00 +#define ASN_CONSTRUCTOR 0x20 +#endif + +// #include "RA-RTP.h" +#ifndef RA_RTP_H +#define RTP_PROTOCOL_VERSION 2 +#define RTP_CONTROL_VERSION_SHIFT 6 +#define RTP_HEADER_MINIMAL_LENGTH 12 +#endif + +// #include "RemoteControl.h" +#ifndef REMOTECONTROL_H +#define REMOTE_DEFAULT_PORT 4000 +#define REMOTE_STX 0x02 +#define REMOTE_ETX 0x03 +#endif + +#define HELPER(value) #value +#define STRING(value) HELPER(value) + +#define COUNT(array) sizeof(array) / sizeof(array[0]) + +#ifdef __cplusplus +#define CAST(type, value) const_cast<type>(value) +#else +#define CAST(type, value) (type)value +#define bool int +#define true 1 +#define false 0 +#endif + +#ifdef __linux__ +#define CHECK(event, handle) (event->data.fd == handle) +#endif + +#ifdef __MACH__ +#define CHECK(event, handle) (event->ident == handle) && (event->filter == EVFILT_READ) + +#define htobe16(value) OSSwapHostToBigInt16(value) +#define be16toh(value) OSSwapBigToHostInt16(value) +#define htobe32(value) OSSwapHostToBigInt32(value) +#define be32toh(value) OSSwapBigToHostInt32(value) + +#define htole16(value) OSSwapHostToLittleInt16(value) +#define le16toh(value) OSSwapLittleToHostInt16(value) +#define htole32(value) OSSwapHostToLittleInt32(value) +#define le32toh(value) OSSwapLittleToHostInt32(value) +#endif + + +#define MEDIA_PORT_COUNT (REWIND_TYPE_ANALOG_DATA - REWIND_TYPE_SLOT_1_VOICE + 1) +#define EVENT_LIST_LENGTH (4 + 1 + 3 + MEDIA_PORT_COUNT) + +#define BUFFER_SIZE 4096 +#define EXPIRATION_TIME 20 + + +int main(int argc, const char* argv[]) +{ + printf("\n"); + printf("CronosAgent for BrandMeister DMR Master Server\n"); + printf("Copyright 2016 Artem Prilutskiy (R3ABM, cyanide.burnout@gmail.com)\n"); + printf("\n"); + + // Parameters of server + + const char* serverPort = STRING(REWIND_DEFAULT_PORT); + const char* serverLocation = NULL; + const char* serverPassword = NULL; + struct addrinfo* serverAddress = NULL; + + // Parameters of repeater + + int proxyTrapPort = 162; + int proxyBasePort = 40000; + + uint32_t repeaterNumber = 0; + int repeaterControlPort = REMOTE_DEFAULT_PORT; + + struct sockaddr_in repeaterSocketAddress; + repeaterSocketAddress.sin_family = AF_INET; + repeaterSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); + + // Start up + + struct option options[] = + { + { "repeater-number", required_argument, NULL, 'n' }, + { "repeater-address", required_argument, NULL, 'r' }, + { "repeater-port", required_argument, NULL, 'c' }, + { "server-password", required_argument, NULL, 'w' }, + { "server-address", required_argument, NULL, 's' }, + { "server-port", required_argument, NULL, 'p' }, + { "base-port", required_argument, NULL, 'b' }, + { "trap-port", required_argument, NULL, 't' }, + { NULL, 0, NULL, 0 } + }; + + int selection = 0; + while ((selection = getopt_long(argc, CAST(char* const*, argv), "n:r:c:w:s:p:b:t:", options, NULL)) != EOF) + switch (selection) + { + case 'n': + repeaterNumber = strtol(optarg, NULL, 10); + break; + + case 'r': + inet_pton(AF_INET, optarg, &repeaterSocketAddress.sin_addr); + break; + + case 'c': + repeaterControlPort = strtol(optarg, NULL, 10); + break; + + case 'w': + serverPassword = optarg; + break; + + case 's': + serverLocation = optarg; + break; + + case 'p': + serverPort = optarg; + break; + + case 'b': + proxyBasePort = strtol(optarg, NULL, 10); + break; + + case 't': + proxyTrapPort = strtol(optarg, NULL, 10); + break; + } + + if ((repeaterNumber == 0) || + (serverPassword == NULL) || + (serverLocation == NULL) || + (repeaterSocketAddress.sin_addr.s_addr == htonl(INADDR_ANY))) + { + printf( + "Usage:\n" + " %s\n" + " --repeater-address <IP address of repeater>\n" + " --repeater-number <Registered ID of repeater>\n" + " --repeater-port <port number of interface Remote Control>\n" + " --server-password <access password of BrandMeister DMR Server>\n" + " --server-address <domain name of BrandMeister DMR Server>\n" + " --server-port <port number of RadioActivity Console>\n" + " --base-port <base port for RTP Media>\n" + " --trap-port <port for SNMP Traps>\n" + "\n", + argv[0]); + return EXIT_FAILURE; + } + + // Resolve server address + + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; + +#ifdef __linux__ + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_UNSPEC; +#endif +#ifdef __MACH__ + hints.ai_flags = AI_V4MAPPED; + hints.ai_family = AF_INET6; +#endif + + if (getaddrinfo(serverLocation, serverPort, &hints, &serverAddress) != 0) + { + printf("Error resolving server address %s\n", serverLocation); + return EXIT_FAILURE; + } + + // Initialize proxy sockets + + int trapHandle; + int remoteHandle; + int mediaHandles[MEDIA_PORT_COUNT]; + struct sockaddr_in proxySocketAddress; + + proxySocketAddress.sin_family = AF_INET; + proxySocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); + + proxySocketAddress.sin_port = 0; + remoteHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if((remoteHandle < 0) || + (bind(remoteHandle, (struct sockaddr*)&proxySocketAddress, sizeof(proxySocketAddress)) < 0)) + { + printf("Error opening port for Remote Control\n"); + return EXIT_FAILURE; + } + + proxySocketAddress.sin_port = htons(proxyTrapPort); + trapHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if((trapHandle < 0) || + (bind(trapHandle, (struct sockaddr*)&proxySocketAddress, sizeof(proxySocketAddress)) < 0)) + { + printf("Error opening port for SNMP Traps\n"); + return EXIT_FAILURE; + } + + for (size_t index = 0; index < MEDIA_PORT_COUNT; index ++) + { + proxySocketAddress.sin_port = htons(proxyBasePort + index * 10); + int handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if((handle < 0) || + (bind(handle, (struct sockaddr*)&proxySocketAddress, sizeof(proxySocketAddress)) < 0)) + { + printf("Error opening port for RTP Media\n"); + return EXIT_FAILURE; + } + mediaHandles[index] = handle; + } + + // Initialize uplink socket + + int uplinkHandle; + struct sockaddr_in6 uplinkSocketAddress; + + uplinkSocketAddress.sin6_family = AF_INET6; + uplinkSocketAddress.sin6_addr = in6addr_any; + uplinkSocketAddress.sin6_port = 0; + uplinkSocketAddress.sin6_scope_id = 0; + + uplinkHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if((uplinkHandle < 0) || + (bind(uplinkHandle, (struct sockaddr*)&uplinkSocketAddress, sizeof(uplinkSocketAddress)) < 0)) + { + printf("Error opening port for Rewind Uplink\n"); + return EXIT_FAILURE; + } + +#ifdef __linux__ + + // Initialize timer handle + + int timerHandle; + struct itimerspec timerInterval; + + memset(&timerInterval, 0, sizeof(timerInterval)); + timerInterval.it_interval.tv_sec = REWIND_KEEP_ALIVE_INTERVAL; + timerInterval.it_value.tv_sec = REWIND_KEEP_ALIVE_INTERVAL; + + timerHandle = timerfd_create(CLOCK_MONOTONIC, 0); + timerfd_settime(timerHandle, 0, &timerInterval, NULL); + + // Initialize signal handle + + int signalHandle; + sigset_t signalMask; + + sigemptyset(&signalMask); + sigaddset(&signalMask, SIGINT); + sigaddset(&signalMask, SIGHUP); + sigaddset(&signalMask, SIGTERM); + sigaddset(&signalMask, SIGQUIT); + + sigprocmask(SIG_BLOCK, &signalMask, NULL); + signalHandle = signalfd(-1, &signalMask, 0); + + // Initialize ePoll + + int pollHandle; + struct epoll_event event; + + pollHandle = epoll_create(EVENT_LIST_LENGTH); + + for (size_t index = 0; index < MEDIA_PORT_COUNT; index ++) + { + event.events = EPOLLIN; + event.data.fd = mediaHandles[index]; + epoll_ctl(pollHandle, EPOLL_CTL_ADD, event.data.fd, &event); + } + + event.events = EPOLLIN; + event.data.fd = trapHandle; + epoll_ctl(pollHandle, EPOLL_CTL_ADD, event.data.fd, &event); + + event.events = EPOLLIN; + event.data.fd = remoteHandle; + epoll_ctl(pollHandle, EPOLL_CTL_ADD, event.data.fd, &event); + + event.events = EPOLLIN; + event.data.fd = uplinkHandle; + epoll_ctl(pollHandle, EPOLL_CTL_ADD, event.data.fd, &event); + + event.events = EPOLLIN; + event.data.fd = timerHandle; + epoll_ctl(pollHandle, EPOLL_CTL_ADD, event.data.fd, &event); + + event.events = EPOLLIN; + event.data.fd = signalHandle; + epoll_ctl(pollHandle, EPOLL_CTL_ADD, event.data.fd, &event); + + // Initialize clock + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + +#endif +#ifdef __MACH__ + + // Prepare signal handlers + + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + // Initialize KQueue + + int queueHandle; + + struct kevent changes[EVENT_LIST_LENGTH]; + struct kevent* change = changes; + + queueHandle = kqueue(); + + for (size_t index = 0; index < MEDIA_PORT_COUNT; index ++) + { + EV_SET(change, mediaHandles[index], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + } + + EV_SET(change, uplinkHandle, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + + EV_SET(change, remoteHandle, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + + EV_SET(change, trapHandle, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + + EV_SET(change, 1, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_SECONDS, REWIND_KEEP_ALIVE_INTERVAL, 0); + change ++; + + EV_SET(change, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + + EV_SET(change, SIGHUP, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + + EV_SET(change, SIGTERM, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + + EV_SET(change, SIGQUIT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, 0); + change ++; + + // Initialize clock service + + clock_serv_t clockService; + mach_timespec_t now; + + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clockService); + clock_get_time(clockService, &now); + +#endif + + // Prepare uplink buffers + + struct RewindData* incomingBuffer = (struct RewindData*)alloca(sizeof(struct RewindData) + BUFFER_SIZE); + struct RewindData* outgoingBuffer = (struct RewindData*)alloca(sizeof(struct RewindData) + BUFFER_SIZE); + + memset(outgoingBuffer, 0, sizeof(struct RewindData)); + memcpy(outgoingBuffer, REWIND_PROTOCOL_SIGN, REWIND_SIGN_LENGTH); + + size_t passwordLength = strlen(serverPassword); + time_t watchDog = now.tv_sec + EXPIRATION_TIME; + uint32_t sequenceNumber = 0; + + // Main loop + + bool running = true; + + printf("Server started\n"); + + while (running) + { +#ifdef __linux__ + struct epoll_event events[EVENT_LIST_LENGTH]; + int count = epoll_wait(pollHandle, events, EVENT_LIST_LENGTH, -1); +#endif +#ifdef __MACH__ + struct kevent events[EVENT_LIST_LENGTH]; + int count = kevent(queueHandle, changes, EVENT_LIST_LENGTH, events, EVENT_LIST_LENGTH, NULL); +#endif + + if (count < 0) + { + int error = errno; + printf("Error processing handles: %s (%d)\n", strerror(error), error); + break; + } + + for (size_t index = 0; index < count; index ++) + { +#ifdef __linux__ + struct epoll_event* event = events + index; +#endif +#ifdef __MACH__ + struct kevent* event = events + index; +#endif + + // Handle packet from the uplink + + if (CHECK(event, uplinkHandle)) + { + struct sockaddr_in6 address; + socklen_t size = sizeof(address); + size_t length = recvfrom(uplinkHandle, incomingBuffer, BUFFER_SIZE, 0, (struct sockaddr*)&address, &size); + + if ((length >= sizeof(struct RewindData)) && + ((serverAddress->ai_addr->sa_family == AF_INET) || /* Work-around for Linux */ + (memcmp(&address, serverAddress->ai_addr, serverAddress->ai_addrlen) == 0)) && + (memcmp(incomingBuffer->sign, REWIND_PROTOCOL_SIGN, REWIND_SIGN_LENGTH) == 0)) + { + uint16_t type = le16toh(incomingBuffer->type); + size_t length = le16toh(incomingBuffer->length); + + if ((type >= REWIND_TYPE_SLOT_1_VOICE) && + (type <= REWIND_TYPE_ANALOG_DATA)) + { + size_t index = type - REWIND_TYPE_SLOT_1_VOICE; + repeaterSocketAddress.sin_port = htons(proxyBasePort + index * 10); + sendto(mediaHandles[index], incomingBuffer->data, length, 0, (struct sockaddr*)&repeaterSocketAddress, sizeof(struct sockaddr_in)); + + continue; + } + + if (type == REWIND_TYPE_REMOTE_CONTROL) + { + repeaterSocketAddress.sin_port = htons(repeaterControlPort); + sendto(remoteHandle, incomingBuffer->data, length, 0, (struct sockaddr*)&repeaterSocketAddress, sizeof(struct sockaddr_in)); + + continue; + } + + if (type == REWIND_TYPE_REPORT) + { + incomingBuffer->data[length] = '\0'; + printf("Server message: %s\n", incomingBuffer->data); + continue; + } + + if (type == REWIND_TYPE_CHALLENGE) + { + printf("Authenticating with agent\n"); + + memcpy(incomingBuffer->data + length, serverPassword, passwordLength); + SHA256(incomingBuffer->data, length + passwordLength, outgoingBuffer->data); + + outgoingBuffer->type = htole16(REWIND_TYPE_AUTHENTICATION); + outgoingBuffer->number = htole32(++ sequenceNumber); + outgoingBuffer->length = htole16(SHA256_DIGEST_LENGTH); + + sendto(uplinkHandle, outgoingBuffer, sizeof(struct RewindData) + SHA256_DIGEST_LENGTH, 0, serverAddress->ai_addr, serverAddress->ai_addrlen); + continue; + } + + if (type == REWIND_TYPE_KEEP_ALIVE) + { +#ifdef __linux__ + clock_gettime(CLOCK_MONOTONIC, &now); +#endif +#ifdef __MACH__ + clock_get_time(clockService, &now); +#endif + watchDog = now.tv_sec + EXPIRATION_TIME; + continue; + } + + if (type == REWIND_TYPE_CLOSE) + { + printf("Disconnect request received\n"); + running = false; + break; + } + } + } + + // Handle packet of RTP + + for (size_t index = 0; index < MEDIA_PORT_COUNT; index ++) + { + if (CHECK(event, mediaHandles[index])) + { + struct sockaddr_in address; + socklen_t size = sizeof(address); + uint8_t* buffer = (uint8_t*)outgoingBuffer->data; + size_t length = recvfrom(mediaHandles[index], buffer, BUFFER_SIZE, 0, (struct sockaddr*)&address, &size); + + if ((size == sizeof(struct sockaddr_in)) && + (address.sin_addr.s_addr == repeaterSocketAddress.sin_addr.s_addr) && + ((buffer[0] >> RTP_CONTROL_VERSION_SHIFT) == RTP_PROTOCOL_VERSION) && + (length >= RTP_HEADER_MINIMAL_LENGTH)) + { + outgoingBuffer->type = htole16(index + REWIND_TYPE_SLOT_1_VOICE); + outgoingBuffer->number = htole32(++ sequenceNumber); + outgoingBuffer->length = htole16(length); + length += sizeof(struct RewindData); + sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen); + } + break; + } + } + + // Handle packet of Remote Control + + if (CHECK(event, remoteHandle)) + { + struct sockaddr_in address; + socklen_t size = sizeof(address); + uint8_t* buffer = (uint8_t*)outgoingBuffer->data; + size_t length = recvfrom(remoteHandle, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&address, &size); + + if ((size == sizeof(struct sockaddr_in)) && + (address.sin_addr.s_addr == repeaterSocketAddress.sin_addr.s_addr) && + (buffer[0] == REMOTE_STX) && + (buffer[length - 1] == REMOTE_ETX) && + (length >= 8)) + { + outgoingBuffer->type = htole16(REWIND_TYPE_REMOTE_CONTROL); + outgoingBuffer->number = htole32(++ sequenceNumber); + outgoingBuffer->length = htole16(length); + length += sizeof(struct RewindData); + sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen); + } + continue; + } + + // Handle packet of SNMP Trap + + if (CHECK(event, trapHandle)) + { + struct sockaddr_in address; + socklen_t size = sizeof(address); + uint8_t* buffer = (uint8_t*)outgoingBuffer->data; + size_t length = recvfrom(trapHandle, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&address, &size); + + if ((size == sizeof(struct sockaddr_in)) && + (address.sin_addr.s_addr == repeaterSocketAddress.sin_addr.s_addr) && + (buffer[0] == (ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE)) && + (length >= 20)) + { + outgoingBuffer->type = htole16(REWIND_TYPE_SNMP_TRAP); + outgoingBuffer->number = htole32(++ sequenceNumber); + outgoingBuffer->length = htole16(length); + length += sizeof(struct RewindData); + sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen); + } + + continue; + } + + // Handle timer to transmit keep-alive +#ifdef __linux__ + if (event->data.fd == timerHandle) + { + uint64_t information; + read(timerHandle, &information, sizeof(information)); + + clock_gettime(CLOCK_MONOTONIC, &now); +#endif +#ifdef __MACH__ + if (event->filter == EVFILT_TIMER) + { + clock_get_time(clockService, &now); +#endif + if (now.tv_sec > watchDog) + { + printf("Connection time-out expired\n"); + running = false; + break; + } + + size_t length = sizeof(struct RewindVersionData); + struct RewindVersionData* data = (struct RewindVersionData*)outgoingBuffer->data; + length += sprintf(data->version, "CronosAgent " STRING(VERSION) " " BUILD); + data->number = htole32(repeaterNumber); + + outgoingBuffer->type = htole16(REWIND_TYPE_KEEP_ALIVE); + outgoingBuffer->number = htole32(++ sequenceNumber); + outgoingBuffer->length = htobe16(length); + length += sizeof(struct RewindData); + sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen); + + continue; + } + + // Handle signal from the kernel + +#ifdef __linux__ + if (event->data.fd == signalHandle) + { + struct signalfd_siginfo information; + read(signalHandle, &information, sizeof(information)); +#endif +#ifdef __MACH__ + if (event->filter == EVFILT_SIGNAL) + { +#endif + outgoingBuffer->type = htole16(REWIND_TYPE_CLOSE); + outgoingBuffer->number = htole32(++ sequenceNumber); + outgoingBuffer->length = 0; + sendto(uplinkHandle, outgoingBuffer, sizeof(struct RewindData), 0, serverAddress->ai_addr, serverAddress->ai_addrlen); +#ifdef __linux__ + if ((information.ssi_signo == SIGINT) || + (information.ssi_signo == SIGTERM) || + (information.ssi_signo == SIGQUIT)) +#endif +#ifdef __MACH__ + if ((event->ident == SIGINT) || + (event->ident == SIGTERM) || + (event->ident == SIGQUIT)) +#endif + { + running = false; + break; + } + } + } + } + + printf("Server stopped\n"); + + // Clean up + + close(uplinkHandle); + close(remoteHandle); + close(trapHandle); + + for (size_t index = 0; index < MEDIA_PORT_COUNT; index ++) + { + int handle = mediaHandles[index]; + close(handle); + } + +#ifdef __linux__ + close(pollHandle); + close(timerHandle); + close(signalHandle); +#endif +#ifdef __MACH__ + close(queueHandle); + mach_port_deallocate(mach_task_self(), clockService); +#endif + + freeaddrinfo(serverAddress); + + return EXIT_SUCCESS; +}; diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..36f8e83 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +BUILD := $(shell date -u +%Y%m%d-%H%M%S) +OS := $(shell uname -s) + +TOOLKIT = ../.. + +DIRECTORIES = \ + $(TOOLKIT)/Common \ + $(TOOLKIT)/KAIROS + +ifeq ($(OS), Linux) + LIBRARIES += \ + rt + DEPENDENCIES = \ + openssl +endif + +ifeq ($(OS), Darwin) + CFLAGS += \ + -Wno-deprecated-declarations \ + -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-migrator/sdk/MacOSX.sdk/usr/include/ + LIBRARIES += \ + crypto +endif + +OBJECTS = \ + CronosAgent.o + +FLAGS := -g -fno-omit-frame-pointer -O3 -MMD $(foreach directory, $(DIRECTORIES), -I$(directory)) -DBUILD=\"$(BUILD)\" +LIBS := -lstdc++ $(foreach library, $(LIBRARIES), -l$(library)) + +CC = gcc +CFLAGS += $(FLAGS) -std=gnu99 + +ifeq ($(OS), Linux) + FLAGS += $(shell pkg-config --cflags $(DEPENDENCIES)) -rdynamic + LIBS += $(shell pkg-config --libs $(DEPENDENCIES)) +endif + +all: build + +build: $(PREREQUISITES) $(OBJECTS) + $(CC) $(OBJECTS) $(FLAGS) $(LIBS) -o cronosagent + +clean: + rm -f $(PREREQUISITES) $(OBJECTS) cronosagent + rm -f *.d $(TOOLKIT)/*/*.d + +version: + echo "#define VERSION $(shell svn info | grep -E "^Revision:" | grep -o -E "[0-9]+")" > Version.h + +.PHONY: all build clean install diff --git a/Rewind.h b/Rewind.h new file mode 100644 index 0000000..90a1c82 --- /dev/null +++ b/Rewind.h @@ -0,0 +1,64 @@ +#ifndef REWIND_H +#define REWIND_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +#pragma pack(push, 1) + +#define REWIND_DEFAULT_PORT 54004 +#define REWIND_KEEP_ALIVE_INTERVAL 2 + +#define REWIND_SIGN_LENGTH 8 +#define REWIND_PROTOCOL_SIGN "REWIND01" + +#define REWIND_CLASS_REWIND_CONTROL 0x0000 +#define REWIND_CLASS_SYSTEM_CONSOLE 0x0100 +#define REWIND_CLASS_SERVER_NOTICE 0x0200 +#define REWIND_CLASS_KAIROS_DATA 0x0800 + +#define REWIND_TYPE_KEEP_ALIVE (REWIND_CLASS_REWIND_CONTROL + 0) +#define REWIND_TYPE_CLOSE (REWIND_CLASS_REWIND_CONTROL + 1) +#define REWIND_TYPE_CHALLENGE (REWIND_CLASS_REWIND_CONTROL + 2) +#define REWIND_TYPE_AUTHENTICATION (REWIND_CLASS_REWIND_CONTROL + 3) + +#define REWIND_TYPE_REPORT (REWIND_CLASS_SYSTEM_CONSOLE + 0) + +#define REWIND_TYPE_BUSY_NOTICE (REWIND_CLASS_SERVER_NOTICE + 0) + +#define REWIND_TYPE_REMOTE_CONTROL (REWIND_CLASS_KAIROS_DATA + 0) +#define REWIND_TYPE_SNMP_TRAP (REWIND_CLASS_KAIROS_DATA + 1) +#define REWIND_TYPE_SLOT_1_VOICE (REWIND_CLASS_KAIROS_DATA + 2) +#define REWIND_TYPE_SLOT_2_VOICE (REWIND_CLASS_KAIROS_DATA + 3) +#define REWIND_TYPE_ANALOG_VOICE (REWIND_CLASS_KAIROS_DATA + 4) +#define REWIND_TYPE_SLOT_1_DATA (REWIND_CLASS_KAIROS_DATA + 5) +#define REWIND_TYPE_SLOT_2_DATA (REWIND_CLASS_KAIROS_DATA + 6) +#define REWIND_TYPE_ANALOG_DATA (REWIND_CLASS_KAIROS_DATA + 7) + +struct RewindVersionData +{ + uint32_t number; // Agent ID + char version[0]; // Software version +}; + +struct RewindData +{ + char sign[REWIND_SIGN_LENGTH]; + uint16_t type; // REWIND_TYPE_* + uint16_t flags; // REWIND_FLAG_* + uint32_t number; // Packet sequence number + uint16_t length; // Length of following data + uint8_t data[0]; // +}; + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Version.h b/Version.h new file mode 100644 index 0000000..1623b15 --- /dev/null +++ b/Version.h @@ -0,0 +1 @@ +#define VERSION 6604 diff --git a/cronosagent.service b/cronosagent.service new file mode 100644 index 0000000..e728113 --- /dev/null +++ b/cronosagent.service @@ -0,0 +1,18 @@ +[Unit] +Description=CronosAgent +Afer=network.target + +[Service] +; system.service +Type=simple +ExecStart=/opt/CronosAgent/cronosagent.sh +Restart=always +RestartSec=10 +; system.exec +User=cronos +Group=cronos +StandardOutput=syslog +WorkingDirectory=/opt/CronosAgent + +[Install] +WantedBy=multi-user.target diff --git a/cronosagent.sh b/cronosagent.sh new file mode 100755 index 0000000..3642946 --- /dev/null +++ b/cronosagent.sh @@ -0,0 +1,13 @@ +#/bin/bash + +REPEATER_NUMBER=250304 +REPEATER_ADDRESS=172.33.20.136 +SERVER_ADDRESS=aesyle.dstar.su +SERVER_PASSWORD=passw0rd + +./cronosagent \ + --trap-port 8162 \ + --repeater-number ${REPEATER_NUMBER} \ + --repeater-address ${REPEATER_ADDRESS} \ + --server-password ${SERVER_PASSWORD} \ + --server-address ${SERVER_ADDRESS} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..149fda4 --- /dev/null +++ b/test.sh @@ -0,0 +1,4 @@ +#/bin/bash +make +./cronosagent.sh +make clean