#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #include #include #endif #ifdef __MACH__ #include #include #include #include #include #include #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 "KAIROS-HAM.h" #ifndef KAIROS_HAM_H #define KAIROS_HAM_DEFAULT_PORT 65472 #define KAIROS_HAM_RECORD_ID 0xcbfaded8 #define KAIROS_EXCHANGE_HEADER_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(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) #endif #define MODE_CONSOLE (1 << 0) #define MODE_SYSLOG (1 << 1) #define MODE_DAEMON (1 << 2) #define EVENT_LIST_LENGTH (4 + 1 + 4) #define BUFFER_SIZE 4096 #define EXPIRATION_TIME 20 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("\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 proxyMediaPort = KAIROS_HAM_DEFAULT_PORT; 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' }, { "media-port", required_argument, NULL, 'b' }, { "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:b: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 'b': proxyMediaPort = strtol(optarg, NULL, 10); 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 \n" " --repeater-number \n" " --repeater-port \n" " --server-password \n" " --server-address \n" " --server-port \n" " --media-port \n" " --trap-port \n" " --service-mode \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; 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)) { print("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)) { print("Error opening port for SNMP Traps\n"); return EXIT_FAILURE; } proxySocketAddress.sin_port = htons(proxyMediaPort); mediaHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if((mediaHandle < 0) || (bind(mediaHandle, (struct sockaddr*)&proxySocketAddress, sizeof(proxySocketAddress)) < 0)) { print("Error opening port for KAIROS 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; } // Configure socket options int value = true; setsockopt(remoteHandle, IPPROTO_IP, IP_PKTINFO, &value, sizeof(value)); #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); 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 = 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(); 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, 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 sequenceNumbers[] = { 0, 0, 0, 0 }; // 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) { repeaterSocketAddress.sin_port = htons(proxyMediaPort); 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_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->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); 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)) { uint16_t number = ++ sequenceNumbers[buffer[8] & 3]; outgoingBuffer->type = htole16(REWIND_TYPE_EXTERNAL_SERVER); outgoingBuffer->flags = htole16(buffer[8] < 4); outgoingBuffer->number = htole32(number); 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 = alloca(BUFFER_SIZE); 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->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 = information->ipi_addr; data->port = htons(proxyMediaPort); outgoingBuffer->type = htole16(REWIND_TYPE_ADDRESS_NOTICE); outgoingBuffer->number = htole32(++ sequenceNumbers[0]); outgoingBuffer->length = htobe16(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->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 == 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) { print("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(++ sequenceNumbers[0]); 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(++ 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(timerHandle); close(signalHandle); #endif #ifdef __MACH__ close(queueHandle); mach_port_deallocate(mach_task_self(), clockService); #endif freeaddrinfo(serverAddress); return EXIT_SUCCESS; };