Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2017  Z. Gilboa                 */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/nt_file.h>
#include <ntapi/nt_string.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

static int32_t __ipc_connect_return(
	intptr_t *	hlock,
	int32_t		status)
{
	at_store(hlock,0);
	return status;
}


static int32_t __ipc_connect_by_attr(
	void **			hport,
	const nt_port_attr *	attr,
	nt_unicode_string *	str,
	void *			hconn,
	__out	void **		hsection,
	__out	void **		secaddr,
	__out	size_t *	secsize)
{
	int32_t			status;
	struct dalist_node_ex *	node;
	const nt_port_attr *	conn;
	nt_port_attr *		nconn;
	nt_ipc_conn *		ipc;
	intptr_t *		hlock;
	ntapi_internals *	__internals;

	/* init */
	__internals = __ntapi_internals();

	/* lock */
	hlock = &(__internals->hlock);

	if (at_locked_cas(hlock,0,1))
		return NT_STATUS_RESOURCE_NOT_OWNED;

	/* already connected? */
	node = (struct dalist_node_ex *)__internals->ipc_conns.head;

	for (; node; node=node->next) {
		ipc  = (nt_ipc_conn *)&node->dblock;
		conn = &ipc->attr;

		if ((attr->keys.key[0] == conn->keys.key[0])
				&& (attr->keys.key[1] == conn->keys.key[1])
				&& (attr->keys.key[2] == conn->keys.key[2])
				&& (attr->keys.key[3] == conn->keys.key[3])
				&& (attr->keys.key[4] == conn->keys.key[4])
				&& (attr->keys.key[5] == conn->keys.key[5])
				&& !__ntapi->tt_guid_compare(
					&attr->guid,
					&conn->guid)) {
			/* already connected */
			if (hconn)
				return __ipc_connect_return(
					hlock,
					((uintptr_t)hconn == node->key)
						? NT_STATUS_SUCCESS
						: NT_STATUS_CONTEXT_MISMATCH);

			*hport    = (void *)node->key;
			*hsection = ipc->hsection;
			*secaddr  = ipc->secaddr;
			*secsize  = ipc->secsize;

			return __ipc_connect_return(hlock,NT_STATUS_SUCCESS);
		}
	}

	/* allocate list node */
        if ((status = dalist_get_free_node(
			&__internals->ipc_conns,
			(void **)&node)))
		return __ipc_connect_return(hlock,status);

	/* connect as needed */
	if (!hconn) {
		status = __ntapi->zw_connect_port(
			&hconn,str,0,0,0,0,0,0);

		if (status) {
			dalist_deposit_free_node(
				&__internals->ipc_conns,
				node);

			return __ipc_connect_return(hlock,status);
		}
	}

	/* add connection */
	node->key = (uintptr_t)hconn;
	ipc   = (nt_ipc_conn *)&node->dblock;
	nconn = &ipc->attr;

	__ntapi->tt_aligned_block_memcpy(
		(uintptr_t *)nconn,
		(uintptr_t *)attr,
		sizeof(nt_port_attr));

	ipc->hsection = 0;
	ipc->secaddr  = 0;
	ipc->secsize  = 0;

	dalist_insert_node_by_key(
		&__internals->ipc_conns,
		node);

	/* all done */
	*hport    = hconn;
	*hsection = 0;
	*secaddr  = 0;
	*secsize  = 0;

	return __ipc_connect_return(hlock,NT_STATUS_SUCCESS);
}


int32_t __stdcall __ntapi_ipc_connect_section_by_attr(
	__out	void **		hport,
	__in	nt_port_attr *	attr,
	__out	void **		hsection,
	__out	void **		secaddr,
	__out	size_t *	secsize)
{
	nt_port_name		name;
	nt_unicode_string	str;

	__ntapi->tt_port_name_from_attr(
		&name,attr);

	str.strlen = ((size_t)&(((nt_port_name *)0)->null_termination));
	str.maxlen = 0;
	str.buffer = &name.base_named_objects[0];

	return __ipc_connect_by_attr(
		hport,attr,&str,0,
		hsection,secaddr,secsize);
}


