⛏️ index : haiku.git

/*
 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */

#include "UnixAddress.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <util/StringHash.h>

#include <net_datalink.h>
#include <NetUtilities.h>

#include "unix.h"


static const sockaddr_un kEmptyAddress = {
	4,				// sun_len
	AF_UNIX,		// sun_family
	{ '\0', '\0' }	// sun_path
};


char*
UnixAddress::ToString(char *buffer, size_t bufferSize) const
{
	if (!IsValid())
		strlcpy(buffer, "<empty>", bufferSize);
	else if (IsInternalAddress())
		snprintf(buffer, bufferSize, "<%05" B_PRIx32 ">", fInternalID);
	else {
		snprintf(buffer, bufferSize, "<%" B_PRIdDEV ", %" B_PRIdINO ">",
			fVolumeID, fNodeID);
	}

	return buffer;
}


// #pragma mark -


static status_t
unix_copy_address(const sockaddr *from, sockaddr **to, bool replaceWithZeros,
	const sockaddr *mask)
{
	if (replaceWithZeros) {
		sockaddr_un* newAddress = (sockaddr_un*)malloc(kEmptyAddress.sun_len);
		if (newAddress == NULL)
			return B_NO_MEMORY;

		memcpy(newAddress, &kEmptyAddress, kEmptyAddress.sun_len);

		*to = (sockaddr*)newAddress;
		return B_OK;
	} else {
		if (from->sa_family != AF_UNIX)
			return B_MISMATCHED_VALUES;

		*to = (sockaddr*)malloc(from->sa_len);
		if (*to == NULL)
			return B_NO_MEMORY;

		memcpy(*to, from, from->sa_len);

		return B_OK;
	}
}


static bool
unix_equal_addresses(const sockaddr *a, const sockaddr *b)
{
	// NOTE: We compare syntactically only. The real check would involve
	// looking up the node, if for FS addresses.
	if (a->sa_len != b->sa_len)
		return false;

	return memcmp(a, b, a->sa_len) == 0;
}


static bool
unix_equal_ports(const sockaddr *a, const sockaddr *b)
{
	// no ports
	return true;
}


static bool
unix_equal_addresses_and_ports(const sockaddr *a, const sockaddr *b)
{
	return unix_equal_addresses(a, b);
}


static bool
unix_equal_masked_addresses(const sockaddr *a, const sockaddr *b,
	const sockaddr *mask)
{
	// no masks
	return unix_equal_addresses(a, b);
}


static bool
unix_is_empty_address(const sockaddr *address, bool checkPort)
{
	return address == NULL || address->sa_len == 0
		|| address->sa_family == AF_UNSPEC
		|| (address->sa_len >= kEmptyAddress.sun_len
			&& memcmp(address, &kEmptyAddress, kEmptyAddress.sun_len) == 0);
}


static bool
unix_is_same_family(const sockaddr *address)
{
	if (address == NULL)
		return false;

	return address->sa_family == AF_UNIX;
}


static int32
unix_first_mask_bit(const sockaddr *mask)
{
	return 0;
}


static bool
unix_check_mask(const sockaddr *address)
{
	return false;
}


static status_t
unix_print_address_buffer(const sockaddr *_address, char *buffer,
	size_t bufferSize, bool printPort)
{
	if (!buffer)
		return B_BAD_VALUE;

	sockaddr_un* address = (sockaddr_un*)_address;

	if (address == NULL)
		strlcpy(buffer, "<none>", bufferSize);
	else if (address->sun_path[0] != '\0')
		strlcpy(buffer, address->sun_path, bufferSize);
	else if (address->sun_path[1] != '\0')
		snprintf(buffer, bufferSize, "<%.5s>", address->sun_path + 1);
	else
		strlcpy(buffer, "<empty>", bufferSize);

	return B_OK;
}


static status_t
unix_print_address(const sockaddr *address, char **_buffer, bool printPort)
{
	char buffer[128];
	status_t error = unix_print_address_buffer(address, buffer, sizeof(buffer),
		printPort);
	if (error != B_OK)
		return error;

	*_buffer = strdup(buffer);
	return *_buffer != NULL ? B_OK : B_NO_MEMORY;
}


static uint16
unix_get_port(const sockaddr *address)
{
	return 0;
}


static status_t
unix_set_port(sockaddr *address, uint16 port)
{
	return B_BAD_VALUE;
}


static status_t
unix_set_to(sockaddr *address, const sockaddr *from)
{
	if (address == NULL || from == NULL)
		return B_BAD_VALUE;

	if (from->sa_family != AF_UNIX)
		return B_MISMATCHED_VALUES;

	memcpy(address, from, from->sa_len);
	return B_OK;
}


static status_t
unix_set_to_empty_address(sockaddr *address)
{
	return unix_set_to(address, (sockaddr*)&kEmptyAddress);
}


static status_t
unix_mask_address(const sockaddr *address, const sockaddr *mask,
	sockaddr *result)
{
	// no masks
	return unix_set_to(result, address);
}


static status_t
unix_set_to_defaults(sockaddr *defaultMask, sockaddr *defaultBroadcast,
	const sockaddr *address, const sockaddr *netmask)
{
	if (address == NULL)
		return B_BAD_VALUE;

	status_t error = B_OK;
	if (defaultMask != NULL)
		error = unix_set_to_empty_address(defaultMask);
	if (error == B_OK && defaultBroadcast != NULL)
		error = unix_set_to_empty_address(defaultBroadcast);

	return error;
}


static status_t
unix_update_to(sockaddr *address, const sockaddr *from)
{
	if (address == NULL || from == NULL)
		return B_BAD_VALUE;

	if (unix_is_empty_address(from, false))
		return B_OK;

	return unix_set_to(address, from);
}


static uint32
unix_hash_address(const sockaddr* _address, bool includePort)
{
	sockaddr_un* address = (sockaddr_un*)_address;
	if (address == NULL)
		return 0;

	if (address->sun_path[0] == '\0') {
		char buffer[6];
		strlcpy(buffer, address->sun_path + 1, 6);
		return hash_hash_string(buffer);
	}

	return hash_hash_string(address->sun_path);
}


static uint32
unix_hash_address_pair(const sockaddr *ourAddress, const sockaddr *peerAddress)
{
	return unix_hash_address(ourAddress, false) * 17
		+ unix_hash_address(peerAddress, false);
}


static status_t
unix_checksum_address(Checksum *checksum, const sockaddr *_address)
{
	if (checksum == NULL || _address == NULL)
		return B_BAD_VALUE;

	sockaddr_un* address = (sockaddr_un*)_address;
	int len = (char*)address + address->sun_len - address->sun_path;
	for (int i = 0; i < len; i++)
		(*checksum) << (uint8)address->sun_path[i];

	return B_OK;
}


net_address_module_info gAddressModule = {
	{
		NULL,
		0,
		NULL
	},
	NET_ADDRESS_MODULE_FLAG_BROADCAST_ADDRESS,
	unix_copy_address,
	unix_mask_address,
	unix_equal_addresses,
	unix_equal_ports,
	unix_equal_addresses_and_ports,
	unix_equal_masked_addresses,
	unix_is_empty_address,
	unix_is_same_family,
	unix_first_mask_bit,
	unix_check_mask,
	unix_print_address,
	unix_print_address_buffer,
	unix_get_port,
	unix_set_port,
	unix_set_to,
	unix_set_to_empty_address,
	unix_set_to_defaults,
	unix_update_to,
	unix_hash_address,
	unix_hash_address_pair,
	unix_checksum_address,
	NULL	// get_loopback_address
};