[программка] HiddenPhone - голосовые сообщения через .onion ¶
By: ББ on 2018-05-01 13 ч.
[программка] HiddenPhone - голосовые сообщения через .onion
Всем привет, пришло время опубликовать еще одну самодельную программу.
HiddenPhone
Получилось что-то вроде рации Zello, только попроще и более приспособленное под onion.
Особенности программы:
Без использования UDP, встроенная поддержка SOCKSv4, полностью готова к работе с Tor
Управление через горячую клавишу.
Написана на языке Си, легковесная, быстрая
Использует библиотеку Xlib для контроля горячей клавиши и библиотеку libasound для доступа к звуковой карте, поэтому работает только на Linux, для запуска на Windows потребуется глубокая переделка.
Звуковое сообщение всегда доставляется целиком, воспроизведение не начнется, пока сообщение не будет записано и полностью доставлено. Такой подход позволяет программе надежно работать на самых медленных соединениях. В отличие от других мессенджеров программа не пытается сократить задержку звука до минимума в ущерб качеству.
В качестве кодека используется MP2 из библиотеки libavcodec
Программа реализует соединение типа точка - точка. Подсоединение третьего абонента возможно, но при этом предыдущее соединение разрывается.
На принимающей стороне звучат звуковые сигналы начала и конца сообщения
Можно включить запись входящих сообщений (опция -mp2save)
У программы есть русскоязычная поддержка в лице меня

Код
Три файла
hp.c
// Файл hp.c
// Программа HiddenPhone впервые публикуется на форуме Runion https://lwplxqzvmgu43uff.onion.tor.my
// Рядом с файлом hp.c должны лежать файлы hp_audio.c и hp_key.c
// Компилировать командой:
// gcc -Wall hphone1.c -lasound -lavcodec -lavutil -lX11 -o hphone1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include "libavcodec/avcodec.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/samplefmt.h"
#include "../include/alsa/asoundlib.h"
#define TOR_PORT_FIRST 9150
#define TOR_PORT_SECOND 9050
uint16_t onion_target_port = 0;
uint16_t bind_port = 0;
uint16_t clearnet_port = 0;
uint16_t tor_port = TOR_PORT_FIRST; // Программа автоматически пробует оба порта
// Предпочтение отдается порту Tor-Browser
char *oniondomain = "";
char *clearnet_ip = "";
int server_socket_fd = 0;
int connection_socket_fd = 0;
int intro_socket_fd = 0;
int max_fd;
int connection_works = 0;
int intro_works = 0;
int noheartbeat_count = 0;
int xlib_pid = -1;
enum {BUFFER_STATE_READY, BUFFER_STATE_PARTIAL, BUFFER_STATE_ERROR_FD_CLOSED};
// очередь на отправку
struct upload_page_header {void *nextpage; long int datasize;};
long int upload_queue_bytes_in_use = 0;
uint8_t *upload_queue_first = NULL;
uint8_t *upload_queue_last = NULL;
long int upload_queue_p = 0;
uint8_t intro_buffer [2048];
long int intro_buffer_p = 0;
uint8_t download_header_buffer [16];
long int download_header_buffer_p = 0;
int disablebutton = 0;
int enableheartbeat = 0;
int temporary_enableheartbeat = 0;
int normalize = 0;
int mp2save = 0;
int dont_close_read_device = 0;
snd_pcm_t *recorddevice_handle = NULL;
struct parsed_header_struct {int signature_ok;
uint32_t bodysize;
uint32_t msg_type;
uint32_t additional_size;} parsed_header;
uint8_t *download_body_buffer = NULL;
long int download_body_buffer_size = 0;
long int download_body_additional_info_size = 0;
uint32_t download_message_type;
long int download_body_buffer_p = 0;
unsigned long int raw_audio_bytes_in_use = 0;
enum {CONNECT, BIND, NOPE} mode = NOPE;
enum {TOR, CLEARNET} clearnet = TOR;
// типы сообщений
#define HIDDENPHONE_MSG_AUTH_PASSWORD 2
#define HIDDENPHONE_MSG_PASSWORD_OK 10
#define HIDDENPHONE_MSG_SOUND 30
#define HIDDENPHONE_MSG_SOUND_ARRIVED 40 // не используется
#define HIDDENPHONE_MSG_HEARTBEAT_PLEASE 75
#define HIDDENPHONE_MSG_HEARTBEAT 76
#define HIDDENPHONE_MSG_HEARTBEAT_OK 77
#define HIDDENPHONE_MSG_HEARTBEAT_ENABLE 78
char *password = "";
int disable_socksproxy = 0;
struct audio_page_struct {void *next_page; int page_size; void *samples; int samples_size;};
void print_error_and_exit(char * str) {
fprintf(stderr, "Error: %s\n", str);
if (xlib_pid != -1) kill(xlib_pid, SIGTERM);
exit (1);
};
int hphone_try_to_read (int fd, uint8_t *buffer, long int *buffer_p, long int buffer_size) {
long int read_count_needed = buffer_size - (*buffer_p);
int ret = read(fd, buffer + (*buffer_p), read_count_needed);
if (ret == -1) {
perror("read");
fprintf(stderr, "Ошибка чтения, закрываю дескриптор\n");
close(fd);
return BUFFER_STATE_ERROR_FD_CLOSED;
};
if (ret == 0) {
fprintf(stderr, "Соединение закрылось\n");
close(fd);
return BUFFER_STATE_ERROR_FD_CLOSED;
};
if (ret > 0) {
(*buffer_p) += ret;
if ((*buffer_p) == buffer_size) return BUFFER_STATE_READY;
};
return BUFFER_STATE_PARTIAL;
};
/* Функция ставит страницу в очередь на отправку, если к странице
привязаны еще страницы, то они тоже ставятся */
void insert_in_upload_queue (void *page) {
struct upload_page_header *lastpage_header;
struct upload_page_header *new_last_page_header = page;
if (!upload_queue_first) upload_queue_first = page;
if (upload_queue_last) {
lastpage_header = (struct upload_page_header*) upload_queue_last;
lastpage_header->nextpage = page;
};
// Нужно найти последнюю страницу и записать ее адрес в upload_queue_last
while (new_last_page_header->nextpage) new_last_page_header = new_last_page_header->nextpage;
upload_queue_last = (uint8_t *) new_last_page_header;
};
void connection_buffers_reset () {
connection_works = 0;
download_header_buffer_p = 0;
if (download_body_buffer) free(download_body_buffer);
download_body_buffer = NULL;
download_body_buffer_size = 0;
download_body_buffer_p = 0;
// этот код отчищает очередь отправки
void *nextpage;
struct upload_page_header *header;
while (upload_queue_first) {
header = (struct upload_page_header *) upload_queue_first;
upload_queue_bytes_in_use -= sizeof(struct upload_page_header) + header->datasize;
nextpage = header->nextpage;
free(upload_queue_first);
upload_queue_first = nextpage;
};
upload_queue_last = NULL;
upload_queue_p = 0;
if (upload_queue_bytes_in_use != 0) fprintf(stderr, "Утечка памяти\n");
};
void send_one_upload_page (void) {
if (!upload_queue_first) print_error_and_exit("Нет страниц для отправки");
struct upload_page_header *header = (struct upload_page_header *) upload_queue_first;
uint8_t *pagedata = upload_queue_first + sizeof(struct upload_page_header);
long int need_to_send = header->datasize - upload_queue_p;
if (need_to_send == 0) print_error_and_exit("Эту страницу мы уже отправили");
int send_flags = MSG_NOSIGNAL | MSG_DONTWAIT | ((header->nextpage) ? MSG_MORE : 0);
int ret = send(connection_socket_fd, pagedata + upload_queue_p, need_to_send, send_flags);
if (ret > 0) upload_queue_p += ret;
if (ret == 0) print_error_and_exit("send вернул 0");
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
fprintf(stderr, "Буфер отправления переполнился\n");
} else {
perror("send");
fprintf(stderr, "Ошибка соединения, через некоторое время откроем снова\n");
close(connection_socket_fd);
connection_buffers_reset();
};
};
if (ret == need_to_send) {
//fprintf(stderr, "Отправлена страница %x\n", (unsigned int)upload_queue_first);
// страница отправлена, теперь нужно освободить память
void *prev_page = upload_queue_first;
upload_queue_bytes_in_use -= sizeof(struct upload_page_header) + header->datasize;
upload_queue_first = header->nextpage;
//fprintf(stderr, "Следующая страница %x\n", (unsigned int)upload_queue_first);
free(prev_page);
prev_page = NULL;
upload_queue_p = 0;
if (upload_queue_first == NULL) upload_queue_last = NULL;
if (upload_queue_first == NULL) fprintf(stderr, "Сообщение ушло, очередь отправки пуста\n");
if (upload_queue_bytes_in_use != 0) fprintf(stderr, "Утечка памяти\n");
};
};
uint32_t hphone_get_value (uint8_t *pointer) {
uint32_t lo = ntohl(*((uint32_t *)pointer));
return lo;
};
// эта функция заполняет поля структуры parsed_header
void hphone_parse_header(uint8_t *pointer) {
parsed_header.signature_ok = (strncmp((char *)pointer, "HPHO", 4) == 0) ? 1 : 0;
parsed_header.bodysize = hphone_get_value(pointer+4);
parsed_header.msg_type = hphone_get_value(pointer+8);
parsed_header.additional_size = hphone_get_value(pointer+12);
};
void list_options () {
print_error_and_exit("Неправильно задана опция, вот список опций:\n\n"
"-bind PORT\n-bindclearnet PORT - открыть порт на localhost или на всех интерфейсах\n"
"-tor ONIONADDRESS PORT - подключиться к onion адресу через Тор\n"
"-clearnet IPADDERSS PORT - подключиться напрямую к порту\n"
"-pass PASSWORD - пароль для проверки\n"
"-pd PLAYBACKDEVICE\n"
"-rd RECORDDEVICE - выбор аудиокарты, напр. plughw:1\n"
"-dontclose - не закрывать карту после записи. Помогает, если карта медленно включается\n"
"-mp2save - создавать mp2 файлы с входящими сообщениями\n"
"-heartbeat - посылать heartbeat сообщения для проверки соединения\n"
"-normalize - пока не реализованная функция,\n"
" автоматически поднимает громкость сообщения перед отправкой\n"
"-nobutton - выключить кнопку\n");
};
void * hphone_make_upload_page (long int size) {
void *page = malloc(sizeof(struct upload_page_header) + size);
upload_queue_bytes_in_use += sizeof(struct upload_page_header) + size;
if (!page) print_error_and_exit("Память не выделяется");
struct upload_page_header *header = page;
header->datasize = size;
header->nextpage = NULL;
return page;
};
void hphone_send_header (uint32_t body_size, uint32_t msg_type, uint32_t info_size) {
uint8_t *page = hphone_make_upload_page(16);
uint8_t *pagedata = page+sizeof(struct upload_page_header);
strcpy((char *)pagedata, "HPHO");
*((uint32_t *)(pagedata+4)) = htonl(body_size);
*((uint32_t *)(pagedata+8)) = htonl(msg_type);
*((uint32_t *)(pagedata+12)) = htonl(info_size);
// Кусок памяти заполнен, теперь его надо поставить в очередь на отправку
insert_in_upload_queue(page);
};
void reconnect_socket (void) {
if (connection_works) {
fprintf(stderr, "Старое соединение закрывается\n");
close(connection_socket_fd);
connection_buffers_reset();
};
if (mode != CONNECT) print_error_and_exit("Ошибка режима");
connection_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in connect_sockaddr_in;
connect_sockaddr_in.sin_family = AF_INET;
int ret;
if (clearnet == TOR) {
connect_sockaddr_in.sin_port = htons(tor_port);
ret = inet_aton("127.0.0.1", &connect_sockaddr_in.sin_addr);
};
if (clearnet == CLEARNET) {
fprintf(stderr, "Осторожно CLEARNET, шифрование выключено\n");
connect_sockaddr_in.sin_port = htons(clearnet_port);
ret = inet_aton(clearnet_ip, &connect_sockaddr_in.sin_addr);
};
if (!ret) print_error_and_exit("IP не валидный");
fprintf(stderr, "Подсоединяюсь к порту %i, по адресу %s\n", ntohs(connect_sockaddr_in.sin_port),
inet_ntoa(connect_sockaddr_in.sin_addr));
ret = connect(connection_socket_fd, (struct sockaddr *) &connect_sockaddr_in, sizeof (connect_sockaddr_in));
if (ret == -1) {
if (errno == ECONNREFUSED) {
fprintf(stderr, "Соединение сброшено, будет повторная попытка\n");
close(connection_socket_fd);
if (clearnet == TOR && tor_port == TOR_PORT_FIRST) {
tor_port = TOR_PORT_SECOND; // если на 9150 не получилось, сразу же пробуем на 9050
reconnect_socket();
return;
};
} else if (errno == EINPROGRESS) {
fprintf(stderr, "Асинхронное открытие.\n");
connection_works = 1;
} else {
perror("connect");
fprintf(stderr, "Ошибка при открытии соединения\n");
}
} else {
connection_works = 1;
fprintf(stderr, "Соединение принято\n");
}
uint16_t socks4a_port;
char socks4a_header[] = "\x04\x01\x00\x50\x00\x00\x00\x01user";
uint8_t socks_response[9];
uint8_t *response_byte;
int oniondomain_length = strlen(oniondomain)+1;
if (connection_works) {
if (clearnet == TOR) {
fprintf(stderr, "Посылаю заголовок SOCKSv4a\n");
socks4a_port = htons(onion_target_port);
memcpy(socks4a_header+2, &socks4a_port, 2);
ret = send(connection_socket_fd, socks4a_header, sizeof(socks4a_header), MSG_MORE);
if (ret < sizeof(socks4a_header)) print_error_and_exit("Не отправляется");
ret = send(connection_socket_fd, oniondomain, oniondomain_length, 0);
if (ret < oniondomain_length) print_error_and_exit("Слишком длинный адрес");
memset(socks_response, 0, 9);
ret = recv(connection_socket_fd, socks_response, 8, MSG_WAITALL);
if (ret < 8) print_error_and_exit("Ответ не читается");
response_byte = socks_response+1;
switch(*response_byte) {
case 0x5A: fprintf(stderr, "Запрос принят\n");
break;
case 0x5B: print_error_and_exit("SOCKS запрос отклонен");
break;
default: print_error_and_exit("SOCKS непонятный ответ");
};
};
fprintf(stderr, "Посылаю 2048 байт с паролем\n");
hphone_send_header(2048 - 16, HIDDENPHONE_MSG_AUTH_PASSWORD, 0);
void *intro_page = hphone_make_upload_page (2048 - 16);
void *intro_page_data = intro_page + sizeof(struct upload_page_header);
memset(intro_page_data, '\0', 2048 - 16);
strncpy((char *)intro_page_data, password, 2048-50);
insert_in_upload_queue(intro_page);
if (enableheartbeat) hphone_send_header(0, HIDDENPHONE_MSG_HEARTBEAT_ENABLE,0);
};
};
#include "hp_key.c"
#include "hp_audio.c"
int main (int argc, char **argv) {
int ret, i;
int buffer_state;
void *decoded_sound = NULL;
char *record_device_name = "default";
char *playback_device_name = "default";
void *encoded_sound_first_page = NULL;
long int encoded_sound_size = 0;
avcodec_register_all();
// парсим опции
for (i = 1; i < argc; i++) {
if (strncmp(argv[i], "-bind", 5) == 0) {
if (!argv[i+1]) list_options();
if (atol(argv[i+1]) > 65535) print_error_and_exit("Номер порта слишком большой");
bind_port = atol(argv[i+1]);
if (bind_port <= 0) print_error_and_exit("Недопустимый номер");
mode = BIND;
if (strcmp(argv[i], "-bindclearnet") == 0) clearnet = CLEARNET;
i++;
continue;
};
if (strcmp(argv[i], "-pass") == 0) {
if (!argv[i+1]) list_options();
password = argv[i+1];
i++;
continue;
};
if (strcmp(argv[i], "-clearnet") == 0) {
if (!argv[i+1]) list_options();
if (!argv[i+2]) list_options();
clearnet_ip = argv[i+1];
if (atol(argv[i+2]) > 65535) print_error_and_exit("Номер порта слишком большой");
clearnet_port = atol(argv[i+2]);
if (clearnet_port <= 0) print_error_and_exit("Недопустимый номер");
mode = CONNECT;
clearnet = CLEARNET;
i+=2;
continue;
};
if (strcmp(argv[i], "-tor") == 0) {
if (!argv[i+1]) list_options();
if (!argv[i+2]) list_options();
oniondomain = argv[i+1];
if (atol(argv[i+2]) > 65535) print_error_and_exit("Номер порта слишком большой");
onion_target_port = atol(argv[i+2]);
mode = CONNECT;
clearnet = TOR;
i+=2;
continue;
};
if (strcmp(argv[i], "-rd") == 0) {
if (!argv[i+1]) list_options();
record_device_name = argv[i+1];
i++;
continue;
};
if (strcmp(argv[i], "-pd") == 0) {
if (!argv[i+1]) list_options();
playback_device_name = argv[i+1];
i++;
continue;
};
if (strcmp(argv[i], "-dontclose") == 0) {
dont_close_read_device = 1;
continue;
};
if (strcmp(argv[i], "-nobutton") == 0) {
disablebutton = 1;
continue;
};
if (strcmp(argv[i], "-heartbeat") == 0) {
enableheartbeat = 1;
continue;
};
if (strcmp(argv[i], "-normalize") == 0) {
normalize = 1;
continue;
};
if (strcmp(argv[i], "-mp2save") == 0) {
mp2save = 1;
continue;
};
fprintf(stderr, "Неправильная опция %s\n", argv[i]);
list_options();
};
// ставим ловушку на клавишу в Xlib
int xlib_pipe[2];
if (pipe(xlib_pipe) != 0) print_error_and_exit("Пайп не открывается");
if (!disablebutton) {
xlib_pid = fork();
if (xlib_pid == 0) {
hphone_monitor_key(xlib_pipe[1]);
exit(0); // Заканчиваю дочерний процесс
} else fprintf(stderr, "Дочерний процесс %i мониторит кнопку\n", xlib_pid);
};
struct sockaddr_in local_sockaddr_in;
if (mode == BIND) {
local_sockaddr_in.sin_family = AF_INET;
local_sockaddr_in.sin_port = htons(bind_port);
if (clearnet == TOR) {
ret = inet_aton("127.0.0.1", &local_sockaddr_in.sin_addr);
if (!ret) print_error_and_exit("IP не валидный");
} else if (clearnet == CLEARNET) {
fprintf(stderr, "Осторожно CLEARNET, порт будет виден с соседних компьютеров\n");
local_sockaddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
};
server_socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
fprintf(stderr, "Открываю порт %i, на адресе %s\n", ntohs(local_sockaddr_in.sin_port),
inet_ntoa(local_sockaddr_in.sin_addr));
ret = bind(server_socket_fd, (struct sockaddr *) &local_sockaddr_in, sizeof(local_sockaddr_in));
if (ret) {
perror("bind");
print_error_and_exit("Порт не привязывается");
};
ret = listen(server_socket_fd, 5);
if (ret) {
perror("listen");
print_error_and_exit("Порт не открывается на прием");
};
};
if (mode == CONNECT) {
reconnect_socket();
};
if (mode == NOPE) fprintf(stderr, "Вы не задали режим работы, соединения не будет.\n"
"Используйте опции -tor или -bind\n");
fd_set read_fd_kit, write_fd_kit;
struct timeval time_value;
char c = ' ';
while (1) {
// главный цикл
FD_ZERO(&read_fd_kit);
FD_ZERO(&write_fd_kit);
FD_SET(xlib_pipe[0], &read_fd_kit);
if (connection_works) FD_SET(connection_socket_fd, &read_fd_kit);
if (mode == BIND) FD_SET(server_socket_fd, &read_fd_kit);
if (intro_works) FD_SET(intro_socket_fd, &read_fd_kit);
if (upload_queue_first && connection_works) FD_SET(connection_socket_fd, &write_fd_kit);
max_fd = xlib_pipe[0];
if (connection_socket_fd > max_fd) max_fd = connection_socket_fd;
if (server_socket_fd > max_fd) max_fd = server_socket_fd;
if (intro_socket_fd > max_fd) max_fd = intro_socket_fd;
time_value.tv_sec = (mode == BIND) ? 40 : 60;
time_value.tv_usec = 0;
//fprintf(stderr, "Захожу в select\n");
ret = select(max_fd+1, &read_fd_kit, &write_fd_kit, NULL, &time_value);
if (ret == -1) {
perror("select()");
print_error_and_exit("Ошибка с дескрипторами");
};
if (ret == 0 && temporary_enableheartbeat) {
if (mode == BIND && connection_works) {
fprintf(stderr, "Посылаю heartbeat\n");
hphone_send_header(0, HIDDENPHONE_MSG_HEARTBEAT, 0);
};
if (mode == CONNECT) {
noheartbeat_count++;
if (noheartbeat_count == 3 && connection_works) hphone_send_header(0, HIDDENPHONE_MSG_HEARTBEAT_PLEASE, 0);
if (noheartbeat_count > 5) {
noheartbeat_count = 0;
reconnect_socket();
};
};
};
if (mode == CONNECT && !connection_works) {
if (tor_port == TOR_PORT_SECOND) tor_port = TOR_PORT_FIRST;
reconnect_socket();
}
if (intro_works && FD_ISSET(intro_socket_fd, &read_fd_kit)) {
fprintf(stderr, "На пригласительный сокет пришла порция данных\n");
buffer_state = hphone_try_to_read(intro_socket_fd, intro_buffer, &intro_buffer_p, sizeof(intro_buffer));
if (buffer_state == BUFFER_STATE_ERROR_FD_CLOSED) {
intro_works = 0;
intro_buffer_p = 0;
};
if (buffer_state == BUFFER_STATE_READY) {
// проверка пароля
intro_buffer[2047] = '\0';
hphone_parse_header(intro_buffer);
if (parsed_header.signature_ok &&
parsed_header.bodysize == 2048 - 16 &&
parsed_header.msg_type == HIDDENPHONE_MSG_AUTH_PASSWORD &&
parsed_header.additional_size == 0 &&
strcmp((char *)intro_buffer+16, password) == 0 ) {
fprintf(stderr, "Пароль правильный\n");
if (connection_works) {
fprintf(stderr, "Закрываю старое звуковое соединение, начинаю использовать новое\n");
connection_buffers_reset();
ret = close(connection_socket_fd);
if (ret) fprintf(stderr, "Предыдущий звуковой сокет при закрытии сообщил о какой-то фигне\n");
};
connection_socket_fd = intro_socket_fd;
intro_works = 0;
connection_works = 1;
hphone_send_header(0, HIDDENPHONE_MSG_PASSWORD_OK, 0);
temporary_enableheartbeat = enableheartbeat;
if (enableheartbeat) hphone_send_header(0, HIDDENPHONE_MSG_HEARTBEAT_ENABLE,0);
continue; // Нужно заново запустить select
} else {
fprintf(stderr, "Проверка не пройдена, либо пароль неправильный, либо протокол чужой\n");
intro_works = 0;
intro_buffer_p = 0;
close(intro_socket_fd);
};
};
};
if (mode == BIND && FD_ISSET(server_socket_fd, &read_fd_kit)) {
fprintf(stderr, "Входящее соединение, принимаю на пригласительный сокет\n");
if (intro_works) {
fprintf(stderr, "Предыдущий пригласительный сокет не отработал, заменяю на этот\n");
ret = close(intro_socket_fd);
if (ret) fprintf(stderr, "Предыдущий пригласительный сокет при закрытии сообщил о какой-то фигне\n");
intro_works = 0;
intro_buffer_p = 0;
};
intro_socket_fd = accept(server_socket_fd, NULL, NULL);
if (intro_socket_fd == -1) fprintf(stderr, "Соединение не удалось\n");
else {
intro_works = 1;
intro_buffer_p = 0;
};
};
if (connection_works && FD_ISSET(connection_socket_fd, &read_fd_kit)) {
//fprintf(stderr, "select чтение на звуковом сокете\n");
if (download_header_buffer_p < sizeof(download_header_buffer)) {
// читаем заголовок
buffer_state = hphone_try_to_read(connection_socket_fd, download_header_buffer,
&download_header_buffer_p, sizeof(download_header_buffer));
if (buffer_state == BUFFER_STATE_ERROR_FD_CLOSED) {
connection_buffers_reset();
};
if (buffer_state == BUFFER_STATE_READY) {
hphone_parse_header(download_header_buffer);
if (!parsed_header.signature_ok) print_error_and_exit("Сигнатура не сходится");
switch (parsed_header.msg_type) {
case HIDDENPHONE_MSG_PASSWORD_OK:
fprintf(stderr, "Пароль подошел\n");
break;
case HIDDENPHONE_MSG_SOUND:
fprintf(stderr, "На подходе звуковое сообщение, длина %li байт\n", (long int) parsed_header.bodysize);
break;
case HIDDENPHONE_MSG_SOUND_ARRIVED:
fprintf(stderr, "Звук доставлен\n");
break;
case HIDDENPHONE_MSG_HEARTBEAT:
fprintf(stderr, "Пришел heartbeat, отвечаю\n");
noheartbeat_count = 0;
hphone_send_header(0, HIDDENPHONE_MSG_HEARTBEAT_OK, 0);
break;
case HIDDENPHONE_MSG_HEARTBEAT_OK:
fprintf(stderr, "Пришел ответ на heartbeat\n");
break;
case HIDDENPHONE_MSG_HEARTBEAT_PLEASE:
fprintf(stderr, "Клиент просит отправить heartbeat, отправляю\n");
hphone_send_header(0, HIDDENPHONE_MSG_HEARTBEAT, 0);
break;
case HIDDENPHONE_MSG_HEARTBEAT_ENABLE:
fprintf(stderr, "Другой конец попросил включить heartbeat\n");
temporary_enableheartbeat = 1;
break;
default:
fprintf(stderr, "Неизвестный тип сообщения %li\n", (long int) parsed_header.msg_type);
break;
};
if (parsed_header.bodysize > 0) {
if (download_body_buffer) print_error_and_exit("Буфер не освобожден");
download_body_buffer = malloc(parsed_header.bodysize + (FF_INPUT_BUFFER_PADDING_SIZE * 2));
if (!download_body_buffer) print_error_and_exit("Память не выделяется");
download_body_buffer_size = parsed_header.bodysize;
download_body_additional_info_size = parsed_header.additional_size;
download_message_type = parsed_header.msg_type;
download_body_buffer_p = 0;
} else {
download_header_buffer_p = 0; // буфер готов к следующему заголовку
};
};
} else {
// читаем тело сообщения
if (!download_body_buffer) print_error_and_exit("Буфера нет");
buffer_state = hphone_try_to_read(connection_socket_fd, download_body_buffer,
&download_body_buffer_p, download_body_buffer_size);
if (buffer_state == BUFFER_STATE_ERROR_FD_CLOSED) {
connection_buffers_reset();
};
if (buffer_state == BUFFER_STATE_READY) {
if (download_message_type == HIDDENPHONE_MSG_SOUND) {
//body_to_file(stdout ,download_body_buffer,
// download_body_buffer_size, download_body_additional_info_size);
decoded_sound = hphone_decode_buffer(download_body_buffer,
download_body_buffer_size, download_body_additional_info_size);
if (decoded_sound) {
if (mp2save) save_to_mp2file(download_body_buffer, download_body_buffer_size,
download_body_additional_info_size);
play_link_sound(decoded_sound, playback_device_name);
discard_link_sound(&decoded_sound);
} else fprintf(stderr, "Не получилось декодировать\n");
};
if (decoded_sound) discard_link_sound(decoded_sound);
free(download_body_buffer);
download_body_buffer = NULL;
download_header_buffer_p = 0;
download_body_buffer_size = 0;
};
};
};
if ((upload_queue_first != NULL) && FD_ISSET(connection_socket_fd, &write_fd_kit)) {
//fprintf(stderr, "select запись на звуковом сокете\n");
send_one_upload_page();
};
if (FD_ISSET(xlib_pipe[0], &read_fd_kit)) {
read(xlib_pipe[0], &c, 1);
if (c == 'B') {
fprintf(stderr, "Нажата кнопка\n");
if (upload_queue_first || !connection_works) {
if (!connection_works) fprintf(stderr, "Соединения нет, отсечка\n");
else fprintf(stderr, "Предыдущее сообщение еще не ушло, отсечка\n");
continue;
};
encoded_sound_first_page = hphone_record_normalize_encode
(xlib_pipe[0], record_device_name, &encoded_sound_size);
if (!encoded_sound_first_page) {
fprintf(stderr, "Отправление отменено\n");
continue;
};
fprintf(stderr, "Отправляю\n");
hphone_send_header(encoded_sound_size, HIDDENPHONE_MSG_SOUND, 0);
insert_in_upload_queue(encoded_sound_first_page);
} else fprintf(stderr, "Кнопка отпущена\n");
};
};
return 0;
};hp_audio.c
// Файл hp_audio.c
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096
// Эта функция предназначена для экспериментов
void *get_link_audio_from_stdin(int page_size, long int read_size) {
fprintf(stderr, "Подайте на вход сырой поток 48000 mono s16 l-e\n");
void *first_page_struct = NULL;
struct audio_page_struct *prev_page = NULL;
struct audio_page_struct *struc;
long int n = 0;
int n2;
int ret, bytes_needed;
// мы делаем линкованый список
while (n < read_size) {
struc = malloc(sizeof(struct audio_page_struct));
raw_audio_bytes_in_use += sizeof(struct audio_page_struct);
if (!struc) print_error_and_exit("Память не выделяется struc");
//fprintf(stderr, "Создана структура страницы по адресу %x\n", (unsigned int)struc);
((*struc).samples) = malloc(page_size);
raw_audio_bytes_in_use += page_size;
if (!(*struc).samples) print_error_and_exit("Память не выделяется .samples");
if (prev_page) {
(*prev_page).next_page = struc;
};
if (!first_page_struct) first_page_struct = struc;
// буфер готов, пора его заполнить
bytes_needed = page_size;
n2 = 0;
while (bytes_needed > 0) {
ret = fread((*struc).samples+n2, 1, bytes_needed, stdin);
n2 += ret;
(*struc).samples_size = n2;
n += ret;
bytes_needed -= ret;
if (ret == 0) break;
};
//fprintf(stderr, "Страница содержит %i байт звука.\n", (*struc).samples_size);
if (feof(stdin)) break;
if (ferror(stdin)) print_error_and_exit("Ошибка входного потока");
(*struc).next_page = NULL;
prev_page = struc;
};
fprintf(stderr, "Принято %li байт.\n", n);
return first_page_struct;
};
// эта функция отправляет тело сообщения в файл
void body_to_file (FILE *f, uint8_t *ptr, long int size, long int additional_info_size) {
long int need_to_send;
int ret;
long int buffer_p = additional_info_size; // Перепрыгиваем сразу к данным
while (buffer_p < size) {
need_to_send = size - buffer_p;
if (need_to_send > 2048) need_to_send = 2048;
ret = fwrite(ptr + buffer_p, 1, need_to_send, f);
buffer_p += ret;
if (ret == 0) print_error_and_exit("Какая-то фигня");
};
};
int record_check_button (int stop_pipe) {
char c;
struct timeval time1;
time1.tv_sec = 0;
time1.tv_usec = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(stop_pipe, &readfds);
int ret = select(stop_pipe+1, &readfds, NULL, NULL, &time1);
if (ret > 0) read(stop_pipe, &c, 1);
if (c == 'R') return 1;
return 0;
};
// Внимание, samples_size указывает не на размер страницы, а количество занятых байт в ней
void *create_audio_page (int page_size, struct audio_page_struct **prev_struc) {
struct audio_page_struct *struc = malloc(sizeof(struct audio_page_struct));
raw_audio_bytes_in_use += sizeof(struct audio_page_struct);
if (!struc) print_error_and_exit("Память не выделяется");
//fprintf(stderr, "Создана структура страницы по адресу %x\n", (unsigned int)struc);
(struc->samples) = malloc(page_size);
raw_audio_bytes_in_use += page_size;
if (!struc->samples) print_error_and_exit("Память не выделяется .samples");
struc->page_size = page_size;
struc->samples_size = 0;
struc->next_page = NULL;
if (!prev_struc) return struc;
if (*prev_struc) (*prev_struc)->next_page = struc;
*prev_struc = struc;
return struc;
};
void discard_link_sound (void **sound) {
if (!sound || !*sound) print_error_and_exit("Нуль на входе, освобождать нечего");
//fprintf(stderr, "Освобождаю raw звук\n");
struct audio_page_struct *runner = *sound;
struct audio_page_struct *nextrunner;
while (runner) {
raw_audio_bytes_in_use -= runner->page_size;
free(runner->samples);
nextrunner = runner->next_page;
raw_audio_bytes_in_use -= sizeof(struct audio_page_struct);
free(runner);
runner = nextrunner;
};
*sound = NULL;
if (raw_audio_bytes_in_use != 0) {
fprintf(stderr, "Утечка памяти где-то\n");
fprintf(stderr, "Занято байт %li\n", raw_audio_bytes_in_use);
};
};
void save_to_mp2file (uint8_t *buf, long int bufsize, long int addsize) {
FILE *fileptr;
char filename[30];
int filename_int = 0;
fprintf(stderr, "Сохраняю в файл >");
while (1) {
snprintf(filename, 28, "message%04i.mp2", filename_int);
//fprintf(stderr, "%s? \n", filename);
if (access(filename, F_OK) == -1) break;
filename_int++;
}
fprintf(stderr, "%s\n", filename);
fileptr = fopen(filename, "wb");
if (!fileptr) print_error_and_exit("Файл не открывается на запись");
body_to_file(fileptr, buf, bufsize, addsize);
fclose(fileptr);
};
void *hphone_record_sound (int stop_pipe, char *record_device_name, int page_size) {
if (record_check_button(stop_pipe)) return NULL;
if ((page_size & 1) == 1) print_error_and_exit("Размер страницы не четный");
fprintf(stderr, "Запись\n");
int err;
if (!recorddevice_handle) {
err = snd_pcm_open(&recorddevice_handle, record_device_name, SND_PCM_STREAM_CAPTURE, 0);
if (err < 0) {
fprintf(stderr, "Capture open error: %s\n", snd_strerror(err));
print_error_and_exit("Звуковая карта не открывается");
};
err = snd_pcm_set_params(recorddevice_handle,
SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
100000);
if (err < 0) {
fprintf(stderr, "Capture open error: %s\n", snd_strerror(err));
//print_error_and_exit("Параметры не задаются");
};
};
struct audio_page_struct *struc = NULL;
struct audio_page_struct *prev_struc = NULL;
struct audio_page_struct *first_struc = NULL;
snd_pcm_sframes_t frames_read;
long int audiopage_i, need_frames;
snd_pcm_prepare(recorddevice_handle);
while (1) {
struc = create_audio_page(page_size, &prev_struc);
if (!first_struc) first_struc = struc;
struc->samples_size = page_size;
// страница создана
audiopage_i = 0;
while (audiopage_i < page_size) {
need_frames = (page_size - audiopage_i) / 2;
//fprintf(stderr, "Запрашиваю %li фреймов\n", need_frames);
frames_read = snd_pcm_readi(recorddevice_handle, ((uint8_t *)struc->samples)+audiopage_i, need_frames);
if (frames_read < 0) frames_read = snd_pcm_recover(recorddevice_handle, frames_read, 0);
if (frames_read < 0) {
printf("snd_pcm_readi failed: %s\n", snd_strerror(frames_read));
print_error_and_exit("Ошибка при записи");
};
if (frames_read == 0) print_error_and_exit("snd_pcm_readi не читает");
audiopage_i += frames_read * 2;
};
fprintf(stderr, ".");
if (record_check_button(stop_pipe)) break;
};
fprintf(stderr, "\n");
snd_pcm_drop(recorddevice_handle);
if (!dont_close_read_device) {
snd_pcm_close(recorddevice_handle);
recorddevice_handle = NULL;
};
return first_struc;
};
void hphone_normalize_volume (struct audio_page_struct *struc) {
// этот кусок кода напишите самостоятельно
};
void store_encoded_packet (AVPacket pkt, void **first_encoded_page, void **last_encoded_page) {
uint8_t *newpage = hphone_make_upload_page(pkt.size);
void *pagedata = newpage + sizeof(struct upload_page_header);
memcpy(pagedata, pkt.data, pkt.size);
struct upload_page_header *prevpageheader;
if (*last_encoded_page) {
prevpageheader = *last_encoded_page;
prevpageheader->nextpage = newpage;
};
*last_encoded_page = newpage;
if (!(*first_encoded_page)) *first_encoded_page = newpage;
};
void *hphone_record_normalize_encode (int stop_pipe, char *record_device_name, long int *encoded_size) {
void *first_encoded_page = NULL;
void *last_encoded_page = NULL;
*encoded_size = 0;
// Нужно выяснить размер страницы, удобный для кодека
int ret;
AVCodec *codec;
AVCodecContext *c = NULL;
AVFrame *frame;
AVPacket pkt;
int got_output;
codec = avcodec_find_encoder(AV_CODEC_ID_MP2);
if (!codec) print_error_and_exit("Кодек MP2 не найден");
c = avcodec_alloc_context3(codec);
c->bit_rate = 128000;
fprintf(stderr, "Кодек MP2, битрейт = %i бит/сек\n", c->bit_rate);
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->sample_rate = 48000;
c->channel_layout = AV_CH_LAYOUT_MONO;
c->channels = 1;
if (avcodec_open2(c, codec, NULL) < 0) print_error_and_exit("Кодек не открывается");
int page_size = av_samples_get_buffer_size(NULL, c->channels, c->frame_size,
c->sample_fmt, 0);
fprintf(stderr, "Размер страницы для кодека %i байт\n", page_size);
// Теперь запуск записи
void *link_sound = NULL;
//link_sound = get_link_audio_from_stdin(page_size, 40000000);
link_sound = hphone_record_sound(stop_pipe, record_device_name, page_size);
if (!link_sound) {
avcodec_close(c);
av_free(c);
av_frame_free(&frame);
return first_encoded_page;
};
if (normalize) hphone_normalize_volume(link_sound);
// Энкодинг
fprintf(stderr, "Энкодинг\n");
frame = av_frame_alloc();
if (!frame) print_error_and_exit("Структура не создается");
frame->nb_samples = c->frame_size;
frame->format = c->sample_fmt;
frame->channel_layout = c->channel_layout;
//fprintf(stderr, "codec frame size = %i\n", c->frame_size);
struct audio_page_struct *page = link_sound;
while (page) {
frame->nb_samples = (*page).samples_size / 2;
//fprintf(stderr, "Задаю указатели: адрес %x, размер %i, nb %i\n",
// (unsigned int)(*page).samples, page_size, frame->nb_samples);
ret = avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
(const uint8_t*)(*page).samples, page_size, 0);
if (ret < 0) print_error_and_exit("Указатели не задаются");
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
ret = avcodec_encode_audio2(c, &pkt, frame, &got_output);
if (ret < 0) print_error_and_exit("Ошибка кодирования");
if (got_output) {
store_encoded_packet(pkt, &first_encoded_page, &last_encoded_page);
*encoded_size += pkt.size;
av_free_packet(&pkt);
};
page = (*page).next_page;
};
avcodec_close(c);
av_free(c);
av_frame_free(&frame);
discard_link_sound(&link_sound);
return first_encoded_page;
};
void play_link_sound (void *link_sound, char *playback_device_name) {
if (!link_sound) print_error_and_exit("Нулевой указатель на входе");
int ret;
struct audio_page_struct *page = link_sound;
unsigned long int frame_count;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
if ((ret = snd_pcm_open(&handle, playback_device_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
print_error_and_exit("Выходная аудиокарта не открывается, проверьте имя аудиокарты");
};
ret = snd_pcm_set_params(handle,
SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000);
if (ret < 0) print_error_and_exit("Выходная аудиокарта, параметры не ставятся");
fprintf(stderr, "Воспроизвожу звук\n");
snd_pcm_prepare(handle);
while (page) {
//fprintf(stderr, "Читаю страницу по адресу %x\n", (unsigned int)page);
frame_count = (*page).samples_size / 2; // в каждом фрейме по два байта
frames = snd_pcm_writei(handle, (*page).samples, frame_count);
if (frames < 0) frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) print_error_and_exit("Аудиокарта, звук не отправляется");
if (frames > 0 && frames < frame_count) fprintf(stderr, "Звук отправлен на аудиокарту не полностью,\n"
"отправлено %li фреймов, а нужно %li фреймов\n", frame_count, frames);
page = (*page).next_page;
};
snd_pcm_drain(handle); // жду, пока звук доиграет
snd_pcm_close(handle);
fprintf(stderr, "Готово\n");
};
void hphone_make_beep (int tone, int sample_rate, struct audio_page_struct *audiopage) {
if (!audiopage || !audiopage->samples ||
sample_rate <= 0 || audiopage->page_size <= 0) print_error_and_exit("Ошибка make beep");
memset(audiopage->samples, 0x00, audiopage->page_size);
int period = sample_rate / tone;
int half_period = period / 2;
int i = 0;
int buffer_p = 0;
int16_t *writehead = audiopage->samples;
while (buffer_p < (audiopage->page_size)) {
*writehead = (i > half_period) ? -60000 : 60000;
i++;
if (i >= period) i = 0;
buffer_p += 2;
writehead++;
};
audiopage->samples_size = audiopage->page_size;
};
void *hphone_decode_buffer (uint8_t *buffer, long int size, long int additional_size) {
long int buffer_p = 0;
struct audio_page_struct *decoded_page = NULL;
struct audio_page_struct *decoded_first_page = NULL;
struct audio_page_struct *decoded_prev_page = NULL;
AVCodec *codec;
AVCodecContext *c= NULL;
int len;
//uint8_t inbuf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
AVPacket avpkt;
AVFrame *decoded_frame = NULL;
av_init_packet(&avpkt);
printf("Декодирую\n");
codec = avcodec_find_decoder(AV_CODEC_ID_MP2);
if (!codec) print_error_and_exit("Кодек MP2 не найден");
c = avcodec_alloc_context3(codec);
if (avcodec_open2(c, codec, NULL) < 0) print_error_and_exit("Кодек не открывается");
buffer_p = additional_size;
avpkt.data = buffer+buffer_p;
avpkt.size = (size - buffer_p > 4096) ? 4096 : size - buffer_p;
while (avpkt.size > 0) {
int got_frame = 0;
if (!decoded_frame) {
if (!(decoded_frame = av_frame_alloc())) print_error_and_exit("Память не выделяется");
};
len = avcodec_decode_audio4(c, decoded_frame, &got_frame, &avpkt);
if (len < 0) {
fprintf(stderr, "Ошибка декодирования\n");
break;
};
if (got_frame) {
if (!decoded_first_page) {
// Сигнал о начале сообщения
decoded_first_page = create_audio_page((48000 / 10)*2, &decoded_prev_page);
hphone_make_beep(2000, 48000, decoded_first_page);
};
int data_size = av_samples_get_buffer_size(NULL, c->channels,
decoded_frame->nb_samples,
c->sample_fmt, 1);
decoded_page = create_audio_page(data_size, &decoded_prev_page);
if (!decoded_first_page) decoded_first_page = decoded_page;
memcpy(decoded_page->samples, decoded_frame->data[0], data_size);
decoded_page->samples_size = data_size;
};
avpkt.size -= len;
avpkt.data += len;
buffer_p += len;
if (avpkt.size < AUDIO_REFILL_THRESH) {
// Наполнить буфер
avpkt.size = (size - buffer_p > 4096) ? 4096 : size - buffer_p;
};
};
if (decoded_first_page) {
// Сигнал о конце сообщения
decoded_page = create_audio_page((48000 / 10)*2, &decoded_prev_page);
hphone_make_beep(1000, 48000, decoded_page);
};
return decoded_first_page;
};hp_key.c
// Файл hp_key.c
#define HPHONE_KEY XK_F12
void hphone_monitor_key(int pipe_fd) {
Display *d;
XEvent event;
int autorepeat_down_pending, i;
struct timespec little_timeout = { 0, 10000000 };
fprintf(stderr, "Для включения записи нажмите %s\n", XKeysymToString(HPHONE_KEY));
d = XOpenDisplay(NULL);
if ( d != NULL ) {
XGrabKey(d, XKeysymToKeycode(d, HPHONE_KEY), AnyModifier,
DefaultRootWindow(d), True, GrabModeAsync, GrabModeAsync);
autorepeat_down_pending = 0;
while (1) {
XNextEvent(d, &event);
if ( event.type == KeyPress ) {
KeySym s = XLookupKeysym(&event.xkey, 0);
if ( s == HPHONE_KEY ) {
if (autorepeat_down_pending) autorepeat_down_pending = 0;
else {
//fprintf(stderr, "Клавиша нажата\n");
write(pipe_fd, "B", 1);
};
};
};
if ( event.type == KeyRelease ) {
KeySym s = XLookupKeysym(&event.xkey, 0);
if ( s == HPHONE_KEY ) {
// этот код обнаруживает автоповторы и вычищает их
for (i = 0; i < 3; i++) {
nanosleep(&little_timeout, NULL);
if (XEventsQueued(d, QueuedAfterReading)) {
autorepeat_down_pending = 1;
break;
};
};
if (!autorepeat_down_pending) {
//fprintf(stderr, "Клавиша отпущена\n");
write(pipe_fd, "R", 1);
};
};
};
};
};
};Как это запустить?
Программу можно запустить только на Linux.
Программа для запуска требует, чтобы в системе стояли некоторые библиотеки, их нужно установить. Для этого выполните следующие две команды от имени супер-пользователя:
apt-get update
apt-get install libavcodec-dev libavutil-dev libasound2-dev libx11-devЭта команда скачает из интернета все что нужно и установит библиотеки.
Теперь создайте папку с тремя файлами исходного кода, наполните их кодом.
Компилируем
gcc -Wall hp.c -lasound -lavcodec -lavutil -lX11 -o hpКомпиляция прошла без ошибок? Хорошо. В результате у вас появился исполняемый файл hp который мы сейчас запустим:
./hpЧтобы остановить программу, достаточно нажать Ctrl+C
Примеры использования
Программа может быть сервером или клиентом. Для начала определитесь, где будет сервер, а где клиент. Сервер может обслуживать клиентов только по одному.
Чтобы запустить программу в режиме сервера:
./hp -bind 42232 -pass xyzzyНа локалхосте открывается порт для входящих соединений, порт виден только для программ, работающих на этом-же компьютере. Другая команда открывает порт на всех интерфейсах, порт будет виден с соседних компьютеров:
./hp -bindclearnet 42232Слово clearnet означает, что трафик попрет не через Tor, а напрямую без шифрования по белому интернету, чтобы открыть сервер в Tor, следует открывать порт только на локалхосте.
На том же компьютере, где запущен сервер, нужно запустить Tor и настроить HiddenService. Процесс Тора будет ловить соединения из луковой сети и перенаправлять их на наш порт.
При запуске программа подключается к Иксам (X server, это такая программа, через которую другие программы рисуют на экране окошки, буквы, а взамен получают сообщения о мышекликах и нажатиях клавиш) и вешает ловушку на клавишу F12. Если вы хотите запустить две программы на одном компьютере для проверки работы, то вам нужно выключить ловушку у одной из программ, для этого есть опция -nobutton
./hp -clearnet 127.0.0.1 42232 -pass xyzzy -nobuttonЭта команда пробует подключиться напрямую к порту 42232 на локалхосте, и если соединение будет принято, то программа отправляет пароль. Сервер либо закрывает соединение, либо говорит, что пароль правильный. После этого можно нажать и удерживать F12 для записи. После отпускания клавиши сообщение сжимается, отправляется, на другом конце распаковывается и воспроизводится.
Следующая команда вместо прямого соединения пытается найти Тор сначала на порту 9150, потом на 9050.
./hp -tor https://abcdabcdabcd1234.onion.tor.my 42232Если получилось, то программа отправляет SOCKS заголовок с .onion адресом и ждет, пока Тор установит соединение, затем все происходит как обычно, но только весь траффик идет через Тор.
Чуть подробнее о том, как настроить HiddenService
Для клиента достаточно, чтобы на том же компьютере работал TorBrowser. В Торбраузер встроен Тор, который слушает на порту 9150.
Если у вас в линуксе стоит консольный Тор, то он будет слушать на порту 9050.
А теперь чуть по ближе к HiddenService, чтобы его настроить, нужно найти конфигурационный файл Тора.
У Торбраузера он лежит по адресу tor-browser-folder/Browser/TorBrowser/Data/Tor/torrc
Если у вас консольный Тор, то скорее всего это /etc/tor/torrc
Это обыкновенный текстовый файл. Если это консольный Тор, то найдите в файле и уберите в начале символы решетки у этих двух строк:
#HiddenServiceDir /var/lib/tor/hidden_service/
#HiddenServicePort 80 127.0.0.1:80HiddenServiceDir - это папка, в которой Тор будет искать ключ от скрытого сервиса. Если его нет, то он будет сгенерирован и положен в эту папку.
HiddenServicePort указывает, на какой порт будут направляться запросы из Луковой сети. Его нужно поменять примерно так (номер порта поставьте любой с потолка, но не более 65535):
HiddenServicePort 42232 127.0.0.1:42232Если вы настраиваете Torbrowser, то вам нужно просто добавить эти пару строк в файл. Папку для ключа лучше указать там, куда у Тора будет доступ, например /home/bb/hiddenphone/
Чтобы попросить процесс Тора проглотить новый конфигурационный файл, выполните
killall -s SIGHUP torЕсли Тор запустился как надо, то в папке появится приватный ключ, и рядом с ним файлик hostname, который содержит адрес только что созданного .onion скрытого сервиса.
Приватный ключ никогда никому не давайте. А вот адрес onion мож%
Протокол сообщений
Поток данных между программами состоит из сообщений. Каждое сообщение начинается с заголовка. Первые четыре байта, это сигнатура HPHO, затем идет длина сообщения в формате big-endian с учетом доп. данных но без учета заголовка, затем идет номер, указывающий на тип сообщения, затем число, указывающее количество дополнительных данных, которые находятся в начале тела сообщения. Следом за заголовком идет тело сообщения, длина которого указана в заголовке. Следом может идти заголовок следующего сообщения. Соединение можно закрыть в любой момент.
Дополнительные данные в этой версии не используются, но они есть для совместимости с будущими версиями программы.
Пока не реализованные функции
Normalize. В коде содержится функция hphone_normalize_volume() которая должна измерить максимальный уровень сообщения, затем поднять громкость всего сообщения до предела, но чтобы не было искажений. Если у вас есть желание, то напишите эту функцию самостоятельно.
Модель безопасности
Несмотря на то, что все данные зашифрованы, остается возможность профилирования размера передаваемых данных и направления их следования. Если внешний наблюдатель будет фиксировать размер передаваемых кусков и точное время их появления, то он заметит отличия от протокола HTTP. Протокол HiddenPhone, в отличие от HTTP, не отправляет ответ сразу. В том числе он не отправляет сообщение о доставке звука. Внешний наблюдатель может сделать вывод, что вы используете не HTTP сессию а что-то другое.
Рекомендуется не давать onion адрес телефона посторонним людям, чтобы уберечь его от перебора пароля.
Эта программа работает со звуком, следовательно, стоит подумать о звуковой деанонимизации. Программу следует использовать для связи с людьми, с которыми вы знакомы лично, и общение с которыми вы хотите зашифровать.
Как и в любой программе для защиты информации, уровень безопасности никогда не бывает абсолютным. С удовольствием отвечу на все вопросы по поводу безопасности данной программы.
Вот такая программка. С удовольствием прочитаю любые комментарии и отчеты о запуске.
Всегда ваш(а), ББ.