int32_t __stdcall __ntapi_ipc_connect_section_by_name(
	__out	void **		hport,
	__in	nt_port_name *	name,
	__out	void **		hsection,
	__out	void **		secaddr,
	__out	size_t *	secsize)
{
	int32_t			status;
	nt_port_attr		attr;
	nt_unicode_string	str;

	if ((status = __ntapi->tt_port_attr_from_name(&attr,name)))
		return status;

	str.strlen = ((size_t)&(((nt_port_name *)0)->null_termination));
	str.maxlen = 0;
	str.buffer = &name->base_named_objects[0];

	return __ipc_connect_by_attr(
		hport,&attr,&str,0,
		hsection,secaddr,secsize);
}


int32_t __stdcall __ntapi_ipc_connect_section_by_symlink(
	__out	void **		hport,
	__in	void *		hsymlink,
	__out	void **		hsection,
	__out	void **		secaddr,
	__out	size_t *	secsize)
{
	int32_t			status;
	nt_port_attr		attr;
	nt_port_name *		name;
	size_t			namelen;
	uintptr_t		buffer[512/sizeof(uintptr_t)];
	nt_unicode_string *	str;

	str         = (nt_unicode_string *)buffer;
	str->strlen = 0;
	str->maxlen = sizeof(buffer) - sizeof(nt_unicode_string);
	str->buffer = (wchar16_t *)&str[1];

	if ((status = __ntapi->zw_query_symbolic_link_object(
			hsymlink,str,&namelen)))
		return status;

	if (str->strlen != ((size_t)&(((nt_port_name *)0)->null_termination)))
		return NT_STATUS_INVALID_PORT_ATTRIBUTES;

	name = (nt_port_name *)str->buffer;

	if ((status = __ntapi->tt_port_attr_from_name(&attr,name)))
		return status;

	return __ipc_connect_by_attr(
		hport,&attr,str,0,
		hsection,secaddr,secsize);
}


int32_t __stdcall __ntapi_ipc_connect_section_by_port(
	__in	void *		hconn,
	__in	nt_port_attr *	attr,
	__out	void **		hsection,
	__out	void **		secaddr,
	__out	size_t *	secsize)
{
	return __ipc_connect_by_attr(
		&(void *){0},attr,0,hconn,
		hsection,secaddr,secsize);
}


int32_t __stdcall __ntapi_ipc_connect_by_attr(
	__out	void **		hport,
	__in	nt_port_attr *	attr)
{
	return __ntapi_ipc_connect_section_by_attr(
		hport,attr,
		&(void *){0},
		&(void *){0},
		&(size_t){0});
}


int32_t __stdcall __ntapi_ipc_connect_by_name(
	__out	void **		hport,
	__in	nt_port_name *	name)
{
	return __ntapi_ipc_connect_section_by_name(
		hport,name,
		&(void *){0},
		&(void *){0},
		&(size_t){0});
}


int32_t __stdcall __ntapi_ipc_connect_by_symlink(
	__out	void **		hport,
	__in	void *		hsymlink)
{
	return __ntapi_ipc_connect_section_by_symlink(
		hport,hsymlink,
		&(void *){0},
		&(void *){0},
		&(size_t){0});
}


int32_t __stdcall __ntapi_ipc_connect_by_port(
	__in	void *		hconn,
	__in	nt_port_attr *	attr)
{
	return __ntapi_ipc_connect_section_by_port(
		hconn,attr,
		&(void *){0},
		&(void *){0},
		&(size_t){0});
}


int __ntapi_ipc_page_alloc(
	struct dalist_ex *	dlist,
	void **			addr,
	size_t *		alloc_size)
{
	int32_t			status;
	ntapi_internals *	__internals;

	__internals = __ntapi_internals();

	if (__internals->ipc_page == __NT_IPC_PAGES)
		return NT_STATUS_QUOTA_EXCEEDED;

	if ((status = __ntapi->zw_allocate_virtual_memory(
			NT_CURRENT_PROCESS_HANDLE,
			addr,0,alloc_size,
			NT_MEM_COMMIT,
			NT_PAGE_READWRITE)))
		return status;

	dalist_deposit_memory_block(
		dlist,*addr,*alloc_size);

	__internals->ipc_pages[__internals->ipc_page++] = *addr;

	return 0;
}


