%%{
#include "zeek/Reporter.h"

// Like toupper(), but avoid potential for locale-dependence.
static char netbios_toupper(char c)
	{
	if ( c >= 'a' && c <= 'z' )
		return c - 32;
	return c;
	}
%%}

## Decode a NetBIOS name.  See https://jeffpar.github.io/kbarchive/kb/194/Q194203/.
##
## name: The encoded NetBIOS name, e.g., ``"FEEIEFCAEOEFFEECEJEPFDCAEOEBENEF"``.
##
## Returns: The decoded NetBIOS name, e.g., ``"THE NETBIOS NAM"``.  An empty
##          string is returned if the argument is not a valid NetBIOS encoding
##          (though an encoding that would decode to something that includes
##          only null-bytes or space-characters also yields an empty string).
##
## .. zeek:see:: decode_netbios_name_type
function decode_netbios_name%(name: string%): string
	%{
	if ( name->Len() != 32 )
		return val_mgr->EmptyString();

	char buf[16];
	const u_char* s = name->Bytes();
	int i, j;
	int length = 0;

	for ( i = 0, j = 0; i < 16; ++i )
		{
		char c0 = netbios_toupper(s[j++]);
		char c1 = netbios_toupper(s[j++]);

		if ( c0 < 'A' || c0 > 'P' || c1 < 'A' || c1 > 'P' )
			return val_mgr->EmptyString();

		buf[i] = ((c0 - 'A') << 4) + (c1 - 'A');

		if ( isalnum(buf[i]) || ispunct(buf[i]) || buf[i] == ' ' ||
		     // \x01\x02 is seen in at least one case as the first two bytes.
		     // I think that any \x01 and \x02 should always be passed through.
		     buf[i] < 3 )
			++length;
		else
			break;
		}


	// The 16th byte indicates the suffix/type, so don't include it
	if ( length == 16 )
		length = 15;

	// Walk back and remove any trailing spaces or nulls
	for ( ; ; )
		{
		if ( length == 0 )
			return val_mgr->EmptyString();

		auto c = buf[length - 1];

		if ( c != ' ' && c != 0 )
			break;

		--length;
		}

	return zeek::make_intrusive<zeek::StringVal>(length, buf);
	%}

## Converts a NetBIOS name type to its corresponding numeric value.
## See https://en.wikipedia.org/wiki/NetBIOS#NetBIOS_Suffixes.
##
## name: An encoded NetBIOS name.
##
## Returns: The numeric value of *name* or 256 if it's not a valid encoding.
##
## .. zeek:see:: decode_netbios_name
function decode_netbios_name_type%(name: string%): count
	%{
	if ( name->Len() != 32 )
		return val_mgr->Count(256);

	const u_char* s = name->Bytes();

	for ( auto i = 0; i < 32; ++i )
		{
		char c = netbios_toupper(s[i]);

		if ( c < 'A' || c > 'P' )
			return val_mgr->Count(256);
		}

	char c0 = toupper(s[30]);
	char c1 = toupper(s[31]);
	char return_val = ((c0 - 'A') << 4) + (c1 - 'A');
	return zeek::val_mgr->Count(return_val);
	%}
