1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * lws-minimal-http-client-captive-portal 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * Written in 2010-2020 by Andy Green <andy@warmcat.com> 5d4afb5ceSopenharmony_ci * 6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0 7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication. 8d4afb5ceSopenharmony_ci * 9d4afb5ceSopenharmony_ci * This demonstrates how to use the lws_system captive portal detect integration 10d4afb5ceSopenharmony_ci * 11d4afb5ceSopenharmony_ci * We check for a captive portal by doing a GET from 12d4afb5ceSopenharmony_ci * http://connectivitycheck.android.com/generate_204, if we really are going 13d4afb5ceSopenharmony_ci * out on the Internet he'll return with a 204 response code and we will 14d4afb5ceSopenharmony_ci * understand there's no captive portal. If we get something else, we take it 15d4afb5ceSopenharmony_ci * there is a captive portal. 16d4afb5ceSopenharmony_ci */ 17d4afb5ceSopenharmony_ci 18d4afb5ceSopenharmony_ci#include <libwebsockets.h> 19d4afb5ceSopenharmony_ci#include <string.h> 20d4afb5ceSopenharmony_ci#include <signal.h> 21d4afb5ceSopenharmony_ci 22d4afb5ceSopenharmony_cistatic struct lws_context *context; 23d4afb5ceSopenharmony_cistatic int interrupted, bad = 1, status; 24d4afb5ceSopenharmony_cistatic lws_state_notify_link_t nl; 25d4afb5ceSopenharmony_ci 26d4afb5ceSopenharmony_ci/* 27d4afb5ceSopenharmony_ci * this is the user code http handler 28d4afb5ceSopenharmony_ci */ 29d4afb5ceSopenharmony_ci 30d4afb5ceSopenharmony_cistatic int 31d4afb5ceSopenharmony_cicallback_http(struct lws *wsi, enum lws_callback_reasons reason, 32d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 33d4afb5ceSopenharmony_ci{ 34d4afb5ceSopenharmony_ci switch (reason) { 35d4afb5ceSopenharmony_ci 36d4afb5ceSopenharmony_ci /* because we are protocols[0] ... */ 37d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 38d4afb5ceSopenharmony_ci lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", 39d4afb5ceSopenharmony_ci in ? (char *)in : "(null)"); 40d4afb5ceSopenharmony_ci interrupted = 1; 41d4afb5ceSopenharmony_ci break; 42d4afb5ceSopenharmony_ci 43d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: 44d4afb5ceSopenharmony_ci { 45d4afb5ceSopenharmony_ci char buf[128]; 46d4afb5ceSopenharmony_ci 47d4afb5ceSopenharmony_ci lws_get_peer_simple(wsi, buf, sizeof(buf)); 48d4afb5ceSopenharmony_ci status = (int)lws_http_client_http_response(wsi); 49d4afb5ceSopenharmony_ci 50d4afb5ceSopenharmony_ci lwsl_user("Connected to %s, http response: %d\n", 51d4afb5ceSopenharmony_ci buf, status); 52d4afb5ceSopenharmony_ci } 53d4afb5ceSopenharmony_ci break; 54d4afb5ceSopenharmony_ci 55d4afb5ceSopenharmony_ci /* chunks of chunked content, with header removed */ 56d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: 57d4afb5ceSopenharmony_ci lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); 58d4afb5ceSopenharmony_ci 59d4afb5ceSopenharmony_ci#if 0 /* enable to dump the html */ 60d4afb5ceSopenharmony_ci { 61d4afb5ceSopenharmony_ci const char *p = in; 62d4afb5ceSopenharmony_ci 63d4afb5ceSopenharmony_ci while (len--) 64d4afb5ceSopenharmony_ci if (*p < 0x7f) 65d4afb5ceSopenharmony_ci putchar(*p++); 66d4afb5ceSopenharmony_ci else 67d4afb5ceSopenharmony_ci putchar('.'); 68d4afb5ceSopenharmony_ci } 69d4afb5ceSopenharmony_ci#endif 70d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 71d4afb5ceSopenharmony_ci 72d4afb5ceSopenharmony_ci /* uninterpreted http content */ 73d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: 74d4afb5ceSopenharmony_ci { 75d4afb5ceSopenharmony_ci char buffer[1024 + LWS_PRE]; 76d4afb5ceSopenharmony_ci char *px = buffer + LWS_PRE; 77d4afb5ceSopenharmony_ci int lenx = sizeof(buffer) - LWS_PRE; 78d4afb5ceSopenharmony_ci 79d4afb5ceSopenharmony_ci if (lws_http_client_read(wsi, &px, &lenx) < 0) 80d4afb5ceSopenharmony_ci return -1; 81d4afb5ceSopenharmony_ci } 82d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 83d4afb5ceSopenharmony_ci 84d4afb5ceSopenharmony_ci case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: 85d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); 86d4afb5ceSopenharmony_ci interrupted = 1; 87d4afb5ceSopenharmony_ci bad = status != 200; 88d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ 89d4afb5ceSopenharmony_ci break; 90d4afb5ceSopenharmony_ci 91d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLOSED_CLIENT_HTTP: 92d4afb5ceSopenharmony_ci interrupted = 1; 93d4afb5ceSopenharmony_ci bad = status != 200; 94d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ 95d4afb5ceSopenharmony_ci break; 96d4afb5ceSopenharmony_ci 97d4afb5ceSopenharmony_ci default: 98d4afb5ceSopenharmony_ci break; 99d4afb5ceSopenharmony_ci } 100d4afb5ceSopenharmony_ci 101d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 102d4afb5ceSopenharmony_ci} 103d4afb5ceSopenharmony_ci 104d4afb5ceSopenharmony_ci/* 105d4afb5ceSopenharmony_ci * This is the platform's custom captive portal detection handler 106d4afb5ceSopenharmony_ci */ 107d4afb5ceSopenharmony_ci 108d4afb5ceSopenharmony_cistatic int 109d4afb5ceSopenharmony_cicallback_cpd_http(struct lws *wsi, enum lws_callback_reasons reason, 110d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 111d4afb5ceSopenharmony_ci{ 112d4afb5ceSopenharmony_ci int resp; 113d4afb5ceSopenharmony_ci 114d4afb5ceSopenharmony_ci switch (reason) { 115d4afb5ceSopenharmony_ci 116d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: 117d4afb5ceSopenharmony_ci resp = (int)lws_http_client_http_response(wsi); 118d4afb5ceSopenharmony_ci if (!resp) 119d4afb5ceSopenharmony_ci break; 120d4afb5ceSopenharmony_ci lwsl_user("%s: established with resp %d\n", __func__, resp); 121d4afb5ceSopenharmony_ci switch (resp) { 122d4afb5ceSopenharmony_ci 123d4afb5ceSopenharmony_ci case HTTP_STATUS_NO_CONTENT: 124d4afb5ceSopenharmony_ci /* 125d4afb5ceSopenharmony_ci * We got the 204 which is used to distinguish the real 126d4afb5ceSopenharmony_ci * endpoint 127d4afb5ceSopenharmony_ci */ 128d4afb5ceSopenharmony_ci lws_system_cpd_set(lws_get_context(wsi), 129d4afb5ceSopenharmony_ci LWS_CPD_INTERNET_OK); 130d4afb5ceSopenharmony_ci return 0; 131d4afb5ceSopenharmony_ci 132d4afb5ceSopenharmony_ci /* also case HTTP_STATUS_OK: ... */ 133d4afb5ceSopenharmony_ci default: 134d4afb5ceSopenharmony_ci break; 135d4afb5ceSopenharmony_ci } 136d4afb5ceSopenharmony_ci 137d4afb5ceSopenharmony_ci /* fallthru */ 138d4afb5ceSopenharmony_ci 139d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_HTTP_REDIRECT: 140d4afb5ceSopenharmony_ci lws_system_cpd_set(lws_get_context(wsi), LWS_CPD_CAPTIVE_PORTAL); 141d4afb5ceSopenharmony_ci /* don't follow it, just report it */ 142d4afb5ceSopenharmony_ci return 1; 143d4afb5ceSopenharmony_ci 144d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 145d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLOSED_CLIENT_HTTP: 146d4afb5ceSopenharmony_ci /* only the first result counts */ 147d4afb5ceSopenharmony_ci lws_system_cpd_set(lws_get_context(wsi), LWS_CPD_NO_INTERNET); 148d4afb5ceSopenharmony_ci break; 149d4afb5ceSopenharmony_ci 150d4afb5ceSopenharmony_ci default: 151d4afb5ceSopenharmony_ci break; 152d4afb5ceSopenharmony_ci } 153d4afb5ceSopenharmony_ci 154d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 155d4afb5ceSopenharmony_ci} 156d4afb5ceSopenharmony_ci 157d4afb5ceSopenharmony_cistatic const struct lws_protocols protocols[] = { 158d4afb5ceSopenharmony_ci { 159d4afb5ceSopenharmony_ci "http", 160d4afb5ceSopenharmony_ci callback_http, 161d4afb5ceSopenharmony_ci 0, 0, 0, NULL, 0 162d4afb5ceSopenharmony_ci }, { 163d4afb5ceSopenharmony_ci "lws-cpd-http", 164d4afb5ceSopenharmony_ci callback_cpd_http, 165d4afb5ceSopenharmony_ci 0, 0, 0, NULL, 0 166d4afb5ceSopenharmony_ci }, 167d4afb5ceSopenharmony_ci LWS_PROTOCOL_LIST_TERM 168d4afb5ceSopenharmony_ci}; 169d4afb5ceSopenharmony_ci 170d4afb5ceSopenharmony_civoid sigint_handler(int sig) 171d4afb5ceSopenharmony_ci{ 172d4afb5ceSopenharmony_ci interrupted = 1; 173d4afb5ceSopenharmony_ci} 174d4afb5ceSopenharmony_ci 175d4afb5ceSopenharmony_ci/* 176d4afb5ceSopenharmony_ci * This triggers our platform implementation of captive portal detection, the 177d4afb5ceSopenharmony_ci * actual test can be whatever you need. 178d4afb5ceSopenharmony_ci * 179d4afb5ceSopenharmony_ci * In this example, we detect it using Android's 180d4afb5ceSopenharmony_ci * 181d4afb5ceSopenharmony_ci * http://connectivitycheck.android.com/generate_204 182d4afb5ceSopenharmony_ci * 183d4afb5ceSopenharmony_ci * and seeing if we get an http 204 back. 184d4afb5ceSopenharmony_ci */ 185d4afb5ceSopenharmony_ci 186d4afb5ceSopenharmony_cistatic int 187d4afb5ceSopenharmony_cicaptive_portal_detect_request(struct lws_context *context) 188d4afb5ceSopenharmony_ci{ 189d4afb5ceSopenharmony_ci struct lws_client_connect_info i; 190d4afb5ceSopenharmony_ci 191d4afb5ceSopenharmony_ci memset(&i, 0, sizeof i); 192d4afb5ceSopenharmony_ci i.context = context; 193d4afb5ceSopenharmony_ci i.port = 80; 194d4afb5ceSopenharmony_ci i.address = "connectivitycheck.android.com"; 195d4afb5ceSopenharmony_ci i.path = "/generate_204"; 196d4afb5ceSopenharmony_ci i.host = i.address; 197d4afb5ceSopenharmony_ci i.origin = i.address; 198d4afb5ceSopenharmony_ci i.method = "GET"; 199d4afb5ceSopenharmony_ci 200d4afb5ceSopenharmony_ci i.protocol = "lws-cpd-http"; 201d4afb5ceSopenharmony_ci 202d4afb5ceSopenharmony_ci return !lws_client_connect_via_info(&i); 203d4afb5ceSopenharmony_ci} 204d4afb5ceSopenharmony_ci 205d4afb5ceSopenharmony_ci 206d4afb5ceSopenharmony_cilws_system_ops_t ops = { 207d4afb5ceSopenharmony_ci .captive_portal_detect_request = captive_portal_detect_request 208d4afb5ceSopenharmony_ci}; 209d4afb5ceSopenharmony_ci 210d4afb5ceSopenharmony_ci 211d4afb5ceSopenharmony_cistatic int 212d4afb5ceSopenharmony_ciapp_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, 213d4afb5ceSopenharmony_ci int current, int target) 214d4afb5ceSopenharmony_ci{ 215d4afb5ceSopenharmony_ci struct lws_context *cx = lws_system_context_from_system_mgr(mgr); 216d4afb5ceSopenharmony_ci 217d4afb5ceSopenharmony_ci switch (target) { 218d4afb5ceSopenharmony_ci case LWS_SYSTATE_CPD_PRE_TIME: 219d4afb5ceSopenharmony_ci if (lws_system_cpd_state_get(cx)) 220d4afb5ceSopenharmony_ci return 0; /* allow it */ 221d4afb5ceSopenharmony_ci 222d4afb5ceSopenharmony_ci lwsl_info("%s: LWS_SYSTATE_CPD_PRE_TIME\n", __func__); 223d4afb5ceSopenharmony_ci lws_system_cpd_start(cx); 224d4afb5ceSopenharmony_ci /* we'll move the state on when we get a result */ 225d4afb5ceSopenharmony_ci return 1; 226d4afb5ceSopenharmony_ci 227d4afb5ceSopenharmony_ci case LWS_SYSTATE_OPERATIONAL: 228d4afb5ceSopenharmony_ci if (current == LWS_SYSTATE_OPERATIONAL) { 229d4afb5ceSopenharmony_ci struct lws_client_connect_info i; 230d4afb5ceSopenharmony_ci 231d4afb5ceSopenharmony_ci lwsl_user("%s: OPERATIONAL, cpd %d\n", __func__, 232d4afb5ceSopenharmony_ci lws_system_cpd_state_get(cx)); 233d4afb5ceSopenharmony_ci 234d4afb5ceSopenharmony_ci /* 235d4afb5ceSopenharmony_ci * When we reach the OPERATIONAL lws_system state, we 236d4afb5ceSopenharmony_ci * can do our main job knowing we have DHCP, ntpclient, 237d4afb5ceSopenharmony_ci * captive portal testing done. 238d4afb5ceSopenharmony_ci */ 239d4afb5ceSopenharmony_ci 240d4afb5ceSopenharmony_ci if (lws_system_cpd_state_get(cx) != LWS_CPD_INTERNET_OK) { 241d4afb5ceSopenharmony_ci lwsl_warn("%s: There's no internet...\n", __func__); 242d4afb5ceSopenharmony_ci interrupted = 1; 243d4afb5ceSopenharmony_ci break; 244d4afb5ceSopenharmony_ci } 245d4afb5ceSopenharmony_ci 246d4afb5ceSopenharmony_ci memset(&i, 0, sizeof i); 247d4afb5ceSopenharmony_ci i.context = context; 248d4afb5ceSopenharmony_ci i.ssl_connection = LCCSCF_USE_SSL; 249d4afb5ceSopenharmony_ci i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | 250d4afb5ceSopenharmony_ci LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; 251d4afb5ceSopenharmony_ci i.port = 443; 252d4afb5ceSopenharmony_ci i.address = "warmcat.com"; 253d4afb5ceSopenharmony_ci i.path = "/"; 254d4afb5ceSopenharmony_ci i.host = i.address; 255d4afb5ceSopenharmony_ci i.origin = i.address; 256d4afb5ceSopenharmony_ci i.method = "GET"; 257d4afb5ceSopenharmony_ci 258d4afb5ceSopenharmony_ci i.protocol = protocols[0].name; 259d4afb5ceSopenharmony_ci 260d4afb5ceSopenharmony_ci lws_client_connect_via_info(&i); 261d4afb5ceSopenharmony_ci break; 262d4afb5ceSopenharmony_ci } 263d4afb5ceSopenharmony_ci default: 264d4afb5ceSopenharmony_ci break; 265d4afb5ceSopenharmony_ci } 266d4afb5ceSopenharmony_ci 267d4afb5ceSopenharmony_ci return 0; 268d4afb5ceSopenharmony_ci} 269d4afb5ceSopenharmony_ci 270d4afb5ceSopenharmony_cistatic lws_state_notify_link_t * const app_notifier_list[] = { 271d4afb5ceSopenharmony_ci &nl, NULL 272d4afb5ceSopenharmony_ci}; 273d4afb5ceSopenharmony_ci 274d4afb5ceSopenharmony_ci/* 275d4afb5ceSopenharmony_ci * We made this into a different thread to model it being run from completely 276d4afb5ceSopenharmony_ci * different codebase that's all linked together 277d4afb5ceSopenharmony_ci */ 278d4afb5ceSopenharmony_ci 279d4afb5ceSopenharmony_ci 280d4afb5ceSopenharmony_ciint main(int argc, const char **argv) 281d4afb5ceSopenharmony_ci{ 282d4afb5ceSopenharmony_ci int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; 283d4afb5ceSopenharmony_ci struct lws_context_creation_info info; 284d4afb5ceSopenharmony_ci const char *p; 285d4afb5ceSopenharmony_ci 286d4afb5ceSopenharmony_ci signal(SIGINT, sigint_handler); 287d4afb5ceSopenharmony_ci 288d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "-d"))) 289d4afb5ceSopenharmony_ci logs = atoi(p); 290d4afb5ceSopenharmony_ci 291d4afb5ceSopenharmony_ci lws_set_log_level(logs, NULL); 292d4afb5ceSopenharmony_ci lwsl_user("LWS minimal http client captive portal detect\n"); 293d4afb5ceSopenharmony_ci 294d4afb5ceSopenharmony_ci memset(&info, 0, sizeof info); 295d4afb5ceSopenharmony_ci info.port = CONTEXT_PORT_NO_LISTEN; 296d4afb5ceSopenharmony_ci info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 297d4afb5ceSopenharmony_ci info.system_ops = &ops; 298d4afb5ceSopenharmony_ci info.protocols = protocols; 299d4afb5ceSopenharmony_ci 300d4afb5ceSopenharmony_ci /* integrate us with lws system state management when context created */ 301d4afb5ceSopenharmony_ci 302d4afb5ceSopenharmony_ci nl.name = "app"; 303d4afb5ceSopenharmony_ci nl.notify_cb = app_system_state_nf; 304d4afb5ceSopenharmony_ci info.register_notifier_list = app_notifier_list; 305d4afb5ceSopenharmony_ci 306d4afb5ceSopenharmony_ci context = lws_create_context(&info); 307d4afb5ceSopenharmony_ci if (!context) { 308d4afb5ceSopenharmony_ci lwsl_err("lws init failed\n"); 309d4afb5ceSopenharmony_ci return 1; 310d4afb5ceSopenharmony_ci } 311d4afb5ceSopenharmony_ci 312d4afb5ceSopenharmony_ci while (!interrupted) 313d4afb5ceSopenharmony_ci if (lws_service(context, 0)) 314d4afb5ceSopenharmony_ci interrupted = 1; 315d4afb5ceSopenharmony_ci 316d4afb5ceSopenharmony_ci lws_context_destroy(context); 317d4afb5ceSopenharmony_ci 318d4afb5ceSopenharmony_ci lwsl_user("%s: finished %s\n", __func__, bad ? "FAIL": "OK"); 319d4afb5ceSopenharmony_ci 320d4afb5ceSopenharmony_ci return bad; 321d4afb5ceSopenharmony_ci} 322