root/src/kits/network/libnetapi/NetworkSettings.cpp
/*
 * Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Axel Dörfler, axeld@pinc-software.de
 */


#include <NetworkSettings.h>

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

#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <fs_interface.h>
#include <NetworkDevice.h>
#include <NetworkInterface.h>
#include <Path.h>
#include <PathMonitor.h>
#include <String.h>

#include <DriverSettingsMessageAdapter.h>
#include <NetServer.h>


using namespace BNetworkKit;


static const char* kInterfaceSettingsName = "interfaces";
static const char* kServicesSettingsName = "services";
static const char* kNetworksSettingsName = "wireless_networks";


// Interface templates

namespace BPrivate {


class InterfaceAddressFamilyConverter : public DriverSettingsConverter {
public:
        virtual status_t                        ConvertFromDriverSettings(
                                                                        const driver_parameter& parameter,
                                                                        const char* name, int32 index, uint32 type,
                                                                        BMessage& target);
        virtual status_t                        ConvertToDriverSettings(const BMessage& source,
                                                                        const char* name, int32 index,
                                                                        uint32 type, BString& value);
};


}       // namespace BPrivate

using BPrivate::InterfaceAddressFamilyConverter;


const static settings_template kInterfaceAddressTemplate[] = {
        {B_STRING_TYPE, "family", NULL, true, new InterfaceAddressFamilyConverter},
        {B_STRING_TYPE, "address", NULL},
        {B_STRING_TYPE, "mask", NULL},
        {B_STRING_TYPE, "peer", NULL},
        {B_STRING_TYPE, "broadcast", NULL},
        {B_STRING_TYPE, "gateway", NULL},
        {B_BOOL_TYPE, "auto_config", NULL},
        {0, NULL, NULL}
};

const static settings_template kInterfaceNetworkTemplate[] = {
        {B_STRING_TYPE, "name", NULL, true},
        {B_STRING_TYPE, "mac", NULL},
};

const static settings_template kInterfaceTemplate[] = {
        {B_STRING_TYPE, "device", NULL, true},
        {B_BOOL_TYPE, "disabled", NULL},
        {B_MESSAGE_TYPE, "address", kInterfaceAddressTemplate},
        {B_MESSAGE_TYPE, "network", kInterfaceNetworkTemplate},
        {B_INT32_TYPE, "flags", NULL},
        {B_INT32_TYPE, "metric", NULL},
        {B_INT32_TYPE, "mtu", NULL},
        {0, NULL, NULL}
};

const static settings_template kInterfacesTemplate[] = {
        {B_MESSAGE_TYPE, "interface", kInterfaceTemplate},
        {0, NULL, NULL}
};

// Network templates

const static settings_template kNetworkTemplate[] = {
        {B_STRING_TYPE, "name", NULL, true},
        {B_STRING_TYPE, "mac", NULL},
        {B_STRING_TYPE, "password", NULL},
        {B_STRING_TYPE, "authentication", NULL},
        {B_STRING_TYPE, "cipher", NULL},
        {B_STRING_TYPE, "group_cipher", NULL},
        {B_STRING_TYPE, "key", NULL},
        {0, NULL, NULL}
};

const static settings_template kNetworksTemplate[] = {
        {B_MESSAGE_TYPE, "network", kNetworkTemplate},
        {0, NULL, NULL}
};

// Service templates

const static settings_template kServiceAddressTemplate[] = {
        {B_STRING_TYPE, "family", NULL, true},
        {B_STRING_TYPE, "type", NULL},
        {B_STRING_TYPE, "protocol", NULL},
        {B_STRING_TYPE, "address", NULL},
        {B_INT32_TYPE, "port", NULL},
        {0, NULL, NULL}
};

const static settings_template kServiceTemplate[] = {
        {B_STRING_TYPE, "name", NULL, true},
        {B_BOOL_TYPE, "disabled", NULL},
        {B_MESSAGE_TYPE, "address", kServiceAddressTemplate},
        {B_STRING_TYPE, "user", NULL},
        {B_STRING_TYPE, "group", NULL},
        {B_STRING_TYPE, "launch", NULL},
        {B_STRING_TYPE, "family", NULL},
        {B_STRING_TYPE, "type", NULL},
        {B_STRING_TYPE, "protocol", NULL},
        {B_INT32_TYPE, "port", NULL},
        {B_BOOL_TYPE, "stand_alone", NULL},
        {0, NULL, NULL}
};

const static settings_template kServicesTemplate[] = {
        {B_MESSAGE_TYPE, "service", kServiceTemplate},
        {0, NULL, NULL}
};


struct address_family {
        int                     family;
        const char*     name;
        const char*     identifiers[4];
};


static const address_family kFamilies[] = {
        {
                AF_INET,
                "inet",
                {"AF_INET", "inet", "ipv4", NULL},
        },
        {
                AF_INET6,
                "inet6",
                {"AF_INET6", "inet6", "ipv6", NULL},
        },
        { -1, NULL, {NULL} }
};


static const char*
get_family_name(int family)
{
        for (int32 i = 0; kFamilies[i].family >= 0; i++) {
                if (kFamilies[i].family == family)
                        return kFamilies[i].name;
        }
        return NULL;
}


static int
get_address_family(const char* argument)
{
        for (int32 i = 0; kFamilies[i].family >= 0; i++) {
                for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
                        if (!strcmp(argument, kFamilies[i].identifiers[j])) {
                                // found a match
                                return kFamilies[i].family;
                        }
                }
        }

        return AF_UNSPEC;
}


