/*
 * Copyright 2006-2018, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Axel Dörfler, axeld@pinc-software.de
 * 		Vegard Wærp, vegarwa@online.no
 *		Alexander von Gluck, kallisti5@unixzen.com
 */
 
 
#include "NetServer.h"
 
#include <errno.h>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <strings.h>
#include <syslog.h>
#include <unistd.h>
 
#include <arpa/inet.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/sockio.h>
 
#include <Alert.h>
#include <Deskbar.h>
#include <Directory.h>
#include <Entry.h>
#include <NetworkDevice.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include <NetworkSettings.h>
#include <Path.h>
#include <PathMonitor.h>
#include <Roster.h>
#include <Server.h>
#include <TextView.h>
#include <FindDirectory.h>
 
#include <AutoDeleter.h>
#include <WPASupplicant.h>
 
#include "AutoconfigLooper.h"
#include "Services.h"
 
extern "C" {
#	include <freebsd_network/compat/sys/cdefs.h>
#	include <freebsd_network/compat/sys/ioccom.h>
#	include <net80211/ieee80211_ioctl.h>
}
 
 
using namespace BNetworkKit;
 
 
typedef std::map<std::string, AutoconfigLooper*> LooperMap;
 
 
class NetServer : public BServer {
public:
								NetServer(status_t& status);
	virtual						~NetServer();
 
	virtual	void				AboutRequested();
	virtual	void				ReadyToRun();
	virtual	void				MessageReceived(BMessage* message);
 
private:
			bool				_IsValidFamily(uint32 family);
			bool				_IsValidInterface(BNetworkInterface& interface);
			void				_RemoveInvalidInterfaces();
			status_t			_RemoveInterface(const char* name);
			status_t			_DisableInterface(const char* name);
			bool				_TestForInterface(const char* name);
			status_t			_ConfigureInterface(BMessage& interface);
			status_t			_ConfigureResolver(
									BMessage& resolverConfiguration);
			bool				_QuitLooperForDevice(const char* device);
			AutoconfigLooper*	_LooperForDevice(const char* device);
			status_t			_ConfigureDevice(const char* path);
			void				_ConfigureDevices(const char* path,
									BStringList& devicesAlreadyConfigured,
									BMessage* suggestedInterface = NULL);
			void				_ConfigureInterfacesFromSettings(
									BStringList& devicesSet,
									BMessage* _missingDevice = NULL);
			void				_ConfigureIPv6LinkLocal(const char* name);
 
			void				_BringUpInterfaces();
			void				_StartServices();
			status_t			_HandleDeviceMonitor(BMessage* message);
 
			status_t			_AutoJoinNetwork(const BMessage& message);
			status_t			_JoinNetwork(const BMessage& message,
									const BNetworkAddress* address = NULL,
									const char* name = NULL);
			status_t			_LeaveNetwork(const BMessage& message);
 
