Esempio n. 1
0
// Handler called when the server posts a "finished" message to /bs
static int BootStrapPost(void * ctxt, AddressType * addr, const char * path, const char * query, ContentType contentType,
                         const char * requestContent, size_t requestContentLen, char * responseContent, size_t * responseContentLen, int * responseCode)
{
    Lwm2mContextType * context = (Lwm2mContextType *)ctxt;
    Lwm2mBootStrapState state = Lwm2mCore_GetBootstrapState(context);

    *responseContentLen = 0;  // no content

    Lwm2m_Debug("POST to /bs\n");

    if (state == Lwm2mBootStrapState_BootStrapFinishPending)
    {
        Lwm2m_Info("Bootstrap finished\n");
        Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapped);
        Lwm2mCore_UpdateAllServers(context, Lwm2mRegistrationState_Register);
        *responseCode = AwaResult_SuccessChanged;
    }
    else if ((state == Lwm2mBootStrapState_BootStrapPending) ||
             (state == Lwm2mBootStrapState_ClientHoldOff))
    {
        Lwm2m_Info("Server initiated bootstrap\n");
        Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapped);
        Lwm2mCore_UpdateAllServers(context, Lwm2mRegistrationState_Register);
        *responseCode = AwaResult_SuccessChanged;
    }
    else
    {
        *responseCode = AwaResult_BadRequest;
    }
    return 0;
}
int coap_Destroy(void)
{
    Lwm2m_Info("Close port: \n");     //  TODO - remove
    if (networkSocket)
        NetworkSocket_Free(&networkSocket);
    // TODO - close any open sessions
//    coap_free_context(coapContext);
//    DestroyLists();
    return 0;
}
Esempio n. 3
0
static void HandleBootstrapResponse(void * ctxt, AddressType* address, const char * responsePath, int responseCode, ContentType contentType, char * payload, size_t payloadLen)
{
    Lwm2mContextType * context = ctxt;

    if (responseCode == AwaResult_SuccessChanged)
    {
        Lwm2m_Info("Waiting for bootstrap to finish\n");
        Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapFinishPending);
        Lwm2mCore_SetLastBootStrapUpdate(context, Lwm2mCore_GetTickCountMs());
    }
    else
    {
        Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapFailed);
    }
}
bool DTLS_Decrypt(NetworkAddress * sourceAddress, uint8_t * encrypted, int encryptedLength, uint8_t * decryptBuffer, int decryptBufferLength, int * decryptedLength, void *context)
{
    bool result = false;
    DTLS_Session * session = GetSession(sourceAddress);
    if (session)
    {
        session->Buffer = encrypted;
        session->BufferLength = encryptedLength;
        if (session->SessionEstablished)
        {
            *decryptedLength = gnutls_read(session->Session, decryptBuffer, decryptBufferLength);
            result = (*decryptedLength > 0);
            if (!result)
            {
                FreeSession(session);
                session = NULL;
            }
        }
        else
        {
            *decryptedLength = 0;
            session->SessionEstablished = (gnutls_handshake(session->Session) == GNUTLS_E_SUCCESS);
            if (session->SessionEstablished)
                Lwm2m_Info("Session established");
        }
    }

    if (!session)
    {
        int index;
        for (index = 0;index < MAX_DTLS_SESSIONS; index++)
        {
            if (!sessions[index].Session)
            {
                SetupNewSession(index, sourceAddress, false);
                sessions[index].UserContext = context;
                gnutls_transport_set_push_function(sessions[index].Session, SSLSendCallBack);
                sessions[index].Buffer = encrypted;
                sessions[index].BufferLength = encryptedLength;
                sessions[index].SessionEstablished = (gnutls_handshake(sessions[index].Session) == GNUTLS_E_SUCCESS);
                break;
            }
        }
    }
    return result;
}
Esempio n. 5
0
/*
 * The bootstrap interface is used to optionally configure a LWM2M Client so that it can successfully register with a LWM2M Server.
 * The client bootstrap operation is performed by sending a CoAP POST request to the LWM2M Bootstrap Server at the /bs path including
 * the Endpoint Client Name as a query string parameter.
 *
 * In client-initiated bootstrap, when the Bootstrap Server receives Request Bootstrap operation, the Bootstrap Server performs Write
 * and/or Delete operation. In server initiated bootstrap, the Bootstrap Server performs Write operation.
 * The Write or Delete operation targets to an Object Instance or a Resource. The Write and Delete operation can be sent multiple times.
 * Only in Bootstrap Interface, Delete operation MAY target to “/” URI to delete all the existing Object Instances except
 * LWM2M Bootstrap Server Account in the LWM2M Client for initializing before LWM2M Bootstrap Server sends Write operation(s)
 * to the LWM2M Client. Different from „Write“ operation in Device Management and Service Enablement interface, the LWM2M Client MUST write
 * the value included in the payload regardless of an existence of the targeting Object Instance or Resource.
 */