/*!     Parses the \a argument as network \a address for the specified \a family.
        If \a family is \c AF_UNSPEC, \a family will be overwritten with the family
        of the successfully parsed address.
*/
static bool
parse_address(int32& family, const char* argument, BNetworkAddress& address)
{
        if (argument == NULL) {
                if (family != AF_UNSPEC)
                        address.SetToWildcard(family);
                return false;
        }

        status_t status = address.SetTo(family, argument, (uint16)0,
                B_NO_ADDRESS_RESOLUTION);
        if (status != B_OK)
                return false;

        if (family == AF_UNSPEC) {
                // Test if we support the resulting address family
                bool supported = false;

                for (int32 i = 0; kFamilies[i].family >= 0; i++) {
                        if (kFamilies[i].family == address.Family()) {
                                supported = true;
                                break;
                        }
                }
                if (!supported)
                        return false;

                // Take over family from address
                family = address.Family();
        }

        return true;
}


static int
parse_type(const char* string)
{
        if (!strcasecmp(string, "stream"))
                return SOCK_STREAM;

        return SOCK_DGRAM;
}


static int
parse_protocol(const char* string)
{
        struct protoent* proto = getprotobyname(string);
        if (proto == NULL)
                return IPPROTO_TCP;

        return proto->p_proto;
}


static int
type_for_protocol(int protocol)
{
        // default determined by protocol
        switch (protocol) {
                case IPPROTO_TCP:
                        return SOCK_STREAM;

                case IPPROTO_UDP:
                default:
                        return SOCK_DGRAM;
        }
}


// #pragma mark -


status_t
InterfaceAddressFamilyConverter::ConvertFromDriverSettings(
        const driver_parameter& parameter, const char* name, int32 index,
        uint32 type, BMessage& target)
{
        return B_NOT_SUPPORTED;
}


status_t
InterfaceAddressFamilyConverter::ConvertToDriverSettings(const BMessage& source,
        const char* name, int32 index, uint32 type, BString& value)
{
        int32 family;
        if (source.FindInt32("family", &family) == B_OK) {
                const char* familyName = get_family_name(family);
                if (familyName != NULL)
                        value << familyName;
                else
                        value << family;

                return B_OK;
        }

        return B_NOT_SUPPORTED;
}


// #pragma mark -


BNetworkSettings::BNetworkSettings()
{
        _Load();
}


BNetworkSettings::~BNetworkSettings()
{
}


status_t
BNetworkSettings::GetNextInterface(uint32& cookie, BMessage& interface)
{
        status_t status = fInterfaces.FindMessage("interface", cookie, &interface);
        if (status != B_OK)
                return status;

        cookie++;
        return B_OK;
}


status_t
BNetworkSettings::GetInterface(const char* name, BMessage& interface) const
{
        int32 index;
        return _GetItem(fInterfaces, "interface", "device", name, index, interface);
}


status_t
BNetworkSettings::AddInterface(const BMessage& interface)
{
        const char* name = NULL;
        if (interface.FindString("device", &name) != B_OK)
                return B_BAD_VALUE;

        _RemoveItem(fInterfaces, "interface", "device", name);

        status_t result = fInterfaces.AddMessage("interface", &interface);
        if (result != B_OK)
                return result;

        return _Save(kInterfaceSettingsName);
}


status_t
BNetworkSettings::RemoveInterface(const char* name)
{
        return _RemoveItem(fInterfaces, "interface", "device", name,
                kInterfaceSettingsName);
}


BNetworkInterfaceSettings
BNetworkSettings::Interface(const char* name)
{
        BMessage interface;
        GetInterface(name, interface);
        return BNetworkInterfaceSettings(interface);
}


const BNetworkInterfaceSettings
BNetworkSettings::Interface(const char* name) const
{
        BMessage interface;
        GetInterface(name, interface);
        return BNetworkInterfaceSettings(interface);
}


int32
BNetworkSettings::CountNetworks() const
{
        int32 count = 0;
        if (fNetworks.GetInfo("network", NULL, &count) != B_OK)
                return 0;

        return count;
}


status_t
BNetworkSettings::GetNextNetwork(uint32& cookie, BMessage& network) const
{
        status_t status = fNetworks.FindMessage("network", cookie, &network);
        if (status != B_OK)
                return status;

        cookie++;
        return B_OK;
}


status_t
BNetworkSettings::GetNetwork(const char* name, BMessage& network) const
{
        int32 index;
        return _GetItem(fNetworks, "network", "name", name, index, network);
}


status_t
BNetworkSettings::AddNetwork(const BMessage& network)
{
        const char* name = NULL;
        if (network.FindString("name", &name) != B_OK)
                return B_BAD_VALUE;

        _RemoveItem(fNetworks, "network", "name", name);

        status_t result = fNetworks.AddMessage("network", &network);
        if (result != B_OK)
                return result;

        return _Save(kNetworksSettingsName);
}


status_t
BNetworkSettings::RemoveNetwork(const char* name)
{
        return _RemoveItem(fNetworks, "network", "name", name,
                kNetworksSettingsName);
}


const BMessage&
BNetworkSettings::Services() const
{
        return fServices;
}


status_t
BNetworkSettings::GetNextService(uint32& cookie, BMessage& service)
{
        status_t status = fServices.FindMessage("service", cookie, &service);
        if (status != B_OK)
                return status;

        cookie++;
        return B_OK;
}


