| 1 | /* |
| 2 | * libwebsockets - small server side websockets and web server implementation |
| 3 | * |
| 4 | * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com> |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to |
| 8 | * deal in the Software without restriction, including without limitation the |
| 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| 10 | * sell copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 22 | * IN THE SOFTWARE. |
| 23 | * |
| 24 | * This provides a clean way to interface lws user code to be able to |
| 25 | * work unchanged on different systems for fetching common system information, |
| 26 | * and performing common system operations like reboot. |
| 27 | */ |
| 28 | |
| 29 | /* |
| 30 | * Types of system blob that can be set and retreived |
| 31 | */ |
| 32 | |
| 33 | typedef enum { |
| 34 | LWS_SYSBLOB_TYPE_AUTH, |
| 35 | LWS_SYSBLOB_TYPE_CLIENT_CERT_DER = LWS_SYSBLOB_TYPE_AUTH + 2, |
| 36 | LWS_SYSBLOB_TYPE_CLIENT_KEY_DER, |
| 37 | LWS_SYSBLOB_TYPE_DEVICE_SERIAL, |
| 38 | LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, |
| 39 | LWS_SYSBLOB_TYPE_DEVICE_TYPE, |
| 40 | LWS_SYSBLOB_TYPE_NTP_SERVER, |
| 41 | LWS_SYSBLOB_TYPE_MQTT_CLIENT_ID, |
| 42 | LWS_SYSBLOB_TYPE_MQTT_USERNAME, |
| 43 | LWS_SYSBLOB_TYPE_MQTT_PASSWORD, |
| 44 | |
| 45 | #if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) |
| 46 | /* extend 4 more auth blobs, each has 2 slots */ |
| 47 | LWS_SYSBLOB_TYPE_EXT_AUTH1, |
| 48 | LWS_SYSBLOB_TYPE_EXT_AUTH2 = LWS_SYSBLOB_TYPE_EXT_AUTH1 + 2, |
| 49 | LWS_SYSBLOB_TYPE_EXT_AUTH3 = LWS_SYSBLOB_TYPE_EXT_AUTH2 + 2, |
| 50 | LWS_SYSBLOB_TYPE_EXT_AUTH4 = LWS_SYSBLOB_TYPE_EXT_AUTH3 + 2, |
| 51 | LWS_SYSBLOB_TYPE_EXT_AUTH4_1, |
| 52 | #endif |
| 53 | |
| 54 | LWS_SYSBLOB_TYPE_COUNT /* ... always last */ |
| 55 | } lws_system_blob_item_t; |
| 56 | |
| 57 | /* opaque generic blob whose content may be on-the-heap or pointed-to |
| 58 | * directly case by case. When it's on the heap, it can be produced by |
| 59 | * appending (it's a buflist underneath). Either way, it can be consumed by |
| 60 | * copying out a given length from a given offset. |
| 61 | */ |
| 62 | |
| 63 | typedef struct lws_system_blob lws_system_blob_t; |
| 64 | |
| 65 | LWS_EXTERN LWS_VISIBLE void |
| 66 | lws_system_blob_direct_set(lws_system_blob_t *b, const uint8_t *ptr, size_t len); |
| 67 | |
| 68 | LWS_EXTERN LWS_VISIBLE void |
| 69 | lws_system_blob_heap_empty(lws_system_blob_t *b); |
| 70 | |
| 71 | LWS_EXTERN LWS_VISIBLE int |
| 72 | lws_system_blob_heap_append(lws_system_blob_t *b, const uint8_t *ptr, size_t len); |
| 73 | |
| 74 | LWS_EXTERN LWS_VISIBLE size_t |
| 75 | lws_system_blob_get_size(lws_system_blob_t *b); |
| 76 | |
| 77 | /* return 0 and sets *ptr to point to blob data if possible, nonzero = fail */ |
| 78 | LWS_EXTERN LWS_VISIBLE int |
| 79 | lws_system_blob_get_single_ptr(lws_system_blob_t *b, const uint8_t **ptr); |
| 80 | |
| 81 | LWS_EXTERN LWS_VISIBLE int |
| 82 | lws_system_blob_get(lws_system_blob_t *b, uint8_t *ptr, size_t *len, size_t ofs); |
| 83 | |
| 84 | LWS_EXTERN LWS_VISIBLE void |
| 85 | lws_system_blob_destroy(lws_system_blob_t *b); |
| 86 | |
| 87 | /* |
| 88 | * Get the opaque blob for index idx of various system blobs. Returns 0 if |
| 89 | * *b was set otherwise nonzero means out of range |
| 90 | */ |
| 91 | |
| 92 | LWS_EXTERN LWS_VISIBLE lws_system_blob_t * |
| 93 | lws_system_get_blob(struct lws_context *context, lws_system_blob_item_t type, |
| 94 | int idx); |
| 95 | |
| 96 | /* |
| 97 | * Lws view of system state... normal operation from user code perspective is |
| 98 | * dependent on implicit (eg, knowing the date for cert validation) and |
| 99 | * explicit dependencies. |
| 100 | * |
| 101 | * Bit of lws and user code can register notification handlers that can enforce |
| 102 | * dependent operations before state transitions can complete. |
| 103 | */ |
| 104 | |
| 105 | typedef enum { /* keep system_state_names[] in sync in context.c */ |
| 106 | LWS_SYSTATE_UNKNOWN, |
| 107 | |
| 108 | LWS_SYSTATE_CONTEXT_CREATED, /* context was just created */ |
| 109 | LWS_SYSTATE_INITIALIZED, /* protocols initialized. Lws itself |
| 110 | * can operate normally */ |
| 111 | LWS_SYSTATE_IFACE_COLDPLUG, /* existing net ifaces iterated */ |
| 112 | LWS_SYSTATE_DHCP, /* at least one net iface configured */ |
| 113 | LWS_SYSTATE_CPD_PRE_TIME, /* Captive portal detect without valid |
| 114 | * time, good for non-https tests... if |
| 115 | * you care about it, implement and |
| 116 | * call lws_system_ops_t |
| 117 | * .captive_portal_detect_request() |
| 118 | * and move the state forward according |
| 119 | * to the result. */ |
| 120 | LWS_SYSTATE_TIME_VALID, /* ntpclient ran, or hw time valid... |
| 121 | * tls cannot work until we reach here |
| 122 | */ |
| 123 | LWS_SYSTATE_CPD_POST_TIME, /* Captive portal detect after time was |
| 124 | * time, good for https tests... if |
| 125 | * you care about it, implement and |
| 126 | * call lws_system_ops_t |
| 127 | * .captive_portal_detect_request() |
| 128 | * and move the state forward according |
| 129 | * to the result. */ |
| 130 | |
| 131 | LWS_SYSTATE_POLICY_VALID, /* user code knows how to operate... */ |
| 132 | LWS_SYSTATE_REGISTERED, /* device has an identity... */ |
| 133 | LWS_SYSTATE_AUTH1, /* identity used for main auth token */ |
| 134 | LWS_SYSTATE_AUTH2, /* identity used for optional auth */ |
| 135 | |
| 136 | LWS_SYSTATE_ONE_TIME_UPDATES, /* pre-OPERATIONAL one-time updates, |
| 137 | * when a firmware needs to perform |
| 138 | * one-time upgrades to state before |
| 139 | * OPERATIONAL */ |
| 140 | |
| 141 | LWS_SYSTATE_OPERATIONAL, /* user code can operate normally */ |
| 142 | |
| 143 | LWS_SYSTATE_POLICY_INVALID, /* user code is changing its policies |
| 144 | * drop everything done with old |
| 145 | * policy, switch to new then enter |
| 146 | * LWS_SYSTATE_POLICY_VALID */ |
| 147 | LWS_SYSTATE_CONTEXT_DESTROYING, /* Context is being destroyed */ |
| 148 | LWS_SYSTATE_AWAITING_MODAL_UPDATING, /* We're negotiating with the |
| 149 | * user code for update mode */ |
| 150 | LWS_SYSTATE_MODAL_UPDATING, /* We're updating the firmware */ |
| 151 | } lws_system_states_t; |
| 152 | |
| 153 | /* Captive Portal Detect -related */ |
| 154 | |
| 155 | typedef enum { |
| 156 | LWS_CPD_UNKNOWN = 0, /* test didn't happen ince last DHCP acq yet */ |
| 157 | LWS_CPD_INTERNET_OK, /* no captive portal: our CPD test passed OK, |
| 158 | * we can go out on the internet */ |
| 159 | LWS_CPD_CAPTIVE_PORTAL, /* we inferred we're behind a captive portal */ |
| 160 | LWS_CPD_NO_INTERNET, /* we couldn't touch anything */ |
| 161 | } lws_cpd_result_t; |
| 162 | |
| 163 | typedef void (*lws_attach_cb_t)(struct lws_context *context, int tsi, void *opaque); |
| 164 | struct lws_attach_item; |
| 165 | |
| 166 | LWS_EXTERN LWS_VISIBLE int |
| 167 | lws_tls_jit_trust_got_cert_cb(struct lws_context *cx, void *got_opaque, |
| 168 | const uint8_t *skid, size_t skid_len, |
| 169 | const uint8_t *der, size_t der_len); |
| 170 | |
| 171 | typedef struct lws_system_ops { |
| 172 | int (*reboot)(void); |
| 173 | int (*set_clock)(lws_usec_t us); |
| 174 | int (*attach)(struct lws_context *context, int tsi, lws_attach_cb_t cb, |
| 175 | lws_system_states_t state, void *opaque, |
| 176 | struct lws_attach_item **get); |
| 177 | /**< if \p get is NULL, add an attach callback request to the pt for |
| 178 | * \p cb with arg \p opaque, that should be called when we're at or past |
| 179 | * system state \p state. |
| 180 | * |
| 181 | * If \p get is non-NULL, look for the first listed item on the pt whose |
| 182 | * state situation is ready, and set *get to point to it. If no items, |
| 183 | * or none where the system state is right, set *get to NULL. |
| 184 | * |
| 185 | * It's done like this so (*attach) can perform system-specific |
| 186 | * locking outside of lws core, for both getting and adding items the |
| 187 | * same so it is thread-safe. A non-threadsafe helper |
| 188 | * __lws_system_attach() is provided to do the actual work inside the |
| 189 | * system-specific locking. |
| 190 | */ |
| 191 | int (*captive_portal_detect_request)(struct lws_context *context); |
| 192 | /**< Check if we can go out on the internet cleanly, or if we are being |
| 193 | * redirected or intercepted by a captive portal. |
| 194 | * Start the check that proceeds asynchronously, and report the results |
| 195 | * by calling lws_captive_portal_detect_result() api |
| 196 | */ |
| 197 | |
| 198 | #if defined(LWS_WITH_NETWORK) |
| 199 | int (*metric_report)(lws_metric_pub_t *mdata); |
| 200 | /**< metric \p item is reporting an event of kind \p rpt, |
| 201 | * held in \p mdata... return 0 to leave the metric object as it is, |
| 202 | * or nonzero to reset it. */ |
| 203 | #endif |
| 204 | int (*jit_trust_query)(struct lws_context *cx, const uint8_t *skid, |
| 205 | size_t skid_len, void *got_opaque); |
| 206 | /**< user defined trust store search, if we do trust a cert with SKID |
| 207 | * matching skid / skid_len, then it should get hold of the DER for the |
| 208 | * matching root CA and call |
| 209 | * lws_tls_jit_trust_got_cert_cb(..., got_opaque) before cleaning up and |
| 210 | * returning. The DER should be destroyed if in heap before returning. |
| 211 | */ |
| 212 | |
| 213 | #if defined(LWS_WITH_OTA) |
| 214 | lws_ota_ops_t ota_ops; |
| 215 | /**< Platform OTA interface to lws_ota, see lws-ota.h */ |
| 216 | #endif |
| 217 | |
| 218 | uint32_t wake_latency_us; |
| 219 | /**< time taken for this device to wake from suspend, in us |
| 220 | */ |
| 221 | } lws_system_ops_t; |
| 222 | |
| 223 | #if defined(LWS_WITH_SYS_STATE) |
| 224 | |
| 225 | /** |
| 226 | * lws_system_get_state_manager() - return the state mgr object for system state |
| 227 | * |
| 228 | * \param context: the lws_context |
| 229 | * |
| 230 | * The returned pointer can be used with the lws_state_ apis |
| 231 | */ |
| 232 | |
| 233 | LWS_EXTERN LWS_VISIBLE lws_state_manager_t * |
| 234 | lws_system_get_state_manager(struct lws_context *context); |
| 235 | |
| 236 | #endif |
| 237 | |
| 238 | /* wrappers handle NULL members or no ops struct set at all cleanly */ |
| 239 | |
| 240 | #define LWSSYSGAUTH_HEX (1 << 0) |
| 241 | |
| 242 | /** |
| 243 | * lws_system_get_ops() - get ahold of the system ops struct from the context |
| 244 | * |
| 245 | * \param context: the lws_context |
| 246 | * |
| 247 | * Returns the system ops struct. It may return NULL and if not, anything in |
| 248 | * there may be NULL. |
| 249 | */ |
| 250 | LWS_EXTERN LWS_VISIBLE const lws_system_ops_t * |
| 251 | lws_system_get_ops(struct lws_context *context); |
| 252 | |
| 253 | #if defined(LWS_WITH_SYS_STATE) |
| 254 | |
| 255 | /** |
| 256 | * lws_system_context_from_system_mgr() - return context from system state mgr |
| 257 | * |
| 258 | * \param mgr: pointer to specifically the system state mgr |
| 259 | * |
| 260 | * Returns the context from the system state mgr. Helper since the lws_context |
| 261 | * is opaque. |
| 262 | */ |
| 263 | LWS_EXTERN LWS_VISIBLE struct lws_context * |
| 264 | lws_system_context_from_system_mgr(lws_state_manager_t *mgr); |
| 265 | |
| 266 | #endif |
| 267 | |
| 268 | /** |
| 269 | * __lws_system_attach() - get and set items on context attach list |
| 270 | * |
| 271 | * \param context: context to get or set attach items to |
| 272 | * \param tsi: thread service index (normally 0) |
| 273 | * \param cb: callback to call from context event loop thread |
| 274 | * \param state: the lws_system state we have to be in or have passed through |
| 275 | * \param opaque: optional pointer to user specific info given to callback |
| 276 | * \param get: NULL, or pointer to pointer to take detached tail item on exit |
| 277 | * |
| 278 | * This allows other threads to enqueue callback requests to happen from a pt's |
| 279 | * event loop thread safely. The callback gets the context pointer and a user |
| 280 | * opaque pointer that can be optionally given when the item is added to the |
| 281 | * attach list. |
| 282 | * |
| 283 | * This api is the no-locking core function for getting and setting items on the |
| 284 | * pt's attach list. The lws_system operation (*attach) is the actual |
| 285 | * api that user and internal code calls for this feature, it should perform |
| 286 | * system-specific locking, call this helper, release the locking and then |
| 287 | * return the result. This api is public only so it can be used in the locked |
| 288 | * implementation of (*attach). |
| 289 | * |
| 290 | * If get is NULL, then the call adds to the head of the pt attach list using |
| 291 | * cb, state, and opaque; if get is non-NULL, then *get is set to the first |
| 292 | * waiting attached item that meets the state criteria and that item is removed |
| 293 | * from the list. |
| 294 | * |
| 295 | * This is a non-threadsafe helper only designed to be called from |
| 296 | * implementations of struct lws_system's (*attach) operation where system- |
| 297 | * specific locking has been applied around it, making it threadsafe. |
| 298 | */ |
| 299 | LWS_EXTERN LWS_VISIBLE int |
| 300 | __lws_system_attach(struct lws_context *context, int tsi, lws_attach_cb_t cb, |
| 301 | lws_system_states_t state, void *opaque, |
| 302 | struct lws_attach_item **get); |
| 303 | |
| 304 | |
| 305 | enum { |
| 306 | LWSDH_IPV4_SUBNET_MASK = 0, |
| 307 | LWSDH_IPV4_BROADCAST, |
| 308 | LWSDH_LEASE_SECS, |
| 309 | LWSDH_REBINDING_SECS, |
| 310 | LWSDH_RENEWAL_SECS, |
| 311 | |
| 312 | _LWSDH_NUMS_COUNT, |
| 313 | |
| 314 | LWSDH_SA46_IP = 0, |
| 315 | LWSDH_SA46_DNS_SRV_1, |
| 316 | LWSDH_SA46_DNS_SRV_2, |
| 317 | LWSDH_SA46_DNS_SRV_3, |
| 318 | LWSDH_SA46_DNS_SRV_4, |
| 319 | LWSDH_SA46_IPV4_ROUTER, |
| 320 | LWSDH_SA46_NTP_SERVER, |
| 321 | LWSDH_SA46_DHCP_SERVER, |
| 322 | |
| 323 | _LWSDH_SA46_COUNT, |
| 324 | }; |
| 325 | |
| 326 | #if defined(LWS_WITH_NETWORK) |
| 327 | typedef struct lws_dhcpc_ifstate { |
| 328 | char ifname[16]; |
| 329 | char domain[64]; |
| 330 | uint8_t mac[6]; |
| 331 | uint32_t nums[_LWSDH_NUMS_COUNT]; |
| 332 | lws_sockaddr46 sa46[_LWSDH_SA46_COUNT]; |
| 333 | } lws_dhcpc_ifstate_t; |
| 334 | |
| 335 | typedef int (*dhcpc_cb_t)(void *opaque, lws_dhcpc_ifstate_t *is); |
| 336 | |
| 337 | /** |
| 338 | * lws_dhcpc_request() - add a network interface to dhcpc management |
| 339 | * |
| 340 | * \param c: the lws_context |
| 341 | * \param i: the interface name, like "eth0" |
| 342 | * \param af: address family |
| 343 | * \param cb: the change callback |
| 344 | * \param opaque: opaque pointer given to the callback |
| 345 | * |
| 346 | * Register a network interface as being managed by DHCP. lws will proceed to |
| 347 | * try to acquire an IP. Requires LWS_WITH_SYS_DHCP_CLIENT at cmake. |
| 348 | */ |
| 349 | LWS_EXTERN LWS_VISIBLE int |
| 350 | lws_dhcpc_request(struct lws_context *c, const char *i, int af, dhcpc_cb_t cb, |
| 351 | void *opaque); |
| 352 | |
| 353 | /** |
| 354 | * lws_dhcpc_remove() - remove a network interface to dhcpc management |
| 355 | * |
| 356 | * \param context: the lws_context |
| 357 | * \param iface: the interface name, like "eth0" |
| 358 | * |
| 359 | * Remove handling of the network interface from dhcp. |
| 360 | */ |
| 361 | LWS_EXTERN LWS_VISIBLE int |
| 362 | lws_dhcpc_remove(struct lws_context *context, const char *iface); |
| 363 | |
| 364 | /** |
| 365 | * lws_dhcpc_status() - has any interface reached BOUND state |
| 366 | * |
| 367 | * \param context: the lws_context |
| 368 | * \param sa46: set to a DNS server from a bound interface, or NULL |
| 369 | * |
| 370 | * Returns 1 if any network interface managed by dhcpc has reached the BOUND |
| 371 | * state (has acquired an IP, gateway and DNS server), otherwise 0. |
| 372 | */ |
| 373 | LWS_EXTERN LWS_VISIBLE int |
| 374 | lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46); |
| 375 | |
| 376 | /** |
| 377 | * lws_system_cpd_start() - helper to initiate captive portal detection |
| 378 | * |
| 379 | * \param context: the lws_context |
| 380 | * |
| 381 | * Resets the context's captive portal state to LWS_CPD_UNKNOWN and calls the |
| 382 | * lws_system_ops_t captive_portal_detect_request() implementation to begin |
| 383 | * testing the captive portal state. |
| 384 | */ |
| 385 | LWS_EXTERN LWS_VISIBLE int |
| 386 | lws_system_cpd_start(struct lws_context *context); |
| 387 | |
| 388 | LWS_EXTERN LWS_VISIBLE void |
| 389 | lws_system_cpd_start_defer(struct lws_context *cx, lws_usec_t defer_us); |
| 390 | |
| 391 | |
| 392 | /** |
| 393 | * lws_system_cpd_set() - report the result of the captive portal detection |
| 394 | * |
| 395 | * \param context: the lws_context |
| 396 | * \param result: one of the LWS_CPD_ constants representing captive portal state |
| 397 | * |
| 398 | * Sets the context's captive portal detection state to result. User captive |
| 399 | * portal detection code would call this once it had a result from its test. |
| 400 | */ |
| 401 | LWS_EXTERN LWS_VISIBLE void |
| 402 | lws_system_cpd_set(struct lws_context *context, lws_cpd_result_t result); |
| 403 | |
| 404 | |
| 405 | /** |
| 406 | * lws_system_cpd_state_get() - returns the last tested captive portal state |
| 407 | * |
| 408 | * \param context: the lws_context |
| 409 | * |
| 410 | * Returns one of the LWS_CPD_ constants indicating the system's understanding |
| 411 | * of the current captive portal situation. |
| 412 | */ |
| 413 | LWS_EXTERN LWS_VISIBLE lws_cpd_result_t |
| 414 | lws_system_cpd_state_get(struct lws_context *context); |
| 415 | |
| 416 | #endif |
| 417 | |
| 418 | |