static void SendBootStrapRequest(Lwm2mContextType * context, int shortServerID)
{
    char * uriPath = "/bs";
    char uriQuery[128];
    char buffer[128];
    char serverPath[128];
    char uri[1024];

    Lwm2mCore_GetEndPointClientName(context, buffer, sizeof(buffer));
    sprintf(uriQuery, "?ep=%s", buffer);

    Lwm2m_GetServerURI(context, shortServerID, serverPath, sizeof(serverPath));

    sprintf(uri, "%s%s%s", serverPath, uriPath, uriQuery);
    Lwm2m_Info("Bootstrap with %s\n", uri);

    coap_PostRequest(context, uri, ContentType_None, NULL, 0, HandleBootstrapResponse);
}
bool DTLS_Encrypt(NetworkAddress * destAddress, uint8_t * plainText, int plainTextLength, uint8_t * encryptedBuffer, int encryptedBufferLength, int * encryptedLength, void *context)
{
    bool result = false;
    DTLS_Session * session = GetSession(destAddress);
    if (session)
    {
        if (session->SessionEstablished)
        {
            gnutls_transport_set_push_function(session->Session, EncryptCallBack);
            session->Buffer = encryptedBuffer;
            session->BufferLength = encryptedBufferLength;
            int written = gnutls_write(session->Session, plainText, plainTextLength);
            if (written >= 0)
            {
                *encryptedLength = encryptedBufferLength - session->BufferLength;
                result = (*encryptedLength > 0);
            }
        }
        else
        {
            session->UserContext = context;
            gnutls_transport_set_push_function(session->Session, SSLSendCallBack);
            session->SessionEstablished = (gnutls_handshake(session->Session) == GNUTLS_E_SUCCESS);
            if (session->SessionEstablished)
                Lwm2m_Info("DTLS Session established\n");
        }
    }
    else
    {
        int index;
        for (index = 0;index < MAX_DTLS_SESSIONS; index++)
        {
            if (!sessions[index].Session)
            {
                SetupNewSession(index, destAddress, true);
                sessions[index].UserContext = context;
                gnutls_transport_set_push_function(sessions[index].Session, SSLSendCallBack);
                sessions[index].SessionEstablished = (gnutls_handshake(sessions[index].Session) == GNUTLS_E_SUCCESS);
                break;
            }
        }
    }
    return result;
}
CoapInfo * coap_Init(const char * ipAddress, int port, bool secure, int logLevel)
{
    Lwm2m_Info("Bind port: %d\n", port);
    memset(CurrentTransaction, 0, sizeof(CurrentTransaction));
    memset(Observations, 0, sizeof(Observations));
    coap_init_transactions();
    coap_set_service_callback(coap_HandleRequest);
    DTLS_Init();
    if (secure)
    	networkSocket = NetworkSocket_New(NetworkSocketType_UDP | NetworkSocketType_Secure, port);
    else
    	networkSocket = NetworkSocket_New(NetworkSocketType_UDP, port);
    if (networkSocket)
    {
        if (NetworkSocket_StartListening(networkSocket))
        {
            coapInfo.fd = NetworkSocket_GetFileDescriptor(networkSocket);
        }
    }
    return &coapInfo;
}
Esempio n. 8
0
/* The LWM2M Client MUST follow the procedure specified as below when attempting to bootstrap a LWM2M Device:
 * 1. If the LWM2M Device has Smartcard, the LWM2M Client tries to obtain Bootstrap Information
 *    from the Smartcard using the Bootstrap from Smartcard mode.
 * 2. If the LWM2M Client is not configured using the Bootstrap from Smartcard mode, the LWM2M
 *    Client tries to obtain the Bootstrap Information by using Factory Bootstrap mode.
 * 3. If the LWM2M Client has any LWM2M Server Object Instances from the previous steps, the LWM2M
 *    Client tries to register to the LWM2M Server(s) configured in the LWM2M Server Object Instance(s).
 * 4. If LWM2M Client fails to register to all the LWM2M Servers or the Client doesn’t have any
 *    LWM2M Server Object Instances, and the LWM2M Client hasn’t received a Server Initiated Bootstrap
 *    within the ClientHoldOffTime, the LWM2M Client performs the Client Initiated Bootstrap.
 */
