warmcat / libwebsockets

canonical libwebsockets.org networking library
https://libwebsockets.org
Other
4.73k stars 1.48k forks source link

Unable to run an LWS client code on an embeded ARM device #1670

Closed chrimsonite closed 5 years ago

chrimsonite commented 5 years ago

Hello,

We are developing an application on an embedded device running Linux, which will use libwebsockets. For testing the library we implemented an lws client to connect to an echo server (wss://echo.websocket.org) and send a random number every second and receive the echoed result.

Following is the code we tested -

#include <libwebsockets.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <csignal>

constexpr auto EXAMPLE_RX_BUFFER_BYTES = 10;

auto isRunning = true;

void SIGINT_HANDER(int signo)
{
    printf("signal handled!!!\n");

    isRunning = false;
}

char sigsev_buf[32]{"default"};

void SIGSEGV_HANDLER(int signo)
{
    printf("segfault!!!!\n");
    printf("%s", sigsev_buf);
    printf("\n");

    exit(0);
}

static int callback_example( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
{
    switch(reason)
    {
        case LWS_CALLBACK_CLIENT_ESTABLISHED:
            printf("Estb\n");
            lws_callback_on_writable(wsi);
            break;

        case LWS_CALLBACK_CLIENT_RECEIVE:
            printf("Recieve\n");
            printf("read = %s\n", 
                reinterpret_cast<char*>(in));
            break;

        case LWS_CALLBACK_CLIENT_WRITEABLE:
        {
            printf("Writable\n");
            unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + EXAMPLE_RX_BUFFER_BYTES];
            unsigned char* ptr = &buf[LWS_SEND_BUFFER_PRE_PADDING];
            size_t n = sprintf(reinterpret_cast<char*>(ptr), "%u", rand());
            printf("write = %s\n", ptr);
            lws_write(wsi, ptr, n, LWS_WRITE_TEXT);
            break;
        }

        case LWS_CALLBACK_CLOSED:
            printf("Closed\n");
        break;

        case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
            printf("Connection Errrorsrr!!!\n");
            break;

        case LWS_CALLBACK_WSI_DESTROY:
            printf("destroyed!!!!\n");

        default:
            break;
    }

    return 0;
}

enum protocols
{
    PROTOCOL_EXAMPLE = 0,
    PROTOCOL_COUNT
};

static struct lws_protocols protocols[]{
    {
        "websocket",
        callback_example,
        0,
        EXAMPLE_RX_BUFFER_BYTES,
    },
    { NULL, NULL, 0, 0 } /* terminator */
};

static const struct lws_extension extensions[]{
    {
        "permessage-deflate",
        lws_extension_callback_pm_deflate,
        "permessage-deflate; client_max_window_bits"
    },
    {
        "deflate-frame",
        lws_extension_callback_pm_deflate,
        "deflate_frame"
    },
    { NULL, NULL, NULL } // Always needed at the end
};

constexpr auto LOG_LEVEL = LLL_CLIENT | LLL_COUNT | LLL_DEBUG | LLL_ERR | LLL_EXT | LLL_HEADER
        | LLL_INFO | LLL_LATENCY | LLL_NOTICE | LLL_PARSER | LLL_THREAD | LLL_USER | LLL_WARN;

int main(int argc, char *argv[])
{
    signal(SIGINT, SIGINT_HANDER);
    signal(SIGSEGV, SIGSEGV_HANDLER);

    lws_set_log_level(LOG_LEVEL, lwsl_emit_stderr);

    lws_context_creation_info info;
    memset(&info, 0, sizeof(info));

    info.port = CONTEXT_PORT_NO_LISTEN;
    info.protocols = protocols;
    info.gid = -1;
    info.uid = -1;
        info.extensions = extensions;

    info.ssl_ca_filepath = "./cacert.pem";
    info.client_ssl_ca_filepath = "./cacert.pem";

        info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;

    sprintf(sigsev_buf, "segfault = pre_lws_create");

    lws_context *context = lws_create_context( &info );

    sprintf(sigsev_buf, "segfault = post_lws_create");

    char inputURL[]{"wss://echo.websocket.org"};

    lws_client_connect_info ccinfo;
    memset(&ccinfo, 0, sizeof(ccinfo));

    const char* urlProtocol; 
    const char* urlTempPath;
    char urlPath[300]; 

    if (lws_parse_uri(inputURL, &urlProtocol, &ccinfo.address, &ccinfo.port, &urlTempPath))
    {
        printf("Couldn't parse URL\n");
    }
    printf("protocol[%s] host,orgin,address[%s] port[%d] temppath[%s]\n",urlProtocol,ccinfo.address,ccinfo.port,urlTempPath);
    urlPath[0] = '/';
    strncpy(urlPath + 1, urlTempPath, sizeof(urlPath) - 2);
    urlPath[sizeof(urlPath) - 1] = '\0';

    ccinfo.path = urlPath;

    ccinfo.context = context;
    ccinfo.ssl_connection = 1;
    ccinfo.host = ccinfo.address;
    ccinfo.origin = ccinfo.address;
    ccinfo.ietf_version_or_minus_one = -1;
    ccinfo.protocol = protocols[PROTOCOL_EXAMPLE].name;
    ccinfo.client_exts = extensions;

    lws *web_socket = nullptr;
    time_t old = 0;
    while(isRunning)
    {
        timeval tv;
        gettimeofday(&tv, NULL);

        if(!web_socket && tv.tv_sec != old)
        {
            web_socket = lws_client_connect_via_info(&ccinfo);
        }

        if( tv.tv_sec != old )
        {
            // Send a random number to the server every second
            lws_callback_on_writable( web_socket );
            old = tv.tv_sec;
        }

        lws_service( context, 250 );
    }

    lws_context_destroy( context );

    return 0;
}

cacert.pem is a certificate file we obtained from libcurl's website - https://curl.haxx.se/docs/caextract.html

We tested our lws client on an Ubuntu machine (version 18.04) with libwebsockets version 3.2.99 (self compiled) and libssl version 1.1.1 (distro supplied) and it ran as expected.

On testing the same code on our embeded deivce (ARM9 chip and Linux Kernel version 3.18.20) with libwebsockets version 3.2.99 (self compiled) and libssl version 1.1.1 (self compiled), the code experienced a segfault during context creation, i.e. on calling lws_create_context.

Following is the LWS log when the code was tested on the embedded device at log level 4095 -

[2019/08/28 14:06:10:0247] I: Initial logging level 4095
[2019/08/28 14:06:10:0255] I: Libwebsockets version: 3.2.99 v3.1.0-277-gf027f0b6
[2019/08/28 14:06:10:0256] I: IPV6 not compiled in
[2019/08/28 14:06:10:0257] I:  LWS_DEF_HEADER_LEN    : 4096
[2019/08/28 14:06:10:0258] I:  LWS_MAX_PROTOCOLS     : 5
[2019/08/28 14:06:10:0259] I:  LWS_MAX_SMP           : 1
[2019/08/28 14:06:10:0260] I:  sizeof (*info)        : 392
[2019/08/28 14:06:10:0265] I:  SYSTEM_RANDOM_FILEPATH: '/dev/urandom'
[2019/08/28 14:06:10:0267] I:  HTTP2 support         : not configured
[2019/08/28 14:06:10:0268] D: _realloc: size 680: context
[2019/08/28 14:06:10:0299] E: lws_plat_drop_app_privileges: unknown groupname ''
[2019/08/28 14:06:10:0301] I: lws_context_destroy: ctx 0xb020
lws-team commented 5 years ago

We tested our lws client on an Ubuntu machine (version 18.04) with libwebsockets version 3.2.99 (self compiled) and libssl version 1.1.1 (distro supplied) and it ran as expected.

Good. 3.2.99 just means it's master "after v3.2 and before the next release", it probably doesn't matter for this but FYI you need to look at the git hash to determine what it actually is.

[2019/08/28 14:06:10:0299] E: lws_plat_drop_app_privileges: unknown groupname '�'

int
lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop)
{
    struct passwd *p;
    struct group *g;

    /* if he gave us the groupname, align gid to match it */

    if (context->groupname) {    <<<<<=========
        g = getgrnam(context->groupname);

        if (g) {
            lwsl_info("%s: group %s -> gid %u\n", __func__,
                  context->groupname, g->gr_gid);
            context->gid = g->gr_gid;
        } else {
            lwsl_err("%s: unknown groupname '%s'\n", __func__,    <<<<======
                 context->groupname);

            return 1;
        }
    }
...

context->groupname is set to info->groupname from the context creation info struct (conetxt.c)

    context->username = info->username;
    context->groupname = info->groupname;

This and the code you have are saying contradictory things... info.groupname should be NULL from the memset, but apparently it isn't. Maybe dump info.groupname before the call to context create and at the code I pasted (lib/plat/unix/unix-caps.c) to try to see what happened. Your memset() is somehow a NOP?

lws-team commented 5 years ago

... another possibility, the libwebsockets.h and friends you built your app against are not the ones from the library but significantly older. The memset is happening but the info struct it has been told about is smaller than the one the library was built for.

chrimsonite commented 5 years ago

You are right! I mistakenly mentioned path to an old version's directory in the makefile for device build. Thanks for your kind help.