int32_t __stdcall __ntapi_ipc_init_section_by_port(
	__in	void *		hconn,
	__out	void **		hsection,
	__out	void **		secaddr,
	__out	size_t *	secsize)
{
	int32_t			status;
	nt_tty_section_info	secinfo;
	nt_iosb			iosb;
	struct dalist_node_ex *	node;
	nt_ipc_conn *		ipc;
	void *			addr;
	size_t			size;
	ntapi_internals *	__internals;

	/* init */
	__internals = __ntapi_internals();

	/* lock */
	if (at_locked_cas(&__internals->hlock,0,1))
		return NT_STATUS_RESOURCE_NOT_OWNED;

	/* connection node */
	if ((status = dalist_get_node_by_key(
			&__internals->ipc_conns,
			&node,(uintptr_t)hconn,
			DALIST_NODE_TYPE_EXISTING,
			&(uintptr_t){0})))
		return __ipc_connect_return(
			&__internals->hlock,
			NT_STATUS_INTERNAL_ERROR);

	else if (!node)
		return NT_STATUS_NOT_FOUND;

	else
		ipc = (nt_ipc_conn *)&node->dblock;

	/* already mapped? */
	if (ipc->secaddr)
		return __ipc_connect_return(
			&__internals->hlock,
			NT_STATUS_SUCCESS);

	/* section info */
	if ((status = __ntapi->tty_query_information_section(
			hconn,&iosb,&secinfo,0)))
		return __ipc_connect_return(
			&__internals->hlock,
			status);

	/* map section */
	addr = 0;
	size = 0;

	if ((status = __ntapi->zw_map_view_of_section(
			secinfo.section,
			NT_CURRENT_PROCESS_HANDLE,
			&addr,0,
			secinfo.section_size,0,&size,
			NT_VIEW_UNMAP,0,
			NT_PAGE_READWRITE)))
		return __ipc_connect_return(
			&__internals->hlock,
			status);

	/* update */
	*hsection = secinfo.section;
	*secaddr  = addr;
	*secsize  = size;

	/* all done */
	return __ipc_connect_return(
		&__internals->hlock,
		NT_STATUS_SUCCESS);
}


int32_t __stdcall __ntapi_ipc_disconnect_unmap_section_by_port(
	__in	void *		hconn)
{
	int32_t			status;
	struct dalist_node_ex *	node;
	nt_ipc_conn *		ipc;
	ntapi_internals *	__internals;

	/* init */
	__internals = __ntapi_internals();

	/* lock */
	if (at_locked_cas(&__internals->hlock,0,1))
		return NT_STATUS_RESOURCE_NOT_OWNED;

	/* connection node */
	if ((status = dalist_get_node_by_key(
			&__internals->ipc_conns,
			&node,(uintptr_t)hconn,
			DALIST_NODE_TYPE_EXISTING,
			&(uintptr_t){0})))
		return __ipc_connect_return(
			&__internals->hlock,
			NT_STATUS_INTERNAL_ERROR);

	else if (!node)
		return __ipc_connect_return(
			&__internals->hlock,
			NT_STATUS_NOT_FOUND);

	else
		ipc = (nt_ipc_conn *)&node->dblock;

	/* unmap section */
	if (ipc->secaddr)
		__ntapi->zw_unmap_view_of_section(
			NT_CURRENT_PROCESS_HANDLE,
			ipc->secaddr);

	/* close section */
	if (ipc->hsection)
		__ntapi->zw_close(ipc->hsection);

	/* disconnect */
	__ntapi->zw_close(hconn);

	/* remove node */
	dalist_discard_node(&__internals->ipc_conns,node);

	return __ipc_connect_return(
		&__internals->hlock,
		NT_STATUS_SUCCESS);
}