1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

Fault injection

add lws_xos: xoshiro256 PRNG
This commit is contained in:
Andy Green 2021-03-16 13:32:05 +00:00
parent 33f5bf2e2d
commit 51490ae6e6
53 changed files with 1543 additions and 842 deletions

View file

@ -117,6 +117,10 @@
"cmake": "-DLWS_WITH_UDP=0",
"platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, freertos-linkit/arm32-m4-mt7697-usi/gcc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, w10/x86_64-amd/mingw32, w10/x86_64-amd/mingw64, netbsd/aarch64BE-bcm2837-a53/gcc, w10/x86_64-amd/wmbedtlsmsvc"
},
"fault-injection": {
"cmake": "-DLWS_WITH_SYS_FAULT_INJECTION=1 -DLWS_WITH_MINIMAL_EXAMPLES=1",
"platforms": "w10/x86_64-amd/msvc"
},
"esp32-heltec": {
"cmake": "-DLWS_IPV6=0",
"cpack": "esp-heltec-wb32",

View file

@ -1,40 +1,116 @@
# `lws_fi` Fault Injection
To provide better quality there's a need to not just test the code paths for
normal operation, but also that it acts correctly under various fault
conditions that may be difficult to arrange at test-time.
Most efforts during development go towards trying to make the system do what
it is supposed to do during normal operation.
Code handling the failures may be anywhere including during early initialization
or in user code before lws intialization.
But to provide reliable quality there's a need to not just test the code paths
for normal operation, but also to be able to easily confirm that they act
correctly under various fault conditions that may be difficult to arrange at
test-time. It's otherwise very easy for error conditions that are low
probability to be overlooked and turn out to do the wrong thing, eg, try to
clean up things they had not actually initialized, or forget to free things etc.
To help with this lws has `LWS_WITH_SYS_FAULT_INJECTION` build option that
provides a simple but powerful api for fault injection in any lws or user code.
Code handling the operational failures we want to check may be anywhere,
including during early initialization or in user code before lws intialization.
To help with this lws has a `LWS_WITH_SYS_FAULT_INJECTION` build option that
provides a simple but powerful api for targeted fault injection in any lws or
user code, and provides a wide range of well-known internal faults inside lws
you can trigger from outside.
## Fault contexts and faults
`lws_fi_t` objects represent a named fault injection rules, just in terms of
whether and how often to inject the fault.
The basic idea is objects in the user code can choose to initialize "fault
contexts" inside objects, that list named, well-known "faults" that the code
supoorts and that the user wants to inject.
`lws_fi_ctx_t` objects are linked-lists of `lws_fi_t` objects. When Fault
Injection is enabled at build-time, the key system objects like the
`lws_context`, `lws_vhost`, `wsi` and Secure Stream handles / SSPC handles
contain their own `lws_fi_ctx_t` lists that may have any number of `lws_fi_t`
added to them.
Although these "fault contexts" can be embedded in objects directly at object
creation time, eg, for lws in the lws_context creation info struct, or the
client connection info struct, or Secure Stream info struct, it's usually
inconvenient to pass the desired faults directly deep into the code and attach
them at creation time. Eg, if you want to cause a fault in a wsi instantiated
by a Secure Stream, that is internal lws code one step removed from the Secure
Stream object creation making it difficult to arrange.
`lws_fi_ctx_t` objects are hierarchical, if a named rule is not found in, eg,
a wsi Fault injection context, then the vhost and finally the lws_context Fault
Injection contexts are searched for it before giving up. This allows for both
global and individual overridden Fault Injection rules at each level.
For that reason, faults have a targeted inheritance scheme using namespace
paths, it's usually enough to just list the faults you want at context creation
time and they will be filter down to the internal objects you want to target
when they are created later.
## Integrating fault injection conditionals into code
![Fault Injection Overview](../doc-assets/fault-injection.png)
A simple api `lws_fi(fi_ctx, "name")` is provided that returns 0 if no fault to
be injected, or 1 if the fault should be synthesized. If there is no rule
matching "name", the answer is always to not inject a fault, ie, returns 0.
A fault injection request is made in `lws_fi_t` objects, specifying the
fault name and whether, and how often to inject the fault.
The "fault context" objects `lws_fi_ctx_t` embedded in the creation info
structs are linked-lists of `lws_fi_t` objects. When Fault Injection is enabled
at build-time, the key system objects like the `lws_context`, `lws_vhost`, `wsi`
and Secure Stream handles / SSPC handles contain their own `lws_fi_ctx_t` lists
that may have any number of `lws_fi_t` added to them.
When downstream objects are created, eg, when an lws_context creates a Secure
Stream, in addition to using any faults provided directly in the SS info,
the lws_context faults are consulted to see if any relate to that streamtype
and should be applied.
Although faults can be added to objects at creation, it is far more convenient
to just pass a list of faults you want into the lws_context and have the
objects later match them using namespacing, described later.
## Integrating fault injection conditionals into code in private lws code
A simple query api `lws_fi(fi_ctx, "name")` is provided that returns 0 if no
fault to be injected, or 1 if the fault should be synthesized. If there is no
rule matching "name", the answer is always to not inject a fault, ie, returns 0.
Similarly for convenience if FAULT_INJECTION is disabled at build, the `lws_fi()`
call always returns the constant `0`.
By default then just enabling Fault Injection at build does not have any impact
on code operation since the user must first add the fault injection rules he
wants.
on code operation since the user must also add the fault injection rules he
wants to the objects's Fault Injection context.
## Integrating fault injection conditionals into user code with public apis
These public apis query the fault context in a wsi, lws_context, ss handle, or
sspc handle (client side of proxy) to find any matching rule, if so they return
1 if the conditions (eg, probability) are met and the fault should be injected.
These allow user code to use the whole Fault Injection system without having to
understand anything except the common object like a wsi they want to query and
the name of the fault rule they are checking.
|FI context owner|Public API|
|---|---|
|lws_context|`int lws_fi_user_context_fi(struct lws_context *ctx, const char *rule)`|
|wsi|`int lws_fi_user_wsi_fi(struct lws *wsi, const char *rule)`|
|ss handle|`int lws_fi_user_ss_fi(struct lws_ss_handle *h, const char *rule)`|
|sspc handle|`int lws_fi_user_sspc_fi(struct lws_sspc_handle *h, const char *rule)`|
For example, the minimal-http-client user code example contains this in its
ESTABLISHED callback
```
if (lws_fi_user_wsi_fi(wsi, "user_reject_at_est"))
return -1;
```
which can be triggered by running it with
`lws-minimal-http-client --fault-injection 'wsi/user_reject_at_est'`, causing
```
...
[2021/03/11 13:41:05:2769] U: Connected to 46.105.127.147, http response: 200
[2021/03/11 13:41:05:2776] W: lws_fi: Injecting fault unk->user_reject_at_est
[2021/03/11 13:41:05:2789] E: CLIENT_CONNECTION_ERROR: HS: disallowed at ESTABLISHED
...
```
When `LWS_WITH_SYS_FAULT_INJECTION` is disabled, these public apis become
preprocessor defines to `(0)`, so the related code is removed by the compiler.
## Types of fault injection "when" strategy
The api keeps track of each time the context was asked and uses this information
to drive the decision about when to say yes, according to the type of rule
@ -44,49 +120,215 @@ to drive the decision about when to say yes, according to the type of rule
|`LWSFI_ALWAYS`|Unconditionally inject the fault|
|`LWSFI_DETERMINISTIC`|after `pre` times without the fault, the next `count` times exhibit the fault`|
|`LWSFI_PROBABILISTIC`|exhibit a fault `pre` percentage of the time|
|`LWSFI_PATTERN`|Reference `pre` bits pointed to by `pattern` and fault if the bit set|
|`LWSFI_PATTERN`|Reference `pre` bits pointed to by `pattern` and fault if the bit set, pointing to static array|
|`LWSFI_PATTERN_ALLOC`|Reference `pre` bits pointed to by `pattern` and fault if the bit set, pointing to allocated array, freed when fault goes out of scope|
Probabalistic choices are sourced from a PRNG with a seed set in the context
creation info Fault Injection Context. By default the lws helper
`lws_cmdline_option_handle_builtin()` sets this to the time in us, but it can
be overridden using `--fault-seed <decimal>`, and the effective PRNG seed is
logged when the commandline options are initially parsed.
## Addings Fault Injection Rules to `lws_fi_ctx_t`
User code should prepare a `lws_fi_ctx_t` cleared down to zero if necessary,
and one of these, eg on the stack
Typically the lws_context is used as the central, toplevel place to define
faults. This is done by adding prepared `lws_fi_t` objects on the stack one by
one to the context creation info struct's `.fic` member, using
`lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi);`, this will allocate and copy
the provided `fi` into the allocation, and attach it to the `lws_fi_ctx_t` list.
```
typedef struct lws_fi {
const char *name;
uint8_t *pattern;
uint64_t pre__prob1;
uint64_t count__prob2;
char type; /* LWSFI_* */
} lws_fi_t;
```
and call `lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi);`, this will
allocate and copy the provided `fi` into the allocation, and attach it to
the `lws_fi_ctx_t` list.
The creation info struct associated with the context, vhost, wsi or Secure
Stream has a `*fi` pointer you can set to your `lws_fi_ctx_t`, when creating
the object it will take ownership of any `lws_fi_t` you attached to it.
So the `lws_fi_ctx_t` and the `lws_fi_t` used as a template for adding the
rules may be on the stack and safely and go out of scope after the object
creation api is called. The `lws_fi_t` `name` is also copied into the
allocation and does not need to continue to exist after it is added to the
`lws_fi_ctx_t`. The only exception is the `pattern` member if used, the
array pointed to is not copied and must exist for the lifetime of the rule.
When the context (or other object using the same scheme) is created, it imports
all the faults from the info structure `.fic` and takes ownership of them,
leaving the info `.fic` empty and ready to go out of scope.
## Passing in fault injection rules
A key requirement is that Fault Injection rules must be availble to the code
creating an object before the object has been created. This is why the user
code prepares a temporary context listing his rules, and offers it as part of
the creation info struct, rather than waiting for the object to be created and
then attach Fault Injection rules... it's too late to test faults during the
creation by then otherwise.
code prepares a Fault Injection context listing his rules in the creation info
struct, rather than waiting for the object to be created and then attach Fault
Injection rules... it's too late then to test faults during the creation.
## Directly applying fault contexts
You can pass in a Fault Injection context prepared with lws_fi_t added to it
when creating the following kinds of objects
|Object being created|info struct|Fault injection Context member|
|---|---|---|
|lws context|struct lws_context_creation_info|`fic`|
|vhost|struct lws_context_creation_info|`fic`|
|Secure Stream|struct lws_ss_info|`fic`|
|client wsi|struct lws_client_connect_info|`fic`|
However typically the approach is just provide a list of faults at context
creation time, and let the objects match and inherit using namespacing,
described next.
## Using the namespace to target specific instances
Wsi client connection can directly have fault injection objects attached to it
at client connection creation time.
Lws objects created by the user can directly have a Fault Injection context
attached to them at creation time, so the fault injection objects directly
relate to the object.
But in other common scenarios, there is no direct visibility of the object that
we want to trigger faults in, it may not exist until some time later. Eg, we
want to trigger faults in the listen socket of a vhost. To allow this, the
fault names can be structured with a /path/ type namespace so objects created
later can inherit faults.
Notice that if you are directly creating the vhost, Secure Stream or wsi, you
can directly attach the subrule yourself without the namespacing needed. The
namespacing is used when you have access to a higher level object at creation-
time, like the lws_context, and it will itself create the object you want to
target without your having any direct access to it.
|namespace form|effect|
|---|---|
|**vh=myvhost/**subrule|subrule is inherited by the vhost named "myvhost" when it is created|
|**vh/**subrule|subrule is inherited by any vhost when it is created|
|**ss=mystream/**subrule|subrule is inherited by SS of streamtype "mystream" (also covers SSPC / proxy client)|
|**ss/**subrule|subrule is inherited by all SS of any streamtype (also covers SSPC / proxy client)|
|**wsi=myname/**subrule|subrule is inherited by client wsi created with `info->fi_wsi_name` "myname"|
|**wsi/**subrule|subrule is inherited by any wsi|
Namespaces can be combined, for example `vh=myvhost/wsi/listenskt` will set the
`listenskt` fault on wsi created by the server vhost "myvhost", ie, it will
cause the listen socket for the vhost to error out on creation.
In the case of wsi migration when it's the network connection wsi on an h2
connection that is migrated to be SID 1, the attached faults also migrate.
Here is which Fault Injection Contexts each type of object inherits matching
Fault Injection rules from:
|Object type|Initialized with|Inherit matching faults from|
|---|---|---|
|context|`struct lws_context_creation_info` .fic|-|
|vhost|`struct lws_context_creation_info` .fic|context FIC|
|client wsi|`struct lws_client_connect_info` .fic|context FIC, vhost FIC|
|ss / sspc|`lws_ss_info_t` .fic|context FIC|
|ss / sspc wsi|-|context FIC, vhost FIC, ss / sspc .fic|
Since everything can be reached from the lws_context fault context, directly or
by additional inheritence, and that's the most convenient to set from the
outside, that's typically the original source of all injected faults.
## Integration with minimal examples
All the minimal examples that use the `lws_cmdline_option_handle_builtin()` api
can take an additional `--fault-injection "...,..."` switch, which automatically
parses the comma-separated list in the argument to add faults with the given
name to the lws_context. For example,
`lws-minimal-http-client --fault-injection "wsi/dnsfail"`
will force all wsi dns lookups to fail for that run of the example.
### Specifying when to inject the fault
By default, if you just give the name part, if the namespace is absent or
matches an object, the fault will be injected every time. It's also possible
to make the fault inject itself at a random probability, or in a cyclic pattern,
by giving additional information in brackets, eg
|Syntax|Meaning|
|---|---|
|`wsi/thefault`|Inject the fault every time|
|`wsi/thefault(10%)`|Randomly inject the fault at 10% probability|
|`wsi/thefault(.............X.X)`|Inject the fault on the 14th and 16th try, every 16 tries|
You must quote the strings containing these symbols, since they may otherwise be
interpreted by your shell.
## Well-known fault names in lws
|Scope|Namespc|Name|Fault effect|
|---|---|---|---|
|context||`ctx_createfail1`|Fail context creation immediately at entry|
|context||`ctx_createfail_plugin_init`|Fail context creation as if a plugin init failed (if plugins enabled)|
|context||`ctx_createfail_evlib_plugin`|Fail context creation due to event lib plugin failed init (if evlib plugins enabled)|
|context||`ctx_createfail_evlib_sel`|Fail context creation due to unable to select event lib|
|context||`ctx_createfail_oom_ctx`|Fail context creation due to OOM on context object|
|context||`ctx_createfail_privdrop`|Fail context creation due to failure dropping privileges|
|context||`ctx_createfail_maxfds`|Fail context creation due to unable to determine process fd limit|
|context||`ctx_createfail_oom_fds`|Fail context creation due to OOM on fds table|
|context||`ctx_createfail_plat_init`|Fail context creation due to platform init failed|
|context||`ctx_createfail_evlib_init`|Fail context creation due to event lib init failed|
|context||`ctx_createfail_evlib_pt`|Fail context creation due to event lib pt init failed|
|context||`ctx_createfail_sys_vh`|Fail context creation due to system vhost creation failed|
|context||`ctx_createfail_sys_vh_init`|Fail context creaton due to system vhost init failed|
|context||`ctx_createfail_def_vh`|Fail context creation due to default vhost creation failed|
|context||`ctx_createfail_ss_pol1`|Fail context creation due to ss policy parse start failed (if policy enabled)|
|context||`ctx_createfail_ss_pol2`|Fail context creation due to ss policy parse failed (if policy enabled)|
|context||`ctx_createfail_ss_pol3`|Fail context creation due to ss policy set failed (if policy enabled)|
|vhost|`vh`|`vh_create_oom`|Fail vh creation on vh object alloc OOM|
|vhost|`vh`|`vh_create_pcols_oom`|Fail vh creation at protocols alloc OOM|
|vhost|`vh`|`vh_create_access_log_open_fail`|Fail vh creation due to unable to open access log (LWS_WITH_ACCESS_LOG)|
|vhost|`vh`|`vh_create_ssl_srv`|Fail server ssl_ctx init|
|vhost|`vh`|`vh_create_ssl_cli`|Fail client ssl_ctx init|
|vhost|`vh`|`vh_create_srv_init`|Fail server init|
|vhost|`vh`|`vh_create_protocol_init`|Fail late protocol init (for late vhost creation)|
|srv vhost|`vh=xxx/wsi`|`listenskt`|Causes `socket()` allocation for vhost listen socket to fail|
|cli wsi|`wsi`|`dnsfail`|Sync: `getaddrinfo()` is not called and a EAI_FAIL return synthesized, Async: request not started and immediate fail synthesized|
|cli wsi|`wsi`|`sendfail`|Attempts to send data on the wsi socket fail|
|cli wsi|`wsi`|`connfail`|Attempts to connect on the wsi socket fail|
|cli wsi|`wsi`|`createfail`|Creating the client wsi itself fails|
|udp wsi|`wsi`|`udp_rx_loss`|Drop UDP RX that was actually received, useful with probabalistic mode|
|udp wsi|`wsi`|`udp_tx_loss`|Drop UDP TX so that it's not actually sent, useful with probabalistic mode|
|srv ss|`ss`|`ss_srv_vh_fail`|Secure Streams Server vhost creation forced to fail|
|cli ss|`ss`|`ss_no_streamtype_policy`|The policy for the streamtype is made to seem as if it is missing|
|sspc|`ss`|`sspc_fail_on_linkup`|Reject the connection to the proxy when we hear it has succeeded, it will provoke endless retries|
|sspc|`ss`|`sspc_fake_rxparse_disconnect_me`|Force client-proxy link parse to seem to ask to be disconnected, it will provoke endless retries|
|sspc|`ss`|`sspc_fake_rxparse_destroy_me`|Force client-proxy link parse to seem to ask to destroy the SS, it will destroy the SS cleanly|
|sspc|`ss`|`sspc_link_write_fail`|Force write on the link to fail, it will provoke endless retries|
|sspc|`ss`|`sspc_create_oom`|Cause the sspc handle allocation to fail as if OOM at creation time|
|sspc|`ss`|`sspc_fail_metadata_set`|Cause the metadata allocation to fail|
|sspc|`ss`|`sspc_rx_fake_destroy_me`|Make it seem that client's user code *rx() returned DESTROY_ME|
|sspc|`ss`|`sspc_rx_metadata_oom`|Cause metadata from proxy allocation to fail|
|ssproxy|`ss`|`ssproxy_dsh_create_oom`|Cause proxy's creation of DSH to fail|
|ssproxy|`ss`|`ssproxy_dsh_rx_queue_oom`|Cause proxy's allocation in the onward SS->P[->C] DSH rx direction to fail as if OOM, this causes the onward connection to disconnect|
|ssproxy|`wsi`|`ssproxy_client_adopt_oom`|Cause proxy to be unable to allocate for new client - proxy link connection object|
|ssproxy|`wsi`|`ssproxy_client_write_fail`|Cause proxy write to client to fail|
|ssproxy|`wsi`|`sspc_dsh_ss2p_oom`|Cause ss->proxy dsh allocation to fail|
|ssproxy|`ss`|`ssproxy_onward_conn_fail`|Act as if proxy onward client connection failed immediately|
|ssproxy|`ss`|`ssproxy_dsh_c2p_pay_oom`|Cause proxy's DSH alloc for C->P payload to fail|
## Well-known namespace targets
Namespaces can be used to target these more precisely, for example even though
we are only passing the faults we want inject at the lws_context, we can use
the namespace "paths" to target only the wsis created by other things.
To target wsis from SS-based connections, you can use `ss=stream_type_name/`,
eg for captive portal detection, to have it unable to find its policy entry:
`ss=captive_portal_detect/ss_no_streamtype_policy` (disables CPD from operating)
...to force it to fail to resolve the server DNS:
`ss=captive_portal_detect/wsi/dnsfail` (this makes CPD feel there is no internet)
...to target the connection part of the captive portal testing instead:
`ss=captive_portal_detect/wsi/connfail` (this also makes CPD feel there is no internet)
### Well-known internal wsi type names
Wsi created for internal features like Async DNS processing can also be targeted
|wsi target|Meaning|
|---|---|
|`wsi=asyncdns/`|UDP wsi used by lws Async DNS support to talk to DNS servers|
|`wsi=dhcpc/`|UDP wsi used by lws DHCP Client|
|`wsi=ntpclient/`|UDP wsi used by lws NTP Client|
For example, passing in at lws_context level `wsi=asyncdns/udp_tx_loss`
will force async dns to be unable to resolve anything since its UDP tx is
being suppressed.
At client connection creation time, user code can also specify their own names
to match on these `wsi=xxx/` namespace parts, so the faults only apply to
specific wsi they are creating themselves later. This is done by setting the
client creation info struct `.fi_wsi_name` to the string "xxx".

View file

@ -44,7 +44,7 @@ In the callback, it should simply call `lws_callback_on_writable()` for the udp
## Simulating packetloss
lws now allows you to set the amount of simulated packetloss on udp rx and tx in
the context creation info struct, using `.udp_loss_sim_tx_pc` and `.udp_loss_sim_rx_pc`,
the values are percentages between 0 and 100. 0, the default, means no packetloss.
You can simulate udp packetloss at tx and rx by using the Fault Injection apis
with the well-known fault names "udp_tx_loss" and "udp_rx_loss", typically
with the probabilistic setting, in commandline format something like
`--fault-injection "wsi/udp_tx_loss(10%)"`

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

View file

@ -152,6 +152,7 @@ typedef struct lws_adopt_desc {
const char *vh_prot_name; /**< NULL or vh protocol name to bind raw connection to */
struct lws *parent; /**< NULL or struct lws to attach new_wsi to as a child */
void *opaque; /**< opaque pointer to set on created wsi */
const char *fi_wsi_name; /**< NULL, or Fault Injection inheritence filter for wsi=string/ context faults */
} lws_adopt_desc_t;
/**
@ -249,6 +250,11 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
* \param parent_wsi: NULL or parent wsi new wsi will be a child of
* \param opaque: set created wsi opaque ptr to this
* \param retry_policy: NULL for vhost default policy else wsi specific policy
* \param fi_wsi_name: NULL, or string to inherit Fault Injection rules in
* form "wsi=string/rule". "wsi/rule" faults will be
* automatically applied as well. It's done at creation
* time so the rules can, eg, inject faults related to
* creation.
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
@ -257,7 +263,7 @@ LWS_VISIBLE LWS_EXTERN struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
int flags, const char *protocol_name, const char *ifname,
struct lws *parent_wsi, void *opaque,
const lws_retry_bo_t *retry_policy);
const lws_retry_bo_t *retry_policy, const char *fi_wsi_name);
#endif

View file

@ -206,10 +206,15 @@ struct lws_client_connect_info {
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t *fi;
lws_fi_ctx_t fic;
/**< Attach external Fault Injection context to the client wsi,
* hierarchy is wsi -> vhost -> context */
#endif
/* for convenience, available when FI disabled in build */
const char *fi_wsi_name;
/**< specific Fault Injection namespace name for wsi created for this
* connection, allows targeting by "wsi=XXX/..." if you give XXX here.
*/
uint16_t keep_warm_secs;
/**< 0 means 5s. If the client connection to the endpoint becomes idle,

View file

@ -810,17 +810,9 @@ struct lws_context_creation_info {
*/
#endif /* PEER_LIMITS */
#if defined(LWS_WITH_UDP)
uint8_t udp_loss_sim_tx_pc;
/**< CONTEXT: percentage of udp writes we could have performed
* to instead not do, in order to simulate and test udp retry flow */
uint8_t udp_loss_sim_rx_pc;
/**< CONTEXT: percentage of udp reads we actually received
* to make disappear, in order to simulate and test udp retry flow */
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t *fi;
lws_fi_ctx_t fic;
/**< CONTEXT | VHOST: attach external Fault Injection context to the
* lws_context or vhost. If creating the context + default vhost in
* one step, only the context binds to \p fi. When creating a vhost

View file

@ -24,13 +24,54 @@
* Fault injection api if built with LWS_WITH_SYS_FAULT_INJECTION
*/
typedef struct lws_xos {
uint64_t s[4];
} lws_xos_t;
/**
* lws_xos_init() - seed xoshiro256 PRNG
*
* \param xos: the prng state object to initialize
* \param seed: the 64-bit seed
*
* Initialize PRNG \xos with the starting state represented by \p seed
*/
LWS_VISIBLE LWS_EXTERN void
lws_xos_init(struct lws_xos *xos, uint64_t seed);
/**
* lws_xos() - get next xoshiro256 PRNG result and update state
*
* \param xos: the PRNG state to use
*
* Returns next 64-bit PRNG result. These are cheap to get,
* quite a white noise sequence, and completely deterministic
* according to the seed it was initialized with.
*/
LWS_VISIBLE LWS_EXTERN uint64_t LWS_WARN_UNUSED_RESULT
lws_xos(struct lws_xos *xos);
/**
* lws_xos_percent() - return 1 a given percent of the time on average
*
* \param xos: the PRNG state to use
* \param percent: chance in 100 of returning 1
*
* Returns 1 if next random % 100 is < \p percent, such that
* 100 always returns 1, 0 never returns 1, and the chance linearly scales
* inbetween
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_xos_percent(struct lws_xos *xos, int percent);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
enum {
LWSFI_ALWAYS,
LWSFI_DETERMINISTIC, /* do .count injections after .pre then stop */
LWSFI_PROBABILISTIC, /* .prob1 / .prob2 chance of injection */
LWSFI_PROBABILISTIC, /* .pre % chance of injection */
LWSFI_PATTERN, /* use .count bits in .pattern after .pre */
LWSFI_PATTERN_ALLOC, /* as _PATTERN, but .pattern is malloc'd */
};
typedef struct lws_fi {
@ -44,8 +85,8 @@ typedef struct lws_fi {
typedef struct lws_fi_ctx {
lws_dll2_owner_t fi_owner;
struct lws_xos xos;
const char *name;
struct lws_fi_ctx *parent;
} lws_fi_ctx_t;
/**
@ -65,7 +106,7 @@ typedef struct lws_fi_ctx {
* If LWS_WITH_SYS_FAULT_INJECTION is not defined, then this always return 0.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fi(lws_fi_ctx_t *fic, const char *fi_name);
lws_fi(const lws_fi_ctx_t *fic, const char *fi_name);
/**
* lws_fi_add() - add an allocated copy of fault injection to a context
@ -74,7 +115,7 @@ lws_fi(lws_fi_ctx_t *fic, const char *fi_name);
* \param fi: the fault injection details
*
* This allocates a copy of \p fi and attaches it to the fault injection context
* \p fic.
* \p fic. \p fi can go out of scope after this safely.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi);
@ -91,6 +132,35 @@ lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi);
LWS_VISIBLE LWS_EXTERN void
lws_fi_remove(lws_fi_ctx_t *fic, const char *name);
/**
* lws_fi_import() - transfers all the faults from one context to another
*
* \param fic_dest: the fault context to receive the faults
* \param fic_src: the fault context that will be emptied out into \p fic_dest
*
* This is used to initialize created object fault injection contexts from
* the caller.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_import(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src);
/**
* lws_fi_inherit_copy() - attach copies of matching fault injection objects to dest
*
* \param fic_dest: destination Fault Injection context
* \param fic_src: parent fault context that may contain matching rules
* \param scope: the name of the path match required, eg, "vh"
* \param value: the dynamic name of our match, eg, "myvhost"
*
* If called with scope "vh" and value "myvhost", then matches faults starting
* "vh=myvhost/", strips that part of the name if it matches and makes a copy
* of the rule with the modified name attached to the destination Fault Injection
* context.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_inherit_copy(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src,
const char *scope, const char *value);
/**
* lws_fi_destroy() - removes all allocated fault injection entries
*
@ -101,7 +171,44 @@ lws_fi_remove(lws_fi_ctx_t *fic, const char *name);
* not usually directly allocated.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_destroy(lws_fi_ctx_t *fic);
lws_fi_destroy(const lws_fi_ctx_t *fic);
/**
* lws_fi_deserialize() - adds fault in string form to Fault Injection Context
*
* \p fic: the fault injection context
* \p sers: the string serializing the desired fault details
*
* This turns a string like "ss=captive_portal_detect/wsi/dnsfail(10%)" into
* a fault injection struct added to the fault injection context \p fic
*
* You can prepare the context creation info .fic with these before creating
* the context, and use namespace paths on those to target other objects.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers);
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_wsi_fi(struct lws *wsi, const char *name);
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_context_fi(struct lws_context *ctx, const char *name);
#if defined(LWS_WITH_SECURE_STREAMS)
struct lws_ss_handle;
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_ss_fi(struct lws_ss_handle *h, const char *name);
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
struct lws_sspc_handle;
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_sspc_fi(struct lws_sspc_handle *h, const char *name);
#endif
#endif
#define lws_fi_user_wsi_fi(_wsi, _name) _lws_fi_user_wsi_fi(_wsi, _name)
#define lws_fi_user_context_fi(_ctx, _name) _lws_fi_user_context_fi(_ctx, _name)
#define lws_fi_user_ss_fi(_h, _name) _lws_fi_user_ss_fi(_h, _name)
#define lws_fi_user_sspc_fi(_h, _name) _lws_fi_user_sspc_fi(_h, _name)
#else
@ -111,5 +218,12 @@ lws_fi_destroy(lws_fi_ctx_t *fic);
*/
#define lws_fi(_fi_name, _fic) (0)
#define lws_fi_destroy(_x)
#define lws_fi_inherit_copy(_a, _b, _c, _d)
#define lws_fi_deserialize(_x, _y)
#define lws_fi_user_wsi_fi(_wsi, _name) (0)
#define lws_fi_user_context_fi(_wsi, _name) (0)
#define lws_fi_user_ss_fi(_h, _name) (0)
#define lws_fi_user_sspc_fi(_h, _name) (0)
#endif

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
* Copyright (C) 2010 - 2021 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

View file

@ -61,6 +61,7 @@
#define lws_ss_change_handlers lws_sspc_change_handlers
#define lws_smd_ss_rx_forward lws_smd_sspc_rx_forward
#define lws_ss_tag lws_sspc_tag
#define _lws_fi_user_ss_fi _lws_fi_user_sspc_fi
#endif

View file

@ -354,7 +354,7 @@ typedef struct lws_ss_info {
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t *fi;
lws_fi_ctx_t fic;
/**< Attach external Fault Injection context to the stream, hierarchy
* is ss->context */
#endif

View file

@ -115,7 +115,7 @@ lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi, const char *de
static struct lws *
lws_adopt_descriptor_vhost1(struct lws_vhost *vh, lws_adoption_type type,
const char *vh_prot_name, struct lws *parent,
void *opaque)
void *opaque, const char *fi_wsi_name)
{
struct lws_context *context = vh->context;
struct lws_context_per_thread *pt;
@ -139,6 +139,15 @@ lws_adopt_descriptor_vhost1(struct lws_vhost *vh, lws_adoption_type type,
return NULL;
}
/* bring in specific fault injection rules early */
lws_fi_inherit_copy(&new_wsi->fic, &context->fic, "wsi", fi_wsi_name);
if (lws_fi(&new_wsi->fic, "createfail")) {
lws_fi_destroy(&new_wsi->fic);
lws_context_unlock(vh->context);
return NULL;
}
new_wsi->a.opaque_user_data = opaque;
pt = &context->pt[(int)new_wsi->tsi];
@ -201,6 +210,8 @@ bail:
if (new_wsi->user_space)
lws_free(new_wsi->user_space);
lws_fi_destroy(&new_wsi->fic);
lws_vhost_unbind_wsi(new_wsi);
lws_free(new_wsi);
@ -509,7 +520,7 @@ lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info)
new_wsi = lws_adopt_descriptor_vhost1(info->vh, info->type,
info->vh_prot_name, info->parent,
info->opaque);
info->opaque, info->fi_wsi_name);
if (!new_wsi) {
if (info->type & LWS_ADOPT_SOCKET)
compatible_close(info->fd.sockfd);
@ -772,7 +783,7 @@ struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
int flags, const char *protocol_name, const char *ifname,
struct lws *parent_wsi, void *opaque,
const lws_retry_bo_t *retry_policy)
const lws_retry_bo_t *retry_policy, const char *fi_wsi_name)
{
#if !defined(LWS_PLAT_OPTEE)
struct lws *wsi;
@ -782,8 +793,10 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
/* create the logical wsi without any valid fd */
wsi = lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_SOCKET | LWS_ADOPT_RAW_SOCKET_UDP,
protocol_name, parent_wsi, opaque);
wsi = lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_SOCKET |
LWS_ADOPT_RAW_SOCKET_UDP,
protocol_name, parent_wsi, opaque,
fi_wsi_name);
if (!wsi) {
lwsl_err("%s: udp wsi creation failed\n", __func__);
goto bail;

View file

@ -86,7 +86,7 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
struct lws *wsi, *safe = NULL;
const struct lws_protocols *p;
const char *cisin[CIS_COUNT];
struct lws_vhost *vh;
struct lws_vhost *vh, *v;
int
#if LWS_MAX_SMP > 1
tid = 0,
@ -151,15 +151,41 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
vh = vh->vhost_next;
}
#if defined(LWS_WITH_SECURE_STREAMS)
/* any of these imply we are a client wsi bound to an SS, which
* implies our opaque user ptr is the ss (or sspc if PROXY_LINK) handle
*/
wsi->for_ss = !!(i->ssl_connection & (LCCSCF_SECSTREAM_CLIENT | LCCSCF_SECSTREAM_PROXY_LINK | LCCSCF_SECSTREAM_PROXY_ONWARD));
wsi->client_bound_sspc = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK); /* so wsi close understands need to remove sspc ptr to wsi */
wsi->client_proxy_onward = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD);
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
wsi->fi.name = "wsi";
wsi->fi.parent = &vh->fi;
if (i->fi)
wsi->fic.name = "wsi";
if (i->fic.fi_owner.count)
/*
* This moves all the lws_fi_t from info->fi to the vhost fi,
* This moves all the lws_fi_t from i->fi to the vhost fi,
* leaving it empty
*/
lws_fi_import(&wsi->fi, i->fi);
lws_fi_import(&wsi->fic, &i->fic);
lws_fi_inherit_copy(&wsi->fic, &i->context->fic, "wsi", i->fi_wsi_name);
if (lws_fi(&wsi->fic, "createfail"))
goto bail;
#if defined(LWS_WITH_SECURE_STREAMS)
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
if (wsi->client_bound_sspc) {
lws_sspc_handle_t *fih = (lws_sspc_handle_t *)i->opaque_user_data;
lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL);
}
#endif
if (wsi->for_ss) {
lws_ss_handle_t *fih = (lws_ss_handle_t *)i->opaque_user_data;
lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL);
}
#endif
#endif
/*
@ -187,7 +213,7 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
wsi->conn_validity_wakesuspend = 1;
if (!i->vhost) {
struct lws_vhost *v = i->context->vhost_list;
v = i->context->vhost_list;
if (!v) { /* coverity */
lwsl_err("%s: no vhost\n", __func__);
@ -195,9 +221,15 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
}
if (!strcmp(v->name, "system"))
v = v->vhost_next;
lws_vhost_bind_wsi(v, wsi);
} else
lws_vhost_bind_wsi(i->vhost, wsi);
v = i->vhost;
lws_vhost_bind_wsi(v, wsi);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
/* additionally inerit from vhost we bound to */
lws_fi_inherit_copy(&wsi->fic, &v->fic, "wsi", i->fi_wsi_name);
#endif
if (!wsi->a.vhost) {
lwsl_err("%s: No vhost in the context\n", __func__);
@ -332,12 +364,6 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
i->opaque_user_data;
#if defined(LWS_WITH_SECURE_STREAMS)
/* any of these imply we are a client wsi bound to an SS, which
* implies our opaque user ptr is the ss (or sspc if PROXY_LINK) handle
*/
wsi->for_ss = !!(i->ssl_connection & (LCCSCF_SECSTREAM_CLIENT | LCCSCF_SECSTREAM_PROXY_LINK | LCCSCF_SECSTREAM_PROXY_ONWARD));
wsi->client_bound_sspc = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK); /* so wsi close understands need to remove sspc ptr to wsi */
wsi->client_proxy_onward = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD);
if (wsi->for_ss) {
/* it's related to ss... the options are
@ -515,6 +541,7 @@ bail1:
lws_free_set_NULL(wsi->stash);
bail:
lws_fi_destroy(&wsi->fic);
lws_free(wsi);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
bail2:

View file

@ -64,7 +64,10 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
#endif
wsi->dns_reachability = 0;
n = getaddrinfo(ads, NULL, &hints, result);
if (lws_fi(&wsi->fic, "dnsfail"))
n = EAI_FAIL;
else
n = getaddrinfo(ads, NULL, &hints, result);
#if defined(LWS_WITH_CONMON)
wsi->conmon.ciu_dns = (lws_conmon_interval_us_t)
@ -99,7 +102,7 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
lws_snprintf(buckname, sizeof(buckname), "dns=\"unreachable %d\"", n);
lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname);
#endif
lwsl_notice("%s: asking to recheck CPD in 1s\n", __func__);
lwsl_debug("%s: asking to recheck CPD in 1s\n", __func__);
lws_system_cpd_start_defer(wsi->a.context, LWS_US_PER_SEC);
}
@ -340,7 +343,10 @@ solo:
#else
/* this is either FAILED, CONTINUING, or already called connect_4 */
n = lws_async_dns_query(wsi->a.context, wsi->tsi, ads,
if (lws_fi(&wsi->fic, "dnsfail"))
return lws_client_connect_3_connect(wsi, NULL, NULL, -4, NULL);
else
n = lws_async_dns_query(wsi->a.context, wsi->tsi, ads,
LWS_ADNS_RECORD_A, lws_client_connect_3_connect,
wsi, NULL);

View file

@ -151,6 +151,9 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
lws_dns_sort_t *curr;
ssize_t plen = 0;
lws_dll2_t *d;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
int cfail;
#endif
int m;
/*
@ -433,7 +436,14 @@ ads_known:
#endif
wsi->socket_is_permanently_unusable = 0;
m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa, (unsigned int)n);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
cfail = lws_fi(&wsi->fic, "connfail");
if (cfail)
m = -1;
else
#endif
m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa, (unsigned int)n);
#if defined(LWS_WITH_CONMON)
wsi->conmon_datum = lws_now_usecs();
@ -450,6 +460,12 @@ ads_known:
int errno_copy = LWS_ERRNO;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
if (cfail)
/* fake an abnormal, fatal situation */
errno_copy = 999;
#endif
lwsl_debug("%s: connect: errno: %d\n", __func__, errno_copy);
if (errno_copy &&
@ -481,10 +497,10 @@ ads_known:
lws_sa46_write_numeric_address(&wsi->sa46_peer, nads,
sizeof(nads));
lwsl_info("%s: Connect failed: %s port %d\n", __func__,
nads, port);
wsi->sa46_peer.sa4.sin_family = 0;
lwsl_info("%s: Connect failed: %s port %d (errno %d)\n",
__func__, nads, port, errno_copy);
#if defined(LWS_WITH_UNIX_SOCK)
}
#endif

View file

@ -831,7 +831,7 @@ __lws_close_free_wsi_final(struct lws *wsi)
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_destroy(&wsi->fi);
lws_fi_destroy(&wsi->fic);
#endif
__lws_wsi_remove_from_sul(wsi);

View file

@ -114,7 +114,11 @@ lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
/* nope, send it on the socket directly */
m = (unsigned int)lws_ssl_capable_write(wsi, buf, n);
if (lws_fi(&wsi->fic, "sendfail"))
m = (unsigned int)LWS_SSL_CAPABLE_ERROR;
else
m = (unsigned int)lws_ssl_capable_write(wsi, buf, n);
lwsl_info("%s: ssl_capable_write (%d) says %d\n", __func__, n, m);
/* something got written, it can have been truncated now */
@ -329,20 +333,11 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, size_t len)
#if defined(LWS_WITH_UDP)
if (lws_wsi_is_udp(wsi)) {
if (wsi->a.context->udp_loss_sim_tx_pc) {
uint16_t u16;
/*
* We should randomly drop some of these
*/
if (lws_get_random(wsi->a.context, &u16, 2) == 2 &&
((u16 * 100) / 0xffff) <=
wsi->a.context->udp_loss_sim_tx_pc) {
lwsl_warn("%s: dropping udp tx\n", __func__);
/* pretend it was sent */
n = (int)(ssize_t)len;
goto post_send;
}
if (lws_fi(&wsi->fic, "udp_tx_loss")) {
/* pretend it was sent */
n = (int)(ssize_t)len;
goto post_send;
}
if (lws_has_buffered_out(wsi))

View file

@ -469,7 +469,7 @@ struct lws_vhost {
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t fi;
lws_fi_ctx_t fic;
/**< Fault Injection ctx for the vhost, hierarchy vhost->context */
#endif
@ -693,7 +693,7 @@ struct lws {
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t fi;
lws_fi_ctx_t fic;
/**< Fault Injection ctx for the wsi, hierarchy wsi->vhost->context */
#endif

View file

@ -159,9 +159,14 @@ _lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj, int flag
void
_lws_route_table_empty(struct lws_context_per_thread *pt)
{
if (!pt->context)
return;
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
lws_dll2_get_head(&pt->context->routing_table)) {
lws_route_t *rou = lws_container_of(d, lws_route_t, list);
lws_dll2_remove(&rou->list);
lws_free(rou);

View file

@ -559,14 +559,16 @@ lws_create_vhost(struct lws_context *context,
#endif
int n;
vh = lws_zalloc(sizeof(*vh)
if (lws_fi(&info->fic, "vh_create_oom"))
vh = NULL;
else
vh = lws_zalloc(sizeof(*vh)
#if defined(LWS_WITH_EVENT_LIBS)
+ context->event_loop_ops->evlib_size_vh
#endif
, __func__);
if (!vh)
return NULL;
goto early_bail;
#if defined(LWS_WITH_EVENT_LIBS)
vh->evlib_vh = (void *)&vh[1];
@ -599,14 +601,17 @@ lws_create_vhost(struct lws_context *context,
info->iface ? info->iface : "", info->port);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
vh->fi.name = "vh";
vh->fi.parent = &context->fi;
if (info->fi)
vh->fic.name = "vh";
if (info->fic.fi_owner.count)
/*
* This moves all the lws_fi_t from info->fi to the vhost fi,
* leaving it empty
*/
lws_fi_import(&vh->fi, info->fi);
lws_fi_import(&vh->fic, &info->fic);
lws_fi_inherit_copy(&vh->fic, &context->fic, "vh", vh->name);
if (lws_fi(&vh->fic, "vh_create_oom"))
goto bail;
#endif
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
@ -725,16 +730,19 @@ lws_create_vhost(struct lws_context *context,
* - the ones that came from plugins
* - his user protocols
*/
lwsp = lws_zalloc(sizeof(struct lws_protocols) *
if (lws_fi(&vh->fic, "vh_create_pcols_oom"))
lwsp = NULL;
else
lwsp = lws_zalloc(sizeof(struct lws_protocols) *
((unsigned int)vh->count_protocols +
(unsigned int)abs_pcol_count +
(unsigned int)sec_pcol_count +
(unsigned int)context->plugin_protocol_count +
(unsigned int)fx + 1),
"vhost-specific plugin table");
(unsigned int)fx + 1), "vh plugin table");
if (!lwsp) {
lwsl_err("OOM\n");
return NULL;
goto bail;
}
/*
@ -923,7 +931,10 @@ lws_create_vhost(struct lws_context *context,
#ifdef LWS_WITH_ACCESS_LOG
if (info->log_filepath) {
vh->log_fd = lws_open(info->log_filepath,
if (lws_fi(&vh->fic, "vh_create_access_log_open_fail"))
vh->log_fd = (int)LWS_INVALID_FILE;
else
vh->log_fd = lws_open(info->log_filepath,
O_CREAT | O_APPEND | O_RDWR, 0600);
if (vh->log_fd == (int)LWS_INVALID_FILE) {
lwsl_err("unable to open log filepath %s\n",
@ -940,17 +951,22 @@ lws_create_vhost(struct lws_context *context,
} else
vh->log_fd = (int)LWS_INVALID_FILE;
#endif
if (lws_context_init_server_ssl(info, vh)) {
if (lws_fi(&vh->fic, "vh_create_ssl_srv") ||
lws_context_init_server_ssl(info, vh)) {
lwsl_err("%s: lws_context_init_server_ssl failed\n", __func__);
goto bail1;
}
if (lws_context_init_client_ssl(info, vh)) {
if (lws_fi(&vh->fic, "vh_create_ssl_cli") ||
lws_context_init_client_ssl(info, vh)) {
lwsl_err("%s: lws_context_init_client_ssl failed\n", __func__);
goto bail1;
}
#if defined(LWS_WITH_SERVER)
lws_context_lock(context, __func__);
n = _lws_vhost_init_server(info, vh);
if (lws_fi(&vh->fic, "vh_create_srv_init"))
n = -1;
else
n = _lws_vhost_init_server(info, vh);
lws_context_unlock(context);
if (n < 0) {
lwsl_err("init server failed\n");
@ -978,7 +994,8 @@ lws_create_vhost(struct lws_context *context,
/* for the case we are adding a vhost much later, after server init */
if (context->protocol_init_done)
if (lws_protocol_init(context)) {
if (lws_fi(&vh->fic, "vh_create_protocol_init") ||
lws_protocol_init(context)) {
lwsl_err("%s: lws_protocol_init failed\n", __func__);
goto bail1;
}
@ -990,10 +1007,13 @@ bail1:
return NULL;
#ifdef LWS_WITH_ACCESS_LOG
bail:
__lws_lc_untag(&vh->lc);
lws_fi_destroy(&vh->fic);
lws_free(vh);
#endif
early_bail:
lws_fi_destroy(&info->fic);
return NULL;
}
@ -1449,7 +1469,7 @@ __lws_vhost_destroy2(struct lws_vhost *vh)
lws_dll2_remove(&vh->vh_being_destroyed_list);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_destroy(&vh->fi);
lws_fi_destroy(&vh->fic);
#endif
__lws_lc_untag(&vh->lc);

View file

@ -240,6 +240,18 @@ __lws_wsi_create_with_role(struct lws_context *context, int tsi,
// lwsl_debug("%s: tsi %d: role: %s\n", __func__, tsi,
// ops ? ops->name : "none");
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_xos_init(&wsi->fic.xos, lws_xos(&context->fic.xos));
#endif
lws_fi_inherit_copy(&wsi->fic, &context->fic, "wsi", NULL);
if (lws_fi(&wsi->fic, "createfail")) {
lws_fi_destroy(&wsi->fic);
lws_free(wsi);
return NULL;
}
return wsi;
}

View file

@ -323,6 +323,9 @@ static const char * const opts_str =
#if defined(LWS_WITH_MBEDTLS)
"MbedTLS "
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
"FLTINJ "
#endif
#if defined(LWS_WITH_SYS_ASYNC_DNS)
"ASYNC_DNS "
#endif
@ -399,6 +402,9 @@ lws_create_context(const struct lws_context_creation_info *info)
char fatal_exit_defer = 0;
#endif
if (lws_fi(&info->fic, "ctx_createfail1"))
goto early_bail;
if (lpf) {
lpf+= 2;
#if defined(LWS_WITH_SYS_ASYNC_DNS)
@ -424,7 +430,7 @@ lws_create_context(const struct lws_context_creation_info *info)
lwsl_notice("%s%s\n", opts_str, s);
if (lws_plat_context_early_init())
return NULL;
goto early_bail;
#if defined(LWS_WITH_NETWORK)
if (info->count_threads)
@ -482,7 +488,7 @@ lws_create_context(const struct lws_context_creation_info *info)
map[n].name, NULL, NULL))
ok = 1;
if (!ok) {
if (!ok || lws_fi(&info->fic, "ctx_createfail_plugin_init")) {
lwsl_err("%s: failed to load %s\n", __func__,
map[n].name);
goto bail;
@ -493,7 +499,8 @@ lws_create_context(const struct lws_context_creation_info *info)
fatal_exit_defer = !!info->foreign_loops;
#endif
if (!evlib_plugin_list) {
if (!evlib_plugin_list ||
lws_fi(&info->fic, "ctx_createfail_evlib_plugin")) {
lwsl_err("%s: unable to load evlib plugin %s\n",
__func__, map[n].name);
@ -570,7 +577,7 @@ lws_create_context(const struct lws_context_creation_info *info)
#endif /* not with ev plugins */
if (!plev)
if (!plev || lws_fi(&info->fic, "ctx_createfail_evlib_sel"))
goto fail_event_libs;
#if defined(LWS_WITH_NETWORK)
@ -581,9 +588,12 @@ lws_create_context(const struct lws_context_creation_info *info)
#endif
context = lws_zalloc(size, "context");
if (!context) {
lwsl_err("No memory for websocket context\n");
return NULL;
if (!context || lws_fi(&info->fic, "ctx_createfail_oom_ctx")) {
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_free(context);
#endif
lwsl_err("No memory for lws_context\n");
goto early_bail;
}
#if defined(LWS_WITH_NETWORK)
@ -609,13 +619,13 @@ lws_create_context(const struct lws_context_creation_info *info)
context->pt_serv_buf_size = (unsigned int)s1;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
context->fi.name = "ctx";
if (info->fi)
context->fic.name = "ctx";
if (info->fic.fi_owner.count)
/*
* This moves all the lws_fi_t from info->fi to the context fi,
* leaving it empty, so no injection added to default vhost
*/
lws_fi_import(&context->fi, info->fi);
lws_fi_import(&context->fic, &info->fic);
#endif
@ -635,16 +645,6 @@ lws_create_context(const struct lws_context_creation_info *info)
#endif
#endif
#if defined(LWS_WITH_UDP)
context->udp_loss_sim_tx_pc = info->udp_loss_sim_tx_pc;
context->udp_loss_sim_rx_pc = info->udp_loss_sim_rx_pc;
if (context->udp_loss_sim_tx_pc || context->udp_loss_sim_rx_pc)
lwsl_warn("%s: simulating udp loss tx: %d%%, rx: %d%%\n",
__func__, context->udp_loss_sim_tx_pc,
context->udp_loss_sim_rx_pc);
#endif
#if defined(LWS_WITH_NETWORK)
context->lcg[LWSLCG_WSI].tag_prefix = "wsi";
context->lcg[LWSLCG_VHOST].tag_prefix = "vh";
@ -800,8 +800,9 @@ lws_create_context(const struct lws_context_creation_info *info)
#endif
/* if he gave us names, set the uid / gid */
if (lws_plat_drop_app_privileges(context, 0))
goto bail;
if (lws_plat_drop_app_privileges(context, 0) ||
lws_fi(&context->fic, "ctx_createfail_privdrop"))
goto free_context_fail2;
#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK)
#if defined(LWS_WITH_MBEDTLS)
@ -900,7 +901,7 @@ lws_create_context(const struct lws_context_creation_info *info)
if (n == -1) {
lwsl_err("Get RLIMIT_NOFILE failed!\n");
goto free_context_fail;
goto free_context_fail2;
}
context->max_fds = (unsigned int)rt.rlim_cur;
#else
@ -920,11 +921,12 @@ lws_create_context(const struct lws_context_creation_info *info)
context->max_fds = (unsigned int)l;
}
#endif
if ((int)context->max_fds < 0) {
if ((int)context->max_fds < 0 ||
lws_fi(&context->fic, "ctx_createfail_maxfds")) {
lwsl_err("%s: problem getting process max files\n",
__func__);
goto free_context_fail;
goto free_context_fail2;
}
#endif
@ -1094,7 +1096,7 @@ lws_create_context(const struct lws_context_creation_info *info)
if (!info->ka_interval && info->ka_time > 0) {
lwsl_err("info->ka_interval can't be 0 if ka_time used\n");
return NULL;
goto free_context_fail;
}
#if defined(LWS_WITH_PEER_LIMITS)
@ -1120,9 +1122,13 @@ lws_create_context(const struct lws_context_creation_info *info)
n = (int)(sizeof(struct lws_pollfd) * context->count_threads *
context->fd_limit_per_thread);
context->pt[0].fds = lws_zalloc((unsigned int)n, "fds table");
if (context->pt[0].fds == NULL) {
if (context->pt[0].fds == NULL ||
lws_fi(&context->fic, "ctx_createfail_oom_fds")) {
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_free(context->pt[0].fds);
#endif
lwsl_err("OOM allocating %d fds\n", context->max_fds);
goto bail;
goto free_context_fail;
}
#endif
@ -1163,14 +1169,21 @@ lws_create_context(const struct lws_context_creation_info *info)
* loop and if libuv, have to take care about how to unpick them...
*/
if (lws_plat_init(context, info))
if (lws_plat_init(context, info) ||
lws_fi(&context->fic, "ctx_createfail_plat_init"))
goto bail_libuv_aware;
#if defined(LWS_WITH_NETWORK)
if (lws_fi(&context->fic, "ctx_createfail_evlib_init"))
goto bail_libuv_aware;
if (context->event_loop_ops->init_context)
if (context->event_loop_ops->init_context(context, info))
goto bail_libuv_aware;
if (lws_fi(&context->fic, "ctx_createfail_evlib_pt"))
goto bail_libuv_aware;
if (context->event_loop_ops->init_pt)
for (n = 0; n < context->count_threads; n++) {
@ -1255,7 +1268,10 @@ lws_create_context(const struct lws_context_creation_info *info)
ii.pprotocols = pp;
ii.port = CONTEXT_PORT_NO_LISTEN;
vh = lws_create_vhost(context, &ii);
if (lws_fi(&context->fic, "ctx_createfail_sys_vh"))
vh = NULL;
else
vh = lws_create_vhost(context, &ii);
if (!vh) {
lwsl_err("%s: failed to create system vhost\n",
__func__);
@ -1264,7 +1280,8 @@ lws_create_context(const struct lws_context_creation_info *info)
context->vhost_system = vh;
if (lws_protocol_init_vhost(vh, NULL)) {
if (lws_protocol_init_vhost(vh, NULL) ||
lws_fi(&context->fic, "ctx_createfail_sys_vh_init")) {
lwsl_err("%s: failed to init system vhost\n", __func__);
goto bail_libuv_aware;
}
@ -1309,15 +1326,17 @@ lws_create_context(const struct lws_context_creation_info *info)
* if he's not saying he'll make his own vhosts later then act
* compatibly and make a default vhost using the data in the info
*/
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
if (!lws_create_vhost(context, info)) {
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
if (!lws_create_vhost(context, info) ||
lws_fi(&context->fic, "ctx_createfail_def_vh")) {
lwsl_err("Failed to create default vhost\n");
#if defined(LWS_WITH_PEER_LIMITS)
lws_free_set_NULL(context->pl_hash_table);
#endif
goto fail_clean_pipes;
goto bail;
}
}
#if defined(LWS_WITH_SECURE_STREAMS)
@ -1330,16 +1349,25 @@ lws_create_context(const struct lws_context_creation_info *info)
assert(lws_check_opt(info->options,
LWS_SERVER_OPTION_EXPLICIT_VHOSTS));
if (lws_ss_policy_parse_begin(context, 0))
if (lws_ss_policy_parse_begin(context, 0) ||
lws_fi(&context->fic, "ctx_createfail_ss_pol1")) {
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_ss_policy_parse_abandon(context);
#endif
goto bail_libuv_aware;
}
n = lws_ss_policy_parse(context,
(uint8_t *)context->pss_policies_json,
strlen(context->pss_policies_json));
if (n != LEJP_CONTINUE && n < 0)
if ((n != LEJP_CONTINUE && n < 0) ||
lws_fi(&context->fic, "ctx_createfail_ss_pol2")) {
lws_ss_policy_parse_abandon(context);
goto bail_libuv_aware;
}
if (lws_ss_policy_set(context, "hardcoded")) {
if (lws_ss_policy_set(context, "hardcoded") ||
lws_fi(&context->fic, "ctx_createfail_ss_pol3")) {
lwsl_err("%s: policy set failed\n", __func__);
goto bail_libuv_aware;
}
@ -1348,7 +1376,8 @@ lws_create_context(const struct lws_context_creation_info *info)
if (context->pss_policies) {
/* user code set the policy objects directly, no parsing step */
if (lws_ss_policy_set(context, "hardcoded")) {
if (lws_ss_policy_set(context, "hardcoded") ||
lws_fi(&context->fic, "ctx_createfail_ss_pol3")) {
lwsl_err("%s: policy set failed\n", __func__);
goto bail_libuv_aware;
}
@ -1368,7 +1397,8 @@ lws_create_context(const struct lws_context_creation_info *info)
* listening, we don't want the power for anything else
*/
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
if (lws_plat_drop_app_privileges(context, 1))
if (lws_plat_drop_app_privileges(context, 1) ||
lws_fi(&context->fic, "ctx_createfail_privdrop"))
goto bail_libuv_aware;
#if defined(LWS_WITH_SYS_STATE)
@ -1391,6 +1421,12 @@ lws_create_context(const struct lws_context_creation_info *info)
return context;
early_bail:
lws_fi_destroy(&info->fic);
return NULL;
#if 0
#if defined(LWS_WITH_NETWORK)
fail_clean_pipes:
@ -1410,11 +1446,15 @@ fail_clean_pipes:
return NULL;
#endif
#endif
#if defined(LWS_WITH_NETWORK)
bail:
lws_fi_destroy(&info->fic);
lws_context_destroy(context);
return NULL;
#endif
bail_libuv_aware:
lws_context_destroy(context);
@ -1429,7 +1469,22 @@ fail_event_libs:
lwsl_err("Requested event library support not configured\n");
#endif
#if defined(LWS_WITH_NETWORK)
free_context_fail:
if (context) {
#if defined(LWS_WITH_SYS_SMD)
_lws_smd_destroy(context);
#endif
}
#endif
free_context_fail2:
if (context) {
#if defined(LWS_WITH_SYS_METRICS)
lws_metrics_destroy(context);
#endif
lws_fi_destroy(&context->fic);
}
lws_fi_destroy(&info->fic);
lws_free(context);
return NULL;
@ -1912,6 +1967,7 @@ next:
for (n = 0; n < context->count_threads; n++) {
struct lws_context_per_thread *pt = &context->pt[n];
(void)pt;
#if defined(LWS_WITH_SEQUENCER)
lws_seq_destroy_all_on_pt(pt);
@ -2074,7 +2130,7 @@ next:
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_destroy(&context->fi);
lws_fi_destroy(&context->fic);
#endif
lws_free(context);

View file

@ -1335,13 +1335,18 @@ lws_cmdline_option(int argc, const char **argv, const char *val)
static const char * const builtins[] = {
"-d",
#if defined(LWS_WITH_UDP)
"--udp-tx-loss",
"--udp-rx-loss",
#endif
"--fault-injection",
"--fault-seed",
"--ignore-sigterm"
};
enum opts {
OPT_DEBUGLEVEL,
OPT_FAULTINJECTION,
OPT_FAULT_SEED,
OPT_IGNORE_SIGTERM,
};
#if !defined(LWS_PLAT_FREERTOS)
static void
lws_sigterm_catch(int sig)
@ -1355,6 +1360,9 @@ lws_cmdline_option_handle_builtin(int argc, const char **argv,
{
const char *p;
int n, m, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
uint64_t seed = (uint64_t)lws_now_usecs();
#endif
for (n = 0; n < (int)LWS_ARRAY_SIZE(builtins); n++) {
p = lws_cmdline_option(argc, argv, builtins[n]);
@ -1364,20 +1372,24 @@ lws_cmdline_option_handle_builtin(int argc, const char **argv,
m = atoi(p);
switch (n) {
case 0:
case OPT_DEBUGLEVEL:
logs = m;
break;
#if defined(LWS_WITH_UDP)
case 1:
info->udp_loss_sim_tx_pc = (uint8_t)m;
break;
case 2:
info->udp_loss_sim_rx_pc = (uint8_t)m;
break;
case 3:
#else
case 1:
case OPT_FAULTINJECTION:
#if !defined(LWS_WITH_SYS_FAULT_INJECTION)
lwsl_err("%s: FAULT_INJECTION not built\n", __func__);
#endif
lws_fi_deserialize(&info->fic, p);
break;
case OPT_FAULT_SEED:
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
seed = (uint64_t)atoll(p);
#endif
break;
case OPT_IGNORE_SIGTERM:
#if !defined(LWS_PLAT_FREERTOS)
signal(SIGTERM, lws_sigterm_catch);
#endif
@ -1385,7 +1397,16 @@ lws_cmdline_option_handle_builtin(int argc, const char **argv,
}
}
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_xos_init(&info->fic.xos, seed);
#endif
lws_set_log_level(logs, NULL);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
if (info->fic.fi_owner.count)
lwsl_notice("%s: Fault Injection seed %llu\n", __func__,
(unsigned long long)seed);
#endif
}

View file

@ -524,7 +524,7 @@ struct lws_context {
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t fi;
lws_fi_ctx_t fic;
/**< Toplevel Fault Injection ctx */
#endif
@ -729,8 +729,6 @@ struct lws_context {
uint16_t us_wait_resolution;
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;

View file

@ -33,6 +33,7 @@ include_directories(.)
list(APPEND SOURCES
misc/base64-decode.c
misc/prng.c
misc/lws-ring.c)
if (LWS_WITH_FTS)

80
lib/misc/prng.c Normal file
View file

@ -0,0 +1,80 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 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.
*
* After Public Domain implementations
*
* https://github.com/svaarala/duktape/tree/master/misc
*/
#include <private-lib-core.h>
static inline uint64_t rol64(uint64_t x, int k)
{
return (x << k) | (x >> (64 - k));
}
uint64_t
lws_xos(struct lws_xos *xos)
{
uint64_t *s = &xos->s[0];
uint64_t const result = rol64(s[1] * 5, 7) * 9;
uint64_t const c = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= c;
s[3] = rol64(s[3], 45);
return result;
}
static uint64_t
splitmix64(uint64_t *s)
{
uint64_t r = *s;
*s = r + 0x9E3779B97F4A7C15ull;
r = (r ^ (r >> 30)) * 0xBF58476D1CE4E5B9ull;
r = (r ^ (r >> 27)) * 0x94D049BB133111EBull;
return r ^ (r >> 31);
}
void
lws_xos_init(struct lws_xos *xos, uint64_t seed)
{
int n;
for (n = 0; n < 4; n++)
xos->s[n] = splitmix64(&seed);
}
int
lws_xos_percent(struct lws_xos *xos, int percent)
{
return (int)(lws_xos(xos) % 100) < percent;
}

View file

@ -54,6 +54,7 @@ _lws_plat_service_forced_tsi(struct lws_context *context, int tsi)
__func__, m);
return -1;
}
/* if something closed, retry this slot since may have been
* swapped with end fd */
if (m && pt->fds[n].fd != fd)

View file

@ -1459,6 +1459,9 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
/* pass on the initial headers to SID 1 */
h2n->swsi->http.ah = wsi->http.ah;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_import(&h2n->swsi->fic, &wsi->fic);
#endif
h2n->swsi->client_mux_substream = 1;
h2n->swsi->client_h2_alpn = 1;
#if defined(LWS_WITH_CLIENT)

View file

@ -175,17 +175,23 @@ done_list:
#endif
for (m = 0; m < limit; m++) {
if (lws_fi(&vhost->fic, "listenskt")) {
sockfd = LWS_SOCK_INVALID;
} else {
#ifdef LWS_WITH_UNIX_SOCK
if (LWS_UNIX_SOCK_ENABLED(vhost))
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
else
if (LWS_UNIX_SOCK_ENABLED(vhost))
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
else
#endif
#ifdef LWS_WITH_IPV6
if (LWS_IPV6_ENABLED(vhost))
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
else
if (LWS_IPV6_ENABLED(vhost))
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
else
#endif
sockfd = socket(AF_INET, SOCK_STREAM, 0);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
}
if (sockfd == LWS_SOCK_INVALID) {
lwsl_err("ERROR opening socket\n");

View file

@ -142,20 +142,9 @@ rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi,
}
#if defined(LWS_WITH_UDP)
if (wsi->a.context->udp_loss_sim_rx_pc) {
uint16_t u16;
/*
* We should randomly drop some of these
*/
if (lws_get_random(wsi->a.context, &u16, 2) == 2 &&
((u16 * 100) / 0xffff) <=
wsi->a.context->udp_loss_sim_rx_pc) {
lwsl_warn("%s: dropping udp rx\n", __func__);
/* pretend it was handled */
n = ebuf.len;
goto post_rx;
}
if (lws_fi(&wsi->fic, "udp_rx_loss")) {
n = ebuf.len;
goto post_rx;
}
#endif

View file

@ -1115,6 +1115,21 @@ int
lws_ss_policy_parse_abandon(struct lws_context *context)
{
struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
lws_ss_x509_t *x;
x = args->heads[LTY_X509].x;
while (x) {
/*
* Free all the client DER buffers now they have been parsed
* into tls library X.509 objects
*/
if (!x->keep) { /* used for server */
lws_free((void *)x->ca_der);
x->ca_der = NULL;
}
x = x->next;
}
lejp_destruct(&args->jctx);
lwsac_free(&args->ac);

View file

@ -59,7 +59,7 @@ typedef struct lws_ss_handle {
struct lws_dll2 cli_list; /**< same server clients list */
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t fi; /**< Fault Injection context */
lws_fi_ctx_t fic; /**< Fault Injection context */
#endif
struct lws_dll2_owner src_list; /**< sink's list of bound sources */
@ -285,7 +285,7 @@ typedef struct lws_sspc_handle {
struct lws_ss_serialization_parser parser;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t fi; /**< Fault Injection context */
lws_fi_ctx_t fic; /**< Fault Injection context */
#endif
lws_dll2_owner_t metadata_owner;
@ -402,7 +402,7 @@ lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi,
lws_ss_tx_ordinal_t ord, uint8_t *buf,
size_t *len, int *flags);
int
lws_ss_serialize_state(struct lws_dsh *dsh, lws_ss_constate_t state,
lws_ss_serialize_state(struct lws *wsi, struct lws_dsh *dsh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack);
void

View file

@ -174,7 +174,7 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
break;
case LWS_CALLBACK_RAW_CONNECTED:
if (!h)
if (!h || lws_fi(&h->fic, "sspc_fail_on_linkup"))
return -1;
lwsl_info("%s: CONNECTED (%s)\n", __func__, h->ssi.streamtype);
@ -234,9 +234,18 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
return -1;
}
n = lws_ss_deserialize_parse(&h->parser, lws_get_context(wsi),
h->dsh, in, len, &h->state, h,
(lws_ss_handle_t **)m, &h->ssi, 1);
if (lws_fi(&h->fic, "sspc_fake_rxparse_disconnect_me"))
n = LWSSSSRET_DISCONNECT_ME;
else
if (lws_fi(&h->fic, "sspc_fake_rxparse_destroy_me"))
n = LWSSSSRET_DESTROY_ME;
else
n = lws_ss_deserialize_parse(&h->parser,
lws_get_context(wsi),
h->dsh, in, len,
&h->state, h,
(lws_ss_handle_t **)m,
&h->ssi, 1);
switch (n) {
case LWSSSSRET_OK:
break;
@ -476,7 +485,10 @@ do_write_nz:
break;
do_write:
n = lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);
if (lws_fi(&h->fic, "sspc_link_write_fail"))
n = -1;
else
n = lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);
if (n < 0) {
lwsl_notice("%s: WRITEABLE: %d\n", __func__, n);
@ -530,20 +542,32 @@ lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
* and the streamname */
h = malloc(sizeof(lws_sspc_handle_t) + ssi->user_alloc +
strlen(ssi->streamtype) + 1);
strlen(ssi->streamtype) + 1);
if (!h)
return 1;
memset(h, 0, sizeof(*h));
__lws_lc_tag(&context->lcg[LWSLCG_SSP_CLIENT], &h->lc, ssi->streamtype);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
h->fi.name = "sspc";
h->fi.parent = &context->fi;
if (ssi->fi)
lws_fi_import(&h->fi, ssi->fi);
h->fic.name = "sspc";
lws_xos_init(&h->fic.xos, lws_xos(&context->fic.xos));
if (ssi->fic.fi_owner.count)
lws_fi_import(&h->fic, &ssi->fic);
lws_fi_inherit_copy(&h->fic, &context->fic, "ss", ssi->streamtype);
#endif
if (lws_fi(&h->fic, "sspc_create_oom")) {
/*
* We have to do this a litte later, so we can cleanly inherit
* the OOM pieces and drain the info fic
*/
lws_fi_destroy(&h->fic);
free(h);
return 1;
}
__lws_lc_tag(&context->lcg[LWSLCG_SSP_CLIENT], &h->lc, ssi->streamtype);
memcpy(&h->ssi, ssi, sizeof(*ssi));
ua = (uint8_t *)&h[1];
memset(ua, 0, ssi->user_alloc);
@ -633,7 +657,7 @@ lws_sspc_destroy(lws_sspc_handle_t **ph)
}
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_destroy(&h->fi);
lws_fi_destroy(&h->fic);
#endif
lws_sul_cancel(&h->sul_retry);
@ -823,7 +847,10 @@ _lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,
* We have to stash the metadata and pass it to the proxy
*/
md = lws_malloc(sizeof(*md) + len, "set metadata");
if (lws_fi(&h->fic, "sspc_fail_metadata_set"))
md = NULL;
else
md = lws_malloc(sizeof(*md) + len, "set metadata");
if (!md) {
lwsl_err("%s: OOM\n", __func__);

View file

@ -91,6 +91,8 @@ __lws_ss_proxy_bind_ss_to_conn_wsi(void *parconn, size_t dsh_size)
pt = &conn->wsi->a.context->pt[(int)conn->wsi->tsi];
if (lws_fi(&conn->ss->fic, "ssproxy_dsh_create_oom"))
return -1;
conn->dsh = lws_dsh_create(&pt->ss_dsh_owner, dsh_size, 2);
if (!conn->dsh)
return -1;
@ -100,7 +102,7 @@ __lws_ss_proxy_bind_ss_to_conn_wsi(void *parconn, size_t dsh_size)
return 0;
}
/* secure streams payload interface */
/* Onward secure streams payload interface */
static lws_ss_state_return_t
ss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
@ -120,9 +122,22 @@ ss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
flags |= LWSSS_FLAG_RIDESHARE;
}
n = lws_ss_serialize_rx_payload(m->conn->dsh, buf, len, flags, rsp);
/*
* Apply SSS framing around this chunk of RX and stash it in the dsh
* in ss -> proxy [ -> client] direction. This can fail...
*/
if (lws_fi(&m->ss->fic, "ssproxy_dsh_rx_queue_oom"))
n = 1;
else
n = lws_ss_serialize_rx_payload(m->conn->dsh, buf, len,
flags, rsp);
if (n)
return n;
/*
* We couldn't buffer this rx, eg due to OOM, let's escalate it
* to be a "loss of connection", which it basically is...
*/
return LWSSSSRET_DISCONNECT_ME;
/*
* Manage rx flow on the SS (onward) side according to our situation
@ -141,7 +156,7 @@ ss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
if (m->conn->wsi) /* if possible, request client conn write */
lws_callback_on_writable(m->conn->wsi);
return 0;
return LWSSSSRET_OK;
}
/*
@ -221,6 +236,8 @@ ss_proxy_onward_state(void *userobj, void *sh,
__func__, lws_ss_tag(m->ss),
(unsigned long)dsh_size);
/* this includes ssproxy_dsh_create_oom fault generation */
if (__lws_ss_proxy_bind_ss_to_conn_wsi(m->conn, dsh_size)) {
/* failed to allocate the dsh */
@ -257,7 +274,12 @@ ss_proxy_onward_state(void *userobj, void *sh,
return LWSSSSRET_OK;
}
lws_ss_serialize_state(m->conn->dsh, state, ack);
if (lws_ss_serialize_state(m->conn->wsi, m->conn->dsh, state, ack))
/*
* Failed to alloc state packet that we want to send in dsh,
* we will lose coherence and have to disconnect the link
*/
return LWSSSSRET_DISCONNECT_ME;
if (m->conn->wsi) /* if possible, request client conn write */
lws_callback_on_writable(m->conn->wsi);
@ -280,7 +302,7 @@ ss_proxy_onward_txcr(void *userobj, int bump)
}
/*
* Client - Proxy connection on unix domain socket
* Client <-> Proxy connection, usually on Unix Domain Socket
*/
static int
@ -315,9 +337,14 @@ callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
lwsl_info("LWS_CALLBACK_RAW_ADOPT\n");
if (!pss)
return -1;
pss->conn = malloc(sizeof(struct conn));
if (lws_fi(&wsi->fic, "ssproxy_client_adopt_oom"))
pss->conn = NULL;
else
pss->conn = malloc(sizeof(struct conn));
if (!pss->conn)
return -1;
memset(pss->conn, 0, sizeof(*pss->conn));
/* dsh is allocated when the onward ss is done */
@ -354,8 +381,8 @@ callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
assert(conn->wsi == wsi);
conn->wsi = NULL;
lwsl_notice("%s: cli->prox link %s closing\n",
__func__, lws_wsi_tag(wsi));
lwsl_notice("%s: cli->prox link %s closing\n", __func__,
lws_wsi_tag(wsi));
/* sever relationship with conn */
lws_set_opaque_user_data(wsi, NULL);
@ -518,9 +545,11 @@ callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
conn->state = LPCSPROX_OPERATIONAL;
lws_set_timeout(wsi, 0, 0);
break;
case LPCSPROX_OPERATIONAL:
/*
* returning [onward -> ] proxy]-> client
* rx metadata has priority
*/
@ -546,7 +575,8 @@ callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
p[3] = (uint8_t)naml;
memcpy(&p[4], md->name, naml);
p += 4 + naml;
memcpy(p, md->value__may_own_heap, md->length);
memcpy(p, md->value__may_own_heap,
md->length);
p += md->length;
n = lws_ptr_diff(p, cp);
@ -607,7 +637,10 @@ again:
if (!n)
break;
n = lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);
if (lws_fi(&wsi->fic, "ssproxy_client_write_fail"))
n = -1;
else
n = lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);
if (n < 0) {
lwsl_info("%s: WRITEABLE: %d\n", __func__, n);

View file

@ -262,7 +262,7 @@ lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi,
*/
int
lws_ss_serialize_state(struct lws_dsh *dsh, lws_ss_constate_t state,
lws_ss_serialize_state(struct lws *wsi, struct lws_dsh *dsh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack)
{
uint8_t pre[12];
@ -285,7 +285,8 @@ lws_ss_serialize_state(struct lws_dsh *dsh, lws_ss_constate_t state,
lws_ser_wu32be(&pre[n], ack);
if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, (unsigned int)n + 4, NULL, 0)) {
if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, (unsigned int)n + 4, NULL, 0) ||
(wsi && lws_fi(&wsi->fic, "sspc_dsh_ss2p_oom"))) {
lwsl_err("%s: unable to alloc in dsh 2\n", __func__);
return 1;
@ -428,7 +429,9 @@ lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par,
* We're going to try to do the onward connect
*/
if (_lws_ss_client_connect(proxy_pss_to_ss_h(pss),
if ((proxy_pss_to_ss_h(pss) &&
lws_fi(&proxy_pss_to_ss_h(pss)->fic, "ssproxy_onward_conn_fail")) ||
_lws_ss_client_connect(proxy_pss_to_ss_h(pss),
0, parconn) ==
LWSSSSRET_DESTROY_ME)
goto hangup;
@ -685,7 +688,9 @@ payload_ff:
/* time used later to find proxy hold time */
lws_ser_wu64be(&p[15], (uint64_t)us);
if (lws_dsh_alloc_tail(dsh, KIND_C_TO_P, pre,
if ((proxy_pss_to_ss_h(pss) &&
lws_fi(&proxy_pss_to_ss_h(pss)->fic, "ssproxy_dsh_c2p_pay_oom")) ||
lws_dsh_alloc_tail(dsh, KIND_C_TO_P, pre,
23, cp, (unsigned int)n)) {
lwsl_err("%s: unable to alloc in dsh 3\n",
__func__);
@ -715,6 +720,11 @@ payload_ff:
/* we still have an sspc handle */
int ret = ssi->rx(client_pss_to_userdata(pss),
(uint8_t *)cp, (unsigned int)n, (int)flags);
if (client_pss_to_sspc_h(pss, ssi) &&
lws_fi(&client_pss_to_sspc_h(pss, ssi)->fic, "sspc_rx_fake_destroy_me"))
ret = LWSSSSRET_DESTROY_ME;
switch (ret) {
case LWSSSSRET_OK:
break;
@ -1005,7 +1015,10 @@ payload_ff:
* Create the client's rx metadata entry
*/
md = lws_malloc(sizeof(lws_sspc_metadata_t) +
if (h && lws_fi(&h->fic, "sspc_rx_metadata_oom"))
md = NULL;
else
md = lws_malloc(sizeof(lws_sspc_metadata_t) +
par->rem + 1, "rxmeta");
if (!md) {
lwsl_err("%s: OOM\n", __func__);
@ -1057,7 +1070,13 @@ payload_ff:
lws_free_set_NULL(par->ssmd->value__may_own_heap);
par->ssmd->value_on_lws_heap = 0;
par->ssmd->value__may_own_heap = lws_malloc((unsigned int)par->rem + 1, "metadata");
if (proxy_pss_to_ss_h(pss) &&
lws_fi(&proxy_pss_to_ss_h(pss)->fic, "ssproxy_rx_metadata_oom"))
par->ssmd->value__may_own_heap = NULL;
else
par->ssmd->value__may_own_heap =
lws_malloc((unsigned int)par->rem + 1, "metadata");
if (!par->ssmd->value__may_own_heap) {
lwsl_err("%s: OOM mdv\n", __func__);
goto hangup;

View file

@ -747,7 +747,29 @@ lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
pol = ssi->policy;
if (!pol) {
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t temp_fic;
/*
* We have to do a temp inherit from context to find out
* early if we are supposed to inject a fault concealing
* the policy
*/
memset(&temp_fic, 0, sizeof(temp_fic));
lws_xos_init(&temp_fic.xos, lws_xos(&context->fic.xos));
lws_fi_inherit_copy(&temp_fic, &context->fic, "ss", ssi->streamtype);
if (lws_fi(&temp_fic, "ss_no_streamtype_policy"))
pol = NULL;
else
pol = lws_ss_policy_lookup(context, ssi->streamtype);
lws_fi_destroy(&temp_fic);
#else
pol = lws_ss_policy_lookup(context, ssi->streamtype);
#endif
if (!pol) {
lwsl_info("%s: unknown stream type %s\n", __func__,
ssi->streamtype);
@ -821,10 +843,12 @@ lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
ssi->streamtype ? ssi->streamtype : "nostreamtype");
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
h->fi.name = "ss";
h->fi.parent = &context->fi;
if (ssi->fi)
lws_fi_import(&h->fi, ssi->fi);
h->fic.name = "ss";
lws_xos_init(&h->fic.xos, lws_xos(&context->fic.xos));
if (ssi->fic.fi_owner.count)
lws_fi_import(&h->fic, &ssi->fic);
lws_fi_inherit_copy(&h->fic, &context->fic, "ss", ssi->streamtype);
#endif
h->info = *ssi;
@ -980,7 +1004,10 @@ lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
}
#endif
vho = lws_create_vhost(context, &i);
if (lws_fi(&ssi->fic, "ss_srv_vh_fail"))
vho = NULL;
else
vho = lws_create_vhost(context, &i);
if (!vho) {
lwsl_err("%s: failed to create vh", __func__);
goto late_bail;
@ -1037,6 +1064,7 @@ late_bail:
lws_dll2_remove(&h->list);
lws_pt_unlock(pt);
lws_fi_destroy(&h->fic);
__lws_lc_untag(&h->lc);
lws_free(h);
@ -1199,7 +1227,7 @@ lws_ss_destroy(lws_ss_handle_t **ppss)
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_destroy(&h->fi);
lws_fi_destroy(&h->fic);
#endif
#if defined(LWS_WITH_SYS_METRICS)

View file

@ -28,7 +28,7 @@
static const uint32_t botable[] = { 300, 500, 700, 1250, 5000
/* in case everything just dog slow */ };
static const lws_retry_bo_t retry_policy = {
botable, LWS_ARRAY_SIZE(botable), LWS_ARRAY_SIZE(botable),
botable, LWS_ARRAY_SIZE(botable), LWS_RETRY_CONCEAL_ALWAYS,
/* don't conceal after the last table entry */ 0, 0, 20 };
void
@ -371,7 +371,7 @@ ok:
dns->wsi = lws_create_adopt_udp(context->vhost_list, ads, 53, 0,
lws_async_dns_protocol.name, NULL,
NULL, NULL, &retry_policy);
NULL, NULL, &retry_policy, "asyncdns");
if (!dns->wsi) {
lwsl_err("%s: foreign socket adoption failed\n", __func__);
return 1;

View file

@ -299,7 +299,7 @@ lws_dhcpc4_retry_conn(struct lws_sorted_usec_list *sul)
68, LWS_CAUDP_PF_PACKET |
LWS_CAUDP_BROADCAST,
"lws-dhcp4client", (const char *)&r[1],
NULL, NULL, &bo2);
NULL, NULL, &bo2, "dhcpc");
lwsl_debug("%s: created wsi_raw: %s\n", __func__, lws_wsi_tag(r->wsi_raw));
if (!r->wsi_raw) {
lwsl_err("%s: unable to create udp skt\n", __func__);

View file

@ -26,9 +26,8 @@
#include <assert.h>
static lws_fi_priv_t *
lws_fi_lookup(lws_fi_ctx_t *fic, const char *name)
lws_fi_lookup(const lws_fi_ctx_t *fic, const char *name)
{
lws_start_foreach_dll(struct lws_dll2 *, p, fic->fi_owner.head) {
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
@ -42,58 +41,81 @@ lws_fi_lookup(lws_fi_ctx_t *fic, const char *name)
}
int
lws_fi(lws_fi_ctx_t *fic, const char *name)
lws_fi(const lws_fi_ctx_t *fic, const char *name)
{
lws_fi_priv_t *pv = NULL;
lws_fi_priv_t *pv;
int n;
do {
pv = lws_fi_lookup(fic, name);
pv = lws_fi_lookup(fic, name);
if (pv) {
int n;
if (!pv)
return 0;
switch (pv->fi.type) {
case LWSFI_ALWAYS:
switch (pv->fi.type) {
case LWSFI_ALWAYS:
goto inject;
case LWSFI_DETERMINISTIC:
pv->fi.times++;
if (pv->fi.times >= pv->fi.pre)
if (pv->fi.times < pv->fi.pre + pv->fi.count)
goto inject;
return 0;
case LWSFI_DETERMINISTIC:
pv->fi.times++;
if (pv->fi.times >= pv->fi.pre)
if (pv->fi.times < pv->fi.pre + pv->fi.count)
goto inject;
return 0;
case LWSFI_PROBABILISTIC:
if (lws_xos_percent((lws_xos_t *)&fic->xos, (int)pv->fi.pre))
goto inject;
return 0;
case LWSFI_PROBABILISTIC:
pv->fi.times = (unsigned long)(pv->fi.times * 3) ^
(unsigned long)lws_now_usecs();
if ((uint16_t)pv->fi.times % 101 >= pv->fi.pre)
goto inject;
return 0;
case LWSFI_PATTERN:
case LWSFI_PATTERN_ALLOC:
n = (int)((pv->fi.times++) % pv->fi.count);
if (pv->fi.pattern[n >> 3] & (1 << (n & 7)))
goto inject;
case LWSFI_PATTERN:
n = (int)(pv->fi.times % pv->fi.pre);
if (pv->fi.pattern[n >> 3] & (1 << (n & 7)))
goto inject;
return 0;
return 0;
default:
return 0;
}
}
fic = fic->parent;
} while (fic);
default:
return 0;
}
return 0;
inject:
lwsl_warn("%s: Injecting fault %s->%s\n", __func__, fic->name,
pv->fi.name);
lwsl_warn("%s: Injecting fault %s->%s\n", __func__,
fic->name ? fic->name : "unk", pv->fi.name);
return 1;
}
int
_lws_fi_user_wsi_fi(struct lws *wsi, const char *name)
{
return lws_fi(&wsi->fic, name);
}
int
_lws_fi_user_context_fi(struct lws_context *ctx, const char *name)
{
return lws_fi(&ctx->fic, name);
}
#if defined(LWS_WITH_SECURE_STREAMS)
int
_lws_fi_user_ss_fi(struct lws_ss_handle *h, const char *name)
{
return lws_fi(&h->fic, name);
}
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
int
_lws_fi_user_sspc_fi(struct lws_sspc_handle *h, const char *name)
{
return lws_fi(&h->fic, name);
}
#endif
#endif
int
lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi)
{
@ -130,7 +152,12 @@ lws_fi_remove(lws_fi_ctx_t *fic, const char *name)
void
lws_fi_import(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src)
{
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, fic_src->fi_owner.head) {
/* inherit the PRNG seed for our context from source guy too */
lws_xos_init(&fic_dest->xos, lws_xos((lws_xos_t *)&fic_src->xos));
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
fic_src->fi_owner.head) {
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
lws_dll2_remove(&pv->list);
@ -139,14 +166,210 @@ lws_fi_import(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src)
} lws_end_foreach_dll_safe(p, p1);
}
void
lws_fi_destroy(lws_fi_ctx_t *fic)
static void
do_inherit(lws_fi_ctx_t *fic_dest, lws_fi_t *pfi, size_t trim)
{
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, fic->fi_owner.head) {
lws_fi_t fi = *pfi;
fi.name += trim;
lwsl_info("%s: %s: %s inherited as %s\n", __func__, fic_dest->name,
pfi->name, fi.name);
if (fi.type == LWSFI_PATTERN_ALLOC) {
fi.pattern = lws_malloc((size_t)((fi.count >> 3) + 1), __func__);
if (!fi.pattern)
return;
memcpy((uint8_t *)fi.pattern, pfi->pattern,
(size_t)((fi.count >> 3) + 1));
}
lws_fi_add(fic_dest, &fi);
}
void
lws_fi_inherit_copy(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src,
const char *scope, const char *value)
{
size_t sl = 0, vl = 0;
if (scope)
sl = strlen(scope);
if (value)
vl = strlen(value);
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
fic_src->fi_owner.head) {
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
size_t nl = strlen(pv->fi.name);
if (!scope)
do_inherit(fic_dest, &pv->fi, 0);
else
if (nl > sl + 2 &&
!strncmp(pv->fi.name, scope, sl) &&
pv->fi.name[sl] == '/')
do_inherit(fic_dest, &pv->fi, sl + 1);
else {
if (value && nl > sl + vl + 2 &&
pv->fi.name[sl] == '=' &&
!strncmp(pv->fi.name + sl + 1, value, vl) &&
pv->fi.name[sl + 1 + vl] == '/')
do_inherit(fic_dest, &pv->fi, sl + vl + 2);
}
} lws_end_foreach_dll_safe(p, p1);
}
void
lws_fi_destroy(const lws_fi_ctx_t *fic)
{
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
fic->fi_owner.head) {
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
if (pv->fi.type == LWSFI_PATTERN_ALLOC && pv->fi.pattern) {
lws_free((void *)pv->fi.pattern);
pv->fi.pattern = NULL;
}
lws_dll2_remove(&pv->list);
lws_free(pv);
} lws_end_foreach_dll_safe(p, p1);
}
enum {
PARSE_NAME,
PARSE_WHEN,
PARSE_PC,
PARSE_ENDBR
};
void
lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers)
{
struct lws_tokenize ts;
lws_fi_t fi;
char nm[64];
int state = PARSE_NAME;
/*
* Go through the comma-separated list of faults
* creating them and adding to the lws_context info
*/
lws_tokenize_init(&ts, sers, LWS_TOKENIZE_F_DOT_NONTERM |
LWS_TOKENIZE_F_NO_INTEGERS |
LWS_TOKENIZE_F_NO_FLOATS |
LWS_TOKENIZE_F_EQUALS_NONTERM |
LWS_TOKENIZE_F_SLASH_NONTERM |
LWS_TOKENIZE_F_MINUS_NONTERM);
ts.len = (unsigned int)strlen(sers);
if (ts.len < 1 || ts.len > 10240)
return;
do {
ts.e = (int8_t)lws_tokenize(&ts);
switch (ts.e) {
case LWS_TOKZE_TOKEN:
if (state == PARSE_NAME) {
/*
* One fault to inject looks like, eg,
*
* vh=xxx/listenskt
*/
memset(&fi, 0, sizeof(fi));
lws_strnncpy(nm, ts.token, ts.token_len, sizeof(nm));
fi.name = nm;
fi.type = LWSFI_ALWAYS;
lwsl_notice("%s: name %.*s\n", __func__, (int)ts.token_len, ts.token);
/* added later, potentially after (when) */
break;
}
if (state == PARSE_WHEN) {
/* it's either numeric or a pattern */
lwsl_notice("%s: when\n", __func__);
if (*ts.token == '.' || *ts.token == 'X') {
uint8_t *pat;
size_t n;
/*
* pattern... we need to allocate it
*/
fi.type = LWSFI_PATTERN_ALLOC;
pat = lws_zalloc((ts.token_len >> 3) + 1, __func__);
if (!pat)
return;
fi.pattern = pat;
fi.count = (uint64_t)ts.token_len;
for (n = 0; n < ts.token_len; n++)
if (ts.token[n] == 'X')
pat[n >> 3] = (uint8_t)(
pat[n >> 3] | (1 << (n & 7)));
lwsl_hexdump_notice(pat, (ts.token_len >> 3) + 1);
state = PARSE_ENDBR;
break;
}
fi.pre = (uint64_t)atoi(ts.token);
lwsl_notice("%s: prob %d%%\n", __func__, (int)fi.pre);
fi.type = LWSFI_PROBABILISTIC;
state = PARSE_PC;
break;
}
break;
case LWS_TOKZE_DELIMITER:
if (*ts.token == ',') {
lws_fi_add(fic, &fi);
state = PARSE_NAME;
break;
}
if (*ts.token == '(') {
lwsl_notice("%s: (\n", __func__);
if (state != PARSE_NAME) {
lwsl_err("%s: misplaced (\n", __func__);
return;
}
state = PARSE_WHEN;
break;
}
if (*ts.token == ')') {
if (state != PARSE_ENDBR) {
lwsl_err("%s: misplaced )\n", __func__);
return;
}
state = PARSE_NAME;
break;
}
if (*ts.token == '%') {
if (state != PARSE_PC) {
lwsl_err("%s: misplaced %%\n", __func__);
return;
}
state = PARSE_ENDBR;
break;
}
break;
case LWS_TOKZE_ENDED:
lws_fi_add(fic, &fi);
return;
default:
return;
}
} while (ts.e > 0);
}

View file

@ -83,7 +83,7 @@ lws_ntpc_retry_conn(struct lws_sorted_usec_list *sul)
v->retry_count_write = 0;
v->wsi_udp = lws_create_adopt_udp(v->vhost, v->ntp_server_ads, 123, 0,
v->protocol->name, NULL, NULL, NULL,
&bo2);
&bo2, "ntpclient");
lwsl_debug("%s: created wsi_udp: %s\n", __func__, lws_wsi_tag(v->wsi_udp));
if (!v->wsi_udp) {
lwsl_err("%s: unable to create udp skt\n", __func__);

View file

@ -1,7 +1,7 @@
/*
* lws-api-test-lws_dsh
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
@ -252,7 +252,7 @@ test4(void)
memset(blob, 0, sizeof(blob));
/*
* test 1: use up whole free list, then recover and alloc something
* test 4: use up whole free list, then recover and alloc something
* else
*/
@ -327,6 +327,92 @@ bail:
return 1;
}
int
test5(void)
{
struct lws_dsh *dsh;
unsigned int budget;
uint8_t blob[4096];
lws_xos_t xos;
size_t size;
void *a1;
memset(blob, 0, sizeof(blob));
lws_xos_init(&xos, 0x123456789abcdef0ull);
budget = (unsigned int)(lws_xos(&xos) % 4000) + 4000;
lwsl_notice("%s: budget %u\n", __func__, budget);
/*
* test 5: PRNG-based spamming and erratic bidi draining
*/
dsh = lws_dsh_create(NULL, 409600, 2);
if (!dsh) {
lwsl_err("%s: Failed to create dsh\n", __func__);
return 1;
}
do {
if (lws_xos_percent(&xos, 60)) {
/* kind 0 is going to try to write */
size = (size_t)((lws_xos(&xos) & 127) + 1);
if (!lws_dsh_alloc_tail(dsh, 0, blob, size, NULL, 0))
lwsl_notice("%s: kind 0 alloc %d\n", __func__, (int)size);
}
if (lws_xos_percent(&xos, 80)) {
/* kind 1 is going to try to write */
size = (size_t)((lws_xos(&xos) & 127) + 1);
if (!lws_dsh_alloc_tail(dsh, 1, blob, size, NULL, 0))
lwsl_notice("%s: kind 1 alloc %d\n", __func__, (int)size);
}
if (lws_xos_percent(&xos, 40)) {
/* kind 0 is going to try to read */
while (!lws_dsh_get_head(dsh, 0, &a1, &size)) {
lwsl_notice("%s: kind 0 read %d\n", __func__, (int)size);
lws_dsh_free(&a1);
}
}
if (lws_xos_percent(&xos, 30)) {
/* kind 1 is going to try to read */
while (!lws_dsh_get_head(dsh, 1, &a1, &size)) {
lwsl_notice("%s: kind 1 read %d\n", __func__, (int)size);
lws_dsh_free(&a1);
}
}
} while (budget--);
while (!lws_dsh_get_head(dsh, 0, &a1, &size)) {
lwsl_notice("%s: kind 0 read %d\n", __func__, (int)size);
lws_dsh_free(&a1);
}
while (!lws_dsh_get_head(dsh, 1, &a1, &size)) {
lwsl_notice("%s: kind 1 read %d\n", __func__, (int)size);
lws_dsh_free(&a1);
}
lws_dsh_describe(dsh, "test dsh end state");
lws_dsh_destroy(&dsh);
return 0;
}
int main(int argc, const char **argv)
{
int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
@ -355,6 +441,10 @@ int main(int argc, const char **argv)
lwsl_user("%s: test4: %d\n", __func__, n);
ret |= n;
n = test5();
lwsl_user("%s: test5: %d\n", __func__, n);
ret |= n;
lwsl_user("Completed: %s\n", ret ? "FAIL" : "PASS");
return ret;

View file

@ -794,8 +794,7 @@ done:
return 0;
bail:
if (test2())
return 1;
lwsl_user("Completed: FAIL\n");
return 1;

View file

@ -1,7 +1,7 @@
/*
* lws-api-test-lws_tokenize
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
@ -173,6 +173,10 @@ struct expected expected1[] = {
expected17[] = {
{ LWS_TOKZE_TOKEN, "hello", 5 },
{ LWS_TOKZE_ENDED, "", 0 },
},
expected18[] = {
{ LWS_TOKZE_TOKEN, "x=y", 3 },
{ LWS_TOKZE_ENDED, "", 0 },
}
;
@ -253,6 +257,10 @@ struct tests tests[] = {
{
"# comment1\r\nhello #comment2\r\n#comment3", expected17,
LWS_ARRAY_SIZE(expected17), LWS_TOKENIZE_F_HASH_COMMENT
},
{
"x=y", expected18,
LWS_ARRAY_SIZE(expected18), LWS_TOKENIZE_F_EQUALS_NONTERM
}
};

View file

@ -1,37 +0,0 @@
project(lws-minimal-http-client-fi C)
cmake_minimum_required(VERSION 2.8.12)
find_package(libwebsockets CONFIG REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
include(CheckCSourceCompiles)
include(LwsCheckRequirements)
set(SAMP lws-minimal-http-client-fi)
set(SRCS minimal-http-client.c)
set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITH_CLIENT 1 requirements)
require_lws_config(LWS_WITH_SYS_STATE 1 requirements)
require_lws_config(LWS_WITH_SYS_FAULT_INJECTION 1 requirements)
if (requirements)
add_executable(${SAMP} ${SRCS})
# if (LWS_CTEST_INTERNET_AVAILABLE)
# add_test(NAME http-client-warmcat COMMAND lws-minimal-http-client )
# add_test(NAME http-client-warmcat-h1 COMMAND lws-minimal-http-client --h1)
# set_tests_properties(http-client-warmcat
# http-client-warmcat-h1
# PROPERTIES
# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/http-client/minimal-http-client
# TIMEOUT 20)
#
#endif()
if (websockets_shared)
target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
add_dependencies(${SAMP} websockets_shared)
else()
target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})
endif()
endif()

View file

@ -1,76 +0,0 @@
# lws minimal http client
The application goes to either https://warmcat.com or
https://localhost:7681 (with `-l` option) and receives the page data.
## build
```
$ cmake . && make
```
## usage
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-l| Connect to https://localhost:7681 and accept selfsigned cert
--h1|Specify http/1.1 only using ALPN, rejects h2 even if server supports it
--server <name>|set server name to connect to
-k|Apply tls option LCCSCF_ALLOW_INSECURE
-j|Apply tls option LCCSCF_ALLOW_SELFSIGNED
-m|Apply tls option LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK
-e|Apply tls option LCCSCF_ALLOW_EXPIRED
-v|Connection validity use 3s / 10s instead of default 5m / 5m10s
--nossl| disable ssl connection
--user <username>| Set Basic Auth username
--password <password> | Set Basic Auth password
```
$ ./lws-minimal-http-client
[2018/03/04 14:43:20:8562] USER: LWS minimal http client
[2018/03/04 14:43:20:8571] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 on
[2018/03/04 14:43:20:8616] NOTICE: created client ssl context for default
[2018/03/04 14:43:20:8617] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com
[2018/03/04 14:43:21:1496] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com
[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: incoming content length 26520
[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: client connection up
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 974
[2018/03/04 14:43:22:3022] NOTICE: lws_http_client_read: transaction completed says -1
[2018/03/04 14:43:23:3042] USER: Completed
```
You can also test the client Basic Auth support against the http-server/minimal-http-server-basicauth
example. In one console window run the server and in the other
```
$ lws-minimal-http-client -l --nossl --path /secret/index.html --user user --password password
```
The Basic Auth credentials for the test server are literally username "user" and password "password".

View file

@ -1,346 +0,0 @@
/*
* lws-minimal-http-client
*
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates the a minimal http client using lws.
*
* It visits https://warmcat.com/ and receives the html page there. You
* can dump the page data by changing the #if 0 below.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
static int interrupted, bad = 1, status;
#if defined(LWS_WITH_HTTP2)
static int long_poll;
#endif
static struct lws *client_wsi;
static const char *ba_user, *ba_password;
static const lws_retry_bo_t retry = {
.secs_since_valid_ping = 3,
.secs_since_valid_hangup = 10,
};
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 = (int)lws_http_client_http_response(wsi);
lwsl_user("Connected to %s, http response: %d\n",
buf, status);
}
#if defined(LWS_WITH_HTTP2)
if (long_poll) {
lwsl_user("%s: Client entering long poll mode\n", __func__);
lws_h2_client_stream_long_poll_rxonly(wsi);
}
#endif
break;
#if defined(LWS_WITH_HTTP_BASIC_AUTH)
/* you only need this if you need to do Basic Auth */
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
{
unsigned char **p = (unsigned char **)in, *end = (*p) + len;
char b[128];
if (!ba_user || !ba_password)
break;
if (lws_http_basic_auth_gen(ba_user, ba_password, b, sizeof(b)))
break;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_AUTHORIZATION,
(unsigned char *)b, (int)strlen(b), p, end))
return -1;
break;
}
#endif
/* 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 defined(LWS_WITH_HTTP2)
if (long_poll) {
char dotstar[128];
lws_strnncpy(dotstar, (const char *)in, len,
sizeof(dotstar));
lwsl_notice("long poll rx: %d '%s'\n", (int)len,
dotstar);
}
#endif
#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);
}
static const struct lws_protocols protocols[] = {
{
"http",
callback_http,
0,
0,
},
{ NULL, NULL, 0, 0 }
};
static void
sigint_handler(int sig)
{
interrupted = 1;
}
struct args {
int argc;
const char **argv;
};
static const lws_fi_t fi = {
.name = "cliwsi.dnsfail",
.type = LWSFI_ALWAYS
};
static int
make_client_connection(struct lws_context *context)
{
struct lws_client_connect_info i;
struct args *a = lws_context_user(context);
lws_fi_ctx_t fic;
const char *p;
memset(&fic, 0, sizeof(fic));
lws_fi_add(&fic, &fi);
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
i.fi = &fic;
if (!lws_cmdline_option(a->argc, a->argv, "-n")) {
i.ssl_connection = LCCSCF_USE_SSL;
#if defined(LWS_WITH_HTTP2)
/* requires h2 */
if (lws_cmdline_option(a->argc, a->argv, "--long-poll")) {
lwsl_user("%s: long poll mode\n", __func__);
long_poll = 1;
}
#endif
}
if (lws_cmdline_option(a->argc, a->argv, "-l")) {
i.port = 7681;
i.address = "localhost";
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
} else {
i.port = 443;
i.address = "warmcat.com";
}
if (lws_cmdline_option(a->argc, a->argv, "--nossl"))
i.ssl_connection = 0;
i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR |
LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM;
i.alpn = "h2";
if (lws_cmdline_option(a->argc, a->argv, "--h1"))
i.alpn = "http/1.1";
if (lws_cmdline_option(a->argc, a->argv, "--h2-prior-knowledge"))
i.ssl_connection |= LCCSCF_H2_PRIOR_KNOWLEDGE;
if ((p = lws_cmdline_option(a->argc, a->argv, "-p")))
i.port = atoi(p);
if ((p = lws_cmdline_option(a->argc, a->argv, "--user")))
ba_user = p;
if ((p = lws_cmdline_option(a->argc, a->argv, "--password")))
ba_password = p;
if (lws_cmdline_option(a->argc, a->argv, "-j"))
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
if (lws_cmdline_option(a->argc, a->argv, "-k"))
i.ssl_connection |= LCCSCF_ALLOW_INSECURE;
if (lws_cmdline_option(a->argc, a->argv, "-m"))
i.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
if (lws_cmdline_option(a->argc, a->argv, "-e"))
i.ssl_connection |= LCCSCF_ALLOW_EXPIRED;
if ((p = lws_cmdline_option(a->argc, a->argv, "-f"))) {
i.ssl_connection |= LCCSCF_H2_MANUAL_RXFLOW;
i.manual_initial_tx_credit = atoi(p);
lwsl_notice("%s: manual peer tx credit %d\n", __func__,
i.manual_initial_tx_credit);
}
/* the default validity check is 5m / 5m10s... -v = 3s / 10s */
if (lws_cmdline_option(a->argc, a->argv, "-v"))
i.retry_and_idle_policy = &retry;
if ((p = lws_cmdline_option(a->argc, a->argv, "--server")))
i.address = p;
if ((p = lws_cmdline_option(a->argc, a->argv, "--path")))
i.path = p;
else
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.method = "GET";
i.protocol = protocols[0].name;
i.pwsi = &client_wsi;
return !lws_client_connect_via_info(&i);
}
static int
system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
int current, int target)
{
struct lws_context *context = mgr->parent;
if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL)
return 0;
lwsl_info("%s: operational\n", __func__);
make_client_connection(context);
return 0;
}
int main(int argc, const char **argv)
{
lws_state_notify_link_t notifier = { {0}, system_notify_cb, "app" };
lws_state_notify_link_t *na[] = { &notifier, NULL };
struct lws_context_creation_info info;
struct lws_context *context;
struct args args;
int n = 0;
// uint8_t memcert[4096];
args.argc = argc;
args.argv = argv;
signal(SIGINT, sigint_handler);
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
lws_cmdline_option_handle_builtin(argc, argv, &info);
lwsl_user("LWS minimal http client [-d<verbosity>] [-l] [--h1]\n");
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
info.user = &args;
info.register_notifier_list = na;
info.connect_timeout_secs = 30;
/*
* since we know this lws context is only ever going to be used with
* one client wsis / fds / sockets at a time, let lws know it doesn't
* have to use the default allocations for fd tables up to ulimit -n.
* It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
* will use.
*/
info.fd_limit_per_thread = 1 + 1 + 1;
#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL)
/*
* OpenSSL uses the system trust store. mbedTLS has to be told which
* CA to trust explicitly.
*/
info.client_ssl_ca_filepath = "./warmcat.com.cer";
#endif
#if 0
n = open("./warmcat.com.cer", O_RDONLY);
if (n >= 0) {
info.client_ssl_ca_mem_len = read(n, memcert, sizeof(memcert));
info.client_ssl_ca_mem = memcert;
close(n);
n = 0;
memcert[info.client_ssl_ca_mem_len++] = '\0';
}
#endif
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
return bad;
}

View file

@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

View file

@ -8,16 +8,37 @@ include(LwsCheckRequirements)
set(SAMP lws-minimal-http-client)
set(SRCS minimal-http-client.c)
set(has_fault_injection 1)
set(has_h2 1)
set(has_plugins 1)
set(has_ss_policy_parse 1)
set(has_no_system_vhost 1)
set(has_async_dns 1)
set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITH_CLIENT 1 requirements)
require_lws_config(LWS_WITH_SYS_STATE 1 requirements)
require_lws_config(LWS_ROLE_H2 1 has_h2)
require_lws_config(LWS_WITH_SYS_FAULT_INJECTION 1 has_fault_injection)
require_lws_config(LWS_WITH_EVLIB_PLUGINS 1 has_plugins)
require_lws_config(LWS_WITH_EVENT_LIBS 1 has_plugins)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 has_ss_policy_parse)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 has_ss_policy_parse)
require_lws_config(LWS_WITH_SYS_ASYNC_DNS 0 has_no_system_vhost)
require_lws_config(LWS_WITH_SYS_NTPCLIENT 0 has_no_system_vhost)
require_lws_config(LWS_WITH_SYS_DHCP_CLIENT 0 has_no_system_vhost)
require_lws_config(LWS_WITH_SYS_ASYNC_DNS 1 has_async_dns)
if (requirements)
add_executable(${SAMP} ${SRCS})
find_program(VALGRIND "valgrind")
sai_resource(warmcat_conns 1 40 http_client_warmcat)
@ -27,15 +48,102 @@ if (requirements)
add_test(NAME http-client-warmcat COMMAND lws-minimal-http-client )
list(APPEND mytests http-client-warmcat)
endif()
add_test(NAME http-client-warmcat-h1 COMMAND lws-minimal-http-client --h1)
set_tests_properties(${mytests} PROPERTIES
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/http-client/minimal-http-client
TIMEOUT 20)
if (has_fault_injection)
# creation related faults
list(APPEND mytests http-client-fi-ctx1)
add_test(NAME http-client-fi-ctx1 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail1")
# if (has_plugins)
# !!! need to actually select an available evlib plugin to trigger this
# list(APPEND mytests http-client-fi-pi)
# add_test(NAME http-client-fi-pi COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_plugin_init")
# endif()
list(APPEND mytests http-client-fi-ctx2)
add_test(NAME http-client-fi-ctx2 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_evlib_sel")
list(APPEND mytests http-client-fi-ctx3)
add_test(NAME http-client-fi-ctx3 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_oom_ctx")
list(APPEND mytests http-client-fi-ctx4)
add_test(NAME http-client-fi-ctx4 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_privdrop")
list(APPEND mytests http-client-fi-ctx5)
add_test(NAME http-client-fi-ctx5 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_maxfds")
list(APPEND mytests http-client-fi-ctx6)
add_test(NAME http-client-fi-ctx6 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_oom_fds")
list(APPEND mytests http-client-fi-ctx7)
add_test(NAME http-client-fi-ctx7 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_plat_init")
list(APPEND mytests http-client-fi-ctx8)
add_test(NAME http-client-fi-ctx8 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_evlib_init")
list(APPEND mytests http-client-fi-ctx9)
add_test(NAME http-client-fi-ctx9 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_evlib_pt")
if (NOT has_no_system_vhost)
list(APPEND mytests http-client-fi-ctx10)
add_test(NAME http-client-fi-ctx10 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_sys_vh")
list(APPEND mytests http-client-fi-ctx11)
add_test(NAME http-client-fi-ctx11 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_sys_vh_init")
endif()
list(APPEND mytests http-client-fi-ctx12)
add_test(NAME http-client-fi-ctx12 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "ctx_createfail_def_vh")
list(APPEND mytests http-client-fi-vh1)
add_test(NAME http-client-fi-vh1 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "vh/vh_create_oom")
list(APPEND mytests http-client-fi-vh2)
add_test(NAME http-client-fi-vh2 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "vh/vh_create_pcols_oom")
list(APPEND mytests http-client-fi-vh3)
add_test(NAME http-client-fi-vh3 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "vh/vh_create_ssl_srv")
list(APPEND mytests http-client-fi-vh4)
add_test(NAME http-client-fi-vh4 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "vh/vh_create_ssl_cli")
list(APPEND mytests http-client-fi-vh5)
add_test(NAME http-client-fi-vh5 COMMAND lws-minimal-http-client --expected-exit 5 --fault-injection "vh/vh_create_srv_init")
list(APPEND mytests http-client-fi-dnsfail)
add_test(NAME http-client-fi-dnsfail COMMAND lws-minimal-http-client --expected-exit 3 --fault-injection "wsi=user/dnsfail")
if (has_async_dns)
list(APPEND mytests http-client-fi-connfail)
add_test(NAME http-client-fi-connfail COMMAND lws-minimal-http-client --expected-exit 3 --fault-injection "wsi=user/connfail")
else()
list(APPEND mytests http-client-fi-connfail)
add_test(NAME http-client-fi-connfail COMMAND lws-minimal-http-client --expected-exit 2 --fault-injection "wsi=user/connfail")
endif()
list(APPEND mytests http-client-fi-user-est-fail)
add_test(NAME http-client-fi-user-est-fail COMMAND lws-minimal-http-client --expected-exit 3 --fault-injection "wsi/user_reject_at_est")
endif()
if (DEFINED ENV{SAI_OVN})
set_tests_properties(${mytests} PROPERTIES
FIXTURES_REQUIRED "res_http_client_warmcat")
endif()
endif()
endif()

View file

@ -73,6 +73,7 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
in ? (char *)in : "(null)");
interrupted = 1;
bad = 3; /* connection failed before we could make connection */
lws_cancel_service(lws_get_context(wsi));
#if defined(LWS_WITH_CONMON)
@ -97,6 +98,10 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
lws_h2_client_stream_long_poll_rxonly(wsi);
}
#endif
if (lws_fi_user_wsi_fi(wsi, "user_reject_at_est"))
return -1;
break;
#if defined(LWS_WITH_HTTP_BASIC_AUTH)
@ -132,17 +137,10 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
dotstar);
}
#endif
#if 0 /* enable to dump the html */
{
const char *p = in;
while (len--)
if (*p < 0x7f)
putchar(*p++);
else
putchar('.');
}
#if 0
lwsl_hexdump_notice(in, len);
#endif
return 0; /* don't passthru */
/* uninterpreted http content */
@ -152,6 +150,9 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
char *px = buffer + LWS_PRE;
int lenx = sizeof(buffer) - LWS_PRE;
if (lws_fi_user_wsi_fi(wsi, "user_reject_at_rx"))
return -1;
if (lws_http_client_read(wsi, &px, &lenx) < 0)
return -1;
}
@ -304,8 +305,18 @@ system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
i.protocol = protocols[0].name;
i.pwsi = &client_wsi;
i.fi_wsi_name = "user";
return !lws_client_connect_via_info(&i);
if (!lws_client_connect_via_info(&i)) {
lwsl_err("Client creation failed\n");
interrupted = 1;
bad = 2; /* could not even start client connection */
lws_cancel_service(context);
return 1;
}
return 0;
}
int main(int argc, const char **argv)
@ -314,8 +325,9 @@ int main(int argc, const char **argv)
lws_state_notify_link_t *na[] = { &notifier, NULL };
struct lws_context_creation_info info;
struct lws_context *context;
int n = 0, expected = 0;
struct args args;
int n = 0;
const char *p;
// uint8_t memcert[4096];
args.argc = argc;
@ -334,7 +346,7 @@ int main(int argc, const char **argv)
info.protocols = protocols;
info.user = &args;
info.register_notifier_list = na;
info.connect_timeout_secs = 30;
info.connect_timeout_secs = 30;
/*
* since we know this lws context is only ever going to be used with
@ -365,14 +377,24 @@ int main(int argc, const char **argv)
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
bad = 5;
goto bail;
}
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
return bad;
bail:
if ((p = lws_cmdline_option(argc, argv, "--expected-exit")))
expected = atoi(p);
if (bad == expected) {
lwsl_user("Completed: OK (seen expected %d)\n", expected);
return 0;
} else
lwsl_err("Completed: failed: exit %d, expected %d\n", bad, expected);
return 1;
}

View file

@ -150,6 +150,9 @@ do_client_conn(void)
i.origin = i.address;
i.method = "GET";
i.local_protocol_name = protocols[0].name;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
i.fi_wsi_name = "user";
#endif
if (!lws_client_connect_via_info(&i)) {
lwsl_err("Client creation failed\n");

View file

@ -182,7 +182,8 @@ int main(int argc, const char **argv)
* Create our own "foreign" UDP socket bound to 7681/udp
*/
if (!lws_create_adopt_udp(vhost, NULL, 7681, LWS_CAUDP_BIND,
protocols[0].name, NULL, NULL, NULL, NULL)) {
protocols[0].name, NULL, NULL, NULL, NULL,
"user")) {
lwsl_err("%s: foreign socket adoption failed\n", __func__);
goto bail;
}

View file

@ -34,6 +34,7 @@
static int interrupted, bad = 1, port = 0 /* unix domain socket */;
static const char *ibind = NULL; /* default to unix domain skt "proxy.ss.lws" */
static lws_state_notify_link_t nl;
static struct lws_context *context;
/*
* We just define enough policy so it can fetch the latest one securely
@ -233,41 +234,21 @@ static const lws_system_ops_t system_ops = {
static void
sigint_handler(int sig)
{
lwsl_notice("%s\n", __func__);
interrupted = 1;
}
static void
assert_bt(int sig)
{
#if defined(__APPLE__) || defined(__linux__)
void *array[20];
char **strings;
int size, i;
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
if (!strings)
return;
for (i = 0; i < size; i++)
printf("%s\n", strings[i]);
free (strings);
#endif
lws_cancel_service(context);
}
int main(int argc, const char **argv)
{
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
struct lws_context_creation_info info;
struct lws_context *context;
const char *p;
int n = 0;
memset(&info, 0, sizeof info);
lws_cmdline_option_handle_builtin(argc, argv, &info);
signal(SIGINT, sigint_handler);
signal(SIGABRT, assert_bt);
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
/* connect to ssproxy via UDS by default, else via tcp with this port */
if ((p = lws_cmdline_option(argc, argv, "-p")))
@ -278,11 +259,8 @@ int main(int argc, const char **argv)
if ((p = lws_cmdline_option(argc, argv, "-i")))
ibind = p;
lws_set_log_level(logs, NULL);
lwsl_user("LWS secure streams Proxy [-d<verb>]\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
@ -311,8 +289,9 @@ int main(int argc, const char **argv)
/* the event loop */
while (n >= 0 && !interrupted)
do {
n = lws_service(context, 0);
} while (n >= 0 && !interrupted);
bad = 0;

View file

@ -248,13 +248,18 @@ myss_state(void *userobj, void *sh, lws_ss_constate_t state,
case LWSSSCS_ALL_RETRIES_FAILED:
/* if we're out of retries, we want to close the app and FAIL */
interrupted = 1;
bad = 2;
break;
case LWSSSCS_QOS_ACK_REMOTE:
lwsl_notice("%s: LWSSSCS_QOS_ACK_REMOTE\n", __func__);
break;
case LWSSSCS_TIMEOUT:
lwsl_notice("%s: LWSSSCS_TIMEOUT\n", __func__);
/* if we're out of time */
interrupted = 1;
bad = 3;
break;
case LWSSSCS_USER_BASE:
@ -405,8 +410,8 @@ int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_context *context;
int n = 0, expected = 0;
const char *p;
int n = 0;
signal(SIGINT, sigint_handler);
@ -475,7 +480,7 @@ int main(int argc, const char **argv)
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
goto bail;
}
#if !defined(LWS_SS_USE_SSPC)
@ -520,7 +525,15 @@ int main(int argc, const char **argv)
lws_context_destroy(context);
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
bail:
if ((p = lws_cmdline_option(argc, argv, "--expected-exit")))
expected = atoi(p);
return bad;
if (bad == expected) {
lwsl_user("Completed: OK (seen expected %d)\n", expected);
return 0;
} else
lwsl_err("Completed: failed: exit %d, expected %d\n", bad, expected);
return 1;
}