Commit a027fabb authored by Artem Prilutskiy's avatar Artem Prilutskiy

Initial import

parents
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <endian.h>
#include <sys/timerfd.h>
#include "Version.h"
#include "RewindClient.h"
// #include "DMR.h"
#ifndef DMR_H
#define TDMA_FRAME_DURATION 60
#endif
#define DSD_MAGIC_TEXT ".amb"
#define DSD_MAGIC_SIZE 4
#define DSD_AMBE_CHUNK_SIZE 8
#define HELPER(value) #value
#define STRING(value) HELPER(value)
#define COUNT(array) sizeof(array) / sizeof(array[0])
#define BUFFER_SIZE 1024
#define ATTEMPT_COUNT 5
typedef uint8_t Integer24[3];
struct FullLC
{
uint8_t code;
uint8_t feature;
uint8_t options;
Integer24 destination;
Integer24 source;
Integer24 sum;
};
void EncodeInteger24(uint32_t value, Integer24 data)
{
data[0] = (value >> 16) & 0xff;
data[1] = (value >> 8) & 0xff;
data[2] = value & 0xff;
}
int main(int argc, char* argv[])
{
printf("\n");
printf("DigestPlay for BrandMeister DMR Master Server\n");
printf("Copyright 2017 Artem Prilutskiy (R3ABM, cyanide.burnout@gmail.com)\n");
printf("Software revision " STRING(VERSION) " build " BUILD "\n");
printf("\n");
// Main variables
uint32_t number = 0;
const char* port = "54005";
const char* location = NULL;
const char* password = NULL;
struct FullLC header;
memset(&header, 0, sizeof(struct FullLC));
// Start up
struct option options[] =
{
{ "client-password", required_argument, NULL, 'w' },
{ "client-number", required_argument, NULL, 'c' },
{ "server-address", required_argument, NULL, 's' },
{ "server-port", required_argument, NULL, 'p' },
{ "source-id", required_argument, NULL, 'i' },
{ "group-id", required_argument, NULL, 'g' },
{ NULL, 0, NULL, 0 }
};
int value = 0;
int control = 0;
int selection = 0;
while ((selection = getopt_long(argc, argv, "w:c:s:p:i:g:", options, NULL)) != EOF)
switch (selection)
{
case 'w':
password = optarg;
control |= 0b00001;
break;
case 's':
location = optarg;
control |= 0b00010;
break;
case 'p':
port = optarg;
break;
case 'c':
number = strtol(optarg, NULL, 10);
control |= 0b00100;
break;
case 'i':
value = strtol(optarg, NULL, 10);
if (value > 0)
{
EncodeInteger24(value, header.source);
control |= 0b01000;
}
break;
case 'g':
value = strtol(optarg, NULL, 10);
if (value > 0)
{
EncodeInteger24(value, header.destination);
control |= 0b10000;
}
break;
}
if (control != 0b11111)
{
printf(
"Usage:\n"
" %s\n"
" --client-number <Registered ID of client>\n"
" --client-password <access password for BrandMeister DMR Server>\n"
" --server-address <domain name of BrandMeister DMR Server>\n"
" --server-port <service port for BrandMeister DMR Server>\n"
" --source-id <ID to use as a source>\n"
" --group-id <TG ID>\n"
"\n",
argv[0]);
return EXIT_FAILURE;
}
// Create Rewind client context
struct RewindContext* context = CreateRewindClient(
number,
"DigestPlay " STRING(VERSION) " " BUILD);
if (context == NULL)
{
printf("Error creating context\n");
return EXIT_FAILURE;
}
// Check input data format
char* buffer = (char*)alloca(BUFFER_SIZE);
ssize_t length = read(STDIN_FILENO, buffer, DSD_MAGIC_SIZE);
if ((length != DSD_MAGIC_SIZE) ||
(memcmp(buffer, DSD_MAGIC_TEXT, length) != 0))
{
printf("Error checking input data format\n");
ReleaseRewindClient(context);
return EXIT_FAILURE;
}
// Connect to the server
int result = ConnectRewindClient(context, location, port, password, REWIND_OPTION_SUPER_HEADER);
if (result < 0)
{
printf("Cannot connect to the server (%i)\n", result);
ReleaseRewindClient(context);
return EXIT_FAILURE;
}
// Initialize timer handle
int handle;
struct itimerspec interval;
memset(&interval, 0, sizeof(struct itimerspec));
interval.it_interval.tv_sec = 0;
interval.it_interval.tv_nsec = TDMA_FRAME_DURATION * 1000000;
handle = timerfd_create(CLOCK_MONOTONIC, 0);
timerfd_settime(handle, 0, &interval, NULL);
// Transmit voice header
printf("Playing...\n");
TransmitRewindData(context, REWIND_TYPE_DMR_DATA_BASE + 1, REWIND_FLAG_REAL_TIME_1, &header, sizeof(struct FullLC));
TransmitRewindData(context, REWIND_TYPE_DMR_DATA_BASE + 1, REWIND_FLAG_REAL_TIME_1, &header, sizeof(struct FullLC));
TransmitRewindData(context, REWIND_TYPE_DMR_DATA_BASE + 1, REWIND_FLAG_REAL_TIME_1, &header, sizeof(struct FullLC));
// Main loop
uint64_t mark;
size_t count = 0;
// Wait for timer event (60 milliseconds)
while (read(handle, &mark, sizeof(uint64_t)) > 0)
{
ssize_t length1 = read(STDIN_FILENO, buffer + 0 * DSD_AMBE_CHUNK_SIZE, DSD_AMBE_CHUNK_SIZE);
ssize_t length2 = read(STDIN_FILENO, buffer + 1 * DSD_AMBE_CHUNK_SIZE, DSD_AMBE_CHUNK_SIZE);
ssize_t length3 = read(STDIN_FILENO, buffer + 2 * DSD_AMBE_CHUNK_SIZE, DSD_AMBE_CHUNK_SIZE);
if ((length1 != DSD_AMBE_CHUNK_SIZE) ||
(length2 != DSD_AMBE_CHUNK_SIZE) ||
(length3 != DSD_AMBE_CHUNK_SIZE))
{
printf("Input data stream stopped\n");
break;
}
buffer[0 * DSD_AMBE_CHUNK_SIZE + 7] <<= 7;
buffer[1 * DSD_AMBE_CHUNK_SIZE + 7] <<= 7;
buffer[2 * DSD_AMBE_CHUNK_SIZE + 7] <<= 7;
TransmitRewindData(context, REWIND_TYPE_DMR_AUDIO_FRAME, REWIND_FLAG_REAL_TIME_1, buffer, 3 * DSD_AMBE_CHUNK_SIZE);
if ((count % 83) == 0)
{
// Every 5 seconds of transmission
TransmitRewindKeepAlive(context);
}
count ++;
}
// Clean up
printf("Done\n");
TransmitRewindCloae(context);
ReleaseRewindClient(context);
return EXIT_SUCCESS;
};
USE_OPENSSL := no
BUILD := $(shell date -u +%Y%m%d-%H%M%S)
OS := $(shell uname -s)
PREFIX = $(DESTDIR)/opt/DigestPlay
TOOLKIT = ../..
DIRECTORIES = \
$(TOOLKIT)/Common \
$(TOOLKIT)/Rewind
ifeq ($(OS), Linux)
FLAGS += -rdynamic
KIND := $(shell grep -E "^6.0" /etc/debian_version > /dev/null ; echo $?)
ifneq ($(KIND), 0)
LIBRARIES += rt
endif
ifeq ($(USE_OPENSSL), yes)
FLAGS += -DUSE_OPENSSL
DEPENDENCIES += openssl
endif
endif
OBJECTS = \
RewindClient.o \
DigestPlay.o
ifneq ($(USE_OPENSSL), yes)
OBJECTS += sha256.o
endif
FLAGS += -g -fno-omit-frame-pointer -O3 -MMD $(foreach directory, $(DIRECTORIES), -I$(directory)) -DBUILD=\"$(BUILD)\"
LIBS += $(foreach library, $(LIBRARIES), -l$(library))
CC = gcc
CFLAGS += $(FLAGS) -std=gnu99
ifneq ($(strip $(DEPENDENCIES)),)
FLAGS += $(shell pkg-config --cflags $(DEPENDENCIES))
LIBS += $(shell pkg-config --libs $(DEPENDENCIES))
endif
all: build
build: $(PREREQUISITES) $(OBJECTS)
$(CC) $(OBJECTS) $(FLAGS) $(LIBS) -o digestplay
install:
install -D -d $(PREFIX)
install -o root -g root digestplay $(PREFIX)
clean:
rm -f $(PREREQUISITES) $(OBJECTS) digestplay
rm -f *.d $(TOOLKIT)/*/*.d
version:
echo "#define VERSION $(shell date -u +%Y%m%d)" > Version.h
debian-package:
./UpdateLog.sh
ifdef ARCH
dpkg-buildpackage -b -a$(ARCH) -tc
else
dpkg-buildpackage -b -tc
endif
.PHONY: all build clean install
#include "RewindClient.h"
#include <unistd.h>
#include <string.h>
#include <endian.h>
#include <stdlib.h>
#include <stdio.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
#define BUFFER_SIZE 256
#define ATTEMPT_COUNT 5
static int CompareAddresses(struct sockaddr* value1, struct sockaddr_in6* value2)
{
struct sockaddr_in* value;
if ((value1->sa_family == AF_INET) &&
(value2->sin6_family == AF_INET6) &&
(IN6_IS_ADDR_V4MAPPED(&value2->sin6_addr)))
{
value = (struct sockaddr_in*)value1;
return
(value->sin_addr.s_addr - value2->sin6_addr.__in6_u.__u6_addr32[3]) |
(value->sin_port - value2->sin6_port);
}
if ((value1->sa_family == AF_INET6) &&
(value2->sin6_family == AF_INET6))
{
// Compare full socket address
return memcmp(value1, value2, sizeof(struct sockaddr_in6));
}
return -1;
}
struct RewindContext* CreateRewindClient(uint32_t number, const char* verion)
{
struct utsname name;
struct timeval interval;
struct sockaddr_in6 address;
struct RewindContext* context = (struct RewindContext*)calloc(1, sizeof(struct RewindContext));
if (context != NULL)
{
// Create socket
address.sin6_family = AF_INET6;
address.sin6_addr = in6addr_any;
address.sin6_port = 0;
address.sin6_scope_id = 0;
interval.tv_sec = 2;
interval.tv_usec = 0;
context->handle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if ((context->handle < 0) ||
(bind(context->handle, (struct sockaddr*)&address, sizeof(struct sockaddr_in6)) < 0) ||
(setsockopt(context->handle, SOL_SOCKET, SO_RCVTIMEO, &interval, sizeof(struct timeval)) < 0))
{
close(context->handle);
free(context);
return NULL;
}
// Create supplimentary data
uname(&name);
context->data = (struct RewindVersionData*)malloc(BUFFER_SIZE);
context->length = sizeof(struct RewindVersionData);
context->length += sprintf(
context->data->description,
"%s %s %s",
verion,
name.sysname,
name.machine);
context->data->number = htole32(number);
context->data->service = REWIND_SERVICE_SIMPLE_APPLICATION;
}
return context;
}
void ReleaseRewindClient(struct RewindContext* context)
{
if (context != NULL)
{
freeaddrinfo(context->address);
close(context->handle);
free(context->data);
free(context);
}
}
void TransmitRewindData(struct RewindContext* context, uint16_t type, uint16_t flag, void* data, size_t length)
{
struct msghdr message;
struct iovec vectors[2];
size_t index;
uint32_t number;
struct RewindData header;
memset(&header, 0, sizeof(struct RewindData));
memcpy(&header, REWIND_PROTOCOL_SIGN, REWIND_SIGN_LENGTH);
index = flag & REWIND_FLAG_REAL_TIME_1;
number = context->counters[index];
header.type = htole16(type);
header.flags = htole16(flag);
header.number = htole32(number);
header.length = htole16(length);
vectors[0].iov_base = &header;
vectors[0].iov_len = sizeof(struct RewindData);
vectors[1].iov_base = data;
vectors[1].iov_len = length;
message.msg_name = context->address->ai_addr;
message.msg_namelen = context->address->ai_addrlen;
message.msg_iov = vectors;
message.msg_iovlen = 2;
message.msg_control = NULL;
message.msg_controllen = 0;
message.msg_flags = 0;
sendmsg(context->handle, &message, 0);
context->counters[index] ++;
}
ssize_t ReceiveRewindData(struct RewindContext* context, struct RewindData* buffer, ssize_t length)
{
struct sockaddr_in6 address;
socklen_t size = sizeof(struct sockaddr_in6);
length = recvfrom(context->handle, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&address, &size);
if (length < 0)
return CLIENT_ERROR_SOCKET_IO;
if (CompareAddresses(context->address->ai_addr, &address) != 0)
return CLIENT_ERROR_WRONG_ADDRESS;
if ((length < sizeof(struct RewindData)) ||
(memcmp(buffer, REWIND_PROTOCOL_SIGN, REWIND_SIGN_LENGTH) != 0))
return CLIENT_ERROR_WRONG_DATA;
return length;
}
int ConnectRewindClient(struct RewindContext* context, const char* location, const char* port, const char* password, uint32_t options)
{
struct addrinfo hints;
struct RewindData* buffer = (struct RewindData*)alloca(BUFFER_SIZE);
ssize_t length;
size_t attempt = ATTEMPT_COUNT;
uint8_t* digest = (uint8_t*)alloca(SHA256_DIGEST_LENGTH);
struct RewindConfigurationData data;
// Resolve server IP address
if (context->address != NULL)
{
freeaddrinfo(context->address);
context->address = NULL;
}
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_family = AF_UNSPEC;
if (getaddrinfo(location, port, &hints, &context->address) != 0)
{
// Could not resolve address
return CLIENT_ERROR_DNS_RESOLVE;
}
// Do login procedure
while (attempt > 0)
{
TransmitRewindData(context, REWIND_TYPE_KEEP_ALIVE, REWIND_FLAG_NONE, context->data, context->length);
length = ReceiveRewindData(context, buffer, BUFFER_SIZE);
if (length < CLIENT_ERROR_SOCKET_IO)
{
// Possible we got socket timeout
return length;
}
switch (buffer->type)
{
case htole16(REWIND_TYPE_CHALLENGE):
length -= sizeof(struct RewindData);
length += sprintf(buffer->data + length, "%s", password);
SHA256(buffer->data, length, digest);
TransmitRewindData(context, REWIND_TYPE_AUTHENTICATION, REWIND_FLAG_NONE, digest, SHA256_DIGEST_LENGTH);
break;
case htole16(REWIND_TYPE_KEEP_ALIVE):
if (options != 0)
{
data.options = htole32(options);
TransmitRewindData(context, REWIND_TYPE_CONFIGURATION, REWIND_FLAG_NONE, &data, sizeof(struct RewindConfigurationData));
break;
}
case htole16(REWIND_TYPE_CONFIGURATION):
return CLIENT_ERROR_SUCCESS;
}
attempt --;
}
return CLIENT_ERROR_ATTEMPT_EXCEEDED;
}
#ifndef REWINDCLIENT_H
#define REWINDCLIENT_H
#include <stddef.h>
#include <stdint.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include "Rewind.h"
#ifdef __cplusplus
extern "C"
{
#endif
#define CLIENT_ERROR_SUCCESS 0
#define CLIENT_ERROR_SOCKET_IO -1
#define CLIENT_ERROR_WRONG_ADDRESS -2
#define CLIENT_ERROR_WRONG_DATA -3
#define CLIENT_ERROR_DNS_RESOLVE -4
#define CLIENT_ERROR_ATTEMPT_EXCEEDED -5
struct RewindContext
{
int handle;
struct addrinfo* address;
uint32_t counters[2];
struct RewindVersionData* data;
size_t length;
};
struct RewindContext* CreateRewindClient(uint32_t number, const char* verion);
void ReleaseRewindClient(struct RewindContext* context);
void TransmitRewindData(struct RewindContext* context, uint16_t type, uint16_t flag, void* data, size_t length);
ssize_t ReceiveRewindData(struct RewindContext* context, struct RewindData* buffer, ssize_t length);
int ConnectRewindClient(struct RewindContext* context, const char* location, const char* port, const char* password, uint32_t options);
#define TransmitRewindKeepAlive(context) TransmitRewindData(context, REWIND_TYPE_KEEP_ALIVE, REWIND_FLAG_NONE, context->data, context->length);
#define TransmitRewindCloae(context) TransmitRewindData(context, REWIND_TYPE_CLOSE, REWIND_FLAG_NONE, NULL, 0 );
#ifdef __cplusplus
}
#endif
#endif
\ No newline at end of file
/*********************************************************************
* Filename: sha256.c
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Implementation of the SHA-256 hashing algorithm.
SHA-256 is one of the three algorithms in the SHA2
specification. The others, SHA-384 and SHA-512, are not
offered in this implementation.
Algorithm specification can be found here:
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
This implementation uses little endian byte order.
*********************************************************************/
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
#include <memory.h>
#include "sha256.h"
/****************************** MACROS ******************************/
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
/**************************** VARIABLES *****************************/
static const WORD k[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
/*********************** FUNCTION DEFINITIONS ***********************/
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
{
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];