void Lwm2m_UpdateBootStrapState(Lwm2mContextType * context)
{
    uint32_t now = Lwm2mCore_GetTickCountMs();
    uint32_t clientHoldOff;
    enum { SERVER_BOOTSTRAP = 0 };

    switch (Lwm2mCore_GetBootstrapState(context))
    {
        case Lwm2mBootStrapState_NotBootStrapped:

            // First attempt smart card bootstrap.
            if (BootStrapFromSmartCard(context))
            {
                Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapped);
            }
            // If that fails try and use the factory information.
            else if (BootStrapFromFactory(context))
            {
                Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapped);
            }
            // If that fails wait for the client hold off time, for a server initiated bootstrap.
            else
            {
                Lwm2m_Info("Try existing server entries\n");
                Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_CheckExisting);
            }
            Lwm2mCore_UpdateAllServers(context, Lwm2mRegistrationState_Register);
            Lwm2mCore_SetLastBootStrapUpdate(context, now);
            break;

        case Lwm2mBootStrapState_CheckExisting:
            // Pass control to Registration process, if this fails then we will
            // drop into the ClientHoldOff State
            break;

        case Lwm2mBootStrapState_ClientHoldOff:
            // Wait for server initiated bootstrap.
            // Only one bootstrap server is supported.

            // If the hold off timer has expired, then request a boot strap.
            Lwm2m_GetClientHoldOff(context, SERVER_BOOTSTRAP, &clientHoldOff);

            if (now - Lwm2mCore_GetLastBootStrapUpdate(context) >= (clientHoldOff * 1000))
            {
                Lwm2m_Info("Hold Off expired - attempt client-initiated bootstrap\n");
                Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapPending);
                SendBootStrapRequest(context, SERVER_BOOTSTRAP);
                Lwm2mCore_SetLastBootStrapUpdate(context, now);
            }
            break;

        case Lwm2mBootStrapState_BootStrapPending:

            if (now - Lwm2mCore_GetLastBootStrapUpdate(context) >= BOOTSTRAP_TIMEOUT)
            {
                Lwm2m_Error("No response to client initiated bootstrap\n");
                Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapFailed);
                Lwm2mCore_SetLastBootStrapUpdate(context, now);
            }

            break;

        case Lwm2mBootStrapState_BootStrapFinishPending:
            // The 2015/07/07 LWM2M draft requires that the server sends a bootstrap finished to the clients /bs endpoint,
            // however for now lets just wait up to 15 seconds and then move to BootStrapped state.
            if (now - Lwm2mCore_GetLastBootStrapUpdate(context) >= BOOTSTRAP_FINISHED_TIMEOUT)
            {
                Lwm2m_Warning("No bootstrap finished after 15 seconds, retrying...\n");
                Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_BootStrapFailed);
            }
            break;

        case Lwm2mBootStrapState_BootStrapped:
            break;

        case Lwm2mBootStrapState_BootStrapFailed:

            // If the hold off timer has expired, then request a boot strap.
            Lwm2m_GetClientHoldOff(context, SERVER_BOOTSTRAP, &clientHoldOff);

            if (now - Lwm2mCore_GetLastBootStrapUpdate(context) >= (clientHoldOff * 1000))
            {
                Lwm2m_Warning("HoldOff Expired - Re-attempt bootstrap\n");
                Lwm2mCore_SetBootstrapState(context, Lwm2mBootStrapState_NotBootStrapped);
            }
            break;

        default:
            Lwm2m_Error("Unhandled bootstrap state %d\n", Lwm2mCore_GetBootstrapState(context));
    }
}
Esempio n. 9
0
static int Lwm2mServer_Start(Options * options)
{
    int xmlFd;
    int result = 0;

    if (options->Daemonise)
    {
        Daemonise(options->Verbose);
    }
    else
    {
        signal(SIGINT, Lwm2m_CtrlCSignalHandler);
    }

    signal(SIGTERM, Lwm2m_CtrlCSignalHandler);

    // open log files here
    if (options->LogFile != NULL)
    {
        errno = 0;
        logFile = fopen(options->LogFile, "at");
        if (logFile != NULL)
        {
            Lwm2m_SetOutput(logFile);

            // redirect stdout
            dup2(fileno(logFile), STDOUT_FILENO);
        }
        else
        {
            Lwm2m_Error("Failed to open log file %s: %s\n", options->LogFile, strerror(errno));
        }
    }

    if (options->Version)
    {
        Lwm2m_Printf(0, "%s\n", version);
        goto error_close_log;
    }

    Lwm2m_SetLogLevel((options->Verbose) ? DebugLevel_Debug : DebugLevel_Info);
    Lwm2m_PrintBanner();
    if (options->Verbose)
    {
        PrintOptions(options);
    }
    Lwm2m_Info("Awa LWM2M Server, version %s\n", version);
    Lwm2m_Info("  Process ID     : %d\n", getpid());
    Lwm2m_Info("  DTLS library   : %s\n", DTLS_LibraryName);
    Lwm2m_Info("  CoAP library   : %s\n", coap_LibraryName);
    Lwm2m_Info("  CoAP port      : %d\n", options->CoapPort);
    Lwm2m_Info("  Secure         : %s\n", options->Secure ? "true": "false");
    Lwm2m_Info("  IPC port       : %d\n", options->IpcPort);
    Lwm2m_Info("  Address family : IPv%d\n", options->AddressFamily == AF_INET ? 4 : 6);



    if (options->InterfaceName != NULL)
    {
        Lwm2m_Info("LWM2M server - Using interface %s [IPv%d]\n", options->InterfaceName, options->AddressFamily == AF_INET? 4 : 6);
    }
    else if (strcmp(DEFAULT_IP_ADDRESS, options->IPAddress) != 0)
    {
        Lwm2m_Info("LWM2M server - IP Address %s\n", options->IPAddress);
    }

    char ipAddress[NI_MAXHOST];
    if (options->InterfaceName != NULL)
    {
        if (Lwm2mCore_GetIPAddressFromInterface(options->InterfaceName, options->AddressFamily, ipAddress, sizeof(ipAddress)) != 0)
        {
            result = 1;
            goto error_close_log;
        }
        Lwm2m_Info("LWM2M server - Interface Address %s\n", ipAddress);
    }
    else
    {
        strncpy(ipAddress, options->IPAddress, NI_MAXHOST);
        ipAddress[NI_MAXHOST - 1] = '\0'; // Defensive
    }

    CoapInfo * coap = coap_Init(ipAddress, options->CoapPort, options->Secure, (options->Verbose) ? DebugLevel_Debug : DebugLevel_Info);
    if (coap == NULL)
    {
        printf("Unable to map address to network interface\n");
        result = 1;
        goto error_close_log;
    }

    if (options->Secure)
    {
    	coap_SetCertificate(serverCert, sizeof(serverCert), CertificateFormat_PEM);
        coap_SetPSK(pskIdentity, pskKey, sizeof(pskKey));
    }

    Lwm2mContextType * context = Lwm2mCore_Init(NULL, options->ContentType);  // NULL, don't map coap with objectStore

    // must happen after coap_Init()
    Lwm2m_RegisterObjectTypes(context);

    // listen for UDP packets on port 12345 for now.
    xmlFd = xmlif_init(context, options->IpcPort);
    if (xmlFd < 0)
    {
        result = 1;
        goto error_destroy;
    }
    xmlif_RegisterHandlers();

    // wait for messages on both the "IPC" and coap interfaces
    while (!quit)
    {
        int loop_result;
        struct pollfd fds[2];
        int nfds = 2;
        int timeout;

        fds[0].fd = coap->fd;
        fds[0].events = POLLIN;

        fds[1].fd = xmlFd;
        fds[1].events = POLLIN;

        timeout = Lwm2mCore_Process(context);

        loop_result = poll(fds, nfds, timeout);

        if (loop_result < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            perror("poll:");
            break;
        }
        else if (loop_result > 0)
        {
            if (fds[0].revents == POLLIN)
            {
                coap_HandleMessage();
            }
            if (fds[1].revents == POLLIN)
            {
                xmlif_process(fds[1].fd);
            }
        }
        coap_Process();
    }
    Lwm2m_Debug("Exit triggered\n");

error_destroy:
    xmlif_destroy(xmlFd);
    Lwm2mCore_Destroy(context);
    coap_Destroy();

error_close_log:
    Lwm2m_Info("Server exiting\n");
    if (logFile != NULL)
    {
        fclose(logFile);
    }

    return result;
}
Esempio n. 10
0
static int Lwm2mClient_Start(Options * options)
{
    FILE * logFile = NULL;
    uint8_t * loadedClientCert = NULL;
    int result = 0;
    uint8_t * key = NULL;

    if (options->Daemonise)
    {
        Daemonise(options->Verbose);
    }
    else
    {
        signal(SIGINT, Lwm2m_CtrlCSignalHandler);
    }

    signal(SIGTERM, Lwm2m_CtrlCSignalHandler);

    if (options->LogFile)
    {
        errno = 0;
        logFile = fopen(options->LogFile, "at");
        if (logFile != NULL)
        {
            Lwm2m_SetOutput(logFile);

            // redirect stdout
            dup2(fileno(logFile), STDOUT_FILENO);
        }
        else
        {
            Lwm2m_Error("Failed to open log file %s: %s\n", options->LogFile, strerror(errno));
        }
    }

    if (options->Version)
    {
        Lwm2m_Printf(0, "%s\n", version);
        goto error_close_log;
    }

    srandom((int)time(NULL)*getpid());
    if (options->CoapPort == 0)
    {
        options->CoapPort = 6000 + (rand() % 32768);
    }

    Lwm2m_SetLogLevel((options->Verbose) ? DebugLevel_Debug : DebugLevel_Info);
    Lwm2m_PrintBanner();
    if (options->Verbose)
    {
        PrintOptions(options);
    }
    Lwm2m_Info("Awa LWM2M Client, version %s\n", version);
    Lwm2m_Info("  Process ID     : %d\n", getpid());
    Lwm2m_Info("  Endpoint name  : \'%s\'\n", options->EndPointName);
    Lwm2m_Info("  DTLS library   : %s\n", DTLS_LibraryName);
    Lwm2m_Info("  CoAP library   : %s\n", coap_LibraryName);
    Lwm2m_Info("  CoAP port      : %d\n", options->CoapPort);
    Lwm2m_Info("  IPC port       : %d\n", options->IpcPort);
    Lwm2m_Info("  Address family : IPv%d\n", options->AddressFamily == AF_INET ? 4 : 6);

    Lwm2mCore_SetDefaultContentType(options->DefaultContentType);

    CoapInfo * coap = coap_Init((options->AddressFamily == AF_INET) ? "0.0.0.0" : "::", options->CoapPort, false /* not a server */, (options->Verbose) ? DebugLevel_Debug : DebugLevel_Info);
    if (coap == NULL)
    {
        Lwm2m_Error("Failed to initialise CoAP on port %d\n", options->CoapPort);
        result = 1;
        goto error_close_log;
    }

    // always set key
    if (options->CertificateFile)
    {
        loadedClientCert = LoadCertificateFile(options->CertificateFile);
    }
    else
        coap_SetCertificate(clientCert, sizeof(clientCert), AwaCertificateFormat_PEM);

    if (options->PskIdentity && options->PskKey)
    {
        int hexKeyLength = strlen(options->PskKey);
        int keyLength = hexKeyLength / 2;
        key = (uint8_t *)malloc(keyLength);
        if (key)
        {
           char * value = options->PskKey;
           int index;
           for (index = 0; index < keyLength; index++)
           {
               key[index] = HexToByte(value);
               value += 2;
           }
           coap_SetPSK(options->PskIdentity, key, keyLength);
        }
        else
            coap_SetPSK(pskIdentity, pskKey, sizeof(pskKey));
    }
    else
        coap_SetPSK(pskIdentity, pskKey, sizeof(pskKey));

    // if required read the bootstrap information from a file
    const BootstrapInfo * factoryBootstrapInfo;
    if (options->FactoryBootstrapFile != NULL)
    {
        factoryBootstrapInfo = BootstrapInformation_ReadConfigFile(options->FactoryBootstrapFile);
        if (factoryBootstrapInfo == NULL)
        {
            Lwm2m_Error("Factory Bootstrap configuration file load failed\n");
            result = 1;
            goto error_coap;
        }
        else
        {
            Lwm2m_Info("Factory Bootstrap:\n");
            Lwm2m_Info("Server Configuration\n");
            Lwm2m_Info("====================\n");
            BootstrapInformation_Dump(factoryBootstrapInfo);
        }
    }
    else
    {
        factoryBootstrapInfo = NULL;
    }

    Lwm2mContextType * context = Lwm2mCore_Init(coap, options->EndPointName);

    // Must happen after coap_Init().
    RegisterObjects(context, options);

    if (factoryBootstrapInfo != NULL)
    {
        Lwm2mCore_SetFactoryBootstrap(context, factoryBootstrapInfo);
    }

    // bootstrap information has been loaded, no need to hang onto this anymore
    BootstrapInformation_DeleteBootstrapInfo(factoryBootstrapInfo);

    // load any specified objDef files
    if (LoadObjectDefinitionsFromFiles(context, options->ObjDefsFiles, options->NumObjDefsFiles) != 0)
    {
        goto error_core;
    }

    // Listen for UDP packets on IPC port
    int xmlFd = xmlif_init(context, options->IpcPort);
    if (xmlFd < 0)
    {
        Lwm2m_Error("Failed to initialise XML interface on port %d\n", options->IpcPort);
        result = 1;
        goto error_core;
    }
    xmlif_RegisterHandlers();

    // Wait for messages on both the IPC and CoAP interfaces
    while (!quit)
    {
        int loop_result;
        struct pollfd fds[2];
        int nfds = 2;
        int timeout;

        fds[0].fd = coap->fd;
        fds[0].events = POLLIN;

        fds[1].fd = xmlFd;
        fds[1].events = POLLIN;

        timeout = Lwm2mCore_Process(context);

        loop_result = poll(fds, nfds, timeout);
        if (loop_result < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            perror("poll:");
            break;
        }
        else if (loop_result > 0)
        {
            if (fds[0].revents == POLLIN)
            {
                coap_HandleMessage();
            }
            if (fds[1].revents == POLLIN)
            {
                xmlif_process(fds[1].fd);
            }
        }
        coap_Process();
    }
    Lwm2m_Debug("Exit triggered\n");

    xmlif_DestroyExecuteHandlers();
    xmlif_destroy(xmlFd);
error_core:
    Lwm2mCore_Destroy(context);
error_coap:
    coap_Destroy();

error_close_log:
    free(loadedClientCert);
    free(key);
    Lwm2m_Info("Client exiting\n");
    if (logFile)
    {
        fclose(logFile);
    }

    return result;
}
Esempio n. 11
0
static int Bootstrap_Start(Options * options)
{
    int result = 0;

    if (options->Daemonise)
    {
        Daemonise(options->Verbose);
    }
    else
    {
        signal(SIGINT, CtrlCSignalHandler);
    }

    signal(SIGTERM, CtrlCSignalHandler);

    // open log files here
    if (options->LogFile)
    {
        errno = 0;
        logFile = fopen(options->LogFile, "at");
        if (logFile != NULL)
        {
            Lwm2m_SetOutput(logFile);

            // redirect stdout
            dup2(fileno(logFile), STDOUT_FILENO);
        }
        else
        {
            Lwm2m_Error("Failed to open log file %s: %s\n", options->LogFile, strerror(errno));
        }
    }

    if (options->Version)
    {
        Lwm2m_Printf(0, "%s\n", version);
        goto error_close_log;
    }

    Lwm2m_SetLogLevel((options->Verbose) ? DebugLevel_Debug : DebugLevel_Info);
    Lwm2m_PrintBanner();
    if (options->Verbose)
    {
        PrintOptions(options);
    }
    Lwm2m_Info("Awa LWM2M Bootstrap Server, version %s\n", version);
    Lwm2m_Info("  Process ID     : %d\n", getpid());
    Lwm2m_Info("  CoAP port      : %d\n", options->Port);

    if (options->InterfaceName != NULL)
    {
        Lwm2m_Info("  Interface      : %s [IPv%d]\n", options->InterfaceName, options->AddressFamily == AF_INET? 4 : 6);
    }
    else if (strcmp(DEFAULT_IP_ADDRESS, options->IPAddress) != 0)
    {
        Lwm2m_Info("  IP Address     : %s\n", options->IPAddress);
    }

    char ipAddress[NI_MAXHOST];
    if (options->InterfaceName != NULL)
    {
        if (Lwm2mCore_GetIPAddressFromInterface(options->InterfaceName, options->AddressFamily, ipAddress, sizeof(ipAddress)) != 0)
        {
            result = 1;
            goto error_close_log;
        }
        Lwm2m_Info("  Interface Addr : %s\n", ipAddress);
    }
    else
    {
        strncpy(ipAddress, options->IPAddress, NI_MAXHOST);
        ipAddress[NI_MAXHOST - 1] = '\0';  // Defensive
    }

    CoapInfo * coap = coap_Init(ipAddress, options->Port, (options->Verbose) ? DebugLevel_Debug : DebugLevel_Info);
    if (coap == NULL)
    {
        printf("Unable to map address to network interface\n");
        result = 1;
        goto error_close_log;
    }
    Lwm2mContextType * context = Lwm2mCore_Init(coap);

    // must happen after coap_Init()
    Lwm2m_RegisterObjectTypes(context);

    if (!Lwm2mBootstrap_BootStrapInit(context, options->Config, options->ConfigCount))
    {
        printf("Failed to initialise boostrap\n");
        result = 1;
        goto error_destroy;
    }

    // wait for messages on both the "IPC" and coap interfaces
    while (!quit)
    {
        int loop_result;
        struct pollfd fds[1];
        int nfds = 1;
        int timeout;

        fds[0].fd = coap->fd;
        fds[0].events = POLLIN;

        timeout = Lwm2mCore_Process(context);

        loop_result = poll(fds, nfds, timeout);

        if (loop_result < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }

            perror("poll:");
            break;
        }
        else if (loop_result > 0)
        {
            if (fds[0].revents == POLLIN)
            {
                coap_HandleMessage();
            }
        }
        coap_Process();
    }
    Lwm2m_Debug("Exit triggered\n");

error_destroy:
    Lwm2mBootstrap_Destroy();
    Lwm2mCore_Destroy(context);
    coap_Destroy();

error_close_log:
    Lwm2m_Info("Bootstrap Server exiting\n");
    if (logFile != NULL)
    {
        fclose(logFile);
    }

    return result;
}
void coap_createCoapRequest(coap_method_t method, const char * uri, ContentType contentType, ObserveState observeState,
        const char * payload, int payloadLen, TransactionCallback callback, void * context)
{
    coap_packet_t request;
    char path[MAX_COAP_PATH] =
    { 0 };
    char query[128] =
    { 0 };
    coap_transaction_t *transaction;
    NetworkAddress * remoteAddress = NetworkAddress_New(uri, strlen(uri));

    coap_getPathQueryFromURI(uri, path, query);

    Lwm2m_Info("Coap request: %s\n", uri);
    //Lwm2m_Debug("Coap request path: %s\n", path);
    //Lwm2m_Debug("Coap request query: %s\n", query);

    coap_init_message(&request, COAP_TYPE_CON, method, coap_get_mid());

    coap_set_header_uri_path(&request, path);
    if (strlen(query) > 0)
        coap_set_header_uri_query(&request, query);
    // TODO - REVIEW: Erbium must copy path/query from request - else mem out of scope

    if (contentType != ContentType_None)
    {
        if ((method == COAP_POST) || (method == COAP_PUT))
        {
            coap_set_header_content_format(&request, contentType);
            coap_set_payload(&request, payload, payloadLen);
        }
        else
        {
            coap_set_header_accept(&request, contentType);
        }
    }


    if (method == COAP_GET)
    {
        if (observeState == ObserveState_Establish)
        {
            coap_set_header_observe(&request, 0);
            int token = addObserve(remoteAddress, path, callback, context);
            if (token != 0)
                coap_set_token(&request, (const uint8_t *) &token, sizeof(token));

        }
        else if (observeState == ObserveState_Cancel)
        {
            coap_set_header_observe(&request, 1);
            int token = removeObserve(remoteAddress, path);
            if (token != 0)
                coap_set_token(&request, (const uint8_t *) &token, sizeof(token));
        }
    }

    if (CurrentTransaction[CurrentTransactionIndex].TransactionUsed && CurrentTransaction[CurrentTransactionIndex].TransactionPtr)
    {
        Lwm2m_Warning("Canceled previous transaction [%d]: %p\n", CurrentTransactionIndex,
                CurrentTransaction[CurrentTransactionIndex].TransactionPtr);
        coap_clear_transaction(&CurrentTransaction[CurrentTransactionIndex].TransactionPtr);
    }

    //if ((transaction = coap_new_transaction(request.mid, remote_ipaddr, uip_htons(remote_port))))
    if ((transaction = coap_new_transaction(networkSocket, request.mid, remoteAddress)))
    {
        transaction->callback = coap_CoapRequestCallback;
        memcpy(CurrentTransaction[CurrentTransactionIndex].Path, path, MAX_COAP_PATH);
        CurrentTransaction[CurrentTransactionIndex].Callback = callback;
        CurrentTransaction[CurrentTransactionIndex].Context = context;
        CurrentTransaction[CurrentTransactionIndex].TransactionUsed = true;
        CurrentTransaction[CurrentTransactionIndex].TransactionPtr = transaction;
        NetworkAddress_SetAddressType(remoteAddress, &CurrentTransaction[CurrentTransactionIndex].Address);

        transaction->callback_data = &CurrentTransaction[CurrentTransactionIndex];

        transaction->packet_len = coap_serialize_message(&request, transaction->packet);

        Lwm2m_Debug("Sending transaction [%d]: %p\n", CurrentTransactionIndex, CurrentTransaction[CurrentTransactionIndex].TransactionPtr);
        coap_send_transaction(transaction);

        CurrentTransactionIndex++;

        if (CurrentTransactionIndex >= MAX_COAP_TRANSACTIONS)
        {
            CurrentTransactionIndex = 0;
        }
    }
}