Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
R3ABM Artem
DigestPlay
Commits
a027fabb
Commit
a027fabb
authored
May 23, 2017
by
Artem Prilutskiy
Browse files
Initial import
parents
Changes
6
Hide whitespace changes
Inline
Side-by-side
DigestPlay.c
0 → 100644
View file @
a027fabb
#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
|=
0
b00001
;
break
;
case
's'
:
location
=
optarg
;
control
|=
0
b00010
;
break
;
case
'p'
:
port
=
optarg
;
break
;
case
'c'
:
number
=
strtol
(
optarg
,
NULL
,
10
);
control
|=
0
b00100
;
break
;
case
'i'
:
value
=
strtol
(
optarg
,
NULL
,
10
);
if
(
value
>
0
)
{
EncodeInteger24
(
value
,
header
.
source
);
control
|=
0
b01000
;
}
break
;
case
'g'
:
value
=
strtol
(
optarg
,
NULL
,
10
);
if
(
value
>
0
)
{
EncodeInteger24
(
value
,
header
.
destination
);
control
|=
0
b10000
;
}
break
;
}
if
(
control
!=
0
b11111
)
{
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
;
};
Makefile
0 → 100644
View file @
a027fabb
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
RewindClient.c
0 → 100644
View file @
a027fabb
#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
;
}
RewindClient.h
0 → 100644
View file @
a027fabb
#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
sha256.c
0 → 100644
View file @
a027fabb
/*********************************************************************
* 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))