status_t
BNetworkSettings::GetService(const char* name, BMessage& service) const
{
        int32 index;
        return _GetItem(fServices, "service", "name", name, index, service);
}


status_t
BNetworkSettings::AddService(const BMessage& service)
{
        const char* name = service.GetString("name");
        if (name == NULL)
                return B_BAD_VALUE;

        _RemoveItem(fServices, "service", "name", name);

        status_t result = fServices.AddMessage("service", &service);
        if (result != B_OK)
                return result;

        return _Save(kServicesSettingsName);
}


status_t
BNetworkSettings::RemoveService(const char* name)
{
        return _RemoveItem(fServices, "service", "name", name,
                kServicesSettingsName);
}


BNetworkServiceSettings
BNetworkSettings::Service(const char* name)
{
        BMessage service;
        GetService(name, service);
        return BNetworkServiceSettings(service);
}


const BNetworkServiceSettings
BNetworkSettings::Service(const char* name) const
{
        BMessage service;
        GetService(name, service);
        return BNetworkServiceSettings(service);
}


status_t
BNetworkSettings::StartMonitoring(const BMessenger& target)
{
        if (_IsWatching(target))
                return B_OK;
        if (_IsWatching())
                StopMonitoring(fListener);

        fListener = target;

        status_t status = _StartWatching(kInterfaceSettingsName, target);
        if (status == B_OK)
                status = _StartWatching(kNetworksSettingsName, target);
        if (status == B_OK)
                status = _StartWatching(kServicesSettingsName, target);

        return status;
}


status_t
BNetworkSettings::StopMonitoring(const BMessenger& target)
{
        // TODO: this needs to be changed in case the server will watch
        //      anything else but settings
        return BPrivate::BPathMonitor::StopWatching(target);
}


status_t
BNetworkSettings::Update(BMessage* message)
{
        const char* pathName;
        int32 opcode;
        if (message->FindInt32("opcode", &opcode) != B_OK
                || message->FindString("path", &pathName) != B_OK)
                return B_BAD_VALUE;

        BPath settingsFolderPath;
        _GetPath(NULL, settingsFolderPath);
        if (strncmp(pathName, settingsFolderPath.Path(),
                        strlen(settingsFolderPath.Path()))) {
                return B_NAME_NOT_FOUND;
        }

        if (message->FindBool("removed")) {
                // for now, we only consider existing settings files
                // (ie. deleting "services" won't stop any)
                return B_OK;
        }

        int32 fields;
        if (opcode == B_STAT_CHANGED
                && message->FindInt32("fields", &fields) == B_OK
                && (fields & (B_STAT_MODIFICATION_TIME | B_STAT_SIZE)) == 0) {
                // only update when the modified time or size has changed
                return B_OK;
        }

        BPath path(pathName);
        uint32 type;
        if (_Load(path.Leaf(), &type) == B_OK) {
                BMessage update(type);
                fListener.SendMessage(&update);
        }

        return B_OK;
}


// #pragma mark - private


status_t
BNetworkSettings::_Load(const char* name, uint32* _type)
{
        BPath path;
        status_t status = _GetPath(NULL, path);
        if (status != B_OK)
                return status;

        DriverSettingsMessageAdapter adapter;
        status = B_ENTRY_NOT_FOUND;

        if (name == NULL || strcmp(name, kInterfaceSettingsName) == 0) {
                status = adapter.ConvertFromDriverSettings(
                        _Path(path, kInterfaceSettingsName).Path(), kInterfacesTemplate,
                        fInterfaces);
                if (status == B_OK && _type != NULL)
                        *_type = kMsgInterfaceSettingsUpdated;
        }
        if (name == NULL || strcmp(name, kNetworksSettingsName) == 0) {
                status = adapter.ConvertFromDriverSettings(
                        _Path(path, kNetworksSettingsName).Path(),
                        kNetworksTemplate, fNetworks);
                if (status == B_OK) {
                        // Convert settings for simpler consumption
                        BMessage network;
                        for (int32 index = 0; fNetworks.FindMessage("network", index,
                                        &network); index++) {
                                if (_ConvertNetworkFromSettings(network) == B_OK)
                                        fNetworks.ReplaceMessage("network", index, &network);
                        }

                        if (_type != NULL)
                                *_type = kMsgNetworkSettingsUpdated;
                }
        }
        if (name == NULL || strcmp(name, kServicesSettingsName) == 0) {
                status = adapter.ConvertFromDriverSettings(
                        _Path(path, kServicesSettingsName).Path(), kServicesTemplate,
                        fServices);
                if (status == B_OK && _type != NULL)
                        *_type = kMsgServiceSettingsUpdated;
        }

        return status;
}


status_t
BNetworkSettings::_Save(const char* name)
{
        BPath path;
        status_t status = _GetPath(NULL, path);
        if (status != B_OK)
                return status;

        DriverSettingsMessageAdapter adapter;
        status = B_ENTRY_NOT_FOUND;

        if (name == NULL || strcmp(name, kInterfaceSettingsName) == 0) {
                status = adapter.ConvertToDriverSettings(
                        _Path(path, kInterfaceSettingsName).Path(),
                        kInterfacesTemplate, fInterfaces);
        }
        if (name == NULL || strcmp(name, kNetworksSettingsName) == 0) {
                // Convert settings to storage format
                BMessage networks = fNetworks;
                BMessage network;
                for (int32 index = 0; networks.FindMessage("network", index,
                                &network); index++) {
                        if (_ConvertNetworkToSettings(network) == B_OK)
                                networks.ReplaceMessage("network", index, &network);
                }

                status = adapter.ConvertToDriverSettings(
                        _Path(path, kNetworksSettingsName).Path(),
                        kNetworksTemplate, networks);
        }
        if (name == NULL || strcmp(name, kServicesSettingsName) == 0) {
                status = adapter.ConvertToDriverSettings(
                        _Path(path, kServicesSettingsName).Path(),
                        kServicesTemplate, fServices);
        }

        return status;
}