			status_t			_ConvertNetworkToSettings(BMessage& message);
			status_t			_ConvertNetworkFromSettings(BMessage& message);
 
private:
			BNetworkSettings	fSettings;
			LooperMap			fDeviceMap;
			BMessenger			fServices;
};
 
 
// #pragma mark - private functions
 
 
static status_t
set_80211(const char* name, int32 type, void* data,
	int32 length = 0, int32 value = 0)
{
	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
	if (socket < 0)
		return errno;
 
	FileDescriptorCloser closer(socket);
 
	struct ieee80211req ireq;
	strlcpy(ireq.i_name, name, IF_NAMESIZE);
	ireq.i_type = type;
	ireq.i_val = value;
	ireq.i_len = length;
	ireq.i_data = data;
 
	if (ioctl(socket, SIOCS80211, &ireq, sizeof(struct ieee80211req)) < 0)
		return errno;
 
	return B_OK;
}
 
 
//	#pragma mark -
 
 
NetServer::NetServer(status_t& error)
	:
	BServer(kNetServerSignature, false, &error)
{
}
 
 
NetServer::~NetServer()
{
	BPrivate::BPathMonitor::StopWatching("/dev/net", this);
}
 
 
void
NetServer::AboutRequested()
{
	BAlert *alert = new BAlert("about", "Networking Server\n"
		"\tCopyright " B_UTF8_COPYRIGHT "2006, Haiku.\n", "OK");
	BTextView *view = alert->TextView();
	BFont font;
 
	view->SetStylable(true);
 
	view->GetFont(&font);
	font.SetSize(18);
	font.SetFace(B_BOLD_FACE);
	view->SetFontAndColor(0, 17, &font);
 
	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
	alert->Go(NULL);
}
 
 
void
NetServer::ReadyToRun()
{
	fSettings.StartMonitoring(this);
	_BringUpInterfaces();
	_StartServices();
 
	BPrivate::BPathMonitor::StartWatching("/dev/net",
		B_WATCH_FILES_ONLY | B_WATCH_RECURSIVELY, this);
}
 
 
void
NetServer::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case B_PATH_MONITOR:
		{
			fSettings.Update(message);
			_HandleDeviceMonitor(message);
			break;
		}
 
		case BNetworkSettings::kMsgInterfaceSettingsUpdated:
		{
			BStringList devicesSet;
			_ConfigureInterfacesFromSettings(devicesSet);
			break;
		}
 
		case BNetworkSettings::kMsgServiceSettingsUpdated:
		{
			BMessage update = fSettings.Services();
			update.what = kMsgUpdateServices;
 
			fServices.SendMessage(&update);
			break;
		}
 
		case kMsgConfigureInterface:
		{
			status_t status = _ConfigureInterface(*message);
 
			BMessage reply(B_REPLY);
			reply.AddInt32("status", status);
			message->SendReply(&reply);
			break;
		}
 
		case kMsgConfigureResolver:
		{
			status_t status = _ConfigureResolver(*message);
 
			BMessage reply(B_REPLY);
			reply.AddInt32("status", status);
			message->SendReply(&reply);
			break;
		}
 
		case kMsgJoinNetwork:
		{
			status_t status = _JoinNetwork(*message);
 
			BMessage reply(B_REPLY);
			reply.AddInt32("status", status);
			message->SendReply(&reply);
			break;
		}
 
		case kMsgLeaveNetwork:
		{
			status_t status = _LeaveNetwork(*message);
 
			BMessage reply(B_REPLY);
			reply.AddInt32("status", status);
			message->SendReply(&reply);
			break;
		}
 
		case kMsgAutoJoinNetwork:
		{
			_AutoJoinNetwork(*message);
			break;
		}
 
		case kMsgCountPersistentNetworks:
		{
			BMessage reply(B_REPLY);
			reply.AddInt32("count", fSettings.CountNetworks());
			message->SendReply(&reply);
			break;
		}
 
		case kMsgGetPersistentNetwork:
		{
			uint32 index = 0;
			status_t result = message->FindInt32("index", (int32*)&index);
 
			BMessage reply(B_REPLY);
			if (result == B_OK) {
				BMessage network;
				result = fSettings.GetNextNetwork(index, network);
				if (result == B_OK)
					result = reply.AddMessage("network", &network);
			}
 
			reply.AddInt32("status", result);
			message->SendReply(&reply);
			break;
		}
 
		case kMsgAddPersistentNetwork:
		{
			BMessage network = *message;
			status_t result = fSettings.AddNetwork(network);
 
			BMessage reply(B_REPLY);
			reply.AddInt32("status", result);
			message->SendReply(&reply);
			break;
		}
 
		case kMsgRemovePersistentNetwork:
		{
			const char* networkName = NULL;
			status_t result = message->FindString("name", &networkName);
			if (result == B_OK)
				result = fSettings.RemoveNetwork(networkName);
 
			BMessage reply(B_REPLY);
			reply.AddInt32("status", result);
			message->SendReply(&reply);
			break;
		}
 
		case kMsgIsServiceRunning:
		{
			// Forward the message to the handler that can answer it
			BHandler* handler = fServices.Target(NULL);
			if (handler != NULL)
				handler->MessageReceived(message);
			break;
		}
 
		default:
			BApplication::MessageReceived(message);
			return;
	}
}
 
 
/*!	Checks if provided address family is valid.
	Families include AF_INET, AF_INET6, AF_APPLETALK, etc
*/
bool
NetServer::_IsValidFamily(uint32 family)
{
	// Mostly verifies add-on is present
	int socket = ::socket(family, SOCK_DGRAM, 0);
	if (socket < 0)
		return false;
 
	close(socket);
	return true;
}
 
 
/*!	Checks if an interface is valid, that is, if it has an address in any
	family, and, in case of ethernet, a hardware MAC address.
*/
bool
NetServer::_IsValidInterface(BNetworkInterface& interface)
{
	// check if it has an address
 
	if (interface.CountAddresses() == 0)
		return false;
 
	// check if it has a hardware address, too, in case of ethernet
 
	BNetworkAddress link;
	if (interface.GetHardwareAddress(link) != B_OK)
		return false;
 
	if (link.LinkLevelType() == IFT_ETHER && link.LinkLevelAddressLength() != 6)
		return false;
 
	return true;
}
 
 
void
NetServer::_RemoveInvalidInterfaces()
{
	BNetworkRoster& roster = BNetworkRoster::Default();
	BNetworkInterface interface;
	uint32 cookie = 0;
 
	while (roster.GetNextInterface(&cookie, interface) == B_OK) {
		if (!_IsValidInterface(interface)) {
			// remove invalid interface
			_RemoveInterface(interface.Name());
		}
	}
}
 
 
bool
NetServer::_TestForInterface(const char* name)
{
 
	BNetworkRoster& roster = BNetworkRoster::Default();
	int32 nameLength = strlen(name);
	BNetworkInterface interface;
	uint32 cookie = 0;
 
	while (roster.GetNextInterface(&cookie, interface) == B_OK) {
		if (!strncmp(interface.Name(), name, nameLength))
			return true;
	}
 
	return false;
}
 
 
status_t
NetServer::_RemoveInterface(const char* name)
{
	BNetworkRoster& roster = BNetworkRoster::Default();
	status_t status = roster.RemoveInterface(name);
	if (status != B_OK) {
		fprintf(stderr, "%s: Could not delete interface %s: %s\n",
			Name(), name, strerror(status));
		return status;
	}
 
	return B_OK;
}
 
 
status_t
NetServer::_DisableInterface(const char* name)
{
	BNetworkInterface interface(name);
	int32 flags = interface.Flags();
 
	// Set interface down
	flags &= ~(IFF_UP | IFF_AUTO_CONFIGURED | IFF_CONFIGURING);
 
	status_t status = interface.SetFlags(flags);
	if (status != B_OK) {
		fprintf(stderr, "%s: Setting flags failed: %s\n", Name(),
			strerror(status));
		return status;
	}
 
	fprintf(stderr, "%s: set %s interface down...\n", Name(), name);
	return B_OK;
}
 
 
status_t
NetServer::_ConfigureInterface(BMessage& message)
{
	const char* name;
	if (message.FindString("device", &name) != B_OK)
		return B_BAD_VALUE;
 
	bool startAutoConfig = false;
 
	int32 flags;
	if (message.FindInt32("flags", &flags) != B_OK)
		flags = IFF_UP;
 
	bool autoConfigured;
	if (message.FindBool("auto_configured", &autoConfigured) == B_OK
			&& autoConfigured) {
		flags |= IFF_AUTO_CONFIGURED;
	}
 
	int32 mtu;
	if (message.FindInt32("mtu", &mtu) != B_OK)
		mtu = -1;
 
	int32 metric;
	if (message.FindInt32("metric", &metric) != B_OK)
		metric = -1;
 
	BNetworkInterface interface(name);
	if (!interface.Exists()) {
		// the interface does not exist yet, we have to add it first
		BNetworkRoster& roster = BNetworkRoster::Default();
 
		status_t status = roster.AddInterface(interface);
		if (status != B_OK) {
			fprintf(stderr, "%s: Could not add interface: %s\n",
				interface.Name(), strerror(status));
			return status;
		}
	}
 
	// Set up IPv6 Link Local address (based on MAC, if not loopback)
 
	// TODO: our IPv6 stack is still fairly fragile. We need more v6 work
	// (including IPv6 address scope flags before we start attaching link
	// local addresses by default.
	//_ConfigureIPv6LinkLocal(name);
 
	BMessage addressMessage;
	for (int32 index = 0; message.FindMessage("address", index,
			&addressMessage) == B_OK; index++) {
		BNetworkInterfaceAddressSettings addressSettings(addressMessage);
 
		if (addressSettings.IsAutoConfigure()) {
			_QuitLooperForDevice(name);
			startAutoConfig = true;
		} else if (!addressSettings.Gateway().IsEmpty()) {
			// add gateway route, if we're asked for it
			interface.RemoveDefaultRoute(addressSettings.Family());
				// Try to remove a previous default route, doesn't matter
				// if it fails.
 
			status_t status = interface.AddDefaultRoute(
				addressSettings.Gateway());
			if (status != B_OK) {
				fprintf(stderr, "%s: Could not add route for %s: %s\n",
					Name(), name, strerror(errno));
			}
		}
 
		// set address/mask/broadcast/peer
 
		if (!addressSettings.Address().IsEmpty()
			|| !addressSettings.Mask().IsEmpty()
			|| !addressSettings.Broadcast().IsEmpty()
			|| !addressSettings.Peer().IsEmpty()
			|| !addressSettings.IsAutoConfigure()) {
			BNetworkInterfaceAddress interfaceAddress;
			interfaceAddress.SetAddress(addressSettings.Address());
			interfaceAddress.SetMask(addressSettings.Mask());
			if (!addressSettings.Broadcast().IsEmpty())
				interfaceAddress.SetBroadcast(addressSettings.Broadcast());
			else if (!addressSettings.Peer().IsEmpty())
				interfaceAddress.SetDestination(addressSettings.Peer());
 
			status_t status = interface.SetAddress(interfaceAddress);
			if (status != B_OK) {
				fprintf(stderr, "%s: Setting address failed: %s\n", Name(),
					strerror(status));
				return status;
			}
		}
 
		// set flags
 
		if (flags != 0) {
			int32 newFlags = interface.Flags();
			newFlags = (newFlags & ~IFF_CONFIGURING) | flags;
			if (!autoConfigured)
				newFlags &= ~IFF_AUTO_CONFIGURED;
 
			status_t status = interface.SetFlags(newFlags);
			if (status != B_OK) {
				fprintf(stderr, "%s: Setting flags failed: %s\n", Name(),
					strerror(status));
			}
		}
 
		// set options
 
		if (mtu != -1) {
			status_t status = interface.SetMTU(mtu);
			if (status != B_OK) {
				fprintf(stderr, "%s: Setting MTU failed: %s\n", Name(),
					strerror(status));
			}
		}
 
		if (metric != -1) {
			status_t status = interface.SetMetric(metric);
			if (status != B_OK) {
				fprintf(stderr, "%s: Setting metric failed: %s\n", Name(),
					strerror(status));
			}
		}
	}
 
	// Join the specified networks
	BMessage networkMessage;
	for (int32 index = 0; message.FindMessage("network", index,
			&networkMessage) == B_OK; index++) {
		const char* networkName = message.GetString("name", NULL);
		const char* addressString = message.GetString("mac", NULL);
 
		BNetworkAddress address;
		status_t addressStatus = address.SetTo(AF_LINK, addressString);
 
		BNetworkDevice device(name);
		if (device.IsWireless() && !device.HasLink()) {
			status_t status = _JoinNetwork(message,
				addressStatus == B_OK ? &address : NULL, networkName);
			if (status != B_OK) {
				fprintf(stderr, "%s: joining network \"%s\" failed: %s\n",
					interface.Name(), networkName, strerror(status));
			}
		}
	}
 
	if (startAutoConfig) {
		// start auto configuration
		AutoconfigLooper* looper = new AutoconfigLooper(this, name);
		looper->Run();
 
		fDeviceMap[name] = looper;
	} else if (!autoConfigured)
		_QuitLooperForDevice(name);
 
	return B_OK;
}
 
 
status_t
NetServer::_ConfigureResolver(BMessage& resolverConfiguration)
{
	// Store resolver settings in resolv.conf file, while maintaining any
	// user specified settings already present.
 
	BPath path;
	if (find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path) != B_OK
		|| path.Append("network/resolv.conf") != B_OK)
		return B_ERROR;
 
	FILE* file = fopen(path.Path(), "r+");
	// open existing resolv.conf if possible
	if (file == NULL) {
		// no existing resolv.conf, create a new one
		file = fopen(path.Path(), "w");
		if (file == NULL) {
			fprintf(stderr, "Could not open resolv.conf: %s\n",
				strerror(errno));
			return errno;
		}
	} else {
		// An existing resolv.conf was found, parse it for user settings
		const char* staticDNS = "# Static DNS Only";
		size_t sizeStaticDNS = strlen(staticDNS);
		const char* dynamicDNS = "# Dynamic DNS entries";
		size_t sizeDynamicDNS = strlen(dynamicDNS);
		char resolveConfBuffer[80];
		size_t sizeResolveConfBuffer = sizeof(resolveConfBuffer);
 
		while (fgets(resolveConfBuffer, sizeResolveConfBuffer, file)) {
			if (strncmp(resolveConfBuffer, staticDNS, sizeStaticDNS) == 0) {
				// If DNS is set to static only, don't modify
				fclose(file);
				return B_OK;
			} else if (strncmp(resolveConfBuffer, dynamicDNS, sizeDynamicDNS)
					== 0) {
				// Overwrite existing dynamic entries
				break;
			}
		}
 
		if (feof(file) != 0) {
			// No static entries found, close and re-open as new file
			fclose(file);
			file = fopen(path.Path(), "w");
			if (file == NULL) {
				fprintf(stderr, "Could not open resolv.conf: %s\n",
					strerror(errno));
				return errno;
			}
		}
	}
 
	fprintf(file, "# Added automatically by DHCP\n");
 
	const char* nameserver;
	for (int32 i = 0; resolverConfiguration.FindString("nameserver", i,
			&nameserver) == B_OK; i++) {
		fprintf(file, "nameserver %s\n", nameserver);
	}
 
	const char* domain;
	if (resolverConfiguration.FindString("domain", &domain) == B_OK)
		fprintf(file, "domain %s\n", domain);
 
	fprintf(file, "# End of automatic DHCP additions\n");
 
	fclose(file);
 
	return B_OK;
}
 
 
bool
NetServer::_QuitLooperForDevice(const char* device)
{
	LooperMap::iterator iterator = fDeviceMap.find(device);
	if (iterator == fDeviceMap.end())
		return false;
 
	// there is a looper for this device - quit it
	if (iterator->second->Lock())
		iterator->second->Quit();
 
	fDeviceMap.erase(iterator);
	return true;
}
 
 
AutoconfigLooper*
NetServer::_LooperForDevice(const char* device)
{
	LooperMap::const_iterator iterator = fDeviceMap.find(device);
	if (iterator == fDeviceMap.end())
		return NULL;
 
	return iterator->second;
}
 
 
status_t
NetServer::_ConfigureDevice(const char* device)
{
	// bring interface up, but don't configure it just yet
	BMessage interface;
	interface.AddString("device", device);
	BMessage address;
	address.AddString("family", "inet");
	address.AddBool("auto_config", true);
	interface.AddMessage("address", &address);
 
	return _ConfigureInterface(interface);
}
 
 
/*! \brief Traverses the device tree starting from \a startPath, and configures
		everything that has not yet been configured via settings before.
 
	\param suggestedInterface Contains the configuration of an interface that
		does not have any hardware left. It is used to configure the first
		unconfigured device. This allows to move a Haiku configuration around
		without losing the network configuration.
*/
void
NetServer::_ConfigureDevices(const char* startPath,
	BStringList& devicesAlreadyConfigured, BMessage* suggestedInterface)
{
	BDirectory directory(startPath);
	BEntry entry;
	while (directory.GetNextEntry(&entry) == B_OK) {
		char name[B_FILE_NAME_LENGTH];
		struct stat stat;
		BPath path;
		if (entry.GetName(name) != B_OK
			|| entry.GetPath(&path) != B_OK
			|| entry.GetStat(&stat) != B_OK)
			continue;
 
		if (S_ISBLK(stat.st_mode) || S_ISCHR(stat.st_mode)) {
			if (suggestedInterface != NULL
				&& suggestedInterface->SetString("device", path.Path()) == B_OK
				&& _ConfigureInterface(*suggestedInterface) == B_OK)
				suggestedInterface = NULL;
			else if (!devicesAlreadyConfigured.HasString(path.Path()))
				_ConfigureDevice(path.Path());
		} else if (entry.IsDirectory()) {
			_ConfigureDevices(path.Path(), devicesAlreadyConfigured,
				suggestedInterface);
		}
	}
}
 
 
void
NetServer::_ConfigureInterfacesFromSettings(BStringList& devicesSet,
	BMessage* _missingDevice)
{
	BMessage interface;
	uint32 cookie = 0;
	bool missing = false;
	while (fSettings.GetNextInterface(cookie, interface) == B_OK) {
		const char *device;
		if (interface.FindString("device", &device) != B_OK)
			continue;
 
		bool disabled = false;
		if (interface.FindBool("disabled", &disabled) == B_OK && disabled) {
			// disabled by user request
			_DisableInterface(device);
			continue;
		}
 
		if (!strncmp(device, "/dev/net/", 9)) {
			// it's a kernel device, check if it's present
			BEntry entry(device);
			if (!entry.Exists()) {
				if (!missing && _missingDevice != NULL) {
					*_missingDevice = interface;
					missing = true;
				}
				continue;
			}
		}
 
		if (_ConfigureInterface(interface) == B_OK)
			devicesSet.Add(device);
	}
}
 
 
void
NetServer::_BringUpInterfaces()
{
	// we need a socket to talk to the networking stack
	if (!_IsValidFamily(AF_LINK)) {
		fprintf(stderr, "%s: The networking stack doesn't seem to be "
			"available.\n", Name());
		Quit();
		return;
	}
 
	_RemoveInvalidInterfaces();
 
	// First, we look into the settings, and try to bring everything up from
	// there
 
	BStringList devicesAlreadyConfigured;
	BMessage missingDevice;
	_ConfigureInterfacesFromSettings(devicesAlreadyConfigured, &missingDevice);
 
	// Check configuration
 
	if (!_TestForInterface("loop")) {
		// there is no loopback interface, create one
		BMessage interface;
		interface.AddString("device", "loop");
		BMessage v4address;
		v4address.AddString("family", "inet");
		v4address.AddString("address", "127.0.0.1");
		interface.AddMessage("address", &v4address);
 
		// Check for IPv6 support and add ::1
		if (_IsValidFamily(AF_INET6)) {
			BMessage v6address;
			v6address.AddString("family", "inet6");
			v6address.AddString("address", "::1");
			interface.AddMessage("address", &v6address);
		}
		_ConfigureInterface(interface);
	}
 
	// TODO: also check if the networking driver is correctly initialized!
	//	(and check for other devices to take over its configuration)
 
	// There is no driver configured - see if there is one and try to use it
	_ConfigureDevices("/dev/net", devicesAlreadyConfigured,
		missingDevice.HasString("device") ? &missingDevice : NULL);
}
 
 
/*!	Configure the link local address based on the network card's MAC address
	if this isn't a loopback device.
*/
void
NetServer::_ConfigureIPv6LinkLocal(const char* name)
{
	// Check for IPv6 support
	if (!_IsValidFamily(AF_INET6))
		return;
 
	BNetworkInterface interface(name);
 
	// Lets make sure this is *not* the loopback interface
	if ((interface.Flags() & IFF_LOOPBACK) != 0)
		return;
 
	BNetworkAddress link;
	status_t result = interface.GetHardwareAddress(link);
 
	if (result != B_OK || link.LinkLevelAddressLength() != 6)
		return;
 
	const uint8* mac = link.LinkLevelAddress();
 
	// Check for a few failure situations
	static const uint8 zeroMac[6] = {0, 0, 0, 0, 0, 0};
	static const uint8 fullMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	if (memcmp(mac, zeroMac, 6) == 0
		|| memcmp(mac, fullMac, 6) == 0) {
		// Mac address is all 0 or all FF's
		syslog(LOG_DEBUG, "%s: MacAddress for interface '%s' is invalid.",
			__func__, name);
		return;
	}
 
	// Generate a Link Local Scope address
	// (IPv6 address based on Mac address)
	in6_addr addressRaw;
	memset(addressRaw.s6_addr, 0, sizeof(addressRaw.s6_addr));
	addressRaw.s6_addr[0] = 0xfe;
	addressRaw.s6_addr[1] = 0x80;
	addressRaw.s6_addr[8] = mac[0] ^ 0x02;
	addressRaw.s6_addr[9] = mac[1];
	addressRaw.s6_addr[10] = mac[2];
	addressRaw.s6_addr[11] = 0xff;
	addressRaw.s6_addr[12] = 0xfe;
	addressRaw.s6_addr[13] = mac[3];
	addressRaw.s6_addr[14] = mac[4];
	addressRaw.s6_addr[15] = mac[5];
 
	BNetworkAddress localLinkAddress(addressRaw, 0);
	BNetworkAddress localLinkMask("ffff:ffff:ffff:ffff::"); // 64
	BNetworkAddress localLinkBroadcast("fe80::ffff:ffff:ffff:ffff");
 
	if (interface.FindAddress(localLinkAddress) >= 0) {
		// uhoh... already has a local link address
 
		/*	TODO: Check for any local link scope addresses assigned to card
			There isn't any flag at the moment though for address scope
		*/
		syslog(LOG_DEBUG, "%s: Local Link address already assigned to %s\n",
			__func__, name);
		return;
	}
 
	BNetworkInterfaceAddress interfaceAddress;
	interfaceAddress.SetAddress(localLinkAddress);
	interfaceAddress.SetMask(localLinkMask);
	interfaceAddress.SetBroadcast(localLinkMask);
 
	/*	TODO: Duplicate Address Detection.  (DAD)
		Need to blast an icmp packet over the IPv6 network from :: to ensure
		there aren't duplicate MAC addresses on the network. (definitely an
		edge case, but a possible issue)
	*/
 
	interface.AddAddress(interfaceAddress);
}
 
 
void
NetServer::_StartServices()
{
	BHandler* services = new (std::nothrow) Services(fSettings.Services());
	if (services != NULL) {
		AddHandler(services);
		fServices = BMessenger(services);
	}
}
 
 
status_t
NetServer::_HandleDeviceMonitor(BMessage* message)
{
	int32 opcode;
	const char* path;
	if (message->FindInt32("opcode", &opcode) != B_OK
		|| (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED)
		|| message->FindString("path", &path) != B_OK)
		return B_BAD_VALUE;
 
	if (strncmp(path, "/dev/net/", 9)) {
		// not a valid device entry, ignore
		return B_NAME_NOT_FOUND;
	}
 
	if (opcode == B_ENTRY_CREATED)
		_ConfigureDevice(path);
	else
		_RemoveInterface(path);
 
	return B_OK;
}
 
 
status_t
NetServer::_AutoJoinNetwork(const BMessage& message)
{
	const char* name = NULL;
	if (message.FindString("device", &name) != B_OK)
		return B_BAD_VALUE;
 
	BNetworkDevice device(name);
 
	// Choose among configured networks
 
	uint32 cookie = 0;
	BMessage networkMessage;
	while (fSettings.GetNextNetwork(cookie, networkMessage) == B_OK) {
		status_t status = B_ERROR;
		wireless_network network;
		const char* networkName;
		BNetworkAddress link;
 
		const char* mac = NULL;
		if (networkMessage.FindString("mac", &mac) == B_OK) {
			link.SetTo(AF_LINK, mac);
			status = device.GetNetwork(link, network);
		} else if (networkMessage.FindString("name", &networkName) == B_OK)
			status = device.GetNetwork(networkName, network);
 
		if (status == B_OK) {
			status = _JoinNetwork(message, mac != NULL ? &link : NULL,
				network.name);
			printf("auto join network \"%s\": %s\n", network.name,
				strerror(status));
			if (status == B_OK)
				return B_OK;
		}
	}
 
	return B_NO_INIT;
}
 
 
status_t
NetServer::_JoinNetwork(const BMessage& message, const BNetworkAddress* address,
	const char* name)
{
	const char* deviceName;
	if (message.FindString("device", &deviceName) != B_OK)
		return B_BAD_VALUE;
 
	BNetworkAddress deviceAddress;
	message.FindFlat("address", &deviceAddress);
	if (address == NULL)
		address = &deviceAddress;
 
	if (name == NULL)
		message.FindString("name", &name);
	if (name == NULL) {
		// No name specified, we need a network address
		if (address->Family() != AF_LINK)
			return B_BAD_VALUE;
	}
 
	// Search for a network configuration that may override the defaults
 
	bool found = false;
	uint32 cookie = 0;
	BMessage networkMessage;
	while (fSettings.GetNextNetwork(cookie, networkMessage) == B_OK) {
		const char* networkName;
		if (networkMessage.FindString("name", &networkName) == B_OK
			&& name != NULL && address->Family() != AF_LINK
			&& !strcmp(name, networkName)) {
			found = true;
			break;
		}
 
		const char* mac;
		if (networkMessage.FindString("mac", &mac) == B_OK
			&& address->Family() == AF_LINK) {
			BNetworkAddress link(AF_LINK, mac);
			if (link == *address) {
				found = true;
				break;
			}
		}
	}
 
	const char* password;
	if (message.FindString("password", &password) != B_OK && found)
		password = networkMessage.FindString("password");
 
	// Get network
	BNetworkDevice device(deviceName);
	wireless_network network;
 
	bool askForConfig = false;
	if ((address->Family() != AF_LINK
			|| device.GetNetwork(*address, network) != B_OK)
		&& device.GetNetwork(name, network) != B_OK) {
		// We did not find a network - just ignore that, and continue
		// with some defaults
		strlcpy(network.name, name != NULL ? name : "", sizeof(network.name));
		network.address = *address;
		network.authentication_mode = B_NETWORK_AUTHENTICATION_NONE;
		network.cipher = 0;
		network.group_cipher = 0;
		network.key_mode = 0;
		askForConfig = true;
	}
 
	BString string;
	if ((message.FindString("authentication", &string) == B_OK
			&& !string.IsEmpty())
		|| (found && networkMessage.FindString("authentication", &string)
				== B_OK && !string.IsEmpty())) {
		askForConfig = false;
		if (string.ICompare("wpa2") == 0) {
			network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA2;
			network.key_mode = B_KEY_MODE_IEEE802_1X;
			network.cipher = network.group_cipher = B_NETWORK_CIPHER_CCMP;
		} else if (string.ICompare("wpa") == 0) {
			network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA;
			network.key_mode = B_KEY_MODE_IEEE802_1X;
			network.cipher = network.group_cipher = B_NETWORK_CIPHER_TKIP;
		} else if (string.ICompare("wep") == 0) {
			network.authentication_mode = B_NETWORK_AUTHENTICATION_WEP;
			network.key_mode = B_KEY_MODE_NONE;
			network.cipher = network.group_cipher = B_NETWORK_CIPHER_WEP_40;
		} else if (string.ICompare("none") != 0 && string.ICompare("open") != 0) {
			fprintf(stderr, "%s: invalid authentication mode.\n", name);
			askForConfig = true;
		}
	}
 
	// We always try to join via the wpa_supplicant. Even if we could join
	// ourselves, we need to make sure that the wpa_supplicant knows about
	// our intention, as otherwise it would interfere with it.
 
	BMessenger wpaSupplicant(kWPASupplicantSignature);
	if (!wpaSupplicant.IsValid()) {
		// The wpa_supplicant isn't running yet, we may join ourselves.
		if (!askForConfig
			&& network.authentication_mode == B_NETWORK_AUTHENTICATION_NONE) {
			// We can join this network ourselves.
			status_t status = set_80211(deviceName, IEEE80211_IOC_SSID,
				network.name, strlen(network.name));
			if (status != B_OK) {
				fprintf(stderr, "%s: joining SSID failed: %s\n", name,
					strerror(status));
				return status;
			}
		}
 
		// We need the supplicant, try to launch it.
		status_t status = be_roster->Launch(kWPASupplicantSignature);
		if (status != B_OK && status != B_ALREADY_RUNNING)
			return status;
 
		wpaSupplicant.SetTo(kWPASupplicantSignature);
		if (!wpaSupplicant.IsValid())
			return B_ERROR;
	}
 
	// TODO: listen to notifications from the supplicant!
 
	BMessage join(kMsgWPAJoinNetwork);
	status_t status = join.AddString("device", deviceName);
	if (status == B_OK)
		status = join.AddString("name", network.name);
	if (status == B_OK)
		status = join.AddFlat("address", &network.address);
	if (status == B_OK && !askForConfig)
		status = join.AddUInt32("authentication", network.authentication_mode);
	if (status == B_OK && password != NULL)
		status = join.AddString("password", password);
	if (status != B_OK)
		return status;
 
	status = wpaSupplicant.SendMessage(&join);
	if (status != B_OK)
		return status;
 
	return B_OK;
}
 
 
status_t
NetServer::_LeaveNetwork(const BMessage& message)
{
	const char* deviceName;
	if (message.FindString("device", &deviceName) != B_OK)
		return B_BAD_VALUE;
 
	int32 reason;
	if (message.FindInt32("reason", &reason) != B_OK)
		reason = IEEE80211_REASON_AUTH_LEAVE;
 
	// We always try to send the leave request to the wpa_supplicant.
 
	BMessenger wpaSupplicant(kWPASupplicantSignature);
	if (wpaSupplicant.IsValid()) {
		BMessage leave(kMsgWPALeaveNetwork);
		status_t status = leave.AddString("device", deviceName);
		if (status == B_OK)
			status = leave.AddInt32("reason", reason);
		if (status != B_OK)
			return status;
 
		status = wpaSupplicant.SendMessage(&leave);
		if (status == B_OK)
			return B_OK;
	}
 
	// The wpa_supplicant doesn't seem to be running, check if this was an open
	// network we connected ourselves.
	BNetworkDevice device(deviceName);
	wireless_network network;
 
	uint32 cookie = 0;
	if (device.GetNextAssociatedNetwork(cookie, network) != B_OK
		|| network.authentication_mode != B_NETWORK_AUTHENTICATION_NONE) {
		// We didn't join ourselves, we can't do much.
		return B_ERROR;
	}
 
	// We joined ourselves, so we can just disassociate again.
	ieee80211req_mlme mlmeRequest;
	memset(&mlmeRequest, 0, sizeof(mlmeRequest));
	mlmeRequest.im_op = IEEE80211_MLME_DISASSOC;
	mlmeRequest.im_reason = reason;
 
	return set_80211(deviceName, IEEE80211_IOC_MLME, &mlmeRequest,
		sizeof(mlmeRequest));
}
 
 
//	#pragma mark -
 
 
int
main(int argc, char** argv)
{
	srand(system_time());
 
	status_t status;
	NetServer server(status);
	if (status != B_OK) {
		fprintf(stderr, "net_server: Failed to create application: %s\n",
			strerror(status));
		return 1;
	}
 
	server.Run();
	return 0;
}
 

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.

V614 Uninitialized variable 'status' used.