Initial import
This commit is contained in:
commit
8d7562cd14
7 changed files with 847 additions and 0 deletions
696
CronosAgent.c
Normal file
696
CronosAgent.c
Normal file
|
@ -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;
|
||||
};
|
51
Makefile
Normal file
51
Makefile
Normal file
|
@ -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
|
64
Rewind.h
Normal file
64
Rewind.h
Normal file
|
@ -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
|
1
Version.h
Normal file
1
Version.h
Normal file
|
@ -0,0 +1 @@
|
|||
#define VERSION 6604
|
18
cronosagent.service
Normal file
18
cronosagent.service
Normal file
|
@ -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
|
13
cronosagent.sh
Executable file
13
cronosagent.sh
Executable file
|
@ -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}
|
4
test.sh
Executable file
4
test.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#/bin/bash
|
||||
make
|
||||
./cronosagent.sh
|
||||
make clean
|
Loading…
Add table
Reference in a new issue