Compare commits

..
187 Commits
Author SHA1 Message Date
Sergio R. Caprile b1c2ffe1a0 Merge pull request #3488 from cesanta/fut
fix unit tests
2026-04-01 11:05:28 -03:00
Sergio R. Caprile 9210a762de fix unit tests 2026-04-01 10:30:59 -03:00
Sergio R. Caprile 1620110f50 update version 2026-04-01 08:59:46 -03:00
Sergio R. Caprile 0d882f1b43 patch 2026-03-31 17:38:04 -03:00
Sergio R. Caprile 1bb85799ce fix Makefile 2026-03-25 16:20:01 -03:00
Sergio R. Caprile 12207edd55 Merge pull request #3476 from cesanta/revert-3474-mem
Revert "Make packed fs always available, and manually settable"
2026-03-24 16:24:03 -03:00
Sergio R. Caprile bb37f72a63 Revert "Make packed fs always available, and manually settable" 2026-03-24 16:23:11 -03:00
Sergio R. Caprile b852e0f8c3 Merge pull request #3474 from cesanta/mem
Make packed fs always available, and manually settable
2026-03-24 16:22:33 -03:00
cpq 25dc078af7 Make packed fs always available, and manually settable 2026-03-24 15:03:22 -03:00
Sergio R. Caprile 06f525713f Merge pull request #3473 from cesanta/tests
update tests
2026-03-23 11:45:14 -03:00
Sergio R. Caprile 05ade0ba3c update tests 2026-03-23 11:44:24 -03:00
Sergio R. Caprile ec91eaf0f3 Merge pull request #3472 from cesanta/wifi
update WiFi security definitions
2026-03-20 17:19:25 -03:00
Sergio R. Caprile 8392b9407a update WiFi security definitions 2026-03-20 17:14:38 -03:00
Sergio R. Caprile 60fe567845 Merge pull request #3470 from cesanta/zephyr
fix zephyr tests
2026-03-16 16:30:11 -03:00
Sergio R. Caprile 02e83c77a4 fix zephyr tests 2026-03-16 16:08:34 -03:00
novlean 538a1a9bfa Merge pull request #3469 from cesanta/novlean-patch-1
Add lwIP vs Mongoose benchmark article link
2026-03-16 14:11:02 +00:00
novlean 862fbd0abc Add lwIP vs Mongoose benchmark article link
article
2026-03-16 14:10:46 +00:00
Sergio R. Caprile 2121be1abe Merge pull request #3449 from cesanta/mdns
Obtain IP adress for MDNS automatically
2026-03-13 13:57:14 -03:00
cpq 0c794f19d2 Obtain IP adress for MDNS automatically 2026-03-13 13:47:25 -03:00
Sergio R. Caprile 44ef3b43d2 Merge pull request #3465 from cesanta/volatile
Add volatile to make MIP work on Diab 4.3 for PowerPC
2026-03-12 14:16:40 -03:00
cpq d27e7b3e7e Add volatile to make MIP work with Diab 4.3 on PPC 2026-03-12 13:45:15 -03:00
robertc2000 f0d1800c6b Merge pull request #3462 from cesanta/http-zero-content-len
Content-length not required if request body is empty
2026-03-12 15:40:30 +02:00
robert 81b61d335f Content-length not required if request body is empty 2026-03-12 05:54:21 -04:00
Sergey Lyubka 70f14336f1 Merge pull request #3458 from cesanta/mdnsc
Introduce new mDNS API, add mDNS client example
2026-03-10 19:38:34 +00:00
Sergio R. Caprile 19ea5aba43 Merge pull request #3459 from cesanta/st67w6
Introduce ST67W6 driver
2026-03-09 09:50:19 -03:00
Sergio R. Caprile 2a7fa4cb68 Introduce new mDNS API, add mDNS client example 2026-03-06 11:27:22 -03:00
Sergio R. Caprile 3d094ec921 Introduce ST67W6 driver 2026-03-06 09:05:37 -03:00
Sergio R. Caprile abe19d2367 Merge pull request #3457 from cesanta/cleanup
cleanup source and add some more IPV6 tests
2026-03-03 11:00:56 -03:00
Sergio R. Caprile 2b408e85e9 cleanup source and add some more IPV6 tests 2026-03-03 10:19:52 -03:00
Sergey Lyubka 27897d5514 Merge pull request #3451 from cesanta/dns
add entropy to DNS txnid
2026-03-02 09:38:51 +00:00
cpq dfcfdfd0f7 fix email 2026-02-26 14:49:48 +00:00
Sergio R. Caprile ec032181c6 add entropy to DNS txnid 2026-02-25 11:35:30 -03:00
Sergey Lyubka 1cc6a48eea Merge pull request #3441 from cesanta/certs
Update http-server to load certs from files
2026-02-23 13:50:33 +00:00
cpq 17b4054ea5 Update http-server to load certs from files 2026-02-16 15:45:20 +00:00
Sergio R. Caprile 0ec11128f7 enable Mac WolfSSL tests 2026-02-16 09:38:24 -03:00
Sergio R. Caprile ed0423f79d Merge pull request #3440 from leopoldch/fix/wolfssl-ca-loading
tls: fix WolfSSL CA certificate buffer loading
2026-02-16 09:21:45 -03:00
Léopold Chappuis ba69eb248c tls: fix WolfSSL CA certificate buffer loading 2026-02-13 22:34:13 -05:00
Sergio R. Caprile eefec28b50 Merge pull request #3429 from cesanta/w55rp20
add W55RP20 example
2026-02-13 08:34:48 -03:00
novlean 564c352d0c Merge pull request #3437 from cesanta/novlean-patch-1
Add NXP RW612 OTA Firmware Update link to README
2026-02-11 14:38:37 +00:00
novlean d20eb8b280 Add NXP RW612 OTA Firmware Update link to README
NXP OTA article
2026-02-11 14:38:23 +00:00
Sergio R. Caprile cc617d537d Merge pull request #3436 from cesanta/redirect
provide local info, fix redirect example
2026-02-09 14:51:03 -03:00
Sergio R. Caprile 205d4c6832 provide local info, fix redirect example 2026-02-09 14:42:31 -03:00
robertc2000 18cea946f1 Merge pull request #3435 from cesanta/mqttprop-fix
Improve mqtt props bound checks
2026-02-09 13:11:11 +02:00
robert 0884338b2a Improve mqtt props bound checks 2026-02-09 04:54:58 -05:00
Sergey Lyubka ba768693ed Merge pull request #3432 from cesanta/fuzzbound
validate props, fuzz them
2026-02-06 15:39:14 +00:00
Sergey Lyubka 3170261876 Merge pull request #3431 from cesanta/renesas
fix RA6M4
2026-02-06 15:36:47 +00:00
Sergio R. Caprile 2d7e8aa712 validate props, fuzz them 2026-02-06 10:53:25 -03:00
Sergio R. Caprile c083f204fc fix RA6M4 2026-02-05 15:14:18 -03:00
Sergio R. Caprile e83e13d8c6 add W55RP20 example 2026-02-05 10:45:27 -03:00
Sergio R. Caprile 70d268d95f Merge pull request #3428 from cesanta/teeleese
Fix long standing certverify failure TLS bug
2026-02-03 15:55:28 -03:00
Sergio R. Caprile 71f5556de7 standardize Makefile 2026-02-03 15:41:47 -03:00
Sergio R. Caprile 8c64266f95 Fix long standing certverify failure TLS bug 2026-02-03 15:26:38 -03:00
cpq 124b4e93c7 Switch off buffering 2026-02-02 09:56:07 +00:00
cpq e01eeed303 update 2026-02-02 08:04:39 +00:00
cpq 4b3cfb4bc0 update 2026-02-02 08:03:55 +00:00
cpq e5dfbec36d update 2026-02-02 08:03:28 +00:00
cpq be1587a933 update 2026-02-02 08:03:09 +00:00
cpq 3e87f18a29 update 2026-02-02 08:02:36 +00:00
cpq 2214cf66a4 update 2026-02-02 08:02:09 +00:00
cpq 7b886ccbde update 2026-02-02 08:01:22 +00:00
cpq b69b288d6c better clean 2026-02-01 13:21:00 +00:00
cpq 37fe251d31 Update readme 2026-01-31 13:22:37 +00:00
Sergio R. Caprile 5ec7ec141a Merge pull request #3427 from cesanta/tests
split nightly tests, fix some
2026-01-30 14:00:07 -03:00
Sergio R. Caprile e31079e107 split nightly tests, fix some 2026-01-30 13:58:04 -03:00
Sergio R. Caprile 1701fb0d03 Merge pull request #3425 from cesanta/vc98
Make built-in TLS c89-friendly
2026-01-30 12:46:59 -03:00
cpq f2ce01641e Make built-in TLS vc98-friendly 2026-01-30 11:40:26 -03:00
Sergio R. Caprile b5e4eef966 Merge pull request #3424 from cesanta/tls
Read out all pending TLS data
2026-01-30 11:25:17 -03:00
cpq b93822fc50 Read out all pending TLS data 2026-01-30 10:54:06 -03:00
Sergio R. Caprile 715f98c546 Merge pull request #3426 from cesanta/checksum
Add checksum validation
2026-01-28 13:26:00 -03:00
Sergio R. Caprile b1878dc9d0 Add checksum validation 2026-01-28 13:14:48 -03:00
Sergey Lyubka fe8cf7d9d6 Merge pull request #3423 from cesanta/singleton
Add http singleton client example
2026-01-28 14:50:27 +00:00
cpq 5fa6b0a6ef Add http singleton client example 2026-01-28 14:33:34 +00:00
Sergey Lyubka d3ab638e5d Merge pull request #3422 from cesanta/redir
Add http redirect example
2026-01-28 14:29:24 +00:00
cpq 44e773af54 Add http redirect example 2026-01-28 10:29:46 +00:00
Sergio R. Caprile ece3f43f34 Merge pull request #3421 from cesanta/becksum
Becksum
2026-01-27 10:40:30 -03:00
Sergio R. Caprile edd1350864 make tests big-endian- and Docker-friendly 2026-01-27 10:32:50 -03:00
Sergio R. Caprile 8e0ef3ca5c rewrite L4 checksum calculation 2026-01-27 10:32:50 -03:00
Sergio R. Caprile b74913ded5 Merge pull request #3418 from cesanta/down
Fast fail mg_connect() if network is down
2026-01-27 09:50:54 -03:00
Sergio R. Caprile 42abbb7e55 Merge pull request #3417 from cesanta/ota
Add OTA over mqtt example
2026-01-27 09:01:56 -03:00
cpq a4869febf6 Add OTA over mqtt example 2026-01-27 10:51:37 +00:00
cpq b10355a536 Fast fail mg_connect() if network is down 2026-01-27 10:46:42 +00:00
cpq e86d4da129 Export mg_pfn_iobuf_noresize 2026-01-24 11:32:32 +00:00
cpq 46ad050cea Less noisy tls_builtin 2026-01-23 06:47:39 +00:00
robertc2000 2a7cf8d5a7 Merge pull request #3416 from cesanta/tests-xq
install xq
2026-01-22 16:39:33 +02:00
robert 07fd086230 install xq 2026-01-22 09:15:33 -05:00
Sergio R. Caprile f040d094ea Merge pull request #3414 from cesanta/addnochacha
add test for no chacha20
2026-01-21 15:08:05 -03:00
Sergio R. Caprile f101d1422a add test for no chacha20 2026-01-21 15:06:24 -03:00
Sergio R. Caprile 6631c5db87 Merge pull request #3413 from cesanta/fixbe
fix one too many checks in chacha20
2026-01-21 14:51:03 -03:00
Sergio R. Caprile c429a0dc56 fix one too many checks in chacha20 2026-01-21 14:42:55 -03:00
Sergio R. Caprile 3f59974639 Merge pull request #3412 from cesanta/unbreak
unbreak stuff after last commit
2026-01-20 18:58:12 -03:00
Sergio R. Caprile c10a88b64b unbreak stuff after last commit 2026-01-20 18:40:36 -03:00
cpq d5e5b3397d Add RSA & chain support 2026-01-20 18:23:58 +00:00
Sergio R. Caprile ab83acf2d1 Merge pull request #3411 from cesanta/rndvc98
Fix rnd stuff in vc98 image
2026-01-16 10:47:26 -03:00
Sergio R. Caprile 1db25117b0 Fix rnd stuff in vc98 image 2026-01-16 10:27:13 -03:00
novlean bd1271dd41 Merge pull request #3410 from cesanta/novlean-patch-1
Add STM32 Ethernet and caches article link
2026-01-15 13:42:12 +00:00
novlean 5dddbb3fce Add STM32 Ethernet and caches article link
new article
2026-01-15 13:42:00 +00:00
Sergio R. Caprile a28decbef4 Merge pull request #3409 from cesanta/eseenetepe
Allow more than 1 address in received DHCP options
2026-01-14 17:40:25 -03:00
Sergio R. Caprile 73d9c125c0 Allow more than 1 address in received DHCP options 2026-01-14 17:30:48 -03:00
Sergio R. Caprile 667abfb5e1 disable failing tests (issues created) 2026-01-14 16:59:24 -03:00
Sergey Lyubka 8c65c2f9af Merge pull request #3407 from cesanta/unsub
Add mg_mqtt_unsub()
2026-01-14 11:17:50 +00:00
cpq f36205e65f Add mg_mqtt_unsub() 2026-01-14 04:36:50 -05:00
Sergio R. Caprile e2c0b2b477 Merge pull request #3406 from cesanta/reserved
allow creative usage of TCP reserved space in 'flags'
2026-01-12 13:56:18 -03:00
Sergio R. Caprile 29254ce84b allow creative usage of TCP reserved space in 'flags' 2026-01-12 12:27:36 -03:00
Sergio R. Caprile 1a0716ae4d Merge pull request #3404 from cesanta/der
Allow comments in PEM
2026-01-12 11:58:41 -03:00
Sergio R. Caprile e46ee68a95 nit 2026-01-12 10:55:40 -03:00
Sergio R. Caprile db496fd7dd remove respective old comment 2026-01-12 10:54:15 -03:00
cpq 0a3e129527 Allow comments in PEM 2026-01-12 10:54:15 -03:00
Sergio R. Caprile 12491777bb Merge pull request #3405 from cesanta/cache7
Synopsys driver handles STM32H7 and N6 DCACHE
2026-01-12 10:52:49 -03:00
Sergio R. Caprile ad4b87e24a Synopsys driver handles STM32H7 and N6 DCACHE
(proper memory placement required)
2026-01-12 10:25:26 -03:00
Sergio R. Caprile 7ffc51d38e Merge pull request #3398 from cesanta/zeph
Disable dirent in zephyr
2026-01-08 09:22:36 -03:00
cpq c9243c3f47 Disable dirent in zephyr 2026-01-08 08:50:05 +00:00
Sergey Lyubka 0d6c424e62 Merge pull request #3397 from cesanta/cube
Add support for N6 in ARCH_CUBE
2026-01-07 18:16:27 +00:00
Sergio R. Caprile dc9395c904 Add support for N6 in ARCH_CUBE 2026-01-07 14:03:37 -03:00
Sergio R. Caprile 2bb750070a Merge pull request #3395 from cesanta/tests
unbreak net_builtin tests
2026-01-07 10:59:38 -03:00
Sergio R. Caprile 17b0b65cab unbreak net_builtin tests 2026-01-07 10:48:22 -03:00
Sergio R. Caprile b5b63e731c Merge pull request #3394 from cesanta/match
avoid unlikely and harmless but possible read out of bounds
2026-01-07 10:38:42 -03:00
Sergio R. Caprile 7e376bb860 avoid unlikely and harmless but possible read out of bounds 2026-01-07 10:33:34 -03:00
cpq b313d697ff Limit queue fetches 2026-01-07 06:36:44 +00:00
cpq 1cadd2b2aa Fetch multiple frames from the queue 2026-01-07 05:53:55 +00:00
cpq f40b377fff Make MG_TCPIP_WIN tunable 2026-01-07 05:46:39 +00:00
cpq 43509c1781 Enable zephyr dirent 2026-01-06 18:16:54 +00:00
novlean 209d1dd426 Merge pull request #3392 from cesanta/novlean-patch-1
Update copyright year in LICENSE file
2026-01-05 13:00:15 +00:00
novlean 1f9e1a38b5 Update copyright year in LICENSE file
2026
2026-01-05 13:00:04 +00:00
Sergio R. Caprile 69936ac9f6 Merge pull request #3391 from cesanta/lwipdoze
fix lwIP redefinition of nfds_t
2026-01-05 08:48:06 -03:00
Sergio R. Caprile 6de419d14c fix lwIP redefinition of nfds_t 2026-01-02 14:22:55 -03:00
Sergio R. Caprile 1200f50e2e Merge pull request #3382 from cesanta/mcast
Make mcast/bcast consistent across stacks
2025-12-29 14:24:51 -03:00
Sergio R. Caprile 73bec74380 Make mcast/bcast consistent across stacks
Fix net_builtin inconsistency
2025-12-29 14:22:59 -03:00
Sergio R. Caprile f70647e28e Merge pull request #3386 from cesanta/mbed
Mbedtls nits
2025-12-29 13:30:10 -03:00
cpq effa61f9b2 Mbedtls nits 2025-12-29 13:29:27 -03:00
novlean b8bde63554 Merge pull request #3385 from cesanta/novlean-patch-1
Remove RP2350 OTA Firmware Update link from README
2025-12-24 10:08:49 +01:00
novlean 409d3d388a Remove RP2350 OTA Firmware Update link from README 2025-12-24 10:08:37 +01:00
Sergio R. Caprile 2c4bf4cea4 optimize for size, as everyone else 2025-12-23 14:06:06 -03:00
Sergio R. Caprile 1b9c3a0fb0 Merge pull request #3383 from cesanta/zeph
Zephyr nits
2025-12-23 13:45:42 -03:00
cpq 71cc0cfd80 Zephyr nits 2025-12-23 13:12:56 -03:00
Sergio R. Caprile 63da5aa403 Merge pull request #3381 from cesanta/tests
add on-demand test workflow
2025-12-19 14:32:21 -03:00
Sergio R. Caprile cb13900c32 add on-demand test workflow 2025-12-19 14:31:23 -03:00
Sergio R. Caprile 926a0e2cf2 Merge pull request #3380 from cesanta/l2
fix wrong length returned from l2_eth_rx
2025-12-19 14:18:42 -03:00
Sergio R. Caprile 2bfcfeb359 fix wrong length returned from l2_eth_rx 2025-12-19 14:11:13 -03:00
Sergio R. Caprile 16d8e3b4f9 restrict fuzz run time below GH allowed time 2025-12-18 11:03:17 -03:00
Sergio R. Caprile 682fe2c9a0 try extending fuzz running time 2025-12-17 19:12:02 -03:00
Sergio R. Caprile bdd309b10b Merge pull request #3379 from cesanta/moretests
add more tests, fix NDP
2025-12-17 17:45:17 -03:00
Sergio R. Caprile 21fbf9cdc5 add more tests, fix NDP 2025-12-17 17:39:09 -03:00
Sergio R. Caprile 9ed98cecff Merge pull request #3378 from cesanta/fuzz
fix fuzz, add ICMP tests
2025-12-17 17:36:08 -03:00
Sergio R. Caprile ab4e229f57 fix fuzz, add ICMP tests 2025-12-17 13:22:24 -03:00
novlean 7feab86700 Merge pull request #3377 from cesanta/novlean-patch-1
Add STM32 OTA Firmware Update link to README
2025-12-17 15:39:23 +00:00
novlean 6bc0af5dd0 Add STM32 OTA Firmware Update link to README 2025-12-17 15:39:06 +00:00
Sergio R. Caprile 6cb685b700 fix fuzzer 2025-12-16 16:46:03 -03:00
cpq 5c582c35d8 ota_frdm -> ota_rw612 2025-12-16 05:10:34 +00:00
Sergey Lyubka 10ab9ae1ea Merge pull request #3370 from cesanta/l2
Decouple from Ethernet, introduce "l2"
2025-12-15 14:49:56 +00:00
cpq 756d127cab C89 nitpick 2025-12-15 10:03:12 +00:00
Sergio R. Caprile a49eb47a91 Merge pull request #3373 from cesanta/mbedtls4
Add support for MbedTLS 4
2025-12-12 20:23:43 -03:00
Sergio R. Caprile e15056a9e4 Add support for MbedTLS 4 2025-12-12 20:19:36 -03:00
Sergio R. Caprile 66d4069644 Decouple from Ethernet, introduce "l2" 2025-12-12 13:46:20 -03:00
novlean 9aae863956 Merge pull request #3369 from cesanta/novlean-patch-1
Remove MQTT article link from README
2025-12-11 16:39:09 +00:00
novlean 878ec8a86c Remove MQTT article link from README 2025-12-11 16:38:44 +00:00
Sergio R. Caprile 8fccfca17b Merge pull request #3368 from cesanta/1000m
Add support for GbE
2025-12-10 13:43:01 -03:00
Sergio R. Caprile 69925c774d Add support for GbE 2025-12-10 10:23:47 -03:00
Sergio R. Caprile c1298973e1 Merge pull request #3367 from cesanta/rtl8211
Add support for RTL8211
2025-12-10 09:55:40 -03:00
Sergio R. Caprile bedda34515 Add support for RTL8211 2025-12-10 09:53:55 -03:00
Sergio R. Caprile 767b0ab90b Merge pull request #3366 from cesanta/ip6
Add support for prefix, link local and global in same net
2025-12-08 16:09:35 -03:00
Sergio R. Caprile b0109c3319 Add support for prefix, link local and global in same net 2025-12-08 16:05:32 -03:00
Sergio R. Caprile a42271a088 Merge pull request #3360 from cesanta/tests
fix tests not uploading on failure
2025-12-08 10:56:33 -03:00
Sergio R. Caprile 154e471256 Merge pull request #3361 from cesanta/neats
fix IPv6
2025-12-08 10:32:28 -03:00
cpq bfc411acaf Squash warning 2025-12-07 14:31:28 +00:00
cpq 2acbb0ad62 Using mg_str 2025-12-06 18:59:45 +00:00
Sergio R. Caprile 60ce504489 fix IPv6 2025-12-05 16:22:16 -03:00
Sergio R. Caprile 22b8eda0df fix tests not uploading on failure 2025-12-05 14:19:24 -03:00
Sergio R. Caprile 80ed81583e Merge pull request #3359 from cesanta/tests
fix Wizard-depent tests (track changes)
2025-12-05 13:26:48 -03:00
Sergio R. Caprile ce0c3a0fe7 fix Wizard-depent tests (track changes) 2025-12-05 13:23:43 -03:00
Sergey Lyubka 492c6385da Merge pull request #3358 from cesanta/diab
Add support for Windriver's Diab v4 (stricter C89 w/ extensions)
2025-12-05 10:32:51 +00:00
Sergio R. Caprile 5b023cb85b Add support for Windriver's Diab v4 (stricter C89) 2025-12-04 13:58:49 -03:00
Sergio R. Caprile 4717a15c17 Merge pull request #3357 from cesanta/atonscope
fix mg_aton not setting is_ip6 with scope_ids
2025-12-04 12:22:06 -03:00
Sergio R. Caprile d6e7f8ee83 fix mg_aton not setting is_ip6 with scope_ids 2025-12-04 12:18:45 -03:00
Sergio R. Caprile 17ff656a7d Merge pull request #3354 from cesanta/closefree
do not serve files when closing
2025-12-03 15:39:21 -03:00
Sergio R. Caprile 4e8965cce5 do not serve files when closing 2025-12-03 14:25:55 -03:00
Sergey Lyubka b3d27a3c43 Merge pull request #3353 from cesanta/safety-net
Add safety nets for lousy networks
2025-12-03 15:49:41 +00:00
Sergio R. Caprile 66dcca9392 Add safety nets for lousy networks 2025-12-03 11:31:47 -03:00
Sergio R. Caprile 0f1a3cf84b Merge pull request #3348 from cesanta/dhcp
allow no options in DHCP msgs
2025-12-01 10:46:40 -03:00
Sergio R. Caprile a4b7025396 allow no options in DHCP msgs 2025-12-01 10:46:03 -03:00
Sergey Lyubka 9c148fd47e Merge pull request #3351 from cesanta/dnssd
Introduce DNS-SD using mDNS
2025-12-01 10:10:17 +00:00
Sergio R. Caprile 293d8c22e1 Introduce DNS-SD using mDNS 2025-11-28 16:12:08 -03:00
Sergio R. Caprile 5f2ff680d5 Merge pull request #3350 from cesanta/portenta
fix Portenta Cube after cyw.h updates
2025-11-27 17:09:43 -03:00
Sergio R. Caprile 8eaca9437e fix Portenta Cube after cyw.h updates 2025-11-27 17:08:00 -03:00
cpq 6e72bf4b5f less verbose cyw driver 2025-11-26 16:27:51 +00:00
cpq 710e7b2004 less verbose cyw driver 2025-11-26 16:24:16 +00:00
Sergio R. Caprile 666a3b68e9 update mip test 2025-11-24 16:11:00 -03:00
Sergio R. Caprile 02e97adbef no router => no route 2025-11-24 15:09:35 -03:00
153 changed files with 9856 additions and 3148 deletions

No files matched your search

+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: make -C test ${{ matrix.target }} ARGS="-max_total_time=7200" - run: make -C test ${{ matrix.target }} ARGS="-max_total_time=14400"
# as we're not getting access to test units causing a problem, convert from the log: # as we're not getting access to test units causing a problem, convert from the log:
# base64 -d > dataofdeath # base64 -d > dataofdeath
+23 -148
View File
@@ -1,4 +1,4 @@
name: Full build and test name: Full build
on: on:
schedule: schedule:
- cron: '0 22 * * *' # run at 10 PM UTC - cron: '0 22 * * *' # run at 10 PM UTC
@@ -29,6 +29,11 @@ jobs:
select: "-DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=0" select: "-DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=0"
- ssl: WOLFSSL - ssl: WOLFSSL
select: "-DMG_ENABLE_POLL=1 -DMG_ENABLE_EPOLL=0" select: "-DMG_ENABLE_POLL=1 -DMG_ENABLE_EPOLL=0"
include:
- ssl: BUILTIN
cc: gcc
target: test
select: "-DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=1 -DMG_ENABLE_CHACHA20=0"
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }} name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env: env:
CC: ${{ matrix.cc }} CC: ${{ matrix.cc }}
@@ -69,7 +74,7 @@ jobs:
- uses: webfactory/ssh-agent@v0.9.1 - uses: webfactory/ssh-agent@v0.9.1
with: with:
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }} ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
- run: if [ "${{ matrix.target }}" == "mip_tap_test" ]; then ./test/setup_ga_network.sh ; fi && sudo apt -y update ; sudo apt -y install libmbedtls-dev libwolfssl-dev && make -C test ${{ matrix.target }} IPV6=0 > log - run: if [ "${{ matrix.target }}" == "mip_tap_test" ]; then ./test/setup_ga_network.sh ; export IPV6=0; else export IPV6=1 ; fi && sudo apt -y update ; sudo apt -y install libmbedtls-dev libwolfssl-dev && make -C test ${{ matrix.target }} > log
- if: success() || failure() - if: success() || failure()
run: | run: |
cat log cat log
@@ -87,7 +92,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: make -C test ${{ matrix.target }} - run: make -C test ${{ matrix.target }} IPV6=1
s390: s390:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
@@ -145,25 +150,21 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
# Mongoose does not work with WolfSSL version in MacOS ssl: ["", BUILTIN, MBEDTLS, OPENSSL, WOLFSSL]
ssl: ["", BUILTIN, MBEDTLS, OPENSSL] # , WOLFSSL]
select: [-DMG_ENABLE_POLL=0, -DMG_ENABLE_POLL=1] select: [-DMG_ENABLE_POLL=0, -DMG_ENABLE_POLL=1]
include:
- ssl: MBEDTLS
env:
# MbedTLS version in MacOS refuses to validate a certificate without a hostname, causing MQTT tests to fail when using MQTT_LOCALHOST
tflags: -DNO_MQTT_TESTS
exclude: exclude:
- ssl: MBEDTLS - ssl: MBEDTLS
select: -DMG_ENABLE_POLL=0 select: -DMG_ENABLE_POLL=0
- ssl: MBEDTLS # see issue#2694
select: -DMG_ENABLE_POLL=1
- ssl: OPENSSL - ssl: OPENSSL
select: -DMG_ENABLE_POLL=0 select: -DMG_ENABLE_POLL=0
#- ssl: WOLFSSL - ssl: WOLFSSL
# select: -DMG_ENABLE_POLL=0 select: -DMG_ENABLE_POLL=0
name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }} name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env: env:
SSL: ${{ matrix.ssl }} SSL: ${{ matrix.ssl }}
TFLAGS: ${{ matrix.select }} -DMQTT_LOCALHOST ${{ matrix.env.tflags }} -Wno-sign-conversion # Workaround for MbedTLS 3.5.0 TFLAGS: ${{ matrix.select }} -DMQTT_LOCALHOST ${{ matrix.env.tflags }} -DNO_ABORT -Wno-sign-conversion -Wno-undef # Workarounds for MbedTLS
HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_AUTO_UPDATE: 1
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -195,7 +196,7 @@ jobs:
name: windows ${{ matrix.target }} SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }} name: windows ${{ matrix.target }} SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env: env:
SSL: ${{ matrix.ssl }} SSL: ${{ matrix.ssl }}
TFLAGS: ${{ matrix.select }} -DMQTT_LOCALHOST TFLAGS: ${{ matrix.select }} -DMQTT_LOCALHOST -DNO_ABORT
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
@@ -328,6 +329,7 @@ jobs:
- path: pico-sdk/pico-2-w-picosdk-baremetal-builtin - path: pico-sdk/pico-2-w-picosdk-baremetal-builtin
- path: pico-sdk/w5500-evb-pico-picosdk-baremetal-builtin - path: pico-sdk/w5500-evb-pico-picosdk-baremetal-builtin
- path: pico-sdk/w5500-evb-pico2-picosdk-baremetal-builtin - path: pico-sdk/w5500-evb-pico2-picosdk-baremetal-builtin
- path: pico-sdk/w55rp20-evb-pico-picosdk-baremetal-builtin
- path: stm32/nucleo-f429zi-make-baremetal-builtin - path: stm32/nucleo-f429zi-make-baremetal-builtin
- path: stm32/nucleo-f429zi-make-freertos-builtin - path: stm32/nucleo-f429zi-make-freertos-builtin
- path: stm32/nucleo-f746zg-make-baremetal-builtin - path: stm32/nucleo-f746zg-make-baremetal-builtin
@@ -498,137 +500,10 @@ jobs:
- run: sudo pip install yq - run: sudo pip install yq
- run: cd test/cube && make - run: cd test/cube && make
test_f7: # xpresso_examples:
runs-on: ubuntu-latest # runs-on: ubuntu-latest
steps: # steps:
- uses: actions/checkout@v4 # - uses: actions/checkout@v4
with: { fetch-depth: 2 } # with: { fetch-depth: 2 }
- name: baremetal # - run: sudo pip install yq
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi # - run: cd test/xpresso && make
- run: make -C tutorials/stm32/nucleo-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- name: Cube
run: echo # nothing specific to install or do
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
- run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f746zg-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
test_f4:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-f429zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- name: Cube
run: echo # nothing specific to install or do
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
- run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
test_h743:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
# - name: Cube
# run: echo # nothing specific to install or do
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
test_h723:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-h723zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-h723zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_h5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-h563zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1020:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1060:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/nxp/rt1060-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/nxp/rt1060-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1170:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/nxp/rt1170-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/nxp/rt1170-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_ra6m4:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/renesas/ek-ra6m4-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
# - run: make -C tutorials/renesas/ek-ra6m4-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_tm4c:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/ti/ek-tm4c1294xl-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/ti/ek-tm4c1294xl-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_same54:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/http/device-dashboard/microchip/same54-xpro test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/mqtt/mqtt-client/microchip/same54-xpro test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_pico_w5500:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: SDK
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/pico-sdk/w5500-evb-pico-picosdk-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+143
View File
@@ -0,0 +1,143 @@
name: Full test
on:
schedule:
- cron: '0 23 * * *' # run at 11 PM UTC
# Allow manual runs
workflow_dispatch:
env:
IPV6: 0
jobs:
test_f7:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- name: Cube
run: sudo pip install yq
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
- run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f746zg-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
test_f4:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-f429zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- name: Cube
run: sudo pip install yq
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
- run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
test_h743:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
# - name: Cube
# run: echo # nothing specific to install or do
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
# - run: make -C test/cube PROJECTS=../../tutorials/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
test_h723:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-h723zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-h723zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_h5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/stm32/nucleo-h563zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1020:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1060:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/nxp/rt1060-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/nxp/rt1060-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1170:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/nxp/rt1170-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/nxp/rt1170-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_ra6m4:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/renesas/ek-ra6m4-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
# - run: make -C tutorials/renesas/ek-ra6m4-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_tm4c:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/ti/ek-tm4c1294xl-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/ti/ek-tm4c1294xl-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_same54:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/http/device-dashboard/microchip/same54-xpro test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/mqtt/mqtt-client/microchip/same54-xpro test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_pico_w5500:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: SDK
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/pico-sdk/w5500-evb-pico-picosdk-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+32
View File
@@ -0,0 +1,32 @@
name: On demand ad hoc test
on:
workflow_dispatch:
env:
IPV6: 0
jobs:
macos:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
ssl: [MBEDTLS, WOLFSSL]
select: [-DMG_ENABLE_POLL=1]
name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env:
SSL: ${{ matrix.ssl }}
TFLAGS: ${{ matrix.select }} -DMQTT_LOCALHOST -DNO_ABORT -Wno-sign-conversion -Wno-undef # Workarounds for MbedTLS
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- uses: webfactory/ssh-agent@v0.9.1
with:
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
- run: brew install mbedtls wolfssl mosquitto gawk # jq openssl already pre-installed
- run: /opt/homebrew/opt/mosquitto/sbin/mosquitto -c /Users/runner/work/mongoose/mongoose/test/mosquitto.conf.macos &
- run: make -C test test ASAN_OPTIONS= MBEDTLS=$(echo $(brew --cellar)/mbedtls*/*) OPENSSL=$(echo $(brew --cellar)/openssl*/*) WOLFSSL=$(echo $(brew --cellar)/wolfssl*/*) > log
- if: success() || failure()
run: |
cat log
test/health.awk < log > json
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/macos_test_cc_${{ matrix.ssl }}_${{ matrix.select }}_$(date +"%Y%m%d").json"
+17
View File
@@ -170,6 +170,7 @@ jobs:
- path: pico-sdk/pico-2-w-picosdk-baremetal-builtin - path: pico-sdk/pico-2-w-picosdk-baremetal-builtin
- path: pico-sdk/w5500-evb-pico-picosdk-baremetal-builtin - path: pico-sdk/w5500-evb-pico-picosdk-baremetal-builtin
- path: pico-sdk/w5500-evb-pico2-picosdk-baremetal-builtin - path: pico-sdk/w5500-evb-pico2-picosdk-baremetal-builtin
- path: pico-sdk/w55rp20-evb-pico-picosdk-baremetal-builtin
- path: stm32/nucleo-f429zi-make-baremetal-builtin - path: stm32/nucleo-f429zi-make-baremetal-builtin
- path: stm32/nucleo-f429zi-make-freertos-builtin - path: stm32/nucleo-f429zi-make-freertos-builtin
- path: stm32/nucleo-f746zg-make-baremetal-builtin - path: stm32/nucleo-f746zg-make-baremetal-builtin
@@ -347,6 +348,22 @@ jobs:
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: cd test/cube && make run: cd test/cube && make
# xpresso_examples:
# runs-on: ubuntu-latest
# env:
# GO: 0
# steps:
# - uses: actions/checkout@v4
# with: { fetch-depth: 2 }
# - run: |
# if ./test/match_changed_files.sh '^src|tutorials/stm32/nucleo-*-cube-*'; then
# echo GO=1 >> $GITHUB_ENV
# sudo pip install yq
# fi
# - if: ${{ env.GO == 1 }}
# run: cd test/xpresso && make
# test_f7: # test_f7:
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
# env: # env:
+8 -7
View File
@@ -10,17 +10,18 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
revno: ["--mr v3.7-branch", "--mr v4.2.0"] # LTS, latest stable revno: ["--mr v3.7-branch", "--mr v4.2.0", "--mr v4.3.0"] # LTS, latest stable
name: Zephyr ${{ matrix.revno }} name: Zephyr ${{ matrix.revno }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: test/setup_ga_docker_filesystem.sh - run: test/setup_ga_docker_filesystem.sh
- run: make -C tutorials/zephyr init REPO=zephyrprojectrtos/ci REVNO="${{ matrix.revno }}" - run: make -C tutorials/zephyr init REPO=zephyrprojectrtos/ci:v0.28.8 REVNO="${{ matrix.revno }}"
- run: make -C tutorials/zephyr minify REVNO="${{ matrix.revno }}" - run: make -C tutorials/zephyr minify REVNO="${{ matrix.revno }}"
- run: make -C tutorials/zephyr update - run: make -C tutorials/zephyr update
- run: make -C tutorials/zephyr/device-dashboard build - run: make -C tutorials/zephyr/device-dashboard build REPO=zephyrprojectrtos/ci:v0.28.8
- run: make -C tutorials/zephyr/http-client build - run: make -C tutorials/zephyr/http-client build REPO=zephyrprojectrtos/ci:v0.28.8
- run: make -C tutorials/zephyr/http-server build - run: make -C tutorials/zephyr/http-server build REPO=zephyrprojectrtos/ci:v0.28.8
- run: make -C tutorials/zephyr/mqtt-aws-client build - run: make -C tutorials/zephyr/mqtt-aws-client build REPO=zephyrprojectrtos/ci:v0.28.8
- run: make -C tutorials/zephyr/websocket-server build - run: make -C tutorials/zephyr/websocket-server build REPO=zephyrprojectrtos/ci:v0.28.8
# Latest CI image does not work with pre 4.4, they changed some SDK paths
+1 -1
View File
@@ -1,5 +1,5 @@
Copyright (c) 2004-2013 Sergey Lyubka Copyright (c) 2004-2013 Sergey Lyubka
Copyright (c) 2013-2025 Cesanta Software Limited Copyright (c) 2013-2026 Cesanta Software Limited
All rights reserved All rights reserved
This software is dual-licensed: you can redistribute it and/or modify This software is dual-licensed: you can redistribute it and/or modify
+11 -1
View File
@@ -5,7 +5,7 @@
[![Code Coverage](https://codecov.io/gh/cesanta/mongoose/branch/master/graph/badge.svg)](https://codecov.io/gh/cesanta/mongoose) [![Code Coverage](https://codecov.io/gh/cesanta/mongoose/branch/master/graph/badge.svg)](https://codecov.io/gh/cesanta/mongoose)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/mongoose.svg)](https://issues.oss-fuzz.com/issues?sort=-opened&can=1&q=proj:mongoose) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/mongoose.svg)](https://issues.oss-fuzz.com/issues?sort=-opened&can=1&q=proj:mongoose)
Mongoose is a network library for C/C++. It provides event-driven non-blocking <img src="https://mongoose.ws/images/logo.svg" width="48" height="48" align="left" style="float:left;" /> Mongoose is a network library for C/C++. It provides event-driven non-blocking
APIs for TCP, UDP, HTTP, WebSocket, MQTT, and other protocols. It is designed APIs for TCP, UDP, HTTP, WebSocket, MQTT, and other protocols. It is designed
for connecting devices and bringing them online. On the market since 2004, used for connecting devices and bringing them online. On the market since 2004, used
by vast number of open source and commercial products - it even runs on the by vast number of open source and commercial products - it even runs on the
@@ -198,6 +198,10 @@ We take security seriously:
have specific security requirements and run independent security audits, have specific security requirements and run independent security audits,
of which we get notified and in case of any issue, act similar to (3). of which we get notified and in case of any issue, act similar to (3).
## How to report security vulnerabilities
Please send an email to support@cesanta.com, with the full information.
Do NOT create a github issue.
## Articles ## Articles
@@ -211,6 +215,12 @@ Technical guides and deep dives into embedded web servers, WebUI integration and
- [Web dashboard on Zephyr RTOS](https://mongoose.ws/articles/web-dashboard-on-zephyr-rtos/) - [Web dashboard on Zephyr RTOS](https://mongoose.ws/articles/web-dashboard-on-zephyr-rtos/)
- [Limiting TCP/IP RAM usage on STM32](https://mongoose.ws/articles/limiting-tcpip-ram-usage-on-stm32/) - [Limiting TCP/IP RAM usage on STM32](https://mongoose.ws/articles/limiting-tcpip-ram-usage-on-stm32/)
- [STM32 Ethernet explained](https://mongoose.ws/articles/stm32-ethernet-explained/) - [STM32 Ethernet explained](https://mongoose.ws/articles/stm32-ethernet-explained/)
- [MQTT on a Microcontroller](https://mongoose.ws/articles/mqtt-on-a-microcontroller/)
- [STM32 OTA Firmware Update](https://mongoose.ws/articles/stm32-ota-firmware-update/)
- [RP2350 OTA Firmware Update](https://mongoose.ws/articles/rp2350-ota-firmware-update/)
- [STM32 Ethernet and caches](https://mongoose.ws/articles/stm32-ethernet-and-cache/)
- [NXP RW612 OTA Firmware Update](https://mongoose.ws/articles/rw612-ota-firmware-update/)
- [lwIP vs Mongoose - TCP/IP Stack Integration Benchmark](https://mongoose.ws/articles/lwip-vs-mongoose-tcpip-stack-integration/)
## Contributions ## Contributions
+3741 -1232
View File
File diff suppressed because it is too large. Load diff
+270 -98
View File
@@ -20,7 +20,7 @@
#ifndef MONGOOSE_H #ifndef MONGOOSE_H
#define MONGOOSE_H #define MONGOOSE_H
#define MG_VERSION "7.20" #define MG_VERSION "7.21"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -159,11 +159,14 @@ extern "C" {
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack #define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
#endif #endif
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && !defined(MG_ENABLE_DRIVER_STM32H) #if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N)
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7) #if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
#define MG_ENABLE_DRIVER_STM32F 1 #define MG_ENABLE_DRIVER_STM32F 1
#elif defined(STM32H5) || defined(STM32H7) #elif defined(STM32H5) || defined(STM32H7)
#define MG_ENABLE_DRIVER_STM32H 1 #define MG_ENABLE_DRIVER_STM32H 1
#elif defined(STM32N6)
#define MG_ENABLE_DRIVER_STM32N 1
#else #else
#error Select a driver in mongoose_config.h #error Select a driver in mongoose_config.h
#endif #endif
@@ -173,7 +176,8 @@ extern "C" {
#define MG_TLS MG_TLS_BUILTIN #define MG_TLS MG_TLS_BUILTIN
#endif #endif
#if !defined(MG_OTA) && defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7) #if !defined(MG_OTA) && defined(STM32F1) || defined(STM32F2) || \
defined(STM32F4) || defined(STM32F7)
#define MG_OTA MG_OTA_STM32F #define MG_OTA MG_OTA_STM32F
#elif !defined(MG_OTA) && defined(STM32H5) #elif !defined(MG_OTA) && defined(STM32H5)
#define MG_OTA MG_OTA_STM32H5 #define MG_OTA MG_OTA_STM32H5
@@ -651,7 +655,10 @@ typedef enum { false = 0, true = 1 } bool;
#endif #endif
#endif #endif
typedef unsigned long nfds_t; #if defined(MG_ENABLE_POLL) && MG_ENABLE_POLL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP)
typedef unsigned long nfds_t; // see #3388
#endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
#if MG_ENABLE_WINSOCK #if MG_ENABLE_WINSOCK
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
@@ -722,13 +729,15 @@ typedef int socklen_t;
#if MG_ARCH == MG_ARCH_ZEPHYR #if MG_ARCH == MG_ARCH_ZEPHYR
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/net/socket.h>
// #include <zephyr/posix/dirent.h>
#include <zephyr/posix/fcntl.h>
#include <zephyr/posix/sys/select.h>
#include <zephyr/random/random.h>
#include <zephyr/version.h> #include <zephyr/version.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <zephyr/net/socket.h>
#include <zephyr/posix/fcntl.h>
#include <zephyr/posix/sys/select.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@@ -1242,10 +1251,12 @@ size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap); size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap); size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap); size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_l2addr(void (*out)(char, void *), void *arg, va_list *ap);
// Various output functions // Various output functions
void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf *
void mg_pfn_stdout(char c, void *param); // param: ignored void mg_pfn_iobuf_noresize(char ch, void *param); // param: struct mg_iobuf *
void mg_pfn_stdout(char c, void *param); // param: ignored
// A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi")) // A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi"))
#define MG_ESC(str) mg_print_esc, 0, (str) #define MG_ESC(str) mg_print_esc, 0, (str)
@@ -1267,12 +1278,18 @@ void mg_log_set_fn(mg_pfn_t fn, void *param);
#define mg_log_set(level_) mg_log_level = (level_) #define mg_log_set(level_) mg_log_level = (level_)
#if MG_ENABLE_LOG #if MG_ENABLE_LOG
#define MG_LOG(level, args) \ #if !defined(_MSC_VER) && \
do { \ (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
if ((level) <= mg_log_level) { \ #define MG___FUNC__ ""
mg_log_prefix((level), __FILE__, __LINE__, __func__); \ #else
mg_log args; \ #define MG___FUNC__ __func__ // introduced in C99
} \ #endif
#define MG_LOG(level, args) \
do { \
if ((level) <= mg_log_level) { \
mg_log_prefix((level), __FILE__, __LINE__, MG___FUNC__); \
mg_log args; \
} \
} while (0) } while (0)
#else #else
#define MG_LOG(level, args) \ #define MG_LOG(level, args) \
@@ -1641,6 +1658,8 @@ enum {
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis
MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data
MG_EV_MDNS_REQ, // mDNS request struct mg_mdns_req *
MG_EV_MDNS_RESP, // mDNS response struct mg_mdns_resp *
MG_EV_USER // Starting ID for user events MG_EV_USER // Starting ID for user events
}; };
@@ -1659,11 +1678,11 @@ struct mg_dns {
}; };
struct mg_addr { struct mg_addr {
union { // Holds IPv4 or IPv6 address, in network byte order union { // Holds IPv4 or IPv6 address, in network byte order
uint8_t ip[16]; uint8_t ip[16];
uint32_t ip4; uint32_t ip4;
uint64_t ip6[2]; uint64_t ip6[2];
}; } addr;
uint16_t port; // TCP or UDP port in network byte order uint16_t port; // TCP or UDP port in network byte order
uint8_t scope_id; // IPv6 scope ID uint8_t scope_id; // IPv6 scope ID
bool is_ip6; // True when address is IPv6 address bool is_ip6; // True when address is IPv6 address
@@ -1757,6 +1776,7 @@ struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url, struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data, mg_event_handler_t fn, void *fn_data,
mg_event_handler_t pfn, void *pfn_data); mg_event_handler_t pfn, void *pfn_data);
void mg_multicast_restore(struct mg_connection *c, uint8_t *from);
@@ -1861,6 +1881,7 @@ void mg_tls_handshake(struct mg_connection *);
// Private // Private
void mg_tls_ctx_init(struct mg_mgr *); void mg_tls_ctx_init(struct mg_mgr *);
void mg_tls_ctx_free(struct mg_mgr *); void mg_tls_ctx_free(struct mg_mgr *);
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
// Low-level IO primives used by TLS layer // Low-level IO primives used by TLS layer
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 }; enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 };
@@ -1940,7 +1961,9 @@ int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input,
int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
size_t input_length, const unsigned char *key, size_t input_length, const unsigned char *key,
const size_t key_len, const unsigned char *iv, const size_t key_len, const unsigned char *iv,
const size_t iv_len); const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len);
#endif /* TLS_AES128_H */ #endif /* TLS_AES128_H */
@@ -2706,6 +2729,7 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt( PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE], uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t nonce[RFC_8439_NONCE_SIZE],
const uint8_t *restrict ad, size_t ad_size,
const uint8_t *restrict cipher_text, size_t cipher_text_size); const uint8_t *restrict cipher_text, size_t cipher_text_size);
#if defined(__cplusplus) #if defined(__cplusplus)
} }
@@ -2717,7 +2741,13 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz); int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz);
int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
const uint8_t *dP, size_t dP_len,
const uint8_t *dQ, size_t dQ_len,
const uint8_t *p, size_t p_len,
const uint8_t *q, size_t q_len,
const uint8_t *qInv, size_t qInv_len,
uint8_t *signature, size_t sig_len);
#endif // TLS_RSA_H #endif // TLS_RSA_H
@@ -2909,6 +2939,7 @@ struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts); void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts); uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts); void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts);
void mg_mqtt_unsub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *); int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *);
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags, void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
uint32_t len); uint32_t len);
@@ -2922,6 +2953,12 @@ size_t mg_mqtt_next_prop(struct mg_mqtt_message *, struct mg_mqtt_prop *,
#define MG_DNS_RTYPE_A 1
#define MG_DNS_RTYPE_PTR 12
#define MG_DNS_RTYPE_TXT 16
#define MG_DNS_RTYPE_AAAA 28
#define MG_DNS_RTYPE_SRV 33
// Mongoose sends DNS queries that contain only one question: // Mongoose sends DNS queries that contain only one question:
// either A (IPv4) or AAAA (IPv6) address lookup. // either A (IPv4) or AAAA (IPv6) address lookup.
// Therefore, we expect zero or one answer. // Therefore, we expect zero or one answer.
@@ -2950,13 +2987,42 @@ struct mg_dns_rr {
uint16_t alen; // Address length uint16_t alen; // Address length
}; };
// DNS-SD response record
struct mg_dnssd_record {
struct mg_str srvcproto; // service.proto, service name
struct mg_str txt; // TXT record contents
uint16_t port; // SRV record port
};
// mDNS request and response data structs passed to event handlers
struct mg_mdns_req {
struct mg_dns_rr *rr;
struct mg_dnssd_record *r;
struct mg_str reqname; // requested name in RR
struct mg_str respname; // actual name to use in response
struct mg_addr *addr; // actual address to use in response
bool is_listing;
bool is_resp;
bool is_unicast;
};
struct mg_mdns_resp {
struct mg_dns_rr *rr;
// TODO(scaprile )struct mg_str srvcproto; struct mg_str txt; uint16_t port; ?
struct mg_str name;
struct mg_addr addr;
// TODO(scaprile); bool has_A; bool has_PTR; bool has_SRV; bool has_TXT; ?
};
void mg_resolve(struct mg_connection *, const char *url); void mg_resolve(struct mg_connection *, const char *url);
void mg_resolve_cancel(struct mg_connection *); void mg_resolve_cancel(struct mg_connection *);
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *); bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
bool is_question, struct mg_dns_rr *); bool is_question, struct mg_dns_rr *);
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name); struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
void *fn_data);
bool mg_mdns_query(struct mg_connection *, const char *, unsigned int);
@@ -3033,8 +3099,8 @@ void mg_rpc_list(struct mg_rpc_req *r);
#define MG_OTA_RT1060 302 // IMXRT1060 #define MG_OTA_RT1060 302 // IMXRT1060
#define MG_OTA_RT1064 303 // IMXRT1064 #define MG_OTA_RT1064 303 // IMXRT1064
#define MG_OTA_RT1170 304 // IMXRT1170 #define MG_OTA_RT1170 304 // IMXRT1170
#define MG_OTA_MCXN 310 // MCXN947 #define MG_OTA_MCXN 310 // MCXN947
#define MG_OTA_FRDM 320 // FRDM-RW612 #define MG_OTA_RW612 320 // FRDM-RW612
#define MG_OTA_FLASH 900 // OTA via an internal flash #define MG_OTA_FLASH 900 // OTA via an internal flash
#define MG_OTA_ESP32 910 // ESP32 OTA implementation #define MG_OTA_ESP32 910 // ESP32 OTA implementation
#define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash #define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash
@@ -3101,11 +3167,16 @@ struct mg_wifi_scan_bss_data {
#define MG_WIFI_SECURITY_WPA MG_BIT(1) #define MG_WIFI_SECURITY_WPA MG_BIT(1)
#define MG_WIFI_SECURITY_WPA2 MG_BIT(2) #define MG_WIFI_SECURITY_WPA2 MG_BIT(2)
#define MG_WIFI_SECURITY_WPA3 MG_BIT(3) #define MG_WIFI_SECURITY_WPA3 MG_BIT(3)
#define MG_WIFI_SECURITY_WPA_ENTERPRISE MG_BIT(4)
#define MG_WIFI_SECURITY_WPA2_ENTERPRISE MG_BIT(5)
#define MG_WIFI_SECURITY_WPA3_ENTERPRISE MG_BIT(6)
uint8_t channel; uint8_t channel;
unsigned band : 2; unsigned band : 2;
#define MG_WIFI_BAND_2G 0 #define MG_WIFI_BAND_2G 0
#define MG_WIFI_BAND_5G 1 #define MG_WIFI_BAND_5G 1
unsigned has_n : 1; unsigned has_n : 1;
unsigned has_ac : 1;
unsigned has_ax : 1;
}; };
bool mg_wifi_scan(void); bool mg_wifi_scan(void);
@@ -3118,6 +3189,62 @@ bool mg_wifi_ap_stop(void);
#if MG_ENABLE_TCPIP
// no config defaults to 0 => Ethernet
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP }; // MG_TCPIP_L2_PPPoE
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct mg_l2addr {
union {
uint8_t mac[6];
} addr;
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
#if 0
TODO(): ?
struct eth_opts {
bool enable_crc32_check; // Do a CRC check on RX frames and strip it
bool enable_mac_check; // Do a MAC check on RX frames
};
struct mg_l2opts {
union {
struct eth_opts eth;
};
};
#endif
enum mg_l2proto {
MG_TCPIP_L2PROTO_IPV4 = 0,
MG_TCPIP_L2PROTO_IPV6,
MG_TCPIP_L2PROTO_ARP,
MG_TCPIP_L2PROTO_PPPoE_DISC,
MG_TCPIP_L2PROTO_PPPoE_SESS
};
enum mg_l2addrtype {
MG_TCPIP_L2ADDR_BCAST,
MG_TCPIP_L2ADDR_MCAST,
MG_TCPIP_L2ADDR_MCAST6
};
#endif
#if MG_ENABLE_TCPIP #if MG_ENABLE_TCPIP
@@ -3135,29 +3262,32 @@ typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
void *ev_data); void *ev_data);
enum { enum {
MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state) MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state)
MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr
MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr
MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str * MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str *
MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL
MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi scan results struct mg_wifi_scan_bss_data * MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi scan results struct
MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL // mg_wifi_scan_bss_data *
MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and chip specific MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL
MG_TCPIP_EV_DRIVER, // Driver event driver specific MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and
MG_TCPIP_EV_ST6_CHG, // state6 change uint8_t * (&ifp->state6) // chip specific
MG_TCPIP_EV_USER // Starting ID for user events MG_TCPIP_EV_DRIVER, // Driver event driver specific
MG_TCPIP_EV_ST6_CHG, // state6 change uint8_t *
// (&ifp->state6)
MG_TCPIP_EV_USER // Starting ID for user events
}; };
// Network interface // Network interface
struct mg_tcpip_if { struct mg_tcpip_if {
uint8_t mac[6]; // MAC address. Must be set to a valid MAC uint8_t mac[sizeof(struct mg_l2addr)]; // hw address. Set to a valid addr
uint32_t ip, mask, gw; // IP address, mask, default gateway uint32_t ip, mask, gw; // IP address, mask, default gateway
struct mg_str tx; // Output (TX) buffer struct mg_str tx; // Output (TX) buffer
bool enable_dhcp_client; // Enable DCHP client bool enable_dhcp_client; // Enable DCHP client
bool enable_dhcp_server; // Enable DCHP server bool enable_dhcp_server; // Enable DCHP server
bool enable_get_gateway; // DCHP server sets client as gateway bool enable_get_gateway; // DCHP server sets client as gateway
bool enable_req_dns; // DCHP client requests DNS server bool enable_req_dns; // DCHP client requests DNS server
bool enable_req_sntp; // DCHP client requests SNTP server bool enable_req_sntp; // DCHP client requests SNTP server
bool enable_crc32_check; // Do a CRC check on RX frames and strip it bool enable_crc32_check; // Do a CRC check on RX frames and strip it
bool enable_mac_check; // Do a MAC check on RX frames bool enable_mac_check; // Do a MAC check on RX frames
bool update_mac_hash_table; // Signal drivers to update MAC controller bool update_mac_hash_table; // Signal drivers to update MAC controller
@@ -3168,20 +3298,22 @@ struct mg_tcpip_if {
struct mg_mgr *mgr; // Mongoose event manager struct mg_mgr *mgr; // Mongoose event manager
struct mg_queue recv_queue; // Receive queue struct mg_queue recv_queue; // Receive queue
char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Name for DHCP, "mip" if unset char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Name for DHCP, "mip" if unset
uint16_t mtu; // Interface MTU uint16_t mtu; // Interface link payload
#define MG_TCPIP_MTU_DEFAULT 1500 uint16_t framesize; // Interface frame max length
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses,
uint8_t prefix_len; // Prefix length uint8_t prefix[8]; // prefix,
uint64_t gw6[2]; // Default gateway uint8_t prefix_len; // prefix length,
bool enable_slaac; // Enable IPv6 address autoconfiguration uint64_t gw6[2]; // default gateway.
bool enable_dhcp6_client; // Enable DCHPv6 client bool enable_slaac; // Enable IPv6 address autoconfiguration
bool enable_dhcp6_client; // Enable DCHPv6 client TODO()
#endif #endif
// Internal state, user can use it but should not change it // Internal state, user can use it but should not change it
uint8_t gwmac[6]; // Router's MAC uint8_t gwmac[sizeof(struct mg_l2addr)]; // Router's hw address
char *dns4_url; // DNS server URL enum mg_l2type l2type; // Ethernet, PPP, etc.
uint64_t now; // Current time char *dns4_url; // DNS server URL
uint64_t now; // Current time
uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
uint64_t lease_expire; // Lease expiration time, in ms uint64_t lease_expire; // Lease expiration time, in ms
uint16_t eport; // Next ephemeral port uint16_t eport; // Next ephemeral port
@@ -3195,11 +3327,14 @@ struct mg_tcpip_if {
#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state #define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state
#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned #define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned
#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work #define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work
bool gw_ready; // We've got a hw address for the router
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
uint8_t gw6mac[6]; // IPv6 Router's MAC uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPV6 Router's hw address
uint8_t state6; // Current IPv6 state uint8_t state6; // Current IPv6 state
bool gw6_ready; // We've got a hw address for the IPv6 router
#endif #endif
}; };
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *); void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
void mg_tcpip_free(struct mg_tcpip_if *); void mg_tcpip_free(struct mg_tcpip_if *);
void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp); void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp);
@@ -3222,6 +3357,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
extern struct mg_tcpip_driver mg_tcpip_driver_rw612; extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
extern struct mg_tcpip_driver mg_tcpip_driver_cyw; extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi; extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
// Drivers that require SPI, can use this SPI abstraction // Drivers that require SPI, can use this SPI abstraction
struct mg_tcpip_spi { struct mg_tcpip_spi {
@@ -3231,39 +3367,38 @@ struct mg_tcpip_spi {
uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply
}; };
// Alignment and memory section requirements // Alignment and memory section requirements
#ifndef MG_8BYTE_ALIGNED #ifndef MG_8BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_8BYTE_ALIGNED __attribute__((aligned((8U)))) #define MG_8BYTE_ALIGNED __attribute__((aligned((8U))))
#else #else
#define MG_8BYTE_ALIGNED #define MG_8BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 8BYTE_ALIGNED #endif // 8BYTE_ALIGNED
#ifndef MG_16BYTE_ALIGNED #ifndef MG_16BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_16BYTE_ALIGNED __attribute__((aligned((16U)))) #define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
#else #else
#define MG_16BYTE_ALIGNED #define MG_16BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 16BYTE_ALIGNED #endif // 16BYTE_ALIGNED
#ifndef MG_32BYTE_ALIGNED #ifndef MG_32BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_32BYTE_ALIGNED __attribute__((aligned((32U)))) #define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
#else #else
#define MG_32BYTE_ALIGNED #define MG_32BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 32BYTE_ALIGNED #endif // 32BYTE_ALIGNED
#ifndef MG_64BYTE_ALIGNED #ifndef MG_64BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_64BYTE_ALIGNED __attribute__((aligned((64U)))) #define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
#else #else
#define MG_64BYTE_ALIGNED #define MG_64BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 64BYTE_ALIGNED #endif // 64BYTE_ALIGNED
#ifndef MG_ETH_RAM #ifndef MG_ETH_RAM
#define MG_ETH_RAM #define MG_ETH_RAM
@@ -3630,6 +3765,43 @@ bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f,
#endif #endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
MG_ENABLE_DRIVER_ST67W6
struct mg_tcpip_spi_ {
void *spi; // Opaque SPI bus descriptor
void (*begin)(void *); // SPI begin: slave select low
void (*end)(void *); // SPI end: slave select high
void (*txn)(void *, uint8_t *, uint8_t *,
size_t len); // SPI transaction: write-read len bytes
};
struct mg_tcpip_driver_st67w6_data {
struct mg_wifi_data wifi;
void *spi;
bool (*is_ready)(void); // return state of module RDY pin
struct mg_queue send_queue; // decouple tx calls from module polls
};
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_st67w6_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_WIFI_CONFIG(&driver_data_); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_st67w6; \
mif_.driver_data = &driver_data_; \
mif_.recv_queue.size = 8192; \
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: st67w6, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \ #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \
MG_ENABLE_DRIVER_STM32F MG_ENABLE_DRIVER_STM32F
@@ -3838,41 +4010,6 @@ struct mg_tcpip_driver_tms570_data {
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
struct mg_tcpip_driver_xmc_data { struct mg_tcpip_driver_xmc_data {
@@ -3919,6 +4056,41 @@ struct mg_tcpip_driver_xmc_data {
#endif #endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+6 -2
View File
@@ -35,11 +35,14 @@
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack #define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
#endif #endif
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && !defined(MG_ENABLE_DRIVER_STM32H) #if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N)
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7) #if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
#define MG_ENABLE_DRIVER_STM32F 1 #define MG_ENABLE_DRIVER_STM32F 1
#elif defined(STM32H5) || defined(STM32H7) #elif defined(STM32H5) || defined(STM32H7)
#define MG_ENABLE_DRIVER_STM32H 1 #define MG_ENABLE_DRIVER_STM32H 1
#elif defined(STM32N6)
#define MG_ENABLE_DRIVER_STM32N 1
#else #else
#error Select a driver in mongoose_config.h #error Select a driver in mongoose_config.h
#endif #endif
@@ -49,7 +52,8 @@
#define MG_TLS MG_TLS_BUILTIN #define MG_TLS MG_TLS_BUILTIN
#endif #endif
#if !defined(MG_OTA) && defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7) #if !defined(MG_OTA) && defined(STM32F1) || defined(STM32F2) || \
defined(STM32F4) || defined(STM32F7)
#define MG_OTA MG_OTA_STM32F #define MG_OTA MG_OTA_STM32F
#elif !defined(MG_OTA) && defined(STM32H5) #elif !defined(MG_OTA) && defined(STM32H5)
#define MG_OTA MG_OTA_STM32H5 #define MG_OTA MG_OTA_STM32H5
+4 -1
View File
@@ -110,7 +110,10 @@ typedef enum { false = 0, true = 1 } bool;
#endif #endif
#endif #endif
typedef unsigned long nfds_t; #if defined(MG_ENABLE_POLL) && MG_ENABLE_POLL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP)
typedef unsigned long nfds_t; // see #3388
#endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
#if MG_ENABLE_WINSOCK #if MG_ENABLE_WINSOCK
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
+5 -3
View File
@@ -3,13 +3,15 @@
#if MG_ARCH == MG_ARCH_ZEPHYR #if MG_ARCH == MG_ARCH_ZEPHYR
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/net/socket.h>
// #include <zephyr/posix/dirent.h>
#include <zephyr/posix/fcntl.h>
#include <zephyr/posix/sys/select.h>
#include <zephyr/random/random.h>
#include <zephyr/version.h> #include <zephyr/version.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <zephyr/net/socket.h>
#include <zephyr/posix/fcntl.h>
#include <zephyr/posix/sys/select.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
+332 -57
View File
@@ -63,7 +63,7 @@ static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk
// MG_INFO(("--> [%s]", to)); // MG_INFO(("--> [%s]", to));
} }
if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term if (tolen > 0) to[tolen - 1] = '\0'; // Make sure it is nul-term
return i; return i;
} }
@@ -134,14 +134,15 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name)); mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
ofs += n; ofs += n;
if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) { if (rr.alen == 4 && rr.atype == MG_DNS_RTYPE_A && rr.aclass == 1) {
dm->addr.is_ip6 = false; dm->addr.is_ip6 = false;
memcpy(&dm->addr.ip, &buf[ofs - 4], 4); memcpy(&dm->addr.addr.ip, &buf[ofs - 4], 4);
dm->resolved = true; dm->resolved = true;
break; // Return success break; // Return success
} else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) { } else if (rr.alen == 16 && rr.atype == MG_DNS_RTYPE_AAAA &&
rr.aclass == 1) {
dm->addr.is_ip6 = true; dm->addr.is_ip6 = true;
memcpy(&dm->addr.ip, &buf[ofs - 16], 16); memcpy(&dm->addr.addr.ip, &buf[ofs - 16], 16);
dm->resolved = true; dm->resolved = true;
break; // Return success break; // Return success
} }
@@ -255,8 +256,12 @@ static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
mg_error(c, "resolve OOM"); mg_error(c, "resolve OOM");
} else { } else {
struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests; struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1; uint16_t id;
d->next = (struct dns_data *) c->mgr->active_dns_requests; mg_random(&id, sizeof(uint16_t));
// TODO(): traverse reqs and check id != reqs->txnid; repeat otherwise
if (reqs != NULL) id = (uint16_t) (reqs->txnid + 1); // no collision
d->txnid = id;
d->next = reqs;
c->mgr->active_dns_requests = d; c->mgr->active_dns_requests = d;
d->expire = mg_millis() + (uint64_t) ms; d->expire = mg_millis() + (uint64_t) ms;
d->c = c; d->c = c;
@@ -282,6 +287,7 @@ void mg_resolve(struct mg_connection *c, const char *url) {
} }
} }
// Response header length is 10 bytes
static const uint8_t mdns_answer[] = { static const uint8_t mdns_answer[] = {
0, 1, // 2 bytes - record type, A 0, 1, // 2 bytes - record type, A
0, 1, // 2 bytes - address class, INET 0, 1, // 2 bytes - address class, INET
@@ -289,65 +295,334 @@ static const uint8_t mdns_answer[] = {
0, 4 // 2 bytes - address length 0, 4 // 2 bytes - address length
}; };
// A name length is name->len + '.local' + 2 = name->len + 8
static uint8_t *build_name(struct mg_str *name, uint8_t *p) {
*p++ = (uint8_t) name->len; // label 1
memcpy(p, name->buf, name->len), p += name->len;
*p++ = 5; // label 2
memcpy(p, "local", 5), p += 5;
*p++ = 0; // no more labels
return p;
}
void mg_getlocaddr(struct mg_connection *, struct mg_addr *, struct mg_addr *);
// An A record length is 10 + 4 = 14 bytes
static uint8_t *build_a_record(struct mg_connection *c, uint8_t *p,
struct mg_addr *addr) {
memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer);
if (addr != NULL && !addr->is_ip6) {
memcpy(p, &addr->addr.ip4, 4), p += 4;
} else {
#if MG_ENABLE_TCPIP
memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
#else
struct mg_addr loc, to;
memset(&loc, 0, sizeof(loc));
to.is_ip6 = false;
to.port = mg_htons(5353);
to.addr.ip4 = MG_IPV4(224, 0, 0, 51);
mg_getlocaddr(c, &to, &loc);
memcpy(p, &loc.addr.ip4, 4), p += 4;
#endif
}
return p;
}
// A srv name length is r->srvcproto.len + '.local' + 2 = r->srvcproto.len + 8
static uint8_t *build_srv_name(uint8_t *p, struct mg_dnssd_record *r) {
*p++ = (uint8_t) r->srvcproto.len - 5; // label 1, up to '._tcp'
memcpy(p, r->srvcproto.buf, r->srvcproto.len), p += r->srvcproto.len;
p[-5] = 4; // label 2, '_tcp', overwrite '.'
*p++ = 5; // label 3
memcpy(p, "local", 5), p += 5;
*p++ = 0; // no more labels
return p;
}
#if 0
// TODO(): for listing
static uint8_t *build_mysrv_name(struct mg_str *name, uint8_t *p,
struct mg_dnssd_record *r) {
*p++ = name->len; // label 1
memcpy(p, name->buf, name->len), p += name->len;
return build_srv_name(p, r);
}
#endif
// A PTR record length is 10 + name->len + 3 = name->len + 13
static uint8_t *build_ptr_record(struct mg_str *name, uint8_t *p, uint16_t o) {
uint16_t offset = mg_htons(o);
memcpy(p, mdns_answer, sizeof(mdns_answer));
p[1] = MG_DNS_RTYPE_PTR; // overwrite record type
p += sizeof(mdns_answer);
p[-1] = (uint8_t) name->len +
3; // overwrite response length, label length + label + offset
*p++ = (uint8_t) name->len; // response: label 1
memcpy(p, name->buf, name->len), p += name->len; // copy label
memcpy(p, &offset, 2);
*p |= 0xC0, p += 2;
return p;
}
// An SRV record length is 10 + name->len + 9 = name->len + 19
static uint8_t *build_srv_record(struct mg_str *name, uint8_t *p,
struct mg_dnssd_record *r, uint16_t o) {
uint16_t port = mg_htons(r->port);
uint16_t offset = mg_htons(o);
memcpy(p, mdns_answer, sizeof(mdns_answer));
p[1] = MG_DNS_RTYPE_SRV; // overwrite record type
p += sizeof(mdns_answer);
p[-1] = (uint8_t) name->len + 9; // overwrite response length (4+2+1+2)
*p++ = 0; // priority
*p++ = 0;
*p++ = 0; // weight
*p++ = 0;
memcpy(p, &port, 2), p += 2; // port
*p++ = (uint8_t) name->len; // label 1
memcpy(p, name->buf, name->len), p += name->len;
memcpy(p, &offset, 2);
*p |= 0xC0, p += 2;
return p;
}
// A TXT record length is r->txt.len (txt contents) + 10
static uint8_t *build_txt_record(uint8_t *p, struct mg_dnssd_record *r) {
uint16_t len = mg_htons((uint16_t) r->txt.len);
memcpy(p, mdns_answer, sizeof(mdns_answer));
p[1] = MG_DNS_RTYPE_TXT; // overwrite record type
p += sizeof(mdns_answer);
memcpy(p - 2, &len, 2); // overwrite response length
memcpy(p, r->txt.buf, r->txt.len), p += r->txt.len; // copy record verbatim
return p;
}
// Each additional record has a 2-byte field pointing to the name label
// RFC-6762 16: case-insensitivity --> RFC-1034, 1035
static void handle_mdns_query(struct mg_connection *c) {
struct mg_dns_header *qh = (struct mg_dns_header *) c->recv.buf;
struct mg_dns_rr rr;
size_t n;
// Parse first question, offset 12 is header size
n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
MG_VERBOSE(("mDNS request parsed, result=%d", (int) n));
if (n > 0) {
// RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
uint8_t buf[sizeof(struct mg_dns_header) + 256 + sizeof(mdns_answer) + 4];
struct mg_dns_header *h = (struct mg_dns_header *) buf;
uint8_t *p = &buf[sizeof(*h)];
char name[256];
uint8_t name_len;
// uint16_t q = mg_ntohs(qh->num_questions);
struct mg_str defname = mg_str((const char *) c->fn_data);
struct mg_str *respname;
struct mg_mdns_req req;
memset(&req, 0, sizeof(req));
req.is_unicast = (rr.aclass & MG_BIT(15)) != 0; // QU
rr.aclass &= (uint16_t) ~MG_BIT(15); // remove "QU" (unicast response)
qh->num_questions = mg_htons(1); // parser sanity
mg_dns_parse_name(c->recv.buf, c->recv.len, 12, name, sizeof(name));
name_len = (uint8_t) strlen(name); // verify it ends in .local
if (name_len <= 6 || strcmp(".local", &name[name_len - 6]) != 0 ||
(rr.aclass != 1 && rr.aclass != 0xff))
return;
name[name_len -= 6] = '\0'; // remove .local
MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype,
(unsigned int) rr.aclass, name));
if (rr.atype == MG_DNS_RTYPE_A) {
// TODO(): ensure c->fn_data ends in \0
// if we have a name to match, go; otherwise users will match and fill
// req.r.name and set req.is_resp
if (c->fn_data != NULL && mg_casecmp((char *) c->fn_data, name) != 0)
return;
req.is_resp = (c->fn_data != NULL);
req.reqname = mg_str_n(name, name_len);
} else // users have to match the request to something in their db, then
// fill req.r and set req.is_resp
if (rr.atype == MG_DNS_RTYPE_PTR) {
if (strcmp("_services._dns-sd._udp", name) == 0) req.is_listing = true;
MG_DEBUG(
("PTR request for %s", req.is_listing ? "services listing" : name));
req.reqname = mg_str_n(name, name_len);
} else if (rr.atype == MG_DNS_RTYPE_SRV || rr.atype == MG_DNS_RTYPE_TXT) {
MG_DEBUG(("%s request for %s",
rr.atype == MG_DNS_RTYPE_SRV ? "SRV" : "TXT", name));
// if possible, check it starts with our name, users will check it ends
// in a service name they handle
if (c->fn_data != NULL) {
if (mg_strcasecmp(defname, mg_str_n(name, defname.len)) != 0 ||
name[defname.len] != '.')
return;
req.reqname =
mg_str_n(name + defname.len + 1, name_len - defname.len - 1);
MG_DEBUG(
("That's us, handing %.*s", req.reqname.len, req.reqname.buf));
} else {
req.reqname = mg_str_n(name, name_len);
}
} else { // unhandled record
return;
}
req.rr = &rr;
mg_call(c, MG_EV_MDNS_REQ, &req);
if (!req.is_resp) return;
respname = req.respname.buf != NULL ? &req.respname : &defname;
memset(h, 0, sizeof(*h)); // clear header
h->txnid = req.is_unicast ? qh->txnid : 0; // RFC-6762 18.1
h->num_answers = mg_htons(1); // RFC-6762 6: 0 questions, 1 Answer
h->flags = mg_htons(0x8400); // Authoritative response
if (req.is_listing) {
// TODO(): RFC-6762 6: each responder SHOULD delay its response by a
// random amount of time selected with uniform random distribution in the
// range 20-120 ms.
// TODO():
return;
} else if (rr.atype == MG_DNS_RTYPE_PTR) { // serve PTR + SRV + TXT + A
// TODO(): RFC-6762 6: each responder SHOULD delay its response by a
// random amount of time selected with uniform random distribution in the
// range 20-120 ms. Response to PTR is local_name._myservice._tcp.local
uint8_t *o = p, *aux;
uint16_t offset;
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 13 + 2 +
respname->len + 19 + 2 + req.r->txt.len + 10 + 2 + 14) >
sizeof(buf)) // srv name + PTR + 2 + SRV + 2 + TXT + 2 + A
return;
h->num_other_prs = mg_htons(3); // 3 additional records
p = build_srv_name(p, req.r);
aux = build_ptr_record(respname, p, (uint16_t) (o - buf));
o = p + sizeof(mdns_answer); // point to PTR response (full srvc name)
offset = mg_htons((uint16_t) (o - buf));
o = p - 7; // point to '.local' label (\x05local\x00)
p = aux;
memcpy(p, &offset, 2); // point to full srvc name, in record
*p |= 0xC0, p += 2;
aux = p;
p = build_srv_record(respname, p, req.r, (uint16_t) (o - buf));
o = aux + sizeof(mdns_answer) + 6; // point to target in SRV
memcpy(p, &offset, 2); // point to full srvc name, in record
*p |= 0xC0, p += 2;
p = build_txt_record(p, req.r);
offset = mg_htons((uint16_t) (o - buf));
memcpy(p, &offset, 2); // point to target name, in record
*p |= 0xC0, p += 2;
p = build_a_record(c, p, req.addr);
} else if (rr.atype == MG_DNS_RTYPE_TXT) {
if ((sizeof(*h) + req.r->srvcproto.len + 8 + req.r->txt.len + 10) >
sizeof(buf)) // srv name + TXT
return;
p = build_srv_name(p, req.r);
p = build_txt_record(p, req.r);
} else if (rr.atype == MG_DNS_RTYPE_SRV) { // serve SRV + A
uint8_t *o, *aux;
uint16_t offset;
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 19 + 2 +
14) > sizeof(buf)) // srv name + SRV + 2 + A
return;
h->num_other_prs = mg_htons(1); // 1 additional record
p = build_srv_name(p, req.r);
o = p - 7; // point to '.local' label (\x05local\x00)
aux = p;
p = build_srv_record(respname, p, req.r, (uint16_t) (o - buf));
o = aux + sizeof(mdns_answer) + 6; // point to target in SRV
offset = mg_htons((uint16_t) (o - buf));
memcpy(p, &offset, 2); // point to target name, in record
*p |= 0xC0, p += 2;
p = build_a_record(c, p, req.addr);
} else { // A requested
// RFC-6762 6: 0 Auth, 0 Additional RRs
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + respname->len + 8 + 14) > sizeof(buf)) // name + A
return;
p = build_name(respname, p);
p = build_a_record(c, p, req.addr);
}
if (!req.is_unicast) mg_multicast_restore(c, (uint8_t *) &c->loc);
mg_send(c, buf, (size_t) (p - buf)); // And send it!
MG_DEBUG(("%M > %M", mg_print_ip_port, &c->loc, mg_print_ip_port, &c->rem));
MG_DEBUG(("mDNS %s response sent", req.is_unicast ? "unicast" : "mcast"));
}
}
static void handle_mdns_response(struct mg_connection *c) {
struct mg_dns_header *rh = (struct mg_dns_header *) c->recv.buf;
struct mg_dns_rr rr;
size_t n;
// Parse first response, offset 12 is header size
n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, false, &rr);
MG_VERBOSE(("mDNS response parsed, result=%d", (int) n));
if (n > 0) {
// RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
char name[256];
uint8_t name_len;
struct mg_mdns_resp resp;
memset(&resp, 0, sizeof(resp));
if (rh->num_answers > mg_htons(1)) MG_DEBUG(("ignoring > 1 answers"));
mg_dns_parse_name(c->recv.buf, c->recv.len, 12, name, sizeof(name));
name_len = (uint8_t) strlen(name); // verify it ends in .local
MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype,
(unsigned int) rr.aclass, name));
if (rr.alen == 4 && rr.atype == MG_DNS_RTYPE_A &&
(rr.aclass & 0x7FFF) == 1) {
resp.addr.is_ip6 = false;
memcpy(resp.addr.addr.ip, (char *) (rh + 1) + n - 4, 4);
MG_DEBUG(("A response from %.*s = %M", name_len, name, mg_print_ip,
&resp.addr));
// } else if (rr.alen == 16 && rr.atype == MG_DNS_RTYPE_AAAA &&
// (rr.aclass & 0x7FFF) == 1) {
// resp.addr.is_ip6 = true;
// memcpy(resp.addr.addr.ip, (char *)(rh + 1) + n - 16], 16);
// MG_DEBUG(("AAAA response from %.*s = %M", name_len, name,
// mg_print_ip, &resp.addr));
} else {
return;
}
resp.name = mg_str_n(name, name_len);
resp.rr = &rr;
mg_call(c, MG_EV_MDNS_RESP, &resp);
}
}
static void handle_mdns_record(struct mg_connection *c) {
struct mg_dns_header *h = (struct mg_dns_header *) c->recv.buf;
if (c->recv.len <= 12) return;
if ((h->flags & mg_htons(0xF800)) == 0) {
// flags -> !resp, opcode=0 => query; ignore other opcodes
handle_mdns_query(c);
} else if ((h->flags & mg_htons(0xF800)) == mg_htons(0x8000)) {
// flags -> resp, opcode=0 => response; ignore other opcodes
handle_mdns_response(c);
}
}
static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) { static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_READ) { if (ev == MG_EV_READ) {
struct mg_dns_header *qh = (struct mg_dns_header *) c->recv.buf; handle_mdns_record(c);
if (c->recv.len > 12 && (qh->flags & mg_htons(0xF800)) == 0) {
// flags -> !resp, opcode=0 => query; ignore other opcodes and responses
struct mg_dns_rr rr; // Parse first question, offset 12 is header size
size_t n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
MG_VERBOSE(("mDNS request parsed, result=%d", (int) n));
if (n > 0) {
// RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
// buf and h declared here to ease future expansion to DNS-SD
uint8_t buf[sizeof(struct mg_dns_header) + 256 + sizeof(mdns_answer) + 4];
struct mg_dns_header *h = (struct mg_dns_header *) buf;
char local_name[63 + 7]; // name label + '.' + local label + '\0'
uint8_t name_len = (uint8_t) strlen((char *)c->fn_data);
struct mg_dns_message dm;
bool unicast = (rr.aclass & MG_BIT(15)) != 0; // QU
// uint16_t q = mg_ntohs(qh->num_questions);
rr.aclass &= (uint16_t) ~MG_BIT(15); // remove "QU" (unicast response)
qh->num_questions = mg_htons(1); // parser sanity
mg_dns_parse(c->recv.buf, c->recv.len, &dm);
if (name_len > (sizeof(local_name) - 7)) // leave room for .local\0
name_len = sizeof(local_name) - 7;
memcpy(local_name, c->fn_data, name_len);
strcpy(local_name + name_len, ".local"); // ensure proper name.local\0
if (strcmp(local_name, dm.name) == 0) {
uint8_t *p = &buf[sizeof(*h)];
memset(h, 0, sizeof(*h)); // clear header
h->txnid = unicast ? qh->txnid : 0; // RFC-6762 18.1
// RFC-6762 6: 0 questions, 1 Answer, 0 Auth, 0 Additional RRs
h->num_answers = mg_htons(1); // only one answer
h->flags = mg_htons(0x8400); // Authoritative response
*p++ = name_len; // label 1
memcpy(p, c->fn_data, name_len), p += name_len;
*p++ = 5; // label 2
memcpy(p, "local", 5), p += 5;
*p++ = 0; // no more labels
memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer);
#if MG_ENABLE_TCPIP
memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
#else
memcpy(p, c->data, 4), p += 4;
#endif
if (!unicast) memcpy(&c->rem, &c->loc, sizeof(c->rem));
mg_send(c, buf, (size_t)(p - buf)); // And send it!
MG_DEBUG(("mDNS %c response sent", unicast ? 'U' : 'M'));
}
}
}
mg_iobuf_del(&c->recv, 0, c->recv.len); mg_iobuf_del(&c->recv, 0, c->recv.len);
} }
(void) ev_data; (void) ev_data;
} }
void mg_multicast_add(struct mg_connection *c, char *ip); void mg_multicast_add(struct mg_connection *c, char *ip);
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name) { struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
void *fn_data) {
struct mg_connection *c = struct mg_connection *c =
mg_listen(mgr, "udp://224.0.0.251:5353", mdns_cb, name); mg_listen(mgr, "udp://224.0.0.251:5353", fn, fn_data);
if (c != NULL) mg_multicast_add(c, (char *)"224.0.0.251"); if (c == NULL) return NULL;
c->pfn = mdns_cb, c->pfn_data = fn_data;
mg_multicast_add(c, (char *) "224.0.0.251");
return c; return c;
} }
bool mg_mdns_query(struct mg_connection *c, const char *name,
unsigned int rtype) {
struct mg_str name_;
name_.buf = (char *) name, name_.len = strlen(name);
mg_multicast_restore(c, (uint8_t *) &c->loc);
(void) rtype;
return mg_dns_send(c, &name_, 0, false);
}
+37 -2
View File
@@ -3,6 +3,12 @@
#include "net.h" #include "net.h"
#include "str.h" #include "str.h"
#define MG_DNS_RTYPE_A 1
#define MG_DNS_RTYPE_PTR 12
#define MG_DNS_RTYPE_TXT 16
#define MG_DNS_RTYPE_AAAA 28
#define MG_DNS_RTYPE_SRV 33
// Mongoose sends DNS queries that contain only one question: // Mongoose sends DNS queries that contain only one question:
// either A (IPv4) or AAAA (IPv6) address lookup. // either A (IPv4) or AAAA (IPv6) address lookup.
// Therefore, we expect zero or one answer. // Therefore, we expect zero or one answer.
@@ -31,10 +37,39 @@ struct mg_dns_rr {
uint16_t alen; // Address length uint16_t alen; // Address length
}; };
// DNS-SD response record
struct mg_dnssd_record {
struct mg_str srvcproto; // service.proto, service name
struct mg_str txt; // TXT record contents
uint16_t port; // SRV record port
};
// mDNS request and response data structs passed to event handlers
struct mg_mdns_req {
struct mg_dns_rr *rr;
struct mg_dnssd_record *r;
struct mg_str reqname; // requested name in RR
struct mg_str respname; // actual name to use in response
struct mg_addr *addr; // actual address to use in response
bool is_listing;
bool is_resp;
bool is_unicast;
};
struct mg_mdns_resp {
struct mg_dns_rr *rr;
// TODO(scaprile )struct mg_str srvcproto; struct mg_str txt; uint16_t port; ?
struct mg_str name;
struct mg_addr addr;
// TODO(scaprile); bool has_A; bool has_PTR; bool has_SRV; bool has_TXT; ?
};
void mg_resolve(struct mg_connection *, const char *url); void mg_resolve(struct mg_connection *, const char *url);
void mg_resolve_cancel(struct mg_connection *); void mg_resolve_cancel(struct mg_connection *);
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *); bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
bool is_question, struct mg_dns_rr *); bool is_question, struct mg_dns_rr *);
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name); struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
void *fn_data);
bool mg_mdns_query(struct mg_connection *, const char *, unsigned int);
+12 -12
View File
@@ -293,7 +293,7 @@ static void cyw_handle_bdc_evnt(struct bdc_hdr *bdc, size_t len) {
uint32_t status = mg_ntohl(msg->event.status); uint32_t status = mg_ntohl(msg->event.status);
uint32_t reason = mg_ntohl(msg->event.reason); uint32_t reason = mg_ntohl(msg->event.reason);
uint16_t flags = mg_ntohs(msg->event.flags); uint16_t flags = mg_ntohs(msg->event.flags);
MG_DEBUG(("BCM event %lu %lu %lu %p", event_type, status, reason, flags)); MG_VERBOSE(("BCM evt %lu %lu %lu %p", event_type, status, reason, flags));
if (event_type == 16 && status == 0) { // Link if (event_type == 16 && status == 0) { // Link
s_link = flags & 1; s_link = flags & 1;
} else if (event_type == 46 && s_link) { // PSK sup with link up } else if (event_type == 46 && s_link) { // PSK sup with link up
@@ -359,7 +359,7 @@ static bool cyw_wifi_connect(char *ssid, char *pass) {
uint32_t data[64/4 + 1]; // max pass length: 64 for WPA, 128 for WPA3 SAE uint32_t data[64/4 + 1]; // max pass length: 64 for WPA, 128 for WPA3 SAE
uint16_t *da = (uint16_t *) data; uint16_t *da = (uint16_t *) data;
unsigned int len; unsigned int len;
uint32_t val; uint32_t val;
val = 4; // security type: 0 for none, 2 for WPA, 4 for WPA2/WPA3, 6 for mixed WPA/WPA2 val = 4; // security type: 0 for none, 2 for WPA, 4 for WPA2/WPA3, 6 for mixed WPA/WPA2
// sup_wpa[1] = 0 if not using security // sup_wpa[1] = 0 if not using security
if (!(cyw_ioctl_set(134 /* SET_WSEC */, (uint8_t *)&val, sizeof(val)) if (!(cyw_ioctl_set(134 /* SET_WSEC */, (uint8_t *)&val, sizeof(val))
@@ -401,7 +401,7 @@ static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
uint32_t data[64/4 + 2]; // max pass length: 64 for WPA, 128 for WPA3 SAE uint32_t data[64/4 + 2]; // max pass length: 64 for WPA, 128 for WPA3 SAE
uint16_t *da = (uint16_t *) data; uint16_t *da = (uint16_t *) data;
unsigned int len; unsigned int len;
uint32_t val; uint32_t val;
// CHIP DEPENDENCY // CHIP DEPENDENCY
// RPi set the AMPDU parameter for AP (window size = 2) ***************** // RPi set the AMPDU parameter for AP (window size = 2) *****************
// val = 2 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val)); // val = 2 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
@@ -430,10 +430,10 @@ static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
da[1] = 1; // indicates wireless security key, skip for WPA3 SAE da[1] = 1; // indicates wireless security key, skip for WPA3 SAE
memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE
if (!cyw_ioctl_set_(1, 268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA if (!cyw_ioctl_set_(1, 268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA
/* for WPA3 SAE: /* for WPA3 SAE:
memcpy((uint8_t *)data + sizeof(uint16_t), pass, len); memcpy((uint8_t *)data + sizeof(uint16_t), pass, len);
cyw_ioctl_iovar_set_(1, "sae_password", data, sizeof(data)); */ cyw_ioctl_iovar_set_(1, "sae_password", data, sizeof(data)); */
/* for WPA3 or mixed WPA2/WPA3: /* for WPA3 or mixed WPA2/WPA3:
val = 5 ; cyw_ioctl_iovar_set_(1, "sae_max_pwe_loop", (uint8_t *)&val, sizeof(val)); // Some chipsets do not support this */ val = 5 ; cyw_ioctl_iovar_set_(1, "sae_max_pwe_loop", (uint8_t *)&val, sizeof(val)); // Some chipsets do not support this */
// resume if not using auth // resume if not using auth
@@ -579,7 +579,7 @@ static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, si
bss.band = band & CYW_BSS_BAND2G ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G; bss.band = band & CYW_BSS_BAND2G ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G;
bss.security = (sbss->capability & MG_BIT(4) /* CAP_PRIVACY */) ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN; bss.security = (sbss->capability & MG_BIT(4) /* CAP_PRIVACY */) ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN;
{ // travel IEs (Information Elements) in search of security definitions { // travel IEs (Information Elements) in search of security definitions
const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1 const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1
uint8_t *ie = (uint8_t *)sbss + sbss->ie_offset; uint8_t *ie = (uint8_t *)sbss + sbss->ie_offset;
int bytes = (int) sbss->ie_length; int bytes = (int) sbss->ie_length;
while (bytes > 0 && ie[1] + 2 < bytes) { // ie[0] -> type, ie[1] -> bytes from ie[2] while (bytes > 0 && ie[1] + 2 < bytes) { // ie[0] -> type, ie[1] -> bytes from ie[2]
@@ -590,11 +590,11 @@ static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, si
bytes -= ie[1] + 2; bytes -= ie[1] + 2;
} }
} }
MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security)); MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security));
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss); mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
} else { } else {
MG_ERROR(("scan error")); MG_ERROR(("scan error"));
} }
} }
// clang-format on // clang-format on
@@ -616,7 +616,7 @@ static void cyw_handle_cdc(struct cdc_hdr *cdc, size_t len) {
return; return;
} }
if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump((void *) cdc, len); if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump((void *) cdc, len);
MG_DEBUG(("IOCTL result: %02x %02x %02x %02x ...", r[0], r[1], r[2], r[3])); MG_VERBOSE(("IOCTL result: %02x %02x %02x %02x ..", r[0], r[1], r[2], r[3]));
s_ioctl_resp = r; s_ioctl_resp = r;
} }
// NOTE(): alt no loop handler dispatching IOCTL response to current handler: // NOTE(): alt no loop handler dispatching IOCTL response to current handler:
@@ -786,7 +786,7 @@ static bool cyw_init(uint8_t *mac) {
// CHIP DEPENDENCY // CHIP DEPENDENCY
val = 8 ; cyw_ioctl_iovar_set("ampdu_ba_wsize", (uint8_t *)&val, sizeof(val)); val = 8 ; cyw_ioctl_iovar_set("ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
val = 4 ; cyw_ioctl_iovar_set("ampdu_mpdu", (uint8_t *)&val, sizeof(val)); val = 4 ; cyw_ioctl_iovar_set("ampdu_mpdu", (uint8_t *)&val, sizeof(val));
val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val)); val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val));
// //
{ {
struct cyw_country c; struct cyw_country c;
@@ -1004,7 +1004,7 @@ static bool cyw_load_fwll(void *fwdata, size_t fwlen, void *nvramdata, size_t nv
cyw_load_data(CYW_CHIP_ATCMRAM_BASE, fwdata, fwlen); cyw_load_data(CYW_CHIP_ATCMRAM_BASE, fwdata, fwlen);
mg_delayms(5); // TODO(scaprile): CHECK IF THIS IS ACTUALLY NEEDED mg_delayms(5); // TODO(scaprile): CHECK IF THIS IS ACTUALLY NEEDED
// Load NVRAM and place 'length ~length' at the end; end of chip RAM // Load NVRAM and place 'length ~length' at the end; end of chip RAM
{ {
const uint32_t start = CYW_CHIP_RAM_SIZE - 4 - nvramlen; const uint32_t start = CYW_CHIP_RAM_SIZE - 4 - nvramlen;
cyw_load_data(start, nvramdata, nvramlen); // nvramlen must be a multiple of 4 cyw_load_data(start, nvramdata, nvramlen); // nvramlen must be a multiple of 4
// RAM_SIZE is a multiple of WINSZ, so the place for len ~len will be at the end of the window // RAM_SIZE is a multiple of WINSZ, so the place for len ~len will be at the end of the window
@@ -1295,7 +1295,7 @@ static bool cyw_sdio_init() {
struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus; struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus;
uint32_t val = 0; uint32_t val = 0;
if (!mg_sdio_init(s)) return false; if (!mg_sdio_init(s)) return false;
// no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false; // no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false;
if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_CHIP, 64)) return false; if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_CHIP, 64)) return false;
if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_WLAN, 64)) return false; if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_WLAN, 64)) return false;
// TODO(scaprile): we don't handle SDIO interrupts, study CCCR INTEN and SDIO support (SDIO 6.3, 8) // TODO(scaprile): we don't handle SDIO interrupts, study CCCR INTEN and SDIO support (SDIO 6.3, 8)
+37 -13
View File
@@ -1,13 +1,15 @@
#include "phy.h" #include "phy.h"
enum { // ID1 ID2 enum { // ID1 ID2
MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB MG_PHY_KSZ8x = 0x22, // 0022 156x - KSZ8081RNB, KSZ8091RNB
MG_PHY_DP83x = 0x2000, MG_PHY_DP83x = 0x2000,
MG_PHY_DP83867 = 0xa231, // 2000 a231 - TI DP83867I MG_PHY_DP83867 = 0xa231, // 2000 a231 - TI DP83867I
MG_PHY_DP83825 = 0xa140, // 2000 a140 - TI DP83825I MG_PHY_DP83825 = 0xa140, // 2000 a140 - TI DP83825I
MG_PHY_DP83848 = 0x5ca2, // 2000 5ca2 - TI DP83848I MG_PHY_DP83848 = 0x5ca2, // 2000 5ca2 - TI DP83848I
MG_PHY_LAN87x = 0x7, // 0007 c0fx - LAN8720 MG_PHY_LAN87x = 0x7, // 0007 c0fx - LAN8720
MG_PHY_RTL8201 = 0x1C, // 001c c816 - RTL8201, MG_PHY_RTL82x = 0x1c,
MG_PHY_RTL8201 = 0xc816, // 001c c816 - RTL8201F
MG_PHY_RTL8211 = 0xc916, // 001c c916 - RTL8211F
MG_PHY_ICS1894x = 0x15, MG_PHY_ICS1894x = 0x15,
MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894 MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894
}; };
@@ -24,8 +26,9 @@ enum {
MG_PHY_KSZ8x_REG_PC1R = 30, MG_PHY_KSZ8x_REG_PC1R = 30,
MG_PHY_KSZ8x_REG_PC2R = 31, MG_PHY_KSZ8x_REG_PC2R = 31,
MG_PHY_LAN87x_REG_SCSR = 31, MG_PHY_LAN87x_REG_SCSR = 31,
MG_PHY_RTL8201_REG_RMSR = 16, // in page 7 MG_PHY_RTL82x_REG_PAGESEL = 31,
MG_PHY_RTL8201_REG_PAGESEL = 31, MG_PHY_RTL8201_REG_RMSR = 16, // in page 7
MG_PHY_RTL8211_REG_PHYSR = 26, // in page a43
MG_PHY_ICS189432_REG_POLL = 17 MG_PHY_ICS189432_REG_POLL = 17
}; };
@@ -46,8 +49,15 @@ static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
return "KSZ8x"; return "KSZ8x";
case MG_PHY_LAN87x: case MG_PHY_LAN87x:
return "LAN87x"; return "LAN87x";
case MG_PHY_RTL8201: case MG_PHY_RTL82x:
return "RTL8201"; switch (id2) {
case MG_PHY_RTL8201:
return "RTL8201";
case MG_PHY_RTL8211:
return "RTL8211";
default:
return "RTL82x";
}
case MG_PHY_ICS1894x: case MG_PHY_ICS1894x:
return "ICS1894x"; return "ICS1894x";
default: default:
@@ -90,13 +100,15 @@ void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) {
(uint16_t) (MG_BIT(15) | MG_BIT(8) | MG_BIT(7))); (uint16_t) (MG_BIT(15) | MG_BIT(8) | MG_BIT(7)));
} else if (id1 == MG_PHY_LAN87x) { } else if (id1 == MG_PHY_LAN87x) {
// nothing to do // nothing to do
} else if (id1 == MG_PHY_RTL8201) { } else if (id1 == MG_PHY_RTL82x && id2 == MG_PHY_RTL8201) {
// assume PHY has been hardware strapped properly // assume PHY has been hardware strapped properly
#if 0 #if 0
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 7); // Select page 7 phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 7); // Select page 7
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_RMSR, 0x1ffa); phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_RMSR, 0x1ffa);
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 0); // Select page 0 phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 0); // Select page 0
#endif #endif
} else if (id1 == MG_PHY_RTL82x && id2 == MG_PHY_RTL8211) {
// assume PHY has been hardware strapped properly
} }
} }
@@ -136,10 +148,22 @@ bool mg_phy_up(struct mg_phy *phy, uint8_t phy_addr, bool *full_duplex,
uint16_t scsr = phy->read_reg(phy_addr, MG_PHY_LAN87x_REG_SCSR); uint16_t scsr = phy->read_reg(phy_addr, MG_PHY_LAN87x_REG_SCSR);
*full_duplex = scsr & MG_BIT(4); *full_duplex = scsr & MG_BIT(4);
*speed = (scsr & MG_BIT(3)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M; *speed = (scsr & MG_BIT(3)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
} else if (id1 == MG_PHY_RTL8201) { } else if (id1 == MG_PHY_RTL82x) {
uint16_t bcr = phy->read_reg(phy_addr, MG_PHY_REG_BCR); uint16_t id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
*full_duplex = bcr & MG_BIT(8); if (id2 == MG_PHY_RTL8211) {
*speed = (bcr & MG_BIT(13)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M; uint16_t physr;
phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 0xa43);
physr = phy->read_reg(phy_addr, MG_PHY_RTL8211_REG_PHYSR);
phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 0);
*full_duplex = physr & MG_BIT(3);
*speed = (physr & MG_BIT(5)) ? MG_PHY_SPEED_1000M
: (physr & MG_BIT(4)) ? MG_PHY_SPEED_100M
: MG_PHY_SPEED_10M;
} else {
uint16_t bcr = phy->read_reg(phy_addr, MG_PHY_REG_BCR);
*full_duplex = bcr & MG_BIT(8);
*speed = (bcr & MG_BIT(13)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
}
} else if (id1 == MG_PHY_ICS1894x) { } else if (id1 == MG_PHY_ICS1894x) {
uint16_t poll_reg = phy->read_reg(phy_addr, MG_PHY_ICS189432_REG_POLL); uint16_t poll_reg = phy->read_reg(phy_addr, MG_PHY_ICS189432_REG_POLL);
*full_duplex = poll_reg & MG_BIT(14); *full_duplex = poll_reg & MG_BIT(14);
+1 -1
View File
@@ -160,7 +160,7 @@ static bool mg_tcpip_driver_ra_init(struct mg_tcpip_if *ifp) {
MG_DEBUG(("PHY addr: %d, smispin: %d", d->phy_addr, s_smispin)); MG_DEBUG(("PHY addr: %d, smispin: %d", d->phy_addr, s_smispin));
struct mg_phy phy = {raeth_read_phy, raeth_write_phy}; struct mg_phy phy = {raeth_read_phy, raeth_write_phy};
mg_phy_init(&phy, d->phy_addr, 0); // MAC clocks PHY mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC);
// Select RMII mode, // Select RMII mode,
ETHERC->ECMR = MG_BIT(2) | MG_BIT(1); // 100M, Full-duplex, CRC ETHERC->ECMR = MG_BIT(2) | MG_BIT(1); // 100M, Full-duplex, CRC
+525
View File
@@ -0,0 +1,525 @@
#include "st67w6.h"
#include "net.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
MG_ENABLE_DRIVER_ST67W6
static struct mg_tcpip_if *s_ifp;
static uint32_t s_ip, s_mask;
static bool s_link = false, s_connecting = false;
static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
struct mg_wifi_data *wifi =
&((struct mg_tcpip_driver_st67w6_data *) ifp->driver_data)->wifi;
if (wifi->apmode && ev == MG_TCPIP_EV_ST_CHG &&
*(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
MG_DEBUG(("Access Point started"));
s_ip = ifp->ip, ifp->ip = wifi->apip;
s_mask = ifp->mask, ifp->mask = wifi->apmask;
ifp->enable_dhcp_client = false;
ifp->enable_dhcp_server = true;
}
}
static bool st67w6_init(uint8_t *mac);
static void st67w6_poll(bool is_at);
static bool mg_tcpip_driver_st67w6_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_st67w6_data *d =
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
struct mg_wifi_data *wifi = &d->wifi;
if (MG_BIG_ENDIAN) {
MG_ERROR(("Big-endian host"));
return false;
}
if (d->is_ready == NULL) return false;
s_ifp = ifp;
s_ip = ifp->ip;
s_mask = ifp->mask;
s_link = false;
ifp->pfn = wifi_cb;
if (!st67w6_init(ifp->mac)) return false;
if (d->send_queue.size == 0) d->send_queue.size = 8192;
d->send_queue.buf = (char *) mg_calloc(1, d->send_queue.size);
if (d->send_queue.buf == NULL) {
MG_ERROR(("OOM"));
return false;
}
if (wifi->apmode) {
return mg_wifi_ap_start(wifi);
} else if (wifi->ssid != NULL && wifi->pass != NULL) {
return mg_wifi_connect(wifi);
}
return true;
}
// Decouple; module access depends on it being RDY. See st67w6_poll()
size_t mg_tcpip_driver_st67w6_output(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_st67w6_data *d =
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
char *p;
if (mg_queue_book(&d->send_queue, &p, len) < len) return 0;
memcpy(p, buf, len);
mg_queue_add(&d->send_queue, len);
return len;
}
static bool mg_tcpip_driver_st67w6_poll(struct mg_tcpip_if *ifp, bool s1) {
struct mg_tcpip_driver_st67w6_data *d =
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
if (d->is_ready == NULL) return false;
st67w6_poll(false);
if (!s1) return false;
return s_link;
}
struct mg_tcpip_driver mg_tcpip_driver_st67w6 = {
mg_tcpip_driver_st67w6_init, mg_tcpip_driver_st67w6_output, NULL,
mg_tcpip_driver_st67w6_poll};
// AT | STA | AP | HCI | OT
// --------------------------
// framing <-- includes rx stall indication
// --------------------------
// SPI <-- padded to 32-bit
//
// Transactions take place when the module signals RDY, 'tx' and 'write'
// functions actually just write to memory
// - AT: handles configuration and events (unsolicited +foo:). Responses may
// come as:
// - 1 frame: just OK or ERROR
// - 2 frames: 1 text line each, a response and OK
// - 3 frames: 2 text lines: 1 frame with text and no CR/LF, 1 frame with
// binary and CR/LF, 1 text line with OK
// - STA and AP contain plain Ethernet frames to/from the STA and AP networks,
// respectively
// - HCI: BLE stuff
// - OT: ?
#pragma pack(push, 1)
// little endian
struct spi_hdr {
uint16_t magic;
uint16_t len;
uint8_t vflags; // version :2, rx_stall :1, flags :5
uint8_t type;
uint16_t reserved;
};
#define ST67W6_SPI_TYPE_AT 0
#define ST67W6_SPI_TYPE_STA 1
#define ST67W6_SPI_TYPE_AP 2
#define ST67W6_SPI_TYPE_HCI 3
#define ST67W6_SPI_TYPE_OT 4
#pragma pack(pop)
static uint32_t txdata[2048 / 4], rxdata[2048 / 4];
static uint8_t at_resp[300];
static bool s_at_ok, s_at_err;
static size_t s_at_resp_len;
static void st67w6_handle_wifi_evnt(char *, size_t len);
static void st67w6_handle_scan_result(char *, size_t len);
static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read);
static void st67w6_write(unsigned int f, void *data, uint16_t len);
static void st67w6_update_hash_table(void);
// High-level comm stuff
static void st67w6_poll(bool is_at) {
struct mg_tcpip_driver_st67w6_data *d =
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
struct spi_hdr *h = (struct spi_hdr *) rxdata;
unsigned int type;
uint8_t *txsource = (uint8_t *) txdata;
if (s_ifp->update_mac_hash_table) {
// first call to _poll() is after _init(), so this is safe
st67w6_update_hash_table();
s_ifp->update_mac_hash_table = false;
}
if (!is_at) { // send outstanding WLAN frames in the queue
char *buf;
size_t len;
// NOTE(): have traffic-dependent queues or queue traffic type with data
if ((len = mg_queue_next(&d->send_queue, &buf)) > 0) {
st67w6_write(d->wifi.apmode ? ST67W6_SPI_TYPE_AP : ST67W6_SPI_TYPE_STA,
buf, (uint16_t) len);
mg_queue_del(&d->send_queue, len);
} else { // nothing to send
txsource = NULL;
}
}
if (st67w6_spi_poll(txsource, (uint8_t *) rxdata) == 0) return;
if (h->len == 0) return;
type = h->type;
if (type == ST67W6_SPI_TYPE_AT) {
char *p = (char *) (h + 1);
size_t len = h->len;
if (len > 7 && strncmp(p, "+CWLAP:", 7) == 0) { // scan result
st67w6_handle_scan_result(p + 7, len - 7);
} else if (len > 4 && strncmp(p, "+CW:", 4) == 0) {
st67w6_handle_wifi_evnt(p + 4, len - 4);
} else {
MG_VERBOSE(("AT partial: %.*s", (int) len, p));
if (s_at_resp_len + len >= sizeof(at_resp))
s_at_resp_len = 0; // truncate response, error will be caught later
memcpy(at_resp + s_at_resp_len, p, len);
s_at_resp_len += len;
if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
mg_str_n("*ERROR*", 7), NULL)) {
s_at_err = true;
} else if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
mg_str_n("*OK*", 4), NULL)) {
s_at_ok = true;
}
}
} else if (type == ST67W6_SPI_TYPE_STA || type == ST67W6_SPI_TYPE_AP) {
// WLAN frame reception
mg_tcpip_qwrite(h + 1, h->len, s_ifp);
} // else silently discard
}
// WLAN event handling
// - Do not call any AT functions here, otherwise revise st67w6_at_wait()
// - The module likes to send ERROR along with other events
static void st67w6_handle_wifi_evnt(char *p, size_t len) {
struct mg_str data[2];
MG_VERBOSE(("event: %.*s", (int) len, p));
if (len > 9 && strncmp(p, "CONNECTED", 9) == 0) {
s_link = true;
s_connecting = false;
} else if (len > 12 && strncmp(p, "DISCONNECTED", 12) == 0) {
s_link = false;
s_connecting = false; // should not be needed
} else if (s_connecting && len > 6 &&
mg_match(mg_str_n(p, len), mg_str_n("ERROR,*\r\n*", 10), data)) {
size_t reason = 0;
bool ok = mg_to_size_t(data[0], &reason);
s_connecting = false;
MG_ERROR(("CONNECT FAILED"));
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, ok ? &reason : NULL);
} else if (len > 9 && strncmp(p, "SCAN_DONE", 9) == 0) {
MG_VERBOSE(("scan complete"));
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
} // else silently discard: CONNECTING; STA_CONNECTED,"MAC";
// STA_DISCONNECTED,"MAC"; DIST_STA_IP,"MAC","IP"
}
static bool st67w6_at_cmd(char *cmd, size_t len);
// Wi-Fi network stuff
static bool st67w6_wifi_connect(char *ssid, char *pass) {
char cmd[90]; // ssid + pass + AT
size_t cmd_len;
if (!st67w6_at_cmd("AT+CWMODE=1,0\r\n", 15)) return false;
cmd_len = mg_snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\",,0\r\n", ssid,
pass); // takes >700ms
if (!st67w6_at_cmd(cmd, cmd_len)) return false;
st67w6_at_cmd("AT+CWRECONNCFG=0,0\r\n", 20); // disregard error, connecting
s_connecting = true;
return true;
}
static bool st67w6_wifi_disconnect(void) {
s_connecting = false;
if (!st67w6_at_cmd("AT+CWQAP=0\r\n", 12)) return false; // takes >800ms
return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
}
static bool st67w6_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
char cmd[90]; // ssid + pass + AT
size_t cmd_len;
if (!st67w6_at_cmd("AT+CWMODE=2,0\r\n", 15)) return false; // takes >800ms
cmd_len = mg_snprintf(
cmd, sizeof(cmd), "AT+CWSAP=\"%s\",\"%s\",%u,3,2,0\r\n", ssid, pass,
channel); // 3: WPA2_PSK; 2: max stations // takes >350ms
s_link = true;
return st67w6_at_cmd(cmd, cmd_len);
}
static bool st67w6_wifi_ap_stop(void) {
s_link = false;
return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
}
// WLAN scan handling
// +CWLAP:(security,"SSID",RSSI,"BSSID",channel,cipher,proto,wps)\r\n
// security: OPEN, WEP, WPA, WPA2, WPA-WPA2, WPA-EAP, WPA3-SAE, WPA2-WPA3-SAE
// cipher: NONE, WEP, AES/CCMP, TKIP, TKIP and AES/CCMP
// proto: 4-bit bitmap AX,N,G,B; all set from right to left
static bool st67w6_wifi_scan(void) {
return st67w6_at_cmd("AT+CWLAPOPT=1,1695,-100,255,50\r\n", 32) &&
st67w6_at_cmd("AT+CWLAP=0,,,0\r\n", 16);
}
static void st67w6_handle_scan_result(char *data, size_t len) {
struct mg_wifi_scan_bss_data bss;
struct mg_str fields[2];
char mac[6];
uint8_t val;
unsigned int i;
MG_VERBOSE(("scan result event: %.*s", (int) len, data));
++data, --len; // skip '('
if (!mg_span(mg_str_n(data, len), &fields[0], &fields[1], ',') ||
!mg_str_to_num(fields[0], 10, &val, 1))
return;
bss.security =
(val == 0) ? MG_WIFI_SECURITY_OPEN : (uint8_t) MG_WIFI_SECURITY_WEP;
if (val == 2 || val == 4) bss.security |= MG_WIFI_SECURITY_WPA;
if (val == 3 || val == 4 || val == 7) bss.security |= MG_WIFI_SECURITY_WPA2;
if (val == 6 || val == 7) bss.security |= MG_WIFI_SECURITY_WPA3;
if (val == 5) bss.security |= MG_WIFI_SECURITY_WPA_ENTERPRISE;
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
bss.SSID.buf = fields[0].buf + 1, bss.SSID.len = fields[0].len - 2;
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
while (fields[0].buf[0] == ' ') ++fields[0].buf, --fields[0].len;
if (fields[0].buf[0] != '-') return; // positive RSSI would be great
++fields[0].buf, --fields[0].len;
if (!mg_str_to_num(fields[0], 10, &val, 1)) return;
bss.RSSI = (int8_t) - (int8_t) val;
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
if (fields[0].len < 19) return;
++fields[0].buf, --fields[0].len; // skip '"'
for (i = 0; i < 6; i++) {
struct mg_str str;
str.buf = fields[0].buf + 3 * i;
str.len = 2;
if (!mg_str_to_num(str, 16, &mac[i], 1)) return;
}
bss.BSSID = mac;
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
!mg_str_to_num(fields[0], 10, &bss.channel, 1))
return;
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
!mg_str_to_num(fields[0], 10, &val, 1))
return;
// ignore cypher
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
!mg_str_to_num(fields[0], 10, &val, 1))
return;
bss.has_n = (val & 4) != 0;
bss.has_ax = (val & 8) != 0;
bss.band = MG_WIFI_BAND_2G; // NOT INFORMED with default options, no docs
MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf,
bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI,
bss.security));
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
}
// AT stuff
static inline bool delayms(unsigned int ms) {
mg_delayms(ms);
return true;
}
// send AT command, wait for a response or timeout, meanwhile delivering
// received frames and events
static bool st67w6_at_cmd(char *cmd, size_t len) {
bool is_at = true;
unsigned int times = 1000;
s_at_resp_len = 0;
st67w6_write(ST67W6_SPI_TYPE_AT, cmd, (uint16_t) len);
s_at_ok = false, s_at_err = false;
do { // AT response processing does not call any other AT function
st67w6_poll(is_at); // otherwise we can't allow them to pile up here
is_at = false; // avoid repeating, allow queued WLAN frames to be sent
// network frames will be pushed to the queue so that is safe
} while (!s_at_ok && !s_at_err && times-- > 0 && delayms(1));
MG_VERBOSE(("AT response:\n%.*s", s_at_resp_len, at_resp));
MG_VERBOSE(("ok: %c, err: %c, times: %d", s_at_ok ? '1' : '0',
s_at_err ? '1' : '0', (int) times));
return s_at_ok;
}
static bool st67w6_spi_init(void);
bool mg_to_size_t(struct mg_str str, size_t *val);
static bool st67w6_init(uint8_t *mac) {
// struct mg_tcpip_driver_st67w6_data *d = (struct
// mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
struct mg_str data[3];
size_t val;
bool is_b = false;
if (!st67w6_spi_init()) return false;
if (!st67w6_at_cmd("AT\r\n", 4)) return false;
if (!st67w6_at_cmd("AT+CWNETMODE?\r\n", 15) ||
!mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
mg_str_n("*:*\r\n*", 6), data) ||
!mg_to_size_t(data[1], &val))
return false;
if (val != 0) {
MG_ERROR(("Wrong firmware, T02 is needed"));
return false;
}
// set clock, who cares ???
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
// BT-ENABLED DEPENDENCY
// MODULE DEPENDENCY
if (!st67w6_at_cmd("AT+EFUSE-R=24,\"0x100\"\r\n", 23) ||
!mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
data))
return false;
if (data[1].buf[0] == 'C' && data[1].buf[1] == '6') is_b = true;
MG_DEBUG(("WLAN module is %sB type", is_b ? "" : "not")); // --> VERBOSE
if (is_b) {
// Disable the antenna diversity pin
if (!st67w6_at_cmd("AT+IORST=0\r\n", 12)) return false;
// Apparently they intend to disable some antenna ...
if (!st67w6_at_cmd("AT+CWANTENABLE?\r\n", 17)) return false;
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // --> VERBOSE
}
// Do not set wake-up pin (AT+SLWKIO)
// Disable power save mode
// NOTE(scaprile): (no response if in hibernate mode, though I guess we
// wouldn't have reached this point in that case either)
if (!st67w6_at_cmd("AT+PWR=0\r\n", 12)) return false;
// set Wi-Fi
// set country code
if (!st67w6_at_cmd("AT+CWCOUNTRY=0,\"00\"\r\n", 21)) return false;
#if 0
// set DTIM
if (!st67w6_at_cmd("AT+SLWKDTIM=1\r\n", 15)) return false;
#endif
// Read only default MAC, ignore set bit count (data[6] & 0x3F). Custom MACs
// reside at @0x64 and 0x70
if (st67w6_at_cmd("AT+EFUSE-R=7,\"0x014\"\r\n", 22) &&
mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
data)) {
int i;
for (i = 0; i < 6; i++) mac[i] = data[1].buf[5 - i];
MG_DEBUG(("MAC: %M", mg_print_mac, mac));
} else {
MG_ERROR(("read MAC failed"));
}
return true;
}
static void st67w6_update_hash_table(void) {
// TODO(): read database, rebuild hash table
// uint32_t val = 0;
// val = 1;
// st67w6_at_iovar_set2_(0, "mcast_list", (uint8_t *) &val, sizeof(val),
// (uint8_t *) mcast_addr, sizeof(mcast_addr)); mg_delayms(50);
}
// SPI specifics
#define ST67W6_SPI_MAGIC 0x55AA
// #define ST67W6_SPI_VERSION(x), IS_STALL, FLAGS(x), ...
static const uint8_t idlehdr[sizeof(struct spi_hdr)] = {0xaa, 0x55, 0, 0,
0, 0, 0, 0};
static void st67w6_write(unsigned int f, void *data, uint16_t len) {
struct spi_hdr *h = (struct spi_hdr *) txdata;
h->magic = ST67W6_SPI_MAGIC;
h->type = (uint8_t) f;
h->vflags = 0;
h->len = len;
h->reserved = 0;
memmove(h + 1, data, len);
}
static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read) {
struct mg_tcpip_driver_st67w6_data *d =
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
struct spi_hdr *th, *rh = (struct spi_hdr *) read;
struct mg_tcpip_spi_ *s = (struct mg_tcpip_spi_ *) d->spi;
size_t padded;
unsigned int times;
th = (write != NULL) ? (struct spi_hdr *) write : (struct spi_hdr *) idlehdr;
s->begin(s->spi);
times = 50;
while (times--) {
if (d->is_ready()) break;
if (times == 0) {
MG_ERROR(("RDY TIMEOUT"));
s->end(s->spi);
return 0;
}
mg_delayms(1);
}
padded = (th->len + 3) & ~3;
s->txn(s->spi, (uint8_t *) th, (uint8_t *) rh, sizeof(*th) + padded);
if (rh->magic == ST67W6_SPI_MAGIC && rh->len > padded) {
size_t remaining_padded = (rh->len - padded + 3) & ~3;
if (remaining_padded > (2048 - sizeof(*rh) - padded))
remaining_padded = 2048 - sizeof(*rh) - padded;
s->txn(s->spi, NULL, read + sizeof(*rh) + padded, remaining_padded);
}
times = 50;
while (times--) {
if (!d->is_ready()) break;
if (times == 0) {
MG_ERROR(("!RDY TIMEOUT"));
break;
}
mg_delayms(1);
}
s->end(s->spi);
return (size_t) rh->len;
}
static bool st67w6_spi_init(void) {
struct mg_tcpip_driver_st67w6_data *d =
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
size_t len;
unsigned int times = 1000;
while (times--) {
if (d->is_ready()) break;
if (times == 0) return false;
mg_delayms(1);
}
if (((len = st67w6_spi_poll(NULL, (uint8_t *) rxdata)) == 0) ||
!mg_match(mg_str_n(((char *) rxdata) + sizeof(struct spi_hdr), len),
mg_str_n("*ready*", 7), NULL))
return false;
return true;
}
// Mongoose Wi-Fi API functions
bool mg_wifi_scan(void) {
return st67w6_wifi_scan();
}
bool mg_wifi_connect(struct mg_wifi_data *wifi) {
s_ifp->ip = s_ip;
s_ifp->mask = s_mask;
if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
s_ifp->enable_dhcp_server = false;
MG_DEBUG(("Connecting to '%s'", wifi->ssid));
return st67w6_wifi_connect(wifi->ssid, wifi->pass);
}
bool mg_wifi_disconnect(void) {
return st67w6_wifi_disconnect();
}
bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
return st67w6_wifi_ap_start(wifi->apssid, wifi->appass, wifi->apchannel);
}
bool mg_wifi_ap_stop(void) {
return st67w6_wifi_ap_stop();
}
#endif
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
MG_ENABLE_DRIVER_ST67W6
struct mg_tcpip_spi_ {
void *spi; // Opaque SPI bus descriptor
void (*begin)(void *); // SPI begin: slave select low
void (*end)(void *); // SPI end: slave select high
void (*txn)(void *, uint8_t *, uint8_t *,
size_t len); // SPI transaction: write-read len bytes
};
struct mg_tcpip_driver_st67w6_data {
struct mg_wifi_data wifi;
void *spi;
bool (*is_ready)(void); // return state of module RDY pin
struct mg_queue send_queue; // decouple tx calls from module polls
};
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_st67w6_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_WIFI_CONFIG(&driver_data_); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_st67w6; \
mif_.driver_data = &driver_data_; \
mif_.recv_queue.size = 8192; \
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: st67w6, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
+115 -23
View File
@@ -3,8 +3,26 @@
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \ #if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
MG_ENABLE_DRIVER_STM32N) MG_ENABLE_DRIVER_STM32N)
// STM32H: vendor modded single-queue Synopsys v4.2 // STM32H: vendor modded single-queue Synopsys v4.2
// STM32N, MCXNx4x: dual-queue Synopsys v5.2 with no hash table option // STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
// RT1170 ENET_QOS: quad-queue Synopsys v5.1 // MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
// RT1170: ENET_QOS: quad-queue Synopsys v5.1
#if MG_ENABLE_DRIVER_STM32H
#define SYNOPSYS_ENET_V5 0
#define SYNOPSYS_ENET_SINGLEQ 1
#define SYNOPSYS_ENET_NOHASHTABLE 0
#define SYNOPSYS_ENET_GbE 0
#elif MG_ENABLE_DRIVER_STM32N
#define SYNOPSYS_ENET_V5 1
#define SYNOPSYS_ENET_SINGLEQ 0
#define SYNOPSYS_ENET_NOHASHTABLE 1
#define SYNOPSYS_ENET_GbE 1
#elif MG_ENABLE_DRIVER_MCXN
#define SYNOPSYS_ENET_V5 1
#define SYNOPSYS_ENET_SINGLEQ 0
#define SYNOPSYS_ENET_NOHASHTABLE 1
#define SYNOPSYS_ENET_GbE 0
#endif
struct synopsys_enet_qos { struct synopsys_enet_qos {
volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R, volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR, RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
@@ -37,24 +55,85 @@ struct synopsys_enet_qos {
}; };
#undef ETH #undef ETH
#if MG_ENABLE_DRIVER_STM32H #if MG_ENABLE_DRIVER_STM32H
#define ETH \ #define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40028000UL)
((struct synopsys_enet_qos *) (uintptr_t) (0x40000000UL + 0x00020000UL + \
0x8000UL))
#elif MG_ENABLE_DRIVER_MCXN
#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL)
#elif MG_ENABLE_DRIVER_STM32N #elif MG_ENABLE_DRIVER_STM32N
#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x48036000UL) #define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x48036000UL)
#elif MG_ENABLE_DRIVER_MCXN
#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL)
#endif #endif
#define ETH_PKT_SIZE 1540 // Max frame size #define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count #define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words) #define ETH_DS 4 // Descriptor size (words)
static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED; #if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_STM32N
static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED; #define CACHE_LINESZ 32 // must be a whole number of (d)words (see DESC_SZW)
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED; #ifndef SCB
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED; struct m7_scb {
static struct mg_tcpip_if *s_ifp; // MIP interface volatile uint32_t CPUID, RESERVED1[4], CCR, RESERVED2[145], DCIMVAC, DCISW,
DCCMVAU, DCCMVAC, DCCSW, DCCIMVAC, DCCISW, RESERVED3[10], CACR,
RESERVED4[3];
};
#define SCB ((struct m7_scb *) (uintptr_t) 0xE000ED00UL)
#endif
// ending ISB is not needed because we don't cache instructions in data space
static inline void MG_CACHE_INVAL(uint8_t *addr, int32_t len) {
#if MG_ENABLE_DRIVER_STM32H
if ((SCB->CPUID & 0xfff0) != 0xc270) return; // not a Cortex-M7 => not an H7
#endif
if ((SCB->CCR & MG_BIT(16)) == 0) return; // cache not enabled
MG_DSB();
while (len > 0) {
SCB->DCIMVAC = (uint32_t) addr;
addr += CACHE_LINESZ;
len -= CACHE_LINESZ;
}
MG_DSB();
}
static inline void MG_CACHE_FLUSH(uint8_t *addr, int32_t len) {
#if MG_ENABLE_DRIVER_STM32H
if ((SCB->CPUID & 0xfff0) != 0xc270) return; // not a Cortex-M7 => not an H7
#endif
if ((SCB->CCR & MG_BIT(16)) == 0) return; // cache not enabled
MG_DSB();
while (len > 0) {
SCB->DCCMVAC = (uint32_t) addr;
addr += CACHE_LINESZ;
len -= CACHE_LINESZ;
}
MG_DSB();
}
#define ETH_RAM_ALIGNED MG_32BYTE_ALIGNED // depends on CACHE_LINESZ and ETH
#define CACHE_ALIGN(x) \
((((size_t) (x)) + CACHE_LINESZ - 1) & ~(CACHE_LINESZ - 1))
#define DESC_SZ CACHE_ALIGN(4 * ETH_DS) // grow descriptors to fit a line
#define DESC_SZW (DESC_SZ / 4)
#define BUFF_SZ CACHE_ALIGN(ETH_PKT_SIZE) // grow buffers to fit n lines
#if MG_ENABLE_DRIVER_STM32H
#define DESC_SKIPW (DESC_SZW - ETH_DS) // tell DMA the descriptor size, words
#else
// MG_ENABLE_DRIVER_STM32N, DMA is AXI and specs skip in 64-bit double-words
#define DESC_SKIPW ((DESC_SZW - ETH_DS) / 2)
#endif
#else
#define MG_CACHE_FLUSH(a, b)
#define MG_CACHE_INVAL(a, b)
#define ETH_RAM_ALIGNED MG_8BYTE_ALIGNED // depends on ETH DMA alone
#define DESC_SZ 0
#define DESC_SZW ETH_DS
#define BUFF_SZ ETH_PKT_SIZE
#define DESC_SKIPW 0 // no need to skip, as we're not aligning to cache lines
#endif
// array[rows][cols] = coldata coldata ... for all rows, keeps alignment
static volatile uint32_t s_rxdesc[ETH_DESC_CNT][DESC_SZW] MG_ETH_RAM
ETH_RAM_ALIGNED;
static volatile uint32_t s_txdesc[ETH_DESC_CNT][DESC_SZW] MG_ETH_RAM
ETH_RAM_ALIGNED;
static uint8_t s_rxbuf[ETH_DESC_CNT][BUFF_SZ] MG_ETH_RAM ETH_RAM_ALIGNED;
static uint8_t s_txbuf[ETH_DESC_CNT][BUFF_SZ] MG_ETH_RAM ETH_RAM_ALIGNED;
static struct mg_tcpip_if *s_ifp; // MIP interface
static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMDIOAR &= (0xF << 8); ETH->MACMDIOAR &= (0xF << 8);
@@ -85,12 +164,14 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
s_rxdesc[i][3] = MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V s_rxdesc[i][3] = MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V
} }
MG_CACHE_FLUSH((uint8_t *) s_rxdesc, sizeof(s_rxdesc));
// Init TX descriptors // Init TX descriptors
memset((char *) s_txdesc, 0, sizeof(s_txdesc)); // manual init memset((char *) s_txdesc, 0, sizeof(s_txdesc)); // manual init
for (int i = 0; i < ETH_DESC_CNT; i++) { for (int i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
} }
MG_CACHE_FLUSH((uint8_t *) s_txdesc, sizeof(s_txdesc));
ETH->DMAMR |= MG_BIT(0); // Software reset ETH->DMAMR |= MG_BIT(0); // Software reset
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
@@ -107,7 +188,7 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
ETH->DMASBMR |= MG_BIT(12); // AAL NOTE(scaprile): is this actually needed ETH->DMASBMR |= MG_BIT(12); // AAL NOTE(scaprile): is this actually needed
ETH->MACIER = 0; // Do not enable additional irq sources (reset value) ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
ETH->MACTFCR = MG_BIT(7); // Disable zero-quanta pause ETH->MACTFCR = MG_BIT(7); // Disable zero-quanta pause
#if MG_ENABLE_DRIVER_STM32H #if !SYNOPSYS_ENET_V5
ETH->MACPFR = MG_BIT(10); // Perfect filtering ETH->MACPFR = MG_BIT(10); // Perfect filtering
#endif #endif
struct mg_phy phy = {eth_read_phy, eth_write_phy}; struct mg_phy phy = {eth_read_phy, eth_write_phy};
@@ -123,15 +204,15 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
ETH->DMACTDTPR = ETH->DMACTDTPR =
(uint32_t) (uintptr_t) s_txdesc; // first available descriptor address (uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value) ETH->DMACCR = DESC_SKIPW << 18; // DSL (contiguous/sparse descriptor table)
#if !MG_ENABLE_DRIVER_STM32H #if SYNOPSYS_ENET_V5
MG_SET_BITS(ETH->DMACTCR, 0x3F << 16, MG_BIT(16)); MG_SET_BITS(ETH->DMACTCR, 0x3F << 16, MG_BIT(16));
MG_SET_BITS(ETH->DMACRCR, 0x3F << 16, MG_BIT(16)); MG_SET_BITS(ETH->DMACRCR, 0x3F << 16, MG_BIT(16));
#endif #endif
ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE
ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) | ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) |
MG_BIT(15); // RE, TE, Duplex, Fast, Reserved MG_BIT(15); // RE, TE, Duplex, Fast, (10/100)/Reserved
#if MG_ENABLE_DRIVER_STM32H #if SYNOPSYS_ENET_SINGLEQ
ETH->MTLTQOMR |= MG_BIT(1); // TSF ETH->MTLTQOMR |= MG_BIT(1); // TSF
ETH->MTLRQOMR |= MG_BIT(5); // RSF ETH->MTLRQOMR |= MG_BIT(5); // RSF
#else #else
@@ -155,18 +236,23 @@ static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) { struct mg_tcpip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) { if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len)); MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big return 0; // Frame is too big
} else if ((s_txdesc[s_txno][3] & MG_BIT(31))) { }
MG_CACHE_INVAL((uint8_t *) &s_txdesc[s_txno], DESC_SZ);
if ((s_txdesc[s_txno][3] & MG_BIT(31))) {
ifp->nerr++; ifp->nerr++;
MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno, MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR)); s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
MG_CACHE_INVAL((uint8_t *) s_txdesc, sizeof(s_txdesc));
for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3])); for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
len = 0; // All descriptors are busy, fail len = 0; // All descriptors are busy, fail
} else { } else {
memcpy(s_txbuf[s_txno], buf, len); // Copy data memcpy(s_txbuf[s_txno], buf, len); // Copy data
MG_CACHE_FLUSH((uint8_t *) &s_txbuf[s_txno], BUFF_SZ);
s_txdesc[s_txno][2] = (uint32_t) len; // Set data len s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
s_txdesc[s_txno][3] = MG_BIT(28) | MG_BIT(29); // FD, LD s_txdesc[s_txno][3] = MG_BIT(28) | MG_BIT(29); // FD, LD
s_txdesc[s_txno][3] |= MG_BIT(31); // Set OWN bit - let DMA take over s_txdesc[s_txno][3] |= MG_BIT(31); // Set OWN bit - let DMA take over
MG_CACHE_FLUSH((uint8_t *) &s_txdesc[s_txno], DESC_SZ);
if (++s_txno >= ETH_DESC_CNT) s_txno = 0; if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
} }
ETH->DMACSR |= MG_BIT(2) | MG_BIT(1); // Clear any prior TBU, TPS ETH->DMACSR |= MG_BIT(2) | MG_BIT(1); // Clear any prior TBU, TPS
@@ -176,7 +262,7 @@ static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
} }
static void mg_tcpip_driver_stm32h_update_hash_table(struct mg_tcpip_if *ifp) { static void mg_tcpip_driver_stm32h_update_hash_table(struct mg_tcpip_if *ifp) {
#if MG_ENABLE_DRIVER_MCXN || MG_ENABLE_DRIVER_STM32N #if SYNOPSYS_ENET_NOHASHTABLE
ETH->MACPFR = MG_BIT(4); // Pass Multicast (pass all multicast frames) ETH->MACPFR = MG_BIT(4); // Pass Multicast (pass all multicast frames)
#else #else
// TODO(): read database, rebuild hash table // TODO(): read database, rebuild hash table
@@ -208,8 +294,11 @@ static bool mg_tcpip_driver_stm32h_poll(struct mg_tcpip_if *ifp, bool s1) {
// if(link is slow or half) set flags otherwise // if(link is slow or half) set flags otherwise
// reg = tmp // reg = tmp
uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(13); // 100M, Full-duplex uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(13); // 100M, Full-duplex
if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M #if SYNOPSYS_ENET_GbE
if (full_duplex == false) maccr &= ~MG_BIT(13); // Half-duplex if (speed == MG_PHY_SPEED_1000M) maccr &= ~MG_BIT(15); // 1000M
#endif
if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M
if (full_duplex == false) maccr &= ~MG_BIT(13); // Half-duplex
ETH->MACCR = maccr; // IRQ handler does not fiddle with this register ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10, MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10,
maccr & MG_BIT(13) ? "full" : "half")); maccr & MG_BIT(13) ? "full" : "half"));
@@ -231,6 +320,7 @@ void ETH1_IRQHandler(void) {
if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop
ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
MG_CACHE_INVAL((uint8_t *) &s_rxdesc[s_rxno], DESC_SZ);
if (s_rxdesc[s_rxno][3] & MG_BIT(31)) break; // exit when done if (s_rxdesc[s_rxno][3] & MG_BIT(31)) break; // exit when done
if (((s_rxdesc[s_rxno][3] & (MG_BIT(28) | MG_BIT(29))) == if (((s_rxdesc[s_rxno][3] & (MG_BIT(28) | MG_BIT(29))) ==
(MG_BIT(28) | MG_BIT(29))) && (MG_BIT(28) | MG_BIT(29))) &&
@@ -238,10 +328,12 @@ void ETH1_IRQHandler(void) {
uint32_t len = s_rxdesc[s_rxno][3] & (MG_BIT(15) - 1); uint32_t len = s_rxdesc[s_rxno][3] & (MG_BIT(15) - 1);
// MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3], // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
// ETH->DMACSR)); // ETH->DMACSR));
MG_CACHE_INVAL((uint8_t *) &s_rxbuf[s_rxno], BUFF_SZ);
mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
} }
s_rxdesc[s_rxno][3] = s_rxdesc[s_rxno][3] =
MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V
MG_CACHE_FLUSH((uint8_t *) &s_rxdesc[s_rxno], DESC_SZ);
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
} }
} }
+2
View File
@@ -27,5 +27,7 @@ enum {
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis
MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data
MG_EV_MDNS_REQ, // mDNS request struct mg_mdns_req *
MG_EV_MDNS_RESP, // mDNS response struct mg_mdns_resp *
MG_EV_USER // Starting ID for user events MG_EV_USER // Starting ID for user events
}; };
+6 -4
View File
@@ -22,7 +22,8 @@ static int xisinf(double x) {
union { union {
double f; double f;
uint64_t u; uint64_t u;
} ieee754 = {x}; } ieee754;
ieee754.f = x;
return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 &&
((unsigned) ieee754.u == 0); ((unsigned) ieee754.u == 0);
} }
@@ -31,7 +32,8 @@ static int xisnan(double x) {
union { union {
double f; double f;
uint64_t u; uint64_t u;
} ieee754 = {x}; } ieee754;
ieee754.f = x;
return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) + return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) +
((unsigned) ieee754.u != 0) > ((unsigned) ieee754.u != 0) >
0x7ff00000; 0x7ff00000;
@@ -99,7 +101,7 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) {
} }
while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes
if (tz && n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot if (tz && n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot
n += s; n += s;
if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1; if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1;
buf[n] = '\0'; buf[n] = '\0';
@@ -129,7 +131,7 @@ static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) {
} }
static size_t scpy(void (*out)(char, void *), void *ptr, char *buf, static size_t scpy(void (*out)(char, void *), void *ptr, char *buf,
size_t len) { size_t len) {
size_t i = 0; size_t i = 0;
while (i < len && buf[i] != '\0') out(buf[i++], ptr); while (i < len && buf[i] != '\0') out(buf[i++], ptr);
return i; return i;
+2 -1
View File
@@ -86,7 +86,8 @@ static void mg_fs_ls_fn(const char *filename, void *param) {
} }
bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len) { bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len) {
struct mg_str s = {buf, len}; struct mg_str s;
s.buf = buf, s.len = len;
fs->ls(path, mg_fs_ls_fn, &s); fs->ls(path, mg_fs_ls_fn, &s);
return buf[0] != '\0'; return buf[0] != '\0';
} }
+26 -18
View File
@@ -1,7 +1,7 @@
#include "http.h"
#include "arch.h" #include "arch.h"
#include "base64.h" #include "base64.h"
#include "fmt.h" #include "fmt.h"
#include "http.h"
#include "json.h" #include "json.h"
#include "log.h" #include "log.h"
#include "net.h" #include "net.h"
@@ -237,6 +237,8 @@ static const char *skiptorn(const char *s, const char *end, struct mg_str *v) {
static bool mg_http_parse_headers(const char *s, const char *end, static bool mg_http_parse_headers(const char *s, const char *end,
struct mg_http_header *h, size_t max_hdrs) { struct mg_http_header *h, size_t max_hdrs) {
size_t i, n; size_t i, n;
int cl_count = 0, te_count = 0, auth_count = 0;
int conn_count = 0, cookie_count = 0;
for (i = 0; i < max_hdrs; i++) { for (i = 0; i < max_hdrs; i++) {
struct mg_str k = {NULL, 0}, v = {NULL, 0}; struct mg_str k = {NULL, 0}, v = {NULL, 0};
if (s >= end) return false; if (s >= end) return false;
@@ -252,6 +254,13 @@ static bool mg_http_parse_headers(const char *s, const char *end,
while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) { while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) {
v.len--; // Trim spaces v.len--; // Trim spaces
} }
// detect duplicated headers -> discard
if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) && (++cl_count > 1)) ||
((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) && (++te_count > 1)) ||
((mg_strcasecmp(k, mg_str("Authorization")) == 0) && (++auth_count > 1)) ||
((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
return false;
// MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf)); // MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
h[i].name = k, h[i].value = v; // Success. Assign values h[i].name = k, h[i].value = v; // Success. Assign values
} }
@@ -286,6 +295,8 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
// If we're given a version, check that it is HTTP/x.x // If we're given a version, check that it is HTTP/x.x
version_prefix_valid = version_prefix_valid =
hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0); hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0);
if (!is_response && !version_prefix_valid)
return -1; // no version detected in request
if (!is_response && hm->proto.len > 0 && if (!is_response && hm->proto.len > 0 &&
(!version_prefix_valid || hm->proto.len != 8 || (!version_prefix_valid || hm->proto.len != 8 ||
(hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') || (hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') ||
@@ -317,17 +328,9 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
// responses. If HTTP response does not have Content-Length set, then // responses. If HTTP response does not have Content-Length set, then
// body is read until socket is closed, i.e. body.len is infinite (~0). // body is read until socket is closed, i.e. body.len is infinite (~0).
// //
// For HTTP requests though, according to // For HTTP requests though, if Content-Length is not specified
// http://tools.ietf.org/html/rfc7231#section-8.1.3, // set body length to 0.
// only POST and PUT methods have defined body semantics. if (hm->body.len == (size_t) ~0 && !is_response) {
// Therefore, if Content-Length is not specified and methods are
// not one of PUT or POST, set body length to 0.
//
// So, if it is HTTP request, and Content-Length is not set,
// and method is not (PUT or POST) then reset body length to zero.
if (hm->body.len == (size_t) ~0 && !is_response &&
mg_strcasecmp(hm->method, mg_str("PUT")) != 0 &&
mg_strcasecmp(hm->method, mg_str("POST")) != 0) {
hm->body.len = 0; hm->body.len = 0;
hm->message.len = (size_t) req_len; hm->message.len = (size_t) req_len;
} }
@@ -648,10 +651,10 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
status, mg_http_status_code_str(status), (int) mime.len, mime.buf, status, mg_http_status_code_str(status), (int) mime.len, mime.buf,
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "", etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
range, opts->extra_headers ? opts->extra_headers : ""); range, opts->extra_headers ? opts->extra_headers : "");
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) { if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
c->is_resp = 0; c->is_resp = 0;
mg_fs_close(fd); mg_fs_close(fd);
} else { } else { // start serving static content only if not closing, see #3354
// Track to-be-sent content length at the end of c->data, aligned // Track to-be-sent content length at the end of c->data, aligned
size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) / size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
sizeof(size_t) * sizeof(size_t)]; sizeof(size_t) * sizeof(size_t)];
@@ -1020,7 +1023,7 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
const char *buf = (char *) c->recv.buf + ofs; const char *buf = (char *) c->recv.buf + ofs;
int n = mg_http_parse(buf, c->recv.len - ofs, &hm); int n = mg_http_parse(buf, c->recv.len - ofs, &hm);
struct mg_str *te; // Transfer - encoding header struct mg_str *te; // Transfer - encoding header
bool is_chunked = false; bool is_chunked = false, is_http_1_0 = false;
size_t old_len = c->recv.len; size_t old_len = c->recv.len;
if (n < 0) { if (n < 0) {
// We don't use mg_error() here, to avoid closing pipelined requests // We don't use mg_error() here, to avoid closing pipelined requests
@@ -1043,7 +1046,11 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG
hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf); hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf);
} }
if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) { is_http_1_0 =
hm.proto.len > 8 && mg_ncasecmp(hm.proto.buf, "HTTP/1.0", 8) == 0;
// HTTP/1.0 does not use "Transfer-Encoding: chunked"
if (!is_http_1_0 &&
(te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
if (mg_strcasecmp(*te, mg_str("chunked")) == 0) { if (mg_strcasecmp(*te, mg_str("chunked")) == 0) {
is_chunked = true; is_chunked = true;
} else { } else {
@@ -1058,9 +1065,10 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 || if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 ||
mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) { mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) {
// POST and PUT should include an entity body. Therefore, they should // POST and PUT should include an entity body. Therefore, they should
// contain a Content-length header. Other requests can also contain a // contain a Content-length header (unless the body length is 0, in
// which case it can be omitted). Other requests can also contain a
// body, but their content has no defined semantics (RFC 7231) // body, but their content has no defined semantics (RFC 7231)
require_content_len = true; if (hm.body.len != 0) require_content_len = true;
ofs += (size_t) n; // this request has been processed ofs += (size_t) n; // this request has been processed
} else if (is_response) { } else if (is_response) {
// HTTP spec 7.2 Entity body: All other responses must include a body // HTTP spec 7.2 Entity body: All other responses must include a body
+17 -4
View File
@@ -60,15 +60,28 @@ static double mg_atod(const char *p, int len, int *numlen) {
// Exponential // Exponential
if (i < len && (p[i] == 'e' || p[i] == 'E')) { if (i < len && (p[i] == 'e' || p[i] == 'E')) {
int j, exp = 0, minus = 0; int exp = 0, minus = 0;
i++; i++;
if (i < len && p[i] == '-') minus = 1, i++; if (i < len && p[i] == '-') minus = 1, i++;
if (i < len && p[i] == '+') i++; if (i < len && p[i] == '+') i++;
while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308) while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308)
exp = exp * 10 + (p[i++] - '0'); exp = exp * 10 + (p[i++] - '0');
if (minus) exp = -exp; // use fast exponentiation
for (j = 0; j < exp; j++) d *= 10.0; // https://en.wikipedia.org/wiki/Exponentiation_by_squaring
for (j = 0; j < -exp; j++) d /= 10.0; if (exp != 0) {
double x = 10, y = 1;
if (exp > 308) exp = 308;
if (minus) x = 0.1;
while (exp > 1) {
if (exp & 1) {
y *= x;
--exp;
}
x *= x;
exp >>= 1;
}
d *= x * y;
}
} }
if (numlen != NULL) *numlen = i; if (numlen != NULL) *numlen = i;
+121
View File
@@ -0,0 +1,121 @@
#include "l2.h"
#include "net.h"
#include "net_builtin.h"
#if MG_ENABLE_TCPIP
// L2 API
void mg_l2_init(enum mg_l2type type, uint8_t *addr, uint16_t *mtu,
uint16_t *framesize);
uint8_t *mg_l2_header(enum mg_l2type type, enum mg_l2proto proto, uint8_t *src,
uint8_t *dst, uint8_t *frame);
size_t mg_l2_footer(enum mg_l2type type, size_t len, uint8_t *frame);
bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw);
// TODO(): ? bool mg_l2_rx(enum mg_l2type type, struct mg_l2opts *opts, uint8_t
// *addr, enum mg_l2proto *proto, struct mg_str *pay, struct mg_str *raw);
uint8_t *mg_l2_getaddr(enum mg_l2type type, uint8_t *frame);
uint8_t *mg_l2_mapip(enum mg_l2type type, enum mg_l2addrtype addrtype,
struct mg_addr *ip);
bool mg_l2_genip6(enum mg_l2type type, uint64_t *ip6, uint8_t prefix_len,
uint8_t *addr);
bool mg_l2_ip6get(enum mg_l2type type, uint8_t *addr, uint8_t *opts,
uint8_t len);
uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts);
// clang-format off
extern void mg_l2_eth_init(struct mg_l2addr *, uint16_t *, uint16_t *);
extern uint8_t *mg_l2_eth_header(enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
extern size_t mg_l2_eth_footer(size_t, uint8_t *);
extern bool mg_l2_eth_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
extern struct mg_l2addr *mg_l2_eth_getaddr(uint8_t *);
extern struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype, struct mg_addr *);
extern bool mg_l2_eth_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
extern bool mg_l2_eth_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
extern uint8_t mg_l2_eth_ip6put(struct mg_l2addr *, uint8_t *);
extern void mg_l2_ppp_init(struct mg_l2addr *, uint16_t *, uint16_t *);
extern uint8_t *mg_l2_ppp_header(enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
extern size_t mg_l2_ppp_footer(size_t, uint8_t *);
extern bool mg_l2_ppp_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
extern struct mg_l2addr *mg_l2_ppp_getaddr(uint8_t *);
extern struct mg_l2addr *mg_l2_ppp_mapip(enum mg_l2addrtype, struct mg_addr *);
#if MG_ENABLE_IPV6
extern bool mg_l2_ppp_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
extern bool mg_l2_ppp_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
extern uint8_t mg_l2_ppp_ip6put(struct mg_l2addr *, uint8_t *);
#endif
typedef void (*l2_init_fn)(struct mg_l2addr *, uint16_t *, uint16_t *);
typedef uint8_t *((*l2_header_fn)(enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *));
typedef size_t (*l2_footer_fn)(size_t, uint8_t *);
typedef bool (*l2_rx_fn)(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
typedef struct mg_l2addr (*(*l2_getaddr_fn)(uint8_t *));
typedef struct mg_l2addr (*(*l2_mapip_fn)(enum mg_l2addrtype, struct mg_addr *));
#if MG_ENABLE_IPV6
typedef bool (*l2_genip6_fn)(uint64_t *, uint8_t, struct mg_l2addr *);
typedef bool (*l2_ip6get_fn)(struct mg_l2addr *, uint8_t *, uint8_t);
typedef uint8_t (*l2_ip6put_fn)(struct mg_l2addr *, uint8_t *);
#endif
// clang-format on
static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init};
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header};
static const l2_footer_fn l2_footer[] = {mg_l2_eth_footer, mg_l2_ppp_footer};
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx};
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr,
mg_l2_ppp_getaddr};
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip};
#if MG_ENABLE_IPV6
static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6};
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get};
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put};
#endif
void mg_l2_init(enum mg_l2type type, uint8_t *addr, uint16_t *mtu,
uint16_t *framesize) {
l2_init[type]((struct mg_l2addr *) addr, mtu, framesize);
}
uint8_t *mg_l2_header(enum mg_l2type type, enum mg_l2proto proto, uint8_t *src,
uint8_t *dst, uint8_t *frame) {
return l2_header[type](proto, (struct mg_l2addr *) src,
(struct mg_l2addr *) dst, frame);
}
size_t mg_l2_footer(enum mg_l2type type, size_t len, uint8_t *frame) {
return l2_footer[type](len, frame);
}
bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw) {
return l2_rx[ifp->l2type](ifp, proto, pay, raw);
}
uint8_t *mg_l2_getaddr(enum mg_l2type type, uint8_t *frame) {
return (uint8_t *) l2_getaddr[type](frame);
}
struct mg_l2addr s_mapip;
uint8_t *mg_l2_mapip(enum mg_l2type type, enum mg_l2addrtype addrtype,
struct mg_addr *ip) {
return (uint8_t *) l2_mapip[type](addrtype, ip);
}
#if MG_ENABLE_IPV6
bool mg_l2_genip6(enum mg_l2type type, uint64_t *ip6, uint8_t prefix_len,
uint8_t *addr) {
return l2_genip6[type](ip6, prefix_len, (struct mg_l2addr *) addr);
}
bool mg_l2_ip6get(enum mg_l2type type, uint8_t *addr, uint8_t *opts,
uint8_t len) {
return l2_ip6get[type]((struct mg_l2addr *) addr, opts, len);
}
uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts) {
return l2_ip6put[type]((struct mg_l2addr *) addr, opts);
}
#endif
#endif
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "arch.h"
#include "config.h"
#if MG_ENABLE_TCPIP
// no config defaults to 0 => Ethernet
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP }; // MG_TCPIP_L2_PPPoE
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct mg_l2addr {
union {
uint8_t mac[6];
} addr;
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
#if 0
TODO(): ?
struct eth_opts {
bool enable_crc32_check; // Do a CRC check on RX frames and strip it
bool enable_mac_check; // Do a MAC check on RX frames
};
struct mg_l2opts {
union {
struct eth_opts eth;
};
};
#endif
enum mg_l2proto {
MG_TCPIP_L2PROTO_IPV4 = 0,
MG_TCPIP_L2PROTO_IPV6,
MG_TCPIP_L2PROTO_ARP,
MG_TCPIP_L2PROTO_PPPoE_DISC,
MG_TCPIP_L2PROTO_PPPoE_SESS
};
enum mg_l2addrtype {
MG_TCPIP_L2ADDR_BCAST,
MG_TCPIP_L2ADDR_MCAST,
MG_TCPIP_L2ADDR_MCAST6
};
#endif
+171
View File
@@ -0,0 +1,171 @@
#include "l2.h"
#include "log.h"
#include "net.h"
#include "net_builtin.h"
#include "printf.h"
#include "util.h"
#if MG_ENABLE_TCPIP
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct eth {
uint8_t dst[6]; // Destination MAC address
uint8_t src[6]; // Source MAC address
uint16_t type; // Ethernet type
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
static const uint16_t eth_types[] = {
// order is vital, see l2.h
0x800, // IPv4
0x86dd, // IPv6
0x806, // ARP
0x8863, // PPPoE Discovery Stage
0x8864 // PPPoE Session Stage
};
void mg_l2_eth_init(struct mg_l2addr *l2addr, uint16_t *mtu,
uint16_t *framesize) {
// If MAC is not set, make a random one
if (l2addr->addr.mac[0] == 0 && l2addr->addr.mac[1] == 0 &&
l2addr->addr.mac[2] == 0 && l2addr->addr.mac[3] == 0 &&
l2addr->addr.mac[4] == 0 && l2addr->addr.mac[5] == 0) {
l2addr->addr.mac[0] = 0x02; // Locally administered, unicast
mg_random(&l2addr->addr.mac[1], sizeof(l2addr->addr.mac) - 1);
MG_INFO(
("MAC not set. Generated random: %M", mg_print_mac, l2addr->addr.mac));
}
*mtu = 1500;
*framesize = 1540;
}
uint8_t *mg_l2_eth_header(enum mg_l2proto proto, struct mg_l2addr *src,
struct mg_l2addr *dst, uint8_t *frame) {
struct eth *eth = (struct eth *) frame;
eth->type = mg_htons(eth_types[(unsigned int) proto]);
memcpy(eth->src, src->addr.mac, sizeof(eth->dst));
memcpy(eth->dst, dst->addr.mac, sizeof(eth->dst));
return (uint8_t *) (eth + 1);
}
size_t mg_l2_eth_footer(size_t len, uint8_t *frame) {
struct eth *eth = (struct eth *) frame;
// nothing to do; there is no len field in Ethernet, CRC is hw-calculated
return len + sizeof(*eth);
}
struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype addrtype,
struct mg_addr *addr);
bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw) {
struct eth *eth = (struct eth *) raw->buf;
uint16_t type, len;
unsigned int i;
if (raw->len < sizeof(*eth)) return false; // Truncated - runt?
len = (uint16_t) raw->len;
if (ifp->enable_mac_check &&
memcmp(eth->dst, ifp->mac, sizeof(eth->dst)) != 0 &&
memcmp(eth->dst, mg_l2_eth_mapip(MG_TCPIP_L2ADDR_BCAST, NULL),
sizeof(eth->dst)) != 0)
return false; // TODO(): add multicast addresses
if (ifp->enable_crc32_check && len > sizeof(*eth) + 4) {
uint32_t crc;
len -= 4; // TODO(scaprile): check on bigendian
crc = mg_crc32(0, (const char *) raw->buf, len);
if (memcmp((void *) ((size_t) raw->buf + len), &crc, sizeof(crc)))
return false;
}
pay->buf = (char *) (eth + 1);
pay->len = len - sizeof(*eth);
type = mg_htons(eth->type);
for (i = 0; i < sizeof(eth_types) / sizeof(uint16_t); i++) {
if (type == eth_types[i]) break;
}
if (i == sizeof(eth_types)) {
MG_DEBUG(("Unknown eth type %x", type));
if (mg_log_level >= MG_LL_VERBOSE)
mg_hexdump(raw->buf, raw->len >= 32 ? 32 : raw->len);
return false;
}
*proto = (enum mg_l2proto) i;
return true;
}
struct mg_l2addr *mg_l2_eth_getaddr(uint8_t *frame) {
struct eth *eth = (struct eth *) frame;
return (struct mg_l2addr *) &eth->src;
}
extern struct mg_l2addr s_mapip;
struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype addrtype,
struct mg_addr *addr) {
switch (addrtype) {
case MG_TCPIP_L2ADDR_BCAST:
memset(s_mapip.addr.mac, 0xff, sizeof(s_mapip.addr.mac));
break;
case MG_TCPIP_L2ADDR_MCAST: {
uint8_t *ip = (uint8_t *) &addr->addr.ip4;
// IP multicast group MAC, RFC-1112 6.4
s_mapip.addr.mac[0] = 0x01, s_mapip.addr.mac[1] = 0x00,
s_mapip.addr.mac[2] = 0x5E;
s_mapip.addr.mac[3] = ip[1] & 0x7F; // 23 LSb
s_mapip.addr.mac[4] = ip[2];
s_mapip.addr.mac[5] = ip[3];
break;
}
case MG_TCPIP_L2ADDR_MCAST6: {
// IPv6 multicast address mapping, RFC-2464 7
uint8_t *ip = (uint8_t *) &addr->addr.ip6;
s_mapip.addr.mac[0] = 0x33, s_mapip.addr.mac[1] = 0x33;
s_mapip.addr.mac[2] = ip[12], s_mapip.addr.mac[3] = ip[13],
s_mapip.addr.mac[4] = ip[14], s_mapip.addr.mac[5] = ip[15];
break;
}
}
return &s_mapip;
}
#if MG_ENABLE_IPV6
static void meui64(uint8_t *addr, uint8_t *mac) {
*addr++ = *mac++ ^ (uint8_t) 0x02, *addr++ = *mac++, *addr++ = *mac++;
*addr++ = 0xff, *addr++ = 0xfe;
*addr++ = *mac++, *addr++ = *mac++, *addr = *mac;
}
bool mg_l2_eth_genip6(uint64_t *ip6, uint8_t prefix_len,
struct mg_l2addr *l2addr) {
if (prefix_len > 64) {
MG_ERROR(("Prefix length > 64, UNSUPPORTED"));
return false;
}
ip6[0] = 0;
meui64(((uint8_t *) &ip6[1]), l2addr->addr.mac); // RFC-4291 2.5.4, 2.5.1
return true;
}
bool mg_l2_eth_ip6get(struct mg_l2addr *l2addr, uint8_t *opts, uint8_t len) {
if (len != 1) return false;
memcpy(l2addr->addr.mac, opts, 6);
return true;
}
uint8_t mg_l2_eth_ip6put(struct mg_l2addr *l2addr, uint8_t *opts) {
memcpy(opts, l2addr->addr.mac, 6);
return 1;
}
#endif
#endif
+115
View File
@@ -0,0 +1,115 @@
#include "l2.h"
#include "log.h"
#include "net.h"
#include "net_builtin.h"
#include "printf.h"
#include "util.h"
#if MG_ENABLE_TCPIP
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct ppp { // RFC-1662
uint8_t addr, ctrl;
uint16_t proto;
};
struct lcp { // RFC-1661
uint8_t code, id, len[2];
};
struct ipcp { // RFC-1332
uint8_t code;
};
struct ipv6cp { // RFC-5072
uint8_t code;
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
void mg_l2_ppp_init(struct mg_l2addr *addr, uint16_t *mtu,
uint16_t *framesize) {
(void) addr;
*mtu = 1500; // 1492 for PPPoE
*framesize = 1540; // *** TODO(scaprile): actual value, check for PPPoE too
}
uint8_t *mg_l2_ppp_header(enum mg_l2proto proto, struct mg_l2addr *src,
struct mg_l2addr *dst, uint8_t *frame) {
(void) src;
(void) dst;
(void) proto;
return frame;
}
size_t mg_l2_ppp_footer(size_t len, uint8_t *frame) {
(void) frame;
return len;
}
bool mg_l2_ppp_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw) {
#if 0
if (ppp->addr == MG_PPP_ADDR && ppp->ctrl == MG_PPP_CTRL) {
code = ntohs(ppp->proto);
payload = (uint8_t *) (ppp + 1);
} else { // Address-and-Control-Field-Compressed PPP header
uint16_t *cppp = (uint16_t *) ppp;
code = ntohs(*cppp);
payload = (uint8_t *) (cppp + 1);
}
#endif
*pay = *raw;
*proto = MG_TCPIP_L2PROTO_IPV4;
(void) ifp;
return true;
}
struct mg_l2addr *mg_l2_ppp_getaddr(uint8_t *frame) {
(void) frame;
return &s_mapip; // bogus
}
extern struct mg_l2addr s_mapip;
struct mg_l2addr *mg_l2_ppp_mapip(enum mg_l2addrtype addrtype,
struct mg_addr *addr) {
(void) addrtype;
(void) addr;
return &s_mapip; // bogus
}
#if MG_ENABLE_IPV6
bool mg_l2_ppp_genip6(uint64_t *ip6, uint8_t prefix_len,
struct mg_l2addr *addr) {
(void) ip6;
(void) prefix_len;
(void) addr;
return false;
}
bool mg_l2_ppp_ip6get(struct mg_l2addr *addr, uint8_t *opts, uint8_t len) {
(void) addr;
(void) opts;
(void) len;
return false;
}
uint8_t mg_l2_ppp_ip6put(struct mg_l2addr *addr, uint8_t *opts) {
(void) addr;
(void) opts;
return 0;
}
#endif
#endif
+12 -6
View File
@@ -16,12 +16,18 @@ void mg_log_set_fn(mg_pfn_t fn, void *param);
#define mg_log_set(level_) mg_log_level = (level_) #define mg_log_set(level_) mg_log_level = (level_)
#if MG_ENABLE_LOG #if MG_ENABLE_LOG
#define MG_LOG(level, args) \ #if !defined(_MSC_VER) && \
do { \ (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
if ((level) <= mg_log_level) { \ #define MG___FUNC__ ""
mg_log_prefix((level), __FILE__, __LINE__, __func__); \ #else
mg_log args; \ #define MG___FUNC__ __func__ // introduced in C99
} \ #endif
#define MG_LOG(level, args) \
do { \
if ((level) <= mg_log_level) { \
mg_log_prefix((level), __FILE__, __LINE__, MG___FUNC__); \
mg_log args; \
} \
} while (0) } while (0)
#else #else
#define MG_LOG(level, args) \ #define MG_LOG(level, args) \
+40 -23
View File
@@ -1,8 +1,8 @@
#include "mqtt.h"
#include "arch.h" #include "arch.h"
#include "base64.h" #include "base64.h"
#include "event.h" #include "event.h"
#include "log.h" #include "log.h"
#include "mqtt.h"
#include "url.h" #include "url.h"
#include "util.h" #include "util.h"
@@ -137,17 +137,10 @@ static size_t get_properties_length(struct mg_mqtt_prop *props, size_t count) {
case MQTT_PROP_TYPE_VARIABLE_INT: case MQTT_PROP_TYPE_VARIABLE_INT:
size += varint_size((uint32_t) props[i].iv); size += varint_size((uint32_t) props[i].iv);
break; break;
case MQTT_PROP_TYPE_INT: case MQTT_PROP_TYPE_INT: size += (uint32_t) sizeof(uint32_t); break;
size += (uint32_t) sizeof(uint32_t); case MQTT_PROP_TYPE_SHORT: size += (uint32_t) sizeof(uint16_t); break;
break; case MQTT_PROP_TYPE_BYTE: size += (uint32_t) sizeof(uint8_t); break;
case MQTT_PROP_TYPE_SHORT: default: return size; // cannot parse further down
size += (uint32_t) sizeof(uint16_t);
break;
case MQTT_PROP_TYPE_BYTE:
size += (uint32_t) sizeof(uint8_t);
break;
default:
return size; // cannot parse further down
} }
} }
@@ -213,50 +206,64 @@ size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
uint8_t *i = (uint8_t *) msg->dgram.buf + msg->props_start + ofs; uint8_t *i = (uint8_t *) msg->dgram.buf + msg->props_start + ofs;
uint8_t *end = (uint8_t *) msg->dgram.buf + msg->dgram.len; uint8_t *end = (uint8_t *) msg->dgram.buf + msg->dgram.len;
size_t new_pos = ofs, len; size_t new_pos = ofs, len;
prop->id = i[0];
if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size) if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size || (i + 1) >= end)
return 0; return 0;
memset(prop, 0, sizeof(struct mg_mqtt_prop));
prop->id = i[0];
i++, new_pos++; i++, new_pos++;
switch (mqtt_prop_type_by_id(prop->id)) { switch (mqtt_prop_type_by_id(prop->id)) {
case MQTT_PROP_TYPE_STRING_PAIR: case MQTT_PROP_TYPE_STRING_PAIR:
if (i + 2 >= end) return 0;
prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
prop->key.buf = (char *) i + 2; prop->key.buf = (char *) i + 2;
i += 2 + prop->key.len; i += 2 + prop->key.len;
if (i + 2 >= end) return 0;
prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
prop->val.buf = (char *) i + 2; prop->val.buf = (char *) i + 2;
if (i + 2 + prop->val.len >= end) return 0;
new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len; new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len;
break; break;
case MQTT_PROP_TYPE_BYTE: case MQTT_PROP_TYPE_BYTE:
if (i + 1 >= end) return 0;
prop->iv = (uint8_t) i[0]; prop->iv = (uint8_t) i[0];
new_pos++; new_pos++;
break; break;
case MQTT_PROP_TYPE_SHORT: case MQTT_PROP_TYPE_SHORT:
if (i + 2 >= end) return 0;
prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
new_pos += sizeof(uint16_t); new_pos += sizeof(uint16_t);
break; break;
case MQTT_PROP_TYPE_INT: case MQTT_PROP_TYPE_INT:
if (i + 4 >= end) return 0;
prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) | prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) |
((uint32_t) i[2] << 8) | i[3]; ((uint32_t) i[2] << 8) | i[3];
new_pos += sizeof(uint32_t); new_pos += sizeof(uint32_t);
break; break;
case MQTT_PROP_TYPE_STRING: case MQTT_PROP_TYPE_STRING:
if (i + 2 >= end) return 0;
prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
prop->val.buf = (char *) i + 2; prop->val.buf = (char *) i + 2;
if (i + 2 + prop->val.len >= end) return 0;
new_pos += 2 + prop->val.len; new_pos += 2 + prop->val.len;
break; break;
case MQTT_PROP_TYPE_BINARY_DATA: case MQTT_PROP_TYPE_BINARY_DATA:
if (i + 2 >= end) return 0;
prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
prop->val.buf = (char *) i + 2; prop->val.buf = (char *) i + 2;
if (i + 2 + prop->val.len >= end) return 0;
new_pos += 2 + prop->val.len; new_pos += 2 + prop->val.len;
break; break;
case MQTT_PROP_TYPE_VARIABLE_INT: case MQTT_PROP_TYPE_VARIABLE_INT:
len = decode_varint(i, (size_t) (end - i), (size_t *) &prop->iv); len = decode_varint(i, (size_t) (end - i), (size_t *) &prop->iv);
new_pos = (!len) ? 0 : new_pos + len; if (i + len >= end) return 0;
new_pos = (len == 0) ? 0 : new_pos + len;
break; break;
default: default:
new_pos = 0; new_pos = 0;
break;
} }
return new_pos; return new_pos;
@@ -266,7 +273,8 @@ void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
char client_id[21]; char client_id[21];
struct mg_str cid = opts->client_id; struct mg_str cid = opts->client_id;
size_t total_len = 7 + 1 + 2 + 2; size_t total_len = 7 + 1 + 2 + 2;
uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0}; uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', 0, 0};
hdr[6] = opts->version;
if (cid.len == 0) { if (cid.len == 0) {
mg_random_str(client_id, sizeof(client_id) - 1); mg_random_str(client_id, sizeof(client_id) - 1);
@@ -372,12 +380,14 @@ fail:
return id; return id;
} }
void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { static void mg_mqtt_sub_unsub(struct mg_connection *c,
const struct mg_mqtt_opts *opts, uint8_t cmd) {
uint8_t qos_ = opts->qos & 3; uint8_t qos_ = opts->qos & 3;
bool is_sub = cmd == MQTT_CMD_SUBSCRIBE;
size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0; size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0;
size_t len = 2 + opts->topic.len + 2 + 1 + plen; size_t len = 2 + opts->topic.len + 2 + (is_sub ? 1 : 0) + plen;
if (!mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len)) goto fail; if (!mqtt_send_header(c, cmd, 2, (uint32_t) len)) goto fail;
if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
if (!mg_send_u16(c, mg_htons(c->mgr->mqtt_id))) goto fail; if (!mg_send_u16(c, mg_htons(c->mgr->mqtt_id))) goto fail;
@@ -385,14 +395,22 @@ void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
goto fail; goto fail;
if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) || if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) ||
!mg_send(c, opts->topic.buf, opts->topic.len) || !mg_send(c, opts->topic.buf, opts->topic.len))
!mg_send(c, &qos_, sizeof(qos_)))
goto fail; goto fail;
if (is_sub && !mg_send(c, &qos_, sizeof(qos_))) goto fail;
return; return;
fail: fail:
mg_error(c, "OOM"); mg_error(c, "OOM");
} }
void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
mg_mqtt_sub_unsub(c, opts, MQTT_CMD_SUBSCRIBE);
}
void mg_mqtt_unsub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
mg_mqtt_sub_unsub(c, opts, MQTT_CMD_UNSUBSCRIBE);
}
int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version, int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
struct mg_mqtt_message *m) { struct mg_mqtt_message *m) {
uint8_t lc = 0, *p, *end; uint8_t lc = 0, *p, *end;
@@ -458,8 +476,7 @@ int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
m->data.len = (size_t) (end - p); m->data.len = (size_t) (end - p);
break; break;
} }
default: default: break;
break;
} }
return MQTT_OK; return MQTT_OK;
} }
+1
View File
@@ -104,6 +104,7 @@ struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts); void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts); uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts); void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts);
void mg_mqtt_unsub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *); int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *);
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags, void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
uint32_t len); uint32_t len);
+35 -16
View File
@@ -31,14 +31,14 @@ size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
uint32_t localhost = mg_htonl(0x7f000001); uint32_t localhost = mg_htonl(0x7f000001);
if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false; if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false;
memcpy(addr->ip, &localhost, sizeof(uint32_t)); memcpy(addr->addr.ip, &localhost, sizeof(uint32_t));
addr->is_ip6 = false; addr->is_ip6 = false;
return true; return true;
} }
static bool mg_atone(struct mg_str str, struct mg_addr *addr) { static bool mg_atone(struct mg_str str, struct mg_addr *addr) {
if (str.len > 0) return false; if (str.len > 0) return false;
memset(addr->ip, 0, sizeof(addr->ip)); memset(addr->addr.ip, 0, sizeof(addr->addr.ip));
addr->is_ip6 = false; addr->is_ip6 = false;
return true; return true;
} }
@@ -59,7 +59,7 @@ static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
} }
} }
if (num_dots != 3 || str.buf[i - 1] == '.') return false; if (num_dots != 3 || str.buf[i - 1] == '.') return false;
memcpy(&addr->ip, data, sizeof(data)); memcpy(&addr->addr.ip, data, sizeof(data));
addr->is_ip6 = false; addr->is_ip6 = false;
return true; return true;
} }
@@ -74,10 +74,10 @@ static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) {
} }
// struct mg_str s = mg_str_n(&str.buf[7], str.len - 7); // struct mg_str s = mg_str_n(&str.buf[7], str.len - 7);
if (!mg_aton4(mg_str_n(&str.buf[7], str.len - 7), addr)) return false; if (!mg_aton4(mg_str_n(&str.buf[7], str.len - 7), addr)) return false;
memcpy(&ipv4, addr->ip, sizeof(ipv4)); memcpy(&ipv4, addr->addr.ip, sizeof(ipv4));
memset(addr->ip, 0, sizeof(addr->ip)); memset(addr->addr.ip, 0, sizeof(addr->addr.ip));
addr->ip[10] = addr->ip[11] = 255; addr->addr.ip[10] = addr->addr.ip[11] = 255;
memcpy(&addr->ip[12], &ipv4, 4); memcpy(&addr->addr.ip[12], &ipv4, 4);
addr->is_ip6 = true; addr->is_ip6 = true;
return true; return true;
} }
@@ -86,7 +86,7 @@ static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
size_t i, j = 0, n = 0, dc = 42; size_t i, j = 0, n = 0, dc = 42;
addr->scope_id = 0; addr->scope_id = 0;
if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2; if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2;
if (mg_v4mapped(str, addr)) return true; if (mg_v4mapped(str, addr)) return true; // sets addr->is_ip6
for (i = 0; i < str.len; i++) { for (i = 0; i < str.len; i++) {
if ((str.buf[i] >= '0' && str.buf[i] <= '9') || if ((str.buf[i] >= '0' && str.buf[i] <= '9') ||
(str.buf[i] >= 'a' && str.buf[i] <= 'f') || (str.buf[i] >= 'a' && str.buf[i] <= 'f') ||
@@ -95,8 +95,8 @@ static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
if (i > j + 3) return false; if (i > j + 3) return false;
// MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.buf[j])); // MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.buf[j]));
mg_str_to_num(mg_str_n(&str.buf[j], i - j + 1), 16, &val, sizeof(val)); mg_str_to_num(mg_str_n(&str.buf[j], i - j + 1), 16, &val, sizeof(val));
addr->ip[n] = (uint8_t) ((val >> 8) & 255); addr->addr.ip[n] = (uint8_t) ((val >> 8) & 255);
addr->ip[n + 1] = (uint8_t) (val & 255); addr->addr.ip[n + 1] = (uint8_t) (val & 255);
} else if (str.buf[i] == ':') { } else if (str.buf[i] == ':') {
j = i + 1; j = i + 1;
if (i > 0 && str.buf[i - 1] == ':') { if (i > 0 && str.buf[i - 1] == ':') {
@@ -106,18 +106,23 @@ static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
n += 2; n += 2;
} }
if (n > 14) return false; if (n > 14) return false;
addr->ip[n] = addr->ip[n + 1] = 0; // For trailing :: addr->addr.ip[n] = addr->addr.ip[n + 1] = 0; // For trailing ::
} else if (str.buf[i] == '%') { // Scope ID, last in string } else if (str.buf[i] == '%') { // Scope ID, last in string
return mg_str_to_num(mg_str_n(&str.buf[i + 1], str.len - i - 1), 10, if (mg_str_to_num(mg_str_n(&str.buf[i + 1], str.len - i - 1), 10,
&addr->scope_id, sizeof(uint8_t)); &addr->scope_id, sizeof(uint8_t))) {
addr->is_ip6 = true;
return true;
} else {
return false;
}
} else { } else {
return false; return false;
} }
} }
if (n < 14 && dc == 42) return false; if (n < 14 && dc == 42) return false;
if (n < 14) { if (n < 14) {
memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2); memmove(&addr->addr.ip[dc + (14 - n)], &addr->addr.ip[dc], n - dc + 2);
memset(&addr->ip[dc], 0, 14 - n); memset(&addr->addr.ip[dc], 0, 14 - n);
} }
addr->is_ip6 = true; addr->is_ip6 = true;
@@ -168,6 +173,10 @@ struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
struct mg_connection *c = NULL; struct mg_connection *c = NULL;
if (url == NULL || url[0] == '\0') { if (url == NULL || url[0] == '\0') {
MG_ERROR(("null url")); MG_ERROR(("null url"));
#if MG_ENABLE_TCPIP
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY) {
MG_ERROR(("Network is down"));
#endif
} else if ((c = mg_alloc_conn(mgr)) == NULL) { } else if ((c = mg_alloc_conn(mgr)) == NULL) {
MG_ERROR(("OOM")); MG_ERROR(("OOM"));
} else { } else {
@@ -301,3 +310,13 @@ void mg_mgr_init(struct mg_mgr *mgr) {
: MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL" : MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL"
: "custom")); : "custom"));
} }
#if MG_ENABLE_TCPIP
void mg_tcpip_mapip(struct mg_connection *, struct mg_addr *);
#endif
void mg_multicast_restore(struct mg_connection *c, uint8_t *from) {
memcpy(&c->rem, from, sizeof(c->rem));
#if MG_ENABLE_TCPIP
mg_tcpip_mapip(c, &c->rem);
#endif
}
+3 -2
View File
@@ -14,11 +14,11 @@ struct mg_dns {
}; };
struct mg_addr { struct mg_addr {
union { // Holds IPv4 or IPv6 address, in network byte order union { // Holds IPv4 or IPv6 address, in network byte order
uint8_t ip[16]; uint8_t ip[16];
uint32_t ip4; uint32_t ip4;
uint64_t ip6[2]; uint64_t ip6[2];
}; } addr;
uint16_t port; // TCP or UDP port in network byte order uint16_t port; // TCP or UDP port in network byte order
uint8_t scope_id; // IPv6 scope ID uint8_t scope_id; // IPv6 scope ID
bool is_ip6; // True when address is IPv6 address bool is_ip6; // True when address is IPv6 address
@@ -112,3 +112,4 @@ struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url, struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data, mg_event_handler_t fn, void *fn_data,
mg_event_handler_t pfn, void *pfn_data); mg_event_handler_t pfn, void *pfn_data);
void mg_multicast_restore(struct mg_connection *c, uint8_t *from);
+617 -449
View File
File diff suppressed because it is too large. Load diff
+49 -40
View File
@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "arch.h" #include "arch.h"
#include "l2.h"
#include "net.h" #include "net.h"
#include "queue.h" #include "queue.h"
#include "str.h" #include "str.h"
@@ -20,29 +21,32 @@ typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
void *ev_data); void *ev_data);
enum { enum {
MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state) MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state)
MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr
MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr
MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str * MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str *
MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL
MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi scan results struct mg_wifi_scan_bss_data * MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi scan results struct
MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL // mg_wifi_scan_bss_data *
MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and chip specific MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL
MG_TCPIP_EV_DRIVER, // Driver event driver specific MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and
MG_TCPIP_EV_ST6_CHG, // state6 change uint8_t * (&ifp->state6) // chip specific
MG_TCPIP_EV_USER // Starting ID for user events MG_TCPIP_EV_DRIVER, // Driver event driver specific
MG_TCPIP_EV_ST6_CHG, // state6 change uint8_t *
// (&ifp->state6)
MG_TCPIP_EV_USER // Starting ID for user events
}; };
// Network interface // Network interface
struct mg_tcpip_if { struct mg_tcpip_if {
uint8_t mac[6]; // MAC address. Must be set to a valid MAC uint8_t mac[sizeof(struct mg_l2addr)]; // hw address. Set to a valid addr
uint32_t ip, mask, gw; // IP address, mask, default gateway uint32_t ip, mask, gw; // IP address, mask, default gateway
struct mg_str tx; // Output (TX) buffer struct mg_str tx; // Output (TX) buffer
bool enable_dhcp_client; // Enable DCHP client bool enable_dhcp_client; // Enable DCHP client
bool enable_dhcp_server; // Enable DCHP server bool enable_dhcp_server; // Enable DCHP server
bool enable_get_gateway; // DCHP server sets client as gateway bool enable_get_gateway; // DCHP server sets client as gateway
bool enable_req_dns; // DCHP client requests DNS server bool enable_req_dns; // DCHP client requests DNS server
bool enable_req_sntp; // DCHP client requests SNTP server bool enable_req_sntp; // DCHP client requests SNTP server
bool enable_crc32_check; // Do a CRC check on RX frames and strip it bool enable_crc32_check; // Do a CRC check on RX frames and strip it
bool enable_mac_check; // Do a MAC check on RX frames bool enable_mac_check; // Do a MAC check on RX frames
bool update_mac_hash_table; // Signal drivers to update MAC controller bool update_mac_hash_table; // Signal drivers to update MAC controller
@@ -53,20 +57,22 @@ struct mg_tcpip_if {
struct mg_mgr *mgr; // Mongoose event manager struct mg_mgr *mgr; // Mongoose event manager
struct mg_queue recv_queue; // Receive queue struct mg_queue recv_queue; // Receive queue
char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Name for DHCP, "mip" if unset char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Name for DHCP, "mip" if unset
uint16_t mtu; // Interface MTU uint16_t mtu; // Interface link payload
#define MG_TCPIP_MTU_DEFAULT 1500 uint16_t framesize; // Interface frame max length
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses,
uint8_t prefix_len; // Prefix length uint8_t prefix[8]; // prefix,
uint64_t gw6[2]; // Default gateway uint8_t prefix_len; // prefix length,
bool enable_slaac; // Enable IPv6 address autoconfiguration uint64_t gw6[2]; // default gateway.
bool enable_dhcp6_client; // Enable DCHPv6 client bool enable_slaac; // Enable IPv6 address autoconfiguration
bool enable_dhcp6_client; // Enable DCHPv6 client TODO()
#endif #endif
// Internal state, user can use it but should not change it // Internal state, user can use it but should not change it
uint8_t gwmac[6]; // Router's MAC uint8_t gwmac[sizeof(struct mg_l2addr)]; // Router's hw address
char *dns4_url; // DNS server URL enum mg_l2type l2type; // Ethernet, PPP, etc.
uint64_t now; // Current time char *dns4_url; // DNS server URL
uint64_t now; // Current time
uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
uint64_t lease_expire; // Lease expiration time, in ms uint64_t lease_expire; // Lease expiration time, in ms
uint16_t eport; // Next ephemeral port uint16_t eport; // Next ephemeral port
@@ -80,11 +86,14 @@ struct mg_tcpip_if {
#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state #define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state
#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned #define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned
#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work #define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work
bool gw_ready; // We've got a hw address for the router
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
uint8_t gw6mac[6]; // IPv6 Router's MAC uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPV6 Router's hw address
uint8_t state6; // Current IPv6 state uint8_t state6; // Current IPv6 state
bool gw6_ready; // We've got a hw address for the IPv6 router
#endif #endif
}; };
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *); void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
void mg_tcpip_free(struct mg_tcpip_if *); void mg_tcpip_free(struct mg_tcpip_if *);
void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp); void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp);
@@ -107,6 +116,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
extern struct mg_tcpip_driver mg_tcpip_driver_rw612; extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
extern struct mg_tcpip_driver mg_tcpip_driver_cyw; extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi; extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
// Drivers that require SPI, can use this SPI abstraction // Drivers that require SPI, can use this SPI abstraction
struct mg_tcpip_spi { struct mg_tcpip_spi {
@@ -116,39 +126,38 @@ struct mg_tcpip_spi {
uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply
}; };
// Alignment and memory section requirements // Alignment and memory section requirements
#ifndef MG_8BYTE_ALIGNED #ifndef MG_8BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_8BYTE_ALIGNED __attribute__((aligned((8U)))) #define MG_8BYTE_ALIGNED __attribute__((aligned((8U))))
#else #else
#define MG_8BYTE_ALIGNED #define MG_8BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 8BYTE_ALIGNED #endif // 8BYTE_ALIGNED
#ifndef MG_16BYTE_ALIGNED #ifndef MG_16BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_16BYTE_ALIGNED __attribute__((aligned((16U)))) #define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
#else #else
#define MG_16BYTE_ALIGNED #define MG_16BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 16BYTE_ALIGNED #endif // 16BYTE_ALIGNED
#ifndef MG_32BYTE_ALIGNED #ifndef MG_32BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_32BYTE_ALIGNED __attribute__((aligned((32U)))) #define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
#else #else
#define MG_32BYTE_ALIGNED #define MG_32BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 32BYTE_ALIGNED #endif // 32BYTE_ALIGNED
#ifndef MG_64BYTE_ALIGNED #ifndef MG_64BYTE_ALIGNED
#if defined(__GNUC__) #if defined(__GNUC__)
#define MG_64BYTE_ALIGNED __attribute__((aligned((64U)))) #define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
#else #else
#define MG_64BYTE_ALIGNED #define MG_64BYTE_ALIGNED
#endif // compiler #endif // compiler
#endif // 64BYTE_ALIGNED #endif // 64BYTE_ALIGNED
#ifndef MG_ETH_RAM #ifndef MG_ETH_RAM
#define MG_ETH_RAM #define MG_ETH_RAM
+2 -2
View File
@@ -17,8 +17,8 @@
#define MG_OTA_RT1060 302 // IMXRT1060 #define MG_OTA_RT1060 302 // IMXRT1060
#define MG_OTA_RT1064 303 // IMXRT1064 #define MG_OTA_RT1064 303 // IMXRT1064
#define MG_OTA_RT1170 304 // IMXRT1170 #define MG_OTA_RT1170 304 // IMXRT1170
#define MG_OTA_MCXN 310 // MCXN947 #define MG_OTA_MCXN 310 // MCXN947
#define MG_OTA_FRDM 320 // FRDM-RW612 #define MG_OTA_RW612 320 // FRDM-RW612
#define MG_OTA_FLASH 900 // OTA via an internal flash #define MG_OTA_FLASH 900 // OTA via an internal flash
#define MG_OTA_ESP32 910 // ESP32 OTA implementation #define MG_OTA_ESP32 910 // ESP32 OTA implementation
#define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash #define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash
+1 -1
View File
@@ -2,7 +2,7 @@
#include "log.h" #include "log.h"
#include "ota.h" #include "ota.h"
#if MG_OTA == MG_OTA_FRDM #if MG_OTA == MG_OTA_RW612
MG_IRAM static bool mg_frdm_write(void *, const void *, size_t); MG_IRAM static bool mg_frdm_write(void *, const void *, size_t);
static bool mg_frdm_swap(void); static bool mg_frdm_swap(void);
+27 -9
View File
@@ -1,5 +1,5 @@
#include "printf.h"
#include "fmt.h" #include "fmt.h"
#include "printf.h"
#include "util.h" #include "util.h"
size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) { size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) {
@@ -29,7 +29,7 @@ static void mg_pfn_iobuf_private(char ch, void *param, bool expand) {
} }
} }
static void mg_putchar_iobuf_static(char ch, void *param) { void mg_pfn_iobuf_noresize(char ch, void *param) {
mg_pfn_iobuf_private(ch, param, false); mg_pfn_iobuf_private(ch, param, false);
} }
@@ -38,8 +38,10 @@ void mg_pfn_iobuf(char ch, void *param) {
} }
size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) { size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) {
struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0}; struct mg_iobuf io = {0, 0, 0, 0};
size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap); size_t n;
io.buf = (uint8_t *) buf, io.size = len;
n = mg_vxprintf(mg_pfn_iobuf_noresize, &io, fmt, ap);
if (n < len) buf[n] = '\0'; if (n < len) buf[n] = '\0';
return n; return n;
} }
@@ -96,8 +98,8 @@ size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap) {
size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) { size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) {
struct mg_addr *addr = va_arg(*ap, struct mg_addr *); struct mg_addr *addr = va_arg(*ap, struct mg_addr *);
if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->ip); if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->addr.ip);
return print_ip4(out, arg, (uint8_t *) &addr->ip); return print_ip4(out, arg, (uint8_t *) &addr->addr.ip);
} }
size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) { size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) {
@@ -105,12 +107,27 @@ size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) {
return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port)); return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port));
} }
size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) { static size_t print_mac(void (*out)(char, void *), void *arg, uint8_t *p) {
uint8_t *p = va_arg(*ap, uint8_t *);
return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2],
p[3], p[4], p[5]); p[3], p[4], p[5]);
} }
size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) {
uint8_t *p = va_arg(*ap, uint8_t *);
return print_mac(out, arg, p);
}
#if MG_ENABLE_TCPIP
size_t mg_print_l2addr(void (*out)(char, void *), void *arg, va_list *ap) {
enum mg_l2type type = (enum mg_l2type) va_arg(*ap, int);
if (type == MG_TCPIP_L2_ETH) {
uint8_t *p = va_arg(*ap, uint8_t *);
return print_mac(out, arg, p);
}
return 0;
}
#endif
static char mg_esc(int c, bool esc) { static char mg_esc(int c, bool esc) {
const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\""; const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
for (p = esc ? esc1 : esc2; *p != '\0'; p++) { for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
@@ -145,7 +162,8 @@ static size_t bcpy(void (*out)(char, void *), void *arg, uint8_t *buf,
for (i = 0; i < len; i += 3) { for (i = 0; i < len; i += 3) {
uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0, uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0,
c3 = i + 2 < len ? buf[i + 2] : 0; c3 = i + 2 < len ? buf[i + 2] : 0;
char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='}; char tmp[4] = {0, 0, '=', '='};
tmp[0] = t[c1 >> 2], tmp[1] = t[(c1 & 3) << 4 | (c2 >> 4)];
if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)]; if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)];
if (i + 2 < len) tmp[3] = t[c3 & 63]; if (i + 2 < len) tmp[3] = t[c3 & 63];
for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg); for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg);
+4 -2
View File
@@ -20,10 +20,12 @@ size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap); size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap); size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap); size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap);
size_t mg_print_l2addr(void (*out)(char, void *), void *arg, va_list *ap);
// Various output functions // Various output functions
void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf *
void mg_pfn_stdout(char c, void *param); // param: ignored void mg_pfn_iobuf_noresize(char ch, void *param); // param: struct mg_iobuf *
void mg_pfn_stdout(char c, void *param); // param: ignored
// A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi")) // A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi"))
#define MG_ESC(str) mg_print_esc, 0, (str) #define MG_ESC(str) mg_print_esc, 0, (str)
+43 -1
View File
@@ -180,6 +180,35 @@ void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
#define sig164(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ ((x) >> 6)) #define sig164(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ ((x) >> 6))
static const uint64_t mg_sha256_k2[80] = { static const uint64_t mg_sha256_k2[80] = {
#if defined(__DCC__)
0x428a2f98d728ae22ull, 0x7137449123ef65cdull, 0xb5c0fbcfec4d3b2full,
0xe9b5dba58189dbbcull, 0x3956c25bf348b538ull, 0x59f111f1b605d019ull,
0x923f82a4af194f9bull, 0xab1c5ed5da6d8118ull, 0xd807aa98a3030242ull,
0x12835b0145706fbeull, 0x243185be4ee4b28cull, 0x550c7dc3d5ffb4e2ull,
0x72be5d74f27b896full, 0x80deb1fe3b1696b1ull, 0x9bdc06a725c71235ull,
0xc19bf174cf692694ull, 0xe49b69c19ef14ad2ull, 0xefbe4786384f25e3ull,
0x0fc19dc68b8cd5b5ull, 0x240ca1cc77ac9c65ull, 0x2de92c6f592b0275ull,
0x4a7484aa6ea6e483ull, 0x5cb0a9dcbd41fbd4ull, 0x76f988da831153b5ull,
0x983e5152ee66dfabull, 0xa831c66d2db43210ull, 0xb00327c898fb213full,
0xbf597fc7beef0ee4ull, 0xc6e00bf33da88fc2ull, 0xd5a79147930aa725ull,
0x06ca6351e003826full, 0x142929670a0e6e70ull, 0x27b70a8546d22ffcull,
0x2e1b21385c26c926ull, 0x4d2c6dfc5ac42aedull, 0x53380d139d95b3dfull,
0x650a73548baf63deull, 0x766a0abb3c77b2a8ull, 0x81c2c92e47edaee6ull,
0x92722c851482353bull, 0xa2bfe8a14cf10364ull, 0xa81a664bbc423001ull,
0xc24b8b70d0f89791ull, 0xc76c51a30654be30ull, 0xd192e819d6ef5218ull,
0xd69906245565a910ull, 0xf40e35855771202aull, 0x106aa07032bbd1b8ull,
0x19a4c116b8d2d0c8ull, 0x1e376c085141ab53ull, 0x2748774cdf8eeb99ull,
0x34b0bcb5e19b48a8ull, 0x391c0cb3c5c95a63ull, 0x4ed8aa4ae3418acbull,
0x5b9cca4f7763e373ull, 0x682e6ff3d6b2b8a3ull, 0x748f82ee5defb2fcull,
0x78a5636f43172f60ull, 0x84c87814a1f0ab72ull, 0x8cc702081a6439ecull,
0x90befffa23631e28ull, 0xa4506cebde82bde9ull, 0xbef9a3f7b2c67915ull,
0xc67178f2e372532bull, 0xca273eceea26619cull, 0xd186b8c721c0c207ull,
0xeada7dd6cde0eb1eull, 0xf57d4f7fee6ed178ull, 0x06f067aa72176fbaull,
0x0a637dc5a2c898a6ull, 0x113f9804bef90daeull, 0x1b710b35131c471bull,
0x28db77f523047d84ull, 0x32caab7b40c72493ull, 0x3c9ebe0a15c9bebcull,
0x431d67c49c100d4cull, 0x4cc5d4becb3e42b6ull, 0x597f299cfc657e2aull,
0x5fcb6fab3ad6faecull, 0x6c44198c4a475817ull
#else
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f,
0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019, 0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019,
0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242,
@@ -206,7 +235,9 @@ static const uint64_t mg_sha256_k2[80] = {
0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a,
0x5fcb6fab3ad6faec, 0x6c44198c4a475817}; 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
#endif
};
static void mg_sha384_transform(mg_sha384_ctx *ctx, const uint8_t data[]) { static void mg_sha384_transform(mg_sha384_ctx *ctx, const uint8_t data[]) {
uint64_t m[80]; uint64_t m[80];
@@ -257,6 +288,16 @@ void mg_sha384_init(mg_sha384_ctx *ctx) {
ctx->datalen = 0; ctx->datalen = 0;
ctx->bitlen[0] = 0; ctx->bitlen[0] = 0;
ctx->bitlen[1] = 0; ctx->bitlen[1] = 0;
#if defined(__DCC__)
ctx->state[0] = 0xcbbb9d5dc1059ed8ull;
ctx->state[1] = 0x629a292a367cd507ull;
ctx->state[2] = 0x9159015a3070dd17ull;
ctx->state[3] = 0x152fecd8f70e5939ull;
ctx->state[4] = 0x67332667ffc00b31ull;
ctx->state[5] = 0x8eb44a8768581511ull;
ctx->state[6] = 0xdb0c2e0d64f98fa7ull;
ctx->state[7] = 0x47b5481dbefa4fa4ull;
#else
ctx->state[0] = 0xcbbb9d5dc1059ed8; ctx->state[0] = 0xcbbb9d5dc1059ed8;
ctx->state[1] = 0x629a292a367cd507; ctx->state[1] = 0x629a292a367cd507;
ctx->state[2] = 0x9159015a3070dd17; ctx->state[2] = 0x9159015a3070dd17;
@@ -265,6 +306,7 @@ void mg_sha384_init(mg_sha384_ctx *ctx) {
ctx->state[5] = 0x8eb44a8768581511; ctx->state[5] = 0x8eb44a8768581511;
ctx->state[6] = 0xdb0c2e0d64f98fa7; ctx->state[6] = 0xdb0c2e0d64f98fa7;
ctx->state[7] = 0x47b5481dbefa4fa4; ctx->state[7] = 0x47b5481dbefa4fa4;
#endif
} }
void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len) { void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len) {
+32 -7
View File
@@ -57,13 +57,13 @@ static socklen_t tousa(struct mg_addr *a, union usa *usa) {
memset(usa, 0, sizeof(*usa)); memset(usa, 0, sizeof(*usa));
usa->sin.sin_family = AF_INET; usa->sin.sin_family = AF_INET;
usa->sin.sin_port = a->port; usa->sin.sin_port = a->port;
memcpy(&usa->sin.sin_addr, a->ip, sizeof(uint32_t)); memcpy(&usa->sin.sin_addr, a->addr.ip, sizeof(uint32_t));
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (a->is_ip6) { if (a->is_ip6) {
usa->sin.sin_family = AF_INET6; usa->sin.sin_family = AF_INET6;
usa->sin6.sin6_port = a->port; usa->sin6.sin6_port = a->port;
usa->sin6.sin6_scope_id = a->scope_id; usa->sin6.sin6_scope_id = a->scope_id;
memcpy(&usa->sin6.sin6_addr, a->ip, sizeof(a->ip)); memcpy(&usa->sin6.sin6_addr, a->addr.ip, sizeof(a->addr.ip));
len = sizeof(usa->sin6); len = sizeof(usa->sin6);
} }
#endif #endif
@@ -72,15 +72,17 @@ static socklen_t tousa(struct mg_addr *a, union usa *usa) {
static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) { static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) {
a->is_ip6 = is_ip6; a->is_ip6 = is_ip6;
a->port = usa->sin.sin_port;
memcpy(&a->ip, &usa->sin.sin_addr, sizeof(uint32_t));
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (is_ip6) { if (is_ip6) {
memcpy(a->ip, &usa->sin6.sin6_addr, sizeof(a->ip)); memcpy(a->addr.ip, &usa->sin6.sin6_addr, sizeof(a->addr.ip));
a->port = usa->sin6.sin6_port; a->port = usa->sin6.sin6_port;
a->scope_id = (uint8_t) usa->sin6.sin6_scope_id; a->scope_id = (uint8_t) usa->sin6.sin6_scope_id;
} } else
#endif #endif
{
a->port = usa->sin.sin_port;
memcpy(&a->addr.ip, &usa->sin.sin_addr, sizeof(uint32_t));
}
} }
static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) { static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) {
@@ -91,6 +93,29 @@ static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) {
} }
} }
// Get the local 'addr' the stack will use to connect to 'to'
void mg_getlocaddr(struct mg_connection *c, struct mg_addr *to, struct mg_addr *addr);
void mg_getlocaddr(struct mg_connection *c, struct mg_addr *to, struct mg_addr *addr) {
union usa usa;
socklen_t slen;
MG_SOCKET_TYPE fd;
int rc, af = to->is_ip6 ? AF_INET6 : AF_INET;
fd = socket(af, SOCK_DGRAM, IPPROTO_UDP);
if (fd == MG_INVALID_SOCKET) {
mg_error(c, "socket(): %d", MG_SOCK_ERR(-1));
return;
}
// NOTE(): TI-RTOS NDK may require binding
slen = tousa(to, &usa);
if ((rc = connect(fd, &usa.sa, slen)) != 0) {
mg_error(c, "connect: %d", MG_SOCK_ERR(rc));
return;
}
setlocaddr(fd, addr);
closesocket(fd);
}
static void iolog(struct mg_connection *c, char *buf, long n, bool r) { static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
if (n == MG_IO_WAIT) { if (n == MG_IO_WAIT) {
// Do nothing // Do nothing
@@ -475,7 +500,7 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
setsockopts(c); setsockopts(c);
c->is_accepted = 1; c->is_accepted = 1;
c->is_hexdumping = lsn->is_hexdumping; c->is_hexdumping = lsn->is_hexdumping;
c->loc = lsn->loc; setlocaddr(fd, &c->loc); // set local addr to where the client connected to
c->pfn = lsn->pfn; c->pfn = lsn->pfn;
c->pfn_data = lsn->pfn_data; c->pfn_data = lsn->pfn_data;
c->fn = lsn->fn; c->fn = lsn->fn;
+8 -5
View File
@@ -2,12 +2,14 @@
#include "util.h" #include "util.h"
struct mg_str mg_str_s(const char *s) { struct mg_str mg_str_s(const char *s) {
struct mg_str str = {(char *) s, s == NULL ? 0 : strlen(s)}; struct mg_str str;
str.buf = (char *) s, str.len = (s == NULL) ? 0 : strlen(s);
return str; return str;
} }
struct mg_str mg_str_n(const char *s, size_t n) { struct mg_str mg_str_n(const char *s, size_t n) {
struct mg_str str = {(char *) s, n}; struct mg_str str;
str.buf = (char *) s, str.len = n;
return str; return str;
} }
@@ -85,7 +87,9 @@ bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
} else if (i < p.len && (p.buf[i] == '*' || p.buf[i] == '#')) { } else if (i < p.len && (p.buf[i] == '*' || p.buf[i] == '#')) {
if (caps && !caps->buf) caps->len = 0, caps->buf = &s.buf[j]; // Init cap if (caps && !caps->buf) caps->len = 0, caps->buf = &s.buf[j]; // Init cap
ni = i++, nj = j + 1; ni = i++, nj = j + 1;
} else if (nj > 0 && nj <= s.len && (p.buf[ni] == '#' || s.buf[j] != '/')) { } else if (nj > 0 && nj <= s.len &&
((ni < p.len && p.buf[ni] == '#') ||
(j < s.len && s.buf[j] != '/'))) {
i = ni, j = nj; i = ni, j = nj;
if (caps && caps->buf == NULL && caps->len == 0) { if (caps && caps->buf == NULL && caps->len == 0) {
caps--, caps->len = 0; // Restart previous cap caps--, caps->len = 0; // Restart previous cap
@@ -166,8 +170,7 @@ bool mg_str_to_num(struct mg_str str, int base, void *val, size_t val_len) {
i++, ndigits++; i++, ndigits++;
} }
break; break;
default: default: return false;
return false;
} }
if (ndigits == 0) return false; if (ndigits == 0) return false;
if (i != str.len) return false; if (i != str.len) return false;
+1
View File
@@ -34,6 +34,7 @@ void mg_tls_handshake(struct mg_connection *);
// Private // Private
void mg_tls_ctx_init(struct mg_mgr *); void mg_tls_ctx_init(struct mg_mgr *);
void mg_tls_ctx_free(struct mg_mgr *); void mg_tls_ctx_free(struct mg_mgr *);
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
// Low-level IO primives used by TLS layer // Low-level IO primives used by TLS layer
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 }; enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 };
+93 -81
View File
@@ -40,45 +40,47 @@ static void aes_init_keygen_tables(void);
/****************************************************************************** /******************************************************************************
* AES_SETKEY : called to expand the key for encryption or decryption * AES_SETKEY : called to expand the key for encryption or decryption
******************************************************************************/ ******************************************************************************/
static int aes_setkey(aes_context *ctx, // pointer to context static int aes_setkey(
int mode, // 1 or 0 for Encrypt/Decrypt aes_context *ctx, // pointer to context
const unsigned char *key, // AES input key int mode, // 1 or 0 for Encrypt/Decrypt
unsigned int keysize); // size in bytes (must be 16, 24, 32 for const unsigned char *key, // AES input key
// 128, 192 or 256-bit keys respectively) unsigned int keysize); // size in bytes (must be 16, 24, 32 for
// returns 0 for success // 128, 192 or 256-bit keys respectively)
// returns 0 for success
/****************************************************************************** /******************************************************************************
* AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data * AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data
******************************************************************************/ ******************************************************************************/
static int aes_cipher(aes_context *ctx, // pointer to context static int aes_cipher(
const unsigned char input[16], // 128-bit block to en/decipher aes_context *ctx, // pointer to context
unsigned char output[16]); // 128-bit output result block const unsigned char input[16], // 128-bit block to en/decipher
// returns 0 for success unsigned char output[16]); // 128-bit output result block
// returns 0 for success
/****************************************************************************** /******************************************************************************
* GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx * GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx
******************************************************************************/ ******************************************************************************/
typedef struct { typedef struct {
int mode; // cipher direction: encrypt/decrypt int mode; // cipher direction: encrypt/decrypt
uint64_t len; // cipher data length processed so far uint64_t len; // cipher data length processed so far
uint64_t add_len; // total add data length uint64_t add_len; // total add data length
uint64_t HL[16]; // precalculated lo-half HTable uint64_t HL[16]; // precalculated lo-half HTable
uint64_t HH[16]; // precalculated hi-half HTable uint64_t HH[16]; // precalculated hi-half HTable
unsigned char base_ectr[16]; // first counter-mode cipher output for tag unsigned char base_ectr[16]; // first counter-mode cipher output for tag
unsigned char y[16]; // the current cipher-input IV|Counter value unsigned char y[16]; // the current cipher-input IV|Counter value
unsigned char buf[16]; // buf working value unsigned char buf[16]; // buf working value
aes_context aes_ctx; // cipher context used aes_context aes_ctx; // cipher context used
} gcm_context; } gcm_context;
/****************************************************************************** /******************************************************************************
* GCM_SETKEY : sets the GCM (and AES) keying material for use * GCM_SETKEY : sets the GCM (and AES) keying material for use
******************************************************************************/ ******************************************************************************/
static int gcm_setkey( static int gcm_setkey(
gcm_context *ctx, // caller-provided context ptr gcm_context *ctx, // caller-provided context ptr
const unsigned char *key, // pointer to cipher key const unsigned char *key, // pointer to cipher key
const unsigned int keysize // size in bytes (must be 16, 24, 32 for const unsigned int keysize // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively) // 128, 192 or 256-bit keys respectively)
); // returns 0 for success ); // returns 0 for success
/****************************************************************************** /******************************************************************************
* *
@@ -98,17 +100,17 @@ static int gcm_setkey(
* *
******************************************************************************/ ******************************************************************************/
static int gcm_crypt_and_tag( static int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0) int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0)
const unsigned char *iv, // pointer to the 12-byte initialization vector const unsigned char *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12 size_t iv_len, // byte length if the IV. should always be 12
const unsigned char *add, // pointer to the non-ciphered additional data const unsigned char *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data size_t add_len, // byte length of the additional AEAD data
const unsigned char *input, // pointer to the cipher data source const unsigned char *input, // pointer to the cipher data source
unsigned char *output, // pointer to the cipher data destination unsigned char *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data size_t length, // byte length of the cipher data
unsigned char *tag, // pointer to the tag to be generated unsigned char *tag, // pointer to the tag to be generated
size_t tag_len); // byte length of the tag to be generated size_t tag_len); // byte length of the tag to be generated
/****************************************************************************** /******************************************************************************
* *
@@ -119,12 +121,12 @@ static int gcm_crypt_and_tag(
* *
******************************************************************************/ ******************************************************************************/
static int gcm_start( static int gcm_start(
gcm_context *ctx, // pointer to user-provided GCM context gcm_context *ctx, // pointer to user-provided GCM context
int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0) int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0)
const unsigned char *iv, // pointer to initialization vector const unsigned char *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12) size_t iv_len, // IV length in bytes (should == 12)
const unsigned char *add, // pointer to additional AEAD data (NULL if none) const unsigned char *add, // pointer to additional AEAD data (NULL if none)
size_t add_len); // length of additional AEAD data (bytes) size_t add_len); // length of additional AEAD data (bytes)
/****************************************************************************** /******************************************************************************
* *
@@ -140,7 +142,7 @@ static int gcm_start(
static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process size_t length, // length, in bytes, of data to process
const unsigned char *input, // pointer to source data const unsigned char *input, // pointer to source data
unsigned char *output); // pointer to destination data unsigned char *output); // pointer to destination data
/****************************************************************************** /******************************************************************************
* *
@@ -151,9 +153,9 @@ static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
* *
******************************************************************************/ ******************************************************************************/
static int gcm_finish( static int gcm_finish(
gcm_context *ctx, // pointer to user-provided GCM context gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // ptr to tag buffer - NULL if tag_len = 0 unsigned char *tag, // ptr to tag buffer - NULL if tag_len = 0
size_t tag_len); // length, in bytes, of the tag-receiving buf size_t tag_len); // length, in bytes, of the tag-receiving buf
/****************************************************************************** /******************************************************************************
* *
@@ -204,15 +206,15 @@ static int aes_tables_inited = 0; // run-once flag for performing key
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h. * decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
*/ */
// We always need our forward tables // We always need our forward tables
static unsigned char FSb[256]; // Forward substitution box (FSb) static unsigned char FSb[256]; // Forward substitution box (FSb)
static uint32_t FT0[256]; // Forward key schedule assembly tables static uint32_t FT0[256]; // Forward key schedule assembly tables
static uint32_t FT1[256]; static uint32_t FT1[256];
static uint32_t FT2[256]; static uint32_t FT2[256];
static uint32_t FT3[256]; static uint32_t FT3[256];
#if AES_DECRYPTION // We ONLY need reverse for decryption #if AES_DECRYPTION // We ONLY need reverse for decryption
static unsigned char RSb[256]; // Reverse substitution box (RSb) static unsigned char RSb[256]; // Reverse substitution box (RSb)
static uint32_t RT0[256]; // Reverse key schedule assembly tables static uint32_t RT0[256]; // Reverse key schedule assembly tables
static uint32_t RT1[256]; static uint32_t RT1[256];
static uint32_t RT2[256]; static uint32_t RT2[256];
static uint32_t RT3[256]; static uint32_t RT3[256];
@@ -230,8 +232,8 @@ static uint32_t RCON[10]; // AES round constants
((uint32_t) (b)[(i) + 2] << 16) | ((uint32_t) (b)[(i) + 3] << 24); \ ((uint32_t) (b)[(i) + 2] << 16) | ((uint32_t) (b)[(i) + 3] << 24); \
} }
#define PUT_UINT32_LE(n, b, i) \ #define PUT_UINT32_LE(n, b, i) \
{ \ { \
(b)[(i)] = (unsigned char) ((n)); \ (b)[(i)] = (unsigned char) ((n)); \
(b)[(i) + 1] = (unsigned char) ((n) >> 8); \ (b)[(i) + 1] = (unsigned char) ((n) >> 8); \
(b)[(i) + 2] = (unsigned char) ((n) >> 16); \ (b)[(i) + 2] = (unsigned char) ((n) >> 16); \
@@ -376,7 +378,7 @@ void aes_init_keygen_tables(void) {
******************************************************************************/ ******************************************************************************/
static int aes_set_encryption_key(aes_context *ctx, const unsigned char *key, static int aes_set_encryption_key(aes_context *ctx, const unsigned char *key,
unsigned int keysize) { unsigned int keysize) {
unsigned int i; // general purpose iteration local unsigned int i; // general purpose iteration local
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
for (i = 0; i < (keysize >> 2); i++) { for (i = 0; i < (keysize >> 2); i++) {
@@ -711,8 +713,8 @@ static const uint64_t last4[16] = {
((uint32_t) (b)[(i) + 2] << 8) | ((uint32_t) (b)[(i) + 3]); \ ((uint32_t) (b)[(i) + 2] << 8) | ((uint32_t) (b)[(i) + 3]); \
} }
#define PUT_UINT32_BE(n, b, i) \ #define PUT_UINT32_BE(n, b, i) \
{ \ { \
(b)[(i)] = (unsigned char) ((n) >> 24); \ (b)[(i)] = (unsigned char) ((n) >> 24); \
(b)[(i) + 1] = (unsigned char) ((n) >> 16); \ (b)[(i) + 1] = (unsigned char) ((n) >> 16); \
(b)[(i) + 2] = (unsigned char) ((n) >> 8); \ (b)[(i) + 2] = (unsigned char) ((n) >> 8); \
@@ -745,9 +747,10 @@ int mg_gcm_initialize(void) {
* 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field. * 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field.
* *
******************************************************************************/ ******************************************************************************/
static void gcm_mult(gcm_context *ctx, // pointer to established context static void gcm_mult(
const unsigned char x[16], // pointer to 128-bit input vector gcm_context *ctx, // pointer to established context
unsigned char output[16]) // pointer to 128-bit output vector const unsigned char x[16], // pointer to 128-bit input vector
unsigned char output[16]) // pointer to 128-bit output vector
{ {
int i; int i;
unsigned char lo, hi, rem; unsigned char lo, hi, rem;
@@ -792,10 +795,10 @@ static void gcm_mult(gcm_context *ctx, // pointer to established context
* *
******************************************************************************/ ******************************************************************************/
static int gcm_setkey( static int gcm_setkey(
gcm_context *ctx, // pointer to caller-provided gcm context gcm_context *ctx, // pointer to caller-provided gcm context
const unsigned char *key, // pointer to the AES encryption key const unsigned char *key, // pointer to the AES encryption key
const unsigned int keysize) // size in bytes (must be 16, 24, 32 for const unsigned int keysize) // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively) // 128, 192 or 256-bit keys respectively)
{ {
int ret, i, j; int ret, i, j;
uint64_t hi, lo; uint64_t hi, lo;
@@ -864,18 +867,19 @@ static int gcm_setkey(
* mode, and preprocesses the initialization vector and additional AEAD data. * mode, and preprocesses the initialization vector and additional AEAD data.
* *
******************************************************************************/ ******************************************************************************/
int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context int gcm_start(
int mode, // GCM_ENCRYPT or GCM_DECRYPT gcm_context *ctx, // pointer to user-provided GCM context
const unsigned char *iv, // pointer to initialization vector int mode, // GCM_ENCRYPT or GCM_DECRYPT
size_t iv_len, // IV length in bytes (should == 12) const unsigned char *iv, // pointer to initialization vector
const unsigned char *add, // ptr to additional AEAD data (NULL if none) size_t iv_len, // IV length in bytes (should == 12)
size_t add_len) // length of additional AEAD data (bytes) const unsigned char *add, // ptr to additional AEAD data (NULL if none)
size_t add_len) // length of additional AEAD data (bytes)
{ {
int ret; // our error return if the AES encrypt fails int ret; // our error return if the AES encrypt fails
unsigned char work_buf[16]; // XOR source built from provided IV if len != 16 unsigned char work_buf[16]; // XOR source built from provided IV if len != 16
const unsigned char *p; // general purpose array pointer const unsigned char *p; // general purpose array pointer
size_t use_len; // byte count to process, up to 16 bytes size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator size_t i; // local loop iterator
// since the context might be reused under the same key // since the context might be reused under the same key
// we zero the working buffers for this next new process // we zero the working buffers for this next new process
@@ -932,15 +936,15 @@ int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
* have a partial block length of < 128 bits.) * have a partial block length of < 128 bits.)
* *
******************************************************************************/ ******************************************************************************/
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process size_t length, // length, in bytes, of data to process
const unsigned char *input, // pointer to source data const unsigned char *input, // pointer to source data
unsigned char *output) // pointer to destination data unsigned char *output) // pointer to destination data
{ {
int ret; // our error return if the AES encrypt fails int ret; // our error return if the AES encrypt fails
unsigned char ectr[16]; // counter-mode cipher output for XORing unsigned char ectr[16]; // counter-mode cipher output for XORing
size_t use_len; // byte count to process, up to 16 bytes size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator size_t i; // local loop iterator
ctx->len += length; // bump the GCM context's running length count ctx->len += length; // bump the GCM context's running length count
@@ -996,9 +1000,9 @@ int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
* It performs the final GHASH to produce the resulting authentication TAG. * It performs the final GHASH to produce the resulting authentication TAG.
* *
******************************************************************************/ ******************************************************************************/
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // pointer to buffer which receives the tag unsigned char *tag, // pointer to buffer which receives the tag
size_t tag_len) // length, in bytes, of the tag-receiving buf size_t tag_len) // length, in bytes, of the tag-receiving buf
{ {
unsigned char work_buf[16]; unsigned char work_buf[16];
uint64_t orig_len = ctx->len * 8; uint64_t orig_len = ctx->len * 8;
@@ -1040,22 +1044,22 @@ int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
* *
******************************************************************************/ ******************************************************************************/
int gcm_crypt_and_tag( int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
const unsigned char *iv, // pointer to the 12-byte initialization vector const unsigned char *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12 size_t iv_len, // byte length if the IV. should always be 12
const unsigned char *add, // pointer to the non-ciphered additional data const unsigned char *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data size_t add_len, // byte length of the additional AEAD data
const unsigned char *input, // pointer to the cipher data source const unsigned char *input, // pointer to the cipher data source
unsigned char *output, // pointer to the cipher data destination unsigned char *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data size_t length, // byte length of the cipher data
unsigned char *tag, // pointer to the tag to be generated unsigned char *tag, // pointer to the tag to be generated
size_t tag_len) // byte length of the tag to be generated size_t tag_len) // byte length of the tag to be generated
{ /* { /*
assuming that the caller has already invoked gcm_setkey to assuming that the caller has already invoked gcm_setkey to
prepare the gcm context with the keying material, we simply prepare the gcm context with the keying material, we simply
invoke each of the three GCM sub-functions in turn... invoke each of the three GCM sub-functions in turn...
*/ */
gcm_start(ctx, mode, iv, iv_len, add, add_len); gcm_start(ctx, mode, iv, iv_len, add, add_len);
gcm_update(ctx, length, input, output); gcm_update(ctx, length, input, output);
gcm_finish(ctx, tag, tag_len); gcm_finish(ctx, tag, tag_len);
@@ -1105,20 +1109,28 @@ int mg_aes_gcm_encrypt(unsigned char *output, //
int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
size_t input_length, const unsigned char *key, size_t input_length, const unsigned char *key,
const size_t key_len, const unsigned char *iv, const size_t key_len, const unsigned char *iv,
const size_t iv_len) { const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len) {
int ret = 0; // our return value int ret = 0; // our return value
gcm_context ctx; // includes the AES context structure gcm_context ctx; // includes the AES context structure
unsigned char computed_tag[16];
size_t i;
int diff = 0;
size_t tag_len = 0; if (tag_len > sizeof(computed_tag)) return -1;
unsigned char *tag_buf = NULL;
gcm_setkey(&ctx, key, (unsigned int) key_len); gcm_setkey(&ctx, key, (unsigned int) key_len);
ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, NULL, 0, input, output, ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, aead, aead_len, input,
input_length, tag_buf, tag_len); output, input_length, computed_tag, tag_len);
gcm_zero_ctx(&ctx); gcm_zero_ctx(&ctx);
// compare tags
for (i = 0; i < tag_len; i++) diff |= computed_tag[i] ^ tag[i];
if (diff != 0) ret = -1;
return (ret); return (ret);
} }
#endif #endif
+3 -1
View File
@@ -59,7 +59,9 @@ int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input,
int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
size_t input_length, const unsigned char *key, size_t input_length, const unsigned char *key,
const size_t key_len, const unsigned char *iv, const size_t key_len, const unsigned char *iv,
const size_t iv_len); const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len);
#endif /* TLS_AES128_H */ #endif /* TLS_AES128_H */
+829 -94
View File
File diff suppressed because it is too large. Load diff
+29 -15
View File
@@ -79,9 +79,11 @@ static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx,
#elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ #elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \
defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \
defined(__MIPSEL) || defined(__MIPSEL__) || defined(__XTENSA_EL__) || \ defined(__MIPSEL) || defined(__MIPSEL__) || defined(__XTENSA_EL__) || \
defined(__AVR__) || defined(LITTLE_ENDIAN) defined(__AVR__)
#define __HAVE_LITTLE_ENDIAN 1 #define __HAVE_LITTLE_ENDIAN 1
#endif #endif
// DO NOT test for LITTLE_ENDIAN, as it is defined as 1234 when including
// sys/types.h in GCC
#ifndef TEST_SLOW_PATH #ifndef TEST_SLOW_PATH
#if defined(__HAVE_LITTLE_ENDIAN) #if defined(__HAVE_LITTLE_ENDIAN)
@@ -177,7 +179,7 @@ static void core_block(const uint32_t *restrict start,
TIMES16(__FIN) TIMES16(__FIN)
} }
#define U8(x) ((uint8_t) ((x) &0xFF)) #define U8(x) ((uint8_t) ((x) & 0xFF))
#ifdef FAST_PATH #ifdef FAST_PATH
#define xor32_le(dst, src, pad) \ #define xor32_le(dst, src, pad) \
@@ -502,7 +504,7 @@ static unsigned short U8TO16(const unsigned char *p) {
/* store a 16 bit unsigned integer as two 8 bit unsigned integers in little /* store a 16 bit unsigned integer as two 8 bit unsigned integers in little
* endian */ * endian */
static void U16TO8(unsigned char *p, unsigned short v) { static void U16TO8(unsigned char *p, unsigned short v) {
p[0] = (v) &0xff; p[0] = (v) & 0xff;
p[1] = (v >> 8) & 0xff; p[1] = (v >> 8) & 0xff;
} }
@@ -513,7 +515,7 @@ static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
t0 = U8TO16(&key[0]); t0 = U8TO16(&key[0]);
st->r[0] = (t0) &0x1fff; st->r[0] = (t0) & 0x1fff;
t1 = U8TO16(&key[2]); t1 = U8TO16(&key[2]);
st->r[1] = ((t0 >> 13) | (t1 << 3)) & 0x1fff; st->r[1] = ((t0 >> 13) | (t1 << 3)) & 0x1fff;
t2 = U8TO16(&key[4]); t2 = U8TO16(&key[4]);
@@ -553,7 +555,7 @@ static void poly1305_blocks(poly1305_state_internal_t *st,
/* h += m[i] */ /* h += m[i] */
t0 = U8TO16(&m[0]); t0 = U8TO16(&m[0]);
st->h[0] += (t0) &0x1fff; st->h[0] += (t0) & 0x1fff;
t1 = U8TO16(&m[2]); t1 = U8TO16(&m[2]);
st->h[1] += ((t0 >> 13) | (t1 << 3)) & 0x1fff; st->h[1] += ((t0 >> 13) | (t1 << 3)) & 0x1fff;
t2 = U8TO16(&m[4]); t2 = U8TO16(&m[4]);
@@ -715,7 +717,7 @@ static unsigned long U8TO32(const unsigned char *p) {
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little /* store a 32 bit unsigned integer as four 8 bit unsigned integers in little
* endian */ * endian */
static void U32TO8(unsigned char *p, unsigned long v) { static void U32TO8(unsigned char *p, unsigned long v) {
p[0] = (unsigned char) ((v) &0xff); p[0] = (unsigned char) ((v) & 0xff);
p[1] = (unsigned char) ((v >> 8) & 0xff); p[1] = (unsigned char) ((v >> 8) & 0xff);
p[2] = (unsigned char) ((v >> 16) & 0xff); p[2] = (unsigned char) ((v >> 16) & 0xff);
p[3] = (unsigned char) ((v >> 24) & 0xff); p[3] = (unsigned char) ((v >> 24) & 0xff);
@@ -979,8 +981,8 @@ typedef unsigned uint128_t __attribute__((mode(TI)));
#define MUL128(out, x, y) out = ((uint128_t) x * y) #define MUL128(out, x, y) out = ((uint128_t) x * y)
#define ADD(out, in) out += in #define ADD(out, in) out += in
#define ADDLO(out, in) out += in #define ADDLO(out, in) out += in
#define SHR(in, shift) (uint64_t)(in >> (shift)) #define SHR(in, shift) (uint64_t) (in >> (shift))
#define LO(in) (uint64_t)(in) #define LO(in) (uint64_t) (in)
#define POLY1305_NOINLINE __attribute__((noinline)) #define POLY1305_NOINLINE __attribute__((noinline))
#endif #endif
@@ -1009,7 +1011,7 @@ static uint64_t U8TO64(const unsigned char *p) {
/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little /* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little
* endian */ * endian */
static void U64TO8(unsigned char *p, uint64_t v) { static void U64TO8(unsigned char *p, uint64_t v) {
p[0] = (unsigned char) ((v) &0xff); p[0] = (unsigned char) ((v) & 0xff);
p[1] = (unsigned char) ((v >> 8) & 0xff); p[1] = (unsigned char) ((v >> 8) & 0xff);
p[2] = (unsigned char) ((v >> 16) & 0xff); p[2] = (unsigned char) ((v >> 16) & 0xff);
p[3] = (unsigned char) ((v >> 24) & 0xff); p[3] = (unsigned char) ((v >> 24) & 0xff);
@@ -1027,7 +1029,7 @@ static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
t0 = U8TO64(&key[0]); t0 = U8TO64(&key[0]);
t1 = U8TO64(&key[8]); t1 = U8TO64(&key[8]);
st->r[0] = (t0) &0xffc0fffffff; st->r[0] = (t0) & 0xffc0fffffff;
st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff; st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
st->r[2] = ((t1 >> 24)) & 0x00ffffffc0f; st->r[2] = ((t1 >> 24)) & 0x00ffffffc0f;
@@ -1071,7 +1073,7 @@ static void poly1305_blocks(poly1305_state_internal_t *st,
t0 = U8TO64(&m[0]); t0 = U8TO64(&m[0]);
t1 = U8TO64(&m[8]); t1 = U8TO64(&m[8]);
h0 += ((t0) &0xfffffffffff); h0 += ((t0) & 0xfffffffffff);
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff); h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
h2 += (((t1 >> 24)) & 0x3ffffffffff) | hibit; h2 += (((t1 >> 24)) & 0x3ffffffffff) | hibit;
@@ -1178,7 +1180,7 @@ static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
t0 = st->pad[0]; t0 = st->pad[0];
t1 = st->pad[1]; t1 = st->pad[1];
h0 += ((t0) &0xfffffffffff); h0 += ((t0) & 0xfffffffffff);
c = (h0 >> 44); c = (h0 >> 44);
h0 &= 0xfffffffffff; h0 &= 0xfffffffffff;
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c; h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c;
@@ -1255,7 +1257,7 @@ static PORTABLE_8439_DECL void pad_if_needed(poly1305_context *ctx,
} }
} }
#define __u8(v) ((uint8_t) ((v) &0xFF)) #define __u8(v) ((uint8_t) ((v) & 0xFF))
// TODO: make this depending on the unaligned/native read size possible // TODO: make this depending on the unaligned/native read size possible
static PORTABLE_8439_DECL void write_64bit_int(poly1305_context *ctx, static PORTABLE_8439_DECL void write_64bit_int(poly1305_context *ctx,
@@ -1328,14 +1330,26 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt( PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE], uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
const uint8_t *restrict cipher_text, size_t cipher_text_size) { size_t ad_size, const uint8_t *restrict cipher_text,
size_t cipher_text_size) {
// first we calculate the mac and see if it lines up, only then do we decrypt // first we calculate the mac and see if it lines up, only then do we decrypt
size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE; size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE;
uint8_t computed_mac[RFC_8439_TAG_SIZE];
int diff = 0;
size_t i;
if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) { if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) {
return (size_t) -1; return (size_t) -1;
} }
poly1305_calculate_mac(computed_mac, cipher_text, actual_size, key, nonce, ad,
ad_size);
// compare tags
for (i = 0; i < RFC_8439_TAG_SIZE; i++)
diff |= computed_mac[i] ^ cipher_text[actual_size + i];
if (diff != 0) return (size_t) -1;
chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1); chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1);
return actual_size; return actual_size;
} }
+1
View File
@@ -105,6 +105,7 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt( PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE], uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t nonce[RFC_8439_NONCE_SIZE],
const uint8_t *restrict ad, size_t ad_size,
const uint8_t *restrict cipher_text, size_t cipher_text_size); const uint8_t *restrict cipher_text, size_t cipher_text_size);
#if defined(__cplusplus) #if defined(__cplusplus)
} }
+42 -21
View File
@@ -6,7 +6,8 @@
#if MG_TLS == MG_TLS_MBED #if MG_TLS == MG_TLS_MBED
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \
MBEDTLS_VERSION_NUMBER < 0x04000000
#define MG_MBEDTLS_RNG_GET , mg_mbed_rng, NULL #define MG_MBEDTLS_RNG_GET , mg_mbed_rng, NULL
#else #else
#define MG_MBEDTLS_RNG_GET #define MG_MBEDTLS_RNG_GET
@@ -19,16 +20,19 @@ static int mg_tls_err(struct mg_connection *c, int rc) {
return rc; return rc;
} }
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000
#else
static int mg_mbed_rng(void *ctx, unsigned char *buf, size_t len) { static int mg_mbed_rng(void *ctx, unsigned char *buf, size_t len) {
mg_random(buf, len); mg_random(buf, len);
(void) ctx; (void) ctx;
return 0; return 0;
} }
#endif
static bool mg_load_cert(struct mg_str str, mbedtls_x509_crt *p) { static bool mg_load_cert(struct mg_str str, mbedtls_x509_crt *p) {
int rc; int rc;
if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true; if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true;
if (str.buf[0] == '-') str.len++; // PEM, include trailing NUL if (!MG_IS_DER(str.buf)) str.len++; // PEM, include trailing NUL
if ((rc = mbedtls_x509_crt_parse(p, (uint8_t *) str.buf, str.len)) != 0) { if ((rc = mbedtls_x509_crt_parse(p, (uint8_t *) str.buf, str.len)) != 0) {
MG_ERROR(("cert err %#x", -rc)); MG_ERROR(("cert err %#x", -rc));
return false; return false;
@@ -39,7 +43,7 @@ static bool mg_load_cert(struct mg_str str, mbedtls_x509_crt *p) {
static bool mg_load_key(struct mg_str str, mbedtls_pk_context *p) { static bool mg_load_key(struct mg_str str, mbedtls_pk_context *p) {
int rc; int rc;
if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true; if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true;
if (str.buf[0] == '-') str.len++; // PEM, include trailing NUL if (!MG_IS_DER(str.buf)) str.len++; // PEM, include trailing NUL
if ((rc = mbedtls_pk_parse_key(p, (uint8_t *) str.buf, str.len, NULL, if ((rc = mbedtls_pk_parse_key(p, (uint8_t *) str.buf, str.len, NULL,
0 MG_MBEDTLS_RNG_GET)) != 0) { 0 MG_MBEDTLS_RNG_GET)) != 0) {
MG_ERROR(("key err %#x", -rc)); MG_ERROR(("key err %#x", -rc));
@@ -59,10 +63,9 @@ void mg_tls_free(struct mg_connection *c) {
#ifdef MBEDTLS_SSL_SESSION_TICKETS #ifdef MBEDTLS_SSL_SESSION_TICKETS
mbedtls_ssl_ticket_free(&tls->ticket); mbedtls_ssl_ticket_free(&tls->ticket);
#endif #endif
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \ // PSA has global data. Do not call mbedtls_psa_crypto_free() here,
defined(MBEDTLS_PSA_CRYPTO_C) // it will free all global resources. Call it when actually freeing all
mbedtls_psa_crypto_free(); // https://github.com/Mbed-TLS/mbedtls/issues/9223#issuecomment-2144898336 // application resources (main() exits)
#endif
mg_free(tls); mg_free(tls);
c->tls = NULL; c->tls = NULL;
} }
@@ -122,6 +125,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \ #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \
defined(MBEDTLS_PSA_CRYPTO_C) defined(MBEDTLS_PSA_CRYPTO_C)
psa_crypto_init(); // https://github.com/Mbed-TLS/mbedtls/issues/9072#issuecomment-2084845711 psa_crypto_init(); // https://github.com/Mbed-TLS/mbedtls/issues/9072#issuecomment-2084845711
// this initializes global resources and then just returns when called again
#endif #endif
mbedtls_ssl_init(&tls->ssl); mbedtls_ssl_init(&tls->ssl);
mbedtls_ssl_config_init(&tls->conf); mbedtls_ssl_config_init(&tls->conf);
@@ -139,7 +143,11 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
mg_error(c, "tls defaults %#x", -mg_tls_err(c, rc)); mg_error(c, "tls defaults %#x", -mg_tls_err(c, rc));
goto fail; goto fail;
} }
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000
MG_INFO(("PSA is in control of random number generation"));
#else
mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c); mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c);
#endif
if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) { if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
// NOTE: MBEDTLS_SSL_VERIFY_NONE is not supported for TLS1.3 on client side // NOTE: MBEDTLS_SSL_VERIFY_NONE is not supported for TLS1.3 on client side
@@ -148,11 +156,16 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
} else { } else {
if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail; if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail;
mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL); mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
if (c->is_client && opts->name.buf != NULL && opts->name.buf[0] != '\0') { if (c->is_client) {
char *host = mg_mprintf("%.*s", opts->name.len, opts->name.buf); if (opts->name.buf != NULL && opts->name.buf[0] != '\0') {
mbedtls_ssl_set_hostname(&tls->ssl, host); char *host = mg_mprintf("%.*s", opts->name.len, opts->name.buf);
MG_DEBUG(("%lu hostname verification: %s", c->id, host)); mbedtls_ssl_set_hostname(&tls->ssl, host);
mg_free(host); MG_DEBUG(("%lu hostname verification: %s", c->id, host));
mg_free(host);
} else {
MG_DEBUG(("%lu skipping hostname verification", c->id));
mbedtls_ssl_set_hostname(&tls->ssl, NULL);
}
} }
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
} }
@@ -191,7 +204,7 @@ size_t mg_tls_pending(struct mg_connection *c) {
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len);
if (!c->is_tls_hs && buf == NULL && n == 0) return 0; // TODO(): MIP if (!c->is_tls_hs && buf == NULL && n == 0) return 0; // TODO(): MIP
if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
return MG_IO_WAIT; return MG_IO_WAIT;
#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) #if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
@@ -215,10 +228,11 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE); (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE);
if (was_throttled) return MG_IO_WAIT; // flushed throttled data instead if (was_throttled) return MG_IO_WAIT; // flushed throttled data instead
if (c->is_tls_throttled) { if (c->is_tls_throttled) {
tls->throttled_buf = (unsigned char *)buf; // MbedTLS code actually ignores tls->throttled_buf =
tls->throttled_len = len; // these, but let's play API rules (unsigned char *) buf; // MbedTLS code actually ignores
return (long) len; // already encripted that when throttled tls->throttled_len = len; // these, but let's play API rules
} // if last chunk fails to be sent, it needs to be flushed return (long) len; // already encripted that when throttled
} // if last chunk fails to be sent, it needs to be flushed
if (n <= 0) return MG_IO_ERR; if (n <= 0) return MG_IO_ERR;
return n; return n;
} }
@@ -226,8 +240,10 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
void mg_tls_flush(struct mg_connection *c) { void mg_tls_flush(struct mg_connection *c) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
if (c->is_tls_throttled) { if (c->is_tls_throttled) {
long n = mbedtls_ssl_write(&tls->ssl, tls->throttled_buf, tls->throttled_len); long n =
c->is_tls_throttled = (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE); mbedtls_ssl_write(&tls->ssl, tls->throttled_buf, tls->throttled_len);
c->is_tls_throttled =
(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE);
} }
} }
@@ -239,9 +255,14 @@ void mg_tls_ctx_init(struct mg_mgr *mgr) {
#ifdef MBEDTLS_SSL_SESSION_TICKETS #ifdef MBEDTLS_SSL_SESSION_TICKETS
int rc; int rc;
mbedtls_ssl_ticket_init(&ctx->tickets); mbedtls_ssl_ticket_init(&ctx->tickets);
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000
if ((rc = mbedtls_ssl_ticket_setup(&ctx->tickets, PSA_ALG_GCM,
PSA_KEY_TYPE_AES, 128, 86400))
#else
if ((rc = mbedtls_ssl_ticket_setup(&ctx->tickets, mg_mbed_rng, NULL, if ((rc = mbedtls_ssl_ticket_setup(&ctx->tickets, mg_mbed_rng, NULL,
MBEDTLS_CIPHER_AES_128_GCM, 86400)) != MBEDTLS_CIPHER_AES_128_GCM, 86400))
0) { #endif
!= 0) {
MG_ERROR((" mbedtls_ssl_ticket_setup %#x", -rc)); MG_ERROR((" mbedtls_ssl_ticket_setup %#x", -rc));
} }
#endif #endif
+20 -4
View File
@@ -28,6 +28,7 @@ static int mg_tls_err(struct mg_connection *c, struct mg_tls *tls, int res) {
return err; return err;
} }
#if MG_TLS != MG_TLS_WOLFSSL
static STACK_OF(X509_INFO) * load_ca_certs(struct mg_str ca) { static STACK_OF(X509_INFO) * load_ca_certs(struct mg_str ca) {
BIO *bio = BIO_new_mem_buf(ca.buf, (int) ca.len); BIO *bio = BIO_new_mem_buf(ca.buf, (int) ca.len);
STACK_OF(X509_INFO) *certs = STACK_OF(X509_INFO) *certs =
@@ -47,6 +48,7 @@ static bool add_ca_certs(SSL_CTX *ctx, STACK_OF(X509_INFO) * certs) {
} }
return true; return true;
} }
#endif
static EVP_PKEY *load_key(struct mg_str s) { static EVP_PKEY *load_key(struct mg_str s) {
BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len); BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len);
@@ -58,9 +60,9 @@ static EVP_PKEY *load_key(struct mg_str s) {
static X509 *load_cert(struct mg_str s) { static X509 *load_cert(struct mg_str s) {
BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len); BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len);
X509 *cert = bio == NULL ? NULL X509 *cert = bio == NULL ? NULL
: s.buf[0] == '-' : MG_IS_DER(s.buf)
? PEM_read_bio_X509(bio, NULL, NULL, NULL) // PEM ? d2i_X509_bio(bio, NULL) // DER
: d2i_X509_bio(bio, NULL); // DER : PEM_read_bio_X509(bio, NULL, NULL, NULL); // PEM
if (bio) BIO_free(bio); if (bio) BIO_free(bio);
return cert; return cert;
} }
@@ -171,9 +173,21 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
SSL_set_verify(tls->ssl, SSL_VERIFY_NONE, NULL); SSL_set_verify(tls->ssl, SSL_VERIFY_NONE, NULL);
} }
#endif #endif
if (opts->ca.buf != NULL && opts->ca.buf[0] != '\0') { if (opts->ca.buf != NULL && opts->ca.buf[0] != '\0') {
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NULL); NULL);
#if MG_TLS == MG_TLS_WOLFSSL
extern int wolfSSL_CTX_load_verify_buffer(SSL_CTX *, const unsigned char *,
long, int);
rc = wolfSSL_CTX_load_verify_buffer(tls->ctx,
(const unsigned char *) opts->ca.buf,
(long) opts->ca.len, SSL_FILETYPE_PEM);
if (rc != 1) {
mg_error(c, "CA err");
goto fail;
}
#else
STACK_OF(X509_INFO) *certs = load_ca_certs(opts->ca); STACK_OF(X509_INFO) *certs = load_ca_certs(opts->ca);
rc = add_ca_certs(tls->ctx, certs); rc = add_ca_certs(tls->ctx, certs);
sk_X509_INFO_pop_free(certs, X509_INFO_free); sk_X509_INFO_pop_free(certs, X509_INFO_free);
@@ -181,7 +195,9 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
mg_error(c, "CA err"); mg_error(c, "CA err");
goto fail; goto fail;
} }
#endif
} }
if (opts->cert.buf != NULL && opts->cert.buf[0] != '\0') { if (opts->cert.buf != NULL && opts->cert.buf[0] != '\0') {
X509 *cert = load_cert(opts->cert); X509 *cert = load_cert(opts->cert);
rc = cert == NULL ? 0 : SSL_use_certificate(tls->ssl, cert); rc = cert == NULL ? 0 : SSL_use_certificate(tls->ssl, cert);
@@ -259,7 +275,7 @@ size_t mg_tls_pending(struct mg_connection *c) {
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int n = SSL_read(tls->ssl, buf, (int) len); int n = SSL_read(tls->ssl, buf, (int) len);
if (!c->is_tls_hs && buf == NULL && n == 0) return 0; // TODO(): MIP if (!c->is_tls_hs && buf == NULL && n == 0) return 0; // TODO(): MIP
if (n < 0 && mg_tls_err(c, tls, n) == 0) return MG_IO_WAIT; if (n < 0 && mg_tls_err(c, tls, n) == 0) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR; if (n <= 0) return MG_IO_ERR;
return n; return n;
+45 -2
View File
@@ -1520,7 +1520,6 @@ NS_INTERNAL bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) {
#endif #endif
} }
#if 0
/** /**
* @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts. * @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts.
* *
@@ -1533,6 +1532,7 @@ NS_INTERNAL bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) {
* @param qInv [in] CRT's qInv bigint * @param qInv [in] CRT's qInv bigint
* @return The result of the CRT operation * @return The result of the CRT operation
*/ */
#if 1
NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ, NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ,
bigint *p, bigint *q, bigint *qInv) { bigint *p, bigint *q, bigint *qInv) {
bigint *m1, *m2, *h; bigint *m1, *m2, *h;
@@ -1559,7 +1559,6 @@ NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ,
} }
#endif #endif
// Proper lib usage: // Proper lib usage:
// - BI_CTX *c = bi_initialize() // - BI_CTX *c = bi_initialize()
// - allocate bigints (e.g.: calling bi_import(), int_to_bi(), ...) // - allocate bigints (e.g.: calling bi_import(), int_to_bi(), ...)
@@ -1588,4 +1587,48 @@ int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t
return 0; return 0;
} }
int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
const uint8_t *dP, size_t dP_len,
const uint8_t *dQ, size_t dQ_len,
const uint8_t *p, size_t p_len,
const uint8_t *q, size_t q_len,
const uint8_t *qInv, size_t qInv_len,
uint8_t *signature, size_t sig_len) {
BI_CTX *ctx;
bigint *em_bi, *dP_bi, *dQ_bi, *p_bi, *q_bi, *qInv_bi, *result_bi;
int ret = -1;
ctx = bi_initialize();
if (ctx == NULL) {
return -1;
}
em_bi = bi_import(ctx, em, (int) em_len);
dP_bi = bi_import(ctx, dP, (int) dP_len);
dQ_bi = bi_import(ctx, dQ, (int) dQ_len);
p_bi = bi_import(ctx, p, (int) p_len);
q_bi = bi_import(ctx, q, (int) q_len);
qInv_bi = bi_import(ctx, qInv, (int) qInv_len);
if (em_bi == NULL || dP_bi == NULL || dQ_bi == NULL ||
p_bi == NULL || q_bi == NULL || qInv_bi == NULL) {
goto cleanup;
}
bi_set_mod(ctx, bi_clone(ctx, p_bi), BIGINT_P_OFFSET);
bi_set_mod(ctx, bi_clone(ctx, q_bi), BIGINT_Q_OFFSET);
result_bi = bi_crt(ctx, em_bi, dP_bi, dQ_bi, p_bi, q_bi, qInv_bi);
if (result_bi == NULL) {
goto cleanup;
}
bi_export(ctx, result_bi, signature, (int) sig_len);
ret = 0; // Success!
cleanup:
bi_free_mod(ctx, BIGINT_P_OFFSET); // cloned p_bi stored in mod context
bi_free_mod(ctx, BIGINT_Q_OFFSET); // cloned q_bi stored in mod context
bi_terminate(ctx);
return ret;
}
#endif /* MG_TLS == MG_TLS_BUILTIN */ #endif /* MG_TLS == MG_TLS_BUILTIN */
+7 -1
View File
@@ -3,5 +3,11 @@
#include "arch.h" #include "arch.h"
int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz); int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz);
int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
const uint8_t *dP, size_t dP_len,
const uint8_t *dQ, size_t dQ_len,
const uint8_t *p, size_t p_len,
const uint8_t *q, size_t q_len,
const uint8_t *qInv, size_t qInv_len,
uint8_t *signature, size_t sig_len);
#endif // TLS_RSA_H #endif // TLS_RSA_H
+11 -3
View File
@@ -27,6 +27,15 @@ bool mg_random(void *buf, size_t len) {
#elif MG_ARCH == MG_ARCH_PICOSDK #elif MG_ARCH == MG_ARCH_PICOSDK
while (len--) *p++ = (unsigned char) (get_rand_32() & 255); while (len--) *p++ = (unsigned char) (get_rand_32() & 255);
success = true; success = true;
#elif MG_ARCH == MG_ARCH_ZEPHYR
#if MG_TLS == MG_TLS_BUILTIN || \
(MG_TLS == MG_TLS_MBED && (!defined(MBEDTLS_VERSION_NUMBER) || \
MBEDTLS_VERSION_NUMBER < 0x04000000))
return (sys_csrand_get(buf, len) == 0); // do not fallback on reseed error
#else
sys_rand_get(buf, len);
success = true;
#endif
#elif MG_ARCH == MG_ARCH_WIN32 #elif MG_ARCH == MG_ARCH_WIN32
#if defined(_MSC_VER) && _MSC_VER < 1700 #if defined(_MSC_VER) && _MSC_VER < 1700
static bool initialised = false; static bool initialised = false;
@@ -120,7 +129,7 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
if (remote_ip->is_ip6) { if (remote_ip->is_ip6) {
return -1; // TODO(): handle IPv6 ACL and addresses return -1; // TODO(): handle IPv6 ACL and addresses
} else { // IPv4 } else { // IPv4
memcpy((void *) &remote_ip4, remote_ip->ip, sizeof(remote_ip4)); memcpy((void *) &remote_ip4, remote_ip->addr.ip, sizeof(remote_ip4));
while (mg_span(acl, &entry, &acl, ',')) { while (mg_span(acl, &entry, &acl, ',')) {
uint32_t net, mask; uint32_t net, mask;
if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1; if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1;
@@ -134,7 +143,7 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
bool mg_path_is_sane(const struct mg_str path) { bool mg_path_is_sane(const struct mg_str path) {
const char *s = path.buf; const char *s = path.buf;
size_t n = path.len; size_t n = path.len;
if (path.buf[0] == '~') return false; // Starts with ~ if (path.buf[0] == '~') return false; // Starts with ~
if (path.buf[0] == '.' && path.buf[1] == '.') return false; // Starts with .. if (path.buf[0] == '.' && path.buf[1] == '.') return false; // Starts with ..
for (; s[0] != '\0' && n > 0; s++, n--) { for (; s[0] != '\0' && n > 0; s++, n--) {
if ((s[0] == '/' || s[0] == '\\') && n >= 2) { // Subdir? if ((s[0] == '/' || s[0] == '\\') && n >= 2) { // Subdir?
@@ -213,7 +222,6 @@ void mg_delayms(unsigned int ms) {
while (mg_millis() < to) (void) 0; while (mg_millis() < to) (void) 0;
} }
#if MG_ENABLE_CUSTOM_CALLOC #if MG_ENABLE_CUSTOM_CALLOC
#else #else
void *mg_calloc(size_t count, size_t size) { void *mg_calloc(size_t count, size_t size) {
+1 -1
View File
@@ -1 +1 @@
#define MG_VERSION "7.20" #define MG_VERSION "7.21"
+5
View File
@@ -24,11 +24,16 @@ struct mg_wifi_scan_bss_data {
#define MG_WIFI_SECURITY_WPA MG_BIT(1) #define MG_WIFI_SECURITY_WPA MG_BIT(1)
#define MG_WIFI_SECURITY_WPA2 MG_BIT(2) #define MG_WIFI_SECURITY_WPA2 MG_BIT(2)
#define MG_WIFI_SECURITY_WPA3 MG_BIT(3) #define MG_WIFI_SECURITY_WPA3 MG_BIT(3)
#define MG_WIFI_SECURITY_WPA_ENTERPRISE MG_BIT(4)
#define MG_WIFI_SECURITY_WPA2_ENTERPRISE MG_BIT(5)
#define MG_WIFI_SECURITY_WPA3_ENTERPRISE MG_BIT(6)
uint8_t channel; uint8_t channel;
unsigned band : 2; unsigned band : 2;
#define MG_WIFI_BAND_2G 0 #define MG_WIFI_BAND_2G 0
#define MG_WIFI_BAND_5G 1 #define MG_WIFI_BAND_5G 1
unsigned has_n : 1; unsigned has_n : 1;
unsigned has_ac : 1;
unsigned has_ax : 1;
}; };
bool mg_wifi_scan(void); bool mg_wifi_scan(void);
+2 -1
View File
@@ -3,7 +3,8 @@
#if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) && \ #if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) && \
(!defined(MG_ENABLE_DRIVER_CYW) || !MG_ENABLE_DRIVER_CYW) && \ (!defined(MG_ENABLE_DRIVER_CYW) || !MG_ENABLE_DRIVER_CYW) && \
(!defined(MG_ENABLE_DRIVER_CYW_SDIO) || !MG_ENABLE_DRIVER_CYW_SDIO) && \ (!defined(MG_ENABLE_DRIVER_CYW_SDIO) || !MG_ENABLE_DRIVER_CYW_SDIO) && \
(!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI) (!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI) && \
(!defined(MG_ENABLE_DRIVER_ST67W6) || !MG_ENABLE_DRIVER_ST67W6)
bool mg_wifi_scan(void) { bool mg_wifi_scan(void) {
+7 -3
View File
@@ -173,9 +173,13 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data) {
while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg) > 0) { while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg) > 0) {
char *s = (char *) c->recv.buf + ofs + msg.header_len; char *s = (char *) c->recv.buf + ofs + msg.header_len;
struct mg_ws_message m = {{s, msg.data_len}, msg.flags}; struct mg_ws_message m;
size_t len = msg.header_len + msg.data_len; size_t len;
uint8_t final = msg.flags & 128, op = msg.flags & 15; uint8_t final, op;
m.data.buf = s, m.data.len = msg.data_len, m.flags = msg.flags;
len = msg.header_len + msg.data_len;
final = msg.flags & 128;
op = msg.flags & 15;
// MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op, // MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op,
// (int) m.data.len, (int) m.data.len, m.data.buf)); // (int) m.data.len, (int) m.data.len, m.data.buf));
switch (op) { switch (op) {
+26 -4
View File
@@ -91,9 +91,18 @@ mip_tap_test: mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_mult
$(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ $(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
# bridge UDP port <--> TAP interface
port_tap_bridge: port_tap_bridge.c
cc port_tap_bridge.c -o $@
# requires port_tap_bridge to be running
mip_port_test: mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server
$(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
mip_vc98: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile mip_vc98: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
$(DOCKER) mdashnet/vc98 wine cl mip_test.c packed_fs.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe $(DOCKER) mdashnet/vc98 wine cl mip_test.c packed_fs.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
$(DOCKER) mdashnet/vc98 wine $@.exe $(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key
$(CC) $(CFLAGS) pack.c -o pack $(CC) $(CFLAGS) pack.c -o pack
@@ -179,11 +188,23 @@ armhf: test
s390: ASAN= s390: ASAN=
s390: IPV6=0 s390: IPV6=0
s390: CFLAGS += -Wno-stringop-overflow s390: CFLAGS += -Wno-stringop-overflow
s390: DEFS += -DMG_ENABLE_CHACHA20=0
s390: CC = $(DOCKER) mdashnet/s390 cc s390: CC = $(DOCKER) mdashnet/s390 cc
s390: RUN = $(DOCKER) --init mdashnet/s390 s390: RUN = $(DOCKER) --init mdashnet/s390
s390: test s390: test
mip_s390: ASAN=
mip_s390: CC = $(DOCKER) mdashnet/s390 cc
mip_s390: RUN = $(DOCKER) --init mdashnet/s390
mip_s390: mip_test
mip_port_s390: ASAN=
mip_port_s390: IPV6=0
# This test requires curl, not present in this image
mip_port_s390: CFLAGS += -DNO_HTTPSERVER_TEST
mip_port_s390: CC = $(DOCKER) mdashnet/s390 cc
mip_port_s390: RUN = $(DOCKER) --init mdashnet/s390
mip_port_s390: mip_port_test
arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT
arm: mongoose.h $(SRCS) arm: mongoose.h $(SRCS)
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0 $(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0
@@ -194,7 +215,7 @@ riscv: mongoose.h $(SRCS)
vc98: Makefile mongoose.h $(SRCS) vc98: Makefile mongoose.h $(SRCS)
$(DOCKER) mdashnet/vc98 wine cl $(SRCS) $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe $(DOCKER) mdashnet/vc98 wine cl $(SRCS) $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
$(DOCKER) mdashnet/vc98 wine $@.exe $(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
vc17: Makefile mongoose.h $(SRCS) vc17: Makefile mongoose.h $(SRCS)
$(DOCKER) mdashnet/vc17 wine64 cl $(SRCS) $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe $(DOCKER) mdashnet/vc17 wine64 cl $(SRCS) $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
@@ -230,11 +251,12 @@ mongoose.c: Makefile $(wildcard ../src/*.c) $(wildcard ../src/drivers/*.c)
cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
mongoose.h: $(HDRS) Makefile mongoose.h: $(HDRS) Makefile
cd .. && (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/flash.h src/wifi.h src/net_builtin.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ cd .. && (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
clean: clean_tutorials clean_tutorials_embedded clean: clean_tutorials clean_tutorials_embedded
rm -rf $(PROG) *.exe *.o *.dSYM *_test ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* packed_fs.c pack rm -rf $(PROG) *.exe *.o *.dSYM *_test ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* packed_fs.c pack
$(MAKE) -C tls_multirec $@
#find tutorials -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C #find tutorials -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C
-3
View File
@@ -1,6 +1,3 @@
- Make sure your PEM file starts with a dash -
Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1 Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1
Not Before: Mar 13 00:00:00 2024 GMT Not Before: Mar 13 00:00:00 2024 GMT
Not After : Mar 12 23:59:59 2027 GMT Not After : Mar 12 23:59:59 2027 GMT
-2
View File
@@ -1,5 +1,3 @@
- Make sure your PEM file starts with a dash -
1 s:C=US, O=Let's Encrypt, CN=E8 1 s:C=US, O=Let's Encrypt, CN=E8
i:C=US, O=Internet Security Research Group, CN=ISRG Root X1 i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
-2
View File
@@ -1,5 +1,3 @@
- Make sure your PEM file starts with a dash -
Certificate: Certificate:
Data: Data:
Version: 3 (0x2) Version: 3 (0x2)
-2
View File
@@ -1,5 +1,3 @@
- Make sure your PEM file starts with a dash -
Certificate: Certificate:
Data: Data:
Version: 3 (0x2) Version: 3 (0x2)
-2
View File
@@ -1,5 +1,3 @@
- Make sure your PEM file starts with a dash -
Certificate: Certificate:
Data: Data:
Version: 3 (0x2) Version: 3 (0x2)
+26 -4
View File
@@ -62,6 +62,14 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
mg_crc32(0, mm.topic.buf, mm.topic.len); mg_crc32(0, mm.topic.buf, mm.topic.len);
mg_crc32(0, mm.data.buf, mm.data.len); mg_crc32(0, mm.data.buf, mm.data.len);
mg_crc32(0, mm.dgram.buf, mm.dgram.len); mg_crc32(0, mm.dgram.buf, mm.dgram.len);
{
struct mg_mqtt_prop prop;
size_t ofs = 0;
while ((ofs = mg_mqtt_next_prop(&mm, &prop, ofs)) > 0) {
mg_crc32(0, prop.key.buf, prop.key.len);
mg_crc32(0, prop.val.buf, prop.val.len);
}
}
} }
mg_mqtt_parse(NULL, 0, 5, &mm); mg_mqtt_parse(NULL, 0, 5, &mm);
@@ -86,9 +94,19 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Test built-in TCP/IP stack // Test built-in TCP/IP stack
if (size > 0) { if (size > 0) {
struct mg_tcpip_if mif = {.ip = 0x01020304, struct mg_tcpip_if mif = {.ip = 1,
.mask = 255, .mask = 255,
.gw = 0x01010101, .gw = 1,
.gw_ready = true,
.state = MG_TCPIP_STATE_READY,
#if MG_ENABLE_IPV6
.ip6[0] = 1;
.prefix[0] = 1;
.prefix_len = 64;
.gw6[0] = 1;
.gw6_ready = true;
.state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops
#endif
.driver = &mg_tcpip_driver_mock}; .driver = &mg_tcpip_driver_mock};
struct mg_mgr mgr; struct mg_mgr mgr;
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
@@ -100,11 +118,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
memcpy(pkt, data, size); memcpy(pkt, data, size);
if (size > sizeof(*eth)) { if (size > sizeof(*eth)) {
static size_t i; static size_t i;
uint16_t eth_types[] = {0x800, 0x806, 0x86dd}; // IPv4, ARP, IPv6 // eth_types[] exists in l2_eth.c
memcpy(eth->dst, mif.mac, 6); // Set valid destination MAC memcpy(eth->dst, mif.mac, 6); // Set valid destination MAC
// send all handled eth types, then 2 random ones // send all handled eth types, then 2 random ones
if (i >= (sizeof(eth_types) / sizeof(eth_types[0]) + 2)) i = 0; if (i >= (sizeof(eth_types) / sizeof(eth_types[0]) + 2)) i = 0;
if (i < (sizeof(eth_types) / sizeof(eth_types[0]))) eth->type = (eth_types[i++]); if (i < (sizeof(eth_types) / sizeof(eth_types[0]))) eth->type = mg_htons(eth_types[i++]);
// build proper layer-3 datagrams, to be able to exercise layers above // build proper layer-3 datagrams, to be able to exercise layers above
if (eth->type == mg_htons(0x800) && size > (sizeof(*eth) + sizeof(struct ip))) { // IPv4 if (eth->type == mg_htons(0x800) && size > (sizeof(*eth) + sizeof(struct ip))) { // IPv4
static size_t j; static size_t j;
@@ -151,6 +169,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
} }
} }
#if defined(MAIN)
printf("Sending to net_builtin:\n");
mg_hexdump(pkt, size);
#endif
mg_tcpip_rx(&mif, pkt, size); mg_tcpip_rx(&mif, pkt, size);
// Test HTTP serving (via our built-in TCP/IP stack) // Test HTTP serving (via our built-in TCP/IP stack)
+120
View File
@@ -0,0 +1,120 @@
#define MIPPORTTEST_USING_DHCP 1
#include <sys/socket.h>
#include <sys/ioctl.h>
#include "mip_x_test.c"
struct port_driver_data {
int sockfd;
struct sockaddr_in port_addr;
struct sockaddr_in tap_addr;
};
// MIP Socket driver
static size_t sock_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
ssize_t received = recv(((struct port_driver_data *)ifp->driver_data)->sockfd, buf, len, 0);
usleep(1); // This is to avoid 100% CPU
if (received < 0) return 0;
return (size_t) received;
}
static size_t sock_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
ssize_t res = send(((struct port_driver_data *)ifp->driver_data)->sockfd, buf, len, 0);
if (res < 0) {
MG_ERROR(("sock_tx failed: %d", errno));
return 0;
}
return (size_t) res;
}
static bool sock_poll(struct mg_tcpip_if *ifp, bool s1) {
return s1 && ifp->driver_data ? true : false;
}
int main(void) {
bool result;
const char *debug_level = getenv("V");
// Setup interface
const uint16_t port = 0xAA55; // UDP port
const uint16_t tap_port = 0x55AA; // UDP port for TAP socket
const char *mac = "02:00:01:02:03:78"; // MAC address
struct port_driver_data pdd;
pdd.sockfd = socket(AF_INET, SOCK_DGRAM, 0);
pdd.port_addr.sin_family = AF_INET;
pdd.port_addr.sin_addr.s_addr = htonl(INADDR_ANY);
pdd.port_addr.sin_port = htons(port);
if (bind(pdd.sockfd, (struct sockaddr *) &pdd.port_addr, sizeof(pdd.port_addr)) < 0) return EXIT_FAILURE;
printf("Opened UDP socket: 127.0.0.1:%u (0x%04x)\n", port, port);
pdd.tap_addr.sin_family = AF_INET;
pdd.tap_addr.sin_addr.s_addr = htonl(0x7f000001U); // 127.0.0.1
pdd.tap_addr.sin_port = htons(0x55AA);
if (connect(pdd.sockfd, (struct sockaddr *) &pdd.tap_addr, sizeof(pdd.tap_addr)) < 0) return EXIT_FAILURE;
printf("Connected to TAP UDP socket: 127.0.0.1:%u (0x%04x)\n", tap_port, tap_port);
usleep(200000); // 200 ms
if (debug_level == NULL) debug_level = "3";
mg_log_set(atoi(debug_level));
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager
// MIP driver
struct mg_tcpip_driver driver;
memset(&driver, 0, sizeof(driver));
driver.tx = sock_tx;
driver.poll = sock_poll;
driver.rx = sock_rx;
struct mg_tcpip_if mif;
memset(&mif, 0, sizeof(mif));
mif.driver = &driver;
mif.driver_data = &pdd;
#if MIPPORTTEST_USING_DHCP == 1
#else
mif.ip = mg_htonl(MG_U32(192, 168, 32, 2)); // Triggering a network failure
mif.mask = mg_htonl(MG_U32(255, 255, 255, 0));
mif.gw = mg_htonl(MG_U32(192, 168, 32, 1));
#endif
sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1],
&mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]);
mg_tcpip_init(&mgr, &mif);
MG_INFO(("Init done, starting main loop"));
usleep(200000); // 200 ms
// Stack initialization, Network configuration (DHCP lease, ...)
#if MIPPORTTEST_USING_DHCP == 0
MG_INFO(("MIF configuration: Static IP"));
ASSERT(mif.ip != 0); // Check we have a satic IP assigned
mg_mgr_poll(&mgr, 100); // For initialisation
#else
MG_INFO(("MIF configuration: DHCP"));
ASSERT(!mif.ip); // Check we are set for DHCP
int pc = 500; // Timeout on DHCP lease 500 ~ approx 5s (typical delay <1s)
while (((pc--) > 0) && !mif.ip) {
mg_mgr_poll(&mgr, 100);
usleep(10000); // 10 ms
}
if (!mif.ip) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n"));
ASSERT(mif.ip); // We have an IP (lease or static)
#endif
while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 100);
usleep(10000); // 10 ms
}
// RUN TESTS
result = mip_x_test(&mgr);
close(pdd.sockfd);
if (!result) return EXIT_FAILURE;
return EXIT_SUCCESS;
}
+4 -452
View File
@@ -1,10 +1,5 @@
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
#define MIPTAPTEST_USING_DHCP 1 #define MIPTAPTEST_USING_DHCP 1
#define FETCH_BUF_SIZE (16 * 1024)
#include <sys/socket.h> #include <sys/socket.h>
#ifndef __OpenBSD__ #ifndef __OpenBSD__
#include <linux/if.h> #include <linux/if.h>
@@ -16,112 +11,8 @@
#endif #endif
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "mongoose.c" #include "mip_x_test.c"
#include "driver_mock.c"
#ifdef MQTT_LOCALHOST
#define MQTT_URL "mqtt://127.0.0.1:1883"
#else
#define MQTT_URL "mqtt://broker.hivemq.com:1883"
#endif
#if MG_TLS == MG_TLS_BUILTIN
#define MQTTS_URL "mqtts://mongoose.ws:8883" // test requires TLS 1.3
#define MQTTS_CA mg_str(s_ca_cert)
static const char *s_ca_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
"-----END CERTIFICATE-----\n";
#elif MG_TLS
#ifdef MQTT_LOCALHOST
// we'll generate MQTTS_URL
#define MQTTS_CA mg_str(s_ca_cert)
static const char *s_ca_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n"
"b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n"
"TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n"
"fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n"
"epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n"
"us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n"
"-----END CERTIFICATE-----\n";
#else
#define MQTTS_URL "mqtts://broker.hivemq.com:8883"
#define MQTTS_CA mg_unpacked("/data/ca.pem")
#endif // MQTT_LOCALHOST
#endif
static char *host_ip;
static int s_num_tests = 0;
static bool s_error = false;
#ifdef NO_ABORT
static int s_abort = 0;
#define ABORT() ++s_abort, s_error = true
#else
#ifdef NO_SLEEP_ABORT
#define ABORT() abort()
#else
#define ABORT() \
sleep(2); /* 2s, GH print reason */ \
abort();
#endif
#endif
#define ASSERT(expr) \
do { \
s_num_tests++; \
if (!(expr)) { \
printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \
fflush(stdout); \
ABORT(); \
} \
} while (0)
static struct mg_http_message gethm(const char *buf) {
struct mg_http_message hm;
memset(&hm, 0, sizeof(hm));
mg_http_parse(buf, strlen(buf), &hm);
return hm;
}
static int cmpbody(const char *buf, const char *str) {
struct mg_str s = mg_str(str);
struct mg_http_message hm = gethm(buf);
size_t len = strlen(buf);
if (hm.body.len > len) hm.body.len = len - (size_t) (hm.body.buf - buf);
return mg_strcmp(hm.body, s);
}
// MIP TUNTAP driver // MIP TUNTAP driver
static size_t tap_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) { static size_t tap_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
@@ -144,313 +35,9 @@ static bool tap_poll(struct mg_tcpip_if *ifp, bool s1) {
return s1 && ifp->driver_data ? true : false; return s1 && ifp->driver_data ? true : false;
} }
static void eh1(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts *topts = (struct mg_tls_opts *) c->fn_data;
if (ev == MG_EV_ACCEPT && topts != NULL) mg_tls_init(c, topts);
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
MG_DEBUG(("[%.*s %.*s] message len %d", (int) hm->method.len,
hm->method.buf, (int) hm->uri.len, hm->uri.buf,
(int) hm->message.len));
if (mg_match(hm->uri, mg_str("/foo/*"), NULL)) {
mg_http_reply(c, 200, "", "uri: %.*s", hm->uri.len - 5, hm->uri.buf + 5);
} else if (mg_match(hm->uri, mg_str("/ws"), NULL)) {
mg_ws_upgrade(c, hm, NULL);
} else if (mg_match(hm->uri, mg_str("/body"), NULL)) {
mg_http_reply(c, 200, "", "%.*s", (int) hm->body.len, hm->body.buf);
} else {
struct mg_http_serve_opts sopts;
memset(&sopts, 0, sizeof(sopts));
sopts.root_dir = "./data";
mg_http_serve_dir(c, hm, &sopts);
}
} else if (ev == MG_EV_WS_OPEN) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
ASSERT(mg_strcmp(hm->uri, mg_str("/ws")) == 0);
mg_ws_send(c, "opened", 6, WEBSOCKET_OP_BINARY);
} else if (ev == MG_EV_WS_MSG) {
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
mg_ws_send(c, wm->data.buf, wm->data.len, WEBSOCKET_OP_BINARY);
}
}
struct fetch_data {
char *buf;
const char *url;
int code, closed;
};
static void fcb(struct mg_connection *c, int ev, void *ev_data) {
struct fetch_data *fd = (struct fetch_data *) c->fn_data;
if (ev == MG_EV_CONNECT) {
MG_DEBUG(("CONNECT"));
if (mg_url_is_ssl(fd->url)) {
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts)); // read CA from packed_fs
if (host_ip != NULL && strstr(fd->url, host_ip) != NULL) {
MG_DEBUG(("Local connection, using self-signed certificates"));
opts.name = mg_str_s("localhost");
opts.ca = mg_unpacked("/certs/ca.crt");
} else {
opts.name = mg_url_host(fd->url);
opts.ca = mg_unpacked("/data/ca.pem");
#if MG_TLS == MG_TLS_BUILTIN
// our TLS does not search for the proper CA in a bundle
opts.ca = mg_file_read(&mg_fs_posix, "data/e5.crt");
#endif
}
mg_tls_init(c, &opts);
#if MG_TLS == MG_TLS_BUILTIN
mg_free((void *) opts.ca.buf);
#endif
}
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len,
hm->message.buf);
fd->code = atoi(hm->uri.buf);
fd->closed = 1;
c->is_closing = 1;
MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len,
hm->message.buf));
(void) c;
} else if (ev == MG_EV_CLOSE) {
MG_DEBUG(("CLOSE"));
fd->closed = 1;
} else if (ev == MG_EV_READ) {
long bytes = *(long *) ev_data;
MG_DEBUG(("READ %d: %.*s", (int) bytes, (int) bytes, c->recv.buf));
}
}
static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
const char *fmt, ...) {
struct fetch_data fd = {buf, url, 0, 0};
int i;
struct mg_connection *c = NULL;
va_list ap;
mg_mgr_poll(mgr, 0); // update ifp->now to avoid ARP lookup using an old
// timestamp (from an ancient call in other test)
c = mg_http_connect(mgr, url, fcb, &fd);
ASSERT(c != NULL);
va_start(ap, fmt);
mg_vprintf(c, fmt, &ap);
va_end(ap);
buf[0] = '\0';
// - TLS: multiple (small) records: allow enough loops so mg_mgr_poll can
// process buffered records when no more frames are coming in
for (i = 0; i < 500 && buf[0] == '\0' && !fd.closed; i++) {
mg_mgr_poll(mgr, 0);
usleep(5000); // 5 ms. Slow down poll loop to ensure packet transit, but
// allow enough loops to get the ARP response, otherwise,
// given enough traffic, the timer expires before we get a
// chance to see the response
}
if (!fd.closed) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
return fd.code;
}
static void test_http_client(struct mg_mgr *mgr) {
char buf[FETCH_BUF_SIZE];
int rc = 0;
const bool ipv6 = MG_ENABLE_IPV6;
#if MG_TLS
if (ipv6) {
rc = fetch(mgr, buf, "https://ipv6.google.com",
"GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n");
} else {
rc = fetch(mgr, buf, "https://cesanta.com",
"GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n");
}
ASSERT(rc == 200); // OK
#else
if (ipv6) {
rc = fetch(mgr, buf, "http://ipv6.google.com",
"GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n");
ASSERT(rc == 200); // OK
} else {
rc = fetch(mgr, buf, "http://cesanta.com",
"GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n");
ASSERT(rc == 301); // OK: Permanently moved (HTTP->HTTPS redirect)
}
#endif
}
static struct mg_connection *s_conn;
static char s_topic[16];
struct mqtt_data {
char *url;
bool passed;
};
static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data) {
struct mqtt_data *data = (struct mqtt_data *) c->fn_data;
if (ev == MG_EV_CONNECT) {
MG_DEBUG(("CONNECT"));
#if MG_TLS
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts));
opts.ca = MQTTS_CA;
opts.name = mg_url_host(data->url);
mg_tls_init(c, &opts);
#endif
} else if (ev == MG_EV_MQTT_OPEN) {
MG_DEBUG(("MQTT CONNECT"));
struct mg_mqtt_opts sub_opts;
memset(&sub_opts, 0, sizeof(sub_opts));
sub_opts.topic = mg_str(mg_random_str(s_topic, sizeof(s_topic)));
sub_opts.qos = 1;
mg_mqtt_sub(c, &sub_opts);
struct mg_mqtt_opts pub_opts;
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = sub_opts.topic;
pub_opts.message = mg_str("hi");
pub_opts.qos = 1, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
} else if (ev == MG_EV_MQTT_MSG) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
MG_DEBUG(("TOPIC: %.*s, MSG: %.*s", (int) mm->topic.len, mm->topic.buf,
mm->data.len > 10 ? 10 : (int) mm->data.len, mm->data.buf));
ASSERT(mm->topic.len == strlen(s_topic) &&
strncmp(mm->topic.buf, s_topic, mm->topic.len) == 0);
if (mm->data.len == 2 && strncmp(mm->data.buf, "hi", 2) == 0) {
struct mg_mqtt_opts pub_opts;
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = mm->topic;
// send more than 1 record, content is not relevant
pub_opts.message = mg_str_n((char *)(size_t) mqtt_fn, 21098);
pub_opts.qos = 1, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
} else if (mm->data.len == 8 && strncmp(mm->data.buf, "farewell", 8) == 0) {
// close on farewell
MG_INFO(("%lu CLOSING", c->id));
mg_mqtt_disconnect(c, NULL);
data->passed = true;
} else if (mm->data.len == 21098) {
struct mg_mqtt_opts pub_opts;
ASSERT(memcmp((const char *) (size_t) mqtt_fn, mm->data.buf, 21098) == 0);
// send farewell after receiving big data
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = mm->topic;
pub_opts.message = mg_str("farewell");
pub_opts.qos = 1, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
}
} else if (ev == MG_EV_CLOSE) {
MG_DEBUG(("CLOSE"));
s_conn = NULL;
} else if (ev == MG_EV_ERROR) {
MG_ERROR(("%lu ERROR %s", c->id, (char *) ev_data));
}
}
static void test_mqtt_connsubpub(struct mg_mgr *mgr) {
struct mqtt_data data;
struct mg_mqtt_opts opts;
memset(&opts, 0, sizeof(opts));
opts.clean = true, opts.version = 4;
data.passed = false;
#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN
if (host_ip == NULL) {
printf("\nMQTT_LOCALHOST defined but no HOST_IP provided, skipping MQTTS tests\n");
return;
}
printf("HOST_IP: %s\n", host_ip);
#endif
#if MG_TLS
#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN
data.url = mg_mprintf("mqtts://%s:8883", host_ip);
#else
data.url = strdup(MQTTS_URL);
#endif
#else
#ifdef MQTT_LOCALHOST
data.url = mg_mprintf("mqtt://%s:1883", host_ip);
#else
data.url = strdup(MQTT_URL);
#endif
#endif
s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
ASSERT(s_conn != NULL);
for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
mg_mgr_poll(mgr, 0);
usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit
}
ASSERT(data.passed);
mg_mgr_poll(mgr, 0);
free(data.url);
}
#include <pthread.h>
static void *poll_thread(void *p) {
struct mg_mgr *mgr = (struct mg_mgr *) p;
int i;
for (i = 0; i < 300; i++) {
mg_mgr_poll(mgr, 0);
usleep(10000); // 10 ms. Slow down poll loop to ensure packet transit
}
return NULL;
}
static void test_http_server(struct mg_mgr *mgr) {
struct mg_connection *c;
char *cmd;
pthread_t thread_id = (pthread_t) 0;
#if MG_TLS
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts));
// opts.ca = mg_str(s_tls_ca);
opts.cert = mg_unpacked("/certs/server.crt");
opts.key = mg_unpacked("/certs/server.key");
c = mg_http_listen(mgr, "https://0.0.0.0:12347", eh1, &opts);
cmd = mg_mprintf("./mip_curl.sh --insecure https://%M:12347", mg_print_ip4,
&mgr->ifp->ip);
#else
c = mg_http_listen(mgr, "http://0.0.0.0:12347", eh1, NULL);
cmd =
mg_mprintf("./mip_curl.sh http://%M:12347", mg_print_ip4, &mgr->ifp->ip);
#endif
ASSERT(c != NULL);
ASSERT (mg_send(c, "NADA", 0)); // check mg_send allows len=0
pthread_create(&thread_id, NULL, poll_thread,
mgr); // simpler this way, no concurrency anyway
MG_DEBUG(("CURL"));
ASSERT(system(cmd) == 0); // wait for curl
MG_DEBUG(("MONGOOSE"));
pthread_join(thread_id, NULL); // wait for Mongoose
MG_DEBUG(("DONE"));
free(cmd);
}
static void test_tls(struct mg_mgr *mgr) {
#if MG_TLS
char *url;
char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile
struct mg_str data = mg_unpacked("/Makefile");
if (host_ip == NULL) {
printf("\nNo HOST_IP provided, skipping TLS tests\n");
return;
}
printf("HOST_IP: %s\n", host_ip);
// - POST a large file, make sure we drain TLS buffers and read all: done at
// server test, using curl as POSTing client
// - Fire patched server, test multiple TLS records per TCP segment handling
url = mg_mprintf("https://%s:8443", host_ip); // for historic reasons
ASSERT(system("tls_multirec/server -d tls_multirec &") == 0);
sleep(1);
ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200);
ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile
ASSERT(system("killall tls_multirec/server") == 0);
free(url);
#else
(void) cmpbody("", "");
(void) mgr;
#endif
}
int main(void) { int main(void) {
bool result;
const char *debug_level = getenv("V"); const char *debug_level = getenv("V");
// Setup interface // Setup interface
const char *iface = "tap0"; // Network iface const char *iface = "tap0"; // Network iface
@@ -485,9 +72,6 @@ int main(void) {
if (debug_level == NULL) debug_level = "3"; if (debug_level == NULL) debug_level = "3";
mg_log_set(atoi(debug_level)); mg_log_set(atoi(debug_level));
host_ip = getenv("HOST_IP");
// Events
struct mg_mgr mgr; // Event manager struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager mg_mgr_init(&mgr); // Initialise event manager
@@ -540,41 +124,9 @@ int main(void) {
usleep(10000); // 10 ms usleep(10000); // 10 ms
} }
#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true");
// RUN TESTS // RUN TESTS
usleep(500000); // 500 ms result = mip_x_test(&mgr);
s_error = false;
test_http_client(&mgr);
DASHBOARD("http_client");
usleep(500000); // 500 ms
s_error = false;
test_http_server(&mgr);
DASHBOARD("http_server");
usleep(500000); // 500 ms
s_error = false;
test_tls(&mgr);
DASHBOARD("tls");
usleep(500000); // 500 ms
s_error = false;
test_mqtt_connsubpub(&mgr);
DASHBOARD("mqtt");
// Clear
s_error = false;
mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL); // Deconstruction OK
close(fd); close(fd);
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true"); if (!result) return EXIT_FAILURE;
// last entry with no comma
#ifdef NO_ABORT
if (s_abort != 0) return EXIT_FAILURE;
#endif
printf("SUCCESS. Total tests: %d\n", s_num_tests);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+271 -47
View File
@@ -41,10 +41,23 @@ struct ipp {
static void test_csum(void) { static void test_csum(void) {
uint8_t ip[20] = {0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x28, 0x11, uint8_t ip[20] = {0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x28, 0x11,
0x94, 0xcf, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01}; 0x94, 0xcf, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01};
uint8_t pseudo_udp_odd[53] = {
0xc0, 0xa8, 0x45, 0x58, 0x08, 0x08, 0x08, 0x08, 0x00, 0x11, 0x00,
0x29, 0xad, 0x67, 0x00, 0x35, 0x00, 0x29, 0xaf, 0xf8, 0x00, 0x01,
0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x74, 0x69, 0x6d, 0x65, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01};
ASSERT(ipcsum(ip, 20) == 0); ASSERT(ipcsum(ip, 20) == 0);
ASSERT(ipcsum(pseudo_udp_odd, 53) == 0);
// UDP and TCP checksum calc funcions use the same basic calls as ipcsum() // UDP and TCP checksum calc funcions use the same basic calls as ipcsum()
} }
#if !MG_ENABLE_IPV6
#define udp6csum_ok(d, u) true
#define tcp6csum_ok(d, t) true
#endif
static bool executed = false; static bool executed = false;
static void mif_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) { static void mif_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
@@ -78,8 +91,8 @@ static void mif6_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
static void test_state6change(void) { static void test_state6change(void) {
struct mg_tcpip_if iface; struct mg_tcpip_if iface;
memset(&iface, 0, sizeof(iface)); memset(&iface, 0, sizeof(iface));
iface.ip6[0] = (uint64_t) mg_htonl(0x01020304); iface.ip6[0] = mg_htonll(0x01020304);
iface.ip6[1] = (uint64_t) mg_htonl(0x05060708); iface.ip6[1] = mg_htonll(0x05060708);
iface.state6 = MG_TCPIP_STATE_READY; iface.state6 = MG_TCPIP_STATE_READY;
iface.driver = &mg_tcpip_driver_mock; iface.driver = &mg_tcpip_driver_mock;
iface.fn = mif6_fn; iface.fn = mif6_fn;
@@ -145,7 +158,7 @@ static void test_poll(void) {
memset(&mif, 0, sizeof(mif)); memset(&mif, 0, sizeof(mif));
mif.driver = &mg_tcpip_driver_mock; mif.driver = &mg_tcpip_driver_mock;
mg_tcpip_init(&mgr, &mif); mg_tcpip_init(&mgr, &mif);
mg_http_listen(&mgr, "http://127.0.0.1:12346", ph, &count); mg_http_listen(&mgr, "http://0.0.0.0:12346", ph, &count);
for (i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0); for (i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0);
ASSERT(count == 10); ASSERT(count == 10);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
@@ -180,7 +193,7 @@ static size_t if_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
if (len > driver_data->len) len = driver_data->len; if (len > driver_data->len) len = driver_data->len;
memcpy(buf, driver_data->buf, len); memcpy(buf, driver_data->buf, len);
driver_data->len = 0; // cleaning up the buffer driver_data->len = 0; // cleaning up the buffer
driver_data->tx_ready = false; driver_data->tx_ready = false;
return len; return len;
} }
@@ -195,7 +208,7 @@ static void create_tcp_seg(struct eth *e, struct ipp *ipp, uint32_t seq,
uint16_t dport, size_t payload_len, void *opts, uint16_t dport, size_t payload_len, void *opts,
unsigned int opts_len) { unsigned int opts_len) {
struct tcp t; struct tcp t;
memset(&t, 0, sizeof(struct tcp)); memset(&t, 0, sizeof(t));
t.flags = flags; t.flags = flags;
t.seq = mg_htonl(seq); t.seq = mg_htonl(seq);
t.ack = mg_htonl(ack); t.ack = mg_htonl(ack);
@@ -206,25 +219,33 @@ static void create_tcp_seg(struct eth *e, struct ipp *ipp, uint32_t seq,
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (ipp->ip6 != NULL) { if (ipp->ip6 != NULL) {
struct ip6 *ip = ipp->ip6; struct ip6 *ip = ipp->ip6;
struct tcp *tcp =
(struct tcp *) (s_driver_data.buf + sizeof(*e) + sizeof(*ip));
ip->plen = mg_htons((uint16_t) (4 * (t.off >> 4) + payload_len)); ip->plen = mg_htons((uint16_t) (4 * (t.off >> 4) + payload_len));
memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip)); memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip));
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip), &t, sizeof(t)); memcpy(tcp, &t, sizeof(t));
if (opts != NULL && opts_len) if (opts != NULL && opts_len)
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(t), opts, memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(t), opts,
opts_len); opts_len);
tcp->csum = p6csum(ip, tcp, (4 * (t.off >> 4) + payload_len));
s_driver_data.len = s_driver_data.len =
sizeof(*e) + sizeof(*ip) + sizeof(t) + payload_len + opts_len; sizeof(*e) + sizeof(*ip) + sizeof(t) + payload_len + opts_len;
} else } else
#endif #endif
{ {
struct ip *ip = ipp->ip4; struct ip *ip = ipp->ip4;
struct tcp *tcp =
(struct tcp *) (s_driver_data.buf + sizeof(*e) + sizeof(*ip));
ip->len = ip->len =
mg_htons((uint16_t) (sizeof(*ip) + 4 * (t.off >> 4) + payload_len)); mg_htons((uint16_t) (sizeof(*ip) + 4 * (t.off >> 4) + payload_len));
ip->csum = ipcsum(ip, sizeof(*ip)); // no options
memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip)); memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip));
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip), &t, sizeof(t)); ip->csum = 0;
memcpy(tcp, &t, sizeof(t));
if (opts != NULL && opts_len) if (opts != NULL && opts_len)
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(t), opts, memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(t), opts,
opts_len); opts_len);
tcp->csum = pcsum(ip, tcp, (4 * (t.off >> 4) + payload_len));
s_driver_data.len = s_driver_data.len =
sizeof(*e) + sizeof(*ip) + sizeof(t) + payload_len + opts_len; sizeof(*e) + sizeof(*ip) + sizeof(t) + payload_len + opts_len;
} }
@@ -250,14 +271,20 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
mif->driver_data = &s_driver_data; mif->driver_data = &s_driver_data;
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (ipp->ip6 != NULL) { if (ipp->ip6 != NULL) {
mif->ip6[0] = 1; mif->ip6[0] = mg_htonll(0x100000000000000);
mif->state = MG_TCPIP_STATE_READY; // so DHCP stops mif->prefix[0] = 1;
mif->prefix_len = 64;
mif->gw6[0] = mg_htonll(0x100000000000000);
mif->gw6_ready = true;
mif->state = MG_TCPIP_STATE_READY; // so DHCP stops
mif->state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops mif->state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops
} else } else
#endif #endif
{ {
mif->ip = 1; mif->ip = mg_htonl(0x1000000);
mif->mask = 255; // use router, to avoid firing an ARP request mif->gw = mg_htonl(0x1000000);
mif->gw_ready = true;
mif->mask = 255; // use router, to avoid firing an ARP request
mif->state = MG_TCPIP_STATE_READY; // so mg_send() works and DHCP stops mif->state = MG_TCPIP_STATE_READY; // so mg_send() works and DHCP stops
} }
mg_tcpip_init(mgr, mif); mg_tcpip_init(mgr, mif);
@@ -275,7 +302,7 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
ip->ver = 0x60; ip->ver = 0x60;
ip->next = proto; ip->next = proto;
// must be outside of Mongoose network to avoid firing NS requests // must be outside of Mongoose network to avoid firing NS requests
ip->src[0] = 2; ip->src[0] = mg_htonll(0x200000000000000);
ip->dst[0] = mif->ip6[0]; ip->dst[0] = mif->ip6[0];
} else } else
#endif #endif
@@ -285,7 +312,7 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
ip->ver = (4 << 4) | 5; ip->ver = (4 << 4) | 5;
ip->proto = proto; ip->proto = proto;
// must be outside of Mongoose network to avoid firing ARP requests // must be outside of Mongoose network to avoid firing ARP requests
ip->src = 2; ip->src = mg_htonl(0x2000000);
ip->dst = mif->ip; ip->dst = mif->ip;
} }
} }
@@ -338,7 +365,8 @@ static void test_tcp_basics(bool ipv6) {
struct ip ip; struct ip ip;
struct ip6 ip6; struct ip6 ip6;
struct ipp ipp; struct ipp ipp;
struct tcp *t = (struct tcp *) (s_driver_data.buf + sizeof(e) + (!ipv6 ? sizeof(ip) : sizeof(ip6))); struct tcp *t = (struct tcp *) (s_driver_data.buf + sizeof(e) +
(!ipv6 ? sizeof(ip) : sizeof(ip6)));
struct ip *i = (struct ip *) (s_driver_data.buf + sizeof(e)); struct ip *i = (struct ip *) (s_driver_data.buf + sizeof(e));
struct ip6 *i6 = (struct ip6 *) (s_driver_data.buf + sizeof(e)); struct ip6 *i6 = (struct ip6 *) (s_driver_data.buf + sizeof(e));
uint64_t start, now; uint64_t start, now;
@@ -359,11 +387,14 @@ static void test_tcp_basics(bool ipv6) {
ASSERT(t->seq == mg_htonl(0)); ASSERT(t->seq == mg_htonl(0));
ASSERT(t->ack == mg_htonl(1235)); ASSERT(t->ack == mg_htonl(1235));
if (ipv6) { if (ipv6) {
ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
ASSERT(tcp6csum_ok(i6, t));
} else { } else {
ASSERT(i->src == 1 && i->dst == 2); ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
ASSERT(tcpcsum_ok(i, t));
} }
// send SYN+ACK, expect RST // send SYN+ACK, expect RST
create_tcp_seg(&e, &ipp, 1234, 4321, TH_SYN | TH_ACK, 1, 69, 0, NULL, 0); create_tcp_seg(&e, &ipp, 1234, 4321, TH_SYN | TH_ACK, 1, 69, 0, NULL, 0);
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
@@ -413,9 +444,10 @@ static void test_tcp_basics(bool ipv6) {
ASSERT(t->flags == TH_RST); ASSERT(t->flags == TH_RST);
ASSERT(t->seq == mg_htonl(4321)); ASSERT(t->seq == mg_htonl(4321));
if (ipv6) { if (ipv6) {
ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
} else { } else {
ASSERT(i->src == 1 && i->dst == 2); ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
} }
// send SYN+ACK, expect RST // send SYN+ACK, expect RST
@@ -451,9 +483,10 @@ static void test_tcp_basics(bool ipv6) {
ASSERT((t->flags == TH_ACK)); ASSERT((t->flags == TH_ACK));
ASSERT((t->ack == mg_htonl(1001))); // expecting 1001, dude ASSERT((t->ack == mg_htonl(1001))); // expecting 1001, dude
if (ipv6) { if (ipv6) {
ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
} else { } else {
ASSERT(i->src == 1 && i->dst == 2); ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
} }
// segment with seq_no way out of window // segment with seq_no way out of window
@@ -506,8 +539,8 @@ static void test_tcp_basics(bool ipv6) {
if (received_response(&s_driver_data) && (ipv6 ? i6->next : i->proto) == 6) if (received_response(&s_driver_data) && (ipv6 ? i6->next : i->proto) == 6)
break; // check first break; // check first
now = mg_millis() - start; now = mg_millis() - start;
} while (now < (12 * MIP_TCP_FIN_MS) / 10); } while (now < (12 * MG_TCPIP_FIN_MS) / 10);
ASSERT(now > MIP_TCP_FIN_MS); ASSERT(now > MG_TCPIP_FIN_MS);
// make sure it is closed // make sure it is closed
ASSERT(mgr.conns->next == NULL); // only one connection: the listener ASSERT(mgr.conns->next == NULL); // only one connection: the listener
@@ -524,7 +557,7 @@ static void test_tcp_basics(bool ipv6) {
while (!received_response(&s_driver_data)) { while (!received_response(&s_driver_data)) {
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
now = mg_millis() - start; now = mg_millis() - start;
if (now > 2 * MIP_TCP_ACK_MS) if (now > 2 * MG_TCPIP_ACK_MS)
ASSERT(0); // response should have been received by now ASSERT(0); // response should have been received by now
} }
ASSERT((t->seq == mg_htonl(2))); ASSERT((t->seq == mg_htonl(2)));
@@ -561,8 +594,8 @@ static void test_tcp_basics(bool ipv6) {
if (received_response(&s_driver_data)) break; // check first if (received_response(&s_driver_data)) break; // check first
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
now = mg_millis() - start; now = mg_millis() - start;
} while (now < 2 * MIP_TCP_ACK_MS); // keep timeout below 1s (DHCP discover) } while (now < 2 * MG_TCPIP_ACK_MS); // keep timeout below 1s (DHCP discover)
ASSERT(now >= 2 * MIP_TCP_ACK_MS); ASSERT(now >= 2 * MG_TCPIP_ACK_MS);
// make sure it is still open // make sure it is still open
ASSERT(mgr.conns->next != ASSERT(mgr.conns->next !=
NULL); // more than one connection: the listener + us NULL); // more than one connection: the listener + us
@@ -594,9 +627,10 @@ static void test_tcp_basics(bool ipv6) {
if (received_response(&s_driver_data)) break; // check first if (received_response(&s_driver_data)) break; // check first
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
now = mg_millis() - start; now = mg_millis() - start;
} while (now < 2 * MIP_TCP_ACK_MS); // keep timeout below 1s (DHCP discover) } while (now < 2 * MG_TCPIP_ACK_MS); // keep timeout below 1s (DHCP discover)
// ASSERT(now < 2 * MIP_TCP_ACK_MS); ******** WE FAIL THIS, Mongoose does not // ASSERT(now < 2 * MG_TCPIP_ACK_MS); ******** WE FAIL THIS, Mongoose does
// retransmit, FIN is not an additional element in the stream ASSERT((t->seq // not retransmit, FIN is not an additional element in the stream
// ASSERT((t->seq
// == mg_htonl(2))); ASSERT((t->ack == mg_htonl(1001))); ASSERT(t->flags == // == mg_htonl(2))); ASSERT((t->ack == mg_htonl(1001))); ASSERT(t->flags ==
// (TH_FIN | TH_ACK)); // Mongoose retransmits FIN // (TH_FIN | TH_ACK)); // Mongoose retransmits FIN
// send FIN + ACK // send FIN + ACK
@@ -622,7 +656,7 @@ static void test_tcp_basics(bool ipv6) {
while (!received_response(&s_driver_data)) { while (!received_response(&s_driver_data)) {
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
now = mg_millis() - start; now = mg_millis() - start;
if (now > 2 * MIP_TCP_ACK_MS) if (now > 2 * MG_TCPIP_ACK_MS)
ASSERT(0); // response should have been received by now ASSERT(0); // response should have been received by now
} }
ASSERT((t->seq == mg_htonl(2))); ASSERT((t->seq == mg_htonl(2)));
@@ -659,7 +693,7 @@ static void test_tcp_basics(bool ipv6) {
// this creates a listener we won't use // this creates a listener we won't use
init_tcp_tests(&mgr, &e, &ipp, &driver, &mif, tcpclosure_fn); init_tcp_tests(&mgr, &e, &ipp, &driver, &mif, tcpclosure_fn);
// must be outside of our network to avoid firing ARP requests // must be outside of our network to avoid firing ARP requests
if (ipv6) { if (ipv6) {
c = mg_connect(&mgr, "tcp://[200::]:1234/", client_fn, &event); c = mg_connect(&mgr, "tcp://[200::]:1234/", client_fn, &event);
} else { } else {
@@ -670,15 +704,18 @@ static void test_tcp_basics(bool ipv6) {
ASSERT((t->flags == TH_SYN)); ASSERT((t->flags == TH_SYN));
ASSERT(event == 255); ASSERT(event == 255);
if (ipv6) { if (ipv6) {
ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
ASSERT(tcp6csum_ok(i6, t));
} else { } else {
ASSERT(i->src == 1 && i->dst == 2); ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
ASSERT(tcpcsum_ok(i, t));
} }
// invalid SYN + ACK to connecting client (after SYN...), send ACK != seq // invalid SYN + ACK to connecting client (after SYN...), send ACK != seq
ackno = mg_ntohl(t->seq) + 1000; ackno = mg_ntohl(t->seq) + 1000;
// create_tcp_seg(&e, &ipp, 4321, ackno, TH_SYN | TH_ACK, 1234, // create_tcp_seg(&e, &ipp, 4321, ackno, TH_SYN | TH_ACK, 1234,
// mg_ntohs(c->loc.port), 0, NULL, 0); mg_mgr_poll(&mgr, 0); // mg_ntohs(c->loc.port), 0, NULL, 0); mg_mgr_poll(&mgr, 0);
// while(!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0); // while(!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
// ASSERT((t->flags == (TH_RST | TH_ACK))); // ASSERT((t->flags == (TH_RST | TH_ACK)));
// ******** WE FAIL THIS, Mongoose does not validate the ACK number // ******** WE FAIL THIS, Mongoose does not validate the ACK number
@@ -757,7 +794,7 @@ static void test_tcp_retransmit(void) {
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
now = mg_millis() - start; now = mg_millis() - start;
// we wait enough time for a reply // we wait enough time for a reply
if (now > 2 * MIP_TCP_ACK_MS) { if (now > 2 * MG_TCPIP_ACK_MS) {
response_recv = false; response_recv = false;
break; break;
} }
@@ -771,7 +808,7 @@ static void test_tcp_retransmit(void) {
while (!received_response(&s_driver_data)) { while (!received_response(&s_driver_data)) {
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
now = mg_millis() - start; now = mg_millis() - start;
if (now > 2 * MIP_TCP_ACK_MS) if (now > 2 * MG_TCPIP_ACK_MS)
ASSERT(0); // response should have been received by now ASSERT(0); // response should have been received by now
} }
ASSERT((t->flags == TH_ACK)); ASSERT((t->flags == TH_ACK));
@@ -790,7 +827,7 @@ static void test_tcp_retransmit(void) {
while (!received_response(&s_driver_data)) { while (!received_response(&s_driver_data)) {
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
now = mg_millis() - start; now = mg_millis() - start;
if (now > 2 * MIP_TCP_ACK_MS) if (now > 2 * MG_TCPIP_ACK_MS)
ASSERT(0); // response should have been received by now ASSERT(0); // response should have been received by now
} }
ASSERT((t->flags == TH_ACK)); ASSERT((t->flags == TH_ACK));
@@ -919,7 +956,7 @@ static void test_tcp_backlog(void) {
while (!received_response(&s_driver_data) || i->proto != 6) while (!received_response(&s_driver_data) || i->proto != 6)
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
ASSERT(t->flags == (TH_SYN | TH_ACK)); ASSERT(t->flags == (TH_SYN | TH_ACK));
seqnos[j] = ntohl(t->seq); seqnos[j] = mg_ntohl(t->seq);
MG_VERBOSE(("SEQ: %p", seqnos[j])); MG_VERBOSE(("SEQ: %p", seqnos[j]));
} }
// check backlog is full and MSS are there // check backlog is full and MSS are there
@@ -969,7 +1006,7 @@ static void test_tcp_backlog(void) {
static void test_tcp(bool ipv6) { static void test_tcp(bool ipv6) {
test_tcp_basics(ipv6); test_tcp_basics(ipv6);
if(!ipv6) { if (!ipv6) {
test_tcp_backlog(); test_tcp_backlog();
test_tcp_retransmit(); test_tcp_retransmit();
} }
@@ -984,7 +1021,7 @@ static void udp_fn(struct mg_connection *c, int ev, void *ev_data) {
static void create_udp_dat(struct eth *e, struct ipp *ipp, uint16_t sport, static void create_udp_dat(struct eth *e, struct ipp *ipp, uint16_t sport,
uint16_t dport, size_t payload_len) { uint16_t dport, size_t payload_len) {
struct udp u; struct udp u;
memset(&u, 0, sizeof(struct udp)); memset(&u, 0, sizeof(u));
u.sport = mg_htons(sport); u.sport = mg_htons(sport);
u.dport = mg_htons(dport); u.dport = mg_htons(dport);
u.len = mg_htons((uint16_t) (sizeof(u) + payload_len)); u.len = mg_htons((uint16_t) (sizeof(u) + payload_len));
@@ -992,19 +1029,27 @@ static void create_udp_dat(struct eth *e, struct ipp *ipp, uint16_t sport,
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (ipp->ip6 != NULL) { if (ipp->ip6 != NULL) {
struct ip6 *ip = ipp->ip6; struct ip6 *ip = ipp->ip6;
struct udp *udp =
(struct udp *) (s_driver_data.buf + sizeof(*e) + sizeof(*ip));
ip->plen = mg_htons((uint16_t) (sizeof(u) + payload_len)); ip->plen = mg_htons((uint16_t) (sizeof(u) + payload_len));
memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip)); memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip));
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip), &u, sizeof(u)); memcpy(udp, &u, sizeof(u));
*(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(u)) = 'p'; *(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(u)) = 'p';
udp->csum = p6csum(ip, udp, (sizeof(u) + payload_len));
s_driver_data.len = sizeof(*e) + sizeof(*ip) + sizeof(u) + payload_len; s_driver_data.len = sizeof(*e) + sizeof(*ip) + sizeof(u) + payload_len;
} else } else
#endif #endif
{ {
struct ip *ip = ipp->ip4; struct ip *ip = ipp->ip4;
struct udp *udp =
(struct udp *) (s_driver_data.buf + sizeof(*e) + sizeof(*ip));
ip->len = mg_htons((uint16_t) (sizeof(*ip) + sizeof(u) + payload_len)); ip->len = mg_htons((uint16_t) (sizeof(*ip) + sizeof(u) + payload_len));
ip->csum = ipcsum(ip, sizeof(*ip)); // no options
memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip)); memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip));
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip), &u, sizeof(u)); ip->csum = 0;
memcpy(udp, &u, sizeof(u));
*(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(u)) = 'p'; *(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(u)) = 'p';
udp->csum = pcsum(ip, udp, (sizeof(u) + payload_len));
s_driver_data.len = sizeof(*e) + sizeof(*ip) + sizeof(u) + payload_len; s_driver_data.len = sizeof(*e) + sizeof(*ip) + sizeof(u) + payload_len;
} }
if (s_driver_data.len < 64) s_driver_data.len = 64; // add padding if needed if (s_driver_data.len < 64) s_driver_data.len = 64; // add padding if needed
@@ -1016,7 +1061,7 @@ static void init_udp_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
init_tests(mgr, e, ipp, driver, mif, 17); // 17 -> UDP init_tests(mgr, e, ipp, driver, mif, 17); // 17 -> UDP
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (ipp->ip6 != NULL) { if (ipp->ip6 != NULL) {
mif->state = MG_TCPIP_STATE_READY; // so DHCP stops mif->state = MG_TCPIP_STATE_READY; // so DHCP stops
mif->state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops mif->state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops
mg_listen(mgr, "udp://[::]:888", f, NULL); mg_listen(mgr, "udp://[::]:888", f, NULL);
} else } else
@@ -1034,7 +1079,8 @@ static void test_udp(bool ipv6) {
struct ip ip; struct ip ip;
struct ip6 ip6; struct ip6 ip6;
struct ipp ipp; struct ipp ipp;
struct udp *u = (struct udp *) (s_driver_data.buf + sizeof(e) + (!ipv6 ? sizeof(ip) : sizeof(ip6))); struct udp *u = (struct udp *) (s_driver_data.buf + sizeof(e) +
(!ipv6 ? sizeof(ip) : sizeof(ip6)));
struct ip *i = (struct ip *) (s_driver_data.buf + sizeof(e)); struct ip *i = (struct ip *) (s_driver_data.buf + sizeof(e));
struct ip6 *i6 = (struct ip6 *) (s_driver_data.buf + sizeof(e)); struct ip6 *i6 = (struct ip6 *) (s_driver_data.buf + sizeof(e));
struct mg_tcpip_driver driver; struct mg_tcpip_driver driver;
@@ -1061,15 +1107,185 @@ static void test_udp(bool ipv6) {
ASSERT(u->len == mg_htons(sizeof(*u) + 3)); ASSERT(u->len == mg_htons(sizeof(*u) + 3));
ASSERT(*((char *) (u + 1)) == 'P'); ASSERT(*((char *) (u + 1)) == 'P');
if (ipv6) { if (ipv6) {
ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
ASSERT(udp6csum_ok(i6, u));
} else { } else {
ASSERT(i->src == 1 && i->dst == 2); ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
ASSERT(udpcsum_ok(i, u));
} }
s_driver_data.len = 0; s_driver_data.len = 0;
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
} }
static void create_icmp_dat(struct eth *e, struct ipp *ipp, uint8_t type,
uint8_t code, size_t payload_len) {
struct ip *ip = ipp->ip4;
struct icmp i,
*icmp = (struct icmp *) (s_driver_data.buf + sizeof(*e) + sizeof(*ip));
memset(&i, 0, sizeof(i));
i.type = type;
i.code = code;
memcpy(s_driver_data.buf, e, sizeof(*e));
ip->len = mg_htons((uint16_t) (sizeof(*ip) + sizeof(*icmp) + payload_len));
ip->csum = ipcsum(ip, sizeof(*ip));
memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip));
ip->csum = 0;
memcpy(icmp, &i, sizeof(i));
icmp->csum = ipcsum(icmp, sizeof(*icmp) + payload_len);
s_driver_data.len = sizeof(*e) + sizeof(*ip) + sizeof(*icmp) + payload_len;
if (s_driver_data.len < 64) s_driver_data.len = 64; // add padding if needed
}
static void init_icmp_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
struct mg_tcpip_driver *driver,
struct mg_tcpip_if *mif) {
init_tests(mgr, e, ipp, driver, mif, 1); // 1 -> ICMP
#if MG_ENABLE_IPV6
mif->state6 = MG_TCPIP_STATE_READY; // so RS stops
mg_mgr_poll(mgr, 0);
#endif
}
static void test_icmp_basics(void) {
struct mg_mgr mgr;
struct eth e;
struct ip ip;
struct ipp ipp;
struct icmp *icmp =
(struct icmp *) (s_driver_data.buf + sizeof(e) + sizeof(ip));
struct ip *i = (struct ip *) (s_driver_data.buf + sizeof(e));
struct mg_tcpip_driver driver;
struct mg_tcpip_if mif;
ipp.ip4 = &ip;
ipp.ip6 = NULL;
init_icmp_tests(&mgr, &e, &ipp, &driver, &mif);
create_icmp_dat(&e, &ipp, 8, 0, 0); // Echo Request
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
ASSERT(i->proto == 1);
ASSERT(i->len == mg_htons(sizeof(*i) + sizeof(*icmp) + 0));
ASSERT(ipcsum_ok(i)); // Bonus, not tested elsewhere
ASSERT(icmp->type == 0); // Echo Reply
ASSERT(icmp->code == 0);
ASSERT(icmpcsum_ok(icmp, sizeof(*icmp) + 0));
create_icmp_dat(&e, &ipp, 8, 0, 69); // Echo Request
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
ASSERT(i->proto == 1);
ASSERT(i->len == mg_htons(sizeof(*i) + sizeof(*icmp) + 69));
ASSERT(icmp->type == 0); // Echo Reply
ASSERT(icmp->code == 0);
ASSERT(ipcsum(icmp, sizeof(*icmp) + 69) == 0);
s_driver_data.len = 0;
mg_mgr_free(&mgr);
}
static void test_icmp(void) {
test_icmp_basics();
}
#if MG_ENABLE_IPV6
static void create_icmp6_dat(struct eth *e, struct ipp *ipp, uint8_t type,
uint8_t code, uint8_t *payload,
size_t payload_len) {
struct ip6 *ip6 = ipp->ip6;
struct icmp6 i6,
*icmp6 = (struct icmp6 *) (s_driver_data.buf + sizeof(*e) + sizeof(*ip6));
memset(&i6, 0, sizeof(i6));
i6.type = type;
i6.code = code;
if (payload != NULL)
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip6) + sizeof(*icmp6),
payload, payload_len);
ip6->plen = mg_htons((uint16_t) (sizeof(*icmp6) + payload_len));
memcpy(s_driver_data.buf, e, sizeof(*e));
memcpy(s_driver_data.buf + sizeof(*e), ip6, sizeof(*ip6));
memcpy(icmp6, &i6, sizeof(i6));
icmp6->csum = p6csum(ip6, icmp6, sizeof(*icmp6) + payload_len);
s_driver_data.len = sizeof(*e) + sizeof(*ip6) + sizeof(*icmp6) + payload_len;
if (s_driver_data.len < 64) s_driver_data.len = 64; // add padding if needed
}
static void init_icmp6_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
struct mg_tcpip_driver *driver,
struct mg_tcpip_if *mif) {
init_tests(mgr, e, ipp, driver, mif, 58); // 58 -> ICMPv6
}
static void test_icmp6_basics(void) {
struct mg_mgr mgr;
struct eth e;
struct ip6 ip6;
struct ipp ipp;
struct icmp6 *icmp6 =
(struct icmp6 *) (s_driver_data.buf + sizeof(e) + sizeof(ip6));
struct ip6 *i = (struct ip6 *) (s_driver_data.buf + sizeof(e));
struct mg_tcpip_driver driver;
struct mg_tcpip_if mif;
uint8_t payload[28], *p = (uint8_t *) (icmp6 + 1);
ipp.ip4 = NULL;
ipp.ip6 = &ip6;
init_icmp6_tests(&mgr, &e, &ipp, &driver, &mif);
create_icmp6_dat(&e, &ipp, 128, 0, NULL, 0); // Echo Request
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
ASSERT(i->src[0] == mg_htonll(0x100000000000000) && i->src[1] == 0 &&
i->dst[0] == mg_htonll(0x200000000000000) && i->dst[1] == 0);
ASSERT(i->next == 58);
ASSERT(i->plen == mg_htons(sizeof(*icmp6) + 0));
ASSERT(icmp6->type == 129); // Echo Reply
ASSERT(icmp6->code == 0);
ASSERT(icmp6csum_ok(i, icmp6));
create_icmp6_dat(&e, &ipp, 128, 0, NULL, 69); // Echo Request
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
ASSERT(i->src[0] == mg_htonll(0x100000000000000) && i->src[1] == 0 &&
i->dst[0] == mg_htonll(0x200000000000000) && i->dst[1] == 0);
ASSERT(i->next == 58);
ASSERT(i->plen == mg_htons(sizeof(*icmp6) + 69));
ASSERT(icmp6->type == 129); // Echo Reply
ASSERT(icmp6->code == 0);
ASSERT(icmp6csum_ok(i, icmp6));
// Neighbor Solicitation
memset(payload, 0, sizeof(payload));
memcpy(payload + 4, mif.ip6, 16);
payload[20] = 1; // source hwaddr
payload[21] = 1; // hwaddr len
create_icmp6_dat(&e, &ipp, 135, 0, payload, 28);
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
ASSERT(i->src[0] == mg_htonll(0x100000000000000) && i->src[1] == 0 &&
i->dst[0] == mg_htonll(0x200000000000000) && i->dst[1] == 0);
ASSERT(i->next == 58);
ASSERT(i->plen == mg_htons(sizeof(*icmp6) + 28));
ASSERT(icmp6->type == 136); // Neighbor Advertisement
ASSERT(icmp6->code == 0);
ASSERT(p[0] == 0x60); // solicited + override
ASSERT(memcmp(p + 4, mif.ip6, 16) == 0); // target address
ASSERT(p[20] == 2); // target hwaddr
ASSERT(p[21] == 1); // hwaddr len
ASSERT(memcmp(p + 22, mif.mac, 6) == 0);
s_driver_data.len = 0;
mg_mgr_free(&mgr);
}
static void test_icmp6(void) {
test_icmp6_basics();
}
#endif
#define DASHBOARD(x) \ #define DASHBOARD(x) \
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true"); printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true");
@@ -1087,6 +1303,10 @@ int main(void) {
test_poll(); test_poll();
DASHBOARD("poll"); DASHBOARD("poll");
s_error = false;
test_icmp();
DASHBOARD("icmp");
s_error = false; s_error = false;
test_tcp(false); test_tcp(false);
DASHBOARD("tcp"); DASHBOARD("tcp");
@@ -1096,6 +1316,10 @@ int main(void) {
DASHBOARD("udp"); DASHBOARD("udp");
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
s_error = false;
test_icmp6();
DASHBOARD("icmp6");
s_error = false; s_error = false;
test_state6change(); test_state6change();
DASHBOARD("state6change"); DASHBOARD("state6change");
@@ -1103,7 +1327,7 @@ int main(void) {
s_error = false; s_error = false;
test_tcp(true); test_tcp(true);
DASHBOARD("tcp_ipv6"); DASHBOARD("tcp_ipv6");
s_error = false; s_error = false;
test_udp(true); test_udp(true);
DASHBOARD("udp_ipv6"); DASHBOARD("udp_ipv6");
+468
View File
@@ -0,0 +1,468 @@
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
#define FETCH_BUF_SIZE (16 * 1024)
#include "mongoose.c"
bool mip_x_test(struct mg_mgr *);
#ifdef MQTT_LOCALHOST
#define MQTT_URL "mqtt://127.0.0.1:1883"
#else
#define MQTT_URL "mqtt://broker.hivemq.com:1883"
#endif
#if MG_TLS == MG_TLS_BUILTIN
#define MQTTS_URL "mqtts://mongoose.ws:8883" // test requires TLS 1.3
#define MQTTS_CA mg_str(s_ca_cert)
static const char *s_ca_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
"-----END CERTIFICATE-----\n";
#elif MG_TLS
#ifdef MQTT_LOCALHOST
// we'll generate MQTTS_URL
#define MQTTS_CA mg_str(s_ca_cert)
static const char *s_ca_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n"
"b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n"
"TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n"
"fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n"
"epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n"
"us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n"
"-----END CERTIFICATE-----\n";
#else
#define MQTTS_URL "mqtts://broker.hivemq.com:8883"
#define MQTTS_CA mg_unpacked("/data/ca.pem")
#endif // MQTT_LOCALHOST
#endif
static char *host_ip;
static int s_num_tests = 0;
static bool s_error = false;
#ifdef NO_ABORT
static int s_abort = 0;
#define ABORT() ++s_abort, s_error = true
#else
#ifdef NO_SLEEP_ABORT
#define ABORT() abort()
#else
#define ABORT() \
sleep(2); /* 2s, GH print reason */ \
abort();
#endif
#endif
#define ASSERT(expr) \
do { \
s_num_tests++; \
if (!(expr)) { \
printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \
fflush(stdout); \
ABORT(); \
} \
} while (0)
static struct mg_http_message gethm(const char *buf) {
struct mg_http_message hm;
memset(&hm, 0, sizeof(hm));
mg_http_parse(buf, strlen(buf), &hm);
return hm;
}
static int cmpbody(const char *buf, const char *str) {
struct mg_str s = mg_str(str);
struct mg_http_message hm = gethm(buf);
size_t len = strlen(buf);
if (hm.body.len > len) hm.body.len = len - (size_t) (hm.body.buf - buf);
return mg_strcmp(hm.body, s);
}
#ifndef NO_HTTPSERVER_TEST
static void eh1(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts *topts = (struct mg_tls_opts *) c->fn_data;
if (ev == MG_EV_ACCEPT && topts != NULL) mg_tls_init(c, topts);
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
MG_DEBUG(("[%.*s %.*s] message len %d", (int) hm->method.len,
hm->method.buf, (int) hm->uri.len, hm->uri.buf,
(int) hm->message.len));
if (mg_match(hm->uri, mg_str("/foo/*"), NULL)) {
mg_http_reply(c, 200, "", "uri: %.*s", hm->uri.len - 5, hm->uri.buf + 5);
} else if (mg_match(hm->uri, mg_str("/ws"), NULL)) {
mg_ws_upgrade(c, hm, NULL);
} else if (mg_match(hm->uri, mg_str("/body"), NULL)) {
mg_http_reply(c, 200, "", "%.*s", (int) hm->body.len, hm->body.buf);
} else {
struct mg_http_serve_opts sopts;
memset(&sopts, 0, sizeof(sopts));
sopts.root_dir = "./data";
mg_http_serve_dir(c, hm, &sopts);
}
} else if (ev == MG_EV_WS_OPEN) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
ASSERT(mg_strcmp(hm->uri, mg_str("/ws")) == 0);
mg_ws_send(c, "opened", 6, WEBSOCKET_OP_BINARY);
} else if (ev == MG_EV_WS_MSG) {
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
mg_ws_send(c, wm->data.buf, wm->data.len, WEBSOCKET_OP_BINARY);
}
}
#endif
struct fetch_data {
char *buf;
const char *url;
int code, closed;
};
static void fcb(struct mg_connection *c, int ev, void *ev_data) {
struct fetch_data *fd = (struct fetch_data *) c->fn_data;
if (ev == MG_EV_CONNECT) {
MG_DEBUG(("CONNECT"));
if (mg_url_is_ssl(fd->url)) {
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts)); // read CA from packed_fs
if (host_ip != NULL && strstr(fd->url, host_ip) != NULL) {
MG_DEBUG(("Local connection, using self-signed certificates"));
opts.name = mg_str_s("localhost");
opts.ca = mg_unpacked("/certs/ca.crt");
} else {
opts.name = mg_url_host(fd->url);
opts.ca = mg_unpacked("/data/ca.pem");
#if MG_TLS == MG_TLS_BUILTIN
// our TLS does not search for the proper CA in a bundle
opts.ca = mg_file_read(&mg_fs_posix, "data/e5.crt");
#endif
}
mg_tls_init(c, &opts);
#if MG_TLS == MG_TLS_BUILTIN
mg_free((void *) opts.ca.buf);
#endif
}
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len,
hm->message.buf);
fd->code = atoi(hm->uri.buf);
fd->closed = 1;
c->is_closing = 1;
MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len,
hm->message.buf));
(void) c;
} else if (ev == MG_EV_CLOSE) {
MG_DEBUG(("CLOSE"));
fd->closed = 1;
} else if (ev == MG_EV_READ) {
long bytes = *(long *) ev_data;
MG_DEBUG(("READ %d: %.*s", (int) bytes, (int) bytes, c->recv.buf));
}
}
static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
const char *fmt, ...) {
struct fetch_data fd = {buf, url, 0, 0};
int i;
struct mg_connection *c = NULL;
va_list ap;
mg_mgr_poll(mgr, 0); // update ifp->now to avoid ARP lookup using an old
// timestamp (from an ancient call in other test)
c = mg_http_connect(mgr, url, fcb, &fd);
ASSERT(c != NULL);
va_start(ap, fmt);
mg_vprintf(c, fmt, &ap);
va_end(ap);
buf[0] = '\0';
// - TLS: multiple (small) records: allow enough loops so mg_mgr_poll can
// process buffered records when no more frames are coming in
for (i = 0; i < 500 && buf[0] == '\0' && !fd.closed; i++) {
mg_mgr_poll(mgr, 0);
usleep(5000); // 5 ms. Slow down poll loop to ensure packet transit, but
// allow enough loops to get the ARP response, otherwise,
// given enough traffic, the timer expires before we get a
// chance to see the response
}
if (!fd.closed) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
return fd.code;
}
static void test_http_client(struct mg_mgr *mgr) {
char buf[FETCH_BUF_SIZE];
int rc = 0;
const bool ipv6 = MG_ENABLE_IPV6;
#if MG_TLS
if (ipv6) {
rc = fetch(mgr, buf, "https://ipv6.google.com",
"GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n");
} else {
rc = fetch(mgr, buf, "https://cesanta.com",
"GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n");
}
ASSERT(rc == 200); // OK
#else
if (ipv6) {
rc = fetch(mgr, buf, "http://ipv6.google.com",
"GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n");
ASSERT(rc == 200); // OK
} else {
rc = fetch(mgr, buf, "http://cesanta.com",
"GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n");
ASSERT(rc == 301); // OK: Permanently moved (HTTP->HTTPS redirect)
}
#endif
}
static struct mg_connection *s_conn;
static char s_topic[16];
struct mqtt_data {
char *url;
bool passed;
};
static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data) {
struct mqtt_data *data = (struct mqtt_data *) c->fn_data;
if (ev == MG_EV_CONNECT) {
MG_DEBUG(("CONNECT"));
#if MG_TLS
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts));
opts.ca = MQTTS_CA;
opts.name = mg_url_host(data->url);
mg_tls_init(c, &opts);
#endif
} else if (ev == MG_EV_MQTT_OPEN) {
MG_DEBUG(("MQTT CONNECT"));
struct mg_mqtt_opts sub_opts;
memset(&sub_opts, 0, sizeof(sub_opts));
sub_opts.topic = mg_str(mg_random_str(s_topic, sizeof(s_topic)));
sub_opts.qos = 1;
mg_mqtt_sub(c, &sub_opts);
struct mg_mqtt_opts pub_opts;
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = sub_opts.topic;
pub_opts.message = mg_str("hi");
pub_opts.qos = 1, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
} else if (ev == MG_EV_MQTT_MSG) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
MG_DEBUG(("TOPIC: %.*s, MSG: %.*s", (int) mm->topic.len, mm->topic.buf,
mm->data.len > 10 ? 10 : (int) mm->data.len, mm->data.buf));
ASSERT(mm->topic.len == strlen(s_topic) &&
strncmp(mm->topic.buf, s_topic, mm->topic.len) == 0);
if (mm->data.len == 2 && strncmp(mm->data.buf, "hi", 2) == 0) {
struct mg_mqtt_opts pub_opts;
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = mm->topic;
// send more than 1 record, content is not relevant
pub_opts.message = mg_str_n((char *)(size_t) mqtt_fn, 21098);
pub_opts.qos = 1, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
} else if (mm->data.len == 8 && strncmp(mm->data.buf, "farewell", 8) == 0) {
// close on farewell
MG_INFO(("%lu CLOSING", c->id));
mg_mqtt_disconnect(c, NULL);
data->passed = true;
} else if (mm->data.len == 21098) {
struct mg_mqtt_opts pub_opts;
ASSERT(memcmp((const char *) (size_t) mqtt_fn, mm->data.buf, 21098) == 0);
// send farewell after receiving big data
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = mm->topic;
pub_opts.message = mg_str("farewell");
pub_opts.qos = 1, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
}
} else if (ev == MG_EV_CLOSE) {
MG_DEBUG(("CLOSE"));
s_conn = NULL;
} else if (ev == MG_EV_ERROR) {
MG_ERROR(("%lu ERROR %s", c->id, (char *) ev_data));
}
}
static void test_mqtt_connsubpub(struct mg_mgr *mgr) {
struct mqtt_data data;
struct mg_mqtt_opts opts;
memset(&opts, 0, sizeof(opts));
opts.clean = true, opts.version = 4;
data.passed = false;
#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN
if (host_ip == NULL) {
printf("\nMQTT_LOCALHOST defined but no HOST_IP provided, skipping MQTTS tests\n");
return;
}
printf("HOST_IP: %s\n", host_ip);
#endif
#if MG_TLS
#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN
data.url = mg_mprintf("mqtts://%s:8883", host_ip);
#else
data.url = strdup(MQTTS_URL);
#endif
#else
#ifdef MQTT_LOCALHOST
data.url = mg_mprintf("mqtt://%s:1883", host_ip);
#else
data.url = strdup(MQTT_URL);
#endif
#endif
s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
ASSERT(s_conn != NULL);
for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
mg_mgr_poll(mgr, 0);
usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit
}
ASSERT(data.passed);
mg_mgr_poll(mgr, 0);
free(data.url);
}
#ifndef NO_HTTPSERVER_TEST
#include <pthread.h>
static void *poll_thread(void *p) {
struct mg_mgr *mgr = (struct mg_mgr *) p;
int i;
for (i = 0; i < 300; i++) {
mg_mgr_poll(mgr, 0);
usleep(10000); // 10 ms. Slow down poll loop to ensure packet transit
}
return NULL;
}
#endif
static void test_http_server(struct mg_mgr *mgr) {
#ifdef NO_HTTPSERVER_TEST
(void) mgr;
#else
struct mg_connection *c;
char *cmd;
pthread_t thread_id = (pthread_t) 0;
#if MG_TLS
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts));
// opts.ca = mg_str(s_tls_ca);
opts.cert = mg_unpacked("/certs/server.crt");
opts.key = mg_unpacked("/certs/server.key");
c = mg_http_listen(mgr, "https://0.0.0.0:12347", eh1, &opts);
cmd = mg_mprintf("./mip_curl.sh --insecure https://%M:12347", mg_print_ip4,
&mgr->ifp->ip);
#else
c = mg_http_listen(mgr, "http://0.0.0.0:12347", eh1, NULL);
cmd =
mg_mprintf("./mip_curl.sh http://%M:12347", mg_print_ip4, &mgr->ifp->ip);
#endif
ASSERT(c != NULL);
ASSERT (mg_send(c, "NADA", 0)); // check mg_send allows len=0
pthread_create(&thread_id, NULL, poll_thread,
mgr); // simpler this way, no concurrency anyway
MG_DEBUG(("CURL"));
ASSERT(system(cmd) == 0); // wait for curl
MG_DEBUG(("MONGOOSE"));
pthread_join(thread_id, NULL); // wait for Mongoose
MG_DEBUG(("DONE"));
free(cmd);
#endif
}
static void test_tls(struct mg_mgr *mgr) {
#if MG_TLS
char *url;
char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile
struct mg_str data = mg_unpacked("/Makefile");
if (host_ip == NULL) {
printf("\nNo HOST_IP provided, skipping TLS tests\n");
return;
}
printf("HOST_IP: %s\n", host_ip);
// - POST a large file, make sure we drain TLS buffers and read all: done at
// server test, using curl as POSTing client
// - Fire patched server, test multiple TLS records per TCP segment handling
url = mg_mprintf("https://%s:8443", host_ip); // for historic reasons
ASSERT(system("tls_multirec/server -d tls_multirec &") == 0);
sleep(1);
ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200);
ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile
ASSERT(system("killall tls_multirec/server") == 0);
free(url);
#else
(void) cmpbody("", "");
(void) mgr;
#endif
}
bool mip_x_test(struct mg_mgr *mgr) {
host_ip = getenv("HOST_IP");
#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true");
usleep(500000); // 500 ms
s_error = false;
test_http_client(mgr);
DASHBOARD("http_client");
usleep(500000); // 500 ms
s_error = false;
test_http_server(mgr);
DASHBOARD("http_server");
usleep(500000); // 500 ms
s_error = false;
test_tls(mgr);
DASHBOARD("tls");
usleep(500000); // 500 ms
s_error = false;
test_mqtt_connsubpub(mgr);
DASHBOARD("mqtt");
// Clear
s_error = false;
mg_mgr_free(mgr);
ASSERT(mgr->conns == NULL); // Deconstruction OK
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true");
// last entry with no comma
#ifdef NO_ABORT
if (s_abort != 0) return false;
#endif
printf("SUCCESS. Total tests: %d\n", s_num_tests);
return true;
}
+105
View File
@@ -0,0 +1,105 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#ifndef __OpenBSD__
#include <linux/if.h>
#include <linux/if_tun.h>
#else
#include <net/if.h>
#include <net/if_tun.h>
#include <net/if_types.h>
#endif
static int s_signo;
void signal_handler(int signo) {
s_signo = signo;
}
int main(void) {
// Setup interface
const char *iface = "tap0"; // Network iface
const uint16_t port = 0x55AA; // UDP port
#ifndef __OpenBSD__
const char *tuntap_device = "/dev/net/tun";
#else
const char *tuntap_device = "/dev/tap0";
#endif
int fd = open(tuntap_device, O_RDWR);
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
#ifndef __OpenBSD__
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
#else
ifr.ifr_flags = (short) (IFF_UP | IFF_BROADCAST | IFF_MULTICAST);
if (ioctl(fd, TUNSIFMODE, (void *) &ifr) < 0) {
#endif
printf("Failed to setup TAP interface: %s", ifr.ifr_name);
return EXIT_FAILURE;
}
printf("Opened TAP interface: %s\n", iface);
int sockfd;
struct sockaddr_in tap_addr, port_addr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
tap_addr.sin_family = AF_INET;
tap_addr.sin_addr.s_addr = htonl(0x7f000001U); // 127.0.0.1
tap_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr *) &tap_addr, sizeof(tap_addr)) < 0) return EXIT_FAILURE;
printf("Opened UDP socket: 127.0.0.1:%u (0x%04x)\n", port, port);
memset(&port_addr, 0, sizeof(port_addr));
while (s_signo == 0) {
struct pollfd pfd[2];
int result;
do {
pfd[0].fd = fd;
pfd[0].events = POLLIN | POLLPRI; // will wait for input available (read ready), use POLLOUT for write
pfd[1].fd = sockfd;
pfd[1].events = POLLIN | POLLPRI; // will wait for input available (read ready), use POLLOUT for write
// only read ready shown here, for write ready use the 3rd arg, and the 4th for exceptions
result = poll(pfd, 2, 1000); // 2 pfd, TIMEOUT in milliseconds
switch(result) {
case -1: // error
break;
case 0: // timeout (if a timeout struct was passed to select)
break;
default: // the number of fd/sockets with data available (see pfd.revents for each one);
uint8_t buf[1540];
if (pfd[0].revents & (POLLIN | POLLPRI)) {
ssize_t nread = read(fd, buf, 1540);
// printf("Got at TAP: %d bytes\n", (int) nread);
if (port_addr.sin_family != 0) { // something has been written to this struct (we got a conterpart)
sendto(sockfd, buf, (size_t) nread, 0, (struct sockaddr *) &port_addr, sizeof(port_addr));
// printf("Sent to %s port %d\n", inet_ntoa(port_addr.sin_addr), ntohs(port_addr.sin_port));
}
}
if (pfd[1].revents & (POLLIN | POLLPRI)) {
socklen_t len = sizeof(port_addr);
ssize_t nread = recvfrom(sockfd, buf, 1540, 0, (struct sockaddr *) &port_addr, &len);
// printf("Got at UDP: %d bytes from %s port %d\n", nread, inet_ntoa(port_addr.sin_addr), ntohs(port_addr.sin_port));
write(fd, buf, (size_t) nread);
// printf("Sent to TAP\n");
}
break;
}
} while (result >= 0);
printf("looks like we got an error here... should I quit ? Waiting for CTRL-C\n");
}
close(sockfd);
close(fd);
}
+1 -1
View File
@@ -6,4 +6,4 @@ server: main.c patched_mongoose.c mongoose.h Makefile
$(CC) main.c patched_mongoose.c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@ $(CC) main.c patched_mongoose.c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
clean: clean:
rm -f server rm -rf server *.exe *.o *.dSYM
+152 -70
View File
@@ -72,6 +72,13 @@ static void test_match(void) {
ASSERT(mg_match(mg_str_n("caabxa", 6), mg_str_n("#aa#bb#", 7), NULL) == 0); ASSERT(mg_match(mg_str_n("caabxa", 6), mg_str_n("#aa#bb#", 7), NULL) == 0);
ASSERT(mg_match(mg_str_n("a__b_c", 6), mg_str_n("a*b*c", 5), NULL) == 1); ASSERT(mg_match(mg_str_n("a__b_c", 6), mg_str_n("a*b*c", 5), NULL) == 1);
{
char *ae_value = (char *) malloc(1); // Exact size for ASAN
ae_value[0] = 'g';
mg_match(mg_str_n(ae_value, 1), mg_str("*gzip*"), NULL); // Check for OOB
free(ae_value);
}
{ {
struct mg_str caps[3]; struct mg_str caps[3];
ASSERT(mg_match(mg_str("//a.c"), mg_str("#.c"), NULL) == true); ASSERT(mg_match(mg_str("//a.c"), mg_str("#.c"), NULL) == true);
@@ -439,7 +446,7 @@ static const char *s_ca_cert =
#else #else
#define MQTTS_URL "mqtts://broker.hivemq.com:8883" #define MQTTS_URL "mqtts://broker.hivemq.com:8883"
#define MQTTS_CA mg_unpacked("/data/ca.pem") #define MQTTS_CA mg_unpacked("/data/ca.pem")
#endif // MQTT_LOCALHOST #endif // MQTT_LOCALHOST
#endif #endif
struct mqtt_data { struct mqtt_data {
@@ -454,6 +461,7 @@ struct mqtt_data {
#define flags_received (1 << 2) #define flags_received (1 << 2)
#define flags_released (1 << 3) #define flags_released (1 << 3)
#define flags_completed (1 << 4) #define flags_completed (1 << 4)
#define flags_unsubscribed (1 << 5)
static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) { static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
struct mqtt_data *test_data = (struct mqtt_data *) c->fn_data; struct mqtt_data *test_data = (struct mqtt_data *) c->fn_data;
@@ -463,7 +471,7 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts; struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.ca = MQTTS_CA; opts.ca = MQTTS_CA;
#if defined( MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN #if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN
MG_ERROR(("Hostname not tested")); MG_ERROR(("Hostname not tested"));
#else #else
opts.name = mg_url_host(MQTTS_URL); opts.name = mg_url_host(MQTTS_URL);
@@ -487,6 +495,8 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
test_data->flags |= flags_released; test_data->flags |= flags_released;
} else if (mm->cmd == MQTT_CMD_PUBCOMP) { } else if (mm->cmd == MQTT_CMD_PUBCOMP) {
test_data->flags |= flags_completed; test_data->flags |= flags_completed;
} else if (mm->cmd == MQTT_CMD_UNSUBACK) {
test_data->flags = flags_unsubscribed;
} }
} else if (ev == MG_EV_MQTT_MSG) { } else if (ev == MG_EV_MQTT_MSG) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
@@ -499,7 +509,7 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
size_t pos = 0, i = 0, j = 0; size_t pos = 0, i = 0, j = 0;
struct mg_mqtt_prop prop; struct mg_mqtt_prop prop;
for (i = 0; i < 5 ; i++) { for (i = 0; i < 5; i++) {
ASSERT((pos = mg_mqtt_next_prop(mm, &prop, pos)) > 0); ASSERT((pos = mg_mqtt_next_prop(mm, &prop, pos)) > 0);
if (prop.id == MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) { if (prop.id == MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) {
ASSERT(prop.iv == 10); ASSERT(prop.iv == 10);
@@ -508,14 +518,20 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
j += 2; j += 2;
continue; continue;
} else if (prop.id == MQTT_PROP_CONTENT_TYPE) { } else if (prop.id == MQTT_PROP_CONTENT_TYPE) {
ASSERT(strncmp(prop.val.buf, "test_content_val_2", prop.val.len) == 0 && prop.val.len == strlen("test_content_val_2")); ASSERT(strncmp(prop.val.buf, "test_content_val_2", prop.val.len) ==
0 &&
prop.val.len == strlen("test_content_val_2"));
j += 4; j += 4;
} else if (prop.id == MQTT_PROP_USER_PROPERTY) { } else if (prop.id == MQTT_PROP_USER_PROPERTY) {
if (strncmp(prop.key.buf, "test_key_1", prop.key.len) == 0 && prop.key.len == strlen("test_key_1")) { if (strncmp(prop.key.buf, "test_key_1", prop.key.len) == 0 &&
ASSERT(strncmp(prop.val.buf, "test_value_1", prop.val.len) == 0 && prop.val.len == strlen("test_value_1")); prop.key.len == strlen("test_key_1")) {
ASSERT(strncmp(prop.val.buf, "test_value_1", prop.val.len) == 0 &&
prop.val.len == strlen("test_value_1"));
j += 8; j += 8;
} else if (strncmp(prop.key.buf, "test_key_2", prop.key.len) == 0 && prop.key.len == strlen("test_key_2")) { } else if (strncmp(prop.key.buf, "test_key_2", prop.key.len) == 0 &&
ASSERT(strncmp(prop.val.buf, "test_value_2", prop.val.len) == 0 && prop.val.len == strlen("test_value_2")); prop.key.len == strlen("test_key_2")) {
ASSERT(strncmp(prop.val.buf, "test_value_2", prop.val.len) == 0 &&
prop.val.len == strlen("test_value_2"));
j += 16; j += 16;
} else { } else {
ASSERT(0); ASSERT(0);
@@ -759,6 +775,12 @@ static void test_mqtt_ver(uint8_t mqtt_version) {
memset(mbuf + 1, 0, sizeof(mbuf) - 1); memset(mbuf + 1, 0, sizeof(mbuf) - 1);
test_data.flags = 0; test_data.flags = 0;
opts.props = 0; opts.num_props = 0;
mg_mqtt_unsub(c, &opts);
for (i = 0; i < 500 && test_data.flags == 0; i++) mg_mgr_poll(&mgr, 10);
ASSERT(test_data.flags == flags_unsubscribed);
test_data.flags = 0;
// dirty disconnect // dirty disconnect
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL); ASSERT(mgr.conns == NULL);
@@ -875,7 +897,7 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
if (strstr(url, "localhost") != NULL) { if (strstr(url, "localhost") != NULL) {
// Local connection, use self-signed certificates // Local connection, use self-signed certificates
opts.ca = mg_unpacked("/certs/ca.crt"); opts.ca = mg_unpacked("/certs/ca.crt");
if (strstr(url, "23456") != NULL) { // hinted from caller if (strstr(url, "23456") != NULL) { // hinted from caller
opts.cert = mg_unpacked("/certs/client.crt"); opts.cert = mg_unpacked("/certs/client.crt");
opts.key = mg_unpacked("/certs/client.key"); opts.key = mg_unpacked("/certs/client.key");
} }
@@ -1451,7 +1473,7 @@ static void test_tls(void) {
"%s", "%s",
bd.len, bd.buf) == 200); bd.len, bd.buf) == 200);
ASSERT(cmpbody(buf, bd.buf) == 0); ASSERT(cmpbody(buf, bd.buf) == 0);
#if MG_TLS == MG_TLS_BUILTIN && defined(__linux__) #if MG_TLS == MG_TLS_BUILTIN && defined(__linux__) && MG_ENABLE_CHACHA20 // skip for non-CHACHA tests
// fire patched server, test multiple TLS records per TCP segment handling // fire patched server, test multiple TLS records per TCP segment handling
// skip other TLS stacks to avoid "bad client hello", we are 1.3 only // skip other TLS stacks to avoid "bad client hello", we are 1.3 only
if (access("tls_multirec/server", X_OK) == 0) { if (access("tls_multirec/server", X_OK) == 0) {
@@ -1464,7 +1486,8 @@ static void test_tls(void) {
ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile
ASSERT(system("killall tls_multirec/server") == 0); ASSERT(system("killall tls_multirec/server") == 0);
} else { } else {
MG_ERROR(("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT")); MG_ERROR(
("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT"));
} }
#else #else
printf( printf(
@@ -1474,14 +1497,14 @@ static void test_tls(void) {
#endif #endif
// Repeat the simplest test with two-way authentication // Repeat the simplest test with two-way authentication
opts.ca = mg_unpacked("/certs/ca.crt"); // configure the server for two-way opts.ca = mg_unpacked("/certs/ca.crt"); // configure the server for two-way
// make it fail: the client will not use 2-way // make it fail: the client will not use 2-way
ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") != 200); ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") != 200);
// make it work // make it work
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL); ASSERT(mgr.conns == NULL);
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
url = "https://localhost:23456"; // port # hints the client to use two-way url = "https://localhost:23456"; // port # hints the client to use two-way
c = mg_http_listen(&mgr, url, eh1, &opts); c = mg_http_listen(&mgr, url, eh1, &opts);
ASSERT(c != NULL); ASSERT(c != NULL);
ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200); ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200);
@@ -1496,7 +1519,8 @@ static void f3(struct mg_connection *c, int ev, void *ev_data) {
// MG_INFO(("%d", ev)); // MG_INFO(("%d", ev));
if (ev == MG_EV_CONNECT) { if (ev == MG_EV_CONNECT) {
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
ASSERT((c->loc.ip[0] != 0)); // Make sure that c->loc address is populated ASSERT((c->loc.addr.ip[0] !=
0)); // Make sure that c->loc address is populated
mg_printf(c, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", mg_printf(c, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
c->rem.is_ip6 ? "" : "/robots.txt", c->rem.is_ip6 ? "" : "/robots.txt",
c->rem.is_ip6 ? "ipv6.google.com" : "cesanta.com"); c->rem.is_ip6 ? "ipv6.google.com" : "cesanta.com");
@@ -1533,7 +1557,7 @@ static void test_http_client(void) {
ASSERT(ok == 301 || ok == 200); ASSERT(ok == 301 || ok == 200);
mg_mgr_poll(&mgr, 0); mg_mgr_poll(&mgr, 0);
ok = 0; ok = 0;
#if MG_TLS #if MG_TLS && MG_TLS != MG_TLS_BUILTIN
url = "https://cesanta.com"; url = "https://cesanta.com";
opts.name = mg_url_host(url); opts.name = mg_url_host(url);
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
@@ -1655,14 +1679,12 @@ static void test_http_no_content_length(void) {
// 12348 is in TIME_WAIT, use another port // 12348 is in TIME_WAIT, use another port
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
mg_http_listen(&mgr, url2, f41, (void *) NULL); mg_http_listen(&mgr, url2, f41, (void *) NULL);
ASSERT(fetch(&mgr, buf, url2, "POST / HTTP/1.1\r\n\r\n") == 411); ASSERT(fetch(&mgr, buf, url2, "POST / HTTP/1.1\r\n\r\n") == 200);
ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 200\r\n\r\n") == 411); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 200\r\n\r\n") == 411);
ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 100\r\n\r\n") != 411); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 100\r\n\r\n") != 411);
ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 304\r\n\r\n") != 411); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 304\r\n\r\n") != 411);
ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 305\r\n\r\n") == 411); ASSERT(fetch(&mgr, buf, url2, "HTTP/1.1 305\r\n\r\n") == 411);
ASSERT(fetch(&mgr, buf, url2, post_req) != 411); ASSERT(fetch(&mgr, buf, url2, post_req) != 411);
// Check it is processed only once (see #2811)
ASSERT(fpr(&mgr, buf, url2, "POST / HTTP/1.1\r\n\r\n") == 411);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL); ASSERT(mgr.conns == NULL);
} }
@@ -1781,15 +1803,15 @@ static void test_http_parse(void) {
} }
// #2292: fail on stray \r inside the headers // #2292: fail on stray \r inside the headers
ASSERT(mg_http_parse("a є\n\n", 6, &req) == 6); ASSERT(mg_http_parse("a є HTTP/1.0\n\n", 15, &req) == 15);
ASSERT(mg_http_parse("a b\n\n", 5, &req) == 5); ASSERT(mg_http_parse("a b HTTP/1.0\n\n", 14, &req) == 14);
ASSERT(mg_http_parse("a b\na:\n\n", 8, &req) > 0); ASSERT(mg_http_parse("a b HTTP/1.0\na:\n\n", 17, &req) > 0);
ASSERT(mg_http_parse("a b\na:\r\n\n", 9, &req) > 0); ASSERT(mg_http_parse("a b HTTP/1.0\na:\r\n\n", 18, &req) > 0);
ASSERT(mg_http_parse("a b\n\ra:\r\n\n", 10, &req) == -1); ASSERT(mg_http_parse("a b HTTP/1.0\n\ra:\r\n\n", 19, &req) == -1);
ASSERT(mg_http_parse("a b\na:\r1\n\n", 10, &req) == -1); ASSERT(mg_http_parse("a b HTTP/1.0\na:\r1\n\n", 19, &req) == -1);
ASSERT(mg_http_parse("a b\na: \r1\n\n", 11, &req) == -1); ASSERT(mg_http_parse("a b HTTP/1.0\na: \r1\n\n", 20, &req) == -1);
ASSERT(mg_http_parse("a b\na: \rb:\n\n", 12, &req) == -1); ASSERT(mg_http_parse("a b HTTP/1.0\na: \rb:\n\n", 21, &req) == -1);
ASSERT(mg_http_parse("a b\na: \nb:\n\n", 12, &req) > 0); ASSERT(mg_http_parse("a b HTTP/1.0\na: \nb:\n\n", 21, &req) > 0);
{ {
const char *s = "ґєт /слеш HTTP/1.0\nмісто: кіїв \n\n"; const char *s = "ґєт /слеш HTTP/1.0\nмісто: кіїв \n\n";
@@ -1827,7 +1849,7 @@ static void test_http_parse(void) {
{ {
const char *s = "POST /x HTTP/1.0\n\n"; const char *s = "POST /x HTTP/1.0\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s)); ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT(req.body.len == (size_t) ~0); ASSERT(req.body.len == 0);
} }
{ {
@@ -1866,8 +1888,7 @@ static void test_http_parse(void) {
} }
{ {
static const char *s = const char *s = "a b HTTP/1.0\na:1\nb:2\nc:3\nd:4\ne:5\nf:6\ng:7\nh:8\n\n";
"a b HTTP/1.0\na:1\nb:2\nc:3\nd:4\ne:5\nf:6\ng:7\nh:8\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s)); ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT((v = mg_http_get_header(&req, "e")) != NULL); ASSERT((v = mg_http_get_header(&req, "e")) != NULL);
ASSERT(vcmp(*v, "5")); ASSERT(vcmp(*v, "5"));
@@ -1876,6 +1897,24 @@ static void test_http_parse(void) {
ASSERT((v = mg_http_get_header(&req, "h")) == NULL); // MG_MAX_HTTP_HEADERS ASSERT((v = mg_http_get_header(&req, "h")) == NULL); // MG_MAX_HTTP_HEADERS
} }
{
// no HTTP version
const char *s = "a b\na:1\nb:2\nc:2\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
// HTTP version
s = "a b HTTP/1.0\na:1\nb:2\nc:2\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
// Content-Length
s = "a b HTTP/1.0\nContent-Length:10\nb:2\nc:2\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
// duplicated Content-Length
s = "a b HTTP/1.0\nContent-Length:10\nb:2\nContent-Length:20\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
}
{ {
struct mg_connection c; struct mg_connection c;
struct mg_str s, struct mg_str s,
@@ -1901,15 +1940,15 @@ static void test_http_parse(void) {
struct mg_http_message hm; struct mg_http_message hm;
const char *s = "a b HTTP/1.0\n\n"; const char *s = "a b HTTP/1.0\n\n";
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s)); ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
s = "a b\nc:d\n\n"; s = "a b HTTP/1.0\nc:d\n\n";
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s)); ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
s = "a\nb:b\nc:c\n\n"; s = "a\nb:b\nc:c\n\n";
ASSERT(mg_http_parse(s, strlen(s), &hm) < 0); ASSERT(mg_http_parse(s, strlen(s), &hm) < 0);
s = "a b\nc: \xc0\n\n"; // Invalid UTF in the header value: accept s = "a b HTTP/1.0\nc: \xc0\n\n"; // Invalid UTF in the header value: accept
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s)); ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
ASSERT((v = mg_http_get_header(&hm, "c")) != NULL); ASSERT((v = mg_http_get_header(&hm, "c")) != NULL);
ASSERT(vcmp(*v, "\xc0")); ASSERT(vcmp(*v, "\xc0"));
s = "a b\n\xc0: 2\n\n"; // Invalid UTF in the header name: do NOT accept s = "a b HTTP/1.0\n\xc0: 2\n\n"; // Invalid UTF in the header name: do NOT accept
ASSERT(mg_http_parse(s, strlen(s), &hm) == -1); ASSERT(mg_http_parse(s, strlen(s), &hm) == -1);
} }
@@ -2265,15 +2304,15 @@ static void test_str(void) {
ASSERT(sn("%s ", "a")); ASSERT(sn("%s ", "a"));
ASSERT(sn("%s %s", "a", "b")); ASSERT(sn("%s %s", "a", "b"));
ASSERT(sn("%2s %s", "a", "b")); ASSERT(sn("%2s %s", "a", "b"));
{ // mg_queue_printf() { // mg_queue_printf()
struct mg_queue q; struct mg_queue q;
char buf[128]; char buf[128];
uint32_t p = 0xffffffff; uint32_t p = 0xffffffff;
size_t len; size_t len;
mg_queue_init(&q, buf, sizeof(buf)); mg_queue_init(&q, buf, sizeof(buf));
len = mg_queue_printf(&q, "A%p%p%pB", p, p, p); len = mg_queue_printf(&q, "A%p%p%pB", p, p, p);
ASSERT(len == 32); ASSERT(len == 32);
ASSERT(memcmp(buf + 4, "A0xffffffff0xffffffff0xffffffffB", 32) == 0); ASSERT(memcmp(buf + 4, "A0xffffffff0xffffffff0xffffffffB", 32) == 0);
} }
// Non-standard formatting // Non-standard formatting
@@ -2478,7 +2517,7 @@ static void test_str(void) {
char buf[100]; char buf[100];
struct mg_addr a; struct mg_addr a;
uint32_t addr = mg_htonl(0x2000001); uint32_t addr = mg_htonl(0x2000001);
memcpy(a.ip, &addr, sizeof(uint32_t)); memcpy(a.addr.ip, &addr, sizeof(uint32_t));
a.port = mg_htons(3); a.port = mg_htons(3);
a.is_ip6 = false; a.is_ip6 = false;
@@ -2488,8 +2527,8 @@ static void test_str(void) {
11); 11);
ASSERT(strcmp(buf, "2.0.0.1:3 7") == 0); ASSERT(strcmp(buf, "2.0.0.1:3 7") == 0);
memset(a.ip, 0, sizeof(a.ip)); memset(a.addr.ip, 0, sizeof(a.addr.ip));
a.ip[0] = 1, a.ip[1] = 100, a.ip[2] = 33; a.addr.ip[0] = 1, a.addr.ip[1] = 100, a.addr.ip[2] = 33;
a.is_ip6 = true; a.is_ip6 = true;
ASSERT(mg_snprintf(buf, sizeof(buf), "%M %d", mg_print_ip, &a, 7) == 24); ASSERT(mg_snprintf(buf, sizeof(buf), "%M %d", mg_print_ip, &a, 7) == 24);
ASSERT(strcmp(buf, "[164:2100:0:0:0:0:0:0] 7") == 0); ASSERT(strcmp(buf, "[164:2100:0:0:0:0:0:0] 7") == 0);
@@ -2636,7 +2675,7 @@ static void test_util(void) {
ASSERT(mg_aton(mg_str("0.0.0.-1"), &a) == false); ASSERT(mg_aton(mg_str("0.0.0.-1"), &a) == false);
ASSERT(mg_aton(mg_str("127.0.0.1"), &a) == true); ASSERT(mg_aton(mg_str("127.0.0.1"), &a) == true);
ASSERT(a.is_ip6 == false); ASSERT(a.is_ip6 == false);
memcpy(&ipv4, a.ip, sizeof(ipv4)); memcpy(&ipv4, &a.addr.ip4, sizeof(ipv4));
ASSERT(ipv4 == mg_htonl(0x7f000001)); ASSERT(ipv4 == mg_htonl(0x7f000001));
ASSERT(mg_ntohl(ipv4) == 0x7f000001); ASSERT(mg_ntohl(ipv4) == 0x7f000001);
MG_STORE_BE32(&ipv4, 0x5678abcd); MG_STORE_BE32(&ipv4, 0x5678abcd);
@@ -2667,48 +2706,48 @@ static void test_util(void) {
((uint8_t *) &ipv3)[6] == 0x9e && ((uint8_t *) &ipv3)[7] == 0xf0); ((uint8_t *) &ipv3)[6] == 0x9e && ((uint8_t *) &ipv3)[7] == 0xf0);
ASSERT(MG_LOAD_BE64(&ipv3) == 0x5678abcd12349ef0); ASSERT(MG_LOAD_BE64(&ipv3) == 0x5678abcd12349ef0);
memset(a.ip, 0xa5, sizeof(a.ip)); memset(a.addr.ip, 0xa5, sizeof(a.addr.ip)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("1:2:3:4:5:6:7:8"), &a) == true); ASSERT(mg_aton(mg_str("1:2:3:4:5:6:7:8"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
e = "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08"; e = "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0); ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
memset(a.ip, 0xa5, sizeof(a.ip)); memset(a.addr.ip, 0xa5, sizeof(a.addr.ip)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("1:2::3"), &a) == true); ASSERT(mg_aton(mg_str("1:2::3"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
e = "\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03"; e = "\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0); ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(a.addr.ip, 0xaa, sizeof(a.addr.ip)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("1::1"), &a) == true); ASSERT(mg_aton(mg_str("1::1"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
e = "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; e = "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0); ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(a.addr.ip, 0xaa, sizeof(a.addr.ip)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("::fFff:1.2.3.4"), &a) == true); ASSERT(mg_aton(mg_str("::fFff:1.2.3.4"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
e = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04"; e = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0); ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(a.addr.ip, 0xaa, sizeof(a.addr.ip)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("::1"), &a) == true); ASSERT(mg_aton(mg_str("::1"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
ASSERT(a.scope_id == 0); ASSERT(a.scope_id == 0);
e = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; e = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0); ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(a.addr.ip, 0xaa, sizeof(a.addr.ip)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("1::"), &a) == true); ASSERT(mg_aton(mg_str("1::"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
e = "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; e = "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0); ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(a.addr.ip, 0xaa, sizeof(a.addr.ip)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("2001:4860:4860::8888"), &a) == true); ASSERT(mg_aton(mg_str("2001:4860:4860::8888"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
e = "\x20\x01\x48\x60\x48\x60\x00\x00\x00\x00\x00\x00\x00\x00\x88\x88"; e = "\x20\x01\x48\x60\x48\x60\x00\x00\x00\x00\x00\x00\x00\x00\x88\x88";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0); ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0); ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0);
ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0); ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0);
@@ -2717,22 +2756,22 @@ static void test_util(void) {
ASSERT(mg_url_decode("a=%123", 6, buf, sizeof(buf), 0) == 4 && ASSERT(mg_url_decode("a=%123", 6, buf, sizeof(buf), 0) == 4 &&
buf[2] == 0x12 && buf[3] == '3'); buf[2] == 0x12 && buf[3] == '3');
memset(a.ip, 0xaa, sizeof(a.ip)); memset(&a, 0, sizeof(a));
ASSERT(mg_aton(mg_str("::1%1"), &a) == true); ASSERT(mg_aton(mg_str("::1%1"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
ASSERT(a.scope_id == 1); ASSERT(a.scope_id == 1);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(&a, 0, sizeof(a));
ASSERT(mg_aton(mg_str("abcd::aabb:ccdd%17"), &a) == true); ASSERT(mg_aton(mg_str("abcd::aabb:ccdd%17"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
ASSERT(a.scope_id == 17); ASSERT(a.scope_id == 17);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(&a, 0xaa, sizeof(a)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("::1%17"), &a) == true); ASSERT(mg_aton(mg_str("::1%17"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
ASSERT(a.scope_id == 17); ASSERT(a.scope_id == 17);
memset(a.ip, 0xaa, sizeof(a.ip)); memset(&a, 0xaa, sizeof(a)), a.is_ip6 = false;
ASSERT(mg_aton(mg_str("::1%255"), &a) == true); ASSERT(mg_aton(mg_str("::1%255"), &a) == true);
ASSERT(a.is_ip6 == true); ASSERT(a.is_ip6 == true);
ASSERT(a.scope_id == 255); ASSERT(a.scope_id == 255);
@@ -2979,7 +3018,7 @@ static void eZ(struct mg_connection *c, int ev, void *ev_data) {
static void eS(struct mg_connection *c, int ev, void *ev_data) { static void eS(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
ASSERT (mg_send(c, "NADA", 0)); ASSERT(mg_send(c, "NADA", 0));
mg_http_reply(c, 200, "", "abcd"); mg_http_reply(c, 200, "", "abcd");
} }
(void) ev_data; (void) ev_data;
@@ -3610,6 +3649,39 @@ static void test_json(void) {
val = mg_json_get_tok(json, "$.a"); val = mg_json_get_tok(json, "$.a");
ASSERT(mg_strcmp(val, expected) == 0); ASSERT(mg_strcmp(val, expected) == 0);
} }
// mg_json_get_num: parsing exponential
{
double d = 0.0, tolerance = 1e-12;
json = mg_str(
"{"
"\"i\":1e3,"
"\"n\":-2.5e2,"
"\"p\":3E+4,"
"\"m\":4.25E-1,"
"\"z\":0e0,"
"\"s\":-0.0e+0,"
"\"a\":[6.123456789e3,-1e-9],"
"\"bad\":\"1e3\""
"}");
ASSERT(mg_json_get_num(json, "$.i", &d) == true);
ASSERT(fabs(d - 1000.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.n", &d) == true);
ASSERT(fabs(d - -250.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.p", &d) == true);
ASSERT(fabs(d - 30000.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.m", &d) == true);
ASSERT(fabs(d - 0.425) < tolerance);
ASSERT(mg_json_get_num(json, "$.z", &d) == true);
ASSERT(fabs(d - 0.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.s", &d) == true);
ASSERT(fabs(d - 0.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.a[0]", &d) == true);
ASSERT(fabs(d - 6123.456789) < tolerance);
ASSERT(mg_json_get_num(json, "$.a[1]", &d) == true);
ASSERT(fabs(d - -1e-9) < tolerance);
ASSERT(mg_json_get_num(json, "$.bad", &d) == false);
}
} }
static void resp_rpc(struct mg_rpc_req *r) { static void resp_rpc(struct mg_rpc_req *r) {
@@ -4023,8 +4095,8 @@ static void test_crypto(void) {
test_rsa(); test_rsa();
} }
#define DASHBOARD(x) \
#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true"); printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true");
int main(void) { int main(void) {
const char *debug_level = getenv("V"); const char *debug_level = getenv("V");
@@ -4121,9 +4193,13 @@ int main(void) {
test_sntp(); test_sntp();
DASHBOARD("sntp"); DASHBOARD("sntp");
#if MG_TLS != MG_TLS_BUILTIN
s_error = false; s_error = false;
test_mqtt(); // sorry, MQTT_LOCALHOST is also skipped test_mqtt(); // sorry, MQTT_LOCALHOST is also skipped
DASHBOARD("mqtt"); DASHBOARD("mqtt");
#else
(void) test_mqtt;
#endif
s_error = false; s_error = false;
test_http_client(); test_http_client();
@@ -4134,13 +4210,19 @@ int main(void) {
#endif #endif
s_error = false; s_error = false;
test_poll(); test_poll();
printf("HEALTH_DASHBOARD\t\"poll\": %s\n", s_error ? "false":"true"); printf("HEALTH_DASHBOARD\t\"poll\": %s\n", s_error ? "false" : "true");
// last entry with no comma // last entry with no comma
#ifdef NO_ABORT #ifdef NO_ABORT
if (s_abort != 0) return EXIT_FAILURE; if (s_abort != 0) return EXIT_FAILURE;
#endif #endif
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \
defined(MBEDTLS_PSA_CRYPTO_C)
// Call mbedtls_psa_crypto_free() here to avoid triggering memory-leak
// detectors. We are actually freeing all our resources and leaving
mbedtls_psa_crypto_free();
#endif
printf("SUCCESS. Total tests: %d\n", s_num_tests); printf("SUCCESS. Total tests: %d\n", s_num_tests);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+8
View File
@@ -0,0 +1,8 @@
-----BEGIN CERTIFICATE-----
MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv
b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI
TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl
fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg
epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs
us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=
-----END CERTIFICATE-----
+9
View File
@@ -0,0 +1,9 @@
-----BEGIN CERTIFICATE-----
MIIBMTCB2aADAgECAgkAluqkgeuV/zUwCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI
TW9uZ29vc2UwHhcNMjQwNTA3MTQzNzM2WhcNMzQwNTA1MTQzNzM2WjARMQ8wDQYD
VQQDDAZzZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASo3oEiG+BuTt5y
ZRyfwNr0C+SP+4M0RG2pYkb2v+ivbpfi72NHkmXiF/kbHXtgmSrn/PeTqiA8M+mg
BhYjDX+zoxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDRwAw
RAIgTXW9MITQSwzqbNTxUUdt9DcB+8pPUTbWZpiXcA26GMYCIBiYw+DSFMLHmkHF
+5U3NXW3gVCLN9ntD5DAx8LTG8sB
-----END CERTIFICATE-----
+5
View File
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49
AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl
4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==
-----END EC PRIVATE KEY-----
+15 -35
View File
@@ -9,38 +9,14 @@ static const char *s_root_dir = ".";
static const char *s_listening_addr = "http://0.0.0.0:8000"; static const char *s_listening_addr = "http://0.0.0.0:8000";
static const char *s_enable_hexdump = "no"; static const char *s_enable_hexdump = "no";
static const char *s_ssi_pattern = "#.html"; static const char *s_ssi_pattern = "#.html";
static const char *s_upload_dir = NULL; // File uploads disabled by default static const char *s_upload_dir = NULL; // File uploads disabled by default
static const char *s_ca_path = NULL; // TLS CA file. Enables mutual TLS
static const char *s_crt_path = "crt.pem"; // TLS cert file
static const char *s_key_path = "key.pem"; // TLS key file
// Self signed certificates, see // For self signed certificates, see
// https://github.com/cesanta/mongoose/blob/master/test/certs/generate.sh // https://github.com/cesanta/mongoose/blob/master/test/certs/generate.sh
#ifdef TLS_TWOWAY static struct mg_str s_ca, s_crt, s_key; // Initialised in main
static const char *s_tls_ca =
"-----BEGIN CERTIFICATE-----\n"
"MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n"
"b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n"
"TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n"
"fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n"
"epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n"
"us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n"
"-----END CERTIFICATE-----\n";
#endif
static const char *s_tls_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIBMTCB2aADAgECAgkAluqkgeuV/zUwCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI\n"
"TW9uZ29vc2UwHhcNMjQwNTA3MTQzNzM2WhcNMzQwNTA1MTQzNzM2WjARMQ8wDQYD\n"
"VQQDDAZzZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASo3oEiG+BuTt5y\n"
"ZRyfwNr0C+SP+4M0RG2pYkb2v+ivbpfi72NHkmXiF/kbHXtgmSrn/PeTqiA8M+mg\n"
"BhYjDX+zoxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDRwAw\n"
"RAIgTXW9MITQSwzqbNTxUUdt9DcB+8pPUTbWZpiXcA26GMYCIBiYw+DSFMLHmkHF\n"
"+5U3NXW3gVCLN9ntD5DAx8LTG8sB\n"
"-----END CERTIFICATE-----\n";
static const char *s_tls_key =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49\n"
"AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl\n"
"4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==\n"
"-----END EC PRIVATE KEY-----\n";
// Handle interrupts, like Ctrl-C // Handle interrupts, like Ctrl-C
static int s_signo; static int s_signo;
@@ -54,11 +30,9 @@ static void cb(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ACCEPT && c->is_tls) { if (ev == MG_EV_ACCEPT && c->is_tls) {
struct mg_tls_opts opts; struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
#ifdef TLS_TWOWAY opts.cert = s_crt;
opts.ca = mg_str(s_tls_ca); opts.key = s_key;
#endif opts.ca = s_ca;
opts.cert = mg_str(s_tls_cert);
opts.key = mg_str(s_tls_key);
mg_tls_init(c, &opts); mg_tls_init(c, &opts);
} }
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
@@ -144,6 +118,11 @@ int main(int argc, char *argv[]) {
} }
} }
// Load certificates from files
s_ca = mg_file_read(&mg_fs_posix, s_ca_path);
s_crt = mg_file_read(&mg_fs_posix, s_crt_path);
s_key = mg_file_read(&mg_fs_posix, s_key_path);
// Root directory must not contain double dots. Make it absolute // Root directory must not contain double dots. Make it absolute
// Do the conversion only if the root dir spec does not contain overrides // Do the conversion only if the root dir spec does not contain overrides
if (strchr(s_root_dir, ',') == NULL) { if (strchr(s_root_dir, ',') == NULL) {
@@ -154,6 +133,7 @@ int main(int argc, char *argv[]) {
// Initialise stuff // Initialise stuff
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler); signal(SIGTERM, signal_handler);
setvbuf(stdout, NULL, _IONBF, 0); // Switch off output buffering
mg_log_set(s_debug_level); mg_log_set(s_debug_level);
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
if ((c = mg_http_listen(&mgr, s_listening_addr, cb, NULL)) == NULL) { if ((c = mg_http_listen(&mgr, s_listening_addr, cb, NULL)) == NULL) {
+31
View File
@@ -0,0 +1,31 @@
PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options
#CFLAGS += -fsanitize=address,undefined,alignment
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_HTTP_DIRLIST_TIME_FMT="%Y/%m/%d %H:%M:%S"
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_IPV6=1 -DMG_ENABLE_SSI=1
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 -lbcrypt # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
OUT ?= -o $(PROG) # Build output
endif
all: $(PROG) # Default target. Build and run program
$(RUN) ./$(PROG) $(ARGS)
$(PROG): $(SOURCES) # Build program from sources
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
vc98:
cl $(SOURCES) -DMG_ENABLE_SSI=1 -DMG_TLS=MG_TLS_BUILTIN
clean: # Cleanup. Delete built program and all build artifacts
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
+89
View File
@@ -0,0 +1,89 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
static int s_debug_level = MG_LL_DEBUG;
static const char *s_http_addr = "http://0.0.0.0:8000";
static const char *s_https_addr = "https://0.0.0.0:8443";
// Self signed certificates, see
// https://github.com/cesanta/mongoose/blob/master/test/certs/generate.sh
static const char *s_tls_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIBMTCB2aADAgECAgkAluqkgeuV/zUwCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI\n"
"TW9uZ29vc2UwHhcNMjQwNTA3MTQzNzM2WhcNMzQwNTA1MTQzNzM2WjARMQ8wDQYD\n"
"VQQDDAZzZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASo3oEiG+BuTt5y\n"
"ZRyfwNr0C+SP+4M0RG2pYkb2v+ivbpfi72NHkmXiF/kbHXtgmSrn/PeTqiA8M+mg\n"
"BhYjDX+zoxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDRwAw\n"
"RAIgTXW9MITQSwzqbNTxUUdt9DcB+8pPUTbWZpiXcA26GMYCIBiYw+DSFMLHmkHF\n"
"+5U3NXW3gVCLN9ntD5DAx8LTG8sB\n"
"-----END CERTIFICATE-----\n";
static const char *s_tls_key =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49\n"
"AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl\n"
"4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==\n"
"-----END EC PRIVATE KEY-----\n";
// HTTPS listener serves requests
static void https_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ACCEPT && c->is_tls) {
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts));
opts.cert = mg_str(s_tls_cert);
opts.key = mg_str(s_tls_key);
mg_tls_init(c, &opts);
}
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/hello"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%d}\n", MG_ESC("status"), 1);
} else {
mg_http_reply(c, 200, "", "Hi! Curent time is %llu", mg_now());
}
}
}
// HTTP listener sends redirects to HTTPS
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
char buf[256];
mg_snprintf(buf, sizeof(buf), "Location: https://%M:%u%.*s\r\n",
mg_print_ip, &c->loc, mg_url_port(s_https_addr), hm->uri.len,
hm->uri.buf);
// If you want to redirect to a name, you must provide a redirection URL:
// - static const char *s_https_redirect_addr = "https://yourname:port"
// - mg_snprintf(buf, sizeof(buf), "Location: %s%.*s\r\n",
// s_https_redirect_addr, hm->uri.len, hm->uri.buf);
mg_http_reply(c, 302, buf, "%s", buf); // 302 redirect
}
}
int main(void) {
struct mg_mgr mgr;
mg_log_set(s_debug_level);
mg_mgr_init(&mgr);
if (mg_http_listen(&mgr, s_http_addr, http_ev_handler, NULL) == NULL) {
exit(EXIT_FAILURE);
}
if (mg_http_listen(&mgr, s_https_addr, https_ev_handler, NULL) == NULL) {
exit(EXIT_FAILURE);
}
MG_INFO(("Mongoose version : v%s", MG_VERSION));
MG_INFO(("HTTP listener : %s", s_http_addr));
MG_INFO(("HTTPS listener : %s", s_https_addr));
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../mongoose.h
+26
View File
@@ -0,0 +1,26 @@
PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options
#CFLAGS += -fsanitize=address,undefined,alignment
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 -lbcrypt # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
endif
all: $(PROG) # Default target. Build and run program
$(RUN) ./$(PROG) $(ARGS)
$(PROG): $(SOURCES) # Build program from sources
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
clean: # Cleanup. Delete built program and all build artifacts
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
+111
View File
@@ -0,0 +1,111 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// This example shows how we can poll an external HTTP server periodically, and
// make sure that not more than one instance of the active request in flight.
// Each request has a configured timeout.
#include "mongoose.h"
static const char *s_url = "https://mongoose.ws/tls/api/ca";
static const char *s_post_data = "hi";
static const uint64_t s_connect_timeout_ms = 5500; // milliseconds
static struct mg_connection *s_conn = NULL;
// TLS certificate, obtained using https://mongoose.ws/tls/
#define TLS_CA \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP\n" \
"MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy\n" \
"Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa\n" \
"Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF\n" \
"bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c\n" \
"S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb\n" \
"R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB\n" \
"9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB\n" \
"MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j\n" \
"cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB\n" \
"BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE\n" \
"DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j\n" \
"ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0\n" \
"RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d\n" \
"AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8\n" \
"otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA\n" \
"aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm\n" \
"Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2\n" \
"HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1\n" \
"Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR\n" \
"xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d\n" \
"tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/\n" \
"jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS\n" \
"u1igv3OefnWjSQ==\n" \
"-----END CERTIFICATE-----"
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
uint64_t *start_time = (uint64_t *) c->data; // Stored start time
bool *success = (bool *) (start_time + 1); // Stored success flag
if (ev == MG_EV_OPEN) {
*start_time = mg_millis();
MG_DEBUG(("%lu Connection started, time: %llu", c->id, *start_time));
} else if (ev == MG_EV_POLL) {
if (*start_time + s_connect_timeout_ms < mg_millis()) {
mg_error(c, "Connect timeout");
}
} else if (ev == MG_EV_CONNECT) {
// Connected to server. Extract host name from URL
struct mg_str host = mg_url_host(s_url);
if (c->is_tls) {
struct mg_tls_opts opts = {};
opts.ca = mg_str(TLS_CA);
opts.name = host;
mg_tls_init(c, &opts);
}
// Send request
int content_length = s_post_data ? strlen(s_post_data) : 0;
mg_printf(c,
"%s %s HTTP/1.0\r\n"
"Host: %.*s\r\n"
"Content-Type: octet-stream\r\n"
"Content-Length: %d\r\n"
"\r\n",
s_post_data ? "POST" : "GET", mg_url_uri(s_url), host.len,
host.buf, content_length);
mg_send(c, s_post_data, content_length);
} else if (ev == MG_EV_HTTP_MSG) {
// Response is received. Print it
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
MG_INFO(("%lu Got response %lu bytes %lu", c->id, hm->body.len, c->rtls.len));
*success = true;
c->is_closing = 1;
} else if (ev == MG_EV_ERROR) {
MG_ERROR(("%lu Connection error: %s", c->id, ev_data));
} else if (ev == MG_EV_CLOSE) {
MG_DEBUG(("%lu Connection closed %lu", c->id, mg_millis() - *start_time));
if (*success == false) MG_ERROR(("%lu NO RESPONSE %lu", c->id, c->rtls.len));
if (c->is_draining == 0) s_conn = NULL;
}
}
static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
// If connection is already there, do nothing. Otherwise, start it
if (s_conn != NULL) {
MG_DEBUG(("Connection is already active..."));
} else {
s_conn = mg_http_connect(mgr, s_url, http_ev_handler, NULL);
}
}
int main(void) {
struct mg_mgr mgr;
mg_log_set(MG_LL_INFO);
mg_mgr_init(&mgr);
// Attempt to start a POST request frequently
mg_timer_add(&mgr, 50, MG_TIMER_REPEAT, timer_fn, &mgr);
for (;;) mg_mgr_poll(&mgr, 10);
mg_mgr_free(&mgr);
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../mongoose.h
@@ -9,7 +9,7 @@ firmware.bin: wizard
make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./ make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h
@@ -9,7 +9,7 @@ firmware.bin: wizard
make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./ make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h
@@ -9,7 +9,7 @@ firmware.bin: wizard
make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./ make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h
+19 -18
View File
@@ -15,27 +15,28 @@
static const char *s_url = "mqtt://broker.hivemq.com:1883"; static const char *s_url = "mqtt://broker.hivemq.com:1883";
static const char *s_sub_topic = "mg/123/rx"; // Subscribe topic static const char *s_sub_topic = "mg/123/rx"; // Subscribe topic
static const char *s_pub_topic = "mg/123/tx"; // Publish topic static const char *s_pub_topic = "mg/123/tx"; // Publish topic
static int s_qos = 1; // MQTT QoS static uint8_t s_qos = 1; // MQTT QoS
static struct mg_connection *s_mqtt_conn; // Client connection static struct mg_connection *s_mqtt_conn; // Client connection
static void subscribe(struct mg_connection *c, const char *topic) { static void subscribe(struct mg_connection *c, struct mg_str topic) {
struct mg_mqtt_opts opts = {}; struct mg_mqtt_opts opts = {};
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.topic = mg_str(topic); opts.topic = topic;
opts.qos = s_qos; opts.qos = s_qos;
mg_mqtt_sub(c, &opts); mg_mqtt_sub(c, &opts);
MG_INFO(("%lu SUBSCRIBED to %s", c->id, topic)); MG_INFO(("%lu SUBSCRIBED to %.*s", c->id, topic.len, topic.buf));
} }
static void publish(struct mg_connection *c, const char *topic, static void publish(struct mg_connection *c, struct mg_str topic,
const char *message) { struct mg_str message) {
struct mg_mqtt_opts opts = {}; struct mg_mqtt_opts opts = {};
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.topic = mg_str(topic); opts.topic = topic;
opts.message = mg_str(message); opts.message = message;
opts.qos = s_qos; opts.qos = s_qos;
mg_mqtt_pub(c, &opts); mg_mqtt_pub(c, &opts);
MG_INFO(("%lu PUBLISHED %s -> %s", c->id, topic, message)); MG_INFO(("%lu PUBLISHED %.*s -> %.*s", c->id, topic.len, topic.buf,
message.len, message.buf));
} }
static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) { static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
@@ -54,14 +55,14 @@ static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
} else if (ev == MG_EV_MQTT_OPEN) { } else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connect is successful // MQTT connect is successful
MG_INFO(("%lu CONNECTED to %s", c->id, s_url)); MG_INFO(("%lu CONNECTED to %s", c->id, s_url));
subscribe(c, s_sub_topic); subscribe(c, mg_str(s_sub_topic));
} else if (ev == MG_EV_MQTT_MSG) { } else if (ev == MG_EV_MQTT_MSG) {
// When we get echo response, print it // When we get echo response, print it
char response[100]; char response[100];
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
mg_snprintf(response, sizeof(response), "Got %.*s -> %.*s", mm->topic.len, mg_snprintf(response, sizeof(response), "Got %.*s -> %.*s", mm->topic.len,
mm->topic.buf, mm->data.len, mm->data.buf); mm->topic.buf, mm->data.len, mm->data.buf);
publish(c, s_pub_topic, response); publish(c, mg_str(s_pub_topic), mg_str(response));
} else if (ev == MG_EV_MQTT_CMD) { } else if (ev == MG_EV_MQTT_CMD) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
if (mm->cmd == MQTT_CMD_PINGREQ) mg_mqtt_pong(c); if (mm->cmd == MQTT_CMD_PINGREQ) mg_mqtt_pong(c);
@@ -73,14 +74,14 @@ static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
// Timer function - recreate client connection if it is closed // Timer function - recreate client connection if it is closed
static void timer_fn(void *arg) { static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
struct mg_mqtt_opts opts = {.clean = true,
.qos = s_qos,
.topic = mg_str(s_pub_topic),
.keepalive = 5,
.version = 4,
.message = mg_str("bye")};
if (s_mqtt_conn == NULL) { if (s_mqtt_conn == NULL) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
struct mg_mqtt_opts opts = {.clean = true,
.qos = s_qos,
.topic = mg_str(s_pub_topic),
.keepalive = 5,
.version = 4,
.message = mg_str("bye")};
s_mqtt_conn = mg_mqtt_connect(mgr, s_url, &opts, mqtt_ev_handler, NULL); s_mqtt_conn = mg_mqtt_connect(mgr, s_url, &opts, mqtt_ev_handler, NULL);
} else { } else {
mg_mqtt_ping(s_mqtt_conn); mg_mqtt_ping(s_mqtt_conn);
+28
View File
@@ -0,0 +1,28 @@
PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files, packed_fs.c contains ca.pem, which contains CA certs for TLS
CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1
CFLAGS_MONGOOSE += -DMG_TLS=MG_TLS_BUILTIN
#CFLAGS_MONGOOSE += -DMG_TLS=MG_TLS_OPENSSL -L/opt/homebrew/opt/openssl/lib -I/opt/homebrew/opt/openssl/include -lssl -lcrypto
#CFLAGS_MONGOOSE += -DMG_TLS=MG_TLS_MBED -L/opt/homebrew/opt/mbedtls/lib -I/opt/homebrew/opt/mbedtls/include -lmbedtls -lmbedcrypto -lmbedx509
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
OUT ?= -o $(PROG) # Build output
endif
all: $(PROG) # Default target. Build and run program
$(RUN) ./$(PROG) $(ARGS)
$(PROG): $(SOURCES) # Build program from sources
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
clean: # Cleanup. Delete built program and all build artifacts
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2013-2026 Cesanta Software Limited
// All rights reserved
//
// This example implements a JSON-RPC server that connects to the MQTT server
// and expects requests on MQTT_RX_TOPIC, and sends responses to the
// MQTT_TX_TOPIC. Only a single RPC method is implemented, "ota.update",
// which expects parameters {"total": XX, "offset": XX, "chunk": "..."}
// The rpc_ota_update() function handles those requests and calls Mongoose
// ota_begin(), ota_write(), ota_end() respectively.
//
// Visit https://mongoose.ws/mqtt/ web page to push the firmware binary.
#include "mongoose.h"
#define MQTT_SERVER_URL "mqtt://broker.hivemq.com:1883"
#define MQTT_RX_TOPIC "mg/123/rx"
#define MQTT_TX_TOPIC "mg/123/tx"
static uint8_t s_qos = 1; // MQTT QoS
static struct mg_connection *s_conn; // MQTT Client connection
static struct mg_rpc *s_rpc = NULL; // List of registered RPC methods
static void rpc_ota_update(struct mg_rpc_req *r) {
long ofs = mg_json_get_long(r->frame, "$.params.offset", -1);
long tot = mg_json_get_long(r->frame, "$.params.total", -1);
int len = 0;
char *buf = mg_json_get_b64(r->frame, "$.params.chunk", &len);
if (buf == NULL) {
mg_rpc_err(r, 1, "%m", MG_ESC("Chunk decoding error"));
} else if (ofs < 0 || tot < 0) {
mg_rpc_err(r, 1, "%m", MG_ESC("offset and total not set"));
} else if (ofs == 0 && mg_ota_begin((size_t) tot) == false) {
mg_rpc_err(r, 1, "\"mg_ota_begin(%ld) failed\"", tot);
} else if (len > 0 && mg_ota_write(buf, len) == false) {
mg_rpc_err(r, 1, "\"mg_ota_write(%lu) @%ld failed\"", len, ofs);
mg_ota_end();
} else if (len == 0 && mg_ota_end() == false) {
mg_rpc_err(r, 1, "\"mg_ota_end() failed\"", tot);
} else {
mg_rpc_ok(r, "%m", MG_ESC("ok"));
}
mg_free(buf);
}
static void subscribe(struct mg_connection *c, struct mg_str topic) {
struct mg_mqtt_opts opts = {};
memset(&opts, 0, sizeof(opts));
opts.topic = topic;
opts.qos = s_qos;
mg_mqtt_sub(c, &opts);
MG_INFO(("%lu SUBSCRIBED to %.*s", c->id, topic.len, topic.buf));
}
static void publish(struct mg_connection *c, struct mg_str topic,
struct mg_str message) {
struct mg_mqtt_opts opts = {};
memset(&opts, 0, sizeof(opts));
opts.topic = topic;
opts.message = message;
opts.qos = s_qos;
mg_mqtt_pub(c, &opts);
MG_INFO(("%lu PUBLISHED %.*s -> %.*s", c->id, topic.len, topic.buf,
message.len, message.buf));
}
static void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_OPEN) {
MG_INFO(("%lu CREATED", c->id));
// c->is_hexdumping = 1;
} else if (ev == MG_EV_CONNECT) {
MG_INFO(("Connected"));
} else if (ev == MG_EV_ERROR) {
// On error, log error message
MG_ERROR(("%lu ERROR %s", c->id, (char *) ev_data));
} else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connect is successful
MG_INFO(("%lu CONNECTED", c->id));
subscribe(c, mg_str(MQTT_RX_TOPIC));
} else if (ev == MG_EV_MQTT_MSG) {
// Treat this message as JSON-RPC: process an RPC request
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
struct mg_iobuf io = {0, 0, 0, 512};
struct mg_rpc_req r = {&s_rpc, NULL, mg_pfn_iobuf,
&io, NULL, {mm->data.buf, mm->data.len}};
size_t clipped_len = mm->data.len > 512 ? 512 : mm->data.len;
MG_INFO(("%lu RECEIVED %.*s <- %.*s", c->id, clipped_len, mm->data.buf,
mm->topic.len, mm->topic.buf));
mg_rpc_process(&r);
if (io.buf != NULL && io.len > 0) {
publish(c, mg_str(MQTT_TX_TOPIC), mg_str_n((char *) io.buf, io.len));
}
mg_iobuf_free(&io);
} else if (ev == MG_EV_CLOSE) {
MG_INFO(("%lu CLOSED", c->id));
s_conn = NULL; // Mark that we're closed
}
}
// SNTP timer function. Sync up time - may be needed for TLS on embedded
static void timer_sntp(void *param) {
static uint64_t hourly_timer = 0;
uint64_t t1 = mg_now(), t2 = mg_millis();
if (t1 < t2 + 3600 || mg_timer_expired(&hourly_timer, 3600000, t2)) {
mg_sntp_connect(param, "udp://time.google.com:123", NULL, NULL);
}
}
static void timer_fn(void *arg) {
if (s_conn == NULL) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
struct mg_mqtt_opts opts = {.clean = true,
.qos = s_qos,
.topic = mg_str(MQTT_TX_TOPIC),
.keepalive = 5,
.version = 4,
.message = mg_str("{\"method\":\"bye\"}")};
s_conn = mg_mqtt_connect(mgr, MQTT_SERVER_URL, &opts, ev_handler, NULL);
} else {
mg_mqtt_ping(s_conn);
}
}
int main(void) {
struct mg_mgr mgr;
mg_log_set(MG_LL_INFO);
mg_mgr_init(&mgr);
mg_timer_add(&mgr, 3000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &mgr);
mg_timer_add(&mgr, 100, MG_TIMER_REPEAT, timer_sntp, &mgr);
mg_rpc_add(&s_rpc, mg_str("ota.update"), rpc_ota_update, NULL);
for (;;) mg_mgr_poll(&mgr, 100);
mg_mgr_free(&mgr);
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../mongoose.h
@@ -9,7 +9,7 @@ firmware.bin: wizard
make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./ make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h
@@ -9,7 +9,7 @@ firmware.bin: wizard
make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./ make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h
@@ -17,10 +17,10 @@ firmware.axf: wizard
cp wizard/$(TARGET)/$$PROJNAME.axf firmware.axf cp wizard/$(TARGET)/$$PROJNAME.axf firmware.axf
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/source ; rm mongoose.[ch] ; cp -rL ../../../../../mongoose.[ch] . cd wizard/mongoose ; rm mongoose.[ch] ; cp -rL ../../../../../mongoose.[ch] .
clean: clean:
@@ -17,10 +17,10 @@ firmware.axf: wizard
cp wizard/$(TARGET)/$$PROJNAME.axf firmware.axf cp wizard/$(TARGET)/$$PROJNAME.axf firmware.axf
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/source ; rm mongoose.[ch] ; cp -rL ../../../../../mongoose.[ch] . cd wizard/mongoose ; rm mongoose.[ch] ; cp -rL ../../../../../mongoose.[ch] .
clean: clean:
@@ -471,13 +471,17 @@ static void handle_scan_result(unsigned int i, mg_tcpip_event_handler_t evcb) {
mg_mprintf(bssid, "%M", print_mac, r.bssid); mg_mprintf(bssid, "%M", print_mac, r.bssid);
bss.BSSID = bssid; bss.BSSID = bssid;
bss.RSSI = (int8_t) -r.rssi; bss.RSSI = (int8_t) -r.rssi;
bss.has_n = r.dot11n; // TODO(scaprile): add 11ac, 11ax, etc bss.has_n = r.dot11n;
bss.has_ac = r.dot11ac;
bss.has_ax = r.dot11ax;
bss.channel = r.channel; bss.channel = r.channel;
bss.band = r.channel <= MAX_CHANNELS_BG ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G; bss.band = r.channel <= MAX_CHANNELS_BG ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G;
bss.security = r.wep ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN; bss.security = r.wep ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN;
if (r.wpa) bss.security |= MG_WIFI_SECURITY_WPA; if (r.wpa) bss.security |= MG_WIFI_SECURITY_WPA;
if (r.wpa2) bss.security |= MG_WIFI_SECURITY_WPA2; if (r.wpa2 || r.wpa2_sha256) bss.security |= MG_WIFI_SECURITY_WPA2;
// TODO(scaprile): add WPA3, enterprise if (r.wpa3_sae) bss.security |= MG_WIFI_SECURITY_WPA3;
if (r.wpa2_entp || r.wpa2_entp_sha256) bss.security |= MG_WIFI_SECURITY_WPA2_ENTERPRISE;
if (r.wpa3_1x_sha256 || r.wpa3_1x_sha384) bss.security |= MG_WIFI_SECURITY_WPA3_ENTERPRISE;
MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf,
bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI,
bss.security)); bss.security));
@@ -9,7 +9,7 @@ firmware.bin: wizard
make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./ make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h
@@ -9,7 +9,7 @@ firmware.bin: wizard
make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./ make -C wizard CFLAGS_EXTRA=$(CFLAGS_EXTRA) && mv wizard/firmware.bin ./
wizard: wizard:
hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \ hash=$$(curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s -X POST -H "Content-Type: application/json" -d '{"build":{"board":"$(BOARD)","ide":"$(IDE)","rtos":"$(RTOS)"},"http":{"http": true}}' $(WIZARD_URL)/api/hash | jq -r '.hash') \
&& curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip && curl -H 'Cookie: mongoose_wizard_uid=F2PPlAgQCqSthV1g' -s $(WIZARD_URL)/api/zip -o wizard.zip
unzip wizard.zip unzip wizard.zip
cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h cd wizard/mongoose ; rm mongoose.[ch] ; ln -s ../../../../../mongoose.c ; ln -s ../../../../../mongoose.h
Loaded 100 of 153 files, more files were not shown because too many files have changed in this diff. Show more