BPath
BNetworkSettings::_Path(BPath& parent, const char* leaf)
{
        return BPath(parent.Path(), leaf);
}


status_t
BNetworkSettings::_GetPath(const char* name, BPath& path)
{
        if (find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path, true) != B_OK)
                return B_ERROR;

        path.Append("network");
        create_directory(path.Path(), 0755);

        if (name != NULL)
                path.Append(name);
        return B_OK;
}


status_t
BNetworkSettings::_StartWatching(const char* name, const BMessenger& target)
{
        BPath path;
        status_t status = _GetPath(name, path);
        if (status != B_OK)
                return status;

        return BPrivate::BPathMonitor::StartWatching(path.Path(), B_WATCH_STAT,
                target);
}


status_t
BNetworkSettings::_ConvertNetworkToSettings(BMessage& message)
{
        BNetworkAddress address;
        status_t result = message.FindFlat("address", &address);
        if (result == B_OK)
                message.RemoveName("address");

        if (result == B_OK && address.Family() == AF_LINK) {
                size_t addressLength = address.LinkLevelAddressLength();
                uint8* macAddress = address.LinkLevelAddress();
                bool usable = false;
                BString formatted;

                for (size_t index = 0; index < addressLength; index++) {
                        if (index > 0)
                                formatted.Append(":");
                        char buffer[3];
                        snprintf(buffer, sizeof(buffer), "%2x", macAddress[index]);
                        formatted.Append(buffer, sizeof(buffer));

                        if (macAddress[index] != 0)
                                usable = true;
                }

                if (usable)
                        message.AddString("mac", formatted);
        }

        uint32 authentication = 0;
        result = message.FindUInt32("authentication_mode", &authentication);
        if (result == B_OK) {
                message.RemoveName("authentication_mode");

                const char* authenticationString = NULL;
                switch (authentication) {
                        case B_NETWORK_AUTHENTICATION_NONE:
                                authenticationString = "none";
                                break;
                        case B_NETWORK_AUTHENTICATION_WEP:
                                authenticationString = "wep";
                                break;
                        case B_NETWORK_AUTHENTICATION_WPA:
                                authenticationString = "wpa";
                                break;
                        case B_NETWORK_AUTHENTICATION_WPA2:
                                authenticationString = "wpa2";
                                break;
                }

                if (result == B_OK && authenticationString != NULL)
                        message.AddString("authentication", authenticationString);
        }

        uint32 cipher = 0;
        result = message.FindUInt32("cipher", &cipher);
        if (result == B_OK) {
                message.RemoveName("cipher");

                if ((cipher & B_NETWORK_CIPHER_NONE) != 0)
                        message.AddString("cipher", "none");
                if ((cipher & B_NETWORK_CIPHER_TKIP) != 0)
                        message.AddString("cipher", "tkip");
                if ((cipher & B_NETWORK_CIPHER_CCMP) != 0)
                        message.AddString("cipher", "ccmp");
        }

        uint32 groupCipher = 0;
        result = message.FindUInt32("group_cipher", &groupCipher);
        if (result == B_OK) {
                message.RemoveName("group_cipher");

                if ((groupCipher & B_NETWORK_CIPHER_NONE) != 0)
                        message.AddString("group_cipher", "none");
                if ((groupCipher & B_NETWORK_CIPHER_WEP_40) != 0)
                        message.AddString("group_cipher", "wep40");
                if ((groupCipher & B_NETWORK_CIPHER_WEP_104) != 0)
                        message.AddString("group_cipher", "wep104");
                if ((groupCipher & B_NETWORK_CIPHER_TKIP) != 0)
                        message.AddString("group_cipher", "tkip");
                if ((groupCipher & B_NETWORK_CIPHER_CCMP) != 0)
                        message.AddString("group_cipher", "ccmp");
        }

        // TODO: the other fields aren't currently used, add them when they are
        // and when it's clear how they will be stored
        message.RemoveName("noise_level");
        message.RemoveName("signal_strength");
        message.RemoveName("flags");
        message.RemoveName("key_mode");

        return B_OK;
}


status_t
BNetworkSettings::_ConvertNetworkFromSettings(BMessage& message)
{
        message.RemoveName("mac");
                // TODO: convert into a flat BNetworkAddress "address"

        const char* authentication = NULL;
        if (message.FindString("authentication", &authentication) == B_OK) {
                message.RemoveName("authentication");

                if (strcasecmp(authentication, "none") == 0) {
                        message.AddUInt32("authentication_mode",
                                B_NETWORK_AUTHENTICATION_NONE);
                } else if (strcasecmp(authentication, "wep") == 0) {
                        message.AddUInt32("authentication_mode",
                                B_NETWORK_AUTHENTICATION_WEP);
                } else if (strcasecmp(authentication, "wpa") == 0) {
                        message.AddUInt32("authentication_mode",
                                B_NETWORK_AUTHENTICATION_WPA);
                } else if (strcasecmp(authentication, "wpa2") == 0) {
                        message.AddUInt32("authentication_mode",
                                B_NETWORK_AUTHENTICATION_WPA2);
                }
        }

        int32 index = 0;
        uint32 cipher = 0;
        const char* cipherString = NULL;
        while (message.FindString("cipher", index++, &cipherString) == B_OK) {
                if (strcasecmp(cipherString, "none") == 0)
                        cipher |= B_NETWORK_CIPHER_NONE;
                else if (strcasecmp(cipherString, "tkip") == 0)
                        cipher |= B_NETWORK_CIPHER_TKIP;
                else if (strcasecmp(cipherString, "ccmp") == 0)
                        cipher |= B_NETWORK_CIPHER_CCMP;
        }

        message.RemoveName("cipher");
        if (cipher != 0)
                message.AddUInt32("cipher", cipher);

        index = 0;
        cipher = 0;
        while (message.FindString("group_cipher", index++, &cipherString) == B_OK) {
                if (strcasecmp(cipherString, "none") == 0)
                        cipher |= B_NETWORK_CIPHER_NONE;
                else if (strcasecmp(cipherString, "wep40") == 0)
                        cipher |= B_NETWORK_CIPHER_WEP_40;
                else if (strcasecmp(cipherString, "wep104") == 0)
                        cipher |= B_NETWORK_CIPHER_WEP_104;
                else if (strcasecmp(cipherString, "tkip") == 0)
                        cipher |= B_NETWORK_CIPHER_TKIP;
                else if (strcasecmp(cipherString, "ccmp") == 0)
                        cipher |= B_NETWORK_CIPHER_CCMP;
        }

        message.RemoveName("group_cipher");
        if (cipher != 0)
                message.AddUInt32("group_cipher", cipher);

        message.AddUInt32("flags", B_NETWORK_IS_PERSISTENT);

        // TODO: add the other fields
        message.RemoveName("key");
        return B_OK;
}


status_t
BNetworkSettings::_GetItem(const BMessage& container, const char* itemField,
        const char* nameField, const char* name, int32& _index,
        BMessage& item) const
{
        int32 index = 0;
        while (container.FindMessage(itemField, index, &item) == B_OK) {
                const char* itemName = NULL;
                if (item.FindString(nameField, &itemName) == B_OK
                        && strcmp(itemName, name) == 0) {
                        _index = index;
                        return B_OK;
                }

                index++;
        }

        return B_ENTRY_NOT_FOUND;
}


status_t
BNetworkSettings::_RemoveItem(BMessage& container, const char* itemField,
        const char* nameField, const char* name, const char* store)
{
        BMessage item;
        int32 index;
        if (_GetItem(container, itemField, nameField, name, index, item) == B_OK) {
                container.RemoveData(itemField, index);
                if (store != NULL)
                        return _Save(store);
                return B_OK;
        }

        return B_ENTRY_NOT_FOUND;
}


// #pragma mark - BNetworkInterfaceAddressSettings


BNetworkInterfaceAddressSettings::BNetworkInterfaceAddressSettings()
        :
        fFamily(AF_UNSPEC),
        fAutoConfigure(true)
{
}


BNetworkInterfaceAddressSettings::BNetworkInterfaceAddressSettings(
        const BMessage& data)
{
        if (data.FindInt32("family", &fFamily) != B_OK) {
                const char* familyString;
                if (data.FindString("family", &familyString) == B_OK) {
                        fFamily = get_address_family(familyString);
                        if (fFamily == AF_UNSPEC) {
                                // we don't support this family
                                fprintf(stderr, "Ignore unknown family: %s\n",
                                        familyString);
                                return;
                        }
                } else
                        fFamily = AF_UNSPEC;
        }

        fAutoConfigure = data.GetBool("auto_config", false);

        if (!fAutoConfigure) {
                if (parse_address(fFamily, data.GetString("address", NULL), fAddress))
                        parse_address(fFamily, data.GetString("mask", NULL), fMask);

                parse_address(fFamily, data.GetString("peer", NULL), fPeer);
                parse_address(fFamily, data.GetString("broadcast", NULL), fBroadcast);
                parse_address(fFamily, data.GetString("gateway", NULL), fGateway);
        }
}


BNetworkInterfaceAddressSettings::BNetworkInterfaceAddressSettings(
        const BNetworkInterfaceAddressSettings& other)
        :
        fFamily(other.fFamily),
        fAutoConfigure(other.fAutoConfigure),
        fAddress(other.fAddress),
        fMask(other.fMask),
        fPeer(other.fPeer),
        fBroadcast(other.fBroadcast),
        fGateway(other.fGateway)
{
}


BNetworkInterfaceAddressSettings::~BNetworkInterfaceAddressSettings()
{
}


int
BNetworkInterfaceAddressSettings::Family() const
{
        return fFamily;
}


void
BNetworkInterfaceAddressSettings::SetFamily(int family)
{
        fFamily = family;
}


bool
BNetworkInterfaceAddressSettings::IsAutoConfigure() const
{
        return fAutoConfigure;
}


void
BNetworkInterfaceAddressSettings::SetAutoConfigure(bool configure)
{
        fAutoConfigure = configure;
}


const BNetworkAddress&
BNetworkInterfaceAddressSettings::Address() const
{
        return fAddress;
}


BNetworkAddress&
BNetworkInterfaceAddressSettings::Address()
{
        return fAddress;
}


const BNetworkAddress&
BNetworkInterfaceAddressSettings::Mask() const
{
        return fMask;
}


BNetworkAddress&
BNetworkInterfaceAddressSettings::Mask()
{
        return fMask;
}


const BNetworkAddress&
BNetworkInterfaceAddressSettings::Peer() const
{
        return fPeer;
}


BNetworkAddress&
BNetworkInterfaceAddressSettings::Peer()
{
        return fPeer;
}


const BNetworkAddress&
BNetworkInterfaceAddressSettings::Broadcast() const
{
        return fBroadcast;
}


BNetworkAddress&
BNetworkInterfaceAddressSettings::Broadcast()
{
        return fBroadcast;
}


const BNetworkAddress&
BNetworkInterfaceAddressSettings::Gateway() const
{
        return fGateway;
}


BNetworkAddress&
BNetworkInterfaceAddressSettings::Gateway()
{
        return fGateway;
}


status_t
BNetworkInterfaceAddressSettings::GetMessage(BMessage& data) const
{
        status_t status = B_OK;
        if (fFamily != AF_UNSPEC)
                status = data.SetInt32("family", fFamily);
        if (status == B_OK && fAutoConfigure)
                return data.SetBool("auto_config", fAutoConfigure);

        if (status == B_OK && !fAddress.IsEmpty()) {
                status = data.SetString("address", fAddress.ToString());
                if (status == B_OK && !fMask.IsEmpty())
                        status = data.SetString("mask", fMask.ToString());
        }
        if (status == B_OK && !fPeer.IsEmpty())
                status = data.SetString("peer", fPeer.ToString());
        if (status == B_OK && !fBroadcast.IsEmpty())
                status = data.SetString("broadcast", fBroadcast.ToString());
        if (status == B_OK && !fGateway.IsEmpty())
                status = data.SetString("gateway", fGateway.ToString());

        return status;
}


BNetworkInterfaceAddressSettings&
BNetworkInterfaceAddressSettings::operator=(
        const BNetworkInterfaceAddressSettings& other)
{
        fFamily = other.fFamily;
        fAutoConfigure = other.fAutoConfigure;
        fAddress = other.fAddress;
        fMask = other.fMask;
        fPeer = other.fPeer;
        fBroadcast = other.fBroadcast;
        fGateway = other.fGateway;

        return *this;
}


// #pragma mark - BNetworkInterfaceSettings


BNetworkInterfaceSettings::BNetworkInterfaceSettings()
        :
        fFlags(0),
        fMTU(0),
        fMetric(0)
{
}


BNetworkInterfaceSettings::BNetworkInterfaceSettings(const BMessage& message)
{
        fName = message.GetString("device");
        fFlags = message.GetInt32("flags", 0);
        fMTU = message.GetInt32("mtu", 0);
        fMetric = message.GetInt32("metric", 0);

        BMessage addressData;
        for (int32 index = 0; message.FindMessage("address", index,
                        &addressData) == B_OK; index++) {
                BNetworkInterfaceAddressSettings address(addressData);
                fAddresses.push_back(address);
        }
}


BNetworkInterfaceSettings::~BNetworkInterfaceSettings()
{
}


const char*
BNetworkInterfaceSettings::Name() const
{
        return fName;
}


void
BNetworkInterfaceSettings::SetName(const char* name)
{
        fName = name;
}


int32
BNetworkInterfaceSettings::Flags() const
{
        return fFlags;
}


void
BNetworkInterfaceSettings::SetFlags(int32 flags)
{
        fFlags = flags;
}


int32
BNetworkInterfaceSettings::MTU() const
{
        return fMTU;
}


void
BNetworkInterfaceSettings::SetMTU(int32 mtu)
{
        fMTU = mtu;
}


int32
BNetworkInterfaceSettings::Metric() const
{
        return fMetric;
}


void
BNetworkInterfaceSettings::SetMetric(int32 metric)
{
        fMetric = metric;
}


int32
BNetworkInterfaceSettings::CountAddresses() const
{
        return fAddresses.size();
}


const BNetworkInterfaceAddressSettings&
BNetworkInterfaceSettings::AddressAt(int32 index) const
{
        return fAddresses[index];
}


BNetworkInterfaceAddressSettings&
BNetworkInterfaceSettings::AddressAt(int32 index)
{
        return fAddresses[index];
}


int32
BNetworkInterfaceSettings::FindFirstAddress(int family) const
{
        for (int32 index = 0; index < CountAddresses(); index++) {
                const BNetworkInterfaceAddressSettings address = AddressAt(index);
                if (address.Family() == family)
                        return index;
        }
        return -1;
}


void
BNetworkInterfaceSettings::AddAddress(
        const BNetworkInterfaceAddressSettings& address)
{
        fAddresses.push_back(address);
}


void
BNetworkInterfaceSettings::RemoveAddress(int32 index)
{
        fAddresses.erase(fAddresses.begin() + index);
}


/*!     This is a convenience method that returns the current state of the
        interface, not just the one configured.

        This means, even if the settings say: auto configured, this method
        may still return false, if the configuration has been manually tempered
        with.
*/
bool
BNetworkInterfaceSettings::IsAutoConfigure(int family) const
{
        BNetworkInterface interface(fName);
        // TODO: this needs to happen at protocol level
        if ((interface.Flags() & (IFF_AUTO_CONFIGURED | IFF_CONFIGURING)) != 0)
                return true;

        BNetworkInterfaceAddress address;
        status_t status = B_ERROR;

        int32 index = interface.FindFirstAddress(family);
        if (index >= 0)
                status = interface.GetAddressAt(index, address);
        if (index < 0 || status != B_OK || address.Address().IsEmpty()) {
                if (status == B_OK) {
                        // Check persistent settings for the mode -- the address
                        // can also be empty if the automatic configuration hasn't
                        // started yet (for example, because there is no link).
                        int32 index = FindFirstAddress(family);
                        if (index < 0)
                                index = FindFirstAddress(AF_UNSPEC);
                        if (index >= 0) {
                                const BNetworkInterfaceAddressSettings& address
                                        = AddressAt(index);
                                return address.IsAutoConfigure();
                        }
                }
        }

        return false;
}


status_t
BNetworkInterfaceSettings::GetMessage(BMessage& data) const
{
        status_t status = data.SetString("device", fName);
        if (status == B_OK && fFlags != 0)
                status = data.SetInt32("flags", fFlags);
        if (status == B_OK && fMTU != 0)
                status = data.SetInt32("mtu", fMTU);
        if (status == B_OK && fMetric != 0)
                status = data.SetInt32("metric", fMetric);

        for (int32 i = 0; i < CountAddresses(); i++) {
                BMessage address;
                status = AddressAt(i).GetMessage(address);
                if (status == B_OK)
                        status = data.AddMessage("address", &address);
                if (status != B_OK)
                        break;
        }
        return status;
}


// #pragma mark - BNetworkServiceAddressSettings


BNetworkServiceAddressSettings::BNetworkServiceAddressSettings()
{
}


BNetworkServiceAddressSettings::BNetworkServiceAddressSettings(
        const BMessage& data, int serviceFamily, int serviceType,
        int serviceProtocol, int servicePort)
{
        // TODO: dump problems in the settings to syslog
        if (data.FindInt32("family", &fFamily) != B_OK) {
                const char* familyString;
                if (data.FindString("family", &familyString) == B_OK) {
                        fFamily = get_address_family(familyString);
                        if (fFamily == AF_UNSPEC) {
                                // we don't support this family
                                fprintf(stderr, "Ignore unknown family: %s\n",
                                        familyString);
                                return;
                        }
                } else
                        fFamily = serviceFamily;
        }

        if (!parse_address(fFamily, data.GetString("address"), fAddress))
                fAddress.SetToWildcard(fFamily);

        const char* string;
        if (data.FindString("protocol", &string) == B_OK)
                fProtocol = parse_protocol(string);
        else
                fProtocol = serviceProtocol;

        if (data.FindString("type", &string) == B_OK)
                fType = parse_type(string);
        else if (fProtocol != serviceProtocol)
                fType = type_for_protocol(fProtocol);
        else
                fType = serviceType;

        fAddress.SetPort(data.GetInt32("port", servicePort));
}


BNetworkServiceAddressSettings::~BNetworkServiceAddressSettings()
{
}


int
BNetworkServiceAddressSettings::Family() const
{
        return fFamily;
}


void
BNetworkServiceAddressSettings::SetFamily(int family)
{
        fFamily = family;
}


int
BNetworkServiceAddressSettings::Protocol() const
{
        return fProtocol;
}


void
BNetworkServiceAddressSettings::SetProtocol(int protocol)
{
        fProtocol = protocol;
}


int
BNetworkServiceAddressSettings::Type() const
{
        return fType;
}


void
BNetworkServiceAddressSettings::SetType(int type)
{
        fType = type;
}


const BNetworkAddress&
BNetworkServiceAddressSettings::Address() const
{
        return fAddress;
}


BNetworkAddress&
BNetworkServiceAddressSettings::Address()
{
        return fAddress;
}


status_t
BNetworkServiceAddressSettings::GetMessage(BMessage& data) const
{
        // TODO!
        return B_NOT_SUPPORTED;
}


bool
BNetworkServiceAddressSettings::operator==(
        const BNetworkServiceAddressSettings& other) const
{
        return Family() == other.Family()
                && Type() == other.Type()
                && Protocol() == other.Protocol()
                && Address() == other.Address();
}


// #pragma mark - BNetworkServiceSettings


BNetworkServiceSettings::BNetworkServiceSettings()
        :
        fFamily(AF_UNSPEC),
        fType(-1),
        fProtocol(-1),
        fPort(-1),
        fEnabled(true),
        fStandAlone(false)
{
}


BNetworkServiceSettings::BNetworkServiceSettings(const BMessage& message)
        :
        fType(-1),
        fProtocol(-1),
        fPort(-1),
        fEnabled(true),
        fStandAlone(false)
{
        // TODO: user/group is currently ignored!

        fName = message.GetString("name");

        // Default family/port/protocol/type for all addresses

        // we default to inet/tcp/port-from-service-name if nothing is specified
        const char* string;
        if (message.FindString("family", &string) != B_OK)
                string = "inet";

        fFamily = get_address_family(string);
        if (fFamily == AF_UNSPEC)
                fFamily = AF_INET;

        if (message.FindString("protocol", &string) == B_OK)
                fProtocol = parse_protocol(string);
        else {
                string = "tcp";
                        // we set 'string' here for an eventual call to getservbyname()
                        // below
                fProtocol = IPPROTO_TCP;
        }

        if (message.FindInt32("port", &fPort) != B_OK) {
                struct servent* servent = getservbyname(Name(), string);
                if (servent != NULL)
                        fPort = ntohs(servent->s_port);
                else
                        fPort = -1;
        }

        if (message.FindString("type", &string) == B_OK)
                fType = parse_type(string);
        else
                fType = type_for_protocol(fProtocol);

        fStandAlone = message.GetBool("stand_alone");

        const char* argument;
        for (int i = 0; message.FindString("launch", i, &argument) == B_OK; i++) {
                fArguments.Add(argument);
        }

        BMessage addressData;
        int32 i = 0;
        for (; message.FindMessage("address", i, &addressData) == B_OK; i++) {
                BNetworkServiceAddressSettings address(addressData, fFamily,
                        fType, fProtocol, fPort);
                fAddresses.push_back(address);
        }

        if (i == 0 && (fFamily < 0 || fPort < 0)) {
                // no address specified
                printf("service %s has no address specified\n", Name());
                return;
        }

        if (i == 0) {
                // no address specified, but family/port were given; add empty address
                BNetworkServiceAddressSettings address;
                address.SetFamily(fFamily);
                address.SetType(fType);
                address.SetProtocol(fProtocol);
                address.Address().SetToWildcard(fFamily, fPort);

                fAddresses.push_back(address);
        }
}


