1/* 2 * lws-minimal-secure-streams 3 * 4 * Written in 2010-2020 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * 10 * This demonstrates various kinds of successful and failed connection 11 * situations in order to confirm the correct states are coming. 12 * 13 * You can control how much bulk data is requested from the peer using 14 * --amount xxx, the default without that is 12345 bytes. 15 */ 16 17#include <libwebsockets.h> 18#include <string.h> 19#include <signal.h> 20 21static int interrupted, tests, tests_pass, tests_fail; 22static lws_sorted_usec_list_t sul_next_test; 23static lws_state_notify_link_t nl; 24struct lws_context *context; 25size_t amount = 12345; 26 27static void 28tests_start_next(lws_sorted_usec_list_t *sul); 29 30/* 31 * If the -proxy app is fulfilling our connection, then we don't need to have 32 * the policy in the client. 33 * 34 * When we build with LWS_SS_USE_SSPC, the apis hook up to a proxy process over 35 * a Unix Domain Socket. To test that, you need to separately run the 36 * ./lws-minimal-secure-streams-proxy test app on the same machine. 37 */ 38 39#if !defined(LWS_SS_USE_SSPC) 40static const char * const default_ss_policy = 41 "{" 42 "\"release\":" "\"01234567\"," 43 "\"product\":" "\"myproduct\"," 44 "\"schema-version\":" "1," 45#if defined(VIA_LOCALHOST_SOCKS) 46 "\"via-socks5\":" "\"127.0.0.1:1080\"," 47#endif 48 49 "\"retry\": [" /* named backoff / retry strategies */ 50 "{\"default\": {" 51 "\"backoff\": [ 1000, 1000, 1000, 1000" 52 "]," 53 "\"conceal\":" "4," 54 "\"jitterpc\":" "20," 55 "\"svalidping\":" "30," 56 "\"svalidhup\":" "35" 57 "}}" 58 "]," 59 "\"certs\": [" /* named individual certificates in BASE64 DER */ 60 /* 61 * Let's Encrypt certs for warmcat.com / libwebsockets.org 62 * 63 * We fetch the real policy from there using SS and switch to 64 * using that. 65 */ 66 "{\"isrg_root_x1\": \"" 67 "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" 68 "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" 69 "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" 70 "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" 71 "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" 72 "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" 73 "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" 74 "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" 75 "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" 76 "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" 77 "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" 78 "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" 79 "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" 80 "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" 81 "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" 82 "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" 83 "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" 84 "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" 85 "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" 86 "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" 87 "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" 88 "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" 89 "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" 90 "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" 91 "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" 92 "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" 93 "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" 94 "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" 95 "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" 96 "\"},{" 97 "\"digicert_global_root_g2\": \"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7K" 98 "GSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMR" 99 "GlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDE" 100 "xdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxM" 101 "TUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxG" 102 "TAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb" 103 "2JhbCBSb290IEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNN" 104 "Nx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpim" 105 "n7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kq" 106 "bitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauVB" 107 "JVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdz" 108 "XOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FD" 109 "KZJobq7nMWxM4MphQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/" 110 "wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNA" 111 "QELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQ" 112 "oQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98" 113 "kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8" 114 "PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgR" 115 "PTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3f" 116 "e0Dkhvld1927jyNxF1WW6LZZm6zNTflMrY=\"" 117 "}, {" 118 "\"digicert_global_ca_g2\": \"MIIEizCCA3OgAwIBAgIQDI7gyQ1" 119 "qiRWIBAYe4kH5rzANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1U" 120 "EChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgY" 121 "DVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0" 122 "yODA4MDExMjAwMDBaMEQxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCB" 123 "JbmMxHjAcBgNVBAMTFURpZ2lDZXJ0IEdsb2JhbCBDQSBHMjCCASIwDQYJKoZIhvc" 124 "NAQEBBQADggEPADCCAQoCggEBANNIfL7zBYZdW9UvhU5L4IatFaxhz1uvPmoKR/u" 125 "adpFgC4przc/cV35gmAvkVNlW7SHMArZagV+Xau4CLyMnuG3UsOcGAngLH1ypmTb" 126 "+u6wbBfpXzYEQQGfWMItYNdSWYb7QjHqXnxr5IuYUL6nG6AEfq/gmD6yOTSwyOR2" 127 "Bm40cZbIc22GoiS9g5+vCShjEbyrpEJIJ7RfRACvmfe8EiRROM6GyD5eHn7OgzS+" 128 "8LOy4g2gxPR/VSpAQGQuBldYpdlH5NnbQtwl6OErXb4y/E3w57bqukPyV93t4CTZ" 129 "edJMeJfD/1K2uaGvG/w/VNfFVbkhJ+Pi474j48V4Rd6rfArMCAwEAAaOCAVowggF" 130 "WMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwE" 131 "BBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1U" 132 "dHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEd" 133 "sb2JhbFJvb3RHMi5jcmwwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9" 134 "EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAY" 135 "IKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBY" 136 "EFCRuKy3QapJRUSVpAaqaR6aJ50AgMB8GA1UdIwQYMBaAFE4iVCAYlebjbuYP+vq" 137 "5Eu0GF485MA0GCSqGSIb3DQEBCwUAA4IBAQALOYSR+ZfrqoGvhOlaOJL84mxZvzb" 138 "IRacxAxHhBsCsMsdaVSnaT0AC9aHesO3ewPj2dZ12uYf+QYB6z13jAMZbAuabeGL" 139 "J3LhimnftiQjXS8X9Q9ViIyfEBFltcT8jW+rZ8uckJ2/0lYDblizkVIvP6hnZf1W" 140 "ZUXoOLRg9eFhSvGNoVwvdRLNXSmDmyHBwW4coatc7TlJFGa8kBpJIERqLrqwYEle" 141 "sA8u49L3KJg6nwd3jM+/AVTANlVlOnAM2BvjAjxSZnE0qnsHhfTuvcqdFuhOWKU4" 142 "Z0BqYBvQ3lBetoxi6PrABDJXWKTUgNX31EGDk92hiHuwZ4STyhxGs6QiA\"" 143 "}," 144 "{\"amazon_root_ca_1\": \"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikP" 145 "mljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1" 146 "hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFo" 147 "XDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjE" 148 "ZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggE" 149 "PADCCAQoCggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtO" 150 "gQ3pOsqTQNroBvo3bSMgHFzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peV" 151 "KVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+Uh" 152 "nMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4c" 153 "X8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34Gf" 154 "ID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAU" 155 "wAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7I" 156 "QTgoIMA0GCSqGSIb3DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5" 157 "IpDB/G/wkjUu0yKGX9rbxenDIU5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZ" 158 "ERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2" 159 "V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR" 160 "1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob" 161 "2xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5\"}" 162 "]," 163 "\"trust_stores\": [" /* named cert chains */ 164 "{" 165 "\"name\": \"api_amazon_com\"," 166 "\"stack\": [\"digicert_global_ca_g2\", \"digicert_global_root_g2\"]" 167 "}, { \"name\": \"arca1\", \"stack\": [\"amazon_root_ca_1\"]}," 168 "{" 169 "\"name\": \"le_via_isrg\"," 170 "\"stack\": [" 171 "\"isrg_root_x1\"" 172 "]" 173 "}" 174 "]," 175 "\"s\": [" 176 177 "{\"api_amazon_com_auth\": {" 178 "\"endpoint\": \"api.amazon.com\"," 179 "\"port\": 443," 180 "\"protocol\": \"h1\"," 181 "\"http_method\": \"POST\"," 182 "\"http_url\": \"auth/o2/token\"," 183 "\"plugins\": []," 184 "\"opportunistic\": true," 185 "\"tls\": true," 186 "\"h2q_oflow_txcr\": true," 187 "\"http_www_form_urlencoded\": true," 188 "\"http_no_content_length\": true," 189 "\"retry\": \"default\"," 190 "\"tls_trust_store\": \"api_amazon_com\"" 191 "}},{" 192 193 /* 194 * Just get a 200 from httpbin.org 195 * on h1:80, h1:443 and h2:443 196 * 197 * sanity check that we're working at all 198 */ 199 200 "\"t_h1\": {" 201 "\"endpoint\": \"httpbin.org\"," 202 "\"port\": 80," 203 "\"protocol\": \"h1\"," 204 "\"http_method\": \"GET\"," 205 "\"http_url\": \"/status/200\"," 206 "\"opportunistic\": true," 207 "\"retry\": \"default\"" 208 "}},{" 209 "\"t_h1_tls\": {" 210 "\"endpoint\": \"httpbin.org\"," 211 "\"port\": 443," 212 "\"protocol\": \"h1\"," 213 "\"http_method\": \"GET\"," 214 "\"http_url\": \"/status/200\"," 215 "\"tls\": true," 216 "\"opportunistic\": true," 217 "\"retry\": \"default\"," 218 "\"tls_trust_store\": \"arca1\"" 219 "}},{" 220 "\"t_h2_tls\": {" 221 "\"endpoint\": \"httpbin.org\"," 222 "\"port\": 443," 223 "\"protocol\": \"h2\"," 224 "\"http_method\": \"GET\"," 225 "\"http_url\": \"/status/200\"," 226 "\"tls\": true," 227 "\"nghttp2_quirk_end_stream\": true," 228 "\"h2q_oflow_txcr\": true," 229 "\"opportunistic\": true," 230 "\"retry\": \"default\"," 231 "\"tls_trust_store\": \"arca1\"" 232 "}},{" 233 234 /* 235 * 10s delayed response from httpbin.org 236 * on h1:80, h1:443 and h2:443 237 * 238 * used to trigger timeout testing 239 */ 240 241 "\"d_h1\": {" 242 "\"endpoint\": \"httpbin.org\"," 243 "\"port\": 80," 244 "\"protocol\": \"h1\"," 245 "\"http_method\": \"GET\"," 246 "\"http_url\": \"/delay/10\"," 247 "\"opportunistic\": true," 248 "\"retry\": \"default\"" 249 "}},{" 250 "\"d_h1_tls\": {" 251 "\"endpoint\": \"httpbin.org\"," 252 "\"port\": 443," 253 "\"protocol\": \"h1\"," 254 "\"http_method\": \"GET\"," 255 "\"http_url\": \"/delay/10\"," 256 "\"tls\": true," 257 "\"opportunistic\": true," 258 "\"retry\": \"default\"," 259 "\"tls_trust_store\": \"arca1\"" 260 "}},{" 261 "\"d_h2_tls\": {" 262 "\"endpoint\": \"httpbin.org\"," 263 "\"port\": 443," 264 "\"protocol\": \"h2\"," 265 "\"http_method\": \"GET\"," 266 "\"http_url\": \"/delay/10\"," 267 "\"tls\": true," 268 "\"nghttp2_quirk_end_stream\": true," 269 "\"h2q_oflow_txcr\": true," 270 "\"opportunistic\": true," 271 "\"retry\": \"default\"," 272 "\"tls_trust_store\": \"arca1\"" 273 "}},{" 274 275 /* 276 * get NXDOMAIN for bogus.nope 277 * on h1:80, h1:443 and h2:443 278 * 279 * Triggers unreachable and eventually all_retries_failed 280 */ 281 282 "\"nxd_h1\": {" 283 "\"endpoint\": \"bogus.nope\"," 284 "\"port\": 80," 285 "\"protocol\": \"h1\"," 286 "\"http_method\": \"GET\"," 287 "\"http_url\": \"/status/200\"," 288 "\"opportunistic\": true," 289 "\"retry\": \"default\"" 290 "}},{" 291 "\"nxd_h1_tls\": {" 292 "\"endpoint\": \"bogus.nope\"," 293 "\"port\": 443," 294 "\"protocol\": \"h1\"," 295 "\"http_method\": \"GET\"," 296 "\"http_url\": \"/status/200\"," 297 "\"tls\": true," 298 "\"opportunistic\": true," 299 "\"retry\": \"default\"," 300 "\"tls_trust_store\": \"arca1\"" 301 "}},{" 302 "\"nxd_h2_tls\": {" 303 "\"endpoint\": \"bogus.nope\"," 304 "\"port\": 443," 305 "\"protocol\": \"h2\"," 306 "\"http_method\": \"GET\"," 307 "\"http_url\": \"/status/200\"," 308 "\"tls\": true," 309 "\"nghttp2_quirk_end_stream\": true," 310 "\"h2q_oflow_txcr\": true," 311 "\"opportunistic\": true," 312 "\"retry\": \"default\"," 313 "\"tls_trust_store\": \"arca1\"" 314 "}},{" 315 316 /* 317 * bulk payload transfer from httpbin.org 318 * on h1:80, h1:443 and h2:443 319 * 320 * Sanity check larger payload 321 */ 322 323 "\"bulk_h1\": {" 324 "\"endpoint\": \"httpbin.org\"," 325 "\"port\": 80," 326 "\"protocol\": \"h1\"," 327 "\"http_method\": \"GET\"," 328 "\"http_url\": \"range/${amount}\"," 329 "\"metadata\": [{" 330 "\"amount\": \"\"" 331 "}]," 332 "\"opportunistic\": true," 333 "\"retry\": \"default\"" 334 "}},{" 335 "\"bulk_h1_tls\": {" 336 "\"endpoint\": \"httpbin.org\"," 337 "\"port\": 443," 338 "\"protocol\": \"h1\"," 339 "\"http_method\": \"GET\"," 340 "\"http_url\": \"range/${amount}\"," 341 "\"metadata\": [{" 342 "\"amount\": \"\"" 343 "}]," 344 "\"tls\": true," 345 "\"opportunistic\": true," 346 "\"retry\": \"default\"," 347 "\"tls_trust_store\": \"arca1\"" 348 "}},{" 349 "\"bulk_h2_tls\": {" 350 "\"endpoint\": \"httpbin.org\"," 351 "\"port\": 443," 352 "\"protocol\": \"h2\"," 353 "\"http_method\": \"GET\"," 354 "\"http_url\": \"range/${amount}\"," 355 "\"metadata\": [{" 356 "\"amount\": \"\"" 357 "}]," 358 "\"tls\": true," 359 "\"nghttp2_quirk_end_stream\": true," 360 "\"h2q_oflow_txcr\": true," 361 "\"opportunistic\": true," 362 "\"retry\": \"default\"," 363 "\"tls_trust_store\": \"arca1\"" 364 365 "}},{" 366 367 /* 368 * Various kinds of tls failure 369 * 370 * hostname.badcert.warmcat.com: serves valid cert but for 371 * warmcat.com 372 * 373 * warmcat.com:446: serves valid but expired cert 374 * 375 * I don't have an easy way to make the test for "not valid yet" 376 * cert without root 377 * 378 * invalidca.badcert.warmcat.com: selfsigned cert for that 379 * hostname 380 */ 381 382 "\"badcert_hostname\": {" 383 "\"endpoint\": \"hostname.badcert.warmcat.com\"," 384 "\"port\": 443," 385 "\"protocol\": \"h1\"," 386 "\"http_method\": \"GET\"," 387 "\"http_url\": \"/\"," 388 "\"tls\": true," 389 "\"opportunistic\": true," 390 "\"retry\": \"default\"," 391 "\"tls_trust_store\": \"le_via_isrg\"" 392 "}},{" 393 "\"badcert_expired\": {" 394 "\"endpoint\": \"warmcat.com\"," 395 "\"port\": 446," 396 "\"protocol\": \"h1\"," 397 "\"http_method\": \"GET\"," 398 "\"http_url\": \"/\"," 399 "\"tls\": true," 400 "\"opportunistic\": true," 401 "\"retry\": \"default\"," 402 "\"tls_trust_store\": \"le_via_isrg\"" 403 "}},{" 404 "\"badcert_selfsigned\": {" 405 "\"endpoint\": \"invalidca.badcert.warmcat.com\"," 406 "\"port\": 443," 407 "\"protocol\": \"h1\"," 408 "\"http_method\": \"GET\"," 409 "\"http_url\": \"/\"," 410 "\"tls\": true," 411 "\"nghttp2_quirk_end_stream\": true," 412 "\"h2q_oflow_txcr\": true," 413 "\"opportunistic\": true," 414 "\"retry\": \"default\"," 415 "\"tls_trust_store\": \"le_via_isrg\"" 416 "}}" 417 "]}" 418; 419 420#endif 421 422/* 423 * This is the sequence of test streams we are going to create, the ss timeout, 424 * and a description of what we want to see to understand the test passed, or 425 * failed. If the test hits destruction without making a explicit pass or fail 426 * decision before, that's a fail. Or, depending on what state we put in 427 * .must_see, we can count a state like UNREACHABLE as a pass. 428 */ 429 430struct tests_seq { 431 const char *name; 432 const char *streamtype; 433 uint64_t timeout_us; 434 lws_ss_constate_t must_see; 435 unsigned int mask_unexpected; 436 size_t eom_pass; 437} tests_seq[] = { 438 439 /* 440 * We just get a 200 from httpbin.org as a sanity check first 441 */ 442 443 { 444 "h1:80 just get 200", 445 "t_h1", 5 * LWS_US_PER_SEC, LWSSSCS_QOS_ACK_REMOTE, 446 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 447 (1 << LWSSSCS_ALL_RETRIES_FAILED), 448 0 449 }, 450 { 451 "h1:443 just get 200", 452 "t_h1_tls", 5 * LWS_US_PER_SEC, LWSSSCS_QOS_ACK_REMOTE, 453 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 454 (1 << LWSSSCS_ALL_RETRIES_FAILED), 455 0 456 }, 457 { 458 "h2:443 just get 200", 459 "t_h2_tls", 5 * LWS_US_PER_SEC, LWSSSCS_QOS_ACK_REMOTE, 460 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 461 (1 << LWSSSCS_ALL_RETRIES_FAILED), 462 0 463 }, 464 465 /* 466 * We arranged that the server will delay 10s before sending the 467 * response, but set our ss timeout for 5s. So we expect to see 468 * our timeout and not an ACK / 200. 469 */ 470 471 { 472 "h1:80 timeout after connection", 473 "d_h1", 5 * LWS_US_PER_SEC, LWSSSCS_TIMEOUT, 474 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 475 (1 << LWSSSCS_ALL_RETRIES_FAILED), 476 0 477 }, 478 { 479 "h1:443 timeout after connection", 480 "d_h1_tls", 5 * LWS_US_PER_SEC, LWSSSCS_TIMEOUT, 481 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 482 (1 << LWSSSCS_ALL_RETRIES_FAILED), 483 0 484 }, 485 { 486 "h2:443 timeout after connection", 487 "d_h2_tls", 5 * LWS_US_PER_SEC, LWSSSCS_TIMEOUT, 488 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 489 (1 << LWSSSCS_ALL_RETRIES_FAILED), 490 0 491 }, 492 493 /* 494 * We are talking to a nonexistant dns address "bogus.nope". We expect 495 * in each case to hear that is unreachable, before any ss timeout. 496 */ 497 498 { 499 "h1:80 NXDOMAIN", 500 "nxd_h1", 65 * LWS_US_PER_SEC, LWSSSCS_UNREACHABLE, 501 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 502 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_ALL_RETRIES_FAILED), 503 0 504 }, 505 { 506 "h1:443 NXDOMAIN", 507 "nxd_h1_tls", 35 * LWS_US_PER_SEC, LWSSSCS_UNREACHABLE, 508 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 509 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_ALL_RETRIES_FAILED), 510 0 511 }, 512 { 513 "h2:443 NXDOMAIN", 514 "nxd_h2_tls", 35 * LWS_US_PER_SEC, LWSSSCS_UNREACHABLE, 515 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 516 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_ALL_RETRIES_FAILED), 517 0 518 }, 519 520 /* 521 * We are talking to a nonexistant dns address "bogus.nope". We expect 522 * that if we stick around longer, retries will also end up all failing. 523 * We might see the timeout depending on blocking getaddrinfo 524 * behaviour. 525 */ 526 527 { 528 "h1:80 NXDOMAIN exhaust retries", 529 "nxd_h1", 65 * LWS_US_PER_SEC, LWSSSCS_ALL_RETRIES_FAILED, 530 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE), 531 0 532 }, 533 { 534 "h1:443 NXDOMAIN exhaust retries", 535 "nxd_h1_tls", 65 * LWS_US_PER_SEC, LWSSSCS_ALL_RETRIES_FAILED, 536 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE), 537 0 538 }, 539 { 540 "h2:443 NXDOMAIN exhaust retries", 541 "nxd_h2_tls", 65 * LWS_US_PER_SEC, LWSSSCS_ALL_RETRIES_FAILED, 542 (1 << LWSSSCS_QOS_ACK_REMOTE) | (1 << LWSSSCS_QOS_NACK_REMOTE), 543 0 544 }, 545 546 /* 547 * Let's request some bulk data from httpbin.org 548 */ 549 550 { 551 "h1:80 read bulk", 552 "bulk_h1", 5 * LWS_US_PER_SEC, LWSSSCS_QOS_ACK_REMOTE, 553 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 554 (1 << LWSSSCS_ALL_RETRIES_FAILED), 555 12345 556 }, 557 { 558 "h1:443 read bulk", 559 "bulk_h1_tls", 5 * LWS_US_PER_SEC, LWSSSCS_QOS_ACK_REMOTE, 560 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 561 (1 << LWSSSCS_ALL_RETRIES_FAILED), 562 12345 563 }, 564 { 565 "h2:443 read bulk", 566 "bulk_h2_tls", 5 * LWS_US_PER_SEC, LWSSSCS_QOS_ACK_REMOTE, 567 (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_QOS_NACK_REMOTE) | 568 (1 << LWSSSCS_ALL_RETRIES_FAILED), 569 12345 570 }, 571 572 /* 573 * Let's fail at the tls negotiation various ways 574 */ 575 576 { 577 "h1:badcert_hostname", 578 "badcert_hostname", 6 * LWS_US_PER_SEC, LWSSSCS_ALL_RETRIES_FAILED, 579 (1 << LWSSSCS_QOS_NACK_REMOTE), 580 0 581 }, 582 { 583 "h1:badcert_expired", 584 "badcert_expired", 6 * LWS_US_PER_SEC, LWSSSCS_ALL_RETRIES_FAILED, 585 (1 << LWSSSCS_QOS_NACK_REMOTE), 586 0 587 }, 588 { 589 "h1:badcert_selfsigned", 590 "badcert_selfsigned", 6 * LWS_US_PER_SEC, LWSSSCS_ALL_RETRIES_FAILED, 591 (1 << LWSSSCS_QOS_NACK_REMOTE), 592 0 593 }, 594 595}; 596 597typedef struct myss { 598 struct lws_ss_handle *ss; 599 void *opaque_data; 600 601 size_t rx_seen; 602 char result_reported; 603} myss_t; 604 605 606/* secure streams payload interface */ 607 608static lws_ss_state_return_t 609myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) 610{ 611 myss_t *m = (myss_t *)userobj; 612 613 m->rx_seen += len; 614 615 if (flags & LWSSS_FLAG_EOM) 616 lwsl_notice("%s: %s len %d, fl %d, received %u bytes\n", 617 __func__, lws_ss_tag(m->ss), (int)len, flags, 618 (unsigned int)m->rx_seen); 619 620 return 0; 621} 622 623static lws_ss_state_return_t 624myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, 625 int *flags) 626{ 627 //myss_t *m = (myss_t *)userobj; 628 629 /* in this example, we don't send stuff */ 630 631 return LWSSSSRET_TX_DONT_SEND; 632} 633 634static lws_ss_state_return_t 635myss_state(void *userobj, void *sh, lws_ss_constate_t state, 636 lws_ss_tx_ordinal_t ack) 637{ 638 myss_t *m = (myss_t *)userobj; 639 struct tests_seq *curr_test = ( struct tests_seq *)m->opaque_data; 640 char buf[8]; 641 size_t sl; 642 643 lwsl_info("%s: %s: %s (%d), ord 0x%x\n", __func__, lws_ss_tag(m->ss), 644 lws_ss_state_name((int)state), state, (unsigned int)ack); 645 646 if (curr_test->mask_unexpected & (1u << state)) { 647 /* 648 * We have definitively failed on an unexpected state received 649 */ 650 651 lwsl_warn("%s: failing on unexpected state %s\n", 652 __func__, lws_ss_state_name((int)state)); 653 654fail: 655 m->result_reported = 1; 656 tests_fail++; 657 /* we'll start the next test next time around the event loop */ 658 lws_sul_schedule(context, 0, &sul_next_test, tests_start_next, 1); 659 660 return LWSSSSRET_OK; 661 } 662 663 if (state == curr_test->must_see) { 664 665 if (curr_test->eom_pass != m->rx_seen) { 666 lwsl_notice("%s: failing on rx %d, expected %d\n", 667 __func__, (int)m->rx_seen, 668 (int)curr_test->eom_pass); 669 goto fail; 670 } 671 672 lwsl_warn("%s: saw expected state %s\n", 673 __func__, lws_ss_state_name((int)state)); 674 m->result_reported = 1; 675 tests_pass++; 676 /* we'll start the next test next time around the event loop */ 677 lws_sul_schedule(context, 0, &sul_next_test, tests_start_next, 1); 678 679 return LWSSSSRET_OK; 680 } 681 682 switch (state) { 683 case LWSSSCS_CREATING: 684 lws_ss_start_timeout(m->ss, 685 (unsigned int)(curr_test->timeout_us / LWS_US_PER_MS)); 686 if (curr_test->eom_pass) { 687 sl = (size_t)lws_snprintf(buf, sizeof(buf), "%u", 688 (unsigned int)curr_test->eom_pass); 689 if (lws_ss_set_metadata(m->ss, "amount", buf, sl)) 690 return LWSSSSRET_DISCONNECT_ME; 691 } 692 return lws_ss_client_connect(m->ss); 693 694 case LWSSSCS_DESTROYING: 695 if (!m->result_reported) { 696 lwsl_user("%s: failing on unexpected destruction\n", 697 __func__); 698 699 tests_fail++; 700 /* we'll start the next test next time around the event loop */ 701 lws_sul_schedule(context, 0, &sul_next_test, tests_start_next, 1); 702 } 703 break; 704 705 default: 706 break; 707 } 708 709 return LWSSSSRET_OK; 710} 711 712static void 713tests_start_next(lws_sorted_usec_list_t *sul) 714{ 715 struct tests_seq *ts; 716 lws_ss_info_t ssi; 717 static struct lws_ss_handle *h; 718 719 /* destroy the old one */ 720 721 if (h) { 722 lwsl_info("%s: destroying previous stream\n", __func__); 723 lws_ss_destroy(&h); 724 } 725 726 if ((unsigned int)tests >= LWS_ARRAY_SIZE(tests_seq)) { 727 lwsl_notice("Completed all tests\n"); 728 interrupted = 1; 729 return; 730 } 731 732 ts = &tests_seq[tests++]; 733 734 /* Create the next test stream */ 735 736 memset(&ssi, 0, sizeof(ssi)); 737 ssi.handle_offset = offsetof(myss_t, ss); 738 ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data); 739 ssi.rx = myss_rx; 740 ssi.tx = myss_tx; 741 ssi.state = myss_state; 742 ssi.user_alloc = sizeof(myss_t); 743 ssi.streamtype = ts->streamtype; 744 745 lwsl_user("%s: %d: %s\n", __func__, tests, ts->name); 746 747 if (lws_ss_create(context, 0, &ssi, ts, &h, NULL, NULL)) { 748 lwsl_err("%s: failed to create secure stream\n", 749 __func__); 750 tests_fail++; 751 interrupted = 1; 752 return; 753 } 754} 755 756static int 757app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, 758 int current, int target) 759{ 760 switch (target) { 761 762 case LWS_SYSTATE_OPERATIONAL: 763 if (current == LWS_SYSTATE_OPERATIONAL) 764 /* we'll start the next test next time around the event loop */ 765 lws_sul_schedule(context, 0, &sul_next_test, tests_start_next, 1); 766 break; 767 } 768 769 return 0; 770} 771 772static lws_state_notify_link_t * const app_notifier_list[] = { 773 &nl, NULL 774}; 775 776#if defined(LWS_WITH_SYS_METRICS) 777static int 778my_metric_report(lws_metric_pub_t *mp) 779{ 780 lws_metric_bucket_t *sub = mp->u.hist.head; 781 char buf[192]; 782 783 do { 784 if (lws_metrics_format(mp, &sub, buf, sizeof(buf))) 785 lwsl_user("%s: %s\n", __func__, buf); 786 } while ((mp->flags & LWSMTFL_REPORT_HIST) && sub); 787 788 /* 0 = leave metric to accumulate, 1 = reset the metric */ 789 790 return 1; 791} 792 793static const lws_system_ops_t system_ops = { 794 .metric_report = my_metric_report, 795}; 796 797#endif 798 799static void 800sigint_handler(int sig) 801{ 802 interrupted = 1; 803} 804 805int 806main(int argc, const char **argv) 807{ 808 struct lws_context_creation_info info; 809 const char *pp; 810 811 signal(SIGINT, sigint_handler); 812 813 memset(&info, 0, sizeof info); 814 lws_cmdline_option_handle_builtin(argc, argv, &info); 815 816 if ((pp = lws_cmdline_option(argc, argv, "--amount"))) 817 amount = (size_t)atoi(pp); 818 819 /* set the expected payload for the bulk-related tests to amount */ 820 821 tests_seq[12].eom_pass = tests_seq[13].eom_pass = 822 tests_seq[14].eom_pass = amount; 823#if !defined(LWS_SS_USE_SSPC) 824 // puts(default_ss_policy); 825#endif 826 827 lwsl_user("LWS secure streams error path tests [-d<verb>]\n"); 828 829 info.fd_limit_per_thread = 1 + 16 + 1; 830 info.port = CONTEXT_PORT_NO_LISTEN; 831#if defined(LWS_SS_USE_SSPC) 832 info.protocols = lws_sspc_protocols; 833 { 834 const char *p; 835 836 /* connect to ssproxy via UDS by default, else via 837 * tcp connection to this port */ 838 if ((p = lws_cmdline_option(argc, argv, "-p"))) 839 info.ss_proxy_port = (uint16_t)atoi(p); 840 841 /* UDS "proxy.ss.lws" in abstract namespace, else this socket 842 * path; when -p given this can specify the network interface 843 * to bind to */ 844 if ((p = lws_cmdline_option(argc, argv, "-i"))) 845 info.ss_proxy_bind = p; 846 847 /* if -p given, -a specifies the proxy address to connect to */ 848 if ((p = lws_cmdline_option(argc, argv, "-a"))) 849 info.ss_proxy_address = p; 850 } 851#else 852 info.pss_policies_json = default_ss_policy; 853 info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | 854 LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW | 855 LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 856#endif 857 858 /* integrate us with lws system state management when context created */ 859 860 nl.name = "app"; 861 nl.notify_cb = app_system_state_nf; 862 info.register_notifier_list = app_notifier_list; 863 864#if defined(LWS_WITH_SYS_METRICS) 865 info.system_ops = &system_ops; 866#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) 867 info.metrics_prefix = "ssmex"; 868#endif 869#endif 870 871 /* create the context */ 872 873 context = lws_create_context(&info); 874 if (!context) { 875 lwsl_err("lws init failed\n"); 876 return 1; 877 } 878 879 /* the event loop */ 880 881 do { } while(lws_service(context, 0) >= 0 && !interrupted); 882 883 lws_context_destroy(context); 884 885 lwsl_user("Completed: %s (pass %d, fail %d)\n", 886 tests_pass == tests && !tests_fail ? "OK" : "failed", 887 tests_pass, tests_fail); 888 889 return !(tests_pass == tests && !tests_fail); 890} 891