/* BSD-2-Clause license * * Copyright (c) 2018-2023 NST , sss . * */ #include #include #include #include #include "globals.h" #include "ws_session.h" #include "wrdp_thpool_internals.h" #include "webrdp_module_api.h" #include "task.h" #include "rdp_backend_api.h" #include "thread_sync.h" #include "utilities.h" #include "thread_impl.h" #include "curl_helpers.h" #include #include "base64_url.h" bool backend_validate(wrdp_backend_module *backend) { if (!backend->callbacks_module->init) { return false; } if (!backend->callbacks_input->kcomb && !backend->callbacks_input->kpress && !backend->callbacks_input->kupdown && !backend->callbacks_input->mouse && !backend->callbacks_input->unicode) { /* backend can't handle any input */ return false; } return true; } static bool validate_backend_name(const char *name) { if (!strcmp(name, "rdp")) { return true; } return false; } void backend_remove_from_pool(wrdp_backend_module *backend) { wrdp_thpool_task *task = backend->wrdp_thpool_task; user_pool_msg *pmsg = calloc(1, sizeof(user_pool_msg)); if (!pmsg) { perror("calloc"); return; } pmsg->type = msg_type_destroy_ws_backend_info; pmsg->backend = backend; wrdp_thpool_send_msg_to_pool(task->thread->pool, pmsg); } static bool backend_create(const char *name, ws_session *session) { wrdp_backend_module *backend = 0; task_info *info = session->task_info; wrdp_thpool_task *task = 0; if (session->sid_base64) { size_t sid_len = strlen(session->sid_base64); char *attach_sid = malloc(sid_len + 1); memcpy(attach_sid, session->sid_base64, sid_len); attach_sid[sid_len] = 0; session->attach_sid_base64 = attach_sid; } if (!validate_backend_name(name)) { return false; } backend = calloc(1, sizeof(wrdp_backend_module)); if (!backend) { perror("calloc"); return false; } bool module_initialized = false; info->backend = backend; backend->task_info = info; session->task_info = info; info->wrdp_thpool_task = session->wrdp_thpool_task; backend->wrdp_thpool_task = session->wrdp_thpool_task; task = session->wrdp_thpool_task; task->userdata = info; { struct ws_session_list_entry_s *entry = calloc(1, sizeof(struct ws_session_list_entry_s)); if (!entry) { perror("calloc"); goto error; } entry->session = session; backend->sessions_list_head = calloc(1, sizeof(SLIST_HEAD(h, ws_session_list_entry_s))); SLIST_HEAD(sessions_head, ws_session_list_entry_s) *sessions_list_head_p = backend->sessions_list_head; SLIST_INIT(sessions_list_head_p); SLIST_INSERT_HEAD(sessions_list_head_p, entry, entries); info->settings = g_globals.settings.ws_session_defaults; } SLIST_INIT(&(session->curls_easy_head)); backend->callbacks_input = calloc(1, sizeof(wrdp_backend_cb_input)); if (!backend->callbacks_input) { perror("calloc"); goto error; } backend->callbacks_module = calloc(1, sizeof(wrdp_backend_cb_module)); if (!backend->callbacks_module) { perror("calloc"); goto error; } backend->callbacks_clipbrd = calloc(1, sizeof(wrdp_backend_cb_clipboard)); if (!backend->callbacks_clipbrd) { perror("calloc"); goto error; } backend->callbacks_ft = calloc(1, sizeof(wrdp_backend_cb_filetransfer)); if (!backend->callbacks_ft) { perror("calloc"); goto error; } if (!strcmp(name, "rdp")) { module_initialized = rdp_create(g_globals.exports, backend); if (!module_initialized) { goto error; } } strcpy(info->backend_name, name); info->backend = backend; if (!backend_validate(info->backend)) { info->backend = 0; goto error; } info->backend->callbacks_module->set_task_info( info, info->backend->backend_internals); { /* TODO: do it in main thread */ user_pool_msg *pmsg = calloc(1, sizeof(user_pool_msg)); wrdp_thpool_task *task = session->wrdp_thpool_task; if (!pmsg) { perror("calloc"); goto error; } pmsg->type = msg_type_backend_created; pmsg->backend = backend; wrdp_thpool_send_msg_to_pool(task->thread->pool, pmsg); } { wrdp_thpool_task *task = session->wrdp_thpool_task; info->ev_timer_watcher = calloc(1, sizeof(ev_timer)); if (!info->ev_timer_watcher) { perror("calloc"); goto error; } ev_timer_init( info->ev_timer_watcher, task_timeouts_check_cb, 1., 0.); info->ev_timer_watcher->data = info; info->ev_timer_watcher->repeat = 1; ev_timer_again( task->thread->ev_th_loop, info->ev_timer_watcher); } return true; error: if (backend) { if (backend->callbacks_input) { free(backend->callbacks_input); } if (backend->callbacks_module) { free(backend->callbacks_module); } if (backend->callbacks_clipbrd) { free(backend->callbacks_clipbrd); } if (backend->callbacks_ft) { free(backend->callbacks_ft); } backend_remove_from_pool(backend); } return false; } void backend_destroy(wrdp_backend_module *backend) { if (backend->callbacks_input) { free(backend->callbacks_input); } if (backend->callbacks_module) { free(backend->callbacks_module); } if (backend->callbacks_ft) { free(backend->callbacks_ft); } if (backend->callbacks_clipbrd) { free(backend->callbacks_clipbrd); } if (backend->sessions_list_head) { free(backend->sessions_list_head); } backend_remove_from_pool(backend); } extern void task_destroy_timers(wrdp_thpool_task *task); void backend_task_destroy(wrdp_thpool_task *task) { task_destroy_timers(task); task_info *info = task->userdata; backend_destroy(info->backend); free(info); } static void ws_session_child_init_cb(wrdp_thpool_task *task, void *userdata) { ws_session *session = userdata; session->wrdp_thpool_task = task; } static bool backend_find(ws_session *session) { for (struct backend_s *t1 = LIST_FIRST(&(g_globals.backends_head)); t1; t1 = LIST_NEXT(t1, entries)) { //TODO: WTF? if (!t1->backend) continue; SLIST_HEAD(sessions_head, ws_session_list_entry_s) *sessions_list_head_p = t1->backend->sessions_list_head; for (struct ws_session_list_entry_s *s = SLIST_FIRST(sessions_list_head_p); s; s = SLIST_NEXT(s, entries)) { if (s->session && session->attach_sid_base64 && s->session->sid_base64 && !strcmp(session->attach_sid_base64, s->session->sid_base64)) { if (session == s->session) { return true; } struct ws_session_list_entry_s *entry = calloc( 1, sizeof(struct ws_session_list_entry_s)); wrdp_thpool_task *t; if (!entry) { session->session_state = ws_session_error; perror("calloc"); return false; } entry->session = session; session->task_info = t1->backend->task_info; SLIST_INSERT_HEAD( sessions_list_head_p, entry, entries); t = t1->backend->wrdp_thpool_task; if (!wrdp_thread_pool_move_task_to_thread( g_globals.thpool, ws_run_session, ws_stop_session, t->thread->thread_id, ws_session_child_init_cb, session->wrdp_thpool_task, session)) { /* TODO: cleanup ? */ return false; } return true; } } } return false; } bool backend_get(const char *name, ws_session *session) { bool found = false; if (session->attach_sid_base64) { found = backend_find(session); } else { found = backend_create(name, session); } return found; } void backend_fill_settings(ws_session *session) { while (!SLIST_EMPTY(&(session->backend_settings_head))) { task_info *info = session->task_info; struct backend_setting_s *s = SLIST_FIRST(&(session->backend_settings_head)); SLIST_REMOVE_HEAD(&(session->backend_settings_head), entries); if (s->type == setting_int) { info->backend->callbacks_module->set_setting_int( &(s->setting_int), info->backend->backend_internals); free(s->setting_int.name); free(s); } else if (s->type == setting_string) { info->backend->callbacks_module->set_setting_str( &(s->setting_string), info->backend->backend_internals); free(s->setting_string.name); free(s->setting_string.value); free(s); } } } bool handle_backend_setting_int(const char *name, int64_t val, ws_session *session) { task_info *info = session->task_info; if (!info || !info->backend) { if (!SLIST_EMPTY(&(session->backend_settings_head))) { for (struct backend_setting_s *s = SLIST_FIRST(&(session->backend_settings_head)); s; s = SLIST_NEXT(s, entries)) { if (s->type != setting_int) continue; if (strcmp(s->setting_int.name, name)) continue; s->setting_int.value = val; return true; } } struct backend_setting_s *s = calloc(1, sizeof(struct backend_setting_s)); if (!s) { perror("calloc"); return false; } s->setting_int.name = strdup(name); s->setting_int.value = val; s->type = setting_int; SLIST_INSERT_HEAD( &(session->backend_settings_head), s, entries); } else { backend_setting_int s; s.name = (char *)name; s.value = val; if (!info->backend->callbacks_module->set_setting_int( &s, info->backend->backend_internals)) { return false; } } return true; } bool handle_backend_setting_string( const char *name, const char *val, ws_session *session) { task_info *info = session->task_info; if (!info || !info->backend) { if (!SLIST_EMPTY(&(session->backend_settings_head))) { for (struct backend_setting_s *s = SLIST_FIRST(&(session->backend_settings_head)); s; s = SLIST_NEXT(s, entries)) { if (s->type != setting_string) continue; if (strcmp(s->setting_string.name, name)) continue; free(s->setting_string.value); s->setting_string.value = strdup(val); return true; } } struct backend_setting_s *s = calloc(1, sizeof(struct backend_setting_s)); if (!s) { perror("calloc"); return false; } s->setting_string.name = strdup(name); s->setting_string.value = strdup(val); s->type = setting_string; SLIST_INSERT_HEAD( &(session->backend_settings_head), s, entries); } else { backend_setting_str s; s.name = (char *)name; s.value = (char *)val; info->backend->callbacks_module->set_setting_str( &s, info->backend->backend_internals); } return true; }