deployd/client/proto.c
Arija A. d1d0aec439
Add dproto documentation
Signed-off-by: Arija A. <ari@ari.lt>
2025-07-24 23:55:42 +03:00

349 lines
11 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "include/conf.h"
#include "include/pow.h"
#include "include/proto.h"
#include "server/include/pow.h"
#include "server/include/proto.h"
static int dc_see_error(SSL *ssl, dp_Logger *logger, uint8_t type) {
if (type != (uint8_t)dp_PacketType_error) {
return 0; /* Not an error packet, nothing to do */
}
uint8_t msg_len_buf[2] = {0};
if (SSL_read(ssl, msg_len_buf, sizeof(msg_len_buf)) !=
sizeof(msg_len_buf)) {
return -1;
}
const uint16_t msg_len =
(uint16_t)msg_len_buf[0] |
(uint16_t)((uint16_t)msg_len_buf[1] << (uint16_t)8);
if (msg_len == 0) {
return -1;
}
uint8_t error_code_buf[2] = {0};
if (SSL_read(ssl, error_code_buf, sizeof(error_code_buf)) !=
sizeof(error_code_buf)) {
return -1;
}
const uint16_t error_code =
(uint16_t)error_code_buf[0] |
(uint16_t)((uint16_t)error_code_buf[1] << (uint16_t)8);
const char *error_label = dp_PacketError_to_str(error_code);
char *msg = DC_MALLOC(msg_len + 1);
if (SSL_read(ssl, msg, msg_len) != msg_len) {
DC_FREE(msg);
return -1;
}
msg[msg_len] = '\0';
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG, "Received %s error (%u): %s",
error_label, error_code, msg);
DC_FREE(msg);
return 1;
}
bool dc_proto_read_server_info(SSL *ssl, dp_Logger *logger) {
if (!ssl || !logger) {
return false;
}
uint8_t server_version = 0;
if (SSL_read(ssl, &server_version, 1) != 1) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/server_info",
"Unable to read server version");
return false;
}
if (server_version != 0x00) {
return false;
}
uint8_t server_info_size = 0;
if (SSL_read(ssl, &server_info_size, 1) != 1) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/server_info",
"Unable to read server info size");
return false;
}
char server_info[257] = {0};
if (SSL_read(ssl, server_info, server_info_size) != server_info_size) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/server_info",
"Unable to read %u bytes of server info", server_info_size);
return false;
}
dp_logf(logger, DP_LOG_INFO, DC_PROTO_LOG "/server_info",
"Server information (version %u): %s", server_version, server_info);
return true;
}
bool dc_proto_pow_solve(SSL *ssl, dp_Logger *logger) {
if (!ssl || !logger) {
return false;
}
uint8_t challenge[DP_POW_CHALLENGE_SIZE] = {0};
uint8_t difficulty = 0;
uint8_t ones = 0;
if (SSL_read(ssl, challenge, sizeof(challenge)) != sizeof(challenge)) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Unable to read %zu bytes for PoW challenge",
sizeof(challenge));
return false;
}
if (SSL_read(ssl, &difficulty, 1) != 1) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Unable to read PoW difficulty");
return false;
}
if (SSL_read(ssl, &ones, 1) != 1) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Unable to read PoW XOR ones");
return false;
}
uint8_t pings = 0;
uint8_t ping_reply = 0;
uint64_t nonce = 0;
bool solved = false;
dp_logf(logger, DP_LOG_INFO, DC_PROTO_LOG "/pow_solve",
"Solving PoW (difficulty=%u, ones=%u)", difficulty, ones);
while (pings < 64 && !solved) {
if (!dp_proto_ping(ssl)) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Failed to send POW_PING to PoW");
return false;
}
if (SSL_read(ssl, &ping_reply, 1) != 1) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Failed to read POW_PING_REPLY");
return false;
}
if (ping_reply != (uint8_t)dp_PacketType_ping_reply) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Got unexpected ping response: %x", ping_reply);
dc_see_error(ssl, logger, ping_reply);
return false;
}
++pings;
const int status = dc_pow_solve_batch(
logger, difficulty, ones, challenge, &nonce, (size_t)(256 * 1024));
if (status < 0 || status > 1) {
return false;
}
if (status == 1) {
solved = true;
break;
}
}
if (solved) {
dp_logf(logger, DP_LOG_INFO, DC_PROTO_LOG "/pow_solve",
"Solved PoW: %lu", nonce);
/* Send ready */
if (!dp_proto_ready(ssl)) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Unable to set state to ready in PoW");
return false;
}
/* Send solution */
uint8_t nonce_buf[8] = {0};
if (!dp_u642buf_le(nonce_buf, nonce)) {
return false;
}
if (SSL_write(ssl, nonce_buf, sizeof(nonce_buf)) != sizeof(nonce_buf)) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Unable to send PoW nonce solution %lu to server", nonce);
return false;
}
/* Check status */
uint8_t allowed = 0x00;
if (SSL_read(ssl, &allowed, 1) != 1) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"Unable to read allow status from server");
return false;
}
if (allowed != (uint8_t)dp_PacketType_allowed) {
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"We were not allowed through");
dc_see_error(ssl, logger, allowed);
return false;
}
} else {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/pow_solve",
"PoW solution could not be found");
}
return solved;
}
bool dc_proto_command(SSL *ssl,
dp_Logger *logger,
dp_DeployCommand command,
bool is_unsafe,
uint64_t tid,
uint8_t domain_len,
const char *domain,
const uint8_t key[DP_AUTH_KEY_SIZE],
const uint8_t token[DP_AUTH_TOKEN_SIZE],
bool stream_logs) {
if (!ssl || !logger || !domain || !key || !token) {
return false;
}
/*
* Packet:
* type (1 byte)
* command (1 byte)
* is_unsafe (1 byte)
* id (8 bytes, little-endian 64 bit unsigned integer)
* domain_len (1 byte, value - up to DP_DOMAIN_LEN)
* domain (domain_len bytes)
* key (DP_AUTH_KEY_SIZE bytes, always static)
* token (DP_AUTH_TOKEN_SIZE bytes, changing)
*/
/* Calculate total packet size */
const size_t packet_size =
1 + 1 + 1 + 8 + 1 + domain_len + DP_AUTH_KEY_SIZE + DP_AUTH_TOKEN_SIZE;
/* Allocate buffer on stack */
uint8_t buffer[1 + 1 + 1 + 8 + 1 + DP_DOMAIN_LEN + DP_AUTH_KEY_SIZE +
DP_AUTH_TOKEN_SIZE] = {0};
uint8_t *ptr = buffer;
/* Pack packet type (1 byte) */
*ptr++ = (uint8_t)dp_PacketType_command;
/* Pack command (1 byte) */
*ptr++ = (uint8_t)command;
/* Pack is_unsafe (1 byte), assuming storing as 0 or 1 */
*ptr++ = (uint8_t)(is_unsafe ? 1 : 0);
/* Pack tid (8 bytes little-endian) */
for (uint8_t idx = 0; idx < 8; ++idx) {
*ptr++ = (uint8_t)((tid >> (uint8_t)(8 * idx)) & (uint8_t)0xFF);
}
/* Pack domain_len (1 byte) */
*ptr++ = domain_len;
/* Pack domain (domain_len bytes) */
memcpy(ptr, domain, domain_len);
ptr += domain_len;
/* Pack key (DP_AUTH_KEY_SIZE bytes) */
memcpy(ptr, key, DP_AUTH_KEY_SIZE);
ptr += DP_AUTH_KEY_SIZE;
/* Pack token (DP_AUTH_TOKEN_SIZE bytes) */
memcpy(ptr, token, DP_AUTH_TOKEN_SIZE);
ptr += DP_AUTH_TOKEN_SIZE;
/* (ptr - buffer) should equal packet_size now (sanity) */
if ((size_t)(ptr - buffer) != packet_size) {
return false;
}
/* Now the buffer is ready send it */
const int sent = SSL_write(ssl, buffer, (int)packet_size);
if (sent != (int)packet_size) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/command",
"Failed to send full packet");
return false;
}
char log_buf[1024] = {0};
uint8_t log_packet_type = 0;
while (true) {
if (SSL_read(ssl, &log_packet_type, 1) != 1) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/command",
"Failed to read log packet type");
return false;
}
switch ((dp_PacketType)log_packet_type) {
case dp_PacketType_log:
{
uint8_t log_size_buf[2] = {0};
if (SSL_read(ssl, log_size_buf, sizeof(log_size_buf)) !=
sizeof(log_size_buf)) {
return -1;
}
uint16_t log_size =
(uint16_t)log_size_buf[0] |
(uint16_t)((uint16_t)log_size_buf[1] << (uint16_t)8);
if (log_size == 0) {
return false;
}
while (log_size > 0) {
const int bytes =
SSL_read(ssl, log_buf,
DP_MIN(log_size, sizeof(log_buf) - 1));
if (bytes <= 0) {
break;
}
log_buf[bytes] = '\0';
if (stream_logs) {
(void)fputs(log_buf, stdout);
(void)fflush(stdout);
}
log_size -= (uint16_t)bytes;
}
if (stream_logs) {
(void)putchar('\n');
(void)fflush(stdout);
}
}
break;
case dp_PacketType_logs_end:
case dp_PacketType_exit:
return true;
break;
case dp_PacketType_ping:
if (!dp_proto_ping_reply(ssl)) {
dp_log(logger, DP_LOG_ERROR, DC_PROTO_LOG "/command",
"Unable to reply to deploy ping");
return false;
}
break;
case dp_PacketType_error:
dc_see_error(ssl, logger, log_packet_type);
return false;
break;
default:
dp_logf(logger, DP_LOG_ERROR, DC_PROTO_LOG "/command",
"Received invalid packet type %x", log_packet_type);
return false;
break;
}
}
return true;
}