Implement service configuration feature

- Add send_registration_message_with_services() to send detailed service info during client registration
- Update wssshc to send service configurations during registration
- Enhance server to parse service arrays and legacy string format
- Fix wsssht to use service from URL in tunnel request messages
- Maintain backward compatibility with existing clients
parent 89c6e3ed
...@@ -526,9 +526,86 @@ int websocket_handle_message(wssshd_state_t *state, ws_connection_t *conn __attr ...@@ -526,9 +526,86 @@ int websocket_handle_message(wssshd_state_t *state, ws_connection_t *conn __attr
// Extract services (optional, defaults to "ssh") // Extract services (optional, defaults to "ssh")
char *services = NULL; char *services = NULL;
char *services_start = strstr(msg_copy, "\"services\":\""); char *services_start = strstr(msg_copy, "\"services\":");
if (services_start) { if (services_start) {
services_start += strlen("\"services\":\""); services_start += strlen("\"services\":");
// Skip whitespace
while (*services_start == ' ' || *services_start == '\t' || *services_start == '\n') services_start++;
if (*services_start == '[') {
// New format: array of service objects
char *services_end = strchr(services_start, ']');
if (services_end && services_end < msg_copy + MSG_BUFFER_SIZE) {
services_end++; // Include the closing bracket
size_t services_len = services_end - services_start;
if (services_len > 0 && services_len < 2048) { // Reasonable limit for services array
char *services_copy = malloc(services_len + 1);
if (services_copy) {
memcpy(services_copy, services_start, services_len);
services_copy[services_len] = '\0';
// Parse service names from the array
char *service_names = malloc(256); // Buffer for concatenated service names
if (service_names) {
service_names[0] = '\0';
char *service_start = services_copy + 1; // Skip opening bracket
while (*service_start && *service_start != ']') {
// Find next service object
if (*service_start == '{') {
char *service_end = strchr(service_start, '}');
if (service_end) {
// Extract service name
char *name_start = strstr(service_start, "\"name\":\"");
if (name_start) {
name_start += strlen("\"name\":\"");
char *name_end = strchr(name_start, '"');
if (name_end) {
size_t name_len = name_end - name_start;
if (name_len > 0 && name_len < 64) {
if (strlen(service_names) > 0) {
strncat(service_names, ",", 255 - strlen(service_names));
}
strncat(service_names, name_start, name_len);
}
}
}
service_start = service_end + 1;
} else {
break;
}
} else {
service_start++;
}
// Skip commas and whitespace
while (*service_start == ',' || *service_start == ' ' || *service_start == '\t' || *service_start == '\n') {
service_start++;
}
}
if (strlen(service_names) > 0) {
services = service_names;
// Convert services to lowercase for consistency
for (char *p = services; *p; p++) {
*p = tolower(*p);
}
if (state->debug) printf("[DEBUG - %s -> wssshd] Extracted services from array: '%s'\n", direction, services);
} else {
free(service_names);
}
}
free(services_copy);
}
} else {
if (state->debug) printf("[DEBUG - %s -> wssshd] Services array length invalid: %zu\n", direction, services_len);
}
} else {
if (state->debug) printf("[DEBUG - %s -> wssshd] Services array end bracket not found\n", direction);
}
} else if (*services_start == '"') {
// Legacy format: simple string
services_start++; // Skip opening quote
char *services_end = strchr(services_start, '"'); char *services_end = strchr(services_start, '"');
if (services_end && services_end > services_start && services_end < msg_copy + MSG_BUFFER_SIZE) { if (services_end && services_end > services_start && services_end < msg_copy + MSG_BUFFER_SIZE) {
size_t services_len = services_end - services_start; size_t services_len = services_end - services_start;
...@@ -542,7 +619,7 @@ int websocket_handle_message(wssshd_state_t *state, ws_connection_t *conn __attr ...@@ -542,7 +619,7 @@ int websocket_handle_message(wssshd_state_t *state, ws_connection_t *conn __attr
for (char *p = services; *p; p++) { for (char *p = services; *p; p++) {
*p = tolower(*p); *p = tolower(*p);
} }
if (state->debug) printf("[DEBUG - %s -> wssshd] Extracted services: '%s'\n", direction, services); if (state->debug) printf("[DEBUG - %s -> wssshd] Extracted services (legacy format): '%s'\n", direction, services);
} }
} else { } else {
if (state->debug) printf("[DEBUG - %s -> wssshd] Services length invalid: %zu\n", direction, services_len); if (state->debug) printf("[DEBUG - %s -> wssshd] Services length invalid: %zu\n", direction, services_len);
...@@ -550,6 +627,7 @@ int websocket_handle_message(wssshd_state_t *state, ws_connection_t *conn __attr ...@@ -550,6 +627,7 @@ int websocket_handle_message(wssshd_state_t *state, ws_connection_t *conn __attr
} else { } else {
if (state->debug) printf("[DEBUG - %s -> wssshd] Services end quote not found or invalid\n", direction); if (state->debug) printf("[DEBUG - %s -> wssshd] Services end quote not found or invalid\n", direction);
} }
}
} else { } else {
if (state->debug) printf("[DEBUG - %s -> wssshd] Services not specified, using default\n", direction); if (state->debug) printf("[DEBUG - %s -> wssshd] Services not specified, using default\n", direction);
} }
......
...@@ -48,29 +48,73 @@ int send_json_message(SSL *ssl, const char *type, const char *client_id, const c ...@@ -48,29 +48,73 @@ int send_json_message(SSL *ssl, const char *type, const char *client_id, const c
} }
int send_registration_message(SSL *ssl, const char *client_id, const char *password, const char *tunnel, const char *tunnel_control, const char *wssshd_private_ip) { int send_registration_message(SSL *ssl, const char *client_id, const char *password, const char *tunnel, const char *tunnel_control, const char *wssshd_private_ip) {
char message[2048]; return send_registration_message_with_services(ssl, client_id, password, tunnel, tunnel_control, wssshd_private_ip, NULL, 0);
}
int send_registration_message_with_services(SSL *ssl, const char *client_id, const char *password, const char *tunnel, const char *tunnel_control, const char *wssshd_private_ip, service_config_t **services, int num_services) {
char message[4096];
char services_str[2048] = "";
// Build services string if services are provided
if (services && num_services > 0) {
strcat(services_str, "\"services\":[");
for (int i = 0; i < num_services; i++) {
if (i > 0) strcat(services_str, ",");
char service_json[256];
snprintf(service_json, sizeof(service_json),
"{\"name\":\"%s\",\"host\":\"%s\",\"port\":%d,\"proto\":\"%s\"}",
services[i]->name, services[i]->tunnel_host, services[i]->tunnel_port,
services[i]->proto ? services[i]->proto : "tcp");
strncat(services_str, service_json, sizeof(services_str) - strlen(services_str) - 1);
}
strcat(services_str, "]");
}
if (password && strlen(password) > 0) { if (password && strlen(password) > 0) {
if (wssshd_private_ip && strlen(wssshd_private_ip) > 0) { if (wssshd_private_ip && strlen(wssshd_private_ip) > 0) {
if (services && num_services > 0) {
snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"password\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"wssshd_private_ip\":\"%s\",%s,\"version\":\"%s\"}",
client_id, password, tunnel, tunnel_control, wssshd_private_ip, services_str, WSSSH_VERSION);
} else {
snprintf(message, sizeof(message), snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"password\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"wssshd_private_ip\":\"%s\",\"version\":\"%s\"}", "{\"type\":\"register\",\"client_id\":\"%s\",\"password\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"wssshd_private_ip\":\"%s\",\"version\":\"%s\"}",
client_id, password, tunnel, tunnel_control, wssshd_private_ip, WSSSH_VERSION); client_id, password, tunnel, tunnel_control, wssshd_private_ip, WSSSH_VERSION);
}
} else {
if (services && num_services > 0) {
snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"password\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",%s,\"version\":\"%s\"}",
client_id, password, tunnel, tunnel_control, services_str, WSSSH_VERSION);
} else { } else {
snprintf(message, sizeof(message), snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"password\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"version\":\"%s\"}", "{\"type\":\"register\",\"client_id\":\"%s\",\"password\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"version\":\"%s\"}",
client_id, password, tunnel, tunnel_control, WSSSH_VERSION); client_id, password, tunnel, tunnel_control, WSSSH_VERSION);
} }
}
} else { } else {
if (wssshd_private_ip && strlen(wssshd_private_ip) > 0) { if (wssshd_private_ip && strlen(wssshd_private_ip) > 0) {
if (services && num_services > 0) {
snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"wssshd_private_ip\":\"%s\",%s,\"version\":\"%s\"}",
client_id, tunnel, tunnel_control, wssshd_private_ip, services_str, WSSSH_VERSION);
} else {
snprintf(message, sizeof(message), snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"wssshd_private_ip\":\"%s\",\"version\":\"%s\"}", "{\"type\":\"register\",\"client_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"wssshd_private_ip\":\"%s\",\"version\":\"%s\"}",
client_id, tunnel, tunnel_control, wssshd_private_ip, WSSSH_VERSION); client_id, tunnel, tunnel_control, wssshd_private_ip, WSSSH_VERSION);
}
} else {
if (services && num_services > 0) {
snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",%s,\"version\":\"%s\"}",
client_id, tunnel, tunnel_control, services_str, WSSSH_VERSION);
} else { } else {
snprintf(message, sizeof(message), snprintf(message, sizeof(message),
"{\"type\":\"register\",\"client_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"version\":\"%s\"}", "{\"type\":\"register\",\"client_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"version\":\"%s\"}",
client_id, tunnel, tunnel_control, WSSSH_VERSION); client_id, tunnel, tunnel_control, WSSSH_VERSION);
} }
} }
}
printf("[DEBUG] Sending registration message: %s\n", message); printf("[DEBUG] Sending registration message: %s\n", message);
fflush(stdout); fflush(stdout);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
// Function declarations for control channel messages // Function declarations for control channel messages
int send_json_message(SSL *ssl, const char *type, const char *client_id, const char *request_id); int send_json_message(SSL *ssl, const char *type, const char *client_id, const char *request_id);
int send_registration_message(SSL *ssl, const char *client_id, const char *password, const char *tunnel, const char *tunnel_control, const char *wssshd_private_ip); int send_registration_message(SSL *ssl, const char *client_id, const char *password, const char *tunnel, const char *tunnel_control, const char *wssshd_private_ip);
int send_registration_message_with_services(SSL *ssl, const char *client_id, const char *password, const char *tunnel, const char *tunnel_control, const char *wssshd_private_ip, service_config_t **services, int num_services);
int send_tunnel_request_message(SSL *ssl, const char *client_id, const char *request_id, const char *tunnel, const char *tunnel_control, const char *service); int send_tunnel_request_message(SSL *ssl, const char *client_id, const char *request_id, const char *tunnel, const char *tunnel_control, const char *service);
int send_tunnel_request_message_with_enc(SSL *ssl, const char *client_id, const char *request_id, const char *tunnel, const char *tunnel_control, const char *service, wsssh_encoding_t encoding); int send_tunnel_request_message_with_enc(SSL *ssl, const char *client_id, const char *request_id, const char *tunnel, const char *tunnel_control, const char *service, wsssh_encoding_t encoding);
int send_tunnel_close_message(SSL *ssl, const char *request_id, int debug); int send_tunnel_close_message(SSL *ssl, const char *request_id, int debug);
......
...@@ -797,12 +797,12 @@ int connect_to_server(const wssshc_config_t *config, service_config_t ***service ...@@ -797,12 +797,12 @@ int connect_to_server(const wssshc_config_t *config, service_config_t ***service
char *best_tunnel = select_best_transport(expanded_tunnel); char *best_tunnel = select_best_transport(expanded_tunnel);
char *best_tunnel_control = select_best_transport(expanded_tunnel_control); char *best_tunnel_control = select_best_transport(expanded_tunnel_control);
// Send registration message // Send registration message with services
if (config->debug) { if (config->debug) {
printf("[DEBUG] Sending registration message...\n"); printf("[DEBUG] Sending registration message with services...\n");
fflush(stdout); fflush(stdout);
} }
int reg_result = send_registration_message(ssl, config->client_id, config->password, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, config->wssshd_private_ip); int reg_result = send_registration_message_with_services(ssl, config->client_id, config->password, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, config->wssshd_private_ip, services ? *services : NULL, services ? *num_services : 0);
if (config->debug && reg_result) { if (config->debug && reg_result) {
// Note: send_registration_message doesn't return the message content, so we can't log the exact JSON here // Note: send_registration_message doesn't return the message content, so we can't log the exact JSON here
// The function internally handles the JSON formatting // The function internally handles the JSON formatting
......
...@@ -401,7 +401,7 @@ int main(int argc, char *argv[]) { ...@@ -401,7 +401,7 @@ int main(int argc, char *argv[]) {
char *best_tunnel = select_best_transport(expanded_tunnel); char *best_tunnel = select_best_transport(expanded_tunnel);
char *best_tunnel_control = select_best_transport(expanded_tunnel_control); char *best_tunnel_control = select_best_transport(expanded_tunnel_control);
if (!send_tunnel_request_message_with_enc(setup_result.ssl, client_id, setup_result.request_id, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, "ssh", config.encoding)) { if (!send_tunnel_request_message_with_enc(setup_result.ssl, client_id, setup_result.request_id, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, config.service ? config.service : "ssh", config.encoding)) {
free(expanded_tunnel); free(expanded_tunnel);
free(expanded_tunnel_control); free(expanded_tunnel_control);
if (best_tunnel) free(best_tunnel); if (best_tunnel) free(best_tunnel);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment