root/src/add-ons/kernel/network/ppp/shared/libppp/PPPInterface.cpp
/*
 * Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
 * Distributed under the terms of the MIT License.
 */

/*!     \class PPPInterface
        \brief This class represents a PPP interface living in kernel space.
        
        Use this class to control PPP interfaces and get the current interface settings.
        You can also use it to enable/disable report messages.
*/

#include "PPPInterface.h"

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <unistd.h>

#include <Path.h>
#include <Directory.h>
#include <Entry.h>

#include <net/if.h>

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


/*!     \brief Sets the interface to \a ID.
        
        \param ID The ID of the new interface.
*/
PPPInterface::PPPInterface(ppp_interface_id ID)
{
        // printf("direct constructor\n");
        int family = AF_INET;
        fFD = socket(family, SOCK_DGRAM, 0);
        
        SetTo(ID);
}


//!     Copy constructor.
PPPInterface::PPPInterface(const PPPInterface& copy)
{
        // printf("Copy constructor\n");
        // fFD = open(get_stack_driver_path(), O_RDWR);
        int family = AF_INET;
        fFD = socket(family, SOCK_DGRAM, 0);
        
        SetTo(copy.ID());
}


//!     Destructor.
PPPInterface::~PPPInterface()
{
        // printf("Destructor\n");
        if (fFD >= 0)
                close(fFD);
}


/*!     \brief Checks if object was created correctly.
        
        You should always call this method after you constructed a PPPInterface object.
        
        \return
                - \c B_OK: Object could be initialized successfully and the interface exists.
                - \c B_BAD_INDEX: The interface does not exist.
                - \c B_ERROR: The PPP stack could not be loaded.
*/
status_t
PPPInterface::InitCheck() const
{
        if (fFD < 0)
                return B_ERROR;

        if (fID == PPP_UNDEFINED_INTERFACE_ID)
                return B_BAD_INDEX;
        
        return B_OK;
}


/*!     \brief Changes the current interface.
        
        If this fails it will set the interface's \a ID to \c PPP_UNDEFINED_INTERFACE_ID.
        
        \param ID The ID of the new interface.
        
        \return
                - \c B_OK: Object could be initialized successfully and the interface exists.
                - \c B_BAD_INDEX: The interface does not exist.
                - any other value: The PPP stack could not be loaded.
        
        \sa Control()
*/
status_t
PPPInterface::SetTo(ppp_interface_id ID)
{
        // printf("Set To %ld begin=============================\n", ID);
        if (fFD < 0) {
                printf("No fFD\n");
                return B_ERROR;
        }

        fID = ID;
        
        ppp_interface_info_t info;
        // printf("SetTo info:%p\n", &info);
        if (GetInterfaceInfo(&info)) {
                fName = info.info.name;
                fID = ID;
                // printf("%s fine: name:%s, fID:%ld\n", __func__, info.info.name, fID);
        } else {
                fName = "";
                fID = PPP_UNDEFINED_INTERFACE_ID;
                // printf("%s fail: name:%s, fID:%ld\n", __func__, "", fID);
                return B_ERROR;
        }
        
        // printf("Set To %ld end =============================\n", ID);

        return B_OK;
}


/*!     \brief Use this method for accessing additional PPP features.
        
        \param op Any value of ppp_control_ops.
        \param data Some ops require you to pass a structure or other data using this
                argument.
        \param length Make sure this value is correct (e.g.: size of structure).
        
        \return
                - \c B_OK: Operation was successful.
                - \c B_BAD_INDEX: The interface does not exist.
                - any other value: The PPP stack could not be loaded.
*/
status_t
PPPInterface::Control(uint32 op, void *data, size_t length) const
{
        if (InitCheck() != B_OK) {
                printf("%s:InitCheck fail\n", __func__);
                return InitCheck();
        }

        ppp_control_info info;
        control_net_module_args args;

        sprintf(args.ifr_name, "%s", "ppp1");
        args.name = PPP_INTERFACE_MODULE_NAME;
        args.op = PPPC_CONTROL_INTERFACE;
        args.data = &info;
        args.length = sizeof(ppp_control_info);

        info.index = ID();
        info.op = op;
        info.data = data;
        info.length = length;

        printf("PPPInterface::Control: ppp_control_info %p control_net_module_args %p data %p \n", &info, &args, data);

        status_t status = ioctl(fFD, NET_STACK_CONTROL_NET_MODULE, &args);
        if (status != B_OK)
                printf("%s:%s: fail\n", __FILE__, __func__);

        return status;
}


//!     Sets the username used for authentication.
bool
PPPInterface::SetUsername(const char *username) const
{
        printf("PPPInterface::SetUsername\n");
        if (InitCheck() != B_OK || !username)
                return false;
        
        return Control(PPPC_SET_USERNAME, const_cast<char*>(username), strlen(username))
                == B_OK;
}


//!     Sets the password used for authentication.
bool
PPPInterface::SetPassword(const char *password) const
{
        printf("PPPInterface::SetPassword\n");
        if (InitCheck() != B_OK || !password)
                return false;
        
        return Control(PPPC_SET_PASSWORD, const_cast<char*>(password), strlen(password))
                == B_OK;
}


//!     Sets whether a request window should be shown before connecting.
bool
PPPInterface::SetAskBeforeConnecting(bool askBeforeConnecting) const
{
        printf("PPPInterface::SetAskBeforeConnecting\n");
        if (InitCheck() != B_OK)
                return false;
        
        uint32 value = askBeforeConnecting ? 1 : 0;
        return Control(PPPC_SET_ASK_BEFORE_CONNECTING, &value, sizeof(value)) == B_OK;
}


/*!     \brief Find BEntry to the interface settings that this object represents.
        
        \param entry The entry gets stored in this argument.
        
        \return
                - \c B_OK: The settings file was saved in \a entry.
                - \c B_BAD_INDEX: The interface does not exist.
                - \c B_BAD_VALUE: Either \a entry was \c NULL or the interface is anonymous.
                - any other value: The interface could not be found.
*/
status_t
PPPInterface::GetSettingsEntry(BEntry *entry) const
{
        printf("PPPInterface::GetSettingsEntry\n");
        if (InitCheck() != B_OK)
                return InitCheck();
        else if (!entry || strlen(Name()) == 0)
                return B_BAD_VALUE;
        
        BDirectory directory(PTP_INTERFACE_SETTINGS_PATH);
        return directory.FindEntry(Name(), entry, true);
}


/*!     \brief Get the ppp_interface_info_t structure.
        
        \return \c true on success, \c false otherwise.
*/
bool
PPPInterface::GetInterfaceInfo(ppp_interface_info_t *info) const
{
        // printf("%s info at:%p", __func__, info);
        if (InitCheck() != B_OK || !info) {
                // printf("InitCheck fail %s info at:%p", __func__, info);
                return false;
        }
        
        return Control(PPPC_GET_INTERFACE_INFO, info, sizeof(ppp_interface_info_t)) 
                == B_OK;
}


bool
PPPInterface::ControlDevice(ppp_device_info_t *info) const
{
        // printf("%s info at:%p", __func__, info);
        if (InitCheck() != B_OK || !info)
                return false;

        ppp_control_info controlInfo;
        controlInfo.op = PPPC_GET_DEVICE_INFO;
        controlInfo.index = 0;
        controlInfo.data = info;
        controlInfo.length = sizeof(ppp_device_info_t);

        return Control(PPPC_CONTROL_DEVICE, &controlInfo, sizeof(ppp_control_info))
                == B_OK;
}


bool
PPPInterface::ControlOptionHandler(ppp_simple_handler_info_t *info, uint32 handlerindex, uint32 handlerOP) const
{
        // printf("%s info at:%p", __func__, info);
        if (InitCheck() != B_OK || !info)
                return false;

        ppp_control_info controlInfo;
        controlInfo.index = handlerindex;
        controlInfo.op = handlerOP;
        controlInfo.data = info;
        controlInfo.length = sizeof(ppp_simple_handler_info_t);

        return Control(PPPC_CONTROL_OPTION_HANDLER, &controlInfo, sizeof(ppp_control_info))
                == B_OK;
}


bool
PPPInterface::ControlChild(void *info, uint32 childindex, uint32 childOP) const
{
        // printf("%s info at:%p", __func__, info);
        if (InitCheck() != B_OK || !info)
                return false;

        ppp_control_info controlInfo;
        controlInfo.index = childindex;
        controlInfo.op = childOP;
        controlInfo.data = info;
        // controlInfo.length = sizeof(ppp_simple_handler_info_t);

        return Control(PPPC_CONTROL_CHILD, &controlInfo, sizeof(ppp_control_info))
                == B_OK;
}


bool
PPPInterface::ControlLCPExtension(ppp_simple_handler_info_t *info, uint32 LCPExtensionindex, uint32 LCPExtensionOP) const
{
        // printf("%s info at:%p", __func__, info);
        if (InitCheck() != B_OK || !info)
                return false;

        ppp_control_info controlInfo;
        controlInfo.index = LCPExtensionindex;
        controlInfo.op = LCPExtensionOP;
        controlInfo.data = info;
        controlInfo.length = sizeof(ppp_simple_handler_info_t);

        return Control(PPPC_CONTROL_LCP_EXTENSION, &controlInfo, sizeof(ppp_control_info))
                == B_OK;
}


bool
PPPInterface::ControlProtocol(ppp_protocol_info_t *info, uint32 protocolindex, uint32 protocolOP) const
{
        // printf("%s info at:%p", __func__, info);
        if (InitCheck() != B_OK || !info)
                return false;

        ppp_control_info controlInfo;
        controlInfo.index = protocolindex;
        controlInfo.op = protocolOP;
        controlInfo.data = info;
        controlInfo.length = sizeof(ppp_protocol_info_t);

        return Control(PPPC_CONTROL_PROTOCOL, &controlInfo, sizeof(ppp_control_info))
                == B_OK;
}


/*!     \brief Get transfer statistics for this interface.
        
        \param statistics The structure is copied into this argument.
        
        \return \c true on success, \c false otherwise.
*/
bool
PPPInterface::GetStatistics(ppp_statistics *statistics) const
{
        printf("PPPInterface::GetStatistics\n");
        if (!statistics)
                return false;
        
        return Control(PPPC_GET_STATISTICS, statistics, sizeof(ppp_statistics)) == B_OK;
}


//!     Compares interface's settings to given driver_settings structure.
bool
PPPInterface::HasSettings(const driver_settings *settings) const
{
        printf("PPPInterface::HasSettings\n");
        if (InitCheck() != B_OK || !settings)
                return false;
        
        if (Control(PPPC_HAS_INTERFACE_SETTINGS, const_cast<driver_settings*>(settings),
                        sizeof(driver_settings)) == B_OK)
                return true;
        
        return false;
}


//!     Brings the interface up.
bool
PPPInterface::Up() const
{
        if (InitCheck() != B_OK)
                return false;

        BNetworkInterface interface(Name());
        if (!interface.Exists())
                return false;

        int32 currentFlags = interface.Flags();
        currentFlags |= IFF_UP;

        status_t status = interface.SetFlags(currentFlags);

        if (status != B_OK)
                return false;

        return true;

        int32 id = ID();
        control_net_module_args args;
        sprintf(args.ifr_name, "%s", "ppp1");
        args.name = PPP_INTERFACE_MODULE_NAME;
        args.op = PPPC_BRING_INTERFACE_UP;
        args.data = &id;
        args.length = sizeof(id);
        
        return ioctl(fFD, NET_STACK_CONTROL_NET_MODULE, &args) == B_OK;
}


//!     Brings the interface down which causes the deletion of the interface.
bool
PPPInterface::Down() const
{
        if (InitCheck() != B_OK)
                return false;

        BNetworkInterface interface(Name());
        if (!interface.Exists())
                return false;

        int32 currentFlags = interface.Flags();
        currentFlags &= ~IFF_UP;

        status_t status = interface.SetFlags(currentFlags);

        if (status != B_OK)
                return false;

        return true;

        int32 id = ID();
        control_net_module_args args;
        sprintf(args.ifr_name, "%s", "ppp1");
        args.name = PPP_INTERFACE_MODULE_NAME;
        args.op = PPPC_BRING_INTERFACE_DOWN;
        args.data = &id;
        args.length = sizeof(id);
        
        return ioctl(fFD, NET_STACK_CONTROL_NET_MODULE, &args) == B_OK;
}


/*!     \brief Requests report messages from the interface.
        
        \param type The type of report.
        \param thread Receiver thread.
        \param flags Optional flags.
        
        \return \c true on success \c false otherwise.
*/
bool
PPPInterface::EnableReports(ppp_report_type type, thread_id thread,
        int32 flags) const
{
        printf("PPPInterface::EnableReports\n");
        ppp_report_request request;
        request.type = type;
        request.thread = thread;
        request.flags = flags;
        
        return Control(PPPC_ENABLE_REPORTS, &request, sizeof(request)) == B_OK;
}


/*!     \brief Removes thread from list of report requestors of this interface.
        
        \param type The type of report.
        \param thread Receiver thread.
        
        \return \c true on success \c false otherwise.
*/
bool
PPPInterface::DisableReports(ppp_report_type type, thread_id thread) const
{
        printf("PPPInterface::DisableReports\n");
        ppp_report_request request;
        request.type = type;
        request.thread = thread;
        
        return Control(PPPC_DISABLE_REPORTS, &request, sizeof(request)) == B_OK;
}