mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-01 13:10:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc71bc0588 | ||
|
|
08305b9000 | ||
|
|
69e8c0a92f | ||
|
|
b766c65615 | ||
|
|
8296b88fb6 | ||
|
|
a5efbf0592 | ||
|
|
37c98dd893 | ||
|
|
770a4ff27e | ||
|
|
b6560306ba | ||
|
|
5b39c93478 | ||
|
|
086008b716 | ||
|
|
35eb50a1e6 | ||
|
|
431e35ae8c | ||
|
|
25e2a99d8e | ||
|
|
88b01ef6f0 | ||
|
|
bd8ee86f9e | ||
|
|
ef96f7a28c | ||
|
|
03f240a244 | ||
|
|
c775f31ca7 | ||
|
|
1ca4fd1fef | ||
|
|
a6b6a0d779 | ||
|
|
38c326644f | ||
|
|
e6e7a690a7 | ||
|
|
c76f092ba1 | ||
|
|
60840d0d89 | ||
|
|
298ee549da | ||
|
|
5e4ae64e1b | ||
|
|
c5778aa608 | ||
|
|
6d32a7c238 | ||
|
|
497a3f5e73 | ||
|
|
10e67fdf2b | ||
|
|
2f2a5f2f02 | ||
|
|
aa21ed443a | ||
|
|
10adde595c | ||
|
|
c957f29f23 | ||
|
|
37f3ed5ba4 | ||
|
|
b68da1548b | ||
|
|
a9a94a6904 | ||
|
|
3a4660cd40 | ||
|
|
fc478a70f7 | ||
|
|
a6b5565e9d | ||
|
|
e7113408a9 | ||
|
|
fdf12844e3 | ||
|
|
bc5afa4519 | ||
|
|
a4607933a4 | ||
|
|
fd7db76324 | ||
|
|
b1e6cb320f | ||
|
|
2b9d805d84 | ||
|
|
a9a843d6e2 | ||
|
|
aae179177c | ||
|
|
300011a882 | ||
|
|
eda9a5726c | ||
|
|
16a5a7d9fb | ||
|
|
69c3bb8d12 | ||
|
|
8fc5651044 | ||
|
|
338756d0b0 | ||
|
|
c30afffcee | ||
|
|
76c0953210 | ||
|
|
381e31198b | ||
|
|
28ba381bd9 | ||
|
|
567963b034 | ||
|
|
7d3fe4f9cb | ||
|
|
c8bf39dcd8 | ||
|
|
393c811f31 | ||
|
|
be40827e17 | ||
|
|
ad45248327 | ||
|
|
c2d95cc19b | ||
|
|
4aa0a243f9 | ||
|
|
65a7996ef9 | ||
|
|
5879c3cbf2 | ||
|
|
cf9d56be79 | ||
|
|
da82df2041 | ||
|
|
954eb07b6d | ||
|
|
d7e708b9bc | ||
|
|
ce35cbf586 | ||
|
|
3aa5f2a1fd | ||
|
|
1b0906510b | ||
|
|
8a116d9289 | ||
|
|
71e0176944 | ||
|
|
bba5a0ca3a | ||
|
|
9c388b5f19 | ||
|
|
8afa940acf | ||
|
|
df1a409b21 | ||
|
|
7ec403341c | ||
|
|
1f2b14bb66 | ||
|
|
8e0c765c2c | ||
|
|
883e0a981f | ||
|
|
ff1fb5c745 | ||
|
|
795f16d5fb | ||
|
|
51773ecea3 | ||
|
|
2834aa57ee | ||
|
|
e2b5d7175f | ||
|
|
a419e16f26 | ||
|
|
789372c5b3 | ||
|
|
81f6acbce7 | ||
|
|
7fa96f3a38 | ||
|
|
876cf5605f | ||
|
|
8df146b1b8 | ||
|
|
f62f3545b4 | ||
|
|
4d6fa4b91c | ||
|
|
1be11a66e3 | ||
|
|
d1060d247b | ||
|
|
002c9df3de | ||
|
|
fa34b7439a |
No files matched your search
@@ -165,7 +165,7 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
if [[ "${{ matrix.target }}" == *port* ]]; then source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2 ; fi
|
if [[ "${{ matrix.target }}" == *port* ]]; then source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2 ; fi
|
||||||
make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} SSL=${{ matrix.ssl }} MULTIREC=NO
|
make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} SSL=${{ matrix.ssl }}
|
||||||
|
|
||||||
miprtxbe:
|
miprtxbe:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -180,7 +180,7 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC -e RTX" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf ; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2
|
source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC -e RTX" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf ; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2
|
||||||
make -C test mip_port_s390 MULTIREC=NO
|
make -C test mip_port_s390
|
||||||
|
|
||||||
l2:
|
l2:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -303,6 +303,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
target: [bsd, bsd_nortos]
|
||||||
cc: [gcc, clang, g++, clang++]
|
cc: [gcc, clang, g++, clang++]
|
||||||
name: bsd CC=${{ matrix.cc }}
|
name: bsd CC=${{ matrix.cc }}
|
||||||
steps:
|
steps:
|
||||||
@@ -316,7 +317,7 @@ jobs:
|
|||||||
make -C test/bsd shm_tap_bridge
|
make -C test/bsd shm_tap_bridge
|
||||||
test/bsd/shm_tap_bridge &
|
test/bsd/shm_tap_bridge &
|
||||||
sleep 2
|
sleep 2
|
||||||
make -C test/bsd CC=${{ matrix.cc }} > log
|
make -C test/bsd ${{ matrix.target }} CC=${{ matrix.cc }} > log
|
||||||
- if: success() || failure()
|
- if: success() || failure()
|
||||||
run: |
|
run: |
|
||||||
cat log
|
cat log
|
||||||
@@ -353,7 +354,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test s390 SSL=${{ matrix.ssl }} MULTIREC=NO
|
- run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test s390 SSL=${{ matrix.ssl }}
|
||||||
armhf:
|
armhf:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -366,7 +367,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test armhf SSL=${{ matrix.ssl }} MULTIREC=NO
|
- run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test armhf SSL=${{ matrix.ssl }}
|
||||||
unamalgamated-mg_prefix:
|
unamalgamated-mg_prefix:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -589,10 +590,12 @@ jobs:
|
|||||||
stm32/nucleo-f746zg/minimal
|
stm32/nucleo-f746zg/minimal
|
||||||
stm32/nucleo-f746zg/minimal-pppoe
|
stm32/nucleo-f746zg/minimal-pppoe
|
||||||
stm32/nucleo-f756zg/minimal
|
stm32/nucleo-f756zg/minimal
|
||||||
stm32/nucleo-h563zi/minimal
|
|
||||||
stm32/nucleo-h723zg/minimal
|
stm32/nucleo-h723zg/minimal
|
||||||
|
stm32/nucleo-h723zg/minimal-bsd-baremetal
|
||||||
|
stm32/nucleo-h723zg/minimal-bsd-freertos
|
||||||
- group: stm32-2
|
- group: stm32-2
|
||||||
examples: |
|
examples: |
|
||||||
|
stm32/nucleo-h563zi/minimal
|
||||||
stm32/nucleo-h743zi/minimal
|
stm32/nucleo-h743zi/minimal
|
||||||
stm32/nucleo-n657x0-q/minimal
|
stm32/nucleo-n657x0-q/minimal
|
||||||
stm32/nucleo-u5a5zj-q/minimal
|
stm32/nucleo-u5a5zj-q/minimal
|
||||||
@@ -633,8 +636,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: micropython/esp32
|
- path: micropython/esp32
|
||||||
- path: http/uart-bridge/esp32
|
- path: esp32/uart-bridge
|
||||||
- path: http/http-client/esp8266/http-client-server
|
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
env:
|
env:
|
||||||
GO: 0
|
GO: 0
|
||||||
@@ -652,7 +654,7 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- group: other
|
- group: other
|
||||||
examples: |
|
examples: |
|
||||||
mqtt/mqtt-client/microchip/same54-xpro
|
microchip/same54-xpro/mqtt-client
|
||||||
rp/pico-rmii
|
rp/pico-rmii
|
||||||
stm32/nucleo-f746zg/minimal-rm2
|
stm32/nucleo-f746zg/minimal-rm2
|
||||||
stm32/nucleo-f746zg/minimal-rndis
|
stm32/nucleo-f746zg/minimal-rndis
|
||||||
@@ -691,13 +693,13 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: esp32
|
- path: esp32/device-dashboard
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: echo # nothing specific to install or do
|
- run: echo # nothing specific to install or do
|
||||||
- run: make -C tutorials/http/device-dashboard/${{ matrix.example.path }} build
|
- run: make -C tutorials/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
|
||||||
device_dashboard_examples_arm:
|
device_dashboard_examples_arm:
|
||||||
@@ -709,12 +711,12 @@ jobs:
|
|||||||
- group: none
|
- group: none
|
||||||
cflags: ""
|
cflags: ""
|
||||||
examples: |
|
examples: |
|
||||||
http/device-dashboard/microchip/same54-xpro
|
microchip/same54-xpro/device-dashboard
|
||||||
renesas/ek-ra6m4-make-baremetal-builtin
|
renesas/ek-ra6m4-make-baremetal-builtin
|
||||||
- group: builtin
|
- group: builtin
|
||||||
cflags: -DMG_TLS=MG_TLS_BUILTIN
|
cflags: -DMG_TLS=MG_TLS_BUILTIN
|
||||||
examples: |
|
examples: |
|
||||||
http/device-dashboard/microchip/same54-xpro
|
microchip/same54-xpro/device-dashboard
|
||||||
renesas/ek-ra6m4-make-baremetal-builtin
|
renesas/ek-ra6m4-make-baremetal-builtin
|
||||||
name: device dashboard ARM ${{ matrix.group }}
|
name: device dashboard ARM ${{ matrix.group }}
|
||||||
steps:
|
steps:
|
||||||
@@ -740,19 +742,19 @@ jobs:
|
|||||||
done
|
done
|
||||||
exit $failed
|
exit $failed
|
||||||
|
|
||||||
# mqtt_dashboard_examples:
|
mqtt_dashboard_examples_arm:
|
||||||
# runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# strategy:
|
strategy:
|
||||||
# fail-fast: false
|
fail-fast: false
|
||||||
# matrix:
|
matrix:
|
||||||
# example:
|
example:
|
||||||
# - path:
|
- path: rp/pico-rndis-device
|
||||||
# name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
# steps:
|
steps:
|
||||||
# - uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
# with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
# - run: echo # nothing specific to install or do
|
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
# - run: make -C tutorials/${{ matrix.example.path }} build
|
- run: make -C tutorials/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
|
||||||
# dual_examples:
|
# dual_examples:
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ jobs:
|
|||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- name: baremetal
|
- name: baremetal
|
||||||
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
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/microchip/same54-xpro/device-dashboard 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}}
|
- run: make -C tutorials/microchip/same54-xpro/mqtt-client test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
|
||||||
test_pico_w5500:
|
test_pico_w5500:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -158,7 +158,8 @@ jobs:
|
|||||||
- path: stm32/nucleo-f746zg/minimal
|
- path: stm32/nucleo-f746zg/minimal
|
||||||
- path: stm32/nucleo-h563zi/minimal
|
- path: stm32/nucleo-h563zi/minimal
|
||||||
- path: stm32/nucleo-h723zg/minimal
|
- path: stm32/nucleo-h723zg/minimal
|
||||||
- path: stm32/nucleo-h723zg/minimal-bsd
|
- path: stm32/nucleo-h723zg/minimal-bsd-baremetal
|
||||||
|
- path: stm32/nucleo-h723zg/minimal-bsd-freertos
|
||||||
- path: stm32/nucleo-h743zi/minimal
|
- path: stm32/nucleo-h743zi/minimal
|
||||||
- path: stm32/stm32h573i-dk/minimal
|
- path: stm32/stm32h573i-dk/minimal
|
||||||
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
|
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
|
||||||
@@ -184,8 +185,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: http/uart-bridge/esp32
|
- path: esp32/uart-bridge
|
||||||
- path: http/http-client/esp8266/http-client-server
|
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
env:
|
env:
|
||||||
GO: 0
|
GO: 0
|
||||||
@@ -227,7 +227,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: esp32
|
- path: esp32/device-dashboard
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
env:
|
env:
|
||||||
GO: 0
|
GO: 0
|
||||||
@@ -235,12 +235,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: |
|
- run: |
|
||||||
if ./test/match_changed_files.sh '^src|tutorials/http/device-dashboard'; then
|
if ./test/match_changed_files.sh '^src|^tutorials/http/device-dashboard|^tutorials/${{ matrix.example.path }}'; then
|
||||||
echo GO=1 >> $GITHUB_ENV
|
echo GO=1 >> $GITHUB_ENV
|
||||||
# nothing specific to install or do
|
# nothing specific to install or do
|
||||||
fi
|
fi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: make -C tutorials/http/device-dashboard/${{ matrix.example.path }} build
|
run: make -C tutorials/${{ matrix.example.path }} build
|
||||||
|
|
||||||
device_dashboard_examples_arm:
|
device_dashboard_examples_arm:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -248,7 +248,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: http/device-dashboard/microchip/same54-xpro
|
- path: microchip/same54-xpro/device-dashboard
|
||||||
- path: renesas/ek-ra6m4-make-baremetal-builtin
|
- path: renesas/ek-ra6m4-make-baremetal-builtin
|
||||||
- path: rp/pico-rndis
|
- path: rp/pico-rndis
|
||||||
- path: rp/pico-w
|
- path: rp/pico-w
|
||||||
@@ -267,3 +267,24 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: make -C tutorials/${{ matrix.example.path }} build
|
run: make -C tutorials/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
mqtt_dashboard_examples_arm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
|
- path: rp/pico-rndis-device
|
||||||
|
name: ${{ matrix.example.path }}
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|^tutorials/mqtt/mqtt-dashboard/device|^tutorials/${{ matrix.example.path }}'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C tutorials/${{ matrix.example.path }} build
|
||||||
@@ -179,7 +179,6 @@ jobs:
|
|||||||
example:
|
example:
|
||||||
- path: esp32/uart-bridge
|
- path: esp32/uart-bridge
|
||||||
- path: esp32/micropython
|
- path: esp32/micropython
|
||||||
- path: esp8266/http-client-server
|
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
env:
|
env:
|
||||||
GO: 0
|
GO: 0
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
# Mongoose - Embedded Network Library
|
# Mongoose: a full-stack embedded networking library
|
||||||
|
|
||||||
|
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
|
||||||
|
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
|
||||||
|
firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on
|
||||||
|
STM32, NXP and other microcontrollers, on bare metal or with an RTOS, with no
|
||||||
|
external dependencies. Use it for cloud connectivity, device web dashboards,
|
||||||
|
secure firmware updates and remote control.
|
||||||
|
|
||||||
Mongoose is an open source, two-file C networking library and embedded web server for
|
|
||||||
microcontrollers that combines TCP/IP stack, HTTP, WebSocket, MQTT, TLS 1.3
|
|
||||||
stack, built-in firmware OTA updates, and device-dashboard infrastructure.
|
|
||||||
License: GPLv2 or commercial.
|
License: GPLv2 or commercial.
|
||||||
Website: https://mongoose.ws/
|
Website: https://mongoose.ws/
|
||||||
GitHub repo: https://github.com/cesanta/mongoose
|
GitHub repo: https://github.com/cesanta/mongoose
|
||||||
@@ -486,7 +490,7 @@ embedded devices and needs only a few lines added to `mongoose_config.h`:
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
#define MG_OTA_URL "https://mongoose.ws/ota/u/PREFIX/ota.json" // Copy from instructions panel
|
#define MG_OTA_URL "https://mongoose.ws/ota/u/PREFIX/ota.json" // Copy from instructions panel
|
||||||
#define MG_OTA_FIRMWARE_VERSION "MY_PRODUCT_1.4.2" // Change this
|
#define MG_FIRMWARE_VERSION "MY_PRODUCT_1.4.2" // Change this
|
||||||
```
|
```
|
||||||
|
|
||||||
No backend programming required. The device registers itself and checks in at
|
No backend programming required. The device registers itself and checks in at
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Mongoose - Embedded Web Server / Embedded Network Library
|
# Mongoose: a full-stack embedded networking library
|
||||||
|
|
||||||
[](https://opensource.org/licenses/gpl-2.0.php)
|
[](https://opensource.org/licenses/gpl-2.0.php)
|
||||||
[](https://github.com/cesanta/mongoose/actions)
|
[](https://github.com/cesanta/mongoose/actions)
|
||||||
@@ -7,11 +7,17 @@
|
|||||||
|
|
||||||
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
|
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
|
||||||
align="left" style="float:left;" />
|
align="left" style="float:left;" />
|
||||||
Two files. Drop `mongoose.c` and `mongoose.h` into any C/C++ project and get a
|
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
|
||||||
full network stack — HTTP, WebSocket, MQTT, TLS, firmware OTA
|
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
|
||||||
updates, device dashboard — on any microcontroller or desktop OS.
|
firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on
|
||||||
|
STM32, NXP and other microcontrollers, on bare metal or with an RTOS, with no
|
||||||
|
external dependencies. Use it for cloud connectivity, device web dashboards,
|
||||||
|
secure firmware updates and remote control.
|
||||||
|
|
||||||
On the market since 2004, used by vast number of open source and commercial
|
Use it instead of lwIP + mbedTLS + httpd + an MQTT client + OTA glue code, or
|
||||||
|
run it on top of lwIP or Zephyr.
|
||||||
|
|
||||||
|
On the market since 2004, used by a vast number of open source and commercial
|
||||||
products - it even runs on the International Space Station! Features include:
|
products - it even runs on the International Space Station! Features include:
|
||||||
|
|
||||||
- Cross-platform:
|
- Cross-platform:
|
||||||
@@ -24,7 +30,7 @@ products - it even runs on the International Space Station! Features include:
|
|||||||
- Source code is both ISO C and ISO C++ compliant
|
- Source code is both ISO C and ISO C++ compliant
|
||||||
- Very easy to integrate
|
- Very easy to integrate
|
||||||
- Own TCP/IP stack for bare metal / RTOS, or run on top of any existing one (lwIP, Zephyr, ...)
|
- Own TCP/IP stack for bare metal / RTOS, or run on top of any existing one (lwIP, Zephyr, ...)
|
||||||
- Built-in TLS 1.3 ECC stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
|
- Built-in TLS 1.3 stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
|
||||||
- Does not depend on any other software to implement networking
|
- Does not depend on any other software to implement networking
|
||||||
- Built-in firmware updates for STM32 H5, STM32 H7, NXP IMXRT, RP2040/2350, ESP32 and more
|
- Built-in firmware updates for STM32 H5, STM32 H7, NXP IMXRT, RP2040/2350, ESP32 and more
|
||||||
- Commercial support with proactive security updates; [CRA-compliant](https://mongoose.ws/security/cra-compliance/)
|
- Commercial support with proactive security updates; [CRA-compliant](https://mongoose.ws/security/cra-compliance/)
|
||||||
@@ -35,8 +41,32 @@ See https://mongoose.ws/ for complete documentation, videos, case studies, etc.
|
|||||||
|
|
||||||
## Supported platforms
|
## Supported platforms
|
||||||
|
|
||||||
Mongoose can work on top of any TCP/IP stack that supports BSD sockets API.
|
Mongoose has its own built-in TCP/IP stack with drivers, so it needs no
|
||||||
Platforms supported by the 3rd party TCP/IP stacks:
|
additional software to implement networking. The built-in stack runs on bare
|
||||||
|
metal and under any RTOS. Platforms supported by the Mongoose built-in TCP/IP
|
||||||
|
stack:
|
||||||
|
|
||||||
|
| MCU | Connectivity | Notes |
|
||||||
|
| :-- | :----------- | :---- |
|
||||||
|
| **STM32** | Built-in Ethernet | Ethernet-capable STM32F, STM32H5, STM32H7, STM32N6, STM32C5 variants |
|
||||||
|
| **NXP i.MX RT** | Built-in Ethernet | RT102x, RT104x, RT105x, RT106x, RT117x, RT118x (NETC) |
|
||||||
|
| **NXP MCX** | Built-in Ethernet | MCXN94x, MCXE247 |
|
||||||
|
| **NXP RW612** | Eth, WiFi, 802.15.4 | Separate Ethernet and radio drivers |
|
||||||
|
| **Microchip ATSAME54** | Built-in Ethernet | GMAC driver |
|
||||||
|
| **Renesas RA** | Built-in Ethernet | RA6M, RA8M |
|
||||||
|
| **Infineon XMC** | Built-in Ethernet | XMC4, XMC7 |
|
||||||
|
| **Texas Instruments** | Built-in Ethernet | TM4C, TMS570 |
|
||||||
|
| **RP2040, RP2350, STM32, others** | CYW43xx Wi-Fi chips | SPI or SDIO; Pico W, Pico 2 W, Pico RM2, Arduino Portenta H7 |
|
||||||
|
| **STM32, others** | ST67W6 Wi-Fi modules | SPI; for example, STM32U5 with the ST67W611M1 module |
|
||||||
|
| **MCUs with an NXP Wi-Fi SDK port** | NXP Wi-Fi | Requires the SDK's Wi-Fi port implementing the driver's network hooks |
|
||||||
|
| **NXP MCXW71** | Built-in 802.15.4 radio | IPv4 over IEEE 802.15.4 |
|
||||||
|
| **NXP RW612** | Built-in 802.15.4 radio | IPv4 over IEEE 802.15.4; requires NXP radio firmware |
|
||||||
|
| **Any MCU with SPI** | W5500 or W5100 Ethernet chips | External Wiznet MAC+PHY |
|
||||||
|
| **MCUs connected to a cellular modem** | Cellular | AT commands and PPP; for example, SIM800 |
|
||||||
|
| **MCUs with CMSIS Ethernet drivers** | Ethernet via CMSIS-Driver | Requires CMSIS Ethernet MAC and PHY drivers |
|
||||||
|
|
||||||
|
Mongoose can also run on top of any TCP/IP stack that supports the BSD
|
||||||
|
sockets API. Platforms supported via 3rd party TCP/IP stacks:
|
||||||
|
|
||||||
| TCP/IP stack | Notes |
|
| TCP/IP stack | Notes |
|
||||||
| :-------------- | :------- |
|
| :-------------- | :------- |
|
||||||
@@ -45,23 +75,6 @@ Platforms supported by the 3rd party TCP/IP stacks:
|
|||||||
| **Other** | Any other TCP/IP stack that supports BSD socket API, for example Amazon FreeRTOS-TCP |
|
| **Other** | Any other TCP/IP stack that supports BSD socket API, for example Amazon FreeRTOS-TCP |
|
||||||
| **Linux, Mac, Windows** | Workstations, server, single board computers, embedded Linux devices running on MPUs or FPGAs |
|
| **Linux, Mac, Windows** | Workstations, server, single board computers, embedded Linux devices running on MPUs or FPGAs |
|
||||||
|
|
||||||
Optionally, Mongoose provides its own built-in TCP/IP stack, eliminating the
|
|
||||||
need for additional software to implement networking functionality. The
|
|
||||||
built-in stack supports operation in both bare-metal and RTOS environments.
|
|
||||||
Platforms supported by the Mongoose built-in TCP/IP stack:
|
|
||||||
|
|
||||||
| Hardware | Notes |
|
|
||||||
| :------------- | :------- |
|
|
||||||
| **STM32** | All STM32 MCUs with built-in Ethernet: STM32Fxx, STM32H5xx, STM32H7xx |
|
|
||||||
| **NXP** | All NXP MCUs with built-in Ethernet: IMXRT102x, IMXRT104x, IMXRT105x, IMXRT106x, IMXRT117x, RW612, MCXN94x |
|
|
||||||
| **Microchip** | ATSAME54 MCUs with built-int Ethernet |
|
|
||||||
| **Renesas** | RA5M, RA6M, RA8M MCUs with built-in Ethernet |
|
|
||||||
| **Infineon** | XMC4, XMC7 MCUs with built-in Ethernet |
|
|
||||||
| **Texas Instruments** | TM4C, TMS570 MCUs with built-in Ethernet |
|
|
||||||
| **Cypress WiFi** | Any MCU with CY43xx WiFi chips, like RP2040 Pico-W, RP2350 Pico2-W, Arduino Portenta |
|
|
||||||
| **Wiznet Ethernet** | Any MCU that use Wiznet W5500 or Wiznet 5100 MAC+PHY chips |
|
|
||||||
| **Cellular** | NRF9160, SIM800 |
|
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
|
|
||||||
Below are quick snippets that should give an idea how simple the API is and
|
Below are quick snippets that should give an idea how simple the API is and
|
||||||
@@ -158,6 +171,17 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Licensing
|
||||||
|
|
||||||
|
Mongoose is dual-licensed:
|
||||||
|
- GPLv2: free for open source projects. Many popular open source projects use
|
||||||
|
Mongoose, for example
|
||||||
|
[SWUpdate](https://github.com/sbabic/swupdate/tree/master/mongoose), the
|
||||||
|
embedded Linux update framework
|
||||||
|
- Commercial: for closed-source products. Includes support directly from the
|
||||||
|
engineers who wrote the whole stack, and early security fixes.
|
||||||
|
See [licensing](https://mongoose.ws/licensing/)
|
||||||
|
|
||||||
## Commercial use
|
## Commercial use
|
||||||
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
||||||
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
|
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
|
||||||
@@ -220,6 +244,15 @@ Technical guides and deep dives into embedded web servers, WebUI integration and
|
|||||||
- [STM32 Ethernet and caches](https://mongoose.ws/articles/stm32-ethernet-and-cache/)
|
- [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/)
|
- [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/)
|
- [lwIP vs Mongoose - TCP/IP Stack Integration Benchmark](https://mongoose.ws/articles/lwip-vs-mongoose-tcpip-stack-integration/)
|
||||||
|
- [Embedded TCP/IP Stacks Comparison](https://mongoose.ws/embedded-tcpip-stack-comparison/)
|
||||||
|
- [STM32H5 File System on Internal Flash: LittleFS + Mongoose](https://mongoose.ws/articles/stm32h5-file-system-on-internal-flash/)
|
||||||
|
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/rw612-mdash/)
|
||||||
|
- [25000 WebSocket messages per second on STM32 Nucleo-H723ZG](https://mongoose.ws/articles/stm32-websocket-benchmark/)
|
||||||
|
- [How to connect STM32 to AWS IoT Core](https://mongoose.ws/articles/stm32-aws-iot/)
|
||||||
|
- [How to connect STM32 to Azure IoT Hub](https://mongoose.ws/articles/stm32-azure-iot-hub/)
|
||||||
|
- [How to connect STM32 to EMQX](https://mongoose.ws/articles/stm32-emqx/)
|
||||||
|
- [STM32 Device Management deep dive: Secure OTA, Remote Control, health monitor](https://mongoose.ws/articles/stm32-mdash-deep-dive/)
|
||||||
|
- [STM32 TCP/IP footprint: how much flash and RAM does Mongoose take?](https://mongoose.ws/articles/tcp-ip-footprint/)
|
||||||
|
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
Mongoose is a full-stack embedded networking library by Cesanta Software Ltd,
|
||||||
|
<https://mongoose.ws>. This policy covers how to report vulnerabilities
|
||||||
|
and how Cesanta handles them.
|
||||||
|
|
||||||
|
## Supported versions
|
||||||
|
|
||||||
|
Security fixes go to the `master` branch and the latest release.
|
||||||
|
|
||||||
|
## Reporting a vulnerability
|
||||||
|
|
||||||
|
Do not report security issues in public GitHub issues. Report them
|
||||||
|
privately using GitHub Security Advisories:
|
||||||
|
<https://github.com/cesanta/mongoose/security/advisories/new>
|
||||||
|
|
||||||
|
Include the Mongoose version or commit, steps or a proof of concept to reproduce, and
|
||||||
|
the impact. Cesanta usually responds the same business day.
|
||||||
|
|
||||||
|
## Disclosure process
|
||||||
|
|
||||||
|
1. Cesanta reviews the report and coordinates a fix with the reporter.
|
||||||
|
2. Eligible commercial customers receive a notification and a private fix
|
||||||
|
one month before the CVE becomes public.
|
||||||
|
3. The fix and the CVE are then published. The reporter is credited in the
|
||||||
|
GitHub Security Advisory.
|
||||||
|
|
||||||
|
## Security testing
|
||||||
|
|
||||||
|
- CI on every push, unit tests built with ASAN and UBSan
|
||||||
|
- CodeQL static analysis on every commit
|
||||||
|
- Continuous fuzzing on OSS-Fuzz, plus nightly libFuzzer runs
|
||||||
|
- AI-assisted security review on every pull request
|
||||||
|
- Regular review by independent security research groups worldwide
|
||||||
|
|
||||||
|
## EU Cyber Resilience Act (CRA)
|
||||||
|
|
||||||
|
Mongoose supports CRA compliance for connected products: a documented
|
||||||
|
vulnerability handling process (this file), TLS 1.3, signed OTA firmware
|
||||||
|
updates, and an SBOM (`sbom.cdx.json`). Details:
|
||||||
|
<https://mongoose.ws/security/cra-compliance/>
|
||||||
+8410
-5564
File diff suppressed because it is too large.
Load diff
+566
-1090
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"componentid": "::Mongoose:Networking Core",
|
||||||
|
"type": "object",
|
||||||
|
"grammarVersion": "3.8.0",
|
||||||
|
"license": "Copyright (c) Cesanta Software Limited",
|
||||||
|
"properties": {
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"info": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "General information",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"title": "Middleware description",
|
||||||
|
"type": "string",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "disable",
|
||||||
|
"condition": "true"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": "Mongoose networking library: TCP/IP, TLS, HTTP, WebSocket, MQTT, OTA. Edit mongoose_config.h to configure it."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<package schemaVersion="1.7.40" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="PACK.xsd">
|
<package schemaVersion="1.7.56" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:stm32="https://developer.st.com/schemas/stm32cube/1.0.0" xsi:noNamespaceSchemaLocation="PACK.xsd">
|
||||||
<vendor>Cesanta</vendor>
|
<vendor>Cesanta</vendor>
|
||||||
<name>I-CUBE-Mongoose</name>
|
<name>I-CUBE-Mongoose</name>
|
||||||
<description>Mongoose - a lightweight networking library providing TCP/IP stack, TLS, HTTP (Embedded Web Server), WebSocket, MQTT, and Firmware OTA updates. Build production-ready web device dashboards and MQTT cloud connectivity without stitching multiple components together.
|
<description>Mongoose - a full-stack networking library for microcontrollers: Ethernet and Wi-Fi drivers, TCP/IP stack, TLS 1.3, HTTP, WebSocket, MQTT, Modbus, OTA updates and device management, in a two-file C/C++ library, on any RTOS or bare metal.</description>
|
||||||
</description>
|
|
||||||
<url>https://mongoose.ws/downloads/stm32pack/</url>
|
<url>https://mongoose.ws/downloads/stm32pack/</url>
|
||||||
<license>License.txt</license>
|
<license>License.txt</license>
|
||||||
<supportContact>https://mongoose.ws/contact/</supportContact>
|
<supportContact>https://mongoose.ws/contact/</supportContact>
|
||||||
|
<environments>
|
||||||
|
<environment name="STM32Cube">
|
||||||
|
<stm32:pack type="MW" friendlyName="Mongoose" shortDesc="Mongoose full-stack networking library">
|
||||||
|
<relocation-info parent="middleware"/>
|
||||||
|
<no-compatibility-constraint/>
|
||||||
|
</stm32:pack>
|
||||||
|
</environment>
|
||||||
|
</environments>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release date="2026-10-21" version="7.23.3">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
|
||||||
|
<release date="2026-10-19" version="7.23.2">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
|
||||||
|
<release date="2026-09-28" version="7.23.1">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
|
||||||
<release date="2026-05-11" version="7.21.7">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
|
<release date="2026-05-11" version="7.21.7">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
|
||||||
<release date="2026-05-05" version="7.21.5">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
|
<release date="2026-05-05" version="7.21.5">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
|
||||||
<release date="2026-04-24" version="7.21.4">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
|
<release date="2026-04-24" version="7.21.4">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
|
||||||
@@ -19,7 +29,7 @@
|
|||||||
<keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword>
|
<keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword>
|
||||||
</keywords>
|
</keywords>
|
||||||
<components>
|
<components>
|
||||||
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.21.7" maxInstances="1">
|
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.3" maxInstances="1">
|
||||||
<description>Networking Core</description>
|
<description>Networking Core</description>
|
||||||
<files>
|
<files>
|
||||||
<file category="doc" name="README.md"/>
|
<file category="doc" name="README.md"/>
|
||||||
@@ -27,7 +37,15 @@
|
|||||||
<file category="sourceC" name="mongoose.c" />
|
<file category="sourceC" name="mongoose.c" />
|
||||||
<file category="header" name="mongoose_config.h"/>
|
<file category="header" name="mongoose_config.h"/>
|
||||||
<file category="utility" name="README.md" public="false"/>
|
<file category="utility" name="README.md" public="false"/>
|
||||||
|
<file category="genAsset" name=".config/mongoose_parameters.json"/>
|
||||||
</files>
|
</files>
|
||||||
|
<environments>
|
||||||
|
<environment name="STM32Cube">
|
||||||
|
<stm32:component>
|
||||||
|
<middleware-entry-point id="Mongoose" name="Mongoose" category="TCP/IP"/>
|
||||||
|
</stm32:component>
|
||||||
|
</environment>
|
||||||
|
</environments>
|
||||||
</component>
|
</component>
|
||||||
</components>
|
</components>
|
||||||
</package>
|
</package>
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
VERSION = 7.21.7
|
VERSION = 7.23.3
|
||||||
REPO_ROOT = ../..
|
REPO_ROOT = ../..
|
||||||
MX ?= /Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX
|
MX ?= /Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX
|
||||||
FILES = Cesanta.I-CUBE-Mongoose.pdsc Release_Notes.html mongoose_config.h README.md
|
FILES = Cesanta.I-CUBE-Mongoose.pdsc Release_Notes.html mongoose_config.h README.md .config/mongoose_parameters.json
|
||||||
GENERATED = License.txt mongoose.h mongoose.c
|
GENERATED = License.txt mongoose.h mongoose.c
|
||||||
|
MX2 ?= $(HOME)/STMicroelectronics/STM32CubeMX2_1.1.2/.bin/cube
|
||||||
|
PACKSDIR ?= $(HOME)/Library/Application Support/stm32cube/packs
|
||||||
|
MX2CPN ?= STM32C5A3ZGT6
|
||||||
|
MX2PROJ = $(CURDIR)/mx2test/test.ioc2
|
||||||
|
|
||||||
|
NAME = Cesanta.I-CUBE-Mongoose.$(VERSION)
|
||||||
|
PACK = $(NAME).pack
|
||||||
|
|
||||||
PACK = Cesanta.I-CUBE-Mongoose.$(VERSION).pack
|
|
||||||
all: $(PACK)
|
all: $(PACK)
|
||||||
|
|
||||||
Cesanta.I-CUBE-Mongoose.$(VERSION).pack: $(FILES) $(GENERATED)
|
Cesanta.I-CUBE-Mongoose.$(VERSION).pack: $(FILES) $(GENERATED)
|
||||||
@@ -44,23 +50,19 @@ mongoose.h: Mongoose
|
|||||||
mongoose.c: Mongoose
|
mongoose.c: Mongoose
|
||||||
cp $(REPO_ROOT)/mongoose.c $@
|
cp $(REPO_ROOT)/mongoose.c $@
|
||||||
|
|
||||||
pk: $(PACK)
|
test_mx2: $(PACK)
|
||||||
rm -f gen.mx
|
-$(MX2) pack remove --cmsis-pack-root "$(PACKSDIR)" $(NAME)
|
||||||
echo swmgr remove Cesanta.I-CUBE-Mongoose.$(VERSION) >> gen.mx
|
rm -f "$(PACKSDIR)/.Download/$(PACK)"
|
||||||
echo swmgr install `pwd`/Cesanta.I-CUBE-Mongoose.$(VERSION).pack ask >> gen.mx
|
$(MX2) pack install --cmsis-pack-root "$(PACKSDIR)" "$(CURDIR)/$(PACK)"
|
||||||
echo exit >> gen.mx
|
export PATH="$(dir $(MX2)):$$PATH"; \
|
||||||
$(MX) -q `pwd`/gen.mx
|
PORT=$$(cube lifecycle-mx start-only | sed -n 's/^BACKEND_PORT *//p'); \
|
||||||
|
cube mx project close --port $$PORT -p "$(MX2PROJ)" --discard > /dev/null 2>&1; \
|
||||||
|
rm -rf mx2test && mkdir mx2test && \
|
||||||
mx: $(PACK)
|
cube mx project create-from-mcu --port $$PORT --cpn $(MX2CPN) -l "$(CURDIR)/mx2test" -n test --empty-project && \
|
||||||
mkdir -p $@
|
cube mx middleware enable-middleware --port $$PORT -p "$(MX2PROJ)" -n Mongoose | tee mx2test/enable.json && \
|
||||||
cp ../downloads/nucleo_h563zi.ioc $@
|
grep -q '"status": "success"' mx2test/enable.json && \
|
||||||
V=`grep 'Mcu.ThirdParty0' $@/nucleo_h563zi.ioc | sed 's,^.*se.,,'`; sed -i '' "s,$$V,$(VERSION),g" $@/nucleo_h563zi.ioc
|
cube mx project save --port $$PORT -p "$(MX2PROJ)" && \
|
||||||
rm -f gen.mx
|
cube mx project close --port $$PORT -p "$(MX2PROJ)" --discard
|
||||||
echo config load `pwd`/$@/nucleo_h563zi.ioc >> gen.mx
|
|
||||||
echo project generate >> gen.mx
|
|
||||||
echo exit >> gen.mx
|
|
||||||
$(MX) -q `pwd`/gen.mx
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.pack STM32CubeMX/*.$(VERSION)_*.xml $(GENERATED) gen.mx mx
|
rm -rf *.pack STM32CubeMX/*.$(VERSION)_*.xml $(GENERATED) mx2test
|
||||||
@@ -13,9 +13,10 @@ Primary files:
|
|||||||
- `src/tls_dummy.c`
|
- `src/tls_dummy.c`
|
||||||
- `src/tls_mbed.c`
|
- `src/tls_mbed.c`
|
||||||
- `src/tls_openssl.c`
|
- `src/tls_openssl.c`
|
||||||
|
- `src/tls_p256_cortex.c`
|
||||||
|
- `src/tls_p256.c`
|
||||||
|
- `src/tls_p384.c`
|
||||||
- `src/tls_rsa.c`
|
- `src/tls_rsa.c`
|
||||||
- `src/tls_uecc.c`
|
|
||||||
- `src/tls_x25519.c`
|
|
||||||
|
|
||||||
Perform a focused security review centered on these files and this security surface.
|
Perform a focused security review centered on these files and this security surface.
|
||||||
|
|
||||||
@@ -38,6 +39,6 @@ Trace attacker-controlled TLS peer input through normal client and server execut
|
|||||||
- Verify that wildcard matching is applied only to certificate presented identifiers, not to arbitrary reference identifiers or general DNS glob matching.
|
- Verify that wildcard matching is applied only to certificate presented identifiers, not to arbitrary reference identifiers or general DNS glob matching.
|
||||||
- Review `mg_match` or other glob/matching helpers if used for certificate identity checks, authorization checks, filesystem paths, routing, or topic matching. Flag cases where glob semantics are broader than the security policy requires.
|
- Review `mg_match` or other glob/matching helpers if used for certificate identity checks, authorization checks, filesystem paths, routing, or topic matching. Flag cases where glob semantics are broader than the security policy requires.
|
||||||
- Check certificate parsing for DER/PEM boundary errors, length overflows, malformed ASN.1, extension parsing mistakes, name constraints, SAN vs CN precedence, embedded NUL, and invalid string type handling.
|
- Check certificate parsing for DER/PEM boundary errors, length overflows, malformed ASN.1, extension parsing mistakes, name constraints, SAN vs CN precedence, embedded NUL, and invalid string type handling.
|
||||||
- Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC/X25519 operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures.
|
- Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC (P-256, P-384) operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures.
|
||||||
- Flag any behavior that silently falls back from verified TLS to unverified TLS or plaintext.
|
- Flag any behavior that silently falls back from verified TLS to unverified TLS or plaintext.
|
||||||
- Check that TLS receive/send error paths cannot corrupt connection state, reuse freed buffers, or continue processing unauthenticated plaintext as encrypted data.
|
- Check that TLS receive/send error paths cannot corrupt connection state, reuse freed buffers, or continue processing unauthenticated plaintext as encrypted data.
|
||||||
+26
-20
@@ -3,9 +3,9 @@
|
|||||||
"bomFormat": "CycloneDX",
|
"bomFormat": "CycloneDX",
|
||||||
"specVersion": "1.6",
|
"specVersion": "1.6",
|
||||||
"serialNumber": "urn:uuid:d3f8b1e2-4a7c-4d9e-bf2a-1c3e5f7a9b0d",
|
"serialNumber": "urn:uuid:d3f8b1e2-4a7c-4d9e-bf2a-1c3e5f7a9b0d",
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"timestamp": "2026-08-12T00:00:00Z",
|
"timestamp": "2026-10-13T00:00:00Z",
|
||||||
"component": {
|
"component": {
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"bom-ref": "mongoose",
|
"bom-ref": "mongoose",
|
||||||
@@ -34,14 +34,31 @@
|
|||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"bom-ref": "micro-ecc",
|
"bom-ref": "bearssl-ec",
|
||||||
"author": "Kenneth MacKay",
|
"author": "Thomas Pornin",
|
||||||
"name": "micro-ecc",
|
"name": "BearSSL EC (P-256, P-384, ECDSA)",
|
||||||
"description": "ECC point operations on GF(p) curves (secp256r1). Used by Mongoose built-in TLS 1.3 for ECDH key exchange and ECDSA signatures. Source: src/tls_uecc.c, src/tls_uecc.h.",
|
"description": "P-256 (ec_p256_m31) and P-384 (ec_prime_i31) elliptic curve arithmetic, ECDSA sign and verify, and the i31 bignum code they use, derived from BearSSL. Used by Mongoose built-in TLS 1.3 for ECDHE key exchange and ECDSA signatures, by JWT ES256 and by OTA firmware signature verification. Source: src/tls_p256.c, src/tls_p256.h, src/tls_p384.c, src/tls_p384.h.",
|
||||||
"licenses": [ { "expression": "BSD-2-Clause" } ],
|
"licenses": [ { "expression": "MIT" } ],
|
||||||
"purl": "pkg:github/kmackay/micro-ecc",
|
|
||||||
"externalReferences": [
|
"externalReferences": [
|
||||||
{ "type": "vcs", "url": "https://github.com/kmackay/micro-ecc" }
|
{ "type": "website", "url": "https://bearssl.org" }
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{ "name": "upstream-commit", "value": "7bea48e5e850ab4cafbe68d3765cdaba13a86d6f" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "p256-cortex-m4",
|
||||||
|
"author": "Emil Lenngren, Shortcut Labs AB",
|
||||||
|
"name": "P256-Cortex-M4",
|
||||||
|
"description": "P-256 ECDH and ECDSA in Cortex-M assembly. Used by Mongoose built-in TLS 1.3 instead of the BearSSL P-256 code on Cortex-M cores with the DSP extension (M4, M7, M33, M55, M85). Source: src/tls_p256_cortex.c.",
|
||||||
|
"licenses": [ { "expression": "MIT" } ],
|
||||||
|
"purl": "pkg:github/Emill/P256-Cortex-M4@ecd3ec2222fc9e18b6b44c86bb7183971a2041fc",
|
||||||
|
"externalReferences": [
|
||||||
|
{ "type": "vcs", "url": "https://github.com/Emill/P256-Cortex-M4" }
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{ "name": "upstream-commit", "value": "ecd3ec2222fc9e18b6b44c86bb7183971a2041fc" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -55,17 +72,6 @@
|
|||||||
{ "type": "website", "url": "https://bearssl.org" }
|
{ "type": "website", "url": "https://bearssl.org" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "library",
|
|
||||||
"bom-ref": "strobe-x25519",
|
|
||||||
"author": "Cryptography Research, Inc. / Mike Hamburg",
|
|
||||||
"name": "STROBE x25519 (adapted)",
|
|
||||||
"description": "X25519 Diffie-Hellman function adapted from the STROBE project. Used by Mongoose built-in TLS 1.3 for ECDH key exchange. Source: src/tls_x25519.c.",
|
|
||||||
"licenses": [ { "expression": "MIT" } ],
|
|
||||||
"externalReferences": [
|
|
||||||
{ "type": "website", "url": "https://strobe.sourceforge.io/" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"bom-ref": "sha1-reid",
|
"bom-ref": "sha1-reid",
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
|
#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
|
||||||
#define MG_ARCH_WIN32 2 // Windows
|
#define MG_ARCH_WIN32 2 // Windows
|
||||||
#define MG_ARCH_ESP32 3 // ESP32
|
#define MG_ARCH_ESP32 3 // ESP32
|
||||||
#define MG_ARCH_ESP8266 4 // ESP8266
|
|
||||||
|
|
||||||
#define MG_ARCH_FREERTOS 5 // FreeRTOS <-- DEPRECATED !!!
|
#define MG_ARCH_FREERTOS 5 // FreeRTOS <-- DEPRECATED !!!
|
||||||
|
|
||||||
@@ -50,7 +49,6 @@
|
|||||||
#include "arch_armgcc.h"
|
#include "arch_armgcc.h"
|
||||||
#include "arch_cube.h"
|
#include "arch_cube.h"
|
||||||
#include "arch_esp32.h"
|
#include "arch_esp32.h"
|
||||||
#include "arch_esp8266.h"
|
|
||||||
#include "arch_rtx.h"
|
#include "arch_rtx.h"
|
||||||
#include "arch_threadx.h"
|
#include "arch_threadx.h"
|
||||||
#include "arch_unix.h"
|
#include "arch_unix.h"
|
||||||
|
|||||||
+2
-1
@@ -12,7 +12,8 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
#define MG_PATH_MAX 100
|
#define MG_PATH_MAX 100
|
||||||
#define MG_ENABLE_DIRLIST 0
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+2
-1
@@ -18,7 +18,8 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
#define MG_PATH_MAX 100
|
#define MG_PATH_MAX 100
|
||||||
#define MG_ENABLE_DIRLIST 0
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+5
-68
@@ -25,10 +25,6 @@
|
|||||||
#define MG_PATH_MAX 100
|
#define MG_PATH_MAX 100
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_DIRLIST
|
|
||||||
#define MG_ENABLE_DIRLIST 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef MG_ENABLE_TCPIP
|
#ifndef MG_ENABLE_TCPIP
|
||||||
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
|
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
|
||||||
@@ -36,6 +32,7 @@
|
|||||||
|
|
||||||
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
|
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
|
||||||
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
|
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
|
||||||
|
!defined(MG_ENABLE_DRIVER_STM32C) && \
|
||||||
!defined(MG_ENABLE_DRIVER_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
|
!defined(MG_ENABLE_DRIVER_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
|
||||||
!defined(MG_ENABLE_DRIVER_ST67W6)
|
!defined(MG_ENABLE_DRIVER_ST67W6)
|
||||||
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
||||||
@@ -44,6 +41,8 @@
|
|||||||
#define MG_ENABLE_DRIVER_STM32H 1
|
#define MG_ENABLE_DRIVER_STM32H 1
|
||||||
#elif defined(STM32N6)
|
#elif defined(STM32N6)
|
||||||
#define MG_ENABLE_DRIVER_STM32N 1
|
#define MG_ENABLE_DRIVER_STM32N 1
|
||||||
|
#elif defined(STM32C5)
|
||||||
|
#define MG_ENABLE_DRIVER_STM32C 1
|
||||||
#else
|
#else
|
||||||
#error Select a driver in mongoose_config.h
|
#error Select a driver in mongoose_config.h
|
||||||
#endif
|
#endif
|
||||||
@@ -60,70 +59,8 @@
|
|||||||
#define MG_OTA MG_OTA_STM32H5
|
#define MG_OTA MG_OTA_STM32H5
|
||||||
#elif !defined(MG_OTA) && defined(STM32H7)
|
#elif !defined(MG_OTA) && defined(STM32H7)
|
||||||
#define MG_OTA MG_OTA_STM32H7
|
#define MG_OTA MG_OTA_STM32H7
|
||||||
#endif
|
#elif !defined(MG_OTA) && defined(STM32C5)
|
||||||
|
#define MG_OTA MG_OTA_STM32C5
|
||||||
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
|
||||||
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
|
||||||
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
|
||||||
// hangs for 10 s, the IWDG fires and the device rolls back.
|
|
||||||
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
|
||||||
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
|
||||||
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
|
||||||
#if defined(IWDG1) && !defined(IWDG)
|
|
||||||
#define MG_IWDG IWDG1
|
|
||||||
#else
|
|
||||||
#define MG_IWDG IWDG
|
|
||||||
#endif
|
|
||||||
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
|
||||||
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_START() \
|
|
||||||
do { \
|
|
||||||
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
|
||||||
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
|
||||||
MG_IWDG->PR = 6U; /* /256 */ \
|
|
||||||
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
|
||||||
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
|
||||||
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// OTA state in a backup register that survives warm resets but clears on POR.
|
|
||||||
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
|
||||||
// and unlocks backup domain write access via direct register writes, so it is
|
|
||||||
// safe to call from any context (before or after HAL_Init).
|
|
||||||
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
|
||||||
#ifndef MG_OTA_STATE_GET
|
|
||||||
#if defined(PWR_DBPCR_DBP)
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
|
||||||
#elif defined(PWR_CR1_DBP)
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
|
||||||
#else
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TAMP)
|
|
||||||
#define MG_OTA_STATE_GET() \
|
|
||||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
|
||||||
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
|
||||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(RTC->BKP0R = (uint32_t) (v)))
|
|
||||||
#else
|
|
||||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(RTC->BKP0R = (uint32_t) (v)))
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// use HAL-defined execute-in-ram section
|
// use HAL-defined execute-in-ram section
|
||||||
|
|||||||
+4
-57
@@ -22,10 +22,8 @@
|
|||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
#include <esp_mac.h>
|
#include <esp_mac.h>
|
||||||
#include <esp_idf_version.h>
|
#include <esp_idf_version.h>
|
||||||
#include <esp_ota_ops.h>
|
// #include <esp_memory_utils.h> // For health record coredumps
|
||||||
#include <esp_task_wdt.h>
|
// #include <esp_core_dump.h> // For health record coredumps
|
||||||
#include <soc/soc.h>
|
|
||||||
#include <soc/rtc_cntl_reg.h>
|
|
||||||
|
|
||||||
#define MG_PATH_MAX 128
|
#define MG_PATH_MAX 128
|
||||||
|
|
||||||
@@ -33,59 +31,8 @@
|
|||||||
#define MG_ENABLE_POSIX_FS 1
|
#define MG_ENABLE_POSIX_FS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_DIRLIST
|
#ifndef MG_HEALTH_DATA_SIZE
|
||||||
#define MG_ENABLE_DIRLIST 1
|
#define MG_HEALTH_DATA_SIZE 20
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_OTA_ESP32_WDT_TIMEOUT_MS
|
|
||||||
#define MG_OTA_ESP32_WDT_TIMEOUT_MS 10000U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline void mg_ota_esp32_timer_start(void) {
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
esp_task_wdt_config_t cfg = {0};
|
|
||||||
esp_err_t err;
|
|
||||||
|
|
||||||
cfg.timeout_ms = MG_OTA_ESP32_WDT_TIMEOUT_MS;
|
|
||||||
cfg.idle_core_mask = 0;
|
|
||||||
cfg.trigger_panic = true;
|
|
||||||
|
|
||||||
err = esp_task_wdt_init(&cfg);
|
|
||||||
if (err == ESP_ERR_INVALID_STATE) esp_task_wdt_reconfigure(&cfg);
|
|
||||||
#else
|
|
||||||
esp_task_wdt_init(MG_OTA_ESP32_WDT_TIMEOUT_MS / 1000U, true);
|
|
||||||
#endif
|
|
||||||
esp_task_wdt_add(NULL);
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t mg_ota_esp32_state_set(uint32_t state) {
|
|
||||||
if (state == 0) esp_ota_mark_app_valid_cancel_rollback();
|
|
||||||
REG_WRITE(RTC_CNTL_STORE0_REG, state);
|
|
||||||
return REG_READ(RTC_CNTL_STORE0_REG);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MG_OTA_STATE_GET() \
|
|
||||||
REG_READ(RTC_CNTL_STORE0_REG)
|
|
||||||
|
|
||||||
#define MG_OTA_STATE_SET(v) mg_ota_esp32_state_set((uint32_t) (v))
|
|
||||||
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_START() mg_ota_esp32_timer_start()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_FEED() \
|
|
||||||
do { \
|
|
||||||
(void) esp_task_wdt_reset(); \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_OTA_ROLLBACK
|
|
||||||
#define MG_OTA_ROLLBACK() \
|
|
||||||
do { \
|
|
||||||
(void) esp_ota_mark_app_invalid_rollback_and_reboot(); \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#if MG_ARCH == MG_ARCH_ESP8266
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <esp_system.h>
|
|
||||||
|
|
||||||
#define MG_PATH_MAX 128
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -27,10 +27,6 @@
|
|||||||
#define MG_PATH_MAX 100
|
#define MG_PATH_MAX 100
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_DIRLIST
|
|
||||||
#define MG_ENABLE_DIRLIST 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_ENABLE_TCPIP
|
#ifndef MG_ENABLE_TCPIP
|
||||||
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
|
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -50,10 +50,6 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#ifndef MG_ENABLE_DIRLIST
|
|
||||||
#define MG_ENABLE_DIRLIST 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_PATH_MAX
|
#ifndef MG_PATH_MAX
|
||||||
#define MG_PATH_MAX FILENAME_MAX
|
#define MG_PATH_MAX FILENAME_MAX
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -166,10 +166,6 @@ typedef int socklen_t;
|
|||||||
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
|
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_DIRLIST
|
|
||||||
#define MG_ENABLE_DIRLIST 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SIGPIPE
|
#ifndef SIGPIPE
|
||||||
#define SIGPIPE 0
|
#define SIGPIPE 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,22 +4,64 @@
|
|||||||
#include "net_builtin.h"
|
#include "net_builtin.h"
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
void mg_bsd_init(struct mg_mgr *);
|
||||||
|
void mg_bsd_poll(struct mg_mgr *);
|
||||||
|
|
||||||
#ifndef MG_ENABLE_BSD_PROTOTYPES
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
#ifndef EAGAIN
|
||||||
#include <netinet/tcp.h>
|
#define EAGAIN 11
|
||||||
#include <poll.h>
|
#endif
|
||||||
#include <sys/select.h>
|
#ifndef EWOULDBLOCK
|
||||||
#include <sys/socket.h>
|
#define EWOULDBLOCK EAGAIN
|
||||||
#include <sys/time.h>
|
#endif
|
||||||
#include <sys/un.h>
|
#ifndef EINPROGRESS
|
||||||
#else
|
#define EINPROGRESS 36
|
||||||
#include <errno.h>
|
#endif
|
||||||
|
#ifndef ENOTCONN
|
||||||
|
#define ENOTCONN 107
|
||||||
|
#endif
|
||||||
|
#ifndef ECONNREFUSED
|
||||||
|
#define ECONNREFUSED 111
|
||||||
|
#endif
|
||||||
|
#ifndef ECONNRESET
|
||||||
|
#define ECONNRESET 104
|
||||||
|
#endif
|
||||||
|
#ifndef EADDRINUSE
|
||||||
|
#define EADDRINUSE 98
|
||||||
|
#endif
|
||||||
|
#ifndef ETIMEDOUT
|
||||||
|
#define ETIMEDOUT 110
|
||||||
|
#endif
|
||||||
|
#ifndef EBADF
|
||||||
|
#define EBADF 9
|
||||||
|
#endif
|
||||||
|
#ifndef EMFILE
|
||||||
|
#define EMFILE 24
|
||||||
|
#endif
|
||||||
|
#ifndef ENOMEM
|
||||||
|
#define ENOMEM 12
|
||||||
|
#endif
|
||||||
|
#ifndef EIO
|
||||||
|
#define EIO 5
|
||||||
|
#endif
|
||||||
|
#ifndef EINVAL
|
||||||
|
#define EINVAL 22
|
||||||
|
#endif
|
||||||
|
#ifndef EPERM
|
||||||
|
#define EPERM 1
|
||||||
|
#endif
|
||||||
|
#ifndef EPIPE
|
||||||
|
#define EPIPE 32
|
||||||
|
#endif
|
||||||
|
#ifndef EPROTONOSUPPORT
|
||||||
|
#define EPROTONOSUPPORT 93
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MG_ENABLE_BSD_PROTOTYPES
|
||||||
typedef unsigned int socklen_t;
|
typedef unsigned int socklen_t;
|
||||||
|
typedef unsigned long nfds_t;
|
||||||
typedef uint32_t in_addr_t;
|
typedef uint32_t in_addr_t;
|
||||||
struct in_addr { in_addr_t s_addr; };
|
struct in_addr { in_addr_t s_addr; };
|
||||||
struct in6_addr { uint8_t s6_addr[16]; };
|
struct in6_addr { uint8_t s6_addr[16]; };
|
||||||
@@ -45,7 +87,6 @@ struct hostent {
|
|||||||
int h_length;
|
int h_length;
|
||||||
char **h_addr_list;
|
char **h_addr_list;
|
||||||
};
|
};
|
||||||
#define h_addr h_addr_list[0]
|
|
||||||
struct addrinfo {
|
struct addrinfo {
|
||||||
int ai_flags;
|
int ai_flags;
|
||||||
int ai_family;
|
int ai_family;
|
||||||
@@ -57,6 +98,32 @@ struct addrinfo {
|
|||||||
struct addrinfo *ai_next;
|
struct addrinfo *ai_next;
|
||||||
};
|
};
|
||||||
struct pollfd { int fd; short events; short revents; };
|
struct pollfd { int fd; short events; short revents; };
|
||||||
|
|
||||||
|
#if !defined(FD_SET) && !defined(FD_ZERO) && !defined(FD_ISSET)
|
||||||
|
#define MG_BSD_SELECT_H
|
||||||
|
typedef unsigned long __fd_mask;
|
||||||
|
#ifndef FD_SETSIZE
|
||||||
|
#define FD_SETSIZE 64
|
||||||
|
#endif
|
||||||
|
#define _NFDBITS ((int) sizeof(__fd_mask) * 8)
|
||||||
|
#define _howmany(x, y) (((x) + ((y) - 1)) / (y))
|
||||||
|
typedef struct fd_set {
|
||||||
|
__fd_mask __fds_bits[_howmany(FD_SETSIZE, _NFDBITS)];
|
||||||
|
} fd_set;
|
||||||
|
#define __fdset_mask(n) ((__fd_mask) 1 << ((n) % _NFDBITS))
|
||||||
|
#define FD_CLR(n, p) ((p)->__fds_bits[(n) / _NFDBITS] &= ~__fdset_mask(n))
|
||||||
|
#define FD_ISSET(n, p) \
|
||||||
|
(((p)->__fds_bits[(n) / _NFDBITS] & __fdset_mask(n)) != 0)
|
||||||
|
#define FD_SET(n, p) ((p)->__fds_bits[(n) / _NFDBITS] |= __fdset_mask(n))
|
||||||
|
#define FD_ZERO(p) \
|
||||||
|
do { \
|
||||||
|
fd_set *_p = (p); \
|
||||||
|
size_t _n = _howmany(FD_SETSIZE, _NFDBITS); \
|
||||||
|
while (_n > 0) _p->__fds_bits[--_n] = 0; \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define h_addr h_addr_list[0]
|
||||||
#define AF_INET 2
|
#define AF_INET 2
|
||||||
#define AF_INET6 10
|
#define AF_INET6 10
|
||||||
#define AF_UNSPEC 0
|
#define AF_UNSPEC 0
|
||||||
@@ -69,6 +136,7 @@ struct pollfd { int fd; short events; short revents; };
|
|||||||
#define IPPROTO_UDP 17
|
#define IPPROTO_UDP 17
|
||||||
#define INADDR_ANY 0
|
#define INADDR_ANY 0
|
||||||
#define INADDR_LOOPBACK 0x7f000001
|
#define INADDR_LOOPBACK 0x7f000001
|
||||||
|
#define INET_ADDRSTRLEN 16
|
||||||
#define SOL_SOCKET 0xffff
|
#define SOL_SOCKET 0xffff
|
||||||
#define SO_REUSEADDR 2
|
#define SO_REUSEADDR 2
|
||||||
#define SO_REUSEPORT 15
|
#define SO_REUSEPORT 15
|
||||||
@@ -77,79 +145,37 @@ struct pollfd { int fd; short events; short revents; };
|
|||||||
#define SO_BROADCAST 6
|
#define SO_BROADCAST 6
|
||||||
#define SO_RCVBUF 8
|
#define SO_RCVBUF 8
|
||||||
#define SO_SNDBUF 7
|
#define SO_SNDBUF 7
|
||||||
|
#define SO_NOSIGPIPE 0x1022
|
||||||
#define TCP_NODELAY 1
|
#define TCP_NODELAY 1
|
||||||
#define MSG_DONTWAIT 0x40
|
#define MSG_DONTWAIT 0x40
|
||||||
#define MSG_NOSIGNAL 0x4000
|
#define MSG_NOSIGNAL 0x4000
|
||||||
#define MSG_PEEK 0x02
|
#define MSG_PEEK 0x02
|
||||||
|
#ifndef F_GETFL
|
||||||
#define F_GETFL 3
|
#define F_GETFL 3
|
||||||
|
#endif
|
||||||
|
#ifndef F_SETFL
|
||||||
#define F_SETFL 4
|
#define F_SETFL 4
|
||||||
#define O_NONBLOCK 0x0004
|
#endif
|
||||||
|
#ifndef O_NONBLOCK
|
||||||
|
#define O_NONBLOCK 0x4000
|
||||||
|
#endif
|
||||||
#define SHUT_RD 0
|
#define SHUT_RD 0
|
||||||
#define SHUT_WR 1
|
#define SHUT_WR 1
|
||||||
#define SHUT_RDWR 2
|
#define SHUT_RDWR 2
|
||||||
#define AI_PASSIVE 0x0001
|
#define AI_PASSIVE 0x0001
|
||||||
#define NI_MAXHOST 1025
|
#define NI_MAXHOST 1025
|
||||||
#define NI_MAXSERV 32
|
#define NI_MAXSERV 32
|
||||||
#ifndef EAGAIN
|
|
||||||
#define EAGAIN 11
|
|
||||||
#endif
|
|
||||||
#ifndef EWOULDBLOCK
|
|
||||||
#define EWOULDBLOCK EAGAIN
|
|
||||||
#endif
|
|
||||||
#ifndef EINPROGRESS
|
|
||||||
#define EINPROGRESS 36
|
|
||||||
#endif
|
|
||||||
#ifndef ENOTCONN
|
|
||||||
#define ENOTCONN 107
|
|
||||||
#endif
|
|
||||||
#ifndef ECONNREFUSED
|
|
||||||
#define ECONNREFUSED 111
|
|
||||||
#endif
|
|
||||||
#ifndef ECONNRESET
|
|
||||||
#define ECONNRESET 104
|
|
||||||
#endif
|
|
||||||
#ifndef EADDRINUSE
|
|
||||||
#define EADDRINUSE 98
|
|
||||||
#endif
|
|
||||||
#ifndef ETIMEDOUT
|
|
||||||
#define ETIMEDOUT 110
|
|
||||||
#endif
|
|
||||||
#define POLLIN 0x001
|
#define POLLIN 0x001
|
||||||
#define POLLOUT 0x004
|
#define POLLOUT 0x004
|
||||||
#define POLLERR 0x008
|
#define POLLERR 0x008
|
||||||
#define POLLHUP 0x010
|
#define POLLHUP 0x010
|
||||||
#define POLLNVAL 0x020
|
#define POLLNVAL 0x020
|
||||||
uint16_t htons(uint16_t);
|
|
||||||
uint16_t ntohs(uint16_t);
|
|
||||||
uint32_t htonl(uint32_t);
|
|
||||||
uint32_t ntohl(uint32_t);
|
|
||||||
#endif // MG_ENABLE_BSD_PROTOTYPES
|
|
||||||
|
|
||||||
#define closesocket(a) close(a)
|
#define htons(x) mg_ntohs(x)
|
||||||
|
#define ntohs(x) mg_ntohs(x)
|
||||||
|
#define htonl(x) mg_ntohl(x)
|
||||||
|
#define ntohl(x) mg_ntohl(x)
|
||||||
|
|
||||||
#ifndef MG_BSD_BACKLOG
|
|
||||||
#define MG_BSD_BACKLOG 5
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_BSD_CONNECT_TIMEOUT_MS
|
|
||||||
#define MG_BSD_CONNECT_TIMEOUT_MS 10000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Mongoose-specific API
|
|
||||||
void mg_bsd_init(void); // must be called before socket()
|
|
||||||
void mg_bsd_poll(struct mg_mgr *); // process pending BSD commands
|
|
||||||
|
|
||||||
// Transport backend: implement these OR define MG_ENABLE_FREERTOS for the built-in backend
|
|
||||||
void *mg_bsd_transport_new(int domain, int type, int proto);
|
|
||||||
void mg_bsd_transport_free(void *t);
|
|
||||||
int mg_bsd_transport_listen(void *t, const struct sockaddr_in *addr);
|
|
||||||
void *mg_bsd_transport_accept(void *t, struct sockaddr_in *peer, bool nonblock);
|
|
||||||
ssize_t mg_bsd_transport_recv(void *t, void *buf, size_t len, bool nonblock);
|
|
||||||
ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonblock);
|
|
||||||
int mg_bsd_transport_connect(void *t, const struct sockaddr_in *addr, bool nonblock);
|
|
||||||
void mg_bsd_transport_close(void *t);
|
|
||||||
|
|
||||||
// Standard BSD socket API
|
|
||||||
int socket(int, int, int);
|
int socket(int, int, int);
|
||||||
int bind(int, const struct sockaddr *, socklen_t);
|
int bind(int, const struct sockaddr *, socklen_t);
|
||||||
int listen(int, int);
|
int listen(int, int);
|
||||||
@@ -157,23 +183,40 @@ int accept(int, struct sockaddr *, socklen_t *);
|
|||||||
int connect(int, const struct sockaddr *, socklen_t);
|
int connect(int, const struct sockaddr *, socklen_t);
|
||||||
ssize_t send(int, const void *, size_t, int);
|
ssize_t send(int, const void *, size_t, int);
|
||||||
ssize_t recv(int, void *, size_t, int);
|
ssize_t recv(int, void *, size_t, int);
|
||||||
ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t);
|
ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *,
|
||||||
|
socklen_t);
|
||||||
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *);
|
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *);
|
||||||
int close(int);
|
int close(int);
|
||||||
int shutdown(int, int);
|
int shutdown(int, int);
|
||||||
int fcntl(int, int, int);
|
|
||||||
int setsockopt(int, int, int, const void *, socklen_t);
|
int setsockopt(int, int, int, const void *, socklen_t);
|
||||||
int getsockopt(int, int, int, void *, socklen_t *);
|
int getsockopt(int, int, int, void *, socklen_t *);
|
||||||
int getsockname(int, struct sockaddr *, socklen_t *);
|
int getsockname(int, struct sockaddr *, socklen_t *);
|
||||||
int getpeername(int, struct sockaddr *, socklen_t *);
|
int getpeername(int, struct sockaddr *, socklen_t *);
|
||||||
int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
|
int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
|
||||||
int poll(struct pollfd *, unsigned int, int);
|
int poll(struct pollfd *, nfds_t, int);
|
||||||
struct hostent *gethostbyname(const char *);
|
struct hostent *gethostbyname(const char *);
|
||||||
int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **);
|
int getaddrinfo(const char *, const char *, const struct addrinfo *,
|
||||||
|
struct addrinfo **);
|
||||||
void freeaddrinfo(struct addrinfo *);
|
void freeaddrinfo(struct addrinfo *);
|
||||||
int inet_pton(int, const char *, void *);
|
int inet_pton(int, const char *, void *);
|
||||||
const char *inet_ntop(int, const void *, char *, socklen_t);
|
const char *inet_ntop(int, const void *, char *, socklen_t);
|
||||||
in_addr_t inet_addr(const char *);
|
in_addr_t inet_addr(const char *);
|
||||||
char *inet_ntoa(struct in_addr);
|
char *inet_ntoa(struct in_addr);
|
||||||
|
#else
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif // MG_ENABLE_BSD_PROTOTYPES
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define mg_bsd_init(mgr)
|
||||||
|
#define mg_bsd_poll(mgr)
|
||||||
#endif // MG_ENABLE_BSD_SOCKETS
|
#endif // MG_ENABLE_BSD_SOCKETS
|
||||||
+23
-5
@@ -82,10 +82,6 @@
|
|||||||
#define MG_ENABLE_WINSOCK 1
|
#define MG_ENABLE_WINSOCK 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_DIRLIST
|
|
||||||
#define MG_ENABLE_DIRLIST 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_ENABLE_CUSTOM_RANDOM
|
#ifndef MG_ENABLE_CUSTOM_RANDOM
|
||||||
#define MG_ENABLE_CUSTOM_RANDOM 0
|
#define MG_ENABLE_CUSTOM_RANDOM 0
|
||||||
#endif
|
#endif
|
||||||
@@ -135,7 +131,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_POSIX_FS
|
#ifndef MG_ENABLE_POSIX_FS
|
||||||
#define MG_ENABLE_POSIX_FS 0
|
#define MG_ENABLE_POSIX_FS MG_ENABLE_LFS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_INVALID_SOCKET
|
#ifndef MG_INVALID_SOCKET
|
||||||
@@ -227,6 +223,28 @@
|
|||||||
#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0
|
#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TLS_RECORD_SIZE
|
||||||
|
#define MG_TLS_RECORD_SIZE 16384 // Max plaintext per sent TLS record, builtin
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_CHACHA20
|
#ifndef MG_ENABLE_CHACHA20
|
||||||
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// P-256 implementation, for MG_TLS_BUILTIN
|
||||||
|
#define MG_P256_LENNGREN 1 // Cortex-M4/M7/M33 asm, see tls_p256_cortex.c
|
||||||
|
#define MG_P256_BEAR 3 // Portable C, BearSSL, see tls_p256.c
|
||||||
|
|
||||||
|
#ifndef MG_P256
|
||||||
|
#if defined(__GNUC__) && defined(__ARM_ARCH_PROFILE) && \
|
||||||
|
__ARM_ARCH_PROFILE == 'M' && defined(__ARM_FEATURE_DSP) && \
|
||||||
|
!defined(__ARM_BIG_ENDIAN)
|
||||||
|
#define MG_P256 MG_P256_LENNGREN // Cortex-M with DSP: M4, M7, M33, M55, M85
|
||||||
|
#else
|
||||||
|
#define MG_P256 MG_P256_BEAR
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_HEALTH
|
||||||
|
#define MG_ENABLE_HEALTH 0
|
||||||
|
#endif
|
||||||
+36
-179
@@ -1,5 +1,6 @@
|
|||||||
#include "dash.h"
|
#include "dash.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
#include "ota.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define MG_NO_CACHE_HEADERS "Cache-Control: no-cache\r\n"
|
#define MG_NO_CACHE_HEADERS "Cache-Control: no-cache\r\n"
|
||||||
@@ -77,12 +78,7 @@ static int mg_dash_array_size(struct mg_field_set *set,
|
|||||||
struct mg_dash_user *u) {
|
struct mg_dash_user *u) {
|
||||||
int saved = *set->index, sz = -1;
|
int saved = *set->index, sz = -1;
|
||||||
*set->index = -1;
|
*set->index = -1;
|
||||||
if (set->fn) {
|
if (set->fn && set->fn(MG_DASH_READ, u)) sz = *set->index;
|
||||||
if (set->fn(MG_DASH_READ, u)) sz = *set->index;
|
|
||||||
} else if (set->get_dir) {
|
|
||||||
mg_dash_dir_read(set, u);
|
|
||||||
sz = *set->index;
|
|
||||||
}
|
|
||||||
*set->index = saved;
|
*set->index = saved;
|
||||||
return sz;
|
return sz;
|
||||||
}
|
}
|
||||||
@@ -100,10 +96,7 @@ static size_t mg_dash_print_array(mg_pfn_t fn, void *arg, va_list *ap) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
bool done = to >= 0 && *set->index > to;
|
bool done = to >= 0 && *set->index > to;
|
||||||
if (!done) {
|
if (!done) {
|
||||||
if (set->fn)
|
if (set->fn) set->fn(MG_DASH_READ, u);
|
||||||
set->fn(MG_DASH_READ, u);
|
|
||||||
else if (set->get_dir)
|
|
||||||
mg_dash_dir_read(set, u);
|
|
||||||
done = *set->index < 0;
|
done = *set->index < 0;
|
||||||
}
|
}
|
||||||
if (done) break;
|
if (done) break;
|
||||||
@@ -185,8 +178,7 @@ void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *set) {
|
|||||||
MG_ESC("change"), MG_ESC("params"), MG_ESC(set->name), sz);
|
MG_ESC("change"), MG_ESC("params"), MG_ESC(set->name), sz);
|
||||||
} else {
|
} else {
|
||||||
int saved_idx = set->index != NULL ? *set->index : 0;
|
int saved_idx = set->index != NULL ? *set->index : 0;
|
||||||
bool ok = set->fn ? set->fn(MG_DASH_READ, u)
|
bool ok = set->fn ? set->fn(MG_DASH_READ, u) : true;
|
||||||
: (set->get_dir ? mg_dash_dir_read(set, u) : true);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
if (set->index != NULL) *set->index = saved_idx;
|
if (set->index != NULL) *set->index = saved_idx;
|
||||||
continue;
|
continue;
|
||||||
@@ -233,8 +225,7 @@ static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) {
|
|||||||
mg_json_get_num(json, json_path, (double *) f->value);
|
mg_json_get_num(json, json_path, (double *) f->value);
|
||||||
} else if (f->type == MG_VAL_STR && f->value_size > 0) {
|
} else if (f->type == MG_VAL_STR && f->value_size > 0) {
|
||||||
struct mg_str tok = mg_json_get_tok(json, json_path);
|
struct mg_str tok = mg_json_get_tok(json, json_path);
|
||||||
ok = tok.len >= 2 && tok.buf[0] == '"' &&
|
ok = tok.len >= 2 && tok.buf[0] == '"' && tok.buf[tok.len - 1] == '"';
|
||||||
tok.buf[tok.len - 1] == '"';
|
|
||||||
if (ok) mg_json_unescape(json, json_path, (char *) f->value, f->value_size);
|
if (ok) mg_json_unescape(json, json_path, (char *) f->value, f->value_size);
|
||||||
} else if (f->type == MG_VAL_RAW && f->value_size > 0) {
|
} else if (f->type == MG_VAL_RAW && f->value_size > 0) {
|
||||||
ok = mg_snprintf((char *) f->value, f->value_size, "%.*s", json.len,
|
ok = mg_snprintf((char *) f->value, f->value_size, "%.*s", json.len,
|
||||||
@@ -271,68 +262,6 @@ static int mg_dash_apply(struct mg_connection *c, struct mg_dash *dash,
|
|||||||
return total_count;
|
return total_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mg_dash_dir_read(struct mg_field_set *set, struct mg_dash_user *u) {
|
|
||||||
char dir[256], fname[128] = "";
|
|
||||||
struct mg_fs *fs = u->dash->upload_fs ? u->dash->upload_fs : &mg_fs_posix;
|
|
||||||
struct mg_field *name_field = NULL, *size_field = NULL;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!set->get_dir(u, dir, sizeof(dir))) return false;
|
|
||||||
|
|
||||||
for (i = 0; set->fields[i].name != NULL; i++) {
|
|
||||||
if (name_field == NULL && set->fields[i].type == MG_VAL_STR &&
|
|
||||||
strcmp(set->fields[i].name, "name") == 0)
|
|
||||||
name_field = &set->fields[i];
|
|
||||||
if (size_field == NULL && strcmp(set->fields[i].name, "size") == 0)
|
|
||||||
size_field = &set->fields[i];
|
|
||||||
}
|
|
||||||
if (name_field == NULL) return false;
|
|
||||||
|
|
||||||
if (*set->index == -1) { // Size query: count all files
|
|
||||||
int count = 0;
|
|
||||||
while (mg_fs_ls(fs, dir, fname, sizeof(fname))) count++;
|
|
||||||
*set->index = count;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Regular read: scan to *set->index
|
|
||||||
int target = *set->index, cur = 0;
|
|
||||||
while (mg_fs_ls(fs, dir, fname, sizeof(fname))) {
|
|
||||||
if (cur++ == target) {
|
|
||||||
mg_snprintf((char *) name_field->value, name_field->value_size, "%s",
|
|
||||||
fname);
|
|
||||||
if (size_field != NULL) {
|
|
||||||
char path[512];
|
|
||||||
size_t sz = 0;
|
|
||||||
mg_snprintf(path, sizeof(path), "%s/%s", dir, fname);
|
|
||||||
fs->st(path, &sz, NULL);
|
|
||||||
if (size_field->type == MG_VAL_UINT64)
|
|
||||||
*(uint64_t *) size_field->value = (uint64_t) sz;
|
|
||||||
else if (size_field->type == MG_VAL_INT)
|
|
||||||
*(int *) size_field->value = (int) sz;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*set->index = -1; // No more entries
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool mg_dash_set_file_name(struct mg_field_set *set,
|
|
||||||
struct mg_str name) {
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; set->fields[i].name != NULL; i++) {
|
|
||||||
struct mg_field *f = &set->fields[i];
|
|
||||||
if (f->type == MG_VAL_STR && strcmp(f->name, "name") == 0) {
|
|
||||||
mg_snprintf((char *) f->value, f->value_size, "%.*s", (int) name.len,
|
|
||||||
name.buf);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void mg_log_http_req(struct mg_connection *c,
|
static inline void mg_log_http_req(struct mg_connection *c,
|
||||||
struct mg_http_message *hm) {
|
struct mg_http_message *hm) {
|
||||||
int len = 0;
|
int len = 0;
|
||||||
@@ -365,33 +294,6 @@ static inline void mg_log_http_req(struct mg_connection *c,
|
|||||||
c->send.buf + n));
|
c->send.buf + n));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_dash_ota_cb(struct mg_connection *c, const char *errmsg) {
|
|
||||||
mg_http_reply(c, errmsg ? 500 : 200, NULL, errmsg ? errmsg : "ok\n");
|
|
||||||
c->is_draining = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mg_dash_upload_cb(struct mg_connection *c, const char *errmsg) {
|
|
||||||
if (errmsg) {
|
|
||||||
mg_http_reply(c, 500, NULL, "%s\n", errmsg);
|
|
||||||
} else {
|
|
||||||
// mg_http_start_upload() repurposes c->data for its own bookkeeping,
|
|
||||||
// so the field set can't be cached there. Re-derive the dashboard from
|
|
||||||
// c->fn_data instead, and notify every file-backed array: the upload
|
|
||||||
// could belong to any of them, and re-querying get_dir() per recipient
|
|
||||||
// is what mg_dash_send_change() does anyway (directories can be
|
|
||||||
// user-specific)
|
|
||||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
|
||||||
struct mg_field_set *fs;
|
|
||||||
mg_http_reply(c, 200, NULL, "ok\n");
|
|
||||||
for (fs = dash->sets; fs != NULL; fs = fs->next) {
|
|
||||||
if (fs->get_dir == NULL) continue;
|
|
||||||
*fs->index = -1; // Signal mg_dash_send_change() to broadcast new size
|
|
||||||
mg_dash_send_change(c->mgr, fs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c->is_draining = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t mg_dash_make_expiration_time(struct mg_dash *dash) {
|
static uint64_t mg_dash_make_expiration_time(struct mg_dash *dash) {
|
||||||
unsigned t = (unsigned) dash->session_auto_expiration_seconds;
|
unsigned t = (unsigned) dash->session_auto_expiration_seconds;
|
||||||
if (t == 0) t = 3600; // Default expiration time in seconds
|
if (t == 0) t = 3600; // Default expiration time in seconds
|
||||||
@@ -631,6 +533,25 @@ static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
|
struct mg_dash_user *u, bool stream) {
|
||||||
|
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||||
|
char root[128] = "/fs/=";
|
||||||
|
if (u == NULL) {
|
||||||
|
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
||||||
|
} else if (dash->files_dir(u, root + 5, sizeof(root) - 5) == false) {
|
||||||
|
mg_http_reply(c, 500, MG_JSON_HEADERS, "Upload dir error\n");
|
||||||
|
} else {
|
||||||
|
struct mg_http_serve_opts o;
|
||||||
|
memset(&o, 0, sizeof(o));
|
||||||
|
o.root_dir = root;
|
||||||
|
o.fs = dash->upload_fs ? dash->upload_fs : &mg_fs_posix;
|
||||||
|
o.enable_delete = o.enable_upload = o.enable_dir_listing = true;
|
||||||
|
if (stream) mg_http_serve_upload(c, hm, &o);
|
||||||
|
else mg_http_serve_dir(c, hm, &o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||||
struct mg_dash_cdata *d = (struct mg_dash_cdata *) c->data;
|
struct mg_dash_cdata *d = (struct mg_dash_cdata *) c->data;
|
||||||
@@ -644,9 +565,6 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
// Received headers - check authentication and possibly start uploads/ota
|
// Received headers - check authentication and possibly start uploads/ota
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
|
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
|
||||||
struct mg_str parts[3];
|
|
||||||
memset(parts, 0, sizeof(parts));
|
|
||||||
|
|
||||||
if (mg_match(hm->uri, mg_str("/api/hi"), NULL) ||
|
if (mg_match(hm->uri, mg_str("/api/hi"), NULL) ||
|
||||||
mg_match(hm->uri, mg_str("/api/logout"), NULL)) {
|
mg_match(hm->uri, mg_str("/api/logout"), NULL)) {
|
||||||
// Do nothing, handle them MG_EV_HTTP_MSG. We bypass auth for those
|
// Do nothing, handle them MG_EV_HTTP_MSG. We bypass auth for those
|
||||||
@@ -656,49 +574,21 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
} else if (mg_match(hm->uri, mg_str("/api/login"), NULL) && u != NULL) {
|
} else if (mg_match(hm->uri, mg_str("/api/login"), NULL) && u != NULL) {
|
||||||
mg_handle_login(c, u);
|
mg_handle_login(c, u);
|
||||||
d->marker = CONN_HANDLED;
|
d->marker = CONN_HANDLED;
|
||||||
|
} else if (u != NULL && dash->files_dir != NULL &&
|
||||||
|
mg_match(hm->uri, mg_str("/fs/#"), NULL)) {
|
||||||
|
mg_serve_fs(c, hm, u, true);
|
||||||
|
return; // Important - the serve_fs() call can invalidate hm
|
||||||
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
||||||
mg_http_start_ota(c, hm, mg_dash_ota_cb);
|
// Authenticated above. Streaming owns the connection and c->data.
|
||||||
} else if (mg_match(hm->uri, mg_str("/fs/*/*"), parts) &&
|
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
(mg_strcasecmp(hm->method, mg_str("POST")) == 0 ||
|
return;
|
||||||
mg_strcasecmp(hm->method, mg_str("PUT")) == 0)) {
|
|
||||||
struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
|
|
||||||
struct mg_str name = parts[1];
|
|
||||||
int len =
|
|
||||||
mg_url_decode(name.buf, name.len, (char *) name.buf, name.len + 1, 0);
|
|
||||||
if (len > 0 && (size_t) len <= name.len) name.len = (size_t) len;
|
|
||||||
if (set == NULL || set->get_dir == NULL) {
|
|
||||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found\n");
|
|
||||||
d->marker = CONN_HANDLED;
|
|
||||||
} else if (u == NULL) {
|
|
||||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
|
||||||
d->marker = CONN_HANDLED;
|
|
||||||
} else if (!mg_path_is_sane(name)) {
|
|
||||||
mg_http_reply(c, 400, MG_JSON_HEADERS, "Bad file name\n");
|
|
||||||
d->marker = CONN_HANDLED;
|
|
||||||
} else {
|
|
||||||
mg_dash_set_file_name(set, name);
|
|
||||||
if (set->fn != NULL && !set->fn(MG_DASH_WRITE, u)) {
|
|
||||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
|
||||||
d->marker = CONN_HANDLED;
|
|
||||||
} else {
|
|
||||||
char dir[256];
|
|
||||||
struct mg_fs *fs = dash->upload_fs ? dash->upload_fs : &mg_fs_posix;
|
|
||||||
if (!set->get_dir(u, dir, sizeof(dir))) {
|
|
||||||
mg_http_reply(c, 500, MG_JSON_HEADERS, "Upload dir error\n");
|
|
||||||
d->marker = CONN_HANDLED;
|
|
||||||
} else {
|
|
||||||
mg_http_start_upload(c, hm, name, mg_str(dir), fs,
|
|
||||||
mg_dash_upload_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (d->marker != '\0') mg_log_http_req(c, hm);
|
if (d->marker != '\0') mg_log_http_req(c, hm);
|
||||||
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
||||||
// The response has been send in EV_HDRS path, so we're not reponding
|
// The response has been send in EV_HDRS path, so we're not reponding
|
||||||
// anything but clearing the marker for the next request.
|
// anything but clearing the marker for the next request.
|
||||||
d->marker = 0;
|
d->marker = 0;
|
||||||
c->is_resp = 0;
|
c->pfn_data = NULL; // Response done
|
||||||
} else if (ev == MG_EV_HTTP_MSG && d->marker == '\0') {
|
} else if (ev == MG_EV_HTTP_MSG && d->marker == '\0') {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
|
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
|
||||||
@@ -714,42 +604,9 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
} else if (mg_match(hm->uri, mg_str("/api/websocket"), NULL)) {
|
} else if (mg_match(hm->uri, mg_str("/api/websocket"), NULL)) {
|
||||||
d->u = u;
|
d->u = u;
|
||||||
mg_ws_upgrade(c, hm, NULL);
|
mg_ws_upgrade(c, hm, NULL);
|
||||||
} else if (mg_match(hm->uri, mg_str("/fs/*/*"), parts)) {
|
} else if (dash->files_dir != NULL &&
|
||||||
struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
|
mg_match(hm->uri, mg_str("/fs/#"), NULL)) {
|
||||||
if (set == NULL || set->get_dir == NULL) {
|
mg_serve_fs(c, hm, u, false);
|
||||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
|
|
||||||
} else if (u == NULL) {
|
|
||||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
|
||||||
} else {
|
|
||||||
char dir[256], path[512];
|
|
||||||
struct mg_fs *fs = dash->upload_fs ? dash->upload_fs : &mg_fs_posix;
|
|
||||||
struct mg_str name = parts[1];
|
|
||||||
int len = mg_url_decode(name.buf, name.len, (char *) name.buf,
|
|
||||||
name.len + 1, 0);
|
|
||||||
if (len > 0 && (size_t) len <= name.len) name.len = (size_t) len;
|
|
||||||
if (!mg_path_is_sane(name)) {
|
|
||||||
mg_http_reply(c, 400, MG_JSON_HEADERS, "Bad file name\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!set->get_dir(u, dir, sizeof(dir))) {
|
|
||||||
mg_http_reply(c, 500, MG_JSON_HEADERS, "Upload dir error\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mg_snprintf(path, sizeof(path), "%s/%.*s", dir, name.len, name.buf);
|
|
||||||
if (mg_strcasecmp(hm->method, mg_str("DELETE")) == 0) {
|
|
||||||
mg_dash_set_file_name(set, name);
|
|
||||||
if (set->fn != NULL && !set->fn(MG_DASH_DELETE, u)) {
|
|
||||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
|
||||||
} else {
|
|
||||||
fs->rm(path);
|
|
||||||
*set->index = -1; // Signal mg_dash_send_change() to send new size
|
|
||||||
mg_dash_send_change(c->mgr, set);
|
|
||||||
mg_http_reply(c, 200, NULL, "true");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mg_http_serve_file(c, hm, path, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (mg_match(hm->uri, mg_str("/api/del/*/*/*"), parts) ||
|
} else if (mg_match(hm->uri, mg_str("/api/del/*/*/*"), parts) ||
|
||||||
mg_match(hm->uri, mg_str("/api/del/*/*"), parts)) {
|
mg_match(hm->uri, mg_str("/api/del/*/*"), parts)) {
|
||||||
mg_dash_handle_del(c, dash, u, parts);
|
mg_dash_handle_del(c, dash, u, parts);
|
||||||
@@ -780,7 +637,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ch == NULL) mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
|
if (ch == NULL) mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found\n");
|
||||||
mg_log_http_req(c, hm);
|
mg_log_http_req(c, hm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-39
@@ -73,8 +73,6 @@ struct mg_field_set {
|
|||||||
// fn sets *index = -1 to signal end of iteration.
|
// fn sets *index = -1 to signal end of iteration.
|
||||||
// Size query: framework sets *index = -1 before fn(READ); fn
|
// Size query: framework sets *index = -1 before fn(READ); fn
|
||||||
// sets *index = total size
|
// sets *index = total size
|
||||||
bool (*get_dir)(const struct mg_dash_user *u, char *buf,
|
|
||||||
size_t len); // Non-NULL: file array
|
|
||||||
struct mg_field_set *next; // Next set in the list
|
struct mg_field_set *next; // Next set in the list
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,6 +94,9 @@ struct mg_dash {
|
|||||||
int session_auto_expiration_seconds; // Session lifetime, 0: 1 hour default
|
int session_auto_expiration_seconds; // Session lifetime, 0: 1 hour default
|
||||||
struct mg_dash_user *guest; // Used when authenticate == NULL
|
struct mg_dash_user *guest; // Used when authenticate == NULL
|
||||||
struct mg_fs *upload_fs; // FS for uploads/listing, default &mg_fs_posix
|
struct mg_fs *upload_fs; // FS for uploads/listing, default &mg_fs_posix
|
||||||
|
// Resolves the built-in file manager's root dir for user u, e.g. per-user
|
||||||
|
// upload directories. NULL: file manager disabled
|
||||||
|
bool (*files_dir)(const struct mg_dash_user *u, char *buf, size_t len);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MG_DASH_ADD_FIELD_SET(dash_, set_) \
|
#define MG_DASH_ADD_FIELD_SET(dash_, set_) \
|
||||||
@@ -121,43 +122,6 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data);
|
|||||||
// array size if *set->index < 0, or a single element update otherwise
|
// array size if *set->index < 0, or a single element update otherwise
|
||||||
void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *);
|
void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *);
|
||||||
|
|
||||||
// Default reader for file-backed array sets (set->get_dir != NULL)
|
|
||||||
// Usage example - this is a complete implemenation of the file manager:
|
|
||||||
// ```c
|
|
||||||
// static struct file {
|
|
||||||
// int index;
|
|
||||||
// char name[64];
|
|
||||||
// size_t size;
|
|
||||||
// uint64_t checksum;
|
|
||||||
// } s_file;
|
|
||||||
// static struct mg_field_set set_files;
|
|
||||||
//
|
|
||||||
// static bool get_dir(const struct mg_dash_user *u, char *buf, size_t len) {
|
|
||||||
// (void) u;
|
|
||||||
// mkdir("/tmp/dashboard", 0755);
|
|
||||||
// mg_snprintf(buf, len, "%s", "/tmp/dashboard");
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static struct mg_field fields_files[] = {
|
|
||||||
// {"name", MG_VAL_STR, s_file.name, sizeof(s_file.name)},
|
|
||||||
// {"size", MG_VAL_UINT64, &s_file.size, 0},
|
|
||||||
// {"checksum", MG_VAL_UINT64, &s_file.checksum, sizeof(s_file.checksum)},
|
|
||||||
// {NULL, MG_VAL_INT, NULL, 0},
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// static bool files_fn(enum mg_dash_op op, struct mg_dash_user *u) {
|
|
||||||
// if (op == MG_DASH_WRITE) return s_uploads_enabled && u->level >= 7;
|
|
||||||
// if (op != MG_DASH_READ) return false;
|
|
||||||
// if (!mg_dash_dir_read(&set_files, u)) return false;
|
|
||||||
// s_file.checksum = 0; // Mock to show how to set custom fields
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static struct mg_field_set set_files = {"files", fields_files, files_fn, &s_file.index, get_dir, NULL};
|
|
||||||
// ```
|
|
||||||
bool mg_dash_dir_read(struct mg_field_set *set, struct mg_dash_user *u);
|
|
||||||
|
|
||||||
// Helper forward declarations for Mongoose CMSIS pack modules
|
// Helper forward declarations for Mongoose CMSIS pack modules
|
||||||
extern struct mg_mgr g_mgr;
|
extern struct mg_mgr g_mgr;
|
||||||
extern void mg_dash_init(struct mg_mgr *);
|
extern void mg_dash_init(struct mg_mgr *);
|
||||||
|
|||||||
@@ -263,14 +263,14 @@ static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
|||||||
bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc);
|
bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc);
|
||||||
bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) {
|
bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) {
|
||||||
if (dnsc->url == NULL) {
|
if (dnsc->url == NULL) {
|
||||||
mg_error(0, "DNS server URL is NULL. Call mg_mgr_init()");
|
MG_ERROR(("DNS server URL is NULL. Call mg_mgr_init()"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (dnsc->c == NULL) {
|
if (dnsc->c == NULL) {
|
||||||
dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL);
|
dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL);
|
||||||
if (dnsc->c == NULL) return false;
|
if (dnsc->c == NULL) return false;
|
||||||
dnsc->c->pfn = dns_cb;
|
|
||||||
}
|
}
|
||||||
|
dnsc->c->pfn = dns_cb; // do it always, DHCP-assigned DNS bootstrap needs it
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#include "net_builtin.h"
|
||||||
|
|
||||||
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
||||||
|
MG_ENABLE_DRIVER_MCXW71
|
||||||
|
|
||||||
|
// ZLL, 802.15.4 link layer. See MCXW71 reference manual and NXP
|
||||||
|
// ieee_802_15_4 PHY sources: PhyPacketProcessor.c, PhyPlmeData.c, PhyISR.c
|
||||||
|
struct mg_zll {
|
||||||
|
volatile uint32_t IRQSTS, PHY_CTRL, EVENT_TMR, TIMESTAMP, T1CMP, T2CMP,
|
||||||
|
T2PRIMECMP, T3CMP, T4CMP, PA_PWR, CHANNEL_NUM0, LQI_AND_RSSI,
|
||||||
|
MACSHORTADDRS0, MACLONGADDRS0_LSB, MACLONGADDRS0_MSB, RX_FRAME_FILTER,
|
||||||
|
CCA_LQI_CTRL, CCA2_CTRL, RESERVED_0, DSM_CTRL, RESERVED_1,
|
||||||
|
MACSHORTADDRS1, MACLONGADDRS1_LSB, MACLONGADDRS1_MSB, DUAL_PAN_CTRL,
|
||||||
|
CHANNEL_NUM1, SAM_CTRL, SAM_TABLE, SAM_MATCH, SAM_FREE_IDX,
|
||||||
|
SEQ_CTRL_STS, ACKDELAY, FILTERFAIL_CODE, RX_WTR_MARK, RESERVED_2,
|
||||||
|
SLOT_PRELOAD, SEQ_STATE, TMR_PRESCALE, LENIENCY_LSB, LENIENCY_MSB,
|
||||||
|
PART_ID, COEX_CTRL, COEX_PRIORITY, ENHACK_CTRL0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TrustZone: code built for the secure state (-mcmse) uses the secure alias
|
||||||
|
// of the radio registers, 0x5xxxxxxx. Otherwise, the non-secure alias
|
||||||
|
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE & 2)
|
||||||
|
#define MGI_ZLL ((struct mg_zll *) 0x58A01000)
|
||||||
|
#define MGI_PKT_RAM ((volatile uint8_t *) 0x58A08000)
|
||||||
|
#else
|
||||||
|
#define MGI_ZLL ((struct mg_zll *) 0x48A01000)
|
||||||
|
#define MGI_PKT_RAM ((volatile uint8_t *) 0x48A08000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MGI_TX_PB MGI_PKT_RAM // Length byte (incl. FCS), then frame
|
||||||
|
#define MGI_RX_PB (MGI_PKT_RAM + 0x80) // Received frame, no length byte
|
||||||
|
|
||||||
|
#define MGI_IRQSTS_SEQIRQ MG_BIT(0)
|
||||||
|
#define MGI_IRQSTS_RXIRQ MG_BIT(2)
|
||||||
|
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
|
||||||
|
#define MGI_PHY_CTRL_XCVSEQ 7U
|
||||||
|
#define MGI_PHY_CTRL_ALL_IRQ_MASKS 0x8007ff00U // Bits 8..18, 31: mask all IRQs
|
||||||
|
#define MGI_SEQ_STATE_MASK 0x1fU
|
||||||
|
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
|
||||||
|
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
|
||||||
|
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
|
||||||
|
|
||||||
|
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2 }; // PHY_CTRL[XCVSEQ]
|
||||||
|
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes
|
||||||
|
|
||||||
|
// Symbol timer, 16 us, 24 bits
|
||||||
|
static uint32_t mg_mcxw71_symbols(void) {
|
||||||
|
return MGI_ZLL->EVENT_TMR >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the sequence to end, up to MGI_TX_TIMEOUT symbols
|
||||||
|
static bool mg_mcxw71_wait(void) {
|
||||||
|
uint32_t start = mg_mcxw71_symbols();
|
||||||
|
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < MGI_TX_TIMEOUT) {
|
||||||
|
if (MGI_ZLL->IRQSTS & MGI_IRQSTS_SEQIRQ) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the current sequence, wait for the sequencer to go idle. See PhyAbort()
|
||||||
|
static void mg_mcxw71_idle(void) {
|
||||||
|
if (MGI_ZLL->PHY_CTRL & MGI_PHY_CTRL_XCVSEQ) {
|
||||||
|
while ((MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) == MGI_SEQ_STATE_RX_WU)
|
||||||
|
(void) 0;
|
||||||
|
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_XCVSEQ;
|
||||||
|
while (MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) (void) 0;
|
||||||
|
}
|
||||||
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS; // Clear all flags, write 1 to clear
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new sequence: MGI_SEQ_RX or MGI_SEQ_TX
|
||||||
|
static void mg_mcxw71_start(uint32_t seq) {
|
||||||
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
||||||
|
MGI_ZLL->PHY_CTRL |= seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_mcxw71_init(struct mg_tcpip_if *ifp) {
|
||||||
|
struct mg_l2_802154_data *d =
|
||||||
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
|
if (d == NULL || d->channel < 11 || d->channel > 26) {
|
||||||
|
MG_ERROR(("Set driver_data, channel 11..26"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
|
||||||
|
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS; // Mask all IRQs, we poll
|
||||||
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
||||||
|
MGI_ZLL->SAM_CTRL = 0;
|
||||||
|
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
|
||||||
|
MGI_ZLL->TMR_PRESCALE = 5; // 16 us timebase, one symbol
|
||||||
|
MGI_ZLL->CHANNEL_NUM0 = d->channel;
|
||||||
|
MGI_ZLL->MACSHORTADDRS0 = (0xfffeU << 16) | d->pan_id; // Filter by PAN ID
|
||||||
|
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
|
||||||
|
mg_mcxw71_start(MGI_SEQ_RX);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send one radio frame: MAC header and payload, no FCS
|
||||||
|
static size_t mg_mcxw71_send(const uint8_t *p, size_t len,
|
||||||
|
struct mg_tcpip_if *ifp) {
|
||||||
|
size_t i;
|
||||||
|
bool ok;
|
||||||
|
mg_mcxw71_idle();
|
||||||
|
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
|
||||||
|
for (i = 0; i < len; i++) MGI_TX_PB[i + 1] = p[i]; // 8-bit access only
|
||||||
|
mg_mcxw71_start(MGI_SEQ_TX);
|
||||||
|
ok = mg_mcxw71_wait();
|
||||||
|
mg_mcxw71_idle();
|
||||||
|
mg_mcxw71_start(MGI_SEQ_RX);
|
||||||
|
(void) ifp;
|
||||||
|
return ok ? len : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mg_mcxw71_tx(const void *buf, size_t len,
|
||||||
|
struct mg_tcpip_if *ifp) {
|
||||||
|
return mg_l2_802154_tx(ifp, buf, len, mg_mcxw71_send);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mg_mcxw71_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
uint32_t irqsts = MGI_ZLL->IRQSTS;
|
||||||
|
uint8_t f[125];
|
||||||
|
size_t i, n = 0;
|
||||||
|
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
|
||||||
|
if (irqsts & MGI_IRQSTS_RXIRQ) {
|
||||||
|
n = MGI_IRQSTS_RX_LEN(irqsts);
|
||||||
|
n = n >= 2 ? n - 2 : 0; // Strip FCS
|
||||||
|
if (n > sizeof(f)) n = sizeof(f);
|
||||||
|
for (i = 0; i < n; i++) f[i] = MGI_RX_PB[i];
|
||||||
|
}
|
||||||
|
mg_mcxw71_idle();
|
||||||
|
mg_mcxw71_start(MGI_SEQ_RX);
|
||||||
|
return mg_l2_802154_rx(ifp, f, n, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||||
|
(void) ifp, (void) s1;
|
||||||
|
return true; // Radio link is always up
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mg_tcpip_driver mg_tcpip_driver_mcxw71 = {mg_mcxw71_init, mg_mcxw71_tx,
|
||||||
|
mg_mcxw71_rx, mg_mcxw71_poll};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
||||||
|
MG_ENABLE_DRIVER_MCXW71
|
||||||
|
|
||||||
|
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
|
||||||
|
// Ethernet frames are split into radio frames, see mg_l2_802154_tx().
|
||||||
|
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
|
||||||
|
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
||||||
|
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||||
|
|
||||||
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
|
do { \
|
||||||
|
static struct mg_l2_802154_data driver_data_; \
|
||||||
|
static struct mg_tcpip_if mif_; \
|
||||||
|
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||||
|
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||||
|
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
||||||
|
mif_.driver_data = &driver_data_; \
|
||||||
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
|
MG_INFO(("Driver: mcxw71, channel %d", MG_L2_802154_CHANNEL)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,450 @@
|
|||||||
|
#include "net_builtin.h"
|
||||||
|
|
||||||
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||||
|
MG_ENABLE_DRIVER_RW612_154
|
||||||
|
|
||||||
|
// The 802.15.4 radio runs NXP combo firmware on CPU2, an OpenThread RCP. We
|
||||||
|
// are CPU3. Messages go through the IMU mailbox: IMU messages carry IMUMC
|
||||||
|
// messages, IMUMC messages carry HDLC framed Spinel. See NXP sources:
|
||||||
|
// mcuxsdk-core drivers/imu/fsl_imu.c, mcux-component
|
||||||
|
// imu_adapter/fsl_adapter_imu.c, connectivity framework
|
||||||
|
// platform/rw61x/fwk_platform_hdlc.c; OpenThread src/lib/spinel/spinel.h
|
||||||
|
|
||||||
|
// IMU message FIFO registers, one block per direction. See fsl_imu.h
|
||||||
|
struct mg_rw612_imu {
|
||||||
|
volatile uint32_t WR_MSG, RD_MSG, STATUS, CNTL, RD_MSG_DBG;
|
||||||
|
};
|
||||||
|
#define MGI_IMU ((struct mg_rw612_imu *) 0x443ff804) // CPU3 -> CPU2
|
||||||
|
#define MGI_IMU_PEER ((struct mg_rw612_imu *) 0x443ff818) // CPU2 -> CPU3
|
||||||
|
#define MGI_IMU_LOCKED MG_BIT(0) // STATUS: FIFO locked
|
||||||
|
#define MGI_IMU_ALMOST_FULL MG_BIT(1) // STATUS: FIFO almost full
|
||||||
|
#define MGI_IMU_EMPTY MG_BIT(3) // STATUS: FIFO empty
|
||||||
|
#define MGI_IMU_WR_PTR(s) (((s) >> 16) & 15U)
|
||||||
|
#define MGI_IMU_INT_CLR MG_BIT(0) // CNTL: clear message ready IRQ
|
||||||
|
#define MGI_IMU_FLUSH MG_BIT(16) // CNTL: flush send FIFO
|
||||||
|
#define MGI_IMU_WAIT_FOR_ACK MG_BIT(17) // CNTL: lock send FIFO
|
||||||
|
#define MGI_IMU_WATERMARK(x) ((uint32_t) (x) << 20)
|
||||||
|
#define MGI_IMU_WATERMARK_MASK (15U << 20)
|
||||||
|
|
||||||
|
// IMU message: header, then payload words. See IMU_Msg_t
|
||||||
|
struct mg_rw612_msg {
|
||||||
|
uint8_t type, subtype, seq, len; // len: number of payload words
|
||||||
|
uint32_t payload[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mailbox in SMU2 shared memory, at the address CPU2 firmware expects
|
||||||
|
struct mg_rw612_mbox {
|
||||||
|
uint32_t magic[2]; // Sync pattern: [0] CPU2, [1] us
|
||||||
|
struct mg_rw612_msg q[2][16]; // Message slots: [0] CPU2 -> us, [1] ours
|
||||||
|
};
|
||||||
|
#define MGI_MBOX ((volatile struct mg_rw612_mbox *) 0x443c0000)
|
||||||
|
#define MGI_TXBUF 0x443c1508U // Our IMUMC buffers: 16 x 264 bytes
|
||||||
|
#define MGI_TXBUF_CNT 16
|
||||||
|
#define MGI_TXBUF_LEN 264U
|
||||||
|
#define MGI_SLEEP_FLAG (*(volatile uint32_t *) 0x443c2588)
|
||||||
|
#define MGI_SYNC_MAGIC 0xabcdef89U
|
||||||
|
|
||||||
|
// IMUMC message, carried in IMU payload words. See imumc_std_msg
|
||||||
|
struct mg_rw612_imumc {
|
||||||
|
uint32_t src, dst; // Endpoint addresses
|
||||||
|
uint16_t rfu, idx; // Reserved
|
||||||
|
uint16_t len; // Payload length, bytes
|
||||||
|
uint16_t flags; // Message flags
|
||||||
|
uint32_t data; // Payload buffer address
|
||||||
|
};
|
||||||
|
#define MGI_EPT_LOCAL 10 // Our 802.15.4 endpoint
|
||||||
|
#define MGI_EPT_REMOTE 20 // Firmware 802.15.4 endpoint
|
||||||
|
|
||||||
|
enum { MGI_MSG_CONTROL = 0, MGI_MSG_IMUMC = 8 }; // IMU message types
|
||||||
|
enum { // Control message subtypes
|
||||||
|
MGI_CTL_SYNC = 1,
|
||||||
|
MGI_CTL_SYNC_ACK = 2,
|
||||||
|
MGI_CTL_ACK_FOR_UNLOCK = 3,
|
||||||
|
MGI_CTL_CMD_BUF_ADDR = 6,
|
||||||
|
MGI_CTL_EPT_QUERY = 11,
|
||||||
|
MGI_CTL_EPT_QUERY_RSP = 12,
|
||||||
|
MGI_CTL_BUF_FREE = 13
|
||||||
|
};
|
||||||
|
|
||||||
|
// Spinel commands, properties and statuses we use
|
||||||
|
enum { MGI_CMD_RESET = 1, MGI_CMD_GET = 2, MGI_CMD_SET = 3, MGI_CMD_IS = 6 };
|
||||||
|
enum {
|
||||||
|
MGI_PROP_LAST_STATUS = 0,
|
||||||
|
MGI_PROP_NCP_VERSION = 2,
|
||||||
|
MGI_PROP_PHY_ENABLED = 0x20,
|
||||||
|
MGI_PROP_PHY_CHAN = 0x21,
|
||||||
|
MGI_PROP_PANID = 0x36,
|
||||||
|
MGI_PROP_RAW_STREAM = 0x37,
|
||||||
|
MGI_PROP_STREAM_RAW = 0x71
|
||||||
|
};
|
||||||
|
#define MGI_RESET_STACK 2 // RESET argument: reset the RCP stack only
|
||||||
|
#define MGI_STATUS_RESET_MIN 112 // LAST_STATUS: RCP has reset
|
||||||
|
#define MGI_STATUS_RESET_MAX 127
|
||||||
|
#define MGI_TX_TIMEOUT_MS 200 // Wait for transmit done, incl. retries
|
||||||
|
#define MGI_RSP_TIMEOUT_MS 500
|
||||||
|
#define MGI_RXQ_CNT 16 // A whole Ethernet frame, 15 radio frames
|
||||||
|
|
||||||
|
// Driver state, one radio
|
||||||
|
struct mg_rw612 {
|
||||||
|
bool up; // IMU link synchronised
|
||||||
|
bool ept_ready; // IMUMC endpoints connected
|
||||||
|
bool reset; // RCP reported a reset
|
||||||
|
uint8_t seq; // IMU message sequence number
|
||||||
|
uint16_t free_bufs; // Free IMUMC buffers, bit per buffer
|
||||||
|
uint8_t hdlc[300]; // HDLC frame being received, unescaped
|
||||||
|
size_t hlen; // Bytes in hdlc
|
||||||
|
bool esc; // Previous byte was an escape
|
||||||
|
uint8_t tid; // Spinel transaction ID of the pending request
|
||||||
|
bool rsp; // Response to the pending request received
|
||||||
|
uint32_t rsp_status; // Its status, if LAST_STATUS, else 0
|
||||||
|
char version[64]; // RCP version string
|
||||||
|
uint8_t rxq[MGI_RXQ_CNT][127]; // Received frames, no FCS
|
||||||
|
uint8_t rxlen[MGI_RXQ_CNT]; // Their lengths
|
||||||
|
uint8_t rxhead, rxtail; // Queue indexes
|
||||||
|
};
|
||||||
|
static struct mg_rw612 s_rw612;
|
||||||
|
|
||||||
|
static void mg_rw612_imu_send(uint8_t type, uint8_t subtype,
|
||||||
|
const uint32_t *payload, uint8_t len) {
|
||||||
|
volatile struct mg_rw612_msg *m;
|
||||||
|
uint64_t expire = mg_millis() + 100;
|
||||||
|
uint8_t i;
|
||||||
|
while ((MGI_IMU->STATUS & (MGI_IMU_ALMOST_FULL | MGI_IMU_LOCKED)) &&
|
||||||
|
mg_millis() < expire) {
|
||||||
|
(void) 0;
|
||||||
|
}
|
||||||
|
m = &MGI_MBOX->q[1][MGI_IMU_WR_PTR(MGI_IMU->STATUS)];
|
||||||
|
m->type = type, m->subtype = subtype, m->seq = s_rw612.seq++, m->len = len;
|
||||||
|
for (i = 0; i < len; i++) m->payload[i] = payload[i];
|
||||||
|
MGI_IMU->WR_MSG = (uint32_t) (uintptr_t) m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_rw612_control(uint8_t subtype, uint32_t a, uint32_t b,
|
||||||
|
uint8_t len) {
|
||||||
|
uint32_t payload[2] = {a, b};
|
||||||
|
mg_rw612_imu_send(MGI_MSG_CONTROL, subtype, payload, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_rw612_queue_rx(const uint8_t *frame, size_t len) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
uint8_t next = (uint8_t) ((s->rxhead + 1) % MGI_RXQ_CNT);
|
||||||
|
if (next == s->rxtail || len > sizeof(s->rxq[0])) return; // Full: drop
|
||||||
|
memcpy(s->rxq[s->rxhead], frame, len);
|
||||||
|
s->rxlen[s->rxhead] = (uint8_t) len;
|
||||||
|
s->rxhead = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spinel packed unsigned integer: 7 bits per byte, LSB first
|
||||||
|
static size_t mg_rw612_unpack(const uint8_t *p, size_t len, uint32_t *v) {
|
||||||
|
size_t i;
|
||||||
|
*v = 0;
|
||||||
|
for (i = 0; i < len && i < 4; i++) {
|
||||||
|
*v |= (uint32_t) (p[i] & 0x7f) << (7 * i);
|
||||||
|
if ((p[i] & 0x80) == 0) return i + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle a received Spinel frame: header, command, property, value
|
||||||
|
static void mg_rw612_spinel(const uint8_t *f, size_t len) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
uint32_t cmd, prop, status = 0;
|
||||||
|
size_t n, ofs = 1;
|
||||||
|
uint8_t tid = f[0] & 15;
|
||||||
|
if (len < 3 || (n = mg_rw612_unpack(f + ofs, len - ofs, &cmd)) == 0) return;
|
||||||
|
ofs += n;
|
||||||
|
if ((n = mg_rw612_unpack(f + ofs, len - ofs, &prop)) == 0) return;
|
||||||
|
ofs += n;
|
||||||
|
if (cmd != MGI_CMD_IS) return;
|
||||||
|
if (prop == MGI_PROP_STREAM_RAW && tid == 0 && len - ofs >= 2) {
|
||||||
|
size_t flen = (size_t) (f[ofs] | (f[ofs + 1] << 8)); // Incl. FCS
|
||||||
|
if (flen >= 2 && ofs + 2 + flen <= len) {
|
||||||
|
mg_rw612_queue_rx(f + ofs + 2, flen - 2);
|
||||||
|
}
|
||||||
|
} else if (prop == MGI_PROP_LAST_STATUS) {
|
||||||
|
mg_rw612_unpack(f + ofs, len - ofs, &status);
|
||||||
|
if (tid == 0 && status >= MGI_STATUS_RESET_MIN &&
|
||||||
|
status <= MGI_STATUS_RESET_MAX) {
|
||||||
|
s->reset = true;
|
||||||
|
}
|
||||||
|
} else if (prop == MGI_PROP_NCP_VERSION) {
|
||||||
|
n = len - ofs < sizeof(s->version) - 1 ? len - ofs : sizeof(s->version) - 1;
|
||||||
|
memcpy(s->version, f + ofs, n);
|
||||||
|
s->version[n] = '\0';
|
||||||
|
}
|
||||||
|
if (tid != 0 && tid == s->tid) s->rsp = true, s->rsp_status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HDLC-lite: 0x7e delimits frames, 0x7d escapes, FCS-16 at the end
|
||||||
|
static uint16_t mg_rw612_fcs(uint16_t fcs, uint8_t c) {
|
||||||
|
int i;
|
||||||
|
fcs ^= c;
|
||||||
|
for (i = 0; i < 8; i++) fcs = (fcs & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1;
|
||||||
|
return fcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_rw612_hdlc_rx(const uint8_t *p, size_t len) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
size_t i, j;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
uint8_t c = p[i];
|
||||||
|
if (c == 0x7e) {
|
||||||
|
if (s->hlen >= 3) {
|
||||||
|
uint16_t fcs = 0xffff;
|
||||||
|
for (j = 0; j < s->hlen; j++) fcs = mg_rw612_fcs(fcs, s->hdlc[j]);
|
||||||
|
if (fcs == 0xf0b8) mg_rw612_spinel(s->hdlc, s->hlen - 2);
|
||||||
|
}
|
||||||
|
s->hlen = 0, s->esc = false;
|
||||||
|
} else if (c == 0x7d) {
|
||||||
|
s->esc = true;
|
||||||
|
} else if (s->hlen < sizeof(s->hdlc)) {
|
||||||
|
s->hdlc[s->hlen++] = s->esc ? c ^ 0x20 : c;
|
||||||
|
s->esc = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_rw612_control_rx(volatile struct mg_rw612_msg *m) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
switch (m->subtype) {
|
||||||
|
case MGI_CTL_ACK_FOR_UNLOCK:
|
||||||
|
MGI_IMU->CNTL &= ~MGI_IMU_WAIT_FOR_ACK;
|
||||||
|
break;
|
||||||
|
case MGI_CTL_SYNC: // As HAL_ImuCtrlHandler(): acknowledge twice
|
||||||
|
mg_rw612_control(MGI_CTL_SYNC_ACK, 0, 0, 0);
|
||||||
|
mg_rw612_control(MGI_CTL_SYNC_ACK, 0, 0, 0);
|
||||||
|
s->up = true;
|
||||||
|
break;
|
||||||
|
case MGI_CTL_SYNC_ACK:
|
||||||
|
case MGI_CTL_CMD_BUF_ADDR:
|
||||||
|
s->up = true;
|
||||||
|
break;
|
||||||
|
case MGI_CTL_EPT_QUERY: { // Firmware asks if our endpoint exists
|
||||||
|
bool ours = m->payload[0] == MGI_EPT_LOCAL;
|
||||||
|
mg_rw612_control(MGI_CTL_EPT_QUERY_RSP, m->payload[0], ours, 2);
|
||||||
|
if (ours) s->ept_ready = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MGI_CTL_EPT_QUERY_RSP:
|
||||||
|
if (m->payload[0] == MGI_EPT_REMOTE && m->payload[1] != 0) {
|
||||||
|
s->ept_ready = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MGI_CTL_BUF_FREE: { // Firmware is done with one of our buffers
|
||||||
|
uint32_t i = (m->payload[0] - MGI_TXBUF) / MGI_TXBUF_LEN;
|
||||||
|
if (i < MGI_TXBUF_CNT) s->free_bufs |= (uint16_t) MG_BIT(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all received IMU messages. See HAL_ImuReceive()
|
||||||
|
static void mg_rw612_poll_imu(void) {
|
||||||
|
bool unlock = false;
|
||||||
|
while ((MGI_IMU_PEER->STATUS & MGI_IMU_EMPTY) == 0) {
|
||||||
|
struct mg_rw612_msg m;
|
||||||
|
volatile struct mg_rw612_msg *p;
|
||||||
|
uint8_t i;
|
||||||
|
MGI_SLEEP_FLAG &= ~1U;
|
||||||
|
p = (volatile struct mg_rw612_msg *) (uintptr_t) MGI_IMU->RD_MSG;
|
||||||
|
if (MGI_IMU_PEER->STATUS & MGI_IMU_LOCKED) unlock = true;
|
||||||
|
if (p == NULL) continue;
|
||||||
|
m.type = p->type, m.subtype = p->subtype, m.len = p->len;
|
||||||
|
for (i = 0; i < 8; i++) m.payload[i] = p->payload[i];
|
||||||
|
if (m.type == MGI_MSG_CONTROL) {
|
||||||
|
mg_rw612_control_rx(&m);
|
||||||
|
} else if (m.type == MGI_MSG_IMUMC) {
|
||||||
|
struct mg_rw612_imumc im;
|
||||||
|
memcpy(&im, m.payload, sizeof(im));
|
||||||
|
if (im.dst == MGI_EPT_LOCAL) {
|
||||||
|
mg_rw612_hdlc_rx((const uint8_t *) (uintptr_t) im.data, im.len);
|
||||||
|
}
|
||||||
|
mg_rw612_control(MGI_CTL_BUF_FREE, im.data, 0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MGI_IMU->CNTL |= MGI_IMU_INT_CLR;
|
||||||
|
MGI_SLEEP_FLAG |= 1U;
|
||||||
|
if (unlock) mg_rw612_control(MGI_CTL_ACK_FOR_UNLOCK, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send bytes to the firmware endpoint, in IMUMC buffer sized chunks
|
||||||
|
static bool mg_rw612_imumc_send(const uint8_t *p, size_t len) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
while (len > 0) {
|
||||||
|
size_t n = len < MGI_TXBUF_LEN ? len : MGI_TXBUF_LEN;
|
||||||
|
uint64_t expire = mg_millis() + 100;
|
||||||
|
struct mg_rw612_imumc im;
|
||||||
|
uint32_t i;
|
||||||
|
while (s->free_bufs == 0) {
|
||||||
|
mg_rw612_poll_imu();
|
||||||
|
if (mg_millis() > expire) return false;
|
||||||
|
}
|
||||||
|
for (i = 0; (s->free_bufs & MG_BIT(i)) == 0; i++) (void) 0;
|
||||||
|
s->free_bufs &= (uint16_t) ~MG_BIT(i);
|
||||||
|
memset(&im, 0, sizeof(im));
|
||||||
|
im.src = MGI_EPT_LOCAL, im.dst = MGI_EPT_REMOTE, im.len = (uint16_t) n;
|
||||||
|
im.data = MGI_TXBUF + i * MGI_TXBUF_LEN;
|
||||||
|
memcpy((void *) (uintptr_t) im.data, p, n);
|
||||||
|
mg_rw612_imu_send(MGI_MSG_IMUMC, 0, (uint32_t *) &im, sizeof(im) / 4);
|
||||||
|
p += n, len -= n;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a Spinel frame, HDLC encoded, and wait for the response with the
|
||||||
|
// same transaction ID. Return false on timeout
|
||||||
|
static bool mg_rw612_request(uint8_t *f, size_t len, uint32_t timeout_ms) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
uint8_t buf[2 * 160 + 6];
|
||||||
|
uint16_t fcs = 0xffff;
|
||||||
|
size_t i, n = 0;
|
||||||
|
uint64_t expire = mg_millis() + timeout_ms;
|
||||||
|
s->tid = (uint8_t) (s->tid % 15 + 1);
|
||||||
|
f[0] = (uint8_t) (0x80 | s->tid); // Header: flag, interface 0, TID
|
||||||
|
s->rsp = false;
|
||||||
|
buf[n++] = 0x7e;
|
||||||
|
for (i = 0; i < len + 2 && n + 3 < sizeof(buf); i++) {
|
||||||
|
uint8_t c;
|
||||||
|
if (i < len) {
|
||||||
|
c = f[i], fcs = mg_rw612_fcs(fcs, c);
|
||||||
|
} else {
|
||||||
|
c = (uint8_t) (i == len ? ~fcs & 0xff : ~fcs >> 8);
|
||||||
|
}
|
||||||
|
if (c == 0x7e || c == 0x7d || c == 0x11 || c == 0x13 || c == 0xf8) {
|
||||||
|
buf[n++] = 0x7d, c ^= 0x20;
|
||||||
|
}
|
||||||
|
buf[n++] = c;
|
||||||
|
}
|
||||||
|
buf[n++] = 0x7e;
|
||||||
|
if (!mg_rw612_imumc_send(buf, n)) return false;
|
||||||
|
while (!s->rsp && mg_millis() < expire) mg_rw612_poll_imu();
|
||||||
|
return s->rsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_rw612_set(uint32_t prop, const uint8_t *val, size_t len) {
|
||||||
|
uint8_t f[16];
|
||||||
|
if (len > sizeof(f) - 3) return false;
|
||||||
|
f[1] = MGI_CMD_SET, f[2] = (uint8_t) prop; // Properties used are < 0x80
|
||||||
|
memcpy(f + 3, val, len);
|
||||||
|
return mg_rw612_request(f, len + 3, MGI_RSP_TIMEOUT_MS) &&
|
||||||
|
s_rw612.rsp_status == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log an init error, return false
|
||||||
|
static bool mg_rw612_fail(const char *msg) {
|
||||||
|
MG_ERROR(("%s", msg));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
struct mg_l2_802154_data *d =
|
||||||
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
|
uint8_t f[8], one = 1, pan[2];
|
||||||
|
uint64_t expire;
|
||||||
|
if (d == NULL || d->channel < 11 || d->channel > 26) {
|
||||||
|
MG_ERROR(("Set driver_data, channel 11..26"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (MGI_MBOX->magic[0] != MGI_SYNC_MAGIC) {
|
||||||
|
MG_ERROR(("Radio firmware is not running on CPU2"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memset(s, 0, sizeof(*s));
|
||||||
|
s->free_bufs = 0xffff;
|
||||||
|
// Reset our send FIFO, drop stale messages. See IMU_Init()
|
||||||
|
MGI_IMU->CNTL |= MGI_IMU_FLUSH;
|
||||||
|
MGI_IMU->CNTL &= ~(MGI_IMU_FLUSH | MGI_IMU_WAIT_FOR_ACK);
|
||||||
|
MGI_IMU->CNTL = (MGI_IMU->CNTL & ~MGI_IMU_WATERMARK_MASK) |
|
||||||
|
MGI_IMU_WATERMARK(15);
|
||||||
|
while ((MGI_IMU_PEER->STATUS & MGI_IMU_EMPTY) == 0) (void) MGI_IMU->RD_MSG;
|
||||||
|
// Synchronise the IMU link, then connect the IMUMC endpoints
|
||||||
|
MGI_MBOX->magic[1] = MGI_SYNC_MAGIC;
|
||||||
|
mg_rw612_control(MGI_CTL_SYNC, 0, 0, 0);
|
||||||
|
expire = mg_millis() + 1000;
|
||||||
|
while (!s->up && mg_millis() < expire) mg_rw612_poll_imu();
|
||||||
|
if (!s->up) return mg_rw612_fail("IMU sync timeout");
|
||||||
|
expire = mg_millis() + 2000;
|
||||||
|
while (!s->ept_ready && mg_millis() < expire) {
|
||||||
|
uint64_t retry = mg_millis() + 100;
|
||||||
|
mg_rw612_control(MGI_CTL_EPT_QUERY, MGI_EPT_REMOTE, 0, 1);
|
||||||
|
while (!s->ept_ready && mg_millis() < retry) mg_rw612_poll_imu();
|
||||||
|
}
|
||||||
|
if (!s->ept_ready) return mg_rw612_fail("IMUMC endpoint timeout");
|
||||||
|
// Reset the RCP stack, wait for its reset status, read its version
|
||||||
|
f[1] = MGI_CMD_RESET, f[2] = MGI_RESET_STACK;
|
||||||
|
mg_rw612_request(f, 3, MGI_RSP_TIMEOUT_MS);
|
||||||
|
expire = mg_millis() + 1000;
|
||||||
|
while (!s->reset && mg_millis() < expire) mg_rw612_poll_imu();
|
||||||
|
if (!s->reset) return mg_rw612_fail("RCP reset timeout");
|
||||||
|
f[1] = MGI_CMD_GET, f[2] = MGI_PROP_NCP_VERSION;
|
||||||
|
mg_rw612_request(f, 3, MGI_RSP_TIMEOUT_MS);
|
||||||
|
MG_INFO(("RCP: %s", s->version));
|
||||||
|
if (!mg_rw612_set(MGI_PROP_PHY_ENABLED, &one, 1)) {
|
||||||
|
return mg_rw612_fail("PHY enable failed");
|
||||||
|
}
|
||||||
|
pan[0] = (uint8_t) d->pan_id, pan[1] = (uint8_t) (d->pan_id >> 8);
|
||||||
|
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &d->channel, 1) ||
|
||||||
|
!mg_rw612_set(MGI_PROP_PANID, pan, 2)) {
|
||||||
|
return mg_rw612_fail("Channel or PAN ID set failed");
|
||||||
|
}
|
||||||
|
if (!mg_rw612_set(MGI_PROP_RAW_STREAM, &one, 1)) {
|
||||||
|
return mg_rw612_fail("Raw stream enable failed");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send one radio frame: MAC header and payload, no FCS
|
||||||
|
static size_t mg_rw612_send(const uint8_t *p, size_t len,
|
||||||
|
struct mg_tcpip_if *ifp) {
|
||||||
|
struct mg_l2_802154_data *d =
|
||||||
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
|
uint8_t f[3 + 2 + 127 + 4];
|
||||||
|
size_t n = 3;
|
||||||
|
f[1] = MGI_CMD_SET, f[2] = MGI_PROP_STREAM_RAW;
|
||||||
|
f[n++] = (uint8_t) (len + 2), f[n++] = 0; // PSDU length, incl. FCS
|
||||||
|
memcpy(f + n, p, len), n += len;
|
||||||
|
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
|
||||||
|
f[n++] = d->channel;
|
||||||
|
f[n++] = 4; // Max CSMA backoffs
|
||||||
|
f[n++] = 0; // Max frame retries: broadcast frames are not acknowledged
|
||||||
|
f[n++] = 1; // CSMA-CA enabled
|
||||||
|
if (!mg_rw612_request(f, n, MGI_TX_TIMEOUT_MS)) {
|
||||||
|
MG_ERROR(("TX timeout"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (s_rw612.rsp_status != 0) {
|
||||||
|
MG_VERBOSE(("TX failed, status %lu", (unsigned long) s_rw612.rsp_status));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mg_rw612_154_tx(const void *buf, size_t len,
|
||||||
|
struct mg_tcpip_if *ifp) {
|
||||||
|
return mg_l2_802154_tx(ifp, buf, len, mg_rw612_send);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mg_rw612_154_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
struct mg_rw612 *s = &s_rw612;
|
||||||
|
size_t res = 0;
|
||||||
|
mg_rw612_poll_imu();
|
||||||
|
while (s->rxtail != s->rxhead && res == 0) {
|
||||||
|
res = mg_l2_802154_rx(ifp, s->rxq[s->rxtail], s->rxlen[s->rxtail], buf,
|
||||||
|
len);
|
||||||
|
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_rw612_154_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||||
|
(void) ifp, (void) s1;
|
||||||
|
return s_rw612.up;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mg_tcpip_driver mg_tcpip_driver_rw612_154 = {
|
||||||
|
mg_rw612_154_init, mg_rw612_154_tx, mg_rw612_154_rx, mg_rw612_154_poll};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||||
|
MG_ENABLE_DRIVER_RW612_154
|
||||||
|
|
||||||
|
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
|
||||||
|
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
|
||||||
|
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
|
||||||
|
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
|
||||||
|
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
|
||||||
|
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
|
||||||
|
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_DRIVER_INIT
|
||||||
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
|
do { \
|
||||||
|
static struct mg_l2_802154_data driver_data_; \
|
||||||
|
static struct mg_tcpip_if mif_; \
|
||||||
|
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||||
|
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||||
|
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
||||||
|
mif_.driver_data = &driver_data_; \
|
||||||
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
|
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -365,7 +365,7 @@ static bool st67w6_init(uint8_t *mac) {
|
|||||||
}
|
}
|
||||||
// set clock, who cares ???
|
// set clock, who cares ???
|
||||||
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
|
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
|
||||||
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
MG_VERBOSE(("%.*s", s_at_resp_len, at_resp));
|
||||||
// BT-ENABLED DEPENDENCY
|
// BT-ENABLED DEPENDENCY
|
||||||
|
|
||||||
// MODULE DEPENDENCY
|
// MODULE DEPENDENCY
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#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)
|
||||||
|
// STM32C: vendor modded single-queue Synopsys v5.4, same settings as STM32H
|
||||||
// STM32H: vendor modded single-queue Synopsys v4.2
|
// STM32H: vendor modded single-queue Synopsys v4.2
|
||||||
// STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
|
// STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
|
||||||
// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
|
// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
|
||||||
@@ -310,7 +311,7 @@ static uint32_t s_rxno;
|
|||||||
#if MG_ENABLE_DRIVER_MCXN
|
#if MG_ENABLE_DRIVER_MCXN
|
||||||
void ETHERNET_IRQHandler(void);
|
void ETHERNET_IRQHandler(void);
|
||||||
void ETHERNET_IRQHandler(void) {
|
void ETHERNET_IRQHandler(void) {
|
||||||
#elif MG_ENABLE_DRIVER_STM32H
|
#elif MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C
|
||||||
void ETH_IRQHandler(void);
|
void ETH_IRQHandler(void);
|
||||||
void ETH_IRQHandler(void) {
|
void ETH_IRQHandler(void) {
|
||||||
#else
|
#else
|
||||||
@@ -339,8 +340,10 @@ void ETH1_IRQHandler(void) {
|
|||||||
}
|
}
|
||||||
ETH->DMACSR =
|
ETH->DMACSR =
|
||||||
MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
|
MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
|
||||||
ETH->DMACRDTPR =
|
// Resume RX: tail is the last descriptor given back to DMA. It must differ
|
||||||
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
|
// from the DMA current descriptor, or some DMAs, e.g. STM32C5, stay suspended
|
||||||
|
ETH->DMACRDTPR = (uint32_t) (uintptr_t) &s_rxdesc[(s_rxno + ETH_DESC_CNT - 1) %
|
||||||
|
ETH_DESC_CNT];
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
|
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
|
||||||
|
|||||||
+11
-2
@@ -4,6 +4,13 @@
|
|||||||
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
||||||
#define MG_ENABLE_DRIVER_STM32H 0
|
#define MG_ENABLE_DRIVER_STM32H 0
|
||||||
#endif
|
#endif
|
||||||
|
#if !defined(MG_ENABLE_DRIVER_STM32C)
|
||||||
|
#define MG_ENABLE_DRIVER_STM32C 0
|
||||||
|
#endif
|
||||||
|
#if MG_ENABLE_DRIVER_STM32C // STM32C5: STM32H5 MAC, the IRQ is ETH1
|
||||||
|
#undef MG_ENABLE_DRIVER_STM32H
|
||||||
|
#define MG_ENABLE_DRIVER_STM32H 1
|
||||||
|
#endif
|
||||||
#if !defined(MG_ENABLE_DRIVER_MCXN)
|
#if !defined(MG_ENABLE_DRIVER_MCXN)
|
||||||
#define MG_ENABLE_DRIVER_MCXN 0
|
#define MG_ENABLE_DRIVER_MCXN 0
|
||||||
#endif
|
#endif
|
||||||
@@ -43,9 +50,11 @@ struct mg_tcpip_driver_stm32h_data {
|
|||||||
#define MG_DRIVER_MDC_CR 4
|
#define MG_DRIVER_MDC_CR 4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
#if MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C && \
|
||||||
|
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
||||||
#elif MG_ENABLE_DRIVER_STM32N && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
#elif (MG_ENABLE_DRIVER_STM32N || MG_ENABLE_DRIVER_STM32C) && \
|
||||||
|
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
|
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
|
||||||
#elif !defined(MG_ENABLE_ETH_IRQ)
|
#elif !defined(MG_ENABLE_ETH_IRQ)
|
||||||
#define MG_ENABLE_ETH_IRQ()
|
#define MG_ENABLE_ETH_IRQ()
|
||||||
|
|||||||
+3
-4
@@ -4,7 +4,7 @@
|
|||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
#ifdef MG_OTA_PUBLIC_KEY
|
#ifdef MG_OTA_PUBLIC_KEY
|
||||||
#include "sha256.h"
|
#include "sha256.h"
|
||||||
#include "tls_uecc.h"
|
#include "tls_p256.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM
|
#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM
|
||||||
@@ -76,9 +76,8 @@ bool mg_ota_flash_end(struct mg_flash *flash) {
|
|||||||
uint8_t hash[32];
|
uint8_t hash[32];
|
||||||
size_t fw_size = s_size - 68; // strip 64-byte sig + 4-byte magic
|
size_t fw_size = s_size - 68; // strip 64-byte sig + 4-byte magic
|
||||||
mg_sha256(hash, (uint8_t *) base, fw_size);
|
mg_sha256(hash, (uint8_t *) base, fw_size);
|
||||||
ok = mg_uecc_verify(s_pubkey, hash, sizeof(hash),
|
ok = mg_p256_verify(s_pubkey, hash, sizeof(hash),
|
||||||
(uint8_t *) base + fw_size,
|
(uint8_t *) base + fw_size) == 1;
|
||||||
mg_uecc_secp256r1()) == 1;
|
|
||||||
MG_INFO(("Signature: %s", ok ? "ok" : "fail"));
|
MG_INFO(("Signature: %s", ok ? "ok" : "fail"));
|
||||||
} else {
|
} else {
|
||||||
ok = false;
|
ok = false;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
#include "log.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
#include "str.h"
|
#include "str.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -47,6 +48,7 @@ bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
|
|||||||
char tmp[MG_PATH_MAX], rnd[10];
|
char tmp[MG_PATH_MAX], rnd[10];
|
||||||
size_t path_len = mg_snprintf(tmp, sizeof(tmp), "%s..%s", path,
|
size_t path_len = mg_snprintf(tmp, sizeof(tmp), "%s..%s", path,
|
||||||
mg_random_str(rnd, sizeof(rnd)));
|
mg_random_str(rnd, sizeof(rnd)));
|
||||||
|
fs->rm(tmp);
|
||||||
if (path_len < sizeof(tmp) &&
|
if (path_len < sizeof(tmp) &&
|
||||||
(fd = mg_fs_open(fs, tmp, MG_FS_WRITE | MG_FS_EXCL)) != NULL) {
|
(fd = mg_fs_open(fs, tmp, MG_FS_WRITE | MG_FS_EXCL)) != NULL) {
|
||||||
result = fs->wr(fd->fd, buf, len) == len;
|
result = fs->wr(fd->fd, buf, len) == len;
|
||||||
@@ -54,8 +56,11 @@ bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
|
|||||||
if (result) {
|
if (result) {
|
||||||
fs->rm(path);
|
fs->rm(path);
|
||||||
result = fs->mv(tmp, path);
|
result = fs->mv(tmp, path);
|
||||||
|
MG_DEBUG(("mv %s -> %s result=%d", tmp, path, result));
|
||||||
}
|
}
|
||||||
fs->rm(tmp);
|
fs->rm(tmp);
|
||||||
|
} else {
|
||||||
|
MG_ERROR(("mg_fs_open(%s) failed, path_len=%zu", tmp, path_len));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "lfs.h"
|
||||||
|
|
||||||
// Flags returned by mg_fs.st() and passed to mg_fs.open().
|
// Flags returned by mg_fs.st() and passed to mg_fs.open().
|
||||||
enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4, MG_FS_EXCL = 8 };
|
enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4, MG_FS_EXCL = 8 };
|
||||||
@@ -40,6 +41,7 @@ void mg_fs_close(struct mg_fd *fd);
|
|||||||
|
|
||||||
// Sequential directory iterator. Call repeatedly with the same buf/len;
|
// Sequential directory iterator. Call repeatedly with the same buf/len;
|
||||||
// each call fills buf with the next entry name. Returns false when done.
|
// each call fills buf with the next entry name. Returns false when done.
|
||||||
|
// Note: make sure buf[0] == 0 before the first call
|
||||||
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);
|
||||||
|
|
||||||
// Reads the entire file into a heap-allocated buffer. The returned mg_str is
|
// Reads the entire file into a heap-allocated buffer. The returned mg_str is
|
||||||
|
|||||||
+4
-4
@@ -10,6 +10,10 @@
|
|||||||
#define MG_STAT_FUNC stat
|
#define MG_STAT_FUNC stat
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MG_ENABLE_LFS
|
||||||
|
#include <dirent.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static int p_stat(const char *path, size_t *size, time_t *mtime) {
|
static int p_stat(const char *path, size_t *size, time_t *mtime) {
|
||||||
#if !defined(S_ISDIR)
|
#if !defined(S_ISDIR)
|
||||||
MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime));
|
MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime));
|
||||||
@@ -161,7 +165,6 @@ struct dirent *readdir(DIR *d) {
|
|||||||
|
|
||||||
static void p_list(const char *dir, void (*fn)(const char *, void *),
|
static void p_list(const char *dir, void (*fn)(const char *, void *),
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
#if MG_ENABLE_DIRLIST
|
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
if ((dirp = (opendir(dir))) == NULL) return;
|
if ((dirp = (opendir(dir))) == NULL) return;
|
||||||
@@ -170,9 +173,6 @@ static void p_list(const char *dir, void (*fn)(const char *, void *),
|
|||||||
fn(dp->d_name, userdata);
|
fn(dp->d_name, userdata);
|
||||||
}
|
}
|
||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
#else
|
|
||||||
(void) dir, (void) fn, (void) userdata;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *p_open(const char *path, int flags) {
|
static void *p_open(const char *path, int flags) {
|
||||||
|
|||||||
+111
-11
@@ -1,21 +1,121 @@
|
|||||||
|
#include "config.h"
|
||||||
#include "health.h"
|
#include "health.h"
|
||||||
#include "health_cortex.h"
|
|
||||||
|
|
||||||
struct mg_health mg_health_record;
|
// The one health record. Lives in RAM that survives a warm reset, see
|
||||||
|
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||||
|
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||||
|
|
||||||
|
// STM32 H723 support. Needs the _estack symbol from the linker script
|
||||||
|
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||||
|
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||||
|
// blocking calls. Copies the unwind registers and raw stack into the health
|
||||||
|
// record, then resets.
|
||||||
|
// "used" keeps the linker from garbage-collecting this section: the only
|
||||||
|
// reference is the "b fault_c" branch in the naked handler below
|
||||||
|
__attribute__((used, noinline)) static void fault_c(uint32_t *frame,
|
||||||
|
uint32_t exc_return) {
|
||||||
|
extern uint32_t _estack;
|
||||||
|
uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18);
|
||||||
|
uint32_t *r = mg_health_record.data;
|
||||||
|
|
||||||
|
if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding
|
||||||
|
r[0] = (uint32_t) (uintptr_t) stack; // SP
|
||||||
|
r[1] = frame[5]; // LR
|
||||||
|
r[2] = frame[6]; // PC
|
||||||
|
for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - 3; i++) { // Raw stack
|
||||||
|
r[3 + i] = stack + i < &_estack ? stack[i] : 0;
|
||||||
|
}
|
||||||
|
mg_health_record.cpuid = SCB->CPUID;
|
||||||
|
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common fault entry. EXC_RETURN bit 2 tells which stack was in use:
|
||||||
|
// 0 = MSP, 1 = PSP. Load the faulting SP into r0 and hand it to fault_c()
|
||||||
|
__attribute__((naked)) void HardFault_Handler(void) {
|
||||||
|
__asm volatile(
|
||||||
|
"mov r1, lr\n\t"
|
||||||
|
"tst lr, #4\n\t"
|
||||||
|
"ite eq\n\t"
|
||||||
|
"mrseq r0, msp\n\t"
|
||||||
|
"mrsne r0, psp\n\t"
|
||||||
|
"b fault_c\n\t");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route the other fault types through the same entry point
|
||||||
|
void MemManage_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||||
|
void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||||
|
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Start a new boot: validate the record, count the boot, store the reset
|
||||||
|
// reason. The current boot moves to prev_* slots first, so the previous
|
||||||
|
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
|
||||||
void mg_health_init(void) {
|
void mg_health_init(void) {
|
||||||
static const char magic[] = MG_HEALTH_MAGIC;
|
char magic[] = MG_HEALTH_MAGIC;
|
||||||
|
// struct mg_health *h = mg_health_get();
|
||||||
|
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
||||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||||
#if MG_HEALTH == MG_HEALTH_CORTEX
|
}
|
||||||
mg_health_cortex_init(&mg_health_cortex);
|
|
||||||
mg_health_record.get_blob = mg_health_cortex_get_blob;
|
#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
mg_health_record.fn_data = &mg_health_cortex;
|
{
|
||||||
|
// After a crash, copy the backtrace from the core dump in flash: the same
|
||||||
|
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
||||||
|
// The core dump stays in flash, so check the reset reason, otherwise an
|
||||||
|
// old crash is reported on every boot
|
||||||
|
static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack
|
||||||
|
esp_reset_reason_t rr = esp_reset_reason();
|
||||||
|
uint32_t *out = mg_health_record.data;
|
||||||
|
size_t n = 0, max = MG_HEALTH_DATA_SIZE;
|
||||||
|
if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT)
|
||||||
|
return; // Not a crash that went through the panic handler
|
||||||
|
if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return;
|
||||||
|
|
||||||
|
mg_health_record.cpuid = 42; // ESP32
|
||||||
|
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
||||||
|
|
||||||
|
memset(out, 0, sizeof(mg_health_record.data));
|
||||||
|
out[n++] = s.exc_pc;
|
||||||
|
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
|
for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
|
||||||
|
if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
|
||||||
|
}
|
||||||
|
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||||
|
// No unwinder: take the return address, then words on the stack that
|
||||||
|
// point into executable memory (a heuristic, may include stale values)
|
||||||
|
if (n < max) out[n++] = s.ex_info.ra;
|
||||||
|
for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
|
||||||
|
uint32_t w;
|
||||||
|
memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array
|
||||||
|
if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mg_str mg_health_get_blob(void) {
|
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
return !mg_health_valid() || mg_health_record.get_blob == NULL
|
size_t n = va_arg(*ap, size_t);
|
||||||
? mg_str("")
|
uint32_t *p = va_arg(*ap, uint32_t *);
|
||||||
: mg_health_record.get_blob(mg_health_record.fn_data);
|
size_t len = 0;
|
||||||
|
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
|
size_t len = 0;
|
||||||
|
if (mg_health_valid() &&
|
||||||
|
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
||||||
|
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||||
|
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
||||||
|
MG_ESC("version"), mg_health_record.magic[3], //
|
||||||
|
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||||
|
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||||
|
} else {
|
||||||
|
len += mg_xprintf(fn, arg, "{}");
|
||||||
|
}
|
||||||
|
(void) ap;
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
+35
-43
@@ -1,24 +1,32 @@
|
|||||||
// Device health monitoring
|
// Device health monitoring
|
||||||
//
|
|
||||||
// A health provider owns the report format and returns it as an opaque blob.
|
|
||||||
// Applications and transports use mg_health_get_blob() without knowing how the
|
|
||||||
// report was collected or encoded.
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "str.h"
|
#include "arch.h"
|
||||||
|
#include "printf.h"
|
||||||
#define MG_HEALTH_NONE 0
|
|
||||||
#define MG_HEALTH_CORTEX 1
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH
|
|
||||||
#define MG_HEALTH MG_HEALTH_NONE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH_MAGIC
|
#ifndef MG_HEALTH_MAGIC
|
||||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||||
|
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||||
|
#define MG_HEALTH_DATA_SIZE 250
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_DATA_SIZE
|
||||||
|
#define MG_HEALTH_DATA_SIZE 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Places the record in RAM that survives a reset. Define in mongoose_config.h
|
||||||
|
// to match the linker script, e.g. __attribute__((section(".mg_health")))
|
||||||
|
#ifndef MG_HEALTH_RAM
|
||||||
|
#define MG_HEALTH_RAM
|
||||||
|
#endif
|
||||||
|
|
||||||
// Why the device restarted. Hardware usually reports several flags at once,
|
// Why the device restarted. Hardware usually reports several flags at once,
|
||||||
// so these are ordered by how much they tell you: the most specific cause a
|
// so these are ordered by how much they tell you: the most specific cause a
|
||||||
// device reports wins
|
// device reports wins
|
||||||
@@ -33,16 +41,14 @@ enum mg_health_reason {
|
|||||||
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
|
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct mg_str mg_health_blob;
|
|
||||||
|
|
||||||
struct mg_health {
|
struct mg_health {
|
||||||
char magic[4];
|
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
||||||
mg_health_blob (*get_blob)(void *);
|
uint32_t reset_reason; // enum mg_health_reason that started this boot
|
||||||
void *fn_data;
|
uint32_t cpuid; // ARM CPU ID
|
||||||
uint32_t reset_reason;
|
uint32_t data[MG_HEALTH_DATA_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct mg_health mg_health_record;
|
extern struct mg_health mg_health_record; // Defined in health.c
|
||||||
|
|
||||||
// Return true if the record holds valid data, i.e. it survived the reset and
|
// Return true if the record holds valid data, i.e. it survived the reset and
|
||||||
// was written by this firmware layout
|
// was written by this firmware layout
|
||||||
@@ -52,14 +58,6 @@ static inline bool mg_health_valid(void) {
|
|||||||
sizeof(mg_health_record.magic)) == 0;
|
sizeof(mg_health_record.magic)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select and initialise the configured health provider. Call after
|
|
||||||
// initializing the board-specific persistent-storage
|
|
||||||
void mg_health_init(void);
|
|
||||||
|
|
||||||
// Return the provider's opaque report. The returned memory is provider-owned
|
|
||||||
// and remains valid until the next call to this function
|
|
||||||
struct mg_str mg_health_get_blob(void);
|
|
||||||
|
|
||||||
static inline int mg_health_reason(void) {
|
static inline int mg_health_reason(void) {
|
||||||
return mg_health_valid() ? (int) mg_health_record.reset_reason
|
return mg_health_valid() ? (int) mg_health_record.reset_reason
|
||||||
: MG_HEALTH_RESET_UNKNOWN;
|
: MG_HEALTH_RESET_UNKNOWN;
|
||||||
@@ -67,23 +65,17 @@ static inline int mg_health_reason(void) {
|
|||||||
|
|
||||||
static inline const char *mg_health_reason_str(int reason) {
|
static inline const char *mg_health_reason_str(int reason) {
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case MG_HEALTH_RESET_POWER:
|
case MG_HEALTH_RESET_POWER: return "power";
|
||||||
return "power";
|
case MG_HEALTH_RESET_BROWNOUT: return "brownout";
|
||||||
case MG_HEALTH_RESET_BROWNOUT:
|
case MG_HEALTH_RESET_PIN: return "pin";
|
||||||
return "brownout";
|
case MG_HEALTH_RESET_SOFTWARE: return "software";
|
||||||
case MG_HEALTH_RESET_PIN:
|
case MG_HEALTH_RESET_LOWPOWER: return "lowpower";
|
||||||
return "pin";
|
case MG_HEALTH_RESET_WATCHDOG: return "watchdog";
|
||||||
case MG_HEALTH_RESET_SOFTWARE:
|
case MG_HEALTH_RESET_FAULT: return "fault";
|
||||||
return "software";
|
default: return "unknown";
|
||||||
case MG_HEALTH_RESET_LOWPOWER:
|
|
||||||
return "lowpower";
|
|
||||||
case MG_HEALTH_RESET_WATCHDOG:
|
|
||||||
return "watchdog";
|
|
||||||
case MG_HEALTH_RESET_FAULT:
|
|
||||||
return "fault";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MG_HEALTH_INIT() mg_health_init()
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
|
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||||
|
extern void mg_health_init(void);
|
||||||
@@ -1,344 +0,0 @@
|
|||||||
#include "health_cortex.h"
|
|
||||||
#include "printf.h"
|
|
||||||
|
|
||||||
#if MG_HEALTH == MG_HEALTH_CORTEX
|
|
||||||
|
|
||||||
#define MG_SCB_CPUID (*(volatile uint32_t *) 0xe000ed00)
|
|
||||||
#define MG_SCB_CFSR (*(volatile uint32_t *) 0xe000ed28)
|
|
||||||
#define MG_SCB_HFSR (*(volatile uint32_t *) 0xe000ed2c)
|
|
||||||
#define MG_SCB_DFSR (*(volatile uint32_t *) 0xe000ed30)
|
|
||||||
#define MG_SCB_MMFAR (*(volatile uint32_t *) 0xe000ed34)
|
|
||||||
#define MG_SCB_BFAR (*(volatile uint32_t *) 0xe000ed38)
|
|
||||||
#define MG_SCB_AFSR (*(volatile uint32_t *) 0xe000ed3c)
|
|
||||||
#define MG_SCB_ABFSR (*(volatile uint32_t *) 0xe000efa8)
|
|
||||||
|
|
||||||
#define MG_CPU_CORTEX_M0 0xc20
|
|
||||||
#define MG_CPU_CORTEX_M23 0xd20
|
|
||||||
#define MG_CPU_CORTEX_M3 0xc23
|
|
||||||
#define MG_CPU_CORTEX_M4 0xc24
|
|
||||||
#define MG_CPU_CORTEX_M7 0xc27
|
|
||||||
#define MG_CPU_CORTEX_M0P 0xc60
|
|
||||||
#define MG_CPU_CORTEX_M33 0xd21
|
|
||||||
#define MG_CPU_CORTEX_M35P 0xd31
|
|
||||||
#define MG_CPU_CORTEX_M52 0xd24
|
|
||||||
#define MG_CPU_CORTEX_M55 0xd22
|
|
||||||
#define MG_CPU_CORTEX_M85 0xd23
|
|
||||||
|
|
||||||
extern uint32_t __StackLimit __attribute__((weak));
|
|
||||||
extern uint32_t __StackTop __attribute__((weak));
|
|
||||||
extern uint32_t _sstack __attribute__((weak));
|
|
||||||
extern uint32_t _estack __attribute__((weak));
|
|
||||||
extern bool mg_health_cortex_stack_range(const void *, uintptr_t *, uintptr_t *)
|
|
||||||
__attribute__((weak));
|
|
||||||
|
|
||||||
static struct mg_health_cortex_record s_record;
|
|
||||||
static char s_report[MG_HEALTH_CORTEX_REPORT_SIZE];
|
|
||||||
|
|
||||||
static bool storage_init(void *storage, size_t size) {
|
|
||||||
// Note: actual init is handled in main()
|
|
||||||
(void) storage;
|
|
||||||
(void) size;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool storage_read(const void *storage, size_t offset, void *buf,
|
|
||||||
size_t len) {
|
|
||||||
volatile const uint8_t *src = (volatile const uint8_t *) storage + offset;
|
|
||||||
uint8_t *dst = (uint8_t *) buf;
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < len; i++) dst[i] = src[i];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool storage_write(void *storage, size_t offset, const void *buf,
|
|
||||||
size_t len) {
|
|
||||||
volatile uint8_t *dst = (volatile uint8_t *) storage + offset;
|
|
||||||
const uint8_t *src = (const uint8_t *) buf;
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < len; i++) dst[i] = src[i];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mg_health_cortex mg_health_cortex = {
|
|
||||||
MG_HEALTH_CORTEX_STORAGE,
|
|
||||||
MG_HEALTH_CORTEX_STORAGE_SIZE,
|
|
||||||
storage_init,
|
|
||||||
storage_read,
|
|
||||||
storage_write,
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool storage_bounds(const struct mg_health_cortex *ctx, size_t offset,
|
|
||||||
size_t len) {
|
|
||||||
return ctx != NULL && ctx->storage != NULL && offset <= ctx->storage_size &&
|
|
||||||
len <= ctx->storage_size - offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool storage_configured(const struct mg_health_cortex *ctx) {
|
|
||||||
return ctx != NULL && ctx->storage != NULL && ctx->storage_size != 0 &&
|
|
||||||
ctx->storage_size >= sizeof(struct mg_health_cortex_record) &&
|
|
||||||
ctx->init != NULL && ctx->read != NULL && ctx->write != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool record_read(struct mg_health_cortex *ctx,
|
|
||||||
struct mg_health_cortex_record *record) {
|
|
||||||
static const char magic[] = MG_HEALTH_MAGIC;
|
|
||||||
if (ctx == NULL || ctx->read == NULL ||
|
|
||||||
!storage_bounds(ctx, 0, sizeof(*record)) ||
|
|
||||||
!ctx->read(ctx->storage, 0, record, sizeof(*record))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return memcmp(record->magic, magic, sizeof(record->magic)) == 0 &&
|
|
||||||
record->version == MG_HEALTH_CORTEX_VERSION &&
|
|
||||||
record->stack_word_count <= MG_HEALTH_CORTEX_STACK_WORDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool record_write(struct mg_health_cortex *ctx,
|
|
||||||
const struct mg_health_cortex_record *record) {
|
|
||||||
char invalid[sizeof(record->magic)] = {0};
|
|
||||||
size_t offset = sizeof(record->magic);
|
|
||||||
if (ctx == NULL || ctx->write == NULL ||
|
|
||||||
!storage_bounds(ctx, 0, sizeof(*record)) ||
|
|
||||||
!ctx->write(ctx->storage, 0, &invalid, sizeof(invalid))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!ctx->write(ctx->storage, offset, (const uint8_t *) record + offset,
|
|
||||||
sizeof(*record) - offset)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!ctx->write(ctx->storage, 0, record->magic, sizeof(record->magic)))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool stack_range(const void *ptr, uintptr_t *lo, uintptr_t *hi) {
|
|
||||||
if (mg_health_cortex_stack_range != NULL &&
|
|
||||||
mg_health_cortex_stack_range(ptr, lo, hi)) {
|
|
||||||
return *lo < *hi;
|
|
||||||
} else if (&__StackLimit != NULL && &__StackTop != NULL) {
|
|
||||||
*lo = (uintptr_t) &__StackLimit;
|
|
||||||
*hi = (uintptr_t) &__StackTop;
|
|
||||||
} else if (&_sstack != NULL && &_estack != NULL) {
|
|
||||||
*lo = (uintptr_t) &_sstack;
|
|
||||||
*hi = (uintptr_t) &_estack;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return *lo < *hi;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int stack_valid(const void *ptr, size_t words) {
|
|
||||||
uintptr_t lo, hi, p = (uintptr_t) ptr;
|
|
||||||
if ((p & 3U) != 0) return 0;
|
|
||||||
if (!stack_range(ptr, &lo, &hi)) return -1;
|
|
||||||
return p >= lo && p <= hi && words <= (hi - p) / sizeof(uint32_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool has_fault_status(uint32_t part) {
|
|
||||||
return part == MG_CPU_CORTEX_M3 || part == MG_CPU_CORTEX_M4 ||
|
|
||||||
part == MG_CPU_CORTEX_M7 || part == MG_CPU_CORTEX_M33 ||
|
|
||||||
part == MG_CPU_CORTEX_M35P || part == MG_CPU_CORTEX_M52 ||
|
|
||||||
part == MG_CPU_CORTEX_M55 || part == MG_CPU_CORTEX_M85;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *core_name(uint32_t part) {
|
|
||||||
switch (part) {
|
|
||||||
case MG_CPU_CORTEX_M0:
|
|
||||||
return "cortex-m0";
|
|
||||||
case MG_CPU_CORTEX_M0P:
|
|
||||||
return "cortex-m0+";
|
|
||||||
case MG_CPU_CORTEX_M23:
|
|
||||||
return "cortex-m23";
|
|
||||||
case MG_CPU_CORTEX_M3:
|
|
||||||
return "cortex-m3";
|
|
||||||
case MG_CPU_CORTEX_M4:
|
|
||||||
return "cortex-m4";
|
|
||||||
case MG_CPU_CORTEX_M7:
|
|
||||||
return "cortex-m7";
|
|
||||||
case MG_CPU_CORTEX_M33:
|
|
||||||
return "cortex-m33";
|
|
||||||
case MG_CPU_CORTEX_M35P:
|
|
||||||
return "cortex-m35p";
|
|
||||||
case MG_CPU_CORTEX_M52:
|
|
||||||
return "cortex-m52";
|
|
||||||
case MG_CPU_CORTEX_M55:
|
|
||||||
return "cortex-m55";
|
|
||||||
case MG_CPU_CORTEX_M85:
|
|
||||||
return "cortex-m85";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *arch_name(uint32_t part) {
|
|
||||||
if (part == MG_CPU_CORTEX_M0 || part == MG_CPU_CORTEX_M0P) return "armv6-m";
|
|
||||||
if (part == MG_CPU_CORTEX_M3) return "armv7-m";
|
|
||||||
if (part == MG_CPU_CORTEX_M4 || part == MG_CPU_CORTEX_M7) return "armv7e-m";
|
|
||||||
return "armv8-m";
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool json_append(char *buf, size_t size, size_t *offset, const char *fmt,
|
|
||||||
...) {
|
|
||||||
va_list ap;
|
|
||||||
size_t available, n;
|
|
||||||
if (*offset >= size) return false;
|
|
||||||
available = size - *offset;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
n = mg_vsnprintf(buf + *offset, available, fmt, &ap);
|
|
||||||
va_end(ap);
|
|
||||||
if (n >= available) {
|
|
||||||
*offset = size;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*offset += n;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mg_health_cortex_init(struct mg_health_cortex *ctx) {
|
|
||||||
if (storage_configured(ctx)) {
|
|
||||||
(void) ctx->init(ctx->storage, ctx->storage_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mg_str mg_health_cortex_get_blob(void *data) {
|
|
||||||
struct mg_health_cortex *ctx = (struct mg_health_cortex *) data;
|
|
||||||
struct mg_health_cortex_record *r = &s_record;
|
|
||||||
uint32_t part;
|
|
||||||
size_t n = 0, i;
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
if (!storage_configured(ctx)) {
|
|
||||||
return mg_str(
|
|
||||||
"{\"valid\":false,\"error\":\"health storage is not configured\"}");
|
|
||||||
}
|
|
||||||
if (!record_read(ctx, r)) return mg_str("{\"valid\":false}");
|
|
||||||
part = (r->cpuid >> 4) & 0xfffU;
|
|
||||||
ok = json_append(
|
|
||||||
s_report, sizeof(s_report), &n,
|
|
||||||
"{\"valid\":true,\"version\":%lu,\"arch\":\"%s\","
|
|
||||||
"\"core\":\"%s\",\"image\":\"firmware.elf\","
|
|
||||||
"\"map\":\"firmware.elf.map\",\"binary\":\"firmware.bin\","
|
|
||||||
"\"cpuid\":\"0x%08lx\",\"exception_sp\":\"0x%08lx\","
|
|
||||||
"\"exc_return\":\"0x%08lx\",\"exception_valid\":%s,"
|
|
||||||
"\"stack_valid\":%s,\"regs\":{"
|
|
||||||
"\"r0\":\"0x%08lx\",\"r1\":\"0x%08lx\","
|
|
||||||
"\"r2\":\"0x%08lx\",\"r3\":\"0x%08lx\","
|
|
||||||
"\"r4\":\"0x%08lx\",\"r5\":\"0x%08lx\","
|
|
||||||
"\"r6\":\"0x%08lx\",\"r7\":\"0x%08lx\","
|
|
||||||
"\"r8\":\"0x%08lx\",\"r9\":\"0x%08lx\","
|
|
||||||
"\"r10\":\"0x%08lx\",\"r11\":\"0x%08lx\","
|
|
||||||
"\"r12\":\"0x%08lx\",\"sp\":\"0x%08lx\","
|
|
||||||
"\"lr\":\"0x%08lx\",\"pc\":\"0x%08lx\","
|
|
||||||
"\"xpsr\":\"0x%08lx\"},\"fault\":{"
|
|
||||||
"\"cfsr\":\"0x%08lx\",\"hfsr\":\"0x%08lx\","
|
|
||||||
"\"dfsr\":\"0x%08lx\",\"afsr\":\"0x%08lx\","
|
|
||||||
"\"mmfar\":\"0x%08lx\",\"bfar\":\"0x%08lx\","
|
|
||||||
"\"abfsr\":\"0x%08lx\"},\"stack\":{"
|
|
||||||
"\"addr\":\"0x%08lx\",\"word_size\":4,\"words\":[",
|
|
||||||
(unsigned long) r->version, arch_name(part), core_name(part),
|
|
||||||
(unsigned long) r->cpuid, (unsigned long) r->exception_sp,
|
|
||||||
(unsigned long) r->exc_return, r->exception_valid > 0 ? "true" : "false",
|
|
||||||
r->stack_valid > 0 ? "true" : "false", (unsigned long) r->r0,
|
|
||||||
(unsigned long) r->r1, (unsigned long) r->r2, (unsigned long) r->r3,
|
|
||||||
(unsigned long) r->r4, (unsigned long) r->r5, (unsigned long) r->r6,
|
|
||||||
(unsigned long) r->r7, (unsigned long) r->r8, (unsigned long) r->r9,
|
|
||||||
(unsigned long) r->r10, (unsigned long) r->r11, (unsigned long) r->r12,
|
|
||||||
(unsigned long) r->sp, (unsigned long) r->lr, (unsigned long) r->pc,
|
|
||||||
(unsigned long) r->psr, (unsigned long) r->cfsr, (unsigned long) r->hfsr,
|
|
||||||
(unsigned long) r->dfsr, (unsigned long) r->afsr,
|
|
||||||
(unsigned long) r->mmfar, (unsigned long) r->bfar,
|
|
||||||
(unsigned long) r->abfsr, (unsigned long) r->sp);
|
|
||||||
for (i = 0; ok && i < r->stack_word_count; i++) {
|
|
||||||
ok = json_append(s_report, sizeof(s_report), &n, "%s\"0x%08lx\"",
|
|
||||||
i == 0 ? "" : ",", (unsigned long) r->stack_words[i]);
|
|
||||||
}
|
|
||||||
if (ok) ok = json_append(s_report, sizeof(s_report), &n, "]}}");
|
|
||||||
return ok ? mg_str_n(s_report, n)
|
|
||||||
: mg_str("{\"valid\":false,\"error\":\"JSON overflow\"}");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((used)) mg_health_cortex_handler(
|
|
||||||
uint32_t *exception_stack, uint32_t lr,
|
|
||||||
const uint32_t *callee_saved) {
|
|
||||||
static const char magic[] = MG_HEALTH_MAGIC;
|
|
||||||
uint32_t cpuid = MG_SCB_CPUID;
|
|
||||||
uint32_t part = (cpuid >> 4) & 0xfffU;
|
|
||||||
size_t fp_words = (lr & (1U << 4)) == 0 ? 18U : 0U;
|
|
||||||
int valid = stack_valid(exception_stack, fp_words + 8U);
|
|
||||||
struct mg_health_cortex_record *r = &s_record;
|
|
||||||
volatile uint32_t *words = (volatile uint32_t *) r;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(*r) / sizeof(*words); i++) words[i] = 0;
|
|
||||||
r->version = MG_HEALTH_CORTEX_VERSION;
|
|
||||||
r->cpuid = cpuid;
|
|
||||||
r->exception_sp = (uint32_t) (uintptr_t) exception_stack;
|
|
||||||
r->exc_return = lr;
|
|
||||||
r->exception_valid = valid;
|
|
||||||
r->r4 = callee_saved[0];
|
|
||||||
r->r5 = callee_saved[1];
|
|
||||||
r->r6 = callee_saved[2];
|
|
||||||
r->r7 = callee_saved[3];
|
|
||||||
r->r8 = callee_saved[4];
|
|
||||||
r->r9 = callee_saved[5];
|
|
||||||
r->r10 = callee_saved[6];
|
|
||||||
r->r11 = callee_saved[7];
|
|
||||||
if (valid > 0) {
|
|
||||||
uint32_t *core_frame = exception_stack + fp_words;
|
|
||||||
uint32_t *fault_sp;
|
|
||||||
uintptr_t lo, hi, p;
|
|
||||||
size_t count;
|
|
||||||
r->r0 = core_frame[0];
|
|
||||||
r->r1 = core_frame[1];
|
|
||||||
r->r2 = core_frame[2];
|
|
||||||
r->r3 = core_frame[3];
|
|
||||||
r->r12 = core_frame[4];
|
|
||||||
r->lr = core_frame[5];
|
|
||||||
r->pc = core_frame[6];
|
|
||||||
r->psr = core_frame[7];
|
|
||||||
fault_sp = core_frame + 8;
|
|
||||||
if (r->psr & (1U << 9)) fault_sp++;
|
|
||||||
r->sp = (uint32_t) (uintptr_t) fault_sp;
|
|
||||||
r->stack_valid = stack_valid(fault_sp, 0);
|
|
||||||
p = (uintptr_t) fault_sp;
|
|
||||||
if (r->stack_valid > 0 && stack_range(fault_sp, &lo, &hi)) {
|
|
||||||
(void) lo;
|
|
||||||
count = (hi - p) / sizeof(uint32_t);
|
|
||||||
if (count > MG_HEALTH_CORTEX_STACK_WORDS)
|
|
||||||
count = MG_HEALTH_CORTEX_STACK_WORDS;
|
|
||||||
r->stack_word_count = (uint32_t) count;
|
|
||||||
for (i = 0; i < count; i++) r->stack_words[i] = fault_sp[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (has_fault_status(part)) {
|
|
||||||
r->cfsr = MG_SCB_CFSR;
|
|
||||||
r->hfsr = MG_SCB_HFSR;
|
|
||||||
r->dfsr = MG_SCB_DFSR;
|
|
||||||
r->afsr = MG_SCB_AFSR;
|
|
||||||
r->bfar = MG_SCB_BFAR;
|
|
||||||
r->mmfar = MG_SCB_MMFAR;
|
|
||||||
if (part == MG_CPU_CORTEX_M7) r->abfsr = MG_SCB_ABFSR;
|
|
||||||
}
|
|
||||||
memcpy(r->magic, magic, sizeof(r->magic));
|
|
||||||
(void) record_write(&mg_health_cortex, r);
|
|
||||||
for (;;) (void) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HardFault_Handler(void);
|
|
||||||
__attribute__((naked)) void HardFault_Handler(void) {
|
|
||||||
__asm volatile(
|
|
||||||
"mov r0, lr \n"
|
|
||||||
"movs r1, #4 \n"
|
|
||||||
"tst r0, r1 \n"
|
|
||||||
"beq 1f \n"
|
|
||||||
"mrs r0, psp \n"
|
|
||||||
"b 2f \n"
|
|
||||||
"1: \n"
|
|
||||||
"mrs r0, msp \n"
|
|
||||||
"2: \n"
|
|
||||||
"mov r1, lr \n"
|
|
||||||
"stmdb sp!, {r4-r11} \n"
|
|
||||||
"mov r2, sp \n"
|
|
||||||
"b mg_health_cortex_handler \n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "health.h"
|
|
||||||
|
|
||||||
#define MG_HEALTH_CORTEX_VERSION 1U
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH_CORTEX_STORAGE
|
|
||||||
#define MG_HEALTH_CORTEX_STORAGE NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH_CORTEX_STORAGE_SIZE
|
|
||||||
#define MG_HEALTH_CORTEX_STORAGE_SIZE 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH_CORTEX_STACK_WORDS
|
|
||||||
#define MG_HEALTH_CORTEX_STACK_WORDS 980U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH_CORTEX_REPORT_SIZE
|
|
||||||
#define MG_HEALTH_CORTEX_REPORT_SIZE 16384U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Storage operations are deliberately format-agnostic. H723 uses the default
|
|
||||||
// direct-memory implementation; boards with different retained storage can
|
|
||||||
// replace these callbacks before calling MG_HEALTH_INIT()
|
|
||||||
struct mg_health_cortex {
|
|
||||||
void *storage;
|
|
||||||
size_t storage_size;
|
|
||||||
bool (*init)(void *, size_t);
|
|
||||||
bool (*read)(const void *, size_t, void *, size_t);
|
|
||||||
bool (*write)(void *, size_t, const void *, size_t);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mg_health_cortex_record {
|
|
||||||
char magic[4];
|
|
||||||
uint32_t version;
|
|
||||||
|
|
||||||
uint32_t cpuid;
|
|
||||||
uint32_t exception_sp;
|
|
||||||
uint32_t sp;
|
|
||||||
uint32_t exc_return;
|
|
||||||
int32_t exception_valid;
|
|
||||||
int32_t stack_valid;
|
|
||||||
|
|
||||||
uint32_t r0;
|
|
||||||
uint32_t r1;
|
|
||||||
uint32_t r2;
|
|
||||||
uint32_t r3;
|
|
||||||
uint32_t r4;
|
|
||||||
uint32_t r5;
|
|
||||||
uint32_t r6;
|
|
||||||
uint32_t r7;
|
|
||||||
uint32_t r8;
|
|
||||||
uint32_t r9;
|
|
||||||
uint32_t r10;
|
|
||||||
uint32_t r11;
|
|
||||||
uint32_t r12;
|
|
||||||
uint32_t lr;
|
|
||||||
uint32_t pc;
|
|
||||||
uint32_t psr;
|
|
||||||
|
|
||||||
uint32_t cfsr;
|
|
||||||
uint32_t hfsr;
|
|
||||||
uint32_t dfsr;
|
|
||||||
uint32_t afsr;
|
|
||||||
uint32_t bfar;
|
|
||||||
uint32_t mmfar;
|
|
||||||
uint32_t abfsr;
|
|
||||||
|
|
||||||
uint32_t stack_word_count;
|
|
||||||
uint32_t stack_words[MG_HEALTH_CORTEX_STACK_WORDS];
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct mg_health_cortex mg_health_cortex;
|
|
||||||
|
|
||||||
void mg_health_cortex_init(struct mg_health_cortex *);
|
|
||||||
struct mg_str mg_health_cortex_get_blob(void *);
|
|
||||||
+218
-265
@@ -3,7 +3,6 @@
|
|||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "ota.h"
|
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
#include "ssi.h"
|
#include "ssi.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -359,7 +358,7 @@ static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt,
|
|||||||
if (c->send.len >= len + 10) {
|
if (c->send.len >= len + 10) {
|
||||||
mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10);
|
mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10);
|
||||||
c->send.buf[len + 8] = '\r';
|
c->send.buf[len + 8] = '\r';
|
||||||
if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker
|
if (c->send.len == len + 10) c->pfn_data = NULL; // Last chunk, done
|
||||||
}
|
}
|
||||||
if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
|
if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
|
||||||
}
|
}
|
||||||
@@ -374,7 +373,7 @@ void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
|
|||||||
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
|
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
|
||||||
mg_printf(c, "%lx\r\n", (unsigned long) len);
|
mg_printf(c, "%lx\r\n", (unsigned long) len);
|
||||||
if (!mg_send(c, buf, len) || !mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
|
if (!mg_send(c, buf, len) || !mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
|
||||||
if (len == 0) c->is_resp = 0;
|
if (len == 0) c->pfn_data = NULL; // Last chunk, response done
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -463,15 +462,14 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers,
|
|||||||
(unsigned long) (c->send.len - len));
|
(unsigned long) (c->send.len - len));
|
||||||
c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space
|
c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space
|
||||||
}
|
}
|
||||||
c->is_resp = 0;
|
c->pfn_data = NULL; // Response done
|
||||||
}
|
}
|
||||||
|
|
||||||
static void http_cb(struct mg_connection *, int, void *);
|
static void http_cb(struct mg_connection *, int, void *);
|
||||||
static void restore_http_cb(struct mg_connection *c) {
|
static void restore_http_cb(struct mg_connection *c) {
|
||||||
mg_fs_close((struct mg_fd *) c->pfn_data);
|
mg_fs_close((struct mg_fd *) c->pfn_data);
|
||||||
c->pfn_data = NULL;
|
c->pfn_data = NULL; // Response done
|
||||||
c->pfn = http_cb;
|
c->pfn = http_cb;
|
||||||
c->is_resp = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
|
char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
|
||||||
@@ -659,7 +657,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
|
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
|
||||||
range, hdrs);
|
range, hdrs);
|
||||||
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
|
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
|
||||||
c->is_resp = 0;
|
c->pfn_data = NULL; // Response done
|
||||||
mg_fs_close(fd);
|
mg_fs_close(fd);
|
||||||
} else { // start serving static content only if not closing, see #3354
|
} 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
|
||||||
@@ -677,161 +675,97 @@ struct printdirentrydata {
|
|||||||
struct mg_http_message *hm;
|
struct mg_http_message *hm;
|
||||||
const struct mg_http_serve_opts *opts;
|
const struct mg_http_serve_opts *opts;
|
||||||
const char *dir;
|
const char *dir;
|
||||||
|
size_t count;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if MG_ENABLE_DIRLIST
|
|
||||||
// Print file name, escaping HTML chars
|
|
||||||
static size_t html_esc(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|
||||||
const char *s = va_arg(*ap, const char *);
|
|
||||||
size_t i, len = 0;
|
|
||||||
for (i = 0; s[i] != '\0'; i++) {
|
|
||||||
if (s[i] == '<') {
|
|
||||||
len += mg_xprintf(fn, arg, "%s", "<");
|
|
||||||
} else if (s[i] == '>') {
|
|
||||||
len += mg_xprintf(fn, arg, "%s", ">");
|
|
||||||
} else if (s[i] == '&') {
|
|
||||||
len += mg_xprintf(fn, arg, "%s", "&");
|
|
||||||
} else {
|
|
||||||
len += mg_xprintf(fn, arg, "%c", s[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printdirentry(const char *name, void *userdata) {
|
static void printdirentry(const char *name, void *userdata) {
|
||||||
struct printdirentrydata *d = (struct printdirentrydata *) userdata;
|
struct printdirentrydata *d = (struct printdirentrydata *) userdata;
|
||||||
struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
|
struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
time_t t = 0;
|
time_t t = 0;
|
||||||
char path[MG_PATH_MAX], sz[40], mod[40];
|
char path[MG_PATH_MAX];
|
||||||
int flags, n = 0;
|
int flags;
|
||||||
|
|
||||||
// MG_DEBUG(("[%s] [%s]", d->dir, name));
|
if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) >=
|
||||||
if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) >
|
|
||||||
sizeof(path)) {
|
sizeof(path)) {
|
||||||
MG_ERROR(("%s truncated", name));
|
MG_ERROR(("%s truncated", name));
|
||||||
} else if ((flags = fs->st(path, &size, &t)) == 0) {
|
} else if ((flags = fs->st(path, &size, &t)) == 0) {
|
||||||
MG_ERROR(("%lu stat(%s)", d->c->id, path));
|
MG_ERROR(("%lu stat(%s)", d->c->id, path));
|
||||||
} else {
|
} else {
|
||||||
const char *slash = flags & MG_FS_DIR ? "/" : "";
|
|
||||||
if (flags & MG_FS_DIR) {
|
if (flags & MG_FS_DIR) {
|
||||||
mg_snprintf(sz, sizeof(sz), "%s", "[DIR]");
|
mg_printf(d->c, "%s\n {%m: %m, %m: true}", //
|
||||||
|
d->count == 0 ? "" : ",", //
|
||||||
|
MG_ESC("name"), MG_ESC(name), MG_ESC("dir"));
|
||||||
} else {
|
} else {
|
||||||
mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size);
|
mg_printf(d->c, "%s\n {%m: %m, %m: %zu, %m: %llu, %m: false}", //
|
||||||
|
d->count == 0 ? "" : ",", //
|
||||||
|
MG_ESC("name"), MG_ESC(name), //
|
||||||
|
MG_ESC("size"), size, //
|
||||||
|
MG_ESC("modified"), (uint64_t) t, MG_ESC("dir"));
|
||||||
}
|
}
|
||||||
#if defined(MG_HTTP_DIRLIST_TIME_FMT)
|
d->count++;
|
||||||
{
|
|
||||||
char time_str[40];
|
|
||||||
struct tm *time_info = localtime(&t);
|
|
||||||
strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info);
|
|
||||||
mg_snprintf(mod, sizeof(mod), "%s", time_str);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
mg_snprintf(mod, sizeof(mod), "%lu", (unsigned long) t);
|
|
||||||
#endif
|
|
||||||
n = (int) mg_url_encode(name, strlen(name), path, sizeof(path));
|
|
||||||
mg_printf(d->c,
|
|
||||||
" <tr><td><a href=\"%.*s%s\">%M%s</a></td>"
|
|
||||||
"<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n",
|
|
||||||
n, path, slash, html_esc, name, slash, (unsigned long) t, mod,
|
|
||||||
flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
const struct mg_http_serve_opts *opts, char *dir) {
|
const struct mg_http_serve_opts *opts, char *dir) {
|
||||||
const char *sort_js_code =
|
|
||||||
"<script>function srt(tb, sc, so, d) {"
|
|
||||||
"var tr = Array.prototype.slice.call(tb.rows, 0),"
|
|
||||||
"tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
|
|
||||||
"n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
|
|
||||||
"t1 = a.cells[2].getAttribute('name'), "
|
|
||||||
"t2 = b.cells[2].getAttribute('name'); "
|
|
||||||
"return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
|
|
||||||
"n1 ? parseInt(n2) - parseInt(n1) : "
|
|
||||||
"c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
|
|
||||||
const char *sort_js_code2 =
|
|
||||||
"for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
|
|
||||||
"if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
|
|
||||||
"};"
|
|
||||||
"window.onload = function() {"
|
|
||||||
"var tb = document.getElementById('tb');"
|
|
||||||
"var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
|
|
||||||
"var sc = m[1], so = m[2]; document.onclick = function(ev) { "
|
|
||||||
"var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
|
|
||||||
"sc = c; ev.preventDefault();}};"
|
|
||||||
"srt(tb, sc, so, true);"
|
|
||||||
"}"
|
|
||||||
"</script>";
|
|
||||||
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
||||||
struct printdirentrydata d = {c, hm, opts, dir};
|
struct printdirentrydata d = {c, hm, opts, dir, 0};
|
||||||
char tmp[10], buf[MG_PATH_MAX];
|
char tmp[10];
|
||||||
size_t off, n;
|
size_t off, n;
|
||||||
int len = mg_url_decode(hm->uri.buf, hm->uri.len, buf, sizeof(buf), 0);
|
|
||||||
struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri;
|
|
||||||
|
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"HTTP/1.1 200 OK\r\n"
|
"HTTP/1.1 200 OK\r\n"
|
||||||
"Content-Type: text/html; charset=utf-8\r\n"
|
"Content-Type: application/json; charset=utf-8\r\n"
|
||||||
"%s"
|
"%s"
|
||||||
"Content-Length: \r\n\r\n",
|
"Content-Length: \r\n\r\n",
|
||||||
opts->extra_headers == NULL ? "" : opts->extra_headers);
|
opts->extra_headers == NULL ? "" : opts->extra_headers);
|
||||||
off = c->send.len; // Start of body
|
off = c->send.len; // Start of body
|
||||||
mg_printf(c,
|
mg_printf(c, "[");
|
||||||
"<!DOCTYPE html><html><head><title>Index of %M</title>%s%s"
|
|
||||||
"<style>th,td {text-align: left; padding-right: 1em; "
|
|
||||||
"font-family: monospace; }</style></head>"
|
|
||||||
"<body><h1>Index of %M</h1><table cellpadding=\"0\"><thead>"
|
|
||||||
"<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
|
|
||||||
"<a href=\"#\" rel=\"1\">Modified</a></th>"
|
|
||||||
"<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
|
|
||||||
"<tr><td colspan=\"3\"><hr></td></tr>"
|
|
||||||
"</thead>"
|
|
||||||
"<tbody id=\"tb\">\n",
|
|
||||||
mg_print_html_esc, (int) uri.len, uri.buf, sort_js_code, sort_js_code2,
|
|
||||||
mg_print_html_esc, (int) uri.len, uri.buf);
|
|
||||||
mg_printf(c, "%s",
|
|
||||||
" <tr><td><a href=\"..\">..</a></td>"
|
|
||||||
"<td name=-1></td><td name=-1>[DIR]</td></tr>\n");
|
|
||||||
fs->ls(dir, printdirentry, &d);
|
fs->ls(dir, printdirentry, &d);
|
||||||
mg_printf(c,
|
mg_printf(c, "\n]\n");
|
||||||
"</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
|
|
||||||
"</table><address>Mongoose v.%s</address></body></html>\n",
|
|
||||||
MG_VERSION);
|
|
||||||
n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
|
n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
|
||||||
if (n > sizeof(tmp)) n = 0;
|
if (n > sizeof(tmp)) n = 0;
|
||||||
memcpy(c->send.buf + off - 12, tmp, n); // Set content length
|
memcpy(c->send.buf + off - 12, tmp, n); // Set content length
|
||||||
c->is_resp = 0; // Mark response end
|
c->pfn_data = NULL; // Response done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map requested URI to the file path (buf,len). Use root directory r.
|
||||||
|
// r could be a path optionally followed by map: PATH,/PREFIX1=PATH1,...
|
||||||
|
static bool uri2path(const char *r, struct mg_str uri, char *buf, size_t len) {
|
||||||
|
struct mg_str k, v, part, s = mg_str(r), u = {NULL, 0}, d = u;
|
||||||
|
size_t n;
|
||||||
|
while (mg_span(s, &part, &s, ',')) {
|
||||||
|
if (!mg_span(part, &k, &v, '=')) k = part, v = mg_str_n(NULL, 0);
|
||||||
|
if (v.len == 0) v = k, k = mg_str("/"), u = k, d = v;
|
||||||
|
if (uri.len < k.len) continue;
|
||||||
|
if (mg_strcmp(k, mg_str_n(uri.buf, k.len)) != 0) continue;
|
||||||
|
u = k, d = v;
|
||||||
|
}
|
||||||
|
n = mg_snprintf(buf, len, "%.*s", (int) d.len, d.buf);
|
||||||
|
if (len == 0 || n + 2 >= len) return false; // Path overflow
|
||||||
|
if (n > 0 && buf[n - 1] != '/') buf[n++] = '/', buf[n] = '\0'; // Add slash
|
||||||
|
if (mg_url_decode(uri.buf + u.len, uri.len - u.len, buf + n, len - n, 0) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf[len - 1] = '\0'; // Double-check
|
||||||
|
n = strlen(buf);
|
||||||
|
if (!mg_path_is_sane(mg_str_n(buf, n))) return false;
|
||||||
|
while (n > 1 && buf[n - 1] == '/') buf[--n] = 0; // Trim trailing slashes
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Resolve requested file into `path` and return its fs->st() result
|
// Resolve requested file into `path` and return its fs->st() result
|
||||||
static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
|
static int uri_to_file_status(struct mg_connection *c,
|
||||||
struct mg_fs *fs, struct mg_str url, struct mg_str dir,
|
struct mg_http_message *hm,
|
||||||
|
struct mg_fs *fs, const char *root_dir,
|
||||||
char *path, size_t path_size) {
|
char *path, size_t path_size) {
|
||||||
int flags, tmp;
|
int flags, tmp;
|
||||||
// Append URI to the root_dir, and sanitize it
|
size_t n;
|
||||||
size_t n = mg_snprintf(path, path_size, "%.*s", (int) dir.len, dir.buf);
|
if (!uri2path(root_dir, hm->uri, path, path_size)) {
|
||||||
if (n + 2 >= path_size) {
|
|
||||||
mg_http_reply(c, 400, "", "Exceeded path size");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
path[path_size - 1] = '\0';
|
|
||||||
// Terminate root dir with slash
|
|
||||||
if (n > 0 && path[n - 1] != '/') path[n++] = '/', path[n] = '\0';
|
|
||||||
if (url.len < hm->uri.len &&
|
|
||||||
mg_url_decode(hm->uri.buf + url.len, hm->uri.len - url.len, path + n,
|
|
||||||
path_size - n, 0) < 0) {
|
|
||||||
mg_http_reply(c, 400, "", "Invalid path");
|
mg_http_reply(c, 400, "", "Invalid path");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
path[path_size - 1] = '\0'; // Double-check
|
|
||||||
n = strlen(path);
|
n = strlen(path);
|
||||||
if (!mg_path_is_sane(mg_str_n(path, n))) {
|
|
||||||
mg_http_reply(c, 400, "", "Invalid path");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
|
|
||||||
flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR
|
flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR
|
||||||
: fs->st(path, NULL, NULL);
|
: fs->st(path, NULL, NULL);
|
||||||
MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path,
|
MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path,
|
||||||
@@ -846,7 +780,7 @@ static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
"Content-Length: 0\r\n"
|
"Content-Length: 0\r\n"
|
||||||
"\r\n",
|
"\r\n",
|
||||||
(int) hm->uri.len, hm->uri.buf);
|
(int) hm->uri.len, hm->uri.buf);
|
||||||
c->is_resp = 0;
|
c->pfn_data = NULL; // Response done
|
||||||
flags = -1;
|
flags = -1;
|
||||||
} else if (flags & MG_FS_DIR) {
|
} else if (flags & MG_FS_DIR) {
|
||||||
if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 &&
|
if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 &&
|
||||||
@@ -868,34 +802,36 @@ static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
|
|
||||||
const struct mg_http_serve_opts *opts, char *path,
|
|
||||||
size_t path_size) {
|
|
||||||
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
|
||||||
struct mg_str k, v, part, s = mg_str(opts->root_dir), u = {NULL, 0}, p = u;
|
|
||||||
while (mg_span(s, &part, &s, ',')) {
|
|
||||||
if (!mg_span(part, &k, &v, '=')) k = part, v = mg_str_n(NULL, 0);
|
|
||||||
if (v.len == 0) v = k, k = mg_str("/"), u = k, p = v;
|
|
||||||
if (hm->uri.len < k.len) continue;
|
|
||||||
if (mg_strcmp(k, mg_str_n(hm->uri.buf, k.len)) != 0) continue;
|
|
||||||
u = k, p = v;
|
|
||||||
}
|
|
||||||
return uri_to_path2(c, hm, fs, u, p, path, path_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
const struct mg_http_serve_opts *opts) {
|
const struct mg_http_serve_opts *opts) {
|
||||||
char path[MG_PATH_MAX];
|
char path[MG_PATH_MAX];
|
||||||
|
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
||||||
const char *sp = opts->ssi_pattern;
|
const char *sp = opts->ssi_pattern;
|
||||||
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
|
int flags = uri_to_file_status(c, hm, fs, opts->root_dir, path, sizeof(path));
|
||||||
if (flags < 0) {
|
if (flags < 0) {
|
||||||
// Do nothing: the response has already been sent by uri_to_path()
|
// Do nothing: the response has already been sent by uri_to_file_status()
|
||||||
} else if (flags & MG_FS_DIR) {
|
} else if (flags & MG_FS_DIR) {
|
||||||
#if MG_ENABLE_DIRLIST
|
if (opts->enable_dir_listing) {
|
||||||
listdir(c, hm, opts, path);
|
listdir(c, hm, opts, path);
|
||||||
#else
|
} else {
|
||||||
mg_http_reply(c, 403, "", "Forbidden\n");
|
mg_http_reply(c, 403, "", "Forbidden\n");
|
||||||
#endif
|
}
|
||||||
|
} else if (opts->enable_delete &&
|
||||||
|
mg_strcasecmp(hm->method, mg_str("DELETE")) == 0) {
|
||||||
|
mg_http_reply(c, flags && fs->rm(path) ? 200 : 404, "", "");
|
||||||
|
} else if (opts->enable_upload &&
|
||||||
|
(mg_strcasecmp(hm->method, mg_str("POST")) == 0 ||
|
||||||
|
mg_strcasecmp(hm->method, mg_str("PUT")) == 0)) {
|
||||||
|
// Small or already fully buffered body. mg_http_serve_upload() handles
|
||||||
|
// the same thing without buffering, for big uploads, from MG_EV_HTTP_HDRS
|
||||||
|
void *fd;
|
||||||
|
bool ok = false;
|
||||||
|
fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
|
||||||
|
if ((fd = fs->op(path, MG_FS_WRITE)) != NULL) {
|
||||||
|
ok = fs->wr(fd, hm->body.buf, hm->body.len) == hm->body.len;
|
||||||
|
fs->cl(fd);
|
||||||
|
}
|
||||||
|
mg_http_reply(c, ok ? 200 : 500, "", "");
|
||||||
} else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) {
|
} else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) {
|
||||||
mg_http_serve_ssi(c, opts->root_dir, path);
|
mg_http_serve_ssi(c, opts->root_dir, path);
|
||||||
} else {
|
} else {
|
||||||
@@ -903,6 +839,52 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mg_http_stream_body() callback for mg_http_serve_upload(). The file is
|
||||||
|
// opened by the caller before streaming starts, so this only writes and closes
|
||||||
|
static bool serve_upload_cb(struct mg_http_message *hm, struct mg_str *data,
|
||||||
|
void **p) {
|
||||||
|
struct mg_fd *fd = (struct mg_fd *) *p;
|
||||||
|
if (hm != NULL) return fd != NULL; // Start
|
||||||
|
if (data != NULL) { // Next chunk
|
||||||
|
return fd != NULL && fd->fs->wr(fd->fd, data->buf, data->len) == data->len;
|
||||||
|
}
|
||||||
|
if (fd != NULL) mg_fs_close(fd); // End
|
||||||
|
*p = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts a streaming upload for a big POST/PUT body, so it never sits fully
|
||||||
|
// buffered in memory. Companion to mg_http_serve_dir(): call this from
|
||||||
|
// MG_EV_HTTP_HDRS, and keep calling mg_http_serve_dir() from MG_EV_HTTP_MSG
|
||||||
|
// as before - it handles uploads too, for bodies this function skips.
|
||||||
|
// Does nothing unless opts->enable_upload is set, the method is POST or PUT,
|
||||||
|
// and Content-Length is known and >= 2 * MG_IO_SIZE.
|
||||||
|
void mg_http_serve_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
|
const struct mg_http_serve_opts *opts) {
|
||||||
|
char path[MG_PATH_MAX];
|
||||||
|
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
||||||
|
if (!opts->enable_upload) {
|
||||||
|
// Not enabled, nothing to do
|
||||||
|
} else if (mg_strcasecmp(hm->method, mg_str("POST")) != 0 &&
|
||||||
|
mg_strcasecmp(hm->method, mg_str("PUT")) != 0) {
|
||||||
|
// Not an upload request, nothing to do
|
||||||
|
} else if (hm->body.len == (size_t) ~0 || hm->body.len < 2 * MG_IO_SIZE) {
|
||||||
|
// Unknown length, or small enough for mg_http_serve_dir() to buffer it
|
||||||
|
} else if (!uri2path(opts->root_dir, hm->uri, path, sizeof(path))) {
|
||||||
|
MG_ERROR(("Invalid upload path: %.*s", (int) hm->uri.len, hm->uri.buf));
|
||||||
|
} else if (fs->st(path, NULL, NULL) & MG_FS_DIR) {
|
||||||
|
// Target is a directory, let mg_http_serve_dir() list it
|
||||||
|
} else {
|
||||||
|
struct mg_fd *fd;
|
||||||
|
fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
|
||||||
|
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||||
|
MG_ERROR(("Cannot open %s for upload", path));
|
||||||
|
} else {
|
||||||
|
mg_http_stream_body(c, hm, serve_upload_cb, fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool mg_is_url_safe(int c) {
|
static bool mg_is_url_safe(int c) {
|
||||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
||||||
(c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~';
|
(c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~';
|
||||||
@@ -1025,139 +1007,108 @@ int mg_http_status(const struct mg_http_message *hm) {
|
|||||||
return atoi(hm->uri.buf);
|
return atoi(hm->uri.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mg_upload_priv {
|
// State of a streamed request or response body, kept in c->data. The callback
|
||||||
size_t expected;
|
// is kept in c->fn_data, which is free because we own the connection
|
||||||
size_t received;
|
struct mg_upload_state {
|
||||||
struct mg_fd *fd; // non-NULL: file upload; NULL: OTA
|
size_t expected; // Number of body bytes to receive
|
||||||
void (*fn)(struct mg_connection *, const char *);
|
size_t received; // Number of body bytes received so far
|
||||||
|
void *user_data; // Passed to cb, free for cb to use
|
||||||
|
bool failed; // Set when cb returned false
|
||||||
|
bool finished; // End callback has been called
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mg_upload_handler(struct mg_connection *c, int ev, void *ev_data) {
|
// Event handler installed by mg_http_stream_body(). Feeds the body to the
|
||||||
struct mg_upload_priv *p = (struct mg_upload_priv *) c->data;
|
// callback, then replies (servers only). It does not restore the previous
|
||||||
if (p->fn == NULL) return;
|
// handler: the connection is closed after the reply.
|
||||||
if (ev == MG_EV_READ && c->recv.len > 0) {
|
static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
size_t alignment = 512;
|
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||||
size_t left = p->expected > p->received ? p->expected - p->received : 0;
|
bool (*fn)(struct mg_http_message *, struct mg_str *, void **) =
|
||||||
size_t aligned = c->recv.len < left ? MG_ROUND_DOWN(c->recv.len, alignment)
|
(bool (*)(struct mg_http_message *, struct mg_str *, void **))
|
||||||
: left;
|
(ptrdiff_t) c->fn_data;
|
||||||
bool ok = true;
|
|
||||||
if (aligned > 0) {
|
|
||||||
if (p->fd != NULL) {
|
|
||||||
ok = p->fd->fs->wr(p->fd->fd, c->recv.buf, aligned) == aligned;
|
|
||||||
} else {
|
|
||||||
ok = mg_ota_write(c->recv.buf, aligned);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p->received += aligned;
|
|
||||||
mg_iobuf_del(&c->recv, 0, aligned);
|
|
||||||
if (!ok) {
|
|
||||||
if (p->fd != NULL) { mg_fs_close(p->fd); p->fd = NULL; }
|
|
||||||
else mg_ota_end();
|
|
||||||
p->fn(c, "write error");
|
|
||||||
mg_free(c->pfn_data); c->pfn_data = NULL; p->fn = NULL;
|
|
||||||
} else if (p->received >= p->expected) {
|
|
||||||
const char *errmsg = NULL;
|
|
||||||
if (p->fd != NULL) { mg_fs_close(p->fd); p->fd = NULL; }
|
|
||||||
else if (!mg_ota_end()) errmsg = "OTA finalize failed";
|
|
||||||
p->fn(c, errmsg);
|
|
||||||
mg_free(c->pfn_data); c->pfn_data = NULL; p->fn = NULL;
|
|
||||||
}
|
|
||||||
} else if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) {
|
|
||||||
if (p->fd != NULL) { mg_fs_close(p->fd); p->fd = NULL; }
|
|
||||||
else mg_ota_end();
|
|
||||||
p->fn(c, ev == MG_EV_ERROR ? (const char *) ev_data : "connection closed");
|
|
||||||
mg_free(c->pfn_data); c->pfn_data = NULL; p->fn = NULL;
|
|
||||||
}
|
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
|
if (ev == MG_EV_CLOSE || ev == MG_EV_ERROR) {
|
||||||
|
if (!us->finished) {
|
||||||
|
// Connection dropped mid-upload. Let cb clean up, e.g. call mg_ota_end()
|
||||||
|
us->finished = true; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||||
|
fn(NULL, NULL, &us->user_data);
|
||||||
}
|
}
|
||||||
|
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
|
||||||
static void mg_upload_default_cb(struct mg_connection *c, const char *status) {
|
if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
|
||||||
MG_INFO(("%lu %s", c->id, status ? status : "ok"));
|
|
||||||
mg_http_reply(c, status ? 500 : 200, "", "%s\n", status ? status : "ok");
|
|
||||||
}
|
|
||||||
|
|
||||||
void mg_http_start_upload(struct mg_connection *c, struct mg_http_message *hm,
|
|
||||||
struct mg_str name, struct mg_str dir,
|
|
||||||
struct mg_fs *fs,
|
|
||||||
void (*fn)(struct mg_connection *, const char *)) {
|
|
||||||
struct mg_upload_priv *p = (struct mg_upload_priv *) c->data;
|
|
||||||
char path[MG_PATH_MAX];
|
|
||||||
struct mg_fd *fd;
|
|
||||||
if (fn == NULL) fn = mg_upload_default_cb;
|
|
||||||
if (sizeof(*p) > sizeof(c->data)) { fn(c, "data too small"); return; }
|
|
||||||
if (!mg_path_is_sane(name)) { fn(c, "bad name"); return; }
|
|
||||||
mg_snprintf(path, sizeof(path), "%.*s%c%.*s", (int) dir.len, dir.buf,
|
|
||||||
MG_DIRSEP, (int) name.len, name.buf);
|
|
||||||
fd = mg_fs_open(fs, path, MG_FS_WRITE);
|
|
||||||
if (fd == NULL) { fn(c, "open failed"); return; }
|
|
||||||
p->expected = hm->body.len;
|
|
||||||
p->received = 0;
|
|
||||||
p->fd = fd;
|
|
||||||
p->fn = fn;
|
|
||||||
c->fn = mg_upload_handler;
|
|
||||||
c->pfn_data = strdup(path);
|
|
||||||
c->pfn = NULL;
|
|
||||||
mg_iobuf_del(&c->recv, 0, hm->head.len);
|
|
||||||
mg_call(c, MG_EV_READ, &c->recv.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mg_http_start_ota(struct mg_connection *c, struct mg_http_message *hm,
|
|
||||||
void (*fn)(struct mg_connection *, const char *)) {
|
|
||||||
struct mg_upload_priv *p = (struct mg_upload_priv *) c->data;
|
|
||||||
if (fn == NULL) fn = mg_upload_default_cb;
|
|
||||||
if (sizeof(*p) > sizeof(c->data)) { fn(c, "data too small"); return; }
|
|
||||||
if (!mg_ota_begin(hm->body.len)) { fn(c, "ota begin failed"); return; }
|
|
||||||
p->expected = hm->body.len;
|
|
||||||
p->received = 0;
|
|
||||||
p->fd = NULL;
|
|
||||||
p->fn = fn;
|
|
||||||
c->fn = mg_upload_handler;
|
|
||||||
c->pfn = NULL;
|
|
||||||
mg_iobuf_del(&c->recv, 0, hm->head.len);
|
|
||||||
mg_call(c, MG_EV_READ, &c->recv.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mg_http_stream_body(struct mg_connection *c, int ev, void *ev_data,
|
|
||||||
struct mg_str uri_pattern,
|
|
||||||
void (*cb)(struct mg_http_message *, struct mg_str *,
|
|
||||||
void **user_data)) {
|
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
|
||||||
|
|
||||||
// Catch upload requests early, without buffering whole body
|
|
||||||
// When we receive MG_EV_HTTP_HDRS event, that means we've received all
|
|
||||||
// HTTP headers but not necessarily full HTTP body
|
|
||||||
if (ev == MG_EV_HTTP_HDRS &&
|
|
||||||
(mg_strcmp(hm->method, mg_str("POST")) == 0 ||
|
|
||||||
mg_strcmp(hm->method, mg_str("PUT")) == 0) &&
|
|
||||||
hm->body.len != (size_t) ~0 && mg_match(hm->uri, uri_pattern, NULL)) {
|
|
||||||
c->pfn = NULL; // Silence HTTP protocol handler, we'll use MG_EV_READ
|
|
||||||
c->pfn_data = (void *) (ptrdiff_t) hm->body.len; // Record expected len
|
|
||||||
cb(hm, NULL, &c->fn_data); // Call handler before deleting headers
|
|
||||||
if (mg_http_get_header(hm, "Expect") != NULL) {
|
|
||||||
mg_http_reply(c, 100, NULL, ""); // If curl expects, we continue
|
|
||||||
}
|
|
||||||
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->pfn == NULL && ev != MG_EV_OPEN) {
|
|
||||||
if (c->pfn_data != NULL && c->recv.len > 0) {
|
|
||||||
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA
|
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA
|
||||||
size_t left = (size_t) (ptrdiff_t) c->pfn_data, r = c->recv.len;
|
size_t left = us->expected - us->received, r = c->recv.len;
|
||||||
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left;
|
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left;
|
||||||
struct mg_str data = mg_str_n((char *) c->recv.buf, aligned);
|
struct mg_str data = mg_str_n((char *) c->recv.buf, aligned);
|
||||||
if (data.len) cb(NULL, &data, &c->fn_data);
|
if (data.len > 0 && !us->failed) {
|
||||||
c->pfn_data = (void *) (ptrdiff_t) (left - aligned);
|
us->failed = !fn(NULL, &data, &us->user_data);
|
||||||
c->recv.len -= aligned;
|
}
|
||||||
|
us->received += aligned;
|
||||||
|
mg_iobuf_del(&c->recv, 0, aligned); // Shift the unprocessed tail
|
||||||
|
}
|
||||||
|
if (!us->finished && (us->failed || us->received >= us->expected)) {
|
||||||
|
// Finished upload. Always call cb, even after error, so it can clean up
|
||||||
|
us->finished = true;
|
||||||
|
us->failed |= !fn(NULL, NULL, &us->user_data);
|
||||||
|
if (c->is_client && us->failed) {
|
||||||
|
mg_error(c, "Body streaming failed");
|
||||||
|
} else if (!c->is_client) {
|
||||||
|
mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
|
||||||
|
us->received, us->failed ? "fail" : "ok");
|
||||||
}
|
}
|
||||||
if (c->pfn_data == NULL && c->is_draining == 0) {
|
|
||||||
// Finished upload
|
|
||||||
mg_http_reply(c, 200, NULL, "ok\n");
|
|
||||||
c->is_draining = 1;
|
c->is_draining = 1;
|
||||||
cb(NULL, NULL, &c->fn_data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
|
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
||||||
|
void **),
|
||||||
|
void *user_data) {
|
||||||
|
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||||
|
int code = 0;
|
||||||
|
size_t head_len = hm->head.len;
|
||||||
|
|
||||||
|
// Catch upload requests early, without buffering whole body. Called on the
|
||||||
|
// MG_EV_HTTP_HDRS event: we've received all HTTP headers but not necessarily
|
||||||
|
// the full HTTP body
|
||||||
|
if (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||||
|
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||||
|
code = 405;
|
||||||
|
} else if (mg_http_get_header(hm, "Transfer-Encoding") != NULL) {
|
||||||
|
code = 400;
|
||||||
|
} else if (hm->body.len == (size_t) ~0 ||
|
||||||
|
(!c->is_client && mg_http_get_header(hm, "Content-Length") == NULL)) {
|
||||||
|
code = 411;
|
||||||
|
} else if (sizeof(*us) > sizeof(c->data)) {
|
||||||
|
code = 500;
|
||||||
|
}
|
||||||
|
c->pfn = NULL; // Take ownership even when validation fails
|
||||||
|
if (code != 0) {
|
||||||
|
c->fn = NULL;
|
||||||
|
c->fn_data = NULL;
|
||||||
|
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||||
|
cb(NULL, NULL, &user_data); // Release resources even if start was skipped
|
||||||
|
if (c->is_client) {
|
||||||
|
mg_error(c, "%s", mg_http_status_code_str(code));
|
||||||
|
} else {
|
||||||
|
mg_http_reply(c, code, code == 405 ? "Allow: POST, PUT\r\n" : NULL, "");
|
||||||
|
}
|
||||||
|
c->is_draining = 1;
|
||||||
|
} else {
|
||||||
|
memset(us, 0, sizeof(*us));
|
||||||
|
us->expected = hm->body.len; // Store number of bytes we expect
|
||||||
|
us->user_data = user_data;
|
||||||
|
c->fn = mg_stream_handler;
|
||||||
|
c->fn_data = (void *) (ptrdiff_t) cb;
|
||||||
|
us->failed = !cb(hm, NULL, &us->user_data); // Call before deleting headers
|
||||||
|
if (!c->is_client && !us->failed && !us->finished &&
|
||||||
|
mg_http_get_header(hm, "Expect") != NULL) {
|
||||||
|
mg_http_reply(c, 100, NULL, ""); // If curl expects, we continue
|
||||||
|
}
|
||||||
|
mg_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
|
||||||
|
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_hex_digit(int c) {
|
static bool is_hex_digit(int c) {
|
||||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
|
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
|
||||||
(c >= 'A' && c <= 'F');
|
(c >= 'A' && c <= 'F');
|
||||||
@@ -1184,7 +1135,9 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
c->recv.len > 0)) { // see #2796
|
c->recv.len > 0)) { // see #2796
|
||||||
struct mg_http_message hm;
|
struct mg_http_message hm;
|
||||||
size_t ofs = 0; // Parsing offset
|
size_t ofs = 0; // Parsing offset
|
||||||
while (c->is_resp == 0 && ofs < c->recv.len) {
|
// c->pfn_data is the "response generation in progress" marker. While it
|
||||||
|
// is set, don't parse the next pipelined request, to keep responses in order
|
||||||
|
while (c->pfn_data == NULL && ofs < c->recv.len) {
|
||||||
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
|
||||||
@@ -1277,9 +1230,9 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
ofs += (size_t) n + hm.body.len;
|
ofs += (size_t) n + hm.body.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->is_accepted) c->is_resp = 1; // Start generating response
|
if (c->is_accepted) c->pfn_data = c; // Response started
|
||||||
mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp
|
mg_call(c, MG_EV_HTTP_MSG, &hm); // Handler sets pfn_data NULL when done
|
||||||
if (c->is_accepted && !c->is_resp) {
|
if (c->is_accepted && c->pfn_data == NULL) {
|
||||||
struct mg_str *cc = mg_http_get_header(&hm, "Connection");
|
struct mg_str *cc = mg_http_get_header(&hm, "Connection");
|
||||||
if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) {
|
if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) {
|
||||||
c->is_draining = 1; // honor "Connection: close"
|
c->is_draining = 1; // honor "Connection: close"
|
||||||
|
|||||||
+41
-31
@@ -32,6 +32,9 @@ struct mg_http_serve_opts {
|
|||||||
const char *mime_types; // Additional MIME types: "ext1=type1,ext2=type2". NULL for defaults only
|
const char *mime_types; // Additional MIME types: "ext1=type1,ext2=type2". NULL for defaults only
|
||||||
const char *page404; // Path to a custom 404 page, e.g. "/404.html". NULL for built-in
|
const char *page404; // Path to a custom 404 page, e.g. "/404.html". NULL for built-in
|
||||||
struct mg_fs *fs; // Filesystem to use. NULL defaults to POSIX
|
struct mg_fs *fs; // Filesystem to use. NULL defaults to POSIX
|
||||||
|
bool enable_delete; // Allow to DELETE files
|
||||||
|
bool enable_upload; // Allow to POST/PUT files
|
||||||
|
bool enable_dir_listing; // Enable directory listing
|
||||||
};
|
};
|
||||||
|
|
||||||
// A single part of a multipart/form-data body, filled by mg_http_next_multipart().
|
// A single part of a multipart/form-data body, filled by mg_http_next_multipart().
|
||||||
@@ -105,8 +108,11 @@ struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
|
|||||||
// mg_http_listen(), mg_http_serve_file(), mg_http_reply(), mg_match()
|
// mg_http_listen(), mg_http_serve_file(), mg_http_reply(), mg_match()
|
||||||
// Notes:
|
// Notes:
|
||||||
// Call from an MG_EV_HTTP_MSG handler. The uri in hm is mapped under
|
// Call from an MG_EV_HTTP_MSG handler. The uri in hm is mapped under
|
||||||
// opts->root_dir. Directory listing depends on MG_ENABLE_DIRLIST; SSI uses
|
// opts->root_dir. opts->enable_dir_listing enables directory listing; SSI
|
||||||
// opts->ssi_pattern when configured.
|
// uses opts->ssi_pattern when configured. opts->enable_delete enables
|
||||||
|
// DELETE; opts->enable_upload enables POST/PUT, writing the whole body to
|
||||||
|
// a file - see mg_http_serve_upload() to also stream big uploads without
|
||||||
|
// buffering.
|
||||||
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
||||||
const struct mg_http_serve_opts *);
|
const struct mg_http_serve_opts *);
|
||||||
|
|
||||||
@@ -114,6 +120,15 @@ void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
|||||||
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
|
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
|
||||||
const char *path, const struct mg_http_serve_opts *);
|
const char *path, const struct mg_http_serve_opts *);
|
||||||
|
|
||||||
|
// Streams a big POST/PUT upload straight to a file, instead of buffering the
|
||||||
|
// whole body. Optional companion to mg_http_serve_dir(): call this from
|
||||||
|
// MG_EV_HTTP_HDRS with the same opts, and keep calling mg_http_serve_dir()
|
||||||
|
// from MG_EV_HTTP_MSG as usual - it still handles uploads this function skips.
|
||||||
|
// Does nothing unless opts->enable_upload is set, the method is POST or PUT,
|
||||||
|
// and Content-Length is known and big enough.
|
||||||
|
void mg_http_serve_upload(struct mg_connection *, struct mg_http_message *hm,
|
||||||
|
const struct mg_http_serve_opts *);
|
||||||
|
|
||||||
// Sends a complete HTTP response with Content-Length.
|
// Sends a complete HTTP response with Content-Length.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
@@ -160,20 +175,6 @@ void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
|
|||||||
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
struct mg_fs *fs, const char *dir, size_t max_size);
|
struct mg_fs *fs, const char *dir, size_t max_size);
|
||||||
|
|
||||||
// Streams the raw request body into dir/name via fs. Non-blocking: installs an
|
|
||||||
// internal handler and returns immediately. fn is called with NULL on success
|
|
||||||
// or an error string on failure.
|
|
||||||
void mg_http_start_upload(struct mg_connection *c, struct mg_http_message *hm,
|
|
||||||
struct mg_str name, struct mg_str dir,
|
|
||||||
struct mg_fs *fs,
|
|
||||||
void (*fn)(struct mg_connection *, const char *));
|
|
||||||
|
|
||||||
// Starts an OTA firmware update from an HTTP POST upload.
|
|
||||||
// Calls mg_ota_begin/write/end internally; fn is called with NULL on success,
|
|
||||||
// or an error string on failure.
|
|
||||||
void mg_http_start_ota(struct mg_connection *c, struct mg_http_message *hm,
|
|
||||||
void (*fn)(struct mg_connection *, const char *));
|
|
||||||
|
|
||||||
// Sends a 401 Unauthorized response with a Basic Auth WWW-Authenticate challenge.
|
// Sends a 401 Unauthorized response with a Basic Auth WWW-Authenticate challenge.
|
||||||
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
||||||
|
|
||||||
@@ -192,7 +193,7 @@ struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
|
|||||||
// Full examples:
|
// Full examples:
|
||||||
// tutorials/http/file-upload-html-form, tutorials/http/http-server
|
// tutorials/http/file-upload-html-form, tutorials/http/http-server
|
||||||
// Related APIs:
|
// Related APIs:
|
||||||
// mg_http_listen(), mg_http_start_upload()
|
// mg_http_listen(), mg_http_stream_body()
|
||||||
// Notes:
|
// Notes:
|
||||||
// Call from an MG_EV_HTTP_MSG handler after the full request body is
|
// Call from an MG_EV_HTTP_MSG handler after the full request body is
|
||||||
// received. part.name, part.filename, and part.body are zero-copy slices into
|
// received. part.name, part.filename, and part.body are zero-copy slices into
|
||||||
@@ -203,24 +204,33 @@ size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
|
|||||||
int mg_http_status(const struct mg_http_message *hm);
|
int mg_http_status(const struct mg_http_message *hm);
|
||||||
|
|
||||||
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
||||||
// Call it from your connection handler on every event.
|
// Call it from an MG_EV_HTTP_HDRS handler, after matching the request, e.g.
|
||||||
|
// with mg_match(). Always takes over the connection. Invalid requests receive
|
||||||
|
// an HTTP error; client-side failures call mg_error(). Replies and connection
|
||||||
|
// draining are handled internally, including callback failures.
|
||||||
//
|
//
|
||||||
// Callback upload_fn(hm, data, user_data) is called:
|
// Callback upload_fn(hm, data, user_data) is called:
|
||||||
// - at start: hm is set, data is NULL. Open the file, set user_data.
|
// - at start: hm is set. Open the file, set *user_data.
|
||||||
// - per chunk: hm is NULL, data is set. Chunks are 512 bytes aligned
|
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
||||||
// except the last one.
|
// again after a failure.
|
||||||
// - at end: both NULL. Close the file.
|
// - at end: both NULL. Always called exactly once, including validation
|
||||||
// After the last chunk, "200 ok" is sent and the connection is drained.
|
// failures before start and connections that drop early. Release resources
|
||||||
|
// here. A truncated body looks like a complete one: validate before
|
||||||
|
// committing.
|
||||||
|
// The callback returns true on success, false on error. A failure stops
|
||||||
|
// streaming immediately and calls the end callback before closing.
|
||||||
// Example:
|
// Example:
|
||||||
// mg_http_stream_body(c, ev, ev_data, mg_str("/fs/*"), upload);
|
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL)) {
|
||||||
|
// mg_http_stream_body(c, hm, upload, NULL);
|
||||||
|
// }
|
||||||
// Full example:
|
// Full example:
|
||||||
// tutorials/http/file-upload-single-post
|
// tutorials/http/file-upload-single-post
|
||||||
// Related APIs:
|
// Related APIs:
|
||||||
// mg_http_start_upload(), mg_http_start_ota()
|
// mg_http_listen(), mg_http_next_multipart()
|
||||||
// Notes:
|
// Notes:
|
||||||
// Requires Content-Length, chunked bodies are not streamed. user_data is
|
// The existing event handler is no longer called. hm is invalid after this
|
||||||
// &c->fn_data. While streaming, c->pfn is NULL
|
// call; c->data and c->fn_data are reserved for streaming state.
|
||||||
void mg_http_stream_body(struct mg_connection *c, int ev, void *ev_data,
|
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
struct mg_str uri_pattern,
|
bool (*upload_fn)(struct mg_http_message *,
|
||||||
void (*upload_fn)(struct mg_http_message *,
|
struct mg_str *, void **user_data),
|
||||||
struct mg_str *, void **user_data));
|
void *user_data);
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
#include "tls_uecc.h"
|
#include "tls_p256.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static size_t jwt_split(struct mg_str jwt, struct mg_str *h, struct mg_str *p,
|
static size_t jwt_split(struct mg_str jwt, struct mg_str *h, struct mg_str *p,
|
||||||
@@ -95,39 +95,14 @@ size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
typedef struct {
|
|
||||||
MG_UECC_HashContext uECC;
|
|
||||||
mg_sha256_ctx sha256;
|
|
||||||
} jwt_hash_ctx;
|
|
||||||
|
|
||||||
static void jwt_hash_init(const MG_UECC_HashContext *ctx) {
|
|
||||||
jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
|
|
||||||
mg_sha256_init(&c->sha256);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jwt_hash_update(const MG_UECC_HashContext *ctx, const uint8_t *msg,
|
|
||||||
unsigned len) {
|
|
||||||
jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
|
|
||||||
mg_sha256_update(&c->sha256, msg, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jwt_hash_finish(const MG_UECC_HashContext *ctx, uint8_t *hash) {
|
|
||||||
jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
|
|
||||||
mg_sha256_final(hash, &c->sha256);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
|
size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
|
||||||
size_t len) {
|
size_t len) {
|
||||||
uint8_t hash[32], sig[64], tmp[2 * 32 + 64];
|
uint8_t hash[32], sig[64];
|
||||||
jwt_hash_ctx ctx = {
|
|
||||||
{jwt_hash_init, jwt_hash_update, jwt_hash_finish, 64, 32, tmp},
|
|
||||||
{{0}, 0, 0, {0}}};
|
|
||||||
size_t n = jwt_header_payload("ES256", opts, buf, len);
|
size_t n = jwt_header_payload("ES256", opts, buf, len);
|
||||||
size_t m;
|
size_t m;
|
||||||
if (n == 0 || n + 1 >= len) return 0;
|
if (n == 0 || n + 1 >= len) return 0;
|
||||||
mg_sha256(hash, (uint8_t *) buf, n);
|
mg_sha256(hash, (uint8_t *) buf, n);
|
||||||
if (mg_uecc_sign_deterministic(opts->private_key, hash, sizeof(hash),
|
if (mg_p256_sign(opts->private_key, hash, sizeof(hash), sig) != 1)
|
||||||
&ctx.uECC, sig, mg_uecc_secp256r1()) != 1)
|
|
||||||
return 0;
|
return 0;
|
||||||
buf[n++] = '.';
|
buf[n++] = '.';
|
||||||
m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n);
|
m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n);
|
||||||
@@ -143,8 +118,7 @@ size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
|||||||
if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64)
|
if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64)
|
||||||
return 0;
|
return 0;
|
||||||
mg_sha256(hash, (uint8_t *) jwt.buf, n);
|
mg_sha256(hash, (uint8_t *) jwt.buf, n);
|
||||||
if (mg_uecc_verify(opts->public_key, hash, sizeof(hash), sig,
|
if (mg_p256_verify(opts->public_key, hash, sizeof(hash), sig) != 1)
|
||||||
mg_uecc_secp256r1()) != 1)
|
|
||||||
return 0;
|
return 0;
|
||||||
return mg_base64url_decode(p.buf, p.len, buf, len);
|
return mg_base64url_decode(p.buf, p.len, buf, len);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#include "l2.h"
|
||||||
|
#include "net_builtin.h"
|
||||||
|
|
||||||
|
#if MG_ENABLE_TCPIP
|
||||||
|
|
||||||
|
// Radio frame: 802.15.4 MAC header, 15 bytes: data frame, PAN ID compression,
|
||||||
|
// broadcast destination, our EUI-64 made from MAC as source. Then 2 bytes:
|
||||||
|
// tag, fragment index << 4 | last fragment index. Then data. 125 bytes max,
|
||||||
|
// 127 minus FCS
|
||||||
|
#define MGI_802154_HDR 17
|
||||||
|
#define MGI_802154_DATA (125 - MGI_802154_HDR)
|
||||||
|
|
||||||
|
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||||
|
size_t (*send_fn)(const uint8_t *, size_t,
|
||||||
|
struct mg_tcpip_if *)) {
|
||||||
|
struct mg_l2_802154_data *d =
|
||||||
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
|
const uint8_t *p = (const uint8_t *) buf, *m = ifp->mac;
|
||||||
|
uint8_t f[MGI_802154_HDR + MGI_802154_DATA];
|
||||||
|
size_t i, n, last = (len - 1) / MGI_802154_DATA;
|
||||||
|
if (len == 0 || last > 15) return 0;
|
||||||
|
d->tag++;
|
||||||
|
for (i = 0; i <= last; i++) {
|
||||||
|
n = i == last ? len - i * MGI_802154_DATA : MGI_802154_DATA;
|
||||||
|
f[0] = 0x41, f[1] = 0xd8, f[2] = d->seq++; // 2006 frame, see above
|
||||||
|
f[3] = (uint8_t) d->pan_id, f[4] = (uint8_t) (d->pan_id >> 8);
|
||||||
|
f[5] = f[6] = 0xff;
|
||||||
|
f[7] = m[5], f[8] = m[4], f[9] = m[3], f[10] = 0xfe, f[11] = 0xff;
|
||||||
|
f[12] = m[2], f[13] = m[1], f[14] = m[0]; // Little endian EUI-64
|
||||||
|
f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
|
||||||
|
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
||||||
|
if (send_fn(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
|
||||||
|
void *buf, size_t len) {
|
||||||
|
struct mg_l2_802154_data *d =
|
||||||
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
|
size_t idx, last;
|
||||||
|
if (n <= MGI_802154_HDR || f[0] != 0x41 || f[1] != 0xd8 ||
|
||||||
|
f[3] != (uint8_t) d->pan_id || f[4] != (uint8_t) (d->pan_id >> 8))
|
||||||
|
return 0; // Not ours
|
||||||
|
idx = f[16] >> 4, last = f[16] & 15;
|
||||||
|
if (idx == 0) d->rxtag = f[15], d->rxnext = 0, d->rxlen = 0;
|
||||||
|
if (idx == 0) memcpy(d->rxsrc, f + 7, sizeof(d->rxsrc));
|
||||||
|
n -= MGI_802154_HDR;
|
||||||
|
if (idx != d->rxnext || idx > last || f[15] != d->rxtag ||
|
||||||
|
memcmp(d->rxsrc, f + 7, sizeof(d->rxsrc)) != 0 ||
|
||||||
|
d->rxlen + n > sizeof(d->rxbuf))
|
||||||
|
return 0; // Missed a fragment, or from another sender
|
||||||
|
memcpy(d->rxbuf + d->rxlen, f + MGI_802154_HDR, n);
|
||||||
|
d->rxlen = (uint16_t) (d->rxlen + n), d->rxnext++;
|
||||||
|
if (idx < last || d->rxlen > len) return 0;
|
||||||
|
memcpy(buf, d->rxbuf, d->rxlen);
|
||||||
|
return d->rxlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -3,19 +3,18 @@
|
|||||||
|
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "lfs.h"
|
||||||
|
|
||||||
#if MG_ENABLE_LFS
|
#if MG_ENABLE_LFS
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <littlefs/lfs.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifndef LFS_USE_RAM
|
#ifndef LFS_USE_RAM
|
||||||
#define LFS_USE_RAM 0 // If 1, use RAM FS. If 0, use flash
|
#define LFS_USE_RAM 0 // If 1, use RAM FS. If 0, use flash
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_LFS_SIZE
|
||||||
|
#define MG_LFS_SIZE (64 * 1024)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MG_LFS_BUF_SIZE
|
#ifndef MG_LFS_BUF_SIZE
|
||||||
#define MG_LFS_BUF_SIZE 64 // Buffer size used for reads, writes, and cache
|
#define MG_LFS_BUF_SIZE 64 // Buffer size used for reads, writes, and cache
|
||||||
#endif
|
#endif
|
||||||
@@ -28,19 +27,6 @@
|
|||||||
#define MG_LFS_BLOCK_SIZE 8192
|
#define MG_LFS_BLOCK_SIZE 8192
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DT_DIR
|
|
||||||
#define DT_DIR 4
|
|
||||||
#endif
|
|
||||||
#ifndef DT_REG
|
|
||||||
#define DT_REG 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct mg_lfs_fd DIR;
|
|
||||||
struct dirent {
|
|
||||||
char d_name[LFS_NAME_MAX + 1];
|
|
||||||
unsigned char d_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mg_lfs_fd {
|
struct mg_lfs_fd {
|
||||||
struct mg_lfs_fd *next;
|
struct mg_lfs_fd *next;
|
||||||
bool isopen;
|
bool isopen;
|
||||||
@@ -52,7 +38,6 @@ struct mg_lfs_fd {
|
|||||||
static lfs_t s_lfs;
|
static lfs_t s_lfs;
|
||||||
static struct mg_lfs_fd *s_fds;
|
static struct mg_lfs_fd *s_fds;
|
||||||
static uint8_t *s_fs;
|
static uint8_t *s_fs;
|
||||||
static int s_next_fd = MG_LFS_FD_BASE;
|
|
||||||
static bool s_lfs_ready;
|
static bool s_lfs_ready;
|
||||||
#if LFS_USE_RAM
|
#if LFS_USE_RAM
|
||||||
static uint8_t *s_ram_fs;
|
static uint8_t *s_ram_fs;
|
||||||
@@ -110,29 +95,50 @@ static int lfs_driver_sync(const struct lfs_config *cfg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool mg_lfs_init(size_t size) {
|
bool mg_lfs_init(size_t size) {
|
||||||
int result = 0;
|
//bool ok = false;
|
||||||
|
MG_DEBUG(("size: %zu", size));
|
||||||
if (s_lfs_ready) return true;
|
if (s_lfs_ready) return true;
|
||||||
if (size == 0) return false;
|
if (size == 0) {
|
||||||
|
MG_ERROR(("invalidsize = 0"));
|
||||||
|
} else {
|
||||||
#if LFS_USE_RAM
|
#if LFS_USE_RAM
|
||||||
s_cfg.block_size = MG_LFS_BLOCK_SIZE;
|
s_cfg.block_size = MG_LFS_BLOCK_SIZE;
|
||||||
if (s_ram_fs == NULL) s_ram_fs = (uint8_t *) mg_calloc(1, size);
|
if (s_ram_fs == NULL) s_ram_fs = (uint8_t *) mg_calloc(1, size);
|
||||||
s_fs = s_ram_fs;
|
s_fs = s_ram_fs;
|
||||||
#else
|
#else
|
||||||
if (mg_flash == NULL || mg_flash->secsz == 0 || mg_flash->size < size) return false;
|
{
|
||||||
if (mg_flash->write_fn == NULL) return false;
|
// Some flash attributes may be initialised on ota_begin
|
||||||
|
int lev = mg_log_level;
|
||||||
|
mg_log_level = MG_LL_NONE;
|
||||||
|
mg_ota_begin(1);
|
||||||
|
mg_ota_end();
|
||||||
|
mg_log_set(lev);
|
||||||
|
}
|
||||||
|
if (mg_flash == NULL || mg_flash->secsz == 0 || mg_flash->size < size) {
|
||||||
|
MG_ERROR(("mg_flash %p secsz %zu flash_size %zu < size %zu", mg_flash, mg_flash->secsz, mg_flash->size, size));
|
||||||
|
} else if (mg_flash->write_fn == NULL) {
|
||||||
|
MG_ERROR(("flash write_fn == NULL"));
|
||||||
|
} else {
|
||||||
s_cfg.block_size = mg_flash->secsz;
|
s_cfg.block_size = mg_flash->secsz;
|
||||||
s_fs = (uint8_t *) mg_flash->start + mg_flash->size - size;
|
s_fs = (uint8_t *) mg_flash->start + mg_flash->size - size;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (s_fs == NULL || s_cfg.block_size == 0 || size % s_cfg.block_size != 0) {
|
if (s_fs == NULL || s_cfg.block_size == 0 || size % s_cfg.block_size != 0) {
|
||||||
return false;
|
MG_ERROR(("s_fs %p blk_size %zu size %zu", s_fs, s_cfg.block_size));
|
||||||
}
|
} else {
|
||||||
s_cfg.block_count = (lfs_size_t) (size / s_cfg.block_size);
|
s_cfg.block_count = (lfs_size_t) (size / s_cfg.block_size);
|
||||||
if (s_cfg.block_count == 0) return false;
|
if (s_cfg.block_count == 0) {
|
||||||
|
MG_ERROR(("blk_count == 0"));
|
||||||
|
} else {
|
||||||
|
if (lfs_mount(&s_lfs, &s_cfg) != 0) lfs_format(&s_lfs, &s_cfg);
|
||||||
if (lfs_mount(&s_lfs, &s_cfg) != 0) {
|
if (lfs_mount(&s_lfs, &s_cfg) != 0) {
|
||||||
lfs_format(&s_lfs, &s_cfg);
|
MG_ERROR(("lfs_mount"));
|
||||||
if (lfs_mount(&s_lfs, &s_cfg) != 0) result = -1;
|
} else {
|
||||||
|
s_lfs_ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s_lfs_ready = result == 0;
|
|
||||||
return s_lfs_ready;
|
return s_lfs_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,16 +151,12 @@ static struct mg_lfs_fd *find_fd(int fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct mg_lfs_fd *open_fd(void) {
|
static struct mg_lfs_fd *open_fd(void) {
|
||||||
struct mg_lfs_fd *f = NULL;
|
struct mg_lfs_fd *f = NULL, *p = s_fds;
|
||||||
if (s_lfs_ready) {
|
if (s_lfs_ready == false) mg_lfs_init(MG_LFS_SIZE);
|
||||||
f = (struct mg_lfs_fd *) mg_calloc(1, sizeof(*f));
|
if (s_lfs_ready && (f = (struct mg_lfs_fd *) mg_calloc(1, sizeof(*f)))) {
|
||||||
if (f != NULL) {
|
f->isopen = true, f->fd = MG_LFS_FD_BASE, f->next = s_fds;
|
||||||
f->isopen = true;
|
while (p != NULL) p = p->fd == f->fd ? (f->fd++, s_fds) : p->next;
|
||||||
f->fd = s_next_fd++;
|
|
||||||
f->next = s_fds;
|
|
||||||
s_fds = f;
|
s_fds = f;
|
||||||
if (s_next_fd < MG_LFS_FD_BASE) s_next_fd = MG_LFS_FD_BASE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@@ -173,6 +175,7 @@ int _open(const char *path, int flags, mode_t mode) {
|
|||||||
int err, lfs_flags = 0, fd = -1;
|
int err, lfs_flags = 0, fd = -1;
|
||||||
struct mg_lfs_fd *f = open_fd();
|
struct mg_lfs_fd *f = open_fd();
|
||||||
(void) mode;
|
(void) mode;
|
||||||
|
MG_DEBUG(("%s -> %p, lfs_ready %d", path, f, s_lfs_ready));
|
||||||
if (f == NULL) return -1;
|
if (f == NULL) return -1;
|
||||||
fd = f->fd;
|
fd = f->fd;
|
||||||
if ((flags & 3) == O_RDONLY) lfs_flags |= LFS_O_RDONLY;
|
if ((flags & 3) == O_RDONLY) lfs_flags |= LFS_O_RDONLY;
|
||||||
@@ -214,11 +217,21 @@ int _lseek(int fd, int offset, int whence) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int _rename(const char *oldname, const char *newname) {
|
int _rename(const char *oldname, const char *newname) {
|
||||||
|
if (s_lfs_ready == false) mg_lfs_init(MG_LFS_SIZE);
|
||||||
return s_lfs_ready ? lfs_rename(&s_lfs, oldname, newname) : -1;
|
return s_lfs_ready ? lfs_rename(&s_lfs, oldname, newname) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _unlink_r(void *r, const char *a) {
|
// Some libc rename() implementations have no atomic rename syscall and emulate
|
||||||
(void) r;
|
// it as link() + unlink() instead of calling _rename() directly. LittleFS has
|
||||||
|
// no real hard links, but forwarding to _rename() gives the same end result
|
||||||
|
// (oldname becomes newname), which is all mg_file_write()'s atomic-write
|
||||||
|
// pattern (write to a temp file, then rename it into place) needs
|
||||||
|
int _link(const char *oldname, const char *newname) {
|
||||||
|
return _rename(oldname, newname);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _unlink(const char *a) {
|
||||||
|
if (s_lfs_ready == false) mg_lfs_init(MG_LFS_SIZE);
|
||||||
return s_lfs_ready ? lfs_remove(&s_lfs, a) : -1;
|
return s_lfs_ready ? lfs_remove(&s_lfs, a) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +240,7 @@ DIR *opendir(const char *name) {
|
|||||||
if (f == NULL) return NULL;
|
if (f == NULL) return NULL;
|
||||||
if (lfs_dir_open(&s_lfs, &f->dir, name) != 0) {
|
if (lfs_dir_open(&s_lfs, &f->dir, name) != 0) {
|
||||||
close_fd(f);
|
close_fd(f);
|
||||||
return NULL;
|
f = NULL;
|
||||||
}
|
}
|
||||||
return (DIR *) f;
|
return (DIR *) f;
|
||||||
}
|
}
|
||||||
@@ -254,7 +267,9 @@ struct dirent *readdir(DIR *dir) {
|
|||||||
|
|
||||||
int _stat(const char *path, struct stat *st) {
|
int _stat(const char *path, struct stat *st) {
|
||||||
struct lfs_info info;
|
struct lfs_info info;
|
||||||
|
if (s_lfs_ready == false) mg_lfs_init(MG_LFS_SIZE);
|
||||||
if (!s_lfs_ready || lfs_stat(&s_lfs, path, &info) != 0) return -1;
|
if (!s_lfs_ready || lfs_stat(&s_lfs, path, &info) != 0) return -1;
|
||||||
|
memset(st, 0, sizeof(*st));
|
||||||
st->st_mode = info.type == LFS_TYPE_DIR ? S_IFDIR : S_IFREG;
|
st->st_mode = info.type == LFS_TYPE_DIR ? S_IFDIR : S_IFREG;
|
||||||
st->st_size = info.size;
|
st->st_size = info.size;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -268,7 +283,7 @@ int _fstat(int fd, struct stat *st) {
|
|||||||
|
|
||||||
int mkdir(const char *path, mode_t mode) {
|
int mkdir(const char *path, mode_t mode) {
|
||||||
(void) mode;
|
(void) mode;
|
||||||
|
if (s_lfs_ready == false) mg_lfs_init(MG_LFS_SIZE);
|
||||||
return s_lfs_ready ? lfs_mkdir(&s_lfs, path) : -1;
|
return s_lfs_ready ? lfs_mkdir(&s_lfs, path) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if MG_ENABLE_LFS
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <lfs.h>
|
||||||
|
|
||||||
|
#ifndef DT_DIR
|
||||||
|
#define DT_DIR 4
|
||||||
|
#endif
|
||||||
|
#ifndef DT_REG
|
||||||
|
#define DT_REG 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct dirent {
|
||||||
|
char d_name[LFS_NAME_MAX + 1];
|
||||||
|
unsigned char d_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct mg_lfs_fd DIR;
|
||||||
|
#endif
|
||||||
+35
-18
@@ -55,7 +55,10 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
// c->is_hexdumping = true;
|
// c->is_hexdumping = true;
|
||||||
if (mg_url_is_ssl(MG_MDASH_URL)) {
|
if (mg_url_is_ssl(MG_MDASH_URL)) {
|
||||||
struct mg_str host = mg_url_host(MG_MDASH_URL);
|
struct mg_str host = mg_url_host(MG_MDASH_URL);
|
||||||
struct mg_tls_opts opts = {.ca = mg_str(mg_mdash_ca_pem), .name = host};
|
struct mg_tls_opts opts;
|
||||||
|
memset(&opts, 0, sizeof(struct mg_tls_opts));
|
||||||
|
opts.ca = mg_str(mg_mdash_ca_pem);
|
||||||
|
opts.name = host;
|
||||||
mg_tls_init(c, &opts);
|
mg_tls_init(c, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,16 +68,18 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
// mDashTriggerEvent(s_mDashState, NULL);
|
// mDashTriggerEvent(s_mDashState, NULL);
|
||||||
} else if (ev == MG_EV_WS_MSG) {
|
} else if (ev == MG_EV_WS_MSG) {
|
||||||
struct mg_iobuf io = {0, 0, 0, 512};
|
struct mg_iobuf io = {0, 0, 0, 512};
|
||||||
struct mg_ws_message *wm = ev_data;
|
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
|
||||||
struct mg_str s = wm->data;
|
struct mg_str s = wm->data;
|
||||||
|
struct mg_rpc_req r;
|
||||||
while (s.len > 0 && isspace((uint8_t) s.buf[s.len - 1])) s.len--;
|
while (s.len > 0 && isspace((uint8_t) s.buf[s.len - 1])) s.len--;
|
||||||
MG_INFO(("RPC: %.*s", (int) s.len, s.buf));
|
MG_INFO(("RPC: %.*s", (int) s.len, s.buf));
|
||||||
struct mg_rpc_req r = {.head = &c->mgr->rpcs,
|
memset(&r, 0, sizeof(struct mg_rpc_req));
|
||||||
.rpc = NULL,
|
r.head = &c->mgr->rpcs;
|
||||||
.pfn = mg_pfn_iobuf,
|
r.rpc = NULL;
|
||||||
.pfn_data = &io,
|
r.pfn = mg_pfn_iobuf;
|
||||||
.req_data = NULL,
|
r.pfn_data = &io;
|
||||||
.frame = s};
|
r.req_data = NULL;
|
||||||
|
r.frame = s;
|
||||||
mg_rpc_process(&r);
|
mg_rpc_process(&r);
|
||||||
if (io.buf != NULL) {
|
if (io.buf != NULL) {
|
||||||
MG_INFO((" %s", io.buf));
|
MG_INFO((" %s", io.buf));
|
||||||
@@ -91,15 +96,13 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
||||||
struct mg_str blob = mg_health_get_blob();
|
const char *reset_reason = mg_health_reason_str(mg_health_reason());
|
||||||
if (blob.buf == NULL) blob.len = 0;
|
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:%M}", //
|
||||||
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:{%m:%m,%m:%llu,%m:%m}}",
|
MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), //
|
||||||
MG_ESC("fw_version"), MG_ESC(MG_MDASH_FIRMWARE_VERSION), MG_ESC("uptime"),
|
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||||
(uint64_t) (mg_millis() / 1000), MG_ESC("reboot_reason"),
|
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||||
MG_ESC(mg_health_reason_str(mg_health_reason())), MG_ESC("arch"), MG_ARCH,
|
MG_ESC("arch"), MG_ARCH, //
|
||||||
MG_ESC("health"), MG_ESC("encoding"), MG_ESC("base64"), MG_ESC("size"),
|
MG_ESC("crash"), mg_print_crash_record);
|
||||||
(uint64_t) blob.len, MG_ESC("data"), mg_print_base64, (int) blob.len,
|
|
||||||
(uint8_t *) (blob.buf == NULL ? "" : blob.buf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||||
@@ -153,14 +156,25 @@ void mg_mdash_init(struct mg_mgr *mgr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_mdash_poll(struct mg_mgr *mgr) {
|
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||||
|
#if MG_TLS == MG_TLS_NONE
|
||||||
|
(void) mgr, (void) mg_mdash_fn; // mdash needs wss://, nothing to do
|
||||||
|
#else
|
||||||
static uint64_t timer = 0;
|
static uint64_t timer = 0;
|
||||||
if (mg_mdash_conn == NULL && mg_timer_expired(&timer, 1000, mg_millis())) {
|
// No RPC handlers means mdash was never set up, or mg_mdash_free() ran.
|
||||||
|
// No key means mdash would reject us, so do not connect at all
|
||||||
|
if (mg_mdash_conn == NULL && mgr->rpcs != NULL && MG_MDASH_KEY[0] != '\0' &&
|
||||||
|
mg_timer_expired(&timer, 1000, mg_millis())) {
|
||||||
MG_INFO(("Connecting to %s", MG_MDASH_URL));
|
MG_INFO(("Connecting to %s", MG_MDASH_URL));
|
||||||
mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL,
|
mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL,
|
||||||
"Origin: %s\r\n"
|
"Origin: %s\r\n"
|
||||||
"Authorization: Bearer %s\r\n",
|
"Authorization: Bearer %s\r\n",
|
||||||
MG_MDASH_URL, MG_MDASH_KEY);
|
MG_MDASH_URL, MG_MDASH_KEY);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_mdash_free(struct mg_mgr *mgr) {
|
||||||
|
mg_rpc_del(&mgr->rpcs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -170,4 +184,7 @@ void mg_mdash_init(struct mg_mgr *mgr) {
|
|||||||
void mg_mdash_poll(struct mg_mgr *mgr) {
|
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||||
(void) mgr;
|
(void) mgr;
|
||||||
}
|
}
|
||||||
|
void mg_mdash_free(struct mg_mgr *mgr) {
|
||||||
|
(void) mgr;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
+1
-4
@@ -12,13 +12,10 @@
|
|||||||
#define MG_MDASH_KEY ""
|
#define MG_MDASH_KEY ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_MDASH_FIRMWARE_VERSION
|
|
||||||
#define MG_MDASH_FIRMWARE_VERSION "1.0.0"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_ENABLE_MDASH
|
#ifndef MG_ENABLE_MDASH
|
||||||
#define MG_ENABLE_MDASH 0
|
#define MG_ENABLE_MDASH 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void mg_mdash_init(struct mg_mgr *);
|
void mg_mdash_init(struct mg_mgr *);
|
||||||
void mg_mdash_poll(struct mg_mgr *);
|
void mg_mdash_poll(struct mg_mgr *);
|
||||||
|
void mg_mdash_free(struct mg_mgr *);
|
||||||
+10
@@ -438,6 +438,16 @@ int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
|
|||||||
case MQTT_CMD_CONNACK:
|
case MQTT_CMD_CONNACK:
|
||||||
if (end - p < 2) return MQTT_MALFORMED;
|
if (end - p < 2) return MQTT_MALFORMED;
|
||||||
m->ack = p[1];
|
m->ack = p[1];
|
||||||
|
p += 2;
|
||||||
|
if (version == 5) {
|
||||||
|
if (p >= end) return MQTT_MALFORMED;
|
||||||
|
len_len = (uint32_t) decode_varint(p, (size_t) (end - p), &tmp);
|
||||||
|
if (!len_len) return MQTT_MALFORMED;
|
||||||
|
m->props_size = (size_t) tmp;
|
||||||
|
m->props_start = (size_t) (p + len_len - buf);
|
||||||
|
p += len_len + m->props_size;
|
||||||
|
if (p > end) return MQTT_MALFORMED;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MQTT_CMD_PUBACK:
|
case MQTT_CMD_PUBACK:
|
||||||
case MQTT_CMD_PUBREC:
|
case MQTT_CMD_PUBREC:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "net.h"
|
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mdash.h"
|
#include "mdash.h"
|
||||||
|
#include "net.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
@@ -39,10 +39,10 @@ bool mg_dscp(struct mg_connection *c, uint8_t dscp) {
|
|||||||
int tos = dscp << 2, level = IPPROTO_IP, optname = IP_TOS;
|
int tos = dscp << 2, level = IPPROTO_IP, optname = IP_TOS;
|
||||||
c->dscp = (unsigned) (dscp & 63);
|
c->dscp = (unsigned) (dscp & 63);
|
||||||
#if MG_ENABLE_IPV6 && !MG_ENABLE_LWIP
|
#if MG_ENABLE_IPV6 && !MG_ENABLE_LWIP
|
||||||
if (c->loc.is_ip6)
|
if (c->loc.is_ip6) level = IPPROTO_IPV6, optname = IPV6_TCLASS;
|
||||||
level = IPPROTO_IPV6, optname = IPV6_TCLASS;
|
|
||||||
#endif
|
#endif
|
||||||
return setsockopt((MG_SOCKET_TYPE) (size_t) c->fd, level, optname, (char *) &tos, sizeof(tos)) == 0;
|
return setsockopt((MG_SOCKET_TYPE) (size_t) c->fd, level, optname,
|
||||||
|
(char *) &tos, sizeof(tos)) == 0;
|
||||||
#endif
|
#endif
|
||||||
#elif MG_ENABLE_TCPIP
|
#elif MG_ENABLE_TCPIP
|
||||||
c->dscp = (unsigned) (dscp & 63);
|
c->dscp = (unsigned) (dscp & 63);
|
||||||
@@ -299,6 +299,9 @@ void mg_mgr_free(struct mg_mgr *mgr) {
|
|||||||
while (t != NULL) tmp = t->next, mg_free(t), t = tmp;
|
while (t != NULL) tmp = t->next, mg_free(t), t = tmp;
|
||||||
mgr->timers = NULL; // Important. Next call to poll won't touch timers
|
mgr->timers = NULL; // Important. Next call to poll won't touch timers
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
|
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
|
||||||
|
#if MG_ENABLE_MDASH
|
||||||
|
mg_mdash_free(mgr); // Before the final poll, so mdash does not reconnect
|
||||||
|
#endif
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
#if MG_ENABLE_FREERTOS_TCP
|
#if MG_ENABLE_FREERTOS_TCP
|
||||||
FreeRTOS_DeleteSocketSet(mgr->ss);
|
FreeRTOS_DeleteSocketSet(mgr->ss);
|
||||||
@@ -343,13 +346,15 @@ void mg_mgr_init(struct mg_mgr *mgr) {
|
|||||||
mgr->dns4.url = "udp://8.8.8.8:53";
|
mgr->dns4.url = "udp://8.8.8.8:53";
|
||||||
mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
|
mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
|
||||||
mg_tls_ctx_init(mgr);
|
mg_tls_ctx_init(mgr);
|
||||||
MG_DEBUG(("MG_IO_SIZE: %lu, TLS: %s", MG_IO_SIZE,
|
MG_DEBUG(("MG_IO_SIZE: %lu, TLS: %s, conn size: %zu, mgr size: %zu",
|
||||||
|
MG_IO_SIZE,
|
||||||
MG_TLS == MG_TLS_NONE ? "none"
|
MG_TLS == MG_TLS_NONE ? "none"
|
||||||
: MG_TLS == MG_TLS_MBED ? "MbedTLS"
|
: MG_TLS == MG_TLS_MBED ? "MbedTLS"
|
||||||
: MG_TLS == MG_TLS_OPENSSL ? "OpenSSL"
|
: MG_TLS == MG_TLS_OPENSSL ? "OpenSSL"
|
||||||
: MG_TLS == MG_TLS_BUILTIN ? "builtin"
|
: MG_TLS == MG_TLS_BUILTIN ? "builtin"
|
||||||
: MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL"
|
: MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL"
|
||||||
: "custom"));
|
: "custom",
|
||||||
|
sizeof(struct mg_connection), sizeof(struct mg_mgr)));
|
||||||
#if MG_ENABLE_MDASH
|
#if MG_ENABLE_MDASH
|
||||||
mg_mdash_init(mgr);
|
mg_mdash_init(mgr);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -92,12 +92,12 @@ struct mg_connection {
|
|||||||
unsigned is_mqtt5 : 1; // MQTT5 connection (set by mg_mqtt_login)
|
unsigned is_mqtt5 : 1; // MQTT5 connection (set by mg_mqtt_login)
|
||||||
unsigned is_hexdumping : 1; // Log hex dump of all in/out traffic
|
unsigned is_hexdumping : 1; // Log hex dump of all in/out traffic
|
||||||
unsigned is_draining : 1; // Flush send buffer, then close
|
unsigned is_draining : 1; // Flush send buffer, then close
|
||||||
unsigned is_closing : 1; // Close and free immediately on next poll
|
unsigned is_closing : 1; // Request Close and free on next poll
|
||||||
unsigned is_full : 1; // Pause incoming reads until cleared
|
unsigned is_full : 1; // Pause incoming reads until cleared
|
||||||
unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending
|
unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending
|
||||||
unsigned is_resp : 1; // HTTP: response is still being generated
|
|
||||||
unsigned is_readable : 1; // Socket is ready to read (epoll/select)
|
unsigned is_readable : 1; // Socket is ready to read (epoll/select)
|
||||||
unsigned is_writable : 1; // Socket is ready to write (epoll/select)
|
unsigned is_writable : 1; // Socket is ready to write (epoll/select)
|
||||||
|
unsigned is_bsd : 1; // Built-in TCP/IP stack BSD socket
|
||||||
};
|
};
|
||||||
|
|
||||||
// Runs one iteration of the event loop.
|
// Runs one iteration of the event loop.
|
||||||
|
|||||||
+137
-80
@@ -1,4 +1,5 @@
|
|||||||
#include "net_builtin.h"
|
#include "net_builtin.h"
|
||||||
|
#include "bsd.h"
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
|
|
||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
@@ -54,6 +55,8 @@ struct connstate {
|
|||||||
bool twclosure; // 3-way closure done
|
bool twclosure; // 3-way closure done
|
||||||
bool retransmit; // Retain sent data until acknowledged
|
bool retransmit; // Retain sent data until acknowledged
|
||||||
bool is_full; // Advertise a zero receive window
|
bool is_full; // Advertise a zero receive window
|
||||||
|
bool is_closing; // Finished, remove connection (TCP after FIN)
|
||||||
|
bool send_rst; // Send RST before removing a TCP connection
|
||||||
struct mg_iobuf txq; // Length-prefixed sent TCP segments (RFC-9293, 3.8)
|
struct mg_iobuf txq; // Length-prefixed sent TCP segments (RFC-9293, 3.8)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -310,6 +313,8 @@ static bool tcpcsum_ok(void *d, void *t) {
|
|||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MG_IPISLOCAL(i, a) ((a & i->mask) == (i->ip & i->mask))
|
||||||
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
static uint16_t p6csum(void *d, void *p, uint8_t proto, size_t plen) {
|
static uint16_t p6csum(void *d, void *p, uint8_t proto, size_t plen) {
|
||||||
uint32_t sum;
|
uint32_t sum;
|
||||||
@@ -418,12 +423,13 @@ static void onstatechange(struct mg_tcpip_if *ifp) {
|
|||||||
ifp->is_ip_changed = false;
|
ifp->is_ip_changed = false;
|
||||||
}
|
}
|
||||||
} else if (ifp->state == MG_TCPIP_STATE_IP) {
|
} else if (ifp->state == MG_TCPIP_STATE_IP) {
|
||||||
if (ifp->gw != 0 && ifp->l2type == MG_TCPIP_L2_ETH)
|
if (ifp->l2type == MG_TCPIP_L2_ETH) {
|
||||||
mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request
|
ifp->gw_ready = false; // remove any prior state
|
||||||
|
if (ifp->gw != 0) // send an unsolicited GW ARP request
|
||||||
|
mg_tcpip_arp_request(ifp, ifp->gw, NULL);
|
||||||
|
}
|
||||||
} else if (ifp->state == MG_TCPIP_STATE_UP) {
|
} else if (ifp->state == MG_TCPIP_STATE_UP) {
|
||||||
srand((unsigned int) mg_millis());
|
srand((unsigned int) mg_millis());
|
||||||
} else if (ifp->state == MG_TCPIP_STATE_DOWN) {
|
|
||||||
MG_ERROR(("Link down"));
|
|
||||||
}
|
}
|
||||||
mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state);
|
mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state);
|
||||||
}
|
}
|
||||||
@@ -672,6 +678,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip);
|
static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip);
|
||||||
|
static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip);
|
||||||
|
|
||||||
static bool dhcp_opt_len_ok(uint8_t len, uint8_t *p, uint8_t *end) {
|
static bool dhcp_opt_len_ok(uint8_t len, uint8_t *p, uint8_t *end) {
|
||||||
return (len >= 4 && (len & 3) == 0 && p + 6 < end);
|
return (len >= 4 && (len & 3) == 0 && p + 6 < end);
|
||||||
@@ -727,21 +734,21 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
: pkt->dhcp->siaddr ? pkt->dhcp->siaddr
|
: pkt->dhcp->siaddr ? pkt->dhcp->siaddr
|
||||||
: pkt->ip->src);
|
: pkt->ip->src);
|
||||||
ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state
|
ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state
|
||||||
|
ifp->lease_expire = ifp->now + 5000; // No ACK by then: start over
|
||||||
} else if (msgtype == 5) { // DHCPACK
|
} else if (msgtype == 5) { // DHCPACK
|
||||||
if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP
|
if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP
|
||||||
uint64_t rand;
|
uint64_t rand;
|
||||||
ifp->lease_expire = ifp->now + (uint64_t) lease * 1000;
|
ifp->lease_expire = ifp->now + (uint64_t) lease * 1000;
|
||||||
MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
|
MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
|
||||||
// assume DHCP server = router until ARP resolves
|
|
||||||
memcpy(ifp->gwmac, mg_l2_getaddr(ifp, pkt->l2), sizeof(ifp->gwmac));
|
|
||||||
ifp->gw_ready = true; // NOTE(): actual gw ARP won't retry now
|
|
||||||
if (ifp->ip != ip) ifp->is_ip_changed = true;
|
if (ifp->ip != ip) ifp->is_ip_changed = true;
|
||||||
ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
|
ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
|
||||||
ifp->state = MG_TCPIP_STATE_IP; // BOUND state
|
ifp->state = MG_TCPIP_STATE_IP; // BOUND state
|
||||||
mg_random(&rand, sizeof(rand));
|
mg_random(&rand, sizeof(rand));
|
||||||
srand((unsigned int) (rand + mg_millis()));
|
srand((unsigned int) (rand + mg_millis()));
|
||||||
if (ifp->enable_req_dns && dns != 0) {
|
if (ifp->enable_req_dns && dns != 0) {
|
||||||
setdns4(ifp, &dns);
|
setdns4(ifp, &dns); // configure the provided DNS
|
||||||
|
if (MG_IPISLOCAL(ifp, dns)) // connect to a local DNS at this point
|
||||||
|
conndns4(ifp, dns);
|
||||||
mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns);
|
mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns);
|
||||||
}
|
}
|
||||||
if (ifp->enable_req_sntp && sntp != 0)
|
if (ifp->enable_req_sntp && sntp != 0)
|
||||||
@@ -792,7 +799,7 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
res.xid = pkt->dhcp->xid;
|
res.xid = pkt->dhcp->xid;
|
||||||
mac = mg_l2_getaddr(ifp, pkt->l2);
|
mac = mg_l2_getaddr(ifp, pkt->l2);
|
||||||
if (ifp->enable_get_gateway) {
|
if (ifp->enable_get_gateway) {
|
||||||
ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's
|
ifp->gw = res.yiaddr; // set gw IP, and MAC
|
||||||
memcpy(ifp->gwmac, mac, sizeof(ifp->gwmac));
|
memcpy(ifp->gwmac, mac, sizeof(ifp->gwmac));
|
||||||
}
|
}
|
||||||
tx_udp4(ifp, mac, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr,
|
tx_udp4(ifp, mac, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr,
|
||||||
@@ -1049,6 +1056,7 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
|
if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
|
||||||
if (changed) ifp->is_ip6_changed = true;
|
if (changed) ifp->is_ip6_changed = true;
|
||||||
ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1];
|
ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1];
|
||||||
|
ifp->gw6_ready = false; // Remove any prior state
|
||||||
if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac));
|
if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac));
|
||||||
if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP ||
|
if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP ||
|
||||||
ifp->l2type == MG_TCPIP_L2_PPPoE) {
|
ifp->l2type == MG_TCPIP_L2_PPPoE) {
|
||||||
@@ -1063,10 +1071,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
mtu));
|
mtu));
|
||||||
ifp->mtu = (uint16_t) mtu;
|
ifp->mtu = (uint16_t) mtu;
|
||||||
}
|
}
|
||||||
if (ifp->state6 != MG_TCPIP_STATE_READY) {
|
if (ifp->state6 != MG_TCPIP_STATE_READY)
|
||||||
tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution
|
|
||||||
ifp->state6 = MG_TCPIP_STATE_IP;
|
ifp->state6 = MG_TCPIP_STATE_IP;
|
||||||
}
|
|
||||||
onstate6change(ifp);
|
onstate6change(ifp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1182,7 +1188,7 @@ static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (ifp->ip != 0 && ((rem->addr.ip4 & ifp->mask) == (ifp->ip & ifp->mask)))
|
if (ifp->ip != 0 && MG_IPISLOCAL(ifp, rem->addr.ip4))
|
||||||
return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame
|
return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame
|
||||||
if (ifp->gw_ready) // use the router
|
if (ifp->gw_ready) // use the router
|
||||||
return ifp->gwmac; // ignore source address in frame
|
return ifp->gwmac; // ignore source address in frame
|
||||||
@@ -1192,6 +1198,20 @@ static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
|
||||||
|
char buf[64];
|
||||||
|
va_list ap;
|
||||||
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
va_start(ap, f);
|
||||||
|
mg_vsnprintf(buf, sizeof(buf), f, &ap);
|
||||||
|
va_end(ap);
|
||||||
|
MG_ERROR(("%lu %ld %s", c->id, c->fd, buf));
|
||||||
|
c->is_closing = 1; // Stop Mongoose I/O
|
||||||
|
s->is_closing = 1; // Transport-terminal; do not send FIN
|
||||||
|
if (r && !c->is_udp) s->send_rst = true; // honot TCP RST request
|
||||||
|
mg_call(c, MG_EV_ERROR, buf);
|
||||||
|
}
|
||||||
|
|
||||||
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
||||||
struct connstate *s;
|
struct connstate *s;
|
||||||
@@ -1214,10 +1234,10 @@ static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
return false; // safety net for lousy networks
|
return false; // safety net for lousy networks
|
||||||
memcpy(s->mac, l2addr, sizeof(s->mac));
|
memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
||||||
mg_error(c, "max_recv_buf_size reached");
|
proto_error(c, false, "max_recv_buf_size reached");
|
||||||
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
|
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
|
||||||
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
|
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
|
||||||
mg_error(c, "oom");
|
proto_error(c, false, "oom");
|
||||||
} else {
|
} else {
|
||||||
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
||||||
c->recv.len += pkt->pay.len;
|
c->recv.len += pkt->pay.len;
|
||||||
@@ -1491,7 +1511,7 @@ static void handle_tls_recv(struct mg_connection *c) {
|
|||||||
size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail;
|
size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail;
|
||||||
struct mg_iobuf *io = &c->recv; // allocated on first avail > 0
|
struct mg_iobuf *io = &c->recv; // allocated on first avail > 0
|
||||||
if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) {
|
if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) {
|
||||||
mg_error(c, "oom");
|
proto_error(c, true, "oom");
|
||||||
} else {
|
} else {
|
||||||
// Decrypt data directly into c->recv. If io->buf = NULL or
|
// Decrypt data directly into c->recv. If io->buf = NULL or
|
||||||
// io->len = io->size (no room), there can be outstanding data that can't be
|
// io->len = io->size (no room), there can be outstanding data that can't be
|
||||||
@@ -1500,7 +1520,7 @@ static void handle_tls_recv(struct mg_connection *c) {
|
|||||||
long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf,
|
long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf,
|
||||||
io->size - io->len);
|
io->size - io->len);
|
||||||
if (n == MG_IO_ERR) {
|
if (n == MG_IO_ERR) {
|
||||||
mg_error(c, "TLS recv error");
|
proto_error(c, true, "TLS recv error");
|
||||||
} else if (n > 0) {
|
} else if (n > 0) {
|
||||||
// Decrypted successfully - trigger MG_EV_READ
|
// Decrypted successfully - trigger MG_EV_READ
|
||||||
io->len += (size_t) n;
|
io->len += (size_t) n;
|
||||||
@@ -1544,7 +1564,7 @@ static void retransmit(struct mg_connection *c) {
|
|||||||
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
|
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
|
||||||
mg_htonl(s->txq_seq), mg_htonl(s->ack),
|
mg_htonl(s->txq_seq), mg_htonl(s->ack),
|
||||||
s->is_full ? 0 : MG_TCPIP_WIN, buf, len) == (size_t) -1) {
|
s->is_full ? 0 : MG_TCPIP_WIN, buf, len) == (size_t) -1) {
|
||||||
mg_error(c, "retransmit");
|
proto_error(c, false, "retransmit"); // should RST, but sending just failed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Back off RTO after every timeout (RFC-6298, 5.5)
|
// Back off RTO after every timeout (RFC-6298, 5.5)
|
||||||
@@ -1577,15 +1597,11 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1);
|
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1);
|
||||||
s->fin_rcvd = true;
|
s->fin_rcvd = true;
|
||||||
if (c->is_draining && s->ttype == MIP_TTYPE_FIN) {
|
if (c->is_draining && s->ttype == MIP_TTYPE_FIN) {
|
||||||
if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ?
|
// Simultaneous closure ?
|
||||||
s->seq++; // Yes. Increment our SEQ
|
|
||||||
} else { // Otherwise,
|
|
||||||
s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK
|
|
||||||
}
|
|
||||||
s->twclosure = true;
|
s->twclosure = true;
|
||||||
} else {
|
} else {
|
||||||
// Peer closed first: send ACK only, enter CLOSE_WAIT.
|
// Peer closed first: send ACK only, enter CLOSE_WAIT.
|
||||||
// The connection loop will call init_closure after pending send data
|
// The connection loop will initiate closure after pending send data
|
||||||
// is flushed, then send our FIN.
|
// is flushed, then send our FIN.
|
||||||
c->is_draining = 1;
|
c->is_draining = 1;
|
||||||
}
|
}
|
||||||
@@ -1619,18 +1635,11 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
s->twclosure = true;
|
s->twclosure = true;
|
||||||
}
|
}
|
||||||
if (pkt->pay.len == 0) return;
|
if (pkt->pay.len == 0) return;
|
||||||
if (io->size - io->len < pkt->pay.len &&
|
if (!c->is_closing && io->size - io->len < pkt->pay.len &&
|
||||||
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
|
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
|
||||||
mg_error(c, "oom");
|
proto_error(c, true, "oom");
|
||||||
return; // drop it
|
return; // drop it
|
||||||
}
|
}
|
||||||
// Copy TCP payload into the IO buffer. If the connection is plain text,
|
|
||||||
// we copy to c->recv. If the connection is TLS, this data is encrypted,
|
|
||||||
// therefore we copy that encrypted data to the c->rtls iobuffer instead,
|
|
||||||
// and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
|
|
||||||
// call back mg_io_recv() which grabs raw data from c->rtls
|
|
||||||
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
|
|
||||||
io->len += pkt->pay.len;
|
|
||||||
MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
|
MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
|
||||||
if (!(pkt->tcp->flags & TH_FIN)) { // FIN already advanced s->ack
|
if (!(pkt->tcp->flags & TH_FIN)) { // FIN already advanced s->ack
|
||||||
// Advance ACK counter
|
// Advance ACK counter
|
||||||
@@ -1638,7 +1647,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
s->unacked += pkt->pay.len;
|
s->unacked += pkt->pay.len;
|
||||||
}
|
}
|
||||||
// size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack;
|
// size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack;
|
||||||
if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) {
|
if ((s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) ||
|
||||||
|
s->ttype == MIP_TTYPE_FIN) {
|
||||||
// Send ACK immediately
|
// Send ACK immediately
|
||||||
MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked));
|
MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked));
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
@@ -1651,6 +1661,14 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
// if not already running, setup a timer to send an ACK later
|
// if not already running, setup a timer to send an ACK later
|
||||||
if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
|
if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
|
||||||
}
|
}
|
||||||
|
if (c->is_closing) return; // we have a "closed socket", ignore data
|
||||||
|
// Copy TCP payload into the IO buffer. If the connection is plain text,
|
||||||
|
// we copy to c->recv. If the connection is TLS, this data is encrypted,
|
||||||
|
// therefore we copy that encrypted data to the c->rtls iobuffer instead,
|
||||||
|
// and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
|
||||||
|
// call back mg_io_recv() which grabs raw data from c->rtls
|
||||||
|
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
|
||||||
|
io->len += pkt->pay.len;
|
||||||
if (c->is_tls) {
|
if (c->is_tls) {
|
||||||
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
|
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
|
||||||
} else {
|
} else {
|
||||||
@@ -1759,11 +1777,11 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
if (c->is_tls_hs) mg_tls_handshake(c);
|
if (c->is_tls_hs) mg_tls_handshake(c);
|
||||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||||
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
|
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
|
||||||
mg_error(c, "connection refused");
|
proto_error(c, false, "connection refused");
|
||||||
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
|
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
|
||||||
uint32_t seqno = mg_ntohl(pkt->tcp->seq);
|
uint32_t seqno = mg_ntohl(pkt->tcp->seq);
|
||||||
if (seqno >= s->ack && seqno < (s->ack + MG_TCPIP_WIN)) // RFC-9293 3.5.3
|
if (seqno >= s->ack && seqno < (s->ack + MG_TCPIP_WIN)) // RFC-9293 3.5.3
|
||||||
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
|
proto_error(c, false, "peer RST"); // RFC-1122 4.2.2.13
|
||||||
} else if (c != NULL) {
|
} else if (c != NULL) {
|
||||||
// process segment
|
// process segment
|
||||||
s->tmiss = 0; // Reset missed keep-alive counter
|
s->tmiss = 0; // Reset missed keep-alive counter
|
||||||
@@ -1845,7 +1863,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
|
if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
|
||||||
if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
||||||
c = getpeer(ifp->mgr, pkt, false);
|
c = getpeer(ifp->mgr, pkt, false);
|
||||||
if (c) mg_error(c, "Received fragmented packet");
|
if (c) proto_error(c, false, "Received fragmented packet");
|
||||||
} else if (pkt->ip->proto == 1) {
|
} else if (pkt->ip->proto == 1) {
|
||||||
pkt->icmp = (struct icmp *) (pkt->pay.buf);
|
pkt->icmp = (struct icmp *) (pkt->pay.buf);
|
||||||
if (pkt->pay.len < sizeof(*pkt->icmp)) return;
|
if (pkt->pay.len < sizeof(*pkt->icmp)) return;
|
||||||
@@ -1928,7 +1946,7 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
|
if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
|
||||||
if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
||||||
c = getpeer(ifp->mgr, pkt, false);
|
c = getpeer(ifp->mgr, pkt, false);
|
||||||
if (c) mg_error(c, "Received fragmented packet");
|
if (c) proto_error(c, false, "Received fragmented packet");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 59: // No Next Header 4.7
|
case 59: // No Next Header 4.7
|
||||||
@@ -2024,6 +2042,9 @@ static void mg_ip_poll(struct mg_tcpip_if *ifp, bool s1) {
|
|||||||
if (ifp->enable_dhcp_client && s1) {
|
if (ifp->enable_dhcp_client && s1) {
|
||||||
if (ifp->state == MG_TCPIP_STATE_UP) {
|
if (ifp->state == MG_TCPIP_STATE_UP) {
|
||||||
tx_dhcp_discover(ifp); // INIT (4.4.1)
|
tx_dhcp_discover(ifp); // INIT (4.4.1)
|
||||||
|
} else if (ifp->state == MG_TCPIP_STATE_REQ &&
|
||||||
|
ifp->now >= ifp->lease_expire) {
|
||||||
|
ifp->state = MG_TCPIP_STATE_UP; // REQUEST or ACK lost: DISCOVER again
|
||||||
} else if (ifp->state == MG_TCPIP_STATE_READY &&
|
} else if (ifp->state == MG_TCPIP_STATE_READY &&
|
||||||
ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
|
ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
|
||||||
if (ifp->now >= ifp->lease_expire) {
|
if (ifp->now >= ifp->lease_expire) {
|
||||||
@@ -2122,7 +2143,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
}
|
}
|
||||||
// Handle gw ARP request timeout, order is important
|
// Handle gw ARP request timeout, order is important
|
||||||
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
|
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
|
||||||
ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark
|
ifp->state = MG_TCPIP_STATE_READY; // gw MAC not resolved, we'll retry
|
||||||
onstatechange(ifp);
|
onstatechange(ifp);
|
||||||
}
|
}
|
||||||
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready &&
|
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready &&
|
||||||
@@ -2131,10 +2152,10 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
// Handle gw NS/NA req/resp timeout, order is important
|
// Handle gw NS/NA req/resp timeout, order is important
|
||||||
if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_IP) {
|
if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_IP) {
|
||||||
ifp->state6 = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark
|
ifp->state6 = MG_TCPIP_STATE_READY; // gw6 MAC not resolved, we'll retry
|
||||||
onstate6change(ifp);
|
onstate6change(ifp);
|
||||||
}
|
}
|
||||||
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw6_ready &&
|
if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_READY && !ifp->gw6_ready &&
|
||||||
(ifp->gw6[0] != 0 || ifp->gw6[1] != 0))
|
(ifp->gw6[0] != 0 || ifp->gw6[1] != 0))
|
||||||
tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution
|
tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution
|
||||||
#endif
|
#endif
|
||||||
@@ -2193,7 +2214,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
}
|
}
|
||||||
if (ifp->now > s->timer) {
|
if (ifp->now > s->timer) {
|
||||||
if (s->ttype == MIP_TTYPE_ARP) {
|
if (s->ttype == MIP_TTYPE_ARP) {
|
||||||
mg_error(c, "ARP timeout");
|
proto_error(c, false, "ARP timeout");
|
||||||
} else if (c->is_udp) {
|
} else if (c->is_udp) {
|
||||||
continue;
|
continue;
|
||||||
} else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) {
|
} else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) {
|
||||||
@@ -2202,13 +2223,13 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
|
mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
|
||||||
s->acked = s->ack;
|
s->acked = s->ack;
|
||||||
} else if (s->ttype == MIP_TTYPE_SYN) {
|
} else if (s->ttype == MIP_TTYPE_SYN) {
|
||||||
mg_error(c, "Connection timeout");
|
proto_error(c, false, "Connection timeout");
|
||||||
} else if (s->ttype == MIP_TTYPE_FIN) {
|
} else if (s->ttype == MIP_TTYPE_FIN) {
|
||||||
c->is_closing = 1;
|
s->is_closing = 1;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
if (s->tmiss++ > 2) {
|
if (s->tmiss++ > 2) {
|
||||||
mg_error(c, "keepalive");
|
proto_error(c, false, "keepalive");
|
||||||
} else {
|
} else {
|
||||||
MG_VERBOSE(("%lu keepalive", c->id));
|
MG_VERBOSE(("%lu keepalive", c->id));
|
||||||
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
@@ -2265,10 +2286,20 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
|||||||
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
|
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
|
||||||
// MG_EPHEMERAL_PORT_BASE to 65535
|
// MG_EPHEMERAL_PORT_BASE to 65535
|
||||||
if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM"));
|
if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM"));
|
||||||
|
MG_DEBUG(("Builtin TCP/IP init done. Queue size: %zu, extra conn size: %zu",
|
||||||
|
ifp->recv_queue.size, mgr->extraconnsize));
|
||||||
}
|
}
|
||||||
|
mg_bsd_init(mgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_conn(struct mg_connection *c) {
|
||||||
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
mg_iobuf_free(&s->txq);
|
||||||
|
mg_close_conn(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_tcpip_free(struct mg_tcpip_if *ifp) {
|
void mg_tcpip_free(struct mg_tcpip_if *ifp) {
|
||||||
|
while (ifp->mgr->conns != NULL) close_conn(ifp->mgr->conns); // TCP FIN wait
|
||||||
mg_free(ifp->recv_queue.buf);
|
mg_free(ifp->recv_queue.buf);
|
||||||
mg_free(ifp->tx.buf);
|
mg_free(ifp->tx.buf);
|
||||||
mg_free(ifp->dns4_url);
|
mg_free(ifp->dns4_url);
|
||||||
@@ -2335,12 +2366,12 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
} else {
|
} else {
|
||||||
MG_ERROR(("No IPv6 gateway, can't connect"));
|
mg_error(c, "No IPv6 gateway, can't connect");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
uint32_t rem_ip = c->rem.addr.ip4;
|
uint32_t rem_ip = c->rem.addr.ip4;
|
||||||
if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) &&
|
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
||||||
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
||||||
// If we're in the same LAN, fire an ARP lookup.
|
// If we're in the same LAN, fire an ARP lookup.
|
||||||
MG_DEBUG(("%lu ARP lookup...", c->id));
|
MG_DEBUG(("%lu ARP lookup...", c->id));
|
||||||
@@ -2351,7 +2382,7 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
} else {
|
} else {
|
||||||
MG_ERROR(("No gateway, can't connect"));
|
mg_error(c, "No gateway, can't connect");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2370,29 +2401,13 @@ static void write_conn(struct mg_connection *c) {
|
|||||||
: mg_io_send(c, c->send.buf, c->send.len);
|
: mg_io_send(c, c->send.buf, c->send.len);
|
||||||
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
||||||
if (len == MG_IO_ERR) {
|
if (len == MG_IO_ERR) {
|
||||||
mg_error(c, "tx err");
|
proto_error(c, false, "tx err");
|
||||||
} else if (len > 0) {
|
} else if (len > 0) {
|
||||||
mg_iobuf_del(&c->send, 0, (size_t) len);
|
mg_iobuf_del(&c->send, 0, (size_t) len);
|
||||||
mg_call(c, MG_EV_WRITE, &len);
|
mg_call(c, MG_EV_WRITE, &len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_closure(struct mg_connection *c) {
|
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
|
||||||
if (c->is_listening == false && c->is_connecting == false) {
|
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_FIN | TH_ACK,
|
|
||||||
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
|
|
||||||
NULL, 0);
|
|
||||||
settmout(c, MIP_TTYPE_FIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close_conn(struct mg_connection *c) {
|
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
|
||||||
mg_iobuf_free(&s->txq);
|
|
||||||
mg_close_conn(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool can_write(struct mg_connection *c) {
|
static bool can_write(struct mg_connection *c) {
|
||||||
return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
|
return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
|
||||||
c->is_tls_hs == 0 && c->is_arplooking == 0;
|
c->is_tls_hs == 0 && c->is_arplooking == 0;
|
||||||
@@ -2413,29 +2428,50 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
!c->is_listening && !c->is_connecting;
|
!c->is_listening && !c->is_connecting;
|
||||||
tmp = c->next;
|
tmp = c->next;
|
||||||
mg_call(c, MG_EV_POLL, &now);
|
mg_call(c, MG_EV_POLL, &now);
|
||||||
MG_VERBOSE(("%lu .. %c%c%c%c%c %lu %lu", c->id, c->is_tls ? 'T' : 't',
|
MG_VERBOSE(("%lu .. %c%c%c%c%c%c %lu %lu", c->id, c->is_tls ? 'T' : 't',
|
||||||
c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
|
c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
|
||||||
c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c',
|
c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c',
|
||||||
mg_tls_pending(c), c->rtls.len));
|
s->is_closing ? 'F' : 'f', mg_tls_pending(c), c->rtls.len));
|
||||||
|
if (!c->is_closing) {
|
||||||
// order is important, TLS conn close with > 1 record in buffer (below)
|
// order is important, TLS conn close with > 1 record in buffer (below)
|
||||||
if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0))
|
if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0))
|
||||||
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
|
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
|
||||||
if (can_write(c)) write_conn(c);
|
if (can_write(c)) write_conn(c);
|
||||||
if (is_tls && c->send.len == 0) flush = mg_tls_flush(c);
|
if (is_tls && c->send.len == 0) flush = mg_tls_flush(c);
|
||||||
if (flush == MG_IO_ERR) mg_error(c, "tx err");
|
|
||||||
if (c->is_draining && c->send.len == 0) {
|
|
||||||
if (c->is_udp) c->is_closing = 1;
|
|
||||||
if (!c->is_udp && flush == 0 && s->txq.len == 0 &&
|
|
||||||
s->ttype != MIP_TTYPE_FIN)
|
|
||||||
init_closure(c);
|
|
||||||
}
|
}
|
||||||
|
if (flush == MG_IO_ERR) proto_error(c, false, "tx err"); // should RST but
|
||||||
|
if (c->is_draining && c->send.len == 0 &&
|
||||||
|
(c->is_udp ||
|
||||||
|
(flush == 0 && s->txq.len == 0 && s->ttype != MIP_TTYPE_FIN)))
|
||||||
|
c->is_closing = 1;
|
||||||
// For non-TLS, close immediately upon completing the 3-way closure
|
// For non-TLS, close immediately upon completing the 3-way closure
|
||||||
// For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires
|
// For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires
|
||||||
if (s->twclosure &&
|
if (s->twclosure &&
|
||||||
(!c->is_tls || (c->rtls.len == 0 && mg_tls_pending(c) == 0)))
|
(!c->is_tls || (c->rtls.len == 0 && mg_tls_pending(c) == 0))) {
|
||||||
c->is_closing = 1;
|
s->is_closing = 1;
|
||||||
if (c->is_closing) close_conn(c);
|
} else if (c->is_closing && !s->is_closing) {
|
||||||
|
if (c->is_udp || c->is_listening || c->is_resolving || c->is_arplooking ||
|
||||||
|
c->is_connecting) {
|
||||||
|
close_conn(c);
|
||||||
|
continue;
|
||||||
|
} else if (s->ttype != MIP_TTYPE_FIN) { // active TCP: initiate closure
|
||||||
|
size_t tx = tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp,
|
||||||
|
TH_FIN | TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack),
|
||||||
|
s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
|
||||||
|
if (tx == (size_t) -1) {
|
||||||
|
s->is_closing = 1;
|
||||||
|
} else if (tx > 0) {
|
||||||
|
s->seq++;
|
||||||
|
settmout(c, MIP_TTYPE_FIN);
|
||||||
|
} // else tx = 0, should recover later or we have worse problems
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (s->send_rst) // proto_error requested TCP RST, send it and close
|
||||||
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_RST,
|
||||||
|
mg_htonl(s->seq), 0, 0, NULL, 0);
|
||||||
|
if (s->is_closing) close_conn(c); // then removes the conn
|
||||||
|
}
|
||||||
|
mg_bsd_poll(mgr);
|
||||||
(void) ms;
|
(void) ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2443,10 +2479,10 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||||
bool res = false;
|
bool res = false;
|
||||||
if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
|
if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
|
||||||
mg_error(c, "net down");
|
proto_error(c, false, "net down");
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
} else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
|
} else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
|
||||||
mg_error(c, "net down");
|
proto_error(c, false, "net down");
|
||||||
#endif
|
#endif
|
||||||
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
|
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
|
||||||
// Fail to send, no target MAC or IP
|
// Fail to send, no target MAC or IP
|
||||||
@@ -2480,10 +2516,31 @@ static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) {
|
|||||||
MG_DEBUG(("Set DNS URL to %s", dnsc->url));
|
MG_DEBUG(("Set DNS URL to %s", dnsc->url));
|
||||||
if (ifp->mgr->use_dns6) return;
|
if (ifp->mgr->use_dns6) return;
|
||||||
if (dnsc->c != NULL) mg_close_conn(dnsc->c);
|
if (dnsc->c != NULL) mg_close_conn(dnsc->c);
|
||||||
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
|
|
||||||
MG_ERROR(("DNS connection creation failed"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip) {
|
||||||
|
struct mg_connection *c;
|
||||||
|
struct mg_dns *dnsc = &ifp->mgr->dns4;
|
||||||
|
if (ifp->mgr->use_dns6) return;
|
||||||
|
if ((c = mg_alloc_conn(ifp->mgr)) == NULL) {
|
||||||
|
MG_ERROR(("OOM, DNS connection creation failed"));
|
||||||
|
return;
|
||||||
|
} // mg_connect_svc() would block us, so we bypass it here
|
||||||
|
LIST_ADD_HEAD(struct mg_connection, &ifp->mgr->conns, c);
|
||||||
|
c->is_udp = true;
|
||||||
|
c->fd = (void *) (size_t) MG_INVALID_SOCKET;
|
||||||
|
c->is_client = true;
|
||||||
|
dnsc->c = c;
|
||||||
|
mg_call(c, MG_EV_OPEN, (void *) dnsc->url);
|
||||||
|
MG_DEBUG(("%lu %ld %s", c->id, c->fd, dnsc->url));
|
||||||
|
c->rem.addr.ip4 = ip;
|
||||||
|
c->rem.port = mg_htons(53);
|
||||||
|
mg_connect_resolved(c);
|
||||||
|
mg_dnsc_init(ifp->mgr, dnsc); // will fill c->pfn = dns_cb
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) {
|
void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
uint8_t *l2addr = tcpip_mapip(c->mgr->ifp, ip);
|
uint8_t *l2addr = tcpip_mapip(c->mgr->ifp, ip);
|
||||||
|
|||||||
@@ -154,6 +154,41 @@ extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
|||||||
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
||||||
|
extern struct mg_tcpip_driver mg_tcpip_driver_mcxw71;
|
||||||
|
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
|
||||||
|
|
||||||
|
// IEEE 802.15.4 radio channel and PAN ID. Settable in mongoose_config.h
|
||||||
|
#ifndef MG_L2_802154_CHANNEL
|
||||||
|
#define MG_L2_802154_CHANNEL 15 // 11..26
|
||||||
|
#endif
|
||||||
|
#ifndef MG_L2_802154_PAN_ID
|
||||||
|
#define MG_L2_802154_PAN_ID 0xabcd
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// driver_data for IEEE 802.15.4 radio drivers. The interface is Ethernet:
|
||||||
|
// each Ethernet frame is split into broadcast radio frames, and reassembled
|
||||||
|
// on the other side, see mg_l2_802154_tx() and mg_l2_802154_rx()
|
||||||
|
struct mg_l2_802154_data {
|
||||||
|
uint8_t channel; // User-set channel, 11..26
|
||||||
|
uint16_t pan_id; // User-set PAN ID
|
||||||
|
uint8_t seq, tag; // MAC sequence number, tag of the last sent frame
|
||||||
|
uint8_t rxtag, rxnext; // Frame being reassembled: tag, next fragment
|
||||||
|
uint8_t rxsrc[8]; // Its sender
|
||||||
|
uint16_t rxlen; // Its bytes received so far
|
||||||
|
uint8_t rxbuf[1514]; // Reassembly buffer, one Ethernet frame
|
||||||
|
};
|
||||||
|
|
||||||
|
// Splits an Ethernet frame into radio frames, sends each by calling send().
|
||||||
|
// Called by radio drivers' tx(). Return len, or 0 on error
|
||||||
|
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||||
|
size_t (*send)(const uint8_t *, size_t,
|
||||||
|
struct mg_tcpip_if *));
|
||||||
|
|
||||||
|
// Adds a received radio frame f of n bytes to the frame being reassembled.
|
||||||
|
// Called by radio drivers' rx(). Return the Ethernet frame length, copied to
|
||||||
|
// buf, when it is complete, otherwise 0
|
||||||
|
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
|
||||||
|
void *buf, size_t len);
|
||||||
|
|
||||||
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||||
// Populate and assign to the driver's driver_data field.
|
// Populate and assign to the driver's driver_data field.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
// Scannable version tag embedded in every firmware binary, for server-side
|
// Scannable version tag embedded in every firmware binary, for server-side
|
||||||
// version extraction. The version string starts after the "MG_VERSION:" prefix.
|
// version extraction. The version string starts after the "MG_VERSION:" prefix.
|
||||||
static const char s_fw_version[] = "MG_VERSION:" MG_OTA_FIRMWARE_VERSION;
|
static const char s_fw_version[] = "MG_VERSION:" MG_FIRMWARE_VERSION;
|
||||||
|
|
||||||
static bool s_autocommit_ok; // True after OTA server confirms "same version"
|
static bool s_autocommit_ok; // True after OTA server confirms "same version"
|
||||||
|
|
||||||
@@ -23,6 +23,9 @@ static struct mg_ota_state {
|
|||||||
size_t size;
|
size_t size;
|
||||||
uint8_t sha256[32];
|
uint8_t sha256[32];
|
||||||
void (*fn)(const char *error_message);
|
void (*fn)(const char *error_message);
|
||||||
|
size_t received; // Firmware bytes written so far
|
||||||
|
bool open; // mg_ota_begin() succeeded, mg_ota_end() is pending
|
||||||
|
const char *err; // First error during download, NULL if none
|
||||||
} *s_ota;
|
} *s_ota;
|
||||||
|
|
||||||
static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data);
|
static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data);
|
||||||
@@ -143,11 +146,38 @@ static void status_fn(const char *errmsg) {
|
|||||||
if (errmsg) MG_ERROR(("OTA failed: %s", errmsg));
|
if (errmsg) MG_ERROR(("OTA failed: %s", errmsg));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void status_fn_2(struct mg_connection *c, const char *errmsg) {
|
// Firmware download callback for mg_http_stream_body(). Writes chunks to the
|
||||||
if (s_ota) s_ota->fn(errmsg);
|
// OTA target and reports the result via s_ota->fn(). The end call also happens
|
||||||
|
// when the connection drops before the whole body arrived.
|
||||||
|
static bool s_firmware_cb(struct mg_http_message *hm, struct mg_str *data,
|
||||||
|
void **p) {
|
||||||
|
bool finalized = false, ok;
|
||||||
|
(void) p;
|
||||||
|
if (s_ota == NULL) return false;
|
||||||
|
if (hm != NULL) { // Start
|
||||||
|
s_ota->received = 0;
|
||||||
|
s_ota->open = mg_ota_begin(hm->body.len);
|
||||||
|
if (!s_ota->open) s_ota->err = "OTA begin failed";
|
||||||
|
return s_ota->open;
|
||||||
|
} else if (data != NULL) { // Next chunk
|
||||||
|
if (!mg_ota_write(data->buf, data->len)) {
|
||||||
|
s_ota->err = "OTA write failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s_ota->received += data->len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// End
|
||||||
|
if (s_ota->open) finalized = mg_ota_end();
|
||||||
|
if (s_ota->err == NULL && s_ota->received != s_ota->size) {
|
||||||
|
s_ota->err = "Connection closed";
|
||||||
|
}
|
||||||
|
if (s_ota->err == NULL && !finalized) s_ota->err = "OTA finalize failed";
|
||||||
|
ok = s_ota->err == NULL;
|
||||||
|
s_ota->fn(s_ota->err);
|
||||||
mg_free(s_ota);
|
mg_free(s_ota);
|
||||||
s_ota = NULL;
|
s_ota = NULL;
|
||||||
mg_http_reply(c, errmsg ? 500 : 200, "", "%s\n", errmsg ? errmsg : "ok");
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
@@ -178,7 +208,7 @@ static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
|
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
|
||||||
} else {
|
} else {
|
||||||
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
|
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
|
||||||
mg_http_start_ota(c, hm, status_fn_2);
|
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_ERROR) {
|
} else if (ev == MG_EV_ERROR) {
|
||||||
s_ota->fn((char *) ev_data);
|
s_ota->fn((char *) ev_data);
|
||||||
@@ -211,6 +241,21 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *json_url,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uses *p as a flag: non-NULL means an OTA session is open and must be
|
||||||
|
// closed by mg_ota_end()
|
||||||
|
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||||
|
if (hm != NULL) { // Start
|
||||||
|
if (!mg_ota_begin(hm->body.len)) return false;
|
||||||
|
*p = (void *) (ptrdiff_t) 1;
|
||||||
|
return true;
|
||||||
|
} else if (data != NULL) { // Next chunk
|
||||||
|
return mg_ota_write(data->buf, data->len);
|
||||||
|
}
|
||||||
|
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
||||||
|
*p = NULL;
|
||||||
|
return mg_ota_end();
|
||||||
|
}
|
||||||
|
|
||||||
void mg_ota_poll(struct mg_mgr *mgr) {
|
void mg_ota_poll(struct mg_mgr *mgr) {
|
||||||
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// This is a low-level OTA API, not intended to be called directly by users.
|
// This is a low-level OTA API, not intended to be called directly by users.
|
||||||
// Users are provided with a higher-level API:
|
// Users are provided with a higher-level API:
|
||||||
// - defining MG_OTA_URL enables HTTP or HTTPS pull-based updates
|
// - defining MG_OTA_URL enables HTTP or HTTPS pull-based updates
|
||||||
// - mg_http_start_ota() enables push-based updates
|
// - mg_http_stream_body() enables push-based updates
|
||||||
//
|
//
|
||||||
// However, it is possible to use the mg_ota_* API directly. Below is the
|
// However, it is possible to use the mg_ota_* API directly. Below is the
|
||||||
// example code of a function that is called repeatedly. It expects the
|
// example code of a function that is called repeatedly. It expects the
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
|
#include "http.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
// Supported OTA targets
|
// Supported OTA targets
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
#define MG_OTA_STM32H7 2 // STM32 H7
|
#define MG_OTA_STM32H7 2 // STM32 H7
|
||||||
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
||||||
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
|
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
|
||||||
|
#define MG_OTA_STM32C5 5 // STM32 C5, same flash controller as H5
|
||||||
#define MG_OTA_CH32V307 100 // WCH CH32V307
|
#define MG_OTA_CH32V307 100 // WCH CH32V307
|
||||||
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
||||||
#define MG_OTA_RT1020 300 // IMXRT1020
|
#define MG_OTA_RT1020 300 // IMXRT1020
|
||||||
@@ -43,6 +45,7 @@
|
|||||||
#define MG_OTA_RT1170 304 // IMXRT1170
|
#define MG_OTA_RT1170 304 // IMXRT1170
|
||||||
#define MG_OTA_RT1180 305 // IMXRT1180
|
#define MG_OTA_RT1180 305 // IMXRT1180
|
||||||
#define MG_OTA_MCXN 310 // MCXN947
|
#define MG_OTA_MCXN 310 // MCXN947
|
||||||
|
#define MG_OTA_MCXW71 311 // MCXW71
|
||||||
#define MG_OTA_RW612 320 // FRDM-RW612
|
#define MG_OTA_RW612 320 // FRDM-RW612
|
||||||
#define MG_OTA_FLASH 900 // OTA via internal flash
|
#define MG_OTA_FLASH 900 // OTA via internal flash
|
||||||
#define MG_OTA_ESP32 910 // ESP32 OTA implementation
|
#define MG_OTA_ESP32 910 // ESP32 OTA implementation
|
||||||
@@ -72,6 +75,18 @@ bool mg_ota_write(const void *buf, size_t len);
|
|||||||
// Ends the OTA process and commits the written firmware.
|
// Ends the OTA process and commits the written firmware.
|
||||||
bool mg_ota_end(void);
|
bool mg_ota_end(void);
|
||||||
|
|
||||||
|
// Push-based OTA: mg_http_stream_body() callback that flashes the request
|
||||||
|
//
|
||||||
|
// ```c
|
||||||
|
// if (ev == MG_EV_HTTP_HDRS) {
|
||||||
|
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||||
|
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p);
|
||||||
|
|
||||||
// MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the
|
// MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the
|
||||||
// default calls swap_fn() (no-op on single-bank) then resets. Override in
|
// default calls swap_fn() (no-op on single-bank) then resets. Override in
|
||||||
// mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.).
|
// mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.).
|
||||||
@@ -154,7 +169,7 @@ enum {
|
|||||||
// needs to be defined. Fetches metadata_url, which must
|
// needs to be defined. Fetches metadata_url, which must
|
||||||
// return a JSON object like:
|
// return a JSON object like:
|
||||||
// { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
|
// { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
|
||||||
// If the server version differs from MG_OTA_FIRMWARE_VERSION, downloads
|
// If the server version differs from MG_FIRMWARE_VERSION, downloads
|
||||||
// FIRMWARE_URL and performs the OTA update. fn is called on every outcome:
|
// FIRMWARE_URL and performs the OTA update. fn is called on every outcome:
|
||||||
// NULL on successful flash, "Same version" when already up to date, or an
|
// NULL on successful flash, "Same version" when already up to date, or an
|
||||||
// error string on failure. Pass NULL to use the default handler which logs
|
// error string on failure. Pass NULL to use the default handler which logs
|
||||||
@@ -181,8 +196,8 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Firmware version for mg_ota_poll(). Settable in mongoose_config.h
|
// Firmware version for mg_ota_poll(). Settable in mongoose_config.h
|
||||||
#ifndef MG_OTA_FIRMWARE_VERSION
|
#ifndef MG_FIRMWARE_VERSION
|
||||||
#define MG_OTA_FIRMWARE_VERSION "1.0.0"
|
#define MG_FIRMWARE_VERSION "1.0.0"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Maximum version string length
|
// Maximum version string length
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
#include "flash.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "ota.h"
|
||||||
|
|
||||||
|
#if MG_OTA == MG_OTA_MCXW71
|
||||||
|
|
||||||
|
// MCXW71SRM, section 11.2: ROM flash API, 8 KB sectors, 16-byte phrases,
|
||||||
|
// 128-byte pages. Only the CM33 application flash is used; RF flash and IFR
|
||||||
|
// (including the write-once ROM configuration) are left alone.
|
||||||
|
struct mg_mcxw71_flash_config {
|
||||||
|
struct {
|
||||||
|
uint32_t base, size, blocks;
|
||||||
|
uint32_t ifr_base, ifr_size;
|
||||||
|
} mem[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mg_mcxw71_flash_api {
|
||||||
|
uint32_t (*init)(struct mg_mcxw71_flash_config *);
|
||||||
|
uint32_t (*erase)(struct mg_mcxw71_flash_config *, void *, uint32_t, uint32_t,
|
||||||
|
uint32_t);
|
||||||
|
uint32_t (*phrase)(struct mg_mcxw71_flash_config *, void *, uint32_t,
|
||||||
|
uint32_t *, uint32_t);
|
||||||
|
uint32_t (*page)(struct mg_mcxw71_flash_config *, void *, uint32_t,
|
||||||
|
uint32_t *, uint32_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The flash API pointer is the second entry in the ROM API table.
|
||||||
|
#define MG_MCXW71_FLASH_API \
|
||||||
|
(*(const struct mg_mcxw71_flash_api *const *) 0x14816fe4U)
|
||||||
|
#define MG_MCXW71_ERASE_KEY 0x6b65666cU // FOUR_CHAR_CODE('l', 'f', 'e', 'k')
|
||||||
|
|
||||||
|
static struct mg_mcxw71_flash_config s_mcxw71_config;
|
||||||
|
static size_t s_mcxw71_size, s_mcxw71_received, s_mcxw71_pending;
|
||||||
|
static uint32_t s_mcxw71_buf[4];
|
||||||
|
static bool s_mcxw71_failed;
|
||||||
|
|
||||||
|
static bool mg_mcxw71_write(void *, const void *, size_t);
|
||||||
|
static bool mg_mcxw71_swap(void);
|
||||||
|
static struct mg_flash s_mg_flash_mcxw71 = {
|
||||||
|
(void *) 0, 0x100000, 8192, 16, mg_mcxw71_write, mg_mcxw71_swap};
|
||||||
|
struct mg_flash *mg_flash = &s_mg_flash_mcxw71;
|
||||||
|
|
||||||
|
// Everything called while flash is busy, or after the application has been
|
||||||
|
// erased, must execute from RAM or ROM. Do not log or call libc here.
|
||||||
|
MG_IRAM static bool mg_mcxw71_write(void *addr, const void *buf, size_t len) {
|
||||||
|
uintptr_t dst = (uintptr_t) addr;
|
||||||
|
const volatile uint8_t *src = (const volatile uint8_t *) buf;
|
||||||
|
uint32_t irq, status = 0;
|
||||||
|
if (dst >= s_mg_flash_mcxw71.size || len > s_mg_flash_mcxw71.size - dst ||
|
||||||
|
(dst % 16) != 0 || (len % 16) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
irq = __get_PRIMASK();
|
||||||
|
__disable_irq();
|
||||||
|
while (len > 0 && status == 0) {
|
||||||
|
uint32_t words[32]; // ROM needs a word-aligned source in RAM
|
||||||
|
volatile uint8_t *p = (volatile uint8_t *) words;
|
||||||
|
size_t i, n = (dst % 128) == 0 && len >= 128 ? 128 : 16;
|
||||||
|
for (i = 0; i < n; i++) p[i] = src[i];
|
||||||
|
|
||||||
|
// Match the MCUXpresso ROM wrappers' cache maintenance. Interrupts stay
|
||||||
|
// masked until the flash command completes and stale cache data is gone.
|
||||||
|
MCM->CPCR2 |= MCM_CPCR2_CCBC_MASK;
|
||||||
|
__DSB();
|
||||||
|
__ISB();
|
||||||
|
if ((dst % 8192) == 0) {
|
||||||
|
status = MG_MCXW71_FLASH_API->erase(
|
||||||
|
&s_mcxw71_config, FMU0, (uint32_t) dst, 8192, MG_MCXW71_ERASE_KEY);
|
||||||
|
}
|
||||||
|
if (status == 0) {
|
||||||
|
if (n == 128) {
|
||||||
|
status = MG_MCXW71_FLASH_API->page(&s_mcxw71_config, FMU0,
|
||||||
|
(uint32_t) dst, words, 128);
|
||||||
|
} else {
|
||||||
|
status = MG_MCXW71_FLASH_API->phrase(&s_mcxw71_config, FMU0,
|
||||||
|
(uint32_t) dst, words, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MCM->CPCR2 |= MCM_CPCR2_CCBC_MASK;
|
||||||
|
__DSB();
|
||||||
|
__ISB();
|
||||||
|
dst += n, src += n, len -= n;
|
||||||
|
}
|
||||||
|
__set_PRIMASK(irq);
|
||||||
|
return status == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_mcxw71_swap(void) {
|
||||||
|
// The common helper verifies the staged image before the RAM copy below.
|
||||||
|
// A failed write must never commit, even if all expected bytes arrived.
|
||||||
|
return !s_mcxw71_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
MG_IRAM static void mg_mcxw71_apply(size_t size) {
|
||||||
|
size_t ofs, half = s_mg_flash_mcxw71.size / 2;
|
||||||
|
__disable_irq();
|
||||||
|
SysTick->CTRL = 0;
|
||||||
|
for (ofs = 0; ofs < size; ofs += 8192) {
|
||||||
|
if (!mg_mcxw71_write((void *) (uintptr_t) ofs,
|
||||||
|
(const void *) (uintptr_t) (half + ofs), 8192)) {
|
||||||
|
// The application may already be erased. Stay in RAM for debugger
|
||||||
|
// recovery rather than return to partially overwritten flash.
|
||||||
|
for (;;) __NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__DSB();
|
||||||
|
SCB->AIRCR = 0x05fa0004U;
|
||||||
|
__DSB();
|
||||||
|
for (;;) __NOP();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_ota_begin(size_t new_firmware_size) {
|
||||||
|
uint32_t status;
|
||||||
|
if (s_mcxw71_size != 0 || new_firmware_size == 0) return false;
|
||||||
|
status = MG_MCXW71_FLASH_API->init(&s_mcxw71_config);
|
||||||
|
if (status != 0 || s_mcxw71_config.mem[0].base != 0 ||
|
||||||
|
s_mcxw71_config.mem[0].size < 16384 ||
|
||||||
|
(s_mcxw71_config.mem[0].size % 16384) != 0) {
|
||||||
|
MG_ERROR(("Flash init failed: %lu", (unsigned long) status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s_mg_flash_mcxw71.size = s_mcxw71_config.mem[0].size;
|
||||||
|
if (!mg_ota_flash_begin(new_firmware_size, &s_mg_flash_mcxw71)) return false;
|
||||||
|
s_mcxw71_size = new_firmware_size;
|
||||||
|
s_mcxw71_received = s_mcxw71_pending = 0;
|
||||||
|
s_mcxw71_failed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_ota_write(const void *buf, size_t len) {
|
||||||
|
const uint8_t *p = (const uint8_t *) buf;
|
||||||
|
uint8_t *pending = (uint8_t *) s_mcxw71_buf;
|
||||||
|
if (s_mcxw71_size == 0 || s_mcxw71_failed) return false;
|
||||||
|
if (len > s_mcxw71_size - s_mcxw71_received || (len > 0 && buf == NULL)) {
|
||||||
|
s_mcxw71_failed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s_mcxw71_received += len;
|
||||||
|
if (s_mcxw71_pending > 0) {
|
||||||
|
size_t n = 16 - s_mcxw71_pending;
|
||||||
|
if (n > len) n = len;
|
||||||
|
if (n > 0) memcpy(pending + s_mcxw71_pending, p, n);
|
||||||
|
s_mcxw71_pending += n;
|
||||||
|
if (n > 0) p += n;
|
||||||
|
len -= n;
|
||||||
|
if (s_mcxw71_pending == 16) {
|
||||||
|
s_mcxw71_failed = !mg_ota_flash_write(pending, 16, &s_mg_flash_mcxw71);
|
||||||
|
s_mcxw71_pending = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!s_mcxw71_failed && len >= 16) {
|
||||||
|
size_t n = MG_ROUND_DOWN(len, 16);
|
||||||
|
s_mcxw71_failed = !mg_ota_flash_write(p, n, &s_mg_flash_mcxw71);
|
||||||
|
p += n, len -= n;
|
||||||
|
}
|
||||||
|
if (!s_mcxw71_failed && len > 0) {
|
||||||
|
memcpy(pending + s_mcxw71_pending, p, len);
|
||||||
|
s_mcxw71_pending += len;
|
||||||
|
}
|
||||||
|
return !s_mcxw71_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_ota_end(void) {
|
||||||
|
bool ok;
|
||||||
|
size_t size = s_mcxw71_size;
|
||||||
|
if (size == 0) return false;
|
||||||
|
if (s_mcxw71_received != size) s_mcxw71_failed = true;
|
||||||
|
if (!s_mcxw71_failed && s_mcxw71_pending > 0) {
|
||||||
|
s_mcxw71_failed =
|
||||||
|
!mg_ota_flash_write(s_mcxw71_buf, s_mcxw71_pending, &s_mg_flash_mcxw71);
|
||||||
|
}
|
||||||
|
ok = mg_ota_flash_end(&s_mg_flash_mcxw71);
|
||||||
|
s_mcxw71_size = s_mcxw71_pending = 0;
|
||||||
|
if (ok) {
|
||||||
|
MG_INFO(("Applying %lu bytes from staging flash", (unsigned long) size));
|
||||||
|
mg_mcxw71_apply(size);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+5
-7
@@ -291,24 +291,22 @@ MG_IRAM static bool mg_frdm_write(void *addr, const void *buf, size_t len) {
|
|||||||
}
|
}
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_frdm.start;
|
uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_frdm.start;
|
||||||
|
size_t tmp_buf_size = s_mg_flash_frdm.align / sizeof(uint32_t);
|
||||||
|
uint32_t tmp[tmp_buf_size];
|
||||||
if ((char *) buf >= (char *) s_mg_flash_frdm.start &&
|
if ((char *) buf >= (char *) s_mg_flash_frdm.start &&
|
||||||
(char *) buf <
|
(char *) buf <
|
||||||
(char *) (s_mg_flash_frdm.start + s_mg_flash_frdm.size)) {
|
(char *) (s_mg_flash_frdm.start + s_mg_flash_frdm.size)) {
|
||||||
// If we copy from FLASH to FLASH, then we first need to copy the source
|
// If we copy from FLASH to FLASH, then we first need to copy the source
|
||||||
// to RAM
|
// to RAM
|
||||||
size_t tmp_buf_size = s_mg_flash_frdm.align / sizeof(uint32_t);
|
|
||||||
uint32_t tmp[tmp_buf_size];
|
|
||||||
|
|
||||||
for (size_t i = 0; i < tmp_buf_size; i++) {
|
for (size_t i = 0; i < tmp_buf_size; i++) {
|
||||||
flash_wait();
|
flash_wait();
|
||||||
tmp[i] = src[i];
|
tmp[i] = src[i];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(tmp, src, sizeof(tmp));
|
||||||
|
}
|
||||||
status = flexspi_nor->page_program(MG_FLEXSPI_NOR_INSTANCE, &config,
|
status = flexspi_nor->page_program(MG_FLEXSPI_NOR_INSTANCE, &config,
|
||||||
(uint32_t) dst_ofs, tmp, false);
|
(uint32_t) dst_ofs, tmp, false);
|
||||||
} else {
|
|
||||||
status = flexspi_nor->page_program(MG_FLEXSPI_NOR_INSTANCE, &config,
|
|
||||||
(uint32_t) dst_ofs, src, false);
|
|
||||||
}
|
|
||||||
src = (uint32_t *) ((char *) src + s_mg_flash_frdm.align);
|
src = (uint32_t *) ((char *) src + s_mg_flash_frdm.align);
|
||||||
dst = (uint32_t *) ((char *) dst + s_mg_flash_frdm.align);
|
dst = (uint32_t *) ((char *) dst + s_mg_flash_frdm.align);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
|
|
||||||
#if MG_OTA == MG_OTA_STM32H5
|
#if MG_OTA == MG_OTA_STM32H5 || MG_OTA == MG_OTA_STM32C5
|
||||||
|
|
||||||
static bool mg_stm32h5_write(void *, const void *, size_t);
|
static bool mg_stm32h5_write(void *, const void *, size_t);
|
||||||
static bool mg_stm32h5_swap(void);
|
static bool mg_stm32h5_swap(void);
|
||||||
|
|||||||
+3
-1
@@ -71,8 +71,10 @@ char *mg_mprintf(const char *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_pfn_stdout(char c, void *param) {
|
void mg_pfn_stdout(char c, void *param) {
|
||||||
|
#if MG_ENABLE_POSIX_FS
|
||||||
putchar(c);
|
putchar(c);
|
||||||
(void) param;
|
#endif
|
||||||
|
(void) param, (void) c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) {
|
static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ uint64_t mg_now(void);
|
|||||||
// MG_INFO(("every second"));
|
// MG_INFO(("every second"));
|
||||||
// }
|
// }
|
||||||
// Full examples:
|
// Full examples:
|
||||||
// tutorials/http/http-server/arduino/teensy41-http,
|
|
||||||
// tutorials/mqtt/mqtt-client
|
// tutorials/mqtt/mqtt-client
|
||||||
// Related APIs:
|
// Related APIs:
|
||||||
// mg_millis(), mg_now()
|
// mg_millis(), mg_now()
|
||||||
|
|||||||
@@ -772,13 +772,8 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
|
|
||||||
for (c = mgr->conns; c != NULL; c = tmp) {
|
for (c = mgr->conns; c != NULL; c = tmp) {
|
||||||
long flush = 0;
|
long flush = 0;
|
||||||
bool is_resp = c->is_resp;
|
|
||||||
tmp = c->next;
|
tmp = c->next;
|
||||||
mg_call(c, MG_EV_POLL, &now);
|
mg_call(c, MG_EV_POLL, &now);
|
||||||
if (is_resp && !c->is_resp) {
|
|
||||||
long n = 0;
|
|
||||||
mg_call(c, MG_EV_READ, &n);
|
|
||||||
}
|
|
||||||
MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id,
|
MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id,
|
||||||
c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-',
|
c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-',
|
||||||
c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c',
|
c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c',
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ void mg_tls_ctx_free(struct mg_mgr *);
|
|||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
// Extracts a 32-byte P-256 private scalar from EC PRIVATE KEY or PKCS#8 PRIVATE
|
// Extracts a 32-byte P-256 private scalar from EC PRIVATE KEY or PKCS#8 PRIVATE
|
||||||
// KEY input. Accepts PEM or DER. Returns key size, or 0 on error.
|
// KEY input. Accepts PEM or DER. Returns key size, or 0 on error.
|
||||||
size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len);
|
size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Low-level IO primitives used by TLS layer
|
// Low-level IO primitives used by TLS layer
|
||||||
|
|||||||
+254
-165
@@ -6,8 +6,8 @@
|
|||||||
#include "tls_aes128.h"
|
#include "tls_aes128.h"
|
||||||
#include "tls_chacha20.h"
|
#include "tls_chacha20.h"
|
||||||
#include "tls_rsa.h"
|
#include "tls_rsa.h"
|
||||||
#include "tls_uecc.h"
|
#include "tls_p256.h"
|
||||||
#include "tls_x25519.h"
|
#include "tls_p384.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
@@ -61,6 +61,7 @@ enum mg_tls_hs_state {
|
|||||||
|
|
||||||
// Server state machine:
|
// Server state machine:
|
||||||
MG_TLS_STATE_SERVER_START, // Wait for ClientHello
|
MG_TLS_STATE_SERVER_START, // Wait for ClientHello
|
||||||
|
MG_TLS_STATE_SERVER_RETRY, // Wait for ClientHello after HelloRetry
|
||||||
MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate
|
MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate
|
||||||
MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify
|
MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify
|
||||||
MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish
|
MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish
|
||||||
@@ -106,8 +107,9 @@ struct tls_data {
|
|||||||
|
|
||||||
uint8_t random[32]; // client random from ClientHello
|
uint8_t random[32]; // client random from ClientHello
|
||||||
uint8_t session_id[32]; // client session ID between the handshake states
|
uint8_t session_id[32]; // client session ID between the handshake states
|
||||||
uint8_t x25519_cli[32]; // client X25519 key between the handshake states
|
uint8_t p256_cli[32]; // client P-256 key between the handshake states
|
||||||
uint8_t x25519_sec[32]; // x25519 secret between the handshake states
|
uint8_t p256_peer[64]; // server: client P-256 public key X || Y
|
||||||
|
uint8_t ecdhe_sec[32]; // P-256 ECDHE shared secret between the states
|
||||||
|
|
||||||
bool skip_verification; // do not perform checks on server certificate
|
bool skip_verification; // do not perform checks on server certificate
|
||||||
bool cert_requested; // client received a CertificateRequest
|
bool cert_requested; // client received a CertificateRequest
|
||||||
@@ -130,6 +132,9 @@ struct tls_data {
|
|||||||
// verify cert
|
// verify cert
|
||||||
size_t pubkeysz; // size of the server public key
|
size_t pubkeysz; // size of the server public key
|
||||||
uint8_t sighash[32]; // calculated signature verification hash
|
uint8_t sighash[32]; // calculated signature verification hash
|
||||||
|
uint8_t sighash384[48]; // same, SHA-384, for ecdsa_secp384r1_sha384
|
||||||
|
uint64_t hs_start; // mg_millis() when the handshake started
|
||||||
|
uint64_t hs_proc; // ms spent processing handshake messages
|
||||||
|
|
||||||
struct tls_enc enc; // actual keys in use at this time
|
struct tls_enc enc; // actual keys in use at this time
|
||||||
struct tls_enc app_keys; // storage during two-way auth handshake
|
struct tls_enc app_keys; // storage during two-way auth handshake
|
||||||
@@ -321,7 +326,7 @@ static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz,
|
|||||||
memmove(hash, secret, hashsz);
|
memmove(hash, secret, hashsz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point we have x25519 shared secret, we can generate a set of derived
|
// at this point we have ECDHE shared secret, we can generate a set of derived
|
||||||
// handshake encryption keys
|
// handshake encryption keys
|
||||||
static void mg_tls_generate_handshake_keys(struct mg_connection *c) {
|
static void mg_tls_generate_handshake_keys(struct mg_connection *c) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
@@ -342,8 +347,11 @@ static void mg_tls_generate_handshake_keys(struct mg_connection *c) {
|
|||||||
mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest,
|
mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest,
|
||||||
32, pre_extract_secret, 32);
|
32, pre_extract_secret, 32);
|
||||||
mg_hmac_sha256(tls->enc.handshake_secret, pre_extract_secret,
|
mg_hmac_sha256(tls->enc.handshake_secret, pre_extract_secret,
|
||||||
sizeof(pre_extract_secret), tls->x25519_sec,
|
sizeof(pre_extract_secret), tls->ecdhe_sec,
|
||||||
sizeof(tls->x25519_sec));
|
sizeof(tls->ecdhe_sec));
|
||||||
|
// ECDHE keys and secret are not needed anymore, wipe for forward secrecy
|
||||||
|
mg_bzero(tls->p256_cli, sizeof(tls->p256_cli));
|
||||||
|
mg_bzero(tls->ecdhe_sec, sizeof(tls->ecdhe_sec));
|
||||||
mg_tls_hexdump("hs secret", tls->enc.handshake_secret, 32);
|
mg_tls_hexdump("hs secret", tls->enc.handshake_secret, 32);
|
||||||
|
|
||||||
// mg_sha256_final is not idempotent, need to copy sha256 context to calculate
|
// mg_sha256_final is not idempotent, need to copy sha256 context to calculate
|
||||||
@@ -461,7 +469,7 @@ static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
|||||||
c->is_client ? tls->enc.client_write_iv : tls->enc.server_write_iv;
|
c->is_client ? tls->enc.client_write_iv : tls->enc.server_write_iv;
|
||||||
|
|
||||||
if (msgsz > 16384) {
|
if (msgsz > 16384) {
|
||||||
MG_ERROR(("msg longer than recordsz"));
|
mg_error(c, "msg longer than recordsz");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +514,8 @@ static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
|||||||
|
|
||||||
static void verbose_alert(uint8_t *buf) {
|
static void verbose_alert(uint8_t *buf) {
|
||||||
uint8_t level = buf[0], desc = buf[1];
|
uint8_t level = buf[0], desc = buf[1];
|
||||||
MG_INFO(("TLS ALERT received: level=%d, desc=%d (%s)", level, desc,
|
MG_LOG(desc == 0 ? MG_LL_VERBOSE : MG_LL_INFO, // close_notify is normal
|
||||||
|
("TLS ALERT received: level=%d, desc=%d (%s)", level, desc,
|
||||||
desc == 0 ? "close_notify"
|
desc == 0 ? "close_notify"
|
||||||
: desc == 10 ? "unexpected_message"
|
: desc == 10 ? "unexpected_message"
|
||||||
: desc == 20 ? "bad_record_mac"
|
: desc == 20 ? "bad_record_mac"
|
||||||
@@ -626,8 +635,11 @@ static int mg_tls_recv_record(struct mg_connection *c) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash the CertificateVerify content: SHA-256 into hash, and if hash384 is not
|
||||||
|
// NULL, SHA-384 into hash384 for ecdsa_secp384r1_sha384
|
||||||
static void mg_tls_calc_cert_verify_hash(struct mg_connection *c,
|
static void mg_tls_calc_cert_verify_hash(struct mg_connection *c,
|
||||||
uint8_t hash[32], bool is_client) {
|
uint8_t hash[32], uint8_t *hash384,
|
||||||
|
bool is_client) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
uint8_t sig_content[130];
|
uint8_t sig_content[130];
|
||||||
mg_sha256_ctx sha256;
|
mg_sha256_ctx sha256;
|
||||||
@@ -647,9 +659,11 @@ static void mg_tls_calc_cert_verify_hash(struct mg_connection *c,
|
|||||||
mg_sha256_init(&sha256);
|
mg_sha256_init(&sha256);
|
||||||
mg_sha256_update(&sha256, sig_content, sizeof(sig_content));
|
mg_sha256_update(&sha256, sig_content, sizeof(sig_content));
|
||||||
mg_sha256_final(hash, &sha256);
|
mg_sha256_final(hash, &sha256);
|
||||||
|
if (hash384 != NULL) mg_sha384(hash384, sig_content, sizeof(sig_content));
|
||||||
}
|
}
|
||||||
|
|
||||||
// read and parse ClientHello record
|
// read and parse ClientHello record. Return 0 if it has a P-256 key share,
|
||||||
|
// 1 if it does not and HelloRetryRequest must be sent, negative otherwise
|
||||||
static int mg_tls_server_recv_hello(struct mg_connection *c) {
|
static int mg_tls_server_recv_hello(struct mg_connection *c) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
struct mg_iobuf *rio = &c->rtls;
|
struct mg_iobuf *rio = &c->rtls;
|
||||||
@@ -659,9 +673,13 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
|
|||||||
uint16_t ext_len;
|
uint16_t ext_len;
|
||||||
uint8_t *ext;
|
uint8_t *ext;
|
||||||
uint16_t msgsz;
|
uint16_t msgsz;
|
||||||
|
bool have_p256 = false;
|
||||||
|
bool is_retry = tls->state == MG_TLS_STATE_SERVER_RETRY;
|
||||||
|
|
||||||
if (!mg_tls_got_record(c)) {
|
for (;;) {
|
||||||
return MG_IO_WAIT;
|
if (!mg_tls_got_record(c)) return MG_IO_WAIT;
|
||||||
|
if (!is_retry || rio->buf[0] != MG_TLS_CHANGE_CIPHER) break;
|
||||||
|
mg_tls_drop_record(c); // compatibility mode CCS before 2nd ClientHello
|
||||||
}
|
}
|
||||||
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
|
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
|
||||||
mg_error(c, "not a client hello packet");
|
mg_error(c, "not a client hello packet");
|
||||||
@@ -705,16 +723,21 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
for (k = 0; k < key_exchange_len;) {
|
for (k = 0; k < key_exchange_len;) {
|
||||||
uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
|
uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
|
||||||
|
uint16_t group = MG_LOAD_BE16(key_exchange + k);
|
||||||
if (((uint32_t) m + k + 4) > key_exchange_len) goto fail;
|
if (((uint32_t) m + k + 4) > key_exchange_len) goto fail;
|
||||||
if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) {
|
// secp256r1, uncompressed point 0x04 || X || Y
|
||||||
memmove(tls->x25519_cli, key_exchange + k + 4, m);
|
if (group == 0x0017 && m == 65 && key_exchange[k + 4] == 0x04) {
|
||||||
mg_tls_drop_record(c);
|
memmove(tls->p256_peer, key_exchange + k + 5, 64);
|
||||||
return 0;
|
have_p256 = true;
|
||||||
}
|
}
|
||||||
k += (uint16_t) (m + 4);
|
k += (uint16_t) (m + 4);
|
||||||
}
|
}
|
||||||
j += (uint16_t) (n + 4);
|
j += (uint16_t) (n + 4);
|
||||||
}
|
}
|
||||||
|
if (have_p256 || !is_retry) { // No P-256 share: ask for it, but only once
|
||||||
|
mg_tls_drop_record(c);
|
||||||
|
return have_p256 ? 0 : 1;
|
||||||
|
}
|
||||||
fail:
|
fail:
|
||||||
mg_error(c, "bad client hello");
|
mg_error(c, "bad client hello");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -728,58 +751,98 @@ fail:
|
|||||||
static bool mg_tls_server_send_hello(struct mg_connection *c) {
|
static bool mg_tls_server_send_hello(struct mg_connection *c) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
struct mg_iobuf *wio = &tls->send;
|
struct mg_iobuf *wio = &tls->send;
|
||||||
|
uint8_t hello[155], pub[65], prv[32], hdr[5] = {0x16, 0x03, 0x03, 0, 0};
|
||||||
|
size_t n = sizeof(hello); // ServerHello length, including 4-byte header
|
||||||
|
|
||||||
// clang-format off
|
// calculate P-256 keyshare and the shared secret
|
||||||
uint8_t msg_server_hello[122] = {
|
pub[0] = 0x04; // uncompressed point 0x04 || X || Y
|
||||||
// server hello, tls 1.2
|
if (!mg_p256_make_key(pub + 1, prv) ||
|
||||||
0x02, 0x00, 0x00, 0x76, 0x03, 0x03,
|
!mg_p256_shared_secret(tls->p256_peer, prv, tls->ecdhe_sec)) {
|
||||||
// random (32 bytes)
|
mg_bzero(prv, sizeof(prv));
|
||||||
PLACEHOLDER_32B,
|
|
||||||
// session ID length + session ID (32 bytes)
|
|
||||||
0x20, PLACEHOLDER_32B,
|
|
||||||
#if MG_ENABLE_CHACHA20
|
|
||||||
// TLS_CHACHA20_POLY1305_SHA256 + no compression
|
|
||||||
0x13, 0x03, 0x00,
|
|
||||||
#else
|
|
||||||
// TLS_AES_128_GCM_SHA256 + no compression
|
|
||||||
0x13, 0x01, 0x00,
|
|
||||||
#endif
|
|
||||||
// extensions + keyshare
|
|
||||||
0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
|
|
||||||
// x25519 keyshare
|
|
||||||
PLACEHOLDER_32B,
|
|
||||||
// supported versions (tls1.3 == 0x304)
|
|
||||||
0x00, 0x2b, 0x00, 0x02, 0x03, 0x04};
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
// calculate keyshare
|
|
||||||
uint8_t x25519_pub[X25519_BYTES];
|
|
||||||
uint8_t x25519_prv[X25519_BYTES];
|
|
||||||
if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG");
|
|
||||||
if( mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1) < 0 || mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1) < 0) {
|
|
||||||
mg_error(c, "bad key");
|
mg_error(c, "bad key");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec));
|
mg_bzero(prv, sizeof(prv));
|
||||||
|
mg_tls_hexdump("s ecdhe sec", tls->ecdhe_sec, sizeof(tls->ecdhe_sec));
|
||||||
|
|
||||||
// fill in the gaps: random + session ID + keyshare
|
hello[0] = 0x02; // server hello, tls 1.2
|
||||||
memmove(msg_server_hello + 6, tls->random, sizeof(tls->random));
|
MG_STORE_BE24(hello + 1, n - 4);
|
||||||
memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id));
|
hello[4] = 0x03, hello[5] = 0x03;
|
||||||
memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub));
|
if (!mg_random(hello + 6, 32)) { // server random, RFC 8446 4.1.3
|
||||||
|
mg_error(c, "RNG");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hello[38] = 0x20; // session ID length + session ID (32 bytes)
|
||||||
|
memmove(hello + 39, tls->session_id, sizeof(tls->session_id));
|
||||||
|
#if MG_ENABLE_CHACHA20
|
||||||
|
hello[71] = 0x13, hello[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256
|
||||||
|
#else
|
||||||
|
hello[71] = 0x13, hello[72] = 0x01; // TLS_AES_128_GCM_SHA256
|
||||||
|
#endif
|
||||||
|
hello[73] = 0x00; // no compression
|
||||||
|
MG_STORE_BE16(hello + 74, 65 + 14); // extensions length
|
||||||
|
MG_STORE_BE16(hello + 76, 0x0033); // key share
|
||||||
|
MG_STORE_BE16(hello + 78, 65 + 4);
|
||||||
|
MG_STORE_BE16(hello + 80, 0x0017); // secp256r1
|
||||||
|
MG_STORE_BE16(hello + 82, 65);
|
||||||
|
memmove(hello + 84, pub, sizeof(pub));
|
||||||
|
memmove(hello + 149, "\x00\x2b\x00\x02\x03\x04", 6); // tls1.3 == 0x304
|
||||||
|
MG_STORE_BE16(hdr + 3, n);
|
||||||
|
|
||||||
// server hello message
|
// server hello message
|
||||||
if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 ||
|
if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 ||
|
||||||
mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) ==
|
mg_iobuf_add(wio, wio->len, hello, n) == 0)
|
||||||
0)
|
|
||||||
return false;
|
return false;
|
||||||
mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello));
|
mg_sha256_update(&tls->sha256, hello, n);
|
||||||
|
|
||||||
// change cipher message
|
// change cipher message, unless it was already sent after HelloRetryRequest
|
||||||
if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0)
|
if (tls->state != MG_TLS_STATE_SERVER_RETRY &&
|
||||||
|
mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HelloRetryRequest random, SHA-256 of "HelloRetryRequest", RFC 8446 4.1.3
|
||||||
|
static const uint8_t hrr_random[32] = {
|
||||||
|
0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c,
|
||||||
|
0x02, 0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb,
|
||||||
|
0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c};
|
||||||
|
|
||||||
|
// put HelloRetryRequest record into wio buffer, asking for a P-256 key share.
|
||||||
|
// Replace ClientHello in the transcript with message_hash, RFC 8446 4.4.1
|
||||||
|
static bool mg_tls_server_send_hrr(struct mg_connection *c) {
|
||||||
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
|
struct mg_iobuf *wio = &tls->send;
|
||||||
|
uint8_t hrr[88], hdr[5] = {0x16, 0x03, 0x03, 0, sizeof(hrr)};
|
||||||
|
uint8_t mh[4 + 32] = {0xfe, 0, 0, 32}; // message_hash(ClientHello1)
|
||||||
|
|
||||||
|
hrr[0] = 0x02; // server hello, tls 1.2
|
||||||
|
MG_STORE_BE24(hrr + 1, sizeof(hrr) - 4);
|
||||||
|
hrr[4] = 0x03, hrr[5] = 0x03;
|
||||||
|
memmove(hrr + 6, hrr_random, sizeof(hrr_random));
|
||||||
|
hrr[38] = 0x20; // session ID length + session ID (32 bytes)
|
||||||
|
memmove(hrr + 39, tls->session_id, sizeof(tls->session_id));
|
||||||
|
#if MG_ENABLE_CHACHA20
|
||||||
|
hrr[71] = 0x13, hrr[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256
|
||||||
|
#else
|
||||||
|
hrr[71] = 0x13, hrr[72] = 0x01; // TLS_AES_128_GCM_SHA256
|
||||||
|
#endif
|
||||||
|
hrr[73] = 0x00; // no compression
|
||||||
|
// extensions: supported versions tls1.3, key share secp256r1 (group only)
|
||||||
|
memmove(hrr + 74, "\x00\x0c\x00\x2b\x00\x02\x03\x04\x00\x33\x00\x02\x00\x17",
|
||||||
|
14);
|
||||||
|
|
||||||
|
mg_sha256_final(mh + 4, &tls->sha256);
|
||||||
|
mg_sha256_init(&tls->sha256);
|
||||||
|
mg_sha256_update(&tls->sha256, mh, sizeof(mh));
|
||||||
|
mg_sha256_update(&tls->sha256, hrr, sizeof(hrr));
|
||||||
|
|
||||||
|
// HelloRetryRequest message, followed by change cipher message
|
||||||
|
return mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) != 0 &&
|
||||||
|
mg_iobuf_add(wio, wio->len, hrr, sizeof(hrr)) != 0 &&
|
||||||
|
mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool mg_tls_server_send_ext(struct mg_connection *c) {
|
static bool mg_tls_server_send_ext(struct mg_connection *c) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
// server extensions
|
// server extensions
|
||||||
@@ -788,10 +851,11 @@ static bool mg_tls_server_send_ext(struct mg_connection *c) {
|
|||||||
return mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
|
return mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// signature algorithms we actually support:
|
// signature algorithms we actually support: ecdsa_secp256r1_sha256,
|
||||||
// rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256
|
// ecdsa_secp384r1_sha384, rsa_pss_rsae_sha256, rsa_pkcs1_sha256
|
||||||
static const uint8_t secp256r1_sig_algs[12] = {
|
static const uint8_t secp256r1_sig_algs[] = {
|
||||||
0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01};
|
0x00, 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x03,
|
||||||
|
0x05, 0x03, 0x08, 0x04, 0x04, 0x01};
|
||||||
|
|
||||||
static bool mg_tls_server_send_cert_request(struct mg_connection *c) {
|
static bool mg_tls_server_send_cert_request(struct mg_connection *c) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
@@ -863,28 +927,6 @@ static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// type adapter between uECC hash context and our sha256 implementation
|
|
||||||
typedef struct SHA256_HashContext {
|
|
||||||
MG_UECC_HashContext uECC;
|
|
||||||
mg_sha256_ctx ctx;
|
|
||||||
} SHA256_HashContext;
|
|
||||||
|
|
||||||
static void init_SHA256(const MG_UECC_HashContext *base) {
|
|
||||||
SHA256_HashContext *c = (SHA256_HashContext *) base;
|
|
||||||
mg_sha256_init(&c->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_SHA256(const MG_UECC_HashContext *base,
|
|
||||||
const uint8_t *message, unsigned message_size) {
|
|
||||||
SHA256_HashContext *c = (SHA256_HashContext *) base;
|
|
||||||
mg_sha256_update(&c->ctx, message, message_size);
|
|
||||||
}
|
|
||||||
static void finish_SHA256(const MG_UECC_HashContext *base,
|
|
||||||
uint8_t *hash_result) {
|
|
||||||
SHA256_HashContext *c = (SHA256_HashContext *) base;
|
|
||||||
mg_sha256_final(hash_result, &c->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mg_tls_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed,
|
static void mg_tls_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed,
|
||||||
size_t seed_len) {
|
size_t seed_len) {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
@@ -1071,7 +1113,7 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
|||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
uint8_t hash[32] = {0};
|
uint8_t hash[32] = {0};
|
||||||
|
|
||||||
mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash, is_client);
|
mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash, NULL, is_client);
|
||||||
|
|
||||||
if (tls->rsa.n.len > 0 && tls->rsa.d.len > 0) {
|
if (tls->rsa.n.len > 0 && tls->rsa.d.len > 0) {
|
||||||
// RSA certificate verify packet
|
// RSA certificate verify packet
|
||||||
@@ -1088,19 +1130,19 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
|||||||
|
|
||||||
// Check bounds
|
// Check bounds
|
||||||
if (emlen > sizeof(em) || verifysz > sizeof(verify)) {
|
if (emlen > sizeof(em) || verifysz > sizeof(verify)) {
|
||||||
MG_ERROR(("RSA key too large for static buffers"));
|
mg_error(c, "RSA key too large for static buffers");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mg_tls_pss_encode(hash, sizeof(hash), &rn, em)) {
|
if (!mg_tls_pss_encode(hash, sizeof(hash), &rn, em)) {
|
||||||
MG_ERROR(("Failed PSS encode"));
|
mg_error(c, "Failed PSS encode");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate PSS encoded message format
|
// Validate PSS encoded message format
|
||||||
if (em[emlen - 1] != 0xbc) {
|
if (em[emlen - 1] != 0xbc) {
|
||||||
MG_ERROR(("Invalid PSS encoding: last byte is 0x%02x, expected 0xbc",
|
mg_error(c, "Invalid PSS encoding: last byte is 0x%02x, expected 0xbc",
|
||||||
em[emlen - 1]));
|
em[emlen - 1]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1113,7 +1155,7 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
|||||||
// Sign directly into the verify buffer (verify + 8 = signature location)
|
// Sign directly into the verify buffer (verify + 8 = signature location)
|
||||||
memset(verify + 8, 0, emlen); // Initialize signature area
|
memset(verify + 8, 0, emlen); // Initialize signature area
|
||||||
if (!mg_tls_rsa_sign(tls, em, emlen, verify + 8)) {
|
if (!mg_tls_rsa_sign(tls, em, emlen, verify + 8)) {
|
||||||
MG_ERROR(("Failed RSA sign"));
|
mg_error(c, "Failed RSA sign");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1127,15 +1169,13 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
|||||||
} else {
|
} else {
|
||||||
// EC certificate verify packet
|
// EC certificate verify packet
|
||||||
uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
|
uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
|
||||||
uint8_t tmp[2 * 32 + 64] = {0};
|
|
||||||
struct SHA256_HashContext ctx = {
|
|
||||||
{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp},
|
|
||||||
{{0}, 0, 0, {0}}};
|
|
||||||
size_t sigsz, verifysz = 0;
|
size_t sigsz, verifysz = 0;
|
||||||
int neg1, neg2;
|
int neg1, neg2;
|
||||||
uint8_t sig[64] = {0};
|
uint8_t sig[64] = {0};
|
||||||
mg_uecc_sign_deterministic(tls->ec_key, hash, sizeof(hash), &ctx.uECC, sig,
|
if (!mg_p256_sign(tls->ec_key, hash, sizeof(hash), sig)) {
|
||||||
mg_uecc_secp256r1());
|
mg_error(c, "P-256 sign");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
neg1 = !!(sig[0] & 0x80);
|
neg1 = !!(sig[0] & 0x80);
|
||||||
neg2 = !!(sig[32] & 0x80);
|
neg2 = !!(sig[32] & 0x80);
|
||||||
@@ -1198,8 +1238,6 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
|
|||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
struct mg_iobuf *wio = &tls->send;
|
struct mg_iobuf *wio = &tls->send;
|
||||||
|
|
||||||
uint8_t x25519_pub[X25519_BYTES];
|
|
||||||
|
|
||||||
// - "signature algorithms we actually support", see above
|
// - "signature algorithms we actually support", see above
|
||||||
// uint8_t secp256r1_sig_algs[]
|
// uint8_t secp256r1_sig_algs[]
|
||||||
// - all popular signature algorithms (if we don't care about verification)
|
// - all popular signature algorithms (if we don't care about verification)
|
||||||
@@ -1211,11 +1249,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
|
|||||||
0xfe, 0x00, 0x00, 0xfe};
|
0xfe, 0x00, 0x00, 0xfe};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
uint8_t msg_client_hello[145] = {
|
uint8_t msg_client_hello[178] = {
|
||||||
// TLS Client Hello header reported as TLS1.2 (5)
|
// TLS Client Hello header reported as TLS1.2 (5)
|
||||||
0x16, 0x03, 0x03, 0x00, 0xfe,
|
0x16, 0x03, 0x03, 0x00, 0xd8,
|
||||||
// client hello, tls 1.2 (6)
|
// client hello, tls 1.2 (6)
|
||||||
0x01, 0x00, 0x00, 0x8c, 0x03, 0x03,
|
0x01, 0x00, 0x00, 0xd4, 0x03, 0x03,
|
||||||
// random (32 bytes)
|
// random (32 bytes)
|
||||||
PLACEHOLDER_32B,
|
PLACEHOLDER_32B,
|
||||||
// session ID length + session ID (32 bytes)
|
// session ID length + session ID (32 bytes)
|
||||||
@@ -1232,15 +1270,15 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
|
|||||||
0x01, 0x00,
|
0x01, 0x00,
|
||||||
// extensions + keyshare
|
// extensions + keyshare
|
||||||
0x00, 0xfe,
|
0x00, 0xfe,
|
||||||
// x25519 keyshare
|
// keyshare: secp256r1, uncompressed point
|
||||||
0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
|
0x00, 0x33, 0x00, 0x47, 0x00, 0x45,
|
||||||
PLACEHOLDER_32B,
|
0x00, 0x17, 0x00, 0x41, 0x04, PLACEHOLDER_32B, PLACEHOLDER_32B,
|
||||||
// supported groups (x25519)
|
// supported groups (secp256r1)
|
||||||
0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x1d,
|
0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x17,
|
||||||
// supported versions (tls1.3 == 0x304)
|
// supported versions (tls1.3 == 0x304)
|
||||||
0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04,
|
0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04,
|
||||||
// session ticket (none)
|
// session ticket (none)
|
||||||
0x00, 0x23, 0x00, 0x00, // 144 bytes till here
|
0x00, 0x23, 0x00, 0x00, // 177 bytes till here
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
const char *hostname = tls->hostname;
|
const char *hostname = tls->hostname;
|
||||||
@@ -1253,11 +1291,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
|
|||||||
|
|
||||||
// patch ClientHello with correct hostname ext length (if any)
|
// patch ClientHello with correct hostname ext length (if any)
|
||||||
MG_STORE_BE16(msg_client_hello + 3,
|
MG_STORE_BE16(msg_client_hello + 3,
|
||||||
hostname_extsz + 183 - 9 - 34 + sig_alg_sz);
|
hostname_extsz + 216 - 9 - 34 + sig_alg_sz);
|
||||||
MG_STORE_BE16(msg_client_hello + 7,
|
MG_STORE_BE16(msg_client_hello + 7,
|
||||||
hostname_extsz + 179 - 9 - 34 + sig_alg_sz);
|
hostname_extsz + 212 - 9 - 34 + sig_alg_sz);
|
||||||
MG_STORE_BE16(msg_client_hello + 82,
|
MG_STORE_BE16(msg_client_hello + 82,
|
||||||
hostname_extsz + 104 - 9 - 34 + sig_alg_sz);
|
hostname_extsz + 137 - 9 - 34 + sig_alg_sz);
|
||||||
|
|
||||||
if (hostnamesz > 0) {
|
if (hostnamesz > 0) {
|
||||||
MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5);
|
MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5);
|
||||||
@@ -1265,16 +1303,20 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
|
|||||||
MG_STORE_BE16(server_name_ext + 7, hostnamesz);
|
MG_STORE_BE16(server_name_ext + 7, hostnamesz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate keyshare
|
// calc+fill keyshare; X || Y, sent as an uncompressed point 0x04 || X || Y
|
||||||
if (!mg_random(tls->x25519_cli, sizeof(tls->x25519_cli))) mg_error(c, "RNG");
|
if (!mg_p256_make_key(msg_client_hello + 95, tls->p256_cli)) {
|
||||||
mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1);
|
mg_error(c, "P-256 key");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// fill in the gaps: random + session ID + keyshare
|
// fill in the gaps: random + session ID
|
||||||
if (!mg_random(tls->session_id, sizeof(tls->session_id))) mg_error(c, "RNG");
|
if (!mg_random(tls->session_id, sizeof(tls->session_id)) ||
|
||||||
if (!mg_random(tls->random, sizeof(tls->random))) mg_error(c, "RNG");
|
!mg_random(tls->random, sizeof(tls->random))) {
|
||||||
|
mg_error(c, "RNG");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
memmove(msg_client_hello + 11, tls->random, sizeof(tls->random));
|
memmove(msg_client_hello + 11, tls->random, sizeof(tls->random));
|
||||||
memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id));
|
memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id));
|
||||||
memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub));
|
|
||||||
|
|
||||||
// client hello message
|
// client hello message
|
||||||
if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) ==
|
if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) ==
|
||||||
@@ -1344,17 +1386,20 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) {
|
|||||||
}
|
}
|
||||||
if (ext_len2 < (2 + 2 + 32)) goto fail;
|
if (ext_len2 < (2 + 2 + 32)) goto fail;
|
||||||
group = MG_LOAD_BE16(ext + j + 4);
|
group = MG_LOAD_BE16(ext + j + 4);
|
||||||
if (group != 0x001d) {
|
key_exchange_len = MG_LOAD_BE16(ext + j + 6);
|
||||||
|
key_exchange = ext + j + 8;
|
||||||
|
if (key_exchange_len > ext_len2 - 4) goto fail;
|
||||||
|
if (group != 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y
|
||||||
mg_error(c, "bad key exchange group");
|
mg_error(c, "bad key exchange group");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
key_exchange_len = MG_LOAD_BE16(ext + j + 6);
|
if (key_exchange_len != 65 || key_exchange[0] != 0x04 ||
|
||||||
key_exchange = ext + j + 8;
|
!mg_p256_shared_secret(key_exchange + 1, tls->p256_cli,
|
||||||
if (key_exchange_len != 32 || mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1) < 0) {
|
tls->ecdhe_sec)) { // Validates the point
|
||||||
mg_error(c, "bad key");
|
mg_error(c, "bad key");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
mg_tls_hexdump("c x25519 sec", tls->x25519_sec, 32);
|
// mg_tls_hexdump("c ecdhe sec", tls->ecdhe_sec, 32);
|
||||||
mg_tls_drop_record(c);
|
mg_tls_drop_record(c);
|
||||||
/* generate handshake keys */
|
/* generate handshake keys */
|
||||||
mg_tls_generate_handshake_keys(c);
|
mg_tls_generate_handshake_keys(c);
|
||||||
@@ -1679,7 +1724,6 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
|
|||||||
MG_ERROR(("cert verification error"));
|
MG_ERROR(("cert verification error"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#if MG_UECC_SUPPORTS_secp256r1
|
|
||||||
if (issuer->pubkey.len == 64) {
|
if (issuer->pubkey.len == 64) {
|
||||||
const uint32_t N = 32;
|
const uint32_t N = 32;
|
||||||
if (a.len > N) a.value += (a.len - N), a.len = N; // padding
|
if (a.len > N) a.value += (a.len - N), a.len = N; // padding
|
||||||
@@ -1688,24 +1732,19 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
|
|||||||
memmove(sig + (N - a.len), a.value, a.len);
|
memmove(sig + (N - a.len), a.value, a.len);
|
||||||
memset(sig + N, 0, N - b.len);
|
memset(sig + N, 0, N - b.len);
|
||||||
memmove(sig + N + (N - b.len), b.value, b.len);
|
memmove(sig + N + (N - b.len), b.value, b.len);
|
||||||
return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
|
return mg_p256_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
|
||||||
(unsigned) cert->tbshashsz, sig,
|
cert->tbshashsz, sig);
|
||||||
mg_uecc_secp256r1());
|
} else if (issuer->pubkey.len == 96) {
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
#if MG_UECC_SUPPORTS_secp384r1
|
|
||||||
if (issuer->pubkey.len == 96) {
|
|
||||||
const uint32_t N = 48;
|
const uint32_t N = 48;
|
||||||
if (a.len > N) a.value += (a.len - N), a.len = N;
|
if (a.len > N) a.value += (a.len - N), a.len = N; // padding
|
||||||
if (b.len > N) b.value += (b.len - N), b.len = N;
|
if (b.len > N) b.value += (b.len - N), b.len = N;
|
||||||
memmove(sig, a.value, N);
|
memset(sig, 0, N - a.len); // short encoding
|
||||||
memmove(sig + N, b.value, N);
|
memmove(sig + (N - a.len), a.value, a.len);
|
||||||
return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
|
memset(sig + N, 0, N - b.len);
|
||||||
(unsigned) cert->tbshashsz, sig,
|
memmove(sig + N + (N - b.len), b.value, b.len);
|
||||||
mg_uecc_secp384r1());
|
return mg_p384_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
|
||||||
} else
|
cert->tbshashsz, sig);
|
||||||
#endif
|
} else {
|
||||||
{
|
|
||||||
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
|
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1894,6 +1933,18 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
|||||||
} // else r = 0 or -2 => subj not found or pubkey not matching
|
} // else r = 0 or -2 => subj not found or pubkey not matching
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Single CA: accept the chain if the CA itself is in it, e.g. a
|
||||||
|
// cross-signed copy of the root. Same subject and same public key
|
||||||
|
// means the same trusted key, and the links below ci are verified
|
||||||
|
if (tls->ca_der.len > 0 && ca.subj.len == ci->subj.len &&
|
||||||
|
memcmp(ca.subj.value, ci->subj.value, ca.subj.len) == 0 &&
|
||||||
|
ca.pubkey.len == ci->pubkey.len &&
|
||||||
|
memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) {
|
||||||
|
found_ca = true;
|
||||||
|
MG_VERBOSE(("CA found in chain at depth %d", certnum - 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) {
|
if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) {
|
||||||
mg_error(c, "too many certificates in the chain");
|
mg_error(c, "too many certificates in the chain");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1928,7 +1979,7 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mg_tls_drop_message(c);
|
mg_tls_drop_message(c);
|
||||||
mg_tls_calc_cert_verify_hash(c, tls->sighash, !is_client);
|
mg_tls_calc_cert_verify_hash(c, tls->sighash, tls->sighash384, !is_client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1991,11 +2042,19 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
MG_VERBOSE(("certificate verification successful (RSA)"));
|
MG_VERBOSE(("certificate verification successful (RSA)"));
|
||||||
} else if (sigalg == 0x0403) { // ecdsa_secp256r1_sha256
|
} else if (sigalg == 0x0403 || sigalg == 0x0503) {
|
||||||
// Extract certificate signature and verify it using pubkey and sighash
|
// ecdsa_secp256r1_sha256 or ecdsa_secp384r1_sha384. Extract certificate
|
||||||
uint8_t sig[64];
|
// signature and verify it using pubkey and sighash
|
||||||
|
bool is384 = sigalg == 0x0503;
|
||||||
|
uint32_t N = is384 ? 48 : 32; // curve size: r and s are N bytes each
|
||||||
|
int ok;
|
||||||
|
uint8_t sig[96];
|
||||||
struct mg_der_tlv seq, r, s;
|
struct mg_der_tlv seq, r, s;
|
||||||
memset(sig, 0, 64);
|
memset(sig, 0, sizeof(sig));
|
||||||
|
if (tls->pubkeysz != 2 * N) {
|
||||||
|
mg_error(c, "certverify scheme %04x does not match the key", sigalg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) {
|
if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) {
|
||||||
mg_error(c, "verification message is not an ASN.1 DER sequence");
|
mg_error(c, "verification message is not an ASN.1 DER sequence");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2009,15 +2068,21 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Integers may be padded with zeroes
|
// Integers may be padded with zeroes
|
||||||
if (r.len > 32) r.value = r.value + (r.len - 32), r.len = 32;
|
if (r.len > N) r.value = r.value + (r.len - N), r.len = N;
|
||||||
if (s.len > 32) s.value = s.value + (s.len - 32), s.len = 32;
|
if (s.len > N) s.value = s.value + (s.len - N), s.len = N;
|
||||||
|
|
||||||
// r or s may be shorter than 32 bytes, "right-justify" (network order)
|
// r or s may be shorter than N bytes, "right-justify" (network order)
|
||||||
memmove(sig + (32 - r.len), r.value, r.len);
|
memmove(sig + (N - r.len), r.value, r.len);
|
||||||
memmove(sig + 32 + (32 - s.len), s.value, s.len);
|
memmove(sig + N + (N - s.len), s.value, s.len);
|
||||||
|
|
||||||
if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig,
|
if (is384) {
|
||||||
mg_uecc_secp256r1()) != 1) {
|
ok = mg_p384_verify(tls->pubkey, tls->sighash384,
|
||||||
|
sizeof(tls->sighash384), sig);
|
||||||
|
} else {
|
||||||
|
ok = mg_p256_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash),
|
||||||
|
sig);
|
||||||
|
}
|
||||||
|
if (ok != 1) {
|
||||||
mg_error(c, "failed to verify EC certificate (certverify)");
|
mg_error(c, "failed to verify EC certificate (certverify)");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2032,8 +2097,8 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
|
|||||||
// 0809 = rsa_pss_pss_sha256
|
// 0809 = rsa_pss_pss_sha256
|
||||||
// 080A = rsa_pss_pss_sha384
|
// 080A = rsa_pss_pss_sha384
|
||||||
// 080B = rsa_pss_pss_sha512
|
// 080B = rsa_pss_pss_sha512
|
||||||
MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes",
|
mg_error(c, "unsupported certverify signature scheme: %x of %d bytes",
|
||||||
sigalg, siglen));
|
sigalg, siglen);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2119,16 +2184,23 @@ static bool mg_tls_client_handshake(struct mg_connection *c) {
|
|||||||
|
|
||||||
static bool mg_tls_server_handshake(struct mg_connection *c) {
|
static bool mg_tls_server_handshake(struct mg_connection *c) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
|
int r;
|
||||||
switch (tls->state) {
|
switch (tls->state) {
|
||||||
case MG_TLS_STATE_SERVER_START:
|
case MG_TLS_STATE_SERVER_START:
|
||||||
if (mg_tls_server_recv_hello(c) < 0) break;
|
case MG_TLS_STATE_SERVER_RETRY:
|
||||||
|
if ((r = mg_tls_server_recv_hello(c)) < 0) break;
|
||||||
|
if (r == 1) { // No P-256 key share, ask for it
|
||||||
|
if (!mg_tls_server_send_hrr(c)) return false;
|
||||||
|
tls->state = MG_TLS_STATE_SERVER_RETRY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (!mg_tls_server_send_hello(c)) return false;
|
if (!mg_tls_server_send_hello(c)) return false;
|
||||||
mg_tls_generate_handshake_keys(c);
|
mg_tls_generate_handshake_keys(c);
|
||||||
if (!mg_tls_server_send_ext(c)) return false;
|
if (!mg_tls_server_send_ext(c)) return false;
|
||||||
if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false;
|
if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false;
|
||||||
if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) ||
|
if (!mg_tls_send_cert(c, false)) return false;
|
||||||
!mg_tls_server_send_finish(c))
|
if (!mg_tls_send_cert_verify(c, false)) return false;
|
||||||
return false;
|
if (!mg_tls_server_send_finish(c)) return false;
|
||||||
if (tls->is_twoway) {
|
if (tls->is_twoway) {
|
||||||
// generate application keys at this point, keep using handshake keys
|
// generate application keys at this point, keep using handshake keys
|
||||||
struct tls_enc hs_keys = tls->enc;
|
struct tls_enc hs_keys = tls->enc;
|
||||||
@@ -2169,16 +2241,27 @@ void mg_tls_handshake(struct mg_connection *c) {
|
|||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
long n;
|
long n;
|
||||||
bool res;
|
bool res;
|
||||||
|
uint64_t t;
|
||||||
if (c->is_closing) return; // we don't clear rx buf, so ignore what's left
|
if (c->is_closing) return; // we don't clear rx buf, so ignore what's left
|
||||||
if (tls->is_sntp_pending) return;
|
if (tls->is_sntp_pending) return;
|
||||||
|
t = mg_millis();
|
||||||
if (c->is_client) {
|
if (c->is_client) {
|
||||||
// will clear is_hs when sending last chunk
|
// will clear is_hs when sending last chunk
|
||||||
res = mg_tls_client_handshake(c);
|
res = mg_tls_client_handshake(c);
|
||||||
} else {
|
} else {
|
||||||
res = mg_tls_server_handshake(c);
|
res = mg_tls_server_handshake(c);
|
||||||
}
|
}
|
||||||
if (!res) {
|
tls->hs_proc += mg_millis() - t;
|
||||||
mg_error(c, "TLS OOM");
|
if (res && !c->is_tls_hs) {
|
||||||
|
// CPU: key exchange, signing, certificate verification. Wait: the rest of
|
||||||
|
// the total, waiting for the peer and the network
|
||||||
|
unsigned long total = (unsigned long) (mg_millis() - tls->hs_start);
|
||||||
|
unsigned long cpu = (unsigned long) tls->hs_proc;
|
||||||
|
MG_DEBUG(("%lu TLS handshake done in %lu ms: %lu ms CPU, %lu ms peer wait",
|
||||||
|
c->id, total, cpu, total > cpu ? total - cpu : 0));
|
||||||
|
}
|
||||||
|
if (!res) { // already mg_error()ed at handshake helper ?
|
||||||
|
if (!c->is_closing) mg_error(c, "TLS OOM"); // No, do it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (tls->send.len > 0 &&
|
while (tls->send.len > 0 &&
|
||||||
@@ -2595,7 +2678,7 @@ static int mg_parse_pem_certs(const struct mg_str pem, struct mg_str **ders) {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) {
|
size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) {
|
||||||
struct mg_str der = mg_str_n(NULL, 0);
|
struct mg_str der = mg_str_n(NULL, 0);
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
if (buf == NULL || len < 32) return 0;
|
if (buf == NULL || len < 32) return 0;
|
||||||
@@ -2635,6 +2718,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
mg_error(c, "tls oom");
|
mg_error(c, "tls oom");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
tls->hs_start = mg_millis();
|
||||||
|
|
||||||
tls->state =
|
tls->state =
|
||||||
c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START;
|
c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START;
|
||||||
@@ -2678,7 +2762,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth
|
if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->cert.buf == NULL) {
|
if (opts->cert.buf == NULL || opts->cert.len == 0) {
|
||||||
MG_VERBOSE(("No certificate provided"));
|
MG_VERBOSE(("No certificate provided"));
|
||||||
goto xit;
|
goto xit;
|
||||||
}
|
}
|
||||||
@@ -2707,7 +2791,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse PEM or DER EC key
|
// parse PEM or DER EC key
|
||||||
if (opts->key.buf == NULL) {
|
if (opts->key.buf == NULL || opts->key.len == 0) {
|
||||||
mg_error(c, "Certificate provided without a private key");
|
mg_error(c, "Certificate provided without a private key");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2758,7 +2842,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
MG_INFO(("Parsed PKCS#8 RSA private key: %d bytes", (int) key.len));
|
MG_INFO(("Parsed PKCS#8 RSA private key: %d bytes", (int) key.len));
|
||||||
} else {
|
} else {
|
||||||
mg_free((void *) key.buf);
|
mg_free((void *) key.buf);
|
||||||
MG_INFO(("Parsed PKCS#8 EC private key"));
|
MG_VERBOSE(("Parsed PKCS#8 EC private key"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mg_free((void *) key.buf);
|
mg_free((void *) key.buf);
|
||||||
@@ -2809,6 +2893,7 @@ void mg_tls_free(struct mg_connection *c) {
|
|||||||
}
|
}
|
||||||
mg_free((void *) tls->ca_der.buf);
|
mg_free((void *) tls->ca_der.buf);
|
||||||
mg_free((void *) tls->rsa_key_der.buf);
|
mg_free((void *) tls->rsa_key_der.buf);
|
||||||
|
mg_bzero((unsigned char *) tls, sizeof(*tls)); // Wipe keys and secrets
|
||||||
}
|
}
|
||||||
mg_free(c->tls);
|
mg_free(c->tls);
|
||||||
c->tls = NULL;
|
c->tls = NULL;
|
||||||
@@ -2819,11 +2904,15 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
long n = MG_IO_WAIT;
|
long n = MG_IO_WAIT;
|
||||||
bool was_throttled = c->is_tls_throttled; // see #3074
|
bool was_throttled = c->is_tls_throttled; // see #3074
|
||||||
if (!was_throttled) { // encrypt new data
|
if (!was_throttled) { // encrypt new data
|
||||||
|
size_t i, rs = MG_TLS_RECORD_SIZE > 16384 ? 16384 : MG_TLS_RECORD_SIZE;
|
||||||
if (len > MG_IO_SIZE) len = MG_IO_SIZE;
|
if (len > MG_IO_SIZE) len = MG_IO_SIZE;
|
||||||
if (len > 16384) len = 16384;
|
for (i = 0; i < len; i += rs) { // Split into records, send them at once
|
||||||
if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA))
|
size_t m = len - i < rs ? len - i : rs;
|
||||||
return 0; // returning 0 means an OOM condition (iobuf couldn't resize),
|
if (!mg_tls_encrypt(c, (const uint8_t *) buf + i, m, MG_TLS_APP_DATA))
|
||||||
// yet this is so far recoverable, let the caller decide
|
return 0; // returning 0 means an OOM condition (iobuf couldn't
|
||||||
|
// resize), yet this is so far recoverable, let the caller
|
||||||
|
// decide
|
||||||
|
}
|
||||||
} // else, resend outstanding encrypted data in tls->send
|
} // else, resend outstanding encrypted data in tls->send
|
||||||
while (tls->send.len > 0 &&
|
while (tls->send.len > 0 &&
|
||||||
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
||||||
|
|||||||
@@ -60,9 +60,6 @@ static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx,
|
|||||||
// ******* END: poly1305-donna/poly1305-donna.h ********
|
// ******* END: poly1305-donna/poly1305-donna.h ********
|
||||||
// ******* BEGIN: chacha-portable.c ********
|
// ******* BEGIN: chacha-portable.c ********
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// this is a fresh implementation of chacha20, based on the description in
|
// this is a fresh implementation of chacha20, based on the description in
|
||||||
// rfc8349 it's such a nice compact algorithm that it is easy to do. In
|
// rfc8349 it's such a nice compact algorithm that it is easy to do. In
|
||||||
// relationship to other c implementation this implementation:
|
// relationship to other c implementation this implementation:
|
||||||
|
|||||||
+2553
File diff suppressed because it is too large.
Load diff
+181
@@ -0,0 +1,181 @@
|
|||||||
|
// P-256 API, mg_p256_*(), used by built-in TLS, JWT and OTA. Implemented by
|
||||||
|
// BearSSL code below (MG_P256=MG_P256_BEAR) or tls_p256_cortex.c (LENNGREN)
|
||||||
|
// BearSSL code: https://bearssl.org commit 7bea48e. Shared bignum and ECDSA
|
||||||
|
// code is always compiled, it is also used by P-384, see tls_p384.c
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||||
|
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, as are the
|
||||||
|
// constant-time helpers from inner.h (NOT, MUX, EQ, ...). Structs are not
|
||||||
|
// typedef-ed. ECDSA signing takes the caller's k instead of HMAC-DRBG, k is
|
||||||
|
// computed by mg_p256_sign() (RFC 6979). ECDSA functions take the curve id and
|
||||||
|
// public key directly
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "str.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
|
||||||
|
|
||||||
|
#define MG_BR_EC_secp256r1 23 // Curve IDs, same as TLS named groups
|
||||||
|
#define MG_BR_EC_secp384r1 24
|
||||||
|
#define MG_BR_MAX_EC_SIZE 384 // Largest supported curve, bits
|
||||||
|
|
||||||
|
// From BearSSL inc/bearssl_ec.h: an EC implementation
|
||||||
|
struct mg_br_ec_impl {
|
||||||
|
uint32_t supported_curves; // Bit mask of curve IDs
|
||||||
|
const unsigned char *(*generator)(int curve, size_t *len);
|
||||||
|
const unsigned char *(*order)(int curve, size_t *len);
|
||||||
|
size_t (*xoff)(int curve, size_t *len);
|
||||||
|
uint32_t (*mul)(unsigned char *G, size_t Glen, const unsigned char *x,
|
||||||
|
size_t xlen, int curve);
|
||||||
|
size_t (*mulgen)(unsigned char *R, const unsigned char *x, size_t xlen,
|
||||||
|
int curve);
|
||||||
|
uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len,
|
||||||
|
const unsigned char *x, size_t xlen,
|
||||||
|
const unsigned char *y, size_t ylen, int curve);
|
||||||
|
};
|
||||||
|
|
||||||
|
// From BearSSL src/inner.h: constant-time helpers
|
||||||
|
static inline uint32_t MG_BR_NOT(uint32_t ctl) {
|
||||||
|
return ctl ^ 1;
|
||||||
|
}
|
||||||
|
static inline uint32_t MG_BR_MUX(uint32_t ctl, uint32_t x, uint32_t y) {
|
||||||
|
return y ^ (-ctl & (x ^ y));
|
||||||
|
}
|
||||||
|
static inline uint32_t MG_BR_EQ(uint32_t x, uint32_t y) {
|
||||||
|
uint32_t q = x ^ y;
|
||||||
|
return MG_BR_NOT((q | -q) >> 31);
|
||||||
|
}
|
||||||
|
static inline uint32_t MG_BR_NEQ(uint32_t x, uint32_t y) {
|
||||||
|
uint32_t q = x ^ y;
|
||||||
|
return (q | -q) >> 31;
|
||||||
|
}
|
||||||
|
static inline uint32_t MG_BR_GT(uint32_t x, uint32_t y) {
|
||||||
|
uint32_t z = y - x;
|
||||||
|
return (z ^ ((x ^ y) & (x ^ z))) >> 31;
|
||||||
|
}
|
||||||
|
#define MG_BR_GE(x, y) MG_BR_NOT(MG_BR_GT(y, x))
|
||||||
|
#define MG_BR_LT(x, y) MG_BR_GT(y, x)
|
||||||
|
static inline int32_t MG_BR_CMP(uint32_t x, uint32_t y) {
|
||||||
|
return (int32_t) MG_BR_GT(x, y) | -(int32_t) MG_BR_GT(y, x);
|
||||||
|
}
|
||||||
|
static inline uint32_t MG_BR_BIT_LENGTH(uint32_t x) {
|
||||||
|
uint32_t k, c;
|
||||||
|
k = MG_BR_NEQ(x, 0);
|
||||||
|
c = MG_BR_GT(x, 0xFFFF), x = MG_BR_MUX(c, x >> 16, x), k += c << 4;
|
||||||
|
c = MG_BR_GT(x, 0x00FF), x = MG_BR_MUX(c, x >> 8, x), k += c << 3;
|
||||||
|
c = MG_BR_GT(x, 0x000F), x = MG_BR_MUX(c, x >> 4, x), k += c << 2;
|
||||||
|
c = MG_BR_GT(x, 0x0003), x = MG_BR_MUX(c, x >> 2, x), k += c << 1;
|
||||||
|
k += MG_BR_GT(x, 0x0001);
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
#define MG_BR_MUL31(x, y) ((uint64_t) (x) * (uint64_t) (y))
|
||||||
|
#define MG_BR_MUL31_lo(x, y) \
|
||||||
|
(((uint32_t) (x) * (uint32_t) (y)) & (uint32_t) 0x7FFFFFFF)
|
||||||
|
#define MG_BR_CCOPY mg_br_ccopy
|
||||||
|
void mg_br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len);
|
||||||
|
uint32_t mg_br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r);
|
||||||
|
static inline uint32_t mg_br_rem(uint32_t hi, uint32_t lo, uint32_t d) {
|
||||||
|
uint32_t r;
|
||||||
|
mg_br_divrem(hi, lo, d, &r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
static inline uint32_t mg_br_div(uint32_t hi, uint32_t lo, uint32_t d) {
|
||||||
|
uint32_t r;
|
||||||
|
return mg_br_divrem(hi, lo, d, &r);
|
||||||
|
}
|
||||||
|
static inline void mg_br_enc32be(void *dst, uint32_t x) {
|
||||||
|
unsigned char *buf = (unsigned char *) dst;
|
||||||
|
buf[0] = (unsigned char) (x >> 24), buf[1] = (unsigned char) (x >> 16);
|
||||||
|
buf[2] = (unsigned char) (x >> 8), buf[3] = (unsigned char) x;
|
||||||
|
}
|
||||||
|
static inline void mg_br_i31_zero(uint32_t *x, uint32_t bit_len) {
|
||||||
|
*x++ = bit_len;
|
||||||
|
memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Big integers, "i31" format, see BearSSL src/inner.h
|
||||||
|
uint32_t mg_br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl);
|
||||||
|
uint32_t mg_br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl);
|
||||||
|
uint32_t mg_br_i31_bit_length(uint32_t *x, size_t xlen);
|
||||||
|
uint32_t mg_br_i31_decode_mod(uint32_t *x, const void *src, size_t len,
|
||||||
|
const uint32_t *m);
|
||||||
|
void mg_br_i31_decode(uint32_t *x, const void *src, size_t len);
|
||||||
|
void mg_br_i31_encode(void *dst, size_t len, const uint32_t *x);
|
||||||
|
void mg_br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i);
|
||||||
|
void mg_br_i31_to_monty(uint32_t *x, const uint32_t *m);
|
||||||
|
uint32_t mg_br_i31_iszero(const uint32_t *x);
|
||||||
|
void mg_br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen,
|
||||||
|
const uint32_t *m, uint32_t m0i, uint32_t *t1,
|
||||||
|
uint32_t *t2);
|
||||||
|
void mg_br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
|
||||||
|
const uint32_t *m, uint32_t m0i);
|
||||||
|
void mg_br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m);
|
||||||
|
uint32_t mg_br_i31_ninv31(uint32_t x);
|
||||||
|
void mg_br_i31_rshift(uint32_t *x, int count);
|
||||||
|
|
||||||
|
// ECDSA, see BearSSL src/ec/ecdsa_i31_*.c. q is an uncompressed point
|
||||||
|
void mg_br_ecdsa_i31_bits2int(uint32_t *x, const void *src, size_t len,
|
||||||
|
uint32_t ebitlen);
|
||||||
|
uint32_t mg_br_ecdsa_i31_vrfy_raw(const struct mg_br_ec_impl *impl, int curve,
|
||||||
|
const void *hash, size_t hash_len,
|
||||||
|
const unsigned char *q, size_t qlen,
|
||||||
|
const void *sig, size_t sig_len);
|
||||||
|
size_t mg_br_ecdsa_i31_sign_raw(const struct mg_br_ec_impl *impl, int curve,
|
||||||
|
const void *hash, size_t hash_len,
|
||||||
|
const unsigned char *x, size_t xlen,
|
||||||
|
const unsigned char *k, void *sig);
|
||||||
|
|
||||||
|
// P-256 (secp256r1) API. Keys and signatures are big endian byte strings,
|
||||||
|
// a public key is X || Y, a signature is r || s. Functions return 1 on
|
||||||
|
// success, 0 on failure
|
||||||
|
|
||||||
|
// Generate a random key pair, using mg_random()
|
||||||
|
int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]);
|
||||||
|
|
||||||
|
// Compute public key from a private key. Return 0 if private key is invalid
|
||||||
|
int mg_p256_compute_public_key(const uint8_t private_key[32],
|
||||||
|
uint8_t public_key[64]);
|
||||||
|
|
||||||
|
// ECDH. Validates the peer public key, returns 0 if it is not on the curve
|
||||||
|
int mg_p256_shared_secret(const uint8_t public_key[64],
|
||||||
|
const uint8_t private_key[32], uint8_t secret[32]);
|
||||||
|
|
||||||
|
// ECDSA sign with a deterministic k, RFC 6979 with HMAC-SHA256
|
||||||
|
int mg_p256_sign(const uint8_t private_key[32], const uint8_t *hash,
|
||||||
|
size_t hash_size, uint8_t signature[64]);
|
||||||
|
|
||||||
|
// ECDSA verify. Validates the public key
|
||||||
|
int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash,
|
||||||
|
size_t hash_size, const uint8_t signature[64]);
|
||||||
|
|
||||||
|
// Internal: ECDSA sign with a given k, 32 bytes big endian, in [1, n-1].
|
||||||
|
// Provided by the selected implementation, used by mg_p256_sign()
|
||||||
|
int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash,
|
||||||
|
size_t hash_size, const uint8_t k[32],
|
||||||
|
uint8_t signature[64]);
|
||||||
|
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
|
||||||
File diff suppressed because it is too large.
Load diff
+834
@@ -0,0 +1,834 @@
|
|||||||
|
// P-384 from BearSSL, https://bearssl.org commit 7bea48e
|
||||||
|
// P-384 signature verification, used by built-in TLS for certificates and
|
||||||
|
// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static
|
||||||
|
// functions with p384_. Trimmed to P-384. Structs are not typedef-ed
|
||||||
|
|
||||||
|
#include "tls_p384.h"
|
||||||
|
#include "tls_p256.h"
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
|
||||||
|
|
||||||
|
// From BearSSL src/ec/ec_secp384r1.c
|
||||||
|
static const unsigned char P384_N[] = {
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF,
|
||||||
|
0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A,
|
||||||
|
0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char P384_G[] = {
|
||||||
|
0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05,
|
||||||
|
0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD,
|
||||||
|
0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B,
|
||||||
|
0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A,
|
||||||
|
0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29,
|
||||||
|
0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A,
|
||||||
|
0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C,
|
||||||
|
0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC,
|
||||||
|
0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14,
|
||||||
|
0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8,
|
||||||
|
0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81,
|
||||||
|
0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E,
|
||||||
|
0x5F
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// From BearSSL src/ec/ec_prime_i31.c
|
||||||
|
/*
|
||||||
|
* Parameters for supported curves (field modulus, and 'b' equation
|
||||||
|
* parameter; both values use the 'i31' format, and 'b' is in Montgomery
|
||||||
|
* representation).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint32_t P384_P[] = {
|
||||||
|
0x0000018C,
|
||||||
|
0x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8,
|
||||||
|
0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
|
||||||
|
0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
|
||||||
|
0x00000FFF
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t P384_R2[] = {
|
||||||
|
0x0000018C,
|
||||||
|
0x00000000, 0x00000080, 0x7FFFFE00, 0x000001FF,
|
||||||
|
0x00000800, 0x00000000, 0x7FFFE000, 0x00001FFF,
|
||||||
|
0x00008000, 0x00008000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t P384_B[] = {
|
||||||
|
0x0000018C,
|
||||||
|
0x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C,
|
||||||
|
0x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B,
|
||||||
|
0x2719BA5F, 0x41F02209, 0x36C5643E, 0x5813EFFE,
|
||||||
|
0x000008A5
|
||||||
|
};
|
||||||
|
|
||||||
|
struct p384_curve_params {
|
||||||
|
const uint32_t *p;
|
||||||
|
const uint32_t *b;
|
||||||
|
const uint32_t *R2;
|
||||||
|
uint32_t p0i;
|
||||||
|
size_t point_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const struct p384_curve_params *
|
||||||
|
p384_id_to_curve(int curve)
|
||||||
|
{
|
||||||
|
static const struct p384_curve_params pp = {
|
||||||
|
P384_P, P384_B, P384_R2, 0x00000001, 97
|
||||||
|
};
|
||||||
|
|
||||||
|
(void)curve;
|
||||||
|
return &pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define I31_LEN ((MG_BR_MAX_EC_SIZE + 61) / 31)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type for a point in Jacobian coordinates:
|
||||||
|
* -- three values, x, y and z, in Montgomery representation
|
||||||
|
* -- affine coordinates are X = x / z^2 and Y = y / z^3
|
||||||
|
* -- for the point at infinity, z = 0
|
||||||
|
*/
|
||||||
|
struct p384_jacobian {
|
||||||
|
uint32_t c[3][I31_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use a custom interpreter that uses a dozen registers, and
|
||||||
|
* only six operations:
|
||||||
|
* MSET(d, a) copy a into d
|
||||||
|
* MADD(d, a) d = d+a (modular)
|
||||||
|
* MSUB(d, a) d = d-a (modular)
|
||||||
|
* MMUL(d, a, b) d = a*b (Montgomery multiplication)
|
||||||
|
* MINV(d, a, b) invert d modulo p; a and b are used as scratch registers
|
||||||
|
* MTZ(d) clear return value if d = 0
|
||||||
|
* Destination of MMUL (d) must be distinct from operands (a and b).
|
||||||
|
* There is no such constraint for MSUB and MADD.
|
||||||
|
*
|
||||||
|
* Registers include the operand coordinates, and temporaries.
|
||||||
|
*/
|
||||||
|
#define MSET(d, a) (0x0000 + ((d) << 8) + ((a) << 4))
|
||||||
|
#define MADD(d, a) (0x1000 + ((d) << 8) + ((a) << 4))
|
||||||
|
#define MSUB(d, a) (0x2000 + ((d) << 8) + ((a) << 4))
|
||||||
|
#define MMUL(d, a, b) (0x3000 + ((d) << 8) + ((a) << 4) + (b))
|
||||||
|
#define MINV(d, a, b) (0x4000 + ((d) << 8) + ((a) << 4) + (b))
|
||||||
|
#define MTZ(d) (0x5000 + ((d) << 8))
|
||||||
|
#define ENDCODE 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Registers for the input operands.
|
||||||
|
*/
|
||||||
|
#define P1x 0
|
||||||
|
#define P1y 1
|
||||||
|
#define P1z 2
|
||||||
|
#define P2x 3
|
||||||
|
#define P2y 4
|
||||||
|
#define P2z 5
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alternate names for the first input operand.
|
||||||
|
*/
|
||||||
|
#define Px 0
|
||||||
|
#define Py 1
|
||||||
|
#define Pz 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Temporaries.
|
||||||
|
*/
|
||||||
|
#define t1 6
|
||||||
|
#define t2 7
|
||||||
|
#define t3 8
|
||||||
|
#define t4 9
|
||||||
|
#define t5 10
|
||||||
|
#define t6 11
|
||||||
|
#define t7 12
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra scratch registers available when there is no second operand (e.g.
|
||||||
|
* for "double" and "affine").
|
||||||
|
*/
|
||||||
|
#define t8 3
|
||||||
|
#define t9 4
|
||||||
|
#define t10 5
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Doubling formulas are:
|
||||||
|
*
|
||||||
|
* s = 4*x*y^2
|
||||||
|
* m = 3*(x + z^2)*(x - z^2)
|
||||||
|
* x' = m^2 - 2*s
|
||||||
|
* y' = m*(s - x') - 8*y^4
|
||||||
|
* z' = 2*y*z
|
||||||
|
*
|
||||||
|
* If y = 0 (P has order 2) then this yields infinity (z' = 0), as it
|
||||||
|
* should. This case should not happen anyway, because our curves have
|
||||||
|
* prime order, and thus do not contain any point of order 2.
|
||||||
|
*
|
||||||
|
* If P is infinity (z = 0), then again the formulas yield infinity,
|
||||||
|
* which is correct. Thus, this code works for all points.
|
||||||
|
*
|
||||||
|
* Cost: 8 multiplications
|
||||||
|
*/
|
||||||
|
static const uint16_t p384_code_double[] = {
|
||||||
|
/*
|
||||||
|
* Compute z^2 (in t1).
|
||||||
|
*/
|
||||||
|
MMUL(t1, Pz, Pz),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute x-z^2 (in t2) and then x+z^2 (in t1).
|
||||||
|
*/
|
||||||
|
MSET(t2, Px),
|
||||||
|
MSUB(t2, t1),
|
||||||
|
MADD(t1, Px),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute m = 3*(x+z^2)*(x-z^2) (in t1).
|
||||||
|
*/
|
||||||
|
MMUL(t3, t1, t2),
|
||||||
|
MSET(t1, t3),
|
||||||
|
MADD(t1, t3),
|
||||||
|
MADD(t1, t3),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3).
|
||||||
|
*/
|
||||||
|
MMUL(t3, Py, Py),
|
||||||
|
MADD(t3, t3),
|
||||||
|
MMUL(t2, Px, t3),
|
||||||
|
MADD(t2, t2),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute x' = m^2 - 2*s.
|
||||||
|
*/
|
||||||
|
MMUL(Px, t1, t1),
|
||||||
|
MSUB(Px, t2),
|
||||||
|
MSUB(Px, t2),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute z' = 2*y*z.
|
||||||
|
*/
|
||||||
|
MMUL(t4, Py, Pz),
|
||||||
|
MSET(Pz, t4),
|
||||||
|
MADD(Pz, t4),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute y' = m*(s - x') - 8*y^4. Note that we already have
|
||||||
|
* 2*y^2 in t3.
|
||||||
|
*/
|
||||||
|
MSUB(t2, Px),
|
||||||
|
MMUL(Py, t1, t2),
|
||||||
|
MMUL(t4, t3, t3),
|
||||||
|
MSUB(Py, t4),
|
||||||
|
MSUB(Py, t4),
|
||||||
|
|
||||||
|
ENDCODE
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Addtions formulas are:
|
||||||
|
*
|
||||||
|
* u1 = x1 * z2^2
|
||||||
|
* u2 = x2 * z1^2
|
||||||
|
* s1 = y1 * z2^3
|
||||||
|
* s2 = y2 * z1^3
|
||||||
|
* h = u2 - u1
|
||||||
|
* r = s2 - s1
|
||||||
|
* x3 = r^2 - h^3 - 2 * u1 * h^2
|
||||||
|
* y3 = r * (u1 * h^2 - x3) - s1 * h^3
|
||||||
|
* z3 = h * z1 * z2
|
||||||
|
*
|
||||||
|
* If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that
|
||||||
|
* z3 == 0, so the result is correct.
|
||||||
|
* If either of P1 or P2 is infinity, but not both, then z3 == 0, which is
|
||||||
|
* not correct.
|
||||||
|
* h == 0 only if u1 == u2; this happens in two cases:
|
||||||
|
* -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2
|
||||||
|
* -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity)
|
||||||
|
*
|
||||||
|
* Thus, the following situations are not handled correctly:
|
||||||
|
* -- P1 = 0 and P2 != 0
|
||||||
|
* -- P1 != 0 and P2 = 0
|
||||||
|
* -- P1 = P2
|
||||||
|
* All other cases are properly computed. However, even in "incorrect"
|
||||||
|
* situations, the three coordinates still are properly formed field
|
||||||
|
* elements.
|
||||||
|
*
|
||||||
|
* The returned flag is cleared if r == 0. This happens in the following
|
||||||
|
* cases:
|
||||||
|
* -- Both points are on the same horizontal line (same Y coordinate).
|
||||||
|
* -- Both points are infinity.
|
||||||
|
* -- One point is infinity and the other is on line Y = 0.
|
||||||
|
* The third case cannot happen with our curves (there is no valid point
|
||||||
|
* on line Y = 0 since that would be a point of order 2). If the two
|
||||||
|
* source points are non-infinity, then remains only the case where the
|
||||||
|
* two points are on the same horizontal line.
|
||||||
|
*
|
||||||
|
* This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and
|
||||||
|
* P2 != 0:
|
||||||
|
* -- If the returned value is not the point at infinity, then it was properly
|
||||||
|
* computed.
|
||||||
|
* -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result
|
||||||
|
* is indeed the point at infinity.
|
||||||
|
* -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should
|
||||||
|
* use the 'double' code.
|
||||||
|
*
|
||||||
|
* Cost: 16 multiplications
|
||||||
|
*/
|
||||||
|
static const uint16_t p384_code_add[] = {
|
||||||
|
/*
|
||||||
|
* Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3).
|
||||||
|
*/
|
||||||
|
MMUL(t3, P2z, P2z),
|
||||||
|
MMUL(t1, P1x, t3),
|
||||||
|
MMUL(t4, P2z, t3),
|
||||||
|
MMUL(t3, P1y, t4),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4).
|
||||||
|
*/
|
||||||
|
MMUL(t4, P1z, P1z),
|
||||||
|
MMUL(t2, P2x, t4),
|
||||||
|
MMUL(t5, P1z, t4),
|
||||||
|
MMUL(t4, P2y, t5),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4).
|
||||||
|
*/
|
||||||
|
MSUB(t2, t1),
|
||||||
|
MSUB(t4, t3),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report cases where r = 0 through the returned flag.
|
||||||
|
*/
|
||||||
|
MTZ(t4),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute u1*h^2 (in t6) and h^3 (in t5).
|
||||||
|
*/
|
||||||
|
MMUL(t7, t2, t2),
|
||||||
|
MMUL(t6, t1, t7),
|
||||||
|
MMUL(t5, t7, t2),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute x3 = r^2 - h^3 - 2*u1*h^2.
|
||||||
|
* t1 and t7 can be used as scratch registers.
|
||||||
|
*/
|
||||||
|
MMUL(P1x, t4, t4),
|
||||||
|
MSUB(P1x, t5),
|
||||||
|
MSUB(P1x, t6),
|
||||||
|
MSUB(P1x, t6),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute y3 = r*(u1*h^2 - x3) - s1*h^3.
|
||||||
|
*/
|
||||||
|
MSUB(t6, P1x),
|
||||||
|
MMUL(P1y, t4, t6),
|
||||||
|
MMUL(t1, t5, t3),
|
||||||
|
MSUB(P1y, t1),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute z3 = h*z1*z2.
|
||||||
|
*/
|
||||||
|
MMUL(t1, P1z, P2z),
|
||||||
|
MMUL(P1z, t1, t2),
|
||||||
|
|
||||||
|
ENDCODE
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that the point is on the curve. This code snippet assumes the
|
||||||
|
* following conventions:
|
||||||
|
* -- Coordinates x and y have been freshly decoded in P1 (but not
|
||||||
|
* converted to Montgomery coordinates yet).
|
||||||
|
* -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1.
|
||||||
|
*/
|
||||||
|
static const uint16_t p384_code_check[] = {
|
||||||
|
|
||||||
|
/* Convert x and y to Montgomery representation. */
|
||||||
|
MMUL(t1, P1x, P2x),
|
||||||
|
MMUL(t2, P1y, P2x),
|
||||||
|
MSET(P1x, t1),
|
||||||
|
MSET(P1y, t2),
|
||||||
|
|
||||||
|
/* Compute x^3 in t1. */
|
||||||
|
MMUL(t2, P1x, P1x),
|
||||||
|
MMUL(t1, P1x, t2),
|
||||||
|
|
||||||
|
/* Subtract 3*x from t1. */
|
||||||
|
MSUB(t1, P1x),
|
||||||
|
MSUB(t1, P1x),
|
||||||
|
MSUB(t1, P1x),
|
||||||
|
|
||||||
|
/* Add b. */
|
||||||
|
MADD(t1, P2y),
|
||||||
|
|
||||||
|
/* Compute y^2 in t2. */
|
||||||
|
MMUL(t2, P1y, P1y),
|
||||||
|
|
||||||
|
/* Compare y^2 with x^3 - 3*x + b; they must match. */
|
||||||
|
MSUB(t1, t2),
|
||||||
|
MTZ(t1),
|
||||||
|
|
||||||
|
/* Set z to 1 (in Montgomery representation). */
|
||||||
|
MMUL(P1z, P2x, P2z),
|
||||||
|
|
||||||
|
ENDCODE
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conversion back to affine coordinates. This code snippet assumes that
|
||||||
|
* the z coordinate of P2 is set to 1 (not in Montgomery representation).
|
||||||
|
*/
|
||||||
|
static const uint16_t p384_code_affine[] = {
|
||||||
|
|
||||||
|
/* Save z*R in t1. */
|
||||||
|
MSET(t1, P1z),
|
||||||
|
|
||||||
|
/* Compute z^3 in t2. */
|
||||||
|
MMUL(t2, P1z, P1z),
|
||||||
|
MMUL(t3, P1z, t2),
|
||||||
|
MMUL(t2, t3, P2z),
|
||||||
|
|
||||||
|
/* Invert to (1/z^3) in t2. */
|
||||||
|
MINV(t2, t3, t4),
|
||||||
|
|
||||||
|
/* Compute y. */
|
||||||
|
MSET(t3, P1y),
|
||||||
|
MMUL(P1y, t2, t3),
|
||||||
|
|
||||||
|
/* Compute (1/z^2) in t3. */
|
||||||
|
MMUL(t3, t2, t1),
|
||||||
|
|
||||||
|
/* Compute x. */
|
||||||
|
MSET(t2, P1x),
|
||||||
|
MMUL(P1x, t2, t3),
|
||||||
|
|
||||||
|
ENDCODE
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
p384_run_code(struct p384_jacobian *P1, const struct p384_jacobian *P2,
|
||||||
|
const struct p384_curve_params *cc, const uint16_t *code)
|
||||||
|
{
|
||||||
|
uint32_t r;
|
||||||
|
uint32_t t[13][I31_LEN];
|
||||||
|
size_t u;
|
||||||
|
|
||||||
|
r = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the two operands in the dedicated registers.
|
||||||
|
*/
|
||||||
|
memcpy(t[P1x], P1->c, 3 * I31_LEN * sizeof(uint32_t));
|
||||||
|
memcpy(t[P2x], P2->c, 3 * I31_LEN * sizeof(uint32_t));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run formulas.
|
||||||
|
*/
|
||||||
|
for (u = 0;; u ++) {
|
||||||
|
unsigned op, d, a, b;
|
||||||
|
|
||||||
|
op = code[u];
|
||||||
|
if (op == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d = (op >> 8) & 0x0F;
|
||||||
|
a = (op >> 4) & 0x0F;
|
||||||
|
b = op & 0x0F;
|
||||||
|
op >>= 12;
|
||||||
|
switch (op) {
|
||||||
|
uint32_t ctl;
|
||||||
|
size_t plen;
|
||||||
|
unsigned char tp[(MG_BR_MAX_EC_SIZE + 7) >> 3];
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
memcpy(t[d], t[a], I31_LEN * sizeof(uint32_t));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ctl = mg_br_i31_add(t[d], t[a], 1);
|
||||||
|
ctl |= MG_BR_NOT(mg_br_i31_sub(t[d], cc->p, 0));
|
||||||
|
mg_br_i31_sub(t[d], cc->p, ctl);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mg_br_i31_add(t[d], cc->p, mg_br_i31_sub(t[d], t[a], 1));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
mg_br_i31_montymul(t[d], t[a], t[b], cc->p, cc->p0i);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;
|
||||||
|
mg_br_i31_encode(tp, plen, cc->p);
|
||||||
|
tp[plen - 1] -= 2;
|
||||||
|
mg_br_i31_modpow(t[d], tp, plen,
|
||||||
|
cc->p, cc->p0i, t[a], t[b]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r &= ~mg_br_i31_iszero(t[d]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy back result.
|
||||||
|
*/
|
||||||
|
memcpy(P1->c, t[P1x], 3 * I31_LEN * sizeof(uint32_t));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
p384_set_one(uint32_t *x, const uint32_t *p)
|
||||||
|
{
|
||||||
|
size_t plen;
|
||||||
|
|
||||||
|
plen = (p[0] + 63) >> 5;
|
||||||
|
memset(x, 0, plen * sizeof *x);
|
||||||
|
x[0] = p[0];
|
||||||
|
x[1] = 0x00000001;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
p384_point_zero(struct p384_jacobian *P, const struct p384_curve_params *cc)
|
||||||
|
{
|
||||||
|
memset(P, 0, sizeof *P);
|
||||||
|
P->c[0][0] = P->c[1][0] = P->c[2][0] = cc->p[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
p384_point_double(struct p384_jacobian *P, const struct p384_curve_params *cc)
|
||||||
|
{
|
||||||
|
p384_run_code(P, P, cc, p384_code_double);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
p384_point_add(struct p384_jacobian *P1, const struct p384_jacobian *P2, const struct p384_curve_params *cc)
|
||||||
|
{
|
||||||
|
return p384_run_code(P1, P2, cc, p384_code_add);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
p384_point_mul(struct p384_jacobian *P, const unsigned char *x, size_t xlen,
|
||||||
|
const struct p384_curve_params *cc)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We do a simple double-and-add ladder with a 2-bit window
|
||||||
|
* to make only one add every two doublings. We thus first
|
||||||
|
* precompute 2P and 3P in some local buffers.
|
||||||
|
*
|
||||||
|
* We always perform two doublings and one addition; the
|
||||||
|
* addition is with P, 2P and 3P and is done in a temporary
|
||||||
|
* array.
|
||||||
|
*
|
||||||
|
* The addition code cannot handle cases where one of the
|
||||||
|
* operands is infinity, which is the case at the start of the
|
||||||
|
* ladder. We therefore need to maintain a flag that controls
|
||||||
|
* this situation.
|
||||||
|
*/
|
||||||
|
uint32_t qz;
|
||||||
|
struct p384_jacobian P2, P3, Q, T, U;
|
||||||
|
|
||||||
|
memcpy(&P2, P, sizeof P2);
|
||||||
|
p384_point_double(&P2, cc);
|
||||||
|
memcpy(&P3, P, sizeof P3);
|
||||||
|
p384_point_add(&P3, &P2, cc);
|
||||||
|
|
||||||
|
p384_point_zero(&Q, cc);
|
||||||
|
qz = 1;
|
||||||
|
while (xlen -- > 0) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
for (k = 6; k >= 0; k -= 2) {
|
||||||
|
uint32_t bits;
|
||||||
|
uint32_t bnz;
|
||||||
|
|
||||||
|
p384_point_double(&Q, cc);
|
||||||
|
p384_point_double(&Q, cc);
|
||||||
|
memcpy(&T, P, sizeof T);
|
||||||
|
memcpy(&U, &Q, sizeof U);
|
||||||
|
bits = (*x >> k) & (uint32_t)3;
|
||||||
|
bnz = MG_BR_NEQ(bits, 0);
|
||||||
|
MG_BR_CCOPY(MG_BR_EQ(bits, 2), &T, &P2, sizeof T);
|
||||||
|
MG_BR_CCOPY(MG_BR_EQ(bits, 3), &T, &P3, sizeof T);
|
||||||
|
p384_point_add(&U, &T, cc);
|
||||||
|
MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q);
|
||||||
|
MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q);
|
||||||
|
qz &= ~bnz;
|
||||||
|
}
|
||||||
|
x ++;
|
||||||
|
}
|
||||||
|
memcpy(P, &Q, sizeof Q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode point into Jacobian coordinates. This function does not support
|
||||||
|
* the point at infinity. If the point is invalid then this returns 0, but
|
||||||
|
* the coordinates are still set to properly formed field elements.
|
||||||
|
*/
|
||||||
|
static uint32_t
|
||||||
|
p384_point_decode(struct p384_jacobian *P, const void *src, size_t len, const struct p384_curve_params *cc)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Points must use uncompressed format:
|
||||||
|
* -- first byte is 0x04;
|
||||||
|
* -- coordinates X and Y use unsigned big-endian, with the same
|
||||||
|
* length as the field modulus.
|
||||||
|
*
|
||||||
|
* We don't support hybrid format (uncompressed, but first byte
|
||||||
|
* has value 0x06 or 0x07, depending on the least significant bit
|
||||||
|
* of Y) because it is rather useless, and explicitly forbidden
|
||||||
|
* by PKIX (RFC 5480, section 2.2).
|
||||||
|
*
|
||||||
|
* We don't support compressed format either, because it is not
|
||||||
|
* much used in practice (there are or were patent-related
|
||||||
|
* concerns about point compression, which explains the lack of
|
||||||
|
* generalised support). Also, point compression support would
|
||||||
|
* need a bit more code.
|
||||||
|
*/
|
||||||
|
const unsigned char *buf;
|
||||||
|
size_t plen, zlen;
|
||||||
|
uint32_t r;
|
||||||
|
struct p384_jacobian Q;
|
||||||
|
|
||||||
|
buf = (const unsigned char *)src;
|
||||||
|
p384_point_zero(P, cc);
|
||||||
|
plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;
|
||||||
|
if (len != 1 + (plen << 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
r = mg_br_i31_decode_mod(P->c[0], buf + 1, plen, cc->p);
|
||||||
|
r &= mg_br_i31_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check first byte.
|
||||||
|
*/
|
||||||
|
r &= MG_BR_EQ(buf[0], 0x04);
|
||||||
|
/* obsolete
|
||||||
|
r &= MG_BR_EQ(buf[0], 0x04) | (MG_BR_EQ(buf[0] & 0xFE, 0x06)
|
||||||
|
& ~(uint32_t)(buf[0] ^ buf[plen << 1]));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert coordinates and check that the point is valid.
|
||||||
|
*/
|
||||||
|
zlen = ((cc->p[0] + 63) >> 5) * sizeof(uint32_t);
|
||||||
|
memcpy(Q.c[0], cc->R2, zlen);
|
||||||
|
memcpy(Q.c[1], cc->b, zlen);
|
||||||
|
p384_set_one(Q.c[2], cc->p);
|
||||||
|
r &= ~p384_run_code(P, &Q, cc, p384_code_check);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode a point. This method assumes that the point is correct and is
|
||||||
|
* not the point at infinity. Encoded size is always 1+2*plen, where
|
||||||
|
* plen is the field modulus length, in bytes.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
p384_point_encode(void *dst, const struct p384_jacobian *P, const struct p384_curve_params *cc)
|
||||||
|
{
|
||||||
|
unsigned char *buf;
|
||||||
|
uint32_t xbl;
|
||||||
|
size_t plen;
|
||||||
|
struct p384_jacobian Q, T;
|
||||||
|
|
||||||
|
buf = (unsigned char *)dst;
|
||||||
|
xbl = cc->p[0];
|
||||||
|
xbl -= (xbl >> 5);
|
||||||
|
plen = (xbl + 7) >> 3;
|
||||||
|
buf[0] = 0x04;
|
||||||
|
memcpy(&Q, P, sizeof *P);
|
||||||
|
p384_set_one(T.c[2], cc->p);
|
||||||
|
p384_run_code(&Q, &T, cc, p384_code_affine);
|
||||||
|
mg_br_i31_encode(buf + 1, plen, Q.c[0]);
|
||||||
|
mg_br_i31_encode(buf + 1 + plen, plen, Q.c[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const unsigned char *
|
||||||
|
p384_api_generator(int curve, size_t *len)
|
||||||
|
{
|
||||||
|
(void)curve;
|
||||||
|
*len = sizeof P384_G;
|
||||||
|
return P384_G;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const unsigned char *
|
||||||
|
p384_api_order(int curve, size_t *len)
|
||||||
|
{
|
||||||
|
(void)curve;
|
||||||
|
*len = sizeof P384_N;
|
||||||
|
return P384_N;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
p384_api_xoff(int curve, size_t *len)
|
||||||
|
{
|
||||||
|
p384_api_generator(curve, len);
|
||||||
|
*len >>= 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
p384_api_mul(unsigned char *G, size_t Glen,
|
||||||
|
const unsigned char *x, size_t xlen, int curve)
|
||||||
|
{
|
||||||
|
uint32_t r;
|
||||||
|
const struct p384_curve_params *cc;
|
||||||
|
struct p384_jacobian P;
|
||||||
|
|
||||||
|
cc = p384_id_to_curve(curve);
|
||||||
|
if (Glen != cc->point_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
r = p384_point_decode(&P, G, Glen, cc);
|
||||||
|
p384_point_mul(&P, x, xlen, cc);
|
||||||
|
p384_point_encode(G, &P, cc);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
p384_api_mulgen(unsigned char *R,
|
||||||
|
const unsigned char *x, size_t xlen, int curve)
|
||||||
|
{
|
||||||
|
const unsigned char *G;
|
||||||
|
size_t Glen;
|
||||||
|
|
||||||
|
G = p384_api_generator(curve, &Glen);
|
||||||
|
memcpy(R, G, Glen);
|
||||||
|
p384_api_mul(R, Glen, x, xlen, curve);
|
||||||
|
return Glen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
p384_api_muladd(unsigned char *A, const unsigned char *B, size_t len,
|
||||||
|
const unsigned char *x, size_t xlen,
|
||||||
|
const unsigned char *y, size_t ylen, int curve)
|
||||||
|
{
|
||||||
|
uint32_t r, t, z;
|
||||||
|
const struct p384_curve_params *cc;
|
||||||
|
struct p384_jacobian P, Q;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: see about merging the two ladders. Right now, we do
|
||||||
|
* two independent point multiplications, which is a bit
|
||||||
|
* wasteful of CPU resources (but yields short code).
|
||||||
|
*/
|
||||||
|
|
||||||
|
cc = p384_id_to_curve(curve);
|
||||||
|
if (len != cc->point_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
r = p384_point_decode(&P, A, len, cc);
|
||||||
|
if (B == NULL) {
|
||||||
|
size_t Glen;
|
||||||
|
|
||||||
|
B = p384_api_generator(curve, &Glen);
|
||||||
|
}
|
||||||
|
r &= p384_point_decode(&Q, B, len, cc);
|
||||||
|
p384_point_mul(&P, x, xlen, cc);
|
||||||
|
p384_point_mul(&Q, y, ylen, cc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We want to compute P+Q. Since the base points A and B are distinct
|
||||||
|
* from infinity, and the multipliers are non-zero and lower than the
|
||||||
|
* curve order, then we know that P and Q are non-infinity. This
|
||||||
|
* leaves two special situations to test for:
|
||||||
|
* -- If P = Q then we must use p384_point_double().
|
||||||
|
* -- If P+Q = 0 then we must report an error.
|
||||||
|
*/
|
||||||
|
t = p384_point_add(&P, &Q, cc);
|
||||||
|
p384_point_double(&Q, cc);
|
||||||
|
z = mg_br_i31_iszero(P.c[2]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we
|
||||||
|
* have the following:
|
||||||
|
*
|
||||||
|
* z = 0, t = 0 return P (normal addition)
|
||||||
|
* z = 0, t = 1 return P (normal addition)
|
||||||
|
* z = 1, t = 0 return Q (a 'double' case)
|
||||||
|
* z = 1, t = 1 report an error (P+Q = 0)
|
||||||
|
*/
|
||||||
|
MG_BR_CCOPY(z & ~t, &P, &Q, sizeof Q);
|
||||||
|
p384_point_encode(A, &P, cc);
|
||||||
|
r &= ~(z & t);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mg_br_ec_impl mg_br_ec_prime_i31 = {
|
||||||
|
(uint32_t)1 << MG_BR_EC_secp384r1,
|
||||||
|
&p384_api_generator,
|
||||||
|
&p384_api_order,
|
||||||
|
&p384_api_xoff,
|
||||||
|
&p384_api_mul,
|
||||||
|
&p384_api_mulgen,
|
||||||
|
&p384_api_muladd
|
||||||
|
};
|
||||||
|
|
||||||
|
// See tls_p384.h
|
||||||
|
int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash,
|
||||||
|
size_t hash_size, const uint8_t signature[96]) {
|
||||||
|
unsigned char q[97];
|
||||||
|
q[0] = 0x04;
|
||||||
|
memcpy(q + 1, public_key, 96);
|
||||||
|
return mg_br_ecdsa_i31_vrfy_raw(&mg_br_ec_prime_i31, MG_BR_EC_secp384r1, hash,
|
||||||
|
hash_size, q, sizeof(q), signature, 96) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undefine local macros, mongoose.c is one translation unit
|
||||||
|
#undef I31_LEN
|
||||||
|
#undef MSET
|
||||||
|
#undef MADD
|
||||||
|
#undef MSUB
|
||||||
|
#undef MMUL
|
||||||
|
#undef MINV
|
||||||
|
#undef MTZ
|
||||||
|
#undef ENDCODE
|
||||||
|
#undef P1x
|
||||||
|
#undef P1y
|
||||||
|
#undef P1z
|
||||||
|
#undef P2x
|
||||||
|
#undef P2y
|
||||||
|
#undef P2z
|
||||||
|
#undef Px
|
||||||
|
#undef Py
|
||||||
|
#undef Pz
|
||||||
|
#undef t1
|
||||||
|
#undef t2
|
||||||
|
#undef t3
|
||||||
|
#undef t4
|
||||||
|
#undef t5
|
||||||
|
#undef t6
|
||||||
|
#undef t7
|
||||||
|
#undef t8
|
||||||
|
#undef t9
|
||||||
|
#undef t10
|
||||||
|
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// P-384 from BearSSL, https://bearssl.org commit 7bea48e
|
||||||
|
// P-384 signature verification, used by built-in TLS for certificates and
|
||||||
|
// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static
|
||||||
|
// functions with p384_. Trimmed to P-384. Structs are not typedef-ed
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
|
||||||
|
// Verify ECDSA P-384 signature r || s over hash. Public key is X || Y.
|
||||||
|
// Return 1 on success, 0 on failure
|
||||||
|
int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash,
|
||||||
|
size_t hash_size, const uint8_t signature[96]);
|
||||||
|
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
|
||||||
+73
-99
@@ -1,6 +1,6 @@
|
|||||||
|
#include "tls_rsa.h"
|
||||||
#include "sha256.h"
|
#include "sha256.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
#include "tls_rsa.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
@@ -41,7 +41,9 @@
|
|||||||
#define MUL31_lo(x, y) (((uint32_t) (x) * (uint32_t) (y)) & 0x7fffffffU)
|
#define MUL31_lo(x, y) (((uint32_t) (x) * (uint32_t) (y)) & 0x7fffffffU)
|
||||||
#define CCOPY br_ccopy
|
#define CCOPY br_ccopy
|
||||||
|
|
||||||
static uint32_t NOT(uint32_t ctl) { return ctl ^ 1; }
|
static uint32_t NOT(uint32_t ctl) {
|
||||||
|
return ctl ^ 1;
|
||||||
|
}
|
||||||
static uint32_t MUX(uint32_t ctl, uint32_t x, uint32_t y) {
|
static uint32_t MUX(uint32_t ctl, uint32_t x, uint32_t y) {
|
||||||
return y ^ (-ctl & (x ^ y));
|
return y ^ (-ctl & (x ^ y));
|
||||||
}
|
}
|
||||||
@@ -62,10 +64,18 @@ static uint32_t GT(uint32_t x, uint32_t y) {
|
|||||||
static uint32_t BIT_LENGTH(uint32_t x) {
|
static uint32_t BIT_LENGTH(uint32_t x) {
|
||||||
uint32_t k, c;
|
uint32_t k, c;
|
||||||
k = NEQ(x, 0);
|
k = NEQ(x, 0);
|
||||||
c = GT(x, 0xffff); x = MUX(c, x >> 16, x); k += c << 4;
|
c = GT(x, 0xffff);
|
||||||
c = GT(x, 0x00ff); x = MUX(c, x >> 8, x); k += c << 3;
|
x = MUX(c, x >> 16, x);
|
||||||
c = GT(x, 0x000f); x = MUX(c, x >> 4, x); k += c << 2;
|
k += c << 4;
|
||||||
c = GT(x, 0x0003); x = MUX(c, x >> 2, x); k += c << 1;
|
c = GT(x, 0x00ff);
|
||||||
|
x = MUX(c, x >> 8, x);
|
||||||
|
k += c << 3;
|
||||||
|
c = GT(x, 0x000f);
|
||||||
|
x = MUX(c, x >> 4, x);
|
||||||
|
k += c << 2;
|
||||||
|
c = GT(x, 0x0003);
|
||||||
|
x = MUX(c, x >> 2, x);
|
||||||
|
k += c << 1;
|
||||||
k += GT(x, 0x0001);
|
k += GT(x, 0x0001);
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
@@ -95,11 +105,14 @@ static uint32_t br_divrem(uint32_t, uint32_t, uint32_t, uint32_t *);
|
|||||||
static void br_i31_muladd_small(uint32_t *, uint32_t, const uint32_t *);
|
static void br_i31_muladd_small(uint32_t *, uint32_t, const uint32_t *);
|
||||||
static void br_i31_reduce(uint32_t *, const uint32_t *, const uint32_t *);
|
static void br_i31_reduce(uint32_t *, const uint32_t *, const uint32_t *);
|
||||||
static void br_i31_rshift(uint32_t *, int);
|
static void br_i31_rshift(uint32_t *, int);
|
||||||
static void br_i31_decode_reduce(uint32_t *, const void *, size_t, const uint32_t *);
|
static void br_i31_decode_reduce(uint32_t *, const void *, size_t,
|
||||||
|
const uint32_t *);
|
||||||
static void br_i31_mulacc(uint32_t *, const uint32_t *, const uint32_t *);
|
static void br_i31_mulacc(uint32_t *, const uint32_t *, const uint32_t *);
|
||||||
static void br_i31_montymul(uint32_t *, const uint32_t *, const uint32_t *, const uint32_t *, uint32_t);
|
static void br_i31_montymul(uint32_t *, const uint32_t *, const uint32_t *,
|
||||||
|
const uint32_t *, uint32_t);
|
||||||
static void br_i31_to_monty(uint32_t *, const uint32_t *);
|
static void br_i31_to_monty(uint32_t *, const uint32_t *);
|
||||||
static void br_i31_modpow(uint32_t *, const unsigned char *, size_t, const uint32_t *, uint32_t, uint32_t *, uint32_t *);
|
static void br_i31_modpow(uint32_t *, const unsigned char *, size_t,
|
||||||
|
const uint32_t *, uint32_t, uint32_t *, uint32_t *);
|
||||||
static void br_i31_zero(uint32_t *x, uint32_t bit_len) {
|
static void br_i31_zero(uint32_t *x, uint32_t bit_len) {
|
||||||
*x++ = bit_len;
|
*x++ = bit_len;
|
||||||
memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
|
memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
|
||||||
@@ -113,9 +126,7 @@ static uint32_t br_div(uint32_t hi, uint32_t lo, uint32_t d) {
|
|||||||
uint32_t r;
|
uint32_t r;
|
||||||
return br_divrem(hi, lo, d, &r);
|
return br_divrem(hi, lo, d, &r);
|
||||||
}
|
}
|
||||||
static uint32_t
|
static uint32_t br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl) {
|
||||||
br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl)
|
|
||||||
{
|
|
||||||
uint32_t cc;
|
uint32_t cc;
|
||||||
size_t u, m;
|
size_t u, m;
|
||||||
|
|
||||||
@@ -132,9 +143,7 @@ br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl)
|
|||||||
}
|
}
|
||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
static uint32_t
|
static uint32_t br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl) {
|
||||||
br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl)
|
|
||||||
{
|
|
||||||
uint32_t cc;
|
uint32_t cc;
|
||||||
size_t u, m;
|
size_t u, m;
|
||||||
|
|
||||||
@@ -151,9 +160,7 @@ br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl)
|
|||||||
}
|
}
|
||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
static uint32_t
|
static uint32_t br_i31_bit_length(uint32_t *x, size_t xlen) {
|
||||||
br_i31_bit_length(uint32_t *x, size_t xlen)
|
|
||||||
{
|
|
||||||
uint32_t tw, twk;
|
uint32_t tw, twk;
|
||||||
|
|
||||||
tw = 0;
|
tw = 0;
|
||||||
@@ -168,9 +175,7 @@ br_i31_bit_length(uint32_t *x, size_t xlen)
|
|||||||
}
|
}
|
||||||
return (twk << 5) + BIT_LENGTH(tw);
|
return (twk << 5) + BIT_LENGTH(tw);
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_decode(uint32_t *x, const void *src, size_t len) {
|
||||||
br_i31_decode(uint32_t *x, const void *src, size_t len)
|
|
||||||
{
|
|
||||||
const unsigned char *buf;
|
const unsigned char *buf;
|
||||||
size_t u, v;
|
size_t u, v;
|
||||||
uint32_t acc;
|
uint32_t acc;
|
||||||
@@ -198,9 +203,7 @@ br_i31_decode(uint32_t *x, const void *src, size_t len)
|
|||||||
}
|
}
|
||||||
x[0] = br_i31_bit_length(x + 1, v - 1);
|
x[0] = br_i31_bit_length(x + 1, v - 1);
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_encode(void *dst, size_t len, const uint32_t *x) {
|
||||||
br_i31_encode(void *dst, size_t len, const uint32_t *x)
|
|
||||||
{
|
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
size_t k, xlen;
|
size_t k, xlen;
|
||||||
uint32_t acc;
|
uint32_t acc;
|
||||||
@@ -250,9 +253,7 @@ br_i31_encode(void *dst, size_t len, const uint32_t *x)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static uint32_t
|
static uint32_t br_i31_ninv31(uint32_t x) {
|
||||||
br_i31_ninv31(uint32_t x)
|
|
||||||
{
|
|
||||||
uint32_t y;
|
uint32_t y;
|
||||||
|
|
||||||
y = 2 - x;
|
y = 2 - x;
|
||||||
@@ -262,9 +263,7 @@ br_i31_ninv31(uint32_t x)
|
|||||||
y *= 2 - y * x;
|
y *= 2 - y * x;
|
||||||
return MUX(x & 1, -y, 0) & 0x7FFFFFFF;
|
return MUX(x & 1, -y, 0) & 0x7FFFFFFF;
|
||||||
}
|
}
|
||||||
static uint32_t
|
static uint32_t br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r) {
|
||||||
br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r)
|
|
||||||
{
|
|
||||||
/* TODO: optimize this */
|
/* TODO: optimize this */
|
||||||
uint32_t q;
|
uint32_t q;
|
||||||
uint32_t ch, cf;
|
uint32_t ch, cf;
|
||||||
@@ -291,9 +290,7 @@ br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r)
|
|||||||
*r = MUX(cf, lo - d, lo);
|
*r = MUX(cf, lo - d, lo);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m) {
|
||||||
br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m)
|
|
||||||
{
|
|
||||||
uint32_t m_bitlen;
|
uint32_t m_bitlen;
|
||||||
unsigned mblr;
|
unsigned mblr;
|
||||||
size_t u, mlen;
|
size_t u, mlen;
|
||||||
@@ -354,14 +351,11 @@ br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m)
|
|||||||
a1 = x[mlen];
|
a1 = x[mlen];
|
||||||
b0 = m[mlen];
|
b0 = m[mlen];
|
||||||
} else {
|
} else {
|
||||||
a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
|
a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & 0x7FFFFFFF;
|
||||||
& 0x7FFFFFFF;
|
|
||||||
memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
|
memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
|
||||||
x[1] = z;
|
x[1] = z;
|
||||||
a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
|
a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & 0x7FFFFFFF;
|
||||||
& 0x7FFFFFFF;
|
b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr)) & 0x7FFFFFFF;
|
||||||
b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr))
|
|
||||||
& 0x7FFFFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -421,9 +415,7 @@ br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m)
|
|||||||
br_i31_add(x, m, over);
|
br_i31_add(x, m, over);
|
||||||
br_i31_sub(x, m, under);
|
br_i31_sub(x, m, under);
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m) {
|
||||||
br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m)
|
|
||||||
{
|
|
||||||
uint32_t m_bitlen, a_bitlen;
|
uint32_t m_bitlen, a_bitlen;
|
||||||
size_t mlen, alen, u;
|
size_t mlen, alen, u;
|
||||||
|
|
||||||
@@ -460,9 +452,7 @@ br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m)
|
|||||||
br_i31_muladd_small(x, a[u], m);
|
br_i31_muladd_small(x, a[u], m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_rshift(uint32_t *x, int count) {
|
||||||
br_i31_rshift(uint32_t *x, int count)
|
|
||||||
{
|
|
||||||
size_t u, len;
|
size_t u, len;
|
||||||
uint32_t r;
|
uint32_t r;
|
||||||
|
|
||||||
@@ -480,10 +470,8 @@ br_i31_rshift(uint32_t *x, int count)
|
|||||||
}
|
}
|
||||||
x[len] = r;
|
x[len] = r;
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_decode_reduce(uint32_t *x, const void *src, size_t len,
|
||||||
br_i31_decode_reduce(uint32_t *x,
|
const uint32_t *m) {
|
||||||
const void *src, size_t len, const uint32_t *m)
|
|
||||||
{
|
|
||||||
uint32_t m_ebitlen, m_rbitlen;
|
uint32_t m_ebitlen, m_rbitlen;
|
||||||
size_t mblen, k;
|
size_t mblen, k;
|
||||||
const unsigned char *buf;
|
const unsigned char *buf;
|
||||||
@@ -556,9 +544,7 @@ br_i31_decode_reduce(uint32_t *x,
|
|||||||
br_i31_muladd_small(x, acc, m);
|
br_i31_muladd_small(x, acc, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b) {
|
||||||
br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b)
|
|
||||||
{
|
|
||||||
size_t alen, blen, u;
|
size_t alen, blen, u;
|
||||||
uint32_t dl, dh;
|
uint32_t dl, dh;
|
||||||
|
|
||||||
@@ -603,10 +589,8 @@ br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b)
|
|||||||
d[1 + u + alen] = (uint32_t) cc;
|
d[1 + u + alen] = (uint32_t) cc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
|
||||||
br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
|
const uint32_t *m, uint32_t m0i) {
|
||||||
const uint32_t *m, uint32_t m0i)
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* Each outer loop iteration computes:
|
* Each outer loop iteration computes:
|
||||||
* d <- (d + xu*y + f*m) / 2^31
|
* d <- (d + xu*y + f*m) / 2^31
|
||||||
@@ -654,28 +638,23 @@ br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
|
|||||||
for (v = 0; v < len4; v += 4) {
|
for (v = 0; v < len4; v += 4) {
|
||||||
uint64_t z;
|
uint64_t z;
|
||||||
|
|
||||||
z = (uint64_t)d[v + 1] + MUL31(xu, y[v + 1])
|
z = (uint64_t) d[v + 1] + MUL31(xu, y[v + 1]) + MUL31(f, m[v + 1]) + r;
|
||||||
+ MUL31(f, m[v + 1]) + r;
|
|
||||||
r = z >> 31;
|
r = z >> 31;
|
||||||
d[v + 0] = (uint32_t) z & 0x7FFFFFFF;
|
d[v + 0] = (uint32_t) z & 0x7FFFFFFF;
|
||||||
z = (uint64_t)d[v + 2] + MUL31(xu, y[v + 2])
|
z = (uint64_t) d[v + 2] + MUL31(xu, y[v + 2]) + MUL31(f, m[v + 2]) + r;
|
||||||
+ MUL31(f, m[v + 2]) + r;
|
|
||||||
r = z >> 31;
|
r = z >> 31;
|
||||||
d[v + 1] = (uint32_t) z & 0x7FFFFFFF;
|
d[v + 1] = (uint32_t) z & 0x7FFFFFFF;
|
||||||
z = (uint64_t)d[v + 3] + MUL31(xu, y[v + 3])
|
z = (uint64_t) d[v + 3] + MUL31(xu, y[v + 3]) + MUL31(f, m[v + 3]) + r;
|
||||||
+ MUL31(f, m[v + 3]) + r;
|
|
||||||
r = z >> 31;
|
r = z >> 31;
|
||||||
d[v + 2] = (uint32_t) z & 0x7FFFFFFF;
|
d[v + 2] = (uint32_t) z & 0x7FFFFFFF;
|
||||||
z = (uint64_t)d[v + 4] + MUL31(xu, y[v + 4])
|
z = (uint64_t) d[v + 4] + MUL31(xu, y[v + 4]) + MUL31(f, m[v + 4]) + r;
|
||||||
+ MUL31(f, m[v + 4]) + r;
|
|
||||||
r = z >> 31;
|
r = z >> 31;
|
||||||
d[v + 3] = (uint32_t) z & 0x7FFFFFFF;
|
d[v + 3] = (uint32_t) z & 0x7FFFFFFF;
|
||||||
}
|
}
|
||||||
for (; v < len; v++) {
|
for (; v < len; v++) {
|
||||||
uint64_t z;
|
uint64_t z;
|
||||||
|
|
||||||
z = (uint64_t)d[v + 1] + MUL31(xu, y[v + 1])
|
z = (uint64_t) d[v + 1] + MUL31(xu, y[v + 1]) + MUL31(f, m[v + 1]) + r;
|
||||||
+ MUL31(f, m[v + 1]) + r;
|
|
||||||
r = z >> 31;
|
r = z >> 31;
|
||||||
d[v] = (uint32_t) z & 0x7FFFFFFF;
|
d[v] = (uint32_t) z & 0x7FFFFFFF;
|
||||||
}
|
}
|
||||||
@@ -703,20 +682,16 @@ br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
|
|||||||
*/
|
*/
|
||||||
br_i31_sub(d, m, NEQ(dh, 0) | NOT(br_i31_sub(d, m, 0)));
|
br_i31_sub(d, m, NEQ(dh, 0) | NOT(br_i31_sub(d, m, 0)));
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_to_monty(uint32_t *x, const uint32_t *m) {
|
||||||
br_i31_to_monty(uint32_t *x, const uint32_t *m)
|
|
||||||
{
|
|
||||||
uint32_t k;
|
uint32_t k;
|
||||||
|
|
||||||
for (k = (m[0] + 31) >> 5; k > 0; k--) {
|
for (k = (m[0] + 31) >> 5; k > 0; k--) {
|
||||||
br_i31_muladd_small(x, 0, m);
|
br_i31_muladd_small(x, 0, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void
|
static void br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen,
|
||||||
br_i31_modpow(uint32_t *x,
|
const uint32_t *m, uint32_t m0i, uint32_t *t1,
|
||||||
const unsigned char *e, size_t elen,
|
uint32_t *t2) {
|
||||||
const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2)
|
|
||||||
{
|
|
||||||
size_t mlen;
|
size_t mlen;
|
||||||
uint32_t k;
|
uint32_t k;
|
||||||
|
|
||||||
@@ -776,8 +751,8 @@ static int rsa_i31_eq(const uint32_t *a, const uint32_t *b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp,
|
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,
|
size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out,
|
||||||
uint8_t *out, size_t outsz) {
|
size_t outsz) {
|
||||||
uint32_t tmp[4 * BR_RSA_WORDS], *m = tmp, *x, *t1, *t2, m0i;
|
uint32_t tmp[4 * BR_RSA_WORDS], *m = tmp, *x, *t1, *t2, m0i;
|
||||||
int fwlen;
|
int fwlen;
|
||||||
if (out == NULL) return -1;
|
if (out == NULL) return -1;
|
||||||
@@ -803,12 +778,10 @@ int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
|
int mg_rsa_crt_sign(const uint8_t *em, size_t em_len, const uint8_t *dP,
|
||||||
const uint8_t *dP, size_t dP_len,
|
size_t dP_len, const uint8_t *dQ, size_t dQ_len,
|
||||||
const uint8_t *dQ, size_t dQ_len,
|
const uint8_t *p, size_t p_len, const uint8_t *q,
|
||||||
const uint8_t *p, size_t p_len,
|
size_t q_len, const uint8_t *qInv, size_t qInv_len,
|
||||||
const uint8_t *q, size_t q_len,
|
|
||||||
const uint8_t *qInv, size_t qInv_len,
|
|
||||||
uint8_t *signature, size_t sig_len) {
|
uint8_t *signature, size_t sig_len) {
|
||||||
uint32_t tmp[8 * BR_RSA_FACTOR_WORDS], *mq = tmp, *s2, *mp, *s1, *t1, *t2;
|
uint32_t tmp[8 * BR_RSA_FACTOR_WORDS], *mq = tmp, *s2, *mp, *s1, *t1, *t2;
|
||||||
uint32_t *c1, *c2;
|
uint32_t *c1, *c2;
|
||||||
@@ -829,9 +802,8 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
|
|||||||
qInv_len = rsa_trim(&qInv, qInv_len);
|
qInv_len = rsa_trim(&qInv, qInv_len);
|
||||||
if (p_len < 128 || q_len < 128 || p_len > (BR_MAX_RSA_FACTOR >> 3) ||
|
if (p_len < 128 || q_len < 128 || p_len > (BR_MAX_RSA_FACTOR >> 3) ||
|
||||||
q_len > (BR_MAX_RSA_FACTOR >> 3) || dP_len == 0 || dQ_len == 0 ||
|
q_len > (BR_MAX_RSA_FACTOR >> 3) || dP_len == 0 || dQ_len == 0 ||
|
||||||
qInv_len == 0 || dP_len > p_len || dQ_len > q_len ||
|
qInv_len == 0 || dP_len > p_len || dQ_len > q_len || qInv_len > p_len ||
|
||||||
qInv_len > p_len || xlen > sizeof(x) || em_len == 0 ||
|
xlen > sizeof(x) || em_len == 0 || em_len > xlen) {
|
||||||
em_len > xlen) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
plen = p_len, qlen = q_len;
|
plen = p_len, qlen = q_len;
|
||||||
@@ -850,7 +822,8 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
|
|||||||
memset(t2, 0, 2 * (size_t) fwlen * sizeof(*t2));
|
memset(t2, 0, 2 * (size_t) fwlen * sizeof(*t2));
|
||||||
t2[0] = mq[0];
|
t2[0] = mq[0];
|
||||||
br_i31_mulacc(t2, mq, mp);
|
br_i31_mulacc(t2, mq, mp);
|
||||||
if (xlen != ((t2[0] + 7) >> 3)) goto done;
|
// Convert i31-encoded bit length to byte length, and check
|
||||||
|
if (xlen != ((t2[0] - (t2[0] >> 5) + 7) >> 3)) goto done;
|
||||||
br_i31_encode(signature, xlen, t2);
|
br_i31_encode(signature, xlen, t2);
|
||||||
for (u = xlen, ok = 0; u > 0; u--) {
|
for (u = xlen, ok = 0; u > 0; u--) {
|
||||||
uint32_t wn = signature[u - 1], wx = x[u - 1];
|
uint32_t wn = signature[u - 1], wx = x[u - 1];
|
||||||
@@ -871,10 +844,10 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
|
|||||||
br_i31_montymul(t2, s1, t1, mp, p0i);
|
br_i31_montymul(t2, s1, t1, mp, p0i);
|
||||||
br_i31_mulacc(s2, mq, t2);
|
br_i31_mulacc(s2, mq, t2);
|
||||||
br_i31_reduce(t1, s2, mq);
|
br_i31_reduce(t1, s2, mq);
|
||||||
ok = rsa_i31_eq(t1, c2);
|
ok = rsa_i31_eq(t1, c2); // fault check s mod q == m2
|
||||||
br_i31_decode(mp, p, plen);
|
br_i31_decode(t2, p, plen); // recombined sig in s2 overlaps mp, keep p in t2
|
||||||
br_i31_reduce(t1, s2, mp);
|
br_i31_reduce(t1, s2, t2);
|
||||||
ok &= rsa_i31_eq(t1, c1);
|
ok &= rsa_i31_eq(t1, c1); // fault check s mod p == m1
|
||||||
if (!ok) goto done;
|
if (!ok) goto done;
|
||||||
ok = 0;
|
ok = 0;
|
||||||
br_i31_encode(signature, sig_len, s2);
|
br_i31_encode(signature, sig_len, s2);
|
||||||
@@ -887,8 +860,8 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MGF1-SHA256 RFC 8017 B.2.1
|
// MGF1-SHA256 RFC 8017 B.2.1
|
||||||
static void mgf1_sha256(const uint8_t *seed, size_t seed_len,
|
static void mgf1_sha256(const uint8_t *seed, size_t seed_len, uint8_t *mask,
|
||||||
uint8_t *mask, size_t mask_len) {
|
size_t mask_len) {
|
||||||
uint8_t cnt[4]; // I2OSP(counter, 4), big-endian
|
uint8_t cnt[4]; // I2OSP(counter, 4), big-endian
|
||||||
uint8_t tmp[32]; // one SHA-256 output block
|
uint8_t tmp[32]; // one SHA-256 output block
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
@@ -912,7 +885,8 @@ static void mgf1_sha256(const uint8_t *seed, size_t seed_len,
|
|||||||
// em - output of RSA public-key primitive (nlen bytes)
|
// em - output of RSA public-key primitive (nlen bytes)
|
||||||
// nlen - modulus byte length (66 to 512)
|
// nlen - modulus byte length (66 to 512)
|
||||||
// mhash - SHA-256 of the TLS 1.3 signed content (tls->sighash, 32 bytes)
|
// mhash - SHA-256 of the TLS 1.3 signed content (tls->sighash, 32 bytes)
|
||||||
static bool pss_verify_sha256(const uint8_t *em, size_t nlen, const uint8_t *mhash) {
|
static bool pss_verify_sha256(const uint8_t *em, size_t nlen,
|
||||||
|
const uint8_t *mhash) {
|
||||||
// TLS 1.3: H_len = salt_len = 32
|
// TLS 1.3: H_len = salt_len = 32
|
||||||
uint8_t db[479]; // unmasked DB after MGF1 XOR
|
uint8_t db[479]; // unmasked DB after MGF1 XOR
|
||||||
uint8_t dbmask[479]; // MGF1-SHA256(H, db_len); nlen - hLen - 1 = 479
|
uint8_t dbmask[479]; // MGF1-SHA256(H, db_len); nlen - hLen - 1 = 479
|
||||||
@@ -957,12 +931,12 @@ bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash) {
|
|||||||
// hashlen - hash length, 32 for SHA-256 or 48 for SHA-384
|
// hashlen - hash length, 32 for SHA-256 or 48 for SHA-384
|
||||||
static bool pkcs1_v15_verify(const uint8_t *em, size_t nlen,
|
static bool pkcs1_v15_verify(const uint8_t *em, size_t nlen,
|
||||||
const uint8_t *hash, size_t hashlen) {
|
const uint8_t *hash, size_t hashlen) {
|
||||||
static const uint8_t sha256_di[] = {
|
static const uint8_t sha256_di[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
|
||||||
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
|
||||||
0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
|
0x01, 0x05, 0x00, 0x04, 0x20};
|
||||||
static const uint8_t sha384_di[] = {
|
static const uint8_t sha384_di[] = {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60,
|
||||||
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
|
||||||
0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
|
0x02, 0x05, 0x00, 0x04, 0x30};
|
||||||
const uint8_t *di = hashlen == 32 ? sha256_di : sha384_di;
|
const uint8_t *di = hashlen == 32 ? sha256_di : sha384_di;
|
||||||
size_t disz = hashlen == 32 ? sizeof(sha256_di) : sizeof(sha384_di);
|
size_t disz = hashlen == 32 ? sizeof(sha256_di) : sizeof(sha384_di);
|
||||||
size_t pslen, i;
|
size_t pslen, i;
|
||||||
|
|||||||
-3304
File diff suppressed because it is too large.
Load diff
-659
@@ -1,659 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "arch.h"
|
|
||||||
|
|
||||||
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
|
||||||
|
|
||||||
#ifndef _UECC_H_
|
|
||||||
#define _UECC_H_
|
|
||||||
|
|
||||||
/* Platform selection options.
|
|
||||||
If MG_UECC_PLATFORM is not defined, the code will try to guess it based on
|
|
||||||
compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */
|
|
||||||
#define mg_uecc_arch_other 0
|
|
||||||
#define mg_uecc_x86 1
|
|
||||||
#define mg_uecc_x86_64 2
|
|
||||||
#define mg_uecc_arm 3
|
|
||||||
#define mg_uecc_arm_thumb 4
|
|
||||||
#define mg_uecc_arm_thumb2 5
|
|
||||||
#define mg_uecc_arm64 6
|
|
||||||
#define mg_uecc_avr 7
|
|
||||||
|
|
||||||
/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform
|
|
||||||
(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will
|
|
||||||
be automatically set based on your platform. */
|
|
||||||
|
|
||||||
/* Optimization level; trade speed for code size.
|
|
||||||
Larger values produce code that is faster but larger.
|
|
||||||
Currently supported values are 0 - 4; 0 is unusably slow for most
|
|
||||||
applications. Optimization level 4 currently only has an effect ARM platforms
|
|
||||||
where more than one curve is enabled. */
|
|
||||||
#ifndef MG_UECC_OPTIMIZATION_LEVEL
|
|
||||||
#define MG_UECC_OPTIMIZATION_LEVEL 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a
|
|
||||||
specific function to be used for (scalar) squaring instead of the generic
|
|
||||||
multiplication function. This can make things faster somewhat faster, but
|
|
||||||
increases the code size. */
|
|
||||||
#ifndef MG_UECC_SQUARE_FUNC
|
|
||||||
#define MG_UECC_SQUARE_FUNC 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will
|
|
||||||
switch to native little-endian format for *all* arrays passed in and out of the
|
|
||||||
public API. This includes public and private keys, shared secrets, signatures
|
|
||||||
and message hashes. Using this switch reduces the amount of call stack memory
|
|
||||||
used by uECC, since less intermediate translations are required. Note that this
|
|
||||||
will *only* work on native little-endian processors and it will treat the
|
|
||||||
uint8_t arrays passed into the public API as word arrays, therefore requiring
|
|
||||||
the provided byte arrays to be word aligned on architectures that do not support
|
|
||||||
unaligned accesses. IMPORTANT: Keys and signatures generated with
|
|
||||||
MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures
|
|
||||||
generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same
|
|
||||||
endianness. */
|
|
||||||
#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
|
|
||||||
#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Curve support selection. Set to 0 to remove that curve. */
|
|
||||||
#ifndef MG_UECC_SUPPORTS_secp160r1
|
|
||||||
#define MG_UECC_SUPPORTS_secp160r1 0
|
|
||||||
#endif
|
|
||||||
#ifndef MG_UECC_SUPPORTS_secp192r1
|
|
||||||
#define MG_UECC_SUPPORTS_secp192r1 0
|
|
||||||
#endif
|
|
||||||
#ifndef MG_UECC_SUPPORTS_secp224r1
|
|
||||||
#define MG_UECC_SUPPORTS_secp224r1 0
|
|
||||||
#endif
|
|
||||||
#ifndef MG_UECC_SUPPORTS_secp256r1
|
|
||||||
#define MG_UECC_SUPPORTS_secp256r1 1
|
|
||||||
#endif
|
|
||||||
#ifndef MG_UECC_SUPPORTS_secp256k1
|
|
||||||
#define MG_UECC_SUPPORTS_secp256k1 0
|
|
||||||
#endif
|
|
||||||
#ifndef MG_UECC_SUPPORTS_secp384r1
|
|
||||||
#define MG_UECC_SUPPORTS_secp384r1 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Specifies whether compressed point format is supported.
|
|
||||||
Set to 0 to disable point compression/decompression functions. */
|
|
||||||
#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT
|
|
||||||
#define MG_UECC_SUPPORT_COMPRESSED_POINT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct MG_UECC_Curve_t;
|
|
||||||
typedef const struct MG_UECC_Curve_t *MG_UECC_Curve;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MG_UECC_SUPPORTS_secp160r1
|
|
||||||
MG_UECC_Curve mg_uecc_secp160r1(void);
|
|
||||||
#endif
|
|
||||||
#if MG_UECC_SUPPORTS_secp192r1
|
|
||||||
MG_UECC_Curve mg_uecc_secp192r1(void);
|
|
||||||
#endif
|
|
||||||
#if MG_UECC_SUPPORTS_secp224r1
|
|
||||||
MG_UECC_Curve mg_uecc_secp224r1(void);
|
|
||||||
#endif
|
|
||||||
#if MG_UECC_SUPPORTS_secp256r1
|
|
||||||
MG_UECC_Curve mg_uecc_secp256r1(void);
|
|
||||||
#endif
|
|
||||||
#if MG_UECC_SUPPORTS_secp256k1
|
|
||||||
MG_UECC_Curve mg_uecc_secp256k1(void);
|
|
||||||
#endif
|
|
||||||
#if MG_UECC_SUPPORTS_secp384r1
|
|
||||||
MG_UECC_Curve mg_uecc_secp384r1(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* MG_UECC_RNG_Function type
|
|
||||||
The RNG function should fill 'size' random bytes into 'dest'. It should return 1
|
|
||||||
if 'dest' was filled with random data, or 0 if the random data could not be
|
|
||||||
generated. The filled-in values should be either truly random, or from a
|
|
||||||
cryptographically-secure PRNG.
|
|
||||||
|
|
||||||
A correctly functioning RNG is required before calling mg_uecc_make_key() or
|
|
||||||
mg_uecc_sign().
|
|
||||||
|
|
||||||
Setting a correctly functioning RNG function improves the resistance to
|
|
||||||
side-channel attacks for mg_uecc_shared_secret() and
|
|
||||||
mg_uecc_sign_deterministic().
|
|
||||||
*/
|
|
||||||
typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size);
|
|
||||||
|
|
||||||
// Mongoose: mg_uecc_make_key() and mg_uecc_sign() use mg_random() when no
|
|
||||||
// explicit uECC RNG is installed. If mg_random() cannot provide a strong source,
|
|
||||||
// those operations fail. Deterministic signing works without RNG, but only an
|
|
||||||
// explicit uECC RNG enables its optional side-channel blinding.
|
|
||||||
|
|
||||||
/* mg_uecc_set_rng() function.
|
|
||||||
Set the function that will be used to generate random bytes. The RNG function
|
|
||||||
should return 1 if the random data was generated, or 0 if the random data could
|
|
||||||
not be generated.
|
|
||||||
|
|
||||||
Mongoose: this overrides the default mg_random() fallback used by
|
|
||||||
mg_uecc_make_key() and mg_uecc_sign(). Do not call mg_uecc_set_rng() unless the
|
|
||||||
application must provide its own cryptographic RNG.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
rng_function - The function that will be used to generate random bytes.
|
|
||||||
*/
|
|
||||||
void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function);
|
|
||||||
|
|
||||||
/* mg_uecc_get_rng() function.
|
|
||||||
|
|
||||||
Returns the function that will be used to generate random bytes.
|
|
||||||
*/
|
|
||||||
MG_UECC_RNG_Function mg_uecc_get_rng(void);
|
|
||||||
|
|
||||||
/* mg_uecc_curve_private_key_size() function.
|
|
||||||
|
|
||||||
Returns the size of a private key for the curve in bytes.
|
|
||||||
*/
|
|
||||||
int mg_uecc_curve_private_key_size(MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* mg_uecc_curve_public_key_size() function.
|
|
||||||
|
|
||||||
Returns the size of a public key for the curve in bytes.
|
|
||||||
*/
|
|
||||||
int mg_uecc_curve_public_key_size(MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* mg_uecc_make_key() function.
|
|
||||||
Create a public/private key pair.
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
public_key - Will be filled in with the public key. Must be at least 2 *
|
|
||||||
the curve size (in bytes) long. For example, if the curve is secp256r1,
|
|
||||||
public_key must be 64 bytes long. private_key - Will be filled in with the
|
|
||||||
private key. Must be as long as the curve order; this is typically the same as
|
|
||||||
the curve size, except for secp160r1. For example, if the curve is secp256r1,
|
|
||||||
private_key must be 32 bytes long.
|
|
||||||
|
|
||||||
For secp160r1, private_key must be 21 bytes long! Note that
|
|
||||||
the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it
|
|
||||||
being non-zero).
|
|
||||||
|
|
||||||
Returns 1 if the key pair was generated successfully, 0 if an error occurred.
|
|
||||||
*/
|
|
||||||
int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key,
|
|
||||||
MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* mg_uecc_shared_secret() function.
|
|
||||||
Compute a shared secret given your secret key and someone else's public key. If
|
|
||||||
the public key is not from a trusted source and has not been previously
|
|
||||||
verified, you should verify it first using mg_uecc_valid_public_key(). Note: It
|
|
||||||
is recommended that you hash the result of mg_uecc_shared_secret() before using
|
|
||||||
it for symmetric encryption or HMAC.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
public_key - The public key of the remote party.
|
|
||||||
private_key - Your private key.
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
secret - Will be filled in with the shared secret value. Must be the same
|
|
||||||
size as the curve size; for example, if the curve is secp256r1, secret must be
|
|
||||||
32 bytes long.
|
|
||||||
|
|
||||||
Returns 1 if the shared secret was generated successfully, 0 if an error
|
|
||||||
occurred.
|
|
||||||
*/
|
|
||||||
int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
|
|
||||||
uint8_t *secret, MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
#if MG_UECC_SUPPORT_COMPRESSED_POINT
|
|
||||||
/* mg_uecc_compress() function.
|
|
||||||
Compress a public key.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
public_key - The public key to compress.
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
compressed - Will be filled in with the compressed public key. Must be at
|
|
||||||
least (curve size + 1) bytes long; for example, if the curve is secp256r1,
|
|
||||||
compressed must be 33 bytes long.
|
|
||||||
*/
|
|
||||||
void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed,
|
|
||||||
MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* mg_uecc_decompress() function.
|
|
||||||
Decompress a compressed public key.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
compressed - The compressed public key.
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
public_key - Will be filled in with the decompressed public key.
|
|
||||||
*/
|
|
||||||
void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key,
|
|
||||||
MG_UECC_Curve curve);
|
|
||||||
#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
|
|
||||||
|
|
||||||
/* mg_uecc_valid_public_key() function.
|
|
||||||
Check to see if a public key is valid.
|
|
||||||
|
|
||||||
Note that you are not required to check for a valid public key before using any
|
|
||||||
other uECC functions. However, you may wish to avoid spending CPU time computing
|
|
||||||
a shared secret or verifying a signature using an invalid public key.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
public_key - The public key to check.
|
|
||||||
|
|
||||||
Returns 1 if the public key is valid, 0 if it is invalid.
|
|
||||||
*/
|
|
||||||
int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* mg_uecc_compute_public_key() function.
|
|
||||||
Compute the corresponding public key for a private key.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
private_key - The private key to compute the public key for
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
public_key - Will be filled in with the corresponding public key
|
|
||||||
|
|
||||||
Returns 1 if the key was computed successfully, 0 if an error occurred.
|
|
||||||
*/
|
|
||||||
int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key,
|
|
||||||
MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* mg_uecc_sign() function.
|
|
||||||
Generate an ECDSA signature for a given hash value.
|
|
||||||
|
|
||||||
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and
|
|
||||||
pass it in to this function along with your private key.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
private_key - Your private key.
|
|
||||||
message_hash - The hash of the message to sign.
|
|
||||||
hash_size - The size of message_hash in bytes.
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
signature - Will be filled in with the signature value. Must be at least 2 *
|
|
||||||
curve size long. For example, if the curve is secp256r1, signature must be 64
|
|
||||||
bytes long.
|
|
||||||
|
|
||||||
Returns 1 if the signature generated successfully, 0 if an error occurred.
|
|
||||||
*/
|
|
||||||
int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash,
|
|
||||||
unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* MG_UECC_HashContext structure.
|
|
||||||
This is used to pass in an arbitrary hash function to
|
|
||||||
mg_uecc_sign_deterministic(). The structure will be used for multiple hash
|
|
||||||
computations; each time a new hash is computed, init_hash() will be called,
|
|
||||||
followed by one or more calls to update_hash(), and finally a call to
|
|
||||||
finish_hash() to produce the resulting hash.
|
|
||||||
|
|
||||||
The intention is that you will create a structure that includes
|
|
||||||
MG_UECC_HashContext followed by any hash-specific data. For example:
|
|
||||||
|
|
||||||
typedef struct SHA256_HashContext {
|
|
||||||
MG_UECC_HashContext uECC;
|
|
||||||
SHA256_CTX ctx;
|
|
||||||
} SHA256_HashContext;
|
|
||||||
|
|
||||||
void init_SHA256(MG_UECC_HashContext *base) {
|
|
||||||
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
|
||||||
SHA256_Init(&context->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_SHA256(MG_UECC_HashContext *base,
|
|
||||||
const uint8_t *message,
|
|
||||||
unsigned message_size) {
|
|
||||||
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
|
||||||
SHA256_Update(&context->ctx, message, message_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) {
|
|
||||||
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
|
||||||
SHA256_Final(hash_result, &context->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
... when signing ...
|
|
||||||
{
|
|
||||||
uint8_t tmp[32 + 32 + 64];
|
|
||||||
SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64,
|
|
||||||
32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
typedef struct MG_UECC_HashContext {
|
|
||||||
void (*init_hash)(const struct MG_UECC_HashContext *context);
|
|
||||||
void (*update_hash)(const struct MG_UECC_HashContext *context,
|
|
||||||
const uint8_t *message, unsigned message_size);
|
|
||||||
void (*finish_hash)(const struct MG_UECC_HashContext *context,
|
|
||||||
uint8_t *hash_result);
|
|
||||||
unsigned
|
|
||||||
block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */
|
|
||||||
unsigned
|
|
||||||
result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */
|
|
||||||
uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size +
|
|
||||||
block_size) bytes. */
|
|
||||||
} MG_UECC_HashContext;
|
|
||||||
|
|
||||||
/* mg_uecc_sign_deterministic() function.
|
|
||||||
Generate an ECDSA signature for a given hash value, using a deterministic
|
|
||||||
algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng()
|
|
||||||
before calling this function; however, if the RNG is defined it will improve
|
|
||||||
resistance to side-channel attacks.
|
|
||||||
|
|
||||||
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and
|
|
||||||
pass it to this function along with your private key and a hash context. Note
|
|
||||||
that the message_hash does not need to be computed with the same hash function
|
|
||||||
used by hash_context.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
private_key - Your private key.
|
|
||||||
message_hash - The hash of the message to sign.
|
|
||||||
hash_size - The size of message_hash in bytes.
|
|
||||||
hash_context - A hash context to use.
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
signature - Will be filled in with the signature value.
|
|
||||||
|
|
||||||
Returns 1 if the signature generated successfully, 0 if an error occurred.
|
|
||||||
*/
|
|
||||||
int mg_uecc_sign_deterministic(const uint8_t *private_key,
|
|
||||||
const uint8_t *message_hash, unsigned hash_size,
|
|
||||||
const MG_UECC_HashContext *hash_context,
|
|
||||||
uint8_t *signature, MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* mg_uecc_verify() function.
|
|
||||||
Verify an ECDSA signature.
|
|
||||||
|
|
||||||
Usage: Compute the hash of the signed data using the same hash as the signer and
|
|
||||||
pass it to this function along with the signer's public key and the signature
|
|
||||||
values (r and s).
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
public_key - The signer's public key.
|
|
||||||
message_hash - The hash of the signed data.
|
|
||||||
hash_size - The size of message_hash in bytes.
|
|
||||||
signature - The signature value.
|
|
||||||
|
|
||||||
Returns 1 if the signature is valid, 0 if it is invalid.
|
|
||||||
*/
|
|
||||||
int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash,
|
|
||||||
unsigned hash_size, const uint8_t *signature,
|
|
||||||
MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* end of extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _UECC_H_ */
|
|
||||||
|
|
||||||
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
|
||||||
|
|
||||||
#ifndef _UECC_TYPES_H_
|
|
||||||
#define _UECC_TYPES_H_
|
|
||||||
|
|
||||||
#ifndef MG_UECC_PLATFORM
|
|
||||||
#if defined(__AVR__) && __AVR__
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_avr
|
|
||||||
#elif defined(__thumb2__) || \
|
|
||||||
defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_arm_thumb2
|
|
||||||
#elif defined(__thumb__)
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_arm_thumb
|
|
||||||
#elif defined(__arm__) || defined(_M_ARM)
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_arm
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_arm64
|
|
||||||
#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \
|
|
||||||
defined(__I86__)
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_x86
|
|
||||||
#elif defined(__amd64__) || defined(_M_X64)
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_x86_64
|
|
||||||
#else
|
|
||||||
#define MG_UECC_PLATFORM mg_uecc_arch_other
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_UECC_ARM_USE_UMAAL
|
|
||||||
#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6)
|
|
||||||
#define MG_UECC_ARM_USE_UMAAL 1
|
|
||||||
#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \
|
|
||||||
(!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__)
|
|
||||||
#define MG_UECC_ARM_USE_UMAAL 1
|
|
||||||
#else
|
|
||||||
#define MG_UECC_ARM_USE_UMAAL 0
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_UECC_WORD_SIZE
|
|
||||||
#if MG_UECC_PLATFORM == mg_uecc_avr
|
|
||||||
#define MG_UECC_WORD_SIZE 1
|
|
||||||
#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64)
|
|
||||||
#define MG_UECC_WORD_SIZE 8
|
|
||||||
#else
|
|
||||||
#define MG_UECC_WORD_SIZE 4
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \
|
|
||||||
(MG_UECC_WORD_SIZE != 8)
|
|
||||||
#error "Unsupported value for MG_UECC_WORD_SIZE"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1))
|
|
||||||
#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR")
|
|
||||||
#undef MG_UECC_WORD_SIZE
|
|
||||||
#define MG_UECC_WORD_SIZE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ((MG_UECC_PLATFORM == mg_uecc_arm || \
|
|
||||||
MG_UECC_PLATFORM == mg_uecc_arm_thumb || \
|
|
||||||
MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \
|
|
||||||
(MG_UECC_WORD_SIZE != 4))
|
|
||||||
#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM")
|
|
||||||
#undef MG_UECC_WORD_SIZE
|
|
||||||
#define MG_UECC_WORD_SIZE 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef int8_t wordcount_t;
|
|
||||||
typedef int16_t bitcount_t;
|
|
||||||
typedef int8_t cmpresult_t;
|
|
||||||
|
|
||||||
#if (MG_UECC_WORD_SIZE == 1)
|
|
||||||
|
|
||||||
typedef uint8_t mg_uecc_word_t;
|
|
||||||
typedef uint16_t mg_uecc_dword_t;
|
|
||||||
|
|
||||||
#define HIGH_BIT_SET 0x80
|
|
||||||
#define MG_UECC_WORD_BITS 8
|
|
||||||
#define MG_UECC_WORD_BITS_SHIFT 3
|
|
||||||
#define MG_UECC_WORD_BITS_MASK 0x07
|
|
||||||
|
|
||||||
#elif (MG_UECC_WORD_SIZE == 4)
|
|
||||||
|
|
||||||
typedef uint32_t mg_uecc_word_t;
|
|
||||||
typedef uint64_t mg_uecc_dword_t;
|
|
||||||
|
|
||||||
#define HIGH_BIT_SET 0x80000000
|
|
||||||
#define MG_UECC_WORD_BITS 32
|
|
||||||
#define MG_UECC_WORD_BITS_SHIFT 5
|
|
||||||
#define MG_UECC_WORD_BITS_MASK 0x01F
|
|
||||||
|
|
||||||
#elif (MG_UECC_WORD_SIZE == 8)
|
|
||||||
|
|
||||||
typedef uint64_t mg_uecc_word_t;
|
|
||||||
|
|
||||||
#define HIGH_BIT_SET 0x8000000000000000U
|
|
||||||
#define MG_UECC_WORD_BITS 64
|
|
||||||
#define MG_UECC_WORD_BITS_SHIFT 6
|
|
||||||
#define MG_UECC_WORD_BITS_MASK 0x03F
|
|
||||||
|
|
||||||
#endif /* MG_UECC_WORD_SIZE */
|
|
||||||
|
|
||||||
#endif /* _UECC_TYPES_H_ */
|
|
||||||
|
|
||||||
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
|
||||||
|
|
||||||
#ifndef _UECC_VLI_H_
|
|
||||||
#define _UECC_VLI_H_
|
|
||||||
|
|
||||||
// #include "types.h"
|
|
||||||
// #include "uECC.h"
|
|
||||||
|
|
||||||
/* Functions for raw large-integer manipulation. These are only available
|
|
||||||
if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */
|
|
||||||
#ifndef MG_UECC_ENABLE_VLI_API
|
|
||||||
#define MG_UECC_ENABLE_VLI_API 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MG_UECC_ENABLE_VLI_API
|
|
||||||
|
|
||||||
void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Constant-time comparison to zero - secure way to compare long integers */
|
|
||||||
/* Returns 1 if vli == 0, 0 otherwise. */
|
|
||||||
mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Returns nonzero if bit 'bit' of vli is set. */
|
|
||||||
mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit);
|
|
||||||
|
|
||||||
/* Counts the number of bits required to represent vli. */
|
|
||||||
bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli,
|
|
||||||
const wordcount_t max_words);
|
|
||||||
|
|
||||||
/* Sets dest = src. */
|
|
||||||
void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Constant-time comparison function - secure way to compare long integers */
|
|
||||||
/* Returns one if left == right, zero otherwise */
|
|
||||||
mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Constant-time comparison function - secure way to compare long integers */
|
|
||||||
/* Returns sign of left - right, in constant time. */
|
|
||||||
cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right, wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes vli = vli >> 1. */
|
|
||||||
void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = left + right, returning carry. Can modify in place. */
|
|
||||||
mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result,
|
|
||||||
const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = left - right, returning borrow. Can modify in place. */
|
|
||||||
mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result,
|
|
||||||
const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = left * right. Result must be 2 * num_words long. */
|
|
||||||
void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right, wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = left^2. Result must be 2 * num_words long. */
|
|
||||||
void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = (left + right) % mod.
|
|
||||||
Assumes that left < mod and right < mod, and that result does not overlap
|
|
||||||
mod. */
|
|
||||||
void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = (left - right) % mod.
|
|
||||||
Assumes that left < mod and right < mod, and that result does not overlap
|
|
||||||
mod. */
|
|
||||||
void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = product % mod, where product is 2N words long.
|
|
||||||
Currently only designed to work for mod == curve->p or curve_n. */
|
|
||||||
void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product,
|
|
||||||
const mg_uecc_word_t *mod, wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Calculates result = product (mod curve->p), where product is up to
|
|
||||||
2 * curve->num_words long. */
|
|
||||||
void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product,
|
|
||||||
MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* Computes result = (left * right) % mod.
|
|
||||||
Currently only designed to work for mod == curve->p or curve_n. */
|
|
||||||
void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = (left * right) % curve->p. */
|
|
||||||
void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result,
|
|
||||||
const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *right, MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* Computes result = left^2 % mod.
|
|
||||||
Currently only designed to work for mod == curve->p or curve_n. */
|
|
||||||
void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
||||||
const mg_uecc_word_t *mod, wordcount_t num_words);
|
|
||||||
|
|
||||||
/* Computes result = left^2 % curve->p. */
|
|
||||||
void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result,
|
|
||||||
const mg_uecc_word_t *left,
|
|
||||||
MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* Computes result = (1 / input) % mod.*/
|
|
||||||
void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input,
|
|
||||||
const mg_uecc_word_t *mod, wordcount_t num_words);
|
|
||||||
|
|
||||||
#if MG_UECC_SUPPORT_COMPRESSED_POINT
|
|
||||||
/* Calculates a = sqrt(a) (mod curve->p) */
|
|
||||||
void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Converts an integer in uECC native format to big-endian bytes. */
|
|
||||||
void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes,
|
|
||||||
const mg_uecc_word_t *native);
|
|
||||||
/* Converts big-endian bytes to an integer in uECC native format. */
|
|
||||||
void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes,
|
|
||||||
int num_bytes);
|
|
||||||
|
|
||||||
unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve);
|
|
||||||
unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve);
|
|
||||||
unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve);
|
|
||||||
unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve);
|
|
||||||
unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve);
|
|
||||||
unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve);
|
|
||||||
const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve);
|
|
||||||
const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve);
|
|
||||||
const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* Multiplies a point by a scalar. Points are represented by the X coordinate
|
|
||||||
followed by the Y coordinate in the same array, both coordinates are
|
|
||||||
curve->num_words long. Note that scalar must be curve->num_n_words long (NOT
|
|
||||||
curve->num_words). */
|
|
||||||
void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point,
|
|
||||||
const mg_uecc_word_t *scalar, MG_UECC_Curve curve);
|
|
||||||
|
|
||||||
/* Generates a random integer in the range 0 < random < top.
|
|
||||||
Both random and top have num_words words. */
|
|
||||||
int mg_uecc_generate_random_int(mg_uecc_word_t *random,
|
|
||||||
const mg_uecc_word_t *top,
|
|
||||||
wordcount_t num_words);
|
|
||||||
|
|
||||||
#endif /* MG_UECC_ENABLE_VLI_API */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* end of extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _UECC_VLI_H_ */
|
|
||||||
|
|
||||||
// End of uecc BSD-2
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
/**
|
|
||||||
* Adapted from STROBE: https://strobe.sourceforge.io/
|
|
||||||
* Copyright (c) 2015-2016 Cryptography Research, Inc.
|
|
||||||
* Author: Mike Hamburg
|
|
||||||
* License: MIT License
|
|
||||||
*/
|
|
||||||
#include "tls.h"
|
|
||||||
#include "tls_x25519.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
|
||||||
|
|
||||||
const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9};
|
|
||||||
|
|
||||||
#define X25519_WBITS 32
|
|
||||||
|
|
||||||
typedef uint32_t limb_t;
|
|
||||||
typedef uint64_t dlimb_t;
|
|
||||||
typedef int64_t sdlimb_t;
|
|
||||||
|
|
||||||
#define NLIMBS (256 / X25519_WBITS)
|
|
||||||
typedef limb_t mg_fe[NLIMBS];
|
|
||||||
|
|
||||||
static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) {
|
|
||||||
dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry;
|
|
||||||
*carry = (limb_t) (tmp >> X25519_WBITS);
|
|
||||||
return (limb_t) tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These functions are implemented in terms of umaal on ARM
|
|
||||||
static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) {
|
|
||||||
dlimb_t total = (dlimb_t) *carry + acc + mand;
|
|
||||||
*carry = (limb_t) (total >> X25519_WBITS);
|
|
||||||
return (limb_t) total;
|
|
||||||
}
|
|
||||||
|
|
||||||
static limb_t adc0(limb_t *carry, limb_t acc) {
|
|
||||||
dlimb_t total = (dlimb_t) *carry + acc;
|
|
||||||
*carry = (limb_t) (total >> X25519_WBITS);
|
|
||||||
return (limb_t) total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Precondition: carry is small.
|
|
||||||
// - Invariant: result of propagate is < 2^255 + 1 word
|
|
||||||
// - In particular, always less than 2p.
|
|
||||||
// - Also, output x >= min(x,19)
|
|
||||||
static void propagate(mg_fe x, limb_t over) {
|
|
||||||
unsigned i;
|
|
||||||
limb_t carry;
|
|
||||||
over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1;
|
|
||||||
x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1));
|
|
||||||
|
|
||||||
carry = over * 19;
|
|
||||||
for (i = 0; i < NLIMBS; i++) {
|
|
||||||
x[i] = adc0(&carry, x[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add(mg_fe out, const mg_fe a, const mg_fe b) {
|
|
||||||
unsigned i;
|
|
||||||
limb_t carry = 0;
|
|
||||||
for (i = 0; i < NLIMBS; i++) {
|
|
||||||
out[i] = adc(&carry, a[i], b[i]);
|
|
||||||
}
|
|
||||||
propagate(out, carry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sub(mg_fe out, const mg_fe a, const mg_fe b) {
|
|
||||||
unsigned i;
|
|
||||||
sdlimb_t carry = -38;
|
|
||||||
for (i = 0; i < NLIMBS; i++) {
|
|
||||||
carry = carry + a[i] - b[i];
|
|
||||||
out[i] = (limb_t) carry;
|
|
||||||
carry >>= X25519_WBITS;
|
|
||||||
}
|
|
||||||
propagate(out, (limb_t) (1 + carry));
|
|
||||||
}
|
|
||||||
|
|
||||||
// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `mg_fe`
|
|
||||||
// to avoid build warnings
|
|
||||||
static void mul(mg_fe out, const mg_fe a, const limb_t *b, unsigned nb) {
|
|
||||||
limb_t accum[2 * NLIMBS] = {0};
|
|
||||||
unsigned i, j;
|
|
||||||
|
|
||||||
limb_t carry2;
|
|
||||||
for (i = 0; i < nb; i++) {
|
|
||||||
limb_t mand = b[i];
|
|
||||||
carry2 = 0;
|
|
||||||
for (j = 0; j < NLIMBS; j++) {
|
|
||||||
limb_t tmp; // "a" may be misaligned
|
|
||||||
memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy
|
|
||||||
accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp);
|
|
||||||
}
|
|
||||||
accum[i + j] = carry2;
|
|
||||||
}
|
|
||||||
|
|
||||||
carry2 = 0;
|
|
||||||
for (j = 0; j < NLIMBS; j++) {
|
|
||||||
out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]);
|
|
||||||
}
|
|
||||||
propagate(out, carry2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sqr(mg_fe out, const mg_fe a) {
|
|
||||||
mul(out, a, a, NLIMBS);
|
|
||||||
}
|
|
||||||
static void mul1(mg_fe out, const mg_fe a) {
|
|
||||||
mul(out, a, out, NLIMBS);
|
|
||||||
}
|
|
||||||
static void sqr1(mg_fe a) {
|
|
||||||
mul1(a, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS],
|
|
||||||
limb_t doswap) {
|
|
||||||
unsigned i;
|
|
||||||
for (i = 0; i < 2 * NLIMBS; i++) {
|
|
||||||
limb_t xor_ab = (a[i] ^ b[i]) & doswap;
|
|
||||||
a[i] ^= xor_ab;
|
|
||||||
b[i] ^= xor_ab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonicalize a field element x, reducing it to the least residue which is
|
|
||||||
// congruent to it mod 2^255-19
|
|
||||||
// - Precondition: x < 2^255 + 1 word
|
|
||||||
static limb_t canon(mg_fe x) {
|
|
||||||
// First, add 19.
|
|
||||||
unsigned i;
|
|
||||||
limb_t carry0 = 19;
|
|
||||||
limb_t res;
|
|
||||||
sdlimb_t carry;
|
|
||||||
for (i = 0; i < NLIMBS; i++) {
|
|
||||||
x[i] = adc0(&carry0, x[i]);
|
|
||||||
}
|
|
||||||
propagate(x, carry0);
|
|
||||||
|
|
||||||
// Here, 19 <= x2 < 2^255
|
|
||||||
// - This is because we added 19, so before propagate it can't be less
|
|
||||||
// than 19. After propagate, it still can't be less than 19, because if
|
|
||||||
// propagate does anything it adds 19.
|
|
||||||
// - We know that the high bit must be clear, because either the input was ~
|
|
||||||
// 2^255 + one word + 19 (in which case it propagates to at most 2 words) or
|
|
||||||
// it was < 2^255. So now, if we subtract 19, we will get back to something in
|
|
||||||
// [0,2^255-19).
|
|
||||||
carry = -19;
|
|
||||||
res = 0;
|
|
||||||
for (i = 0; i < NLIMBS; i++) {
|
|
||||||
carry += x[i];
|
|
||||||
res |= x[i] = (limb_t) carry;
|
|
||||||
carry >>= X25519_WBITS;
|
|
||||||
}
|
|
||||||
return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const limb_t a24[1] = {121665};
|
|
||||||
|
|
||||||
static void ladder_part1(mg_fe xs[5]) {
|
|
||||||
limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
|
|
||||||
add(t1, x2, z2); // t1 = A
|
|
||||||
sub(z2, x2, z2); // z2 = B
|
|
||||||
add(x2, x3, z3); // x2 = C
|
|
||||||
sub(z3, x3, z3); // z3 = D
|
|
||||||
mul1(z3, t1); // z3 = DA
|
|
||||||
mul1(x2, z2); // x3 = BC
|
|
||||||
add(x3, z3, x2); // x3 = DA+CB
|
|
||||||
sub(z3, z3, x2); // z3 = DA-CB
|
|
||||||
sqr1(t1); // t1 = AA
|
|
||||||
sqr1(z2); // z2 = BB
|
|
||||||
sub(x2, t1, z2); // x2 = E = AA-BB
|
|
||||||
mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24
|
|
||||||
add(z2, z2, t1); // z2 = E*a24 + AA
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ladder_part2(mg_fe xs[5], const mg_fe x1) {
|
|
||||||
limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
|
|
||||||
sqr1(z3); // z3 = (DA-CB)^2
|
|
||||||
mul1(z3, x1); // z3 = x1 * (DA-CB)^2
|
|
||||||
sqr1(x3); // x3 = (DA+CB)^2
|
|
||||||
mul1(z2, x2); // z2 = AA*(E*a24+AA)
|
|
||||||
sub(x2, t1, x2); // x2 = BB again
|
|
||||||
mul1(x2, t1); // x2 = AA*BB
|
|
||||||
}
|
|
||||||
|
|
||||||
static void x25519_core(mg_fe xs[5], const uint8_t scalar[X25519_BYTES],
|
|
||||||
const uint8_t *x1, int clamp) {
|
|
||||||
int i;
|
|
||||||
mg_fe x1_limbs;
|
|
||||||
limb_t swap = 0;
|
|
||||||
limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3];
|
|
||||||
memset(xs, 0, 4 * sizeof(mg_fe));
|
|
||||||
x2[0] = z3[0] = 1;
|
|
||||||
for (i = 0; i < NLIMBS; i++) {
|
|
||||||
x3[i] = x1_limbs[i] =
|
|
||||||
MG_U32(x1[i * 4 + 3], x1[i * 4 + 2], x1[i * 4 + 1], x1[i * 4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 255; i >= 0; i--) {
|
|
||||||
uint8_t bytei = scalar[i / 8];
|
|
||||||
limb_t doswap;
|
|
||||||
if (clamp) {
|
|
||||||
if (i / 8 == 0) {
|
|
||||||
bytei &= (uint8_t) ~7U;
|
|
||||||
} else if (i / 8 == X25519_BYTES - 1) {
|
|
||||||
bytei &= 0x7F;
|
|
||||||
bytei |= 0x40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1);
|
|
||||||
condswap(x2, x3, swap ^ doswap);
|
|
||||||
swap = doswap;
|
|
||||||
|
|
||||||
ladder_part1(xs);
|
|
||||||
ladder_part2(xs, (const limb_t *) x1_limbs);
|
|
||||||
}
|
|
||||||
condswap(x2, x3, swap);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES],
|
|
||||||
const uint8_t x1[X25519_BYTES], int clamp) {
|
|
||||||
int i, ret;
|
|
||||||
mg_fe xs[5], out_limbs;
|
|
||||||
limb_t *x2, *z2, *z3, *prev;
|
|
||||||
static const struct {
|
|
||||||
uint8_t a, c, n;
|
|
||||||
} steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1},
|
|
||||||
{3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125},
|
|
||||||
{3, 1, 2}, {3, 1, 2}, {3, 1, 1}};
|
|
||||||
x25519_core(xs, scalar, x1, clamp);
|
|
||||||
|
|
||||||
// Precomputed inversion chain
|
|
||||||
x2 = xs[0];
|
|
||||||
z2 = xs[1];
|
|
||||||
z3 = xs[3];
|
|
||||||
|
|
||||||
prev = z2;
|
|
||||||
for (i = 0; i < 13; i++) {
|
|
||||||
int j;
|
|
||||||
limb_t *a = xs[steps[i].a];
|
|
||||||
for (j = steps[i].n; j > 0; j--) {
|
|
||||||
sqr(a, prev);
|
|
||||||
prev = a;
|
|
||||||
}
|
|
||||||
mul1(a, xs[steps[i].c]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here prev = z3
|
|
||||||
// x2 /= z2
|
|
||||||
mul(out_limbs, x2, z3, NLIMBS);
|
|
||||||
ret = (int) canon(out_limbs);
|
|
||||||
if (!clamp) ret = 0;
|
|
||||||
for (i = 0; i < NLIMBS; i++) {
|
|
||||||
uint32_t n = out_limbs[i];
|
|
||||||
out[i * 4] = (uint8_t) (n & 0xff);
|
|
||||||
out[i * 4 + 1] = (uint8_t) ((n >> 8) & 0xff);
|
|
||||||
out[i * 4 + 2] = (uint8_t) ((n >> 16) & 0xff);
|
|
||||||
out[i * 4 + 3] = (uint8_t) ((n >> 24) & 0xff);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef TLS_X15519_H
|
|
||||||
#define TLS_X15519_H
|
|
||||||
|
|
||||||
#include "arch.h"
|
|
||||||
|
|
||||||
#define X25519_BYTES 32
|
|
||||||
extern const uint8_t X25519_BASE_POINT[X25519_BYTES];
|
|
||||||
|
|
||||||
int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES],
|
|
||||||
const uint8_t x1[X25519_BYTES], int clamp);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* TLS_X15519_H */
|
|
||||||
+25
-21
@@ -1,5 +1,6 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "net.h"
|
||||||
|
|
||||||
// Not using memset for zeroing memory, cause it can be dropped by compiler
|
// Not using memset for zeroing memory, cause it can be dropped by compiler
|
||||||
// See https://github.com/cesanta/mongoose/pull/1265
|
// See https://github.com/cesanta/mongoose/pull/1265
|
||||||
@@ -43,11 +44,12 @@ bool mg_random(void *buf, size_t len) {
|
|||||||
success = true;
|
success = true;
|
||||||
#elif MG_ARCH == MG_ARCH_CUBE && defined(HAL_RNG_MODULE_ENABLED)
|
#elif MG_ARCH == MG_ARCH_CUBE && defined(HAL_RNG_MODULE_ENABLED)
|
||||||
extern RNG_HandleTypeDef hrng;
|
extern RNG_HandleTypeDef hrng;
|
||||||
|
success = true;
|
||||||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
||||||
uint32_t r = HAL_RNG_ReadLastRandomNumber(&hrng);
|
uint32_t r = 0;
|
||||||
|
if (HAL_RNG_GenerateRandomNumber(&hrng, &r) != HAL_OK) success = false;
|
||||||
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
||||||
}
|
}
|
||||||
success = true;
|
|
||||||
#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;
|
||||||
@@ -142,22 +144,21 @@ uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) {
|
|||||||
return (uint16_t) ~c;
|
return (uint16_t) ~c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isbyte(int n) {
|
// Parse "IP" or "IP/BITS" into net and mask. The whole spec must be valid
|
||||||
return n >= 0 && n <= 255;
|
static bool parse_net(struct mg_str spec, uint32_t *net, uint32_t *mask) {
|
||||||
|
struct mg_str caps[3], ip = spec, bits = mg_str("32");
|
||||||
|
struct mg_addr addr;
|
||||||
|
uint8_t n = 0;
|
||||||
|
if (mg_match(spec, mg_str("*/*"), caps)) ip = caps[0], bits = caps[1];
|
||||||
|
// ip.len == 0 check: mg_aton() parses an empty string as 0.0.0.0
|
||||||
|
if (ip.len == 0 || !mg_aton(ip, &addr) || addr.is_ip6 ||
|
||||||
|
!mg_str_to_num(bits, 10, &n, sizeof(n)) || n > 32) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
memcpy(net, addr.addr.ip, sizeof(*net));
|
||||||
static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
|
*net = mg_ntohl(*net);
|
||||||
int n, a, b, c, d, slash = 32, len = 0;
|
*mask = n ? (uint32_t) (0xffffffffU << (32 - n)) : (uint32_t) 0;
|
||||||
if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
|
return true;
|
||||||
sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
|
|
||||||
isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 &&
|
|
||||||
slash < 33) {
|
|
||||||
len = n;
|
|
||||||
*net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) |
|
|
||||||
(uint32_t) d;
|
|
||||||
*mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
|
int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
|
||||||
@@ -171,7 +172,9 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
|
|||||||
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;
|
||||||
if (parse_net(&entry.buf[1], &net, &mask) == 0) return -2;
|
if (!parse_net(mg_str_n(entry.buf + 1, entry.len - 1), &net, &mask)) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0];
|
if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,7 +200,7 @@ bool mg_path_is_sane(const struct mg_str path) {
|
|||||||
#if MG_ENABLE_CUSTOM_MILLIS
|
#if MG_ENABLE_CUSTOM_MILLIS
|
||||||
#else
|
#else
|
||||||
uint64_t mg_millis(void) {
|
uint64_t mg_millis(void) {
|
||||||
#if MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_ESP32 || MG_ENABLE_FREERTOS
|
#if MG_ARCH == MG_ARCH_ESP32 || MG_ENABLE_FREERTOS
|
||||||
return xTaskGetTickCount() * portTICK_PERIOD_MS;
|
return xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||||
#elif MG_ARCH == MG_ARCH_THREADX
|
#elif MG_ARCH == MG_ARCH_THREADX
|
||||||
return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
|
return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
|
||||||
@@ -258,8 +261,9 @@ uint64_t mg_ntohll(uint64_t net) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_delayms(unsigned int ms) {
|
void mg_delayms(unsigned int ms) {
|
||||||
uint64_t to = mg_millis() + ms + 1;
|
uint64_t now, to = mg_millis() + ms + 1;
|
||||||
while (mg_millis() < to) (void) 0;
|
while ((now = mg_millis()) < to)
|
||||||
|
if (now + ms + 1 < to) to = now + ms + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_CUSTOM_CALLOC
|
#if MG_ENABLE_CUSTOM_CALLOC
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
ws_handshake(c, wskey, wsproto, fmt, &ap);
|
ws_handshake(c, wskey, wsproto, fmt, &ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
c->is_websocket = 1;
|
c->is_websocket = 1;
|
||||||
c->is_resp = 0;
|
c->pfn_data = NULL; // HTTP response done; mg_ws_cb uses it as an offset
|
||||||
mg_call(c, MG_EV_WS_OPEN, hm);
|
mg_call(c, MG_EV_WS_OPEN, hm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-20
@@ -1,6 +1,6 @@
|
|||||||
SRCS = mongoose.c unit_test.c packed_fs.c
|
SRCS = mongoose.c unit_test.c packed_fs.c
|
||||||
HDRS = $(wildcard ../src/*.h) $(wildcard ../src/drivers/*.h)
|
HDRS = $(wildcard ../src/*.h) $(wildcard ../src/drivers/*.h)
|
||||||
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 -DMG_ENABLE_IPV6=$(IPV6)
|
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 -DMG_ENABLE_IPV6=$(IPV6) -DMG_ENABLE_MDASH=$(MDASH)
|
||||||
WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef
|
WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef
|
||||||
OPTS ?= -O3 -g3
|
OPTS ?= -O3 -g3
|
||||||
INCS ?= -Isrc -I.
|
INCS ?= -Isrc -I.
|
||||||
@@ -13,11 +13,12 @@ DOCKER_BIN ?= docker
|
|||||||
DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(ROOT_DIR):$(ROOT_DIR) -w $(CWD)
|
DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(ROOT_DIR):$(ROOT_DIR) -w $(CWD)
|
||||||
VCFLAGS ?= /nologo /W3 /O2 /MD /I.
|
VCFLAGS ?= /nologo /W3 /O2 /MD /I.
|
||||||
IPV6 ?= 1
|
IPV6 ?= 1
|
||||||
|
MDASH ?= 1
|
||||||
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
|
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
|
||||||
ASAN_OPTIONS ?= detect_leaks=1
|
ASAN_OPTIONS ?= detect_leaks=1
|
||||||
# This does not include ESP32, ESP8266, Microchip; all examples that are directly placed under an OS tutorial. Also, it is needed that way...
|
TUTORIALS_EMBEDDED := $(dir $(wildcard ../tutorials/esp32/*/Makefile)) $(dir $(wildcard ../tutorials/infineon/*/Makefile)) $(dir $(wildcard ../tutorials/microchip/*/*/Makefile)) $(dir $(wildcard ../tutorials/nordic/*/Makefile)) $(dir $(wildcard ../tutorials/nxp/*/*/Makefile)) $(dir $(wildcard ../tutorials/pico-sdk/*/Makefile)) $(dir $(wildcard ../tutorials/rp/*/Makefile)) $(dir $(wildcard ../tutorials/renesas/*/Makefile)) $(dir $(wildcard ../tutorials/stm32/*/Makefile)) $(dir $(wildcard ../tutorials/stm32/*/*/Makefile)) $(dir $(wildcard ../tutorials/ti/*/Makefile)) $(dir $(wildcard ../tutorials/wch/*/Makefile))
|
||||||
TUTORIALS_EMBEDDED := $(dir $(wildcard ../tutorials/infineon/*/Makefile)) $(dir $(wildcard ../tutorials/nxp/*/Makefile)) $(dir $(wildcard ../tutorials/rp/*/Makefile)) $(dir $(wildcard ../tutorials/renesas/*/Makefile)) $(dir $(wildcard ../tutorials/stm32/*/*/Makefile)) $(dir $(wildcard ../tutorials/ti/*/Makefile)) $(dir $(wildcard ../tutorials/wch/*/Makefile))
|
TUTORIALS_ZEPHYR := $(dir $(wildcard ../tutorials/zephyr/*/Makefile))
|
||||||
TUTORIALS := ../tutorials/micropython/ $(filter-out $(TUTORIALS_EMBEDDED), $(dir $(wildcard ../tutorials/*/*/Makefile)))
|
TUTORIALS := ../tutorials/micropython/ $(filter-out $(TUTORIALS_EMBEDDED) $(TUTORIALS_ZEPHYR), $(dir $(wildcard ../tutorials/*/*/Makefile)))
|
||||||
TUTORIALS_QUICK := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/, $(TUTORIALS))
|
TUTORIALS_QUICK := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/, $(TUTORIALS))
|
||||||
TUTORIALS_MAC := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/ ../tutorials/tcpip/tap-driver/, $(TUTORIALS))
|
TUTORIALS_MAC := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/ ../tutorials/tcpip/tap-driver/, $(TUTORIALS))
|
||||||
TUTORIALS_WIN := $(dir $(wildcard ../tutorials/http/file-*/Makefile) $(wildcard ../tutorials/http/http-*/Makefile) $(wildcard ../tutorials/mqtt/*/Makefile) $(wildcard ../tutorials/websocket/*/Makefile) $(wildcard ../tutorials/webui/*/Makefile))
|
TUTORIALS_WIN := $(dir $(wildcard ../tutorials/http/file-*/Makefile) $(wildcard ../tutorials/http/http-*/Makefile) $(wildcard ../tutorials/mqtt/*/Makefile) $(wildcard ../tutorials/websocket/*/Makefile) $(wildcard ../tutorials/webui/*/Makefile))
|
||||||
@@ -75,19 +76,10 @@ LDFLAGS += -L$(WOLFSSL)/lib -lwolfssl
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(SSL)" "BUILTIN"
|
ifeq "$(SSL)" "BUILTIN"
|
||||||
DEFS += -DMG_TLS=MG_TLS_BUILTIN
|
# Small TLS records: several records per TCP segment, tests record handling
|
||||||
|
DEFS += -DMG_TLS=MG_TLS_BUILTIN -DMG_TLS_RECORD_SIZE=256
|
||||||
endif
|
endif
|
||||||
|
|
||||||
tls_multirec/server: FORCE
|
|
||||||
ifneq "$(SSL)" ""
|
|
||||||
ifeq "$(MULTIREC)" ""
|
|
||||||
$(MAKE) -C tls_multirec CC=gcc
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
true
|
|
||||||
|
|
||||||
FORCE:
|
|
||||||
true
|
|
||||||
|
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@@ -106,13 +98,13 @@ 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 .. && (export LC_ALL=C ; 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/os_*.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/modbus.h src/json.h src/jwt.h src/rpc.h src/dash.h src/ota.h src/health.h src/health_cortex.h src/mdash.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/bsd.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 .. && (export LC_ALL=C ; 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/os_*.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/lfs.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_aes128.h src/tls_p256.h src/tls_p384.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/modbus.h src/json.h src/jwt.h src/rpc.h src/dash.h src/ota.h src/health.h src/mdash.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/bsd.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||||
|
|
||||||
# Check that all external (exported) symbols have "mg_" prefix
|
# Check that all external (exported) symbols have "mg_" prefix
|
||||||
mg_prefix: mongoose.c mongoose.h
|
mg_prefix: mongoose.c mongoose.h
|
||||||
$(CC) mongoose.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T ' | grep -v 'mg_' ; test $$? = 1
|
$(CC) mongoose.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T ' | grep -v 'mg_' ; test $$? = 1
|
||||||
|
|
||||||
test: Makefile mongoose.h $(SRCS) tls_multirec/server
|
test: Makefile mongoose.h $(SRCS)
|
||||||
$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test
|
$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
||||||
|
|
||||||
@@ -262,13 +254,13 @@ packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/range.txt data/ca.pem ce
|
|||||||
|
|
||||||
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 $@
|
|
||||||
$(MAKE) -C ../resources/I-CUBE-Mongoose $@
|
$(MAKE) -C ../resources/I-CUBE-Mongoose $@
|
||||||
#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
|
||||||
|
|
||||||
|
|
||||||
# MIP (net_builtin + L2 + driver) TESTS
|
# MIP (net_builtin + L2 + driver) TESTS
|
||||||
|
|
||||||
|
mip_test: MDASH = 0
|
||||||
mip_test: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
|
mip_test: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
|
||||||
$(CC) mip_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
$(CC) mip_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
@@ -285,7 +277,7 @@ atcmd_test: atcmd_test.c mongoose.c mongoose.h Makefile
|
|||||||
$(CC) atcmd_test.c $(CFLAGS) $(LDFLAGS) -o $@
|
$(CC) atcmd_test.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
mip_tap_test: mip_x_test.c mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server
|
mip_tap_test: mip_x_test.c mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile
|
||||||
$(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) ./$@
|
||||||
|
|
||||||
@@ -298,7 +290,7 @@ port_tap_bridge: port_tap_bridge.c
|
|||||||
cc port_tap_bridge.c -o $@
|
cc port_tap_bridge.c -o $@
|
||||||
|
|
||||||
# requires port_tap_bridge to be running
|
# requires port_tap_bridge to be running
|
||||||
mip_port_test: mip_x_test.c mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server
|
mip_port_test: mip_x_test.c mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile
|
||||||
$(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
$(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
@@ -307,6 +299,7 @@ mip_diabish: CC = gcc
|
|||||||
mip_diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic
|
mip_diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic
|
||||||
mip_diabish: mip_test
|
mip_diabish: mip_test
|
||||||
|
|
||||||
|
mip_vc98: MDASH = 0
|
||||||
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) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
$(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
||||||
|
|||||||
@@ -29,20 +29,20 @@ teensy-board:
|
|||||||
touch teensy-board
|
touch teensy-board
|
||||||
|
|
||||||
seeeduino-xiao: arduino seeeduino-xiao-board
|
seeeduino-xiao: arduino seeeduino-xiao-board
|
||||||
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/sim800-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 sim800-mqtt.ino
|
cd $(ROOT)/tutorials/arduino/sim800-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 sim800-mqtt.ino
|
||||||
cd $(ROOT)/tutorials/http/http-server/arduino/w5500-http && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-http.ino
|
cd $(ROOT)/tutorials/arduino/w5500-http && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-http.ino
|
||||||
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/w5500-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-mqtt.ino
|
cd $(ROOT)/tutorials/arduino/w5500-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-mqtt.ino
|
||||||
|
|
||||||
arduino-nano: arduino
|
arduino-nano: arduino
|
||||||
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/nano-w5500-mqtt && $(ARD)/arduino/arduino --pref compiler.cpp.extra_flags=-DMG_ENABLE_LOG=0 --verbose --verify --board arduino:avr:nano nano-w5500-mqtt.ino
|
cd $(ROOT)/tutorials/arduino/nano-w5500-mqtt && $(ARD)/arduino/arduino --pref compiler.cpp.extra_flags=-DMG_ENABLE_LOG=0 --verbose --verify --board arduino:avr:nano nano-w5500-mqtt.ino
|
||||||
|
|
||||||
esp32: arduino esp32-board
|
esp32: arduino esp32-board
|
||||||
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/esp32-mqtt && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:node32s esp32-mqtt.ino
|
cd $(ROOT)/tutorials/arduino/esp32-mqtt && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:node32s esp32-mqtt.ino
|
||||||
# Needs a board with 'LED_BUILTIN', yet somehow invocation is missing that definition (lack of include somehow amended by the IDE)
|
# Needs a board with 'LED_BUILTIN', yet somehow invocation is missing that definition (lack of include somehow amended by the IDE)
|
||||||
#cd $(ROOT)/tutorials/http/http-server/arduino/esp32-http && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:esp32 esp32-http.ino
|
#cd $(ROOT)/tutorials/arduino/esp32-http && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:esp32 esp32-http.ino
|
||||||
|
|
||||||
teensy41: arduino teensy-board
|
teensy41: arduino teensy-board
|
||||||
cd $(ROOT)/tutorials/http/http-server/arduino/teensy41-http && $(ARD)/arduino/arduino --verbose --verify --board teensy41-http.ino
|
cd $(ROOT)/tutorials/arduino/teensy41-http && $(ARD)/arduino/arduino --verbose --verify --board teensy41-http.ino
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+28
-5
@@ -13,18 +13,21 @@ else
|
|||||||
C_WARN ?= -Wno-deprecated
|
C_WARN ?= -Wno-deprecated
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all bsd_test bsd_client shm_tap_bridge shm_port_bridge bsd_s390 clean
|
.PHONY: all bsd bsd_test bsd_client bsd_nortos bsd_nortos_test shm_tap_bridge \
|
||||||
|
shm_port_bridge bsd_s390 bsd_nortos_s390 clean
|
||||||
|
|
||||||
TEST_SOURCES = bsd_shm_test.c
|
TEST_SOURCES = bsd_shm_test.c
|
||||||
# A C++ application compiles its sources as C++, linking Mongoose and FreeRTOS as C.
|
# A C++ application compiles its sources as C++, linking Mongoose and FreeRTOS as C.
|
||||||
# FreeRTOS does not build as C++, so C sources are compiled separately.
|
# FreeRTOS does not build as C++, so C sources are compiled separately.
|
||||||
C_SOURCES = ../../mongoose.c
|
C_SOURCES = ../../mongoose.c
|
||||||
C_SOURCES += FreeRTOS-Kernel/portable/MemMang/heap_4.c
|
FREERTOS_SOURCES = FreeRTOS-Kernel/portable/MemMang/heap_4.c
|
||||||
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c
|
FREERTOS_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c
|
||||||
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
|
FREERTOS_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
|
||||||
C_SOURCES += $(wildcard FreeRTOS-Kernel/*.c)
|
FREERTOS_SOURCES += $(wildcard FreeRTOS-Kernel/*.c)
|
||||||
C_OBJECTS = $(notdir $(C_SOURCES:.c=.o))
|
C_OBJECTS = $(notdir $(C_SOURCES:.c=.o))
|
||||||
|
FREERTOS_OBJECTS = $(notdir $(FREERTOS_SOURCES:.c=.o))
|
||||||
C_INPUTS = $(foreach S,$(C_SOURCES),-x c $(S))
|
C_INPUTS = $(foreach S,$(C_SOURCES),-x c $(S))
|
||||||
|
FREERTOS_INPUTS = $(foreach S,$(FREERTOS_SOURCES),-x c $(S))
|
||||||
CFLAGS += -IFreeRTOS-Kernel/include
|
CFLAGS += -IFreeRTOS-Kernel/include
|
||||||
CFLAGS += -IFreeRTOS-Kernel/portable/ThirdParty/GCC/Posix -Wno-conversion -Wno-undef
|
CFLAGS += -IFreeRTOS-Kernel/portable/ThirdParty/GCC/Posix -Wno-conversion -Wno-undef
|
||||||
|
|
||||||
@@ -47,8 +50,19 @@ CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
|
|||||||
all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
|
all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
||||||
|
|
||||||
|
# also
|
||||||
|
bsd_nortos: shm_tap_bridge bsd_client bsd_nortos_test
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
|
||||||
|
|
||||||
# requires shm_tap_bridge to be running
|
# requires shm_tap_bridge to be running
|
||||||
|
bsd_test: CFLAGS_MONGOOSE += -DMG_ENABLE_FREERTOS=1
|
||||||
bsd_test: FreeRTOS-Kernel
|
bsd_test: FreeRTOS-Kernel
|
||||||
|
$(CC) $(C_INPUTS) $(FREERTOS_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||||
|
$(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||||
|
$(CC) $(C_OBJECTS) $(FREERTOS_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
|
||||||
|
|
||||||
|
bsd_nortos_test: CFLAGS_MONGOOSE += -DMG_ENABLE_FREERTOS=0
|
||||||
|
bsd_nortos_test:
|
||||||
$(CC) $(C_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
$(CC) $(C_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||||
$(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
$(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||||
$(CC) $(C_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
|
$(CC) $(C_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
|
||||||
@@ -61,12 +75,21 @@ shm_tap_bridge: shm_tap_bridge.c shm_queue.h
|
|||||||
$(CC) shm_tap_bridge.c -pthread -o $@
|
$(CC) shm_tap_bridge.c -pthread -o $@
|
||||||
|
|
||||||
# requires test/port_tap_bridge to be running
|
# requires test/port_tap_bridge to be running
|
||||||
|
bsd_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
||||||
bsd_s390: ASAN=
|
bsd_s390: ASAN=
|
||||||
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test
|
bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test
|
||||||
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||||
|
|
||||||
|
#also
|
||||||
|
bsd_nortos_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
||||||
|
bsd_nortos_s390: ASAN=
|
||||||
|
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
|
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
|
bsd_nortos_s390: shm_port_bridge bsd_client bsd_nortos_test
|
||||||
|
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||||
|
|
||||||
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
||||||
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
||||||
|
|
||||||
|
|||||||
+64
-5
@@ -1,4 +1,5 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -91,22 +92,80 @@ fail:
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum wait_mode { WAIT_ABORT, WAIT_DRAIN, WAIT_HOLD, WAIT_CLOSE };
|
||||||
|
|
||||||
|
// Wait for output, optionally draining through the final marker.
|
||||||
|
static int wait_data(const char *host, enum wait_mode mode) {
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
struct timeval timeout;
|
||||||
|
char buf[512];
|
||||||
|
ssize_t n;
|
||||||
|
size_t i;
|
||||||
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (fd < 0) return 1;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(PORT);
|
||||||
|
if (inet_pton(AF_INET, host, &sa.sin_addr) != 1) goto fail;
|
||||||
|
timeout.tv_sec = 5, timeout.tv_usec = 0;
|
||||||
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0)
|
||||||
|
goto fail;
|
||||||
|
if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0) goto fail;
|
||||||
|
if (mode == WAIT_HOLD) {
|
||||||
|
usleep(100000); // Keep the peer connected while the server fails TX
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (mode == WAIT_CLOSE) {
|
||||||
|
n = recv(fd, buf, sizeof(buf), 0);
|
||||||
|
close(fd);
|
||||||
|
return n == 0 || (n < 0 && errno == ECONNRESET) ? 0 : 1;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
n = recv(fd, buf, sizeof(buf), 0);
|
||||||
|
if (n <= 0) goto fail;
|
||||||
|
if (mode == WAIT_ABORT) break;
|
||||||
|
for (i = 0; i < (size_t) n; i++) {
|
||||||
|
if (buf[i] == '\1') break;
|
||||||
|
}
|
||||||
|
if (i < (size_t) n) break;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
pthread_t threads[NUM_CONNECTIONS];
|
pthread_t threads[NUM_CONNECTIONS];
|
||||||
int i;
|
int connections = NUM_CONNECTIONS, i;
|
||||||
|
|
||||||
if (argc != 2) {
|
if (argc == 3 && strcmp(argv[2], "waitandabort") == 0)
|
||||||
fprintf(stderr, "usage: %s IP\n", argv[0]);
|
return wait_data(argv[1], WAIT_ABORT);
|
||||||
|
if (argc == 3 && strcmp(argv[2], "waitanddrain") == 0)
|
||||||
|
return wait_data(argv[1], WAIT_DRAIN);
|
||||||
|
if (argc == 3 && strcmp(argv[2], "hold") == 0)
|
||||||
|
return wait_data(argv[1], WAIT_HOLD);
|
||||||
|
if (argc == 3 && strcmp(argv[2], "expectclose") == 0)
|
||||||
|
return wait_data(argv[1], WAIT_CLOSE);
|
||||||
|
if (argc == 3) connections = atoi(argv[2]);
|
||||||
|
if ((argc != 2 && argc != 3) || connections < 1 ||
|
||||||
|
connections > NUM_CONNECTIONS) {
|
||||||
|
fprintf(stderr, "usage: %s IP [connections|waitandabort|waitanddrain|hold|expectclose]\n",
|
||||||
|
argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
srandom(1);
|
srandom(1);
|
||||||
|
|
||||||
for (i = 0; i < NUM_CONNECTIONS; i++) {
|
for (i = 0; i < connections; i++) {
|
||||||
if (pthread_create(&threads[i], NULL, worker, argv[1]) != 0) return 1;
|
if (pthread_create(&threads[i], NULL, worker, argv[1]) != 0) return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < NUM_CONNECTIONS; i++) {
|
for (i = 0; i < connections; i++) {
|
||||||
if (pthread_join(threads[i], NULL) != 0) return 1;
|
if (pthread_join(threads[i], NULL) != 0) return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+807
-71
@@ -1,11 +1,11 @@
|
|||||||
#define BSDTEST_USING_DHCP 1
|
#define BSDTEST_USING_DHCP 1
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <spawn.h>
|
#include <spawn.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
static int s_num_tests = 0;
|
static int s_num_tests = 0;
|
||||||
static bool s_error = false;
|
static bool s_error = false;
|
||||||
static volatile bool s_done = false, s_wait;
|
|
||||||
|
|
||||||
#ifdef NO_ABORT
|
#ifdef NO_ABORT
|
||||||
static int s_abort = 0;
|
static int s_abort = 0;
|
||||||
@@ -30,15 +30,177 @@ static int s_abort = 0;
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define DASHBOARD(x) \
|
||||||
|
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
||||||
|
|
||||||
|
static pid_t start_client(const char *ip, const char *connections) {
|
||||||
|
char *cmd[4];
|
||||||
|
pid_t pid;
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
|
cmd[0] = (char *) "./bsd_client";
|
||||||
|
cmd[1] = (char *) ip;
|
||||||
|
cmd[2] = (char *) connections;
|
||||||
|
cmd[3] = NULL;
|
||||||
|
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool client_done(pid_t pid, int *status) {
|
||||||
|
pid_t r = waitpid(pid, status, WNOHANG);
|
||||||
|
ASSERT(r >= 0);
|
||||||
|
return r == pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_client(int status) {
|
||||||
|
ASSERT(WIFEXITED(status));
|
||||||
|
ASSERT(WEXITSTATUS(status) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_listener(int backlog, bool nonblock) {
|
||||||
|
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(1234);
|
||||||
|
sa.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
ASSERT(lfd >= 0);
|
||||||
|
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
||||||
|
ASSERT(listen(lfd, backlog) == 0);
|
||||||
|
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
else ASSERT((fcntl(lfd, F_GETFL, 0) & O_NONBLOCK) == 0);
|
||||||
|
MG_INFO(("BSD echo server on :1234"));
|
||||||
|
return lfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_ready(struct mg_mgr *mgr) {
|
||||||
|
// Stack initialization, Network configuration (DHCP lease, ...)
|
||||||
|
#if BSDTEST_USING_DHCP == 0
|
||||||
|
MG_INFO(("MIF configuration: Static IP"));
|
||||||
|
ASSERT(mgr->ifp->ip != 0); // Check we have a satic IP assigned
|
||||||
|
mg_mgr_poll(mgr, 0); // For initialisation
|
||||||
|
#else
|
||||||
|
MG_INFO(("MIF configuration: DHCP"));
|
||||||
|
ASSERT(mgr->ifp->ip == 0); // Check we are set for DHCP
|
||||||
|
while (mgr->ifp->ip == 0 && mg_millis() < 5000) mg_mgr_poll(mgr, 0);
|
||||||
|
if (mgr->ifp->ip == 0) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n"));
|
||||||
|
ASSERT(mgr->ifp->ip != 0); // We have an IP (lease or static)
|
||||||
|
#endif
|
||||||
|
while (mgr->ifp->state != MG_TCPIP_STATE_READY) mg_mgr_poll(mgr, 0);
|
||||||
|
MG_INFO(("Interface started, starting main loop"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !MG_ENABLE_FREERTOS
|
||||||
|
static bool s_fail_alloc;
|
||||||
|
|
||||||
|
void *mg_calloc(size_t count, size_t size) {
|
||||||
|
return s_fail_alloc ? NULL : calloc(count, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_free(void *ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t s_bootmillis = 0;
|
||||||
|
|
||||||
|
uint64_t mg_millis(void) {
|
||||||
|
struct timespec ts = {0, 0};
|
||||||
|
// See #1615 - prefer monotonic clock
|
||||||
|
#if defined(CLOCK_MONOTONIC_RAW)
|
||||||
|
// Raw hardware-based time that is not subject to NTP adjustment
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||||
|
#elif defined(CLOCK_MONOTONIC)
|
||||||
|
// Affected by the incremental adjustments performed by adjtime and NTP
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
#else
|
||||||
|
// Affected by discontinuous jumps in the system time and by the incremental
|
||||||
|
// adjustments performed by adjtime and NTP
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
#endif
|
||||||
|
return ((uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000) - s_bootmillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MG_ENABLE_FREERTOS
|
||||||
|
static volatile bool s_done = false, s_wait, s_accept_waiting;
|
||||||
|
static volatile bool s_accept_done, s_dontwait_done, s_nonblock_done;
|
||||||
|
static volatile bool s_select_waiting, s_select_done;
|
||||||
|
static volatile bool s_poll_waiting, s_poll_done;
|
||||||
|
static volatile bool s_ready_waiting, s_ready_done;
|
||||||
|
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
|
||||||
|
static volatile bool s_tx_held, s_tx_waits, s_tx_fail, s_tx_error;
|
||||||
|
static volatile bool s_send_waiting;
|
||||||
|
static volatile bool s_send_done, s_send_expect_error;
|
||||||
|
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
|
||||||
|
static volatile bool s_recv_expect_error;
|
||||||
|
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
|
||||||
|
|
||||||
struct client_data {
|
struct client_data {
|
||||||
char hostname[64];
|
char hostname[64];
|
||||||
uint32_t ip;
|
uint32_t ip;
|
||||||
bool done;
|
bool done;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void echo_task(void *args);
|
||||||
|
static void echo_dontwait_task(void *args);
|
||||||
|
static void echo_nonblock_task(void *args);
|
||||||
|
static void send_task(void *args);
|
||||||
|
static void recv_wait_task(void *args);
|
||||||
|
static void select_task(void *args);
|
||||||
|
static void readiness_task(void *args);
|
||||||
|
static void backlog_task(void *args);
|
||||||
|
static void tx_gate_task(void *args);
|
||||||
|
|
||||||
|
struct atask_args {
|
||||||
|
bool listener_nonblock;
|
||||||
|
bool worker_nonblock;
|
||||||
|
bool once;
|
||||||
|
bool receiver;
|
||||||
|
TaskFunction_t task;
|
||||||
|
TaskFunction_t gate;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct atask_args s_echo_server = {true, false, false,
|
||||||
|
false, echo_task, NULL};
|
||||||
|
static const struct atask_args s_accept_server = {false, false, true,
|
||||||
|
false, echo_task, NULL};
|
||||||
|
static const struct atask_args s_nonblock_accept_server = {
|
||||||
|
true, false, true, false, echo_task, NULL};
|
||||||
|
static const struct atask_args s_dontwait_server = {
|
||||||
|
true, false, false, false, echo_dontwait_task, NULL};
|
||||||
|
static const struct atask_args s_nonblock_server = {
|
||||||
|
true, true, false, false, echo_nonblock_task, NULL};
|
||||||
|
static const struct atask_args s_send_server = {true, false, false,
|
||||||
|
true, send_task, tx_gate_task};
|
||||||
|
static const struct atask_args s_send_error_server = {
|
||||||
|
true, false, false, false, send_task, tx_gate_task};
|
||||||
|
|
||||||
|
static size_t tx_gate(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
if (s_tx_fail) return (size_t) -1; // Force MG_IO_ERR after send() waits
|
||||||
|
if (s_tx_held) {
|
||||||
|
s_tx_waits = true;
|
||||||
|
return 0; // Gate TX: MG_IO_WAIT leaves c->send queued
|
||||||
|
}
|
||||||
|
return s_tx(buf, len, ifp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_client(const char *ip, const char *connections) {
|
||||||
|
int status;
|
||||||
|
pid_t pid = start_client(ip, connections);
|
||||||
|
|
||||||
|
while (!client_done(pid, &status)) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
check_client(status);
|
||||||
|
}
|
||||||
|
|
||||||
// client_task: connects to hostname:80, fetches "/", logs response length.
|
// client_task: connects to hostname:80, fetches "/", logs response length.
|
||||||
static void client_task(void *args) {
|
static void client_task(void *args) {
|
||||||
int fd, c;
|
fd_set w, e;
|
||||||
|
struct pollfd p;
|
||||||
|
int fd;
|
||||||
struct client_data *d = (struct client_data *) args;
|
struct client_data *d = (struct client_data *) args;
|
||||||
char req[256];
|
char req[256];
|
||||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@@ -47,14 +209,20 @@ static void client_task(void *args) {
|
|||||||
sa.sin_family = AF_INET;
|
sa.sin_family = AF_INET;
|
||||||
sa.sin_port = htons(80);
|
sa.sin_port = htons(80);
|
||||||
memcpy(&sa.sin_addr, &d->ip, 4);
|
memcpy(&sa.sin_addr, &d->ip, 4);
|
||||||
c = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
|
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
ASSERT(c >= 0);
|
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
|
||||||
if (c < 0) {
|
errno == EINPROGRESS);
|
||||||
MG_ERROR(("connect failed"));
|
FD_ZERO(&w);
|
||||||
close(fd);
|
FD_ZERO(&e);
|
||||||
vTaskDelete(NULL);
|
FD_SET(fd, &w);
|
||||||
return;
|
FD_SET(fd, &e);
|
||||||
}
|
ASSERT(select(fd + 1, NULL, &w, &e, NULL) == 1);
|
||||||
|
ASSERT(FD_ISSET(fd, &w));
|
||||||
|
ASSERT(!FD_ISSET(fd, &e));
|
||||||
|
p.fd = fd, p.events = POLLOUT, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 1);
|
||||||
|
ASSERT(p.revents == POLLOUT);
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, 0) == 0);
|
||||||
snprintf(req, sizeof(req),"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", d->hostname);
|
snprintf(req, sizeof(req),"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", d->hostname);
|
||||||
send(fd, req, strlen(req), 0);
|
send(fd, req, strlen(req), 0);
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
@@ -69,84 +237,468 @@ static void client_task(void *args) {
|
|||||||
(void) args;
|
(void) args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// echo_task: one per accepted connection. Echoes data back.
|
// Echo data back from one accepted connection.
|
||||||
static void echo_task(void *args) {
|
static void echo_task_common(int fd, int flags, volatile bool *done) {
|
||||||
int fd = (int) (uintptr_t) args;
|
bool nonblock = (fcntl(fd, F_GETFL, 0) & O_NONBLOCK) != 0;
|
||||||
|
bool dontwait = (flags & MSG_DONTWAIT) != 0;
|
||||||
|
bool echoed = false, eagain = false;
|
||||||
static const size_t sizes[] = {1, 7, 17, 255, 256, 257, 511, 512};
|
static const size_t sizes[] = {1, 7, 17, 255, 256, 257, 511, 512};
|
||||||
char buf[512];
|
char buf[512];
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
|
|
||||||
MG_INFO(("Echo task started: %d", fd));
|
MG_INFO(("Echo task started: %d", fd));
|
||||||
while ((n = recv(fd, buf, sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))], 0)) > 0)
|
for (;;) {
|
||||||
send(fd, buf, (size_t) n, 0);
|
n = recv(fd, buf, sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))], flags);
|
||||||
|
if (n > 0) {
|
||||||
|
if (!nonblock) {
|
||||||
|
ssize_t w = send(fd, buf, (size_t) n, 0);
|
||||||
|
if (done != NULL) ASSERT(w == n);
|
||||||
|
echoed = w == n;
|
||||||
|
} else {
|
||||||
|
size_t sent = 0;
|
||||||
|
while (sent < (size_t) n) {
|
||||||
|
ssize_t w = send(fd, buf + sent, (size_t) n - sent, 0);
|
||||||
|
if (w > 0) sent += (size_t) w;
|
||||||
|
else if (w < 0 && errno == EAGAIN) vTaskDelay(1);
|
||||||
|
else {
|
||||||
|
ASSERT(w > 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sent != (size_t) n) break;
|
||||||
|
echoed = true;
|
||||||
|
}
|
||||||
|
} else if ((nonblock || dontwait) && n < 0 && errno == EAGAIN) {
|
||||||
|
if (echoed) eagain = true;
|
||||||
|
vTaskDelay(1);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done != NULL) {
|
||||||
|
ASSERT(eagain);
|
||||||
|
*done = true;
|
||||||
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void echo_task(void *args) {
|
||||||
|
echo_task_common((int) (uintptr_t) args, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void echo_dontwait_task(void *args) {
|
||||||
|
echo_task_common((int) (uintptr_t) args, MSG_DONTWAIT, &s_dontwait_done);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void echo_nonblock_task(void *args) {
|
||||||
|
echo_task_common((int) (uintptr_t) args, 0, &s_nonblock_done);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tx_gate_task(void *args) {
|
||||||
|
// receiver/sender > network task > gate: final send is waiting
|
||||||
|
while (!s_send_waiting) vTaskDelay(1);
|
||||||
|
ASSERT(!s_send_done);
|
||||||
|
if (s_send_expect_error) s_tx_error = s_tx_fail = true;
|
||||||
|
else s_tx_held = false;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
(void) args;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recv_wait_task(void *args) {
|
||||||
|
char buf;
|
||||||
|
int fd = (int) (uintptr_t) args;
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
while (!s_recv_start) vTaskDelay(1);
|
||||||
|
s_recv_waiting = true; // send_task starts after recv() is waiting
|
||||||
|
n = recv(fd, &buf, sizeof(buf), 0);
|
||||||
|
if (s_recv_expect_error) {
|
||||||
|
ASSERT(n < 0 && errno == EIO); // bsd_client aborts with unread data
|
||||||
|
} else {
|
||||||
|
ASSERT(n == 0); // bsd_client drains, then closes
|
||||||
|
}
|
||||||
|
s_recv_done = true;
|
||||||
|
close(fd); // Receiver owns the shared socket close
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_task(void *args) {
|
||||||
|
static const char buf[512] = {0};
|
||||||
|
static const char marker = '\1';
|
||||||
|
int fd = (int) (uintptr_t) args;
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
s_tx_held = true; // Gate TX until c->send fills
|
||||||
|
do {
|
||||||
|
n = send(fd, buf, sizeof(buf), 0);
|
||||||
|
} while (n == (ssize_t) sizeof(buf));
|
||||||
|
ASSERT(n < 0 && errno == EAGAIN); // Full c->send: O_NONBLOCK
|
||||||
|
while (!s_tx_waits) vTaskDelay(1); // Next write_conn hits the gate
|
||||||
|
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, 0) == 0);
|
||||||
|
n = send(fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||||
|
ASSERT(n < 0 && errno == EAGAIN); // Same full c->send: MSG_DONTWAIT
|
||||||
|
|
||||||
|
if (!s_send_expect_error) {
|
||||||
|
s_recv_start = true; // O_NONBLOCK is per socket; now start the receiver
|
||||||
|
while (!s_recv_waiting) vTaskDelay(1);
|
||||||
|
}
|
||||||
|
s_send_waiting = true; // Gate task releases TX after this send blocks
|
||||||
|
n = send(fd, &marker, sizeof(marker), 0);
|
||||||
|
if (s_send_expect_error) ASSERT(n < 0 && errno == EPIPE);
|
||||||
|
else ASSERT(n == (ssize_t) sizeof(marker));
|
||||||
|
s_send_done = true;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// accept loop. Waits for incoming connections on port 1234 and spawns
|
// accept loop. Waits for incoming connections on port 1234 and spawns
|
||||||
// an echo_task for each one, allowing concurrent clients.
|
// an echo_task for each one, allowing concurrent clients.
|
||||||
static void atask(void *args) {
|
static void atask(void *args) {
|
||||||
|
const struct atask_args *cfg = (const struct atask_args *) args;
|
||||||
BaseType_t result;
|
BaseType_t result;
|
||||||
mg_bsd_init();
|
int lfd = open_listener(5, cfg->listener_nonblock);
|
||||||
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
if (cfg->once && cfg->listener_nonblock) {
|
||||||
struct sockaddr_in sa;
|
ASSERT(accept(lfd, NULL, NULL) < 0 && errno == EAGAIN);
|
||||||
memset(&sa, 0, sizeof(sa));
|
s_accept_waiting = true;
|
||||||
sa.sin_family = AF_INET;
|
}
|
||||||
sa.sin_port = htons(1234);
|
|
||||||
sa.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
bind(lfd, (struct sockaddr *) &sa, sizeof(sa));
|
|
||||||
listen(lfd, 5);
|
|
||||||
fcntl(lfd, F_SETFL, O_NONBLOCK);
|
|
||||||
MG_INFO(("BSD echo server on :1234"));
|
|
||||||
while (!s_wait) {
|
while (!s_wait) {
|
||||||
int fd = accept(lfd, NULL, NULL);
|
int fd;
|
||||||
if (fd < 0) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }
|
|
||||||
// echo_task at higher priority than task1
|
if (cfg->once && !cfg->listener_nonblock) s_accept_waiting = true;
|
||||||
result = xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd,
|
fd = accept(lfd, NULL, NULL);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (!cfg->listener_nonblock) {
|
||||||
|
ASSERT(fd >= 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Accepted sockets start blocking, regardless of listener flags
|
||||||
|
ASSERT((fcntl(fd, F_GETFL, 0) & O_NONBLOCK) == 0);
|
||||||
|
if (cfg->worker_nonblock) ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
// Worker tasks run at higher priority than task1
|
||||||
|
if (cfg->receiver) {
|
||||||
|
result = xTaskCreate(recv_wait_task, "recv", 512,
|
||||||
|
(void *) (uintptr_t) fd,
|
||||||
|
configMAX_PRIORITIES - 1, NULL);
|
||||||
|
ASSERT(result == pdPASS);
|
||||||
|
}
|
||||||
|
if (cfg->gate != NULL) {
|
||||||
|
result = xTaskCreate(cfg->gate, "gate", 256, NULL,
|
||||||
|
configMAX_PRIORITIES - 3, NULL);
|
||||||
|
ASSERT(result == pdPASS);
|
||||||
|
}
|
||||||
|
result = xTaskCreate(cfg->task, "echo", 512, (void *) (uintptr_t) fd,
|
||||||
configMAX_PRIORITIES - 1, NULL);
|
configMAX_PRIORITIES - 1, NULL);
|
||||||
MG_INFO(("Echo task %d: %s", fd, result == pdPASS ? "created" : "FAILED"));
|
MG_INFO(("Echo task %d: %s", fd, result == pdPASS ? "created" : "FAILED"));
|
||||||
ASSERT(result == pdPASS);
|
ASSERT(result == pdPASS);
|
||||||
|
if (cfg->once) break;
|
||||||
}
|
}
|
||||||
close(lfd);
|
close(lfd);
|
||||||
s_wait = false;
|
if (cfg->once) s_accept_done = true;
|
||||||
|
else s_wait = false;
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
(void) args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void select_task(void *args) {
|
||||||
|
struct timeval zero = {0, 0};
|
||||||
|
fd_set r, w, e;
|
||||||
|
int fd, lfd, n;
|
||||||
|
|
||||||
|
(void) args;
|
||||||
|
lfd = open_listener(5, true);
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_SET(lfd, &r);
|
||||||
|
ASSERT(select(lfd, &r, NULL, NULL, &zero) == 0);
|
||||||
|
ASSERT(!FD_ISSET(lfd, &r));
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_SET(lfd + 1, &r);
|
||||||
|
ASSERT(select(lfd + 2, &r, NULL, NULL, &zero) < 0 && errno == EBADF);
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_ZERO(&w);
|
||||||
|
FD_ZERO(&e);
|
||||||
|
FD_SET(lfd, &r);
|
||||||
|
FD_SET(lfd, &w);
|
||||||
|
FD_SET(lfd, &e);
|
||||||
|
s_select_waiting = true;
|
||||||
|
n = select(lfd + 1, &r, &w, &e, NULL);
|
||||||
|
ASSERT(n == 1);
|
||||||
|
ASSERT(FD_ISSET(lfd, &r));
|
||||||
|
ASSERT(!FD_ISSET(lfd, &w));
|
||||||
|
ASSERT(!FD_ISSET(lfd, &e));
|
||||||
|
fd = accept(lfd, NULL, NULL);
|
||||||
|
ASSERT(fd >= 0);
|
||||||
|
ASSERT(close(fd) == 0);
|
||||||
|
ASSERT(close(lfd) == 0);
|
||||||
|
s_select_done = true;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void poll_task(void *args) {
|
||||||
|
struct pollfd p;
|
||||||
|
int fd, lfd;
|
||||||
|
|
||||||
|
(void) args;
|
||||||
|
lfd = open_listener(5, true);
|
||||||
|
p.fd = lfd, p.events = POLLIN, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 0);
|
||||||
|
ASSERT(p.revents == 0);
|
||||||
|
p.fd = lfd + 1, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 1);
|
||||||
|
ASSERT(p.revents == POLLNVAL);
|
||||||
|
p.fd = lfd, p.revents = 0;
|
||||||
|
s_poll_waiting = true;
|
||||||
|
ASSERT(poll(&p, 1, -1) == 1);
|
||||||
|
ASSERT(p.revents == POLLIN);
|
||||||
|
fd = accept(lfd, NULL, NULL);
|
||||||
|
ASSERT(fd >= 0);
|
||||||
|
ASSERT(close(fd) == 0);
|
||||||
|
ASSERT(close(lfd) == 0);
|
||||||
|
s_poll_done = true;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readiness_task(void *args) {
|
||||||
|
struct pollfd p;
|
||||||
|
fd_set r, w;
|
||||||
|
int fd, lfd;
|
||||||
|
|
||||||
|
(void) args;
|
||||||
|
lfd = open_listener(5, true);
|
||||||
|
s_ready_waiting = true;
|
||||||
|
while ((fd = accept(lfd, NULL, NULL)) < 0) {
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
vTaskDelay(1);
|
||||||
|
}
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_SET(fd, &r);
|
||||||
|
ASSERT(select(fd + 1, &r, NULL, NULL, NULL) == 1);
|
||||||
|
ASSERT(FD_ISSET(fd, &r));
|
||||||
|
p.fd = fd, p.events = POLLIN, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 1);
|
||||||
|
ASSERT(p.revents == POLLIN);
|
||||||
|
FD_ZERO(&w);
|
||||||
|
FD_SET(fd, &w);
|
||||||
|
ASSERT(select(fd + 1, NULL, &w, NULL, NULL) == 1);
|
||||||
|
ASSERT(FD_ISSET(fd, &w));
|
||||||
|
p.events = POLLOUT, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 1);
|
||||||
|
ASSERT(p.revents == POLLOUT);
|
||||||
|
ASSERT(close(lfd) == 0);
|
||||||
|
echo_task_common(fd, 0, &s_ready_done);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void backlog_task(void *args) {
|
||||||
|
struct pollfd p;
|
||||||
|
BaseType_t result;
|
||||||
|
int fd, lfd;
|
||||||
|
|
||||||
|
(void) args;
|
||||||
|
lfd = open_listener(1, true);
|
||||||
|
p.fd = lfd, p.events = POLLIN, p.revents = 0;
|
||||||
|
s_backlog_ready = true;
|
||||||
|
while (poll(&p, 1, 0) == 0) vTaskDelay(1);
|
||||||
|
ASSERT(p.revents == POLLIN);
|
||||||
|
s_backlog_full = true; // One orphan occupies the BSD backlog
|
||||||
|
while (!s_backlog_accept) vTaskDelay(1);
|
||||||
|
ASSERT((fd = accept(lfd, NULL, NULL)) >= 0);
|
||||||
|
result = xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd,
|
||||||
|
configMAX_PRIORITIES - 1, NULL);
|
||||||
|
ASSERT(result == pdPASS);
|
||||||
|
ASSERT(close(lfd) == 0);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// task2: tests owner.
|
// task2: tests owner.
|
||||||
static void task2(void *args) {
|
static void task2(void *args) {
|
||||||
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||||
char *ip, *cmd[3];
|
char *ip;
|
||||||
pid_t pid;
|
|
||||||
int status;
|
int status;
|
||||||
extern char **environ;
|
pid_t first, second;
|
||||||
|
|
||||||
#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true");
|
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||||
s_error = false;
|
s_error = false;
|
||||||
s_wait = false;
|
s_wait = false;
|
||||||
// atask at higher priority than task1
|
|
||||||
xTaskCreate(atask, "atask", 256, NULL, configMAX_PRIORITIES - 1, NULL);
|
|
||||||
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
||||||
cmd[0] = (char *) "./bsd_client";
|
s_poll_waiting = s_poll_done = false;
|
||||||
cmd[1] = ip;
|
ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
|
||||||
cmd[2] = NULL;
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
|
while (!s_poll_waiting) vTaskDelay(1);
|
||||||
for (;;) {
|
ASSERT(!s_poll_done);
|
||||||
pid_t r = waitpid(pid, &status, WNOHANG);
|
run_client(ip, "hold");
|
||||||
ASSERT(r >= 0);
|
while (!s_poll_done) vTaskDelay(1);
|
||||||
if (r == pid) break;
|
DASHBOARD("poll_read");
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
}
|
s_select_waiting = s_select_done = false;
|
||||||
ASSERT(WIFEXITED(status));
|
ASSERT(xTaskCreate(select_task, "select", 512, NULL,
|
||||||
ASSERT(WEXITSTATUS(status) == 0);
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
mg_free(ip);
|
while (!s_select_waiting) vTaskDelay(1);
|
||||||
|
ASSERT(!s_select_done);
|
||||||
|
run_client(ip, "hold");
|
||||||
|
while (!s_select_done) vTaskDelay(1);
|
||||||
|
DASHBOARD("select_read");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_ready_waiting = s_ready_done = false;
|
||||||
|
ASSERT(xTaskCreate(readiness_task, "ready", 512, NULL,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
while (!s_ready_waiting) vTaskDelay(1);
|
||||||
|
run_client(ip, "1");
|
||||||
|
while (!s_ready_done) vTaskDelay(1);
|
||||||
|
DASHBOARD("select_child_read");
|
||||||
|
DASHBOARD("poll_child_read");
|
||||||
|
DASHBOARD("select_child_write");
|
||||||
|
DASHBOARD("poll_child_write");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
|
||||||
|
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
while (!s_backlog_ready) vTaskDelay(1);
|
||||||
|
first = start_client(ip, "1"); // Fills listen(fd, 1)'s pending accept
|
||||||
|
while (!s_backlog_full) vTaskDelay(1);
|
||||||
|
second = start_client(ip, "expectclose"); // Rejected over the backlog
|
||||||
|
while (!client_done(second, &status)) vTaskDelay(1);
|
||||||
|
check_client(status);
|
||||||
|
s_backlog_accept = true;
|
||||||
|
while (!client_done(first, &status)) vTaskDelay(1);
|
||||||
|
check_client(status);
|
||||||
|
DASHBOARD("listen_backlog");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
// atask at higher priority than task1
|
||||||
|
s_accept_waiting = s_accept_done = false;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_accept_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
while (!s_accept_waiting) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
ASSERT(!s_accept_done);
|
||||||
|
run_client(ip, "1");
|
||||||
|
while (!s_accept_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
DASHBOARD("blocking_accept");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_wait = false;
|
||||||
|
s_accept_waiting = s_accept_done = false;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_nonblock_accept_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
while (!s_accept_waiting) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
run_client(ip, "1");
|
||||||
|
while (!s_accept_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
DASHBOARD("nonblocking_accept");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_wait = false;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_echo_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
run_client(ip, NULL);
|
||||||
s_wait = true;
|
s_wait = true;
|
||||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
DASHBOARD("accept");
|
DASHBOARD("accept");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_wait = false;
|
||||||
|
s_dontwait_done = false;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_dontwait_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
run_client(ip, "1");
|
||||||
|
while (!s_dontwait_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
s_wait = true;
|
||||||
|
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
DASHBOARD("dontwait_recv");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_wait = false;
|
||||||
|
s_nonblock_done = false;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_nonblock_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
run_client(ip, "1");
|
||||||
|
while (!s_nonblock_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
s_wait = true;
|
||||||
|
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
DASHBOARD("nonblocking_recv");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
|
||||||
|
s_send_waiting = false;
|
||||||
|
s_send_done = s_send_expect_error = false;
|
||||||
|
s_recv_start = s_recv_waiting = s_recv_done = s_recv_expect_error = false;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
run_client(ip, "waitanddrain"); // Peer drains through the final marker
|
||||||
|
ASSERT(s_send_done);
|
||||||
|
DASHBOARD("nonblocking_send");
|
||||||
|
DASHBOARD("blocking_send");
|
||||||
|
while (!s_recv_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
DASHBOARD("blocking_recv");
|
||||||
|
DASHBOARD("shared_socket");
|
||||||
|
s_wait = true;
|
||||||
|
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
|
||||||
|
s_send_waiting = false;
|
||||||
|
s_send_done = s_send_expect_error = false;
|
||||||
|
s_recv_start = s_recv_waiting = s_recv_done = false;
|
||||||
|
s_recv_expect_error = true;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
run_client(ip, "waitandabort"); // Peer leaves data unread and sends RST
|
||||||
|
ASSERT(s_send_done);
|
||||||
|
while (!s_recv_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
DASHBOARD("error_wakeup");
|
||||||
|
s_wait = true;
|
||||||
|
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
|
||||||
|
s_send_waiting = false;
|
||||||
|
s_send_done = false;
|
||||||
|
s_send_expect_error = true;
|
||||||
|
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_error_server,
|
||||||
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
|
run_client(ip, "hold"); // Peer stays connected while the driver fails TX
|
||||||
|
while (!s_tx_error) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
ASSERT(s_send_done);
|
||||||
|
DASHBOARD("send_error_wakeup");
|
||||||
|
s_wait = true;
|
||||||
|
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
s_tx_held = s_tx_fail = s_tx_error = s_send_expect_error = false;
|
||||||
|
|
||||||
|
mg_free(ip);
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
{
|
||||||
|
fd_set e;
|
||||||
|
struct pollfd p;
|
||||||
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(1234);
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
|
||||||
|
errno == EINPROGRESS);
|
||||||
|
FD_ZERO(&e);
|
||||||
|
FD_SET(fd, &e);
|
||||||
|
ASSERT(select(fd + 1, NULL, NULL, &e, NULL) == 1);
|
||||||
|
ASSERT(FD_ISSET(fd, &e));
|
||||||
|
p.fd = fd, p.events = 0, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, -1) == 1);
|
||||||
|
ASSERT((p.revents & POLLERR) != 0);
|
||||||
|
p.events = POLLOUT, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 1);
|
||||||
|
ASSERT((p.revents & POLLERR) != 0);
|
||||||
|
ASSERT((p.revents & POLLOUT) == 0);
|
||||||
|
ASSERT(close(fd) == 0);
|
||||||
|
}
|
||||||
|
DASHBOARD("select_error");
|
||||||
|
DASHBOARD("poll_error");
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||||
s_error = false;
|
s_error = false;
|
||||||
{
|
{
|
||||||
@@ -188,6 +740,7 @@ static void task2(void *args) {
|
|||||||
while (!cd[0].done || !cd[1].done || !cd[2].done) vTaskDelay(pdMS_TO_TICKS(10));
|
while (!cd[0].done || !cd[1].done || !cd[2].done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
}
|
}
|
||||||
DASHBOARD("connect");
|
DASHBOARD("connect");
|
||||||
|
DASHBOARD("select_write");
|
||||||
// End task 1
|
// End task 1
|
||||||
s_done = true;
|
s_done = true;
|
||||||
while (s_done) vTaskDelay(pdMS_TO_TICKS(10));
|
while (s_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
@@ -206,35 +759,205 @@ static void task2(void *args) {
|
|||||||
static void task1(void *args) {
|
static void task1(void *args) {
|
||||||
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||||
|
|
||||||
// Stack initialization, Network configuration (DHCP lease, ...)
|
wait_ready(mgr);
|
||||||
#if BSDTEST_USING_DHCP == 0
|
|
||||||
MG_INFO(("MIF configuration: Static IP"));
|
|
||||||
ASSERT(mif.ip != 0); // Check we have a satic IP assigned
|
|
||||||
mg_mgr_poll(&mgr, 0); // For initialisation
|
|
||||||
#else
|
|
||||||
MG_INFO(("MIF configuration: DHCP"));
|
|
||||||
ASSERT(mgr->ifp->ip == 0); // Check we are set for DHCP
|
|
||||||
while (mgr->ifp->ip == 0 && mg_millis() < 5000) {
|
|
||||||
mg_mgr_poll(mgr, 0);
|
|
||||||
}
|
|
||||||
if (mgr->ifp->ip == 0) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n"));
|
|
||||||
ASSERT(mgr->ifp->ip != 0); // We have an IP (lease or static)
|
|
||||||
#endif
|
|
||||||
while (mgr->ifp->state != MG_TCPIP_STATE_READY) {
|
|
||||||
mg_mgr_poll(mgr, 0);
|
|
||||||
}
|
|
||||||
MG_INFO(("Interface started, starting main loop"));
|
|
||||||
xTaskCreate(task2, "task2", 256, mgr, configMAX_PRIORITIES - 2, NULL);
|
xTaskCreate(task2, "task2", 256, mgr, configMAX_PRIORITIES - 2, NULL);
|
||||||
|
|
||||||
while (!s_done) {
|
while (!s_done) {
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
mg_bsd_poll(mgr);
|
|
||||||
vTaskDelay(1); // Let idle reclaim deleted echo tasks
|
vTaskDelay(1); // Let idle reclaim deleted echo tasks
|
||||||
}
|
}
|
||||||
s_done = false;
|
s_done = false;
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define NORTOS_ECHO_CLIENTS 3
|
||||||
|
|
||||||
|
struct nortos_echo_client {
|
||||||
|
int fd;
|
||||||
|
size_t len, sent;
|
||||||
|
bool eof;
|
||||||
|
char buf[512];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int nortos_accept(struct mg_mgr *mgr, int lfd) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
while ((fd = accept(lfd, NULL, NULL)) < 0) {
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
mg_mgr_poll(mgr, 0);
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nortos_wait_client(struct mg_mgr *mgr, pid_t pid) {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
while (!client_done(pid, &status)) mg_mgr_poll(mgr, 0);
|
||||||
|
check_client(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nortos_echo(struct mg_mgr *mgr, struct nortos_echo_client *clients,
|
||||||
|
size_t count, pid_t pid) {
|
||||||
|
struct pollfd p[NORTOS_ECHO_CLIENTS];
|
||||||
|
fd_set r;
|
||||||
|
size_t i, live = count;
|
||||||
|
int maxfd, result;
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
while (live > 0) {
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
struct nortos_echo_client *c = &clients[i];
|
||||||
|
if (!c->eof && c->sent < c->len) {
|
||||||
|
n = send(c->fd, c->buf + c->sent, c->len - c->sent, 0);
|
||||||
|
if (n > 0) c->sent += (size_t) n;
|
||||||
|
else ASSERT(n < 0 && errno == EAGAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FD_ZERO(&r);
|
||||||
|
maxfd = -1;
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
struct nortos_echo_client *c = &clients[i];
|
||||||
|
p[i].fd = -1, p[i].events = POLLIN, p[i].revents = 0;
|
||||||
|
if (!c->eof && c->sent == c->len) {
|
||||||
|
p[i].fd = c->fd;
|
||||||
|
FD_SET(c->fd, &r);
|
||||||
|
if (c->fd > maxfd) maxfd = c->fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxfd >= 0) {
|
||||||
|
result = poll(p, (nfds_t) count, 0);
|
||||||
|
ASSERT(result >= 0);
|
||||||
|
if (result > 0) {
|
||||||
|
ASSERT(select(maxfd + 1, &r, NULL, NULL, NULL) == result);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
struct nortos_echo_client *c = &clients[i];
|
||||||
|
bool readable = (p[i].revents & POLLIN) != 0;
|
||||||
|
|
||||||
|
ASSERT(p[i].revents == (readable ? POLLIN : 0));
|
||||||
|
ASSERT(FD_ISSET(c->fd, &r) == readable);
|
||||||
|
if (readable) {
|
||||||
|
n = recv(c->fd, c->buf, sizeof(c->buf), 0);
|
||||||
|
ASSERT(n >= 0);
|
||||||
|
if (n > 0) c->len = (size_t) n, c->sent = 0;
|
||||||
|
else c->eof = true, live--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mg_mgr_poll(mgr, 0);
|
||||||
|
}
|
||||||
|
nortos_wait_client(mgr, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nortos_test(struct mg_mgr *mgr) {
|
||||||
|
static const char bootstrap = '\0';
|
||||||
|
static const char grow[512] = {0};
|
||||||
|
static const char marker = '\1';
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
struct timeval zero = {0, 0};
|
||||||
|
struct pollfd p;
|
||||||
|
fd_set r, w, e;
|
||||||
|
char buf;
|
||||||
|
char *ip;
|
||||||
|
pid_t pid;
|
||||||
|
size_t i, j;
|
||||||
|
int fd, lfd, n;
|
||||||
|
struct nortos_echo_client clients[NORTOS_ECHO_CLIENTS];
|
||||||
|
|
||||||
|
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
||||||
|
ASSERT(ip != NULL);
|
||||||
|
lfd = open_listener(5, true);
|
||||||
|
p.fd = lfd, p.events = POLLIN, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 10) == 0);
|
||||||
|
ASSERT(p.revents == 0);
|
||||||
|
p.fd = lfd + 1, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 10) == 1);
|
||||||
|
ASSERT(p.revents == POLLNVAL);
|
||||||
|
p.fd = lfd, p.revents = 0;
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_SET(lfd, &r);
|
||||||
|
ASSERT(select(lfd, &r, NULL, NULL, &zero) == 0);
|
||||||
|
ASSERT(!FD_ISSET(lfd, &r));
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_SET(lfd + 1, &r);
|
||||||
|
ASSERT(select(lfd + 2, &r, NULL, NULL, &zero) < 0 && errno == EBADF);
|
||||||
|
ASSERT(accept(lfd, NULL, NULL) < 0 && errno == EAGAIN);
|
||||||
|
DASHBOARD("nortos_nonblocking_accept");
|
||||||
|
|
||||||
|
pid = start_client(ip, "waitanddrain");
|
||||||
|
do {
|
||||||
|
p.revents = 0;
|
||||||
|
n = poll(&p, 1, 10);
|
||||||
|
if (n == 0) mg_mgr_poll(mgr, 0);
|
||||||
|
} while (n == 0);
|
||||||
|
ASSERT(n == 1);
|
||||||
|
ASSERT(p.revents == POLLIN);
|
||||||
|
DASHBOARD("nortos_poll_read");
|
||||||
|
do {
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_ZERO(&w);
|
||||||
|
FD_ZERO(&e);
|
||||||
|
FD_SET(lfd, &r);
|
||||||
|
FD_SET(lfd, &w);
|
||||||
|
FD_SET(lfd, &e);
|
||||||
|
n = select(lfd + 1, &r, &w, &e, NULL);
|
||||||
|
if (n == 0) mg_mgr_poll(mgr, 0);
|
||||||
|
} while (n == 0);
|
||||||
|
ASSERT(n == 1);
|
||||||
|
ASSERT(FD_ISSET(lfd, &r));
|
||||||
|
ASSERT(!FD_ISSET(lfd, &w));
|
||||||
|
ASSERT(!FD_ISSET(lfd, &e));
|
||||||
|
DASHBOARD("nortos_select_read");
|
||||||
|
fd = nortos_accept(mgr, lfd);
|
||||||
|
ASSERT(recv(fd, &buf, sizeof(buf), MSG_DONTWAIT) < 0 && errno == EAGAIN);
|
||||||
|
DASHBOARD("nortos_dontwait_recv");
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
ASSERT(recv(fd, &buf, sizeof(buf), 0) < 0 && errno == EAGAIN);
|
||||||
|
DASHBOARD("nortos_nonblocking_recv");
|
||||||
|
|
||||||
|
ASSERT(send(fd, &bootstrap, sizeof(bootstrap), 0) ==
|
||||||
|
(ssize_t) sizeof(bootstrap));
|
||||||
|
s_fail_alloc = true; // Fail growth after c->send has been bootstrapped
|
||||||
|
ASSERT(send(fd, grow, sizeof(grow), 0) < 0 && errno == EAGAIN);
|
||||||
|
s_fail_alloc = false;
|
||||||
|
ASSERT(send(fd, &marker, sizeof(marker), 0) == (ssize_t) sizeof(marker));
|
||||||
|
nortos_wait_client(mgr, pid);
|
||||||
|
ASSERT(close(fd) == 0);
|
||||||
|
DASHBOARD("nortos_nonblocking_send");
|
||||||
|
|
||||||
|
memset(clients, 0, sizeof(clients));
|
||||||
|
pid = start_client(ip, "3");
|
||||||
|
for (i = 0; i < NORTOS_ECHO_CLIENTS; i++) {
|
||||||
|
clients[i].fd = nortos_accept(mgr, lfd);
|
||||||
|
ASSERT(fcntl(clients[i].fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
for (j = 0; j < i; j++) ASSERT(clients[i].fd != clients[j].fd);
|
||||||
|
}
|
||||||
|
nortos_echo(mgr, clients, NORTOS_ECHO_CLIENTS, pid);
|
||||||
|
for (i = 0; i < NORTOS_ECHO_CLIENTS; i++)
|
||||||
|
ASSERT(close(clients[i].fd) == 0);
|
||||||
|
ASSERT(close(lfd) == 0);
|
||||||
|
DASHBOARD("nortos_select_children");
|
||||||
|
DASHBOARD("nortos_poll_children");
|
||||||
|
DASHBOARD("nortos_echo");
|
||||||
|
|
||||||
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(1234);
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
|
||||||
|
errno == EINPROGRESS);
|
||||||
|
ASSERT(close(fd) == 0);
|
||||||
|
mg_mgr_poll(mgr, 0);
|
||||||
|
DASHBOARD("nortos_nonblocking_connect");
|
||||||
|
mg_free(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MG_ENABLE_FREERTOS
|
||||||
|
|
||||||
static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const char *mac, const char *debug_level) {
|
static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const char *mac, const char *debug_level) {
|
||||||
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));
|
||||||
@@ -263,8 +986,21 @@ static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const
|
|||||||
MG_INFO(("Init done, starting interface"));
|
MG_INFO(("Init done, starting interface"));
|
||||||
usleep(200000); // 200 ms
|
usleep(200000); // 200 ms
|
||||||
|
|
||||||
|
#if MG_ENABLE_FREERTOS
|
||||||
|
s_tx = driver->tx;
|
||||||
|
driver->tx = tx_gate;
|
||||||
xTaskCreate(task1, "task1", 2048, &mgr, configMAX_PRIORITIES - 2, NULL);
|
xTaskCreate(task1, "task1", 2048, &mgr, configMAX_PRIORITIES - 2, NULL);
|
||||||
vTaskStartScheduler();
|
vTaskStartScheduler();
|
||||||
|
driver->tx = s_tx;
|
||||||
|
#else
|
||||||
|
s_bootmillis = mg_millis();
|
||||||
|
wait_ready(&mgr);
|
||||||
|
nortos_test(&mgr);
|
||||||
|
s_error = false;
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
ASSERT(mgr.conns == NULL);
|
||||||
|
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false" : "true");
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NO_ABORT
|
#ifdef NO_ABORT
|
||||||
if (s_abort != 0) return false;
|
if (s_abort != 0) return false;
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// See https://mongoose.ws/documentation/#build-options
|
// See https://mongoose.ws/documentation/#build-options
|
||||||
//#undef MG_ARCH
|
|
||||||
//#define MG_ARCH MG_ARCH_UNIX
|
|
||||||
#define MG_ENABLE_FREERTOS 1
|
|
||||||
|
|
||||||
#define MG_ENABLE_TCPIP 1
|
#define MG_ENABLE_TCPIP 1
|
||||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||||
#define MG_ENABLE_BSD_SOCKETS 1
|
#define MG_ENABLE_BSD_SOCKETS 1
|
||||||
#define MG_ENABLE_BSD_LOG 1
|
|
||||||
#define MG_ENABLE_LINES 1
|
#define MG_ENABLE_LINES 1
|
||||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||||
|
|
||||||
//#include <ctype.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@@ -24,7 +19,16 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if MG_ENABLE_FREERTOS
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define MG_ENABLE_CUSTOM_CALLOC 1
|
||||||
|
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
ROOT ?= $(realpath $(CURDIR)/../..)
|
ROOT ?= $(realpath $(CURDIR)/../..)
|
||||||
PROJECTS ?= ../../tutorials/http/device-dashboard/esp32/ ../../tutorials/micropython/esp32/ ../../tutorials/http/uart-bridge/esp32/
|
PROJECTS ?= ../../tutorials/esp32/device-dashboard/ ../../tutorials/micropython/esp32/ ../../tutorials/esp32/uart-bridge/
|
||||||
TARGET ?= Debug
|
TARGET ?= Debug
|
||||||
DOCKER = docker run --rm -v $(ROOT):$(ROOT) -v $(CURDIR):/root -w $(CURDIR)
|
DOCKER = docker run --rm -v $(ROOT):$(ROOT) -v $(CURDIR):/root -w $(CURDIR)
|
||||||
IMAGE ?= espressif/idf
|
IMAGE ?= espressif/idf
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Mesh simulator. `make` builds and runs an example
|
||||||
|
ROOT = ../..
|
||||||
|
SRCS = simulator.c l2_mesh.c l2_802154.c star.c $(ROOT)/mongoose.c
|
||||||
|
HDRS = simulator.h l2_mesh.h l2_802154.h star.h $(ROOT)/mongoose.h
|
||||||
|
WARN = -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common \
|
||||||
|
-Wconversion -Wundef -Wmissing-prototypes -Wstrict-prototypes
|
||||||
|
DEFS = -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ENABLE_IPV6=0 \
|
||||||
|
-DMG_ENABLE_CUSTOM_MILLIS=1
|
||||||
|
CFLAGS ?= -O2 -g0 $(WARN)
|
||||||
|
X ?= 100
|
||||||
|
Y ?= 100
|
||||||
|
ROWS ?= 10
|
||||||
|
COLS ?= 10
|
||||||
|
|
||||||
|
# Nodes at every corner of a ROWS x COLS grid over X,Y, edges included
|
||||||
|
GRID = $(shell for r in $$(seq 0 $(ROWS)); do \
|
||||||
|
for c in $$(seq 0 $(COLS)); do \
|
||||||
|
echo -node $$((c * $(X) / $(COLS))),$$((r * $(Y) / $(ROWS))); \
|
||||||
|
done; done)
|
||||||
|
|
||||||
|
all: mesh_sim
|
||||||
|
./mesh_sim -area $(X),$(Y) \
|
||||||
|
-bridge $$(($(X) / 2)),$$(($(Y) / 2)) \
|
||||||
|
$(GRID)
|
||||||
|
|
||||||
|
mesh_sim: $(SRCS) $(HDRS) Makefile
|
||||||
|
$(CC) $(SRCS) $(CFLAGS) $(DEFS) -I. -I$(ROOT) -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf mesh_sim mesh_sim.dSYM
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# Ethernet over a radio mesh
|
||||||
|
|
||||||
|
## The idea
|
||||||
|
|
||||||
|
Small radio devices, e.g. IEEE 802.15.4, sub-GHz or LoRa boards, should run
|
||||||
|
Mongoose as if they were on an ordinary Ethernet LAN: get an IP address over
|
||||||
|
DHCP, answer HTTP, talk MQTT. No 6LoWPAN, no Thread, no special stack.
|
||||||
|
|
||||||
|
To do that, the radio interface looks like Ethernet to Mongoose
|
||||||
|
(`MG_TCPIP_L2_ETH`). Its driver splits each Ethernet frame into radio
|
||||||
|
frames, sends them over the air, and the receiver puts them back together.
|
||||||
|
One or more bridges connect the radio network to the wired LAN: a bridge has
|
||||||
|
an Ethernet port and a radio. A node in range of a bridge talks to it
|
||||||
|
directly; a node out of range reaches a bridge through other nodes, which
|
||||||
|
forward its frames. That makes the radio network a mesh. With several
|
||||||
|
bridges, each node uses one of them, e.g. the nearest; the mesh covers a
|
||||||
|
bigger area, and keeps working if a bridge goes down. A PC on the LAN sees
|
||||||
|
the radio nodes as regular hosts on the same LAN.
|
||||||
|
|
||||||
|
```
|
||||||
|
+---- bridge 1 ~~~ node ~~~ node
|
||||||
|
| ~
|
||||||
|
PC ---- LAN node
|
||||||
|
| ~
|
||||||
|
+---- bridge 2 ~~~ node ~~~ node ~~~ node
|
||||||
|
```
|
||||||
|
|
||||||
|
`~~~` and `~` are radio hops. Nodes far from a bridge send their frames
|
||||||
|
through nearer nodes. A node in the middle can reach either bridge.
|
||||||
|
|
||||||
|
## Two parts plug in
|
||||||
|
|
||||||
|
- `struct mg_radio`, a radio standard: how big a radio frame is, how
|
||||||
|
addresses are written into it. `mg_radio_802154` is the first one.
|
||||||
|
- `struct mg_mesh`, a mesh strategy: how nodes find a bridge, and where each
|
||||||
|
frame goes next. `mg_mesh_star` is the first and simplest one: every node
|
||||||
|
talks to a bridge directly, one hop.
|
||||||
|
|
||||||
|
Everything else, splitting and reassembling frames, sending again when a
|
||||||
|
frame is lost, is common code that works the same for every radio and every
|
||||||
|
mesh. The goal is to swap radios and mesh strategies without touching
|
||||||
|
anything else.
|
||||||
|
|
||||||
|
## Why a simulator
|
||||||
|
|
||||||
|
Real boards are slow to test with, and you can't put 500 of them on a desk.
|
||||||
|
The simulator runs a whole network in one process: every node is a real
|
||||||
|
Mongoose instance, only the radio and the air between nodes are emulated.
|
||||||
|
That lets us try mesh strategies with many nodes, and measure how long it
|
||||||
|
takes nodes to join, get an address, and answer a request.
|
||||||
|
|
||||||
|
## Virtual time
|
||||||
|
|
||||||
|
The simulator has its own clock, `simulator::now`, in microseconds, and
|
||||||
|
Mongoose reads it through `mg_millis()`. Nothing waits for real time: the
|
||||||
|
simulator moves the clock forward itself, 250 us per step, and polls every
|
||||||
|
station once per step. A minute of network traffic takes about a second to
|
||||||
|
simulate. `-t SEC` sets how long to run, 10 minutes by default.
|
||||||
|
|
||||||
|
The step is a trade-off. Each step polls every station's Mongoose, and even
|
||||||
|
an idle poll costs something, so a smaller step means a slower run. A
|
||||||
|
bigger step makes frames wait longer in queues, see below. Measured with
|
||||||
|
121 nodes and 10 simulated minutes:
|
||||||
|
|
||||||
|
| Step | Request time, mean | Run time |
|
||||||
|
|------|--------------------|----------|
|
||||||
|
| 1 ms | 6.1 ms | 0.9 s |
|
||||||
|
| 250 us | 1.5 ms | 3.0 s |
|
||||||
|
| 100 us | 0.6 ms | 7.2 s |
|
||||||
|
| 10 us | 0.06 ms | 72 s |
|
||||||
|
|
||||||
|
## The setup
|
||||||
|
|
||||||
|
```
|
||||||
|
DHCP server ---------+
|
||||||
|
10.0.0.1 |
|
||||||
|
02:01:00:00:00:00 +-- LAN ---- bridge ~~~ nodes
|
||||||
|
|
|
||||||
|
test station --------+
|
||||||
|
10.0.0.2
|
||||||
|
02:02:00:00:00:00
|
||||||
|
```
|
||||||
|
|
||||||
|
The LAN is assumed infinitely fast, see "How frames move on the LAN" below.
|
||||||
|
Two stations are on it:
|
||||||
|
|
||||||
|
- The DHCP server, 10.0.0.1, MAC 02:01:00:00:00:00, gives each node an
|
||||||
|
address made from its MAC: 02:00:00:X:Y:Z gets 10.X+1.Y.Z, so node
|
||||||
|
addresses never clash with the LAN stations' 10.0.0.x.
|
||||||
|
- The test station, 10.0.0.2, MAC 02:02:00:00:00:00, is a Mongoose
|
||||||
|
instance. It waits for the mesh to settle, 60 seconds by default
|
||||||
|
(`-w SEC`), then sends `GET /` to each node in turn, one at a time, at the
|
||||||
|
node's own IP address. A node with no address, or one that doesn't answer
|
||||||
|
within 10 seconds, counts as failed. At the end it prints how many
|
||||||
|
requests succeeded and how long they took.
|
||||||
|
|
||||||
|
Bridges are on the LAN too, and get every frame, as if in promiscuous mode:
|
||||||
|
a frame for one of their radio nodes isn't addressed to the bridge itself.
|
||||||
|
A bridge forwards every frame it gets from the LAN to the radio, and every
|
||||||
|
frame from the radio to the LAN, with no filtering yet.
|
||||||
|
|
||||||
|
Bridges and radio nodes get MACs 02:00:00:xx:xx:xx, numbered from 1 in
|
||||||
|
command-line order: 02:00:00:00:00:01, 02:00:00:00:00:02, and so on.
|
||||||
|
|
||||||
|
### How frames move on the LAN
|
||||||
|
|
||||||
|
Every LAN station, the DHCP server, the test station and each bridge, has a
|
||||||
|
LAN interface, `lif`: a `struct mg_tcpip_if` with a receive queue. All of
|
||||||
|
them use the same driver, `s_lan_driver`. There are no cables and no switch:
|
||||||
|
|
||||||
|
- **Sending (tx).** A station sends a frame with its driver's `tx`, which is
|
||||||
|
`lan_tx()`. It looks at the destination MAC and copies the frame into the
|
||||||
|
receive queues of the stations it is for:
|
||||||
|
- a broadcast or multicast goes to every station but the sender;
|
||||||
|
- a unicast goes to the station with that MAC;
|
||||||
|
- bridges get every frame, as a real bridge in promiscuous mode would:
|
||||||
|
a frame for a radio node is addressed to the node, not to its bridge.
|
||||||
|
|
||||||
|
That's all `tx` does. Nobody's code runs on the receiving side yet.
|
||||||
|
- **Receiving (rx).** Each station takes the frames from its own queue when
|
||||||
|
the main loop polls it:
|
||||||
|
- the test station runs Mongoose, which reads the queue by itself, as it
|
||||||
|
would with a real Ethernet driver that queues frames from an interrupt;
|
||||||
|
- the DHCP server reads it in `dhcp_poll()`, and replies by sending
|
||||||
|
through its own `lif`;
|
||||||
|
- bridges read it in `bridges_poll()`, and forward it to the radio.
|
||||||
|
|
||||||
|
So a frame waits in a queue until its receiver's next poll, at most one
|
||||||
|
step of virtual time, 250 us. Ethernet is hundreds of times faster than the radio, so that
|
||||||
|
doesn't matter for what we measure. And since sending never calls the
|
||||||
|
receiver's code, a reply is never sent from inside another station's send.
|
||||||
|
|
||||||
|
For example, a node asking for an address:
|
||||||
|
|
||||||
|
1. The node's DHCP DISCOVER crosses the radio to a bridge, which sends it
|
||||||
|
out of its `lif`. It is a broadcast, so `lan_tx()` puts it into the
|
||||||
|
queues of the DHCP server, the test station and the other bridges.
|
||||||
|
2. On its next poll, the DHCP server reads it and sends an OFFER, also a
|
||||||
|
broadcast, which lands in the test station's and the bridges' queues.
|
||||||
|
3. On their next poll, the bridges read the OFFER and send it over the
|
||||||
|
radio to the node. The test station's Mongoose drops it: the test station
|
||||||
|
has a fixed address and no DHCP client.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | What |
|
||||||
|
|------|------|
|
||||||
|
| `l2_mesh.h`, `l2_mesh.c` | `struct mg_radio`, `struct mg_mesh`, common code |
|
||||||
|
| `l2_802154.h`, `l2_802154.c` | `mg_radio_802154`, IEEE 802.15.4 |
|
||||||
|
| `star.h`, `star.c` | `mg_mesh_star`, the star mesh |
|
||||||
|
| `simulator.h`, `simulator.c` | The simulator |
|
||||||
|
| `Makefile` | Builds and runs the simulator |
|
||||||
|
|
||||||
|
## Where we are
|
||||||
|
|
||||||
|
It works end to end: nodes get addresses over DHCP, and the test station's
|
||||||
|
requests reach them through the bridges. But the radio is ideal for now:
|
||||||
|
|
||||||
|
- The radio driver works like the LAN's: it puts whole Ethernet frames
|
||||||
|
straight into receive queues, all of them for a broadcast, else the one
|
||||||
|
with the destination MAC, and bridges get every frame. No range, no
|
||||||
|
loss, no airtime.
|
||||||
|
- Bridges forward everything, with no filtering.
|
||||||
|
- So use one bridge for now. With two, a broadcast from the LAN goes to the
|
||||||
|
radio through both, each hears the other's copy and sends it back to the
|
||||||
|
LAN, and that never ends. Deciding who forwards what, without loops, is
|
||||||
|
the job of `mg_mesh`.
|
||||||
|
|
||||||
|
`mg_radio` and `mg_mesh` are still stubs. Next steps, one at a time: the
|
||||||
|
air (range, loss, airtime), fragmenting frames into radio frames with
|
||||||
|
`mg_radio`, choosing a bridge and forwarding through nodes with `mg_mesh`,
|
||||||
|
and filtering at the bridges.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make # 100x100 m, a bridge, 11x11 nodes
|
||||||
|
make X=200 Y=50 ROWS=2 COLS=8 # Another area and grid
|
||||||
|
```
|
||||||
|
|
||||||
|
`make` puts a bridge in the middle of the area, and a node at every corner
|
||||||
|
of a `ROWS` x `COLS` grid over it. The simulator itself takes:
|
||||||
|
|
||||||
|
```
|
||||||
|
./mesh_sim -area X,Y,Z -bridge X,Y,Z -node X,Y,Z ... -t SEC -w SEC
|
||||||
|
```
|
||||||
|
|
||||||
|
Positions are in metres; `X,Y` means `Z` is 0. Repeat `-bridge` and `-node`
|
||||||
|
for more.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "l2_802154.h"
|
||||||
|
|
||||||
|
const struct mg_radio mg_radio_802154 = {"802.15.4"};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// IEEE 802.15.4 radio
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "l2_mesh.h"
|
||||||
|
|
||||||
|
extern const struct mg_radio mg_radio_802154;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "l2_mesh.h"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// Ethernet over radio, through a mesh. Mongoose sees an Ethernet interface,
|
||||||
|
// its driver splits Ethernet frames into radio frames. Two parts plug in:
|
||||||
|
// - struct mg_radio: a radio standard, e.g. mg_radio_802154
|
||||||
|
// - struct mg_mesh: how the mesh is built and routed, e.g. mg_mesh_star
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
// A radio standard
|
||||||
|
struct mg_radio {
|
||||||
|
const char *name; // For logs
|
||||||
|
};
|
||||||
|
|
||||||
|
// A mesh strategy
|
||||||
|
struct mg_mesh {
|
||||||
|
const char *name; // For logs
|
||||||
|
};
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// Radio mesh simulator. Bridges and nodes are stations at positions in an
|
||||||
|
// area. Time is virtual: see mg_millis(). On the LAN: a DHCP server, and a
|
||||||
|
// test station that sends HTTP requests to the nodes
|
||||||
|
|
||||||
|
#include "simulator.h"
|
||||||
|
|
||||||
|
#define STEP_MICROSECONDS 250 // Virtual time per main loop step, see README.md
|
||||||
|
|
||||||
|
static struct simulator s_sim; // The simulator
|
||||||
|
|
||||||
|
// Mongoose's clock: virtual time. A minute of network traffic takes far less
|
||||||
|
// than a minute to simulate
|
||||||
|
uint64_t mg_millis(void) {
|
||||||
|
return s_sim.now / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse "X,Y,Z", or "X,Y" with Z 0. Return false if it is not that
|
||||||
|
static bool parse_pos(const char *s, struct pos *p) {
|
||||||
|
int n = 0; // Characters parsed
|
||||||
|
p->x = p->y = p->z = 0;
|
||||||
|
return (sscanf(s, "%lf,%lf,%lf%n", &p->x, &p->y, &p->z, &n) == 3 ||
|
||||||
|
sscanf(s, "%lf,%lf%n", &p->x, &p->y, &n) == 2) &&
|
||||||
|
s[n] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is position p inside the area of sim
|
||||||
|
static bool inside(const struct simulator *sim, const struct pos *p) {
|
||||||
|
return p->x >= 0 && p->x <= sim->area.x && p->y >= 0 && p->y <= sim->area.y &&
|
||||||
|
p->z >= 0 && p->z <= sim->area.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(const struct simulator *sim, const char *prog) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s OPTIONS\n"
|
||||||
|
"Positions: X,Y,Z in metres, or X,Y with Z 0\n"
|
||||||
|
" -area X,Y,Z - area, a box, default: %g,%g,%g\n"
|
||||||
|
" -bridge X,Y,Z - a bridge; repeat for more\n"
|
||||||
|
" -node X,Y,Z - a radio node; repeat for more\n"
|
||||||
|
" -t SEC - simulated time, seconds, default: %g\n"
|
||||||
|
" -w SEC - mesh settle time before requests, default: %g\n",
|
||||||
|
prog, sim->area.x, sim->area.y, sim->area.z,
|
||||||
|
(double) sim->duration / 1e6, (double) sim->tester.wait / 1e6);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next radio MAC, to mac: 02:00:00:xx:xx:xx, numbered from 1
|
||||||
|
static void next_mac(struct simulator *sim, uint8_t *mac) {
|
||||||
|
uint32_t n = ++sim->last_mac;
|
||||||
|
mac[0] = 2, mac[1] = mac[2] = 0, mac[3] = (uint8_t) (n >> 16);
|
||||||
|
mac[4] = (uint8_t) (n >> 8), mac[5] = (uint8_t) n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a bridge at position s. Return false if s is not a position
|
||||||
|
static bool add_bridge(struct simulator *sim, const char *s) {
|
||||||
|
struct bridge *b = (struct bridge *) calloc(1, sizeof(*b));
|
||||||
|
if (b == NULL || !parse_pos(s, &b->pos)) return free(b), false;
|
||||||
|
next_mac(sim, b->rif.mac);
|
||||||
|
LIST_ADD_TAIL(struct bridge, &sim->bridges, b);
|
||||||
|
sim->num_bridges++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a radio node at position s. Return false if s is not a position
|
||||||
|
static bool add_node(struct simulator *sim, const char *s) {
|
||||||
|
struct radio_node *n = (struct radio_node *) calloc(1, sizeof(*n));
|
||||||
|
if (n == NULL || !parse_pos(s, &n->pos)) return free(n), false;
|
||||||
|
next_mac(sim, n->rif.mac);
|
||||||
|
LIST_ADD_TAIL(struct radio_node, &sim->nodes, n);
|
||||||
|
sim->num_nodes++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit if position x is outside the area of sim
|
||||||
|
static void check_inside(const struct simulator *sim, const struct pos *x) {
|
||||||
|
if (!inside(sim, x)) {
|
||||||
|
fprintf(stderr, "%g,%g,%g is outside the area\n", x->x, x->y, x->z);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all bridges and nodes are inside the area of sim
|
||||||
|
static void check_area(const struct simulator *sim) {
|
||||||
|
struct bridge *b;
|
||||||
|
struct radio_node *n;
|
||||||
|
for (b = sim->bridges; b != NULL; b = b->next) check_inside(sim, &b->pos);
|
||||||
|
for (n = sim->nodes; n != NULL; n = n->next) check_inside(sim, &n->pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LAN_QUEUE_SIZE (64 * 1024) // Receive queue of a LAN interface
|
||||||
|
|
||||||
|
// Put frame buf, len bytes, into the receive queue of LAN interface to, if
|
||||||
|
// it is not the sender, from
|
||||||
|
static void lan_put(struct mg_tcpip_if *to, struct mg_tcpip_if *from,
|
||||||
|
const void *buf, size_t len) {
|
||||||
|
if (to != from) mg_tcpip_qwrite((void *) buf, len, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LAN driver, all LAN interfaces share it. The LAN is infinitely fast: tx
|
||||||
|
// puts the frame straight into the receive queues of the interfaces it is
|
||||||
|
// for: all, if broadcast; else the one with the destination MAC. Bridges get
|
||||||
|
// every frame: some are for their radio nodes. Each station takes frames
|
||||||
|
// from its queue when polled, see dhcp_poll(), bridges_poll(); Mongoose does
|
||||||
|
// it for the test station
|
||||||
|
static size_t lan_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
const uint8_t *dst = (const uint8_t *) buf;
|
||||||
|
bool all = dst[0] & 1; // Broadcast, multicast
|
||||||
|
struct bridge *b;
|
||||||
|
if (all || memcmp(dst, s_sim.dhcp.lif.mac, 6) == 0) {
|
||||||
|
lan_put(&s_sim.dhcp.lif, ifp, buf, len);
|
||||||
|
}
|
||||||
|
if (all || memcmp(dst, s_sim.tester.lif.mac, 6) == 0) {
|
||||||
|
lan_put(&s_sim.tester.lif, ifp, buf, len);
|
||||||
|
}
|
||||||
|
for (b = s_sim.bridges; b != NULL; b = b->next) {
|
||||||
|
lan_put(&b->lif, ifp, buf, len);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool lan_init(struct mg_tcpip_if *ifp) {
|
||||||
|
(void) ifp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool lan_link(struct mg_tcpip_if *ifp, bool s1) {
|
||||||
|
(void) ifp, (void) s1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mg_tcpip_driver s_lan_driver = {lan_init, lan_tx, NULL, lan_link};
|
||||||
|
|
||||||
|
// Internet checksum of len bytes
|
||||||
|
static uint16_t ip_csum(const uint8_t *buf, size_t len) {
|
||||||
|
uint32_t sum = 0;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i + 1 < len; i += 2) sum += (uint32_t) (buf[i] << 8 | buf[i + 1]);
|
||||||
|
if (len & 1) sum += (uint32_t) (buf[len - 1] << 8);
|
||||||
|
while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
return (uint16_t) ~sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A node's address, from its MAC 02:00:00:X:Y:Z: 10.X+1.Y.Z, so it never
|
||||||
|
// clashes with the LAN stations' 10.0.0.x
|
||||||
|
static void node_ip(const uint8_t *mac, uint8_t *ip) {
|
||||||
|
ip[0] = 10, ip[1] = (uint8_t) (mac[3] + 1), ip[2] = mac[4], ip[3] = mac[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
// DHCP server, a frame from its queue: a DISCOVER gets an OFFER, a REQUEST
|
||||||
|
// an ACK. Replies are broadcast. The address comes from the client's MAC
|
||||||
|
static void dhcp_rx(struct simulator *sim, const uint8_t *buf, size_t len) {
|
||||||
|
static const uint8_t opts[] = {53, 1, 0, // Message type, set below
|
||||||
|
1, 4, 255, 0, 0, 0, // Mask
|
||||||
|
3, 4, 10, 0, 0, 1, // Router
|
||||||
|
54, 4, 10, 0, 0, 1, // Server
|
||||||
|
51, 4, 0, 0, 14, 16, // Lease, 3600 s
|
||||||
|
255};
|
||||||
|
uint8_t r[14 + 20 + 8 + 240 + sizeof(opts)], type = 0;
|
||||||
|
const uint8_t *dh, *p, *end = buf + len;
|
||||||
|
size_t ihl, rlen = sizeof(r);
|
||||||
|
if ((buf[0] & 1) == 0) return; // Bcast
|
||||||
|
if (len < 34 || buf[12] != 8 || buf[13] != 0 || buf[23] != 17) return; // UDP
|
||||||
|
ihl = (size_t) (buf[14] & 15) * 4, dh = buf + 14 + ihl + 8; // DHCP
|
||||||
|
if (dh + 240 > end || buf[14 + ihl + 2] != 0 || buf[14 + ihl + 3] != 67) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (p = dh + 240; p + 2 <= end && p[0] != 255; p += p[0] ? p[1] + 2 : 1) {
|
||||||
|
if (p[0] == 53 && p[1] == 1 && p + 3 <= end) type = p[2];
|
||||||
|
}
|
||||||
|
if (type != 1 && type != 3) return; // DISCOVER, REQUEST
|
||||||
|
memset(r, 0, sizeof(r));
|
||||||
|
memset(r, 0xff, 6), memcpy(r + 6, sim->dhcp.lif.mac, 6),
|
||||||
|
r[12] = 8; // Ethernet
|
||||||
|
r[14] = 0x45, r[16] = (uint8_t) ((rlen - 14) >> 8); // IPv4
|
||||||
|
r[17] = (uint8_t) (rlen - 14), r[22] = 64, r[23] = 17;
|
||||||
|
r[26] = 10, r[29] = 1, memset(r + 30, 0xff, 4); // 10.0.0.1 -> bcast
|
||||||
|
r[24] = (uint8_t) (ip_csum(r + 14, 20) >> 8);
|
||||||
|
r[25] = (uint8_t) ip_csum(r + 14, 20);
|
||||||
|
r[35] = 67, r[37] = 68; // UDP, csum 0: none
|
||||||
|
r[38] = (uint8_t) ((rlen - 34) >> 8), r[39] = (uint8_t) (rlen - 34);
|
||||||
|
r[42] = 2, r[43] = 1, r[44] = 6; // DHCP: reply, Ethernet
|
||||||
|
memcpy(r + 46, dh + 4, 4); // xid
|
||||||
|
memcpy(r + 52, dh + 10, 2); // flags
|
||||||
|
node_ip(dh + 28, r + 58); // yiaddr
|
||||||
|
r[62] = 10, r[65] = 1; // siaddr
|
||||||
|
memcpy(r + 70, dh + 28, 16); // chaddr
|
||||||
|
r[278] = 99, r[279] = 130, r[280] = 83, r[281] = 99; // Magic
|
||||||
|
memcpy(r + 282, opts, sizeof(opts));
|
||||||
|
r[284] = type == 1 ? 2 : 5; // OFFER, ACK
|
||||||
|
sim->dhcp.lif.driver->tx(r, rlen, &sim->dhcp.lif);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DHCP server's work: take the frames from its queue
|
||||||
|
static void dhcp_poll(struct simulator *sim) {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
while ((len = mg_queue_next(&sim->dhcp.lif.recv_queue, &buf)) > 0) {
|
||||||
|
dhcp_rx(sim, (const uint8_t *) buf, len);
|
||||||
|
mg_queue_del(&sim->dhcp.lif.recv_queue, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The request on the way finished: ok, or failed
|
||||||
|
static void tester_done(struct lan_tester *t, uint64_t now, bool ok) {
|
||||||
|
double ms = (double) (now - t->start) / 1000;
|
||||||
|
if (ok) {
|
||||||
|
t->ok++, t->sum += ms;
|
||||||
|
if (t->ok == 1 || ms < t->min) t->min = ms;
|
||||||
|
if (ms > t->max) t->max = ms;
|
||||||
|
} else {
|
||||||
|
t->failed++;
|
||||||
|
}
|
||||||
|
if (t->c != NULL) t->c->is_closing = 1, t->c->fn_data = NULL;
|
||||||
|
t->c = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP client: GET /, the reply ends the request
|
||||||
|
static void tester_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
struct lan_tester *t = (struct lan_tester *) c->fn_data;
|
||||||
|
if (t == NULL) return; // Done with
|
||||||
|
if (ev == MG_EV_CONNECT) {
|
||||||
|
mg_printf(c, "GET / HTTP/1.0\r\n\r\n");
|
||||||
|
} else if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
tester_done(t, s_sim.now, true);
|
||||||
|
} else if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) {
|
||||||
|
tester_done(t, s_sim.now, false);
|
||||||
|
}
|
||||||
|
(void) ev_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test station's work: Mongoose, then requests, one at a time, once the mesh
|
||||||
|
// had time to settle. A node without an IP address fails at once
|
||||||
|
static void tester_poll(struct simulator *sim) {
|
||||||
|
struct radio_node *n;
|
||||||
|
char url[64];
|
||||||
|
mg_mgr_poll(&sim->tester.mgr, 0);
|
||||||
|
if (sim->tester.c != NULL && sim->now - sim->tester.start >
|
||||||
|
(uint64_t) sim->request_timeout_ms * 1000) {
|
||||||
|
tester_done(&sim->tester, sim->now, false);
|
||||||
|
}
|
||||||
|
if (sim->now < sim->tester.wait || sim->tester.c != NULL ||
|
||||||
|
sim->tester.next == NULL)
|
||||||
|
return;
|
||||||
|
n = sim->tester.next;
|
||||||
|
sim->tester.next = n->next, sim->tester.start = sim->now;
|
||||||
|
if (n->rif.ip == 0) {
|
||||||
|
tester_done(&sim->tester, sim->now, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mg_snprintf(url, sizeof(url), "http://%M/", mg_print_ip4, &n->rif.ip);
|
||||||
|
if ((sim->tester.c = mg_http_connect(&sim->tester.mgr, url, tester_fn,
|
||||||
|
&sim->tester)) == NULL) {
|
||||||
|
tester_done(&sim->tester, sim->now, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up sim: defaults, LAN stations
|
||||||
|
static void init(struct simulator *sim) {
|
||||||
|
static const uint8_t dmac[6] = {2, 1, 0, 0, 0, 0}, // DHCP server MAC
|
||||||
|
tmac[6] = {2, 2, 0, 0, 0, 0}; // Test station MAC
|
||||||
|
memset(sim, 0, sizeof(*sim));
|
||||||
|
sim->area.x = sim->area.y = 100;
|
||||||
|
sim->duration = 600 * 1000000ULL, sim->tester.wait = 60 * 1000000ULL;
|
||||||
|
sim->request_timeout_ms = 10000;
|
||||||
|
memcpy(sim->dhcp.lif.mac, dmac, 6), sim->dhcp.lif.driver = &s_lan_driver;
|
||||||
|
mg_queue_init(&sim->dhcp.lif.recv_queue, (char *) calloc(1, LAN_QUEUE_SIZE),
|
||||||
|
LAN_QUEUE_SIZE);
|
||||||
|
memcpy(sim->tester.lif.mac, tmac, 6);
|
||||||
|
sim->tester.lif.ip = MG_IPV4(10, 0, 0, 2),
|
||||||
|
sim->tester.lif.mask = MG_IPV4(255, 0, 0, 0);
|
||||||
|
sim->tester.lif.driver = &s_lan_driver,
|
||||||
|
sim->tester.lif.enable_mac_check = true;
|
||||||
|
sim->tester.lif.recv_queue.size = 128 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Radio driver, all radio interfaces share it. An ideal radio for now:
|
||||||
|
// whole Ethernet frames, no range, no loss, no airtime. It works like the
|
||||||
|
// LAN, see lan_tx(): a frame goes into the receive queues of all other
|
||||||
|
// radio interfaces if broadcast, else of the one with the destination MAC.
|
||||||
|
// Bridges get every frame: some are for the LAN. Who forwards what, and
|
||||||
|
// loops with several bridges, are for mg_mesh
|
||||||
|
static size_t radio_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
const uint8_t *dst = (const uint8_t *) buf;
|
||||||
|
bool all = dst[0] & 1; // Broadcast, multicast
|
||||||
|
struct bridge *b;
|
||||||
|
struct radio_node *n;
|
||||||
|
for (b = s_sim.bridges; b != NULL; b = b->next) {
|
||||||
|
lan_put(&b->rif, ifp, buf, len);
|
||||||
|
}
|
||||||
|
for (n = s_sim.nodes; n != NULL; n = n->next) {
|
||||||
|
if (all || memcmp(dst, n->rif.mac, 6) == 0) {
|
||||||
|
lan_put(&n->rif, ifp, buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool radio_init(struct mg_tcpip_if *ifp) {
|
||||||
|
(void) ifp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool radio_link(struct mg_tcpip_if *ifp, bool s1) {
|
||||||
|
(void) ifp, (void) s1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mg_tcpip_driver s_radio_driver = {radio_init, radio_tx, NULL,
|
||||||
|
radio_link};
|
||||||
|
|
||||||
|
// A node's HTTP server: the test station requests GET /
|
||||||
|
static void node_http_ev_handler(struct mg_connection *c, int ev,
|
||||||
|
void *ev_data) {
|
||||||
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
mg_http_reply(c, 200, "", "Hi from Mongoose, my MAC is %M\n", mg_print_mac,
|
||||||
|
c->fn_data);
|
||||||
|
}
|
||||||
|
(void) ev_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start each node's Mongoose: its radio interface, no IP address, so DHCP,
|
||||||
|
// and an HTTP server
|
||||||
|
static void nodes_init(struct simulator *sim) {
|
||||||
|
struct radio_node *n;
|
||||||
|
for (n = sim->nodes; n != NULL; n = n->next) {
|
||||||
|
n->rif.driver = &s_radio_driver;
|
||||||
|
n->rif.recv_queue.size = LAN_QUEUE_SIZE;
|
||||||
|
mg_mgr_init(&n->mgr);
|
||||||
|
mg_tcpip_init(&n->mgr, &n->rif);
|
||||||
|
mg_http_listen(&n->mgr, "http://0.0.0.0:80", node_http_ev_handler,
|
||||||
|
n->rif.mac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start each bridge: its LAN and radio drivers. No Mongoose, no IP
|
||||||
|
static void bridges_init(struct simulator *sim) {
|
||||||
|
struct bridge *b;
|
||||||
|
for (b = sim->bridges; b != NULL; b = b->next) {
|
||||||
|
b->lif.driver = &s_lan_driver, b->rif.driver = &s_radio_driver;
|
||||||
|
mg_queue_init(&b->lif.recv_queue, (char *) calloc(1, LAN_QUEUE_SIZE),
|
||||||
|
LAN_QUEUE_SIZE);
|
||||||
|
mg_queue_init(&b->rif.recv_queue, (char *) calloc(1, LAN_QUEUE_SIZE),
|
||||||
|
LAN_QUEUE_SIZE);
|
||||||
|
b->lif.driver->init(&b->lif), b->rif.driver->init(&b->rif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the frames waiting in the receive queue of interface from out of
|
||||||
|
// interface to
|
||||||
|
static void bridge_forward(struct mg_tcpip_if *from, struct mg_tcpip_if *to) {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
while ((len = mg_queue_next(&from->recv_queue, &buf)) > 0) {
|
||||||
|
// MG_DEBUG(("Bridging ETH frame, size %zu", len));
|
||||||
|
to->driver->tx(buf, len, to);
|
||||||
|
mg_queue_del(&from->recv_queue, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bridges' work: forward every frame, LAN to radio and radio to LAN. No
|
||||||
|
// filtering yet
|
||||||
|
static void bridges_poll(struct simulator *sim) {
|
||||||
|
struct bridge *b;
|
||||||
|
for (b = sim->bridges; b != NULL; b = b->next) {
|
||||||
|
bridge_forward(&b->lif, &b->rif);
|
||||||
|
bridge_forward(&b->rif, &b->lif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll each node's Mongoose
|
||||||
|
static void nodes_poll(struct simulator *sim) {
|
||||||
|
struct radio_node *n;
|
||||||
|
for (n = sim->nodes; n != NULL; n = n->next) mg_mgr_poll(&n->mgr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
clock_t started; // CPU time, to show how fast the simulation runs
|
||||||
|
int i;
|
||||||
|
|
||||||
|
init(&s_sim);
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
usage(&s_sim, argv[0]);
|
||||||
|
} else if (strcmp(argv[i], "-area") == 0) {
|
||||||
|
if (!parse_pos(argv[++i], &s_sim.area)) usage(&s_sim, argv[0]);
|
||||||
|
} else if (strcmp(argv[i], "-bridge") == 0) {
|
||||||
|
if (!add_bridge(&s_sim, argv[++i])) usage(&s_sim, argv[0]);
|
||||||
|
} else if (strcmp(argv[i], "-node") == 0) {
|
||||||
|
if (!add_node(&s_sim, argv[++i])) usage(&s_sim, argv[0]);
|
||||||
|
} else if (strcmp(argv[i], "-t") == 0) {
|
||||||
|
s_sim.duration = (uint64_t) (atof(argv[++i]) * 1e6);
|
||||||
|
} else if (strcmp(argv[i], "-w") == 0) {
|
||||||
|
s_sim.tester.wait = (uint64_t) (atof(argv[++i]) * 1e6);
|
||||||
|
} else {
|
||||||
|
usage(&s_sim, argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Area %gx%gx%g m, radio %s, mesh %s, bridges: %d, nodes: %d\n",
|
||||||
|
s_sim.area.x, s_sim.area.y, s_sim.area.z, mg_radio_802154.name,
|
||||||
|
mg_mesh_star.name, s_sim.num_bridges, s_sim.num_nodes);
|
||||||
|
check_area(&s_sim);
|
||||||
|
|
||||||
|
// mg_log_set(MG_LL_INFO);
|
||||||
|
|
||||||
|
mg_mgr_init(&s_sim.tester.mgr);
|
||||||
|
mg_tcpip_init(&s_sim.tester.mgr, &s_sim.tester.lif);
|
||||||
|
s_sim.tester.next = s_sim.nodes;
|
||||||
|
bridges_init(&s_sim);
|
||||||
|
nodes_init(&s_sim);
|
||||||
|
|
||||||
|
// Main loop: STEP_MICROSECONDS of virtual time per step
|
||||||
|
started = clock();
|
||||||
|
while (s_sim.now < s_sim.duration) {
|
||||||
|
dhcp_poll(&s_sim);
|
||||||
|
tester_poll(&s_sim);
|
||||||
|
bridges_poll(&s_sim);
|
||||||
|
nodes_poll(&s_sim);
|
||||||
|
s_sim.now += STEP_MICROSECONDS;
|
||||||
|
}
|
||||||
|
printf("Requests: %d ok, %d failed, of %d nodes", s_sim.tester.ok,
|
||||||
|
s_sim.tester.failed, s_sim.num_nodes);
|
||||||
|
if (s_sim.tester.ok > 0) {
|
||||||
|
printf("; ms: min %.1f, mean %.1f, max %.1f", s_sim.tester.min,
|
||||||
|
s_sim.tester.sum / s_sim.tester.ok, s_sim.tester.max);
|
||||||
|
}
|
||||||
|
printf("\nSimulated %g s in %.3f s\n", (double) s_sim.now / 1e6,
|
||||||
|
(double) (clock() - started) / CLOCKS_PER_SEC);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define MG_ENABLE_TCPIP 1
|
||||||
|
|
||||||
|
#include "l2_802154.h"
|
||||||
|
#include "l2_mesh.h"
|
||||||
|
#include "mongoose.h"
|
||||||
|
#include "star.h"
|
||||||
|
|
||||||
|
// A position in metres
|
||||||
|
struct pos {
|
||||||
|
double x, y, z; // Coordinates
|
||||||
|
};
|
||||||
|
|
||||||
|
// A bridge: forwards frames between the LAN and the radio, no IP stack
|
||||||
|
struct bridge {
|
||||||
|
struct bridge *next; // Next in simulator::bridges
|
||||||
|
struct pos pos; // Where it is
|
||||||
|
struct mg_tcpip_if lif; // LAN side: LAN driver, gets every LAN frame
|
||||||
|
struct mg_tcpip_if rif; // Radio side: radio driver, gets every frame
|
||||||
|
};
|
||||||
|
|
||||||
|
// A radio node: Mongoose on a radio interface
|
||||||
|
struct radio_node {
|
||||||
|
struct radio_node *next; // Next in simulator::nodes
|
||||||
|
struct pos pos; // Where it is
|
||||||
|
struct mg_mgr mgr; // Mongoose event manager
|
||||||
|
struct mg_tcpip_if rif; // Radio interface. rif.ip from DHCP, 0: none yet
|
||||||
|
};
|
||||||
|
|
||||||
|
struct simulator;
|
||||||
|
|
||||||
|
// LAN station: a DHCP server, 10.0.0.1. Gives each client an address made
|
||||||
|
// from its MAC, see node_ip()
|
||||||
|
struct lan_dhcp_server {
|
||||||
|
struct mg_tcpip_if lif; // Its LAN interface, no Mongoose on it
|
||||||
|
};
|
||||||
|
|
||||||
|
// LAN station: the test station, 10.0.0.2, a Mongoose instance. Waits for
|
||||||
|
// the mesh to settle, then sends GET / to each node
|
||||||
|
struct lan_tester {
|
||||||
|
struct mg_mgr mgr; // Its Mongoose
|
||||||
|
struct mg_tcpip_if lif; // Its LAN interface
|
||||||
|
uint64_t wait; // Mesh settle time: start requests then, us
|
||||||
|
struct radio_node *next; // Node to request next, NULL: all done
|
||||||
|
struct mg_connection *c; // Request on the way, NULL: none
|
||||||
|
uint64_t start; // When it started, us
|
||||||
|
int ok, failed; // Requests done, by result
|
||||||
|
double min, max, sum; // Times of successful requests, ms
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simulator state
|
||||||
|
struct simulator {
|
||||||
|
struct pos area; // A box: all positions inside
|
||||||
|
struct bridge *bridges; // Bridges, a list
|
||||||
|
struct radio_node *nodes; // Radio nodes, a list
|
||||||
|
int num_bridges, num_nodes; // How many
|
||||||
|
uint64_t now; // Virtual time, us since the start
|
||||||
|
uint64_t duration; // Run this long, us
|
||||||
|
uint32_t last_mac; // Low 3 bytes of the last MAC given
|
||||||
|
int request_timeout_ms; // A request that takes longer failed
|
||||||
|
struct lan_dhcp_server dhcp; // DHCP server on the LAN
|
||||||
|
struct lan_tester tester; // Test station on the LAN
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "star.h"
|
||||||
|
|
||||||
|
const struct mg_mesh mg_mesh_star = {"star"};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// Star mesh: one hop, nodes talk to a bridge
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "l2_mesh.h"
|
||||||
|
|
||||||
|
extern const struct mg_mesh mg_mesh_star;
|
||||||
@@ -80,6 +80,10 @@ static void test_statechange(void) {
|
|||||||
onstatechange(&iface);
|
onstatechange(&iface);
|
||||||
ASSERT(executed == true);
|
ASSERT(executed == true);
|
||||||
executed = false;
|
executed = false;
|
||||||
|
iface.enable_dhcp_client = true; // DHCP REQUEST unanswered: start over
|
||||||
|
iface.state = MG_TCPIP_STATE_REQ, iface.lease_expire = 0;
|
||||||
|
mg_ip_poll(&iface, true);
|
||||||
|
ASSERT(iface.state == MG_TCPIP_STATE_UP);
|
||||||
}
|
}
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
static void mif6_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
|
static void mif6_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
|
||||||
|
|||||||
+6
-7
@@ -176,7 +176,6 @@ static void fcb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len,
|
snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len,
|
||||||
hm->message.buf);
|
hm->message.buf);
|
||||||
fd->code = atoi(hm->uri.buf);
|
fd->code = atoi(hm->uri.buf);
|
||||||
fd->closed = 1;
|
|
||||||
c->is_closing = 1;
|
c->is_closing = 1;
|
||||||
MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len,
|
MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len,
|
||||||
hm->message.buf));
|
hm->message.buf));
|
||||||
@@ -214,7 +213,8 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
|
|||||||
// given enough traffic, the timer expires before we get a
|
// given enough traffic, the timer expires before we get a
|
||||||
// chance to see the response
|
// chance to see the response
|
||||||
}
|
}
|
||||||
if (!fd.closed) c->is_closing = 1;
|
if (!fd.closed) // failed, force closure and conn removal, and loop end
|
||||||
|
((struct connstate *) (c + 1))->is_closing = 1;
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
return fd.code;
|
return fd.code;
|
||||||
}
|
}
|
||||||
@@ -480,8 +480,7 @@ static void test_http_server(struct mg_mgr *mgr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_tls(struct mg_mgr *mgr) {
|
static void test_tls(struct mg_mgr *mgr) {
|
||||||
#if MG_TLS
|
#if MG_TLS && 0 // TODO(cpq): restore multi-record TLS test
|
||||||
char *url;
|
|
||||||
char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile
|
char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile
|
||||||
mg_mem_files = mg_packed_files; // Use generated packed filesystem
|
mg_mem_files = mg_packed_files; // Use generated packed filesystem
|
||||||
struct mg_str data = mg_unpacked("/Makefile");
|
struct mg_str data = mg_unpacked("/Makefile");
|
||||||
@@ -496,7 +495,8 @@ static void test_tls(struct mg_mgr *mgr) {
|
|||||||
// - 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) {
|
||||||
url = mg_mprintf("https://%s:8443", host_ip);
|
char url[256];
|
||||||
|
mg_snrintf(url, sizeof(url), "https://%s:8443", host_ip);
|
||||||
ASSERT(system("tls_multirec/server -d tls_multirec &") == 0);
|
ASSERT(system("tls_multirec/server -d tls_multirec &") == 0);
|
||||||
sleep(1);
|
sleep(1);
|
||||||
// fetch() needs to loop enough times in order to process all TLS records;
|
// fetch() needs to loop enough times in order to process all TLS records;
|
||||||
@@ -504,7 +504,6 @@ static void test_tls(struct mg_mgr *mgr) {
|
|||||||
ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200);
|
ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200);
|
||||||
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);
|
||||||
free(url);
|
|
||||||
} else {
|
} else {
|
||||||
printf("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n");
|
printf("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n");
|
||||||
}
|
}
|
||||||
@@ -512,7 +511,6 @@ static void test_tls(struct mg_mgr *mgr) {
|
|||||||
printf("SKIPPED TLS MULTIPLE RECORDS TEST, not a known TLS 1.3 stack\n");
|
printf("SKIPPED TLS MULTIPLE RECORDS TEST, not a known TLS 1.3 stack\n");
|
||||||
(void) cmpbody("", "");
|
(void) cmpbody("", "");
|
||||||
(void) mgr;
|
(void) mgr;
|
||||||
(void) url; // these three: NO SIMPLE TLS TEST
|
|
||||||
(void) buf;
|
(void) buf;
|
||||||
(void) data;
|
(void) data;
|
||||||
#endif
|
#endif
|
||||||
@@ -593,6 +591,7 @@ bool mip_x_test(struct mg_mgr *mgr) {
|
|||||||
|
|
||||||
// Clear
|
// Clear
|
||||||
s_error = false;
|
s_error = false;
|
||||||
|
mg_mgr_poll(mgr, 0); // prevent mDash from trashing mg_mgr_free()
|
||||||
mg_mgr_free(mgr);
|
mg_mgr_free(mgr);
|
||||||
ASSERT(mgr->conns == NULL); // Deconstruction OK
|
ASSERT(mgr->conns == NULL); // Deconstruction OK
|
||||||
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true");
|
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true");
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
CFLAGS = -g -I. # This is a known version, no need to check
|
|
||||||
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_IPV6=1
|
|
||||||
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
|
|
||||||
|
|
||||||
server: main.c patched_mongoose.c mongoose.h Makefile
|
|
||||||
$(CC) main.c patched_mongoose.c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf server *.exe *.o *.dSYM
|
|
||||||
Loaded 100 of 714 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user