mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-25 12:04:47 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02bdbb9ed6 | ||
|
|
84553d06a0 | ||
|
|
de7cd48a79 | ||
|
|
5df2445bc9 | ||
|
|
90890e4c63 | ||
|
|
a6deed5868 | ||
|
|
5cdb25f05a | ||
|
|
d8764746a4 | ||
|
|
fa4a2563e5 | ||
|
|
9ccfc02a86 | ||
|
|
f58f29b9a0 | ||
|
|
282ae0d968 | ||
|
|
735038c98c | ||
|
|
2696c1ff94 | ||
|
|
9f16806a22 | ||
|
|
8d1b9047b6 | ||
|
|
2988bc9df3 | ||
|
|
5e733a1461 | ||
|
|
34813493a4 | ||
|
|
f220223947 | ||
|
|
758e301f0c | ||
|
|
7fa830fc0c | ||
|
|
fc699a0b0b | ||
|
|
4e3e7ac4b2 | ||
|
|
2838c85342 | ||
|
|
bc17f1667b | ||
|
|
309cb1006f | ||
|
|
501c5a4bcd | ||
|
|
765bb237b7 | ||
|
|
b0f5e9cec4 | ||
|
|
125b79bd70 | ||
|
|
7821fb9ab4 | ||
|
|
03be16803c | ||
|
|
3c11521446 | ||
|
|
4faa3d5bf1 | ||
|
|
bfdc14ce0a | ||
|
|
ac9ebdfb1a | ||
|
|
85d77ec3ac | ||
|
|
9b7d318df5 | ||
|
|
6cbd810206 | ||
|
|
771f5f2ed9 | ||
|
|
6b8d8b5caa | ||
|
|
ba942dbd7a | ||
|
|
2db7119080 | ||
|
|
6a3599682b | ||
|
|
77e0faf020 | ||
|
|
08e01cb312 | ||
|
|
d8fa55e1d6 | ||
|
|
61638caeae | ||
|
|
f78cb1b02f | ||
|
|
f8e785f56b | ||
|
|
e473f2189c | ||
|
|
66eac28c20 | ||
|
|
cdd957572e | ||
|
|
af7eaaa9b1 | ||
|
|
8d7bbda48d | ||
|
|
17a25373db | ||
|
|
b86f255407 | ||
|
|
cb57182ee0 | ||
|
|
72ff0dfb07 | ||
|
|
dc52d8b1dc | ||
|
|
e2e2db8900 | ||
|
|
b6a353cae9 | ||
|
|
cc7c6d2d18 | ||
|
|
7a2a3fc136 | ||
|
|
2ec27994f4 | ||
|
|
bc69625a5e | ||
|
|
a8b4ae95db | ||
|
|
f52e4f4bb0 | ||
|
|
0aa25dbd18 | ||
|
|
72a0787161 | ||
|
|
37234ad26f | ||
|
|
96050c2587 | ||
|
|
09254c9e64 | ||
|
|
a43599c081 | ||
|
|
f391c4eea0 | ||
|
|
6709c028aa | ||
|
|
f84598f66a | ||
|
|
02625d3bb6 | ||
|
|
957b9a991c | ||
|
|
875c0fcd4f | ||
|
|
ad269fa4d5 | ||
|
|
9448625662 | ||
|
|
ba2abbc444 | ||
|
|
a865890a09 | ||
|
|
ad2ffa8e45 | ||
|
|
b424eb79cf | ||
|
|
ac8fe85478 | ||
|
|
38b6b13547 | ||
|
|
9bc4d9741e | ||
|
|
cee80a8391 | ||
|
|
9d3926a25a | ||
|
|
28cd3439dd | ||
|
|
6fcf32f203 | ||
|
|
9879f4d715 | ||
|
|
8fb3aa26cd | ||
|
|
dfbbe5fa8b | ||
|
|
751f14742d | ||
|
|
8bb65c6492 | ||
|
|
f8816d67c3 | ||
|
|
d897b32c64 | ||
|
|
f055e45755 | ||
|
|
5bf953cb4a | ||
|
|
8e7f5ab00e | ||
|
|
e5e13ff156 | ||
|
|
fdcefb087e | ||
|
|
699067ff58 | ||
|
|
ed19d5290e | ||
|
|
848ef126a3 | ||
|
|
80670e9ebc | ||
|
|
b607a4209a | ||
|
|
b3ab9c618e | ||
|
|
8c0b6e335e | ||
|
|
d8e629a048 |
No files matched your search
@@ -69,6 +69,7 @@ jobs:
|
|||||||
- name: Run Claude full codebase security scan
|
- name: Run Claude full codebase security scan
|
||||||
id: claude_scan
|
id: claude_scan
|
||||||
uses: anthropics/claude-code-action@v1
|
uses: anthropics/claude-code-action@v1
|
||||||
|
timeout-minutes: 30
|
||||||
with:
|
with:
|
||||||
anthropic_api_key: ${{ secrets.CLAUDE_API_KEY }}
|
anthropic_api_key: ${{ secrets.CLAUDE_API_KEY }}
|
||||||
|
|
||||||
@@ -94,9 +95,8 @@ jobs:
|
|||||||
Do not create GitHub issues.
|
Do not create GitHub issues.
|
||||||
|
|
||||||
claude_args: |
|
claude_args: |
|
||||||
--max-turns 20
|
|
||||||
--allowedTools "Read,Glob,Grep,Bash(git:*),Bash(find:*),Bash(grep:*),Bash(sed:*),Bash(cat:*)"
|
--allowedTools "Read,Glob,Grep,Bash(git:*),Bash(find:*),Bash(grep:*),Bash(sed:*),Bash(cat:*)"
|
||||||
--model claude-opus-4-8
|
--model claude-opus-5
|
||||||
|
|
||||||
- name: Prepare private security scan outputs
|
- name: Prepare private security scan outputs
|
||||||
if: always() && steps.claude_scan.outputs.execution_file != ''
|
if: always() && steps.claude_scan.outputs.execution_file != ''
|
||||||
|
|||||||
@@ -111,4 +111,4 @@ jobs:
|
|||||||
|
|
||||||
claude_args: |
|
claude_args: |
|
||||||
--allowedTools "Read,Bash(cat:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr comment:*)"
|
--allowedTools "Read,Bash(cat:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr comment:*)"
|
||||||
--model claude-opus-4-8
|
--model claude-opus-5
|
||||||
@@ -12,13 +12,99 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target: [fuzz, fuzz_tls]
|
ipv6: [0, 1] # fuzz code always feeds IPv6, so this fuzzes ignoring while going parallel in sync
|
||||||
name: ${{ matrix.target }}
|
name: fuzz IPV6=${{ matrix.ipv6 }}
|
||||||
|
env:
|
||||||
|
TFLAGS: -DMG_ENABLE_IPV6=${{ matrix.ipv6 }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: make -C test ${{ matrix.target }} ARGS="-max_total_time=14400"
|
- name: Restore Combined Fuzzing Corpus
|
||||||
|
uses: dawidd6/action-download-artifact@v21
|
||||||
|
with:
|
||||||
|
name: combined-fuzz-corpus
|
||||||
|
path: test/corpus_dir
|
||||||
|
branch: ${{ github.base_ref || github.ref_name }}
|
||||||
|
search_artifacts: true
|
||||||
|
if_no_artifact_found: warn
|
||||||
|
- name: Run Matrix Fuzzers
|
||||||
|
run: mkdir -p test/corpus_dir && make -C test fuzz ARGS="corpus_dir -seed=${{ github.run_id }} -max_total_time=14400"
|
||||||
|
# Keep failures split by matrix leg so the offending input is easy to find.
|
||||||
|
- name: Upload Fuzz Failure Artifacts
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: fuzz-failure-ipv6-${{ matrix.ipv6 }}
|
||||||
|
path: |
|
||||||
|
test/corpus_dir
|
||||||
|
test/crash-*
|
||||||
|
test/leak-*
|
||||||
|
test/oom-*
|
||||||
|
test/timeout-*
|
||||||
|
if-no-files-found: ignore
|
||||||
|
retention-days: 14
|
||||||
|
# v4 artifacts are immutable, so each parallel leg uploads its own corpus.
|
||||||
|
- name: Upload Corpus Shard
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: fuzz-corpus-ipv6-${{ matrix.ipv6 }}
|
||||||
|
path: test/corpus_dir
|
||||||
|
retention-days: 14
|
||||||
|
|
||||||
# as we're not getting access to test units causing a problem, convert from the log:
|
merge_fuzz_corpus:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: fuzz
|
||||||
|
name: merge fuzz corpus
|
||||||
|
steps:
|
||||||
|
# Merge both parallel fuzzing corpuses into the shared seed for next run.
|
||||||
|
- name: Merge Corpus Shards
|
||||||
|
uses: actions/upload-artifact/merge@v7
|
||||||
|
with:
|
||||||
|
name: combined-fuzz-corpus
|
||||||
|
pattern: fuzz-corpus-ipv6-*
|
||||||
|
delete-merged: true
|
||||||
|
retention-days: 14
|
||||||
|
# see NOTE
|
||||||
|
|
||||||
|
fuzz_tls:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: fuzz_tls
|
||||||
|
env:
|
||||||
|
TFLAGS: -DMG_ENABLE_IPV6=0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- name: Restore Fuzzing Corpus
|
||||||
|
uses: actions/cache/restore@v6
|
||||||
|
with:
|
||||||
|
path: test/corpus_dir
|
||||||
|
key: fuzz-corpus-${{ github.head_ref || github.ref_name }}-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
fuzz-corpus-${{ github.head_ref || github.ref_name }}-
|
||||||
|
fuzz-corpus-main-
|
||||||
|
fuzz-corpus-
|
||||||
|
- name: Run Fuzzer
|
||||||
|
run: mkdir -p test/corpus_dir && make -C test fuzz_tls ARGS="corpus_dir -seed=${{ github.run_id }} -max_total_time=14400"
|
||||||
|
- name: Upload Fuzz TLS Failure Artifacts
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: fuzz-tls-failure-artifacts
|
||||||
|
path: |
|
||||||
|
test/corpus_dir
|
||||||
|
test/crash-*
|
||||||
|
test/leak-*
|
||||||
|
test/oom-*
|
||||||
|
test/timeout-*
|
||||||
|
if-no-files-found: ignore
|
||||||
|
retention-days: 14
|
||||||
|
- name: Save Updated Corpus to Cache
|
||||||
|
uses: actions/cache/save@v6
|
||||||
|
with:
|
||||||
|
path: test/corpus_dir
|
||||||
|
key: fuzz-corpus-${{ github.head_ref || github.ref_name }}-${{ github.run_id }}
|
||||||
|
|
||||||
|
# NOTE
|
||||||
|
# In case we can't get access to test units causing a problem, convert from the log:
|
||||||
# base64 -d > dataofdeath
|
# base64 -d > dataofdeath
|
||||||
# then paste the base64 data, enter, Ctrl-D; maybe check with hexdump -C dataofdeath
|
# then paste the base64 data, enter, Ctrl-D; maybe check with hexdump -C dataofdeath
|
||||||
+368
-83
@@ -1,7 +1,7 @@
|
|||||||
name: Full build
|
name: Full build
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 22 * * *' # run at 10 PM UTC
|
- cron: '17 22 * * *' # run at 10:17 PM UTC
|
||||||
# Allow manual runs
|
# Allow manual runs
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
env:
|
env:
|
||||||
@@ -51,6 +51,25 @@ jobs:
|
|||||||
cat log
|
cat log
|
||||||
test/health.awk < log > json
|
test/health.awk < log > json
|
||||||
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/linux_${{ matrix.target }}_${{ matrix.cc }}_${{ matrix.ssl }}_${{ matrix.select }}_$(date +"%Y%m%d").json"
|
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/linux_${{ matrix.target }}_${{ matrix.cc }}_${{ matrix.ssl }}_${{ matrix.select }}_$(date +"%Y%m%d").json"
|
||||||
|
|
||||||
|
c89:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [diabish, ppc_diabish]
|
||||||
|
ssl: ["", BUILTIN]
|
||||||
|
name: C89 ${{ matrix.target }} SSL=${{ matrix.ssl }}
|
||||||
|
env:
|
||||||
|
SSL: ${{ matrix.ssl }}
|
||||||
|
TFLAGS: -DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=0 -DMG_ENABLE_CHACHA20=0 -DMQTT_LOCALHOST -DNO_ZU_TEST
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: | # GH runners seem to overflow the 32-bit FS for the QEMU PPC (works for me in RHEL9)
|
||||||
|
if [[ "${{ matrix.target }}" = ppc_* ]]; then export TFLAGS="$TFLAGS -D_FILE_OFFSET_BITS=64"; fi
|
||||||
|
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 ${{ matrix.target }}
|
||||||
|
|
||||||
mip:
|
mip:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -59,10 +78,6 @@ jobs:
|
|||||||
cc: [gcc, clang, g++, clang++]
|
cc: [gcc, clang, g++, clang++]
|
||||||
target: [mip_test, mip_tap_test]
|
target: [mip_test, mip_tap_test]
|
||||||
ssl: ["", BUILTIN, MBEDTLS, OPENSSL, WOLFSSL]
|
ssl: ["", BUILTIN, MBEDTLS, OPENSSL, WOLFSSL]
|
||||||
# #3226: built-in TCP is currently not working with WolfSSL (builds fine)
|
|
||||||
exclude:
|
|
||||||
- ssl: WOLFSSL
|
|
||||||
target: mip_tap_test
|
|
||||||
name: ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}
|
name: ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.cc }}
|
CC: ${{ matrix.cc }}
|
||||||
@@ -75,25 +90,222 @@ jobs:
|
|||||||
- uses: webfactory/ssh-agent@v0.10.0
|
- uses: webfactory/ssh-agent@v0.10.0
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
||||||
|
# WolfSSL version (5.6.6) in current GH runner (Ubuntu 24.04) is broken for EC
|
||||||
|
# https://github.com/wolfSSL/wolfssl/pull/10065
|
||||||
|
- name: Build non-broken WolfSSL version
|
||||||
|
if: matrix.ssl == 'WOLFSSL'
|
||||||
|
run: |
|
||||||
|
sudo apt -y update
|
||||||
|
sudo apt -y install autoconf automake libtool
|
||||||
|
git clone --depth 1 --branch v5.9.1-stable https://github.com/wolfSSL/wolfssl
|
||||||
|
cd wolfssl
|
||||||
|
./autogen.sh
|
||||||
|
./configure --prefix="$GITHUB_WORKSPACE/wolfssl-install" --enable-all
|
||||||
|
make -j"$(nproc)"
|
||||||
|
make install
|
||||||
|
echo "WOLFSSL=$GITHUB_WORKSPACE/wolfssl-install" >> "$GITHUB_ENV"
|
||||||
|
echo "LD_LIBRARY_PATH=$GITHUB_WORKSPACE/wolfssl-install/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" >> "$GITHUB_ENV"
|
||||||
|
if [ "${{ matrix.cc }}" = "g++" ] || [ "${{ matrix.cc }}" = "clang++" ]; then
|
||||||
|
echo "TFLAGS=$TFLAGS -DWOLFSSL_USE_OPTIONS_H -Wno-pedantic" >> "$GITHUB_ENV"
|
||||||
|
else
|
||||||
|
echo "TFLAGS=$TFLAGS -DWOLFSSL_USE_OPTIONS_H" >> "$GITHUB_ENV"
|
||||||
|
fi
|
||||||
- run: if [ "${{ matrix.target }}" == "mip_tap_test" ]; then source ./test/setup_ga_network.sh ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh; fi && sudo apt -y update ; sudo apt -y install libmbedtls-dev libwolfssl-dev && make -C test ${{ matrix.target }} > log
|
- run: if [ "${{ matrix.target }}" == "mip_tap_test" ]; then source ./test/setup_ga_network.sh ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh; fi && sudo apt -y update ; sudo apt -y install libmbedtls-dev libwolfssl-dev && make -C test ${{ matrix.target }} > log
|
||||||
- if: success() || failure()
|
- if: success() || failure()
|
||||||
run: |
|
run: |
|
||||||
cat log
|
cat log
|
||||||
test/health.awk < log > json
|
test/health.awk < log > json
|
||||||
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/${{ matrix.target }}_${{ matrix.cc }}_${{ matrix.ssl }}_$(date +"%Y%m%d").json"
|
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/${{ matrix.target }}_${{ matrix.cc }}_${{ matrix.ssl }}_$(date +"%Y%m%d").json"
|
||||||
|
|
||||||
mip89:
|
mip89:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 1 # no parallel runs, to minimize MQTT errors
|
|
||||||
matrix:
|
matrix:
|
||||||
target: [mip_vc98]
|
target: [mip_vc98, mip_diabish, mip_ppc_diabish]
|
||||||
name: mip89 ${{ matrix.target }}
|
ipv6: [0, 1]
|
||||||
|
name: MIP C89 ${{ matrix.target }} IPV6=${{ matrix.ipv6 }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: make -C test ${{ matrix.target }} IPV6=1
|
- 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 && make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }}
|
||||||
|
mipbe:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [mip_s390, mip_port_s390, mip_ppc, mip_port_ppc, mip_mips, mip_port_mips]
|
||||||
|
ipv6: [0, 1]
|
||||||
|
ssl: ["", BUILTIN]
|
||||||
|
name: MIP be ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} SSL=${{ matrix.ssl }}
|
||||||
|
env:
|
||||||
|
TFLAGS: -DMQTT_HOST -DIPV6_NOROUTING
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- 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
|
||||||
|
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
|
||||||
|
|
||||||
|
l2:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cc: [gcc, clang, g++, clang++]
|
||||||
|
target: [eth_test, ppp_test]
|
||||||
|
ipv6: [0, 1]
|
||||||
|
name: L2 ${{ matrix.target }} CC=${{ matrix.cc }} IPV6=${{ matrix.ipv6 }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- uses: webfactory/ssh-agent@v0.10.0
|
||||||
|
with:
|
||||||
|
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
||||||
|
- run: make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} CC=${{ matrix.cc }} > log
|
||||||
|
- if: success() || failure()
|
||||||
|
run: |
|
||||||
|
cat log
|
||||||
|
test/health.awk < log > json
|
||||||
|
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/${{ matrix.target }}_${{ matrix.cc }}_${{ matrix.ipv6 }}_$(date +"%Y%m%d").json"
|
||||||
|
l289:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [eth_vc98, ppp_vc98]
|
||||||
|
ipv6: [0, 1]
|
||||||
|
name: L2 C89 ${{ matrix.target }} IPV6=${{ matrix.ipv6 }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }}
|
||||||
|
l2be:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [eth_s390, ppp_s390]
|
||||||
|
ipv6: [0, 1]
|
||||||
|
name: L2 be ${{ matrix.target }} IPV6=${{ matrix.ipv6 }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- 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 && make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }}
|
||||||
|
pppoe_vlan: # run tests, build tests performed by the l2 job may not catch runtime alignment and protocol issues, etc.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cc: [gcc, clang, g++, clang++]
|
||||||
|
ipv6: [0, 1]
|
||||||
|
name: PPPoE over VLAN CC=${{ matrix.cc }} IPV6=${{ matrix.ipv6 }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- uses: webfactory/ssh-agent@v0.10.0
|
||||||
|
with:
|
||||||
|
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
||||||
|
- run: |
|
||||||
|
source ./test/setup_ga_network.sh
|
||||||
|
make -C test pppoe_tap_test CC=${{ matrix.cc }} IPV6=${{ matrix.ipv6 }} > log
|
||||||
|
- if: success() || failure()
|
||||||
|
run: |
|
||||||
|
cat log
|
||||||
|
test/health.awk < log > json
|
||||||
|
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/pppoe-vlan_${{ matrix.cc }}_${{ matrix.ipv6 }}_$(date +"%Y%m%d").json"
|
||||||
|
|
||||||
|
atcmd:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cc: [gcc, clang, g++, clang++]
|
||||||
|
name: ATcmd CC=${{ matrix.cc }}
|
||||||
|
env:
|
||||||
|
IPV6: 0 # makes no sense, shorten code
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- uses: webfactory/ssh-agent@v0.10.0
|
||||||
|
with:
|
||||||
|
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
||||||
|
- run: make -C test atcmd_test CC=${{ matrix.cc }} > log
|
||||||
|
- if: success() || failure()
|
||||||
|
run: |
|
||||||
|
cat log
|
||||||
|
test/health.awk < log > json
|
||||||
|
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/ATcmd_${{ matrix.cc }}_$(date +"%Y%m%d").json"
|
||||||
|
atcmd89:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [atcmd_vc98]
|
||||||
|
name: ATcmd C89 ${{ matrix.target }}
|
||||||
|
env:
|
||||||
|
IPV6: 0 # makes no sense, shorten code
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: make -C test ${{ matrix.target }}
|
||||||
|
atcmdbe:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [atcmd_s390]
|
||||||
|
name: ATcmd be ${{ matrix.target }}
|
||||||
|
env:
|
||||||
|
IPV6: 0 # makes no sense, shorten code
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- 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 && make -C test ${{ matrix.target }}
|
||||||
|
|
||||||
|
bsd:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cc: [gcc, clang, g++, clang++]
|
||||||
|
name: bsd CC=${{ matrix.cc }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- uses: webfactory/ssh-agent@v0.10.0
|
||||||
|
with:
|
||||||
|
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
||||||
|
- run: |
|
||||||
|
source ./test/setup_ga_network.sh
|
||||||
|
make -C test/bsd shm_tap_bridge
|
||||||
|
test/bsd/shm_tap_bridge &
|
||||||
|
sleep 2
|
||||||
|
make -C test/bsd CC=${{ matrix.cc }} > log
|
||||||
|
- if: success() || failure()
|
||||||
|
run: |
|
||||||
|
cat log
|
||||||
|
test/health.awk < log > json
|
||||||
|
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/bsd_${{ matrix.cc }}_$(date +"%Y%m%d").json"
|
||||||
|
|
||||||
|
bsdbe:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [bsd_s390]
|
||||||
|
name: bsd be ${{ matrix.target }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- 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
|
||||||
|
source ./test/setup_ga_network.sh
|
||||||
|
make -C test port_tap_bridge
|
||||||
|
test/port_tap_bridge &
|
||||||
|
sleep 2
|
||||||
|
make -C test/bsd ${{ matrix.target }}
|
||||||
|
|
||||||
s390:
|
s390:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -156,8 +368,6 @@ jobs:
|
|||||||
exclude:
|
exclude:
|
||||||
- ssl: MBEDTLS
|
- ssl: MBEDTLS
|
||||||
select: -DMG_ENABLE_POLL=0
|
select: -DMG_ENABLE_POLL=0
|
||||||
- ssl: MBEDTLS # see issue#2694
|
|
||||||
select: -DMG_ENABLE_POLL=1
|
|
||||||
- ssl: OPENSSL
|
- ssl: OPENSSL
|
||||||
select: -DMG_ENABLE_POLL=0
|
select: -DMG_ENABLE_POLL=0
|
||||||
- ssl: WOLFSSL
|
- ssl: WOLFSSL
|
||||||
@@ -188,7 +398,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 1 # no parallel runs, to minimize MQTT errors
|
max-parallel: 1 # no parallel runs, to minimize MQTT errors
|
||||||
matrix:
|
matrix:
|
||||||
target: [vc98, vc17, vc22, mingw, mingw++]
|
target: [vc98, vc17, vc22, mingw, mingw++] # current mdash/mingw image compiler does not have support for %zu; test disabled below
|
||||||
ssl: ["", BUILTIN]
|
ssl: ["", BUILTIN]
|
||||||
select: [-DMG_ENABLE_POLL=0, -DMG_ENABLE_POLL=1]
|
select: [-DMG_ENABLE_POLL=0, -DMG_ENABLE_POLL=1]
|
||||||
exclude:
|
exclude:
|
||||||
@@ -197,14 +407,14 @@ jobs:
|
|||||||
name: windows ${{ matrix.target }} SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
|
name: windows ${{ matrix.target }} SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
|
||||||
env:
|
env:
|
||||||
SSL: ${{ matrix.ssl }}
|
SSL: ${{ matrix.ssl }}
|
||||||
TFLAGS: ${{ matrix.select }} -DMQTT_LOCALHOST -DNO_ABORT
|
TFLAGS: ${{ matrix.select }} -DMQTT_LOCALHOST -DNO_ABORT
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- uses: webfactory/ssh-agent@v0.10.0
|
- uses: webfactory/ssh-agent@v0.10.0
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
||||||
- run: ./test/setup_mqtt_server.sh && make -C test ${{ matrix.target }} > log
|
- run: if [ "${{ matrix.target }}" == "mingw" ]; then export TFLAGS="$TFLAGS -DNO_ZU_TEST"; fi && ./test/setup_mqtt_server.sh && make -C test ${{ matrix.target }} > log
|
||||||
- if: success() || failure()
|
- if: success() || failure()
|
||||||
run: |
|
run: |
|
||||||
cat log
|
cat log
|
||||||
@@ -318,41 +528,66 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
include:
|
||||||
- path: infineon/xmc47_relax-make-baremetal-builtin
|
- group: infineon-nxp
|
||||||
- path: infineon/xmc_plt2go_4400-make-baremetal-builtin
|
examples: |
|
||||||
- path: infineon/xmc72_evk-make-baremetal-builtin
|
infineon/xmc47_relax-make-baremetal-builtin
|
||||||
- path: nxp/frdm-mcxn947-make-baremetal-builtin
|
infineon/xmc_plt2go_4400-make-baremetal-builtin
|
||||||
- path: nxp/frdm-mcxn947-make-freertos-builtin
|
infineon/xmc72_evk-make-baremetal-builtin
|
||||||
- path: nxp/rt1020-evk-make-baremetal-builtin
|
nxp/frdm-mcxn947-make-baremetal-builtin
|
||||||
- path: nxp/rt1060-evk-make-baremetal-builtin
|
nxp/frdm-mcxn947-make-freertos-builtin
|
||||||
- path: nxp/rt1170-evk-make-baremetal-builtin
|
nxp/rt1020-evk-make-baremetal-builtin
|
||||||
- path: rp/pico-w
|
nxp/rt1060-evk-make-baremetal-builtin
|
||||||
- path: rp/w5500-evb-pico
|
nxp/rt1170-evk-make-baremetal-builtin
|
||||||
- path: rp/w55rp20-evb-pico
|
- group: rp-ti
|
||||||
- path: rp/pico-rndis
|
examples: |
|
||||||
- path: stm32/nucleo-f429zi/minimal
|
rp/pico-w
|
||||||
- path: stm32/nucleo-f746zg/minimal
|
rp/w5500-evb-pico
|
||||||
- path: stm32/nucleo-f746zg/minimal-pppoe
|
rp/w55rp20-evb-pico
|
||||||
- path: stm32/nucleo-f756zg/minimal
|
rp/pico-rndis
|
||||||
- path: stm32/nucleo-h563zi/minimal
|
ti/ek-tm4c1294xl-make-baremetal-builtin
|
||||||
- path: stm32/nucleo-h723zg/minimal
|
ti/ek-tm4c1294xl-make-freertos-builtin
|
||||||
- path: stm32/nucleo-h743zi/minimal
|
- group: stm32-1
|
||||||
- path: stm32/nucleo-n657x0-q/minimal
|
examples: |
|
||||||
- path: stm32/nucleo-u5a5zj-q/minimal
|
stm32/nucleo-f429zi/minimal
|
||||||
- path: stm32/portenta-h7/minimal
|
stm32/nucleo-f746zg/minimal
|
||||||
- path: stm32/stm32h573i-dk/minimal
|
stm32/nucleo-f746zg/minimal-pppoe
|
||||||
- path: stm32/stm32h747i-disco/minimal
|
stm32/nucleo-f756zg/minimal
|
||||||
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
|
stm32/nucleo-h563zi/minimal
|
||||||
- path: ti/ek-tm4c1294xl-make-freertos-builtin
|
stm32/nucleo-h723zg/minimal
|
||||||
name: ${{ matrix.example.path }}
|
- group: stm32-2
|
||||||
|
examples: |
|
||||||
|
stm32/nucleo-h743zi/minimal
|
||||||
|
stm32/nucleo-n657x0-q/minimal
|
||||||
|
stm32/nucleo-u5a5zj-q/minimal
|
||||||
|
stm32/portenta-h7/minimal
|
||||||
|
stm32/stm32h573i-dk/minimal
|
||||||
|
stm32/stm32h747i-disco/minimal
|
||||||
|
name: wizard ARM ${{ matrix.group }}
|
||||||
env:
|
env:
|
||||||
GO: 0
|
GO: 0
|
||||||
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 gcc-arm-none-eabi
|
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
- run: make -C tutorials/${{ matrix.example.path }} build
|
- run: |
|
||||||
|
failed=0
|
||||||
|
sep=
|
||||||
|
examples='${{ matrix.examples }}'
|
||||||
|
for x in $examples; do
|
||||||
|
echo "========== $x =========="
|
||||||
|
if make -C "tutorials/$x" build; then
|
||||||
|
ok=true
|
||||||
|
echo "SUCCESS: $x"
|
||||||
|
else
|
||||||
|
ok=false
|
||||||
|
failed=1
|
||||||
|
echo "FAILURE: $x"
|
||||||
|
fi
|
||||||
|
printf 'HEALTH_DASHBOARD\t%s"%s": %s\n' "$sep" "$x" "$ok"
|
||||||
|
sep=,
|
||||||
|
done
|
||||||
|
exit $failed
|
||||||
|
|
||||||
|
|
||||||
generic_examples:
|
generic_examples:
|
||||||
@@ -378,40 +613,63 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
include:
|
||||||
- path: mqtt/mqtt-client/microchip/same54-xpro
|
- group: nxp-1
|
||||||
- path: nxp/nxp-frdmk66f-freertos
|
examples: |
|
||||||
- path: nxp/nxp-lpcxpresso54s018m-freertos
|
nxp/nxp-frdmk66f-freertos
|
||||||
- path: nxp/nxp-mimxrt1020-freertos
|
nxp/nxp-lpcxpresso54s018m-freertos
|
||||||
- path: nxp/nxp-evkbimxrt1050-lwip-freertos
|
nxp/nxp-mimxrt1020-freertos
|
||||||
- path: nxp/nxp-evkmimxrt1020-lwip-freertos
|
nxp/nxp-evkbimxrt1050-lwip-freertos
|
||||||
- path: nxp/nxp-evkmimxrt1024-lwip-freertos
|
nxp/nxp-evkmimxrt1020-lwip-freertos
|
||||||
- path: nxp/nxp-evkmimxrt1060-lwip-freertos
|
nxp/nxp-evkmimxrt1024-lwip-freertos
|
||||||
- path: nxp/nxp-evkmimxrt1064-lwip-freertos
|
nxp/nxp-evkmimxrt1060-lwip-freertos
|
||||||
- path: nxp/nxp-evkmimxrt1160-cm7-lwip-freertos
|
nxp/nxp-evkmimxrt1064-lwip-freertos
|
||||||
- path: nxp/nxp-evkmimxrt1170-cm7-lwip-freertos
|
nxp/nxp-evkmimxrt1160-cm7-lwip-freertos
|
||||||
- path: nxp/nxp-frdmk64f-lwip-freertos
|
- group: nxp-2
|
||||||
- path: nxp/nxp-frdmk66f-lwip-freertos
|
examples: |
|
||||||
- path: nxp/nxp-lpcxpresso54018-lwip-freertos
|
nxp/nxp-evkmimxrt1170-cm7-lwip-freertos
|
||||||
- path: nxp/nxp-lpcxpresso54608-lwip-freertos
|
nxp/nxp-frdmk64f-lwip-freertos
|
||||||
- path: nxp/nxp-lpcxpresso54618-lwip-freertos
|
nxp/nxp-frdmk66f-lwip-freertos
|
||||||
- path: nxp/nxp-lpcxpresso54628-lwip-freertos
|
nxp/nxp-lpcxpresso54018-lwip-freertos
|
||||||
- path: nxp/nxp-twrk65f180m-lwip-freertos
|
nxp/nxp-lpcxpresso54608-lwip-freertos
|
||||||
- path: nxp/nxp-twrkv58f220m-lwip-freertos
|
nxp/nxp-lpcxpresso54618-lwip-freertos
|
||||||
- path: rp/pico-rmii
|
nxp/nxp-lpcxpresso54628-lwip-freertos
|
||||||
- path: stm32/nucleo-f746zg/minimal-rm2
|
nxp/nxp-twrk65f180m-lwip-freertos
|
||||||
- path: stm32/nucleo-f746zg/minimal-rndis
|
nxp/nxp-twrkv58f220m-lwip-freertos
|
||||||
- path: stm32/nucleo-f429zi/minimal-rm2
|
- group: other
|
||||||
- path: stm32/nucleo-f429zi/minimal-rndis
|
examples: |
|
||||||
- path: stm32/nucleo-g031k8/minimal-w5500
|
mqtt/mqtt-client/microchip/same54-xpro
|
||||||
- path: ti/ti-ek-tm4c1294xl-http-server
|
rp/pico-rmii
|
||||||
- path: ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
|
stm32/nucleo-f746zg/minimal-rm2
|
||||||
name: ${{ matrix.example.path }}
|
stm32/nucleo-f746zg/minimal-rndis
|
||||||
|
stm32/nucleo-f429zi/minimal-rm2
|
||||||
|
stm32/nucleo-f429zi/minimal-rndis
|
||||||
|
stm32/nucleo-g031k8/minimal-w5500
|
||||||
|
ti/ti-ek-tm4c1294xl-http-server
|
||||||
|
ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
|
||||||
|
name: generic ARM ${{ matrix.group }}
|
||||||
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 gcc-arm-none-eabi
|
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
- run: make -C tutorials/${{ matrix.example.path }} build
|
- run: |
|
||||||
|
failed=0
|
||||||
|
sep=
|
||||||
|
examples='${{ matrix.examples }}'
|
||||||
|
for x in $examples; do
|
||||||
|
echo "========== $x =========="
|
||||||
|
if make -C "tutorials/$x" build; then
|
||||||
|
ok=true
|
||||||
|
echo "SUCCESS: $x"
|
||||||
|
else
|
||||||
|
ok=false
|
||||||
|
failed=1
|
||||||
|
echo "FAILURE: $x"
|
||||||
|
fi
|
||||||
|
printf 'HEALTH_DASHBOARD\t%s"%s": %s\n' "$sep" "$x" "$ok"
|
||||||
|
sep=,
|
||||||
|
done
|
||||||
|
exit $failed
|
||||||
|
|
||||||
device_dashboard_examples:
|
device_dashboard_examples:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -433,19 +691,46 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ssl: ["", -DMG_TLS=MG_TLS_BUILTIN]
|
include:
|
||||||
example:
|
- group: none
|
||||||
- path: http/device-dashboard/microchip/same54-xpro
|
cflags: ""
|
||||||
- path: nxp/rt1020-evk-make-freertos-builtin
|
examples: |
|
||||||
- path: nxp/rt1060-evk-make-freertos-builtin
|
http/device-dashboard/microchip/same54-xpro
|
||||||
- path: nxp/rt1170-evk-make-freertos-builtin
|
nxp/rt1020-evk-make-freertos-builtin
|
||||||
- path: renesas/ek-ra6m4-make-baremetal-builtin
|
nxp/rt1060-evk-make-freertos-builtin
|
||||||
name: ${{ matrix.example.path }} ${{ matrix.ssl }}
|
nxp/rt1170-evk-make-freertos-builtin
|
||||||
|
renesas/ek-ra6m4-make-baremetal-builtin
|
||||||
|
- group: builtin
|
||||||
|
cflags: -DMG_TLS=MG_TLS_BUILTIN
|
||||||
|
examples: |
|
||||||
|
http/device-dashboard/microchip/same54-xpro
|
||||||
|
nxp/rt1020-evk-make-freertos-builtin
|
||||||
|
nxp/rt1060-evk-make-freertos-builtin
|
||||||
|
nxp/rt1170-evk-make-freertos-builtin
|
||||||
|
renesas/ek-ra6m4-make-baremetal-builtin
|
||||||
|
name: device dashboard ARM ${{ matrix.group }}
|
||||||
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 gcc-arm-none-eabi
|
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
- run: make -C tutorials/${{ matrix.example.path }} build CFLAGS_EXTRA="${{ matrix.ssl }}"
|
- run: |
|
||||||
|
failed=0
|
||||||
|
sep=
|
||||||
|
examples='${{ matrix.examples }}'
|
||||||
|
for x in $examples; do
|
||||||
|
echo "========== $x =========="
|
||||||
|
if make -C "tutorials/$x" build CFLAGS_EXTRA="${{ matrix.cflags }}"; then
|
||||||
|
ok=true
|
||||||
|
echo "SUCCESS: $x"
|
||||||
|
else
|
||||||
|
ok=false
|
||||||
|
failed=1
|
||||||
|
echo "FAILURE: $x"
|
||||||
|
fi
|
||||||
|
printf 'HEALTH_DASHBOARD\t%s"%s": %s\n' "$sep" "$x" "$ok"
|
||||||
|
sep=,
|
||||||
|
done
|
||||||
|
exit $failed
|
||||||
|
|
||||||
# mqtt_dashboard_examples:
|
# mqtt_dashboard_examples:
|
||||||
# runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Retry failed nightly jobs
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '17 2 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
jobs:
|
||||||
|
retry:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
min=$(date -u -d '5 hours ago' +%s)
|
||||||
|
max=$(date -u -d '2 hours ago' +%s)
|
||||||
|
gh api --paginate "/repos/$GITHUB_REPOSITORY/actions/workflows/nightly.yml/runs?status=failure&per_page=100" --jq '.workflow_runs[] | select(.run_attempt == 1) | [.id, .updated_at] | @tsv' |
|
||||||
|
while IFS=$'\t' read -r id updated; do
|
||||||
|
when=$(date -u -d "$updated" +%s)
|
||||||
|
if [ "$when" -ge "$min" ] && [ "$when" -le "$max" ]; then
|
||||||
|
gh api --method POST "/repos/$GITHUB_REPOSITORY/actions/runs/$id/rerun-failed-jobs"
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -4,35 +4,47 @@ on:
|
|||||||
env:
|
env:
|
||||||
IPV6: 0
|
IPV6: 0
|
||||||
jobs:
|
jobs:
|
||||||
mip:
|
atcmd:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
cc: [gcc]
|
cc: [gcc, clang, g++, clang++]
|
||||||
target: [mip_tap_test]
|
name: ATcmd CC=${{ matrix.cc }}
|
||||||
ssl: ["", BUILTIN, MBEDTLS, OPENSSL, WOLFSSL]
|
|
||||||
# #3226: built-in TCP is currently not working with WolfSSL (builds fine)
|
|
||||||
exclude:
|
|
||||||
- ssl: WOLFSSL
|
|
||||||
target: mip_tap_test
|
|
||||||
name: ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}
|
|
||||||
env:
|
|
||||||
CC: ${{ matrix.cc }}
|
|
||||||
SSL: ${{ matrix.ssl }}
|
|
||||||
TFLAGS: -DMQTT_HOST -DIPV6_NOROUTING -DNO_ABORT # -DMQTT_HOST_NAME=\"server\"
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- uses: webfactory/ssh-agent@v0.10.0
|
- uses: webfactory/ssh-agent@v0.10.0
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
ssh-private-key: ${{ secrets.HEALTH_TESTS_SSH_KEY }}
|
||||||
- run: if [ "${{ matrix.target }}" == "mip_tap_test" ]; then source ./test/setup_ga_network.sh ; export IPV6=1; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh; else export IPV6=1 ; fi && sudo apt -y update ; sudo apt -y install libmbedtls-dev libwolfssl-dev && make -C test ${{ matrix.target }} > log
|
- run: make -C test atcmd_test CC=${{ matrix.cc }} > log
|
||||||
- if: success() || failure()
|
- if: success() || failure()
|
||||||
run: |
|
run: |
|
||||||
cat log
|
cat log
|
||||||
test/health.awk < log > json
|
test/health.awk < log > json
|
||||||
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/${{ matrix.target }}_${{ matrix.cc }}_${{ matrix.ssl }}_$(date +"%Y%m%d").json"
|
scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/atcmd_${{ matrix.cc }}_$(date +"%Y%m%d").json"
|
||||||
|
atcmd89:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [atcmd_vc98]
|
||||||
|
name: ATcmd C89 ${{ matrix.target }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: make -C test ${{ matrix.target }}
|
||||||
|
atcmdbe:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [atcmd_s390]
|
||||||
|
name: ATcmd be ${{ matrix.target }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- 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 && make -C test ${{ matrix.target }}
|
||||||
|
|
||||||
|
|
||||||
# macos:
|
# macos:
|
||||||
|
|||||||
@@ -16,8 +16,13 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
cc: [gcc, clang++]
|
cc: [gcc, clang++]
|
||||||
target: [test, mip_test]
|
target: [test, mip_test, eth_test, ppp_test]
|
||||||
ssl: ["", BUILTIN]
|
ssl: ["", BUILTIN]
|
||||||
|
exclude:
|
||||||
|
- target: eth_test
|
||||||
|
ssl: BUILTIN
|
||||||
|
- target: ppp_test
|
||||||
|
ssl: BUILTIN
|
||||||
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}
|
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.cc }}
|
CC: ${{ matrix.cc }}
|
||||||
@@ -152,6 +157,7 @@ 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-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
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ GitHub repo: https://github.com/cesanta/mongoose
|
|||||||
functionality such as HTTP, MQTT, Modbus, or device-dashboard logic.
|
functionality such as HTTP, MQTT, Modbus, or device-dashboard logic.
|
||||||
- Once Mongoose is integrated, desktop examples from `tutorials/http`,
|
- Once Mongoose is integrated, desktop examples from `tutorials/http`,
|
||||||
`tutorials/mqtt`, and similar directories also work on embedded systems.
|
`tutorials/mqtt`, and similar directories also work on embedded systems.
|
||||||
|
- For STM32, NXP IMXRT, RP2040/2350 use Mongoose's built-in TCP/IP stack
|
||||||
|
- When building an STM32 project from scratch, use the closest tutorials/stm32/*/cube/*.ioc file as a base
|
||||||
|
|
||||||
## API prefix map
|
## API prefix map
|
||||||
|
|
||||||
@@ -49,14 +51,17 @@ function signatures, structs, options, examples, and constraints.
|
|||||||
- `MG_OTA_*`, `mg_ota_*` - firmware OTA support
|
- `MG_OTA_*`, `mg_ota_*` - firmware OTA support
|
||||||
|
|
||||||
|
|
||||||
## Integration
|
## How to integrate Mongoose into an existing project
|
||||||
|
|
||||||
Copy `mongoose.h` and `mongoose.c` into a `mongoose/` subdirectory of the
|
Create `mongoose/` directory in your project.
|
||||||
project and add `mongoose/mongoose.c` to the build. The easiest is to
|
Download `mongoose.h` and `mongoose.c` into a `mongoose/` directory.
|
||||||
copy from GitHub:
|
|
||||||
|
|
||||||
https://raw.githubusercontent.com/cesanta/mongoose/refs/heads/master/mongoose.c
|
```sh
|
||||||
https://raw.githubusercontent.com/cesanta/mongoose/refs/heads/master/mongoose.h
|
curl --fail --silent --create-dirs -o mongoose/mongoose.c https://raw.githubusercontent.com/cesanta/mongoose/refs/heads/master/mongoose.c
|
||||||
|
curl --fail --silent --create-dirs -o mongoose/mongoose.h https://raw.githubusercontent.com/cesanta/mongoose/refs/heads/master/mongoose.h
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `mongoose/mongoose.c` to the build.
|
||||||
|
|
||||||
**Desktop/server** (Linux, macOS, Windows): two files are sufficient.
|
**Desktop/server** (Linux, macOS, Windows): two files are sufficient.
|
||||||
|
|
||||||
@@ -89,6 +94,27 @@ Minimal `mongoose_config.h` should set `MG_ARCH`. For example, for STM32:
|
|||||||
#define MG_ARCH MG_ARCH_CUBE
|
#define MG_ARCH MG_ARCH_CUBE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## How to generate a new STM32 project from scratch
|
||||||
|
|
||||||
|
Download and unzip the pre-generated project which is the closest to your MCU:
|
||||||
|
|
||||||
|
- https://mongoose.ws/downloads/nucleo-h723zg-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/nucleo-f429zi-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/nucleo-h563zi-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/nucleo-f756zg-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/nucleo-n657x0-q-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/nucleo-u5a5zj-q-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/portenta-h7-dashboard-full.zip
|
||||||
|
|
||||||
|
## How to generate a new RP2040 / RP2350 project from scratch
|
||||||
|
|
||||||
|
Download and unzip the pre-generated project which is the closest to your MCU:
|
||||||
|
|
||||||
|
- https://mongoose.ws/downloads/w5500-evb-pico-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/w55rp20-evb-pico-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/pico-w-dashboard-full.zip
|
||||||
|
- https://mongoose.ws/downloads/pico-rndis-dashboard-full.zip
|
||||||
|
|
||||||
## Core API
|
## Core API
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@@ -204,9 +230,11 @@ device dashboard.
|
|||||||
|
|
||||||
### Required files
|
### Required files
|
||||||
|
|
||||||
|
The following files must exist at these exact paths, regardless of the build environment:
|
||||||
|
|
||||||
```
|
```
|
||||||
your_project/
|
your_project/
|
||||||
├── main.c # your code
|
├── ... # IDE-specific project scaffolding
|
||||||
└── mongoose/
|
└── mongoose/
|
||||||
├── mongoose.h # single header
|
├── mongoose.h # single header
|
||||||
├── mongoose.c # single source file
|
├── mongoose.c # single source file
|
||||||
@@ -394,6 +422,11 @@ Each fieldset is exported via the get/set JSON-RPC interface, as well via the RE
|
|||||||
|
|
||||||
Do not use the API directly, dashboard.js interfaces with the UI via the data-* attributes.
|
Do not use the API directly, dashboard.js interfaces with the UI via the data-* attributes.
|
||||||
|
|
||||||
|
The dashboard backend must expose these exact functions: mg_dash_init() and mg_dash_poll().
|
||||||
|
|
||||||
|
Do not rename, wrap, relocate, or replace these files or functions. Do not introduce alternatives such as app_dashboard_init(), app_dashboard_poll(), web/dashboard.html, or Core/Src/dashboard.c.
|
||||||
|
|
||||||
|
Board-specific STM32Cube files may exist outside mongoose/, but the Mongoose dashboard files must retain the structure and API documented above.
|
||||||
|
|
||||||
## Filesystem
|
## Filesystem
|
||||||
|
|
||||||
|
|||||||
+1212
-461
File diff suppressed because it is too large.
Load diff
+307
-24
@@ -20,7 +20,7 @@
|
|||||||
#ifndef MONGOOSE_H
|
#ifndef MONGOOSE_H
|
||||||
#define MONGOOSE_H
|
#define MONGOOSE_H
|
||||||
|
|
||||||
#define MG_VERSION "7.22"
|
#define MG_VERSION "7.23"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -46,6 +46,7 @@ extern "C" {
|
|||||||
#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS
|
#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS
|
||||||
#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT
|
#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT
|
||||||
#define MG_ARCH_CUBE 16 // STM32Cube environment
|
#define MG_ARCH_CUBE 16 // STM32Cube environment
|
||||||
|
#define MG_ARCH_MCUXPRESSO 17 // MCUXpresso environment
|
||||||
|
|
||||||
#define MG_ARCH_NEWLIB MG_ARCH_ARMGCC // Alias, deprecate in 2025
|
#define MG_ARCH_NEWLIB MG_ARCH_ARMGCC // Alias, deprecate in 2025
|
||||||
|
|
||||||
@@ -314,6 +315,11 @@ extern "C" {
|
|||||||
#include <esp_timer.h> // amalgamation ditching them
|
#include <esp_timer.h> // amalgamation ditching them
|
||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
#include <esp_mac.h>
|
#include <esp_mac.h>
|
||||||
|
#include <esp_idf_version.h>
|
||||||
|
#include <esp_ota_ops.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
#include <soc/soc.h>
|
||||||
|
#include <soc/rtc_cntl_reg.h>
|
||||||
|
|
||||||
#define MG_PATH_MAX 128
|
#define MG_PATH_MAX 128
|
||||||
|
|
||||||
@@ -325,6 +331,57 @@ extern "C" {
|
|||||||
#define MG_ENABLE_DIRLIST 1
|
#define MG_ENABLE_DIRLIST 1
|
||||||
#endif
|
#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
|
||||||
|
|
||||||
|
|
||||||
@@ -354,6 +411,158 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if MG_ARCH == MG_ARCH_MCUXPRESSO
|
||||||
|
#define _POSIX_TIMERS
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.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 <unistd.h>
|
||||||
|
|
||||||
|
// MCUXpresso-generated header, includes the SoC peripheral definitions.
|
||||||
|
// NOTE: use angle brackets to prevent amalgamator ditching it
|
||||||
|
#include <fsl_device_registers.h>
|
||||||
|
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
|
#define MG_PATH_MAX 100
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_DIRLIST
|
||||||
|
#define MG_ENABLE_DIRLIST 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_TCPIP
|
||||||
|
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_IMXRT10) && \
|
||||||
|
!defined(MG_ENABLE_DRIVER_IMXRT11)
|
||||||
|
#if defined(CPU_MIMXRT1021DAG5A) || defined(CPU_MIMXRT1024DAG5A) || \
|
||||||
|
defined(CPU_MIMXRT1042XJM5B) || defined(CPU_MIMXRT1052DVL6B) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6B) || defined(CPU_MIMXRT1064DVL6B) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6A) || defined(CPU_MIMXRT1064DVL6A) || \
|
||||||
|
defined(CPU_MIMXRT1062DVMAA_cm7) || defined(CPU_MIMXRT1062DVL6A_cm7) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6B_cm7) || defined(_MIMXRT1021_H_) || \
|
||||||
|
defined(_MIMXRT1024_H_) || defined(_MIMXRT1042_H_) || \
|
||||||
|
defined(_MIMXRT1052_H_) || defined(_MIMXRT1062_H_) || \
|
||||||
|
defined(_MIMXRT1064_H_)
|
||||||
|
#define MG_ENABLE_DRIVER_IMXRT10 1
|
||||||
|
#elif defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1176DVMAA_cm4) || \
|
||||||
|
defined(_MIMXRT1176_cm7_H_) || defined(_MIMXRT1176_cm4_H_) || \
|
||||||
|
defined(_MIMXRT1176_H_)
|
||||||
|
#define MG_ENABLE_DRIVER_IMXRT11 1
|
||||||
|
#else
|
||||||
|
#error Select an Ethernet driver in mongoose_config.h
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TLS
|
||||||
|
#define MG_TLS MG_TLS_BUILTIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1021DAG5A) || defined(CPU_MIMXRT1024DAG5A) || \
|
||||||
|
defined(_MIMXRT1021_H_) || defined(_MIMXRT1024_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1020
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1052DVL6B) || defined(_MIMXRT1052_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1050
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1062DVL6B) || defined(CPU_MIMXRT1062DVL6A) || \
|
||||||
|
defined(CPU_MIMXRT1042XJM5B) || defined(_MIMXRT1042_H_) || \
|
||||||
|
defined(CPU_MIMXRT1062DVMAA_cm7) || defined(CPU_MIMXRT1062DVL6A_cm7) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6B_cm7) || defined(_MIMXRT1062_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1060
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1064DVL6B) || defined(CPU_MIMXRT1064DVL6A) || \
|
||||||
|
defined(_MIMXRT1064_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1064
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1176DVMAA_cm4) || \
|
||||||
|
defined(_MIMXRT1176_cm7_H_) || defined(_MIMXRT1176_cm4_H_) || \
|
||||||
|
defined(_MIMXRT1176_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1170
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IRAM
|
||||||
|
#define MG_IRAM __attribute__((noinline, section(".data_RAM2")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_SET_MAC_ADDRESS
|
||||||
|
#if defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11
|
||||||
|
#define MG_OCOTP_FUSES ((volatile uint32_t *) 0x40cac900)
|
||||||
|
#else
|
||||||
|
#define MG_OCOTP_FUSES ((volatile uint32_t *) 0x401f4410)
|
||||||
|
#endif
|
||||||
|
#define MG_SET_MAC_ADDRESS(mac) \
|
||||||
|
do { \
|
||||||
|
mac[0] = 2; \
|
||||||
|
mac[1] = (MG_OCOTP_FUSES[0] >> 0) & 255; \
|
||||||
|
mac[2] = (MG_OCOTP_FUSES[0] >> 10) & 255; \
|
||||||
|
mac[3] = ((MG_OCOTP_FUSES[0] >> 19) ^ (MG_OCOTP_FUSES[4] >> 19)) & 255; \
|
||||||
|
mac[4] = (MG_OCOTP_FUSES[4] >> 10) & 255; \
|
||||||
|
mac[5] = (MG_OCOTP_FUSES[4] >> 0) & 255; \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IMXRT_WDOG1_TIMEOUT_MS
|
||||||
|
#define MG_IMXRT_WDOG1_TIMEOUT_MS 10000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MG_IMXRT_WDOG1_FEED() \
|
||||||
|
do { \
|
||||||
|
WDOG1->WSR = 0x5555; \
|
||||||
|
WDOG1->WSR = 0xaaaa; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||||
|
do { \
|
||||||
|
uint16_t wt_ = (uint16_t) ((MG_IMXRT_WDOG1_TIMEOUT_MS / 500) - 1); \
|
||||||
|
if ((WDOG1->WCR & WDOG_WCR_WDE_MASK) == 0) { \
|
||||||
|
WDOG1->WMCR = 0; \
|
||||||
|
WDOG1->WCR = (uint16_t) (WDOG_WCR_WDZST_MASK | WDOG_WCR_WDBG_MASK | \
|
||||||
|
WDOG_WCR_WDE_MASK | WDOG_WCR_WDT_MASK | \
|
||||||
|
WDOG_WCR_SRS_MASK | WDOG_WCR_WDW_MASK | \
|
||||||
|
WDOG_WCR_WT(wt_)); \
|
||||||
|
} \
|
||||||
|
MG_IMXRT_WDOG1_FEED(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_FEED() MG_IMXRT_WDOG1_FEED()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_STATE_GET
|
||||||
|
#define MG_OTA_STATE_GET() SNVS->LPGPR[0]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_STATE_SET
|
||||||
|
#define MG_OTA_STATE_SET(v) \
|
||||||
|
do { \
|
||||||
|
if (SNVS->LPSR & (1u << 3)) { \
|
||||||
|
SNVS->LPLVDR = 0x41736166u; \
|
||||||
|
SNVS->LPSR = 1u << 3; \
|
||||||
|
} \
|
||||||
|
SNVS->LPGPR[0] = (uint32_t) (v); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if MG_ARCH == MG_ARCH_PICOSDK
|
#if MG_ARCH == MG_ARCH_PICOSDK
|
||||||
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -778,6 +987,7 @@ typedef int socklen_t;
|
|||||||
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
|
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
|
||||||
#define sleep(x) Sleep((x) *1000)
|
#define sleep(x) Sleep((x) *1000)
|
||||||
#define mkdir(a, b) _mkdir(a)
|
#define mkdir(a, b) _mkdir(a)
|
||||||
|
#define rmdir(a) _rmdir(a)
|
||||||
#define timegm(x) _mkgmtime(x)
|
#define timegm(x) _mkgmtime(x)
|
||||||
|
|
||||||
#ifndef S_ISDIR
|
#ifndef S_ISDIR
|
||||||
@@ -896,7 +1106,8 @@ static inline void *mg_calloc(size_t cnt, size_t size) {
|
|||||||
((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
|
((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
|
||||||
(errcode) == -pdFREERTOS_ERRNO_EISCONN || \
|
(errcode) == -pdFREERTOS_ERRNO_EISCONN || \
|
||||||
(errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
|
(errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
|
||||||
(errcode) == -pdFREERTOS_ERRNO_EAGAIN)
|
(errcode) == -pdFREERTOS_ERRNO_EAGAIN || \
|
||||||
|
(errcode) == -pdFREERTOS_ERRNO_ENOSPC)
|
||||||
|
|
||||||
#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
|
#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
|
||||||
|
|
||||||
@@ -1731,6 +1942,9 @@ void mg_free(void *ptr);
|
|||||||
// sensitive data (keys, passwords).
|
// sensitive data (keys, passwords).
|
||||||
void mg_bzero(volatile unsigned char *buf, size_t len);
|
void mg_bzero(volatile unsigned char *buf, size_t len);
|
||||||
|
|
||||||
|
// Fixed-length constant-time byte equality. Use for MACs, tags, signatures.
|
||||||
|
bool mg_memeq(const void *a, const void *b, size_t n);
|
||||||
|
|
||||||
// Fills buf with len cryptographically random bytes. Uses the best available
|
// Fills buf with len cryptographically random bytes. Uses the best available
|
||||||
// hardware or OS source (hardware RNG, /dev/urandom, CryptGenRandom, etc.).
|
// hardware or OS source (hardware RNG, /dev/urandom, CryptGenRandom, etc.).
|
||||||
// Falls back to rand() with an error log if no strong source is available.
|
// Falls back to rand() with an error log if no strong source is available.
|
||||||
@@ -1746,6 +1960,10 @@ char *mg_random_str(char *buf, size_t len);
|
|||||||
// a new checksum; pass the result of a prior call to extend over more data.
|
// a new checksum; pass the result of a prior call to extend over more data.
|
||||||
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
|
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
|
||||||
|
|
||||||
|
// Computes CRC16 (HDLC, polynomial 0x8408) over buf/len. Pass crc=0 to start
|
||||||
|
// a new checksum; pass the result of a prior call to extend over more data.
|
||||||
|
uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len);
|
||||||
|
|
||||||
// Returns true if path is safe to serve from the filesystem. Rejects paths
|
// Returns true if path is safe to serve from the filesystem. Rejects paths
|
||||||
// that start with '~' or '..', or contain a '/../' component, to prevent
|
// that start with '~' or '..', or contain a '/../' component, to prevent
|
||||||
// directory traversal attacks.
|
// directory traversal attacks.
|
||||||
@@ -1755,6 +1973,9 @@ bool mg_path_is_sane(const struct mg_str path);
|
|||||||
// calling context; avoid in event handlers.
|
// calling context; avoid in event handlers.
|
||||||
void mg_delayms(unsigned int ms);
|
void mg_delayms(unsigned int ms);
|
||||||
|
|
||||||
|
uint64_t mg_timegm(unsigned int year, unsigned int month, unsigned int day,
|
||||||
|
unsigned int hour, unsigned int min, unsigned int sec);
|
||||||
|
|
||||||
// Packs four byte values into a uint32_t in big-endian order.
|
// Packs four byte values into a uint32_t in big-endian order.
|
||||||
// MG_U32(1, 2, 3, 4) == 0x01020304
|
// MG_U32(1, 2, 3, 4) == 0x01020304
|
||||||
#define MG_U32(a, b, c, d) \
|
#define MG_U32(a, b, c, d) \
|
||||||
@@ -1975,6 +2196,9 @@ size_t mg_base64_update(unsigned char input_byte, char *buf, size_t len);
|
|||||||
size_t mg_base64_final(char *buf, size_t len);
|
size_t mg_base64_final(char *buf, size_t len);
|
||||||
size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t);
|
size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t);
|
||||||
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t);
|
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t);
|
||||||
|
size_t mg_base64url_encode(const unsigned char *p, size_t n, char *buf,
|
||||||
|
size_t);
|
||||||
|
size_t mg_base64url_decode(const char *src, size_t n, char *dst, size_t);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2158,6 +2382,10 @@ struct mg_connection {
|
|||||||
void *pfn_data; // Protocol-level handler argument
|
void *pfn_data; // Protocol-level handler argument
|
||||||
char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable
|
char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable
|
||||||
void *tls; // TLS state (internal)
|
void *tls; // TLS state (internal)
|
||||||
|
unsigned dscp : 6; // Differentiated Services Code Point
|
||||||
|
#define MG_DSCP_CS(class_) ((class_) << 3)
|
||||||
|
#define MG_DSCP_AF(class_, drop_) (((class_) << 3) | ((drop_) << 1))
|
||||||
|
#define MG_DSCP_EF 46
|
||||||
unsigned is_listening : 1; // Listening connection; accepts inbound connections
|
unsigned is_listening : 1; // Listening connection; accepts inbound connections
|
||||||
unsigned is_client : 1; // Outbound connection created by mg_connect()
|
unsigned is_client : 1; // Outbound connection created by mg_connect()
|
||||||
unsigned is_accepted : 1; // Inbound connection accepted from a listener
|
unsigned is_accepted : 1; // Inbound connection accepted from a listener
|
||||||
@@ -2238,6 +2466,11 @@ struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
|
|||||||
struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
|
struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
|
||||||
mg_event_handler_t fn, void *fn_data);
|
mg_event_handler_t fn, void *fn_data);
|
||||||
|
|
||||||
|
// Sets the 6-bit Differentiated Services Code Point for outgoing datagrams.
|
||||||
|
// Call on MG_EV_OPEN for UDP listeners, MG_EV_CONNECT for clients, or
|
||||||
|
// MG_EV_ACCEPT for TCP servers, before sending application data.
|
||||||
|
bool mg_dscp(struct mg_connection *, uint8_t dscp);
|
||||||
|
|
||||||
// Called internally after DNS resolution completes to create the OS socket
|
// Called internally after DNS resolution completes to create the OS socket
|
||||||
// and initiate the TCP/IP connect. Normally not called by user code.
|
// and initiate the TCP/IP connect. Normally not called by user code.
|
||||||
void mg_connect_resolved(struct mg_connection *);
|
void mg_connect_resolved(struct mg_connection *);
|
||||||
@@ -2608,6 +2841,12 @@ void mg_tls_ctx_init(struct mg_mgr *);
|
|||||||
void mg_tls_ctx_free(struct mg_mgr *);
|
void mg_tls_ctx_free(struct mg_mgr *);
|
||||||
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
|
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
|
// 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.
|
||||||
|
size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Low-level IO primitives used by TLS layer
|
// Low-level IO primitives used by TLS layer
|
||||||
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2 };
|
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2 };
|
||||||
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
|
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
|
||||||
@@ -2808,28 +3047,28 @@ 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
|
generated. The filled-in values should be either truly random, or from a
|
||||||
cryptographically-secure PRNG.
|
cryptographically-secure PRNG.
|
||||||
|
|
||||||
A correctly functioning RNG function must be set (using mg_uecc_set_rng())
|
A correctly functioning RNG is required before calling mg_uecc_make_key() or
|
||||||
before calling mg_uecc_make_key() or mg_uecc_sign().
|
mg_uecc_sign().
|
||||||
|
|
||||||
Setting a correctly functioning RNG function improves the resistance to
|
Setting a correctly functioning RNG function improves the resistance to
|
||||||
side-channel attacks for mg_uecc_shared_secret() and
|
side-channel attacks for mg_uecc_shared_secret() and
|
||||||
mg_uecc_sign_deterministic().
|
mg_uecc_sign_deterministic().
|
||||||
|
|
||||||
A correct RNG function is set by default when building for Windows, Linux, or OS
|
|
||||||
X. If you are building on another POSIX-compliant system that supports
|
|
||||||
/dev/random or /dev/urandom, you can define MG_UECC_POSIX to use the predefined
|
|
||||||
RNG. For embedded platforms there is no predefined RNG function; you must
|
|
||||||
provide your own.
|
|
||||||
*/
|
*/
|
||||||
typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size);
|
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.
|
/* mg_uecc_set_rng() function.
|
||||||
Set the function that will be used to generate random bytes. The 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
|
should return 1 if the random data was generated, or 0 if the random data could
|
||||||
not be generated.
|
not be generated.
|
||||||
|
|
||||||
On platforms where there is no predefined RNG function (eg embedded platforms),
|
Mongoose: this overrides the default mg_random() fallback used by
|
||||||
this must be called before mg_uecc_make_key() or mg_uecc_sign() are used.
|
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:
|
Inputs:
|
||||||
rng_function - The function that will be used to generate random bytes.
|
rng_function - The function that will be used to generate random bytes.
|
||||||
@@ -3362,7 +3601,7 @@ int mg_uecc_generate_random_int(mg_uecc_word_t *random,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN && MG_ENABLE_CHACHA20
|
||||||
#ifndef __PORTABLE_8439_H
|
#ifndef __PORTABLE_8439_H
|
||||||
#define __PORTABLE_8439_H
|
#define __PORTABLE_8439_H
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
@@ -3478,6 +3717,8 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
|
|||||||
const uint8_t *q, size_t q_len,
|
const uint8_t *q, size_t q_len,
|
||||||
const uint8_t *qInv, size_t qInv_len,
|
const uint8_t *qInv, size_t qInv_len,
|
||||||
uint8_t *signature, size_t sig_len);
|
uint8_t *signature, size_t sig_len);
|
||||||
|
bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash,
|
||||||
|
size_t hashlen);
|
||||||
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
|
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
|
||||||
#endif // TLS_RSA_H
|
#endif // TLS_RSA_H
|
||||||
|
|
||||||
@@ -3506,13 +3747,14 @@ struct mg_tls {
|
|||||||
mbedtls_x509_crt cert; // Parsed certificate
|
mbedtls_x509_crt cert; // Parsed certificate
|
||||||
mbedtls_pk_context pk; // Private key context
|
mbedtls_pk_context pk; // Private key context
|
||||||
mbedtls_ssl_context ssl; // SSL/TLS context
|
mbedtls_ssl_context ssl; // SSL/TLS context
|
||||||
mbedtls_ssl_config conf; // SSL-TLS config
|
mbedtls_ssl_config conf; // SSL/TLS config
|
||||||
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
||||||
mbedtls_ssl_ticket_context ticket; // Session tickets context
|
mbedtls_ssl_ticket_context ticket; // Session tickets context
|
||||||
#endif
|
#endif
|
||||||
// https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29
|
// https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29
|
||||||
unsigned char *throttled_buf; // see #3074
|
unsigned char *throttled_buf; // see #3074
|
||||||
size_t throttled_len;
|
size_t throttled_len;
|
||||||
|
bool check_name; // set when hostname was set, but no CA certificate given
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -3521,11 +3763,14 @@ struct mg_tls {
|
|||||||
|
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/x509v3.h>
|
||||||
|
|
||||||
struct mg_tls {
|
struct mg_tls {
|
||||||
BIO_METHOD *bm;
|
BIO_METHOD *bm;
|
||||||
SSL_CTX *ctx;
|
SSL_CTX *ctx;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
char *name; // matching hostname
|
||||||
|
bool check_name; // set when hostname was set, but no CA certificate given
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -3954,11 +4199,12 @@ struct mg_mdns_req {
|
|||||||
bool is_unicast; // True if the client requested a unicast (QU) response
|
bool is_unicast; // True if the client requested a unicast (QU) response
|
||||||
};
|
};
|
||||||
|
|
||||||
// ev_data for MG_EV_MDNS_RESP, carrying the resolved address from an mDNS response.
|
// ev_data for MG_EV_MDNS_RESP, carrying address/data from a mDNS response
|
||||||
struct mg_mdns_resp {
|
struct mg_mdns_resp {
|
||||||
struct mg_dns_rr *rr; // Resource record from the response
|
struct mg_dns_rr *rr; // Resource record from the response (1st in chain)
|
||||||
struct mg_str name; // Resolved hostname, without the .local suffix
|
struct mg_str name; // Resolved hostname, without the .local suffix
|
||||||
struct mg_addr addr; // Resolved IP address
|
struct mg_addr addr; // Resolved IP address
|
||||||
|
struct mg_dnssd_record sd; // Service Discovery data
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parses a DNS query or response from buf/len into dm. Returns true on success.
|
// Parses a DNS query or response from buf/len into dm. Returns true on success.
|
||||||
@@ -3975,6 +4221,7 @@ size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
|
|||||||
// Creates an mDNS listener on UDP multicast 224.0.0.251:5353. Registers it as
|
// Creates an mDNS listener on UDP multicast 224.0.0.251:5353. Registers it as
|
||||||
// the manager's mDNS resolver (mgr->mdns). Fires MG_EV_MDNS_REQ for incoming
|
// the manager's mDNS resolver (mgr->mdns). Fires MG_EV_MDNS_REQ for incoming
|
||||||
// queries and MG_EV_MDNS_RESP for incoming responses. Returns NULL on error.
|
// queries and MG_EV_MDNS_RESP for incoming responses. Returns NULL on error.
|
||||||
|
// fn_data is a NUL-terminated server name, or NULL to pass all requests to fn.
|
||||||
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
|
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
|
||||||
void *fn_data);
|
void *fn_data);
|
||||||
|
|
||||||
@@ -4097,7 +4344,7 @@ char *mg_json_get_str(struct mg_str json, const char *path);
|
|||||||
|
|
||||||
// Decodes a hex-encoded JSON string at path into a heap-allocated byte array.
|
// Decodes a hex-encoded JSON string at path into a heap-allocated byte array.
|
||||||
// Sets *len to the decoded byte count. Caller must mg_free() the result.
|
// Sets *len to the decoded byte count. Caller must mg_free() the result.
|
||||||
// Returns NULL if not found or not a string.
|
// Returns NULL if not found, not a string, or not valid hexadecimal.
|
||||||
char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
|
char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
|
||||||
|
|
||||||
// Decodes a base64-encoded JSON string at path into a heap-allocated byte
|
// Decodes a base64-encoded JSON string at path into a heap-allocated byte
|
||||||
@@ -4133,6 +4380,33 @@ size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct mg_jwt_opts {
|
||||||
|
struct mg_str claims; // JSON payload
|
||||||
|
struct mg_str header; // Extra header members, no braces; alg/typ are set
|
||||||
|
struct mg_str kid; // Key ID protected header
|
||||||
|
struct mg_str secret; // HS256 secret
|
||||||
|
const uint8_t *private_key; // ES256 private key (MG_TLS_BUILTIN only)
|
||||||
|
const uint8_t *public_key; // ES256 public key (MG_TLS_BUILTIN only)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signs JSON claims as a compact JWT. Returns JWT length, or 0 on error.
|
||||||
|
size_t mg_jwt_sign_hs256(const struct mg_jwt_opts *opts, char *buf,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
// Verifies JWT and decodes JSON claims into buf. Returns claims length, or 0.
|
||||||
|
size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len);
|
||||||
|
|
||||||
|
size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
|
||||||
|
size_t len);
|
||||||
|
size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// In-flight JSON-RPC request. Populate and pass to mg_rpc_process() to
|
// In-flight JSON-RPC request. Populate and pass to mg_rpc_process() to
|
||||||
// dispatch, or receive one pre-filled in a handler registered with mg_rpc_add().
|
// dispatch, or receive one pre-filled in a handler registered with mg_rpc_add().
|
||||||
struct mg_rpc_req {
|
struct mg_rpc_req {
|
||||||
@@ -4354,7 +4628,7 @@ extern void mg_mqtt_poll(struct mg_mgr *);
|
|||||||
//
|
//
|
||||||
// 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 pull-based updates
|
// - defining MG_OTA_URL enables HTTP or HTTPS pull-based updates
|
||||||
// - mg_http_start_ota() enables push-based updates
|
// - mg_http_start_ota() 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
|
||||||
@@ -4501,8 +4775,10 @@ enum {
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// Pull-based OTA over HTTP. Fetches metadata_url, which must return a JSON
|
// Pull-based OTA over HTTP or HTTPS. If it's HTTPS, MG_OTA_TLS_CA
|
||||||
// object like: { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
|
// needs to be defined. Fetches metadata_url, which must
|
||||||
|
// return a JSON object like:
|
||||||
|
// { "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_OTA_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
|
||||||
@@ -4512,12 +4788,18 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
|
|||||||
void (*fn)(const char *error_message));
|
void (*fn)(const char *error_message));
|
||||||
|
|
||||||
// Firmware info URL for mg_ota_poll() which calls mg_ota_url_check().
|
// Firmware info URL for mg_ota_poll() which calls mg_ota_url_check().
|
||||||
// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/
|
// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/
|
||||||
// Settable in mongoose_config.h
|
// Settable in mongoose_config.h
|
||||||
#ifndef MG_OTA_URL
|
#ifndef MG_OTA_URL
|
||||||
#define MG_OTA_URL NULL
|
#define MG_OTA_URL NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// PEM or DER CA certificate used to authenticate an HTTPS OTA server. Set to
|
||||||
|
// NULL to use HTTP. Set in mongoose_config.h.
|
||||||
|
#ifndef MG_OTA_TLS_CA
|
||||||
|
#define MG_OTA_TLS_CA NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
// OTA status callback function for mg_ota_poll()
|
// OTA status callback function for mg_ota_poll()
|
||||||
#ifndef MG_OTA_STATUS_FN
|
#ifndef MG_OTA_STATUS_FN
|
||||||
#define MG_OTA_STATUS_FN NULL
|
#define MG_OTA_STATUS_FN NULL
|
||||||
@@ -4547,8 +4829,9 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
|
|||||||
// E.g. on STM32, uses the 96-bit MCU UID converted to a hex string.
|
// E.g. on STM32, uses the 96-bit MCU UID converted to a hex string.
|
||||||
void mg_ota_device_id(char *buf, size_t len);
|
void mg_ota_device_id(char *buf, size_t len);
|
||||||
|
|
||||||
// Checks for a firmware update over HTTP. Called automatically by mg_mgr_poll()
|
// Checks for a firmware update over HTTP or HTTPS. Called automatically by
|
||||||
// when MG_OTA_URL is set in mongoose_config.h. Do not call directly.
|
// mg_mgr_poll() when MG_OTA_URL is set in mongoose_config.h. Do not call
|
||||||
|
// directly.
|
||||||
void mg_ota_poll(struct mg_mgr *);
|
void mg_ota_poll(struct mg_mgr *);
|
||||||
|
|
||||||
|
|
||||||
@@ -4624,7 +4907,7 @@ bool mg_wifi_ap_stop(void);
|
|||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
|
|
||||||
// no config defaults to 0 => Ethernet
|
// no config defaults to 0 => Ethernet
|
||||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE };
|
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||||
|
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|||||||
@@ -63,3 +63,5 @@
|
|||||||
29. Keep custom backend findings only when the issue is in Mongoose’s backend interface, default backend, documented backend contract, or common backend implementation.
|
29. Keep custom backend findings only when the issue is in Mongoose’s backend interface, default backend, documented backend contract, or common backend implementation.
|
||||||
30. When uncertain, keep findings with clear external input, reachable code path, and concrete impact in Mongoose library/protocol/driver code; filter internal-access, impossible-state, local-misuse, generic-hardening, and speculative reports.
|
30. When uncertain, keep findings with clear external input, reachable code path, and concrete impact in Mongoose library/protocol/driver code; filter internal-access, impossible-state, local-misuse, generic-hardening, and speculative reports.
|
||||||
31. For dashboard reports, do not infer unauthenticated access from a route-specific check alone; account for the broader authentication flow. Keep concrete reachable bugs such as path traversal, unsafe decoded paths, or file read/write/delete issues.
|
31. For dashboard reports, do not infer unauthenticated access from a route-specific check alone; account for the broader authentication flow. Keep concrete reachable bugs such as path traversal, unsafe decoded paths, or file read/write/delete issues.
|
||||||
|
32. Filter reports regarding DNS transaction ID predictability or lack of strict question-name matching in DNS response processing, as these are acknowledged design limitations of the current implementation.
|
||||||
|
33. Filter reports that unauthenticated SNTP time affects TLS certificate validity when Mongoose only performs the SNTP sync if mg_boot_timestamp_ms has not already been initialized.
|
||||||
@@ -19,6 +19,15 @@
|
|||||||
|
|
||||||
# SECURITY CATEGORIES TO EXAMINE
|
# SECURITY CATEGORIES TO EXAMINE
|
||||||
|
|
||||||
|
## Mandatory High-Risk Coverage: Built-In TLS
|
||||||
|
|
||||||
|
The built-in TLS implementation (`MG_TLS_BUILTIN`) is a mandatory high-risk review target. Perform a substantial security analysis of all security-relevant built-in TLS code, including parsing, cryptographic processing, authentication and certificate validation, length and buffer handling, protocol state, cross-call behavior, and failure paths.
|
||||||
|
|
||||||
|
Trace attacker-controlled TLS peer input through normal client and server execution, including traffic received before peer authentication is complete. Report any credible memory-safety, authentication, confidentiality, integrity, state-corruption, or remotely triggerable availability vulnerability.
|
||||||
|
|
||||||
|
Do not set `review_completed` to `true` unless the built-in TLS implementation has been meaningfully inspected as a whole.
|
||||||
|
|
||||||
|
|
||||||
**C Memory Safety and Length-Handling Vulnerabilities:**
|
**C Memory Safety and Length-Handling Vulnerabilities:**
|
||||||
- Look for writes to fixed-size stack or heap buffers where the loop bound is derived from attacker-controlled protocol fields, including topic counts, header counts, chunk counts, multipart parts, DNS labels, WebSocket fragments, TCP/IP options, or filesystem path components.
|
- Look for writes to fixed-size stack or heap buffers where the loop bound is derived from attacker-controlled protocol fields, including topic counts, header counts, chunk counts, multipart parts, DNS labels, WebSocket fragments, TCP/IP options, or filesystem path components.
|
||||||
- Check all conversions between `size_t`, `int`, `long`, `uint16_t`, `uint32_t`, and signed protocol lengths. Flag integer truncation, wraparound, negative-to-large conversion, or off-by-one behavior that can affect allocation, parsing, copying, or bounds checks.
|
- Check all conversions between `size_t`, `int`, `long`, `uint16_t`, `uint32_t`, and signed protocol lengths. Flag integer truncation, wraparound, negative-to-large conversion, or off-by-one behavior that can affect allocation, parsing, copying, or bounds checks.
|
||||||
|
|||||||
+4
-4
@@ -5,20 +5,20 @@
|
|||||||
"serialNumber": "urn:uuid:d3f8b1e2-4a7c-4d9e-bf2a-1c3e5f7a9b0d",
|
"serialNumber": "urn:uuid:d3f8b1e2-4a7c-4d9e-bf2a-1c3e5f7a9b0d",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"timestamp": "2026-06-01T00:00:00Z",
|
"timestamp": "2026-08-12T00:00:00Z",
|
||||||
"component": {
|
"component": {
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"bom-ref": "mongoose",
|
"bom-ref": "mongoose",
|
||||||
"supplier": { "name": "Cesanta Software Limited", "url": ["https://cesanta.com"] },
|
"supplier": { "name": "Cesanta Software Limited", "url": ["https://cesanta.com"] },
|
||||||
"author": "Cesanta Software Limited",
|
"author": "Cesanta Software Limited",
|
||||||
"name": "mongoose",
|
"name": "mongoose",
|
||||||
"version": "7.21",
|
"version": "7.23",
|
||||||
"description": "Two-file C/C++ embedded networking library. HTTP, WebSocket, MQTT, TLS 1.3, OTA firmware updates, device dashboard. Runs on microcontrollers and desktop OS.",
|
"description": "Two-file C/C++ embedded networking library. HTTP, WebSocket, MQTT, TLS 1.3, OTA firmware updates, device dashboard. Runs on microcontrollers and desktop OS.",
|
||||||
"licenses": [
|
"licenses": [
|
||||||
{ "expression": "GPL-2.0-only" },
|
{ "expression": "GPL-2.0-only" },
|
||||||
{ "license": { "name": "Cesanta Commercial License", "url": "https://mongoose.ws/licensing/" } }
|
{ "license": { "name": "Cesanta Commercial License", "url": "https://mongoose.ws/licensing/" } }
|
||||||
],
|
],
|
||||||
"purl": "pkg:github/cesanta/mongoose@7.21",
|
"purl": "pkg:github/cesanta/mongoose@7.23",
|
||||||
"externalReferences": [
|
"externalReferences": [
|
||||||
{ "type": "website", "url": "https://mongoose.ws" },
|
{ "type": "website", "url": "https://mongoose.ws" },
|
||||||
{ "type": "vcs", "url": "https://github.com/cesanta/mongoose" },
|
{ "type": "vcs", "url": "https://github.com/cesanta/mongoose" },
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
{ "type": "distribution", "url": "https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c" }
|
{ "type": "distribution", "url": "https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c" }
|
||||||
],
|
],
|
||||||
"properties": [
|
"properties": [
|
||||||
{ "name": "cpe", "value": "cpe:2.3:a:cesanta:mongoose:7.21:*:*:*:*:*:*:*" }
|
{ "name": "cpe", "value": "cpe:2.3:a:cesanta:mongoose:7.23:*:*:*:*:*:*:*" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS
|
#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS
|
||||||
#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT
|
#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT
|
||||||
#define MG_ARCH_CUBE 16 // STM32Cube environment
|
#define MG_ARCH_CUBE 16 // STM32Cube environment
|
||||||
|
#define MG_ARCH_MCUXPRESSO 17 // MCUXpresso environment
|
||||||
|
|
||||||
#define MG_ARCH_NEWLIB MG_ARCH_ARMGCC // Alias, deprecate in 2025
|
#define MG_ARCH_NEWLIB MG_ARCH_ARMGCC // Alias, deprecate in 2025
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,11 @@
|
|||||||
#include <esp_timer.h> // amalgamation ditching them
|
#include <esp_timer.h> // amalgamation ditching them
|
||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
#include <esp_mac.h>
|
#include <esp_mac.h>
|
||||||
|
#include <esp_idf_version.h>
|
||||||
|
#include <esp_ota_ops.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
#include <soc/soc.h>
|
||||||
|
#include <soc/rtc_cntl_reg.h>
|
||||||
|
|
||||||
#define MG_PATH_MAX 128
|
#define MG_PATH_MAX 128
|
||||||
|
|
||||||
@@ -32,4 +37,55 @@
|
|||||||
#define MG_ENABLE_DIRLIST 1
|
#define MG_ENABLE_DIRLIST 1
|
||||||
#endif
|
#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
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if MG_ARCH == MG_ARCH_MCUXPRESSO
|
||||||
|
#define _POSIX_TIMERS
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.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 <unistd.h>
|
||||||
|
|
||||||
|
// MCUXpresso-generated header, includes the SoC peripheral definitions.
|
||||||
|
// NOTE: use angle brackets to prevent amalgamator ditching it
|
||||||
|
#include <fsl_device_registers.h>
|
||||||
|
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
|
#define MG_PATH_MAX 100
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_DIRLIST
|
||||||
|
#define MG_ENABLE_DIRLIST 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_TCPIP
|
||||||
|
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_IMXRT10) && \
|
||||||
|
!defined(MG_ENABLE_DRIVER_IMXRT11)
|
||||||
|
#if defined(CPU_MIMXRT1021DAG5A) || defined(CPU_MIMXRT1024DAG5A) || \
|
||||||
|
defined(CPU_MIMXRT1042XJM5B) || defined(CPU_MIMXRT1052DVL6B) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6B) || defined(CPU_MIMXRT1064DVL6B) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6A) || defined(CPU_MIMXRT1064DVL6A) || \
|
||||||
|
defined(CPU_MIMXRT1062DVMAA_cm7) || defined(CPU_MIMXRT1062DVL6A_cm7) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6B_cm7) || defined(_MIMXRT1021_H_) || \
|
||||||
|
defined(_MIMXRT1024_H_) || defined(_MIMXRT1042_H_) || \
|
||||||
|
defined(_MIMXRT1052_H_) || defined(_MIMXRT1062_H_) || \
|
||||||
|
defined(_MIMXRT1064_H_)
|
||||||
|
#define MG_ENABLE_DRIVER_IMXRT10 1
|
||||||
|
#elif defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1176DVMAA_cm4) || \
|
||||||
|
defined(_MIMXRT1176_cm7_H_) || defined(_MIMXRT1176_cm4_H_) || \
|
||||||
|
defined(_MIMXRT1176_H_)
|
||||||
|
#define MG_ENABLE_DRIVER_IMXRT11 1
|
||||||
|
#else
|
||||||
|
#error Select an Ethernet driver in mongoose_config.h
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TLS
|
||||||
|
#define MG_TLS MG_TLS_BUILTIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1021DAG5A) || defined(CPU_MIMXRT1024DAG5A) || \
|
||||||
|
defined(_MIMXRT1021_H_) || defined(_MIMXRT1024_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1020
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1052DVL6B) || defined(_MIMXRT1052_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1050
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1062DVL6B) || defined(CPU_MIMXRT1062DVL6A) || \
|
||||||
|
defined(CPU_MIMXRT1042XJM5B) || defined(_MIMXRT1042_H_) || \
|
||||||
|
defined(CPU_MIMXRT1062DVMAA_cm7) || defined(CPU_MIMXRT1062DVL6A_cm7) || \
|
||||||
|
defined(CPU_MIMXRT1062DVL6B_cm7) || defined(_MIMXRT1062_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1060
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1064DVL6B) || defined(CPU_MIMXRT1064DVL6A) || \
|
||||||
|
defined(_MIMXRT1064_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1064
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1176DVMAA_cm4) || \
|
||||||
|
defined(_MIMXRT1176_cm7_H_) || defined(_MIMXRT1176_cm4_H_) || \
|
||||||
|
defined(_MIMXRT1176_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1170
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IRAM
|
||||||
|
#define MG_IRAM __attribute__((noinline, section(".data_RAM2")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_SET_MAC_ADDRESS
|
||||||
|
#if defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11
|
||||||
|
#define MG_OCOTP_FUSES ((volatile uint32_t *) 0x40cac900)
|
||||||
|
#else
|
||||||
|
#define MG_OCOTP_FUSES ((volatile uint32_t *) 0x401f4410)
|
||||||
|
#endif
|
||||||
|
#define MG_SET_MAC_ADDRESS(mac) \
|
||||||
|
do { \
|
||||||
|
mac[0] = 2; \
|
||||||
|
mac[1] = (MG_OCOTP_FUSES[0] >> 0) & 255; \
|
||||||
|
mac[2] = (MG_OCOTP_FUSES[0] >> 10) & 255; \
|
||||||
|
mac[3] = ((MG_OCOTP_FUSES[0] >> 19) ^ (MG_OCOTP_FUSES[4] >> 19)) & 255; \
|
||||||
|
mac[4] = (MG_OCOTP_FUSES[4] >> 10) & 255; \
|
||||||
|
mac[5] = (MG_OCOTP_FUSES[4] >> 0) & 255; \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IMXRT_WDOG1_TIMEOUT_MS
|
||||||
|
#define MG_IMXRT_WDOG1_TIMEOUT_MS 10000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MG_IMXRT_WDOG1_FEED() \
|
||||||
|
do { \
|
||||||
|
WDOG1->WSR = 0x5555; \
|
||||||
|
WDOG1->WSR = 0xaaaa; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||||
|
do { \
|
||||||
|
uint16_t wt_ = (uint16_t) ((MG_IMXRT_WDOG1_TIMEOUT_MS / 500) - 1); \
|
||||||
|
if ((WDOG1->WCR & WDOG_WCR_WDE_MASK) == 0) { \
|
||||||
|
WDOG1->WMCR = 0; \
|
||||||
|
WDOG1->WCR = (uint16_t) (WDOG_WCR_WDZST_MASK | WDOG_WCR_WDBG_MASK | \
|
||||||
|
WDOG_WCR_WDE_MASK | WDOG_WCR_WDT_MASK | \
|
||||||
|
WDOG_WCR_SRS_MASK | WDOG_WCR_WDW_MASK | \
|
||||||
|
WDOG_WCR_WT(wt_)); \
|
||||||
|
} \
|
||||||
|
MG_IMXRT_WDOG1_FEED(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_FEED() MG_IMXRT_WDOG1_FEED()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_STATE_GET
|
||||||
|
#define MG_OTA_STATE_GET() SNVS->LPGPR[0]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_STATE_SET
|
||||||
|
#define MG_OTA_STATE_SET(v) \
|
||||||
|
do { \
|
||||||
|
if (SNVS->LPSR & (1u << 3)) { \
|
||||||
|
SNVS->LPLVDR = 0x41736166u; \
|
||||||
|
SNVS->LPSR = 1u << 3; \
|
||||||
|
} \
|
||||||
|
SNVS->LPGPR[0] = (uint32_t) (v); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -159,6 +159,7 @@ typedef int socklen_t;
|
|||||||
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
|
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
|
||||||
#define sleep(x) Sleep((x) *1000)
|
#define sleep(x) Sleep((x) *1000)
|
||||||
#define mkdir(a, b) _mkdir(a)
|
#define mkdir(a, b) _mkdir(a)
|
||||||
|
#define rmdir(a) _rmdir(a)
|
||||||
#define timegm(x) _mkgmtime(x)
|
#define timegm(x) _mkgmtime(x)
|
||||||
|
|
||||||
#ifndef S_ISDIR
|
#ifndef S_ISDIR
|
||||||
|
|||||||
+49
-3
@@ -12,16 +12,16 @@ static int mg_base64_encode_single(int c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mg_base64_decode_single(int c) {
|
static int base64_decode_single(int c, char plus, char slash) {
|
||||||
if (c >= 'A' && c <= 'Z') {
|
if (c >= 'A' && c <= 'Z') {
|
||||||
return c - 'A';
|
return c - 'A';
|
||||||
} else if (c >= 'a' && c <= 'z') {
|
} else if (c >= 'a' && c <= 'z') {
|
||||||
return c + 26 - 'a';
|
return c + 26 - 'a';
|
||||||
} else if (c >= '0' && c <= '9') {
|
} else if (c >= '0' && c <= '9') {
|
||||||
return c + 52 - '0';
|
return c + 52 - '0';
|
||||||
} else if (c == '+') {
|
} else if (c == plus) {
|
||||||
return 62;
|
return 62;
|
||||||
} else if (c == '/') {
|
} else if (c == slash) {
|
||||||
return 63;
|
return 63;
|
||||||
} else if (c == '=') {
|
} else if (c == '=') {
|
||||||
return 64;
|
return 64;
|
||||||
@@ -30,6 +30,14 @@ static int mg_base64_decode_single(int c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mg_base64_decode_single(int c) {
|
||||||
|
return base64_decode_single(c, '+', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mg_base64url_decode_single(int c) {
|
||||||
|
return base64_decode_single(c, '-', '_');
|
||||||
|
}
|
||||||
|
|
||||||
size_t mg_base64_update(unsigned char ch, char *to, size_t n) {
|
size_t mg_base64_update(unsigned char ch, char *to, size_t n) {
|
||||||
unsigned long rem = (n & 3) % 3;
|
unsigned long rem = (n & 3) % 3;
|
||||||
if (rem == 0) {
|
if (rem == 0) {
|
||||||
@@ -91,3 +99,41 @@ fail:
|
|||||||
if (dl > 0) dst[0] = '\0';
|
if (dl > 0) dst[0] = '\0';
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t mg_base64url_encode(const unsigned char *p, size_t n, char *to,
|
||||||
|
size_t dl) {
|
||||||
|
size_t i, len = mg_base64_encode(p, n, to, dl);
|
||||||
|
if (len == 0) return 0;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (to[i] == '+') {
|
||||||
|
to[i] = '-';
|
||||||
|
} else if (to[i] == '/') {
|
||||||
|
to[i] = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (len > 0 && to[len - 1] == '=') to[--len] = '\0';
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_base64url_decode(const char *src, size_t n, char *dst, size_t dl) {
|
||||||
|
size_t i, len = 0;
|
||||||
|
unsigned int bits = 0, v = 0;
|
||||||
|
if (dl == 0 || (n & 3) == 1) goto fail;
|
||||||
|
for (i = 0; src != NULL && i < n; i++) {
|
||||||
|
int c = mg_base64url_decode_single(src[i]);
|
||||||
|
if (c == 64) break;
|
||||||
|
if (c < 0) goto fail;
|
||||||
|
v = (v << 6) | (unsigned int) c;
|
||||||
|
bits += 6;
|
||||||
|
if (bits >= 8) {
|
||||||
|
bits -= 8;
|
||||||
|
if (len + 1 >= dl) goto fail;
|
||||||
|
dst[len++] = (char) ((v >> bits) & 255U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst[len] = '\0';
|
||||||
|
return len;
|
||||||
|
fail:
|
||||||
|
if (dl > 0) dst[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -4,3 +4,6 @@ size_t mg_base64_update(unsigned char input_byte, char *buf, size_t len);
|
|||||||
size_t mg_base64_final(char *buf, size_t len);
|
size_t mg_base64_final(char *buf, size_t len);
|
||||||
size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t);
|
size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t);
|
||||||
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t);
|
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t);
|
||||||
|
size_t mg_base64url_encode(const unsigned char *p, size_t n, char *buf,
|
||||||
|
size_t);
|
||||||
|
size_t mg_base64url_decode(const char *src, size_t n, char *dst, size_t);
|
||||||
@@ -2,6 +2,67 @@
|
|||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
|
||||||
|
// Queue-based BSD shim is currently TCP/SOCK_STREAM only. UDP/SOCK_DGRAM would
|
||||||
|
// need datagram boundaries, e.g. datagram queues or SOD/EOD framing.
|
||||||
|
// Unconnected UDP also needs per-packet peer addresses for sendto()/recvfrom().
|
||||||
|
// It is also IPv4-only: transports store sockaddr_in and build IPv4 URLs.
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_BSD_LOG
|
||||||
|
#define MG_ENABLE_BSD_LOG 0
|
||||||
|
#define bsd_log(type, tag, a, b, c, n)
|
||||||
|
#else
|
||||||
|
#define MG_BSD_LOG_SOCK 1
|
||||||
|
#define MG_BSD_LOG_ACCEPT 2
|
||||||
|
#define MG_BSD_LOG_CLOSE 3
|
||||||
|
#define MG_BSD_LOG_TRANSPORT 4
|
||||||
|
#define MG_BSD_LOG_RESULT 5
|
||||||
|
#define MG_BSD_LOG_CONNECT 6
|
||||||
|
// Type is always a constant, so optimised builds fold the switch to one log.
|
||||||
|
#define bsd_log(type, tag, a, b, c, n) \
|
||||||
|
do { \
|
||||||
|
switch (type) { \
|
||||||
|
case MG_BSD_LOG_SOCK: { \
|
||||||
|
struct mg_bsd_sock *log_s = (struct mg_bsd_sock *) (a); \
|
||||||
|
MG_DEBUG(("SOCK %s %ld %p", tag, (long) (n), log_s->t)); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
case MG_BSD_LOG_ACCEPT: { \
|
||||||
|
struct mg_connection *log_mc = (struct mg_connection *) (a); \
|
||||||
|
if ((n) >= 0) { \
|
||||||
|
MG_DEBUG(("ACCEPT %lu %p %u %ld", log_mc->id, c, \
|
||||||
|
(unsigned) mg_ntohs(log_mc->rem.port), (long) (n))); \
|
||||||
|
} else { \
|
||||||
|
MG_DEBUG(("ACCEPT %lu %p %u", log_mc->id, c, \
|
||||||
|
(unsigned) mg_ntohs(log_mc->rem.port))); \
|
||||||
|
} \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
case MG_BSD_LOG_CLOSE: { \
|
||||||
|
struct mg_connection *log_mc = (struct mg_connection *) (a); \
|
||||||
|
if ((n) >= 0) MG_INFO(("CLOSE %s %lu %p %ld", tag, log_mc->id, b, \
|
||||||
|
(long) (n))); \
|
||||||
|
else MG_DEBUG(("CLOSE %s %lu %p", tag, log_mc->id, b)); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
case MG_BSD_LOG_TRANSPORT: \
|
||||||
|
if ((n) >= 0) MG_INFO(("TRANSP %s %p %ld", tag, a, (long) (n))); \
|
||||||
|
else MG_DEBUG(("TRANSP %s %p", tag, a)); \
|
||||||
|
break; \
|
||||||
|
case MG_BSD_LOG_RESULT: \
|
||||||
|
MG_DEBUG(("RESULT %s %p %ld", tag, a, (long) (n))); \
|
||||||
|
break; \
|
||||||
|
case MG_BSD_LOG_CONNECT: \
|
||||||
|
if ((c) != NULL) { \
|
||||||
|
MG_DEBUG(("CONNECT %s %p %p %s %ld", tag, a, b, \
|
||||||
|
(const char *) (c), (long) (n))); \
|
||||||
|
} else { \
|
||||||
|
MG_DEBUG(("CONNECT %s %p %p %ld", tag, a, b, (long) (n))); \
|
||||||
|
} \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mg_bsd_sock {
|
struct mg_bsd_sock {
|
||||||
void *t; // opaque transport handle
|
void *t; // opaque transport handle
|
||||||
int fd;
|
int fd;
|
||||||
@@ -39,12 +100,12 @@ static void release_sock(int fd) {
|
|||||||
if (*p) *p = (*p)->next;
|
if (*p) *p = (*p)->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int socket(int domain, int type, int proto) {
|
int socket(int domain, int type, int proto) {
|
||||||
struct mg_bsd_sock *s = (struct mg_bsd_sock *) calloc(1, sizeof(*s));
|
struct mg_bsd_sock *s = (struct mg_bsd_sock *) calloc(1, sizeof(*s));
|
||||||
if (!s) { errno = ENOMEM; return -1; }
|
if (!s) { errno = ENOMEM; return -1; }
|
||||||
s->t = mg_bsd_transport_new(domain, type, proto);
|
s->t = mg_bsd_transport_new(domain, type, proto);
|
||||||
if (!s->t || alloc_sock(s) < 0) { free(s); errno = ENOMEM; return -1; }
|
if (!s->t) { free(s); return -1; }
|
||||||
|
if (alloc_sock(s) < 0) { mg_bsd_transport_free(s->t); free(s); errno = ENOMEM; return -1; }
|
||||||
s->domain = domain; s->type = type; s->proto = proto;
|
s->domain = domain; s->type = type; s->proto = proto;
|
||||||
return s->fd;
|
return s->fd;
|
||||||
}
|
}
|
||||||
@@ -66,11 +127,12 @@ int listen(int fd, int backlog) {
|
|||||||
int accept(int fd, struct sockaddr *addr, socklen_t *addrlen) {
|
int accept(int fd, struct sockaddr *addr, socklen_t *addrlen) {
|
||||||
struct mg_bsd_sock *ls = get(fd);
|
struct mg_bsd_sock *ls = get(fd);
|
||||||
if (!ls) return -1;
|
if (!ls) return -1;
|
||||||
struct sockaddr_in peer = {0};
|
struct sockaddr_in peer;
|
||||||
|
memset(&peer, 0, sizeof(peer));
|
||||||
void *t = mg_bsd_transport_accept(ls->t, &peer, ls->nonblock);
|
void *t = mg_bsd_transport_accept(ls->t, &peer, ls->nonblock);
|
||||||
if (!t) { if (ls->nonblock) errno = EAGAIN; return -1; }
|
if (!t) return -1; // errno was set by transport_accept()
|
||||||
struct mg_bsd_sock *ns = (struct mg_bsd_sock *) calloc(1, sizeof(*ns));
|
struct mg_bsd_sock *ns = (struct mg_bsd_sock *) calloc(1, sizeof(*ns));
|
||||||
if (!ns || alloc_sock(ns) < 0) { mg_bsd_transport_free(t); free(ns); errno = ENOMEM; return -1; }
|
if (!ns || alloc_sock(ns) < 0) { mg_bsd_transport_close(t); free(ns); errno = ENOMEM; return -1; }
|
||||||
ns->t = t; ns->domain = ls->domain; ns->type = ls->type; ns->peer = peer;
|
ns->t = t; ns->domain = ls->domain; ns->type = ls->type; ns->peer = peer;
|
||||||
if (addr && addrlen) {
|
if (addr && addrlen) {
|
||||||
size_t sz = sizeof(peer) < *addrlen ? sizeof(peer) : *addrlen;
|
size_t sz = sizeof(peer) < *addrlen ? sizeof(peer) : *addrlen;
|
||||||
@@ -101,22 +163,16 @@ ssize_t recv(int fd, void *buf, size_t len, int flags) {
|
|||||||
|
|
||||||
ssize_t sendto(int fd, const void *buf, size_t len, int flags,
|
ssize_t sendto(int fd, const void *buf, size_t len, int flags,
|
||||||
const struct sockaddr *dest, socklen_t addrlen) {
|
const struct sockaddr *dest, socklen_t addrlen) {
|
||||||
(void) dest; (void) addrlen;
|
(void) fd; (void) buf; (void) len; (void) flags; (void) dest; (void) addrlen;
|
||||||
return send(fd, buf, len, flags);
|
errno = EPROTONOSUPPORT;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t recvfrom(int fd, void *buf, size_t len, int flags,
|
ssize_t recvfrom(int fd, void *buf, size_t len, int flags,
|
||||||
struct sockaddr *src, socklen_t *addrlen) {
|
struct sockaddr *src, socklen_t *addrlen) {
|
||||||
ssize_t n = recv(fd, buf, len, flags);
|
(void) fd; (void) buf; (void) len; (void) flags; (void) src; (void) addrlen;
|
||||||
if (n > 0 && src && addrlen) {
|
errno = EPROTONOSUPPORT;
|
||||||
struct mg_bsd_sock *s = get(fd);
|
return -1;
|
||||||
if (s) {
|
|
||||||
size_t sz = sizeof(s->peer) < *addrlen ? sizeof(s->peer) : *addrlen;
|
|
||||||
memcpy(src, &s->peer, sz);
|
|
||||||
*addrlen = (socklen_t) sizeof(s->peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t write(int fd, const void *buf, size_t len) { return send(fd, buf, len, 0); }
|
ssize_t write(int fd, const void *buf, size_t len) { return send(fd, buf, len, 0); }
|
||||||
@@ -125,7 +181,9 @@ ssize_t read(int fd, void *buf, size_t len) { return recv(fd, buf, len, 0); }
|
|||||||
int close(int fd) {
|
int close(int fd) {
|
||||||
struct mg_bsd_sock *s = get(fd);
|
struct mg_bsd_sock *s = get(fd);
|
||||||
if (!s) return -1;
|
if (!s) return -1;
|
||||||
|
bsd_log(MG_BSD_LOG_SOCK, "CLOSE", s, NULL, NULL, (long) fd);
|
||||||
mg_bsd_transport_close(s->t);
|
mg_bsd_transport_close(s->t);
|
||||||
|
bsd_log(MG_BSD_LOG_SOCK, "FREE", s, NULL, NULL, (long) fd);
|
||||||
release_sock(fd);
|
release_sock(fd);
|
||||||
free(s);
|
free(s);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -193,7 +251,8 @@ void freeaddrinfo(struct addrinfo *res) { (void) res; }
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int inet_pton(int af, const char *src, void *dst) {
|
int inet_pton(int af, const char *src, void *dst) {
|
||||||
struct mg_addr a = {0};
|
struct mg_addr a;
|
||||||
|
memset(&a, 0, sizeof(a));
|
||||||
if (af == AF_INET && mg_aton(mg_str_s(src), &a)) { memcpy(dst, &a.addr.ip4, 4); return 1; }
|
if (af == AF_INET && mg_aton(mg_str_s(src), &a)) { memcpy(dst, &a.addr.ip4, 4); return 1; }
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -208,7 +267,8 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
in_addr_t inet_addr(const char *cp) {
|
in_addr_t inet_addr(const char *cp) {
|
||||||
struct mg_addr a = {0};
|
struct mg_addr a;
|
||||||
|
memset(&a, 0, sizeof(a));
|
||||||
return mg_aton(mg_str_s(cp), &a) ? a.addr.ip4 : (in_addr_t) -1;
|
return mg_aton(mg_str_s(cp), &a) ? a.addr.ip4 : (in_addr_t) -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +299,9 @@ uint32_t ntohl(uint32_t n) { return mg_htonl(n); }
|
|||||||
#ifndef MG_BSD_Q_DEPTH
|
#ifndef MG_BSD_Q_DEPTH
|
||||||
#define MG_BSD_Q_DEPTH 4
|
#define MG_BSD_Q_DEPTH 4
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef MG_BSD_ACCEPT_MS
|
||||||
|
#define MG_BSD_ACCEPT_MS 3000 // Timeout for accept queue handoff
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mg_bsd_chunk { uint8_t data[MG_BSD_CHUNK_SIZE]; uint16_t len; };
|
struct mg_bsd_chunk { uint8_t data[MG_BSD_CHUNK_SIZE]; uint16_t len; };
|
||||||
|
|
||||||
@@ -248,7 +311,11 @@ struct mg_xport {
|
|||||||
QueueHandle_t send_q; // task2 writes in send(), task1 drains on MG_EV_POLL
|
QueueHandle_t send_q; // task2 writes in send(), task1 drains on MG_EV_POLL
|
||||||
QueueHandle_t accept_q; // task1 writes on MG_EV_ACCEPT, task2 reads in accept()
|
QueueHandle_t accept_q; // task1 writes on MG_EV_ACCEPT, task2 reads in accept()
|
||||||
struct sockaddr_in peer;
|
struct sockaddr_in peer;
|
||||||
|
uint16_t rx_off;
|
||||||
|
uint64_t accept_expire;
|
||||||
|
int err;
|
||||||
bool closed;
|
bool closed;
|
||||||
|
bool orphan; // accepted connection not handed to socket owner
|
||||||
TaskHandle_t connect_waiter; // task blocked in connect(), woken by MG_EV_CONNECT
|
TaskHandle_t connect_waiter; // task blocked in connect(), woken by MG_EV_CONNECT
|
||||||
int *connect_result; // where to store 0/−1 connect outcome
|
int *connect_result; // where to store 0/−1 connect outcome
|
||||||
};
|
};
|
||||||
@@ -264,17 +331,48 @@ struct mg_bsd_cmd {
|
|||||||
|
|
||||||
static QueueHandle_t s_cmd_q;
|
static QueueHandle_t s_cmd_q;
|
||||||
|
|
||||||
// Single-slot DNS resolve state (not reentrant, sufficient for demos)
|
static bool bsd_qsend(QueueHandle_t q, const void *item, TickType_t ticks,
|
||||||
static struct { struct mg_addr addr; bool done, error; TaskHandle_t caller; } s_resolve;
|
bool reserve) {
|
||||||
|
BaseType_t ok = pdFALSE;
|
||||||
|
if (!reserve || uxQueueSpacesAvailable(q) > 1) ok = xQueueSend(q, item, ticks);
|
||||||
|
if (ok != pdTRUE) MG_ERROR(("%p", q));
|
||||||
|
return ok == pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xport_accept(struct mg_xport *x) {
|
||||||
|
if (bsd_qsend(x->accept_q, &x, 0, true)) {
|
||||||
|
x->orphan = false; // not an orphan anymore
|
||||||
|
x->accept_q = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; // still an orphan, retry later
|
||||||
|
}
|
||||||
|
|
||||||
|
// static DNS resolve state; not reentrant (see below)
|
||||||
|
static struct { struct mg_addr addr; bool error; TaskHandle_t caller; } s_resolve;
|
||||||
|
// gethostbyname statics (official isn't reentrant anyway, and is obsolete)
|
||||||
|
static struct hostent s_hostent;
|
||||||
|
static char *s_h_aliases[1];
|
||||||
|
static char *s_h_addr_list[2];
|
||||||
|
static uint32_t s_h_addr;
|
||||||
|
static char s_h_name[64];
|
||||||
|
|
||||||
static void resolve_cb(struct mg_connection *c, int ev, void *ev_data) {
|
static void resolve_cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_RESOLVE) { s_resolve.addr = c->rem; s_resolve.done = true; c->is_closing = 1; }
|
bool notify = false;
|
||||||
else if ((ev == MG_EV_ERROR || ev == MG_EV_CLOSE) && !s_resolve.done) s_resolve.error = true;
|
if (ev == MG_EV_RESOLVE) {
|
||||||
if ((s_resolve.done || s_resolve.error) && s_resolve.caller) {
|
s_resolve.addr = c->rem;
|
||||||
TaskHandle_t h = s_resolve.caller;
|
c->is_closing = 1;
|
||||||
s_resolve.caller = NULL; // prevent double-notify on subsequent MG_EV_CLOSE
|
MG_DEBUG(("%lu resolved", c->id));
|
||||||
xTaskNotifyGive(h);
|
notify = true;
|
||||||
|
} else if (ev == MG_EV_ERROR) {
|
||||||
|
s_resolve.error = true;
|
||||||
|
MG_DEBUG(("%lu failed", c->id));
|
||||||
|
notify = true;
|
||||||
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
s_resolve.caller = NULL; // The resolver connection has fully unwound.
|
||||||
|
MG_DEBUG(("%lu done", c->id));
|
||||||
}
|
}
|
||||||
|
if (notify) xTaskNotifyGive(s_resolve.caller);
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,9 +380,11 @@ static void resolve_cb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
static struct mg_xport *xport_alloc(void) {
|
static struct mg_xport *xport_alloc(void) {
|
||||||
struct mg_xport *x = (struct mg_xport *) calloc(1, sizeof(*x));
|
struct mg_xport *x = (struct mg_xport *) calloc(1, sizeof(*x));
|
||||||
if (!x) return NULL;
|
if (!x) return NULL;
|
||||||
x->recv_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
|
// +1 keeps a terminal EOF/error slot for MG_EV_CLOSE
|
||||||
|
x->recv_q = xQueueCreate(MG_BSD_Q_DEPTH + 1, sizeof(struct mg_bsd_chunk));
|
||||||
x->send_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
|
x->send_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
|
||||||
if (!x->recv_q || !x->send_q) { mg_bsd_transport_free(x); return NULL; }
|
if (!x->recv_q || !x->send_q) { mg_bsd_transport_free(x); return NULL; }
|
||||||
|
x->orphan = true; // haven't attached this connection to its socket
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,51 +394,101 @@ static void xport_ev(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
|
|
||||||
if (ev == MG_EV_ACCEPT) {
|
if (ev == MG_EV_ACCEPT) {
|
||||||
// c is the new accepted connection; x is the listening transport
|
// c is the new accepted connection; x is the listening transport
|
||||||
|
bool ok;
|
||||||
struct mg_xport *nx = xport_alloc();
|
struct mg_xport *nx = xport_alloc();
|
||||||
if (!nx) { c->is_closing = 1; return; }
|
if (!nx) { c->fn_data = NULL; mg_error(c, "accept OOM"); return; }
|
||||||
nx->c = c;
|
nx->c = c;
|
||||||
|
nx->accept_q = x->accept_q;
|
||||||
|
nx->accept_expire = mg_millis() + MG_BSD_ACCEPT_MS;
|
||||||
nx->peer.sin_family = AF_INET;
|
nx->peer.sin_family = AF_INET;
|
||||||
nx->peer.sin_port = c->rem.port;
|
nx->peer.sin_port = c->rem.port;
|
||||||
memcpy(&nx->peer.sin_addr, &c->rem.addr.ip4, 4);
|
memcpy(&nx->peer.sin_addr, &c->rem.addr.ip4, 4);
|
||||||
c->fn_data = nx;
|
c->fn_data = nx;
|
||||||
xQueueSend(x->accept_q, &nx, 0);
|
ok = xport_accept(nx); // leaves orphaned on failure, retry on POLL
|
||||||
} else if (ev == MG_EV_READ && x->recv_q) {
|
bsd_log(MG_BSD_LOG_ACCEPT, NULL, c, x, nx, ok ? 1 : 0);
|
||||||
// Drain c->recv into recv_q in fixed-size chunks; task1 owns c->recv
|
} else if (ev == MG_EV_POLL && x->orphan && x->accept_q) {
|
||||||
size_t off = 0;
|
if (uxQueueSpacesAvailable(x->accept_q) > 1 && xport_accept(x)) {
|
||||||
while (off < c->recv.len) {
|
bsd_log(MG_BSD_LOG_ACCEPT, NULL, c, NULL, x, 1);
|
||||||
|
} else if (mg_millis() > x->accept_expire) { // retried enough, give up
|
||||||
|
x->err = EIO;
|
||||||
|
mg_error(c, "accept_q");
|
||||||
|
}
|
||||||
|
} else if (ev == MG_EV_READ || ev == MG_EV_POLL) {
|
||||||
|
if (x->recv_q) { // let POLL resume abandoned READ processing on full queue
|
||||||
|
// Drain c->recv into recv_q in fixed-size chunks; task1 owns c->recv
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < c->recv.len) {
|
||||||
|
struct mg_bsd_chunk chunk;
|
||||||
|
size_t n = c->recv.len - off;
|
||||||
|
if (n > MG_BSD_CHUNK_SIZE) n = MG_BSD_CHUNK_SIZE;
|
||||||
|
memcpy(chunk.data, c->recv.buf + off, n);
|
||||||
|
chunk.len = (uint16_t) n;
|
||||||
|
if (uxQueueSpacesAvailable(x->recv_q) <= 1 ||
|
||||||
|
!bsd_qsend(x->recv_q, &chunk, 0, false)) break; // retry later
|
||||||
|
off += n;
|
||||||
|
}
|
||||||
|
mg_iobuf_del(&c->recv, 0, off);
|
||||||
|
}
|
||||||
|
if (ev == MG_EV_POLL && x->send_q) {
|
||||||
|
// Drain send_q → mg_send(); task1 owns c
|
||||||
struct mg_bsd_chunk chunk;
|
struct mg_bsd_chunk chunk;
|
||||||
size_t n = c->recv.len - off;
|
while (xQueuePeek(x->send_q, &chunk, 0) == pdTRUE) {
|
||||||
if (n > MG_BSD_CHUNK_SIZE) n = MG_BSD_CHUNK_SIZE;
|
if (!mg_send(c, chunk.data, chunk.len)) break; // retry later
|
||||||
memcpy(chunk.data, c->recv.buf + off, n);
|
xQueueReceive(x->send_q, &chunk, 0);
|
||||||
chunk.len = (uint16_t) n;
|
}
|
||||||
xQueueSend(x->recv_q, &chunk, portMAX_DELAY);
|
|
||||||
off += n;
|
|
||||||
}
|
}
|
||||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
|
||||||
} else if (ev == MG_EV_POLL && x->send_q) {
|
|
||||||
// Drain send_q → mg_send(); task1 owns c
|
|
||||||
struct mg_bsd_chunk chunk;
|
|
||||||
while (xQueueReceive(x->send_q, &chunk, 0) == pdTRUE)
|
|
||||||
mg_send(c, chunk.data, chunk.len);
|
|
||||||
} else if (ev == MG_EV_CONNECT) {
|
} else if (ev == MG_EV_CONNECT) {
|
||||||
// Outgoing connection established: wake the task blocked in connect()
|
// Outgoing connection established: wake the task blocked in connect()
|
||||||
if (x->connect_waiter) {
|
if (x->connect_waiter) {
|
||||||
|
bsd_log(MG_BSD_LOG_CONNECT, "OK", x, c, NULL, 0);
|
||||||
if (x->connect_result) *x->connect_result = 0;
|
if (x->connect_result) *x->connect_result = 0;
|
||||||
TaskHandle_t h = x->connect_waiter;
|
TaskHandle_t h = x->connect_waiter;
|
||||||
x->connect_waiter = NULL; x->connect_result = NULL;
|
x->connect_waiter = NULL; x->connect_result = NULL;
|
||||||
xTaskNotifyGive(h);
|
xTaskNotifyGive(h);
|
||||||
}
|
}
|
||||||
|
} else if (ev == MG_EV_ERROR) { // remember error condition
|
||||||
|
if (x->err == 0) x->err = EIO; // and let CLOSE handle it
|
||||||
|
bsd_log(MG_BSD_LOG_CLOSE, "ERR", c, x, NULL, (long) x->err);
|
||||||
} else if (ev == MG_EV_CLOSE) {
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
bsd_log(MG_BSD_LOG_CLOSE, "IN", c, x, NULL, -1);
|
||||||
x->c = NULL; x->closed = true; c->fn_data = NULL;
|
x->c = NULL; x->closed = true; c->fn_data = NULL;
|
||||||
// If connect() is still waiting, signal failure
|
// If connect() is still waiting, signal failure
|
||||||
if (x->connect_waiter) {
|
if (x->connect_waiter) {
|
||||||
|
bsd_log(MG_BSD_LOG_CONNECT, "FAIL", x, c, NULL, -1);
|
||||||
if (x->connect_result) *x->connect_result = -1;
|
if (x->connect_result) *x->connect_result = -1;
|
||||||
TaskHandle_t h = x->connect_waiter;
|
TaskHandle_t h = x->connect_waiter;
|
||||||
x->connect_waiter = NULL; x->connect_result = NULL;
|
x->connect_waiter = NULL; x->connect_result = NULL;
|
||||||
xTaskNotifyGive(h);
|
xTaskNotifyGive(h);
|
||||||
|
// The notified task owns the transport and can close/free it immediately.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (x->recv_q) { struct mg_bsd_chunk eof = {.len = 0}; xQueueSend(x->recv_q, &eof, 0); }
|
if (x->orphan) { // connection --> socket attachment failed
|
||||||
if (x->accept_q) { struct mg_xport *nil = NULL; xQueueSend(x->accept_q, &nil, 0); }
|
x->accept_q = NULL; // borrowed from listener; do not delete it here
|
||||||
|
bsd_log(MG_BSD_LOG_CLOSE, "FREE", c, x, NULL, -1);
|
||||||
|
mg_bsd_transport_free(x); // connection closed, release resources
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (x->recv_q) {
|
||||||
|
struct mg_bsd_chunk eof;
|
||||||
|
bool ok;
|
||||||
|
memset(&eof, 0, sizeof(eof));
|
||||||
|
eof.len = 0;
|
||||||
|
ok = bsd_qsend(x->recv_q, &eof, 0, false);
|
||||||
|
bsd_log(MG_BSD_LOG_CLOSE, "EOF>", c, x, NULL, ok ? 1 : 0);
|
||||||
|
if (!ok) MG_ERROR(("recv_q close notification failed"));
|
||||||
|
// recv() wakeup transfers control to the transport owner.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (x->accept_q) {
|
||||||
|
struct mg_xport *nil = NULL;
|
||||||
|
bool ok;
|
||||||
|
ok = bsd_qsend(x->accept_q, &nil, 0, false);
|
||||||
|
bsd_log(MG_BSD_LOG_CLOSE, "ACCEPT>", c, x, NULL, ok ? 1 : 0);
|
||||||
|
if (!ok) MG_ERROR(("accept_q close notification failed"));
|
||||||
|
// accept() wakeup transfers control to the transport owner.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bsd_log(MG_BSD_LOG_CLOSE, "OUT", c, x, NULL, -1);
|
||||||
}
|
}
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
@@ -357,36 +507,52 @@ void mg_bsd_poll(struct mg_mgr *mgr) {
|
|||||||
cmd.x->c = c;
|
cmd.x->c = c;
|
||||||
*cmd.result = c ? 0 : -1;
|
*cmd.result = c ? 0 : -1;
|
||||||
} else if (cmd.type == BSD_CMD_CLOSE) {
|
} else if (cmd.type == BSD_CMD_CLOSE) {
|
||||||
|
bsd_log(MG_BSD_LOG_TRANSPORT, "CMD", cmd.x, NULL, NULL, -1);
|
||||||
if (cmd.x->c) {
|
if (cmd.x->c) {
|
||||||
cmd.x->c->fn_data = NULL;
|
cmd.x->c->fn_data = NULL;
|
||||||
cmd.x->c->is_draining = 1;
|
cmd.x->c->is_draining = 1;
|
||||||
|
cmd.x->c = NULL;
|
||||||
}
|
}
|
||||||
|
cmd.x->closed = true;
|
||||||
*cmd.result = 0;
|
*cmd.result = 0;
|
||||||
} else if (cmd.type == BSD_CMD_CONNECT) {
|
} else if (cmd.type == BSD_CMD_CONNECT) {
|
||||||
cmd.x->connect_waiter = cmd.caller;
|
cmd.x->connect_waiter = cmd.caller;
|
||||||
cmd.x->connect_result = cmd.result;
|
cmd.x->connect_result = cmd.result;
|
||||||
struct mg_connection *c = mg_connect(mgr, cmd.url, xport_ev, cmd.x);
|
struct mg_connection *c = mg_connect(mgr, cmd.url, xport_ev, cmd.x);
|
||||||
|
bsd_log(MG_BSD_LOG_CONNECT, "NEW", cmd.x, c, cmd.url, c ? 1 : 0);
|
||||||
cmd.x->c = c;
|
cmd.x->c = c;
|
||||||
if (!c) { *cmd.result = -1; cmd.x->connect_waiter = NULL; cmd.x->connect_result = NULL; }
|
if (!c) { *cmd.result = -1; cmd.x->connect_waiter = NULL; cmd.x->connect_result = NULL; }
|
||||||
else notify = false; // xport_ev notifies when connected or on error
|
else notify = false; // xport_ev notifies when connected or on error
|
||||||
} else if (cmd.type == BSD_CMD_RESOLVE) {
|
} else if (cmd.type == BSD_CMD_RESOLVE) {
|
||||||
s_resolve.done = s_resolve.error = false;
|
s_resolve.error = false;
|
||||||
s_resolve.caller = cmd.caller;
|
s_resolve.caller = cmd.caller;
|
||||||
char url[80];
|
struct mg_connection *c;
|
||||||
snprintf(url, sizeof(url), "tcp://%s:0", cmd.url);
|
c = mg_alloc_conn(mgr);
|
||||||
if (!mg_connect(mgr, url, resolve_cb, NULL)) s_resolve.error = true;
|
if (c == NULL) {
|
||||||
else notify = false; // resolve_cb notifies when done
|
} else {
|
||||||
|
c->fn = resolve_cb;
|
||||||
|
LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
|
||||||
|
mg_call(c, MG_EV_OPEN, NULL);
|
||||||
|
MG_DEBUG(("%lu resolve %s", c->id, cmd.url));
|
||||||
|
mg_resolve(c, cmd.url);
|
||||||
|
notify = false; // resolve_cb notifies when done
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (notify) xTaskNotifyGive(cmd.caller);
|
if (notify) xTaskNotifyGive(cmd.caller);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *mg_bsd_transport_new(int domain, int type, int proto) {
|
void *mg_bsd_transport_new(int domain, int type, int proto) {
|
||||||
(void) domain; (void) type; (void) proto;
|
if (domain != AF_INET || type != SOCK_STREAM ||
|
||||||
// For socket() calls: allocate accept_q only; recv/send added when needed
|
(proto != 0 && proto != IPPROTO_TCP)) {
|
||||||
|
errno = EPROTONOSUPPORT;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// For socket() calls: allocate accept_q only; recv/send added when needed.
|
||||||
|
// +1 keeps a terminal slot for MG_EV_CLOSE without blocking Mongoose.
|
||||||
struct mg_xport *x = (struct mg_xport *) calloc(1, sizeof(*x));
|
struct mg_xport *x = (struct mg_xport *) calloc(1, sizeof(*x));
|
||||||
if (!x) return NULL;
|
if (!x) return NULL;
|
||||||
x->accept_q = xQueueCreate(MG_BSD_BACKLOG, sizeof(struct mg_xport *));
|
x->accept_q = xQueueCreate(MG_BSD_BACKLOG + 1, sizeof(struct mg_xport *));
|
||||||
if (!x->accept_q) { free(x); return NULL; }
|
if (!x->accept_q) { free(x); return NULL; }
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@@ -394,6 +560,7 @@ void *mg_bsd_transport_new(int domain, int type, int proto) {
|
|||||||
void mg_bsd_transport_free(void *t) {
|
void mg_bsd_transport_free(void *t) {
|
||||||
struct mg_xport *x = (struct mg_xport *) t;
|
struct mg_xport *x = (struct mg_xport *) t;
|
||||||
if (!x) return;
|
if (!x) return;
|
||||||
|
bsd_log(MG_BSD_LOG_TRANSPORT, "FREE", x, NULL, NULL, -1);
|
||||||
if (x->recv_q) vQueueDelete(x->recv_q);
|
if (x->recv_q) vQueueDelete(x->recv_q);
|
||||||
if (x->send_q) vQueueDelete(x->send_q);
|
if (x->send_q) vQueueDelete(x->send_q);
|
||||||
if (x->accept_q) vQueueDelete(x->accept_q);
|
if (x->accept_q) vQueueDelete(x->accept_q);
|
||||||
@@ -405,7 +572,7 @@ int mg_bsd_transport_listen(void *t, const struct sockaddr_in *addr) {
|
|||||||
int result = -1;
|
int result = -1;
|
||||||
struct mg_bsd_cmd cmd = {BSD_CMD_LISTEN, x, {0}, xTaskGetCurrentTaskHandle(), &result};
|
struct mg_bsd_cmd cmd = {BSD_CMD_LISTEN, x, {0}, xTaskGetCurrentTaskHandle(), &result};
|
||||||
snprintf(cmd.url, sizeof(cmd.url), "tcp://0.0.0.0:%d", mg_ntohs(addr->sin_port));
|
snprintf(cmd.url, sizeof(cmd.url), "tcp://0.0.0.0:%d", mg_ntohs(addr->sin_port));
|
||||||
xQueueSend(s_cmd_q, &cmd, portMAX_DELAY);
|
if (!bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false)) return -1;
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -414,28 +581,50 @@ void *mg_bsd_transport_accept(void *t, struct sockaddr_in *peer, bool nonblock)
|
|||||||
struct mg_xport *x = (struct mg_xport *) t;
|
struct mg_xport *x = (struct mg_xport *) t;
|
||||||
struct mg_xport *nx = NULL;
|
struct mg_xport *nx = NULL;
|
||||||
TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
|
TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
|
||||||
if (xQueueReceive(x->accept_q, &nx, ticks) != pdTRUE || !nx) return NULL;
|
if (xQueueReceive(x->accept_q, &nx, ticks) != pdTRUE) {
|
||||||
|
errno = x->closed ? (x->err ? x->err : EIO) : (nonblock ? EAGAIN : EIO);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!nx) { errno = x->err ? x->err : EIO; return NULL; }
|
||||||
if (peer) *peer = nx->peer;
|
if (peer) *peer = nx->peer;
|
||||||
return nx;
|
return nx;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t mg_bsd_transport_recv(void *t, void *buf, size_t len, bool nonblock) {
|
ssize_t mg_bsd_transport_recv(void *t, void *buf, size_t len, bool nonblock) {
|
||||||
struct mg_xport *x = (struct mg_xport *) t;
|
struct mg_xport *x = (struct mg_xport *) t;
|
||||||
struct mg_bsd_chunk chunk;
|
uint8_t *p = (uint8_t *) buf;
|
||||||
|
size_t recvd = 0;
|
||||||
TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
|
TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
|
||||||
if (xQueueReceive(x->recv_q, &chunk, ticks) != pdTRUE) {
|
while (recvd < len) {
|
||||||
errno = EAGAIN;
|
struct mg_bsd_chunk chunk;
|
||||||
return -1;
|
if (xQueuePeek(x->recv_q, &chunk, recvd == 0 ? ticks : 0) != pdTRUE) {
|
||||||
|
if (recvd > 0) break;
|
||||||
|
errno = x->closed ? (x->err ? x->err : EIO) : (nonblock ? EAGAIN : EIO);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (chunk.len == 0) {
|
||||||
|
if (recvd > 0) break;
|
||||||
|
xQueueReceive(x->recv_q, &chunk, 0);
|
||||||
|
if (x->err) { errno = x->err; return -1; }
|
||||||
|
return 0; // EOF
|
||||||
|
} else {
|
||||||
|
size_t n = chunk.len - x->rx_off;
|
||||||
|
if (n > len - recvd) n = len - recvd;
|
||||||
|
memcpy(p + recvd, chunk.data + x->rx_off, n);
|
||||||
|
recvd += n;
|
||||||
|
x->rx_off = (uint16_t) (x->rx_off + n);
|
||||||
|
if (x->rx_off >= chunk.len) {
|
||||||
|
xQueueReceive(x->recv_q, &chunk, 0);
|
||||||
|
x->rx_off = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (chunk.len == 0) return 0; // EOF
|
return (ssize_t) recvd;
|
||||||
size_t n = chunk.len < len ? chunk.len : len;
|
|
||||||
memcpy(buf, chunk.data, n);
|
|
||||||
return (ssize_t) n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonblock) {
|
ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonblock) {
|
||||||
struct mg_xport *x = (struct mg_xport *) t;
|
struct mg_xport *x = (struct mg_xport *) t;
|
||||||
if (x->closed) return -1;
|
if (x->closed) { errno = x->err ? x->err : EPIPE; return -1; }
|
||||||
size_t sent = 0;
|
size_t sent = 0;
|
||||||
TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
|
TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
|
||||||
while (sent < len) {
|
while (sent < len) {
|
||||||
@@ -444,7 +633,10 @@ ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonbloc
|
|||||||
if (n > MG_BSD_CHUNK_SIZE) n = MG_BSD_CHUNK_SIZE;
|
if (n > MG_BSD_CHUNK_SIZE) n = MG_BSD_CHUNK_SIZE;
|
||||||
memcpy(chunk.data, (const uint8_t *) buf + sent, n);
|
memcpy(chunk.data, (const uint8_t *) buf + sent, n);
|
||||||
chunk.len = (uint16_t) n;
|
chunk.len = (uint16_t) n;
|
||||||
if (xQueueSend(x->send_q, &chunk, ticks) != pdTRUE) break;
|
if (!bsd_qsend(x->send_q, &chunk, ticks, false)) {
|
||||||
|
errno = x->closed ? (x->err ? x->err : EPIPE) : (nonblock ? EAGAIN : EIO);
|
||||||
|
return sent > 0 ? (ssize_t) sent : -1;
|
||||||
|
}
|
||||||
sent += n;
|
sent += n;
|
||||||
}
|
}
|
||||||
return sent > 0 ? (ssize_t) sent : (errno = EAGAIN, -1);
|
return sent > 0 ? (ssize_t) sent : (errno = EAGAIN, -1);
|
||||||
@@ -453,7 +645,7 @@ ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonbloc
|
|||||||
int mg_bsd_transport_connect(void *t, const struct sockaddr_in *addr, bool nonblock) {
|
int mg_bsd_transport_connect(void *t, const struct sockaddr_in *addr, bool nonblock) {
|
||||||
struct mg_xport *x = (struct mg_xport *) t;
|
struct mg_xport *x = (struct mg_xport *) t;
|
||||||
(void) nonblock;
|
(void) nonblock;
|
||||||
if (!x->recv_q) x->recv_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
|
if (!x->recv_q) x->recv_q = xQueueCreate(MG_BSD_Q_DEPTH + 1, sizeof(struct mg_bsd_chunk));
|
||||||
if (!x->send_q) x->send_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
|
if (!x->send_q) x->send_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
|
||||||
if (!x->recv_q || !x->send_q) { errno = ENOMEM; return -1; }
|
if (!x->recv_q || !x->send_q) { errno = ENOMEM; return -1; }
|
||||||
int result = -1;
|
int result = -1;
|
||||||
@@ -461,22 +653,18 @@ int mg_bsd_transport_connect(void *t, const struct sockaddr_in *addr, bool nonbl
|
|||||||
uint8_t *ip = (uint8_t *) &addr->sin_addr.s_addr;
|
uint8_t *ip = (uint8_t *) &addr->sin_addr.s_addr;
|
||||||
snprintf(cmd.url, sizeof(cmd.url), "tcp://%d.%d.%d.%d:%d",
|
snprintf(cmd.url, sizeof(cmd.url), "tcp://%d.%d.%d.%d:%d",
|
||||||
ip[0], ip[1], ip[2], ip[3], mg_ntohs(addr->sin_port));
|
ip[0], ip[1], ip[2], ip[3], mg_ntohs(addr->sin_port));
|
||||||
xQueueSend(s_cmd_q, &cmd, portMAX_DELAY);
|
bsd_log(MG_BSD_LOG_CONNECT, "REQ", x, NULL, cmd.url, -1);
|
||||||
|
if (!bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false)) return -1;
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
if (result != 0) errno = x->err ? x->err : EIO;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// gethostbyname: resolve via Mongoose DNS (not reentrant)
|
// gethostbyname: resolve via Mongoose DNS (not reentrant)
|
||||||
static struct hostent s_hostent;
|
|
||||||
static char *s_h_aliases[1];
|
|
||||||
static char *s_h_addr_list[2];
|
|
||||||
static uint32_t s_h_addr;
|
|
||||||
static char s_h_name[64];
|
|
||||||
|
|
||||||
struct hostent *gethostbyname(const char *name) {
|
struct hostent *gethostbyname(const char *name) {
|
||||||
struct mg_bsd_cmd cmd = {BSD_CMD_RESOLVE, NULL, {0}, xTaskGetCurrentTaskHandle(), NULL};
|
struct mg_bsd_cmd cmd = {BSD_CMD_RESOLVE, NULL, {0}, xTaskGetCurrentTaskHandle(), NULL};
|
||||||
snprintf(cmd.url, sizeof(cmd.url), "%s", name);
|
snprintf(cmd.url, sizeof(cmd.url), "%s", name);
|
||||||
xQueueSend(s_cmd_q, &cmd, portMAX_DELAY);
|
if (!bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false)) return NULL;
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
if (s_resolve.error) return NULL;
|
if (s_resolve.error) return NULL;
|
||||||
s_h_addr = s_resolve.addr.addr.ip4;
|
s_h_addr = s_resolve.addr.addr.ip4;
|
||||||
@@ -521,12 +709,18 @@ void freeaddrinfo(struct addrinfo *res) {
|
|||||||
|
|
||||||
void mg_bsd_transport_close(void *t) {
|
void mg_bsd_transport_close(void *t) {
|
||||||
struct mg_xport *x = (struct mg_xport *) t;
|
struct mg_xport *x = (struct mg_xport *) t;
|
||||||
|
bsd_log(MG_BSD_LOG_TRANSPORT, "CLOSE", x, NULL, NULL, -1);
|
||||||
if (!x->closed && x->c) {
|
if (!x->closed && x->c) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
struct mg_bsd_cmd cmd = {BSD_CMD_CLOSE, x, {0}, xTaskGetCurrentTaskHandle(), &result};
|
struct mg_bsd_cmd cmd = {BSD_CMD_CLOSE, x, {0}, xTaskGetCurrentTaskHandle(), &result};
|
||||||
xQueueSend(s_cmd_q, &cmd, portMAX_DELAY);
|
bool ok = bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false);
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
bsd_log(MG_BSD_LOG_TRANSPORT, "CMD>", x, NULL, NULL, ok ? 1 : 0);
|
||||||
|
if (ok) {
|
||||||
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
bsd_log(MG_BSD_LOG_RESULT, "CMD", x, NULL, NULL, (long) result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
bsd_log(MG_BSD_LOG_TRANSPORT, "FREE_REQ", x, NULL, NULL, -1);
|
||||||
mg_bsd_transport_free(x);
|
mg_bsd_transport_free(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,9 +793,8 @@ bool mg_wakeup(struct mg_mgr *mgr, unsigned long conn_id, const void *buf,
|
|||||||
m->id = conn_id;
|
m->id = conn_id;
|
||||||
m->len = len;
|
m->len = len;
|
||||||
memcpy(m->data, buf, len);
|
memcpy(m->data, buf, len);
|
||||||
if (xQueueSend((QueueHandle_t) mgr->pipe.q, &m, 0) != pdTRUE) {
|
if (!bsd_qsend((QueueHandle_t) mgr->pipe.q, &m, 0, false)) {
|
||||||
free(m);
|
free(m);
|
||||||
MG_ERROR(("xQueueSend"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+4
-2
@@ -167,9 +167,11 @@ static size_t mg_dash_print_endpoint(mg_pfn_t fn, void *arg, va_list *ap) {
|
|||||||
void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *set) {
|
void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *set) {
|
||||||
struct mg_connection *c;
|
struct mg_connection *c;
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||||
struct mg_dash_cdata *d = (struct mg_dash_cdata *) c->data;
|
struct mg_dash_cdata *d;
|
||||||
struct mg_dash_user *u = d->u;
|
struct mg_dash_user *u;
|
||||||
if (!c->is_websocket) continue;
|
if (!c->is_websocket) continue;
|
||||||
|
d = (struct mg_dash_cdata *) c->data;
|
||||||
|
u = d->u;
|
||||||
if (u == NULL) continue;
|
if (u == NULL) continue;
|
||||||
if (set->index != NULL && *set->index < 0) {
|
if (set->index != NULL && *set->index < 0) {
|
||||||
int sz = mg_dash_array_size(set, u);
|
int sz = mg_dash_array_size(set, u);
|
||||||
|
|||||||
@@ -228,16 +228,16 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
static bool dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||||
uint16_t txnid, bool ipv6) {
|
unsigned int rtype, uint16_t txnid, uint16_t flags) {
|
||||||
struct {
|
struct { // RFC-1035 4.1.2
|
||||||
struct mg_dns_header header;
|
struct mg_dns_header header;
|
||||||
uint8_t data[256];
|
uint8_t data[256];
|
||||||
} pkt;
|
} pkt;
|
||||||
size_t i, n;
|
size_t i, n;
|
||||||
memset(&pkt, 0, sizeof(pkt));
|
memset(&pkt, 0, sizeof(pkt));
|
||||||
pkt.header.txnid = mg_htons(txnid);
|
pkt.header.txnid = mg_htons(txnid);
|
||||||
pkt.header.flags = mg_htons(0x100);
|
pkt.header.flags = mg_htons(flags);
|
||||||
pkt.header.num_questions = mg_htons(1);
|
pkt.header.num_questions = mg_htons(1);
|
||||||
for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
|
for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
|
||||||
if (name->buf[i] == '.' || i >= name->len) {
|
if (name->buf[i] == '.' || i >= name->len) {
|
||||||
@@ -247,14 +247,19 @@ static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
|||||||
}
|
}
|
||||||
if (i >= name->len) break;
|
if (i >= name->len) break;
|
||||||
}
|
}
|
||||||
memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
|
memset(&pkt.data[n], 0, 5); // nul, QTYPE, QCLASS
|
||||||
|
pkt.data[n + 2] = (uint8_t) rtype; // QTYPE = rtype, only 0-255 supported
|
||||||
|
pkt.data[n + 4] = 1; // QCLASS = IN
|
||||||
n += 5;
|
n += 5;
|
||||||
if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
|
|
||||||
// memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
|
|
||||||
// n += 6;
|
|
||||||
return mg_send(c, &pkt, sizeof(pkt.header) + n);
|
return mg_send(c, &pkt, sizeof(pkt.header) + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||||
|
uint16_t txnid, bool ipv6) {
|
||||||
|
return dns_send(c, name, ipv6 ? MG_DNS_RTYPE_AAAA : MG_DNS_RTYPE_A, txnid,
|
||||||
|
0x100); // RD
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -458,7 +463,6 @@ static void handle_mdns_query(struct mg_connection *c) {
|
|||||||
MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype,
|
MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype,
|
||||||
(unsigned int) rr.aclass, name));
|
(unsigned int) rr.aclass, name));
|
||||||
if (rr.atype == MG_DNS_RTYPE_A) {
|
if (rr.atype == MG_DNS_RTYPE_A) {
|
||||||
// TODO(): ensure c->fn_data ends in \0
|
|
||||||
// if we have a name to match, go; otherwise users will match and fill
|
// if we have a name to match, go; otherwise users will match and fill
|
||||||
// req.r.name and set req.is_resp
|
// req.r.name and set req.is_resp
|
||||||
if (c->fn_data != NULL && mg_casecmp((char *) c->fn_data, name) != 0)
|
if (c->fn_data != NULL && mg_casecmp((char *) c->fn_data, name) != 0)
|
||||||
@@ -574,43 +578,149 @@ static void handle_mdns_query(struct mg_connection *c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static size_t srvtoname(char *svc) {
|
||||||
|
char *dot = strchr(svc, '.');
|
||||||
|
if (dot == NULL) return 0;
|
||||||
|
*dot = '\0';
|
||||||
|
return (size_t) (dot - svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MG_MAX_MDNS_RECORDS 8
|
||||||
|
// Condense one useful mDNS/DNS-SD chain from a response datagram into a single
|
||||||
|
// MG_EV_MDNS_RESP event. A/AAAA as the first supported record are terminal
|
||||||
|
// address answers (AAAA currently ignored). For PTR/SRV/TXT, subsequent
|
||||||
|
// records are attached only when their label matches the service instance, or
|
||||||
|
// the SRV target for address records.
|
||||||
|
// Some devices send SRV/TXT as extra Answer RRs instead of Additional RRs, so
|
||||||
|
// both sections are parsed as one chain.
|
||||||
static void handle_mdns_response(struct mg_connection *c) {
|
static void handle_mdns_response(struct mg_connection *c) {
|
||||||
struct mg_dns_header *rh = (struct mg_dns_header *) c->recv.buf;
|
struct mg_dns_header *rh = (struct mg_dns_header *) c->recv.buf;
|
||||||
struct mg_dns_rr rr;
|
struct mg_dns_rr rr, rr_[5]; // A, PTR, SRV, TXT, AAAA, in that order
|
||||||
size_t n;
|
struct mg_mdns_resp resp;
|
||||||
// Parse first response, offset 12 is header size
|
size_t n, i, num, answers, roff_[5], roff = 12; // offset 12 is header size
|
||||||
n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, false, &rr);
|
// RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
|
||||||
MG_VERBOSE(("mDNS response parsed, result=%d", (int) n));
|
char name[256], srvcproto[256], instance[256], host[256];
|
||||||
if (n > 0) {
|
uint16_t atype = 0;
|
||||||
// RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
|
answers = mg_ntohs(rh->num_answers);
|
||||||
char name[256];
|
num = answers + mg_ntohs(rh->num_other_prs);
|
||||||
uint8_t name_len;
|
if (answers == 0) return;
|
||||||
struct mg_mdns_resp resp;
|
MG_VERBOSE(("mDNS response: %u answer, %u additional", (unsigned) answers,
|
||||||
memset(&resp, 0, sizeof(resp));
|
(unsigned) (num - answers)));
|
||||||
if (rh->num_answers > mg_htons(1)) MG_DEBUG(("ignoring > 1 answers"));
|
if (num > MG_MAX_MDNS_RECORDS) {
|
||||||
mg_dns_parse_name(c->recv.buf, c->recv.len, 12, name, sizeof(name));
|
num = MG_MAX_MDNS_RECORDS;
|
||||||
name_len = (uint8_t) strlen(name);
|
MG_DEBUG(("ignoring > %u records", MG_MAX_MDNS_RECORDS));
|
||||||
MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype,
|
|
||||||
(unsigned int) rr.aclass, name));
|
|
||||||
if (rr.alen == 4 && rr.atype == MG_DNS_RTYPE_A &&
|
|
||||||
(rr.aclass & 0x7FFF) == 1) {
|
|
||||||
resp.addr.is_ip6 = false;
|
|
||||||
memcpy(resp.addr.addr.ip, (char *) (rh + 1) + n - 4, 4);
|
|
||||||
MG_DEBUG(("A response from %.*s = %M", name_len, name, mg_print_ip,
|
|
||||||
&resp.addr));
|
|
||||||
// } else if (rr.alen == 16 && rr.atype == MG_DNS_RTYPE_AAAA &&
|
|
||||||
// (rr.aclass & 0x7FFF) == 1) {
|
|
||||||
// resp.addr.is_ip6 = true;
|
|
||||||
// memcpy(resp.addr.addr.ip, (char *)(rh + 1) + n - 16], 16);
|
|
||||||
// MG_DEBUG(("AAAA response from %.*s = %M", name_len, name,
|
|
||||||
// mg_print_ip, &resp.addr));
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resp.name = mg_str_n(name, name_len);
|
|
||||||
resp.rr = &rr;
|
|
||||||
mg_call(c, MG_EV_MDNS_RESP, &resp);
|
|
||||||
}
|
}
|
||||||
|
memset(roff_, 0, sizeof(roff_));
|
||||||
|
memset(&rr_, 0, sizeof(rr_));
|
||||||
|
memset(&resp, 0, sizeof(resp));
|
||||||
|
instance[0] = host[0] = '\0';
|
||||||
|
for (i = 0; i < num; i++) { // First Answer RR is primary; the rest must match it
|
||||||
|
n = mg_dns_parse_rr(c->recv.buf, c->recv.len, roff, false, &rr);
|
||||||
|
MG_VERBOSE(("mDNS record parsed, result=%u", n));
|
||||||
|
if (n == 0) return;
|
||||||
|
if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff, name, sizeof(name)) == 0) return;
|
||||||
|
MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype, (unsigned int) rr.aclass, name));
|
||||||
|
if (rr.alen == 4 && rr.atype == MG_DNS_RTYPE_A && (rr.aclass & 0x7FFF) == 1 &&
|
||||||
|
(i == 0 || (host[0] != '\0' && strcmp(name, host) == 0) ||
|
||||||
|
(host[0] == '\0' && instance[0] != '\0' && strcmp(name, instance) == 0))) {
|
||||||
|
if (rr_[0].atype != 0) break;
|
||||||
|
if (i == 0) atype = rr.atype;
|
||||||
|
resp.name = mg_str(name);
|
||||||
|
resp.addr.is_ip6 = false;
|
||||||
|
memcpy(resp.addr.addr.ip, c->recv.buf + roff + n - 4, 4);
|
||||||
|
MG_VERBOSE(("A record"));
|
||||||
|
roff_[0] = roff, rr_[0] = rr;
|
||||||
|
if (atype == MG_DNS_RTYPE_A) break;
|
||||||
|
} else if (rr.alen == 16 && rr.atype == MG_DNS_RTYPE_AAAA && (rr.aclass & 0x7FFF) == 1 &&
|
||||||
|
(i == 0 || (host[0] != '\0' && strcmp(name, host) == 0) ||
|
||||||
|
(host[0] == '\0' && instance[0] != '\0' && strcmp(name, instance) == 0))) {
|
||||||
|
# if 0
|
||||||
|
if (rr_[4].atype != 0) break;
|
||||||
|
resp.addr.is_ip6 = true;
|
||||||
|
if (i == 0) atype = rr.atype;
|
||||||
|
resp.name = mg_str(name);
|
||||||
|
memcpy(resp.addr.addr.ip, c->recv.buf + roff + n - 16, 16);
|
||||||
|
MG_VERBOSE(("AAAA record"));
|
||||||
|
roff_[4] = roff, rr_[4] = rr;
|
||||||
|
if (atype == MG_DNS_RTYPE_AAAA) break;
|
||||||
|
#else
|
||||||
|
MG_VERBOSE(("ignored AAAA record"));
|
||||||
|
#endif
|
||||||
|
} else if (rr.atype == MG_DNS_RTYPE_PTR && (rr.aclass & 0x7FFF) == 1 && i == 0) {
|
||||||
|
if (rr_[1].atype != 0) break;
|
||||||
|
atype = rr.atype;
|
||||||
|
if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff + rr.nlen + 10,
|
||||||
|
instance, sizeof(instance)) == 0) return;
|
||||||
|
MG_VERBOSE(("PTR record"));
|
||||||
|
roff_[1] = roff, rr_[1] = rr;
|
||||||
|
} else if (rr.atype == MG_DNS_RTYPE_SRV && rr.alen >= 6 && (rr.aclass & 0x7FFF) == 1 &&
|
||||||
|
(i == 0 || (instance[0] != '\0' && strcmp(name, instance) == 0))) {
|
||||||
|
if (rr_[2].atype != 0) break;
|
||||||
|
if (i == 0) atype = rr.atype;
|
||||||
|
resp.sd.port = MG_LOAD_BE16(c->recv.buf + roff + rr.nlen + 14);
|
||||||
|
resp.addr.port = mg_htons(resp.sd.port);
|
||||||
|
if (instance[0] == '\0') mg_snprintf(instance, sizeof(instance), "%s", name);
|
||||||
|
if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff + rr.nlen + 16,
|
||||||
|
host, sizeof(host)) == 0) return;
|
||||||
|
MG_VERBOSE(("SRV record"));
|
||||||
|
roff_[2] = roff, rr_[2] = rr;
|
||||||
|
} else if (rr.atype == MG_DNS_RTYPE_TXT && (rr.aclass & 0x7FFF) == 1 &&
|
||||||
|
(i == 0 || (instance[0] != '\0' && strcmp(name, instance) == 0))) {
|
||||||
|
if (rr_[3].atype != 0) break;
|
||||||
|
if (i == 0) atype = rr.atype;
|
||||||
|
if (instance[0] == '\0') mg_snprintf(instance, sizeof(instance), "%s", name);
|
||||||
|
resp.sd.txt = mg_str_n((char *) c->recv.buf + roff + rr.nlen + 10, rr.alen);
|
||||||
|
MG_VERBOSE(("TXT record"));
|
||||||
|
roff_[3] = roff, rr_[3] = rr;
|
||||||
|
}
|
||||||
|
roff += n;
|
||||||
|
if (i == 0 && atype == 0) return;
|
||||||
|
}
|
||||||
|
if (atype == MG_DNS_RTYPE_A) {
|
||||||
|
resp.rr = &rr_[0];
|
||||||
|
MG_DEBUG(("A response from %s = %M", name, mg_print_ip, &resp.addr));
|
||||||
|
#if 0
|
||||||
|
} else if (atype == MG_DNS_RTYPE_AAAA) {
|
||||||
|
resp.rr = &rr_[4];
|
||||||
|
MG_DEBUG(("AAAA response from %s = %M", name, mg_print_ip6, resp.addr.addr.ip));
|
||||||
|
#endif
|
||||||
|
} else if (atype == MG_DNS_RTYPE_PTR) {
|
||||||
|
size_t len;
|
||||||
|
resp.rr = &rr_[1];
|
||||||
|
if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff_[1], srvcproto,
|
||||||
|
sizeof(srvcproto)) == 0) return;
|
||||||
|
len = strlen(srvcproto);
|
||||||
|
if (len <= 6) return;
|
||||||
|
resp.sd.srvcproto = mg_str_n(srvcproto, len - 6); // remove .local
|
||||||
|
if (host[0] != '\0') {
|
||||||
|
resp.name = mg_str(host);
|
||||||
|
} else if (rr_[0].atype == 0) {
|
||||||
|
mg_snprintf(name, sizeof(name), "%s", instance);
|
||||||
|
resp.name = mg_str_n(name, srvtoname(name));
|
||||||
|
} else {
|
||||||
|
if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff_[0], name,
|
||||||
|
sizeof(name)) == 0) return;
|
||||||
|
resp.name = mg_str(name);
|
||||||
|
}
|
||||||
|
MG_DEBUG(("PTR response for %s from %s", srvcproto, name));
|
||||||
|
} else if (atype == MG_DNS_RTYPE_SRV || atype == MG_DNS_RTYPE_TXT) {
|
||||||
|
size_t len, name_len;
|
||||||
|
resp.rr = atype == MG_DNS_RTYPE_SRV ? &rr_[2] : &rr_[3];
|
||||||
|
if (mg_dns_parse_name(c->recv.buf, c->recv.len,
|
||||||
|
atype == MG_DNS_RTYPE_SRV ? roff_[2] : roff_[3],
|
||||||
|
srvcproto, sizeof(srvcproto)) == 0)
|
||||||
|
return;
|
||||||
|
len = strlen(srvcproto);
|
||||||
|
name_len = srvtoname(srvcproto);
|
||||||
|
if (name_len == 0 || len <= name_len + 7) return; // remove .local
|
||||||
|
resp.sd.srvcproto = mg_str_n(srvcproto + name_len + 1, len - name_len - 7);
|
||||||
|
resp.name = mg_str_n(srvcproto, name_len);
|
||||||
|
MG_DEBUG(("%s response for %s from %s", atype == MG_DNS_RTYPE_SRV ? "SRV" : "TXT", srvcproto, name));
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mg_call(c, MG_EV_MDNS_RESP, &resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_mdns_record(struct mg_connection *c) {
|
static void handle_mdns_record(struct mg_connection *c) {
|
||||||
@@ -684,8 +794,7 @@ struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
|
|||||||
static bool mdns_query(struct mg_connection *c, struct mg_str *name,
|
static bool mdns_query(struct mg_connection *c, struct mg_str *name,
|
||||||
unsigned int rtype) {
|
unsigned int rtype) {
|
||||||
mg_multicast_restore(c, (uint8_t *) &c->loc);
|
mg_multicast_restore(c, (uint8_t *) &c->loc);
|
||||||
(void) rtype;
|
return dns_send(c, name, rtype, 0, 0); // RFC-6762 18.1 id = 0, 18.6 RD = 0
|
||||||
return mg_dns_send(c, name, 0, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mg_mdns_query(struct mg_connection *c, const char *name,
|
bool mg_mdns_query(struct mg_connection *c, const char *name,
|
||||||
|
|||||||
@@ -60,11 +60,12 @@ struct mg_mdns_req {
|
|||||||
bool is_unicast; // True if the client requested a unicast (QU) response
|
bool is_unicast; // True if the client requested a unicast (QU) response
|
||||||
};
|
};
|
||||||
|
|
||||||
// ev_data for MG_EV_MDNS_RESP, carrying the resolved address from an mDNS response.
|
// ev_data for MG_EV_MDNS_RESP, carrying address/data from a mDNS response
|
||||||
struct mg_mdns_resp {
|
struct mg_mdns_resp {
|
||||||
struct mg_dns_rr *rr; // Resource record from the response
|
struct mg_dns_rr *rr; // Resource record from the response (1st in chain)
|
||||||
struct mg_str name; // Resolved hostname, without the .local suffix
|
struct mg_str name; // Resolved hostname, without the .local suffix
|
||||||
struct mg_addr addr; // Resolved IP address
|
struct mg_addr addr; // Resolved IP address
|
||||||
|
struct mg_dnssd_record sd; // Service Discovery data
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parses a DNS query or response from buf/len into dm. Returns true on success.
|
// Parses a DNS query or response from buf/len into dm. Returns true on success.
|
||||||
@@ -81,6 +82,7 @@ size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
|
|||||||
// Creates an mDNS listener on UDP multicast 224.0.0.251:5353. Registers it as
|
// Creates an mDNS listener on UDP multicast 224.0.0.251:5353. Registers it as
|
||||||
// the manager's mDNS resolver (mgr->mdns). Fires MG_EV_MDNS_REQ for incoming
|
// the manager's mDNS resolver (mgr->mdns). Fires MG_EV_MDNS_REQ for incoming
|
||||||
// queries and MG_EV_MDNS_RESP for incoming responses. Returns NULL on error.
|
// queries and MG_EV_MDNS_RESP for incoming responses. Returns NULL on error.
|
||||||
|
// fn_data is a NUL-terminated server name, or NULL to pass all requests to fn.
|
||||||
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
|
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
|
||||||
void *fn_data);
|
void *fn_data);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
|
|
||||||
static size_t print_atcmd(void (*out)(char, void *), void *arg, va_list *ap) {
|
static size_t print_atcmd(void (*out)(char, void *), void *arg, va_list *ap) {
|
||||||
struct mg_str s = va_arg(*ap, struct mg_str);
|
struct mg_str s = va_arg(*ap, struct mg_str);
|
||||||
for (size_t i = 0; i < s.len; i++) out(s.buf[i] < 0x20 ? '.' : s.buf[i], arg);
|
size_t i;
|
||||||
|
for (i = 0; i < s.len; i++)
|
||||||
|
out((char) (s.buf[i] < 0x20 ? '.' : s.buf[i]), arg);
|
||||||
return s.len;
|
return s.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ static bool mg_atcmd_handle(struct mg_tcpip_if *ifp) {
|
|||||||
if (dd->script_index % 2 == 0) { // send AT command
|
if (dd->script_index % 2 == 0) { // send AT command
|
||||||
const char *cmd = dd->script[dd->script_index];
|
const char *cmd = dd->script[dd->script_index];
|
||||||
MG_DEBUG(("send AT[%d]: %M", dd->script_index, print_atcmd, mg_str(cmd)));
|
MG_DEBUG(("send AT[%d]: %M", dd->script_index, print_atcmd, mg_str(cmd)));
|
||||||
while (*cmd) dd->tx(dd->usart, *cmd++);
|
while (*cmd != '\0') dd->tx(dd->usart, (uint8_t) *cmd++);
|
||||||
dd->script_index++;
|
dd->script_index++;
|
||||||
ifp->recv_queue.head = 0;
|
ifp->recv_queue.head = 0;
|
||||||
} else { // check AT command response
|
} else { // check AT command response
|
||||||
@@ -43,7 +45,7 @@ static bool mg_atcmd_handle(struct mg_tcpip_if *ifp) {
|
|||||||
return false; // FAIL
|
return false; // FAIL
|
||||||
}
|
}
|
||||||
if ((c = dd->rx(dd->usart)) < 0) return false; // no data
|
if ((c = dd->rx(dd->usart)) < 0) return false; // no data
|
||||||
q->buf[q->head++] = c;
|
q->buf[q->head++] = (char) c;
|
||||||
if (mg_match(mg_str_n(q->buf, q->head), mg_str(expect), NULL)) {
|
if (mg_match(mg_str_n(q->buf, q->head), mg_str(expect), NULL)) {
|
||||||
MG_DEBUG(("recv AT[%d]: %M", dd->script_index, print_atcmd,
|
MG_DEBUG(("recv AT[%d]: %M", dd->script_index, print_atcmd,
|
||||||
mg_str_n(q->buf, q->head)));
|
mg_str_n(q->buf, q->head)));
|
||||||
@@ -84,7 +86,7 @@ static size_t mg_atcmd_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!dd->no_byte_stuff && !byte_unstuff(dd, &b)) continue;
|
if (!dd->no_byte_stuff && !byte_unstuff(dd, &b)) continue;
|
||||||
q->buf[q->head++] = b;
|
q->buf[q->head++] = (char) b;
|
||||||
}
|
}
|
||||||
len = (q->head <= len) ? q->head : 0;
|
len = (q->head <= len) ? q->head : 0;
|
||||||
memmove(buf, q->buf, len);
|
memmove(buf, q->buf, len);
|
||||||
@@ -130,7 +132,7 @@ struct mg_tcpip_driver mg_tcpip_driver_atcmd = {mg_atcmd_init, mg_atcmd_tx, mg_a
|
|||||||
static void byte_stuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t b) {
|
static void byte_stuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t b) {
|
||||||
if ((b < 0x20) || (b == MG_HDLC_ESC) || (b == MG_HDLC_FLAG)) {
|
if ((b < 0x20) || (b == MG_HDLC_ESC) || (b == MG_HDLC_FLAG)) {
|
||||||
dd->tx(dd->usart, MG_HDLC_ESC);
|
dd->tx(dd->usart, MG_HDLC_ESC);
|
||||||
dd->tx(dd->usart, b ^ 0x20);
|
dd->tx(dd->usart, (uint8_t) (b ^ 0x20));
|
||||||
} else {
|
} else {
|
||||||
dd->tx(dd->usart, b);
|
dd->tx(dd->usart, b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash) {
|
|||||||
bool ok = false;
|
bool ok = false;
|
||||||
if (s_size == 0) {
|
if (s_size == 0) {
|
||||||
MG_ERROR(("OTA is not started, call mg_ota_begin()"));
|
MG_ERROR(("OTA is not started, call mg_ota_begin()"));
|
||||||
|
} else if (s_addr + MG_ROUND_UP(len, flash->align) >
|
||||||
|
(char *) flash->start + flash->size) {
|
||||||
|
MG_ERROR(("Flash overflow: attempting to write past the flash boundary"));
|
||||||
} else {
|
} else {
|
||||||
size_t len_aligned_down = MG_ROUND_DOWN(len, flash->align);
|
size_t len_aligned_down = MG_ROUND_DOWN(len, flash->align);
|
||||||
if (len_aligned_down) ok = flash->write_fn(s_addr, buf, len_aligned_down);
|
if (len_aligned_down) ok = flash->write_fn(s_addr, buf, len_aligned_down);
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt,
|
|||||||
is_long++, c = fmt[++i];
|
is_long++, c = fmt[++i];
|
||||||
if (c == 'l') is_long++, c = fmt[++i];
|
if (c == 'l') is_long++, c = fmt[++i];
|
||||||
}
|
}
|
||||||
|
if (c == 'z') is_long = 1, c = fmt[++i];
|
||||||
if (c == 'p') x = 1, is_long = 1;
|
if (c == 'p') x = 1, is_long = 1;
|
||||||
if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' ||
|
if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' ||
|
||||||
c == 'g' || c == 'f') {
|
c == 'g' || c == 'f') {
|
||||||
|
|||||||
+8
-33
@@ -1,44 +1,19 @@
|
|||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#if MG_ENABLE_FATFS
|
#if MG_ENABLE_FATFS
|
||||||
#include <ff.h>
|
#include <ff.h>
|
||||||
|
|
||||||
static int mg_days_from_epoch(int y, int m, int d) {
|
|
||||||
y -= m <= 2;
|
|
||||||
int era = y / 400;
|
|
||||||
int yoe = y - era * 400;
|
|
||||||
int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
|
||||||
int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
|
||||||
return era * 146097 + doe - 719468;
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_t mg_timegm(const struct tm *t) {
|
|
||||||
int year = t->tm_year + 1900;
|
|
||||||
int month = t->tm_mon; // 0-11
|
|
||||||
if (month > 11) {
|
|
||||||
year += month / 12;
|
|
||||||
month %= 12;
|
|
||||||
} else if (month < 0) {
|
|
||||||
int years_diff = (11 - month) / 12;
|
|
||||||
year -= years_diff;
|
|
||||||
month += 12 * years_diff;
|
|
||||||
}
|
|
||||||
int x = mg_days_from_epoch(year, month + 1, t->tm_mday);
|
|
||||||
return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) {
|
static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) {
|
||||||
struct tm tm;
|
unsigned int sec = (ftime << 1) & 0x3e;
|
||||||
memset(&tm, 0, sizeof(struct tm));
|
unsigned int min = (ftime >> 5) & 0x3f;
|
||||||
tm.tm_sec = (ftime << 1) & 0x3e;
|
unsigned int hour = (ftime >> 11) & 0x1f;
|
||||||
tm.tm_min = ((ftime >> 5) & 0x3f);
|
unsigned int day = fdate & 0x1f;
|
||||||
tm.tm_hour = ((ftime >> 11) & 0x1f);
|
unsigned int month = (fdate >> 5) & 0x0f;
|
||||||
tm.tm_mday = (fdate & 0x1f);
|
unsigned int year = ((fdate >> 9) & 0x7f) + 1980;
|
||||||
tm.tm_mon = ((fdate >> 5) & 0x0f) - 1;
|
return (time_t) mg_timegm(year, month, day, hour, min, sec);
|
||||||
tm.tm_year = ((fdate >> 9) & 0x7f) + 80;
|
|
||||||
return mg_timegm(&tm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ff_stat(const char *path, size_t *size, time_t *mtime) {
|
static int ff_stat(const char *path, size_t *size, time_t *mtime) {
|
||||||
|
|||||||
+5
-6
@@ -1030,7 +1030,7 @@ static void mg_upload_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
size_t alignment = 512;
|
size_t alignment = 512;
|
||||||
size_t left = p->expected > p->received ? p->expected - p->received : 0;
|
size_t left = p->expected > p->received ? p->expected - p->received : 0;
|
||||||
size_t aligned = c->recv.len < left ? MG_ROUND_DOWN(c->recv.len, alignment)
|
size_t aligned = c->recv.len < left ? MG_ROUND_DOWN(c->recv.len, alignment)
|
||||||
: c->recv.len;
|
: left;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (aligned > 0) {
|
if (aligned > 0) {
|
||||||
if (p->fd != NULL) {
|
if (p->fd != NULL) {
|
||||||
@@ -1114,15 +1114,14 @@ static bool is_hex_digit(int c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int skip_chunk(const char *buf, int len, int *pl, int *dl) {
|
static int skip_chunk(const char *buf, int len, int *pl, int *dl) {
|
||||||
int i = 0, n = 0;
|
int i = 0, n = 0; // pass int to mg_str_to_num, treats as unsigned
|
||||||
if (len < 3) return 0;
|
if (len < 3) return 0;
|
||||||
while (i < len && is_hex_digit(buf[i])) i++;
|
while (i < len && is_hex_digit(buf[i])) i++;
|
||||||
if (i == 0) return -1; // Error, no length specified
|
if (i == 0) return -1; // Error, no length specified
|
||||||
if (i > (int) sizeof(int) * 2) return -1; // Chunk length is too big
|
|
||||||
if (len < i + 2 || buf[i] != '\r' || buf[i + 1] != '\n') return -1; // Error
|
if (len < i + 2 || buf[i] != '\r' || buf[i + 1] != '\n') return -1; // Error
|
||||||
if (mg_str_to_num(mg_str_n(buf, (size_t) i), 16, &n, sizeof(int)) == false)
|
if (mg_str_to_num(mg_str_n(buf, (size_t) i), 16, &n, sizeof(n)) == false)
|
||||||
return -1; // Decode chunk length, overflow
|
return -1; // Decode chunk length, overflow
|
||||||
if (n < 0) return -1; // Error. TODO(): some checks now redundant
|
if (n < 0) return -1; // Chunk length is too big <0 = >0x80000000
|
||||||
if (n > len - i - 4) return 0; // Chunk not yet fully buffered
|
if (n > len - i - 4) return 0; // Chunk not yet fully buffered
|
||||||
if (buf[i + n + 2] != '\r' || buf[i + n + 3] != '\n') return -1; // Error
|
if (buf[i + n + 2] != '\r' || buf[i + n + 3] != '\n') return -1; // Error
|
||||||
*pl = i + 2, *dl = n;
|
*pl = i + 2, *dl = n;
|
||||||
|
|||||||
+8
-5
@@ -363,15 +363,18 @@ char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) {
|
|||||||
char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
|
char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
int len = 0, off = mg_json_get(json, path, &len);
|
int len = 0, off = mg_json_get(json, path, &len);
|
||||||
if (off >= 0 && json.buf[off] == '"' && len > 1 &&
|
if (off >= 0 && json.buf[off] == '"' && len > 1 && (len - 2) % 2 == 0 &&
|
||||||
(result = (char *) mg_calloc(1, (size_t) len / 2)) != NULL) {
|
(result = (char *) mg_calloc(1, (size_t) len / 2)) != NULL) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < len - 2; i += 2) {
|
for (i = 0; i < len - 2; i += 2) {
|
||||||
mg_str_to_num(mg_str_n(json.buf + off + 1 + i, 2), 16, &result[i >> 1],
|
if (!mg_str_to_num(mg_str_n(json.buf + off + 1 + i, 2), 16,
|
||||||
sizeof(uint8_t));
|
&result[i >> 1], sizeof(uint8_t))) break;
|
||||||
|
}
|
||||||
|
if (i < len - 2) mg_free(result), result = NULL;
|
||||||
|
if (result != NULL) {
|
||||||
|
result[len / 2 - 1] = '\0';
|
||||||
|
if (slen != NULL) *slen = len / 2 - 1;
|
||||||
}
|
}
|
||||||
result[len / 2 - 1] = '\0';
|
|
||||||
if (slen != NULL) *slen = len / 2 - 1;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ char *mg_json_get_str(struct mg_str json, const char *path);
|
|||||||
|
|
||||||
// Decodes a hex-encoded JSON string at path into a heap-allocated byte array.
|
// Decodes a hex-encoded JSON string at path into a heap-allocated byte array.
|
||||||
// Sets *len to the decoded byte count. Caller must mg_free() the result.
|
// Sets *len to the decoded byte count. Caller must mg_free() the result.
|
||||||
// Returns NULL if not found or not a string.
|
// Returns NULL if not found, not a string, or not valid hexadecimal.
|
||||||
char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
|
char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
|
||||||
|
|
||||||
// Decodes a base64-encoded JSON string at path into a heap-allocated byte
|
// Decodes a base64-encoded JSON string at path into a heap-allocated byte
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
#include "jwt.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "json.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "printf.h"
|
||||||
|
#include "sha256.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
|
#include "tls_uecc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static size_t jwt_split(struct mg_str jwt, struct mg_str *h, struct mg_str *p,
|
||||||
|
struct mg_str *s) {
|
||||||
|
size_t i, a = jwt.len, b = jwt.len;
|
||||||
|
for (i = 0; i < jwt.len; i++) {
|
||||||
|
if (jwt.buf[i] == '.') {
|
||||||
|
if (a == jwt.len) a = i;
|
||||||
|
if (a != i) b = i;
|
||||||
|
if (b != jwt.len) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a == jwt.len || b == jwt.len || b + 1 >= jwt.len) return 0;
|
||||||
|
*h = mg_str_n(jwt.buf, a);
|
||||||
|
*p = mg_str_n(jwt.buf + a + 1, b - a - 1);
|
||||||
|
*s = mg_str_n(jwt.buf + b + 1, jwt.len - b - 1);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool jwt_alg(struct mg_str h, const char *alg) {
|
||||||
|
char got[16];
|
||||||
|
size_t len = h.len * 3 / 4 + 2;
|
||||||
|
char *buf = (char *) mg_calloc(1, len);
|
||||||
|
bool ok;
|
||||||
|
if (buf == NULL) return false;
|
||||||
|
ok = mg_base64url_decode(h.buf, h.len, buf, len) > 0 &&
|
||||||
|
mg_json_unescape(mg_str(buf), "$.alg", got, sizeof(got)) > 0 &&
|
||||||
|
strcmp(got, alg) == 0;
|
||||||
|
mg_free(buf);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t jwt_header_payload(const char *alg, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len) {
|
||||||
|
char *header, *kid = NULL;
|
||||||
|
size_t n, m;
|
||||||
|
if (opts->kid.len > 0) {
|
||||||
|
kid = mg_mprintf(",%m:%m", MG_ESC("kid"), mg_print_esc,
|
||||||
|
(int) opts->kid.len, opts->kid.buf);
|
||||||
|
if (kid == NULL) return 0;
|
||||||
|
}
|
||||||
|
header = mg_mprintf("{%m:%m,%m:%m%.*s%.*s%.*s}", MG_ESC("alg"),
|
||||||
|
MG_ESC(alg), MG_ESC("typ"), MG_ESC("JWT"),
|
||||||
|
kid == NULL ? 0 : (int) strlen(kid),
|
||||||
|
kid == NULL ? "" : kid,
|
||||||
|
opts->header.len > 0 ? 1 : 0, ",",
|
||||||
|
(int) opts->header.len, opts->header.buf);
|
||||||
|
mg_free(kid);
|
||||||
|
if (header == NULL) return 0;
|
||||||
|
n = mg_base64url_encode((uint8_t *) header, strlen(header), buf, len);
|
||||||
|
mg_free(header);
|
||||||
|
if (n == 0 || n + 1 >= len) return 0;
|
||||||
|
buf[n++] = '.';
|
||||||
|
m = mg_base64url_encode((uint8_t *) opts->claims.buf, opts->claims.len,
|
||||||
|
buf + n, len - n);
|
||||||
|
return m == 0 ? 0 : n + m;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_jwt_sign_hs256(const struct mg_jwt_opts *opts, char *buf,
|
||||||
|
size_t len) {
|
||||||
|
uint8_t sig[32];
|
||||||
|
size_t n = jwt_header_payload("HS256", opts, buf, len);
|
||||||
|
size_t m;
|
||||||
|
if (n == 0 || n + 1 >= len) return 0;
|
||||||
|
mg_hmac_sha256(sig, (uint8_t *) opts->secret.buf, opts->secret.len,
|
||||||
|
(uint8_t *) buf, n);
|
||||||
|
buf[n++] = '.';
|
||||||
|
m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n);
|
||||||
|
return m == 0 ? 0 : n + m;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len) {
|
||||||
|
struct mg_str h, p, s;
|
||||||
|
uint8_t sig[33], digest[32];
|
||||||
|
size_t n = jwt_split(jwt, &h, &p, &s);
|
||||||
|
if (n == 0 || !jwt_alg(h, "HS256")) return 0;
|
||||||
|
if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) !=
|
||||||
|
sizeof(digest))
|
||||||
|
return 0;
|
||||||
|
mg_hmac_sha256(digest, (uint8_t *) opts->secret.buf, opts->secret.len,
|
||||||
|
(uint8_t *) jwt.buf, n);
|
||||||
|
if (!mg_memeq(sig, digest, sizeof(digest))) return 0;
|
||||||
|
return mg_base64url_decode(p.buf, p.len, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 len) {
|
||||||
|
uint8_t hash[32], sig[64], tmp[2 * 32 + 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 m;
|
||||||
|
if (n == 0 || n + 1 >= len) return 0;
|
||||||
|
mg_sha256(hash, (uint8_t *) buf, n);
|
||||||
|
if (mg_uecc_sign_deterministic(opts->private_key, hash, sizeof(hash),
|
||||||
|
&ctx.uECC, sig, mg_uecc_secp256r1()) != 1)
|
||||||
|
return 0;
|
||||||
|
buf[n++] = '.';
|
||||||
|
m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n);
|
||||||
|
return m == 0 ? 0 : n + m;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len) {
|
||||||
|
struct mg_str h, p, s;
|
||||||
|
uint8_t sig[65], hash[32];
|
||||||
|
size_t n = jwt_split(jwt, &h, &p, &s);
|
||||||
|
if (n == 0 || !jwt_alg(h, "ES256")) return 0;
|
||||||
|
if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64)
|
||||||
|
return 0;
|
||||||
|
mg_sha256(hash, (uint8_t *) jwt.buf, n);
|
||||||
|
if (mg_uecc_verify(opts->public_key, hash, sizeof(hash), sig,
|
||||||
|
mg_uecc_secp256r1()) != 1)
|
||||||
|
return 0;
|
||||||
|
return mg_base64url_decode(p.buf, p.len, buf, len);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
|
||||||
|
size_t len) {
|
||||||
|
(void) opts, (void) buf, (void) len;
|
||||||
|
MG_ERROR(("JWT ES256 requires built-in TLS"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len) {
|
||||||
|
(void) jwt, (void) opts, (void) buf, (void) len;
|
||||||
|
return mg_jwt_sign_es256(NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "str.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
struct mg_jwt_opts {
|
||||||
|
struct mg_str claims; // JSON payload
|
||||||
|
struct mg_str header; // Extra header members, no braces; alg/typ are set
|
||||||
|
struct mg_str kid; // Key ID protected header
|
||||||
|
struct mg_str secret; // HS256 secret
|
||||||
|
const uint8_t *private_key; // ES256 private key (MG_TLS_BUILTIN only)
|
||||||
|
const uint8_t *public_key; // ES256 public key (MG_TLS_BUILTIN only)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signs JSON claims as a compact JWT. Returns JWT length, or 0 on error.
|
||||||
|
size_t mg_jwt_sign_hs256(const struct mg_jwt_opts *opts, char *buf,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
// Verifies JWT and decodes JSON claims into buf. Returns claims length, or 0.
|
||||||
|
size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len);
|
||||||
|
|
||||||
|
size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
|
||||||
|
size_t len);
|
||||||
|
size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
|
||||||
|
char *buf, size_t len);
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
|
|
||||||
// no config defaults to 0 => Ethernet
|
// no config defaults to 0 => Ethernet
|
||||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE };
|
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||||
|
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|||||||
+13
-5
@@ -94,6 +94,13 @@ size_t mg_l2_eth_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) {
|
|||||||
struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype addrtype,
|
struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype addrtype,
|
||||||
struct mg_addr *addr);
|
struct mg_addr *addr);
|
||||||
|
|
||||||
|
// Read an unaligned little-endian value from byte pointer p into a native integer.
|
||||||
|
// Safe on architectures that forbid unaligned access (e.g. Cortex-M0).
|
||||||
|
#define MG_LOAD_LE32(p) \
|
||||||
|
((uint32_t) (((uint32_t) MG_U8P(p)[3] << 24U) | \
|
||||||
|
((uint32_t) MG_U8P(p)[2] << 16U) | \
|
||||||
|
((uint32_t) MG_U8P(p)[1] << 8U) | MG_U8P(p)[0]))
|
||||||
|
|
||||||
bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||||
struct mg_str *pay, struct mg_str *raw) {
|
struct mg_str *pay, struct mg_str *raw) {
|
||||||
struct eth *eth = (struct eth *) raw->buf;
|
struct eth *eth = (struct eth *) raw->buf;
|
||||||
@@ -109,7 +116,7 @@ bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
|||||||
} else { // We do, check 802.1Q tag
|
} else { // We do, check 802.1Q tag
|
||||||
struct qtag *qtag = (struct qtag *) ð->type;
|
struct qtag *qtag = (struct qtag *) ð->type;
|
||||||
if (qtag->tpid != mg_htons(0x8100)) return false; // Untagged frame
|
if (qtag->tpid != mg_htons(0x8100)) return false; // Untagged frame
|
||||||
if (mg_ntohs(VLAN_ID(qtag->tci)) != VLAN_ID(d->vlan_id))
|
if (VLAN_ID(mg_ntohs(qtag->tci)) != VLAN_ID(d->vlan_id))
|
||||||
return false; // Not our VLAN
|
return false; // Not our VLAN
|
||||||
type = MG_LOAD_BE16(qtag + 1);
|
type = MG_LOAD_BE16(qtag + 1);
|
||||||
}
|
}
|
||||||
@@ -119,10 +126,11 @@ bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
|||||||
sizeof(eth->dst)) != 0)
|
sizeof(eth->dst)) != 0)
|
||||||
return false; // TODO(): add multicast addresses
|
return false; // TODO(): add multicast addresses
|
||||||
if (ifp->enable_fcs_check && len > hdrlen + 4) {
|
if (ifp->enable_fcs_check && len > hdrlen + 4) {
|
||||||
uint32_t crc;
|
uint32_t crc, crc_rx;
|
||||||
len -= 4; // TODO(scaprile): check on bigendian
|
len -= 4;
|
||||||
crc = mg_crc32(0, (const char *) raw->buf, len);
|
crc = mg_crc32(0, (const char *) raw->buf, len);
|
||||||
if (memcmp((void *) ((size_t) raw->buf + len), &crc, sizeof(crc)))
|
crc_rx = MG_LOAD_LE32(raw->buf + len);
|
||||||
|
if (crc_rx != crc)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pay->buf = ((char *) eth) + hdrlen;
|
pay->buf = ((char *) eth) + hdrlen;
|
||||||
@@ -130,7 +138,7 @@ bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
|||||||
for (i = 0; i < sizeof(eth_types) / sizeof(uint16_t); i++) {
|
for (i = 0; i < sizeof(eth_types) / sizeof(uint16_t); i++) {
|
||||||
if (type == eth_types[i]) break;
|
if (type == eth_types[i]) break;
|
||||||
}
|
}
|
||||||
if (i == sizeof(eth_types)) {
|
if (i == sizeof(eth_types) / sizeof(eth_types[0])) {
|
||||||
MG_DEBUG(("Unknown eth type %x", type));
|
MG_DEBUG(("Unknown eth type %x", type));
|
||||||
if (mg_log_level >= MG_LL_VERBOSE)
|
if (mg_log_level >= MG_LL_VERBOSE)
|
||||||
mg_hexdump(raw->buf, raw->len >= 32 ? 32 : raw->len);
|
mg_hexdump(raw->buf, raw->len >= 32 ? 32 : raw->len);
|
||||||
|
|||||||
+19
-34
@@ -169,30 +169,14 @@ uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
|
|||||||
s_id, src, dst, frame));
|
s_id, src, dst, frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate FCS/CRC for PPP frames. Could be implemented faster using lookup
|
|
||||||
// tables.
|
|
||||||
static uint16_t fcs_do(uint8_t *frame, size_t len) {
|
|
||||||
uint32_t fcs = 0xffff;
|
|
||||||
unsigned int j;
|
|
||||||
for (j = 0; j < len; j++) {
|
|
||||||
unsigned int i;
|
|
||||||
uint8_t x = frame[j];
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
fcs = ((fcs ^ x) & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1;
|
|
||||||
x >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (uint16_t) (fcs & 0xffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t mg_l2_ppp_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) {
|
size_t mg_l2_ppp_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) {
|
||||||
uint16_t crc;
|
uint16_t crc;
|
||||||
uint8_t *frame;
|
uint8_t *frame;
|
||||||
len += sizeof(struct ppp) + sizeof(struct hdlc_);
|
len += sizeof(struct ppp) + sizeof(struct hdlc_);
|
||||||
frame = cur - len;
|
frame = cur - len;
|
||||||
crc = fcs_do(frame, len);
|
crc = mg_crc16(0, (const char *) frame, len);
|
||||||
*cur++ = (uint8_t) ~crc; // add CRC, note the byte order
|
*cur++ = (uint8_t) crc; // add CRC, note the byte order
|
||||||
*cur++ = (uint8_t) (~crc >> 8);
|
*cur++ = (uint8_t) (crc >> 8);
|
||||||
// there is no len field in PPP
|
// there is no len field in PPP
|
||||||
(void) ifp;
|
(void) ifp;
|
||||||
return len + 2;
|
return len + 2;
|
||||||
@@ -243,6 +227,7 @@ static void ppp_handle_lcp(struct mg_tcpip_if *ifp, uint8_t *lcpp,
|
|||||||
if (lcpsz < sizeof(*lcp)) return;
|
if (lcpsz < sizeof(*lcp)) return;
|
||||||
id = lcp->id;
|
id = lcp->id;
|
||||||
len = mg_ntohs(lcp->len);
|
len = mg_ntohs(lcp->len);
|
||||||
|
if (len < sizeof(*lcp) || len > lcpsz) return;
|
||||||
switch (lcp->code) {
|
switch (lcp->code) {
|
||||||
case MG_PPP_LCP_CFG_REQ: {
|
case MG_PPP_LCP_CFG_REQ: {
|
||||||
if (len == sizeof(*lcp)) {
|
if (len == sizeof(*lcp)) {
|
||||||
@@ -299,7 +284,7 @@ static void ppp_handle_ipcp(struct mg_tcpip_if *ifp, uint8_t *ipcpp,
|
|||||||
if (ipcpsz < sizeof(*ipcp)) return;
|
if (ipcpsz < sizeof(*ipcp)) return;
|
||||||
id = ipcp->id;
|
id = ipcp->id;
|
||||||
len = mg_ntohs(ipcp->len);
|
len = mg_ntohs(ipcp->len);
|
||||||
if (len > ipcpsz) return;
|
if (len < sizeof(*ipcp) || len > ipcpsz) return;
|
||||||
switch (ipcp->code) {
|
switch (ipcp->code) {
|
||||||
case MG_PPP_IPCP_CFG_REQ:
|
case MG_PPP_IPCP_CFG_REQ:
|
||||||
MG_VERBOSE(("got IPCP config request, acknowledging..."));
|
MG_VERBOSE(("got IPCP config request, acknowledging..."));
|
||||||
@@ -359,6 +344,7 @@ static void ppp_handle_ipv6cp(struct mg_tcpip_if *ifp, uint8_t *ipv6cpp,
|
|||||||
if (ipv6cpsz < sizeof(*ipv6cp)) return;
|
if (ipv6cpsz < sizeof(*ipv6cp)) return;
|
||||||
id = ipv6cp->id;
|
id = ipv6cp->id;
|
||||||
len = mg_ntohs(ipv6cp->len);
|
len = mg_ntohs(ipv6cp->len);
|
||||||
|
if (len < sizeof(*ipv6cp) || len > ipv6cpsz) return;
|
||||||
switch (ipv6cp->code) {
|
switch (ipv6cp->code) {
|
||||||
case MG_PPP_IPV6CP_CFG_REQ:
|
case MG_PPP_IPV6CP_CFG_REQ:
|
||||||
MG_VERBOSE(("got IPV6CP config request..."));
|
MG_VERBOSE(("got IPV6CP config request..."));
|
||||||
@@ -366,10 +352,10 @@ static void ppp_handle_ipv6cp(struct mg_tcpip_if *ifp, uint8_t *ipv6cpp,
|
|||||||
find_opt(
|
find_opt(
|
||||||
MG_PPP_IPV6CP_OPT_IFCID, 10, (const uint8_t *) (ipv6cp + 1),
|
MG_PPP_IPV6CP_OPT_IFCID, 10, (const uint8_t *) (ipv6cp + 1),
|
||||||
len - sizeof(*ipv6cp),
|
len - sizeof(*ipv6cp),
|
||||||
(uint8_t *) &((struct mg_l2addr *) (ifp->gwmac))->addr.ieee64)) {
|
(uint8_t *) &((struct mg_l2addr *) (ifp->gw6mac))->addr.ieee64)) {
|
||||||
if (((struct mg_l2addr *) (ifp->gwmac))->addr.ieee64 != 0) {
|
if (((struct mg_l2addr *) (ifp->gw6mac))->addr.ieee64 != 0) {
|
||||||
MG_DEBUG(("IPV6CP cfg, GW IFCID: %M", mg_print_ieee64,
|
MG_DEBUG(("IPV6CP cfg, GW IFCID: %M", mg_print_ieee64,
|
||||||
&((struct mg_l2addr *) (ifp->gwmac))->addr.ieee64));
|
&((struct mg_l2addr *) (ifp->gw6mac))->addr.ieee64));
|
||||||
ipv6cp->code = MG_PPP_IPV6CP_CFG_ACK;
|
ipv6cp->code = MG_PPP_IPV6CP_CFG_ACK;
|
||||||
ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
|
ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
|
||||||
req[1] = id;
|
req[1] = id;
|
||||||
@@ -393,22 +379,21 @@ static void ppp_handle_ipv6cp(struct mg_tcpip_if *ifp, uint8_t *ipv6cpp,
|
|||||||
// Our peer accepted our ifc id
|
// Our peer accepted our ifc id
|
||||||
MG_VERBOSE(("got IPV6CP config ack"));
|
MG_VERBOSE(("got IPV6CP config ack"));
|
||||||
break;
|
break;
|
||||||
case MG_PPP_IPV6CP_CFG_NACK:
|
case MG_PPP_IPV6CP_CFG_NACK: {
|
||||||
|
struct mg_l2addr l2;
|
||||||
MG_VERBOSE(("got IPV6CP config nack"));
|
MG_VERBOSE(("got IPV6CP config nack"));
|
||||||
// NACK contains our "suggested" IFC id, use it
|
// NACK contains our "suggested" IFC id, use it
|
||||||
if (len >= 10 &&
|
if (len >= 10 &&
|
||||||
find_opt(
|
find_opt(MG_PPP_IPV6CP_OPT_IFCID, 10, (const uint8_t *) (ipv6cp + 1),
|
||||||
MG_PPP_IPV6CP_OPT_IFCID, 10, (const uint8_t *) (ipv6cp + 1),
|
len - sizeof(*ipv6cp), (uint8_t *) &l2.addr.ieee64)) {
|
||||||
len - sizeof(*ipv6cp),
|
MG_DEBUG(("IPV6CP cfg, IFCID: %M", mg_print_ieee64, &l2.addr.ieee64));
|
||||||
(uint8_t *) &((struct mg_l2addr *) (ifp->mac))->addr.ieee64)) {
|
ifp->ip6ll[1] = l2.addr.ieee64; // RFC-5072 5, signal L3 we're ready
|
||||||
MG_DEBUG(("IPV6CP cfg, IFCID: %M", mg_print_ieee64,
|
|
||||||
&((struct mg_l2addr *) (ifp->mac))->addr.ieee64));
|
|
||||||
ipv6cp->code = MG_PPP_IPV6CP_CFG_REQ;
|
ipv6cp->code = MG_PPP_IPV6CP_CFG_REQ;
|
||||||
ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
|
ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
|
||||||
} else {
|
} else {
|
||||||
MG_ERROR(("Peer is not able to offer an interface id"));
|
MG_ERROR(("Peer is not able to offer an interface id"));
|
||||||
}
|
}
|
||||||
break;
|
} break;
|
||||||
case MG_PPP_IPV6CP_CFG_REJECT:
|
case MG_PPP_IPV6CP_CFG_REJECT:
|
||||||
MG_ERROR(("Peer rejected our interface id"));
|
MG_ERROR(("Peer rejected our interface id"));
|
||||||
break;
|
break;
|
||||||
@@ -573,10 +558,10 @@ bool mg_l2_pppoe_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
|||||||
uint8_t *p = (uint8_t *) (pppoe + 1);
|
uint8_t *p = (uint8_t *) (pppoe + 1);
|
||||||
uint16_t taglen;
|
uint16_t taglen;
|
||||||
while (len >= 4) { // parse tags for a possible AC-Cookie
|
while (len >= 4) { // parse tags for a possible AC-Cookie
|
||||||
uint16_t curtag = *((uint16_t *) p);
|
uint16_t curtag = MG_LOAD_BE16(p);
|
||||||
taglen = mg_ntohs(*(((uint16_t *) p) + 1));
|
taglen = MG_LOAD_BE16(p + 2);
|
||||||
if (taglen > len - 4) return false; // truncated / malformed
|
if (taglen > len - 4) return false; // truncated / malformed
|
||||||
if (curtag == mg_htons(0x0104)) {
|
if (curtag == 0x0104) {
|
||||||
has_cookie = true;
|
has_cookie = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,28 @@ size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mg_dscp(struct mg_connection *c, uint8_t dscp) {
|
||||||
|
#if MG_ENABLE_SOCKET
|
||||||
|
#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_THREADX || \
|
||||||
|
MG_ARCH == MG_ARCH_TIRTOS || MG_ARCH == MG_ARCH_WIN32
|
||||||
|
(void) c;
|
||||||
|
(void) dscp;
|
||||||
|
#else
|
||||||
|
int tos = dscp << 2, level = IPPROTO_IP, optname = IP_TOS;
|
||||||
|
c->dscp = (unsigned) (dscp & 63);
|
||||||
|
#if MG_ENABLE_IPV6 && !MG_ENABLE_LWIP
|
||||||
|
if (c->loc.is_ip6)
|
||||||
|
level = IPPROTO_IPV6, optname = IPV6_TCLASS;
|
||||||
|
#endif
|
||||||
|
return setsockopt((MG_SOCKET_TYPE) (size_t) c->fd, level, optname, (char *) &tos, sizeof(tos)) == 0;
|
||||||
|
#endif
|
||||||
|
#elif MG_ENABLE_TCPIP
|
||||||
|
c->dscp = (unsigned) (dscp & 63);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
|
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
|
||||||
uint32_t localhost = mg_htonl(0x7f000001);
|
uint32_t localhost = mg_htonl(0x7f000001);
|
||||||
if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false;
|
if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false;
|
||||||
@@ -85,22 +107,31 @@ static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) {
|
|||||||
|
|
||||||
static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
|
static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
|
||||||
size_t i, j = 0, n = 0, dc = 42;
|
size_t i, j = 0, n = 0, dc = 42;
|
||||||
|
uint16_t val = 0;
|
||||||
|
bool have = false;
|
||||||
addr->scope_id = 0;
|
addr->scope_id = 0;
|
||||||
if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2;
|
if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2;
|
||||||
if (mg_v4mapped(str, addr)) return true; // sets addr->is_ip6
|
if (mg_v4mapped(str, addr)) return true; // sets addr->is_ip6
|
||||||
for (i = 0; i < str.len; i++) {
|
for (i = 0; i < str.len; i++) {
|
||||||
if ((str.buf[i] >= '0' && str.buf[i] <= '9') ||
|
int x = str.buf[i] >= '0' && str.buf[i] <= '9' ? str.buf[i] - '0'
|
||||||
(str.buf[i] >= 'a' && str.buf[i] <= 'f') ||
|
: str.buf[i] >= 'a' && str.buf[i] <= 'f' ? str.buf[i] - 'a' + 10
|
||||||
(str.buf[i] >= 'A' && str.buf[i] <= 'F')) {
|
: str.buf[i] >= 'A' && str.buf[i] <= 'F' ? str.buf[i] - 'A' + 10
|
||||||
unsigned long val = 0; // TODO(): This loops on chars, refactor
|
: -1;
|
||||||
|
if (x >= 0) {
|
||||||
if (i > j + 3) return false;
|
if (i > j + 3) return false;
|
||||||
// MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.buf[j]));
|
val = (uint16_t) ((val << 4) | (uint16_t) x);
|
||||||
mg_str_to_num(mg_str_n(&str.buf[j], i - j + 1), 16, &val, sizeof(val));
|
have = true;
|
||||||
addr->addr.ip[n] = (uint8_t) ((val >> 8) & 255);
|
continue;
|
||||||
addr->addr.ip[n + 1] = (uint8_t) (val & 255);
|
}
|
||||||
} else if (str.buf[i] == ':') {
|
if (have) {
|
||||||
|
addr->addr.ip[n] = (uint8_t) (val >> 8),
|
||||||
|
addr->addr.ip[n + 1] = (uint8_t) val;
|
||||||
|
val = 0, have = false;
|
||||||
|
}
|
||||||
|
if (str.buf[i] == ':') {
|
||||||
j = i + 1;
|
j = i + 1;
|
||||||
if (i > 0 && str.buf[i - 1] == ':') {
|
if (i > 0 && str.buf[i - 1] == ':') {
|
||||||
|
if (dc != 42) return false;
|
||||||
dc = n; // Double colon
|
dc = n; // Double colon
|
||||||
if (i > 1 && str.buf[i - 2] == ':') return false;
|
if (i > 1 && str.buf[i - 2] == ':') return false;
|
||||||
} else if (i > 0) {
|
} else if (i > 0) {
|
||||||
@@ -120,12 +151,14 @@ static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (have)
|
||||||
|
addr->addr.ip[n] = (uint8_t) (val >> 8),
|
||||||
|
addr->addr.ip[n + 1] = (uint8_t) val;
|
||||||
if (n < 14 && dc == 42) return false;
|
if (n < 14 && dc == 42) return false;
|
||||||
if (n < 14) {
|
if (n < 14) {
|
||||||
memmove(&addr->addr.ip[dc + (14 - n)], &addr->addr.ip[dc], n - dc + 2);
|
memmove(&addr->addr.ip[dc + (14 - n)], &addr->addr.ip[dc], n - dc + 2);
|
||||||
memset(&addr->addr.ip[dc], 0, 14 - n);
|
memset(&addr->addr.ip[dc], 0, 14 - n);
|
||||||
}
|
}
|
||||||
|
|
||||||
addr->is_ip6 = true;
|
addr->is_ip6 = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ struct mg_connection {
|
|||||||
void *pfn_data; // Protocol-level handler argument
|
void *pfn_data; // Protocol-level handler argument
|
||||||
char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable
|
char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable
|
||||||
void *tls; // TLS state (internal)
|
void *tls; // TLS state (internal)
|
||||||
|
unsigned dscp : 6; // Differentiated Services Code Point
|
||||||
|
#define MG_DSCP_CS(class_) ((class_) << 3)
|
||||||
|
#define MG_DSCP_AF(class_, drop_) (((class_) << 3) | ((drop_) << 1))
|
||||||
|
#define MG_DSCP_EF 46
|
||||||
unsigned is_listening : 1; // Listening connection; accepts inbound connections
|
unsigned is_listening : 1; // Listening connection; accepts inbound connections
|
||||||
unsigned is_client : 1; // Outbound connection created by mg_connect()
|
unsigned is_client : 1; // Outbound connection created by mg_connect()
|
||||||
unsigned is_accepted : 1; // Inbound connection accepted from a listener
|
unsigned is_accepted : 1; // Inbound connection accepted from a listener
|
||||||
@@ -154,6 +158,11 @@ struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
|
|||||||
struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
|
struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
|
||||||
mg_event_handler_t fn, void *fn_data);
|
mg_event_handler_t fn, void *fn_data);
|
||||||
|
|
||||||
|
// Sets the 6-bit Differentiated Services Code Point for outgoing datagrams.
|
||||||
|
// Call on MG_EV_OPEN for UDP listeners, MG_EV_CONNECT for clients, or
|
||||||
|
// MG_EV_ACCEPT for TCP servers, before sending application data.
|
||||||
|
bool mg_dscp(struct mg_connection *, uint8_t dscp);
|
||||||
|
|
||||||
// Called internally after DNS resolution completes to create the OS socket
|
// Called internally after DNS resolution completes to create the OS socket
|
||||||
// and initiate the TCP/IP connect. Normally not called by user code.
|
// and initiate the TCP/IP connect. Normally not called by user code.
|
||||||
void mg_connect_resolved(struct mg_connection *);
|
void mg_connect_resolved(struct mg_connection *);
|
||||||
|
|||||||
+122
-92
@@ -46,7 +46,7 @@ struct connstate {
|
|||||||
|
|
||||||
struct ip {
|
struct ip {
|
||||||
uint8_t ver; // Version
|
uint8_t ver; // Version
|
||||||
uint8_t tos; // Unused
|
uint8_t tos; // DS field
|
||||||
uint16_t len; // Datagram length
|
uint16_t len; // Datagram length
|
||||||
uint16_t id; // Unused
|
uint16_t id; // Unused
|
||||||
uint16_t frag; // Fragmentation
|
uint16_t frag; // Fragmentation
|
||||||
@@ -412,13 +412,15 @@ static void onstatechange(struct mg_tcpip_if *ifp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto,
|
static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto,
|
||||||
uint32_t ip_src, uint32_t ip_dst, size_t plen) {
|
uint8_t dscp, uint32_t ip_src, uint32_t ip_dst,
|
||||||
|
size_t plen) {
|
||||||
// ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
|
// ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
|
||||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||||
struct ip *ip = (struct ip *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV4,
|
struct ip *ip = (struct ip *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV4,
|
||||||
ifp->mac, l2_dst, l2p);
|
ifp->mac, l2_dst, l2p);
|
||||||
memset(ip, 0, sizeof(*ip));
|
memset(ip, 0, sizeof(*ip));
|
||||||
ip->ver = 0x45; // Version 4, header length 5 words
|
ip->ver = 0x45; // Version 4, header length 5 words
|
||||||
|
ip->tos = (uint8_t) (dscp << 2);
|
||||||
ip->frag = mg_htons(0x4000); // Don't fragment
|
ip->frag = mg_htons(0x4000); // Don't fragment
|
||||||
ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen));
|
ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen));
|
||||||
ip->ttl = 64;
|
ip->ttl = 64;
|
||||||
@@ -431,12 +433,12 @@ static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto,
|
|||||||
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||||
uint8_t next, uint64_t *ip_src, uint64_t *ip_dst,
|
uint8_t next, uint8_t dscp, uint64_t *ip_src,
|
||||||
size_t plen);
|
uint64_t *ip_dst, size_t plen);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||||
struct mg_addr *ip_src, struct mg_addr *ip_dst,
|
struct mg_addr *ip_src, struct mg_addr *ip_dst, uint8_t dscp,
|
||||||
const void *buf, size_t len) {
|
const void *buf, size_t len) {
|
||||||
uint8_t *l3p;
|
uint8_t *l3p;
|
||||||
size_t l2_len;
|
size_t l2_len;
|
||||||
@@ -445,7 +447,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
|||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
struct ip6 *ip6 = NULL;
|
struct ip6 *ip6 = NULL;
|
||||||
if (ip_dst->is_ip6) {
|
if (ip_dst->is_ip6) {
|
||||||
ip6 = tx_ip6(ifp, l2_dst, 17, ip_src->addr.ip6, ip_dst->addr.ip6,
|
ip6 = tx_ip6(ifp, l2_dst, 17, dscp, ip_src->addr.ip6, ip_dst->addr.ip6,
|
||||||
len + sizeof(struct udp));
|
len + sizeof(struct udp));
|
||||||
udp = (struct udp *) (ip6 + 1);
|
udp = (struct udp *) (ip6 + 1);
|
||||||
l2_len = sizeof(*ip6) + sizeof(*udp) + len;
|
l2_len = sizeof(*ip6) + sizeof(*udp) + len;
|
||||||
@@ -453,7 +455,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
ip = tx_ip(ifp, l2_dst, 17, ip_src->addr.ip4, ip_dst->addr.ip4,
|
ip = tx_ip(ifp, l2_dst, 17, dscp, ip_src->addr.ip4, ip_dst->addr.ip4,
|
||||||
len + sizeof(struct udp));
|
len + sizeof(struct udp));
|
||||||
udp = (struct udp *) (ip + 1);
|
udp = (struct udp *) (ip + 1);
|
||||||
l2_len = sizeof(*ip) + sizeof(*udp) + len;
|
l2_len = sizeof(*ip) + sizeof(*udp) + len;
|
||||||
@@ -486,7 +488,7 @@ static bool tx_udp4(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src,
|
|||||||
memset(&ipd, 0, sizeof(ipd));
|
memset(&ipd, 0, sizeof(ipd));
|
||||||
ipd.addr.ip4 = ip_dst;
|
ipd.addr.ip4 = ip_dst;
|
||||||
ipd.port = dport;
|
ipd.port = dport;
|
||||||
return tx_udp(ifp, l2_dst, &ips, &ipd, buf, len);
|
return tx_udp(ifp, l2_dst, &ips, &ipd, 0, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src,
|
static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src,
|
||||||
@@ -642,7 +644,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
if ((l2addr = get_return_l2addr(ifp, &ips, false, pkt)) == NULL)
|
if ((l2addr = get_return_l2addr(ifp, &ips, false, pkt)) == NULL)
|
||||||
return; // safety net for lousy networks
|
return; // safety net for lousy networks
|
||||||
if (plen > room) plen = room;
|
if (plen > room) plen = room;
|
||||||
ip = tx_ip(ifp, l2addr, 1, ifp->ip, pkt->ip->src, sizeof(*icmp) + plen);
|
ip = tx_ip(ifp, l2addr, 1, 0, ifp->ip, pkt->ip->src, sizeof(*icmp) + plen);
|
||||||
icmp = (struct icmp *) (ip + 1);
|
icmp = (struct icmp *) (ip + 1);
|
||||||
memset(icmp, 0, sizeof(*icmp)); // Set csum, type, code to 0
|
memset(icmp, 0, sizeof(*icmp)); // Set csum, type, code to 0
|
||||||
memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX
|
memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX
|
||||||
@@ -666,24 +668,31 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
|
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
|
||||||
// min header length checked at payload calculation, options are optional
|
// min header length checked at payload calculation, options are optional
|
||||||
if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
|
if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
|
||||||
while (p + 1 < end && p[0] != 255) { // Parse options, get #1; RFC-2132 9
|
while (p < end && p[0] != 255) { // RFC-2132 9
|
||||||
if (p[0] == 1 && p[1] == 4 && p + 6 < end) { // Mask, 3.3
|
if (p[0] == 0) { p++; continue; } // Pad
|
||||||
|
if ((size_t) (end - p) < 2 || (size_t) (end - p) < 2U + p[1]) break;
|
||||||
|
if (p[0] == 1) { // Mask, 3.3
|
||||||
|
if (p[1] != 4) break;
|
||||||
memcpy(&mask, p + 2, sizeof(mask));
|
memcpy(&mask, p + 2, sizeof(mask));
|
||||||
} else if (p[0] == 3 && dhcp_opt_len_ok(p[1], p, end)) { // GW, 3.5
|
} else if (p[0] == 3) { // GW, 3.5
|
||||||
|
if (!dhcp_opt_len_ok(p[1], p, end)) break;
|
||||||
memcpy(&gw, p + 2, sizeof(gw));
|
memcpy(&gw, p + 2, sizeof(gw));
|
||||||
ip = pkt->dhcp->yiaddr;
|
ip = pkt->dhcp->yiaddr;
|
||||||
} else if (ifp->enable_req_dns && p[0] == 6 &&
|
} else if (ifp->enable_req_dns && p[0] == 6) { // DNS, 3.8
|
||||||
dhcp_opt_len_ok(p[1], p, end)) { // DNS, 3.8
|
if (!dhcp_opt_len_ok(p[1], p, end)) break;
|
||||||
memcpy(&dns, p + 2, sizeof(dns));
|
memcpy(&dns, p + 2, sizeof(dns));
|
||||||
} else if (ifp->enable_req_sntp && p[0] == 42 &&
|
} else if (ifp->enable_req_sntp && p[0] == 42) { // SNTP, 8.3
|
||||||
dhcp_opt_len_ok(p[1], p, end)) { // SNTP, 8.3
|
if (!dhcp_opt_len_ok(p[1], p, end)) break;
|
||||||
memcpy(&sntp, p + 2, sizeof(sntp));
|
memcpy(&sntp, p + 2, sizeof(sntp));
|
||||||
} else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease
|
} else if (p[0] == 51) { // Lease
|
||||||
|
if (p[1] != 4) break;
|
||||||
memcpy(&lease, p + 2, sizeof(lease));
|
memcpy(&lease, p + 2, sizeof(lease));
|
||||||
lease = mg_ntohl(lease);
|
lease = mg_ntohl(lease);
|
||||||
} else if (p[0] == 53 && p[1] == 1 && p + 6 < end) { // Msg Type
|
} else if (p[0] == 53) { // Msg Type
|
||||||
|
if (p[1] != 1) break;
|
||||||
msgtype = p[2];
|
msgtype = p[2];
|
||||||
} else if (p[0] == 54 && p[1] == 4 && p + 6 < end) { // Server id 9.7
|
} else if (p[0] == 54) { // Server id 9.7
|
||||||
|
if (p[1] != 4) break;
|
||||||
memcpy(&owner, p + 2, sizeof(sntp)); // This is the lease owner
|
memcpy(&owner, p + 2, sizeof(sntp)); // This is the lease owner
|
||||||
}
|
}
|
||||||
p += p[1] + 2;
|
p += p[1] + 2;
|
||||||
@@ -738,8 +747,11 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
// min header length checked at payload calculation, options are optional
|
// min header length checked at payload calculation, options are optional
|
||||||
res.yiaddr = ifp->ip;
|
res.yiaddr = ifp->ip;
|
||||||
((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
|
((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
|
||||||
while (p + 1 < end && p[0] != 255) { // Parse options
|
while (p < end && p[0] != 255) { // Parse options
|
||||||
if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
|
if (p[0] == 0) { p++; continue; } // Pad
|
||||||
|
if ((size_t) (end - p) < 2 || (size_t) (end - p) < 2U + p[1]) break;
|
||||||
|
if (p[0] == 53) { // Message type
|
||||||
|
if (p[1] != 1) break;
|
||||||
op = p[2];
|
op = p[2];
|
||||||
}
|
}
|
||||||
p += p[1] + 2;
|
p += p[1] + 2;
|
||||||
@@ -773,14 +785,15 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||||
uint8_t next, uint64_t *ip_src, uint64_t *ip_dst,
|
uint8_t next, uint8_t dscp, uint64_t *ip_src,
|
||||||
size_t plen) {
|
uint64_t *ip_dst, size_t plen) {
|
||||||
// ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
|
// ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
|
||||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||||
struct ip6 *ip6 = (struct ip6 *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV6,
|
struct ip6 *ip6 = (struct ip6 *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV6,
|
||||||
ifp->mac, l2_dst, l2p);
|
ifp->mac, l2_dst, l2p);
|
||||||
memset(ip6, 0, sizeof(*ip6));
|
memset(ip6, 0, sizeof(*ip6));
|
||||||
ip6->ver = 0x60; // Version 6, traffic class 0
|
ip6->ver = (uint8_t) (0x60 | (dscp >> 2)); // Version 6, traffic class
|
||||||
|
ip6->label[0] = (uint8_t) (dscp << 6);
|
||||||
ip6->plen = mg_htons((uint16_t) plen);
|
ip6->plen = mg_htons((uint16_t) plen);
|
||||||
ip6->next = next;
|
ip6->next = next;
|
||||||
ip6->hops = 255; // NDP requires max
|
ip6->hops = 255; // NDP requires max
|
||||||
@@ -796,7 +809,7 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
|
|||||||
const void *buf, size_t len) {
|
const void *buf, size_t len) {
|
||||||
struct ip6 *ip6;
|
struct ip6 *ip6;
|
||||||
struct icmp6 *icmp6;
|
struct icmp6 *icmp6;
|
||||||
ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len);
|
ip6 = tx_ip6(ifp, l2_dst, 58, 0, ip_src, ip_dst, sizeof(*icmp6) + len);
|
||||||
icmp6 = (struct icmp6 *) (ip6 + 1);
|
icmp6 = (struct icmp6 *) (ip6 + 1);
|
||||||
memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0
|
memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0
|
||||||
icmp6->type = type;
|
icmp6->type = type;
|
||||||
@@ -828,15 +841,16 @@ static void onstate6change(struct mg_tcpip_if *ifp);
|
|||||||
|
|
||||||
static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
struct ndp_na *na = (struct ndp_na *) pkt->pay.buf;
|
struct ndp_na *na = (struct ndp_na *) pkt->pay.buf;
|
||||||
uint8_t *opts = (uint8_t *) (na + 1), *endp = opts + pkt->pay.len - sizeof(*na);
|
uint8_t *opts = (uint8_t *) (na + 1),
|
||||||
if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts
|
*endp = opts + pkt->pay.len - sizeof(*na);
|
||||||
if ((na->res[0] & 0x40) == 0) return; // not "solicited"
|
if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts
|
||||||
if (*opts++ != 2) return; // no target hwaddr, must have
|
if ((na->res[0] & 0x40) == 0) return; // not "solicited"
|
||||||
|
if (*opts++ != 2) return; // no target hwaddr, must have
|
||||||
MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr));
|
MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr));
|
||||||
if (MG_IP6MATCH(na->addr, ifp->gw6)) {
|
if (MG_IP6MATCH(na->addr, ifp->gw6)) {
|
||||||
// Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY
|
// Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY
|
||||||
uint8_t len = *opts++; // check valid hwaddr and get it
|
uint8_t len = *opts++; // check valid hwaddr and get it
|
||||||
if ((opts + 8 * len - 2) > endp) return; // truncated
|
if ((opts + 8 * len - 2) > endp) return; // truncated
|
||||||
if (!mg_l2_ip6get(ifp->l2type, ifp->gw6mac, opts, len)) return;
|
if (!mg_l2_ip6get(ifp->l2type, ifp->gw6mac, opts, len)) return;
|
||||||
ifp->gw6_ready = true;
|
ifp->gw6_ready = true;
|
||||||
if (ifp->state6 == MG_TCPIP_STATE_IP) {
|
if (ifp->state6 == MG_TCPIP_STATE_IP) {
|
||||||
@@ -847,8 +861,8 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
|
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
|
||||||
if (c != NULL && c->is_arplooking) {
|
if (c != NULL && c->is_arplooking) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
uint8_t len = *opts++; // check valid hwaddr and get it
|
uint8_t len = *opts++; // check valid hwaddr and get it
|
||||||
if ((opts + 8 * len - 2) > endp) return; // truncated
|
if ((opts + 8 * len - 2) > endp) return; // truncated
|
||||||
if (!mg_l2_ip6get(ifp->l2type, s->mac, opts, len)) return;
|
if (!mg_l2_ip6get(ifp->l2type, s->mac, opts, len)) return;
|
||||||
MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6,
|
MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6,
|
||||||
&c->rem.addr.ip6, mg_print_l2addr, ifp->l2type, s->mac));
|
&c->rem.addr.ip6, mg_print_l2addr, ifp->l2type, s->mac));
|
||||||
@@ -860,17 +874,18 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
|
|
||||||
// Neighbor Solicitation, 4.3
|
// Neighbor Solicitation, 4.3
|
||||||
static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na
|
struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na
|
||||||
uint64_t target[2];
|
uint64_t target[2];
|
||||||
if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts
|
if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts
|
||||||
memcpy(target, ns->addr, sizeof(target));
|
memcpy(target, ns->addr, sizeof(target));
|
||||||
if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) {
|
if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) {
|
||||||
uint64_t req[2]; // requester address
|
uint64_t req[2]; // requester address
|
||||||
uint8_t l2[sizeof(struct mg_l2addr)];
|
uint8_t l2[sizeof(struct mg_l2addr)];
|
||||||
uint8_t len, *opts = (uint8_t *) (ns + 1), *endp = opts + pkt->pay.len - sizeof(*ns);
|
uint8_t len, *opts = (uint8_t *) (ns + 1),
|
||||||
if (*opts++ != 1) return; // no requester hwaddr (source)
|
*endp = opts + pkt->pay.len - sizeof(*ns);
|
||||||
len = *opts++; // check valid hwaddr and get it
|
if (*opts++ != 1) return; // no requester hwaddr (source)
|
||||||
if ((opts + 8 * len - 2) > endp) return; // truncated
|
len = *opts++; // check valid hwaddr and get it
|
||||||
|
if ((opts + 8 * len - 2) > endp) return; // truncated
|
||||||
if (!mg_l2_ip6get(ifp->l2type, l2, opts, len)) return;
|
if (!mg_l2_ip6get(ifp->l2type, l2, opts, len)) return;
|
||||||
req[0] = pkt->ip6->src[0], req[1] = pkt->ip6->src[1]; // align to 64-bit
|
req[0] = pkt->ip6->src[0], req[1] = pkt->ip6->src[1]; // align to 64-bit
|
||||||
tx_ndp_na(ifp, l2, target, req, true, ifp->mac);
|
tx_ndp_na(ifp, l2, target, req, true, ifp->mac);
|
||||||
@@ -972,7 +987,7 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
bool gotl2addr = false, gotprefix = false, changed = false;
|
bool gotl2addr = false, gotprefix = false, changed = false;
|
||||||
uint8_t l2[sizeof(struct mg_l2addr)];
|
uint8_t l2[sizeof(struct mg_l2addr)];
|
||||||
uint32_t mtu = 0;
|
uint32_t mtu = 0;
|
||||||
uint8_t *prefix, prefix_len;
|
uint8_t *prefix = NULL, prefix_len = 0;
|
||||||
|
|
||||||
if (pkt->pay.len < sizeof(*ra)) return;
|
if (pkt->pay.len < sizeof(*ra)) return;
|
||||||
if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
||||||
@@ -989,13 +1004,14 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
} else if (type == 5 && length >= 8) {
|
} else if (type == 5 && length >= 8) {
|
||||||
// process MTU if available, ignore if it smells
|
// process MTU if available, ignore if it smells
|
||||||
mtu = MG_LOAD_BE32(opts + 4);
|
mtu = MG_LOAD_BE32(opts + 4);
|
||||||
if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU
|
if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU
|
||||||
} else if (type == 3 && length >= 32) {
|
} else if (type == 3 && length >= 32) {
|
||||||
// process prefix, 4.6.2
|
// process prefix, 4.6.2, ignore if it smells
|
||||||
uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40
|
uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40
|
||||||
uint32_t valid = MG_LOAD_BE32(opts + 4);
|
uint32_t valid = MG_LOAD_BE32(opts + 4);
|
||||||
uint32_t pref_lifetime = MG_LOAD_BE32(opts + 8);
|
uint32_t pref_lifetime = MG_LOAD_BE32(opts + 8);
|
||||||
prefix_len = opts[2];
|
prefix_len = opts[2];
|
||||||
|
if (prefix_len >= 128) break;
|
||||||
prefix = opts + 16;
|
prefix = opts + 16;
|
||||||
|
|
||||||
// TODO (robertc2000): handle prefix options if necessary
|
// TODO (robertc2000): handle prefix options if necessary
|
||||||
@@ -1004,7 +1020,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
(void) pref_lifetime;
|
(void) pref_lifetime;
|
||||||
|
|
||||||
gotprefix = true;
|
gotprefix = true;
|
||||||
if (prefix_len != ifp->prefix_len || !match_prefix(prefix, ifp->prefix, ifp->prefix_len))
|
if (prefix_len != ifp->prefix_len ||
|
||||||
|
!match_prefix(prefix, ifp->prefix, ifp->prefix_len))
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
opts += length;
|
opts += length;
|
||||||
@@ -1016,7 +1033,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
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];
|
||||||
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 || ifp->l2type == MG_TCPIP_L2_PPPoE) {
|
if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP ||
|
||||||
|
ifp->l2type == MG_TCPIP_L2_PPPoE) {
|
||||||
ifp->state6 = MG_TCPIP_STATE_READY;
|
ifp->state6 = MG_TCPIP_STATE_READY;
|
||||||
ifp->gw6_ready = true;
|
ifp->gw6_ready = true;
|
||||||
}
|
}
|
||||||
@@ -1101,7 +1119,8 @@ static void onstate6change(struct mg_tcpip_if *ifp) {
|
|||||||
ifp->is_ip6_changed = false;
|
ifp->is_ip6_changed = false;
|
||||||
}
|
}
|
||||||
} else if (ifp->state6 == MG_TCPIP_STATE_IP) {
|
} else if (ifp->state6 == MG_TCPIP_STATE_IP) {
|
||||||
if ((ifp->gw6[0] != 0 || ifp->gw6[1] != 0) && (ifp->l2type != MG_TCPIP_L2_PPP && ifp->l2type != MG_TCPIP_L2_PPPoE))
|
if ((ifp->gw6[0] != 0 || ifp->gw6[1] != 0) &&
|
||||||
|
(ifp->l2type != MG_TCPIP_L2_PPP && ifp->l2type != MG_TCPIP_L2_PPPoE))
|
||||||
tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution
|
tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution
|
||||||
} else if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
} else if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
||||||
MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll));
|
MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll));
|
||||||
@@ -1191,8 +1210,8 @@ static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
|
|
||||||
static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||||
struct mg_addr *ip_src, struct mg_addr *ip_dst,
|
struct mg_addr *ip_src, struct mg_addr *ip_dst,
|
||||||
uint8_t flags, uint32_t seq, uint32_t ack, const void *buf,
|
uint8_t dscp, uint8_t flags, uint32_t seq, uint32_t ack,
|
||||||
size_t len) {
|
const void *buf, size_t len) {
|
||||||
uint8_t *l3p;
|
uint8_t *l3p;
|
||||||
struct ip *ip = NULL;
|
struct ip *ip = NULL;
|
||||||
struct tcp *tcp;
|
struct tcp *tcp;
|
||||||
@@ -1217,14 +1236,15 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
|||||||
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (ip_dst->is_ip6) {
|
if (ip_dst->is_ip6) {
|
||||||
ip6 =
|
ip6 = tx_ip6(ifp, l2_dst, 6, dscp, ip_src->addr.ip6, ip_dst->addr.ip6,
|
||||||
tx_ip6(ifp, l2_dst, 6, ip_src->addr.ip6, ip_dst->addr.ip6, hlen + len);
|
hlen + len);
|
||||||
tcp = (struct tcp *) (ip6 + 1);
|
tcp = (struct tcp *) (ip6 + 1);
|
||||||
l3p = (uint8_t *) ip6;
|
l3p = (uint8_t *) ip6;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
ip = tx_ip(ifp, l2_dst, 6, ip_src->addr.ip4, ip_dst->addr.ip4, hlen + len);
|
ip = tx_ip(ifp, l2_dst, 6, dscp, ip_src->addr.ip4, ip_dst->addr.ip4,
|
||||||
|
hlen + len);
|
||||||
tcp = (struct tcp *) (ip + 1);
|
tcp = (struct tcp *) (ip + 1);
|
||||||
l3p = (uint8_t *) ip;
|
l3p = (uint8_t *) ip;
|
||||||
}
|
}
|
||||||
@@ -1274,11 +1294,12 @@ static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
|||||||
ipd.port = pkt->tcp->sport;
|
ipd.port = pkt->tcp->sport;
|
||||||
if ((l2addr = get_return_l2addr(ifp, &ipd, false, pkt)) == NULL)
|
if ((l2addr = get_return_l2addr(ifp, &ipd, false, pkt)) == NULL)
|
||||||
return 0; // safety net for lousy networks
|
return 0; // safety net for lousy networks
|
||||||
return tx_tcp(ifp, l2addr, &ips, &ipd, flags, seqno, ackno, NULL, 0);
|
return tx_tcp(ifp, l2addr, &ips, &ipd, 0, flags, seqno, ackno, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
|
static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
|
||||||
return tx_tcp_ctrlresp(ifp, pkt, toack ? TH_RST : (TH_RST | TH_ACK),
|
return tx_tcp_ctrlresp(ifp, pkt,
|
||||||
|
(uint8_t) (toack ? TH_RST : (TH_RST | TH_ACK)),
|
||||||
toack ? pkt->tcp->ack : 0);
|
toack ? pkt->tcp->ack : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1294,7 +1315,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
|||||||
s = (struct connstate *) (c + 1);
|
s = (struct connstate *) (c + 1);
|
||||||
s->dmss = mss; // from options in client SYN
|
s->dmss = mss; // from options in client SYN
|
||||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
|
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
|
||||||
s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t)(s->seq + s->win);
|
s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win);
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (lsn->loc.is_ip6) {
|
if (lsn->loc.is_ip6) {
|
||||||
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
||||||
@@ -1379,7 +1400,7 @@ static bool udp_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
ips.addr.ip4 = ifp->ip;
|
ips.addr.ip4 = ifp->ip;
|
||||||
}
|
}
|
||||||
ips.port = c->loc.port;
|
ips.port = c->loc.port;
|
||||||
return tx_udp(ifp, s->mac, &ips, &c->rem, buf, len);
|
return tx_udp(ifp, s->mac, &ips, &c->rem, c->dscp, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
|
long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||||
@@ -1394,7 +1415,7 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
if (room == 0) return MG_IO_WAIT;
|
if (room == 0) return MG_IO_WAIT;
|
||||||
if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts
|
if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts
|
||||||
if ((uint32_t) len > room) len = room;
|
if ((uint32_t) len > room) len = room;
|
||||||
sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_PUSH | TH_ACK,
|
sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
|
||||||
mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
|
mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
|
||||||
if (sent == 0) {
|
if (sent == 0) {
|
||||||
return MG_IO_WAIT;
|
return MG_IO_WAIT;
|
||||||
@@ -1409,16 +1430,16 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void handle_tls_recv(struct mg_connection *c) {
|
static void handle_tls_recv(struct mg_connection *c) {
|
||||||
size_t avail = mg_tls_pending(c); // will change after mg_tls_recv()
|
size_t avail = mg_tls_pending(c); // will change after mg_tls_recv()
|
||||||
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");
|
mg_error(c, "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
|
||||||
// moved (mg_tls_pending() > 0 after mg_tls_recv() returns). So mg_tls_recv()
|
// moved (mg_tls_pending() > 0 after mg_tls_recv() returns). So
|
||||||
// takes care of returning 0 in this case (commented as "MIP")
|
// mg_tls_recv() takes care of returning 0 in this case (commented as "MIP")
|
||||||
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) {
|
||||||
@@ -1433,7 +1454,7 @@ static void handle_tls_recv(struct mg_connection *c) {
|
|||||||
|
|
||||||
static void handle_ack(struct connstate *s, uint32_t ackno, uint16_t win) {
|
static void handle_ack(struct connstate *s, uint32_t ackno, uint16_t win) {
|
||||||
if (ackno < (s->seq - s->win) || ackno > s->seq) return;
|
if (ackno < (s->seq - s->win) || ackno > s->seq) return;
|
||||||
s->maxseq = (uint32_t)(ackno + win);
|
s->maxseq = (uint32_t) (ackno + win);
|
||||||
s->win = win;
|
s->win = win;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1445,8 +1466,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
uint8_t flags = TH_ACK;
|
uint8_t flags = TH_ACK;
|
||||||
if (mg_ntohl(pkt->tcp->seq) != s->ack) {
|
if (mg_ntohl(pkt->tcp->seq) != s->ack) {
|
||||||
MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack));
|
MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack));
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
mg_htonl(s->ack), "", 0);
|
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If we initiated the closure, we reply with ACK upon receiving FIN
|
// If we initiated the closure, we reply with ACK upon receiving FIN
|
||||||
@@ -1467,14 +1488,14 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
// is flushed, then send our FIN.
|
// is flushed, then send our FIN.
|
||||||
c->is_draining = 1;
|
c->is_draining = 1;
|
||||||
}
|
}
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, flags, mg_htonl(s->seq),
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, flags,
|
||||||
mg_htonl(s->ack), "", 0);
|
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
|
||||||
if (pkt->pay.len == 0) return; // if no data, we're done
|
if (pkt->pay.len == 0) return; // if no data, we're done
|
||||||
} else if (pkt->pay.len <= 1 && mg_ntohl(pkt->tcp->seq) == s->ack - 1) {
|
} else if (pkt->pay.len <= 1 && mg_ntohl(pkt->tcp->seq) == s->ack - 1) {
|
||||||
// Keep-Alive (RFC-9293 3.8.4, allow erroneous implementations)
|
// Keep-Alive (RFC-9293 3.8.4, allow erroneous implementations)
|
||||||
MG_VERBOSE(("%lu keepalive ACK", c->id));
|
MG_VERBOSE(("%lu keepalive ACK", c->id));
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
mg_htonl(s->ack), NULL, 0);
|
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
|
||||||
return; // no data to process
|
return; // no data to process
|
||||||
} else if (pkt->pay.len == 0) { // this is an ACK
|
} else if (pkt->pay.len == 0) { // this is an ACK
|
||||||
if (pkt->tcp->flags & TH_ACK)
|
if (pkt->tcp->flags & TH_ACK)
|
||||||
@@ -1487,8 +1508,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
MG_VERBOSE(("ignoring duplicate pkt"));
|
MG_VERBOSE(("ignoring duplicate pkt"));
|
||||||
} else {
|
} else {
|
||||||
MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
|
MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
mg_htonl(s->ack), "", 0);
|
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
|
||||||
}
|
}
|
||||||
return; // drop it
|
return; // drop it
|
||||||
} else if (io->size - io->len < pkt->pay.len &&
|
} else if (io->size - io->len < pkt->pay.len &&
|
||||||
@@ -1496,8 +1517,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
mg_error(c, "oom");
|
mg_error(c, "oom");
|
||||||
return; // drop it
|
return; // drop it
|
||||||
}
|
}
|
||||||
if (pkt->tcp->flags & TH_ACK)
|
if (pkt->tcp->flags & TH_ACK)
|
||||||
handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win));
|
handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win));
|
||||||
// Copy TCP payload into the IO buffer. If the connection is plain text,
|
// 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,
|
// 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,
|
// therefore we copy that encrypted data to the c->rtls iobuffer instead,
|
||||||
@@ -1513,8 +1534,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) {
|
if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) {
|
||||||
// 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, TH_ACK, mg_htonl(s->seq),
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
mg_htonl(s->ack), NULL, 0);
|
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
|
||||||
s->unacked = 0;
|
s->unacked = 0;
|
||||||
s->acked = s->ack;
|
s->acked = s->ack;
|
||||||
if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
|
if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||||
@@ -1587,9 +1608,9 @@ static bool handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
|
|||||||
s->dmss = ip6 ? 1220 : 536; // assume default, RFC-9293 3.7.1
|
s->dmss = ip6 ? 1220 : 536; // assume default, RFC-9293 3.7.1
|
||||||
while (len > 0) { // RFC-9293 3.1 3.2
|
while (len > 0) { // RFC-9293 3.1 3.2
|
||||||
uint8_t kind = opts[0], optlen = 1;
|
uint8_t kind = opts[0], optlen = 1;
|
||||||
if (kind != 1) { // No-Operation
|
if (kind != 1) { // No-Operation
|
||||||
if (kind == 0) break; // End of Option List
|
if (kind == 0) break; // End of Option List
|
||||||
if (len < 2 || opts[1] == 0) return false; // Malformed options
|
if (len < 2 || opts[1] == 0 || opts[1] > len) return false; // Malformed
|
||||||
optlen = opts[1];
|
optlen = opts[1];
|
||||||
if (kind == 2 && optlen == 4) // set received MSS
|
if (kind == 2 && optlen == 4) // set received MSS
|
||||||
s->dmss = (uint16_t) (((uint16_t) opts[2] << 8) + opts[3]);
|
s->dmss = (uint16_t) (((uint16_t) opts[2] << 8) + opts[3]);
|
||||||
@@ -1616,7 +1637,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
if (!handle_opt(s, pkt->tcp, pkt->ip6 != NULL))
|
if (!handle_opt(s, pkt->tcp, pkt->ip6 != NULL))
|
||||||
return; // process options (MSS)
|
return; // process options (MSS)
|
||||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
|
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
|
||||||
s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t)(s->seq + s->win);
|
s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win);
|
||||||
tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
|
tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
|
||||||
c->is_connecting = 0; // Client connected
|
c->is_connecting = 0; // Client connected
|
||||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||||
@@ -1922,11 +1943,12 @@ static void mg_ip6_poll(struct mg_tcpip_if *ifp, bool s1) {
|
|||||||
static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
|
static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
|
||||||
bool cur_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN);
|
bool cur_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN);
|
||||||
bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP);
|
bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP);
|
||||||
|
const uint8_t px[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6
|
||||||
if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
|
if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
|
||||||
if (l2_up && ifp->ip6ll[0] == 0 && ifp->ip6ll[1] == 0) { // gen ll address
|
if (ifp->l2type == MG_TCPIP_L2_ETH && l2_up && ifp->ip6ll[0] == 0 &&
|
||||||
uint8_t px[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6
|
ifp->ip6ll[1] == 0) { // gen ll address
|
||||||
mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
|
mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
|
||||||
memcpy(ifp->ip6ll, px, 8); // RFC-4291 2.5.4
|
memcpy(ifp->ip6ll, px, 8); // RFC-2464 5
|
||||||
} // just got our link local address if we didn't have one.
|
} // just got our link local address if we didn't have one.
|
||||||
// If static configuration is used, global addresses,
|
// If static configuration is used, global addresses,
|
||||||
// prefix length, and gw are already filled at this point.
|
// prefix length, and gw are already filled at this point.
|
||||||
@@ -1934,14 +1956,21 @@ static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
|
|||||||
if (!l2_up && ifp->enable_slaac) ifp->ip6[0] = ifp->ip6[1] = 0;
|
if (!l2_up && ifp->enable_slaac) ifp->ip6[0] = ifp->ip6[1] = 0;
|
||||||
ifp->state6 = !drv_up ? MG_TCPIP_STATE_DOWN
|
ifp->state6 = !drv_up ? MG_TCPIP_STATE_DOWN
|
||||||
: !l2_up ? MG_TCPIP_STATE_LINK_UP
|
: !l2_up ? MG_TCPIP_STATE_LINK_UP
|
||||||
: ifp->enable_slaac || ifp->ip6[0] == 0 ? MG_TCPIP_STATE_UP
|
: ifp->ip6ll[0] == 0 || ifp->enable_slaac || ifp->ip6[0] == 0
|
||||||
: MG_TCPIP_STATE_IP;
|
? MG_TCPIP_STATE_UP
|
||||||
|
: MG_TCPIP_STATE_IP;
|
||||||
onstate6change(ifp);
|
onstate6change(ifp);
|
||||||
} else if (!ifp->enable_slaac && ifp->state6 == MG_TCPIP_STATE_UP &&
|
} else if (!ifp->enable_slaac && ifp->state6 == MG_TCPIP_STATE_UP &&
|
||||||
ifp->ip6[0]) {
|
ifp->ip6ll[0] != 0 && ifp->ip6[0] != 0) {
|
||||||
ifp->state6 = MG_TCPIP_STATE_IP; // ifp->fn has set an IP
|
ifp->state6 = MG_TCPIP_STATE_IP; // ifp->fn has set an IP
|
||||||
onstate6change(ifp);
|
onstate6change(ifp);
|
||||||
}
|
}
|
||||||
|
if ((ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE) &&
|
||||||
|
ifp->state6 == MG_TCPIP_STATE_UP && ifp->ip6ll[0] == 0 &&
|
||||||
|
ifp->ip6ll[1] != 0) { // IPV6CP has got an IFCID, gen ll address
|
||||||
|
memcpy(ifp->ip6ll, px, 8); // RFC-5072 5
|
||||||
|
onstate6change(ifp);
|
||||||
|
} // just got our link local address if we didn't have one.
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define mg_ip6_poll(x, y)
|
#define mg_ip6_poll(x, y)
|
||||||
@@ -1994,8 +2023,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
drv_up = ifp->driver->poll ? ifp->driver->poll(ifp, expired_1000ms) : true;
|
drv_up = ifp->driver->poll ? ifp->driver->poll(ifp, expired_1000ms) : true;
|
||||||
l2_up = mg_l2_poll(ifp, expired_1000ms); // Handle L2 up/down link status;
|
l2_up = mg_l2_poll(ifp, expired_1000ms); // Handle L2 up/down link status;
|
||||||
if (expired_1000ms) { // ifp->state rules over state6
|
if (expired_1000ms) { // ifp->state rules over state6
|
||||||
mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4
|
mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4
|
||||||
mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6
|
mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6
|
||||||
if (ifp->state < MG_TCPIP_STATE_UP) MG_ERROR(("Network is down"));
|
if (ifp->state < MG_TCPIP_STATE_UP) MG_ERROR(("Network is down"));
|
||||||
mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL);
|
mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL);
|
||||||
}
|
}
|
||||||
@@ -2034,7 +2063,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
continue;
|
continue;
|
||||||
} else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) {
|
} else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) {
|
||||||
MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack));
|
MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack));
|
||||||
tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
|
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq),
|
||||||
mg_htonl(s->ack), NULL, 0);
|
mg_htonl(s->ack), NULL, 0);
|
||||||
s->acked = s->ack;
|
s->acked = s->ack;
|
||||||
} else if (s->ttype == MIP_TTYPE_SYN) {
|
} else if (s->ttype == MIP_TTYPE_SYN) {
|
||||||
@@ -2047,8 +2076,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
mg_error(c, "keepalive");
|
mg_error(c, "keepalive");
|
||||||
} else {
|
} else {
|
||||||
MG_VERBOSE(("%lu keepalive", c->id));
|
MG_VERBOSE(("%lu keepalive", c->id));
|
||||||
tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq - 1),
|
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
mg_htonl(s->ack), NULL, 0);
|
mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2111,7 +2140,8 @@ void mg_tcpip_free(struct mg_tcpip_if *ifp) {
|
|||||||
static void send_syn(struct mg_connection *c) {
|
static void send_syn(struct mg_connection *c) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
|
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_SYN, isn, 0, NULL, 0);
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_SYN, isn, 0, NULL,
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l2addr_resolved(struct mg_connection *c) {
|
static void l2addr_resolved(struct mg_connection *c) {
|
||||||
@@ -2132,9 +2162,9 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
c->loc.port = mg_htons(ifp->eport++);
|
c->loc.port = mg_htons(ifp->eport++);
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->rem.is_ip6) {
|
if (c->rem.is_ip6) {
|
||||||
if(c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
||||||
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
||||||
} else { // use global address
|
} else { // use global address
|
||||||
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
||||||
}
|
}
|
||||||
c->loc.is_ip6 = true;
|
c->loc.is_ip6 = true;
|
||||||
@@ -2214,7 +2244,7 @@ static void init_closure(struct mg_connection *c) {
|
|||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
if (c->is_udp == false && c->is_listening == false &&
|
if (c->is_udp == false && c->is_listening == false &&
|
||||||
c->is_connecting == false) { // For TCP conns,
|
c->is_connecting == false) { // For TCP conns,
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_FIN | TH_ACK,
|
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), NULL, 0);
|
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
|
||||||
settmout(c, MIP_TTYPE_FIN);
|
settmout(c, MIP_TTYPE_FIN);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -29,7 +29,8 @@
|
|||||||
((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
|
((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
|
||||||
(errcode) == -pdFREERTOS_ERRNO_EISCONN || \
|
(errcode) == -pdFREERTOS_ERRNO_EISCONN || \
|
||||||
(errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
|
(errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
|
||||||
(errcode) == -pdFREERTOS_ERRNO_EAGAIN)
|
(errcode) == -pdFREERTOS_ERRNO_EAGAIN || \
|
||||||
|
(errcode) == -pdFREERTOS_ERRNO_ENOSPC)
|
||||||
|
|
||||||
#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
|
#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "json.h"
|
#include "json.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
|
#include "tls.h"
|
||||||
#include "url.h"
|
#include "url.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -26,6 +27,19 @@ static struct mg_ota_state {
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
static bool s_tls_init(struct mg_connection *c, const char *url) {
|
||||||
|
struct mg_tls_opts opts;
|
||||||
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
opts.ca = mg_str(MG_OTA_TLS_CA);
|
||||||
|
opts.name = mg_url_host(url);
|
||||||
|
if (opts.ca.len == 0) {
|
||||||
|
mg_error(c, "MG_OTA_TLS_CA is not configured");
|
||||||
|
} else {
|
||||||
|
mg_tls_init(c, &opts);
|
||||||
|
}
|
||||||
|
return c->is_tls_hs != 0 && !c->is_closing;
|
||||||
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_CUSTOM_DEVICE_ID
|
#if MG_ENABLE_CUSTOM_DEVICE_ID
|
||||||
#else
|
#else
|
||||||
void mg_ota_device_id(char *buf, size_t len) {
|
void mg_ota_device_id(char *buf, size_t len) {
|
||||||
@@ -58,6 +72,11 @@ void mg_ota_device_id(char *buf, size_t len) {
|
|||||||
|
|
||||||
static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
uint64_t expiration = *(uint64_t *) c->data;
|
uint64_t expiration = *(uint64_t *) c->data;
|
||||||
|
if (s_ota == NULL) {
|
||||||
|
c->fn = NULL;
|
||||||
|
c->is_closing = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (ev == MG_EV_POLL) {
|
if (ev == MG_EV_POLL) {
|
||||||
if (mg_millis() > expiration) mg_error(c, "Metadata timeout");
|
if (mg_millis() > expiration) mg_error(c, "Metadata timeout");
|
||||||
} else if (ev == MG_EV_CONNECT) {
|
} else if (ev == MG_EV_CONNECT) {
|
||||||
@@ -65,6 +84,9 @@ static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_str host = mg_url_host(s_ota->json_url);
|
struct mg_str host = mg_url_host(s_ota->json_url);
|
||||||
const char *uri = mg_url_uri(s_ota->json_url);
|
const char *uri = mg_url_uri(s_ota->json_url);
|
||||||
const char *sep = strchr(uri, '?') == NULL ? "?" : "&";
|
const char *sep = strchr(uri, '?') == NULL ? "?" : "&";
|
||||||
|
if (mg_url_is_ssl(s_ota->json_url)) {
|
||||||
|
if (!s_tls_init(c, s_ota->json_url)) return;
|
||||||
|
}
|
||||||
mg_ota_device_id(id, sizeof(id));
|
mg_ota_device_id(id, sizeof(id));
|
||||||
id[sizeof(id) - 1] = '\0';
|
id[sizeof(id) - 1] = '\0';
|
||||||
mg_printf(
|
mg_printf(
|
||||||
@@ -130,10 +152,18 @@ static void status_fn_2(struct mg_connection *c, const char *errmsg) {
|
|||||||
|
|
||||||
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) {
|
||||||
uint64_t expiration = *(uint64_t *) c->data;
|
uint64_t expiration = *(uint64_t *) c->data;
|
||||||
|
if (s_ota == NULL) {
|
||||||
|
c->fn = NULL;
|
||||||
|
c->is_closing = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (ev == MG_EV_POLL) {
|
if (ev == MG_EV_POLL) {
|
||||||
if (mg_millis() > expiration) mg_error(c, "OTA timeout");
|
if (mg_millis() > expiration) mg_error(c, "OTA timeout");
|
||||||
} else if (ev == MG_EV_CONNECT) {
|
} else if (ev == MG_EV_CONNECT) {
|
||||||
struct mg_str host = mg_url_host(s_ota->url);
|
struct mg_str host = mg_url_host(s_ota->url);
|
||||||
|
if (mg_url_is_ssl(s_ota->url)) {
|
||||||
|
if (!s_tls_init(c, s_ota->url)) return;
|
||||||
|
}
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"GET %s HTTP/1.1\r\n"
|
"GET %s HTTP/1.1\r\n"
|
||||||
"Host: %.*s\r\n"
|
"Host: %.*s\r\n"
|
||||||
@@ -185,7 +215,7 @@ 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
|
||||||
|
|
||||||
static uint64_t feed_timer; // Advances 500ms per tick; tracks elapsed time
|
static uint64_t feed_timer; // Advances 500ms per tick; tracks elapsed time
|
||||||
if (MG_OTA_STATE_GET() == MG_OTA_TESTING &&
|
if (MG_OTA_STATE_GET() == MG_OTA_FAILED &&
|
||||||
mg_timer_expired(&feed_timer, 500, mg_millis())) {
|
mg_timer_expired(&feed_timer, 500, mg_millis())) {
|
||||||
if (feed_timer < (uint64_t) MG_OTA_ROLLBACK_TIMEOUT_SECONDS * 1000) {
|
if (feed_timer < (uint64_t) MG_OTA_ROLLBACK_TIMEOUT_SECONDS * 1000) {
|
||||||
MG_OTA_ROLLBACK_TIMER_FEED(); // Feed watchdog every 500ms
|
MG_OTA_ROLLBACK_TIMER_FEED(); // Feed watchdog every 500ms
|
||||||
|
|||||||
@@ -2,7 +2,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 pull-based updates
|
// - defining MG_OTA_URL enables HTTP or HTTPS pull-based updates
|
||||||
// - mg_http_start_ota() enables push-based updates
|
// - mg_http_start_ota() 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
|
||||||
@@ -149,8 +149,10 @@ enum {
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// Pull-based OTA over HTTP. Fetches metadata_url, which must return a JSON
|
// Pull-based OTA over HTTP or HTTPS. If it's HTTPS, MG_OTA_TLS_CA
|
||||||
// object like: { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
|
// needs to be defined. Fetches metadata_url, which must
|
||||||
|
// return a JSON object like:
|
||||||
|
// { "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_OTA_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
|
||||||
@@ -160,12 +162,18 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
|
|||||||
void (*fn)(const char *error_message));
|
void (*fn)(const char *error_message));
|
||||||
|
|
||||||
// Firmware info URL for mg_ota_poll() which calls mg_ota_url_check().
|
// Firmware info URL for mg_ota_poll() which calls mg_ota_url_check().
|
||||||
// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/
|
// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/
|
||||||
// Settable in mongoose_config.h
|
// Settable in mongoose_config.h
|
||||||
#ifndef MG_OTA_URL
|
#ifndef MG_OTA_URL
|
||||||
#define MG_OTA_URL NULL
|
#define MG_OTA_URL NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// PEM or DER CA certificate used to authenticate an HTTPS OTA server. Set to
|
||||||
|
// NULL to use HTTP. Set in mongoose_config.h.
|
||||||
|
#ifndef MG_OTA_TLS_CA
|
||||||
|
#define MG_OTA_TLS_CA NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
// OTA status callback function for mg_ota_poll()
|
// OTA status callback function for mg_ota_poll()
|
||||||
#ifndef MG_OTA_STATUS_FN
|
#ifndef MG_OTA_STATUS_FN
|
||||||
#define MG_OTA_STATUS_FN NULL
|
#define MG_OTA_STATUS_FN NULL
|
||||||
@@ -195,6 +203,7 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
|
|||||||
// E.g. on STM32, uses the 96-bit MCU UID converted to a hex string.
|
// E.g. on STM32, uses the 96-bit MCU UID converted to a hex string.
|
||||||
void mg_ota_device_id(char *buf, size_t len);
|
void mg_ota_device_id(char *buf, size_t len);
|
||||||
|
|
||||||
// Checks for a firmware update over HTTP. Called automatically by mg_mgr_poll()
|
// Checks for a firmware update over HTTP or HTTPS. Called automatically by
|
||||||
// when MG_OTA_URL is set in mongoose_config.h. Do not call directly.
|
// mg_mgr_poll() when MG_OTA_URL is set in mongoose_config.h. Do not call
|
||||||
|
// directly.
|
||||||
void mg_ota_poll(struct mg_mgr *);
|
void mg_ota_poll(struct mg_mgr *);
|
||||||
@@ -46,6 +46,10 @@ bool mg_ota_end(void) {
|
|||||||
}
|
}
|
||||||
MG_DEBUG(("Finished ESP32 OTA, success: %d", s_ota_success));
|
MG_DEBUG(("Finished ESP32 OTA, success: %d", s_ota_success));
|
||||||
s_ota_update_partition = NULL;
|
s_ota_update_partition = NULL;
|
||||||
|
if (s_ota_success) {
|
||||||
|
MG_OTA_STATE_SET(MG_OTA_TESTING);
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
return s_ota_success;
|
return s_ota_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-25
@@ -491,10 +491,6 @@ MG_IRAM static bool mg_imxrt_erase(void *addr) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MG_IRAM bool mg_imxrt_swap(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MG_IRAM static bool mg_imxrt_write(void *addr, const void *buf, size_t len) {
|
MG_IRAM static bool mg_imxrt_write(void *addr, const void *buf, size_t len) {
|
||||||
struct mg_flexspi_nor_config config, *config_ptr = &config;
|
struct mg_flexspi_nor_config config, *config_ptr = &config;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -550,16 +546,40 @@ fwxit:
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just overwrite instead of swap
|
|
||||||
MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
|
MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
|
||||||
// no stdlib calls here
|
char *scratch = p2 + s;
|
||||||
for (size_t ofs = 0; ofs < s; ofs += ss) {
|
for (size_t ofs = 0; ofs < s; ofs += ss) {
|
||||||
|
MG_OTA_ROLLBACK_TIMER_FEED();
|
||||||
|
mg_imxrt_write(scratch, p1 + ofs, ss);
|
||||||
|
MG_OTA_ROLLBACK_TIMER_FEED();
|
||||||
mg_imxrt_write(p1 + ofs, p2 + ofs, ss);
|
mg_imxrt_write(p1 + ofs, p2 + ofs, ss);
|
||||||
|
MG_OTA_ROLLBACK_TIMER_FEED();
|
||||||
|
mg_imxrt_write(p2 + ofs, scratch, ss);
|
||||||
}
|
}
|
||||||
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
|
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MG_IRAM static bool mg_imxrt_swap(void) {
|
||||||
|
size_t ss = s_mg_flash_imxrt.secsz;
|
||||||
|
char *p1 = (char *) s_mg_flash_imxrt.start;
|
||||||
|
char *p2 = p1 + s_mg_flash_imxrt.size / 2;
|
||||||
|
size_t s = s_mg_flash_imxrt.size / 2 - ss;
|
||||||
|
MG_INFO(("Swapping partitions, %u bytes", s));
|
||||||
|
MG_INFO(("Do NOT power off..."));
|
||||||
|
MG_OTA_ROLLBACK_TIMER_FEED();
|
||||||
|
mg_log_level = MG_LL_NONE;
|
||||||
|
s_flash_irq_disabled = true;
|
||||||
|
single_bank_swap(p1, p2, s, ss);
|
||||||
|
return true; // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
bool mg_ota_begin(size_t new_firmware_size) {
|
bool mg_ota_begin(size_t new_firmware_size) {
|
||||||
|
size_t max = s_mg_flash_imxrt.size / 2 - s_mg_flash_imxrt.secsz;
|
||||||
|
if (new_firmware_size > max) {
|
||||||
|
MG_ERROR(("Firmware %lu too big for single-bank OTA, max %lu",
|
||||||
|
new_firmware_size, max));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_imxrt);
|
return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_imxrt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,25 +588,7 @@ bool mg_ota_write(const void *buf, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool mg_ota_end(void) {
|
bool mg_ota_end(void) {
|
||||||
if (mg_ota_flash_end(&s_mg_flash_imxrt)) {
|
mg_ota_flash_end(&s_mg_flash_imxrt);
|
||||||
if (0) { // is_dualbank()
|
|
||||||
// TODO(): no devices so far
|
|
||||||
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
|
|
||||||
} else {
|
|
||||||
// Swap partitions. Pray power does not go away
|
|
||||||
MG_INFO(("Swapping partitions, size %u (%u sectors)",
|
|
||||||
s_mg_flash_imxrt.size,
|
|
||||||
s_mg_flash_imxrt.size / s_mg_flash_imxrt.secsz));
|
|
||||||
MG_INFO(("Do NOT power off..."));
|
|
||||||
mg_log_level = MG_LL_NONE;
|
|
||||||
s_flash_irq_disabled = true;
|
|
||||||
// Runs in RAM, will reset when finished
|
|
||||||
single_bank_swap(
|
|
||||||
(char *) s_mg_flash_imxrt.start,
|
|
||||||
(char *) s_mg_flash_imxrt.start + s_mg_flash_imxrt.size / 2,
|
|
||||||
s_mg_flash_imxrt.size / 2, s_mg_flash_imxrt.secsz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
struct mg_flash *mg_flash = &s_mg_flash_imxrt;
|
struct mg_flash *mg_flash = &s_mg_flash_imxrt;
|
||||||
|
|||||||
+1
-3
@@ -45,11 +45,9 @@ MG_IRAM static int is_dualbank(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MG_IRAM static void flash_unlock(void) {
|
MG_IRAM static void flash_unlock(void) {
|
||||||
static bool unlocked = false;
|
if (MG_REG(MG_FLASH_BASE + MG_FLASH_CR) & MG_BIT(31)) {
|
||||||
if (unlocked == false) {
|
|
||||||
MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0x45670123;
|
MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0x45670123;
|
||||||
MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0xcdef89ab;
|
MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0xcdef89ab;
|
||||||
unlocked = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -39,13 +39,14 @@ static uint32_t sectors_per_bank(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void flash_unlock(void) {
|
static void flash_unlock(void) {
|
||||||
static bool unlocked = false;
|
if (MG_REG(FLASH_NSCR) & MG_BIT(0)) {
|
||||||
if (unlocked == false) {
|
|
||||||
MG_REG(FLASH_KEYR) = 0x45670123;
|
MG_REG(FLASH_KEYR) = 0x45670123;
|
||||||
MG_REG(FLASH_KEYR) = 0Xcdef89ab;
|
MG_REG(FLASH_KEYR) = 0xcdef89ab;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MG_REG(FLASH_OPTCR) & MG_BIT(0)) {
|
||||||
MG_REG(FLASH_OPTKEYR) = 0x08192a3b;
|
MG_REG(FLASH_OPTKEYR) = 0x08192a3b;
|
||||||
MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f;
|
MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f;
|
||||||
unlocked = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -53,17 +53,17 @@ MG_IRAM static bool is_dualbank(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MG_IRAM static void flash_unlock(void) {
|
MG_IRAM static void flash_unlock(void) {
|
||||||
static bool unlocked = false;
|
if (MG_REG(FLASH_BASE1 + FLASH_CR) & MG_BIT(0)) {
|
||||||
if (unlocked == false) {
|
MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0x45670123;
|
||||||
MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0x45670123;
|
MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0xcdef89ab;
|
||||||
MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0xcdef89ab;
|
}
|
||||||
if (is_dualbank()) {
|
if (is_dualbank() && MG_REG(FLASH_BASE2 + FLASH_CR) & MG_BIT(0)) {
|
||||||
MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0x45670123;
|
MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0x45670123;
|
||||||
MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0xcdef89ab;
|
MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0xcdef89ab;
|
||||||
}
|
}
|
||||||
MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x08192a3b; // opt reg is "shared"
|
if (MG_REG(FLASH_BASE1 + FLASH_OPTCR) & MG_BIT(0)) {
|
||||||
MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x4c5d6e7f; // thus unlock once
|
MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x08192a3b; // opt reg is "shared"
|
||||||
unlocked = true;
|
MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x4c5d6e7f; // thus unlock once
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -449,6 +449,7 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
if (rc == 0) { // Success
|
if (rc == 0) { // Success
|
||||||
setlocaddr(FD(c), &c->loc);
|
setlocaddr(FD(c), &c->loc);
|
||||||
mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
|
mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
|
||||||
|
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 (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
|
} else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
|
||||||
MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem));
|
MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem));
|
||||||
@@ -465,6 +466,14 @@ static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa,
|
|||||||
do {
|
do {
|
||||||
memset(usa, 0, sizeof(*usa));
|
memset(usa, 0, sizeof(*usa));
|
||||||
fd = accept(sock, &usa->sa, len);
|
fd = accept(sock, &usa->sa, len);
|
||||||
|
#if MG_ENABLE_FREERTOS_TCP
|
||||||
|
// FreeRTOS_accept() returns NULL to mean "no pending connection",
|
||||||
|
// therefore we avoid retrying it forever on non-blocking listeners
|
||||||
|
if (fd == NULL) {
|
||||||
|
fd = MG_INVALID_SOCKET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} while (MG_SOCK_INTR(fd));
|
} while (MG_SOCK_INTR(fd));
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ void mg_tls_ctx_init(struct mg_mgr *);
|
|||||||
void mg_tls_ctx_free(struct mg_mgr *);
|
void mg_tls_ctx_free(struct mg_mgr *);
|
||||||
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
|
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
|
// 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.
|
||||||
|
size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Low-level IO primitives used by TLS layer
|
// Low-level IO primitives used by TLS layer
|
||||||
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2 };
|
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2 };
|
||||||
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
|
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
|
||||||
|
|||||||
+5
-9
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
#include "util.h"
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
#define AES_DECRYPTION 1 // whether AES decryption is supported
|
#define AES_DECRYPTION 1 // whether AES decryption is supported
|
||||||
@@ -1061,10 +1062,9 @@ int gcm_crypt_and_tag(
|
|||||||
prepare the gcm context with the keying material, we simply
|
prepare the gcm context with the keying material, we simply
|
||||||
invoke each of the three GCM sub-functions in turn...
|
invoke each of the three GCM sub-functions in turn...
|
||||||
*/
|
*/
|
||||||
gcm_start(ctx, mode, iv, iv_len, add, add_len);
|
if (gcm_start(ctx, mode, iv, iv_len, add, add_len) != 0) return -1;
|
||||||
gcm_update(ctx, length, input, output);
|
if (gcm_update(ctx, length, input, output) != 0) return -1;
|
||||||
gcm_finish(ctx, tag, tag_len);
|
return gcm_finish(ctx, tag, tag_len);
|
||||||
return (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
@@ -1116,8 +1116,6 @@ int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
|
|||||||
int ret = 0; // our return value
|
int ret = 0; // our return value
|
||||||
gcm_context ctx; // includes the AES context structure
|
gcm_context ctx; // includes the AES context structure
|
||||||
unsigned char computed_tag[16];
|
unsigned char computed_tag[16];
|
||||||
size_t i;
|
|
||||||
int diff = 0;
|
|
||||||
|
|
||||||
if (tag_len > sizeof(computed_tag)) return -1;
|
if (tag_len > sizeof(computed_tag)) return -1;
|
||||||
|
|
||||||
@@ -1128,9 +1126,7 @@ int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
|
|||||||
|
|
||||||
gcm_zero_ctx(&ctx);
|
gcm_zero_ctx(&ctx);
|
||||||
|
|
||||||
// compare tags
|
if (!mg_memeq(computed_tag, tag, tag_len)) ret = -1;
|
||||||
for (i = 0; i < tag_len; i++) diff |= computed_tag[i] ^ tag[i];
|
|
||||||
if (diff != 0) ret = -1;
|
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|||||||
+99
-46
@@ -112,6 +112,8 @@ struct tls_data {
|
|||||||
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
|
||||||
bool is_twoway; // server is configured to authenticate clients
|
bool is_twoway; // server is configured to authenticate clients
|
||||||
|
bool is_sntp_pending; // TLS handshake is waiting for wall-clock time
|
||||||
|
struct mg_connection *timec; // SNTP connection for wall-clock time
|
||||||
struct mg_str cert_der; // certificate in DER format
|
struct mg_str cert_der; // certificate in DER format
|
||||||
struct mg_str ca_der; // current CA certificate
|
struct mg_str ca_der; // current CA certificate
|
||||||
struct mg_str *ca_bundle_der; // bundle of CA certificates
|
struct mg_str *ca_bundle_der; // bundle of CA certificates
|
||||||
@@ -192,16 +194,14 @@ static int mg_der_to_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
|
|||||||
if (tlv->len > 0x7f) { // long-form length
|
if (tlv->len > 0x7f) { // long-form length
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
n = tlv->len - 0x80;
|
n = tlv->len - 0x80;
|
||||||
if (n > 4 || dersz < (2 + n)) return -1;
|
if (n == 0 || n > 4 || dersz < (2 + n)) return -1;
|
||||||
tlv->len = 0;
|
tlv->len = 0;
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
tlv->len = (tlv->len << 8) | der[2 + i];
|
tlv->len = (tlv->len << 8) | der[2 + i];
|
||||||
}
|
}
|
||||||
if (n > (dersz - 2)) return -1;
|
|
||||||
tlv->value += n;
|
tlv->value += n;
|
||||||
} else {
|
|
||||||
if (der + dersz < tlv->value + tlv->len) return -1;
|
|
||||||
}
|
}
|
||||||
|
if (tlv->len > dersz - (size_t) (tlv->value - der)) return -1;
|
||||||
return (int) n;
|
return (int) n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ static int mg_tls_recv_record(struct mg_connection *c) {
|
|||||||
|
|
||||||
msgsz = MG_LOAD_BE16(rio->buf + 3);
|
msgsz = MG_LOAD_BE16(rio->buf + 3);
|
||||||
msg = rio->buf + 5;
|
msg = rio->buf + 5;
|
||||||
if (msgsz < 16) {
|
if (msgsz < 17) {
|
||||||
mg_error(c, "wrong size");
|
mg_error(c, "wrong size");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -1319,6 +1319,7 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) {
|
|||||||
|
|
||||||
if (rio->len < 5 + 39 + 32 + 3 + 2) goto fail;
|
if (rio->len < 5 + 39 + 32 + 3 + 2) goto fail;
|
||||||
msgsz = MG_LOAD_BE16(rio->buf + 3);
|
msgsz = MG_LOAD_BE16(rio->buf + 3);
|
||||||
|
if (msgsz > rio->len - 5) goto fail;
|
||||||
mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz);
|
mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz);
|
||||||
|
|
||||||
ext_len = MG_LOAD_BE16(rio->buf + 5 + 39 + 32 + 3);
|
ext_len = MG_LOAD_BE16(rio->buf + 5 + 39 + 32 + 3);
|
||||||
@@ -1404,8 +1405,7 @@ static void mg_der_debug_cert_name(const char *name, struct mg_der_tlv *tlv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t asnt2t(uint8_t *v, uint8_t type) {
|
static uint64_t asnt2t(uint8_t *v, uint8_t type) {
|
||||||
unsigned int y, mo, d, h, mi, ss, ly;
|
unsigned int y, mo, d, h, mi, ss;
|
||||||
uint16_t dm[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
|
|
||||||
y = 10U * (*v++ - '0'), y += (*v++ - '0');
|
y = 10U * (*v++ - '0'), y += (*v++ - '0');
|
||||||
if (type == 0x17) { // UTCTime, RFC-5280 4.1.2.5.1 YYMMDDHHMMSSZ
|
if (type == 0x17) { // UTCTime, RFC-5280 4.1.2.5.1 YYMMDDHHMMSSZ
|
||||||
if (y >= 50) return (uint64_t) 0; // 19YY is in the past
|
if (y >= 50) return (uint64_t) 0; // 19YY is in the past
|
||||||
@@ -1413,17 +1413,13 @@ static uint64_t asnt2t(uint8_t *v, uint8_t type) {
|
|||||||
} else { // GeneralizedTime, RFC-5280 4.1.2.5.2 YYYYMMDDHHMMSSZ
|
} else { // GeneralizedTime, RFC-5280 4.1.2.5.2 YYYYMMDDHHMMSSZ
|
||||||
y *= 100U, y += 10U * (*v++ - '0'), y += (*v++ - '0');
|
y *= 100U, y += 10U * (*v++ - '0'), y += (*v++ - '0');
|
||||||
}
|
}
|
||||||
y -= 1900;
|
|
||||||
mo = 10U * (*v++ - '0'), mo += (*v++ - '0');
|
mo = 10U * (*v++ - '0'), mo += (*v++ - '0');
|
||||||
d = 10U * (*v++ - '0'), d += (*v++ - '0');
|
d = 10U * (*v++ - '0'), d += (*v++ - '0');
|
||||||
h = 10U * (*v++ - '0'), h += (*v++ - '0');
|
h = 10U * (*v++ - '0'), h += (*v++ - '0');
|
||||||
mi = 10U * (*v++ - '0'), mi += (*v++ - '0');
|
mi = 10U * (*v++ - '0'), mi += (*v++ - '0');
|
||||||
ss = 10U * (*v++ - '0'), ss += (*v++ - '0');
|
ss = 10U * (*v++ - '0'), ss += (*v++ - '0');
|
||||||
if (*v != 'Z') return 0; // invalid
|
if (*v != 'Z') return 0; // invalid
|
||||||
ly = (mo > 2) ? y + 1 : y;
|
return mg_timegm(y, mo, d, h, mi, ss);
|
||||||
return (uint64_t) ss + 60U * mi + 3600U * h + 86400U * (dm[mo - 1] + d - 1) +
|
|
||||||
31536000U * (y - 70) + 86400U * ((ly - 69) / 4U) -
|
|
||||||
86400U * ((ly - 1) / 100U) + 86400U * ((ly + 299) / 400U);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mg_tls_parse_cert_der(void *buf, size_t dersz,
|
static int mg_tls_parse_cert_der(void *buf, size_t dersz,
|
||||||
@@ -1704,7 +1700,6 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint8_t r;
|
|
||||||
const uint8_t *n;
|
const uint8_t *n;
|
||||||
size_t nlen;
|
size_t nlen;
|
||||||
uint8_t sig2[512]; // 4096 bits
|
uint8_t sig2[512]; // 4096 bits
|
||||||
@@ -1724,15 +1719,7 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = 0;
|
return mg_rsa_pkcs_verify(sig2, nlen, cert->tbshash, cert->tbshashsz);
|
||||||
{
|
|
||||||
const uint8_t *p, *q;
|
|
||||||
size_t i;
|
|
||||||
p = sig2 + nlen - cert->tbshashsz;
|
|
||||||
q = cert->tbshash;
|
|
||||||
for (i = 0; i < cert->tbshashsz; i++) r |= (uint8_t)(*p++ ^ *q++);
|
|
||||||
}
|
|
||||||
return r == 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1903,7 +1890,11 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
|||||||
mg_error(c, r < 0 ? "failed to parse CA bundle"
|
mg_error(c, r < 0 ? "failed to parse CA bundle"
|
||||||
: "failed to find issuing CA in bundle");
|
: "failed to find issuing CA in bundle");
|
||||||
return -1;
|
return -1;
|
||||||
} // else r > 0 => issuer found
|
} else if (!ca.is_ca || // candidate issuer found
|
||||||
|
!mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) {
|
||||||
|
mg_error(c, "failed to verify CA");
|
||||||
|
return -1;
|
||||||
|
} // issuer verified. NOTE(): may fail if two diff CAs share a CN
|
||||||
MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf));
|
MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf));
|
||||||
}
|
}
|
||||||
if (!found_ca && tls->ca_der.len > 0) { // single cert or server
|
if (!found_ca && tls->ca_der.len > 0) { // single cert or server
|
||||||
@@ -2159,6 +2150,7 @@ void mg_tls_handshake(struct mg_connection *c) {
|
|||||||
long n;
|
long n;
|
||||||
bool res;
|
bool res;
|
||||||
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 (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);
|
||||||
@@ -2198,7 +2190,7 @@ static int mg_rsa_parse_der_int(const uint8_t **p, const uint8_t *end,
|
|||||||
// Long form length
|
// Long form length
|
||||||
uint8_t len_bytes = len & 0x7F;
|
uint8_t len_bytes = len & 0x7F;
|
||||||
MG_VERBOSE(("DER INT: long form, %d length bytes", len_bytes));
|
MG_VERBOSE(("DER INT: long form, %d length bytes", len_bytes));
|
||||||
if (end - *p < len_bytes) {
|
if (len_bytes == 0 || len_bytes > 4 || (size_t) (end - *p) < len_bytes) {
|
||||||
MG_VERBOSE(("DER INT: not enough bytes for length"));
|
MG_VERBOSE(("DER INT: not enough bytes for length"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2211,7 +2203,7 @@ static int mg_rsa_parse_der_int(const uint8_t **p, const uint8_t *end,
|
|||||||
|
|
||||||
MG_VERBOSE(("DER INT: length=%u, remaining=%d", len, (int) (end - *p)));
|
MG_VERBOSE(("DER INT: length=%u, remaining=%d", len, (int) (end - *p)));
|
||||||
|
|
||||||
if (end - *p < (long) len) {
|
if ((size_t) (end - *p) < len) {
|
||||||
MG_VERBOSE(("DER INT: length exceeds remaining bytes"));
|
MG_VERBOSE(("DER INT: length exceeds remaining bytes"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2253,12 +2245,12 @@ static int mg_parse_ec_private_key(const uint8_t *der, size_t dersz,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mg_der_next(&root, &version) < 0 || version.type != 0x02) {
|
if (mg_der_next(&root, &version) <= 0 || version.type != 0x02) {
|
||||||
MG_ERROR(("EC private key: invalid version"));
|
MG_ERROR(("EC private key: invalid version"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mg_der_next(&root, &private_key_octets) < 0 ||
|
if (mg_der_next(&root, &private_key_octets) <= 0 ||
|
||||||
private_key_octets.type != 0x04) {
|
private_key_octets.type != 0x04) {
|
||||||
MG_ERROR(("EC private key: invalid privateKey OCTET STRING"));
|
MG_ERROR(("EC private key: invalid privateKey OCTET STRING"));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2312,7 +2304,7 @@ static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
|
|||||||
// Long form length
|
// Long form length
|
||||||
uint8_t i, len_bytes = seq_len & 0x7F;
|
uint8_t i, len_bytes = seq_len & 0x7F;
|
||||||
MG_VERBOSE(("Long form length: %d bytes", len_bytes));
|
MG_VERBOSE(("Long form length: %d bytes", len_bytes));
|
||||||
if (end - p < len_bytes) {
|
if (len_bytes == 0 || len_bytes > 4 || (size_t) (end - p) < len_bytes) {
|
||||||
MG_ERROR(("Not enough bytes for long form length"));
|
MG_ERROR(("Not enough bytes for long form length"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2326,7 +2318,7 @@ static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
|
|||||||
MG_VERBOSE(
|
MG_VERBOSE(
|
||||||
("SEQUENCE length: %u, total DER size: %u", seq_len, (unsigned) dersz));
|
("SEQUENCE length: %u, total DER size: %u", seq_len, (unsigned) dersz));
|
||||||
|
|
||||||
if (end - p < (long) seq_len) {
|
if ((size_t) (end - p) < seq_len) {
|
||||||
MG_ERROR(("SEQUENCE length exceeds buffer"));
|
MG_ERROR(("SEQUENCE length exceeds buffer"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2334,7 +2326,9 @@ static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
|
|||||||
|
|
||||||
// Parse version (should be 0)
|
// Parse version (should be 0)
|
||||||
MG_VERBOSE(("Before version: offset=%d, bytes: %02x %02x %02x %02x",
|
MG_VERBOSE(("Before version: offset=%d, bytes: %02x %02x %02x %02x",
|
||||||
(int) (p - der), p[0], p[1], p[2], p[3]));
|
(int) (p - der), p < end ? p[0] : 0xFF,
|
||||||
|
p + 1 < end ? p[1] : 0xFF, p + 2 < end ? p[2] : 0xFF,
|
||||||
|
p + 3 < end ? p[3] : 0xFF));
|
||||||
if (mg_rsa_parse_der_int(&p, end, &version) < 0) {
|
if (mg_rsa_parse_der_int(&p, end, &version) < 0) {
|
||||||
MG_ERROR(("Failed to parse version"));
|
MG_ERROR(("Failed to parse version"));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2345,7 +2339,10 @@ static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
|
|||||||
|
|
||||||
// Parse the 8 components: n, e, d, p, q, dP, dQ, qInv
|
// Parse the 8 components: n, e, d, p, q, dP, dQ, qInv
|
||||||
MG_VERBOSE(("Before n: offset=%d, bytes: %02x %02x %02x %02x %02x %02x",
|
MG_VERBOSE(("Before n: offset=%d, bytes: %02x %02x %02x %02x %02x %02x",
|
||||||
(int) (p - der), p[0], p[1], p[2], p[3], p[4], p[5]));
|
(int) (p - der), p < end ? p[0] : 0xFF,
|
||||||
|
p + 1 < end ? p[1] : 0xFF, p + 2 < end ? p[2] : 0xFF,
|
||||||
|
p + 3 < end ? p[3] : 0xFF, p + 4 < end ? p[4] : 0xFF,
|
||||||
|
p + 5 < end ? p[5] : 0xFF));
|
||||||
if (mg_rsa_parse_der_int(&p, end, &key->n) < 0) {
|
if (mg_rsa_parse_der_int(&p, end, &key->n) < 0) {
|
||||||
MG_ERROR(("Failed to parse n (modulus)"));
|
MG_ERROR(("Failed to parse n (modulus)"));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2353,10 +2350,14 @@ static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
|
|||||||
MG_VERBOSE(("Parsed n: %d bytes, offset now=%d, consumed=%d bytes total",
|
MG_VERBOSE(("Parsed n: %d bytes, offset now=%d, consumed=%d bytes total",
|
||||||
(int) key->n.len, (int) (p - der), (int) (p - der)));
|
(int) key->n.len, (int) (p - der), (int) (p - der)));
|
||||||
MG_VERBOSE((" First 8 bytes of n: %02x %02x %02x %02x %02x %02x %02x %02x",
|
MG_VERBOSE((" First 8 bytes of n: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||||
(unsigned char) key->n.buf[0], (unsigned char) key->n.buf[1],
|
key->n.len > 0 ? (unsigned char) key->n.buf[0] : 0xFF,
|
||||||
(unsigned char) key->n.buf[2], (unsigned char) key->n.buf[3],
|
key->n.len > 1 ? (unsigned char) key->n.buf[1] : 0xFF,
|
||||||
(unsigned char) key->n.buf[4], (unsigned char) key->n.buf[5],
|
key->n.len > 2 ? (unsigned char) key->n.buf[2] : 0xFF,
|
||||||
(unsigned char) key->n.buf[6], (unsigned char) key->n.buf[7]));
|
key->n.len > 3 ? (unsigned char) key->n.buf[3] : 0xFF,
|
||||||
|
key->n.len > 4 ? (unsigned char) key->n.buf[4] : 0xFF,
|
||||||
|
key->n.len > 5 ? (unsigned char) key->n.buf[5] : 0xFF,
|
||||||
|
key->n.len > 6 ? (unsigned char) key->n.buf[6] : 0xFF,
|
||||||
|
key->n.len > 7 ? (unsigned char) key->n.buf[7] : 0xFF));
|
||||||
MG_VERBOSE((" Next bytes after n: %02x %02x %02x %02x %02x %02x",
|
MG_VERBOSE((" Next bytes after n: %02x %02x %02x %02x %02x %02x",
|
||||||
p < end ? p[0] : 0xFF, p + 1 < end ? p[1] : 0xFF,
|
p < end ? p[0] : 0xFF, p + 1 < end ? p[1] : 0xFF,
|
||||||
p + 2 < end ? p[2] : 0xFF, p + 3 < end ? p[3] : 0xFF,
|
p + 2 < end ? p[2] : 0xFF, p + 3 < end ? p[3] : 0xFF,
|
||||||
@@ -2410,7 +2411,7 @@ static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
|
|||||||
// parameters ANY OPTIONAL
|
// parameters ANY OPTIONAL
|
||||||
// }
|
// }
|
||||||
static int mg_parse_pkcs8_key(const uint8_t *der, size_t dersz,
|
static int mg_parse_pkcs8_key(const uint8_t *der, size_t dersz,
|
||||||
struct tls_data *tls) {
|
struct mg_rsa_key *rsa, uint8_t *ec_key) {
|
||||||
struct mg_der_tlv root, version, alg_id, private_key_octets;
|
struct mg_der_tlv root, version, alg_id, private_key_octets;
|
||||||
struct mg_der_tlv alg_oid, alg_params;
|
struct mg_der_tlv alg_oid, alg_params;
|
||||||
|
|
||||||
@@ -2419,22 +2420,22 @@ static int mg_parse_pkcs8_key(const uint8_t *der, size_t dersz,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mg_der_next(&root, &version) < 0 || version.type != 0x02) {
|
if (mg_der_next(&root, &version) <= 0 || version.type != 0x02) {
|
||||||
MG_ERROR(("PKCS#8: invalid version"));
|
MG_ERROR(("PKCS#8: invalid version"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mg_der_next(&root, &alg_id) < 0 || alg_id.type != 0x30) {
|
if (mg_der_next(&root, &alg_id) <= 0 || alg_id.type != 0x30) {
|
||||||
MG_ERROR(("PKCS#8: invalid AlgorithmIdentifier SEQUENCE"));
|
MG_ERROR(("PKCS#8: invalid AlgorithmIdentifier SEQUENCE"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mg_der_next(&alg_id, &alg_oid) < 0 || alg_oid.type != 0x06) {
|
if (mg_der_next(&alg_id, &alg_oid) <= 0 || alg_oid.type != 0x06) {
|
||||||
MG_ERROR(("PKCS#8: invalid algorithm OID"));
|
MG_ERROR(("PKCS#8: invalid algorithm OID"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mg_der_next(&root, &private_key_octets) < 0 ||
|
if (mg_der_next(&root, &private_key_octets) <= 0 ||
|
||||||
private_key_octets.type != 0x04) {
|
private_key_octets.type != 0x04) {
|
||||||
MG_ERROR(("PKCS#8: invalid privateKey OCTET STRING"));
|
MG_ERROR(("PKCS#8: invalid privateKey OCTET STRING"));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2443,18 +2444,20 @@ static int mg_parse_pkcs8_key(const uint8_t *der, size_t dersz,
|
|||||||
if (alg_oid.len == sizeof(mg_rsa_oid) &&
|
if (alg_oid.len == sizeof(mg_rsa_oid) &&
|
||||||
memcmp(alg_oid.value, mg_rsa_oid, sizeof(mg_rsa_oid)) == 0) {
|
memcmp(alg_oid.value, mg_rsa_oid, sizeof(mg_rsa_oid)) == 0) {
|
||||||
struct mg_rsa_key rsa_key;
|
struct mg_rsa_key rsa_key;
|
||||||
|
if (rsa == NULL) return -1;
|
||||||
if (mg_rsa_parse_key(private_key_octets.value, private_key_octets.len,
|
if (mg_rsa_parse_key(private_key_octets.value, private_key_octets.len,
|
||||||
&rsa_key) < 0) {
|
&rsa_key) < 0) {
|
||||||
MG_ERROR(("PKCS#8: failed to parse inner RSA key"));
|
MG_ERROR(("PKCS#8: failed to parse inner RSA key"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
tls->rsa = rsa_key;
|
*rsa = rsa_key;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
} else if (alg_oid.len == sizeof(mg_ec_public_key_oid) &&
|
} else if (alg_oid.len == sizeof(mg_ec_public_key_oid) &&
|
||||||
memcmp(alg_oid.value, mg_ec_public_key_oid,
|
memcmp(alg_oid.value, mg_ec_public_key_oid,
|
||||||
sizeof(mg_ec_public_key_oid)) == 0) {
|
sizeof(mg_ec_public_key_oid)) == 0) {
|
||||||
if (mg_der_next(&alg_id, &alg_params) < 0 || alg_params.type != 0x06) {
|
if (ec_key == NULL) return -1;
|
||||||
|
if (mg_der_next(&alg_id, &alg_params) <= 0 || alg_params.type != 0x06) {
|
||||||
MG_ERROR(("PKCS#8: invalid EC parameters OID"));
|
MG_ERROR(("PKCS#8: invalid EC parameters OID"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2467,7 +2470,7 @@ static int mg_parse_pkcs8_key(const uint8_t *der, size_t dersz,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return mg_parse_ec_private_key(private_key_octets.value,
|
return mg_parse_ec_private_key(private_key_octets.value,
|
||||||
private_key_octets.len, tls->ec_key);
|
private_key_octets.len, ec_key);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
MG_ERROR(("PKCS#8: unsupported algorithm"));
|
MG_ERROR(("PKCS#8: unsupported algorithm"));
|
||||||
@@ -2572,6 +2575,38 @@ 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) {
|
||||||
|
struct mg_str der = mg_str_n(NULL, 0);
|
||||||
|
size_t n = 0;
|
||||||
|
if (buf == NULL || len < 32) return 0;
|
||||||
|
// current mg_parse_ec_private_key() only handles 32-byte keys
|
||||||
|
if (mg_parse_pem(key, mg_str_s("EC PRIVATE KEY"), &der) == 0) {
|
||||||
|
if (mg_parse_ec_private_key((uint8_t *) der.buf, der.len, buf) == 0) n = 32;
|
||||||
|
} else if (mg_parse_pem(key, mg_str_s("PRIVATE KEY"), &der) == 0) {
|
||||||
|
if (mg_parse_pkcs8_key((uint8_t *) der.buf, der.len, NULL, buf) == 0) n = 32;
|
||||||
|
}
|
||||||
|
mg_free((void *) der.buf);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sync_time_cancel(struct mg_connection *c) {
|
||||||
|
c->fn = NULL;
|
||||||
|
c->is_closing = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sync_time_cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
if (ev == MG_EV_SNTP_TIME || (ev == MG_EV_CLOSE)) {
|
||||||
|
struct mg_connection *tlsc = (struct mg_connection *) c->fn_data;
|
||||||
|
struct tls_data *tls = (struct tls_data *) tlsc->tls;
|
||||||
|
sync_time_cancel(c); // c = tls->timec
|
||||||
|
tls->timec = NULL;
|
||||||
|
tls->is_sntp_pending = false;
|
||||||
|
if (ev == MG_EV_SNTP_TIME) mg_tls_handshake(tlsc);
|
||||||
|
if (ev == MG_EV_CLOSE) mg_error(tlsc, "time sync failed");
|
||||||
|
}
|
||||||
|
(void) ev_data;
|
||||||
|
}
|
||||||
|
|
||||||
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
||||||
struct mg_str key;
|
struct mg_str key;
|
||||||
struct tls_data *tls =
|
struct tls_data *tls =
|
||||||
@@ -2617,7 +2652,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
} else { // parse again for a possible DER (or a truncated begin string)
|
} else { // parse again for a possible DER (or a truncated begin string)
|
||||||
if (mg_parse_pem(opts->ca, mg_str_s("CERTIFICATE"), &tls->ca_der) < 0) {
|
if (mg_parse_pem(opts->ca, mg_str_s("CERTIFICATE"), &tls->ca_der) < 0) {
|
||||||
MG_ERROR(("Failed to load CA certificate"));
|
MG_ERROR(("Failed to load CA certificate"));
|
||||||
return;
|
goto xit;
|
||||||
}
|
}
|
||||||
} // ca_bundle_len != 0 && ca_der.len = 0 => bundle
|
} // ca_bundle_len != 0 && ca_der.len = 0 => bundle
|
||||||
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
|
||||||
@@ -2625,7 +2660,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
|
|
||||||
if (opts->cert.buf == NULL) {
|
if (opts->cert.buf == NULL) {
|
||||||
MG_VERBOSE(("No certificate provided"));
|
MG_VERBOSE(("No certificate provided"));
|
||||||
return;
|
goto xit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse PEM or DER certificate
|
// parse PEM or DER certificate
|
||||||
@@ -2696,7 +2731,8 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
// Copy parsed RSA key components to tls->rsa for signing operations
|
// Copy parsed RSA key components to tls->rsa for signing operations
|
||||||
tls->rsa = rsa_key;
|
tls->rsa = rsa_key;
|
||||||
} else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) {
|
} else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) {
|
||||||
if (mg_parse_pkcs8_key((const uint8_t *) key.buf, key.len, tls) == 0) {
|
if (mg_parse_pkcs8_key((const uint8_t *) key.buf, key.len, &tls->rsa,
|
||||||
|
tls->ec_key) == 0) {
|
||||||
if (tls->rsa.n.len > 0) {
|
if (tls->rsa.n.len > 0) {
|
||||||
tls->rsa_key_der = key;
|
tls->rsa_key_der = key;
|
||||||
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));
|
||||||
@@ -2712,6 +2748,19 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
} else {
|
} else {
|
||||||
mg_error(
|
mg_error(
|
||||||
c, "Expected EC PRIVATE KEY, RSA PRIVATE KEY, or PRIVATE KEY (PKCS#8)");
|
c, "Expected EC PRIVATE KEY, RSA PRIVATE KEY, or PRIVATE KEY (PKCS#8)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xit:
|
||||||
|
if (mg_boot_timestamp_ms == 0 && !tls->skip_verification &&
|
||||||
|
(tls->ca_bundle_len > 0 || tls->ca_der.len > 0)) {
|
||||||
|
struct mg_connection *timec;
|
||||||
|
timec = mg_sntp_connect(c->mgr, NULL, sync_time_cb, c);
|
||||||
|
if (timec == NULL) {
|
||||||
|
mg_error(c, "time sync failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tls->is_sntp_pending = true;
|
||||||
|
tls->timec = timec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2719,6 +2768,10 @@ void mg_tls_free(struct mg_connection *c) {
|
|||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
size_t i;
|
size_t i;
|
||||||
if (tls != NULL) {
|
if (tls != NULL) {
|
||||||
|
if (tls->timec != NULL) {
|
||||||
|
sync_time_cancel(tls->timec);
|
||||||
|
tls->timec = NULL;
|
||||||
|
}
|
||||||
mg_iobuf_free(&tls->send);
|
mg_iobuf_free(&tls->send);
|
||||||
if (tls->chain_der != NULL) {
|
if (tls->chain_der != NULL) {
|
||||||
for (i = 0; i < tls->chain_len; i++) {
|
for (i = 0; i < tls->chain_len; i++) {
|
||||||
|
|||||||
+4
-7
@@ -6,8 +6,9 @@
|
|||||||
|
|
||||||
#include "tls_chacha20.h"
|
#include "tls_chacha20.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN && MG_ENABLE_CHACHA20
|
||||||
// ******* BEGIN: chacha-portable/chacha-portable.h ********
|
// ******* BEGIN: chacha-portable/chacha-portable.h ********
|
||||||
|
|
||||||
#if !defined(__cplusplus) && !defined(_MSC_VER) && \
|
#if !defined(__cplusplus) && !defined(_MSC_VER) && \
|
||||||
@@ -1336,8 +1337,6 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
|
|||||||
// first we calculate the mac and see if it lines up, only then do we decrypt
|
// first we calculate the mac and see if it lines up, only then do we decrypt
|
||||||
size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE;
|
size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE;
|
||||||
uint8_t computed_mac[RFC_8439_TAG_SIZE];
|
uint8_t computed_mac[RFC_8439_TAG_SIZE];
|
||||||
int diff = 0;
|
|
||||||
size_t i;
|
|
||||||
if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) {
|
if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) {
|
||||||
return (size_t) -1;
|
return (size_t) -1;
|
||||||
}
|
}
|
||||||
@@ -1345,10 +1344,8 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
|
|||||||
poly1305_calculate_mac(computed_mac, cipher_text, actual_size, key, nonce, ad,
|
poly1305_calculate_mac(computed_mac, cipher_text, actual_size, key, nonce, ad,
|
||||||
ad_size);
|
ad_size);
|
||||||
|
|
||||||
// compare tags
|
if (!mg_memeq(computed_mac, cipher_text + actual_size, RFC_8439_TAG_SIZE))
|
||||||
for (i = 0; i < RFC_8439_TAG_SIZE; i++)
|
return (size_t) -1;
|
||||||
diff |= computed_mac[i] ^ cipher_text[actual_size + i];
|
|
||||||
if (diff != 0) return (size_t) -1;
|
|
||||||
|
|
||||||
chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1);
|
chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1);
|
||||||
return actual_size;
|
return actual_size;
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
|
||||||
#if MG_TLS == MG_TLS_BUILTIN
|
#if MG_TLS == MG_TLS_BUILTIN && MG_ENABLE_CHACHA20
|
||||||
#ifndef __PORTABLE_8439_H
|
#ifndef __PORTABLE_8439_H
|
||||||
#define __PORTABLE_8439_H
|
#define __PORTABLE_8439_H
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
|
|||||||
+26
-14
@@ -92,6 +92,11 @@ void mg_tls_handshake(struct mg_connection *c) {
|
|||||||
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
||||||
int rc = mbedtls_ssl_handshake(&tls->ssl);
|
int rc = mbedtls_ssl_handshake(&tls->ssl);
|
||||||
if (rc == 0) { // Success
|
if (rc == 0) { // Success
|
||||||
|
if (tls->check_name && (mbedtls_ssl_get_verify_result(&tls->ssl) &
|
||||||
|
MBEDTLS_X509_BADCERT_CN_MISMATCH)) {
|
||||||
|
mg_error(c, "failed to verify hostname");
|
||||||
|
return;
|
||||||
|
} // ignore MBEDTLS_X509_BADCERT_NOT_TRUSTED, no CA cert given
|
||||||
MG_DEBUG(("%lu success", c->id));
|
MG_DEBUG(("%lu success", c->id));
|
||||||
c->is_tls_hs = 0;
|
c->is_tls_hs = 0;
|
||||||
mg_call(c, MG_EV_TLS_HS, NULL);
|
mg_call(c, MG_EV_TLS_HS, NULL);
|
||||||
@@ -113,6 +118,7 @@ static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) {
|
|||||||
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
||||||
struct mg_tls *tls = (struct mg_tls *) mg_calloc(1, sizeof(*tls));
|
struct mg_tls *tls = (struct mg_tls *) mg_calloc(1, sizeof(*tls));
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
bool check_name = false;
|
||||||
c->tls = tls;
|
c->tls = tls;
|
||||||
if (c->tls == NULL) {
|
if (c->tls == NULL) {
|
||||||
mg_error(c, "TLS OOM");
|
mg_error(c, "TLS OOM");
|
||||||
@@ -143,24 +149,30 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c);
|
mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (c->is_client && opts->name.buf != NULL && opts->name.len > 0 &&
|
||||||
|
opts->name.buf[0] != '\0') {
|
||||||
|
char *host = mg_mprintf("%.*s", opts->name.len, opts->name.buf);
|
||||||
|
mbedtls_ssl_set_hostname(&tls->ssl, host);
|
||||||
|
#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < 0x03030000 || \
|
||||||
|
MBEDTLS_VERSION_NUMBER >= 0x04000000
|
||||||
|
// NOTE: MBEDTLS_SSL_VERIFY_OPTIONAL is not supported for TLS1.3 on client
|
||||||
|
// side See https://github.com/Mbed-TLS/mbedtls/issues/7075
|
||||||
|
check_name = true;
|
||||||
|
#endif
|
||||||
|
MG_DEBUG(("%lu hostname verification: %s", c->id, host));
|
||||||
|
mg_free(host);
|
||||||
|
} else {
|
||||||
|
MG_DEBUG(("%lu skipping hostname verification", c->id));
|
||||||
|
mbedtls_ssl_set_hostname(&tls->ssl, NULL);
|
||||||
|
}
|
||||||
if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
|
if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
|
||||||
// NOTE: MBEDTLS_SSL_VERIFY_NONE is not supported for TLS1.3 on client side
|
mbedtls_ssl_conf_authmode(&tls->conf, check_name // see set comment above
|
||||||
// See https://github.com/Mbed-TLS/mbedtls/issues/7075
|
? MBEDTLS_SSL_VERIFY_OPTIONAL
|
||||||
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
|
: MBEDTLS_SSL_VERIFY_NONE);
|
||||||
|
tls->check_name = check_name; // host name set but no CA cert given
|
||||||
} else {
|
} else {
|
||||||
if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail;
|
if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail;
|
||||||
mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
|
mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
|
||||||
if (c->is_client) {
|
|
||||||
if (opts->name.buf != NULL && opts->name.buf[0] != '\0') {
|
|
||||||
char *host = mg_mprintf("%.*s", opts->name.len, opts->name.buf);
|
|
||||||
mbedtls_ssl_set_hostname(&tls->ssl, host);
|
|
||||||
MG_DEBUG(("%lu hostname verification: %s", c->id, host));
|
|
||||||
mg_free(host);
|
|
||||||
} else {
|
|
||||||
MG_DEBUG(("%lu skipping hostname verification", c->id));
|
|
||||||
mbedtls_ssl_set_hostname(&tls->ssl, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||||
}
|
}
|
||||||
if (!mg_load_cert(opts->cert, &tls->cert)) goto fail;
|
if (!mg_load_cert(opts->cert, &tls->cert)) goto fail;
|
||||||
|
|||||||
+2
-1
@@ -24,12 +24,13 @@ struct mg_tls {
|
|||||||
mbedtls_x509_crt cert; // Parsed certificate
|
mbedtls_x509_crt cert; // Parsed certificate
|
||||||
mbedtls_pk_context pk; // Private key context
|
mbedtls_pk_context pk; // Private key context
|
||||||
mbedtls_ssl_context ssl; // SSL/TLS context
|
mbedtls_ssl_context ssl; // SSL/TLS context
|
||||||
mbedtls_ssl_config conf; // SSL-TLS config
|
mbedtls_ssl_config conf; // SSL/TLS config
|
||||||
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
||||||
mbedtls_ssl_ticket_context ticket; // Session tickets context
|
mbedtls_ssl_ticket_context ticket; // Session tickets context
|
||||||
#endif
|
#endif
|
||||||
// https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29
|
// https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29
|
||||||
unsigned char *throttled_buf; // see #3074
|
unsigned char *throttled_buf; // see #3074
|
||||||
size_t throttled_len;
|
size_t throttled_len;
|
||||||
|
bool check_name; // set when hostname was set, but no CA certificate given
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
+33
-8
@@ -141,6 +141,7 @@ void mg_tls_free(struct mg_connection *c) {
|
|||||||
SSL_free(tls->ssl);
|
SSL_free(tls->ssl);
|
||||||
SSL_CTX_free(tls->ctx);
|
SSL_CTX_free(tls->ctx);
|
||||||
BIO_meth_free(tls->bm);
|
BIO_meth_free(tls->bm);
|
||||||
|
mg_free(tls->name);
|
||||||
mg_free(tls);
|
mg_free(tls);
|
||||||
c->tls = NULL;
|
c->tls = NULL;
|
||||||
}
|
}
|
||||||
@@ -148,6 +149,7 @@ void mg_tls_free(struct mg_connection *c) {
|
|||||||
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
||||||
struct mg_tls *tls = (struct mg_tls *) mg_calloc(1, sizeof(*tls));
|
struct mg_tls *tls = (struct mg_tls *) mg_calloc(1, sizeof(*tls));
|
||||||
const char *id = "mongoose";
|
const char *id = "mongoose";
|
||||||
|
bool check_name = false;
|
||||||
static unsigned char s_initialised = 0;
|
static unsigned char s_initialised = 0;
|
||||||
BIO *bio = NULL;
|
BIO *bio = NULL;
|
||||||
int rc;
|
int rc;
|
||||||
@@ -189,6 +191,14 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (c->is_client && opts->name.buf != NULL && opts->name.len > 0 && opts->name.buf[0] != '\0') {
|
||||||
|
tls->name = mg_mprintf("%.*s", (int) opts->name.len, opts->name.buf);
|
||||||
|
if (tls->name == NULL) {
|
||||||
|
mg_error(c, "TLS OOM");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
check_name = true;
|
||||||
|
}
|
||||||
#if MG_TLS == MG_TLS_WOLFSSL && !defined(OPENSSL_COMPATIBLE_DEFAULTS)
|
#if MG_TLS == MG_TLS_WOLFSSL && !defined(OPENSSL_COMPATIBLE_DEFAULTS)
|
||||||
if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
|
if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
|
||||||
// Older versions require that either the CA is loaded or SSL_VERIFY_NONE
|
// Older versions require that either the CA is loaded or SSL_VERIFY_NONE
|
||||||
@@ -197,7 +207,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (opts->ca.buf != NULL && opts->ca.buf[0] != '\0') {
|
if (opts->ca.buf != NULL && opts->ca.len > 0 && opts->ca.buf[0] != '\0') {
|
||||||
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||||
NULL);
|
NULL);
|
||||||
#if MG_TLS == MG_TLS_WOLFSSL
|
#if MG_TLS == MG_TLS_WOLFSSL
|
||||||
@@ -219,6 +229,8 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
tls->check_name = check_name; // host name set but no CA cert given
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->cert.buf != NULL && opts->cert.buf[0] != '\0') {
|
if (opts->cert.buf != NULL && opts->cert.buf[0] != '\0') {
|
||||||
@@ -243,15 +255,13 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|||||||
(void) SSL_set_ecdh_auto(tls->ssl, 1);
|
(void) SSL_set_ecdh_auto(tls->ssl, 1);
|
||||||
#endif
|
#endif
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||||
if (opts->name.len > 0) {
|
if (tls->name != NULL) {
|
||||||
char *s = mg_mprintf("%.*s", (int) opts->name.len, opts->name.buf);
|
|
||||||
#if MG_TLS != MG_TLS_WOLFSSL || LIBWOLFSSL_VERSION_HEX >= 0x05005002
|
#if MG_TLS != MG_TLS_WOLFSSL || LIBWOLFSSL_VERSION_HEX >= 0x05005002
|
||||||
SSL_set1_host(tls->ssl, s);
|
SSL_set1_host(tls->ssl, tls->name);
|
||||||
#else
|
#else
|
||||||
X509_VERIFY_PARAM_set1_host(SSL_get0_param(tls->ssl), s, 0);
|
X509_VERIFY_PARAM_set1_host(SSL_get0_param(tls->ssl), tls->name, 0);
|
||||||
#endif
|
#endif
|
||||||
SSL_set_tlsext_host_name(tls->ssl, s);
|
SSL_set_tlsext_host_name(tls->ssl, tls->name);
|
||||||
mg_free(s);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if MG_TLS == MG_TLS_WOLFSSL
|
#if MG_TLS == MG_TLS_WOLFSSL
|
||||||
@@ -279,6 +289,16 @@ void mg_tls_handshake(struct mg_connection *c) {
|
|||||||
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
||||||
int rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
|
int rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
|
||||||
if (rc == 1) {
|
if (rc == 1) {
|
||||||
|
if (tls->check_name) { // host name set but no CA cert given
|
||||||
|
X509 *cert = SSL_get_peer_certificate(tls->ssl);
|
||||||
|
bool ok =
|
||||||
|
cert != NULL && X509_check_host(cert, tls->name, 0, 0, NULL) == 1;
|
||||||
|
X509_free(cert);
|
||||||
|
if (!ok) {
|
||||||
|
mg_error(c, "failed to verify hostname");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
MG_DEBUG(("%lu success", c->id));
|
MG_DEBUG(("%lu success", c->id));
|
||||||
c->is_tls_hs = 0;
|
c->is_tls_hs = 0;
|
||||||
mg_call(c, MG_EV_TLS_HS, NULL);
|
mg_call(c, MG_EV_TLS_HS, NULL);
|
||||||
@@ -295,7 +315,12 @@ size_t mg_tls_pending(struct mg_connection *c) {
|
|||||||
|
|
||||||
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
|
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
|
||||||
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
||||||
int n = SSL_read(tls->ssl, buf, (int) len);
|
int n;
|
||||||
|
#if MG_TLS == MG_TLS_WOLFSSL
|
||||||
|
uint8_t dummy; // WolfSSL requires destination != NULL
|
||||||
|
if (buf == NULL && len == 0) buf = &dummy;
|
||||||
|
#endif
|
||||||
|
n = SSL_read(tls->ssl, buf, (int) len);
|
||||||
if (!c->is_tls_hs && (buf == NULL || len == 0) && n == 0) return 0; // MIP
|
if (!c->is_tls_hs && (buf == NULL || len == 0) && n == 0) return 0; // MIP
|
||||||
if (n < 0 && mg_tls_err(c, tls, n) == 0) return MG_IO_WAIT;
|
if (n < 0 && mg_tls_err(c, tls, n) == 0) return MG_IO_WAIT;
|
||||||
if (n <= 0) return MG_IO_ERR;
|
if (n <= 0) return MG_IO_ERR;
|
||||||
|
|||||||
@@ -4,10 +4,13 @@
|
|||||||
|
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/x509v3.h>
|
||||||
|
|
||||||
struct mg_tls {
|
struct mg_tls {
|
||||||
BIO_METHOD *bm;
|
BIO_METHOD *bm;
|
||||||
SSL_CTX *ctx;
|
SSL_CTX *ctx;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
char *name; // matching hostname
|
||||||
|
bool check_name; // set when hostname was set, but no CA certificate given
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@@ -950,4 +950,41 @@ bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash) {
|
|||||||
return pss_verify_sha256(em, nlen, mhash);
|
return pss_verify_sha256(em, nlen, mhash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Full RSASSA-PKCS1-v1_5-VERIFY for rsa_pkcs1_sha256/sha384 (RFC 8017 9.2)
|
||||||
|
// em - output of RSA public-key primitive (nlen bytes)
|
||||||
|
// nlen - modulus byte length
|
||||||
|
// hash - SHA-256 or SHA-384 of the certificate TBS data
|
||||||
|
// hashlen - hash length, 32 for SHA-256 or 48 for SHA-384
|
||||||
|
static bool pkcs1_v15_verify(const uint8_t *em, size_t nlen,
|
||||||
|
const uint8_t *hash, size_t hashlen) {
|
||||||
|
static const uint8_t sha256_di[] = {
|
||||||
|
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
||||||
|
0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
|
||||||
|
static const uint8_t sha384_di[] = {
|
||||||
|
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
||||||
|
0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
|
||||||
|
const uint8_t *di = hashlen == 32 ? sha256_di : sha384_di;
|
||||||
|
size_t disz = hashlen == 32 ? sizeof(sha256_di) : sizeof(sha384_di);
|
||||||
|
size_t pslen, i;
|
||||||
|
uint8_t bad = 0;
|
||||||
|
|
||||||
|
if (hashlen != 32 && hashlen != 48) return false;
|
||||||
|
if (nlen < 3 + 8 + disz + hashlen) return false;
|
||||||
|
pslen = nlen - 3 - disz - hashlen;
|
||||||
|
|
||||||
|
bad |= (uint8_t) (em[0] | (em[1] ^ 0x01));
|
||||||
|
for (i = 0; i < pslen; i++) bad |= (uint8_t) (em[2 + i] ^ 0xff);
|
||||||
|
bad |= em[2 + pslen];
|
||||||
|
for (i = 0; i < disz; i++) bad |= (uint8_t) (em[3 + pslen + i] ^ di[i]);
|
||||||
|
for (i = 0; i < hashlen; i++)
|
||||||
|
bad |= (uint8_t) (em[3 + pslen + disz + i] ^ hash[i]);
|
||||||
|
|
||||||
|
return bad == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash,
|
||||||
|
size_t hashlen) {
|
||||||
|
return pkcs1_v15_verify(em, nlen, hash, hashlen);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* MG_TLS == MG_TLS_BUILTIN */
|
#endif /* MG_TLS == MG_TLS_BUILTIN */
|
||||||
@@ -10,5 +10,7 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
|
|||||||
const uint8_t *q, size_t q_len,
|
const uint8_t *q, size_t q_len,
|
||||||
const uint8_t *qInv, size_t qInv_len,
|
const uint8_t *qInv, size_t qInv_len,
|
||||||
uint8_t *signature, size_t sig_len);
|
uint8_t *signature, size_t sig_len);
|
||||||
|
bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash,
|
||||||
|
size_t hashlen);
|
||||||
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
|
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
|
||||||
#endif // TLS_RSA_H
|
#endif // TLS_RSA_H
|
||||||
+4
-12
@@ -222,11 +222,7 @@ static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left,
|
|||||||
#define asm_mmod_fast_secp256r1 0
|
#define asm_mmod_fast_secp256r1 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(default_RNG_defined) && default_RNG_defined
|
static MG_UECC_RNG_Function g_rng_function = NULL;
|
||||||
static MG_UECC_RNG_Function g_rng_function = &default_RNG;
|
|
||||||
#else
|
|
||||||
static MG_UECC_RNG_Function g_rng_function = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function) {
|
void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function) {
|
||||||
g_rng_function = rng_function;
|
g_rng_function = rng_function;
|
||||||
@@ -2560,15 +2556,11 @@ MG_UECC_VLI_API int mg_uecc_generate_random_int(mg_uecc_word_t *random,
|
|||||||
mg_uecc_word_t tries;
|
mg_uecc_word_t tries;
|
||||||
bitcount_t num_bits = mg_uecc_vli_numBits(top, num_words);
|
bitcount_t num_bits = mg_uecc_vli_numBits(top, num_words);
|
||||||
|
|
||||||
if (!g_rng_function) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
|
for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
|
||||||
if (!g_rng_function((uint8_t *) random,
|
unsigned int len = (unsigned int) (num_words * MG_UECC_WORD_SIZE);
|
||||||
(unsigned int) (num_words * MG_UECC_WORD_SIZE))) {
|
if (!(g_rng_function == NULL ? mg_random((uint8_t *) random, len)
|
||||||
|
: g_rng_function((uint8_t *) random, len)))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
random[num_words - 1] &=
|
random[num_words - 1] &=
|
||||||
mask >> ((bitcount_t) (num_words * MG_UECC_WORD_SIZE * 8 - num_bits));
|
mask >> ((bitcount_t) (num_words * MG_UECC_WORD_SIZE * 8 - num_bits));
|
||||||
if (!mg_uecc_vli_isZero(random, num_words) &&
|
if (!mg_uecc_vli_isZero(random, num_words) &&
|
||||||
|
|||||||
+10
-10
@@ -113,28 +113,28 @@ 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
|
generated. The filled-in values should be either truly random, or from a
|
||||||
cryptographically-secure PRNG.
|
cryptographically-secure PRNG.
|
||||||
|
|
||||||
A correctly functioning RNG function must be set (using mg_uecc_set_rng())
|
A correctly functioning RNG is required before calling mg_uecc_make_key() or
|
||||||
before calling mg_uecc_make_key() or mg_uecc_sign().
|
mg_uecc_sign().
|
||||||
|
|
||||||
Setting a correctly functioning RNG function improves the resistance to
|
Setting a correctly functioning RNG function improves the resistance to
|
||||||
side-channel attacks for mg_uecc_shared_secret() and
|
side-channel attacks for mg_uecc_shared_secret() and
|
||||||
mg_uecc_sign_deterministic().
|
mg_uecc_sign_deterministic().
|
||||||
|
|
||||||
A correct RNG function is set by default when building for Windows, Linux, or OS
|
|
||||||
X. If you are building on another POSIX-compliant system that supports
|
|
||||||
/dev/random or /dev/urandom, you can define MG_UECC_POSIX to use the predefined
|
|
||||||
RNG. For embedded platforms there is no predefined RNG function; you must
|
|
||||||
provide your own.
|
|
||||||
*/
|
*/
|
||||||
typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size);
|
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.
|
/* mg_uecc_set_rng() function.
|
||||||
Set the function that will be used to generate random bytes. The 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
|
should return 1 if the random data was generated, or 0 if the random data could
|
||||||
not be generated.
|
not be generated.
|
||||||
|
|
||||||
On platforms where there is no predefined RNG function (eg embedded platforms),
|
Mongoose: this overrides the default mg_random() fallback used by
|
||||||
this must be called before mg_uecc_make_key() or mg_uecc_sign() are used.
|
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:
|
Inputs:
|
||||||
rng_function - The function that will be used to generate random bytes.
|
rng_function - The function that will be used to generate random bytes.
|
||||||
|
|||||||
+38
@@ -9,6 +9,30 @@ void mg_bzero(volatile unsigned char *buf, size_t len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t mg_timegm(unsigned int year, unsigned int month, unsigned int day,
|
||||||
|
unsigned int hour, unsigned int min, unsigned int sec) {
|
||||||
|
static const uint16_t dm[12] = {0, 31, 59, 90, 120, 151,
|
||||||
|
181, 212, 243, 273, 304, 334};
|
||||||
|
const uint64_t day_secs = 86400;
|
||||||
|
const uint64_t year_secs = 31536000;
|
||||||
|
unsigned int y, ly;
|
||||||
|
if (year < 1970) return 0;
|
||||||
|
y = year - 1900;
|
||||||
|
ly = month > 2 ? y + 1 : y;
|
||||||
|
return (uint64_t) sec + 60 * min + 3600 * hour +
|
||||||
|
day_secs * (dm[month - 1] + day - 1) + year_secs * (y - 70) +
|
||||||
|
day_secs * ((ly - 69) / 4) - day_secs * ((ly - 1) / 100) +
|
||||||
|
day_secs * ((ly + 299) / 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_memeq(const void *a, const void *b, size_t n) {
|
||||||
|
const uint8_t *p = (const uint8_t *) a, *q = (const uint8_t *) b;
|
||||||
|
uint8_t r = 0;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < n; i++) r = (uint8_t) (r | (uint8_t) (p[i] ^ q[i]));
|
||||||
|
return r == 0;
|
||||||
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_CUSTOM_RANDOM
|
#if MG_ENABLE_CUSTOM_RANDOM
|
||||||
#else
|
#else
|
||||||
bool mg_random(void *buf, size_t len) {
|
bool mg_random(void *buf, size_t len) {
|
||||||
@@ -104,6 +128,20 @@ uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) {
|
|||||||
return ~crc;
|
return ~crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) {
|
||||||
|
static const uint16_t crclut[16] = {
|
||||||
|
// table for polynomial 0x8408 (reflected)
|
||||||
|
0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387,
|
||||||
|
0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F};
|
||||||
|
unsigned int c = (unsigned int) ~crc & 0xffff;
|
||||||
|
while (len--) {
|
||||||
|
uint8_t b = *(uint8_t *) buf++;
|
||||||
|
c = crclut[(c ^ b) & 0x0F] ^ (c >> 4);
|
||||||
|
c = crclut[(c ^ (b >> 4)) & 0x0F] ^ (c >> 4);
|
||||||
|
}
|
||||||
|
return (uint16_t) ~c;
|
||||||
|
}
|
||||||
|
|
||||||
static int isbyte(int n) {
|
static int isbyte(int n) {
|
||||||
return n >= 0 && n <= 255;
|
return n >= 0 && n <= 255;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -22,6 +22,9 @@ void mg_free(void *ptr);
|
|||||||
// sensitive data (keys, passwords).
|
// sensitive data (keys, passwords).
|
||||||
void mg_bzero(volatile unsigned char *buf, size_t len);
|
void mg_bzero(volatile unsigned char *buf, size_t len);
|
||||||
|
|
||||||
|
// Fixed-length constant-time byte equality. Use for MACs, tags, signatures.
|
||||||
|
bool mg_memeq(const void *a, const void *b, size_t n);
|
||||||
|
|
||||||
// Fills buf with len cryptographically random bytes. Uses the best available
|
// Fills buf with len cryptographically random bytes. Uses the best available
|
||||||
// hardware or OS source (hardware RNG, /dev/urandom, CryptGenRandom, etc.).
|
// hardware or OS source (hardware RNG, /dev/urandom, CryptGenRandom, etc.).
|
||||||
// Falls back to rand() with an error log if no strong source is available.
|
// Falls back to rand() with an error log if no strong source is available.
|
||||||
@@ -37,6 +40,10 @@ char *mg_random_str(char *buf, size_t len);
|
|||||||
// a new checksum; pass the result of a prior call to extend over more data.
|
// a new checksum; pass the result of a prior call to extend over more data.
|
||||||
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
|
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
|
||||||
|
|
||||||
|
// Computes CRC16 (HDLC, polynomial 0x8408) over buf/len. Pass crc=0 to start
|
||||||
|
// a new checksum; pass the result of a prior call to extend over more data.
|
||||||
|
uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len);
|
||||||
|
|
||||||
// Returns true if path is safe to serve from the filesystem. Rejects paths
|
// Returns true if path is safe to serve from the filesystem. Rejects paths
|
||||||
// that start with '~' or '..', or contain a '/../' component, to prevent
|
// that start with '~' or '..', or contain a '/../' component, to prevent
|
||||||
// directory traversal attacks.
|
// directory traversal attacks.
|
||||||
@@ -46,6 +53,9 @@ bool mg_path_is_sane(const struct mg_str path);
|
|||||||
// calling context; avoid in event handlers.
|
// calling context; avoid in event handlers.
|
||||||
void mg_delayms(unsigned int ms);
|
void mg_delayms(unsigned int ms);
|
||||||
|
|
||||||
|
uint64_t mg_timegm(unsigned int year, unsigned int month, unsigned int day,
|
||||||
|
unsigned int hour, unsigned int min, unsigned int sec);
|
||||||
|
|
||||||
// Packs four byte values into a uint32_t in big-endian order.
|
// Packs four byte values into a uint32_t in big-endian order.
|
||||||
// MG_U32(1, 2, 3, 4) == 0x01020304
|
// MG_U32(1, 2, 3, 4) == 0x01020304
|
||||||
#define MG_U32(a, b, c, d) \
|
#define MG_U32(a, b, c, d) \
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
#define MG_VERSION "7.22"
|
#define MG_VERSION "7.23"
|
||||||
+219
-65
@@ -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
|
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 -DMG_ENABLE_IPV6=$(IPV6)
|
||||||
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.
|
||||||
@@ -25,11 +25,11 @@ PREFIX ?= /usr/local
|
|||||||
LIBDIR ?= $(PREFIX)/lib
|
LIBDIR ?= $(PREFIX)/lib
|
||||||
INCLUDEDIR ?= $(PREFIX)/include
|
INCLUDEDIR ?= $(PREFIX)/include
|
||||||
VERSION ?= $(shell cut -d'"' -f2 ../src/version.h)
|
VERSION ?= $(shell cut -d'"' -f2 ../src/version.h)
|
||||||
COMMON_CFLAGS ?= $(C_WARN) $(WARN) $(INCS) $(DEFS) -DMG_ENABLE_IPV6=$(IPV6) $(TFLAGS) -pthread
|
COMMON_CFLAGS ?= $(C_WARN) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -pthread
|
||||||
CFLAGS ?= $(OPTS) $(ASAN) $(COMMON_CFLAGS)
|
CFLAGS ?= $(OPTS) $(ASAN) $(COMMON_CFLAGS)
|
||||||
VALGRIND_CFLAGS ?= $(OPTS) $(COMMON_CFLAGS)
|
VALGRIND_CFLAGS ?= $(OPTS) $(COMMON_CFLAGS)
|
||||||
VALGRIND_RUN ?= valgrind --tool=memcheck --gen-suppressions=all --leak-check=full --show-leak-kinds=all --leak-resolution=high --track-origins=yes --error-exitcode=1 --exit-on-first-error=yes --fair-sched=yes
|
VALGRIND_RUN ?= valgrind --tool=memcheck --gen-suppressions=all --leak-check=full --show-leak-kinds=all --leak-resolution=high --track-origins=yes --error-exitcode=1 --exit-on-first-error=yes --fair-sched=yes
|
||||||
.PHONY: clean_tutorials tutorials mip_test test valgrind
|
.PHONY: tutorials mip_test eth_test ppp_test atcmd_test eth_vc98 ppp_vc98 atcmd_vc98 eth_s390 ppp_s390 atcmd_s390 test valgrind
|
||||||
|
|
||||||
ifeq "$(findstring ++,$(CC))" ""
|
ifeq "$(findstring ++,$(CC))" ""
|
||||||
# $(CC) does not end with ++, i.e. we're using C. Apply C flags
|
# $(CC) does not end with ++, i.e. we're using C. Apply C flags
|
||||||
@@ -78,40 +78,44 @@ ifeq "$(SSL)" "BUILTIN"
|
|||||||
DEFS += -DMG_TLS=MG_TLS_BUILTIN
|
DEFS += -DMG_TLS=MG_TLS_BUILTIN
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
tls_multirec/server: FORCE
|
||||||
|
ifneq "$(SSL)" ""
|
||||||
|
ifeq "$(MULTIREC)" ""
|
||||||
|
$(MAKE) -C tls_multirec CC=gcc
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
true
|
||||||
|
|
||||||
|
FORCE:
|
||||||
|
true
|
||||||
|
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(MAKE) -C ../tutorials/http/http-server
|
$(MAKE) -C ../tutorials/http/http-server
|
||||||
|
|
||||||
tall: mg_prefix unamalgamated test valgrind fuzz fuzz_tls vc98 vc17 vc22 mingw mingw++ arm armhf riscv s390 tutorials tutorials_win tutorials_embedded mip_test mip_vc98
|
tall: mg_prefix unamalgamated test valgrind fuzz fuzz_tls vc98 vc17 vc22 mingw mingw++ arm armhf riscv \
|
||||||
|
diabish ppc_diabish s390 ppc mips tutorials tutorials_win tutorials_embedded
|
||||||
|
|
||||||
mip_test: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
|
tmip: mip_test eth_test ppp_test atcmd_test mip_tap_test pppoe_tap_test mip_port_test mip_diabish \
|
||||||
$(CC) mip_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
mip_vc98 eth_vc98 ppp_vc98 atcmd_vc98 mip_ppc_diabish mip_s390 eth_s390 ppp_s390 \
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
atcmd_s390 mip_port_s390 mip_ppc eth_ppc ppp_ppc atcmd_ppc mip_port_ppc mip_mips \
|
||||||
|
eth_mips ppp_mips atcmd_mips mip_port_mips
|
||||||
|
|
||||||
mip_tap_test: mip_x_test.c mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server
|
# Amalgamated source files
|
||||||
$(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
mongoose.c: Makefile $(wildcard ../src/*.c) $(wildcard ../src/drivers/*.c)
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
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))> $@
|
||||||
|
|
||||||
# bridge UDP port <--> TAP interface
|
mongoose.h: $(HDRS) Makefile
|
||||||
port_tap_bridge: port_tap_bridge.c
|
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/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')> $@
|
||||||
cc port_tap_bridge.c -o $@
|
|
||||||
|
|
||||||
# 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
|
|
||||||
$(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
|
||||||
|
|
||||||
mip_vc98: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
|
|
||||||
$(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
|
|
||||||
|
|
||||||
packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key
|
|
||||||
$(CC) $(CFLAGS) pack.c -o pack
|
|
||||||
$(RUN) ./pack Makefile data/ssi.h fuzz.c data/a.txt data/range.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key certs/client.key certs/client.crt > $@
|
|
||||||
|
|
||||||
# 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
|
||||||
|
$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
||||||
|
|
||||||
musl: test
|
musl: test
|
||||||
musl: ASAN =
|
musl: ASAN =
|
||||||
musl: WARN += -Wno-sign-conversion
|
musl: WARN += -Wno-sign-conversion
|
||||||
@@ -143,22 +147,6 @@ fuzz_tls2: mongoose.c mongoose.h Makefile fuzz_tls.c
|
|||||||
$(RUN) ./fuzzer_tls $(FUZZDATA)
|
$(RUN) ./fuzzer_tls $(FUZZDATA)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
test: Makefile mongoose.h $(SRCS) tls_multirec/server
|
|
||||||
$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test
|
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
|
||||||
|
|
||||||
tls_multirec/server: FORCE
|
|
||||||
ifneq "$(SSL)" ""
|
|
||||||
ifeq "$(MULTIREC)" ""
|
|
||||||
$(MAKE) -C tls_multirec CC=gcc
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
true
|
|
||||||
|
|
||||||
FORCE:
|
|
||||||
true
|
|
||||||
|
|
||||||
coverage: CFLAGS += -coverage
|
coverage: CFLAGS += -coverage
|
||||||
coverage: test
|
coverage: test
|
||||||
gcov -l -n *.gcno | sed '/^$$/d' | sed 'N;s/\n/ /'
|
gcov -l -n *.gcno | sed '/^$$/d' | sed 'N;s/\n/ /'
|
||||||
@@ -178,6 +166,16 @@ misra:
|
|||||||
cppcheck --addon=misra --clang -DMG_ENABLE_LINES=1 -DMG_ENABLE_SSI=1 mongoose.c >>/tmp/x 2>&1
|
cppcheck --addon=misra --clang -DMG_ENABLE_LINES=1 -DMG_ENABLE_SSI=1 mongoose.c >>/tmp/x 2>&1
|
||||||
less /tmp/x
|
less /tmp/x
|
||||||
|
|
||||||
|
arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT
|
||||||
|
arm: mongoose.h $(SRCS) mip_test.c
|
||||||
|
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0
|
||||||
|
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb mip_test.c packed_fs.c $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o mip_test -nostartfiles --specs nosys.specs -e 0
|
||||||
|
|
||||||
|
riscv: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT
|
||||||
|
riscv: mongoose.h $(SRCS) mip_test.c
|
||||||
|
$(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test
|
||||||
|
$(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 mip_test.c packed_fs.c $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o mip_test
|
||||||
|
|
||||||
# https://ddanilov.me/how-signals-are-handled-in-a-docker-container
|
# https://ddanilov.me/how-signals-are-handled-in-a-docker-container
|
||||||
armhf: ASAN=
|
armhf: ASAN=
|
||||||
armhf: IPV6=0
|
armhf: IPV6=0
|
||||||
@@ -185,6 +183,21 @@ armhf: CC = $(DOCKER) mdashnet/armhf cc
|
|||||||
armhf: RUN = $(DOCKER) --init mdashnet/armhf
|
armhf: RUN = $(DOCKER) --init mdashnet/armhf
|
||||||
armhf: test
|
armhf: test
|
||||||
|
|
||||||
|
# C89 FOLKS
|
||||||
|
diabish: CC = gcc
|
||||||
|
diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic
|
||||||
|
diabish: test
|
||||||
|
# end-of
|
||||||
|
|
||||||
|
# C89 + BIG-ENDIAN FOLKS
|
||||||
|
ppc_diabish: ASAN=
|
||||||
|
ppc_diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic
|
||||||
|
ppc_diabish: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static
|
||||||
|
ppc_diabish: RUN = qemu-ppc-static
|
||||||
|
ppc_diabish: test
|
||||||
|
# end-of
|
||||||
|
|
||||||
|
# BIG-ENDIAN FOLKS
|
||||||
s390: ASAN=
|
s390: ASAN=
|
||||||
s390: IPV6=0
|
s390: IPV6=0
|
||||||
s390: CFLAGS += -Wno-stringop-overflow
|
s390: CFLAGS += -Wno-stringop-overflow
|
||||||
@@ -192,27 +205,19 @@ s390: CC = $(DOCKER) mdashnet/s390 cc
|
|||||||
s390: RUN = $(DOCKER) --init mdashnet/s390
|
s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
s390: test
|
s390: test
|
||||||
|
|
||||||
mip_s390: ASAN=
|
ppc: ASAN=
|
||||||
mip_s390: CC = $(DOCKER) mdashnet/s390 cc
|
ppc: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static
|
||||||
mip_s390: RUN = $(DOCKER) --init mdashnet/s390
|
ppc: RUN = $(DOCKER) --init vicamo/debian:stable-powerpc # doesn't have curl
|
||||||
mip_s390: mip_test
|
ppc: test
|
||||||
|
|
||||||
mip_port_s390: ASAN=
|
mips: ASAN=
|
||||||
mip_port_s390: IPV6=0
|
mips: CC = $(DOCKER) dockcross/linux-mips mips-unknown-linux-gnu-gcc -static
|
||||||
# This test requires curl, not present in this image
|
mips: RUN = $(DOCKER) --init vicamo/debian:stable-mips # not working in GH (?)
|
||||||
mip_port_s390: CFLAGS += -DNO_HTTPSERVER_TEST
|
mips: test
|
||||||
mip_port_s390: CC = $(DOCKER) mdashnet/s390 cc
|
# end-of
|
||||||
mip_port_s390: RUN = $(DOCKER) --init mdashnet/s390
|
|
||||||
mip_port_s390: mip_port_test
|
|
||||||
|
|
||||||
arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT
|
|
||||||
arm: mongoose.h $(SRCS)
|
|
||||||
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0
|
|
||||||
|
|
||||||
riscv: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT
|
|
||||||
riscv: mongoose.h $(SRCS)
|
|
||||||
$(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test
|
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
vc98: IPV6=0 # VC98 does NOT support IPV6 sockets
|
||||||
vc98: Makefile mongoose.h $(SRCS)
|
vc98: Makefile mongoose.h $(SRCS)
|
||||||
$(DOCKER) mdashnet/vc98 wine cl $(SRCS) $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
|
$(DOCKER) mdashnet/vc98 wine cl $(SRCS) $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
|
||||||
$(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
$(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
||||||
@@ -232,7 +237,9 @@ mingw: Makefile mongoose.h $(SRCS)
|
|||||||
mingw++: Makefile mongoose.h $(SRCS)
|
mingw++: Makefile mongoose.h $(SRCS)
|
||||||
$(DOCKER) mdashnet/mingw x86_64-w64-mingw32-g++ $(SRCS) -W -Wall -Werror -I. $(DEFS) $(TFLAGS) -lws2_32 -lbcrypt -o $@.exe
|
$(DOCKER) mdashnet/mingw x86_64-w64-mingw32-g++ $(SRCS) -W -Wall -Werror -I. $(DEFS) $(TFLAGS) -lws2_32 -lbcrypt -o $@.exe
|
||||||
@echo "NOT RUNNING"
|
@echo "NOT RUNNING"
|
||||||
|
# end-of
|
||||||
|
|
||||||
|
# Linux easter eggs
|
||||||
linux-libs: CFLAGS += -fPIC
|
linux-libs: CFLAGS += -fPIC
|
||||||
linux-libs: LDFLAGS += -Wl,-soname,libmongoose.so.$(VERSION)
|
linux-libs: LDFLAGS += -Wl,-soname,libmongoose.so.$(VERSION)
|
||||||
linux-libs: mongoose.o
|
linux-libs: mongoose.o
|
||||||
@@ -247,11 +254,10 @@ install: linux-libs
|
|||||||
uninstall:
|
uninstall:
|
||||||
rm -rf $(DESTDIR)$(LIBDIR)/libmongoose.a $(DESTDIR)$(LIBDIR)/libmongoose.so.$(VERSION) $(DESTDIR)$(INCLUDEDIR)/mongoose.h $(DESTDIR)$(LIBDIR)/libmongoose.so
|
rm -rf $(DESTDIR)$(LIBDIR)/libmongoose.a $(DESTDIR)$(LIBDIR)/libmongoose.so.$(VERSION) $(DESTDIR)$(INCLUDEDIR)/mongoose.h $(DESTDIR)$(LIBDIR)/libmongoose.so
|
||||||
|
|
||||||
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))> $@
|
|
||||||
|
|
||||||
mongoose.h: $(HDRS) Makefile
|
packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/range.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key certs/client.key certs/client.crt
|
||||||
cd .. && (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/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/rpc.h src/dash.h src/ota.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')> $@
|
$(CC) $(CFLAGS) pack.c -o pack
|
||||||
|
$(RUN) ./pack Makefile data/ssi.h fuzz.c data/a.txt data/range.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key certs/client.key certs/client.crt > $@
|
||||||
|
|
||||||
|
|
||||||
clean: clean_tutorials clean_tutorials_embedded
|
clean: clean_tutorials clean_tutorials_embedded
|
||||||
@@ -261,6 +267,154 @@ clean: clean_tutorials clean_tutorials_embedded
|
|||||||
#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_test: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
|
||||||
|
$(CC) mip_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
|
eth_test: eth_test.c mongoose.c mongoose.h Makefile
|
||||||
|
$(CC) eth_test.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
|
ppp_test: ppp_test.c mongoose.c mongoose.h Makefile
|
||||||
|
$(CC) ppp_test.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
|
atcmd_test: atcmd_test.c mongoose.c mongoose.h Makefile
|
||||||
|
$(CC) atcmd_test.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
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
|
||||||
|
$(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
|
pppoe_tap_test: pppoe_tap_test.c mongoose.c mongoose.h Makefile
|
||||||
|
$(CC) pppoe_tap_test.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
|
# bridge UDP port <--> TAP interface
|
||||||
|
port_tap_bridge: port_tap_bridge.c
|
||||||
|
cc port_tap_bridge.c -o $@
|
||||||
|
|
||||||
|
# requires port_tap_bridge to be running
|
||||||
|
mip_port_test: mip_x_test.c mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server
|
||||||
|
$(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
|
||||||
|
|
||||||
|
# C89 FOLKS
|
||||||
|
mip_diabish: CC = gcc
|
||||||
|
mip_diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic
|
||||||
|
mip_diabish: mip_test
|
||||||
|
|
||||||
|
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) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
||||||
|
|
||||||
|
eth_vc98: eth_test.c mongoose.c mongoose.h Makefile
|
||||||
|
$(DOCKER) mdashnet/vc98 wine cl eth_test.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
|
||||||
|
$(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
||||||
|
|
||||||
|
ppp_vc98: ppp_test.c driver_mock.c mongoose.c mongoose.h Makefile
|
||||||
|
$(DOCKER) mdashnet/vc98 wine cl ppp_test.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
|
||||||
|
$(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
||||||
|
|
||||||
|
atcmd_vc98: atcmd_test.c mongoose.c mongoose.h Makefile
|
||||||
|
$(DOCKER) mdashnet/vc98 wine cl atcmd_test.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
|
||||||
|
$(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
|
||||||
|
# end-of
|
||||||
|
|
||||||
|
# C89 + BIG-ENDIAN FOLKS
|
||||||
|
mip_ppc_diabish: ASAN=
|
||||||
|
mip_ppc_diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic
|
||||||
|
mip_ppc_diabish: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static
|
||||||
|
mip_ppc_diabish: RUN = qemu-ppc-static
|
||||||
|
mip_ppc_diabish: mip_test
|
||||||
|
# end-of
|
||||||
|
|
||||||
|
# BIG-ENDIAN FOLKS
|
||||||
|
mip_s390: ASAN=
|
||||||
|
mip_s390: CFLAGS += -Wno-stringop-overflow
|
||||||
|
mip_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
|
mip_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
|
mip_s390: mip_test
|
||||||
|
|
||||||
|
eth_s390: ASAN=
|
||||||
|
eth_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
|
eth_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
|
eth_s390: eth_test
|
||||||
|
|
||||||
|
ppp_s390: ASAN=
|
||||||
|
ppp_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
|
ppp_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
|
ppp_s390: ppp_test
|
||||||
|
|
||||||
|
atcmd_s390: ASAN=
|
||||||
|
atcmd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
|
atcmd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
|
atcmd_s390: atcmd_test
|
||||||
|
|
||||||
|
mip_port_s390: ASAN=
|
||||||
|
mip_port_s390: CFLAGS += -Wno-stringop-overflow -DNO_HTTPSERVER_TEST # This test requires curl, not present in this image
|
||||||
|
mip_port_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
|
mip_port_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
|
mip_port_s390: mip_port_test
|
||||||
|
|
||||||
|
mip_ppc: ASAN=
|
||||||
|
mip_ppc: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static -Wno-maybe-uninitialized
|
||||||
|
mip_ppc: RUN = qemu-ppc-static
|
||||||
|
mip_ppc: mip_test
|
||||||
|
|
||||||
|
eth_ppc: ASAN=
|
||||||
|
eth_ppc: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static -Wno-maybe-uninitialized
|
||||||
|
eth_ppc: RUN = qemu-ppc-static
|
||||||
|
eth_ppc: eth_test
|
||||||
|
|
||||||
|
ppp_ppc: ASAN=
|
||||||
|
ppp_ppc: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static -Wno-maybe-uninitialized
|
||||||
|
ppp_ppc: RUN = qemu-ppc-static
|
||||||
|
ppp_ppc: ppp_test
|
||||||
|
|
||||||
|
atcmd_ppc: ASAN=
|
||||||
|
atcmd_ppc: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static -Wno-maybe-uninitialized
|
||||||
|
atcmd_ppc: RUN = qemu-ppc-static
|
||||||
|
atcmd_ppc: atcmd_test
|
||||||
|
|
||||||
|
mip_port_ppc: ASAN=
|
||||||
|
mip_port_ppc: CFLAGS += -DNO_HTTPSERVER_TEST # *** avoid system() calls, no OS image !
|
||||||
|
mip_port_ppc: CC = $(DOCKER) dockcross/linux-ppc powerpc-unknown-linux-gnu-gcc -static -Wno-maybe-uninitialized
|
||||||
|
mip_port_ppc: RUN = qemu-ppc-static
|
||||||
|
mip_port_ppc: mip_port_test
|
||||||
|
|
||||||
|
mip_mips: ASAN=
|
||||||
|
mip_mips: CC = $(DOCKER) dockcross/linux-mips mips-unknown-linux-gnu-gcc -static
|
||||||
|
mip_mips: RUN = qemu-mips-static
|
||||||
|
mip_mips: mip_test
|
||||||
|
|
||||||
|
eth_mips: ASAN=
|
||||||
|
eth_mips: CC = $(DOCKER) dockcross/linux-mips mips-unknown-linux-gnu-gcc -static
|
||||||
|
eth_mips: RUN = qemu-mips-static
|
||||||
|
eth_mips: eth_test
|
||||||
|
|
||||||
|
ppp_mips: ASAN=
|
||||||
|
ppp_mips: CC = $(DOCKER) dockcross/linux-mips mips-unknown-linux-gnu-gcc -static
|
||||||
|
ppp_mips: RUN = qemu-mips-static
|
||||||
|
ppp_mips: ppp_test
|
||||||
|
|
||||||
|
atcmd_mips: ASAN=
|
||||||
|
atcmd_mips: CC = $(DOCKER) dockcross/linux-mips mips-unknown-linux-gnu-gcc -static
|
||||||
|
atcmd_mips: RUN = qemu-mips-static
|
||||||
|
atcmd_mips: atcmd_test
|
||||||
|
|
||||||
|
mip_port_mips: ASAN=
|
||||||
|
mip_port_mips: CFLAGS += -DNO_HTTPSERVER_TEST # *** avoid system() calls, no OS image !
|
||||||
|
mip_port_mips: CC = $(DOCKER) dockcross/linux-mips mips-unknown-linux-gnu-gcc -static
|
||||||
|
mip_port_mips: RUN = qemu-mips-static
|
||||||
|
mip_port_mips: mip_port_test
|
||||||
|
# end-of
|
||||||
|
|
||||||
|
# TUTORIALs TESTS
|
||||||
|
|
||||||
tutorials:
|
tutorials:
|
||||||
@for X in $(TUTORIALS); do test -f $$X/Makefile || continue; $(MAKE) -C $$X example || exit 1; done
|
@for X in $(TUTORIALS); do test -f $$X/Makefile || continue; $(MAKE) -C $$X example || exit 1; done
|
||||||
clean_tutorials:
|
clean_tutorials:
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#define MG_ENABLE_TCPIP 1
|
||||||
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||||
|
#define MG_ENABLE_DRIVER_ATCMD 1
|
||||||
|
|
||||||
|
#include "mongoose.c"
|
||||||
|
|
||||||
|
static int s_num_tests = 0;
|
||||||
|
static bool s_error = false;
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
static int s_abort = 0;
|
||||||
|
#define ABORT() ++s_abort, s_error = true
|
||||||
|
#else
|
||||||
|
#ifdef NO_SLEEP_ABORT
|
||||||
|
#define ABORT() abort()
|
||||||
|
#else
|
||||||
|
#define ABORT() \
|
||||||
|
sleep(2); /* 2s, GH print reason */ \
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ASSERT(expr) \
|
||||||
|
do { \
|
||||||
|
s_num_tests++; \
|
||||||
|
if (!(expr)) { \
|
||||||
|
printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \
|
||||||
|
fflush(stdout); \
|
||||||
|
ABORT(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
struct uart_mock {
|
||||||
|
uint8_t rx[2048];
|
||||||
|
uint8_t tx[256];
|
||||||
|
size_t rx_len;
|
||||||
|
size_t rx_pos;
|
||||||
|
size_t tx_len;
|
||||||
|
int reset_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct test_ctx {
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_tcpip_driver_atcmd_data dd;
|
||||||
|
struct uart_mock mock;
|
||||||
|
char qbuf[1506];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void mock_reset(void *arg) {
|
||||||
|
struct uart_mock *mock = (struct uart_mock *) arg;
|
||||||
|
mock->reset_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mock_tx(void *arg, uint8_t b) {
|
||||||
|
struct uart_mock *mock = (struct uart_mock *) arg;
|
||||||
|
ASSERT(mock->tx_len < sizeof(mock->tx));
|
||||||
|
mock->tx[mock->tx_len++] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mock_rx(void *arg) {
|
||||||
|
struct uart_mock *mock = (struct uart_mock *) arg;
|
||||||
|
return mock->rx_pos < mock->rx_len ? mock->rx[mock->rx_pos++] : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mock_rx_add(struct uart_mock *mock, const void *buf, size_t len) {
|
||||||
|
ASSERT(mock->rx_len + len <= sizeof(mock->rx));
|
||||||
|
memcpy(mock->rx + mock->rx_len, buf, len);
|
||||||
|
mock->rx_len += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_if(struct test_ctx *ctx) {
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
ctx->ifp.driver_data = &ctx->dd;
|
||||||
|
ASSERT(mg_atcmd_init(&ctx->ifp) == true);
|
||||||
|
ASSERT(ctx->ifp.recv_queue.size <= sizeof(ctx->qbuf));
|
||||||
|
ctx->ifp.recv_queue.buf = ctx->qbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_if(struct test_ctx *ctx, const char **script,
|
||||||
|
bool no_byte_stuff) {
|
||||||
|
memset(&ctx->dd, 0, sizeof(ctx->dd));
|
||||||
|
memset(&ctx->mock, 0, sizeof(ctx->mock));
|
||||||
|
ctx->ifp.driver_data = &ctx->dd;
|
||||||
|
ctx->ifp.recv_queue.head = 0;
|
||||||
|
ctx->ifp.recv_queue.tail = 0;
|
||||||
|
ctx->dd.usart = &ctx->mock;
|
||||||
|
ctx->dd.reset = mock_reset;
|
||||||
|
ctx->dd.tx = mock_tx;
|
||||||
|
ctx->dd.rx = mock_rx;
|
||||||
|
ctx->dd.script = script;
|
||||||
|
ctx->dd.no_byte_stuff = no_byte_stuff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DASHBOARD(x) \
|
||||||
|
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct test_ctx ctx;
|
||||||
|
init_if(&ctx);
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
{ // Send AT command, receive OK
|
||||||
|
static const char *script[] = {"AT\r\n", "\r\nOK\r\n", NULL};
|
||||||
|
static const uint8_t at[] = {'A', 'T', '\r', '\n'};
|
||||||
|
static const uint8_t ok[] = {'\r', '\n', 'O', 'K', '\r', '\n'};
|
||||||
|
reset_if(&ctx, script, false);
|
||||||
|
|
||||||
|
ASSERT(mg_atcmd_poll(&ctx.ifp, true) == false);
|
||||||
|
ASSERT(ctx.mock.tx_len == sizeof(at));
|
||||||
|
ASSERT(memcmp(ctx.mock.tx, at, sizeof(at)) == 0);
|
||||||
|
ASSERT(ctx.dd.link == false);
|
||||||
|
mock_rx_add(&ctx.mock, ok, sizeof(ok));
|
||||||
|
ASSERT(mg_atcmd_poll(&ctx.ifp, true) == true);
|
||||||
|
ASSERT(ctx.dd.link == true);
|
||||||
|
ASSERT(ctx.dd.script_index == -1);
|
||||||
|
ASSERT(ctx.mock.reset_count == 0);
|
||||||
|
}
|
||||||
|
DASHBOARD("atcmd");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
{ // HDLC byte stuffing and unstuffing
|
||||||
|
static const uint8_t payload[] = {0x00, 0x01, 'A', 0x7d, 0x7e, 0x20};
|
||||||
|
static const uint8_t frame[] = {0x7e, 0x7d, 0x20, 0x7d, 0x21, 'A',
|
||||||
|
0x7d, 0x5d, 0x7d, 0x5e, 0x20, 0x7e};
|
||||||
|
uint8_t buf[sizeof(payload)];
|
||||||
|
reset_if(&ctx, NULL, false);
|
||||||
|
ASSERT(mg_atcmd_poll(&ctx.ifp, true) == true);
|
||||||
|
|
||||||
|
ASSERT(mg_atcmd_tx(payload, sizeof(payload), &ctx.ifp) == sizeof(payload));
|
||||||
|
ASSERT(ctx.mock.tx_len == sizeof(frame));
|
||||||
|
ASSERT(memcmp(ctx.mock.tx, frame, sizeof(frame)) == 0);
|
||||||
|
|
||||||
|
mock_rx_add(&ctx.mock, frame, sizeof(frame));
|
||||||
|
ASSERT(mg_atcmd_rx(buf, sizeof(buf), &ctx.ifp) == sizeof(payload));
|
||||||
|
ASSERT(memcmp(buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // HDLC framing with byte stuffing disabled
|
||||||
|
static const uint8_t payload[] = {0x00, 0x01, 'A', 0x7d, 0x20};
|
||||||
|
static const uint8_t frame[] = {0x7e, 0x00, 0x01, 'A', 0x7d, 0x20, 0x7e};
|
||||||
|
uint8_t buf[sizeof(payload)];
|
||||||
|
reset_if(&ctx, NULL, true);
|
||||||
|
ASSERT(mg_atcmd_poll(&ctx.ifp, true) == true);
|
||||||
|
|
||||||
|
ASSERT(mg_atcmd_tx(payload, sizeof(payload), &ctx.ifp) == sizeof(payload));
|
||||||
|
ASSERT(ctx.mock.tx_len == sizeof(frame));
|
||||||
|
ASSERT(memcmp(ctx.mock.tx, frame, sizeof(frame)) == 0);
|
||||||
|
|
||||||
|
mock_rx_add(&ctx.mock, frame, sizeof(frame));
|
||||||
|
ASSERT(mg_atcmd_rx(buf, sizeof(buf), &ctx.ifp) == sizeof(payload));
|
||||||
|
ASSERT(memcmp(buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // Oversized frame is rejected
|
||||||
|
uint8_t buf[8];
|
||||||
|
reset_if(&ctx, NULL, true);
|
||||||
|
ASSERT(mg_atcmd_poll(&ctx.ifp, true) == true);
|
||||||
|
|
||||||
|
mock_rx_add(&ctx.mock, "\x7e", 1);
|
||||||
|
memset(ctx.mock.rx + ctx.mock.rx_len, 'A', sizeof(buf) + 1);
|
||||||
|
ctx.mock.rx_len += sizeof(buf) + 1;
|
||||||
|
mock_rx_add(&ctx.mock, "\x7e", 1);
|
||||||
|
|
||||||
|
ASSERT(mg_atcmd_rx(buf, sizeof(buf), &ctx.ifp) == 0);
|
||||||
|
}
|
||||||
|
printf("HEALTH_DASHBOARD\t\"hdlc\": %s\n",
|
||||||
|
s_error ? "false" : "true");
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
if (s_abort != 0) return EXIT_FAILURE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("SUCCESS. Total tests: %d\n", s_num_tests);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
#define FREERTOS_CONFIG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define configUSE_PREEMPTION 1
|
||||||
|
#define configCPU_CLOCK_HZ SYS_FREQUENCY
|
||||||
|
#define configTICK_RATE_HZ 1000
|
||||||
|
#define configMAX_PRIORITIES 5
|
||||||
|
#define configUSE_16_BIT_TICKS 0
|
||||||
|
#define configUSE_TICK_HOOK 0
|
||||||
|
#define configUSE_IDLE_HOOK 0
|
||||||
|
#define configUSE_TIMERS 0
|
||||||
|
#define configUSE_CO_ROUTINES 0
|
||||||
|
#define configUSE_MALLOC_FAILED_HOOK 0
|
||||||
|
#define configUSE_COUNTING_SEMAPHORES 1
|
||||||
|
#define configMINIMAL_STACK_SIZE 128
|
||||||
|
#define configTOTAL_HEAP_SIZE (1024 * 128)
|
||||||
|
#define INCLUDE_vTaskDelay 1
|
||||||
|
#define INCLUDE_vTaskDelete 1
|
||||||
|
#define INCLUDE_xTaskGetSchedulerState 1
|
||||||
|
|
||||||
|
#define configPRIO_BITS 4
|
||||||
|
|
||||||
|
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
|
||||||
|
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||||
|
#define configKERNEL_INTERRUPT_PRIORITY \
|
||||||
|
(configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
|
||||||
|
|
||||||
|
#define configMAX_SYSCALL_INTERRUPT_PRIORITY \
|
||||||
|
(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
|
||||||
|
|
||||||
|
#define configASSERT(expr) \
|
||||||
|
if (!(expr)) printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr)
|
||||||
|
|
||||||
|
//#define vPortSVCHandler SVC_Handler
|
||||||
|
//#define xPortPendSVHandler PendSV_Handler
|
||||||
|
//xPortSysTickHandler
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef
|
||||||
|
OPTS ?= -O3 -g3
|
||||||
|
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
|
||||||
|
ASAN_OPTIONS ?= detect_leaks=1
|
||||||
|
COMMON_CFLAGS ?= $(C_WARN) $(WARN) -I. -I../.. $(TFLAGS)
|
||||||
|
CFLAGS ?= $(OPTS) $(ASAN) $(COMMON_CFLAGS)
|
||||||
|
|
||||||
|
ifeq "$(findstring ++,$(CC))" ""
|
||||||
|
# $(CC) does not end with ++, i.e. we're using C. Apply C flags
|
||||||
|
C_WARN ?= -Wmissing-prototypes -Wstrict-prototypes
|
||||||
|
else
|
||||||
|
# $(CC) ends with ++, i.e. we're using C++. Apply C++ flags
|
||||||
|
C_WARN ?= -Wno-deprecated
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all bsd_test bsd_client shm_tap_bridge shm_port_bridge bsd_s390 clean
|
||||||
|
|
||||||
|
TEST_SOURCES = bsd_shm_test.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.
|
||||||
|
C_SOURCES = ../../mongoose.c
|
||||||
|
C_SOURCES += FreeRTOS-Kernel/portable/MemMang/heap_4.c
|
||||||
|
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c
|
||||||
|
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
|
||||||
|
C_SOURCES += $(wildcard FreeRTOS-Kernel/*.c)
|
||||||
|
C_OBJECTS = $(notdir $(C_SOURCES:.c=.o))
|
||||||
|
C_INPUTS = $(foreach S,$(C_SOURCES),-x c $(S))
|
||||||
|
CFLAGS += -IFreeRTOS-Kernel/include
|
||||||
|
CFLAGS += -IFreeRTOS-Kernel/portable/ThirdParty/GCC/Posix -Wno-conversion -Wno-undef
|
||||||
|
|
||||||
|
ifeq "$(findstring ++,$(CC))" ""
|
||||||
|
TEST_LANGUAGE = c
|
||||||
|
else
|
||||||
|
TEST_LANGUAGE = c++
|
||||||
|
endif
|
||||||
|
|
||||||
|
DOCKER_BIN ?= docker
|
||||||
|
CWD ?= $(realpath $(CURDIR))
|
||||||
|
ROOT_DIR = $(realpath $(CWD)/../..)
|
||||||
|
ENV ?= -e Tmp=. -e WINEDEBUG=-all
|
||||||
|
DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(ROOT_DIR):$(ROOT_DIR) -w $(CWD)
|
||||||
|
|
||||||
|
# Mongoose options are defined in mongoose_config.h, this forces loading and skips OS detection
|
||||||
|
CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
|
||||||
|
|
||||||
|
|
||||||
|
all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
||||||
|
|
||||||
|
# requires shm_tap_bridge to be running
|
||||||
|
bsd_test: FreeRTOS-Kernel
|
||||||
|
$(CC) $(C_INPUTS) -c $(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 $@
|
||||||
|
|
||||||
|
bsd_client: bsd_client.c
|
||||||
|
$(CC) bsd_client.c -pthread -o $@
|
||||||
|
|
||||||
|
# bridge shm queue <--> TAP interface
|
||||||
|
shm_tap_bridge: shm_tap_bridge.c shm_queue.h
|
||||||
|
$(CC) shm_tap_bridge.c -pthread -o $@
|
||||||
|
|
||||||
|
# requires test/port_tap_bridge to be running
|
||||||
|
bsd_s390: ASAN=
|
||||||
|
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
|
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
|
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'
|
||||||
|
|
||||||
|
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
||||||
|
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
||||||
|
|
||||||
|
clean: # Cleanup. Delete built program and all build artifacts
|
||||||
|
rm -rf bsd_test bsd_client shm_port_bridge *.o *.obj FreeRTOS-Kernel
|
||||||
|
|
||||||
|
FreeRTOS-Kernel: # FreeRTOS sources
|
||||||
|
git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define PORT 1234
|
||||||
|
#define NUM_CONNECTIONS 12
|
||||||
|
#define DATA_SIZE (4 * 1024)
|
||||||
|
|
||||||
|
static int read_full(int fd, void *buf, size_t len) {
|
||||||
|
static const size_t sizes[] = {1, 7, 17, 255, 256, 257, 511, 512, 4096};
|
||||||
|
char *p = (char *) buf;
|
||||||
|
size_t i = 0;
|
||||||
|
while (len > 0) {
|
||||||
|
size_t nreq = sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))];
|
||||||
|
ssize_t n;
|
||||||
|
if (nreq > len) nreq = len;
|
||||||
|
n = recv(fd, p, nreq, 0);
|
||||||
|
if (n <= 0) return -1;
|
||||||
|
p += n;
|
||||||
|
len -= (size_t) n;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_full(int fd, const void *buf, size_t len) {
|
||||||
|
const char *p = (const char *) buf;
|
||||||
|
while (len > 0) {
|
||||||
|
ssize_t n = send(fd, p, len, 0);
|
||||||
|
if (n <= 0) return -1;
|
||||||
|
p += n;
|
||||||
|
len -= (size_t) n;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fill_text(char *buf, size_t len) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < len; i++) buf[i] = (char) (' ' + random() % 95);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *worker(void *arg) {
|
||||||
|
const char *host = (const char *) arg;
|
||||||
|
char tx[DATA_SIZE], rx[DATA_SIZE];
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
struct timeval timeout;
|
||||||
|
unsigned port = 0;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fill_text(tx, sizeof(tx));
|
||||||
|
memset(rx, 0, sizeof(rx));
|
||||||
|
|
||||||
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) goto fail;
|
||||||
|
timeout.tv_sec = 5;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0 ||
|
||||||
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0)
|
||||||
|
goto fail_close;
|
||||||
|
|
||||||
|
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_close;
|
||||||
|
printf("%d CONNECTING\n", fd);
|
||||||
|
if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0) goto fail_close;
|
||||||
|
{
|
||||||
|
socklen_t len = sizeof(sa);
|
||||||
|
if (getsockname(fd, (struct sockaddr *) &sa, &len) != 0) goto fail_close;
|
||||||
|
port = (unsigned) ntohs(sa.sin_port);
|
||||||
|
}
|
||||||
|
printf("%d CONNECTED port: %u\n", fd, port);
|
||||||
|
printf("%d WRITING\n", fd);
|
||||||
|
if (write_full(fd, tx, sizeof(tx)) != 0) goto fail_close;
|
||||||
|
printf("%d READING\n", fd);
|
||||||
|
if (read_full(fd, rx, sizeof(rx)) != 0) goto fail_close;
|
||||||
|
if (memcmp(tx, rx, sizeof(tx)) != 0) goto fail_close;
|
||||||
|
|
||||||
|
printf("%d OK-CLOSING\n", fd);
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fail_close:
|
||||||
|
printf("%d ERROR-CLOSING\n", fd);
|
||||||
|
close(fd);
|
||||||
|
fail:
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
pthread_t threads[NUM_CONNECTIONS];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "usage: %s IP\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srandom(1);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_CONNECTIONS; i++) {
|
||||||
|
if (pthread_create(&threads[i], NULL, worker, argv[1]) != 0) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_CONNECTIONS; i++) {
|
||||||
|
if (pthread_join(threads[i], NULL) != 0) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
#include "shm_queue.h"
|
||||||
|
|
||||||
|
#ifndef O_RDWR
|
||||||
|
#define O_RDWR 02
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct shm_driver_data {
|
||||||
|
const char *name;
|
||||||
|
FILE *fp;
|
||||||
|
struct shm_link *shm;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct shm_driver_data shm_data = {
|
||||||
|
SHM_NAME,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool shm_init(struct mg_tcpip_if *ifp) {
|
||||||
|
struct shm_driver_data *d = (struct shm_driver_data *) ifp->driver_data;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = shm_open(d->name, O_RDWR, 0600);
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("shm_open failed: %d\n", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
d->fp = fdopen(fd, "r+");
|
||||||
|
if (d->fp == NULL) {
|
||||||
|
printf("fdopen failed: %d\n", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->shm = (struct shm_link *) mmap(NULL, sizeof(*d->shm),
|
||||||
|
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||||
|
fileno(d->fp), 0);
|
||||||
|
if (d->shm == MAP_FAILED) {
|
||||||
|
printf("mmap failed: %d\n", errno);
|
||||||
|
fclose(d->fp);
|
||||||
|
d->fp = NULL;
|
||||||
|
d->shm = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t shm_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
struct shm_driver_data *d = (struct shm_driver_data *) ifp->driver_data;
|
||||||
|
|
||||||
|
if (!shm_queue_push(&d->shm->to_tap, buf, len)) {
|
||||||
|
printf("\nFRAME LOST\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t shm_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
struct shm_driver_data *d = (struct shm_driver_data *) ifp->driver_data;
|
||||||
|
return shm_queue_pop(&d->shm->from_tap, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool shm_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||||
|
return s1 && ifp->driver_data ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mg_tcpip_driver shm_driver = {
|
||||||
|
shm_init,
|
||||||
|
shm_tx,
|
||||||
|
shm_rx,
|
||||||
|
shm_poll,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool mg_random(void *buf, size_t len) {
|
||||||
|
bool success = false;
|
||||||
|
unsigned char *p = (unsigned char *) buf;
|
||||||
|
FILE *fp = fopen("/dev/urandom", "rb");
|
||||||
|
if (fp != NULL) {
|
||||||
|
if (fread(buf, 1, len, fp) == len) success = true;
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
// If everything above did not work, fallback to a pseudo random generator
|
||||||
|
if (success == false) {
|
||||||
|
printf("\nWeak RNG: using rand()\n");
|
||||||
|
while (len--) *p++ = (unsigned char) (rand() & 255);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include "bsd_x_test.c"
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
bool result;
|
||||||
|
const char *debug_level = getenv("V");
|
||||||
|
// Setup interface
|
||||||
|
const char *mac = "02:00:01:02:03:78"; // MAC address
|
||||||
|
|
||||||
|
printf("Opening SHM interface\n");
|
||||||
|
usleep(200000); // 200 ms
|
||||||
|
|
||||||
|
result = bsd_x_test(&shm_driver, &shm_data, mac, debug_level);
|
||||||
|
if (shm_data.shm != NULL) munmap(shm_data.shm, sizeof(*shm_data.shm));
|
||||||
|
if (shm_data.fp != NULL) fclose(shm_data.fp);
|
||||||
|
if (!result) return EXIT_FAILURE;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
#define BSDTEST_USING_DHCP 1
|
||||||
|
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
static int s_num_tests = 0;
|
||||||
|
static bool s_error = false;
|
||||||
|
static volatile bool s_done = false, s_wait;
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
static int s_abort = 0;
|
||||||
|
#define ABORT() ++s_abort, s_error = true
|
||||||
|
#else
|
||||||
|
#ifdef NO_SLEEP_ABORT
|
||||||
|
#define ABORT() abort()
|
||||||
|
#else
|
||||||
|
#define ABORT() \
|
||||||
|
sleep(2); /* 2s, GH print reason */ \
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ASSERT(expr) \
|
||||||
|
do { \
|
||||||
|
s_num_tests++; \
|
||||||
|
if (!(expr)) { \
|
||||||
|
printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \
|
||||||
|
fflush(stdout); \
|
||||||
|
ABORT(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
struct client_data {
|
||||||
|
char hostname[64];
|
||||||
|
uint32_t ip;
|
||||||
|
bool done;
|
||||||
|
};
|
||||||
|
|
||||||
|
// client_task: connects to hostname:80, fetches "/", logs response length.
|
||||||
|
static void client_task(void *args) {
|
||||||
|
int fd, c;
|
||||||
|
struct client_data *d = (struct client_data *) args;
|
||||||
|
char req[256];
|
||||||
|
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(80);
|
||||||
|
memcpy(&sa.sin_addr, &d->ip, 4);
|
||||||
|
c = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
|
||||||
|
ASSERT(c >= 0);
|
||||||
|
if (c < 0) {
|
||||||
|
MG_ERROR(("connect failed"));
|
||||||
|
close(fd);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snprintf(req, sizeof(req),"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", d->hostname);
|
||||||
|
send(fd, req, strlen(req), 0);
|
||||||
|
size_t total = 0;
|
||||||
|
char buf[512];
|
||||||
|
ssize_t n;
|
||||||
|
while ((n = recv(fd, buf, sizeof(buf), 0)) > 0) total += (size_t) n;
|
||||||
|
MG_INFO(("%s response: %lu bytes", d->hostname, (unsigned long) total));
|
||||||
|
ASSERT(total > 0);
|
||||||
|
close(fd);
|
||||||
|
d->done = true;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
(void) args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo_task: one per accepted connection. Echoes data back.
|
||||||
|
static void echo_task(void *args) {
|
||||||
|
int fd = (int) (uintptr_t) args;
|
||||||
|
static const size_t sizes[] = {1, 7, 17, 255, 256, 257, 511, 512};
|
||||||
|
char buf[512];
|
||||||
|
size_t i = 0;
|
||||||
|
ssize_t n;
|
||||||
|
MG_INFO(("Echo task started: %d", fd));
|
||||||
|
while ((n = recv(fd, buf, sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))], 0)) > 0)
|
||||||
|
send(fd, buf, (size_t) n, 0);
|
||||||
|
close(fd);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// accept loop. Waits for incoming connections on port 1234 and spawns
|
||||||
|
// an echo_task for each one, allowing concurrent clients.
|
||||||
|
static void atask(void *args) {
|
||||||
|
BaseType_t result;
|
||||||
|
mg_bsd_init();
|
||||||
|
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;
|
||||||
|
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) {
|
||||||
|
int fd = accept(lfd, NULL, NULL);
|
||||||
|
if (fd < 0) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }
|
||||||
|
// echo_task at higher priority than task1
|
||||||
|
result = xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd,
|
||||||
|
configMAX_PRIORITIES - 1, NULL);
|
||||||
|
MG_INFO(("Echo task %d: %s", fd, result == pdPASS ? "created" : "FAILED"));
|
||||||
|
ASSERT(result == pdPASS);
|
||||||
|
}
|
||||||
|
close(lfd);
|
||||||
|
s_wait = false;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
(void) args;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// task2: tests owner.
|
||||||
|
static void task2(void *args) {
|
||||||
|
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||||
|
char *ip, *cmd[3];
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
|
#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true");
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||||
|
s_error = 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);
|
||||||
|
cmd[0] = (char *) "./bsd_client";
|
||||||
|
cmd[1] = ip;
|
||||||
|
cmd[2] = NULL;
|
||||||
|
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
|
||||||
|
for (;;) {
|
||||||
|
pid_t r = waitpid(pid, &status, WNOHANG);
|
||||||
|
ASSERT(r >= 0);
|
||||||
|
if (r == pid) break;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
ASSERT(WIFEXITED(status));
|
||||||
|
ASSERT(WEXITSTATUS(status) == 0);
|
||||||
|
mg_free(ip);
|
||||||
|
s_wait = true;
|
||||||
|
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
DASHBOARD("accept");
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||||
|
s_error = false;
|
||||||
|
{
|
||||||
|
struct hostent *h = gethostbyname("invalidhostname");
|
||||||
|
ASSERT(h == NULL);
|
||||||
|
h = gethostbyname("mongoose.ws");
|
||||||
|
ASSERT(h != NULL);
|
||||||
|
if (h == NULL) {
|
||||||
|
MG_ERROR(("DNS failed"));
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DASHBOARD("gethostbyname");
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||||
|
s_error = false;
|
||||||
|
s_wait = true;
|
||||||
|
{
|
||||||
|
struct client_data cd[3];
|
||||||
|
struct hostent *h = gethostbyname("mongoose.ws");
|
||||||
|
strcpy(cd[0].hostname, "mongoose.ws");
|
||||||
|
memcpy(&cd[0].ip, h->h_addr, 4);
|
||||||
|
cd[0].done = false;
|
||||||
|
// client_task at higher priority than task1
|
||||||
|
xTaskCreate(client_task, "client1", 1024, &cd[0], configMAX_PRIORITIES - 1, NULL);
|
||||||
|
h = gethostbyname("cesanta.com");
|
||||||
|
strcpy(cd[1].hostname, "cesanta.com");
|
||||||
|
memcpy(&cd[1].ip, h->h_addr, 4);
|
||||||
|
cd[1].done = false;
|
||||||
|
// client_task at higher priority than task1
|
||||||
|
xTaskCreate(client_task, "client2", 1024, &cd[1], configMAX_PRIORITIES - 1, NULL);
|
||||||
|
h = gethostbyname("google.com");
|
||||||
|
strcpy(cd[2].hostname, "google.com");
|
||||||
|
memcpy(&cd[2].ip, h->h_addr, 4);
|
||||||
|
cd[2].done = false;
|
||||||
|
// client_task at higher priority than task1
|
||||||
|
xTaskCreate(client_task, "client3", 1024, &cd[2], configMAX_PRIORITIES - 1, NULL);
|
||||||
|
while (!cd[0].done || !cd[1].done || !cd[2].done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
DASHBOARD("connect");
|
||||||
|
// End task 1
|
||||||
|
s_done = true;
|
||||||
|
while (s_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
s_error = false;
|
||||||
|
mg_mgr_free(mgr);
|
||||||
|
ASSERT(mgr->conns == NULL); // Deconstruction OK
|
||||||
|
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true");
|
||||||
|
// last entry with no comma
|
||||||
|
vTaskEndScheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
// task1: network owner. Runs the Mongoose event loop and the BSD command queue.
|
||||||
|
// The interface needs to be started here, as FreeRTOS needs to be running (time base)
|
||||||
|
static void task1(void *args) {
|
||||||
|
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||||
|
|
||||||
|
// Stack initialization, Network configuration (DHCP lease, ...)
|
||||||
|
#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);
|
||||||
|
|
||||||
|
while (!s_done) {
|
||||||
|
mg_mgr_poll(mgr, 0);
|
||||||
|
mg_bsd_poll(mgr);
|
||||||
|
vTaskDelay(1); // Let idle reclaim deleted echo tasks
|
||||||
|
}
|
||||||
|
s_done = false;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
mg_log_set(atoi(debug_level));
|
||||||
|
|
||||||
|
struct mg_mgr mgr; // Event manager
|
||||||
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
|
|
||||||
|
struct mg_tcpip_if mif;
|
||||||
|
memset(&mif, 0, sizeof(mif));
|
||||||
|
|
||||||
|
mif.driver = driver;
|
||||||
|
mif.driver_data = driver_data;
|
||||||
|
|
||||||
|
#if BSDTEST_USING_DHCP == 1
|
||||||
|
#else
|
||||||
|
mif.ip = mg_htonl(MG_U32(192, 168, 32, 2)); // Triggering a network failure
|
||||||
|
mif.mask = mg_htonl(MG_U32(255, 255, 255, 0));
|
||||||
|
mif.gw = mg_htonl(MG_U32(192, 168, 32, 1));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1],
|
||||||
|
&mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]);
|
||||||
|
|
||||||
|
mg_tcpip_init(&mgr, &mif);
|
||||||
|
ASSERT(mgr.ifp != NULL); // driver init succeeded
|
||||||
|
MG_INFO(("Init done, starting interface"));
|
||||||
|
usleep(200000); // 200 ms
|
||||||
|
|
||||||
|
xTaskCreate(task1, "task1", 2048, &mgr, configMAX_PRIORITIES - 2, NULL);
|
||||||
|
vTaskStartScheduler();
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
if (s_abort != 0) return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("SUCCESS. Total tests: %d\n", s_num_tests);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// 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_DRIVER_INIT 0
|
||||||
|
#define MG_ENABLE_BSD_SOCKETS 1
|
||||||
|
#define MG_ENABLE_BSD_LOG 1
|
||||||
|
#define MG_ENABLE_LINES 1
|
||||||
|
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||||
|
|
||||||
|
//#include <ctype.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.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 <unistd.h>
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "shm_queue.h"
|
||||||
|
|
||||||
|
static volatile sig_atomic_t s_signo;
|
||||||
|
|
||||||
|
static void signal_handler(int signo) {
|
||||||
|
s_signo = signo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_udp_socket(void) {
|
||||||
|
const uint16_t port = 0xAA55; // Local UDP port
|
||||||
|
const uint16_t tap_port = 0x55AA; // Host port_tap_bridge UDP port
|
||||||
|
struct sockaddr_in port_addr, tap_addr;
|
||||||
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&port_addr, 0, sizeof(port_addr));
|
||||||
|
port_addr.sin_family = AF_INET;
|
||||||
|
port_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
port_addr.sin_port = htons(port);
|
||||||
|
if (bind(fd, (struct sockaddr *) &port_addr, sizeof(port_addr)) < 0) {
|
||||||
|
perror("bind");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
printf("Opened UDP socket: 127.0.0.1:%u (0x%04x)\n", (unsigned) port,
|
||||||
|
(unsigned) port);
|
||||||
|
|
||||||
|
memset(&tap_addr, 0, sizeof(tap_addr));
|
||||||
|
tap_addr.sin_family = AF_INET;
|
||||||
|
tap_addr.sin_addr.s_addr = htonl(0x7f000001U); // 127.0.0.1
|
||||||
|
tap_addr.sin_port = htons(tap_port);
|
||||||
|
if (connect(fd, (struct sockaddr *) &tap_addr, sizeof(tap_addr)) < 0) {
|
||||||
|
perror("connect");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
printf("Connected to TAP UDP socket: 127.0.0.1:%u (0x%04x)\n",
|
||||||
|
(unsigned) tap_port, (unsigned) tap_port);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct shm_link *open_shm(int *shmfd) {
|
||||||
|
struct shm_link *shm;
|
||||||
|
|
||||||
|
shm_unlink(SHM_NAME);
|
||||||
|
*shmfd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600);
|
||||||
|
if (*shmfd < 0) {
|
||||||
|
perror("shm_open");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (ftruncate(*shmfd, (off_t) sizeof(*shm)) != 0) {
|
||||||
|
perror("ftruncate");
|
||||||
|
close(*shmfd);
|
||||||
|
shm_unlink(SHM_NAME);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
shm = (struct shm_link *) mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, *shmfd, 0);
|
||||||
|
if (shm == MAP_FAILED) {
|
||||||
|
perror("mmap");
|
||||||
|
close(*shmfd);
|
||||||
|
shm_unlink(SHM_NAME);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(shm, 0, sizeof(*shm));
|
||||||
|
printf("Opened shared memory: %s\n", SHM_NAME);
|
||||||
|
|
||||||
|
return shm;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
int sockfd, shmfd = -1;
|
||||||
|
struct shm_link *shm;
|
||||||
|
|
||||||
|
sockfd = open_udp_socket();
|
||||||
|
if (sockfd < 0) return EXIT_FAILURE;
|
||||||
|
shm = open_shm(&shmfd);
|
||||||
|
if (shm == NULL) {
|
||||||
|
close(sockfd);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
|
while (s_signo == 0) {
|
||||||
|
struct pollfd pfd = {sockfd, (short) (POLLIN | POLLPRI), 0};
|
||||||
|
int result = poll(&pfd, 1, 1);
|
||||||
|
if (result < 0 && errno != EINTR) {
|
||||||
|
perror("poll");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (result > 0 && (pfd.revents & (POLLIN | POLLPRI))) {
|
||||||
|
uint8_t buf[FRAME_SIZE];
|
||||||
|
ssize_t nread = recv(sockfd, buf, sizeof(buf), 0);
|
||||||
|
if (nread > 0 &&
|
||||||
|
!shm_queue_push(&shm->from_tap, buf, (size_t) nread)) {
|
||||||
|
printf("FRAME LOST: port -> SHM\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint8_t buf[FRAME_SIZE];
|
||||||
|
size_t n = shm_queue_pop(&shm->to_tap, buf, sizeof(buf));
|
||||||
|
if (n > 0) {
|
||||||
|
if (send(sockfd, buf, n, 0) < 0) perror("send");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
munmap(shm, sizeof(*shm));
|
||||||
|
close(shmfd);
|
||||||
|
shm_unlink(SHM_NAME);
|
||||||
|
close(sockfd);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define FRAME_SIZE 1540
|
||||||
|
#define FRAME_QUEUE_DEPTH 32
|
||||||
|
#define SHM_NAME "/mongoose-shm"
|
||||||
|
|
||||||
|
// Keep this SHM ABI semaphore-free. Some S390/Linux headers pull fcntl/socket
|
||||||
|
// declarations through <semaphore.h>, colliding with Mongoose BSD prototypes.
|
||||||
|
// Each direction is single-producer/single-consumer and polled, so a simple
|
||||||
|
// ring is enough for this test path.
|
||||||
|
struct shm_frame {
|
||||||
|
size_t len;
|
||||||
|
uint8_t buf[FRAME_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct shm_queue {
|
||||||
|
volatile size_t head;
|
||||||
|
volatile size_t tail;
|
||||||
|
struct shm_frame frames[FRAME_QUEUE_DEPTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct shm_link {
|
||||||
|
struct shm_queue to_tap;
|
||||||
|
struct shm_queue from_tap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool shm_queue_push(struct shm_queue *q, const void *buf,
|
||||||
|
size_t len) {
|
||||||
|
size_t tail = q->tail, next = (tail + 1) % FRAME_QUEUE_DEPTH;
|
||||||
|
struct shm_frame *f;
|
||||||
|
if (len > sizeof(q->frames[0].buf) || next == q->head) return false;
|
||||||
|
f = &q->frames[tail];
|
||||||
|
memcpy(f->buf, buf, len);
|
||||||
|
f->len = len;
|
||||||
|
__sync_synchronize();
|
||||||
|
q->tail = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t shm_queue_pop(struct shm_queue *q, void *buf, size_t len) {
|
||||||
|
size_t head = q->head, n;
|
||||||
|
struct shm_frame *f;
|
||||||
|
if (head == q->tail) return 0;
|
||||||
|
__sync_synchronize();
|
||||||
|
f = &q->frames[head];
|
||||||
|
n = f->len;
|
||||||
|
if (n > len) {
|
||||||
|
q->head = (head + 1) % FRAME_QUEUE_DEPTH;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(buf, f->buf, n);
|
||||||
|
__sync_synchronize();
|
||||||
|
q->head = (head + 1) % FRAME_QUEUE_DEPTH;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_tun.h>
|
||||||
|
#include "shm_queue.h"
|
||||||
|
|
||||||
|
static volatile sig_atomic_t s_signo;
|
||||||
|
void signal_handler(int signo) {
|
||||||
|
s_signo = signo;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Setup interface
|
||||||
|
const char *iface = "tap0"; // Network iface
|
||||||
|
const char *tuntap_device = "/dev/net/tun";
|
||||||
|
int fd = open(tuntap_device, O_RDWR);
|
||||||
|
struct ifreq ifr;
|
||||||
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
|
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
|
||||||
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||||
|
if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
|
||||||
|
printf("Failed to setup TAP interface: %s", ifr.ifr_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
printf("Opened TAP interface: %s\n", iface);
|
||||||
|
|
||||||
|
int shmfd;
|
||||||
|
struct shm_link *shm;
|
||||||
|
|
||||||
|
shm_unlink(SHM_NAME);
|
||||||
|
shmfd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600);
|
||||||
|
if (shmfd < 0) {
|
||||||
|
printf("shm_open failed: %d\n", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (ftruncate(shmfd, sizeof(*shm)) != 0) {
|
||||||
|
printf("ftruncate failed: %d\n", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
shm = (struct shm_link *) mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, shmfd, 0);
|
||||||
|
if (shm == MAP_FAILED) {
|
||||||
|
printf("mmap failed: %d\n", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(shm, 0, sizeof(*shm));
|
||||||
|
printf("Opened shared memory: %s\n", SHM_NAME);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
|
while (s_signo == 0) {
|
||||||
|
struct pollfd pfd[1];
|
||||||
|
int result;
|
||||||
|
do {
|
||||||
|
pfd[0].fd = fd;
|
||||||
|
pfd[0].events = POLLIN | POLLPRI; // will wait for input available (read ready), use POLLOUT for write
|
||||||
|
|
||||||
|
// only read ready shown here, for write ready use the 3rd arg, and the 4th for exceptions
|
||||||
|
result = poll(pfd, 1, 1); // 1 pfd, TIMEOUT in milliseconds
|
||||||
|
switch(result) {
|
||||||
|
case -1: // error
|
||||||
|
break;
|
||||||
|
case 0: // timeout (if a timeout struct was passed to select)
|
||||||
|
break;
|
||||||
|
default: // the number of fd/sockets with data available (see pfd.revents for each one);
|
||||||
|
if (pfd[0].revents & (POLLIN | POLLPRI)) {
|
||||||
|
uint8_t buf[FRAME_SIZE];
|
||||||
|
ssize_t nread = read(fd, buf, sizeof(buf));
|
||||||
|
// printf("Got at TAP: %d bytes\n", (int) nread);
|
||||||
|
if (nread > 0) {
|
||||||
|
if (!shm_queue_push(&shm->from_tap, buf, (size_t) nread)) {
|
||||||
|
printf("FRAME LOST: TAP -> SHM\n");
|
||||||
|
}
|
||||||
|
// printf("Sent to SHM\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint8_t buf[FRAME_SIZE];
|
||||||
|
size_t n = shm_queue_pop(&shm->to_tap, buf, sizeof(buf));
|
||||||
|
if (n > 0) {
|
||||||
|
// printf("Got at SHM: %d bytes\n", (int) f->len);
|
||||||
|
write(fd, buf, n);
|
||||||
|
// printf("Sent to TAP\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (result >= 0 && s_signo == 0);
|
||||||
|
if (result < 0)
|
||||||
|
printf("looks like we got an error here... should I quit ? Waiting for CTRL-C\n");
|
||||||
|
}
|
||||||
|
munmap(shm, sizeof(*shm));
|
||||||
|
close(shmfd);
|
||||||
|
shm_unlink(SHM_NAME);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
+283
@@ -0,0 +1,283 @@
|
|||||||
|
#define MG_ENABLE_TCPIP 1
|
||||||
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||||
|
|
||||||
|
#include "mongoose.c"
|
||||||
|
|
||||||
|
static int s_num_tests = 0;
|
||||||
|
static bool s_error = false;
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
static int s_abort = 0;
|
||||||
|
#define ABORT() ++s_abort, s_error = true
|
||||||
|
#else
|
||||||
|
#ifdef NO_SLEEP_ABORT
|
||||||
|
#define ABORT() abort()
|
||||||
|
#else
|
||||||
|
#define ABORT() \
|
||||||
|
sleep(2); /* 2s, GH print reason */ \
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ASSERT(expr) \
|
||||||
|
do { \
|
||||||
|
s_num_tests++; \
|
||||||
|
if (!(expr)) { \
|
||||||
|
printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \
|
||||||
|
fflush(stdout); \
|
||||||
|
ABORT(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static const uint8_t s_mac[6] = {2, 3, 4, 5, 6, 7};
|
||||||
|
static const uint8_t s_src[6] = {8, 9, 10, 11, 12, 13};
|
||||||
|
|
||||||
|
static void reset_if(struct mg_tcpip_if *ifp) {
|
||||||
|
memset(ifp, 0, sizeof(*ifp));
|
||||||
|
memcpy(ifp->mac, s_mac, sizeof(s_mac));
|
||||||
|
ifp->l2type = MG_TCPIP_L2_ETH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mk_eth(uint8_t *buf, const uint8_t *dst, uint16_t type,
|
||||||
|
const uint8_t *payload, size_t len) {
|
||||||
|
struct eth *eth = (struct eth *) buf;
|
||||||
|
memcpy(eth->dst, dst, sizeof(eth->dst));
|
||||||
|
memcpy(eth->src, s_src, sizeof(eth->src));
|
||||||
|
eth->type = mg_htons(type);
|
||||||
|
memcpy(eth + 1, payload, len);
|
||||||
|
return sizeof(*eth) + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mk_vlan(uint8_t *buf, const uint8_t *dst, uint16_t vlan,
|
||||||
|
uint16_t type, const uint8_t *payload, size_t len) {
|
||||||
|
struct eth *eth = (struct eth *) buf;
|
||||||
|
struct qtag *qtag = (struct qtag *) ð->type;
|
||||||
|
memcpy(eth->dst, dst, sizeof(eth->dst));
|
||||||
|
memcpy(eth->src, s_src, sizeof(eth->src));
|
||||||
|
qtag->tpid = mg_htons(0x8100);
|
||||||
|
qtag->tci = mg_htons(vlan);
|
||||||
|
MG_STORE_BE16(qtag + 1, type);
|
||||||
|
memcpy((uint8_t *) (qtag + 1) + sizeof(uint16_t), payload, len);
|
||||||
|
return sizeof(*eth) + sizeof(*qtag) + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t add_fcs(uint8_t *buf, size_t len) {
|
||||||
|
uint32_t crc = mg_crc32(0, (const char *) buf, len);
|
||||||
|
buf[len++] = (uint8_t) crc; // store as LE32
|
||||||
|
buf[len++] = (uint8_t) (crc >> 8);
|
||||||
|
buf[len++] = (uint8_t) (crc >> 16);
|
||||||
|
buf[len++] = (uint8_t) (crc >> 24);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DASHBOARD(x) \
|
||||||
|
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
||||||
|
|
||||||
|
static void test_eth(void) {
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
size_t len;
|
||||||
|
uint8_t frame[64];
|
||||||
|
|
||||||
|
{ // Plain Ethernet frame is accepted
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
reset_if(&ifp);
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
MG_DEBUG(("MTU: %u, frame size: %u", ifp.l2mtu, ifp.framesize));
|
||||||
|
ASSERT(ifp.l2mtu != 0);
|
||||||
|
ASSERT(ifp.framesize != 0);
|
||||||
|
len = mk_eth(frame, s_mac, 0x0800, payload, sizeof(payload));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV4);
|
||||||
|
ASSERT(pay.len == sizeof(payload));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // Unknown Ethernet type is discarded
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
char sentinel[] = "discard";
|
||||||
|
reset_if(&ifp);
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = mk_eth(frame, s_mac, 0x1234, payload, sizeof(payload));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
pay = mg_str_n(sentinel, sizeof(sentinel));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_ARP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_mac(void) {
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
size_t len;
|
||||||
|
uint8_t frame[64];
|
||||||
|
|
||||||
|
{ // MAC check accepts frames addressed to us
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
reset_if(&ifp);
|
||||||
|
ifp.enable_mac_check = true;
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = mk_eth(frame, s_mac, 0x0800, payload, sizeof(payload));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV4);
|
||||||
|
ASSERT(pay.len == sizeof(payload));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // MAC check discards alien unicast frames
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4}, dst[6] = {1, 1, 1, 1, 1, 1};
|
||||||
|
char sentinel[] = "discard";
|
||||||
|
reset_if(&ifp);
|
||||||
|
ifp.enable_mac_check = true;
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = mk_eth(frame, dst, 0x0800, payload, sizeof(payload));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
pay = mg_str_n(sentinel, sizeof(sentinel));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_ARP);
|
||||||
|
ASSERT(pay.buf == sentinel);
|
||||||
|
ASSERT(pay.len == sizeof(sentinel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fcs(void) {
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
size_t len;
|
||||||
|
uint8_t frame[64];
|
||||||
|
|
||||||
|
{ // verify add_fcs() stores the CRC bytes in the proper order
|
||||||
|
// (internal consistency test)
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
uint8_t fcs[] = {0xf4, 0xd4, 0x37, 0xf6};
|
||||||
|
len = add_fcs(frame, mk_eth(frame, s_mac, 0x0800, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
ASSERT(memcmp(frame + len - sizeof(fcs), fcs, sizeof(fcs)) == 0);
|
||||||
|
}
|
||||||
|
{ // FCS check strips a valid CRC
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
reset_if(&ifp);
|
||||||
|
ifp.enable_fcs_check = true;
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = add_fcs(frame, mk_eth(frame, s_mac, 0x0800, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV4);
|
||||||
|
ASSERT(pay.len == sizeof(payload));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // FCS check rejects a bad CRC
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
char sentinel[] = "discard";
|
||||||
|
reset_if(&ifp);
|
||||||
|
ifp.enable_fcs_check = true;
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = add_fcs(frame, mk_eth(frame, s_mac, 0x0800, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
frame[len - 1] ^= 1;
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
pay = mg_str_n(sentinel, sizeof(sentinel));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_ARP);
|
||||||
|
ASSERT(pay.buf == sentinel);
|
||||||
|
ASSERT(pay.len == sizeof(sentinel));
|
||||||
|
}
|
||||||
|
{ // Disabled FCS check leaves the CRC in payload
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
reset_if(&ifp);
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = add_fcs(frame, mk_eth(frame, s_mac, 0x0800, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV4);
|
||||||
|
ASSERT(pay.len == sizeof(payload) + sizeof(uint32_t));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vlan(void) {
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
size_t len;
|
||||||
|
uint8_t frame[64];
|
||||||
|
|
||||||
|
{ // Configured VLAN accepts matching tagged frames
|
||||||
|
uint8_t payload[] = {5, 6, 7, 8};
|
||||||
|
reset_if(&ifp);
|
||||||
|
ifp.l2data.eth.vlan_id = 123;
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
MG_DEBUG(("MTU: %u, frame size: %u", ifp.l2mtu, ifp.framesize));
|
||||||
|
ASSERT(ifp.l2mtu != 0);
|
||||||
|
ASSERT(ifp.framesize != 0);
|
||||||
|
len = mk_vlan(frame, s_mac, 123, 0x86dd, payload, sizeof(payload));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV6);
|
||||||
|
ASSERT(pay.len == sizeof(payload));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // Configured VLAN rejects non-matching tags
|
||||||
|
uint8_t payload[] = {5, 6, 7, 8};
|
||||||
|
char sentinel[] = "discard";
|
||||||
|
reset_if(&ifp);
|
||||||
|
ifp.l2data.eth.vlan_id = 123;
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = mk_vlan(frame, s_mac, 124, 0x86dd, payload, sizeof(payload));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
pay = mg_str_n(sentinel, sizeof(sentinel));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_ARP);
|
||||||
|
ASSERT(pay.buf == sentinel);
|
||||||
|
ASSERT(pay.len == sizeof(sentinel));
|
||||||
|
}
|
||||||
|
{ // Tagged frames are rejected when no VLAN is configured
|
||||||
|
uint8_t payload[] = {5, 6, 7, 8};
|
||||||
|
reset_if(&ifp);
|
||||||
|
mg_l2_init(&ifp);
|
||||||
|
len = mk_vlan(frame, s_mac, 123, 0x86dd, payload, sizeof(payload));
|
||||||
|
raw = mg_str_n((char *) frame, len);
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_ARP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
s_error = false;
|
||||||
|
test_eth();
|
||||||
|
DASHBOARD("eth");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
test_mac();
|
||||||
|
DASHBOARD("mac");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
test_fcs();
|
||||||
|
DASHBOARD("fcs");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
test_vlan();
|
||||||
|
printf("HEALTH_DASHBOARD\t\"vlan\": %s\n", s_error ? "false" : "true");
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
if (s_abort != 0) return EXIT_FAILURE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("SUCCESS. Total tests: %d\n", s_num_tests);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
+233
-49
@@ -23,6 +23,52 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deduplicate layer-3 mocking among layer-2s
|
||||||
|
struct fuzz_l3_state {
|
||||||
|
size_t ip_proto, udp_port;
|
||||||
|
size_t ip6_next, icmp6_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fuzz_ip4(void *buf, size_t len, struct fuzz_l3_state *st) {
|
||||||
|
if (len >= sizeof(struct ip)) {
|
||||||
|
uint8_t ip_protos[] = {1, 6, 17}; // ICMP, TCP, UDP
|
||||||
|
struct ip *ip4 = (struct ip *) buf;
|
||||||
|
ip4->ver = (uint8_t) ((ip4->ver & ~0xf0) | (4 << 4));
|
||||||
|
// send all handled IP protos, then 2 random ones
|
||||||
|
if (st->ip_proto >= (sizeof(ip_protos) / sizeof(ip_protos[0]) + 2)) st->ip_proto = 0;
|
||||||
|
if (st->ip_proto < (sizeof(ip_protos) / sizeof(ip_protos[0]))) ip4->proto = ip_protos[st->ip_proto++];
|
||||||
|
if (ip4->proto == 1) { // ICMP
|
||||||
|
} else if (ip4->proto == 6) { // TCP
|
||||||
|
} else if (ip4->proto == 17 && len >= sizeof(struct ip) + sizeof(struct udp)) { // UDP
|
||||||
|
uint16_t udp_ports[] = {67, 68}; // DHCP server and client
|
||||||
|
struct udp *udp = (struct udp *) (ip4 + 1);
|
||||||
|
// send all handled UDP ports, then 2 random ones
|
||||||
|
if (st->udp_port >= (sizeof(udp_ports) / sizeof(udp_ports[0]) + 2)) st->udp_port = 0;
|
||||||
|
if (st->udp_port < (sizeof(udp_ports) / sizeof(udp_ports[0]))) udp->dport = mg_htons(udp_ports[st->udp_port++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fuzz_ip6(void *buf, size_t len, struct fuzz_l3_state *st) {
|
||||||
|
if (len >= sizeof(struct ip6)) {
|
||||||
|
uint8_t ip6_protos[] = {6, 17, 58}; // TCP, UDP, ICMPv6
|
||||||
|
struct ip6 *ip6 = (struct ip6 *) buf;
|
||||||
|
ip6->ver = (uint8_t) ((ip6->ver & ~0xf0) | (6 << 4));
|
||||||
|
// send all handled IPv6 "next headers", then 2 random ones
|
||||||
|
if (st->ip6_next >= (sizeof(ip6_protos) / sizeof(ip6_protos[0]) + 2)) st->ip6_next = 0;
|
||||||
|
if (st->ip6_next < (sizeof(ip6_protos) / sizeof(ip6_protos[0]))) ip6->next = ip6_protos[st->ip6_next++];
|
||||||
|
if (ip6->next == 6) { // TCP
|
||||||
|
} else if (ip6->next == 17) { // UDP
|
||||||
|
} else if (ip6->next == 58 && len >= sizeof(struct ip6) + sizeof(struct icmp6)) { // ICMPv6
|
||||||
|
uint8_t icmp6_types[] = {128, 134, 135, 136}; // Echo Request, RA, NS, NA
|
||||||
|
struct icmp6 *icmp6 = (struct icmp6 *) (ip6 + 1);
|
||||||
|
// send all handled ICMPv6 types, then 2 random ones
|
||||||
|
if (st->icmp6_type >= (sizeof(icmp6_types) / sizeof(icmp6_types[0]) + 2)) st->icmp6_type = 0;
|
||||||
|
if (st->icmp6_type < (sizeof(icmp6_types) / sizeof(icmp6_types[0]))) icmp6->type = icmp6_types[st->icmp6_type++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
mg_log_set(MG_LL_INFO);
|
mg_log_set(MG_LL_INFO);
|
||||||
|
|
||||||
@@ -94,21 +140,26 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
|
|
||||||
// Test built-in TCP/IP stack
|
// Test built-in TCP/IP stack
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
|
static size_t t;
|
||||||
struct mg_tcpip_if mif = {.ip = 1,
|
struct mg_tcpip_if mif = {.ip = 1,
|
||||||
.mask = 255,
|
.mask = 255,
|
||||||
.gw = 1,
|
.gw = 1,
|
||||||
.gw_ready = true,
|
.gw_ready = true,
|
||||||
.state = MG_TCPIP_STATE_READY,
|
.state = MG_TCPIP_STATE_READY,
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
.ip6[0] = 1;
|
.ip6[0] = 1,
|
||||||
.prefix[0] = 1;
|
.prefix[0] = 1,
|
||||||
.prefix_len = 64;
|
.prefix_len = 64,
|
||||||
.gw6[0] = 1;
|
.gw6[0] = 1,
|
||||||
.gw6_ready = true;
|
.gw6_ready = true,
|
||||||
.state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops
|
.state6 = MG_TCPIP_STATE_READY, // so mg_send() works and RS stops
|
||||||
#endif
|
#endif
|
||||||
.driver = &mg_tcpip_driver_mock};
|
.driver = &mg_tcpip_driver_mock};
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
|
|
||||||
|
if (t >= MG_TCPIP_L2_USER) t = 0; // defined in l2.h
|
||||||
|
mif.l2type = (enum mg_l2type) t++; // configure to a L2 type
|
||||||
|
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
mg_tcpip_init(&mgr, &mif);
|
mg_tcpip_init(&mgr, &mif);
|
||||||
|
|
||||||
@@ -116,56 +167,186 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
void *pkt = malloc(size);
|
void *pkt = malloc(size);
|
||||||
struct eth *eth = (struct eth *) pkt;
|
struct eth *eth = (struct eth *) pkt;
|
||||||
memcpy(pkt, data, size);
|
memcpy(pkt, data, size);
|
||||||
if (size > sizeof(*eth)) {
|
if ((mif.l2type == MG_TCPIP_L2_ETH || mif.l2type == MG_TCPIP_L2_PPPoE) && size > sizeof(*eth)) {
|
||||||
static size_t i;
|
static size_t i, v;
|
||||||
|
static struct fuzz_l3_state l3;
|
||||||
|
uint8_t *ethpay = (uint8_t *) (eth + 1);
|
||||||
|
size_t ethhdrsz = sizeof(*eth);
|
||||||
|
uint16_t etype;
|
||||||
// eth_types[] exists in l2_eth.c
|
// eth_types[] exists in l2_eth.c
|
||||||
memcpy(eth->dst, mif.mac, 6); // Set valid destination MAC
|
memcpy(eth->dst, mif.mac, 6); // Set valid destination MAC
|
||||||
// send all handled eth types, then 2 random ones
|
// send all handled eth types, then 2 random ones
|
||||||
if (i >= (sizeof(eth_types) / sizeof(eth_types[0]) + 2)) i = 0;
|
if (i >= (sizeof(eth_types) / sizeof(eth_types[0]) + 2)) i = 0;
|
||||||
if (i < (sizeof(eth_types) / sizeof(eth_types[0]))) eth->type = mg_htons(eth_types[i++]);
|
if (i < (sizeof(eth_types) / sizeof(eth_types[0]))) eth->type = mg_htons(eth_types[i++]);
|
||||||
|
etype = mg_ntohs(eth->type);
|
||||||
|
if (v >= 10) v = 0; // 802.1Q tag 3 out of 10 frames
|
||||||
|
if (v < 3) mif.l2data.eth.vlan_id = 123;
|
||||||
|
if (size > sizeof(*eth) + sizeof(struct qtag)) {
|
||||||
|
struct qtag *qtag = (struct qtag *) ð->type;
|
||||||
|
if (v == 0) {
|
||||||
|
// v=0: configured for VLAN 123, untagged frame, discard path
|
||||||
|
} else if (v == 1) {
|
||||||
|
// v=1: configured for VLAN 123, tagged VLAN 123, accept path
|
||||||
|
qtag->tpid = mg_htons(0x8100);
|
||||||
|
qtag->tci = mg_htons(123);
|
||||||
|
MG_STORE_BE16(qtag + 1, etype);
|
||||||
|
ethhdrsz += sizeof(*qtag);
|
||||||
|
ethpay += sizeof(*qtag);
|
||||||
|
} else if (v == 2) {
|
||||||
|
// v=2: configured for VLAN 123, tagged random VLAN, mismatch path
|
||||||
|
qtag->tpid = mg_htons(0x8100);
|
||||||
|
MG_STORE_BE16(qtag + 1, etype);
|
||||||
|
ethhdrsz += sizeof(*qtag);
|
||||||
|
ethpay += sizeof(*qtag);
|
||||||
|
} else if (v == 3) {
|
||||||
|
// v=3: unconfigured VLAN, tagged frame, discard path
|
||||||
|
qtag->tpid = mg_htons(0x8100);
|
||||||
|
MG_STORE_BE16(qtag + 1, etype);
|
||||||
|
ethhdrsz += sizeof(*qtag);
|
||||||
|
ethpay += sizeof(*qtag);
|
||||||
|
} // else unconfigured VLAN, untagged frame, normal path
|
||||||
|
}
|
||||||
|
++v;
|
||||||
|
if (etype == 0x8863 && size > (ethhdrsz + sizeof(struct pppoe))) { // PPPoE disc
|
||||||
|
static size_t j;
|
||||||
|
uint8_t pppoe_codes[] = {MG_PPPoE_PADO, MG_PPPoE_PADS, MG_PPPoE_PADT}; // defined in l2_ppp.c
|
||||||
|
struct pppoe *pppoe = (struct pppoe *) ethpay;
|
||||||
|
pppoe->vertype = 0x11;
|
||||||
|
// send all handled PPPoE codes, then 2 random ones
|
||||||
|
if (j >= (sizeof(pppoe_codes) / sizeof(pppoe_codes[0]) + 2)) j = 0;
|
||||||
|
if (j < (sizeof(pppoe_codes) / sizeof(pppoe_codes[0]))) pppoe->code = (pppoe_codes[j++]);
|
||||||
|
if (pppoe->code == MG_PPPoE_PADO) {
|
||||||
|
static size_t k;
|
||||||
|
// send id=0, then 2 random ids
|
||||||
|
if (k >= 3) k = 0;
|
||||||
|
if (k == 0) pppoe->id = 0;
|
||||||
|
++k;
|
||||||
|
} else if (pppoe->code == MG_PPPoE_PADS) {
|
||||||
|
static size_t k;
|
||||||
|
// send id=x, then 2 random ids
|
||||||
|
if (k >= 3) k = 0;
|
||||||
|
if (k == 0) pppoe->id = 0x1234;
|
||||||
|
++k;
|
||||||
|
} else if (pppoe->code == MG_PPPoE_PADT) { // keep separate so they do not correlate too much
|
||||||
|
static size_t k;
|
||||||
|
// send id=x, then 2 random ids
|
||||||
|
if (k >= 3) k = 0;
|
||||||
|
if (k == 0) pppoe->id = 0x1234;
|
||||||
|
++k;
|
||||||
|
}
|
||||||
|
} else if (etype == 0x8864 && size > (ethhdrsz + sizeof(struct pppoe))) { // PPPoE sess
|
||||||
|
// PPP rx is covered via HDLC too; PPPoE tx wrapping may remain unexercised.
|
||||||
|
struct pppoe *pppoe = (struct pppoe *) ethpay;
|
||||||
|
static size_t k, l;
|
||||||
|
// send code=0, then 2 random codes
|
||||||
|
if (k >= 3) k = 0;
|
||||||
|
if (k == 0) {
|
||||||
|
static size_t m;
|
||||||
|
pppoe->code = 0;
|
||||||
|
// send id=x, then 2 random ids
|
||||||
|
if (m >= 3) m = 0;
|
||||||
|
if (m == 0) pppoe->id = 0x1234;
|
||||||
|
++m;
|
||||||
|
}
|
||||||
|
++k;
|
||||||
|
// set s_state to valid and invalid
|
||||||
|
if (l >= 2) l = 0;
|
||||||
|
if (l == 0) s_state = MG_PPPoE_ST_SESS; // defined in l2_ppp.c
|
||||||
|
if (l == 1) s_state = MG_PPPoE_ST_REQ;
|
||||||
|
++l;
|
||||||
|
// PPP exercised in other block
|
||||||
|
}
|
||||||
// build proper layer-3 datagrams, to be able to exercise layers above
|
// build proper layer-3 datagrams, to be able to exercise layers above
|
||||||
if (eth->type == mg_htons(0x800) && size > (sizeof(*eth) + sizeof(struct ip))) { // IPv4
|
else if (etype == 0x800 && size > (ethhdrsz + sizeof(struct ip))) { // IPv4
|
||||||
static size_t j;
|
fuzz_ip4(ethpay, size - ethhdrsz, &l3);
|
||||||
uint8_t ip_protos[] = {1, 6, 17}; // ICMP, TCP, UDP
|
} else if (etype == 0x806) { // ARP
|
||||||
struct ip *ip4 = (struct ip *) (eth + 1);
|
|
||||||
ip4->ver = (ip4->ver & ~0xf0) | (4 << 4);
|
|
||||||
// send all handled IP protos, then 2 random ones
|
|
||||||
if (j >= (sizeof(ip_protos) / sizeof(ip_protos[0]) + 2)) j = 0;
|
|
||||||
if (j < (sizeof(ip_protos) / sizeof(ip_protos[0]))) ip4->proto = (ip_protos[j++]);
|
|
||||||
if (ip4->proto == 1) { // ICMP
|
|
||||||
} else if (ip4->proto == 6) { // TCP
|
|
||||||
} else if (ip4->proto == 17) { // UDP
|
|
||||||
if (size > (sizeof(*eth) + sizeof(struct ip) + sizeof(struct udp))) {
|
|
||||||
static size_t k;
|
|
||||||
uint16_t udp_ports[] = {67, 68}; // DHCP server and client
|
|
||||||
struct udp *udp = (struct udp *) (ip4 + 1);
|
|
||||||
// send all handled UDP ports, then 2 random ones
|
|
||||||
if (k >= (sizeof(udp_ports) / sizeof(udp_ports[0]) + 2)) k = 0;
|
|
||||||
if (k < (sizeof(udp_ports) / sizeof(udp_ports[0]))) udp->dport = mg_htons(udp_ports[k++]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (eth->type == mg_htons(0x806)) { // ARP
|
|
||||||
|
|
||||||
} else if (eth->type == mg_htons(0x86dd) && size > (sizeof(*eth) + sizeof(struct ip6))) { // IPv6
|
} else if (etype == 0x86dd && size > (ethhdrsz + sizeof(struct ip6))) { // IPv6
|
||||||
static size_t j;
|
fuzz_ip6(ethpay, size - ethhdrsz, &l3);
|
||||||
uint8_t ip6_protos[] = {6, 17, 58}; // TCP, UDP, ICMPv6
|
}
|
||||||
struct ip6 *ip6 = (struct ip6 *) (eth + 1);
|
} else if (mif.l2type == MG_TCPIP_L2_PPP && size > (sizeof(struct hdlc_) + sizeof(struct ppp) + 2)) {
|
||||||
ip6->ver = (ip6->ver & ~0xf0) | (6 << 4);
|
static size_t i;
|
||||||
// send all handled IPv6 "next headers", then 2 random ones
|
static struct fuzz_l3_state l3;
|
||||||
if (j >= (sizeof(ip6_protos) / sizeof(ip6_protos[0]) + 2)) j = 0;
|
uint16_t ppp_protos[] = {
|
||||||
if (j < (sizeof(ip6_protos) / sizeof(ip6_protos[0]))) ip6->next = (ip6_protos[j++]);
|
MG_PPP_PROTO_LCP, MG_PPP_PROTO_IPCP, MG_PPP_PROTO_IP, MG_PPP_PROTO_IPV6CP, MG_PPP_PROTO_IPV6
|
||||||
if (ip6->next == 6) { // TCP
|
};
|
||||||
} else if (ip6->next == 17) { // UDP
|
struct hdlc_ *hdlc = (struct hdlc_ *) pkt;
|
||||||
} else if (ip6->next == 58) { // ICMPv6
|
struct ppp *ppp = (struct ppp *) (hdlc + 1);
|
||||||
if (size >= (sizeof(*eth) + sizeof(struct ip6) + sizeof(struct icmp6))) {
|
size_t len = size - sizeof(*hdlc) - sizeof(*ppp) - 2;
|
||||||
static size_t k;
|
hdlc->addr = MG_PPP_ADDR;
|
||||||
uint8_t icmp6_types[] = {128, 134, 135, 136}; // Echo Request, RA, NS, NA
|
hdlc->ctrl = MG_PPP_CTRL;
|
||||||
struct icmp6 *icmp6 = (struct icmp6 *) (ip6 + 1);
|
s_lcpup = true;
|
||||||
// send all handled ICMPv6 types, then 2 random ones
|
// send all handled PPP protocols, then 2 random ones
|
||||||
if (k >= (sizeof(icmp6_types) / sizeof(icmp6_types[0]) + 2)) k = 0;
|
if (i >= (sizeof(ppp_protos) / sizeof(ppp_protos[0]) + 2)) i = 0;
|
||||||
if (k < (sizeof(icmp6_types) / sizeof(icmp6_types[0]))) icmp6->type = icmp6_types[k++];
|
if (i < (sizeof(ppp_protos) / sizeof(ppp_protos[0]))) ppp->proto = mg_htons(ppp_protos[i++]);
|
||||||
}
|
if (ppp->proto == mg_htons(MG_PPP_PROTO_LCP) && len >= sizeof(struct lcp)) {
|
||||||
|
static size_t j, k;
|
||||||
|
uint8_t lcp_codes[] = {MG_PPP_LCP_CFG_REQ, MG_PPP_LCP_CFG_ACK, MG_PPP_LCP_CFG_TERM_REQ, MG_PPP_LCP_ECHO_REQ};
|
||||||
|
struct lcp *lcp = (struct lcp *) (ppp + 1);
|
||||||
|
// send all handled LCP codes, then 2 random ones
|
||||||
|
if (j >= (sizeof(lcp_codes) / sizeof(lcp_codes[0]) + 2)) j = 0;
|
||||||
|
if (j < (sizeof(lcp_codes) / sizeof(lcp_codes[0]))) lcp->code = lcp_codes[j++];
|
||||||
|
// send valid length, then 2 random lengths
|
||||||
|
if (k >= 3) k = 0;
|
||||||
|
if (k == 0) lcp->len = mg_htons((uint16_t) sizeof(*lcp));
|
||||||
|
++k;
|
||||||
|
} else if (ppp->proto == mg_htons(MG_PPP_PROTO_IPCP) && len >= 10) {
|
||||||
|
static size_t j, k, m;
|
||||||
|
uint8_t ipcp_codes[] = {MG_PPP_IPCP_CFG_REQ, MG_PPP_IPCP_CFG_ACK, MG_PPP_IPCP_CFG_NACK, MG_PPP_IPCP_CFG_REJECT};
|
||||||
|
struct ipcp *ipcp = (struct ipcp *) (ppp + 1);
|
||||||
|
uint8_t *opts = (uint8_t *) (ipcp + 1);
|
||||||
|
// send all handled IPCP codes, then 2 random ones
|
||||||
|
if (j >= (sizeof(ipcp_codes) / sizeof(ipcp_codes[0]) + 2)) j = 0;
|
||||||
|
if (j < (sizeof(ipcp_codes) / sizeof(ipcp_codes[0]))) ipcp->code = ipcp_codes[j++];
|
||||||
|
// send valid length, then 2 random lengths
|
||||||
|
if (m >= 3) m = 0;
|
||||||
|
if (m == 0) ipcp->len = mg_htons(10);
|
||||||
|
++m;
|
||||||
|
// send valid IPADDR option, then 2 random option types
|
||||||
|
if (k >= 3) k = 0;
|
||||||
|
if (k == 0) {
|
||||||
|
static size_t l;
|
||||||
|
opts[0] = MG_PPP_IPCP_OPT_IPADDR;
|
||||||
|
// for valid option type, send valid length, then random length
|
||||||
|
if (l >= 2) l = 0;
|
||||||
|
if (l == 0) opts[1] = 6;
|
||||||
|
++l;
|
||||||
}
|
}
|
||||||
|
++k;
|
||||||
|
} else if (ppp->proto == mg_htons(MG_PPP_PROTO_IP) && len >= sizeof(struct ip)) {
|
||||||
|
fuzz_ip4(ppp + 1, len, &l3);
|
||||||
|
} else if (ppp->proto == mg_htons(MG_PPP_PROTO_IPV6CP) && len >= 14) {
|
||||||
|
static size_t j, k, m;
|
||||||
|
uint8_t ipv6cp_codes[] = {MG_PPP_IPV6CP_CFG_REQ, MG_PPP_IPV6CP_CFG_ACK, MG_PPP_IPV6CP_CFG_NACK, MG_PPP_IPV6CP_CFG_REJECT};
|
||||||
|
struct ipv6cp *ipv6cp = (struct ipv6cp *) (ppp + 1);
|
||||||
|
uint8_t *opts = (uint8_t *) (ipv6cp + 1);
|
||||||
|
// send all handled IPV6CP codes, then 2 random ones
|
||||||
|
if (j >= (sizeof(ipv6cp_codes) / sizeof(ipv6cp_codes[0]) + 2)) j = 0;
|
||||||
|
if (j < (sizeof(ipv6cp_codes) / sizeof(ipv6cp_codes[0]))) ipv6cp->code = ipv6cp_codes[j++];
|
||||||
|
// send valid length, then 2 random lengths
|
||||||
|
if (m >= 3) m = 0;
|
||||||
|
if (m == 0) ipv6cp->len = mg_htons(14);
|
||||||
|
++m;
|
||||||
|
// send valid IFCID option, then 2 random option types
|
||||||
|
if (k >= 3) k = 0;
|
||||||
|
if (k == 0) {
|
||||||
|
static size_t l;
|
||||||
|
opts[0] = MG_PPP_IPV6CP_OPT_IFCID;
|
||||||
|
// for valid option type, send valid length, then random length
|
||||||
|
if (l >= 2) l = 0;
|
||||||
|
if (l == 0) {
|
||||||
|
static size_t o;
|
||||||
|
opts[1] = 10;
|
||||||
|
// for valid option length, send non-zero IFCID, then 2 random IFCIDs
|
||||||
|
if (o >= 3) o = 0;
|
||||||
|
if (o == 0) opts[9] = 1;
|
||||||
|
++o;
|
||||||
|
}
|
||||||
|
++l;
|
||||||
|
}
|
||||||
|
++k;
|
||||||
|
} else if (ppp->proto == mg_htons(MG_PPP_PROTO_IPV6) && len >= sizeof(struct ip6)) {
|
||||||
|
fuzz_ip6(ppp + 1, len, &l3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +356,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
#endif
|
#endif
|
||||||
mg_tcpip_rx(&mif, pkt, size);
|
mg_tcpip_rx(&mif, pkt, size);
|
||||||
|
|
||||||
|
// prior actions may have had consequences on link state
|
||||||
|
mif.state = MG_TCPIP_STATE_READY;
|
||||||
// Test HTTP serving (via our built-in TCP/IP stack)
|
// Test HTTP serving (via our built-in TCP/IP stack)
|
||||||
const char *url = "http://localhost:12345";
|
const char *url = "http://localhost:12345"; // internally resolves
|
||||||
|
// creates a connection and sends SYN via the router, unless OOM
|
||||||
struct mg_connection *c = mg_http_connect(&mgr, url, fn, NULL);
|
struct mg_connection *c = mg_http_connect(&mgr, url, fn, NULL);
|
||||||
mg_iobuf_add(&c->recv, 0, data, size);
|
mg_iobuf_add(&c->recv, 0, data, size);
|
||||||
c->pfn(c, MG_EV_READ, NULL); // manually invoke protocol event handler
|
c->pfn(c, MG_EV_READ, NULL); // manually invoke protocol event handler
|
||||||
|
|||||||
+15
-5
@@ -110,7 +110,8 @@ static void ph(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
(void) c, (void) ev, (void) ev_data;
|
if (ev == MG_EV_ACCEPT) ASSERT(mg_dscp(c, MG_DSCP_AF(4, 1)));
|
||||||
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcpclosure_fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void tcpclosure_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
@@ -158,7 +159,7 @@ static void frag_send_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
} else if (ev == MG_EV_WRITE) {
|
} else if (ev == MG_EV_WRITE) {
|
||||||
// Checking TCP segment sizes (ev_data points to the TCP payload length)
|
// Checking TCP segment sizes (ev_data points to the TCP payload length)
|
||||||
ASSERT(*(int *) ev_data == s_seg_sizes[s_seg_sent++]);
|
ASSERT(*(long *) ev_data == s_seg_sizes[s_seg_sent++]);
|
||||||
}
|
}
|
||||||
(void) c, (void) ev_data;
|
(void) c, (void) ev_data;
|
||||||
}
|
}
|
||||||
@@ -298,7 +299,7 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
|
|||||||
mif->ip = mg_htonl(0x1000000);
|
mif->ip = mg_htonl(0x1000000);
|
||||||
mif->gw = mg_htonl(0x1000000);
|
mif->gw = mg_htonl(0x1000000);
|
||||||
mif->gw_ready = true;
|
mif->gw_ready = true;
|
||||||
mif->mask = 255; // use router, to avoid firing an ARP request
|
mif->mask = mg_htonl(0xff000000); // use router, avoid firing ARP request
|
||||||
mif->state = MG_TCPIP_STATE_READY; // so mg_send() works and DHCP stops
|
mif->state = MG_TCPIP_STATE_READY; // so mg_send() works and DHCP stops
|
||||||
}
|
}
|
||||||
mg_tcpip_init(mgr, mif);
|
mg_tcpip_init(mgr, mif);
|
||||||
@@ -306,7 +307,7 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp,
|
|||||||
// setting the Ethernet header
|
// setting the Ethernet header
|
||||||
memset(e, 0, sizeof(*e));
|
memset(e, 0, sizeof(*e));
|
||||||
memcpy(e->dst, mif->mac, 6 * sizeof(uint8_t));
|
memcpy(e->dst, mif->mac, 6 * sizeof(uint8_t));
|
||||||
e->type = mg_htons(ipp->ip4 != NULL ? 0x800 : 0x86dd);
|
e->type = mg_htons((uint16_t)(ipp->ip4 != NULL ? 0x800 : 0x86dd));
|
||||||
|
|
||||||
// setting the IP header
|
// setting the IP header
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
@@ -499,8 +500,11 @@ static void test_tcp_basics(bool ipv6) {
|
|||||||
if (ipv6) {
|
if (ipv6) {
|
||||||
ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
|
ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
|
||||||
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
|
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
|
||||||
|
ASSERT((uint8_t) (((i6->ver & 15) << 2) | (i6->label[0] >> 6)) ==
|
||||||
|
MG_DSCP_AF(4, 1));
|
||||||
} else {
|
} else {
|
||||||
ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
|
ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
|
||||||
|
ASSERT(i->tos == (MG_DSCP_AF(4, 1) << 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// segment with seq_no way out of window
|
// segment with seq_no way out of window
|
||||||
@@ -1125,8 +1129,11 @@ static void test_tcp(bool ipv6) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void udp_fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void udp_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_READ && c->recv.len == 2 && c->recv.buf[0] == 'p')
|
if (ev == MG_EV_OPEN) {
|
||||||
|
ASSERT(mg_dscp(c, MG_DSCP_AF(4, 1)));
|
||||||
|
} else if (ev == MG_EV_READ && c->recv.len == 2 && c->recv.buf[0] == 'p') {
|
||||||
mg_send(c, "P90", 3);
|
mg_send(c, "P90", 3);
|
||||||
|
}
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1221,9 +1228,12 @@ static void test_udp(bool ipv6) {
|
|||||||
if (ipv6) {
|
if (ipv6) {
|
||||||
ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
|
ASSERT(i6->src[0] == mg_htonll(0x100000000000000) && i6->src[1] == 0 &&
|
||||||
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
|
i6->dst[0] == mg_htonll(0x200000000000000) && i6->dst[1] == 0);
|
||||||
|
ASSERT((uint8_t) (((i6->ver & 15) << 2) | (i6->label[0] >> 6)) ==
|
||||||
|
MG_DSCP_AF(4, 1));
|
||||||
ASSERT(udp6csum_ok(i6, u));
|
ASSERT(udp6csum_ok(i6, u));
|
||||||
} else {
|
} else {
|
||||||
ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
|
ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000));
|
||||||
|
ASSERT(i->tos == (MG_DSCP_AF(4, 1) << 2));
|
||||||
ASSERT(udpcsum_ok(i, u));
|
ASSERT(udpcsum_ok(i, u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-4
@@ -1,7 +1,7 @@
|
|||||||
#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 FETCH_BUF_SIZE (16 * 1024)
|
#define FETCH_BUF_SIZE (32 * 1024)
|
||||||
|
|
||||||
#include "mongoose.c"
|
#include "mongoose.c"
|
||||||
|
|
||||||
@@ -196,6 +196,7 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
mg_mgr_poll(mgr, 0); // update ifp->now to avoid ARP lookup using an old
|
mg_mgr_poll(mgr, 0); // update ifp->now to avoid ARP lookup using an old
|
||||||
// timestamp (from an ancient call in other test)
|
// timestamp (from an ancient call in other test)
|
||||||
|
mg_mgr_poll(mgr, 0); // (prior idling collects lots of frames)
|
||||||
c = mg_http_connect(mgr, url, fcb, &fd);
|
c = mg_http_connect(mgr, url, fcb, &fd);
|
||||||
ASSERT(c != NULL);
|
ASSERT(c != NULL);
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
@@ -355,12 +356,13 @@ static void test_mqtt_connsubpub(struct mg_mgr *mgr) {
|
|||||||
data.url = strdup(MQTT_URL);
|
data.url = strdup(MQTT_URL);
|
||||||
#endif
|
#endif
|
||||||
#endif // MG_TLS
|
#endif // MG_TLS
|
||||||
mg_mgr_poll(mgr, 0); // update interface timing
|
mg_mgr_poll(mgr, 0); // update interface timing (*)
|
||||||
|
mg_mgr_poll(mgr, 0);
|
||||||
s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
|
s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
|
||||||
ASSERT(s_conn != NULL);
|
ASSERT(s_conn != NULL);
|
||||||
for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
|
for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit
|
usleep(5000); // 5 ms (*) See fetch() above for reasons
|
||||||
}
|
}
|
||||||
ASSERT(data.passed);
|
ASSERT(data.passed);
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
@@ -400,11 +402,13 @@ static void test_mqtt_connsubpub(struct mg_mgr *mgr) {
|
|||||||
#endif
|
#endif
|
||||||
#endif // MG_TLS
|
#endif // MG_TLS
|
||||||
data.passed = false;
|
data.passed = false;
|
||||||
|
mg_mgr_poll(mgr, 0); // update interface timing (*)
|
||||||
|
mg_mgr_poll(mgr, 0);
|
||||||
s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
|
s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
|
||||||
ASSERT(s_conn != NULL);
|
ASSERT(s_conn != NULL);
|
||||||
for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
|
for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit
|
usleep(5000); // 5 ms (*) See fetch() above for reasons
|
||||||
}
|
}
|
||||||
ASSERT(data.passed);
|
ASSERT(data.passed);
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#ifndef __OpenBSD__
|
#ifndef __OpenBSD__
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
#include <net/if_types.h>
|
#include <net/if_types.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int s_signo;
|
static volatile sig_atomic_t s_signo;
|
||||||
void signal_handler(int signo) {
|
void signal_handler(int signo) {
|
||||||
s_signo = signo;
|
s_signo = signo;
|
||||||
}
|
}
|
||||||
@@ -49,6 +50,8 @@ int main(void) {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
printf("Opened TAP interface: %s\n", iface);
|
printf("Opened TAP interface: %s\n", iface);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
int sockfd;
|
int sockfd;
|
||||||
struct sockaddr_in tap_addr, port_addr;
|
struct sockaddr_in tap_addr, port_addr;
|
||||||
@@ -97,8 +100,9 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (result >= 0);
|
} while (result >= 0 && s_signo == 0);
|
||||||
printf("looks like we got an error here... should I quit ? Waiting for CTRL-C\n");
|
if (result < 0)
|
||||||
|
printf("looks like we got an error here... should I quit ? Waiting for CTRL-C\n");
|
||||||
}
|
}
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|||||||
+431
@@ -0,0 +1,431 @@
|
|||||||
|
#define MG_ENABLE_TCPIP 1
|
||||||
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||||
|
|
||||||
|
#include "mongoose.c"
|
||||||
|
#include "driver_mock.c"
|
||||||
|
|
||||||
|
static int s_num_tests = 0;
|
||||||
|
static bool s_error = false;
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
static int s_abort = 0;
|
||||||
|
#define ABORT() ++s_abort, s_error = true
|
||||||
|
#else
|
||||||
|
#ifdef NO_SLEEP_ABORT
|
||||||
|
#define ABORT() abort()
|
||||||
|
#else
|
||||||
|
#define ABORT() \
|
||||||
|
sleep(2); /* 2s, GH print reason */ \
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ASSERT(expr) \
|
||||||
|
do { \
|
||||||
|
s_num_tests++; \
|
||||||
|
if (!(expr)) { \
|
||||||
|
printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \
|
||||||
|
fflush(stdout); \
|
||||||
|
ABORT(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static const uint8_t s_mac[6] = {2, 3, 4, 5, 6, 7};
|
||||||
|
static const uint8_t s_src[6] = {8, 9, 10, 11, 12, 13};
|
||||||
|
|
||||||
|
static uint8_t s_tx[2048];
|
||||||
|
|
||||||
|
static void init_if(struct mg_tcpip_if *ifp, enum mg_l2type type) {
|
||||||
|
memset(ifp, 0, sizeof(*ifp));
|
||||||
|
memcpy(ifp->mac, s_mac, sizeof(s_mac));
|
||||||
|
ifp->driver = &mg_tcpip_driver_mock;
|
||||||
|
ifp->l2type = type;
|
||||||
|
ifp->tx.buf = (char *) s_tx;
|
||||||
|
ifp->tx.len = sizeof(s_tx);
|
||||||
|
mg_l2_init(ifp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mk_ppp(uint8_t *buf, uint16_t proto, const void *payload,
|
||||||
|
size_t len) {
|
||||||
|
struct hdlc_ *hdlc = (struct hdlc_ *) buf;
|
||||||
|
struct ppp *ppp = (struct ppp *) (hdlc + 1);
|
||||||
|
hdlc->addr = MG_PPP_ADDR;
|
||||||
|
hdlc->ctrl = MG_PPP_CTRL;
|
||||||
|
ppp->proto = mg_htons(proto);
|
||||||
|
memcpy(ppp + 1, payload, len);
|
||||||
|
memset((uint8_t *) (ppp + 1) + len, 0, 2);
|
||||||
|
return sizeof(*hdlc) + sizeof(*ppp) + len + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *ppp_payload(uint8_t *frame) {
|
||||||
|
return (struct ppp *) ((struct hdlc_ *) frame + 1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fcs(void) {
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
uint8_t frame[] = {0xff, 0x03, 0x00, 0x21, 1, 2, 3, 4, 0, 0};
|
||||||
|
uint8_t fcs[] = {0xae, 0x77};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
// PPP trailer stores the FCS bytes in the proper order
|
||||||
|
ASSERT(mg_l2_ppp_trailer(&ifp, 4, frame + sizeof(frame) - sizeof(fcs)) ==
|
||||||
|
sizeof(frame));
|
||||||
|
ASSERT(memcmp(frame + sizeof(frame) - sizeof(fcs), fcs, sizeof(fcs)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mk_pppoe_disc(uint8_t *buf, uint8_t code, uint16_t id,
|
||||||
|
const void *payload, size_t len) {
|
||||||
|
struct eth *eth = (struct eth *) buf;
|
||||||
|
struct pppoe *pppoe = (struct pppoe *) (eth + 1);
|
||||||
|
memcpy(eth->dst, s_mac, sizeof(eth->dst));
|
||||||
|
memcpy(eth->src, s_src, sizeof(eth->src));
|
||||||
|
eth->type = mg_htons(0x8863);
|
||||||
|
pppoe->vertype = 0x11;
|
||||||
|
pppoe->code = code;
|
||||||
|
pppoe->id = mg_htons(id);
|
||||||
|
pppoe->len = mg_htons((uint16_t) len);
|
||||||
|
if (len > 0) memcpy(pppoe + 1, payload, len);
|
||||||
|
return sizeof(*eth) + sizeof(*pppoe) + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mk_pppoe_sess(uint8_t *buf, uint16_t id, uint16_t proto,
|
||||||
|
const void *payload, size_t len) {
|
||||||
|
struct eth *eth = (struct eth *) buf;
|
||||||
|
struct pppoe *pppoe = (struct pppoe *) (eth + 1);
|
||||||
|
struct ppp *ppp = (struct ppp *) (pppoe + 1);
|
||||||
|
memcpy(eth->dst, s_mac, sizeof(eth->dst));
|
||||||
|
memcpy(eth->src, s_src, sizeof(eth->src));
|
||||||
|
eth->type = mg_htons(0x8864);
|
||||||
|
pppoe->vertype = 0x11;
|
||||||
|
pppoe->code = 0;
|
||||||
|
pppoe->id = mg_htons(id);
|
||||||
|
pppoe->len = mg_htons((uint16_t) (sizeof(*ppp) + len));
|
||||||
|
ppp->proto = mg_htons(proto);
|
||||||
|
memcpy(ppp + 1, payload, len);
|
||||||
|
return sizeof(*eth) + sizeof(*pppoe) + sizeof(*ppp) + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_lcp(void) {
|
||||||
|
uint8_t frame[64];
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
|
||||||
|
{ // LCP ACK brings PPP link up
|
||||||
|
struct lcp lcp = {MG_PPP_LCP_CFG_ACK, 1, mg_htons(sizeof(lcp))};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = false;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_LCP, &lcp, sizeof(lcp)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_lcpup == true);
|
||||||
|
}
|
||||||
|
{ // Short LCP frame is discarded
|
||||||
|
uint8_t lcp[] = {MG_PPP_LCP_CFG_ACK, 1};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = false;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_LCP, lcp, sizeof(lcp)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_lcpup == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ipcp(void) {
|
||||||
|
uint8_t frame[64];
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
|
||||||
|
{ // IPCP request with peer IP address is ACKed
|
||||||
|
uint8_t ipcp[] = {MG_PPP_IPCP_CFG_REQ, 1, 0, 10,
|
||||||
|
MG_PPP_IPCP_OPT_IPADDR, 6, 192, 0, 2, 1};
|
||||||
|
uint32_t peer;
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = true;
|
||||||
|
memcpy(&peer, ipcp + 6, sizeof(peer));
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IPCP, ipcp, sizeof(ipcp)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(ifp.gw == peer);
|
||||||
|
ASSERT(((struct ipcp *) ppp_payload(frame))->code == MG_PPP_IPCP_CFG_ACK);
|
||||||
|
ASSERT(ifp.nsent == 2);
|
||||||
|
}
|
||||||
|
{ // IPCP request without peer IP address is rejected
|
||||||
|
uint8_t ipcp[] = {MG_PPP_IPCP_CFG_REQ, 1, 0, 4};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IPCP, ipcp, sizeof(ipcp)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(ifp.gw == 0);
|
||||||
|
ASSERT(((struct ipcp *) ppp_payload(frame))->code ==
|
||||||
|
MG_PPP_IPCP_CFG_REJECT);
|
||||||
|
ASSERT(ifp.nsent == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MG_ENABLE_IPV6
|
||||||
|
static void test_ipv6cp(void) {
|
||||||
|
uint8_t frame[64];
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
|
||||||
|
{ // IPv6CP request with interface ID is ACKed
|
||||||
|
uint8_t ipv6cp[] = {MG_PPP_IPV6CP_CFG_REQ, 1, 0, 14,
|
||||||
|
MG_PPP_IPV6CP_OPT_IFCID, 10, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IPV6CP, ipv6cp,
|
||||||
|
sizeof(ipv6cp)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(memcmp(&((struct mg_l2addr *) ifp.gw6mac)->addr.ieee64, ipv6cp + 6,
|
||||||
|
8) == 0);
|
||||||
|
ASSERT(((struct ipv6cp *) ppp_payload(frame))->code ==
|
||||||
|
MG_PPP_IPV6CP_CFG_ACK);
|
||||||
|
ASSERT(ifp.nsent == 2);
|
||||||
|
}
|
||||||
|
{ // IPv6CP request with zero interface ID is rejected
|
||||||
|
uint8_t ipv6cp[] = {MG_PPP_IPV6CP_CFG_REQ, 1, 0, 14,
|
||||||
|
MG_PPP_IPV6CP_OPT_IFCID, 10, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IPV6CP, ipv6cp,
|
||||||
|
sizeof(ipv6cp)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(((struct ipv6cp *) ppp_payload(frame))->code ==
|
||||||
|
MG_PPP_IPV6CP_CFG_REJECT);
|
||||||
|
ASSERT(ifp.nsent == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void test_ppp(void) {
|
||||||
|
uint8_t frame[96];
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
|
||||||
|
{ // IPv4 packet is accepted after LCP is up
|
||||||
|
uint8_t payload[sizeof(struct ip)];
|
||||||
|
memset(payload, 0, sizeof(payload));
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
MG_DEBUG(("MTU: %u, frame size: %u", ifp.l2mtu, ifp.framesize));
|
||||||
|
ASSERT(ifp.l2mtu != 0);
|
||||||
|
ASSERT(ifp.framesize != 0);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IP, payload, sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV4);
|
||||||
|
ASSERT(pay.len == sizeof(payload));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // IPv4 packet is discarded while LCP is down
|
||||||
|
uint8_t payload[sizeof(struct ip)];
|
||||||
|
memset(payload, 0, sizeof(payload));
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = false;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IP, payload, sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
}
|
||||||
|
#if MG_ENABLE_IPV6
|
||||||
|
{ // IPv6 packet is accepted after LCP is up
|
||||||
|
uint8_t payload[sizeof(struct ip6)];
|
||||||
|
memset(payload, 0, sizeof(payload));
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IPV6, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV6);
|
||||||
|
ASSERT(pay.len == sizeof(payload));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // IPv6 packet is discarded while LCP is down
|
||||||
|
uint8_t payload[sizeof(struct ip6)];
|
||||||
|
memset(payload, 0, sizeof(payload));
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = false;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, MG_PPP_PROTO_IPV6, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{ // Unknown protocol is rejected after LCP is up
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, 0x1234, payload, sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(ifp.nsent == 1);
|
||||||
|
}
|
||||||
|
{ // Unknown protocol is discarded while LCP is down
|
||||||
|
uint8_t payload[] = {1, 2, 3, 4};
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPP);
|
||||||
|
s_lcpup = false;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_ppp(frame, 0x1234, payload, sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(ifp.nsent == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pppoe(void) {
|
||||||
|
uint8_t frame[96];
|
||||||
|
struct mg_tcpip_if ifp;
|
||||||
|
struct mg_str raw, pay;
|
||||||
|
enum mg_l2proto proto;
|
||||||
|
|
||||||
|
{ // PADO moves discovery to request state
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
MG_DEBUG(("MTU: %u, frame size: %u", ifp.l2mtu, ifp.framesize));
|
||||||
|
ASSERT(ifp.l2mtu != 0);
|
||||||
|
ASSERT(ifp.framesize != 0);
|
||||||
|
s_state = MG_PPPoE_ST_DISC;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_disc(frame, MG_PPPoE_PADO, 0, NULL, 0));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_state == MG_PPPoE_ST_REQ);
|
||||||
|
ASSERT(ifp.nsent == 1);
|
||||||
|
}
|
||||||
|
{ // PADO is ignored outside discovery state
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
s_state = MG_PPPoE_ST_REQ;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_disc(frame, MG_PPPoE_PADO, 0, NULL, 0));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_state == MG_PPPoE_ST_REQ);
|
||||||
|
ASSERT(ifp.nsent == 0);
|
||||||
|
}
|
||||||
|
{ // PADS establishes the session
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
s_state = MG_PPPoE_ST_REQ;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_disc(frame, MG_PPPoE_PADS, 0x1234, NULL, 0));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_state == MG_PPPoE_ST_SESS);
|
||||||
|
ASSERT(s_id == mg_htons(0x1234));
|
||||||
|
}
|
||||||
|
{ // PADS is ignored outside request state
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
s_state = MG_PPPoE_ST_DISC;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_disc(frame, MG_PPPoE_PADS, 0x1234, NULL, 0));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_state == MG_PPPoE_ST_DISC);
|
||||||
|
}
|
||||||
|
{ // PADT matching the session tears it down
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
s_state = MG_PPPoE_ST_SESS;
|
||||||
|
s_id = mg_htons(0x1234);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_disc(frame, MG_PPPoE_PADT, 0x1234, NULL, 0));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_state == MG_PPPoE_ST_DISC);
|
||||||
|
ASSERT(s_id == 0);
|
||||||
|
ASSERT(s_lcpup == false);
|
||||||
|
}
|
||||||
|
{ // PADT for a different session is ignored
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
s_state = MG_PPPoE_ST_SESS;
|
||||||
|
s_id = mg_htons(0x4321);
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_disc(frame, MG_PPPoE_PADT, 0x1234, NULL, 0));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
ASSERT(s_state == MG_PPPoE_ST_SESS);
|
||||||
|
ASSERT(s_id == mg_htons(0x4321));
|
||||||
|
ASSERT(s_lcpup == true);
|
||||||
|
}
|
||||||
|
{ // PPPoE session packet is accepted in session state
|
||||||
|
uint8_t payload[sizeof(struct ip)];
|
||||||
|
memset(payload, 0, sizeof(payload));
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
s_state = MG_PPPoE_ST_SESS;
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_sess(frame, 0x1234, MG_PPP_PROTO_IP, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == true);
|
||||||
|
ASSERT(proto == MG_TCPIP_L2PROTO_IPV4);
|
||||||
|
ASSERT(pay.len == sizeof(payload));
|
||||||
|
ASSERT(memcmp(pay.buf, payload, sizeof(payload)) == 0);
|
||||||
|
}
|
||||||
|
{ // PPPoE session packet is discarded outside session state
|
||||||
|
uint8_t payload[sizeof(struct ip)];
|
||||||
|
memset(payload, 0, sizeof(payload));
|
||||||
|
init_if(&ifp, MG_TCPIP_L2_PPPoE);
|
||||||
|
s_state = MG_PPPoE_ST_REQ;
|
||||||
|
s_lcpup = true;
|
||||||
|
raw = mg_str_n((char *) frame,
|
||||||
|
mk_pppoe_sess(frame, 0x1234, MG_PPP_PROTO_IP, payload,
|
||||||
|
sizeof(payload)));
|
||||||
|
proto = MG_TCPIP_L2PROTO_ARP;
|
||||||
|
ASSERT(mg_l2_rx(&ifp, &proto, &pay, &raw) == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DASHBOARD(x) \
|
||||||
|
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
s_error = false;
|
||||||
|
test_fcs();
|
||||||
|
DASHBOARD("fcs");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
test_lcp();
|
||||||
|
DASHBOARD("lcp");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
test_ipcp();
|
||||||
|
DASHBOARD("ipcp");
|
||||||
|
|
||||||
|
#if MG_ENABLE_IPV6
|
||||||
|
s_error = false;
|
||||||
|
test_ipv6cp();
|
||||||
|
DASHBOARD("ipv6cp");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
test_ppp();
|
||||||
|
DASHBOARD("ppp");
|
||||||
|
|
||||||
|
s_error = false;
|
||||||
|
test_pppoe();
|
||||||
|
printf("HEALTH_DASHBOARD\t\"pppoe\": %s\n", s_error ? "false" : "true");
|
||||||
|
|
||||||
|
#ifdef NO_ABORT
|
||||||
|
if (s_abort != 0) return EXIT_FAILURE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("SUCCESS. Total tests: %d\n", s_num_tests);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# PPP options for the PPPoE server
|
||||||
|
noauth
|
||||||
|
10.0.0.1:10.67.15.1
|
||||||
|
lcp-echo-interval 1
|
||||||
|
lcp-echo-failure 2
|
||||||
|
+ipv6
|
||||||
|
ipv6cp-use-ipaddr
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#define MG_ENABLE_TCPIP 1
|
||||||
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_tun.h>
|
||||||
|
#else
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_tun.h>
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include "mongoose.c"
|
||||||
|
|
||||||
|
#define WAIT_MS 5000
|
||||||
|
|
||||||
|
static size_t tap_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
ssize_t received = read(*(int *) ifp->driver_data, buf, len);
|
||||||
|
usleep(1);
|
||||||
|
if (received < 0) return 0;
|
||||||
|
return (size_t) received;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t tap_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
|
ssize_t res = write(*(int *) ifp->driver_data, buf, len);
|
||||||
|
if (res < 0) {
|
||||||
|
MG_ERROR(("tap_tx failed: %d", errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (size_t) res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tap_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||||
|
return s1 && ifp->driver_data ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int res = EXIT_SUCCESS;
|
||||||
|
const char *iface = "tap0";
|
||||||
|
const char *mac = "02:00:01:02:03:78";
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
const char *tuntap_device = "/dev/net/tun";
|
||||||
|
#else
|
||||||
|
const char *tuntap_device = "/dev/tap0";
|
||||||
|
#endif
|
||||||
|
uint16_t vlan_id = 3;
|
||||||
|
int fd, i;
|
||||||
|
struct ifreq ifr;
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_tcpip_driver driver;
|
||||||
|
struct mg_tcpip_if mif;
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-i") == 0 && i + 1 < argc) {
|
||||||
|
iface = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-mac") == 0 && i + 1 < argc) {
|
||||||
|
mac = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-v") == 0 && i + 1 < argc) {
|
||||||
|
mg_log_set(atoi(argv[++i]));
|
||||||
|
} else if (strcmp(argv[i], "-vlan") == 0 && i + 1 < argc) {
|
||||||
|
vlan_id = (uint16_t) atoi(argv[++i]);
|
||||||
|
} else {
|
||||||
|
MG_ERROR(("unknown option %s", argv[i]));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(tuntap_device, O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
MG_ERROR(("Failed to open TAP device: %d", errno));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
|
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||||
|
if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
|
||||||
|
MG_ERROR(("Failed to setup TAP interface: %s", ifr.ifr_name));
|
||||||
|
close(fd);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ifr.ifr_flags = (short) (IFF_UP | IFF_BROADCAST | IFF_MULTICAST);
|
||||||
|
if (ioctl(fd, TUNSIFMODE, (void *) &ifr) < 0) {
|
||||||
|
MG_ERROR(("Failed to setup TAP interface: %s", ifr.ifr_name));
|
||||||
|
close(fd);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
|
||||||
|
memset(&driver, 0, sizeof(driver));
|
||||||
|
driver.tx = tap_tx;
|
||||||
|
driver.rx = tap_rx;
|
||||||
|
driver.poll = tap_poll;
|
||||||
|
|
||||||
|
memset(&mif, 0, sizeof(mif));
|
||||||
|
mif.driver = &driver;
|
||||||
|
mif.driver_data = &fd;
|
||||||
|
#if MG_ENABLE_IPV6
|
||||||
|
mif.ip6[0] = mg_htonll(0x100000000000000);
|
||||||
|
mif.prefix[0] = 1;
|
||||||
|
mif.prefix_len = 64;
|
||||||
|
mif.gw6[0] = mg_htonll(0x100000000000000);
|
||||||
|
mif.gw6_ready = true;
|
||||||
|
#endif
|
||||||
|
mif.l2type = MG_TCPIP_L2_PPPoE;
|
||||||
|
mif.l2data.eth.vlan_id = vlan_id;
|
||||||
|
sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1],
|
||||||
|
&mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]);
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
mg_tcpip_init(&mgr, &mif);
|
||||||
|
MG_INFO(("Opened TAP interface %s, VLAN %u", iface, vlan_id));
|
||||||
|
|
||||||
|
for (i = 0; i < WAIT_MS / 10 && (mif.state != MG_TCPIP_STATE_READY
|
||||||
|
#if MG_ENABLE_IPV6
|
||||||
|
|| mif.state6 != MG_TCPIP_STATE_READY
|
||||||
|
#endif
|
||||||
|
); i++) {
|
||||||
|
mg_mgr_poll(&mgr, 10);
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mif.state != MG_TCPIP_STATE_READY) {
|
||||||
|
MG_ERROR(("IP FAILED %u", mif.state));
|
||||||
|
res = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#if MG_ENABLE_IPV6
|
||||||
|
if (mif.state6 != MG_TCPIP_STATE_READY) {
|
||||||
|
MG_ERROR(("IPv6 FAILED %u", mif.state6));
|
||||||
|
res = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
close(fd);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@@ -23,18 +23,20 @@ echo
|
|||||||
|
|
||||||
# Package installation
|
# Package installation
|
||||||
echo "Package installation"
|
echo "Package installation"
|
||||||
sudo apt-get -y install isc-dhcp-server net-tools radvd
|
sudo apt-get -y install isc-dhcp-server net-tools radvd ppp pppoe
|
||||||
sudo systemctl stop radvd # just in case they think it is cool to start it
|
sudo systemctl stop radvd # they think it is cool to start it
|
||||||
# sudo apt-get -y install build-essential sshpass
|
# sudo apt-get -y install build-essential sshpass
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "Network configuration script: Bridge"
|
echo "Network configuration script: Bridge"
|
||||||
sudo ip link add $BRIDGE type bridge # Create brige
|
sudo ip link add $BRIDGE type bridge # Create brige
|
||||||
sudo ifconfig $BRIDGE $BRIDGE_IP netmask $BRIDGE_MASK up
|
sudo ifconfig $BRIDGE $BRIDGE_IP netmask $BRIDGE_MASK up # its IP
|
||||||
sudo ifconfig $BRIDGE:1 $GATEWAY_IP netmask $BRIDGE_MASK up
|
sudo ifconfig $BRIDGE:1 $GATEWAY_IP netmask $BRIDGE_MASK up # IP announced by DHCP as gateway
|
||||||
sudo ifconfig $BRIDGE:2 $HOST_IP netmask $BRIDGE_MASK up
|
sudo ifconfig $BRIDGE:2 $HOST_IP netmask $BRIDGE_MASK up # IP exported for LAN (dut) connections
|
||||||
sudo ip -6 addr add $BRIDGE_IPV6/64 dev $BRIDGE
|
sudo ip -6 addr add $BRIDGE_IPV6/64 dev $BRIDGE # its IPv6
|
||||||
sudo ip -6 addr add $HOST_IPV6/64 dev $BRIDGE:1
|
sudo ip -6 addr add $HOST_IPV6/64 dev $BRIDGE:1 # IPv6 exported for LAN (dut) connections
|
||||||
|
sudo ip link add link $BRIDGE name $BRIDGE.3 type vlan id 3 # Create VLAN for PPPoE+VLAN tests
|
||||||
|
sudo ifconfig $BRIDGE.3 up
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "Network configuration script: TAP"
|
echo "Network configuration script: TAP"
|
||||||
@@ -73,10 +75,20 @@ sudo cp test/dhcpd.conf /etc/dhcp/dhcpd.conf
|
|||||||
sudo chmod a+w /var/lib/dhcp/*
|
sudo chmod a+w /var/lib/dhcp/*
|
||||||
sudo dhcpd mg_bridge0 &
|
sudo dhcpd mg_bridge0 &
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
# Setup Router Advertising server
|
||||||
echo "radvd listening at $BRIDGE_IPV6"
|
echo "radvd listening at $BRIDGE_IPV6"
|
||||||
sudo ip link set $BRIDGE up # there's no physical port, so we need to set link up for radvd to work
|
sudo ip link set $BRIDGE up # there's no physical port, so we need to set link up for radvd to work
|
||||||
sudo radvd -n -d 5 -C test/radvd.conf &
|
sudo radvd -n -d 5 -C test/radvd.conf &
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
# Setup PPPoE server
|
||||||
|
echo "pppoe-server listening at $BRIDGE.3"
|
||||||
|
printf "lock\n" | sudo tee /etc/ppp/options
|
||||||
|
sudo pppoe-server -F -D -O test/pppoe-server-options -I $BRIDGE.3 &
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Export generic server addresses so DUTs can connect to them
|
||||||
echo "Host listening at $HOST_IP and $HOST_IPV6"
|
echo "Host listening at $HOST_IP and $HOST_IPV6"
|
||||||
export HOST_IP
|
export HOST_IP
|
||||||
export HOST_IPV6
|
export HOST_IPV6
|
||||||
@@ -88,8 +100,8 @@ wget https://cesanta.com/robots.txt
|
|||||||
echo robots.txt:
|
echo robots.txt:
|
||||||
cat robots.txt
|
cat robots.txt
|
||||||
rm robots.txt
|
rm robots.txt
|
||||||
wget https://ipv6.google.com/
|
wget https://ipv6.google.com/ || true
|
||||||
rm index.html
|
rm index.html || true
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Confirm OK
|
# Confirm OK
|
||||||
|
|||||||
+206
-15
@@ -301,6 +301,13 @@ static void test_base64(void) {
|
|||||||
ASSERT(mg_base64_decode("AAA=", 4, buf, 2) == 0);
|
ASSERT(mg_base64_decode("AAA=", 4, buf, 2) == 0);
|
||||||
ASSERT(mg_base64_decode("AAA=", 4, buf, 3) == 0);
|
ASSERT(mg_base64_decode("AAA=", 4, buf, 3) == 0);
|
||||||
ASSERT(mg_base64_decode("AAA=", 4, buf, 4) == 2);
|
ASSERT(mg_base64_decode("AAA=", 4, buf, 4) == 2);
|
||||||
|
|
||||||
|
ASSERT(mg_base64url_encode((uint8_t *) "???", 3, buf, sizeof(buf)) == 4);
|
||||||
|
ASSERT(strcmp(buf, "Pz8_") == 0);
|
||||||
|
ASSERT(mg_base64url_decode("Pz8_", 4, buf, sizeof(buf)) == 3);
|
||||||
|
ASSERT(strcmp(buf, "???") == 0);
|
||||||
|
ASSERT(mg_base64url_decode("Pz8/", 4, buf, sizeof(buf)) == 0);
|
||||||
|
ASSERT(mg_base64url_decode("A", 1, buf, sizeof(buf)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_iobuf(void) {
|
static void test_iobuf(void) {
|
||||||
@@ -339,7 +346,7 @@ static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
int64_t received = *(int64_t *) ev_data;
|
int64_t received = *(int64_t *) ev_data;
|
||||||
*(int64_t *) c->fn_data = received;
|
*(int64_t *) c->fn_data = received;
|
||||||
MG_DEBUG(("got time: %lld", received));
|
MG_DEBUG(("got time: %lld", received));
|
||||||
#if MG_ARCH == MG_ARCH_UNIX
|
#if MG_ARCH == MG_ARCH_UNIX && defined(__STDC__) && defined(__STDC_VERSION__)
|
||||||
struct timeval tv = {0, 0};
|
struct timeval tv = {0, 0};
|
||||||
gettimeofday(&tv, 0);
|
gettimeofday(&tv, 0);
|
||||||
int64_t ms = (int64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
int64_t ms = (int64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||||
@@ -1462,7 +1469,7 @@ static void test_tls(void) {
|
|||||||
const char *url = "https://localhost:12347";
|
const char *url = "https://localhost:12347";
|
||||||
char buf[FETCH_BUF_SIZE];
|
char buf[FETCH_BUF_SIZE];
|
||||||
struct mg_tls_opts opts;
|
struct mg_tls_opts opts;
|
||||||
struct mg_str data = mg_unpacked("/Makefile");
|
struct mg_str data = mg_unpacked("/fuzz.c");
|
||||||
char bigdata[FETCH_BUF_SIZE - 256]; // leave extra room
|
char bigdata[FETCH_BUF_SIZE - 256]; // leave extra room
|
||||||
struct mg_str bd;
|
struct mg_str bd;
|
||||||
ASSERT(data.buf != NULL && data.len > 0);
|
ASSERT(data.buf != NULL && data.len > 0);
|
||||||
@@ -1505,6 +1512,7 @@ static void test_tls(void) {
|
|||||||
// otherwise it will end with 200 and shorter file contents
|
// otherwise it will end with 200 and shorter file contents
|
||||||
ASSERT(fetch(&mgr, buf, "https://localhost:8443",
|
ASSERT(fetch(&mgr, buf, "https://localhost:8443",
|
||||||
"GET /thefile HTTP/1.0\n\n") == 200);
|
"GET /thefile HTTP/1.0\n\n") == 200);
|
||||||
|
data = mg_unpacked("/Makefile");
|
||||||
ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile
|
ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile
|
||||||
ASSERT(system("killall tls_multirec/server") == 0);
|
ASSERT(system("killall tls_multirec/server") == 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -1612,9 +1620,8 @@ static void test_http_client(void) {
|
|||||||
opts.name = mg_url_host(url);
|
opts.name = mg_url_host(url);
|
||||||
|
|
||||||
// Test empty CA
|
// Test empty CA
|
||||||
// Disable mbedTLS: https://github.com/Mbed-TLS/mbedtls/issues/7075
|
|
||||||
#if MG_TLS != MG_TLS_MBED
|
|
||||||
opts.ca = mg_str("");
|
opts.ca = mg_str("");
|
||||||
|
opts.name = mg_url_host(url);
|
||||||
c = mg_http_connect(&mgr, url, f3, &ok);
|
c = mg_http_connect(&mgr, url, f3, &ok);
|
||||||
mg_tls_init(c, &opts);
|
mg_tls_init(c, &opts);
|
||||||
ok = 0;
|
ok = 0;
|
||||||
@@ -1622,7 +1629,21 @@ static void test_http_client(void) {
|
|||||||
MG_INFO(("OK: %d", ok));
|
MG_INFO(("OK: %d", ok));
|
||||||
ASSERT(ok == 200);
|
ASSERT(ok == 200);
|
||||||
mg_mgr_poll(&mgr, 1);
|
mg_mgr_poll(&mgr, 1);
|
||||||
|
// Make host validation fail
|
||||||
|
c = mg_http_connect(&mgr, url, f3, &ok);
|
||||||
|
ASSERT(c != NULL);
|
||||||
|
opts.name = mg_str("dummy"); // Set some invalid hostname value
|
||||||
|
mg_tls_init(c, &opts);
|
||||||
|
ok = 0;
|
||||||
|
for (i = 0; i < 500 && ok <= 0; i++) mg_mgr_poll(&mgr, 10);
|
||||||
|
MG_INFO(("OK: %d", ok));
|
||||||
|
#if MG_TLS != MG_TLS_MBED || !defined(MBEDTLS_VERSION_NUMBER) || \
|
||||||
|
MBEDTLS_VERSION_NUMBER < 0x03030000 || \
|
||||||
|
MBEDTLS_VERSION_NUMBER >= 0x04000000
|
||||||
|
// Ignore result for 3.3.0 <= mbedTLS < 4.0.0 : https://github.com/Mbed-TLS/mbedtls/issues/7075
|
||||||
|
ASSERT(ok == 777);
|
||||||
#endif
|
#endif
|
||||||
|
mg_mgr_poll(&mgr, 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
@@ -2308,6 +2329,19 @@ static void test_str(void) {
|
|||||||
ASSERT(chkdbl(mg_str("123e-3"), 0.123));
|
ASSERT(chkdbl(mg_str("123e-3"), 0.123));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(NO_ZU_TEST) || (defined(_MSC_VER) && _MSC_VER < 1700)
|
||||||
|
// VC98 does not have '%zu', some old compilers may also not support it
|
||||||
|
#else
|
||||||
|
ASSERT(sn("%zu", 0));
|
||||||
|
ASSERT(sn("%zu", -1));
|
||||||
|
ASSERT(sn("%zu", -2));
|
||||||
|
ASSERT(sn("%zd", 123));
|
||||||
|
#if !defined(_MSC_VER)
|
||||||
|
ASSERT(sn("%zd", -3)); // MSVC casts to unsigned
|
||||||
|
#endif
|
||||||
|
ASSERT(sn("%zu", 0x1234));
|
||||||
|
#endif
|
||||||
|
|
||||||
ASSERT(sn("%d", 0));
|
ASSERT(sn("%d", 0));
|
||||||
ASSERT(sn("%d", 1));
|
ASSERT(sn("%d", 1));
|
||||||
ASSERT(sn("%d", -1));
|
ASSERT(sn("%d", -1));
|
||||||
@@ -2759,6 +2793,11 @@ static void test_util(void) {
|
|||||||
((uint8_t *) &ipv4)[2] == 0x45);
|
((uint8_t *) &ipv4)[2] == 0x45);
|
||||||
ASSERT(MG_LOAD_BE24(&ipv4) == 0xef2345);
|
ASSERT(MG_LOAD_BE24(&ipv4) == 0xef2345);
|
||||||
|
|
||||||
|
ASSERT(mg_timegm(2023, 1, 1, 0, 0, 0) == 1672531200);
|
||||||
|
ASSERT(mg_timegm(2025, 12, 31, 23, 59, 59) == 1767225599);
|
||||||
|
ASSERT(mg_timegm(2024, 2, 1, 0, 0, 0) == 1706745600);
|
||||||
|
ASSERT(mg_timegm(2000, 4, 1, 0, 0, 0) == 954547200);
|
||||||
|
|
||||||
memcpy(&ipv3, d64, sizeof(ipv3));
|
memcpy(&ipv3, d64, sizeof(ipv3));
|
||||||
#if defined(_MSC_VER) && _MSC_VER < 1700
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
||||||
// VC98 doesn't suppport LL suffix
|
// VC98 doesn't suppport LL suffix
|
||||||
@@ -2816,6 +2855,13 @@ static void test_util(void) {
|
|||||||
e = "\x20\x01\x48\x60\x48\x60\x00\x00\x00\x00\x00\x00\x00\x00\x88\x88";
|
e = "\x20\x01\x48\x60\x48\x60\x00\x00\x00\x00\x00\x00\x00\x00\x88\x88";
|
||||||
ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
|
ASSERT(memcmp(&a.addr.ip6, e, sizeof(a.addr.ip6)) == 0);
|
||||||
|
|
||||||
|
ASSERT(mg_aton(mg_str("12345::1"), &a) == false);
|
||||||
|
ASSERT(mg_aton(mg_str("1:::2"), &a) == false);
|
||||||
|
ASSERT(mg_aton(mg_str("1::2::3"), &a) == false);
|
||||||
|
ASSERT(mg_aton(mg_str("1:2:3:4:5:6:7"), &a) == false);
|
||||||
|
ASSERT(mg_aton(mg_str("1:2:3:4:5:6:7:8:9"), &a) == false);
|
||||||
|
ASSERT(mg_aton(mg_str("1:2:3:4:5:6:7:g"), &a) == false);
|
||||||
|
|
||||||
ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0);
|
ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0);
|
||||||
ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0);
|
ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0);
|
||||||
ASSERT(mg_url_decode("a=%1", 4, buf, sizeof(buf), 0) < 0);
|
ASSERT(mg_url_decode("a=%1", 4, buf, sizeof(buf), 0) < 0);
|
||||||
@@ -2988,11 +3034,18 @@ static void test_util(void) {
|
|||||||
static void test_crc32(void) {
|
static void test_crc32(void) {
|
||||||
// echo -n aaa | cksum -o3
|
// echo -n aaa | cksum -o3
|
||||||
ASSERT(mg_crc32(0, 0, 0) == 0);
|
ASSERT(mg_crc32(0, 0, 0) == 0);
|
||||||
ASSERT(mg_crc32(0, "a", 1) == 3904355907);
|
ASSERT(mg_crc32(0, "a", 1) == 3904355907U);
|
||||||
ASSERT(mg_crc32(0, "abc", 3) == 891568578);
|
ASSERT(mg_crc32(0, "abc", 3) == 891568578);
|
||||||
ASSERT(mg_crc32(mg_crc32(0, "ab", 2), "c", 1) == 891568578);
|
ASSERT(mg_crc32(mg_crc32(0, "ab", 2), "c", 1) == 891568578);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_crc16(void) {
|
||||||
|
ASSERT(mg_crc16(0, 0, 0) == 0);
|
||||||
|
ASSERT(mg_crc16(0, "a", 1) == 0x82f7);
|
||||||
|
ASSERT(mg_crc16(0, "abc", 3) == 0x9e25);
|
||||||
|
ASSERT(mg_crc16(mg_crc16(0, "ab", 2), "c", 1) == 0x9e25);
|
||||||
|
}
|
||||||
|
|
||||||
static void us(struct mg_connection *c, int ev, void *ev_data) {
|
static void us(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (ev == MG_EV_HTTP_MSG && mg_match(hm->uri, mg_str("/upload"), NULL)) {
|
if (ev == MG_EV_HTTP_MSG && mg_match(hm->uri, mg_str("/upload"), NULL)) {
|
||||||
@@ -3160,7 +3213,7 @@ static void test_http_chunked_case(mg_event_handler_t s, mg_event_handler_t c,
|
|||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
uint32_t i, crc = 0, expected_crc = mg_crc32(0, expected, strlen(expected));
|
uint32_t i, crc = 0, expected_crc = mg_crc32(0, expected, strlen(expected));
|
||||||
struct mg_connection *conn;
|
struct mg_connection *conn;
|
||||||
static uint16_t port = 32344; // To prevent bind errors on Windows
|
static uint16_t port = 32344; // To prevent bind errors on Windows
|
||||||
mg_snprintf(url, sizeof(url), "http://127.0.0.1:%d", port++);
|
mg_snprintf(url, sizeof(url), "http://127.0.0.1:%d", port++);
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
mg_http_listen(&mgr, url, s, NULL);
|
mg_http_listen(&mgr, url, s, NULL);
|
||||||
@@ -3714,13 +3767,23 @@ static void test_json(void) {
|
|||||||
ASSERT(b == true);
|
ASSERT(b == true);
|
||||||
ASSERT(mg_json_get(json, "$.b[2]", &len) < 0);
|
ASSERT(mg_json_get(json, "$.b[2]", &len) < 0);
|
||||||
|
|
||||||
json = mg_str("[\"YWJj\", \"0100026869\"]");
|
json = mg_str("[\"YWJj\", \"0100026869\", \"\", \"abc\", \"0g\"]");
|
||||||
ASSERT((str = mg_json_get_b64(json, "$[0]", &len)) != NULL);
|
ASSERT((str = mg_json_get_b64(json, "$[0]", &len)) != NULL);
|
||||||
ASSERT(len == 3 && memcmp(str, "abc", (size_t) len) == 0);
|
ASSERT(len == 3 && memcmp(str, "abc", (size_t) len) == 0);
|
||||||
mg_free(str);
|
mg_free(str);
|
||||||
ASSERT((str = mg_json_get_hex(json, "$[1]", &len)) != NULL);
|
ASSERT((str = mg_json_get_hex(json, "$[1]", &len)) != NULL);
|
||||||
ASSERT(len == 5 && memcmp(str, "\x01\x00\x02hi", (size_t) len) == 0);
|
ASSERT(len == 5 && memcmp(str, "\x01\x00\x02hi", (size_t) len) == 0);
|
||||||
mg_free(str);
|
mg_free(str);
|
||||||
|
ASSERT((str = mg_json_get_hex(json, "$[2]", &len)) != NULL);
|
||||||
|
ASSERT(len == 0 && str[0] == '\0');
|
||||||
|
mg_free(str);
|
||||||
|
len = 42;
|
||||||
|
str = mg_json_get_hex(json, "$[3]", &len);
|
||||||
|
ASSERT(str == NULL && len == 42);
|
||||||
|
mg_free(str);
|
||||||
|
str = mg_json_get_hex(json, "$[4]", &len);
|
||||||
|
ASSERT(str == NULL && len == 42);
|
||||||
|
mg_free(str);
|
||||||
|
|
||||||
json = mg_str("{\"a\":[1,2,3], \"ab\": 2}");
|
json = mg_str("{\"a\":[1,2,3], \"ab\": 2}");
|
||||||
ASSERT(mg_json_get_long(json, "$.a[0]", -42) == 1);
|
ASSERT(mg_json_get_long(json, "$.a[0]", -42) == 1);
|
||||||
@@ -3930,8 +3993,7 @@ static void test_poll(void) {
|
|||||||
int count = 0, i;
|
int count = 0, i;
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
mg_http_listen(&mgr, "http://127.0.0.1:42346", ph,
|
mg_http_listen(&mgr, "http://127.0.0.1:12340", ph, &count);
|
||||||
&count); // To prevent bind errors on Windows
|
|
||||||
for (i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0);
|
for (i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0);
|
||||||
ASSERT(count == 10);
|
ASSERT(count == 10);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
@@ -4298,6 +4360,128 @@ static void test_rsa(void) {
|
|||||||
ASSERT(!mg_rsa_verify(em, 256, tv_mhash));
|
ASSERT(!mg_rsa_verify(em, 256, tv_mhash));
|
||||||
em[50] = tv_em[50];
|
em[50] = tv_em[50];
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
static const uint8_t sha256_di[] = {
|
||||||
|
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
||||||
|
0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
|
||||||
|
static const uint8_t sha384_di[] = {
|
||||||
|
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
||||||
|
0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
|
||||||
|
uint8_t hash[48], em[256];
|
||||||
|
size_t i, pslen;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(hash); i++) hash[i] = (uint8_t) i;
|
||||||
|
|
||||||
|
memset(em, 0xff, sizeof(em));
|
||||||
|
em[0] = 0, em[1] = 1;
|
||||||
|
pslen = sizeof(em) - 3 - sizeof(sha256_di) - 32;
|
||||||
|
em[2 + pslen] = 0;
|
||||||
|
memcpy(em + 3 + pslen, sha256_di, sizeof(sha256_di));
|
||||||
|
memcpy(em + 3 + pslen + sizeof(sha256_di), hash, 32);
|
||||||
|
ASSERT(mg_rsa_pkcs_verify(em, sizeof(em), hash, 32));
|
||||||
|
|
||||||
|
em[1] = 2; // bad block type
|
||||||
|
ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32));
|
||||||
|
em[1] = 1;
|
||||||
|
em[10] = 0; // bad padding
|
||||||
|
ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32));
|
||||||
|
em[10] = 0xff;
|
||||||
|
em[3 + pslen + 15] ^= 1; // bad DigestInfo
|
||||||
|
ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32));
|
||||||
|
em[3 + pslen + 15] ^= 1;
|
||||||
|
em[sizeof(em) - 1] ^= 1; // bad hash
|
||||||
|
ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32));
|
||||||
|
|
||||||
|
memset(em, 0, sizeof(em)); // former suffix-only bypass
|
||||||
|
memcpy(em + sizeof(em) - 32, hash, 32);
|
||||||
|
ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32));
|
||||||
|
|
||||||
|
memset(em, 0xff, sizeof(em));
|
||||||
|
em[0] = 0, em[1] = 1;
|
||||||
|
pslen = sizeof(em) - 3 - sizeof(sha384_di) - 48;
|
||||||
|
em[2 + pslen] = 0;
|
||||||
|
memcpy(em + 3 + pslen, sha384_di, sizeof(sha384_di));
|
||||||
|
memcpy(em + 3 + pslen + sizeof(sha384_di), hash, 48);
|
||||||
|
ASSERT(mg_rsa_pkcs_verify(em, sizeof(em), hash, 48));
|
||||||
|
ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 20));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_jwt(void) {
|
||||||
|
char jwt[512], claims[128], got[128], hdr[128], extra[64], tampered[512];
|
||||||
|
struct mg_jwt_opts opts, bad;
|
||||||
|
const char *dot;
|
||||||
|
char *str;
|
||||||
|
size_t n, m;
|
||||||
|
|
||||||
|
mg_snprintf(claims, sizeof(claims), "{%m:%m,%m:%d}", MG_ESC("sub"),
|
||||||
|
MG_ESC("admin"), MG_ESC("iat"), 123);
|
||||||
|
mg_snprintf(extra, sizeof(extra), "%m:%m", MG_ESC("cty"), MG_ESC("JWT"));
|
||||||
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
opts.claims = mg_str(claims);
|
||||||
|
opts.header = mg_str(extra);
|
||||||
|
opts.kid = mg_str("hmac-key-1");
|
||||||
|
opts.secret = mg_str("secret");
|
||||||
|
n = mg_jwt_sign_hs256(&opts, jwt, sizeof(jwt));
|
||||||
|
ASSERT(n > 0 && n + 1 < sizeof(jwt));
|
||||||
|
jwt[n] = '\0';
|
||||||
|
|
||||||
|
dot = strchr(jwt, '.');
|
||||||
|
ASSERT(dot != NULL);
|
||||||
|
ASSERT(mg_base64url_decode(jwt, (size_t) (dot - jwt), hdr,
|
||||||
|
sizeof(hdr)) > 0);
|
||||||
|
str = mg_json_get_str(mg_str(hdr), "$.kid");
|
||||||
|
ASSERT(str != NULL && strcmp(str, "hmac-key-1") == 0);
|
||||||
|
mg_free(str);
|
||||||
|
str = mg_json_get_str(mg_str(hdr), "$.cty");
|
||||||
|
ASSERT(str != NULL && strcmp(str, "JWT") == 0);
|
||||||
|
mg_free(str);
|
||||||
|
|
||||||
|
m = mg_jwt_verify_hs256(mg_str_n(jwt, n), &opts, got, sizeof(got));
|
||||||
|
ASSERT(m == strlen(claims));
|
||||||
|
ASSERT(strcmp(got, claims) == 0);
|
||||||
|
bad = opts;
|
||||||
|
bad.secret = mg_str("bad-secret");
|
||||||
|
ASSERT(mg_jwt_verify_hs256(mg_str_n(jwt, n), &bad, got,
|
||||||
|
sizeof(got)) == 0);
|
||||||
|
memcpy(tampered, jwt, n + 1);
|
||||||
|
tampered[n - 1] = tampered[n - 1] == 'A' ? 'B' : 'A';
|
||||||
|
ASSERT(mg_jwt_verify_hs256(mg_str_n(tampered, n), &opts, got,
|
||||||
|
sizeof(got)) == 0);
|
||||||
|
ASSERT(mg_jwt_sign_hs256(&opts, got, 10) == 0);
|
||||||
|
ASSERT(mg_jwt_verify_hs256(mg_str_n(jwt, n), &opts, got, 4) == 0);
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
|
{
|
||||||
|
const char *key_pem =
|
||||||
|
"-----BEGIN EC PRIVATE KEY-----\n"
|
||||||
|
"MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49\n"
|
||||||
|
"AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl\n"
|
||||||
|
"4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==\n"
|
||||||
|
"-----END EC PRIVATE KEY-----\n";
|
||||||
|
uint8_t key[32], pub[64];
|
||||||
|
|
||||||
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
opts.claims = mg_str(claims);
|
||||||
|
opts.kid = mg_str("ec-key-1");
|
||||||
|
opts.private_key = key;
|
||||||
|
opts.public_key = pub;
|
||||||
|
ASSERT(mg_uecc_parse_private_key(mg_str(key_pem), key, sizeof(key)) == 32);
|
||||||
|
ASSERT(mg_uecc_compute_public_key(key, pub, mg_uecc_secp256r1()) == 1);
|
||||||
|
n = mg_jwt_sign_es256(&opts, jwt, sizeof(jwt));
|
||||||
|
ASSERT(n > 0 && n + 1 < sizeof(jwt));
|
||||||
|
jwt[n] = '\0';
|
||||||
|
m = mg_jwt_verify_es256(mg_str_n(jwt, n), &opts, got, sizeof(got));
|
||||||
|
ASSERT(m == strlen(claims));
|
||||||
|
ASSERT(strcmp(got, claims) == 0);
|
||||||
|
ASSERT(mg_jwt_verify_hs256(mg_str_n(jwt, n), &bad, got,
|
||||||
|
sizeof(got)) == 0);
|
||||||
|
memcpy(tampered, jwt, n + 1);
|
||||||
|
tampered[n - 1] = tampered[n - 1] == 'A' ? 'B' : 'A';
|
||||||
|
ASSERT(mg_jwt_verify_es256(mg_str_n(tampered, n), &opts, got,
|
||||||
|
sizeof(got)) == 0);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4308,6 +4492,7 @@ static void test_crypto(void) {
|
|||||||
test_sha384();
|
test_sha384();
|
||||||
test_x25519();
|
test_x25519();
|
||||||
test_rsa();
|
test_rsa();
|
||||||
|
test_jwt();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dash_custom(struct mg_connection *c, int ev, void *ev_data) {
|
static void dash_custom(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
@@ -4422,15 +4607,15 @@ static bool set3_fn(enum mg_dash_op op, struct mg_dash_user *u) {
|
|||||||
static bool test_upload_dir(const struct mg_dash_user *u, char *buf, size_t len) {
|
static bool test_upload_dir(const struct mg_dash_user *u, char *buf, size_t len) {
|
||||||
(void) u;
|
(void) u;
|
||||||
#if MG_ARCH == MG_ARCH_UNIX || MG_ARCH == MG_ARCH_WIN32
|
#if MG_ARCH == MG_ARCH_UNIX || MG_ARCH == MG_ARCH_WIN32
|
||||||
mkdir("/tmp/mongoose_dash_test", 0755);
|
mkdir("mongoose_dash_test", 0755);
|
||||||
#endif
|
#endif
|
||||||
mg_snprintf(buf, len, "%s", "/tmp/mongoose_dash_test");
|
mg_snprintf(buf, len, "%s", "mongoose_dash_test");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct test_file_entry {
|
struct test_file_entry {
|
||||||
char name[64];
|
char name[64];
|
||||||
size_t size;
|
uint64_t size;
|
||||||
};
|
};
|
||||||
static struct test_file_entry s_test_file;
|
static struct test_file_entry s_test_file;
|
||||||
static int s_test_file_index;
|
static int s_test_file_index;
|
||||||
@@ -4588,7 +4773,7 @@ static void test_dash(void) {
|
|||||||
|
|
||||||
memset(&dash, 0, sizeof(dash));
|
memset(&dash, 0, sizeof(dash));
|
||||||
dash.session_auto_expiration_seconds = 1;
|
dash.session_auto_expiration_seconds = 1;
|
||||||
remove("/tmp/mongoose_dash_test/test.bin");
|
remove("mongoose_dash_test/test.bin");
|
||||||
|
|
||||||
MG_DASH_ADD_FIELD_SET(&dash, &test_files_set);
|
MG_DASH_ADD_FIELD_SET(&dash, &test_files_set);
|
||||||
MG_DASH_ADD_FIELD_SET(&dash, &set1);
|
MG_DASH_ADD_FIELD_SET(&dash, &set1);
|
||||||
@@ -4917,6 +5102,11 @@ static void test_dash(void) {
|
|||||||
ASSERT(strstr(buf, test_bin_probe) != NULL);
|
ASSERT(strstr(buf, test_bin_probe) != NULL);
|
||||||
mg_mgr_free(&mgr3);
|
mg_mgr_free(&mgr3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remove("mongoose_dash_test/test.bin");
|
||||||
|
#if MG_ARCH == MG_ARCH_UNIX || MG_ARCH == MG_ARCH_WIN32
|
||||||
|
rmdir("mongoose_dash_test");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DASHBOARD(x) \
|
#define DASHBOARD(x) \
|
||||||
@@ -5288,8 +5478,8 @@ static void test_wakeup(void) {
|
|||||||
struct wudata *data;
|
struct wudata *data;
|
||||||
int i;
|
int i;
|
||||||
mg_mgr_init(&mgr); // Initialise event manager
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
mg_log_set(MG_LL_DEBUG); // Set debug log level
|
mg_log_set(MG_LL_DEBUG);
|
||||||
c = mg_http_listen(&mgr, "http://127.0.0.1:42347", hwu, NULL);
|
c = mg_http_listen(&mgr, "http://127.0.0.1:12341", hwu, NULL);
|
||||||
mg_wakeup_init(&mgr); // Initialise wakeup socket pair
|
mg_wakeup_init(&mgr); // Initialise wakeup socket pair
|
||||||
data = (struct wudata *) calloc(1, sizeof(*data)); // wuthread owns it
|
data = (struct wudata *) calloc(1, sizeof(*data)); // wuthread owns it
|
||||||
data->conn_id = c->id;
|
data->conn_id = c->id;
|
||||||
@@ -5328,6 +5518,7 @@ int main(void) {
|
|||||||
test_str();
|
test_str();
|
||||||
test_match();
|
test_match();
|
||||||
test_crc32();
|
test_crc32();
|
||||||
|
test_crc16();
|
||||||
DASHBOARD("misc");
|
DASHBOARD("misc");
|
||||||
|
|
||||||
s_error = false;
|
s_error = false;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
PROG ?= example # Program we are building
|
||||||
|
DELETE = rm -rf # Command to remove files
|
||||||
|
OUT ?= -o $(PROG) # Compiler argument for output file
|
||||||
|
SOURCES = main.c mongoose.c # Source code files
|
||||||
|
CFLAGS = -W -Wall -Wextra -g -I. # Build options
|
||||||
|
|
||||||
|
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
|
||||||
|
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1
|
||||||
|
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
|
||||||
|
PROG ?= example.exe # Use .exe suffix for the binary
|
||||||
|
CC = gcc # Use MinGW gcc compiler
|
||||||
|
CFLAGS += -lws2_32 -lbcrypt # Link against Winsock library
|
||||||
|
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
|
||||||
|
OUT ?= -o $(PROG) # Build output
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG) # Default target. Build and run program
|
||||||
|
$(RUN) ./$(PROG) $(ARGS)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES) # Build program from sources
|
||||||
|
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
|
||||||
|
|
||||||
|
clean: # Cleanup. Delete built program and all build artifacts
|
||||||
|
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# JWT Bearer Authentication
|
||||||
|
|
||||||
|
## Build and run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## HS256:
|
||||||
|
|
||||||
|
This path signs and verifies JWTs with the shared secret from `main.c`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -i http://127.0.0.1:8000/login-hs256
|
||||||
|
TOKEN=$(curl -s http://127.0.0.1:8000/login-hs256 | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
|
||||||
|
curl -i -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
```txt
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 47
|
||||||
|
|
||||||
|
{"sub":"admin","scope":"dash:read dash:write"}
|
||||||
|
```
|
||||||
|
|
||||||
|
`scope` here is a JSON string value containing a space-delimited permission list; as an example.
|
||||||
|
|
||||||
|
## ES256 (Requires MG_TLS_BUILTIN):
|
||||||
|
|
||||||
|
This path parses the EC private key from `main.c`, signs the JWT with it, and
|
||||||
|
derives the public key for verification to keep the example self-contained.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -i http://127.0.0.1:8000/login-es256
|
||||||
|
TOKEN=$(curl -s http://127.0.0.1:8000/login-es256 | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
|
||||||
|
curl -i -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
```txt
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 36
|
||||||
|
|
||||||
|
{"sub":"admin","scope":"dash:read"}
|
||||||
|
```
|
||||||
|
|
||||||
|
`scope` here is a JSON string value containing a space-delimited permission list; as an example.
|
||||||
|
|
||||||
|
## Unauthorized access attempt example
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -i -H "Authorization: Bearer bad.token.here" http://127.0.0.1:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
```txt
|
||||||
|
HTTP/1.1 401 Unauthorized
|
||||||
|
WWW-Authenticate: Bearer
|
||||||
|
Content-Length: 13
|
||||||
|
|
||||||
|
Unauthorized
|
||||||
|
```
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// HTTP server example. It issues and verifies JWT Bearer tokens.
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_listen_on = "http://0.0.0.0:8000";
|
||||||
|
static const char *s_secret = "change-me";
|
||||||
|
enum { TOKEN_LEN = 1024, CLAIMS_LEN = 256, JWT_LEN = 1024 };
|
||||||
|
static const char *s_ec_private_key =
|
||||||
|
"-----BEGIN EC PRIVATE KEY-----\n"
|
||||||
|
"MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49\n"
|
||||||
|
"AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl\n"
|
||||||
|
"4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==\n"
|
||||||
|
"-----END EC PRIVATE KEY-----\n";
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
|
static size_t verify_es256(struct mg_str token, char *claims, size_t len) {
|
||||||
|
uint8_t key[32], pub[64];
|
||||||
|
struct mg_jwt_opts opts = {.public_key = pub};
|
||||||
|
// This keeps the example self-contained. A real verifier would usually keep
|
||||||
|
// only the issuer public key, selected by kid, not the signing private key.
|
||||||
|
if (mg_uecc_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key)) !=
|
||||||
|
32 ||
|
||||||
|
mg_uecc_compute_public_key(key, pub, mg_uecc_secp256r1()) != 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return mg_jwt_verify_es256(token, &opts, claims, len);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
char user[1], *token, *claims, *jwt;
|
||||||
|
token = (char *) mg_calloc(1, TOKEN_LEN);
|
||||||
|
claims = (char *) mg_calloc(1, CLAIMS_LEN);
|
||||||
|
jwt = (char *) mg_calloc(1, JWT_LEN);
|
||||||
|
if (token == NULL || claims == NULL || jwt == NULL) {
|
||||||
|
mg_http_reply(c, 500, "", "OOM\n");
|
||||||
|
mg_free(token);
|
||||||
|
mg_free(claims);
|
||||||
|
mg_free(jwt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mg_http_creds(hm, user, sizeof(user), token, TOKEN_LEN);
|
||||||
|
|
||||||
|
if (mg_match(hm->uri, mg_str("/login-hs256"), NULL)) {
|
||||||
|
struct mg_jwt_opts opts = {.kid = mg_str("hmac-key-1"),
|
||||||
|
.secret = mg_str(s_secret)};
|
||||||
|
size_t n;
|
||||||
|
mg_snprintf(claims, CLAIMS_LEN, "{%m:%m,%m:%m}", MG_ESC("sub"),
|
||||||
|
MG_ESC("admin"), MG_ESC("scope"),
|
||||||
|
MG_ESC("dash:read dash:write"));
|
||||||
|
opts.claims = mg_str(claims);
|
||||||
|
n = mg_jwt_sign_hs256(&opts, jwt, JWT_LEN);
|
||||||
|
mg_http_reply(c, n == 0 ? 500 : 200, "Content-Type: application/json\r\n",
|
||||||
|
"{%m:%m}\n", MG_ESC("token"), MG_ESC(n == 0 ? "" : jwt));
|
||||||
|
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
|
} else if (mg_match(hm->uri, mg_str("/login-es256"), NULL)) {
|
||||||
|
uint8_t key[32];
|
||||||
|
struct mg_jwt_opts opts = {.kid = mg_str("ec-key-1"),
|
||||||
|
.private_key = key};
|
||||||
|
size_t keysz, n;
|
||||||
|
keysz =
|
||||||
|
mg_uecc_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key));
|
||||||
|
// ES256 uses a raw 64-byte r || s signature. mg_jwt_sign_es256() signs it
|
||||||
|
// deterministically; applications can also hash the signing input and
|
||||||
|
// call mg_uecc_sign() directly if they need random-nonce ECDSA.
|
||||||
|
mg_snprintf(claims, CLAIMS_LEN, "{%m:%m,%m:%m}", MG_ESC("sub"),
|
||||||
|
MG_ESC("admin"), MG_ESC("scope"), MG_ESC("dash:read"));
|
||||||
|
opts.claims = mg_str(claims);
|
||||||
|
n = keysz == 32 ? mg_jwt_sign_es256(&opts, jwt, JWT_LEN) : 0;
|
||||||
|
mg_http_reply(c, n == 0 ? 500 : 200, "Content-Type: application/json\r\n",
|
||||||
|
"{%m:%m}\n", MG_ESC("token"), MG_ESC(n == 0 ? "" : jwt));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} else {
|
||||||
|
struct mg_jwt_opts opts = {.secret = mg_str(s_secret)};
|
||||||
|
if (mg_jwt_verify_hs256(mg_str(token), &opts, claims, CLAIMS_LEN) > 0) {
|
||||||
|
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\n",
|
||||||
|
claims);
|
||||||
|
#if MG_TLS == MG_TLS_BUILTIN
|
||||||
|
} else if (verify_es256(mg_str(token), claims, CLAIMS_LEN) > 0) {
|
||||||
|
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\n",
|
||||||
|
claims);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
mg_http_reply(c, 401, "WWW-Authenticate: Bearer\r\n", "Unauthorized\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mg_free(token);
|
||||||
|
mg_free(claims);
|
||||||
|
mg_free(jwt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
mg_log_set(MG_LL_DEBUG);
|
||||||
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
|
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
|
||||||
|
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.c
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.h
|
||||||
@@ -12,7 +12,13 @@ struct mg_tcpip_spi spi = {
|
|||||||
NULL, // SPI metadata
|
NULL, // SPI metadata
|
||||||
[](void *) { digitalWrite(SS_PIN, LOW); SPI.beginTransaction(SPISettings()); },
|
[](void *) { digitalWrite(SS_PIN, LOW); SPI.beginTransaction(SPISettings()); },
|
||||||
[](void *) { digitalWrite(SS_PIN, HIGH); SPI.endTransaction(); },
|
[](void *) { digitalWrite(SS_PIN, HIGH); SPI.endTransaction(); },
|
||||||
[](void *, uint8_t c) { return SPI.transfer(c); }, // Execute transaction
|
[](void *, uint8_t *w, uint8_t *r, size_t n) { // Execute transaction
|
||||||
|
while (n--) {
|
||||||
|
uint8_t c = (w != NULL) ? *w++ : 0xFF;
|
||||||
|
uint8_t d = SPI.transfer(c);
|
||||||
|
if (r != NULL) *r++ = d;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
struct mg_mgr mgr; // Mongoose event manager
|
struct mg_mgr mgr; // Mongoose event manager
|
||||||
struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 5}}; // Network interface
|
struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 5}}; // Network interface
|
||||||
|
|||||||
+1
-1
@@ -249,7 +249,7 @@ static struct mg_tcpip_if *s_ifp;
|
|||||||
static void deliver_packet_above(struct pb *buf, int ifc) {
|
static void deliver_packet_above(struct pb *buf, int ifc) {
|
||||||
struct eth_hdr *ethhdr = (struct eth_hdr *) buf->data;
|
struct eth_hdr *ethhdr = (struct eth_hdr *) buf->data;
|
||||||
w_pkt_d("Data RX: Driver=>Kernel, if %d, len %d", ifc, buf->len);
|
w_pkt_d("Data RX: Driver=>Kernel, if %d, len %d", ifc, buf->len);
|
||||||
if (mg_ntohs(ethhdr->type == 0x888E)) {
|
if (ethhdr->type == mg_htons(0x888E)) {
|
||||||
#if CONFIG_WPS2
|
#if CONFIG_WPS2
|
||||||
if (wps_rx_callback != NULL) wps_rx_callback(buf->data, buf->len);
|
if (wps_rx_callback != NULL) wps_rx_callback(buf->data, buf->len);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ char s_theone[] = "Server.local";
|
|||||||
static void mdns_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
static void mdns_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_MDNS_RESP) { // is this the one ?
|
if (ev == MG_EV_MDNS_RESP) { // is this the one ?
|
||||||
struct mg_mdns_resp *resp = (struct mg_mdns_resp *) ev_data;
|
struct mg_mdns_resp *resp = (struct mg_mdns_resp *) ev_data;
|
||||||
if (mg_strcasecmp(mg_str(s_theone), resp->name) == 0)
|
if (resp->rr->atype == MG_DNS_RTYPE_A &&
|
||||||
|
mg_strcasecmp(mg_str(s_theone), resp->name) == 0)
|
||||||
MG_INFO(("%.*s is: %M", resp->name.len, resp->name.buf, mg_print_ip, &resp->addr));
|
MG_INFO(("%.*s is: %M", resp->name.len, resp->name.buf, mg_print_ip, &resp->addr));
|
||||||
}
|
}
|
||||||
(void) c;
|
(void) c;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
PROG ?= example # Program we are building
|
||||||
|
DELETE = rm -rf # Command to remove files
|
||||||
|
OUT ?= -o $(PROG) # Compiler argument for output file
|
||||||
|
SOURCES = main.c mongoose.c # Source code files
|
||||||
|
CFLAGS = -W -Wall -Wextra -g -I. # Build options
|
||||||
|
|
||||||
|
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
|
||||||
|
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
|
||||||
|
PROG ?= example.exe # Use .exe suffix for the binary
|
||||||
|
CC = gcc # Use MinGW gcc compiler
|
||||||
|
CFLAGS += -lws2_32 # Link against Winsock library
|
||||||
|
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
|
||||||
|
OUT ?= -o $(PROG) # Build output
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG) # Default target. Build and run program
|
||||||
|
$(RUN) ./$(PROG) $(ARGS)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES) # Build program from sources
|
||||||
|
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
|
||||||
|
|
||||||
|
clean: # Cleanup. Delete built program and all build artifacts
|
||||||
|
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Browses for `_http._tcp.local` services on the local network using mDNS/DNS-SD.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
cc main.c mongoose.c -W -Wall -Wextra -g -I. -DMG_ENABLE_LINES -o example
|
||||||
|
./example
|
||||||
|
1304aa594 3 net.c:311:mg_mgr_init MG_IO_SIZE: 16384, TLS: none
|
||||||
|
1304aa594 3 net.c:224:mg_listen 1 4 udp://224.0.0.251:5353
|
||||||
|
1304aa594 3 sock.c:167:mg_send 1 4 0:0:0 34 err 0
|
||||||
|
1304aa594 3 sock.c:361:read_conn 1 4 0:0:0 34 err 0
|
||||||
|
1304aa594 3 dns.c:478:handle_mdns_query PTR request for _http._tcp
|
||||||
|
1304aa5d0 3 sock.c:361:read_conn 1 4 0:0:0 289 err 0
|
||||||
|
1304aa5d1 3 dns.c:703:handle_mdns_respo PTR response for _http._tcp.local from 3D03F3CPAU00096.local
|
||||||
|
1304aa5d1 2 main.c:15:mdns_ev_handler Got a response
|
||||||
|
SERVICE -> _http._tcp
|
||||||
|
NAME -> 3D03F3CPAU00096.local
|
||||||
|
PORT -> 80
|
||||||
|
TXT -> host=3D03F3CPAU00096mac=e0:50:8b:8d:2f:75ip=192.168.69.220
|
||||||
|
URL -> http://3D03F3CPAU00096.local:80
|
||||||
|
|
||||||
|
1304aa622 3 sock.c:361:read_conn 1 4 0:0:0 226 err 0
|
||||||
|
1304aa622 3 dns.c:703:handle_mdns_respo PTR response for _http._tcp.local from DIP-3TIR3020140717CCWR473261824.local
|
||||||
|
1304aa622 2 main.c:15:mdns_ev_handler Got a response
|
||||||
|
SERVICE -> _http._tcp
|
||||||
|
NAME -> DIP-3TIR3020140717CCWR473261824.local
|
||||||
|
PORT -> 80
|
||||||
|
ADDRESS -> 192.168.69.225
|
||||||
|
TXT -> path=/
|
||||||
|
URL -> http://192.168.69.225:80
|
||||||
|
|
||||||
|
1304aa6f5 3 sock.c:361:read_conn 1 4 0:0:0 169 err 0
|
||||||
|
1304aa6f5 3 dns.c:703:handle_mdns_respo PTR response for _http._tcp.local from PHILIPS_HD9255_3_6B7E5E.local
|
||||||
|
1304aa6f5 2 main.c:15:mdns_ev_handler Got a response
|
||||||
|
SERVICE -> _http._tcp
|
||||||
|
NAME -> PHILIPS_HD9255_3_6B7E5E.local
|
||||||
|
PORT -> 80
|
||||||
|
ADDRESS -> 192.168.69.167
|
||||||
|
TXT -> Device Type=Domestic Appliance
|
||||||
|
URL -> http://192.168.69.167:80
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static char s_name[] = "Mongoose";
|
||||||
|
static char s_service[] = "_http._tcp";
|
||||||
|
|
||||||
|
static void mdns_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
if (ev == MG_EV_MDNS_RESP) {
|
||||||
|
struct mg_mdns_resp *resp = (struct mg_mdns_resp *) ev_data;
|
||||||
|
if (resp->rr->atype == MG_DNS_RTYPE_PTR &&
|
||||||
|
mg_strcasecmp(mg_str(s_service), resp->sd.srvcproto) == 0) {
|
||||||
|
char addr[40];
|
||||||
|
MG_INFO(("Got a response"));
|
||||||
|
printf("\tSERVICE -> %.*s\n", (int) resp->sd.srvcproto.len,
|
||||||
|
resp->sd.srvcproto.buf);
|
||||||
|
printf("\tNAME -> %.*s\n", (int) resp->name.len, resp->name.buf);
|
||||||
|
if (resp->sd.port != 0)
|
||||||
|
printf("\tPORT -> %u\n", resp->sd.port);
|
||||||
|
if (resp->addr.addr.ip4 != 0) {
|
||||||
|
mg_snprintf(addr, sizeof(addr), "%M", mg_print_ip, &resp->addr);
|
||||||
|
printf("\tADDRESS -> %s\n", addr);
|
||||||
|
}
|
||||||
|
if (resp->sd.txt.buf != NULL)
|
||||||
|
printf("\tTXT -> %.*s\n", (int) resp->sd.txt.len, resp->sd.txt.buf);
|
||||||
|
if (resp->addr.addr.ip4 != 0 && resp->addr.port != 0) {
|
||||||
|
mg_snprintf(addr, sizeof(addr), "http://%M:%u", mg_print_ip, &resp->addr,
|
||||||
|
mg_ntohs(resp->addr.port));
|
||||||
|
printf("\tURL -> %s\n", addr);
|
||||||
|
} else if (resp->sd.port != 0 && resp->name.buf != NULL) {
|
||||||
|
printf("\tURL -> http://%.*s:%u\n", (int) resp->name.len,
|
||||||
|
resp->name.buf, resp->sd.port);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
char query[256];
|
||||||
|
static struct mg_connection *c;
|
||||||
|
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||||
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
|
|
||||||
|
c = mg_mdns_listen(&mgr, mdns_ev_handler, s_name); // Start mDNS server
|
||||||
|
if (c == NULL) return 1;
|
||||||
|
mg_snprintf(query, sizeof(query), "%s.local", s_service);
|
||||||
|
// Request service providers
|
||||||
|
if (!mg_mdns_query(c, query, MG_DNS_RTYPE_PTR)) return 1;
|
||||||
|
|
||||||
|
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
|
||||||
|
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.c
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.h
|
||||||
Reference in new issue
Block a user