mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
captive portal
Implement Captive Portal detection support in lws, with the actual detection happening in platform code hooked up by lws_system_ops_t. Add an implementation using Secure Streams as well, if the policy defines captive_portal_detect streamtype, a SS using that streamtype is used to probe if it's behind a captive portal.
This commit is contained in:
parent
fdb9b7fdd3
commit
a60cb84c9e
19 changed files with 914 additions and 11 deletions
|
@ -1178,6 +1178,7 @@ if (LWS_WITH_NETWORK)
|
|||
lib/secure-streams/secure-streams.c
|
||||
lib/secure-streams/policy.c
|
||||
lib/secure-streams/system/fetch-policy/fetch-policy.c
|
||||
lib/secure-streams/system/captive-portal-detect/captive-portal-detect.c
|
||||
)
|
||||
if (LWS_ROLE_H1)
|
||||
list(APPEND SOURCES
|
||||
|
@ -1218,6 +1219,7 @@ if (LWS_WITH_NETWORK)
|
|||
lib/secure-streams/system/auth-api.amazon.com/auth.c
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_STATS)
|
||||
|
|
88
READMEs/README.captive-portal-detection.md
Normal file
88
READMEs/README.captive-portal-detection.md
Normal file
|
@ -0,0 +1,88 @@
|
|||
# Captive Portal Detection
|
||||
|
||||
## Background
|
||||
|
||||
Wifi devices may face some interception of their connection to the
|
||||
internet, it's very common for, eg, coffee shop wifi to present some
|
||||
kind of login or other clickthrough before access to the Internet is
|
||||
granted. Devices may need to understand that they are in this
|
||||
situation, and there are several different techniques for trying to
|
||||
gague it.
|
||||
|
||||
Sequence-wise the device has been granted a DHCP lease and has been
|
||||
configured with DNS, but the DNS may be wrongly resolving everything
|
||||
to an address on the LAN or a portal on the net.
|
||||
|
||||
Whether there is a captive portal active should be a sticky state for a given
|
||||
connection if there is not going to be any attempt to login or pass the landing
|
||||
page, it only needs checking for after DHCP acquisition then. If there will be
|
||||
an attempt to satisfy the landing page, the test should be repeated after the
|
||||
attempt.
|
||||
|
||||
## Detection schemes
|
||||
|
||||
The most popular detection scheme by numbers is Android's method,
|
||||
which is to make an HTTP client GET to `http://connectivitycheck.android.com/generate_204`
|
||||
and see if a 204 is coming back... if intercepted, typically there'll be a
|
||||
3xx redirect to the portal, perhaps on https. Or, it may reply on http with
|
||||
a 200 and show the portal directly... either way it won't deliver a 204
|
||||
like the real remote server does.
|
||||
|
||||
Variations include expecting a 200 but with specific http body content, and
|
||||
doing a DNS lookup for a static IP that the device knows; if it's resolved to
|
||||
something else, it knows there's monkey business implying a captive portal.
|
||||
|
||||
Other schemes involve https connections going out and detecting that the cert
|
||||
of the server it's actually talking to doesn't check out, although this is
|
||||
potentially ambiguous.
|
||||
|
||||
Yet more methods are possible outside of tcp or http.
|
||||
|
||||
## lws captive portal detect support
|
||||
|
||||
lws provides a generic api to start captive portal detection...
|
||||
|
||||
```
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
lws_system_cpd_start(struct lws_context *context);
|
||||
```
|
||||
|
||||
and two states in `lws_system` states to trigger it from, either
|
||||
`LWS_SYSTATE_CPD_PRE_TIME` which happens after DHCP acquisition but before
|
||||
ntpclient and is suitable for non https-based scheme where the time doesn't
|
||||
need to be known, or the alternative `LWS_SYSTATE_CPD_POST_TIME` state which
|
||||
happens after ntpclient has completed and we know the time.
|
||||
|
||||
The actual platform implementation is set using `lws_system_ops_t` function
|
||||
pointer `captive_portal_detect_request`, ie
|
||||
|
||||
```
|
||||
int (*captive_portal_detect_request)(struct lws_context *context);
|
||||
/**< Check if we can go out on the internet cleanly, or if we are being
|
||||
* redirected or intercepted by a captive portal.
|
||||
* Start the check that proceeds asynchronously, and report the results
|
||||
* by calling lws_captive_portal_detect_result() api
|
||||
*/
|
||||
```
|
||||
|
||||
User platform code can provide this to implement whatever scheme they want, when
|
||||
it has arrived at a result, it can call the lws api `lws_system_cpd_result()` to
|
||||
inform lws. If there isn't any captive portal, this will also try to advance the
|
||||
system state towards OPERATIONAL.
|
||||
|
||||
```
|
||||
/**
|
||||
* lws_system_cpd_result() - report the result of the captive portal detection
|
||||
*
|
||||
* \param context: the lws_context
|
||||
* \param result: one of the LWS_CPD_ constants representing captive portal state
|
||||
* \param redirect_url: NULL, or the url we were redirected to if result is
|
||||
* LWS_CPD_HTTP_REDIRECT
|
||||
*
|
||||
* Sets the context's captive portal detection state to result. User captive
|
||||
* portal detection code would call this once it had a result from its test.
|
||||
*/
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
lws_system_cpd_result(struct lws_context *context, int result, const char *redirect_url);
|
||||
```
|
||||
|
|
@ -390,6 +390,9 @@ enum lws_callback_reasons {
|
|||
* lws know by calling lws_client_http_body_pending(wsi, 0)
|
||||
*/
|
||||
|
||||
LWS_CALLBACK_CLIENT_HTTP_REDIRECT = 104,
|
||||
/**< we're handling a 3xx redirect... return nonzero to hang up */
|
||||
|
||||
LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL = 85,
|
||||
LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL = 76,
|
||||
|
||||
|
|
|
@ -206,6 +206,9 @@ typedef struct lws_ss_policy {
|
|||
/* false = TEXT, true = BINARY */
|
||||
} ws;
|
||||
} u;
|
||||
|
||||
uint16_t resp_expect;
|
||||
uint8_t fail_redirect:1;
|
||||
} http;
|
||||
|
||||
struct {
|
||||
|
|
|
@ -98,9 +98,24 @@ typedef enum { /* keep system_state_names[] in sync in context.c */
|
|||
* can operate normally */
|
||||
LWS_SYSTATE_IFACE_COLDPLUG, /* existing net ifaces iterated */
|
||||
LWS_SYSTATE_DHCP, /* at least one net iface configured */
|
||||
LWS_SYSTATE_CPD_PRE_TIME, /* Captive portal detect without valid
|
||||
* time, good for non-https tests... if
|
||||
* you care about it, implement and
|
||||
* call lws_system_ops_t
|
||||
* .captive_portal_detect_request()
|
||||
* and move the state forward according
|
||||
* to the result. */
|
||||
LWS_SYSTATE_TIME_VALID, /* ntpclient ran, or hw time valid...
|
||||
* tls cannot work until we reach here
|
||||
*/
|
||||
LWS_SYSTATE_CPD_POST_TIME, /* Captive portal detect after time was
|
||||
* time, good for https tests... if
|
||||
* you care about it, implement and
|
||||
* call lws_system_ops_t
|
||||
* .captive_portal_detect_request()
|
||||
* and move the state forward according
|
||||
* to the result. */
|
||||
|
||||
LWS_SYSTATE_POLICY_VALID, /* user code knows how to operate... */
|
||||
LWS_SYSTATE_REGISTERED, /* device has an identity... */
|
||||
LWS_SYSTATE_AUTH1, /* identity used for main auth token */
|
||||
|
@ -114,6 +129,16 @@ typedef enum { /* keep system_state_names[] in sync in context.c */
|
|||
* LWS_SYSTATE_POLICY_VALID */
|
||||
} lws_system_states_t;
|
||||
|
||||
/* Captive Portal Detect -related */
|
||||
|
||||
typedef enum {
|
||||
LWS_CPD_UNKNOWN = 0, /* test didn't happen ince last DHCP acq yet */
|
||||
LWS_CPD_INTERNET_OK, /* no captive portal: our CPD test passed OK,
|
||||
* we can go out on the internet */
|
||||
LWS_CPD_CAPTIVE_PORTAL, /* we inferred we're behind a captive portal */
|
||||
LWS_CPD_NO_INTERNET, /* we couldn't touch anything */
|
||||
} lws_cpd_result_t;
|
||||
|
||||
|
||||
typedef void (*lws_attach_cb_t)(struct lws_context *context, int tsi, void *opaque);
|
||||
struct lws_attach_item;
|
||||
|
@ -138,6 +163,12 @@ typedef struct lws_system_ops {
|
|||
* __lws_system_attach() is provided to do the actual work inside the
|
||||
* system-specific locking.
|
||||
*/
|
||||
int (*captive_portal_detect_request)(struct lws_context *context);
|
||||
/**< Check if we can go out on the internet cleanly, or if we are being
|
||||
* redirected or intercepted by a captive portal.
|
||||
* Start the check that proceeds asynchronously, and report the results
|
||||
* by calling lws_captive_portal_detect_result() api
|
||||
*/
|
||||
} lws_system_ops_t;
|
||||
|
||||
/**
|
||||
|
@ -231,7 +262,7 @@ typedef int (*dhcpc_cb_t)(void *opaque, int af, uint8_t *ip, int ip_len);
|
|||
* Register a network interface as being managed by DHCP. lws will proceed to
|
||||
* try to acquire an IP. Requires LWS_WITH_SYS_DHCP_CLIENT at cmake.
|
||||
*/
|
||||
int
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
lws_dhcpc_request(struct lws_context *c, const char *i, int af, dhcpc_cb_t cb,
|
||||
void *opaque);
|
||||
|
||||
|
@ -243,7 +274,7 @@ lws_dhcpc_request(struct lws_context *c, const char *i, int af, dhcpc_cb_t cb,
|
|||
*
|
||||
* Remove handling of the network interface from dhcp.
|
||||
*/
|
||||
int
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
lws_dhcpc_remove(struct lws_context *context, const char *iface);
|
||||
|
||||
/**
|
||||
|
@ -255,5 +286,42 @@ lws_dhcpc_remove(struct lws_context *context, const char *iface);
|
|||
* Returns 1 if any network interface managed by dhcpc has reached the BOUND
|
||||
* state (has acquired an IP, gateway and DNS server), otherwise 0.
|
||||
*/
|
||||
int
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46);
|
||||
|
||||
/**
|
||||
* lws_system_cpd_start() - helper to initiate captive portal detection
|
||||
*
|
||||
* \param context: the lws_context
|
||||
*
|
||||
* Resets the context's captive portal state to LWS_CPD_UNKNOWN and calls the
|
||||
* lws_system_ops_t captive_portal_detect_request() implementation to begin
|
||||
* testing the captive portal state.
|
||||
*/
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
lws_system_cpd_start(struct lws_context *context);
|
||||
|
||||
|
||||
/**
|
||||
* lws_system_cpd_set() - report the result of the captive portal detection
|
||||
*
|
||||
* \param context: the lws_context
|
||||
* \param result: one of the LWS_CPD_ constants representing captive portal state
|
||||
*
|
||||
* Sets the context's captive portal detection state to result. User captive
|
||||
* portal detection code would call this once it had a result from its test.
|
||||
*/
|
||||
LWS_EXTERN LWS_VISIBLE void
|
||||
lws_system_cpd_set(struct lws_context *context, lws_cpd_result_t result);
|
||||
|
||||
|
||||
/**
|
||||
* lws_system_cpd_state_get() - returns the last tested captive portal state
|
||||
*
|
||||
* \param context: the lws_context
|
||||
*
|
||||
* Returns one of the LWS_CPD_ constants indicating the system's understanding
|
||||
* of the current captive portal situation.
|
||||
*/
|
||||
LWS_EXTERN LWS_VISIBLE lws_cpd_result_t
|
||||
lws_system_cpd_state_get(struct lws_context *context);
|
||||
|
|
|
@ -119,6 +119,9 @@ lws_state_transition_steps(lws_state_manager_t *mgr, int target)
|
|||
char temp8[8];
|
||||
#endif
|
||||
|
||||
if (mgr->state > target)
|
||||
return 0;
|
||||
|
||||
while (!n && mgr->state != target)
|
||||
n = _lws_state_transition(mgr, mgr->state + 1);
|
||||
|
||||
|
|
|
@ -82,7 +82,9 @@ static const char * system_state_names[] = {
|
|||
"INITIALIZED",
|
||||
"IFACE_COLDPLUG",
|
||||
"DHCP",
|
||||
"CPD_PRE_TIME",
|
||||
"TIME_VALID",
|
||||
"CPD_POST_TIME",
|
||||
"POLICY_VALID",
|
||||
"REGISTERED",
|
||||
"AUTH1",
|
||||
|
@ -145,6 +147,20 @@ lws_state_notify_protocol_init(struct lws_state_manager *mgr,
|
|||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
/*
|
||||
* See if we should do the SS Captive Portal Detection
|
||||
*/
|
||||
if (target == LWS_SYSTATE_CPD_PRE_TIME) {
|
||||
if (lws_system_cpd_state_get(context))
|
||||
return 0; /* allow it */
|
||||
|
||||
lwsl_info("%s: LWS_SYSTATE_CPD_PRE_TIME\n", __func__);
|
||||
if (!lws_system_cpd_start(context))
|
||||
return 1;
|
||||
|
||||
/* it failed, eg, no streamtype for it in the policy */
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip this if we are running something without the policy for it
|
||||
*/
|
||||
|
@ -896,10 +912,56 @@ fail_event_libs:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
int
|
||||
lws_context_is_deprecated(struct lws_context *context)
|
||||
lws_system_cpd_start(struct lws_context *cx)
|
||||
{
|
||||
return context->deprecated;
|
||||
cx->captive_portal_detect = LWS_CPD_UNKNOWN;
|
||||
|
||||
/* if there's a platform implementation, use it */
|
||||
|
||||
if (lws_system_get_ops(cx) &&
|
||||
lws_system_get_ops(cx)->captive_portal_detect_request)
|
||||
return lws_system_get_ops(cx)->captive_portal_detect_request(cx);
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
/*
|
||||
* Otherwise try to use SS "captive_portal_detect" if that's enabled
|
||||
*/
|
||||
return lws_ss_sys_cpd(cx);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *cname[] = { "?", "OK", "Captive", "No internet" };
|
||||
|
||||
void
|
||||
lws_system_cpd_set(struct lws_context *cx, lws_cpd_result_t result)
|
||||
{
|
||||
if (cx->captive_portal_detect != LWS_CPD_UNKNOWN)
|
||||
return;
|
||||
|
||||
lwsl_notice("%s: setting CPD result %s\n", __func__, cname[result]);
|
||||
|
||||
cx->captive_portal_detect = (uint8_t)result;
|
||||
|
||||
/* if nothing is there to intercept anything, go all the way */
|
||||
lws_state_transition_steps(&cx->mgr_system, LWS_SYSTATE_OPERATIONAL);
|
||||
}
|
||||
|
||||
lws_cpd_result_t
|
||||
lws_system_cpd_state_get(struct lws_context *cx)
|
||||
{
|
||||
return (lws_cpd_result_t)cx->captive_portal_detect;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_context_is_deprecated(struct lws_context *cx)
|
||||
{
|
||||
return cx->deprecated;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -523,6 +523,8 @@ struct lws_context {
|
|||
uint8_t max_fi;
|
||||
uint8_t udp_loss_sim_tx_pc;
|
||||
uint8_t udp_loss_sim_rx_pc;
|
||||
uint8_t captive_portal_detect;
|
||||
uint8_t captive_portal_detect_type;
|
||||
|
||||
#if defined(LWS_WITH_STATS)
|
||||
uint8_t updated;
|
||||
|
|
|
@ -678,6 +678,15 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
goto bail3;
|
||||
}
|
||||
|
||||
/* let's let the user code know, if he cares */
|
||||
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_HTTP_REDIRECT,
|
||||
wsi->user_space, p, n)) {
|
||||
cce = "HS: user code rejected redirect";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some redirect codes imply we have to change the method
|
||||
* used for the subsequent transaction, commonly POST ->
|
||||
|
|
|
@ -177,6 +177,23 @@ to validate the remote server cert.
|
|||
HTTP method to use with http-related protocols, like GET or POST.
|
||||
Not required for ws.
|
||||
|
||||
### `http_expect`
|
||||
|
||||
Optionally indicates that success for HTTP transactions using this
|
||||
streamtype is different than the default 200 - 299.
|
||||
|
||||
Eg, you may choose to set this to 204 for Captive Portal Detect usage
|
||||
if that's what you expect the server to reply with to indicate
|
||||
success. In that case, anything other than 204 will be treated as a
|
||||
connection failure.
|
||||
|
||||
### `http_fail_redirect`
|
||||
|
||||
Set to `true` if you want to fail the connection on meeting an
|
||||
http redirect. This is needed to, eg, detect Captive Portals
|
||||
correctly. Normally, if on https, you would want the default behaviour
|
||||
of following the redirect.
|
||||
|
||||
### `http_url`
|
||||
|
||||
Url path to use with http-related protocols
|
||||
|
@ -352,6 +369,26 @@ The secure-streams-proxy minimal example shows how this is done and
|
|||
fetches its real policy from warmcat.com at startup using the built-in
|
||||
one.
|
||||
|
||||
## Captive Portal Detection
|
||||
|
||||
If the policy contains a streamtype `captive_portal_detect` then the
|
||||
type of transaction described there is automatically performed after
|
||||
acquiring a DHCP address to try to determine the captive portal
|
||||
situation.
|
||||
|
||||
```
|
||||
"captive_portal_detect": {
|
||||
"endpoint": "connectivitycheck.android.com",
|
||||
"port": 80,
|
||||
"protocol": "h1",
|
||||
"http_method": "GET",
|
||||
"http_url": "generate_204",
|
||||
"opportunistic": true,
|
||||
"http_expect": 204,
|
||||
"http_fail_redirect": true
|
||||
}
|
||||
```
|
||||
|
||||
## Stream serialization and proxying
|
||||
|
||||
By default Secure Streams expects to make the outgoing connection described in
|
||||
|
|
|
@ -79,6 +79,8 @@ static const char * const lejp_tokens_policy[] = {
|
|||
"s[].*.http_multipart_filename",
|
||||
"s[].*.http_mime_content_type",
|
||||
"s[].*.http_www_form_urlencoded",
|
||||
"s[].*.http_expect",
|
||||
"s[].*.http_fail_redirect",
|
||||
"s[].*.ws_subprotocol",
|
||||
"s[].*.ws_binary",
|
||||
"s[].*.local_sink",
|
||||
|
@ -142,6 +144,8 @@ typedef enum {
|
|||
LSSPPT_HTTP_MULTIPART_FILENAME,
|
||||
LSSPPT_HTTP_MULTIPART_CONTENT_TYPE,
|
||||
LSSPPT_HTTP_WWW_FORM_URLENCODED,
|
||||
LSSPPT_HTTP_EXPECT,
|
||||
LSSPPT_HTTP_FAIL_REDIRECT,
|
||||
LSSPPT_WS_SUBPROTOCOL,
|
||||
LSSPPT_WS_BINARY,
|
||||
LSSPPT_LOCAL_SINK,
|
||||
|
@ -524,6 +528,10 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
|
|||
a->curr[LTY_POLICY].p->client_cert = atoi(ctx->buf) + 1;
|
||||
break;
|
||||
|
||||
case LSSPPT_HTTP_EXPECT:
|
||||
a->curr[LTY_POLICY].p->u.http.resp_expect = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_OPPORTUNISTIC:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_OPPORTUNISTIC;
|
||||
|
@ -644,6 +652,12 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
|
|||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_HTTP_MULTIPART;
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_content_type;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_HTTP_FAIL_REDIRECT:
|
||||
a->curr[LTY_POLICY].p->u.http.fail_redirect =
|
||||
reason == LEJPCB_VAL_TRUE;
|
||||
break;
|
||||
|
||||
case LSSPPT_WS_SUBPROTOCOL:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.u.ws.subprotocol;
|
||||
goto string2;
|
||||
|
|
|
@ -318,6 +318,9 @@ int
|
|||
lws_ss_exp_cb_metadata(void *priv, const char *name, char *out, size_t *pos,
|
||||
size_t olen, size_t *exp_ofs);
|
||||
|
||||
int
|
||||
lws_ss_sys_cpd(struct lws_context *cx);
|
||||
|
||||
typedef int (* const secstream_protocol_connect_munge_t)(lws_ss_handle_t *h,
|
||||
char *buf, size_t len, struct lws_client_connect_info *i,
|
||||
union lws_ss_contemp *ct);
|
||||
|
|
|
@ -167,7 +167,6 @@ secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
|
||||
switch (reason) {
|
||||
|
||||
/* because we are protocols[0] ... */
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
assert(h);
|
||||
assert(h->policy);
|
||||
|
@ -178,6 +177,13 @@ secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
lws_ss_backoff(h);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_HTTP_REDIRECT:
|
||||
if (h->policy->u.http.fail_redirect)
|
||||
lws_system_cpd_set(lws_get_context(wsi),
|
||||
LWS_CPD_CAPTIVE_PORTAL);
|
||||
/* don't follow it */
|
||||
return 1;
|
||||
|
||||
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
|
||||
if (!h)
|
||||
break;
|
||||
|
@ -201,7 +207,12 @@ secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
// if (!status)
|
||||
/* it's just telling use we connected / joined the nwsi */
|
||||
// break;
|
||||
h->u.http.good_respcode = (status >= 200 && status < 300);
|
||||
|
||||
if (h->policy->u.http.resp_expect)
|
||||
h->u.http.good_respcode =
|
||||
status == h->policy->u.http.resp_expect;
|
||||
else
|
||||
h->u.http.good_respcode = (status >= 200 && status < 300);
|
||||
// lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode);
|
||||
|
||||
if (h->u.http.good_respcode)
|
||||
|
|
|
@ -302,8 +302,8 @@ lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
|
|||
|
||||
pol = lws_ss_policy_lookup(context, ssi->streamtype);
|
||||
if (!pol) {
|
||||
lwsl_err("%s: unknown stream type %s\n", __func__,
|
||||
ssi->streamtype);
|
||||
lwsl_notice("%s: unknown stream type %s\n", __func__,
|
||||
ssi->streamtype);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Captive portal detect for Secure Streams
|
||||
*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef struct ss_cpd {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
|
||||
lws_sorted_usec_list_t sul;
|
||||
|
||||
uint8_t partway;
|
||||
} ss_cpd_t;
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
ss_cpd_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_cpd_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_cpd_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_cpd_t *m = (ss_cpd_t *)userobj;
|
||||
struct lws_context *cx = (struct lws_context *)m->opaque_data;
|
||||
|
||||
lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lws_ss_request_tx(m->ss);
|
||||
break;
|
||||
case LWSSSCS_QOS_ACK_REMOTE:
|
||||
lws_system_cpd_set(cx, LWS_CPD_INTERNET_OK);
|
||||
break;
|
||||
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
/*
|
||||
* First result reported sticks... if nothing else, this will
|
||||
* cover the situation we didn't connect to anything
|
||||
*/
|
||||
lws_system_cpd_set(cx, LWS_CPD_NO_INTERNET);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_sys_cpd(struct lws_context *cx)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_cpd_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_cpd_t, opaque_data);
|
||||
ssi.rx = ss_cpd_rx;
|
||||
ssi.tx = ss_cpd_tx;
|
||||
ssi.state = ss_cpd_state;
|
||||
ssi.user_alloc = sizeof(ss_cpd_t);
|
||||
ssi.streamtype = "captive_portal_detect";
|
||||
|
||||
if (lws_ss_create(cx, 0, &ssi, cx, NULL, NULL, NULL)) {
|
||||
lwsl_info("%s: Create stream failed (policy?)\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
project(lws-minimal-http-client-captive-portal)
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckIncludeFile)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-http-client-captive-portal)
|
||||
set(SRCS minimal-http-client-captive-portal.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
set(requirements 1)
|
||||
if (WIN32)
|
||||
set(requirements 0)
|
||||
endif()
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITH_CLIENT 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared pthread)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets pthread)
|
||||
endif()
|
||||
endif()
|
|
@ -0,0 +1,45 @@
|
|||
# lws minimal http client captive portal detect
|
||||
|
||||
This demonstrates how to perform captive portal detection integrated
|
||||
with `lws_system` states.
|
||||
|
||||
After reaching the `lws_system` DHCP state, the application tries to
|
||||
connect through to `http://connectivitycheck.android.com/generate_204`
|
||||
over http... if it succeeds, it will get a 204 response and set the
|
||||
captive portal detection state to `LWS_CPD_INTERNET_OK` and perform
|
||||
a GET from warmcat.com.
|
||||
|
||||
If there is a problem detected, the captive portal detection state is
|
||||
set accordingly and the app will respond by exiting without trying the
|
||||
read from warmcat.com.
|
||||
|
||||
The captive portal detection scheme is implemented in the user code
|
||||
and can be modified according to the strategy that's desired for
|
||||
captive portal detection.
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
```
|
||||
$ ./bin/lws-minimal-http-client-captive-portal
|
||||
[2020/03/11 13:07:07:4519] U: LWS minimal http client captive portal detect
|
||||
[2020/03/11 13:07:07:4519] N: lws_create_context: using ss proxy bind '(null)', port 0, ads '(null)'
|
||||
[2020/03/11 13:07:07:5022] U: callback_cpd_http: established with resp 204
|
||||
[2020/03/11 13:07:07:5023] U: app_system_state_nf: OPERATIONAL, cpd 1
|
||||
[2020/03/11 13:07:07:5896] U: Connected to 46.105.127.147, http response: 200
|
||||
[2020/03/11 13:07:07:5931] U: RECEIVE_CLIENT_HTTP_READ: read 4087
|
||||
[2020/03/11 13:07:07:5931] U: RECEIVE_CLIENT_HTTP_READ: read 4096
|
||||
[2020/03/11 13:07:07:6092] U: RECEIVE_CLIENT_HTTP_READ: read 4087
|
||||
[2020/03/11 13:07:07:6092] U: RECEIVE_CLIENT_HTTP_READ: read 4096
|
||||
[2020/03/11 13:07:07:6112] U: RECEIVE_CLIENT_HTTP_READ: read 4087
|
||||
[2020/03/11 13:07:07:6113] U: RECEIVE_CLIENT_HTTP_READ: read 4096
|
||||
[2020/03/11 13:07:07:6113] U: RECEIVE_CLIENT_HTTP_READ: read 2657
|
||||
[2020/03/11 13:07:07:6113] U: LWS_CALLBACK_COMPLETED_CLIENT_HTTP
|
||||
[2020/03/11 13:07:07:6119] U: main: finished OK
|
||||
```
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* lws-minimal-http-client-captive-portal
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This demonstrates how to use the lws_system captive portal detect integration
|
||||
*
|
||||
* We check for a captive portal by doing a GET from
|
||||
* http://connectivitycheck.android.com/generate_204, if we really are going
|
||||
* out on the Internet he'll return with a 204 response code and we will
|
||||
* understand there's no captive portal. If we get something else, we take it
|
||||
* there is a captive portal.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static struct lws_context *context;
|
||||
static int interrupted, bad = 1, status;
|
||||
static lws_state_notify_link_t nl;
|
||||
|
||||
/*
|
||||
* this is the user code http handler
|
||||
*/
|
||||
|
||||
static int
|
||||
callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
switch (reason) {
|
||||
|
||||
/* because we are protocols[0] ... */
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
|
||||
in ? (char *)in : "(null)");
|
||||
interrupted = 1;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
lws_get_peer_simple(wsi, buf, sizeof(buf));
|
||||
status = lws_http_client_http_response(wsi);
|
||||
|
||||
lwsl_user("Connected to %s, http response: %d\n",
|
||||
buf, status);
|
||||
}
|
||||
break;
|
||||
|
||||
/* chunks of chunked content, with header removed */
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
|
||||
lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
|
||||
|
||||
#if 0 /* enable to dump the html */
|
||||
{
|
||||
const char *p = in;
|
||||
|
||||
while (len--)
|
||||
if (*p < 0x7f)
|
||||
putchar(*p++);
|
||||
else
|
||||
putchar('.');
|
||||
}
|
||||
#endif
|
||||
return 0; /* don't passthru */
|
||||
|
||||
/* uninterpreted http content */
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
|
||||
{
|
||||
char buffer[1024 + LWS_PRE];
|
||||
char *px = buffer + LWS_PRE;
|
||||
int lenx = sizeof(buffer) - LWS_PRE;
|
||||
|
||||
if (lws_http_client_read(wsi, &px, &lenx) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0; /* don't passthru */
|
||||
|
||||
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
||||
lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
|
||||
interrupted = 1;
|
||||
bad = status != 200;
|
||||
lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
|
||||
interrupted = 1;
|
||||
bad = status != 200;
|
||||
lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the platform's custom captive portal detection handler
|
||||
*/
|
||||
|
||||
static int
|
||||
callback_cpd_http(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
int resp;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
||||
resp = lws_http_client_http_response(wsi);
|
||||
if (!resp)
|
||||
break;
|
||||
lwsl_user("%s: established with resp %d\n", __func__, resp);
|
||||
switch (resp) {
|
||||
|
||||
case HTTP_STATUS_NO_CONTENT:
|
||||
/*
|
||||
* We got the 204 which is used to distinguish the real
|
||||
* endpoint
|
||||
*/
|
||||
lws_system_cpd_set(lws_get_context(wsi),
|
||||
LWS_CPD_INTERNET_OK);
|
||||
return 0;
|
||||
|
||||
/* also case HTTP_STATUS_OK: ... */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWS_CALLBACK_CLIENT_HTTP_REDIRECT:
|
||||
lws_system_cpd_set(lws_get_context(wsi), LWS_CPD_CAPTIVE_PORTAL);
|
||||
/* don't follow it, just report it */
|
||||
return 1;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
|
||||
/* only the first result counts */
|
||||
lws_system_cpd_set(lws_get_context(wsi), LWS_CPD_NO_INTERNET);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
}
|
||||
|
||||
static const struct lws_protocols protocols[] = {
|
||||
{
|
||||
"http",
|
||||
callback_http,
|
||||
0,
|
||||
0,
|
||||
}, {
|
||||
"lws-cpd-http",
|
||||
callback_cpd_http
|
||||
},
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
void sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This triggers our platform implementation of captive portal detection, the
|
||||
* actual test can be whatever you need.
|
||||
*
|
||||
* In this example, we detect it using Android's
|
||||
*
|
||||
* http://connectivitycheck.android.com/generate_204
|
||||
*
|
||||
* and seeing if we get an http 204 back.
|
||||
*/
|
||||
|
||||
static int
|
||||
captive_portal_detect_request(struct lws_context *context)
|
||||
{
|
||||
struct lws_client_connect_info i;
|
||||
|
||||
memset(&i, 0, sizeof i);
|
||||
i.context = context;
|
||||
i.port = 80;
|
||||
i.address = "connectivitycheck.android.com";
|
||||
i.path = "/generate_204";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.method = "GET";
|
||||
|
||||
i.protocol = "lws-cpd-http";
|
||||
|
||||
return !lws_client_connect_via_info(&i);
|
||||
}
|
||||
|
||||
|
||||
lws_system_ops_t ops = {
|
||||
.captive_portal_detect_request = captive_portal_detect_request
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
|
||||
int current, int target)
|
||||
{
|
||||
struct lws_context *cx = lws_system_context_from_system_mgr(mgr);
|
||||
|
||||
switch (target) {
|
||||
case LWS_SYSTATE_CPD_PRE_TIME:
|
||||
if (lws_system_cpd_state_get(cx))
|
||||
return 0; /* allow it */
|
||||
|
||||
lwsl_info("%s: LWS_SYSTATE_CPD_PRE_TIME\n", __func__);
|
||||
lws_system_cpd_start(cx);
|
||||
/* we'll move the state on when we get a result */
|
||||
return 1;
|
||||
|
||||
case LWS_SYSTATE_OPERATIONAL:
|
||||
if (current == LWS_SYSTATE_OPERATIONAL) {
|
||||
struct lws_client_connect_info i;
|
||||
|
||||
lwsl_user("%s: OPERATIONAL, cpd %d\n", __func__,
|
||||
lws_system_cpd_state_get(cx));
|
||||
|
||||
/*
|
||||
* When we reach the OPERATIONAL lws_system state, we
|
||||
* can do our main job knowing we have DHCP, ntpclient,
|
||||
* captive portal testing done.
|
||||
*/
|
||||
|
||||
if (lws_system_cpd_state_get(cx) != LWS_CPD_INTERNET_OK) {
|
||||
lwsl_warn("%s: There's no internet...\n", __func__);
|
||||
interrupted = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&i, 0, sizeof i);
|
||||
i.context = context;
|
||||
i.ssl_connection = LCCSCF_USE_SSL;
|
||||
i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR |
|
||||
LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM;
|
||||
i.port = 443;
|
||||
i.address = "warmcat.com";
|
||||
i.path = "/";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.method = "GET";
|
||||
|
||||
i.protocol = protocols[0].name;
|
||||
|
||||
lws_client_connect_via_info(&i);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lws_state_notify_link_t * const app_notifier_list[] = {
|
||||
&nl, NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* We made this into a different thread to model it being run from completely
|
||||
* different codebase that's all linked together
|
||||
*/
|
||||
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
const char *p;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS minimal http client captive portal detect\n");
|
||||
|
||||
memset(&info, 0, sizeof info);
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.system_ops = &ops;
|
||||
info.protocols = protocols;
|
||||
|
||||
/* integrate us with lws system state management when context created */
|
||||
|
||||
nl.name = "app";
|
||||
nl.notify_cb = app_system_state_nf;
|
||||
info.register_notifier_list = app_notifier_list;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!interrupted)
|
||||
if (lws_service(context, 0))
|
||||
interrupted = 1;
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
lwsl_user("%s: finished %s\n", __func__, bad ? "FAIL": "OK");
|
||||
|
||||
return bad;
|
||||
}
|
|
@ -154,6 +154,11 @@ static const char * const default_ss_policy =
|
|||
"}"
|
||||
"],"
|
||||
"\"s\": ["
|
||||
/*
|
||||
* "fetch_policy" decides from where the real policy
|
||||
* will be fetched, if present. Otherwise the initial
|
||||
* policy is treated as the whole, hardcoded, policy.
|
||||
*/
|
||||
"{\"fetch_policy\": {"
|
||||
"\"endpoint\":" "\"warmcat.com\","
|
||||
"\"port\":" "443,"
|
||||
|
@ -168,8 +173,42 @@ static const char * const default_ss_policy =
|
|||
"\"opportunistic\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"le_via_isrg\""
|
||||
"}}"
|
||||
"}"
|
||||
"}},{"
|
||||
/*
|
||||
* "captive_portal_detect" describes
|
||||
* what to do in order to check if the path to
|
||||
* the Internet is being interrupted by a
|
||||
* captive portal. If there's a larger policy
|
||||
* fetched from elsewhere, it should also include
|
||||
* this since it needs to be done at least after
|
||||
* every DHCP acquisition
|
||||
*/
|
||||
"\"captive_portal_detect\": {"
|
||||
#if 1
|
||||
/* this does the actual test */
|
||||
"\"endpoint\": \"connectivitycheck.android.com\","
|
||||
"\"http_url\": \"generate_204\","
|
||||
"\"port\": 80,"
|
||||
#endif
|
||||
#if 0
|
||||
/* this looks like a captive portal due to redirect */
|
||||
"\"endpoint\": \"google.com\","
|
||||
"\"http_url\": \"/\","
|
||||
"\"port\": 80,"
|
||||
#endif
|
||||
#if 0
|
||||
/* this looks like no internet */
|
||||
"\"endpoint\": \"warmcat.com\","
|
||||
"\"http_url\": \"/\","
|
||||
"\"port\": 999,"
|
||||
#endif
|
||||
"\"protocol\": \"h1\","
|
||||
"\"http_method\": \"GET\","
|
||||
"\"opportunistic\": true,"
|
||||
"\"http_expect\": 204,"
|
||||
"\"http_fail_redirect\": true"
|
||||
"}}"
|
||||
"]}"
|
||||
;
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue