2. Extended amount of elements in RingBuffer, reduced item length 3. Added system information into keep-alive 4. Updated version number
902 lines
26 KiB
C
902 lines
26 KiB
C
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <getopt.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/ip.h>
|
|
|
|
#include <unistd.h>
|
|
#include <syslog.h>
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#ifdef USE_OPENSSL
|
|
#include <openssl/sha.h>
|
|
#endif
|
|
|
|
#ifndef HEADER_SHA_H
|
|
#include "sha256.h"
|
|
#define SHA256_DIGEST_LENGTH SHA256_BLOCK_SIZE
|
|
#define SHA256(data, length, hash) \
|
|
{ \
|
|
SHA256_CTX context; \
|
|
sha256_init(&context); \
|
|
sha256_update(&context, data, length); \
|
|
sha256_final(&context, hash); \
|
|
}
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
#include <endian.h>
|
|
#include <byteswap.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/timerfd.h>
|
|
#include <sys/signalfd.h>
|
|
#endif
|
|
|
|
#ifdef __MACH__
|
|
#include <launch.h>
|
|
#include <stdbool.h>
|
|
#include <sys/event.h>
|
|
#include <mach/mach.h>
|
|
#include <mach/clock.h>
|
|
#include <machine/endian.h>
|
|
#endif
|
|
|
|
#include "Rewind.h"
|
|
#include "Version.h"
|
|
|
|
#include "RingBuffer.h"
|
|
|
|
// #include "DMR.h"
|
|
#ifndef DMR_H
|
|
#define TDMA_FRAME_DURATION 60
|
|
#endif
|
|
|
|
// #include "ASNTools.h"
|
|
#ifndef ASNTOOLS_H
|
|
#define ASN_SEQUENCE 0x10
|
|
#define ASN_UNIVERSAL 0x00
|
|
#define ASN_CONSTRUCTOR 0x20
|
|
#endif
|
|
|
|
// #include "KAIROS-HAM.h"
|
|
#ifndef KAIROS_HAM_H
|
|
#define KAIROS_HAM_DEFAULT_PORT 65472
|
|
#define KAIROS_HAM_RECORD_ID 0xcbfaded8
|
|
#define KAIROS_EXCHANGE_HEADER_LENGTH 16
|
|
#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
|
|
|
|
#ifndef bool
|
|
#define bool int
|
|
#define true 1
|
|
#define false 0
|
|
#endif
|
|
#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)
|
|
|
|
#define __bswap_16(value) OSSwapConstInt16(value)
|
|
#define __bswap_32(value) OSSwapConstInt32(value)
|
|
#endif
|
|
|
|
|
|
#define MODE_CONSOLE (1 << 0)
|
|
#define MODE_SYSLOG (1 << 1)
|
|
#define MODE_DAEMON (1 << 2)
|
|
|
|
#define EVENT_LIST_LENGTH (4 + 2 + 4)
|
|
|
|
#define BUFFER_SIZE 2048
|
|
#define EXPIRATION_TIME 60
|
|
|
|
|
|
int serviceMode = MODE_CONSOLE;
|
|
|
|
void print(const char* format, ...)
|
|
{
|
|
va_list arguments;
|
|
va_start(arguments, format);
|
|
if (serviceMode & MODE_CONSOLE)
|
|
vprintf(format, arguments);
|
|
if (serviceMode & MODE_SYSLOG)
|
|
vsyslog(LOG_INFO, format, arguments);
|
|
va_end(arguments);
|
|
}
|
|
|
|
int main(int argc, const char* argv[])
|
|
{
|
|
print("\n");
|
|
print("CronosAgent for BrandMeister DMR Master Server\n");
|
|
print("Copyright 2016 Artem Prilutskiy (R3ABM, cyanide.burnout@gmail.com)\n");
|
|
print("Software revision " STRING(VERSION) " build " BUILD "\n");
|
|
print("\n");
|
|
|
|
// Parameters for server
|
|
|
|
const char* serverPort = "54004";
|
|
const char* serverLocation = NULL;
|
|
const char* serverPassword = NULL;
|
|
struct addrinfo* serverAddress = NULL;
|
|
|
|
// Parameters for repeater
|
|
|
|
int proxyTrapPort = 162;
|
|
|
|
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' },
|
|
{ "trap-port", required_argument, NULL, 't' },
|
|
{ "service-mode", required_argument, NULL, 'm' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
int selection = 0;
|
|
while ((selection = getopt_long(argc, CAST(char* const*, argv), "n:r:c:w:s:p:t:m:", 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 't':
|
|
proxyTrapPort = strtol(optarg, NULL, 10);
|
|
break;
|
|
|
|
case 'm':
|
|
serviceMode = strtol(optarg, NULL, 10);
|
|
break;
|
|
}
|
|
|
|
if ((repeaterNumber == 0) ||
|
|
(serverPassword == NULL) ||
|
|
(serverLocation == NULL) ||
|
|
(repeaterSocketAddress.sin_addr.s_addr == htonl(INADDR_ANY)))
|
|
{
|
|
print(
|
|
"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 <local port for BrandMeister DMR Server>\n"
|
|
" --trap-port <port for SNMP Traps>\n"
|
|
" --service-mode <set of bits>\n"
|
|
" bit 0 - print to standard output\n"
|
|
" bit 1 - print to system log\n"
|
|
" bit 2 - run as daemon\n"
|
|
"\n",
|
|
argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
if ((serviceMode & MODE_DAEMON) &&
|
|
(daemon(-1, -1) < 0))
|
|
{
|
|
print("Error launching daemon");
|
|
return EXIT_FAILURE;
|
|
}
|
|
#endif
|
|
#ifdef __MACH__
|
|
if (serviceMode & MODE_DAEMON)
|
|
{
|
|
launch_data_t request = launch_data_new_string(LAUNCH_KEY_CHECKIN);
|
|
launch_data_t response = launch_msg(request);
|
|
launch_data_free(request);
|
|
if (response == NULL)
|
|
{
|
|
print("Error calling launchd");
|
|
return EXIT_FAILURE;
|
|
}
|
|
launch_data_type_t type = launch_data_get_type(response);
|
|
launch_data_free(response);
|
|
if (type == LAUNCH_DATA_ERRNO)
|
|
{
|
|
print("launchd returned error %d\n", launch_data_get_errno(response));
|
|
return EXIT_FAILURE;
|
|
}
|
|
// launchd will return dictionary of job for successful check-in
|
|
if (type != LAUNCH_DATA_DICTIONARY)
|
|
{
|
|
print("Error launching daemon");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// 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)
|
|
{
|
|
print("Error resolving server address %s\n", serverLocation);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Initialize proxy sockets
|
|
|
|
int trapHandle;
|
|
int mediaHandle;
|
|
int remoteHandle;
|
|
int socketOptionValue;
|
|
struct sockaddr_in proxySocketAddress;
|
|
|
|
socklen_t proxySocketLength = sizeof(proxySocketAddress);
|
|
|
|
proxySocketAddress.sin_family = AF_INET;
|
|
proxySocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
proxySocketAddress.sin_port = htons(proxyTrapPort);
|
|
trapHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if((trapHandle < 0) ||
|
|
(bind(trapHandle, (struct sockaddr*)&proxySocketAddress, proxySocketLength) < 0))
|
|
{
|
|
print("Error opening port for SNMP Traps\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
socketOptionValue = true;
|
|
proxySocketAddress.sin_port = 0;
|
|
remoteHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if((remoteHandle < 0) ||
|
|
(bind(remoteHandle, (struct sockaddr*)&proxySocketAddress, proxySocketLength) < 0) ||
|
|
(setsockopt(remoteHandle, IPPROTO_IP, IP_PKTINFO, &socketOptionValue, sizeof(socketOptionValue)) < 0))
|
|
{
|
|
print("Error opening port for Remote Control\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
socketOptionValue = IPTOS_LOWDELAY;
|
|
proxySocketAddress.sin_port = 0;
|
|
mediaHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if((mediaHandle < 0) ||
|
|
(bind(mediaHandle, (struct sockaddr*)&proxySocketAddress, proxySocketLength) < 0) ||
|
|
(getsockname(mediaHandle, (struct sockaddr*)&proxySocketAddress, &proxySocketLength) < 0) ||
|
|
(setsockopt(mediaHandle, IPPROTO_IP, IP_TOS, &socketOptionValue, sizeof(socketOptionValue)) < 0))
|
|
{
|
|
print("Error opening port for External Server\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// 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))
|
|
{
|
|
print("Error opening port for Rewind Uplink\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
|
|
// Initialize timer handle
|
|
|
|
int timerHandle1;
|
|
int timerHandle2;
|
|
struct itimerspec timerInterval;
|
|
memset(&timerInterval, 0, sizeof(timerInterval));
|
|
|
|
timerInterval.it_value.tv_sec = REWIND_KEEP_ALIVE_INTERVAL;
|
|
timerInterval.it_interval.tv_sec = REWIND_KEEP_ALIVE_INTERVAL;
|
|
|
|
timerHandle1 = timerfd_create(CLOCK_MONOTONIC, 0);
|
|
timerfd_settime(timerHandle1, 0, &timerInterval, NULL);
|
|
|
|
timerInterval.it_interval.tv_sec = 0;
|
|
timerInterval.it_interval.tv_nsec = TDMA_FRAME_DURATION * 1000000;
|
|
|
|
timerHandle2 = timerfd_create(CLOCK_MONOTONIC, 0);
|
|
timerfd_settime(timerHandle2, 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);
|
|
|
|
event.events = EPOLLIN;
|
|
event.data.fd = mediaHandle;
|
|
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 = timerHandle1;
|
|
epoll_ctl(pollHandle, EPOLL_CTL_ADD, event.data.fd, &event);
|
|
|
|
event.events = EPOLLIN;
|
|
event.data.fd = timerHandle2;
|
|
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();
|
|
|
|
EV_SET(change, mediaHandle, 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, 2, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_USECONDS, TDMA_FRAME_DURATION, 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 buffers
|
|
|
|
void* controlBuffer = alloca(BUFFER_SIZE);
|
|
|
|
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;
|
|
|
|
struct RingBuffer ringBuffers[2];
|
|
memset(ringBuffers, 0, sizeof(ringBuffers));
|
|
|
|
uint32_t sequenceNumbers[] =
|
|
{
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
};
|
|
|
|
struct utsname systemName;
|
|
uname(&systemName);
|
|
|
|
// Main loop
|
|
|
|
bool running = true;
|
|
|
|
print("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;
|
|
print("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_EXTERNAL_SERVER)
|
|
{
|
|
uint16_t flags = le16toh(incomingBuffer->flags);
|
|
uint32_t number = le32toh(incomingBuffer->number);
|
|
|
|
if (flags & REWIND_FLAG_REAL_TIME_1)
|
|
{
|
|
PokeData(ringBuffers + 0, number, incomingBuffer->data, length);
|
|
continue;
|
|
}
|
|
|
|
if (flags & REWIND_FLAG_REAL_TIME_2)
|
|
{
|
|
PokeData(ringBuffers + 1, number, incomingBuffer->data, length);
|
|
continue;
|
|
}
|
|
|
|
repeaterSocketAddress.sin_port = htons(KAIROS_HAM_DEFAULT_PORT);
|
|
sendto(mediaHandle, 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_ADDRESS_NOTICE)
|
|
{
|
|
socketOptionValue = false;
|
|
setsockopt(remoteHandle, IPPROTO_IP, IP_PKTINFO, &socketOptionValue, sizeof(socketOptionValue));
|
|
continue;
|
|
}
|
|
|
|
if (type == REWIND_TYPE_REPORT)
|
|
{
|
|
incomingBuffer->data[length] = '\0';
|
|
print("Server message: %s\n", incomingBuffer->data);
|
|
continue;
|
|
}
|
|
|
|
if (type == REWIND_TYPE_CHALLENGE)
|
|
{
|
|
print("Authenticating with agent\n");
|
|
|
|
memcpy(incomingBuffer->data + length, serverPassword, passwordLength);
|
|
SHA256(incomingBuffer->data, length + passwordLength, outgoingBuffer->data);
|
|
outgoingBuffer->type = htole16(REWIND_TYPE_AUTHENTICATION);
|
|
outgoingBuffer->flags = htole16(REWIND_FLAG_DEFAULT_SET);
|
|
outgoingBuffer->number = htole32(++ sequenceNumbers[0]);
|
|
outgoingBuffer->length = htole16(SHA256_DIGEST_LENGTH);
|
|
|
|
sendto(uplinkHandle, outgoingBuffer, sizeof(struct RewindData) + SHA256_DIGEST_LENGTH, 0, serverAddress->ai_addr, serverAddress->ai_addrlen);
|
|
|
|
socketOptionValue = true;
|
|
setsockopt(remoteHandle, IPPROTO_IP, IP_PKTINFO, &socketOptionValue, sizeof(socketOptionValue));
|
|
|
|
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)
|
|
{
|
|
print("Disconnect request received\n");
|
|
running = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle packet of External Server
|
|
|
|
if (CHECK(event, mediaHandle))
|
|
{
|
|
struct sockaddr_in address;
|
|
socklen_t size = sizeof(address);
|
|
uint8_t* buffer = (uint8_t*)outgoingBuffer->data;
|
|
size_t length = recvfrom(mediaHandle, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&address, &size);
|
|
|
|
if ((size == sizeof(struct sockaddr_in)) &&
|
|
(address.sin_addr.s_addr == repeaterSocketAddress.sin_addr.s_addr) &&
|
|
(length >= KAIROS_EXCHANGE_HEADER_LENGTH) &&
|
|
(le32toh(*(uint32_t*)buffer) == KAIROS_HAM_RECORD_ID))
|
|
{
|
|
int value = buffer[8] & 3;
|
|
outgoingBuffer->type = htole16(REWIND_TYPE_EXTERNAL_SERVER);
|
|
outgoingBuffer->flags = htole16(value);
|
|
outgoingBuffer->number = htole32(++ sequenceNumbers[value]);
|
|
outgoingBuffer->length = htole16(length);
|
|
length += sizeof(struct RewindData);
|
|
sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Handle packet of Remote Control
|
|
|
|
if (CHECK(event, remoteHandle))
|
|
{
|
|
struct sockaddr_in address;
|
|
uint8_t* buffer = (uint8_t*)outgoingBuffer->data;
|
|
|
|
struct iovec vector;
|
|
vector.iov_base = buffer;
|
|
vector.iov_len = BUFFER_SIZE;
|
|
|
|
struct msghdr message;
|
|
message.msg_name = &address;
|
|
message.msg_namelen = sizeof(address);
|
|
message.msg_iov = &vector;
|
|
message.msg_iovlen = 1;
|
|
message.msg_control = controlBuffer;
|
|
message.msg_controllen = BUFFER_SIZE;
|
|
message.msg_flags = 0;
|
|
|
|
size_t length = recvmsg(remoteHandle, &message, 0);
|
|
|
|
if ((message.msg_namelen == 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->flags = htole16(REWIND_FLAG_DEFAULT_SET);
|
|
outgoingBuffer->number = htole32(++ sequenceNumbers[0]);
|
|
outgoingBuffer->length = htole16(length);
|
|
length += sizeof(struct RewindData);
|
|
sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen);
|
|
|
|
// Transmit destination address of received packet
|
|
// to make correct messages for External Server interface
|
|
|
|
struct cmsghdr* control = CMSG_FIRSTHDR(&message);
|
|
while (control != NULL)
|
|
{
|
|
if (control->cmsg_type == IP_PKTINFO)
|
|
{
|
|
struct in_pktinfo* information = (struct in_pktinfo*)CMSG_DATA(control);
|
|
|
|
size_t length = sizeof(struct RewindAddressData);
|
|
struct RewindAddressData* data = (struct RewindAddressData*)outgoingBuffer->data;
|
|
data->address.s_addr = information->ipi_addr.s_addr;
|
|
data->port = __bswap_16(proxySocketAddress.sin_port);
|
|
|
|
outgoingBuffer->type = htole16(REWIND_TYPE_ADDRESS_NOTICE);
|
|
outgoingBuffer->flags = htole16(REWIND_FLAG_DEFAULT_SET);
|
|
outgoingBuffer->number = htole32(++ sequenceNumbers[0]);
|
|
outgoingBuffer->length = htole16(length);
|
|
length += sizeof(struct RewindData);
|
|
sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen);
|
|
|
|
break;
|
|
}
|
|
control = CMSG_NXTHDR(&message, control);
|
|
}
|
|
}
|
|
|
|
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->flags = htole16(REWIND_FLAG_DEFAULT_SET);
|
|
outgoingBuffer->number = htole32(++ sequenceNumbers[0]);
|
|
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 == timerHandle1)
|
|
{
|
|
uint64_t information;
|
|
read(timerHandle1, &information, sizeof(information));
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
#endif
|
|
#ifdef __MACH__
|
|
if ((event->ident == 1) &&
|
|
(event->filter == EVFILT_TIMER))
|
|
{
|
|
clock_get_time(clockService, &now);
|
|
#endif
|
|
if (now.tv_sec > watchDog)
|
|
{
|
|
print("Connection time-out expired\n");
|
|
running = false;
|
|
break;
|
|
}
|
|
|
|
size_t length = sizeof(struct RewindVersionData);
|
|
struct RewindVersionData* data = (struct RewindVersionData*)outgoingBuffer->data;
|
|
|
|
data->number = htole32(repeaterNumber);
|
|
data->service = REWIND_SERVICE_CRONOS_AGENT;
|
|
|
|
length += sprintf(data->description,
|
|
"CronosAgent " STRING(VERSION) " %s %s " BUILD,
|
|
systemName.sysname,
|
|
systemName.machine);
|
|
|
|
outgoingBuffer->type = htole16(REWIND_TYPE_KEEP_ALIVE);
|
|
outgoingBuffer->flags = htole16(REWIND_FLAG_DEFAULT_SET);
|
|
outgoingBuffer->number = htole32(++ sequenceNumbers[0]);
|
|
outgoingBuffer->length = htole16(length);
|
|
length += sizeof(struct RewindData);
|
|
sendto(uplinkHandle, outgoingBuffer, length, 0, serverAddress->ai_addr, serverAddress->ai_addrlen);
|
|
|
|
continue;
|
|
}
|
|
|
|
// Handle timer to process RingBuffer
|
|
|
|
#ifdef __linux__
|
|
if (event->data.fd == timerHandle2)
|
|
{
|
|
uint64_t information;
|
|
read(timerHandle2, &information, sizeof(information));
|
|
#endif
|
|
#ifdef __MACH__
|
|
if ((event->ident == 2) &&
|
|
(event->filter == EVFILT_TIMER))
|
|
{
|
|
#endif
|
|
repeaterSocketAddress.sin_port = htons(KAIROS_HAM_DEFAULT_PORT);
|
|
ProcessBuffer(ringBuffers + 0, mediaHandle, &repeaterSocketAddress);
|
|
ProcessBuffer(ringBuffers + 1, mediaHandle, &repeaterSocketAddress);
|
|
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->flags = htole16(REWIND_FLAG_DEFAULT_SET);
|
|
outgoingBuffer->number = htole32(++ sequenceNumbers[0]);
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
print("Server stopped\n");
|
|
|
|
// Clean up
|
|
|
|
close(uplinkHandle);
|
|
close(remoteHandle);
|
|
close(mediaHandle);
|
|
close(trapHandle);
|
|
|
|
#ifdef __linux__
|
|
close(pollHandle);
|
|
close(timerHandle1);
|
|
close(timerHandle2);
|
|
close(signalHandle);
|
|
#endif
|
|
#ifdef __MACH__
|
|
close(queueHandle);
|
|
mach_port_deallocate(mach_task_self(), clockService);
|
|
#endif
|
|
|
|
freeaddrinfo(serverAddress);
|
|
|
|
return EXIT_SUCCESS;
|
|
};
|