⛏️ index : haiku.git

/*
 * Copyright 2010, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include <NetworkRoster.h>

#include <errno.h>
#include <sys/sockio.h>

#include <NetworkDevice.h>
#include <NetworkInterface.h>

#include <net_notifications.h>
#include <AutoDeleter.h>
#include <NetServer.h>


// TODO: using AF_INET for the socket isn't really a smart idea, as one
// could completely remove IPv4 support from the stack easily.
// Since in the stack, device_interfaces are pretty much interfaces now, we
// could get rid of them more or less, and make AF_LINK provide the same
// information as AF_INET for the interface functions mostly.


BNetworkRoster BNetworkRoster::sDefault;


/*static*/ BNetworkRoster&
BNetworkRoster::Default()
{
	return sDefault;
}


size_t
BNetworkRoster::CountInterfaces() const
{
	FileDescriptorCloser socket(::socket(AF_INET, SOCK_DGRAM, 0));
	if (!socket.IsSet())
		return 0;

	ifconf config;
	config.ifc_len = sizeof(config.ifc_value);
	if (ioctl(socket.Get(), SIOCGIFCOUNT, &config, sizeof(struct ifconf)) != 0)
		return 0;

	return (size_t)config.ifc_value;
}


status_t
BNetworkRoster::GetNextInterface(uint32* cookie,
	BNetworkInterface& interface) const
{
	// TODO: think about caching the interfaces!

	if (cookie == NULL)
		return B_BAD_VALUE;

	// get a list of all interfaces

	FileDescriptorCloser socket (::socket(AF_INET, SOCK_DGRAM, 0));
	if (!socket.IsSet())
		return errno;

	ifconf config;
	config.ifc_len = sizeof(config.ifc_value);
	if (ioctl(socket.Get(), SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0)
		return errno;

	size_t count = (size_t)config.ifc_value;
	if (count == 0)
		return B_BAD_VALUE;

	char* buffer = (char*)malloc(count * sizeof(struct ifreq));
	if (buffer == NULL)
		return B_NO_MEMORY;

	MemoryDeleter deleter(buffer);

	config.ifc_len = count * sizeof(struct ifreq);
	config.ifc_buf = buffer;
	if (ioctl(socket.Get(), SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0)
		return errno;

	ifreq* interfaces = (ifreq*)buffer;
	ifreq* end = (ifreq*)(buffer + config.ifc_len);

	for (uint32 i = 0; interfaces < end; i++) {
		interface.SetTo(interfaces[0].ifr_name);
		if (i == *cookie) {
			(*cookie)++;
			return B_OK;
		}

		interfaces = (ifreq*)((uint8*)interfaces
			+ _SIZEOF_ADDR_IFREQ(interfaces[0]));
	}

	return B_BAD_VALUE;
}


status_t
BNetworkRoster::AddInterface(const char* name)
{
	FileDescriptorCloser socket (::socket(AF_INET, SOCK_DGRAM, 0));
	if (!socket.IsSet())
		return errno;

	ifaliasreq request;
	memset(&request, 0, sizeof(ifaliasreq));
	strlcpy(request.ifra_name, name, IF_NAMESIZE);

	if (ioctl(socket.Get(), SIOCAIFADDR, &request, sizeof(request)) != 0)
		return errno;

	return B_OK;
}


status_t
BNetworkRoster::AddInterface(const BNetworkInterface& interface)
{
	return AddInterface(interface.Name());
}


status_t
BNetworkRoster::RemoveInterface(const char* name)
{
	FileDescriptorCloser socket(::socket(AF_INET, SOCK_DGRAM, 0));
	if (!socket.IsSet())
		return errno;

	ifreq request;
	strlcpy(request.ifr_name, name, IF_NAMESIZE);

	request.ifr_addr.sa_family = AF_UNSPEC;

	if (ioctl(socket.Get(), SIOCDIFADDR, &request, sizeof(request)) != 0)
		return errno;

	return B_OK;
}


status_t
BNetworkRoster::RemoveInterface(const BNetworkInterface& interface)
{
	return RemoveInterface(interface.Name());
}


int32
BNetworkRoster::CountPersistentNetworks() const
{
	BMessenger networkServer(kNetServerSignature);
	BMessage message(kMsgCountPersistentNetworks);
	BMessage reply;
	if (networkServer.SendMessage(&message, &reply) != B_OK)
		return 0;

	int32 count = 0;
	if (reply.FindInt32("count", &count) != B_OK)
		return 0;

	return count;
}


status_t
BNetworkRoster::GetNextPersistentNetwork(uint32* cookie,
	wireless_network& network) const
{
	BMessenger networkServer(kNetServerSignature);
	BMessage message(kMsgGetPersistentNetwork);
	message.AddInt32("index", (int32)*cookie);

	BMessage reply;
	status_t result = networkServer.SendMessage(&message, &reply);
	if (result != B_OK)
		return result;

	status_t status;
	if (reply.FindInt32("status", &status) != B_OK)
		return B_ERROR;
	if (status != B_OK)
		return status;

	BMessage networkMessage;
	if (reply.FindMessage("network", &networkMessage) != B_OK)
		return B_ERROR;

	BString networkName;
	if (networkMessage.FindString("name", &networkName) != B_OK)
		return B_ERROR;

	memset(network.name, 0, sizeof(network.name));
	strlcpy(network.name, networkName.String(), sizeof(network.name));

	BNetworkAddress address;
	if (networkMessage.FindFlat("address", &network.address) != B_OK)
		network.address.Unset();

	if (networkMessage.FindUInt32("flags", &network.flags) != B_OK)
		network.flags = 0;

	if (networkMessage.FindUInt32("authentication_mode",
			&network.authentication_mode) != B_OK) {
		network.authentication_mode = B_NETWORK_AUTHENTICATION_NONE;
	}

	if (networkMessage.FindUInt32("cipher", &network.cipher) != B_OK)
		network.cipher = B_NETWORK_CIPHER_NONE;

	if (networkMessage.FindUInt32("group_cipher", &network.group_cipher)
			!= B_OK) {
		network.group_cipher = B_NETWORK_CIPHER_NONE;
	}

	if (networkMessage.FindUInt32("key_mode", &network.key_mode) != B_OK)
		network.key_mode = B_KEY_MODE_NONE;

	return B_OK;
}


status_t
BNetworkRoster::AddPersistentNetwork(const wireless_network& network)
{
	BMessage message(kMsgAddPersistentNetwork);
	BString networkName;
	networkName.SetTo(network.name, sizeof(network.name));
	status_t status = message.AddString("name", networkName);
	if (status == B_OK) {
		BNetworkAddress address = network.address;
		status = message.AddFlat("address", &address);
	}

	if (status == B_OK)
		status = message.AddUInt32("flags", network.flags);
	if (status == B_OK) {
		status = message.AddUInt32("authentication_mode",
			network.authentication_mode);
	}
	if (status == B_OK)
		status = message.AddUInt32("cipher", network.cipher);
	if (status == B_OK)
		status = message.AddUInt32("group_cipher", network.group_cipher);
	if (status == B_OK)
		status = message.AddUInt32("key_mode", network.key_mode);

	if (status != B_OK)
		return status;

	BMessenger networkServer(kNetServerSignature);
	BMessage reply;
	status = networkServer.SendMessage(&message, &reply);
	if (status == B_OK)
		reply.FindInt32("status", &status);

	return status;
}


status_t
BNetworkRoster::RemovePersistentNetwork(const char* name)
{
	BMessage message(kMsgRemovePersistentNetwork);
	status_t status = message.AddString("name", name);
	if (status != B_OK)
		return status;

	BMessenger networkServer(kNetServerSignature);
	BMessage reply;
	status = networkServer.SendMessage(&message, &reply);
	if (status == B_OK)
		reply.FindInt32("status", &status);

	return status;
}


status_t
BNetworkRoster::StartWatching(const BMessenger& target, uint32 eventMask)
{
	return start_watching_network(eventMask, target);
}


void
BNetworkRoster::StopWatching(const BMessenger& target)
{
	stop_watching_network(target);
}


// #pragma mark - private


BNetworkRoster::BNetworkRoster()
{
}


BNetworkRoster::~BNetworkRoster()
{
}