BNetworkServiceSettings::~BNetworkServiceSettings()
{
}


status_t
BNetworkServiceSettings::InitCheck() const
{
        if (!fName.IsEmpty() && !fArguments.IsEmpty() && CountAddresses() > 0)
                return B_OK;

        return B_BAD_VALUE;
}


const char*
BNetworkServiceSettings::Name() const
{
        return fName.String();
}


void
BNetworkServiceSettings::SetName(const char* name)
{
        fName = name;
}


bool
BNetworkServiceSettings::IsStandAlone() const
{
        return fStandAlone;
}


void
BNetworkServiceSettings::SetStandAlone(bool alone)
{
        fStandAlone = alone;
}


bool
BNetworkServiceSettings::IsEnabled() const
{
        return InitCheck() == B_OK && fEnabled;
}


void
BNetworkServiceSettings::SetEnabled(bool enable)
{
        fEnabled = enable;
}


int
BNetworkServiceSettings::Family() const
{
        return fFamily;
}


void
BNetworkServiceSettings::SetFamily(int family)
{
        fFamily = family;
}


int
BNetworkServiceSettings::Protocol() const
{
        return fProtocol;
}


void
BNetworkServiceSettings::SetProtocol(int protocol)
{
        fProtocol = protocol;
}


int
BNetworkServiceSettings::Type() const
{
        return fType;
}


void
BNetworkServiceSettings::SetType(int type)
{
        fType = type;
}


int
BNetworkServiceSettings::Port() const
{
        return fPort;
}


void
BNetworkServiceSettings::SetPort(int port)
{
        fPort = port;
}


int32
BNetworkServiceSettings::CountArguments() const
{
        return fArguments.CountStrings();
}


const char*
BNetworkServiceSettings::ArgumentAt(int32 index) const
{
        return fArguments.StringAt(index);
}


void
BNetworkServiceSettings::AddArgument(const char* argument)
{
        fArguments.Add(argument);
}


void
BNetworkServiceSettings::RemoveArgument(int32 index)
{
        fArguments.Remove(index);
}


int32
BNetworkServiceSettings::CountAddresses() const
{
        return fAddresses.size();
}


const BNetworkServiceAddressSettings&
BNetworkServiceSettings::AddressAt(int32 index) const
{
        return fAddresses[index];
}


void
BNetworkServiceSettings::AddAddress(
        const BNetworkServiceAddressSettings& address)
{
        fAddresses.push_back(address);
}


void
BNetworkServiceSettings::RemoveAddress(int32 index)
{
        fAddresses.erase(fAddresses.begin() + index);
}


/*!     This is a convenience method that returns the current state of the
        service, independent of the current settings.
*/
bool
BNetworkServiceSettings::IsRunning() const
{
        BMessage request(kMsgIsServiceRunning);
        request.AddString("name", fName);

        BMessenger networkServer(kNetServerSignature);
        BMessage reply;
        status_t status = networkServer.SendMessage(&request, &reply);
        if (status == B_OK)
                return reply.GetBool("running");

        return false;
}


status_t
BNetworkServiceSettings::GetMessage(BMessage& data) const
{
        status_t status = data.SetString("name", fName);
        if (status == B_OK && !fEnabled)
                status = data.SetBool("disabled", true);
        if (status == B_OK && fStandAlone)
                status = data.SetBool("stand_alone", true);

        if (fFamily != AF_UNSPEC)
                status = data.SetInt32("family", fFamily);
        if (fType != -1)
                status = data.SetInt32("type", fType);
        if (fProtocol != -1)
                status = data.SetInt32("protocol", fProtocol);
        if (fPort != -1)
                status = data.SetInt32("port", fPort);

        for (int32 i = 0; i < fArguments.CountStrings(); i++) {
                if (status == B_OK)
                        status = data.AddString("launch", fArguments.StringAt(i));
                if (status != B_OK)
                        break;
        }

        for (int32 i = 0; i < CountAddresses(); i++) {
                BNetworkServiceAddressSettings address = AddressAt(i);
                if (address.Family() == Family()
                        && address.Type() == Type()
                        && address.Protocol() == Protocol()
                        && address.Address().IsWildcard()
                        && address.Address().Port() == Port()) {
                        // This address will be created automatically, no need to store it
                        continue;
                }

                BMessage addressMessage;
                status = AddressAt(i).GetMessage(addressMessage);
                if (status == B_OK)
                        status = data.AddMessage("address", &addressMessage);
                if (status != B_OK)
                        break;
        }
        return status;
}