Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <ntapi/nt_atomic.h>
#include <ntapi/nt_status.h>
#include <ntapi/nt_thread.h>
#include <ntapi/nt_port.h>
#include <ntapi/nt_daemon.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

int32_t __attr_hidden__ __ntapi_tt_seh_frame(void *, void *, void *, int32_t (*)(nt_daemon_params *));

static int32_t __ntapi_dsr_once(nt_daemon_params * params);
static int32_t __ntapi_dsr_start(nt_daemon_params * params);

int32_t __stdcall __ntapi_dsr_init(nt_daemon_params * params)
{
	int32_t			status;
	nt_thread_params	tparams;

	/* report status */
	at_store_32(
		&params->exit_code_daemon_loop,
		NT_STATUS_PENDING);

	at_store_32(
		&params->exit_code_daemon_start,
		NT_STATUS_PENDING);

	at_store_32(
		&params->exit_code_internal_client,
		NT_STATUS_PENDING);

	/* port_keys */
	if (params->flags & NT_DSR_INIT_GENERATE_KEYS)
		if ((status = __ntapi->tt_port_generate_keys(params->port_keys)))
			return status;

	/* port_name_keys */
	if (params->flags & NT_DSR_INIT_FORMAT_KEYS)
		__ntapi->tt_port_format_keys(
			params->port_keys,
			params->port_name_keys);

	/* 'port-created' event */
	if (!params->hevent_daemon_port) {
		if ((status = __ntapi->tt_create_private_event(
				&params->hevent_daemon_port,
				NT_NOTIFICATION_EVENT,
				NT_EVENT_NOT_SIGNALED)))
			return status;

		if (params->pevent_daemon_port)
			at_store(
				(intptr_t *)params->pevent_daemon_port,
				(intptr_t)params->hevent_daemon_port);
	}

	/* 'daemon-is-ready' event */
	if (!params->hevent_daemon_ready) {
		if ((status = __ntapi->tt_create_private_event(
				&params->hevent_daemon_ready,
				NT_NOTIFICATION_EVENT,
				NT_EVENT_NOT_SIGNALED)))
			return status;

		if (params->pevent_daemon_ready)
			at_store(
				(intptr_t *)params->pevent_daemon_ready,
				(intptr_t)params->hevent_daemon_ready);
	}

	/* 'internal-client-is-ready' event */
	if (!params->hevent_internal_client_ready) {
		if ((status = __ntapi->tt_create_private_event(
				&params->hevent_internal_client_ready,
				NT_NOTIFICATION_EVENT,
				NT_EVENT_NOT_SIGNALED)))
			return status;

		if (params->pevent_internal_client_ready)
			at_store(
				(intptr_t *)params->pevent_internal_client_ready,
				(intptr_t)params->hevent_internal_client_ready);
	}

	/* memory barrier */
	__ntapi->tt_aligned_block_memlock(
		params,
		sizeof(*params));

	__ntapi->tt_aligned_block_memlock(
		params->port_name,
		sizeof(*params->port_name));

	/* daemon dedicated thread: general parameters */
	__ntapi->tt_aligned_block_memset(
		&tparams,0,sizeof(tparams));

	tparams.start = (nt_thread_start_routine *)__ntapi_dsr_start;
	tparams.arg   = params;

	/* daemon dedicated thread: stack parameters (optional) */
	tparams.stack_size_commit	= params->stack_size_commit;
	tparams.stack_size_reserve	= params->stack_size_reserve;
	tparams.stack_info		= params->stack_info;

	/* daemon dedicated thread: create */
	if ((status = __ntapi->tt_create_local_thread(&tparams)))
		return status;

	at_store(
		(intptr_t *)&params->hthread_daemon_loop,
		(intptr_t)tparams.hthread);

	/* daemon dedicated thread: actual stack size */
	params->stack_size_commit	= tparams.stack_size_commit;
	params->stack_size_reserve	= tparams.stack_size_reserve;

	/* wait for the port to be created */
	if ((status = __ntapi->zw_wait_for_single_object(
			params->hevent_daemon_port,
			NT_SYNC_NON_ALERTABLE,0)))
		return status;

	/* overcome potential scheduling bug in recent (10.0) kernel */
	while (params->exit_code_daemon_start == NT_STATUS_PENDING)
		__ntapi->zw_delay_execution(
			NT_SYNC_NON_ALERTABLE,
			&(nt_timeout){{0,0}});

	/* verify the port's successful creation */
	if (at_locked_xadd_32(&params->exit_code_daemon_start,0))
		return NT_STATUS_PORT_NOT_SET;

	/* internal connection task-specific thread */
	__ntapi->tt_aligned_block_memset(
		&tparams,0,sizeof(tparams));

	tparams.start = (nt_thread_start_routine *)__ntapi_dsr_internal_client_connect;
	tparams.arg   = params;

	if ((status = __ntapi->tt_create_local_thread(&tparams)))
		return status;

	at_store(
		(intptr_t *)&params->hthread_internal_client,
		(intptr_t)tparams.hthread);

	/* wait for the task-specific thread to exit */
	if ((status = __ntapi->zw_wait_for_single_object(
			params->hthread_internal_client,
			NT_SYNC_NON_ALERTABLE,0)))
		return status;

	/* overcome potential scheduling bug in recent (10.0) kernel */
	while (params->exit_code_internal_client == NT_STATUS_PENDING)
		__ntapi->zw_delay_execution(
			NT_SYNC_NON_ALERTABLE,
			&(nt_timeout){{0,0}});

	/* verify a successful internal connection */
	if (at_locked_xadd_32(&params->exit_code_internal_client,0))
		return params->exit_code_internal_client;

	/* all_done */
	__ntapi->zw_set_event(
		params->hevent_daemon_ready,
		0);

	if (params->flags & NT_DSR_INIT_CLOSE_EVENTS) {
		__ntapi->zw_close(params->hevent_daemon_port);
		__ntapi->zw_close(params->hevent_daemon_ready);
	}

	return NT_STATUS_SUCCESS;;
}


/* __ntapi_dsr_start executes in the daemon's dedicated thread */
static int32_t __ntapi_dsr_start_impl(nt_daemon_params * params)
{
	int32_t status;
	void *	ctx;
	int32_t (__stdcall *loop)(void *);

	ctx  = params->daemon_loop_context;
	loop = params->daemon_loop_routine;

	if ((status = __ntapi_dsr_once(params)))
		return status;

	if ((status = __ntapi_dsr_create_port(params)))
		return status;

	if ((status = __ntapi_dsr_connect_internal_client(params)))
		return status;

	if ((status = loop(ctx)))
		return status;

	return NT_STATUS_SUCCESS;
}

static int32_t __ntapi_dsr_start_routine(nt_daemon_params * params)
{
	at_store_32(
		&params->exit_code_daemon_loop,
		NT_STATUS_MORE_PROCESSING_REQUIRED);

	return __ntapi->zw_terminate_thread(
		NT_CURRENT_THREAD_HANDLE,
		__ntapi_dsr_start_impl(params));
}

static int32_t __ntapi_dsr_start(nt_daemon_params * params)
{
	return __ntapi_tt_seh_frame(
		params,0,0,
		__ntapi_dsr_start_routine);
}

/* __ntapi_dsr_once executes in the daemon's dedicated thread */
static int32_t __ntapi_dsr_once(nt_daemon_params * params)
{
	int32_t status;

	if (!params->daemon_once_routine)
		return NT_STATUS_SUCCESS;

	if ((status = params->daemon_once_routine(params->daemon_loop_context)))
		at_store_32(
			&params->exit_code_daemon_start,
			status);

	return status;
}

/* __ntapi_dsr_create_port executes in the daemon's dedicated thread */
static int32_t __stdcall __ntapi_dsr_create_port_exit(
	nt_daemon_params *	params,
	int32_t			status)
{
	at_store_32(
		&params->exit_code_daemon_start,
		status);

	__ntapi->zw_set_event(
		params->hevent_daemon_port,
		0);

	return status;
}

int32_t __stdcall __ntapi_dsr_create_port(nt_daemon_params * params)
{
	int32_t				status;
	nt_object_attributes		oa;
	nt_security_quality_of_service	sqos;
	nt_unicode_string		server_name;

	/* init server_name */
	server_name.strlen = (uint16_t)__ntapi->tt_string_null_offset_short((int16_t *)params->port_name);
	server_name.maxlen = 0;
	server_name.buffer = params->port_name;

	/* init security structure */
	sqos.length 			= sizeof(sqos);
	sqos.impersonation_level	= NT_SECURITY_IMPERSONATION;
	sqos.context_tracking_mode	= NT_SECURITY_TRACKING_DYNAMIC;
	sqos.effective_only		= 1;

	/* init the port's object attributes */
	oa.len		= sizeof(oa);
	oa.root_dir	= 0;
	oa.obj_name	= &server_name;
	oa.obj_attr	= 0;
	oa.sec_desc	= params->port_sd;
	oa.sec_qos	= &sqos;

	/* create the port */
	if ((status = __ntapi->zw_create_port(
			&params->hport_daemon,
			&oa,0,(uint32_t)params->port_msg_size,
			0)))
		return __ntapi_dsr_create_port_exit(
			params,status);

	/* return port info */
	if (params->pport_daemon)
		at_store(
			(intptr_t *)params->pport_daemon,
			(intptr_t)params->hport_daemon);

	/* set status */
	at_store_32(
		&params->exit_code_daemon_start,
		NT_STATUS_SUCCESS);

	/* signal the port-created event */
	__ntapi->zw_set_event(
		params->hevent_daemon_port,
		0);

	return NT_STATUS_SUCCESS;
}