#include "hybbx/instance.h" #include "hybbx/log.h" #include "hybbx/util.h" #include static int g_instance_standalone; static int str_ieq(const char *a, const char *b) { if (a == NULL || b == NULL) { return 0; } while (*a != '\0' && *b != '\0') { char ca = (char)(*a >= 'A' && *a <= 'Z' ? *a + 32 : *a); char cb = (char)(*b >= 'A' && *b <= 'Z' ? *b + 32 : *b); if (ca != cb) { return 0; } a++; b++; } return *a == '\0' && *b == '\0'; } #if defined(__GNUC__) || defined(__clang__) __attribute__((weak)) #endif hybbx_instance_role_t hybbx_instance_role(void) { /* Overridden by instance_role_{main,secondary,proxy}.c on each binary. */ return HYBBX_INSTANCE_MAIN; } const char *hybbx_instance_binary_name(void) { switch (hybbx_instance_role()) { case HYBBX_INSTANCE_SECONDARY: return "hybbxsd"; case HYBBX_INSTANCE_PROXY: return "hybbxpd"; case HYBBX_INSTANCE_MAIN: default: return "hybbxd"; } } const char *hybbx_instance_role_name(void) { switch (hybbx_instance_role()) { case HYBBX_INSTANCE_SECONDARY: return "Secondary"; case HYBBX_INSTANCE_PROXY: return "Proxy"; case HYBBX_INSTANCE_MAIN: default: return "Main"; } } int hybbx_instance_offers_user_bbx(void) { return hybbx_instance_role() == HYBBX_INSTANCE_MAIN; } void hybbx_instance_set_standalone(int enabled) { g_instance_standalone = enabled != 0; } int hybbx_instance_standalone(void) { return g_instance_standalone; } static int instance_main_standalone(void) { return hybbx_instance_role() == HYBBX_INSTANCE_MAIN && hybbx_instance_standalone(); } int hybbx_instance_plugin_allowed(const char *plugin_name) { hybbx_instance_role_t role; if (plugin_name == NULL || plugin_name[0] == '\0') { return 0; } role = hybbx_instance_role(); if (instance_main_standalone()) { if (str_ieq(plugin_name, "circuit")) { return 1; } if (str_ieq(plugin_name, "mains_proxy")) { return 1; } return 1; } /* Split layout: packet_radio stays in HyBBX only via MAX25 prep on Secondary/Proxy; * hub Main uses circuit peers instead. */ if (str_ieq(plugin_name, "packet_radio")) { return role != HYBBX_INSTANCE_MAIN; } switch (role) { case HYBBX_INSTANCE_MAIN: if (str_ieq(plugin_name, "baycom") || str_ieq(plugin_name, "ardop") || str_ieq(plugin_name, "crdop")) { return 0; } if (str_ieq(plugin_name, "telnet") || str_ieq(plugin_name, "ssh")) { return 0; } return 1; case HYBBX_INSTANCE_PROXY: if (str_ieq(plugin_name, "websocket")) { return 0; } return 1; case HYBBX_INSTANCE_SECONDARY: if (str_ieq(plugin_name, "telnet") || str_ieq(plugin_name, "ssh") || str_ieq(plugin_name, "websocket")) { return 0; } return 1; default: return 0; } } hybbx_result_t hybbx_networks_enforce_instance(hybbx_networks_config_t *networks) { hybbx_instance_role_t role; if (networks == NULL) { return HYBBX_ERR_INVALID; } role = hybbx_instance_role(); if (instance_main_standalone()) { hybbx_log_info("[instance] standalone=yes — %s keeps INI transports " "(local RF via MAX25 prep when configured)", hybbx_instance_binary_name()); hybbx_log_info("[instance] binary=%s role=%s standalone=yes user_bbx=%s " "telnet=%s ssh=%s ax25=%s baycom=%s ardop=%s crdop=%s " "websocket=%s circuit=%s mains_proxy=%s", hybbx_instance_binary_name(), hybbx_instance_role_name(), hybbx_instance_offers_user_bbx() ? "yes" : "no", hybbx_bool_to_string(networks->telnet), hybbx_bool_to_string(networks->ssh), hybbx_bool_to_string(networks->ax25), hybbx_bool_to_string(networks->baycom), hybbx_bool_to_string(networks->ardop), hybbx_bool_to_string(networks->crdop), hybbx_bool_to_string(networks->websocket), hybbx_bool_to_string(networks->circuit), hybbx_bool_to_string(networks->mains_proxy)); return HYBBX_OK; } if (networks->ax25 && role == HYBBX_INSTANCE_MAIN) { hybbx_log_warn("[instance] %s: ax25/packet_radio on hub Main — forcing off " "(use standalone=yes or Secondary RF host)", hybbx_instance_binary_name()); networks->ax25 = 0; } switch (role) { case HYBBX_INSTANCE_MAIN: if (networks->baycom || networks->ardop || networks->crdop) { hybbx_log_warn("[instance] hybbxd: RF adapters forbidden on Main " "— forcing baycom/ardop/crdop off (use hybbxpd + MAX25)"); networks->baycom = 0; networks->ardop = 0; networks->crdop = 0; } if (networks->ssh) { hybbx_log_warn("[instance] hybbxd: SSH user path forbidden — " "WebSocket/reverse-proxy only; forcing ssh=no"); networks->ssh = 0; } if (networks->telnet) { hybbx_log_warn("[instance] hybbxd: telnet user path forbidden — " "WebSocket/reverse-proxy only; forcing telnet=no"); networks->telnet = 0; } /* WebSocket is the sole remote user path. */ if (!networks->websocket) { hybbx_log_warn("[instance] hybbxd: enabling websocket " "(required user path)"); networks->websocket = 1; } /* Circuit hub for Proxy attach. */ if (!networks->circuit) { hybbx_log_warn("[instance] hybbxd: enabling circuit hub " "(Proxy in/out)"); networks->circuit = 1; } break; case HYBBX_INSTANCE_SECONDARY: if (networks->telnet) { hybbx_log_warn("[instance] hybbxsd: no user telnet — forcing off"); networks->telnet = 0; } if (networks->websocket) { hybbx_log_warn("[instance] hybbxsd: no user WebSocket — forcing off"); networks->websocket = 0; } if (networks->ssh) { hybbx_log_warn("[instance] hybbxsd: no user SSH — forcing off"); networks->ssh = 0; } /* Peer other Mains — mains_proxy required. */ if (!networks->mains_proxy) { hybbx_log_warn("[instance] hybbxsd: enabling mains_proxy " "(Secondary = other-Main peers)"); networks->mains_proxy = 1; } break; case HYBBX_INSTANCE_PROXY: if (networks->websocket) { hybbx_log_warn("[instance] hybbxpd: WebSocket forbidden — forcing off"); networks->websocket = 0; } if (networks->baycom) { hybbx_log_warn("[instance] hybbxpd: baycom enabled — prefer MAX25 " "relay/gateway/digipeater for hyBBX RF"); } if (!networks->circuit) { hybbx_log_warn("[instance] hybbxpd: enabling circuit " "(attach toward Main)"); networks->circuit = 1; } break; default: return HYBBX_ERR_INVALID; } hybbx_log_info("[instance] binary=%s role=%s user_bbx=%s " "telnet=%s ssh=%s ax25=%s baycom=%s ardop=%s crdop=%s " "websocket=%s circuit=%s mains_proxy=%s", hybbx_instance_binary_name(), hybbx_instance_role_name(), hybbx_instance_offers_user_bbx() ? "yes" : "no", hybbx_bool_to_string(networks->telnet), hybbx_bool_to_string(networks->ssh), hybbx_bool_to_string(networks->ax25), hybbx_bool_to_string(networks->baycom), hybbx_bool_to_string(networks->ardop), hybbx_bool_to_string(networks->crdop), hybbx_bool_to_string(networks->websocket), hybbx_bool_to_string(networks->circuit), hybbx_bool_to_string(networks->mains_proxy)); return HYBBX_OK; }