mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e7c664455 | ||
|
|
60c75a22b8 | ||
|
|
bbee8ae61f | ||
|
|
9173c8dfda | ||
|
|
b93cbbeb13 | ||
|
|
aa1dfe8000 | ||
|
|
e8a82d1821 | ||
|
|
201a81a315 | ||
|
|
aae31aba9c | ||
|
|
29aa53cebd | ||
|
|
5d4fffbb14 | ||
|
|
ed5ef09d1c | ||
|
|
1e99974b83 | ||
|
|
6814b44b03 | ||
|
|
7e9ddc2d09 | ||
|
|
c13d5d7a56 | ||
|
|
555c5a76be | ||
|
|
863aea34c4 | ||
|
|
830f6c589b | ||
|
|
cc64c41737 | ||
|
|
4660f2ca0f | ||
|
|
ffcad7ee47 | ||
|
|
09a97b915a | ||
|
|
35a9242b67 | ||
|
|
5c34018899 | ||
|
|
c857ddae57 | ||
|
|
830acc19ee | ||
|
|
f87d439710 | ||
|
|
86adf12a77 | ||
|
|
a561f3e096 | ||
|
|
2b0a5e9701 | ||
|
|
8346a60971 | ||
|
|
140fa6cebf | ||
|
|
cd05b162a8 | ||
|
|
3fde04137c | ||
|
|
5591b7d131 | ||
|
|
9ddac8faa7 | ||
|
|
9123330f72 | ||
|
|
b938728c2a | ||
|
|
fab922230c | ||
|
|
90418ffb1b | ||
|
|
5a317e81d9 | ||
|
|
7e8c3a59e2 | ||
|
|
6093ac12a0 | ||
|
|
e7cfaa70c7 | ||
|
|
897d26edfb | ||
|
|
6b5d358ede | ||
|
|
0d97280af4 | ||
|
|
afc84b7c92 | ||
|
|
66bf5952a4 | ||
|
|
f002c30cbb | ||
|
|
53d2356472 | ||
|
|
e312219e80 | ||
|
|
2aff813795 | ||
|
|
9c4506763b | ||
|
|
ec3b9e7c4f | ||
|
|
ddd9651f91 | ||
|
|
304ea2a649 | ||
|
|
83bac34094 | ||
|
|
02bdbb9ed6 | ||
|
|
84553d06a0 | ||
|
|
de7cd48a79 | ||
|
|
5df2445bc9 | ||
|
|
90890e4c63 | ||
|
|
a6deed5868 | ||
|
|
5cdb25f05a | ||
|
|
d8764746a4 | ||
|
|
fa4a2563e5 | ||
|
|
9ccfc02a86 | ||
|
|
f58f29b9a0 | ||
|
|
282ae0d968 | ||
|
|
9c6b4edd37 | ||
|
|
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
@@ -1,16 +1,42 @@
|
|||||||
name: Claude Full Codebase Security Scan
|
name: Claude Codebase Security Scan
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
prompt_path:
|
|
||||||
description: "Path to the repo prompt file Claude should read"
|
|
||||||
required: true
|
|
||||||
default: "resources/specs/claude-security-scan.md"
|
|
||||||
target_ref:
|
target_ref:
|
||||||
description: "Branch, tag, or SHA to scan"
|
description: "Branch, tag, or SHA to scan"
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
|
common_misc:
|
||||||
|
description: "Run common/misc security scan"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
mongoose_core:
|
||||||
|
description: "Run Mongoose core security scan"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
web_http_auth:
|
||||||
|
description: "Run web/HTTP/auth security scan"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
tls:
|
||||||
|
description: "Run TLS security scan"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
net_builtin:
|
||||||
|
description: "Run built-in network security scan"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
protocols:
|
||||||
|
description: "Run protocol security scan"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
#schedule:
|
#schedule:
|
||||||
# Weekly scan: Sunday 02:00 UTC
|
# Weekly scan: Sunday 02:00 UTC
|
||||||
# - cron: "0 2 * * 0"
|
# - cron: "0 2 * * 0"
|
||||||
@@ -20,73 +46,163 @@ permissions:
|
|||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: claude-full-codebase-security-scan-${{ github.ref }}
|
group: claude-security-scans
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
queue: max
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
scan:
|
prepare:
|
||||||
name: Full codebase security scan
|
name: Prepare selected scan matrix
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 60
|
|
||||||
|
|
||||||
env:
|
outputs:
|
||||||
DEFAULT_PROMPT_PATH: "resources/specs/claude-security-scan.md"
|
target_ref: ${{ steps.plan.outputs.target_ref }}
|
||||||
|
matrix: ${{ steps.plan.outputs.matrix }}
|
||||||
|
has_scans: ${{ steps.plan.outputs.has_scans }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Resolve scan inputs
|
- name: Resolve scan inputs and build matrix
|
||||||
id: vars
|
id: plan
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
PROMPT_PATH="${{ inputs.prompt_path }}"
|
|
||||||
TARGET_REF="${{ inputs.target_ref }}"
|
TARGET_REF="${{ inputs.target_ref }}"
|
||||||
|
RUN_COMMON_MISC="${{ inputs.common_misc }}"
|
||||||
|
RUN_MONGOOSE_CORE="${{ inputs.mongoose_core }}"
|
||||||
|
RUN_WEB_HTTP_AUTH="${{ inputs.web_http_auth }}"
|
||||||
|
RUN_TLS="${{ inputs.tls }}"
|
||||||
|
RUN_NET_BUILTIN="${{ inputs.net_builtin }}"
|
||||||
|
RUN_PROTOCOLS="${{ inputs.protocols }}"
|
||||||
else
|
else
|
||||||
PROMPT_PATH="${DEFAULT_PROMPT_PATH}"
|
|
||||||
TARGET_REF=""
|
TARGET_REF=""
|
||||||
|
RUN_COMMON_MISC="true"
|
||||||
|
RUN_MONGOOSE_CORE="true"
|
||||||
|
RUN_WEB_HTTP_AUTH="true"
|
||||||
|
RUN_TLS="true"
|
||||||
|
RUN_NET_BUILTIN="true"
|
||||||
|
RUN_PROTOCOLS="true"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$TARGET_REF" ]; then
|
if [ -z "$TARGET_REF" ]; then
|
||||||
TARGET_REF="${{ github.ref }}"
|
TARGET_REF="${{ github.ref }}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "prompt_path=$PROMPT_PATH" >> "$GITHUB_OUTPUT"
|
MATRIX="$(jq -cn \
|
||||||
echo "target_ref=$TARGET_REF" >> "$GITHUB_OUTPUT"
|
--arg common_misc "$RUN_COMMON_MISC" \
|
||||||
|
--arg mongoose_core "$RUN_MONGOOSE_CORE" \
|
||||||
|
--arg web_http_auth "$RUN_WEB_HTTP_AUTH" \
|
||||||
|
--arg tls "$RUN_TLS" \
|
||||||
|
--arg net_builtin "$RUN_NET_BUILTIN" \
|
||||||
|
--arg protocols "$RUN_PROTOCOLS" \
|
||||||
|
'{
|
||||||
|
include: ([
|
||||||
|
{
|
||||||
|
enabled: ($common_misc == "true"),
|
||||||
|
area: "common_misc",
|
||||||
|
display_name: "Common / Misc",
|
||||||
|
area_prompt: "resources/specs/areas/common_misc.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: ($mongoose_core == "true"),
|
||||||
|
area: "mongoose_core",
|
||||||
|
display_name: "Mongoose Core",
|
||||||
|
area_prompt: "resources/specs/areas/mongoose_core.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: ($web_http_auth == "true"),
|
||||||
|
area: "web_http_auth",
|
||||||
|
display_name: "Web / HTTP / Auth",
|
||||||
|
area_prompt: "resources/specs/areas/web_http_auth.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: ($tls == "true"),
|
||||||
|
area: "tls",
|
||||||
|
display_name: "TLS",
|
||||||
|
area_prompt: "resources/specs/areas/tls.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: ($net_builtin == "true"),
|
||||||
|
area: "net_builtin",
|
||||||
|
display_name: "Built-In Network",
|
||||||
|
area_prompt: "resources/specs/areas/net_builtin.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: ($protocols == "true"),
|
||||||
|
area: "protocols",
|
||||||
|
display_name: "Protocols",
|
||||||
|
area_prompt: "resources/specs/areas/protocols.md"
|
||||||
|
}
|
||||||
|
] | map(select(.enabled)) | map(del(.enabled)))
|
||||||
|
}'
|
||||||
|
)"
|
||||||
|
|
||||||
|
HAS_SCANS="$(jq -r '.include | length > 0' <<< "$MATRIX")"
|
||||||
|
|
||||||
|
echo "target_ref=$TARGET_REF" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "has_scans=$HAS_SCANS" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
scan:
|
||||||
|
name: ${{ matrix.display_name }} security scan
|
||||||
|
needs: prepare
|
||||||
|
if: needs.prepare.outputs.has_scans == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 1
|
||||||
|
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
MASTER_PROMPT_PATH: "resources/specs/claude-security-scan.md"
|
||||||
|
FILTERING_PROMPT_PATH: "resources/specs/claude-false-positive-filtering.md"
|
||||||
|
SCAN_AREA: ${{ matrix.area }}
|
||||||
|
AREA_PROMPT_PATH: ${{ matrix.area_prompt }}
|
||||||
|
|
||||||
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.vars.outputs.target_ref }}
|
ref: ${{ needs.prepare.outputs.target_ref }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Verify prompt file exists
|
- name: Verify required prompt files exist
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
test -f "${{ steps.vars.outputs.prompt_path }}" || {
|
for prompt_file in "$MASTER_PROMPT_PATH" "$AREA_PROMPT_PATH" "$FILTERING_PROMPT_PATH"; do
|
||||||
echo "Prompt file not found: ${{ steps.vars.outputs.prompt_path }}"
|
test -f "$prompt_file" || {
|
||||||
exit 1
|
echo "Required prompt file not found: $prompt_file"
|
||||||
}
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
- name: Run Claude full codebase security scan
|
- name: Run Claude ${{ matrix.display_name }} 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 }}
|
||||||
|
|
||||||
prompt: |
|
prompt: |
|
||||||
REPO: ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
REF: ${{ steps.vars.outputs.target_ref }}
|
REF: ${{ needs.prepare.outputs.target_ref }}
|
||||||
PROMPT_FILE: ${{ steps.vars.outputs.prompt_path }}
|
MASTER_PROMPT_FILE: ${{ env.MASTER_PROMPT_PATH }}
|
||||||
|
SCAN_AREA: ${{ matrix.area }}
|
||||||
|
AREA_PROMPT_FILE: ${{ matrix.area_prompt }}
|
||||||
|
FILTERING_PROMPT_FILE: ${{ env.FILTERING_PROMPT_PATH }}
|
||||||
|
|
||||||
You are running a full-codebase security scan in GitHub Actions.
|
You are running a focused security scan for the assigned area in GitHub Actions.
|
||||||
|
|
||||||
First, read the main prompt file at:
|
First, read the master prompt at ${{ env.MASTER_PROMPT_PATH }}.
|
||||||
${{ steps.vars.outputs.prompt_path }}
|
Then read the assigned area prompt at ${{ matrix.area_prompt }}.
|
||||||
|
Read the false-positive filtering prompt at ${{ env.FILTERING_PROMPT_PATH }} as required by the master prompt.
|
||||||
|
|
||||||
Treat that file as the authoritative security-review instruction set. You will also
|
Perform the security review for the ${{ matrix.area }} area. Follow relevant supporting code when necessary, but do not independently scan unrelated areas.
|
||||||
read and analyze additional prompt files mentioned in the main prompt file by their absolute paths in the repo,
|
Return only the JSON required by the master prompt.
|
||||||
as per the instructions found in it.
|
|
||||||
|
|
||||||
Scan the checked-out repository as a whole codebase.
|
|
||||||
Do not modify files.
|
Do not modify files.
|
||||||
Do not create commits.
|
Do not create commits.
|
||||||
Do not open pull requests.
|
Do not open pull requests.
|
||||||
@@ -94,9 +210,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 != ''
|
||||||
@@ -106,8 +221,8 @@ jobs:
|
|||||||
set -u
|
set -u
|
||||||
mkdir -p private-scan-results
|
mkdir -p private-scan-results
|
||||||
TODAY="$(date -u +%Y%m%d)"
|
TODAY="$(date -u +%Y%m%d)"
|
||||||
EXECUTION_OUTPUT_FILE="claude-execution-output_${TODAY}.json"
|
EXECUTION_OUTPUT_FILE="claude-execution-output_${SCAN_AREA}_${TODAY}.json"
|
||||||
FINDINGS_FILE="security-findings_${TODAY}.json"
|
FINDINGS_FILE="security-findings_${SCAN_AREA}_${TODAY}.json"
|
||||||
|
|
||||||
HAS_EXECUTION_OUTPUT="false"
|
HAS_EXECUTION_OUTPUT="false"
|
||||||
HAS_JSON_FINDINGS="false"
|
HAS_JSON_FINDINGS="false"
|
||||||
@@ -177,5 +292,5 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git commit -m "Add security scan results ${{ steps.prepare_outputs.outputs.date_suffix }}"
|
git commit -m "Add ${SCAN_AREA} security scan results ${{ steps.prepare_outputs.outputs.date_suffix }}"
|
||||||
git push
|
git push
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
name: Claude PR Security Review
|
name: Claude PR Security Review
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
pr_number:
|
pr_number:
|
||||||
@@ -22,7 +19,7 @@ permissions:
|
|||||||
issues: write
|
issues: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: claude-pr-security-review-${{ github.event.pull_request.number || inputs.pr_number }}
|
group: claude-pr-security-review-${{ inputs.pr_number }}
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -33,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
|
PR_NUMBER: ${{ inputs.pr_number }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Resolve workflow variables
|
- name: Resolve workflow variables
|
||||||
@@ -111,4 +108,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
|
||||||
+374
-81
@@ -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,6 +90,26 @@ 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: |
|
||||||
@@ -82,18 +117,230 @@ jobs:
|
|||||||
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"
|
||||||
|
|
||||||
|
miprtx:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
ssl: ["", BUILTIN]
|
||||||
|
name: MIP RTX TAP SSL=${{ matrix.ssl }}
|
||||||
|
env:
|
||||||
|
IPV6: 0
|
||||||
|
RTX: 1
|
||||||
|
SSL: ${{ matrix.ssl }}
|
||||||
|
TFLAGS: -DMQTT_HOST -DIPV6_NOROUTING -DNO_ABORT
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: |
|
||||||
|
source ./test/setup_ga_network.sh ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf ; ./test/setup_mqtt_server.sh
|
||||||
|
make -C test mip_tap_test
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
miprtxbe:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: MIP RTX be s390 port
|
||||||
|
env:
|
||||||
|
IPV6: 0
|
||||||
|
RTX: 1
|
||||||
|
TFLAGS: -DMQTT_HOST -DIPV6_NOROUTING -DNO_ABORT
|
||||||
|
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 ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC -e RTX" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf ; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2
|
||||||
|
make -C test mip_port_s390 MULTIREC=NO
|
||||||
|
|
||||||
|
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 +403,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 +433,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:
|
||||||
@@ -204,7 +449,7 @@ 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 }}
|
||||||
- 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 +563,67 @@ 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-imxrt1186/minimal
|
||||||
- path: nxp/rt1060-evk-make-baremetal-builtin
|
nxp/frdm-mcxe247/minimal
|
||||||
- path: nxp/rt1170-evk-make-baremetal-builtin
|
nxp/frdm-mcxn947/minimal
|
||||||
- path: rp/pico-w
|
nxp/frdm-rw612/minimal
|
||||||
- path: rp/w5500-evb-pico
|
nxp/rt1060-evkb/minimal
|
||||||
- path: rp/w55rp20-evb-pico
|
nxp/rt1170-evkb/minimal
|
||||||
- path: rp/pico-rndis
|
- group: rp-ti
|
||||||
- path: stm32/nucleo-f429zi/minimal
|
examples: |
|
||||||
- path: stm32/nucleo-f746zg/minimal
|
rp/pico-w
|
||||||
- path: stm32/nucleo-f746zg/minimal-pppoe
|
rp/w5500-evb-pico
|
||||||
- path: stm32/nucleo-f756zg/minimal
|
rp/w55rp20-evb-pico
|
||||||
- path: stm32/nucleo-h563zi/minimal
|
rp/pico-rndis
|
||||||
- path: stm32/nucleo-h723zg/minimal
|
ti/ek-tm4c1294xl-make-baremetal-builtin
|
||||||
- path: stm32/nucleo-h743zi/minimal
|
ti/ek-tm4c1294xl-make-freertos-builtin
|
||||||
- path: stm32/nucleo-n657x0-q/minimal
|
- group: stm32-1
|
||||||
- path: stm32/nucleo-u5a5zj-q/minimal
|
examples: |
|
||||||
- path: stm32/portenta-h7/minimal
|
stm32/nucleo-f429zi/minimal
|
||||||
- path: stm32/stm32h573i-dk/minimal
|
stm32/nucleo-f746zg/minimal
|
||||||
- path: stm32/stm32h747i-disco/minimal
|
stm32/nucleo-f746zg/minimal-pppoe
|
||||||
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
|
stm32/nucleo-f756zg/minimal
|
||||||
- path: ti/ek-tm4c1294xl-make-freertos-builtin
|
stm32/nucleo-h563zi/minimal
|
||||||
name: ${{ matrix.example.path }}
|
stm32/nucleo-h723zg/minimal
|
||||||
|
- 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 +649,41 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
include:
|
||||||
- path: mqtt/mqtt-client/microchip/same54-xpro
|
- group: other
|
||||||
- path: nxp/nxp-frdmk66f-freertos
|
examples: |
|
||||||
- path: nxp/nxp-lpcxpresso54s018m-freertos
|
mqtt/mqtt-client/microchip/same54-xpro
|
||||||
- path: nxp/nxp-mimxrt1020-freertos
|
rp/pico-rmii
|
||||||
- path: nxp/nxp-evkbimxrt1050-lwip-freertos
|
stm32/nucleo-f746zg/minimal-rm2
|
||||||
- path: nxp/nxp-evkmimxrt1020-lwip-freertos
|
stm32/nucleo-f746zg/minimal-rndis
|
||||||
- path: nxp/nxp-evkmimxrt1024-lwip-freertos
|
stm32/nucleo-f429zi/minimal-rm2
|
||||||
- path: nxp/nxp-evkmimxrt1060-lwip-freertos
|
stm32/nucleo-f429zi/minimal-rndis
|
||||||
- path: nxp/nxp-evkmimxrt1064-lwip-freertos
|
stm32/nucleo-g031k8/minimal-w5500
|
||||||
- path: nxp/nxp-evkmimxrt1160-cm7-lwip-freertos
|
ti/ti-ek-tm4c1294xl-http-server
|
||||||
- path: nxp/nxp-evkmimxrt1170-cm7-lwip-freertos
|
ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
|
||||||
- path: nxp/nxp-frdmk64f-lwip-freertos
|
name: generic ARM ${{ matrix.group }}
|
||||||
- path: nxp/nxp-frdmk66f-lwip-freertos
|
|
||||||
- path: nxp/nxp-lpcxpresso54018-lwip-freertos
|
|
||||||
- path: nxp/nxp-lpcxpresso54608-lwip-freertos
|
|
||||||
- path: nxp/nxp-lpcxpresso54618-lwip-freertos
|
|
||||||
- path: nxp/nxp-lpcxpresso54628-lwip-freertos
|
|
||||||
- path: nxp/nxp-twrk65f180m-lwip-freertos
|
|
||||||
- path: nxp/nxp-twrkv58f220m-lwip-freertos
|
|
||||||
- path: rp/pico-rmii
|
|
||||||
- path: stm32/nucleo-f746zg/minimal-rm2
|
|
||||||
- path: stm32/nucleo-f746zg/minimal-rndis
|
|
||||||
- path: stm32/nucleo-f429zi/minimal-rm2
|
|
||||||
- path: stm32/nucleo-f429zi/minimal-rndis
|
|
||||||
- path: stm32/nucleo-g031k8/minimal-w5500
|
|
||||||
- path: ti/ti-ek-tm4c1294xl-http-server
|
|
||||||
- path: ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
|
|
||||||
name: ${{ matrix.example.path }}
|
|
||||||
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 +705,40 @@ 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
|
renesas/ek-ra6m4-make-baremetal-builtin
|
||||||
- path: renesas/ek-ra6m4-make-baremetal-builtin
|
- group: builtin
|
||||||
name: ${{ matrix.example.path }} ${{ matrix.ssl }}
|
cflags: -DMG_TLS=MG_TLS_BUILTIN
|
||||||
|
examples: |
|
||||||
|
http/device-dashboard/microchip/same54-xpro
|
||||||
|
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, .created_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
|
||||||
@@ -53,16 +53,6 @@ jobs:
|
|||||||
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/stm32/nucleo-h563zi/minimal test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
- run: make -C tutorials/stm32/nucleo-h563zi/minimal test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
|
||||||
test_rt1020:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
with: { fetch-depth: 3 }
|
|
||||||
- name: baremetal
|
|
||||||
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- run: make -C tutorials/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- run: make -C tutorials/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
test_rt1060:
|
test_rt1060:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -70,8 +60,7 @@ jobs:
|
|||||||
with: { fetch-depth: 3 }
|
with: { fetch-depth: 3 }
|
||||||
- name: baremetal
|
- name: baremetal
|
||||||
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
- run: make -C tutorials/nxp/rt1060-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
- run: make -C tutorials/nxp/rt1060-evkb/minimal build
|
||||||
- run: make -C tutorials/nxp/rt1060-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
test_rt1170:
|
test_rt1170:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -80,8 +69,7 @@ jobs:
|
|||||||
with: { fetch-depth: 3 }
|
with: { fetch-depth: 3 }
|
||||||
- name: baremetal
|
- name: baremetal
|
||||||
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
- run: make -C tutorials/nxp/rt1170-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
- run: make -C tutorials/nxp/rt1170-evkb/minimal build
|
||||||
- run: make -C tutorials/nxp/rt1170-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
test_ra6m4:
|
test_ra6m4:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -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 }}
|
||||||
@@ -143,15 +148,17 @@ jobs:
|
|||||||
- path: infineon/xmc47_relax-make-baremetal-builtin
|
- path: infineon/xmc47_relax-make-baremetal-builtin
|
||||||
- path: infineon/xmc_plt2go_4400-make-baremetal-builtin
|
- path: infineon/xmc_plt2go_4400-make-baremetal-builtin
|
||||||
- path: infineon/xmc72_evk-make-baremetal-builtin
|
- path: infineon/xmc72_evk-make-baremetal-builtin
|
||||||
- path: nxp/frdm-mcxn947-make-baremetal-builtin
|
- path: nxp/frdm-imxrt1186/minimal
|
||||||
- path: nxp/frdm-mcxn947-make-freertos-builtin
|
- path: nxp/frdm-mcxe247/minimal
|
||||||
- path: nxp/rt1020-evk-make-baremetal-builtin
|
- path: nxp/frdm-mcxn947/minimal
|
||||||
- path: nxp/rt1060-evk-make-baremetal-builtin
|
- path: nxp/frdm-rw612/minimal
|
||||||
- path: nxp/rt1170-evk-make-baremetal-builtin
|
- path: nxp/rt1060-evkb/minimal
|
||||||
|
- path: nxp/rt1170-evkb/minimal
|
||||||
- path: stm32/nucleo-f429zi/minimal
|
- path: stm32/nucleo-f429zi/minimal
|
||||||
- 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
|
||||||
@@ -163,7 +170,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: |
|
- run: |
|
||||||
if ./test/match_changed_files.sh '^src'; then
|
if ./test/match_changed_files.sh '^src|^tutorials/${{ matrix.example.path }}'; then
|
||||||
echo GO=1 >> $GITHUB_ENV
|
echo GO=1 >> $GITHUB_ENV
|
||||||
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
fi
|
fi
|
||||||
@@ -242,9 +249,6 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: http/device-dashboard/microchip/same54-xpro
|
- path: http/device-dashboard/microchip/same54-xpro
|
||||||
- path: nxp/rt1020-evk-make-freertos-builtin
|
|
||||||
- path: nxp/rt1060-evk-make-freertos-builtin
|
|
||||||
- path: nxp/rt1170-evk-make-freertos-builtin
|
|
||||||
- path: renesas/ek-ra6m4-make-baremetal-builtin
|
- path: renesas/ek-ra6m4-make-baremetal-builtin
|
||||||
- path: rp/pico-rndis
|
- path: rp/pico-rndis
|
||||||
- path: rp/pico-w
|
- path: rp/pico-w
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
+2665
-583
File diff suppressed because it is too large.
Load diff
+668
-34
@@ -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,239 @@ 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) && !defined(MG_ENABLE_DRIVER_NETC)
|
||||||
|
#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
|
||||||
|
#elif defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
#define MG_ENABLE_DRIVER_NETC 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
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1180
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IRAM
|
||||||
|
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
#define MG_IRAM \
|
||||||
|
__attribute__((noinline, section(".ramfunc.$SRAM_ITC_cm33")))
|
||||||
|
#else
|
||||||
|
#define MG_IRAM __attribute__((noinline, section(".data_RAM2")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ETH_RAM
|
||||||
|
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
#define MG_ETH_RAM __attribute__((section(".bss.$ETH_RAM")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_SET_MAC_ADDRESS
|
||||||
|
#if defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
|
||||||
|
// No OCOTP_FUSES present on RT1186 to generate the UUID
|
||||||
|
#define MG_SET_MAC_ADDRESS(mac) (void) (mac)
|
||||||
|
#else
|
||||||
|
#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
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IMXRT_WDOG1_TIMEOUT_MS
|
||||||
|
#define MG_IMXRT_WDOG1_TIMEOUT_MS 10000U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
|
||||||
|
#define MG_IMXRT_RTWDOG1_UNLOCK() \
|
||||||
|
do { \
|
||||||
|
if ((RTWDOG1->CS & RTWDOG_CS_CMD32EN_MASK) != 0U) { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_UPDATE_KEY; \
|
||||||
|
} else { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_UPDATE_KEY & 0xffffU; \
|
||||||
|
RTWDOG1->CNT = RTWDOG_UPDATE_KEY >> 16; \
|
||||||
|
} \
|
||||||
|
while ((RTWDOG1->CS & RTWDOG_CS_ULK_MASK) == 0U) (void) 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define MG_IMXRT_RTWDOG1_FEED() \
|
||||||
|
do { \
|
||||||
|
uint32_t primask_ = __get_PRIMASK(); \
|
||||||
|
__disable_irq(); \
|
||||||
|
if ((RTWDOG1->CS & RTWDOG_CS_CMD32EN_MASK) != 0U) { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_REFRESH_KEY; \
|
||||||
|
} else { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_REFRESH_KEY & 0xffffU; \
|
||||||
|
RTWDOG1->CNT = RTWDOG_REFRESH_KEY >> 16; \
|
||||||
|
} \
|
||||||
|
if (primask_ == 0U) __enable_irq(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||||
|
do { \
|
||||||
|
uint32_t primask_ = __get_PRIMASK(); \
|
||||||
|
SRC_GENERAL_REG->SRMASK &= ~SRC_GENERAL_SRMASK_WDOG1_MASK_MASK; \
|
||||||
|
__disable_irq(); \
|
||||||
|
MG_IMXRT_RTWDOG1_UNLOCK(); \
|
||||||
|
RTWDOG1->WIN = 0U; \
|
||||||
|
RTWDOG1->TOVAL = (MG_IMXRT_WDOG1_TIMEOUT_MS + 7U) / 8U; \
|
||||||
|
RTWDOG1->CS = RTWDOG_CS_EN_MASK | RTWDOG_CS_UPDATE_MASK | \
|
||||||
|
RTWDOG_CS_CLK(1U) | RTWDOG_CS_PRES_MASK | \
|
||||||
|
RTWDOG_CS_CMD32EN_MASK; \
|
||||||
|
while ((RTWDOG1->CS & RTWDOG_CS_RCS_MASK) == 0U) (void) 0; \
|
||||||
|
if (primask_ == 0U) __enable_irq(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_FEED() MG_IMXRT_RTWDOG1_FEED()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GPR5 survives RTWDOG global resets and is not reserved by the boot ROM.
|
||||||
|
#ifndef MG_OTA_STATE_GET
|
||||||
|
#define MG_OTA_STATE_GET() SRC_GENERAL_REG->GPR[5]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_STATE_SET
|
||||||
|
#define MG_OTA_STATE_SET(v) (SRC_GENERAL_REG->GPR[5] = (uint32_t) (v))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
#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 +1068,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 +1187,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)
|
||||||
|
|
||||||
@@ -1164,6 +1456,14 @@ struct timeval {
|
|||||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for
|
||||||
#endif // Mongoose built-in network stack
|
#endif // Mongoose built-in network stack
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_TCPIP_TCPRTX
|
||||||
|
#define MG_ENABLE_TCPIP_TCPRTX 0 // TCP retransmission queue
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_DHCPNAME_SIZE
|
||||||
|
#define MG_TCPIP_DHCPNAME_SIZE 18 // struct mg_tcpip_if :: dhcp_name size
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223)
|
#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223)
|
||||||
#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
|
#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
|
||||||
#endif
|
#endif
|
||||||
@@ -1200,10 +1500,6 @@ struct timeval {
|
|||||||
#define MG_SET_MAC_ADDRESS(mac)
|
#define MG_SET_MAC_ADDRESS(mac)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_TCPIP_DHCPNAME_SIZE
|
|
||||||
#define MG_TCPIP_DHCPNAME_SIZE 18 // struct mg_tcpip_if :: dhcp_name size
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_SET_WIFI_CONFIG
|
#ifndef MG_SET_WIFI_CONFIG
|
||||||
#define MG_SET_WIFI_CONFIG(ifp, driver_data)
|
#define MG_SET_WIFI_CONFIG(ifp, driver_data)
|
||||||
#endif
|
#endif
|
||||||
@@ -1460,8 +1756,8 @@ typedef size_t (*mg_pm_t)(mg_pfn_t fn, void *arg, va_list *);
|
|||||||
// - mg_print_base64 - prints a buffer as a base64-encoded string
|
// - mg_print_base64 - prints a buffer as a base64-encoded string
|
||||||
// - mg_print_esc - prints a JSON-escaped string
|
// - mg_print_esc - prints a JSON-escaped string
|
||||||
// - mg_print_hex - prints a buffer as a hex string
|
// - mg_print_hex - prints a buffer as a hex string
|
||||||
// - mg_print_ip - prints an IP address in a struct mg_str
|
// - mg_print_ip - prints an IP address in a struct mg_addr
|
||||||
// - mg_print_ip_port - prints IP address and port in a struct mg_str
|
// - mg_print_ip_port - prints IP address and port in a struct mg_addr
|
||||||
// - mg_print_ip4 - prints an IPv4 address
|
// - mg_print_ip4 - prints an IPv4 address
|
||||||
// - mg_print_ip6 - prints an IPv6 address
|
// - mg_print_ip6 - prints an IPv6 address
|
||||||
// - mg_print_mac - prints a MAC address
|
// - mg_print_mac - prints a MAC address
|
||||||
@@ -1519,15 +1815,15 @@ char *mg_mprintf(const char *fmt, ...);
|
|||||||
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
|
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
|
||||||
|
|
||||||
// Built-in %M/%m printer functions. Each reads its argument(s) from ap.
|
// Built-in %M/%m printer functions. Each reads its argument(s) from ap.
|
||||||
size_t mg_print_base64(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len
|
size_t mg_print_base64(mg_pfn_t, void *arg, va_list *ap); // expects: int len, uint8_t *buf
|
||||||
size_t mg_print_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
size_t mg_print_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
||||||
size_t mg_print_hex(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len
|
size_t mg_print_hex(mg_pfn_t, void *arg, va_list *ap); // expects: int len, uint8_t *buf
|
||||||
size_t mg_print_ip(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
size_t mg_print_ip(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
||||||
size_t mg_print_ip_port(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
size_t mg_print_ip_port(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
||||||
size_t mg_print_ip4(mg_pfn_t, void *arg, va_list *ap); // expects: uint32_t *ipv4
|
size_t mg_print_ip4(mg_pfn_t, void *arg, va_list *ap); // expects: uint32_t *ipv4
|
||||||
size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[16] ipv6
|
size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[16] ipv6
|
||||||
size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac
|
size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac
|
||||||
size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint64_t
|
size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[8] ieee64
|
||||||
size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address
|
size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address
|
||||||
size_t mg_print_html_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
size_t mg_print_html_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
||||||
|
|
||||||
@@ -1731,6 +2027,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 +2045,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 +2058,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 +2281,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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2134,6 +2443,7 @@ struct mg_mgr {
|
|||||||
struct mg_tcpip_if *ifp; // Builtin TCP/IP stack: network interface pointer
|
struct mg_tcpip_if *ifp; // Builtin TCP/IP stack: network interface pointer
|
||||||
size_t extraconnsize; // Builtin TCP/IP stack: extra bytes allocated per connection
|
size_t extraconnsize; // Builtin TCP/IP stack: extra bytes allocated per connection
|
||||||
union mg_pipe pipe; // Socketpair write-end / queue, used by mg_wakeup()
|
union mg_pipe pipe; // Socketpair write-end / queue, used by mg_wakeup()
|
||||||
|
struct mg_rpc *rpcs; // Linked list of registered RPC handlers
|
||||||
#if MG_ENABLE_FREERTOS_TCP
|
#if MG_ENABLE_FREERTOS_TCP
|
||||||
SocketSet_t ss; // FreeRTOS-TCP socket set
|
SocketSet_t ss; // FreeRTOS-TCP socket set
|
||||||
#endif
|
#endif
|
||||||
@@ -2158,6 +2468,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 +2552,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 *);
|
||||||
@@ -2516,6 +2835,29 @@ size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
|
|||||||
// Returns the HTTP status code from a parsed response message (e.g. 200, 404).
|
// Returns the HTTP status code from a parsed response message (e.g. 200, 404).
|
||||||
int mg_http_status(const struct mg_http_message *hm);
|
int mg_http_status(const struct mg_http_message *hm);
|
||||||
|
|
||||||
|
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
||||||
|
// Call it from your connection handler on every event.
|
||||||
|
//
|
||||||
|
// Callback upload_fn(hm, data, user_data) is called:
|
||||||
|
// - at start: hm is set, data is NULL. Open the file, set user_data.
|
||||||
|
// - per chunk: hm is NULL, data is set. Chunks are 512 bytes aligned
|
||||||
|
// except the last one.
|
||||||
|
// - at end: both NULL. Close the file.
|
||||||
|
// After the last chunk, "200 ok" is sent and the connection is drained.
|
||||||
|
// Example:
|
||||||
|
// mg_http_stream_body(c, ev, ev_data, mg_str("/fs/*"), upload);
|
||||||
|
// Full example:
|
||||||
|
// tutorials/http/file-upload-single-post
|
||||||
|
// Related APIs:
|
||||||
|
// mg_http_start_upload(), mg_http_start_ota()
|
||||||
|
// Notes:
|
||||||
|
// Requires Content-Length, chunked bodies are not streamed. user_data is
|
||||||
|
// &c->fn_data. While streaming, c->pfn is NULL
|
||||||
|
void mg_http_stream_body(struct mg_connection *c, int ev, void *ev_data,
|
||||||
|
struct mg_str uri_pattern,
|
||||||
|
void (*upload_fn)(struct mg_http_message *,
|
||||||
|
struct mg_str *, void **user_data));
|
||||||
|
|
||||||
|
|
||||||
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||||
const char *fullpath);
|
const char *fullpath);
|
||||||
@@ -2602,12 +2944,18 @@ void mg_tls_free(struct mg_connection *);
|
|||||||
long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
|
long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
|
||||||
long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
|
long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
|
||||||
size_t mg_tls_pending(struct mg_connection *);
|
size_t mg_tls_pending(struct mg_connection *);
|
||||||
void mg_tls_flush(struct mg_connection *);
|
long mg_tls_flush(struct mg_connection *);
|
||||||
void mg_tls_handshake(struct mg_connection *);
|
void mg_tls_handshake(struct mg_connection *);
|
||||||
void mg_tls_ctx_init(struct mg_mgr *);
|
void mg_tls_ctx_init(struct mg_mgr *);
|
||||||
void mg_tls_ctx_free(struct mg_mgr *);
|
void mg_tls_ctx_free(struct mg_mgr *);
|
||||||
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
|
#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 +3156,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 +3710,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 +3826,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 +3856,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 +3872,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 +4308,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 +4330,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 +4453,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 +4489,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 +4737,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
|
||||||
@@ -4393,6 +4776,7 @@ extern void mg_mqtt_poll(struct mg_mgr *);
|
|||||||
#define MG_OTA_RT1060 302 // IMXRT1060
|
#define MG_OTA_RT1060 302 // IMXRT1060
|
||||||
#define MG_OTA_RT1064 303 // IMXRT1064
|
#define MG_OTA_RT1064 303 // IMXRT1064
|
||||||
#define MG_OTA_RT1170 304 // IMXRT1170
|
#define MG_OTA_RT1170 304 // IMXRT1170
|
||||||
|
#define MG_OTA_RT1180 305 // IMXRT1180
|
||||||
#define MG_OTA_MCXN 310 // MCXN947
|
#define MG_OTA_MCXN 310 // MCXN947
|
||||||
#define MG_OTA_RW612 320 // FRDM-RW612
|
#define MG_OTA_RW612 320 // FRDM-RW612
|
||||||
#define MG_OTA_FLASH 900 // OTA via internal flash
|
#define MG_OTA_FLASH 900 // OTA via internal flash
|
||||||
@@ -4501,8 +4885,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 +4898,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,9 +4939,200 @@ 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 *);
|
||||||
|
// Device health monitoring
|
||||||
|
//
|
||||||
|
// A health provider owns the report format and returns it as an opaque blob.
|
||||||
|
// Applications and transports use mg_health_get_blob() without knowing how the
|
||||||
|
// report was collected or encoded.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define MG_HEALTH_NONE 0
|
||||||
|
#define MG_HEALTH_CORTEX 1
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH
|
||||||
|
#define MG_HEALTH MG_HEALTH_NONE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_MAGIC
|
||||||
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Why the device restarted. Hardware usually reports several flags at once,
|
||||||
|
// so these are ordered by how much they tell you: the most specific cause a
|
||||||
|
// device reports wins
|
||||||
|
enum mg_health_reason {
|
||||||
|
MG_HEALTH_RESET_UNKNOWN = 0, // Nothing reported, or no masks configured
|
||||||
|
MG_HEALTH_RESET_POWER, // Power-on reset, RAM contents are lost
|
||||||
|
MG_HEALTH_RESET_BROWNOUT, // Supply dipped below the brown-out level
|
||||||
|
MG_HEALTH_RESET_PIN, // External reset pin, e.g. the reset button
|
||||||
|
MG_HEALTH_RESET_SOFTWARE, // Firmware asked for it, e.g. NVIC_SystemReset
|
||||||
|
MG_HEALTH_RESET_LOWPOWER, // Illegal or failed low-power transition
|
||||||
|
MG_HEALTH_RESET_WATCHDOG, // Watchdog fired: the application stopped
|
||||||
|
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct mg_str mg_health_blob;
|
||||||
|
|
||||||
|
struct mg_health {
|
||||||
|
char magic[4];
|
||||||
|
mg_health_blob (*get_blob)(void *);
|
||||||
|
void *fn_data;
|
||||||
|
uint32_t reset_reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct mg_health mg_health_record;
|
||||||
|
|
||||||
|
// Return true if the record holds valid data, i.e. it survived the reset and
|
||||||
|
// was written by this firmware layout
|
||||||
|
static inline bool mg_health_valid(void) {
|
||||||
|
static const char magic[] = MG_HEALTH_MAGIC;
|
||||||
|
return memcmp(mg_health_record.magic, magic,
|
||||||
|
sizeof(mg_health_record.magic)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select and initialise the configured health provider. Call after
|
||||||
|
// initializing the board-specific persistent-storage
|
||||||
|
void mg_health_init(void);
|
||||||
|
|
||||||
|
// Return the provider's opaque report. The returned memory is provider-owned
|
||||||
|
// and remains valid until the next call to this function
|
||||||
|
struct mg_str mg_health_get_blob(void);
|
||||||
|
|
||||||
|
static inline int mg_health_reason(void) {
|
||||||
|
return mg_health_valid() ? (int) mg_health_record.reset_reason
|
||||||
|
: MG_HEALTH_RESET_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *mg_health_reason_str(int reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case MG_HEALTH_RESET_POWER:
|
||||||
|
return "power";
|
||||||
|
case MG_HEALTH_RESET_BROWNOUT:
|
||||||
|
return "brownout";
|
||||||
|
case MG_HEALTH_RESET_PIN:
|
||||||
|
return "pin";
|
||||||
|
case MG_HEALTH_RESET_SOFTWARE:
|
||||||
|
return "software";
|
||||||
|
case MG_HEALTH_RESET_LOWPOWER:
|
||||||
|
return "lowpower";
|
||||||
|
case MG_HEALTH_RESET_WATCHDOG:
|
||||||
|
return "watchdog";
|
||||||
|
case MG_HEALTH_RESET_FAULT:
|
||||||
|
return "fault";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define MG_HEALTH_CORTEX_VERSION 1U
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_STORAGE
|
||||||
|
#define MG_HEALTH_CORTEX_STORAGE NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_STORAGE_SIZE
|
||||||
|
#define MG_HEALTH_CORTEX_STORAGE_SIZE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_STACK_WORDS
|
||||||
|
#define MG_HEALTH_CORTEX_STACK_WORDS 980U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_REPORT_SIZE
|
||||||
|
#define MG_HEALTH_CORTEX_REPORT_SIZE 16384U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Storage operations are deliberately format-agnostic. H723 uses the default
|
||||||
|
// direct-memory implementation; boards with different retained storage can
|
||||||
|
// replace these callbacks before calling MG_HEALTH_INIT()
|
||||||
|
struct mg_health_cortex {
|
||||||
|
void *storage;
|
||||||
|
size_t storage_size;
|
||||||
|
bool (*init)(void *, size_t);
|
||||||
|
bool (*read)(const void *, size_t, void *, size_t);
|
||||||
|
bool (*write)(void *, size_t, const void *, size_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mg_health_cortex_record {
|
||||||
|
char magic[4];
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
uint32_t cpuid;
|
||||||
|
uint32_t exception_sp;
|
||||||
|
uint32_t sp;
|
||||||
|
uint32_t exc_return;
|
||||||
|
int32_t exception_valid;
|
||||||
|
int32_t stack_valid;
|
||||||
|
|
||||||
|
uint32_t r0;
|
||||||
|
uint32_t r1;
|
||||||
|
uint32_t r2;
|
||||||
|
uint32_t r3;
|
||||||
|
uint32_t r4;
|
||||||
|
uint32_t r5;
|
||||||
|
uint32_t r6;
|
||||||
|
uint32_t r7;
|
||||||
|
uint32_t r8;
|
||||||
|
uint32_t r9;
|
||||||
|
uint32_t r10;
|
||||||
|
uint32_t r11;
|
||||||
|
uint32_t r12;
|
||||||
|
uint32_t lr;
|
||||||
|
uint32_t pc;
|
||||||
|
uint32_t psr;
|
||||||
|
|
||||||
|
uint32_t cfsr;
|
||||||
|
uint32_t hfsr;
|
||||||
|
uint32_t dfsr;
|
||||||
|
uint32_t afsr;
|
||||||
|
uint32_t bfar;
|
||||||
|
uint32_t mmfar;
|
||||||
|
uint32_t abfsr;
|
||||||
|
|
||||||
|
uint32_t stack_word_count;
|
||||||
|
uint32_t stack_words[MG_HEALTH_CORTEX_STACK_WORDS];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct mg_health_cortex mg_health_cortex;
|
||||||
|
|
||||||
|
void mg_health_cortex_init(struct mg_health_cortex *);
|
||||||
|
struct mg_str mg_health_cortex_get_blob(void *);
|
||||||
|
// Cloud connector API to https://mdash.net - device management cloud
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MG_MDASH_URL
|
||||||
|
#define MG_MDASH_URL "wss://mdash.net/api/v2/rpc"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_MDASH_KEY
|
||||||
|
#define MG_MDASH_KEY ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_MDASH_FIRMWARE_VERSION
|
||||||
|
#define MG_MDASH_FIRMWARE_VERSION "1.0.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_MDASH
|
||||||
|
#define MG_ENABLE_MDASH 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void mg_mdash_init(struct mg_mgr *);
|
||||||
|
void mg_mdash_poll(struct mg_mgr *);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -4624,7 +5207,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)
|
||||||
@@ -4735,6 +5318,7 @@ struct mg_tcpip_if {
|
|||||||
bool enable_req_sntp; // DHCP client requests an SNTP server address
|
bool enable_req_sntp; // DHCP client requests an SNTP server address
|
||||||
bool enable_fcs_check; // Verify and strip FCS from received frames
|
bool enable_fcs_check; // Verify and strip FCS from received frames
|
||||||
bool enable_mac_check; // Drop frames not addressed to this MAC
|
bool enable_mac_check; // Drop frames not addressed to this MAC
|
||||||
|
bool enable_tcp_retransmit; // Enable optional TCP retransmission queue
|
||||||
bool update_mac_hash_table; // Signal driver to refresh MAC multicast hash table
|
bool update_mac_hash_table; // Signal driver to refresh MAC multicast hash table
|
||||||
bool is_ip_changed; // Set by stack when IP changes; triggers connection restart
|
bool is_ip_changed; // Set by stack when IP changes; triggers connection restart
|
||||||
struct mg_tcpip_driver *driver; // Hardware driver; must be set before mg_tcpip_init()
|
struct mg_tcpip_driver *driver; // Hardware driver; must be set before mg_tcpip_init()
|
||||||
@@ -4828,6 +5412,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
|
|||||||
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
||||||
|
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
||||||
|
|
||||||
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||||
// Populate and assign to the driver's driver_data field.
|
// Populate and assign to the driver's driver_data field.
|
||||||
@@ -5172,6 +5757,55 @@ struct mg_tcpip_driver_imxrt_data {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP && \
|
||||||
|
defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
|
||||||
|
|
||||||
|
struct mg_tcpip_driver_netc_data {
|
||||||
|
int mdc_cr;
|
||||||
|
uint8_t phy_addr;
|
||||||
|
|
||||||
|
// Selects the physical Ethernet link (ETH0 through ETH3)
|
||||||
|
// Its switch port is bridged to pseudo-port 4,
|
||||||
|
// which connects internally to ENETC1
|
||||||
|
// IMXRT1180 RM 53.4.1.1 NETC Block diagram
|
||||||
|
uint8_t link;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_PHY_ADDR
|
||||||
|
#define MG_TCPIP_PHY_ADDR 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_DRIVER_MDC_CR
|
||||||
|
#define MG_DRIVER_MDC_CR 48
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_NETC_LINK
|
||||||
|
#define MG_TCPIP_NETC_LINK 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct mg_tcpip_driver;
|
||||||
|
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
||||||
|
|
||||||
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
|
do { \
|
||||||
|
static struct mg_tcpip_driver_netc_data driver_data_; \
|
||||||
|
static struct mg_tcpip_if mif_; \
|
||||||
|
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
||||||
|
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
||||||
|
driver_data_.link = MG_TCPIP_NETC_LINK; \
|
||||||
|
mif_.ip = MG_TCPIP_IP; \
|
||||||
|
mif_.mask = MG_TCPIP_MASK; \
|
||||||
|
mif_.gw = MG_TCPIP_GW; \
|
||||||
|
mif_.driver = &mg_tcpip_driver_netc; \
|
||||||
|
mif_.driver_data = &driver_data_; \
|
||||||
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
|
MG_INFO(("Driver: netc, MAC: %M", mg_print_mac, mif_.mac)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if MG_ENABLE_TCPIP && \
|
#if MG_ENABLE_TCPIP && \
|
||||||
defined(MG_ENABLE_DRIVER_NXP_WIFI) && MG_ENABLE_DRIVER_NXP_WIFI
|
defined(MG_ENABLE_DRIVER_NXP_WIFI) && MG_ENABLE_DRIVER_NXP_WIFI
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ patch_linker_script() {
|
|||||||
HUART=`perl -nle 'print \$1 if /^UART_HandleTypeDef (.+);/' $MAIN_C`
|
HUART=`perl -nle 'print \$1 if /^UART_HandleTypeDef (.+);/' $MAIN_C`
|
||||||
grep -q mongoose.h $MAIN_C || perl -i -ne 'print; print "#include \"mongoose.h\"\n" if /BEGIN Includes/' $MAIN_C
|
grep -q mongoose.h $MAIN_C || perl -i -ne 'print; print "#include \"mongoose.h\"\n" if /BEGIN Includes/' $MAIN_C
|
||||||
grep -q '^static void log_fn' $MAIN_C || perl -i -ne "print; print \"static void log_fn(char ch, void *param) {\n HAL_UART_Transmit(param, (unsigned char *) &ch, 1, HAL_MAX_DELAY);\n}\n\" if /USER CODE BEGIN 0/" $MAIN_C
|
grep -q '^static void log_fn' $MAIN_C || perl -i -ne "print; print \"static void log_fn(char ch, void *param) {\n HAL_UART_Transmit(param, (unsigned char *) &ch, 1, HAL_MAX_DELAY);\n}\n\" if /USER CODE BEGIN 0/" $MAIN_C
|
||||||
grep -q 'mg_mgr_init' $MAIN_C || perl -i -ne 'print; print " struct mg_mgr mgr;\n mg_mgr_init(&mgr);\n mg_log_set_fn(log_fn, &'"${HUART}"');\n\n" if /USER CODE BEGIN WHILE/' $MAIN_C
|
grep -q 'mg_mgr_init' $MAIN_C || perl -i -ne 'print; print " struct mg_mgr mgr;\n mg_log_set_fn(log_fn, &'"${HUART}"');\n mg_mgr_init(&mgr);\n mg_log_set_fn(log_fn, &'"${HUART}"');\n\n" if /USER CODE BEGIN WHILE/' $MAIN_C
|
||||||
grep -q 'mg_mgr_init' $MAIN_C && grep -q 'mg_log_set_fn' $MAIN_C || perl -i -ne 'print; print " mg_log_set_fn(log_fn, &'"${HUART}"');\n" if /mg_mgr_init/' $MAIN_C
|
|
||||||
grep -q 'mg_mgr_poll' $MAIN_C || perl -i -ne 'print " mg_mgr_poll(&mgr, 0);\n" if /USER CODE END WHILE/; print;' $MAIN_C
|
grep -q 'mg_mgr_poll' $MAIN_C || perl -i -ne 'print " mg_mgr_poll(&mgr, 0);\n" if /USER CODE END WHILE/; print;' $MAIN_C
|
||||||
patch_linker_script $DIR/STM32H723XG_FLASH.ld
|
patch_linker_script $DIR/STM32H723XG_FLASH.ld
|
||||||
patch_linker_script $DIR/STM32H743XX_FLASH.ld
|
patch_linker_script $DIR/STM32H743XX_FLASH.ld
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Security Scan Area: common_misc
|
||||||
|
|
||||||
|
## Target
|
||||||
|
|
||||||
|
Area: common_misc
|
||||||
|
|
||||||
|
This area covers common utility code, parsing/string/formatting helpers, URL handling, hash/base64 helpers, filesystem backends, little filesystem integration, and flash support.
|
||||||
|
|
||||||
|
Primary files:
|
||||||
|
- `src/base64.c`
|
||||||
|
- `src/fmt.c`
|
||||||
|
- `src/json.c`
|
||||||
|
- `src/log.c`
|
||||||
|
- `src/md5.c`
|
||||||
|
- `src/printf.c`
|
||||||
|
- `src/sha1.c`
|
||||||
|
- `src/sha256.c`
|
||||||
|
- `src/str.c`
|
||||||
|
- `src/url.c`
|
||||||
|
- `src/util.c`
|
||||||
|
- `src/fs*.c`
|
||||||
|
- `src/lfs.c`
|
||||||
|
- `src/flash.c`
|
||||||
|
|
||||||
|
Perform a focused security review centered on these files and this security surface.
|
||||||
|
|
||||||
|
Supporting code elsewhere under `src/` may be inspected when necessary to establish reachability, data flow, state, validation, mitigation, or impact, but do not inspect or report code outside `src/` or independently review unrelated areas.
|
||||||
|
|
||||||
|
## Area-Specific Security Guidance
|
||||||
|
|
||||||
|
**Filesystem Backends:**
|
||||||
|
- Review packed filesystem code and filesystem backends for traversal, unauthorized file read/write, overwrite, or truncation.
|
||||||
|
- Check path canonicalization across POSIX, Windows, embedded filesystems, packed filesystems, and custom `mg_fs` implementations.
|
||||||
|
- Look for trailing slash confusion, NUL byte truncation, and case sensitivity differences in backend path handling.
|
||||||
|
|
||||||
|
**URL Parsing:**
|
||||||
|
- Review URL parsing for scheme, host, port, IPv6 literal, userinfo, percent-encoding, embedded NUL, empty host, default port, and path/query boundary confusion.
|
||||||
|
- Check for SSRF-relevant behavior only when attacker input can control the destination host, protocol, or network boundary. Path-only control is not sufficient.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Security Scan Area: mongoose_core
|
||||||
|
|
||||||
|
## Target
|
||||||
|
|
||||||
|
Area: mongoose_core
|
||||||
|
|
||||||
|
This area covers the core event/network/connection machinery, iobuf handling, queues, socket transport, timers, and BSD abstraction.
|
||||||
|
|
||||||
|
Primary files:
|
||||||
|
- `src/event.c`
|
||||||
|
- `src/iobuf.c`
|
||||||
|
- `src/net.c`
|
||||||
|
- `src/queue.c`
|
||||||
|
- `src/sock.c`
|
||||||
|
- `src/timer.c`
|
||||||
|
- `src/bsd.c`
|
||||||
|
|
||||||
|
Perform a focused security review centered on these files and this security surface.
|
||||||
|
|
||||||
|
Supporting code elsewhere under `src/` may be inspected when necessary to establish reachability, data flow, state, validation, mitigation, or impact, but do not inspect or report code outside `src/` or independently review unrelated areas.
|
||||||
|
|
||||||
|
## Area-Specific Security Guidance
|
||||||
|
|
||||||
|
**Protocol State Machine and Event-Driven Lifecycle Bugs:**
|
||||||
|
- Review transitions between listening, accepted, connecting, resolving, TLS handshaking, HTTP, WebSocket, MQTT, draining, closing, and closed states.
|
||||||
|
- Check whether malformed input can cause protocol handlers to process data in the wrong state, process stale data after close, or invoke callbacks with invalid event data.
|
||||||
|
- Look for use-after-free or stale pointer use when callbacks call `mg_close_conn`, mark a connection closing, mutate iobufs, change protocol handlers, or initiate TLS/WebSocket/MQTT transitions.
|
||||||
|
- Verify that partial reads/writes, backpressure, `is_full`, draining, TLS pending states, and send-buffer mutations cannot cause duplicated sends, skipped validation, or parser confusion.
|
||||||
|
- Review timer and wakeup handling for stale user data pointers, cross-thread queue misuse, and callbacks firing after associated connections/state have been freed.
|
||||||
|
- Only report concurrency or race issues when a concrete event ordering can trigger memory corruption, unauthorized action, or reliable device/process crash.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Security Scan Area: net_builtin
|
||||||
|
|
||||||
|
## Target
|
||||||
|
|
||||||
|
Area: net_builtin
|
||||||
|
|
||||||
|
This area covers the built-in TCP/IP stack and its selected L2 implementations.
|
||||||
|
|
||||||
|
Primary files:
|
||||||
|
- `src/l2_*.c`
|
||||||
|
- `src/net_builtin.c`
|
||||||
|
|
||||||
|
Perform a focused security review centered on these files and this security surface.
|
||||||
|
|
||||||
|
Supporting code elsewhere under `src/` may be inspected when necessary to establish reachability, data flow, state, validation, mitigation, or impact, but do not inspect or report code outside `src/` or independently review unrelated areas.
|
||||||
|
|
||||||
|
## Area-Specific Security Guidance
|
||||||
|
|
||||||
|
**Built-In TCP/IP Stack, Packet Parsing, and Embedded Network Drivers:**
|
||||||
|
- Review Ethernet, ARP, IP, IPv6, ICMP, UDP, TCP, DHCP, SNTP, and driver receive/transmit paths for packet-length validation before reading protocol headers.
|
||||||
|
- Check IP/TCP/UDP header length fields, options length, fragmentation/reassembly behavior, checksum handling, and payload offset calculations for underflow, overflow, or out-of-bounds access.
|
||||||
|
- Look for malformed packets that can corrupt descriptor rings, RX/TX buffers, queue state, ARP tables, connection state, or timers.
|
||||||
|
- Review interrupt handlers and bare-metal driver code for bounded processing loops. Flag IRQ paths where attacker-controlled traffic can cause livelock, starvation, descriptor ownership loss, or repeated processing of the same malformed frame.
|
||||||
|
- Check DMA descriptor ownership transitions, memory barriers, cache coherency handling, and buffer length fields for race-like bugs that can cause memory corruption or stale packet reuse.
|
||||||
|
- Review platform-specific drivers such as STM32, NXP, TI, Microchip, Renesas, Infineon, Wiznet, Cypress WiFi, cellular, and other built-in stack integrations for inconsistent bounds checks or endian conversions.
|
||||||
|
- Treat a remotely reachable malformed Ethernet/IP/TCP/UDP packet that crashes, wedges, or corrupts an embedded target as reportable.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Security Scan Area: protocols
|
||||||
|
|
||||||
|
## Target
|
||||||
|
|
||||||
|
Area: protocols
|
||||||
|
|
||||||
|
This area covers the smaller externally controlled protocol parsers and state machines other than HTTP, WebSocket, and TLS.
|
||||||
|
|
||||||
|
Primary files:
|
||||||
|
- `src/modbus.c`
|
||||||
|
- `src/rpc.c`
|
||||||
|
- `src/sntp.c`
|
||||||
|
- `src/mqtt.c`
|
||||||
|
- `src/dns.c`
|
||||||
|
|
||||||
|
Perform a focused security review centered on these files and this security surface.
|
||||||
|
|
||||||
|
Supporting code elsewhere under `src/` may be inspected when necessary to establish reachability, data flow, state, validation, mitigation, or impact, but do not inspect or report code outside `src/` or independently review unrelated areas.
|
||||||
|
|
||||||
|
## Area-Specific Security Guidance
|
||||||
|
|
||||||
|
**MQTT Parser and Broker/Client State Machine Vulnerabilities:**
|
||||||
|
- Review MQTT fixed header parsing, remaining-length decoding, variable-length integer loops, packet identifier handling, QoS transitions, topic extraction, property parsing, and MQTT 3.x vs MQTT 5 differences.
|
||||||
|
- Look for malformed MQTT packets that can trigger out-of-bounds reads/writes, stack buffer overflow, heap corruption, assertion/crash, or parser desynchronization.
|
||||||
|
- Specifically check SUBSCRIBE and UNSUBSCRIBE handling for unbounded numbers of topics, malformed topic length fields, missing QoS bytes, invalid QoS values, and writes to fixed-size response arrays.
|
||||||
|
- Verify that PUBLISH handling enforces topic length, packet length, QoS rules, retained flag handling, packet identifier requirements, and message bounds before accessing fields.
|
||||||
|
- Check protocol handlers for authorization bypass, cross-topic data exposure, wildcard topic mishandling, `$SYS` or reserved topic confusion, and unintended publish/subscribe access.
|
||||||
|
- Review MQTT reconnect/resubscribe logic for stale connection state, packet identifier reuse bugs, and memory lifetime problems after close/reconnect.
|
||||||
|
- Treat unauthenticated single-packet broker crash, client crash from malicious broker response, or memory corruption during MQTT parsing as reportable.
|
||||||
|
|
||||||
|
**DNS and mDNS Parsing and Name Resolution:**
|
||||||
|
- Review DNS and mDNS message parsing for label length validation, compression pointer loops, pointer bounds, recursive expansion limits, integer overflow, malformed resource records, and out-of-bounds reads.
|
||||||
|
- Check that DNS answers are matched to outstanding requests by transaction ID, question name/type/class, and source expectations where applicable.
|
||||||
|
- Look for cache poisoning, response spoofing, or name confusion if DNS response validation is weak in client or resolver code.
|
||||||
|
- Verify that DNS failures, timeouts, and retries cannot leave dangling active request state or corrupt connection state.
|
||||||
|
|
||||||
|
**Modbus, RPC, and SNTP:**
|
||||||
|
- Review externally controlled lengths, framing, message parsing, and protocol state for malformed peer input that can cause parser desynchronization, state corruption, or a remotely triggerable crash or wedge.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Security Scan Area: tls
|
||||||
|
|
||||||
|
## Target
|
||||||
|
|
||||||
|
Area: tls
|
||||||
|
|
||||||
|
This area covers the complete Mongoose TLS implementation represented by the primary files.
|
||||||
|
|
||||||
|
Primary files:
|
||||||
|
- `src/tls_aes128.c`
|
||||||
|
- `src/tls_builtin.c`
|
||||||
|
- `src/tls_chacha20.c`
|
||||||
|
- `src/tls_dummy.c`
|
||||||
|
- `src/tls_mbed.c`
|
||||||
|
- `src/tls_openssl.c`
|
||||||
|
- `src/tls_rsa.c`
|
||||||
|
- `src/tls_uecc.c`
|
||||||
|
- `src/tls_x25519.c`
|
||||||
|
|
||||||
|
Perform a focused security review centered on these files and this security surface.
|
||||||
|
|
||||||
|
Supporting code elsewhere under `src/` may be inspected when necessary to establish reachability, data flow, state, validation, mitigation, or impact, but do not inspect or report code outside `src/` or independently review unrelated areas.
|
||||||
|
|
||||||
|
Connection, core, network, and helper code under `src/` may be inspected when needed to understand the complete TLS execution path.
|
||||||
|
|
||||||
|
## Area-Specific Security Guidance
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
**TLS, Certificate, Hostname Verification, and Cryptographic Validation:**
|
||||||
|
- Review all TLS backends, including built-in TLS, mbedTLS, OpenSSL, WolfSSL, and custom TLS integration, for equivalent security behavior where possible.
|
||||||
|
- Flag certificate verification bypasses, accidental `skip_verification` use in security-sensitive defaults, missing hostname verification when `name`/SNI is available, or inconsistent validation across TLS backends.
|
||||||
|
- For hostname verification, check wildcard handling carefully. A wildcard certificate identity must not match multiple DNS labels. For example, `*.example.com` must not match `a.b.example.com`.
|
||||||
|
- Verify that wildcard matching is applied only to certificate presented identifiers, not to arbitrary reference identifiers or general DNS glob matching.
|
||||||
|
- Review `mg_match` or other glob/matching helpers if used for certificate identity checks, authorization checks, filesystem paths, routing, or topic matching. Flag cases where glob semantics are broader than the security policy requires.
|
||||||
|
- Check certificate parsing for DER/PEM boundary errors, length overflows, malformed ASN.1, extension parsing mistakes, name constraints, SAN vs CN precedence, embedded NUL, and invalid string type handling.
|
||||||
|
- Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC/X25519 operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures.
|
||||||
|
- Flag any behavior that silently falls back from verified TLS to unverified TLS or plaintext.
|
||||||
|
- Check that TLS receive/send error paths cannot corrupt connection state, reuse freed buffers, or continue processing unauthenticated plaintext as encrypted data.
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Security Scan Area: web_http_auth
|
||||||
|
|
||||||
|
## Target
|
||||||
|
|
||||||
|
Area: web_http_auth
|
||||||
|
|
||||||
|
This area covers HTTP parsing and framing, HTTP-facing filesystem behavior, SSI, WebSocket, authentication/token/dashboard code, and HTTP-based OTA behavior.
|
||||||
|
|
||||||
|
Primary files:
|
||||||
|
- `src/http.c`
|
||||||
|
- `src/ssi.c`
|
||||||
|
- `src/ws.c`
|
||||||
|
- `src/jwt.c`
|
||||||
|
- `src/dash.c`
|
||||||
|
- `src/ota.c`
|
||||||
|
|
||||||
|
Perform a focused security review centered on these files and this security surface.
|
||||||
|
|
||||||
|
Supporting code elsewhere under `src/` may be inspected when necessary to establish reachability, data flow, state, validation, mitigation, or impact, but do not inspect or report code outside `src/` or independently review unrelated areas.
|
||||||
|
|
||||||
|
For OTA behavior, follow operations under `src/` into TLS, flash, filesystem, JSON, URL, or other support code when necessary, while keeping the review centered on the `web_http_auth` surface.
|
||||||
|
|
||||||
|
## Area-Specific Security Guidance
|
||||||
|
|
||||||
|
**HTTP Parser, Request Smuggling, and Message Framing:**
|
||||||
|
- Verify that HTTP requests or responses containing both `Content-Length` and `Transfer-Encoding` are rejected or handled in a way that cannot cause front-end/back-end desynchronization. Treat CL+TE coexistence as security-relevant request smuggling risk.
|
||||||
|
- Verify that duplicate `Content-Length`, duplicate `Transfer-Encoding`, conflicting transfer codings, malformed chunk sizes, chunk extensions, premature EOF, and body length mismatches cannot cause inconsistent body parsing.
|
||||||
|
- Check whether HTTP/1.0, HTTP/1.1, and malformed protocol versions alter CL/TE handling unsafely. A message with both `Content-Length` and `Transfer-Encoding` should not be accepted merely because of version differences.
|
||||||
|
- Look for parsing paths where headers are normalized differently from later lookup paths, including case folding, whitespace around colon, obs-fold/line folding, embedded NUL, duplicate headers, or partial header names.
|
||||||
|
- Review URL decoding, query parsing, multipart parsing, upload handling, range handling, and chunked responses for bounds mistakes, truncation, or inconsistent decoded-vs-raw path decisions.
|
||||||
|
- Check static file serving and SSI handling for path traversal through percent-encoding, double decoding, backslashes on Windows, mixed separators, dot-dot segments, absolute paths, drive letters, symlinks, embedded NUL, or alias/root_dir confusion.
|
||||||
|
- Review HTTP authentication helper paths for parsing ambiguities in Basic auth, header extraction, credential truncation, empty username/password acceptance, or incorrect comparison behavior.
|
||||||
|
- Check that HTTP upgrade to WebSocket validates method, headers, key, version, and connection semantics consistently before switching protocol state.
|
||||||
|
|
||||||
|
**WebSocket Framing and Upgrade Vulnerabilities:**
|
||||||
|
- Review WebSocket frame parsing for extended payload length handling, 16-bit/64-bit length conversion, masking rules, fragmentation, continuation frames, control-frame length limits, close/ping/pong handling, and compression-related assumptions if present.
|
||||||
|
- Check that client-to-server frames require masking and that malformed mask/payload boundaries cannot read or write out of bounds.
|
||||||
|
- Look for state-machine confusion after HTTP upgrade, especially where partial frames, fragmented messages, or mixed HTTP/WebSocket bytes can cause parser desynchronization.
|
||||||
|
- Check whether large or malformed frame lengths can cause integer overflow, memory corruption, uncontrolled allocation, connection lockup, or single-connection/device crash.
|
||||||
|
- Verify that WebSocket message APIs preserve attacker-controlled binary data length correctly and do not pass non-null-terminated payloads to C-string functions.
|
||||||
|
|
||||||
|
**HTTP-Facing Filesystem Serving, Uploads, and SSI:**
|
||||||
|
- Review `mg_http_serve_dir`, `mg_http_serve_file`, upload APIs, and SSI expansion for traversal, unauthorized file read/write, overwrite, truncation, or unexpected executable/include behavior.
|
||||||
|
- Look for inconsistent handling of decoded and undecoded paths, double decoding, query string leakage into path, trailing slash confusion, NUL byte truncation, and case sensitivity differences.
|
||||||
|
- Review multipart upload parsing for boundary confusion, filename traversal, field length overflow, partial-write behavior, and max-size enforcement.
|
||||||
|
- Check SSI include handling for directory escape, recursive include loops, unintended disclosure of local files, and parsing of attacker-controlled SSI directives.
|
||||||
|
- Verify that range requests and static response headers cannot cause out-of-bounds reads or disclose memory.
|
||||||
|
|
||||||
|
**OTA/Firmware Update and Device Dashboard Security:**
|
||||||
|
- Review firmware update endpoints and code paths for missing authentication, authorization bypass, path confusion, arbitrary write, unsafe flash offset/length handling, and failure to verify firmware authenticity where the code claims secure update behavior.
|
||||||
|
- Check update chunk parsing for integer overflow, flash boundary crossing, rollback/downgrade exposure, incomplete image bootability, and crash/power-loss unsafe state transitions.
|
||||||
|
- Flag any code path where unauthenticated network input can write firmware, configuration, filesystem contents, flash pages, bootloader state, or device identity material.
|
||||||
|
- Review device dashboard code for security-sensitive default routes, credentials, upload handlers, debug endpoints, config mutation endpoints, and CORS/header behavior.
|
||||||
|
|
||||||
|
**Authentication, Authorization, and Access-Control Patterns in Mongoose Integrations:**
|
||||||
|
- Review helper APIs and code paths that implement HTTP auth, cookies, sessions, tokens, device dashboards, REST APIs, upload endpoints, and firmware update endpoints.
|
||||||
|
- Flag authentication bypasses caused by prefix route matching, glob matching, decoded-vs-raw URI confusion, case normalization mistakes, missing boundary checks, or default-open sensitive endpoints.
|
||||||
|
- Check for authorization decisions made in client-side JavaScript only. Client-side checks are not security boundaries.
|
||||||
|
- Review CORS and origin checks only when they protect credentialed sensitive operations and the exploit path is concrete.
|
||||||
|
- Look for credential comparison bugs, truncation of usernames/passwords, accepting empty credentials, parsing only part of an Authorization header, or treating malformed credentials as anonymous-but-authorized.
|
||||||
|
- Flag sensitive data exposure when secrets, credentials, tokens, private keys, firmware signing material, PII, or device identity values can be read by unauthorized remote clients or logged from attacker-triggered paths.
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
3. The exploit depends on NULL, dangling, fake, or corrupted pointers/objects supplied by a local caller, including invalid `mg_connection`, `mg_mgr`, `mg_str`, iobufs, or callback data.
|
3. The exploit depends on NULL, dangling, fake, or corrupted pointers/objects supplied by a local caller, including invalid `mg_connection`, `mg_mgr`, `mg_str`, iobufs, or callback data.
|
||||||
4. The exploit requires manually mutating internal structs, flags, parser offsets, descriptor indexes, connection state, TLS state, MQTT/WebSocket state, or filesystem state into impossible combinations.
|
4. The exploit requires manually mutating internal structs, flags, parser offsets, descriptor indexes, connection state, TLS state, MQTT/WebSocket state, or filesystem state into impossible combinations.
|
||||||
5. The bug exists only in tests, fuzz harnesses, benchmarks, synthetic unit tests, or debug-only code, with no shared production logic.
|
5. The bug exists only in tests, fuzz harnesses, benchmarks, synthetic unit tests, or debug-only code, with no shared production logic.
|
||||||
6. The issue is only in a tutorial/demo/example with no realistic production relevance or sensitive operation.
|
6. The affected file is outside `src/`.
|
||||||
7. The attacker must edit source code, build flags, compile-time macros, TLS backend choices, filesystem roots, or local device configuration.
|
7. The attacker must edit source code, build flags, compile-time macros, TLS backend choices, filesystem roots, or local device configuration.
|
||||||
8. The attacker must directly write hardware registers, MMIO, DMA descriptors, descriptor ownership bits, IRQ state, cache registers, or peripheral configuration.
|
8. The attacker must directly write hardware registers, MMIO, DMA descriptors, descriptor ownership bits, IRQ state, cache registers, or peripheral configuration.
|
||||||
9. Generic DoS from high traffic volume, many valid connections, slow clients, missing quotas, missing rate limits, or ordinary resource saturation.
|
9. Generic DoS from high traffic volume, many valid connections, slow clients, missing quotas, missing rate limits, or ordinary resource saturation.
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
12. Generic crypto advice with no concrete impact on authentication, confidentiality, integrity, downgrade resistance, hostname verification, certificate validation, key handling, nonces/tags, or plaintext fallback.
|
12. Generic crypto advice with no concrete impact on authentication, confidentiality, integrity, downgrade resistance, hostname verification, certificate validation, key handling, nonces/tags, or plaintext fallback.
|
||||||
13. Speculative protocol claims without a specific malformed input shape, reachable parser path, and concrete security impact.
|
13. Speculative protocol claims without a specific malformed input shape, reachable parser path, and concrete security impact.
|
||||||
14. Pure application policy gaps: no default auth, no CSRF, no user access model, no account lockout, no audit logs, or no password policy.
|
14. Pure application policy gaps: no default auth, no CSRF, no user access model, no account lockout, no audit logs, or no password policy.
|
||||||
|
15. OTA findings based only on allowing plain HTTP, lacking firmware hash/signature verification, or permitting update URLs to change scheme/host when this is documented/configurable behavior and no existing security check is bypassed.
|
||||||
|
16. Filter TCP findings that rely primarily on predictability or insufficient entropy in protocol sequence-state generation, including attacks based on guessing or reconstructing connection state without observing the traffic.
|
||||||
|
|
||||||
# SIGNAL QUALITY CRITERIA
|
# SIGNAL QUALITY CRITERIA
|
||||||
**For remaining findings, assess:**
|
**For remaining findings, assess:**
|
||||||
@@ -25,7 +27,7 @@
|
|||||||
6. Does the report provide exact files, functions, line numbers, data flow, bounds reasoning, state reasoning, or a minimal malformed input?
|
6. Does the report provide exact files, functions, line numbers, data flow, bounds reasoning, state reasoning, or a minimal malformed input?
|
||||||
7. Is the malformed input shape clear, such as CL+TE HTTP, oversized MQTT SUBSCRIBE, malformed WebSocket length, DNS compression loop, wildcard TLS certificate, invalid packet header length, or encoded path traversal?
|
7. Is the malformed input shape clear, such as CL+TE HTTP, oversized MQTT SUBSCRIBE, malformed WebSocket length, DNS compression loop, wildcard TLS certificate, invalid packet header length, or encoded path traversal?
|
||||||
8. Is the severity justified by reachability and impact?
|
8. Is the severity justified by reachability and impact?
|
||||||
9. Is this a library/helper/default/example-pattern vulnerability rather than only arbitrary application misuse?
|
9. Is this a library/helper/default vulnerability rather than only arbitrary application misuse?
|
||||||
10. Is the fix specific enough to act on: reject CL+TE, bound arrays, validate DNS pointers, enforce one-label wildcard matching, check packet lengths, canonicalize paths, or bound RX loops?
|
10. Is the fix specific enough to act on: reject CL+TE, bound arrays, validate DNS pointers, enforce one-label wildcard matching, check packet lengths, canonicalize paths, or bound RX loops?
|
||||||
11. Is a DoS finding caused by a concrete implementation flaw rather than volume, slow clients, missing quotas, or capacity limits?
|
11. Is a DoS finding caused by a concrete implementation flaw rather than volume, slow clients, missing quotas, or capacity limits?
|
||||||
12. In embedded context, can a small malformed input reliably crash, wedge, corrupt, or permanently disrupt a device/process?
|
12. In embedded context, can a small malformed input reliably crash, wedge, corrupt, or permanently disrupt a device/process?
|
||||||
@@ -42,24 +44,26 @@
|
|||||||
8. Keep unsafe `mg_match` or glob use at security boundaries: certificate identity, route authorization, filesystem policy, MQTT topic authorization, or access control.
|
8. Keep unsafe `mg_match` or glob use at security boundaries: certificate identity, route authorization, filesystem policy, MQTT topic authorization, or access control.
|
||||||
9. Keep DNS/mDNS label, compression pointer, pointer loop, RR length, transaction matching, spoofing, OOB, or infinite-loop issues with external reachability.
|
9. Keep DNS/mDNS label, compression pointer, pointer loop, RR length, transaction matching, spoofing, OOB, or infinite-loop issues with external reachability.
|
||||||
10. Keep filesystem traversal through exposed serving/upload/SSI paths, including encoding, double decoding, backslashes, dot-dot, absolute paths, drive letters, symlinks, NULs, alias/root confusion, and upload filename traversal.
|
10. Keep filesystem traversal through exposed serving/upload/SSI paths, including encoding, double decoding, backslashes, dot-dot, absolute paths, drive letters, symlinks, NULs, alias/root confusion, and upload filename traversal.
|
||||||
11. Keep OTA/update issues allowing unauthenticated or improperly validated writes to firmware, flash, config, filesystem, boot state, device identity, or update metadata.
|
11. Keep embedded packet parser bugs in Ethernet, ARP, IP, IPv6, ICMP, UDP, TCP, DHCP, SNTP, or drivers when malformed traffic causes OOB, overflow, descriptor corruption, queue corruption, state corruption, or crash.
|
||||||
12. Keep embedded packet parser bugs in Ethernet, ARP, IP, IPv6, ICMP, UDP, TCP, DHCP, SNTP, or drivers when malformed traffic causes OOB, overflow, descriptor corruption, queue corruption, state corruption, or crash.
|
12. Keep IRQ, polling, RX descriptor, and packet-loop livelock/starvation/wedge bugs caused by received traffic.
|
||||||
13. Keep IRQ, polling, RX descriptor, and packet-loop livelock/starvation/wedge bugs caused by received traffic.
|
13. Keep event-lifecycle bugs reachable from external input: UAF, stale pointer, double close, callback lifetime error, iobuf mutation after close, protocol transition confusion, timer/wakeup after free.
|
||||||
14. Keep event-lifecycle bugs reachable from external input: UAF, stale pointer, double close, callback lifetime error, iobuf mutation after close, protocol transition confusion, timer/wakeup after free.
|
14. Keep externally reachable `mg_str` null-termination mistakes causing OOB read, data leak, auth bypass, parser confusion, or crash.
|
||||||
15. Keep externally reachable `mg_str` null-termination mistakes causing OOB read, data leak, auth bypass, parser confusion, or crash.
|
15. Keep integer conversion bugs where external lengths affect allocation, copying, parsing, or bounds checks.
|
||||||
16. Keep integer conversion bugs where external lengths affect allocation, copying, parsing, or bounds checks.
|
16. Filter parser/helper reports based only on a local caller passing invalid pointers, fake lengths, fake structs, or corrupted objects.
|
||||||
17. Filter parser/helper reports based only on a local caller passing invalid pointers, fake lengths, fake structs, or corrupted objects.
|
17. Filter reports requiring direct mutation of connection internals such as protocol flags, closing flags, handler pointers, iobuf fields, callback pointers, or user data.
|
||||||
18. Filter reports requiring direct mutation of connection internals such as protocol flags, closing flags, handler pointers, iobuf fields, callback pointers, or user data.
|
18. Filter reports requiring attacker control of `MG_ENABLE_*`, TLS backend macros, filesystem backend macros, compiler flags, debug flags, sanitizer settings, or platform defines.
|
||||||
19. Filter reports requiring attacker control of `MG_ENABLE_*`, TLS backend macros, filesystem backend macros, compiler flags, debug flags, sanitizer settings, or platform defines.
|
19. Filter direct hardware-access reports requiring writes to registers, DMA descriptors, MMIO, IRQ state, cache controls, or peripheral configuration.
|
||||||
20. Filter direct hardware-access reports requiring writes to registers, DMA descriptors, MMIO, IRQ state, cache controls, or peripheral configuration.
|
20. Filter generic “Mongoose lacks authentication” reports unless a helper, route pattern, auth parser, MQTT broker behavior, dashboard, upload handler, or OTA endpoint creates a concrete issue.
|
||||||
21. Filter generic “Mongoose lacks authentication” reports unless a helper, example, route pattern, auth parser, MQTT broker behavior, dashboard, upload handler, or OTA endpoint creates a concrete issue.
|
21. Filter generic rate-limit/quota/connection-cap/upload-cap findings unless a specific bug lets a small malformed input trigger crash, corruption, infinite loop, or disproportionate exhaustion.
|
||||||
22. Filter generic rate-limit/quota/connection-cap/upload-cap findings unless a specific bug lets a small malformed input trigger crash, corruption, infinite loop, or disproportionate exhaustion.
|
22. Filter purely theoretical UB, portability, unaligned-access, or signed-overflow reports with no supported-platform impact and no realistic external trigger.
|
||||||
23. Filter purely theoretical UB, portability, unaligned-access, or signed-overflow reports with no supported-platform impact and no realistic external trigger.
|
23. Filter RFC non-compliance without concrete smuggling, bypass, memory corruption, data exposure, state corruption, or crash.
|
||||||
24. Filter RFC non-compliance without concrete smuggling, bypass, memory corruption, data exposure, state corruption, or crash.
|
24. Keep security-relevant production library code under `src/` for dashboards, uploads, MQTT, firmware updates, authentication, TLS, and filesystem serving.
|
||||||
25. Keep security-relevant production-like examples: dashboards, upload servers, MQTT brokers, firmware update flows, auth examples, TLS examples, and filesystem-serving examples.
|
25. Keep client-side findings caused by malicious servers, brokers, DNS responders, TLS peers, WebSocket peers, or HTTP peers.
|
||||||
26. Keep client-side findings caused by malicious servers, brokers, DNS responders, TLS peers, WebSocket peers, or HTTP peers.
|
26. Keep callback-related findings only when Mongoose invokes the callback normally and then unsafely continues using freed or mutated state.
|
||||||
27. Keep callback-related findings only when Mongoose invokes the callback normally and then unsafely continues using freed or mutated state.
|
27. Keep availability findings when one malformed packet, request, certificate, DNS response, MQTT message, WebSocket frame, or small sequence can reliably crash, wedge, corrupt, or disrupt a Mongoose target.
|
||||||
28. Keep availability findings when one malformed packet, request, certificate, DNS response, MQTT message, WebSocket frame, or small sequence can reliably crash, wedge, corrupt, or disrupt a Mongoose target.
|
28. 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.
|
29. 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.
|
30. 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.
|
31. 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.
|
||||||
|
32. 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.
|
||||||
|
33. Filter TCP ISN predictability, weak sequence-number generation, RFC 6528 non-compliance, blind/off-path handshake spoofing, forged RST, and sequence-number-guessing reports in the built-in TCP/IP stack.
|
||||||
@@ -1,24 +1,35 @@
|
|||||||
Review the complete PR diff. This contains all code changes in the PR.
|
Review only changes under `src/` in the complete PR diff. Ignore all other changes in the PR.
|
||||||
|
|
||||||
# OBJECTIVE
|
# OBJECTIVE
|
||||||
|
|
||||||
Perform a security-focused code review to identify HIGH-CONFIDENCE security vulnerabilities that could have real exploitation potential.
|
Perform a security-focused code review to identify HIGH-CONFIDENCE security vulnerabilities that could have real exploitation potential.
|
||||||
|
|
||||||
This is not a general code review - focus ONLY on security implications newly added by this PR. Do not comment on existing security concerns.
|
This is not a general code review - focus ONLY on security implications newly added by this PR under `src/`. Do not analyze, count, or report changes outside `src/`. Do not comment on existing security concerns.
|
||||||
|
|
||||||
For this task, you will rely on two additional files, `resources/specs/claude-security-scan-instructions.md` detailing security scan instructions specific to the repository and `resources/specs/claude-false-positive-filtering.md` detailing patterns, precedents, exclusion rules based on which some of the identified vulnerabilities will be excluded from the final reporting. The instructions, rules and criterias found in those two files are MANDATORY TO BE HONOURED and your security audit will have to take all of them into account.
|
For this task, you will rely on the specified shared security-review sections in `resources/specs/claude-security-scan.md`, the specified security-guidance sections in the six `resources/specs/areas/*.md` prompts listed below, and `resources/specs/claude-false-positive-filtering.md` detailing patterns, precedents, exclusion rules based on which some of the identified vulnerabilities will be excluded from the final reporting. The instructions, rules and criterias found in those referenced sections and the filtering file are MANDATORY TO BE HONOURED and your security audit will have to take all of them into account.
|
||||||
|
|
||||||
YOU MUST READ AND ANALYZE BOTH OF THESE FILES BEFORE COMMENCING THE SECURITY AUDIT. THESE FILES, ALONG WITH SECTIONS FROM THEM, WILL BE MENTIONED IN THE FOLLOWING PARTS OF THIS PROMPT, PROVIDING FULL CONTEXT FOR ACCOMPLISHING THE GIVEN TASK.
|
YOU MUST READ AND ANALYZE THE SPECIFIED SECTIONS AND THE FILTERING FILE BEFORE COMMENCING THE SECURITY AUDIT. THESE SOURCES WILL BE MENTIONED IN THE FOLLOWING PARTS OF THIS PROMPT, PROVIDING FULL CONTEXT FOR ACCOMPLISHING THE GIVEN TASK.
|
||||||
|
|
||||||
# CRITICAL INSTRUCTIONS
|
# CRITICAL INSTRUCTIONS
|
||||||
1. MINIMIZE FALSE POSITIVES: Only flag issues where you're >80% confident of actual exploitability
|
1. MINIMIZE FALSE POSITIVES: Only flag issues where you're >80% confident of actual exploitability
|
||||||
2. AVOID NOISE: Skip theoretical issues, style concerns, or low-impact findings
|
2. AVOID NOISE: Skip theoretical issues, style concerns, or low-impact findings
|
||||||
3. UNDERSTANDING CONTEXT AND SCOPE OF REVIEW: In the security scan instructions file `resources/specs/claude-security-scan-instructions.md`, read the `External Attacker Threat Model` and `Mongoose-Specific Security Review Scope` subsections found in the `SECURITY SCAN CONTEXT` main section for a full understanding of the scanning model, code review context and the perspective from which the scanning is going to take place.
|
3. UNDERSTANDING CONTEXT AND SCOPE OF REVIEW: In `resources/specs/claude-security-scan.md`, read only the `External Attacker Threat Model` and `Mongoose-Specific Security Review Scope` subsections found in the `SECURITY SCAN CONTEXT` main section for a full understanding of the scanning model, code review context and the perspective from which the scanning is going to take place. Do not apply unrelated full-scan instructions from that file. In particular, do not inherit assigned-area scan scope, full-scan JSON output requirements, full-scan `review_completed` semantics, instructions to scan long-standing code independently of the PR, or area-selection workflow behavior. This PR prompt's scope, methodology, validation, and output rules remain authoritative.
|
||||||
4. TREAT EXCLUSIONS AS MANDATORY. The exclusions section below is mandatory. If a potential finding falls under an exclusion, do not report it. If a finding partially overlaps an exclusion, only report it if there is a concrete, non-excluded security impact.
|
4. TREAT EXCLUSIONS AS MANDATORY. The exclusions section below is mandatory. If a potential finding falls under an exclusion, do not report it. If a finding partially overlaps an exclusion, only report it if there is a concrete, non-excluded security impact.
|
||||||
|
|
||||||
# SECURITY CATEGORIES TO EXAMINE
|
# SECURITY CATEGORIES TO EXAMINE
|
||||||
|
|
||||||
In the security scan instructions file `resources/specs/claude-security-scan-instructions.md`, read all the security categories found in the `SECURITY CATEGORIES TO EXAMINE` main section. THESE ARE THE SECURITY CATEGORIES WHICH NEED TO BE EXAMINED.
|
Read the general/common security categories in the `GENERAL SECURITY CATEGORIES TO EXAMINE` section of `resources/specs/claude-security-scan.md`.
|
||||||
|
|
||||||
|
Also read the `Area-Specific Security Guidance` section from every area prompt:
|
||||||
|
|
||||||
|
* `resources/specs/areas/common_misc.md`
|
||||||
|
* `resources/specs/areas/mongoose_core.md`
|
||||||
|
* `resources/specs/areas/web_http_auth.md`
|
||||||
|
* `resources/specs/areas/tls.md`
|
||||||
|
* `resources/specs/areas/net_builtin.md`
|
||||||
|
* `resources/specs/areas/protocols.md`
|
||||||
|
|
||||||
|
Use only the security guidance from the area prompts and ignore their full-scan-specific `Target` sections. Consider the complete set of common and area-specific security categories for every file the PR modifies under `src/`. Do not choose one area or make this PR review area-scoped. Ignore all other changed paths and report only security vulnerabilities introduced by the PR under `src/`.
|
||||||
|
|
||||||
Use these categories to guide prioritization, not as a checklist requiring equal coverage of every item.
|
Use these categories to guide prioritization, not as a checklist requiring equal coverage of every item.
|
||||||
|
|
||||||
@@ -37,7 +48,7 @@ Phase 2 - Comparative Analysis:
|
|||||||
- Flag code that introduces new attack surfaces
|
- Flag code that introduces new attack surfaces
|
||||||
|
|
||||||
Phase 3 - Vulnerability Assessment:
|
Phase 3 - Vulnerability Assessment:
|
||||||
- Examine each modified file for security implications
|
- Examine each modified file under `src/` for security implications
|
||||||
- Trace data flow from user inputs to sensitive operations
|
- Trace data flow from user inputs to sensitive operations
|
||||||
- Look for privilege boundaries being crossed unsafely
|
- Look for privilege boundaries being crossed unsafely
|
||||||
- Identify injection points and unsafe deserialization
|
- Identify injection points and unsafe deserialization
|
||||||
@@ -110,6 +121,6 @@ Better to miss some theoretical issues than flood the report with false positive
|
|||||||
|
|
||||||
Read the `HARD EXCLUSIONS` section found in the `resources/specs/claude-false-positive-filtering.md` filtering file, for each possbile vulnerability found, check if it matches any items from the patterns listed in that section and if that is the case, exclude that vulnerability from the report. DO NOT REPORT IT if a match is found. Also, take into account the project-specific filtering and reporting precedents found in the `PRECEDENTS` section of the filtering file.
|
Read the `HARD EXCLUSIONS` section found in the `resources/specs/claude-false-positive-filtering.md` filtering file, for each possbile vulnerability found, check if it matches any items from the patterns listed in that section and if that is the case, exclude that vulnerability from the report. DO NOT REPORT IT if a match is found. Also, take into account the project-specific filtering and reporting precedents found in the `PRECEDENTS` section of the filtering file.
|
||||||
|
|
||||||
Begin your analysis now. Use the repository exploration tools to understand the codebase context, then analyze the PR changes for security implications.
|
Begin your analysis now. Use the repository exploration tools to understand the `src/` codebase context, then analyze only the PR changes under `src/` for security implications.
|
||||||
|
|
||||||
Your final action must be posting the PR comment. You should not reply again after posting the PR comment.
|
Your final action must be posting the PR comment. You should not reply again after posting the PR comment.
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
# SECURITY SCAN CONTEXT
|
|
||||||
|
|
||||||
**External Attacker Threat Model:**
|
|
||||||
- Analyze vulnerabilities from the perspective of an external attacker, not from the perspective of a developer, local user, debugger, test harness, or code running inside the process.
|
|
||||||
- The attacker can provide bytes through real external interfaces exposed by Mongoose or a Mongoose-based application: TCP, UDP, HTTP, WebSocket, MQTT, DNS, mDNS, TLS handshakes/certificates, Ethernet/IP/TCP/UDP packets, uploaded files, request paths, query strings, headers, message bodies, malicious server responses, and other protocol inputs.
|
|
||||||
- The attacker cannot directly call internal C functions, pass arbitrary invalid pointers, mutate internal structs, invoke callbacks manually, edit compile-time macros, alter build flags, write to hardware registers, control DMA descriptors directly, attach a debugger, or run code inside the target process.
|
|
||||||
- For a finding to be valid, show how attacker-controlled external input reaches the vulnerable code path through normal library/application execution.
|
|
||||||
- Do not report findings that require impossible internal states unless the finding also explains how an external input can cause that state.
|
|
||||||
- Do not report “API misuse” as a library vulnerability unless the API contract makes the insecure use likely, undocumented, or unavoidable.
|
|
||||||
- For embedded networking and driver code, assume the attacker can send malformed network traffic to the device, but cannot directly manipulate hardware registers, memory-mapped I/O, DMA rings, or interrupt state except as a consequence of packets processed by the driver.
|
|
||||||
|
|
||||||
**Mongoose-Specific Security Review Scope:**
|
|
||||||
- Treat this repository as a security-sensitive embedded C/C++ networking stack. Mongoose processes attacker-controlled bytes from TCP, UDP, HTTP, WebSocket, MQTT, DNS, mDNS, TLS, filesystem upload/download paths, and built-in TCP/IP drivers.
|
|
||||||
- In this repository, remotely triggerable denial of service is security-relevant when it is caused by a concrete implementation flaw such as memory corruption, parser abort, assertion failure, stack exhaustion, infinite loop, unbounded recursion, IRQ livelock, connection state corruption, descriptor-ring corruption, or a single unauthenticated packet causing process/device crash.
|
|
||||||
- Prioritize HIGH and MEDIUM severity findings. Include LOW only when unusually concrete, externally relevant, and useful. Do not report vague robustness concerns. Every finding must identify the attacker-controlled input, affected parser/state machine/buffer, concrete impact, and the exact code path.
|
|
||||||
- Prefer findings with a plausible packet/request/message shape. For network protocol issues, describe the relevant malformed HTTP request, MQTT packet, WebSocket frame, DNS message, TLS handshake/certificate input, Ethernet/IP/TCP/UDP packet, or filesystem request.
|
|
||||||
- Because this is C/C++, memory safety vulnerabilities are in scope. Buffer overflows, stack overflows, heap overflows, out-of-bounds reads/writes, use-after-free, double free, integer overflow leading to memory corruption, invalid pointer lifetime, and unsafe length conversions must be reviewed carefully.
|
|
||||||
- Do not dismiss issues merely because they are “only DoS” if the issue is remotely triggerable against an embedded server, broker, device dashboard, firmware-update endpoint, or network-facing parser.
|
|
||||||
|
|
||||||
# SECURITY CATEGORIES TO EXAMINE
|
|
||||||
|
|
||||||
**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.
|
|
||||||
- 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.
|
|
||||||
- Review any `memcpy`, `memmove`, `memcmp`, `snprintf`, `mg_snprintf`, `mg_xprintf`, iobuf append/delete/resize operation, and manual pointer increment where source length can originate from network input.
|
|
||||||
- Treat `struct mg_str` values as non-null-terminated unless the code proves otherwise. Flag use of `strlen`, `%s`, `strcmp`, `strstr`, or C-string APIs on attacker-controlled `mg_str.buf` unless length and termination are guaranteed.
|
|
||||||
- Check for parser code that advances pointers without proving that enough bytes remain. Flag any read of fixed-size fields, variable-length integers, DNS labels, TLS vectors, MQTT remaining length, WebSocket extended lengths, or HTTP chunks before bounds are validated.
|
|
||||||
- Look for inconsistent ownership and lifetime of buffers referenced by `mg_str`, connection receive/send iobufs, TLS buffers, filesystem buffers, and event callback data.
|
|
||||||
- Check for reentrancy hazards in event callbacks where user handlers may close a connection, mutate iobufs, start TLS, change protocol handlers, or free state while protocol code continues using stale pointers.
|
|
||||||
|
|
||||||
**HTTP Parser, Request Smuggling, and Message Framing:**
|
|
||||||
- Verify that HTTP requests or responses containing both `Content-Length` and `Transfer-Encoding` are rejected or handled in a way that cannot cause front-end/back-end desynchronization. Treat CL+TE coexistence as security-relevant request smuggling risk.
|
|
||||||
- Verify that duplicate `Content-Length`, duplicate `Transfer-Encoding`, conflicting transfer codings, malformed chunk sizes, chunk extensions, premature EOF, and body length mismatches cannot cause inconsistent body parsing.
|
|
||||||
- Check whether HTTP/1.0, HTTP/1.1, and malformed protocol versions alter CL/TE handling unsafely. A message with both `Content-Length` and `Transfer-Encoding` should not be accepted merely because of version differences.
|
|
||||||
- Look for parsing paths where headers are normalized differently from later lookup paths, including case folding, whitespace around colon, obs-fold/line folding, embedded NUL, duplicate headers, or partial header names.
|
|
||||||
- Review URL decoding, query parsing, multipart parsing, upload handling, range handling, and chunked responses for bounds mistakes, truncation, or inconsistent decoded-vs-raw path decisions.
|
|
||||||
- Check static file serving and SSI handling for path traversal through percent-encoding, double decoding, backslashes on Windows, mixed separators, dot-dot segments, absolute paths, drive letters, symlinks, embedded NUL, or alias/root_dir confusion.
|
|
||||||
- Review HTTP authentication helper paths for parsing ambiguities in Basic auth, header extraction, credential truncation, empty username/password acceptance, or incorrect comparison behavior.
|
|
||||||
- Check that HTTP upgrade to WebSocket validates method, headers, key, version, and connection semantics consistently before switching protocol state.
|
|
||||||
|
|
||||||
**MQTT Parser and Broker/Client State Machine Vulnerabilities:**
|
|
||||||
- Review MQTT fixed header parsing, remaining-length decoding, variable-length integer loops, packet identifier handling, QoS transitions, topic extraction, property parsing, and MQTT 3.x vs MQTT 5 differences.
|
|
||||||
- Look for malformed MQTT packets that can trigger out-of-bounds reads/writes, stack buffer overflow, heap corruption, assertion/crash, or parser desynchronization.
|
|
||||||
- Specifically check SUBSCRIBE and UNSUBSCRIBE handling for unbounded numbers of topics, malformed topic length fields, missing QoS bytes, invalid QoS values, and writes to fixed-size response arrays.
|
|
||||||
- Verify that PUBLISH handling enforces topic length, packet length, QoS rules, retained flag handling, packet identifier requirements, and message bounds before accessing fields.
|
|
||||||
- Check broker examples and protocol handlers for authorization bypass, cross-topic data exposure, wildcard topic mishandling, `$SYS` or reserved topic confusion, and unintended publish/subscribe access.
|
|
||||||
- Review MQTT reconnect/resubscribe logic for stale connection state, packet identifier reuse bugs, and memory lifetime problems after close/reconnect.
|
|
||||||
- Treat unauthenticated single-packet broker crash, client crash from malicious broker response, or memory corruption during MQTT parsing as reportable.
|
|
||||||
|
|
||||||
**WebSocket Framing and Upgrade Vulnerabilities:**
|
|
||||||
- Review WebSocket frame parsing for extended payload length handling, 16-bit/64-bit length conversion, masking rules, fragmentation, continuation frames, control-frame length limits, close/ping/pong handling, and compression-related assumptions if present.
|
|
||||||
- Check that client-to-server frames require masking and that malformed mask/payload boundaries cannot read or write out of bounds.
|
|
||||||
- Look for state-machine confusion after HTTP upgrade, especially where partial frames, fragmented messages, or mixed HTTP/WebSocket bytes can cause parser desynchronization.
|
|
||||||
- Check whether large or malformed frame lengths can cause integer overflow, memory corruption, uncontrolled allocation, connection lockup, or single-connection/device crash.
|
|
||||||
- Verify that WebSocket message APIs preserve attacker-controlled binary data length correctly and do not pass non-null-terminated payloads to C-string functions.
|
|
||||||
|
|
||||||
**TLS, Certificate, Hostname Verification, and Cryptographic Validation:**
|
|
||||||
- Review all TLS backends, including built-in TLS, mbedTLS, OpenSSL, WolfSSL, and custom TLS integration, for equivalent security behavior where possible.
|
|
||||||
- Flag certificate verification bypasses, accidental `skip_verification` use in security-sensitive examples or defaults, missing hostname verification when `name`/SNI is available, or inconsistent validation across TLS backends.
|
|
||||||
- For hostname verification, check wildcard handling carefully. A wildcard certificate identity must not match multiple DNS labels. For example, `*.example.com` must not match `a.b.example.com`.
|
|
||||||
- Verify that wildcard matching is applied only to certificate presented identifiers, not to arbitrary reference identifiers or general DNS glob matching.
|
|
||||||
- Review `mg_match` or other glob/matching helpers if used for certificate identity checks, authorization checks, filesystem paths, routing, or topic matching. Flag cases where glob semantics are broader than the security policy requires.
|
|
||||||
- Check certificate parsing for DER/PEM boundary errors, length overflows, malformed ASN.1, extension parsing mistakes, name constraints, SAN vs CN precedence, embedded NUL, and invalid string type handling.
|
|
||||||
- Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC/X25519 operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures.
|
|
||||||
- Flag any behavior that silently falls back from verified TLS to unverified TLS or plaintext.
|
|
||||||
- Check that TLS receive/send error paths cannot corrupt connection state, reuse freed buffers, or continue processing unauthenticated plaintext as encrypted data.
|
|
||||||
|
|
||||||
**DNS, mDNS, URL Parsing, and Name Resolution:**
|
|
||||||
- Review DNS and mDNS message parsing for label length validation, compression pointer loops, pointer bounds, recursive expansion limits, integer overflow, malformed resource records, and out-of-bounds reads.
|
|
||||||
- Check that DNS answers are matched to outstanding requests by transaction ID, question name/type/class, and source expectations where applicable.
|
|
||||||
- Look for cache poisoning, response spoofing, or name confusion if DNS response validation is weak in client or resolver code.
|
|
||||||
- Review URL parsing for scheme, host, port, IPv6 literal, userinfo, percent-encoding, embedded NUL, empty host, default port, and path/query boundary confusion.
|
|
||||||
- Check for SSRF-relevant behavior only when attacker input can control the destination host, protocol, or network boundary. Path-only control is not sufficient.
|
|
||||||
- Verify that DNS failures, timeouts, and retries cannot leave dangling active request state or corrupt connection state.
|
|
||||||
|
|
||||||
**Built-In TCP/IP Stack, Packet Parsing, and Embedded Network Drivers:**
|
|
||||||
- Review Ethernet, ARP, IP, IPv6, ICMP, UDP, TCP, DHCP, SNTP, and driver receive/transmit paths for packet-length validation before reading protocol headers.
|
|
||||||
- Check IP/TCP/UDP header length fields, options length, fragmentation/reassembly behavior, checksum handling, and payload offset calculations for underflow, overflow, or out-of-bounds access.
|
|
||||||
- Look for malformed packets that can corrupt descriptor rings, RX/TX buffers, queue state, ARP tables, connection state, or timers.
|
|
||||||
- Review interrupt handlers and bare-metal driver code for bounded processing loops. Flag IRQ paths where attacker-controlled traffic can cause livelock, starvation, descriptor ownership loss, or repeated processing of the same malformed frame.
|
|
||||||
- Check DMA descriptor ownership transitions, memory barriers, cache coherency handling, and buffer length fields for race-like bugs that can cause memory corruption or stale packet reuse.
|
|
||||||
- Review platform-specific drivers such as STM32, NXP, TI, Microchip, Renesas, Infineon, Wiznet, Cypress WiFi, cellular, and other built-in stack integrations for inconsistent bounds checks or endian conversions.
|
|
||||||
- Treat a remotely reachable malformed Ethernet/IP/TCP/UDP packet that crashes, wedges, or corrupts an embedded target as reportable.
|
|
||||||
|
|
||||||
**Filesystem Serving, Uploads, Packed Filesystems, and SSI:**
|
|
||||||
- Review `mg_http_serve_dir`, `mg_http_serve_file`, upload APIs, packed filesystem code, and SSI expansion for traversal, unauthorized file read/write, overwrite, truncation, or unexpected executable/include behavior.
|
|
||||||
- Check path canonicalization across POSIX, Windows, embedded filesystems, packed filesystems, and custom `mg_fs` implementations.
|
|
||||||
- Look for inconsistent handling of decoded and undecoded paths, double decoding, query string leakage into path, trailing slash confusion, NUL byte truncation, and case sensitivity differences.
|
|
||||||
- Review multipart upload parsing for boundary confusion, filename traversal, field length overflow, partial-write behavior, and max-size enforcement.
|
|
||||||
- Check SSI include handling for directory escape, recursive include loops, unintended disclosure of local files, and parsing of attacker-controlled SSI directives.
|
|
||||||
- Verify that range requests and static response headers cannot cause out-of-bounds reads or disclose memory.
|
|
||||||
|
|
||||||
**OTA/Firmware Update and Device Dashboard Security:**
|
|
||||||
- Review firmware update endpoints and examples for missing authentication, authorization bypass, path confusion, arbitrary write, unsafe flash offset/length handling, and failure to verify firmware authenticity where the code claims secure update behavior.
|
|
||||||
- Check update chunk parsing for integer overflow, flash boundary crossing, rollback/downgrade exposure, incomplete image bootability, and crash/power-loss unsafe state transitions.
|
|
||||||
- Flag any code path where unauthenticated network input can write firmware, configuration, filesystem contents, flash pages, bootloader state, or device identity material.
|
|
||||||
- Review device dashboard examples for security-sensitive default routes, credentials, upload handlers, debug endpoints, config mutation endpoints, and CORS/header behavior when they are likely to be copied into production.
|
|
||||||
- Do not report ordinary insecure demo behavior unless it creates a realistic copy-paste production vulnerability or affects library code.
|
|
||||||
|
|
||||||
**Protocol State Machine and Event-Driven Lifecycle Bugs:**
|
|
||||||
- Review transitions between listening, accepted, connecting, resolving, TLS handshaking, HTTP, WebSocket, MQTT, draining, closing, and closed states.
|
|
||||||
- Check whether malformed input can cause protocol handlers to process data in the wrong state, process stale data after close, or invoke callbacks with invalid event data.
|
|
||||||
- Look for use-after-free or stale pointer use when callbacks call `mg_close_conn`, mark a connection closing, mutate iobufs, change protocol handlers, or initiate TLS/WebSocket/MQTT transitions.
|
|
||||||
- Verify that partial reads/writes, backpressure, `is_full`, draining, TLS pending states, and send-buffer mutations cannot cause duplicated sends, skipped validation, or parser confusion.
|
|
||||||
- Review timer and wakeup handling for stale user data pointers, cross-thread queue misuse, and callbacks firing after associated connections/state have been freed.
|
|
||||||
- Only report concurrency or race issues when a concrete event ordering can trigger memory corruption, unauthorized action, or reliable device/process crash.
|
|
||||||
|
|
||||||
**Cross-Platform C Portability With Security Impact:**
|
|
||||||
- Review code paths whose security depends on struct packing, alignment, endianness, pointer size, signedness of `char`, integer width, or platform-specific filesystem/network behavior.
|
|
||||||
- Check for unaligned memory access on embedded targets when parsing attacker-controlled network packets.
|
|
||||||
- Verify endian conversions for Ethernet/IP/TCP/UDP/DNS/MQTT/TLS fields before bounds checks and allocations.
|
|
||||||
- Look for platform branches where one backend performs validation and another does not, especially across UNIX, Windows, lwIP, Zephyr, FreeRTOS-like systems, bare-metal, and built-in TCP/IP stack modes.
|
|
||||||
- Flag portability bugs only when they create a realistic vulnerability on a supported platform, not merely theoretical undefined behavior.
|
|
||||||
|
|
||||||
**Authentication, Authorization, and Access-Control Patterns in Mongoose Integrations:**
|
|
||||||
- Review examples and helper APIs that implement HTTP auth, cookies, sessions, tokens, device dashboards, REST APIs, MQTT topic access, upload endpoints, and firmware update endpoints.
|
|
||||||
- Flag authentication bypasses caused by prefix route matching, glob matching, decoded-vs-raw URI confusion, case normalization mistakes, missing boundary checks, or default-open sensitive endpoints.
|
|
||||||
- Check for authorization decisions made in client-side JavaScript only. Client-side checks are not security boundaries.
|
|
||||||
- Review CORS and origin checks only when they protect credentialed sensitive operations and the exploit path is concrete.
|
|
||||||
- Look for credential comparison bugs, truncation of usernames/passwords, accepting empty credentials, parsing only part of an Authorization header, or treating malformed credentials as anonymous-but-authorized.
|
|
||||||
- Flag sensitive data exposure when secrets, credentials, tokens, private keys, firmware signing material, PII, or device identity values can be read by unauthorized remote clients or logged from attacker-triggered paths.
|
|
||||||
@@ -1,24 +1,34 @@
|
|||||||
# Full Codebase Security Scan Prompt
|
# Security Scan Master Prompt
|
||||||
|
|
||||||
You are an expert security reviewer performing a full-codebase security audit of the checked-out repository.
|
You are an expert security reviewer performing a focused security audit centered on one assigned scan area in the checked-out repository.
|
||||||
|
|
||||||
You must analyze the repository as a whole and identify credible, externally reachable security vulnerabilities in the current implementation.
|
You must analyze the assigned target and security surface and identify credible, externally reachable security vulnerabilities in the current implementation.
|
||||||
|
|
||||||
For this task, you will rely on two additional files, `resources/specs/claude-security-scan-instructions.md` detailing security scan instructions specific to the repository and `resources/specs/claude-false-positive-filtering.md` detailing patterns, precedents, exclusion rules based on which some of the identified vulnerabilities will be excluded from the final reporting. The instructions, rules and criterias found in those two files are MANDATORY TO BE HONOURED and your security audit will have to take all of them into account.
|
Every full security scan uses:
|
||||||
|
|
||||||
YOU MUST READ AND ANALYZE BOTH OF THESE FILES BEFORE COMMENCING THE SECURITY AUDIT. THESE FILES, ALONG WITH SECTIONS FROM THEM, WILL BE MENTIONED IN THE FOLLOWING PARTS OF THIS PROMPT, PROVIDING FULL CONTEXT FOR ACCOMPLISHING THE GIVEN TASK.
|
1. the shared master prompt: `resources/specs/claude-security-scan.md`;
|
||||||
|
2. one area prompt selected by the workflow: `resources/specs/areas/<area>.md`;
|
||||||
|
3. the common filtering prompt: `resources/specs/claude-false-positive-filtering.md`.
|
||||||
|
|
||||||
|
The area prompt defines what is being scanned. This master prompt defines how security analysis and reporting are performed. The filtering prompt defines what must not be reported and which historical precedents must be honored.
|
||||||
|
|
||||||
|
YOU MUST READ AND ANALYZE THE ASSIGNED AREA PROMPT AND THE COMMON FILTERING PROMPT BEFORE COMMENCING THE SECURITY AUDIT. THE INSTRUCTIONS, RULES, AND CRITERIA IN ALL THREE PROMPTS ARE MANDATORY TO BE HONORED.
|
||||||
|
|
||||||
## OBJECTIVE
|
## OBJECTIVE
|
||||||
|
|
||||||
You are reviewing a full source repository for security vulnerabilities.
|
You are reviewing one assigned security area in a source repository for security vulnerabilities.
|
||||||
|
|
||||||
The repository may contain application code, protocol parsers, networking code, embedded code, library code, examples, tests, build scripts, documentation, generated files, and third-party/vendor code.
|
The `src/` directory contains the protocol parsers, networking code, embedded code, library code, generated code, and platform-specific code in scope for this review.
|
||||||
|
|
||||||
Your task is to determine which parts of the repository are security-relevant, identify externally reachable attack surfaces, and report only findings with a concrete vulnerability pattern, credible attacker influence, and meaningful security impact.
|
The area-specific prompt defines the primary files and security surface for this invocation. Your task is to identify externally reachable attack surfaces within that assigned area and report only findings with a concrete vulnerability pattern, credible attacker influence, and meaningful security impact.
|
||||||
|
|
||||||
|
The security review target is limited to code under `src/`. Do not analyze, count, or report code outside `src/` as part of the security review.
|
||||||
|
|
||||||
|
You may inspect code outside the listed target files only when it is also under `src/` and is necessary to understand data flow, reachability, call chains, state transitions, memory ownership, mitigations, authentication or authorization, protocol interaction, exploitability, or impact. Inspecting supporting code must not turn into an independent security review of unrelated areas. Findings must remain tied to the assigned target and security surface and must identify an affected file under `src/`.
|
||||||
|
|
||||||
DO NOT modify files, create commits, open PRs, create issues or post comments! You must only produce the final JSON report requested below.
|
DO NOT modify files, create commits, open PRs, create issues or post comments! You must only produce the final JSON report requested below.
|
||||||
|
|
||||||
Identify high-confidence security vulnerabilities in the full codebase. For this prompt, high-confidence means confidence >= 0.7 under the scoring rules below. Focus on vulnerabilities that are reachable through normal execution paths and attacker-controlled inputs. Report only findings that have a concrete security impact.
|
Identify high-confidence security vulnerabilities in the assigned scan area. For this prompt, high-confidence means confidence >= 0.7 under the scoring rules below. Focus on vulnerabilities that are reachable through normal execution paths and attacker-controlled inputs. Report only findings that have a concrete security impact.
|
||||||
|
|
||||||
Do not report vague best-practice concerns, speculative hardening suggestions, style issues, maintainability concerns, generic bug risks, or findings without a clear attacker-controlled path.
|
Do not report vague best-practice concerns, speculative hardening suggestions, style issues, maintainability concerns, generic bug risks, or findings without a clear attacker-controlled path.
|
||||||
|
|
||||||
@@ -26,9 +36,26 @@ Existing vulnerabilities in the current codebase are in scope.
|
|||||||
|
|
||||||
Previously introduced vulnerabilities are in scope.
|
Previously introduced vulnerabilities are in scope.
|
||||||
|
|
||||||
Any security-relevant issue present in the checked-out repository is in scope unless explicitly excluded below.
|
Any security-relevant issue tied to the assigned target or security surface is in scope unless explicitly excluded below.
|
||||||
|
|
||||||
In the security scan instructions file `resources/specs/claude-security-scan-instructions.md`, read the `External Attacker Threat Model` and `Mongoose-Specific Security Review Scope` subsections found in the `SECURITY SCAN CONTEXT` main section for a full understanding of the scanning model and the code review context.
|
## SECURITY SCAN CONTEXT
|
||||||
|
|
||||||
|
**External Attacker Threat Model:**
|
||||||
|
- Analyze vulnerabilities from the perspective of an external attacker, not from the perspective of a developer, local user, debugger, test harness, or code running inside the process.
|
||||||
|
- The attacker can provide bytes through real external interfaces exposed by Mongoose or a Mongoose-based application: TCP, UDP, HTTP, WebSocket, MQTT, DNS, mDNS, TLS handshakes/certificates, Ethernet/IP/TCP/UDP packets, uploaded files, request paths, query strings, headers, message bodies, malicious server responses, and other protocol inputs.
|
||||||
|
- The attacker cannot directly call internal C functions, pass arbitrary invalid pointers, mutate internal structs, invoke callbacks manually, edit compile-time macros, alter build flags, write to hardware registers, control DMA descriptors directly, attach a debugger, or run code inside the target process.
|
||||||
|
- For a finding to be valid, show how attacker-controlled external input reaches the vulnerable code path through normal library/application execution.
|
||||||
|
- Do not report findings that require impossible internal states unless the finding also explains how an external input can cause that state.
|
||||||
|
- Do not report “API misuse” as a library vulnerability unless the API contract makes the insecure use likely, undocumented, or unavoidable.
|
||||||
|
- For embedded networking and driver code, assume the attacker can send malformed network traffic to the device, but cannot directly manipulate hardware registers, memory-mapped I/O, DMA rings, or interrupt state except as a consequence of packets processed by the driver.
|
||||||
|
|
||||||
|
**Mongoose-Specific Security Review Scope:**
|
||||||
|
- Treat this repository as a security-sensitive embedded C/C++ networking stack. Mongoose processes attacker-controlled bytes from TCP, UDP, HTTP, WebSocket, MQTT, DNS, mDNS, TLS, filesystem upload/download paths, and built-in TCP/IP drivers.
|
||||||
|
- In this repository, remotely triggerable denial of service is security-relevant when it is caused by a concrete implementation flaw such as memory corruption, parser abort, assertion failure, stack exhaustion, infinite loop, unbounded recursion, IRQ livelock, connection state corruption, descriptor-ring corruption, or a single unauthenticated packet causing process/device crash.
|
||||||
|
- Prioritize HIGH and MEDIUM severity findings. Include LOW only when unusually concrete, externally relevant, and useful. Do not report vague robustness concerns. Every finding must identify the attacker-controlled input, affected parser/state machine/buffer, concrete impact, and the exact code path.
|
||||||
|
- Prefer findings with a plausible packet/request/message shape. For network protocol issues, describe the relevant malformed HTTP request, MQTT packet, WebSocket frame, DNS message, TLS handshake/certificate input, Ethernet/IP/TCP/UDP packet, or filesystem request.
|
||||||
|
- Because this is C/C++, memory safety vulnerabilities are in scope. Buffer overflows, stack overflows, heap overflows, out-of-bounds reads/writes, use-after-free, double free, integer overflow leading to memory corruption, invalid pointer lifetime, and unsafe length conversions must be reviewed carefully.
|
||||||
|
- Do not dismiss issues merely because they are “only DoS” if the issue is remotely triggerable against an embedded server, broker, device dashboard, firmware-update endpoint, or network-facing parser.
|
||||||
|
|
||||||
## CRITICAL INSTRUCTIONS
|
## CRITICAL INSTRUCTIONS
|
||||||
|
|
||||||
@@ -56,12 +83,14 @@ Do not report:
|
|||||||
* Purely defensive hardening suggestions.
|
* Purely defensive hardening suggestions.
|
||||||
* Issues that only matter under unrealistic assumptions.
|
* Issues that only matter under unrealistic assumptions.
|
||||||
|
|
||||||
### 3. Analyze the repository as a whole
|
### 3. Analyze the assigned scan area
|
||||||
|
|
||||||
This is a full-codebase scan. You should inspect the repository structure, identify major components, and prioritize security-sensitive code paths.
|
This is a targeted scan centered on the primary files and security surface defined by the assigned area prompt. Follow relevant supporting code under `src/` when necessary to establish reachability, data flow, state, validation, mitigation, exploitability, or impact, but do not inspect code outside `src/` as part of the security review or independently review unrelated areas.
|
||||||
|
|
||||||
Do not restrict analysis to a diff. Do not assume only changed files matter. Do not assume an issue is irrelevant because it appears in old or long-standing code.
|
Do not restrict analysis to a diff. Do not assume only changed files matter. Do not assume an issue is irrelevant because it appears in old or long-standing code.
|
||||||
|
|
||||||
|
Findings must remain tied to the assigned target and security surface.
|
||||||
|
|
||||||
### 4. Prioritize externally reachable attack surfaces
|
### 4. Prioritize externally reachable attack surfaces
|
||||||
|
|
||||||
Prioritize code reachable from attacker-controlled or peer-controlled inputs. Relevant inputs may include, but are not limited to:
|
Prioritize code reachable from attacker-controlled or peer-controlled inputs. Relevant inputs may include, but are not limited to:
|
||||||
@@ -94,25 +123,40 @@ The exclusions section below is mandatory. If a potential finding falls under an
|
|||||||
|
|
||||||
If a finding partially overlaps an exclusion, only report it if there is a concrete, non-excluded security impact.
|
If a finding partially overlaps an exclusion, only report it if there is a concrete, non-excluded security impact.
|
||||||
|
|
||||||
## SECURITY CATEGORIES TO EXAMINE
|
## GENERAL SECURITY CATEGORIES TO EXAMINE
|
||||||
|
|
||||||
In the security scan instructions file `resources/specs/claude-security-scan-instructions.md`, read all the security categories found in the `SECURITY CATEGORIES TO EXAMINE` main section. THESE ARE THE SECURITY CATEGORIES WHICH NEED TO BE EXAMINED.
|
The following common security categories apply to every assigned scan area. The assigned area prompt provides additional area-specific security guidance.
|
||||||
|
|
||||||
Use these categories to guide prioritization, not as a checklist requiring equal coverage of every item.
|
Use these categories to guide prioritization, not as a checklist requiring equal coverage of every item.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
- 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.
|
||||||
|
- Review any `memcpy`, `memmove`, `memcmp`, `snprintf`, `mg_snprintf`, `mg_xprintf`, iobuf append/delete/resize operation, and manual pointer increment where source length can originate from network input.
|
||||||
|
- Treat `struct mg_str` values as non-null-terminated unless the code proves otherwise. Flag use of `strlen`, `%s`, `strcmp`, `strstr`, or C-string APIs on attacker-controlled `mg_str.buf` unless length and termination are guaranteed.
|
||||||
|
- Check for parser code that advances pointers without proving that enough bytes remain. Flag any read of fixed-size fields, variable-length integers, DNS labels, TLS vectors, MQTT remaining length, WebSocket extended lengths, or HTTP chunks before bounds are validated.
|
||||||
|
- Look for inconsistent ownership and lifetime of buffers referenced by `mg_str`, connection receive/send iobufs, TLS buffers, filesystem buffers, and event callback data.
|
||||||
|
- Check for reentrancy hazards in event callbacks where user handlers may close a connection, mutate iobufs, start TLS, change protocol handlers, or free state while protocol code continues using stale pointers.
|
||||||
|
|
||||||
|
**Cross-Platform C Portability With Security Impact:**
|
||||||
|
- Review code paths whose security depends on struct packing, alignment, endianness, pointer size, signedness of `char`, integer width, or platform-specific filesystem/network behavior.
|
||||||
|
- Check for unaligned memory access on embedded targets when parsing attacker-controlled network packets.
|
||||||
|
- Verify endian conversions for Ethernet/IP/TCP/UDP/DNS/MQTT/TLS fields before bounds checks and allocations.
|
||||||
|
- Look for platform branches where one backend performs validation and another does not, especially across UNIX, Windows, lwIP, Zephyr, FreeRTOS-like systems, bare-metal, and built-in TCP/IP stack modes.
|
||||||
|
- Flag portability bugs only when they create a realistic vulnerability on a supported platform, not merely theoretical undefined behavior.
|
||||||
|
|
||||||
## ANALYSIS METHODOLOGY
|
## ANALYSIS METHODOLOGY
|
||||||
|
|
||||||
Follow this methodology before producing the final JSON.
|
Follow this methodology before producing the final JSON.
|
||||||
|
|
||||||
### Phase 1: Repository context research
|
### Phase 1: Repository context research
|
||||||
|
|
||||||
First, understand the repository.
|
First, understand the assigned area within `src/`.
|
||||||
|
|
||||||
Identify:
|
Identify:
|
||||||
|
|
||||||
* Main source directories.
|
* Relevant directories under `src/`.
|
||||||
* Build systems.
|
* Public APIs declared under `src/`.
|
||||||
* Public APIs.
|
|
||||||
* Protocol implementations.
|
* Protocol implementations.
|
||||||
* Network-facing components.
|
* Network-facing components.
|
||||||
* File parsing components.
|
* File parsing components.
|
||||||
@@ -120,14 +164,13 @@ Identify:
|
|||||||
* TLS, crypto, or certificate validation logic.
|
* TLS, crypto, or certificate validation logic.
|
||||||
* Memory management patterns.
|
* Memory management patterns.
|
||||||
* Platform-specific code.
|
* Platform-specific code.
|
||||||
* Example, test, fuzz, generated, and vendor directories.
|
* Components in the assigned area most likely to process attacker-controlled input.
|
||||||
* Components most likely to process attacker-controlled input.
|
|
||||||
|
|
||||||
Use repository files to infer the actual architecture instead of assuming a generic web application model.
|
Use files under `src/` to infer the actual architecture instead of assuming a generic web application model.
|
||||||
|
|
||||||
### Phase 2: Attack surface mapping
|
### Phase 2: Attack surface mapping
|
||||||
|
|
||||||
Map the externally reachable attack surfaces.
|
Map the externally reachable attack surfaces within the assigned area.
|
||||||
|
|
||||||
For each important surface, determine:
|
For each important surface, determine:
|
||||||
|
|
||||||
@@ -166,6 +209,7 @@ Before including any finding in the final output, confirm:
|
|||||||
* The severity matches the impact.
|
* The severity matches the impact.
|
||||||
* The finding is not a duplicate of another finding.
|
* The finding is not a duplicate of another finding.
|
||||||
* The finding is not excluded.
|
* The finding is not excluded.
|
||||||
|
* The finding is tied to the assigned target or security surface.
|
||||||
|
|
||||||
If you cannot provide this level of specificity, do not report the finding.
|
If you cannot provide this level of specificity, do not report the finding.
|
||||||
|
|
||||||
@@ -218,6 +262,8 @@ The value of `files_reviewed` should reflect the number of files substantially i
|
|||||||
|
|
||||||
Do not inflate this number by counting files that were only listed or briefly skimmed.
|
Do not inflate this number by counting files that were only listed or briefly skimmed.
|
||||||
|
|
||||||
|
The value `review_completed: true` means the assigned scan area was meaningfully reviewed, not that the entire repository was reviewed.
|
||||||
|
|
||||||
## SEVERITY GUIDELINES
|
## SEVERITY GUIDELINES
|
||||||
|
|
||||||
Use these severity definitions.
|
Use these severity definitions.
|
||||||
@@ -313,10 +359,9 @@ Take into account the project-specific filtering and reporting precedents found
|
|||||||
|
|
||||||
## DIRECTORY AND FILE EXCLUSIONS
|
## DIRECTORY AND FILE EXCLUSIONS
|
||||||
|
|
||||||
Do not analyze or report findings from the following paths unless they are directly used by production/library code:
|
Only analyze and report findings from files under `src/`.
|
||||||
- .github/
|
|
||||||
- resources/
|
Do not analyze or count code outside `src/` as part of the security review.
|
||||||
- test/
|
|
||||||
|
|
||||||
## OUTPUT REQUIREMENTS
|
## OUTPUT REQUIREMENTS
|
||||||
|
|
||||||
@@ -336,4 +381,4 @@ Do not include commentary.
|
|||||||
|
|
||||||
Do not reply again after outputting the JSON.
|
Do not reply again after outputting the JSON.
|
||||||
|
|
||||||
Begin the full-codebase security analysis now.
|
Begin the assigned-area security analysis now.
|
||||||
+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,233 @@
|
|||||||
|
#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) && !defined(MG_ENABLE_DRIVER_NETC)
|
||||||
|
#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
|
||||||
|
#elif defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
#define MG_ENABLE_DRIVER_NETC 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
|
||||||
|
#elif !defined(MG_OTA) && \
|
||||||
|
(defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_))
|
||||||
|
#define MG_OTA MG_OTA_RT1180
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IRAM
|
||||||
|
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
#define MG_IRAM \
|
||||||
|
__attribute__((noinline, section(".ramfunc.$SRAM_ITC_cm33")))
|
||||||
|
#else
|
||||||
|
#define MG_IRAM __attribute__((noinline, section(".data_RAM2")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ETH_RAM
|
||||||
|
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
#define MG_ETH_RAM __attribute__((section(".bss.$ETH_RAM")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_SET_MAC_ADDRESS
|
||||||
|
#if defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
|
||||||
|
// No OCOTP_FUSES present on RT1186 to generate the UUID
|
||||||
|
#define MG_SET_MAC_ADDRESS(mac) (void) (mac)
|
||||||
|
#else
|
||||||
|
#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
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_IMXRT_WDOG1_TIMEOUT_MS
|
||||||
|
#define MG_IMXRT_WDOG1_TIMEOUT_MS 10000U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
|
||||||
|
|
||||||
|
#define MG_IMXRT_RTWDOG1_UNLOCK() \
|
||||||
|
do { \
|
||||||
|
if ((RTWDOG1->CS & RTWDOG_CS_CMD32EN_MASK) != 0U) { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_UPDATE_KEY; \
|
||||||
|
} else { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_UPDATE_KEY & 0xffffU; \
|
||||||
|
RTWDOG1->CNT = RTWDOG_UPDATE_KEY >> 16; \
|
||||||
|
} \
|
||||||
|
while ((RTWDOG1->CS & RTWDOG_CS_ULK_MASK) == 0U) (void) 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define MG_IMXRT_RTWDOG1_FEED() \
|
||||||
|
do { \
|
||||||
|
uint32_t primask_ = __get_PRIMASK(); \
|
||||||
|
__disable_irq(); \
|
||||||
|
if ((RTWDOG1->CS & RTWDOG_CS_CMD32EN_MASK) != 0U) { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_REFRESH_KEY; \
|
||||||
|
} else { \
|
||||||
|
RTWDOG1->CNT = RTWDOG_REFRESH_KEY & 0xffffU; \
|
||||||
|
RTWDOG1->CNT = RTWDOG_REFRESH_KEY >> 16; \
|
||||||
|
} \
|
||||||
|
if (primask_ == 0U) __enable_irq(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||||
|
do { \
|
||||||
|
uint32_t primask_ = __get_PRIMASK(); \
|
||||||
|
SRC_GENERAL_REG->SRMASK &= ~SRC_GENERAL_SRMASK_WDOG1_MASK_MASK; \
|
||||||
|
__disable_irq(); \
|
||||||
|
MG_IMXRT_RTWDOG1_UNLOCK(); \
|
||||||
|
RTWDOG1->WIN = 0U; \
|
||||||
|
RTWDOG1->TOVAL = (MG_IMXRT_WDOG1_TIMEOUT_MS + 7U) / 8U; \
|
||||||
|
RTWDOG1->CS = RTWDOG_CS_EN_MASK | RTWDOG_CS_UPDATE_MASK | \
|
||||||
|
RTWDOG_CS_CLK(1U) | RTWDOG_CS_PRES_MASK | \
|
||||||
|
RTWDOG_CS_CMD32EN_MASK; \
|
||||||
|
while ((RTWDOG1->CS & RTWDOG_CS_RCS_MASK) == 0U) (void) 0; \
|
||||||
|
if (primask_ == 0U) __enable_irq(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||||
|
#define MG_OTA_ROLLBACK_TIMER_FEED() MG_IMXRT_RTWDOG1_FEED()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GPR5 survives RTWDOG global resets and is not reserved by the boot ROM.
|
||||||
|
#ifndef MG_OTA_STATE_GET
|
||||||
|
#define MG_OTA_STATE_GET() SRC_GENERAL_REG->GPR[5]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_OTA_STATE_SET
|
||||||
|
#define MG_OTA_STATE_SET(v) (SRC_GENERAL_REG->GPR[5] = (uint32_t) (v))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|||||||
+8
-4
@@ -175,6 +175,14 @@
|
|||||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for
|
||||||
#endif // Mongoose built-in network stack
|
#endif // Mongoose built-in network stack
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_TCPIP_TCPRTX
|
||||||
|
#define MG_ENABLE_TCPIP_TCPRTX 0 // TCP retransmission queue
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_DHCPNAME_SIZE
|
||||||
|
#define MG_TCPIP_DHCPNAME_SIZE 18 // struct mg_tcpip_if :: dhcp_name size
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223)
|
#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223)
|
||||||
#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
|
#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
|
||||||
#endif
|
#endif
|
||||||
@@ -211,10 +219,6 @@
|
|||||||
#define MG_SET_MAC_ADDRESS(mac)
|
#define MG_SET_MAC_ADDRESS(mac)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_TCPIP_DHCPNAME_SIZE
|
|
||||||
#define MG_TCPIP_DHCPNAME_SIZE 18 // struct mg_tcpip_if :: dhcp_name size
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_SET_WIFI_CONFIG
|
#ifndef MG_SET_WIFI_CONFIG
|
||||||
#define MG_SET_WIFI_CONFIG(ifp, driver_data)
|
#define MG_SET_WIFI_CONFIG(ifp, driver_data)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+9
-2
@@ -167,9 +167,16 @@ 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;
|
||||||
|
if (c->send.len > MG_DASH_MAX_SEND_BUF_SIZE) { // Skip slow consumers
|
||||||
|
MG_ERROR(("skipping, send buf %lu > %d", (unsigned long) c->send.len,
|
||||||
|
MG_DASH_MAX_SEND_BUF_SIZE));
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,468 @@
|
|||||||
|
#include "net_builtin.h"
|
||||||
|
|
||||||
|
#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP && \
|
||||||
|
defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
|
||||||
|
|
||||||
|
// IMXRT1180 RM - Chapter 53 - Ethernet Controller (NETC)
|
||||||
|
|
||||||
|
#define ETH_DESC_CNT 8 // Descriptor count must be multiple of 8 (53.4.6.11.97)
|
||||||
|
#define ETH_PKT_SIZE 1536
|
||||||
|
|
||||||
|
struct netc_pci_function { // 53.4.6.1.1
|
||||||
|
volatile uint32_t DID_VID;
|
||||||
|
volatile uint16_t CMD, STAT;
|
||||||
|
volatile uint8_t reserved0[0x40];
|
||||||
|
volatile uint16_t DEV_CTL, DEV_STAT;
|
||||||
|
volatile uint8_t reserved1[0x34];
|
||||||
|
volatile uint16_t MSIX_CAP_LIST, MSIX_MSG_CTL;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_emdio { // 53.4.6.12
|
||||||
|
volatile uint32_t EMDIO_CFG, EMDIO_CTL, EMDIO_DATA, EMDIO_ADDR, EMDIO_STAT;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_enetc { // 53.4.6.6
|
||||||
|
volatile uint32_t ECAPR0, ECAPR1, ECAPR2, reserved0, PMR;
|
||||||
|
volatile uint8_t reserved1[0x1ec];
|
||||||
|
volatile uint32_t PSIPMMR, PSIPVMR;
|
||||||
|
volatile uint8_t reserved2[0x5f8];
|
||||||
|
volatile uint32_t SMCAPR;
|
||||||
|
volatile uint8_t reserved3[0x17fc];
|
||||||
|
struct {
|
||||||
|
volatile uint32_t PSIPMAR0, PSIPMAR1, PSIVLANR, reserved0, PSICFGR0,
|
||||||
|
PSICFGR1, PSICFGR2;
|
||||||
|
volatile uint8_t reserved1[0x3c];
|
||||||
|
volatile uint32_t PSIMMHFR0, PSIMMHFR1;
|
||||||
|
volatile uint8_t reserved2[0x20];
|
||||||
|
} PSI[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_port { // 53.4.6.8
|
||||||
|
volatile uint32_t PCAPR, PMCAPR, PIOCAPR, reserved0, PCR;
|
||||||
|
volatile uint8_t reserved1[0x0c];
|
||||||
|
volatile uint32_t PMAR0, PMAR1;
|
||||||
|
volatile uint8_t reserved2[0xd8];
|
||||||
|
volatile uint32_t POR, PSR;
|
||||||
|
volatile uint8_t reserved3[0x30];
|
||||||
|
volatile uint32_t PDGSR;
|
||||||
|
volatile uint8_t reserved4[0xc4];
|
||||||
|
struct {
|
||||||
|
volatile uint32_t PTGSTCSR, reserved0, PTCTMSDUR, reserved1, PTCCBSR0,
|
||||||
|
PTCCBSR1;
|
||||||
|
volatile uint8_t reserved2[8];
|
||||||
|
} TCT[8];
|
||||||
|
volatile uint8_t reserved5[0x220];
|
||||||
|
volatile uint32_t BPSTGSR;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_port_mac { // 53.4.6.9
|
||||||
|
volatile uint8_t reserved0[8];
|
||||||
|
volatile uint32_t COMMAND_CONFIG, MAC_ADDR_0, MAC_ADDR_1, MAXFRM, MINFRM;
|
||||||
|
volatile uint8_t reserved1[0x24];
|
||||||
|
volatile uint32_t IEVENT;
|
||||||
|
volatile uint8_t reserved2[0x1dc];
|
||||||
|
volatile uint64_t TFRMN;
|
||||||
|
volatile uint8_t reserved3[0xd8];
|
||||||
|
volatile uint32_t IF_MODE;
|
||||||
|
volatile uint8_t reserved4[0xfc];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_eth_link {
|
||||||
|
struct netc_port_mac PM[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_pseudo_link {
|
||||||
|
volatile uint32_t PPMSR;
|
||||||
|
volatile uint8_t reserved0[0x0c];
|
||||||
|
volatile uint32_t PPMCR;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_switch { // 53.4.6.5
|
||||||
|
volatile uint8_t reserved0[0x400];
|
||||||
|
volatile uint32_t MPCR;
|
||||||
|
volatile uint8_t reserved1[0x1c0c];
|
||||||
|
volatile uint32_t VFHTDECR0, VFHTDECR1, VFHTDECR2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_bdr {
|
||||||
|
volatile uint32_t TBMR, TBSR;
|
||||||
|
volatile uint8_t reserved0[8];
|
||||||
|
volatile uint32_t TBBAR0, TBBAR1, TBPIR, TBCIR, TBLENR;
|
||||||
|
volatile uint8_t reserved1[0x7c];
|
||||||
|
volatile uint32_t TBIER, TBIDR, TBICR0, TBICR1;
|
||||||
|
volatile uint8_t reserved2[0x50];
|
||||||
|
volatile uint32_t RBMR, RBSR, RBBSR, RBCIR, RBBAR0, RBBAR1, RBPIR,
|
||||||
|
reserved3, RBLENR;
|
||||||
|
volatile uint8_t reserved4[0x5c];
|
||||||
|
volatile uint32_t RBDCR;
|
||||||
|
volatile uint8_t reserved5[0x1c];
|
||||||
|
volatile uint32_t RBIER, RBIDR, RBICR0, RBICR1;
|
||||||
|
volatile uint8_t reserved6[0x50];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_si { // 53.4.6.11
|
||||||
|
volatile uint32_t SIMR, SISR;
|
||||||
|
volatile uint8_t reserved0[0x30];
|
||||||
|
volatile uint32_t SIRBGCR;
|
||||||
|
volatile uint8_t reserved1[0x2ec];
|
||||||
|
volatile uint32_t SITFRM0;
|
||||||
|
volatile uint8_t reserved2[0x6fc];
|
||||||
|
volatile uint32_t SIRXIDR0;
|
||||||
|
volatile uint8_t reserved3[0x154];
|
||||||
|
volatile uint32_t SIMSIRRVR[1];
|
||||||
|
volatile uint8_t reserved4[0x747c];
|
||||||
|
struct netc_bdr BDR[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_msix_entry { // 53.4.2.11
|
||||||
|
volatile uint32_t MSG_ADDR, MSG_UPPER_ADDR, MSG_DATA, VECTOR_CTRL;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_msgintr { // 54.4
|
||||||
|
struct {
|
||||||
|
volatile uint32_t MSIIR, MSIR;
|
||||||
|
} MSI[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NETC_EMDIO_PCI \
|
||||||
|
((struct netc_pci_function *) (uintptr_t) 0x60001000U)
|
||||||
|
#define NETC_SWITCH_PCI \
|
||||||
|
((struct netc_pci_function *) (uintptr_t) 0x60002000U)
|
||||||
|
#define NETC_ENETC1_PCI \
|
||||||
|
((struct netc_pci_function *) (uintptr_t) 0x60004000U)
|
||||||
|
#define NETC_EMDIO ((struct netc_emdio *) (uintptr_t) 0x60ba1c00U)
|
||||||
|
|
||||||
|
#define NETC_ENETC1 ((struct netc_enetc *) (uintptr_t) 0x60b50000U)
|
||||||
|
#define NETC_ENETC1_PORT ((struct netc_port *) (uintptr_t) 0x60b54000U)
|
||||||
|
#define NETC_ENETC1_PSEUDO_LINK \
|
||||||
|
((struct netc_pseudo_link *) (uintptr_t) 0x60b55000U)
|
||||||
|
#define NETC_ENETC1_SI0 ((struct netc_si *) (uintptr_t) 0x60b40000U)
|
||||||
|
#define NETC_ENETC1_MSIX \
|
||||||
|
((struct netc_msix_entry *) (uintptr_t) 0x60c00000U)
|
||||||
|
|
||||||
|
#define NETC_SWITCH ((struct netc_switch *) (uintptr_t) 0x60a00000U)
|
||||||
|
#define NETC_SW0_PORT(link) \
|
||||||
|
((struct netc_port *) (uintptr_t) (0x60a04000U + \
|
||||||
|
(uint32_t) (link) * 0x4000U))
|
||||||
|
#define NETC_SW0_ETH(link) \
|
||||||
|
((struct netc_eth_link *) (uintptr_t) (0x60a05000U + \
|
||||||
|
(uint32_t) (link) * 0x4000U))
|
||||||
|
#define NETC_SW0_PORT4 ((struct netc_port *) (uintptr_t) 0x60a14000U)
|
||||||
|
#define NETC_MSGINTR1 \
|
||||||
|
((struct netc_msgintr *) (uintptr_t) 0x428a0000U)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NETC_RX_MSIX_ENTRY = 0,
|
||||||
|
NETC_RX_MSG_INDEX = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_tx_bd {
|
||||||
|
uint64_t buffer_address;
|
||||||
|
uint16_t buffer_length, frame_length;
|
||||||
|
uint32_t control;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_rx_descriptor {
|
||||||
|
uint64_t buffer_address, reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netc_rx_writeback {
|
||||||
|
uint16_t internet_checksum, parser_summary;
|
||||||
|
uint32_t rss_hash;
|
||||||
|
uint16_t buffer_length, vlan;
|
||||||
|
uint32_t status;
|
||||||
|
};
|
||||||
|
|
||||||
|
union netc_rx_bd {
|
||||||
|
struct netc_rx_descriptor descriptor;
|
||||||
|
struct netc_rx_writeback writeback;
|
||||||
|
};
|
||||||
|
|
||||||
|
static volatile struct netc_tx_bd s_txdesc[ETH_DESC_CNT] MG_ETH_RAM
|
||||||
|
__attribute__((aligned(128)));
|
||||||
|
static volatile union netc_rx_bd s_rxdesc[ETH_DESC_CNT] MG_ETH_RAM
|
||||||
|
__attribute__((aligned(128)));
|
||||||
|
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM
|
||||||
|
__attribute__((aligned(64)));
|
||||||
|
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM
|
||||||
|
__attribute__((aligned(64)));
|
||||||
|
|
||||||
|
static uint16_t s_tx_prod;
|
||||||
|
static uint16_t s_tx_clean;
|
||||||
|
static uint16_t s_rx_cons;
|
||||||
|
static struct mg_tcpip_if *s_ifp;
|
||||||
|
|
||||||
|
static uint16_t enet_read_phy(uint8_t addr, uint8_t reg) {
|
||||||
|
NETC_EMDIO->EMDIO_CFG &= ~MG_BIT(6); // ENC45: use Clause 22
|
||||||
|
NETC_EMDIO->EMDIO_CTL = MG_BIT(15) | ((uint32_t) (addr & 0x1fU) << 5) |
|
||||||
|
(reg & 0x1fU); // READ, PORT_ADDR, DEV_ADDR
|
||||||
|
while (NETC_EMDIO->EMDIO_CFG & MG_BIT(31)) (void) 0; // BSY1
|
||||||
|
return (uint16_t) (NETC_EMDIO->EMDIO_DATA & 0xffffU);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enet_write_phy(uint8_t addr, uint8_t reg, uint16_t value) {
|
||||||
|
NETC_EMDIO->EMDIO_CFG &= ~MG_BIT(6); // ENC45: use Clause 22
|
||||||
|
NETC_EMDIO->EMDIO_CTL = ((uint32_t) (addr & 0x1fU) << 5) |
|
||||||
|
(reg & 0x1fU); // PORT_ADDR, DEV_ADDR
|
||||||
|
NETC_EMDIO->EMDIO_DATA = value;
|
||||||
|
while (NETC_EMDIO->EMDIO_CFG & MG_BIT(31)) (void) 0; // BSY1
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_tcpip_driver_netc_init(struct mg_tcpip_if *ifp) {
|
||||||
|
struct mg_tcpip_driver_netc_data *d =
|
||||||
|
(struct mg_tcpip_driver_netc_data *) ifp->driver_data;
|
||||||
|
struct mg_phy phy = {enet_read_phy, enet_write_phy};
|
||||||
|
struct netc_port *switch_port;
|
||||||
|
struct netc_eth_link *eth_link;
|
||||||
|
uint64_t tx_base = (uint64_t) (uintptr_t) s_txdesc;
|
||||||
|
uint64_t rx_base = (uint64_t) (uintptr_t) s_rxdesc;
|
||||||
|
uint32_t mac0 = (uint32_t) ifp->mac[0] |
|
||||||
|
((uint32_t) ifp->mac[1] << 8) |
|
||||||
|
((uint32_t) ifp->mac[2] << 16) |
|
||||||
|
((uint32_t) ifp->mac[3] << 24);
|
||||||
|
uint32_t mac1 = (uint32_t) ifp->mac[4] |
|
||||||
|
((uint32_t) ifp->mac[5] << 8);
|
||||||
|
uint32_t mdc_cr =
|
||||||
|
(uint32_t) (d->mdc_cr < 0 ? MG_DRIVER_MDC_CR : d->mdc_cr);
|
||||||
|
|
||||||
|
switch_port = NETC_SW0_PORT(d->link);
|
||||||
|
eth_link = NETC_SW0_ETH(d->link);
|
||||||
|
s_ifp = ifp;
|
||||||
|
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||||
|
s_txdesc[i].buffer_address = (uint64_t) (uintptr_t) s_txbuf[i];
|
||||||
|
s_rxdesc[i].descriptor.buffer_address =
|
||||||
|
(uint64_t) (uintptr_t) s_rxbuf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
NETC_EMDIO_PCI->DEV_CTL |= MG_BIT(15); // INIT_FLR
|
||||||
|
while (NETC_EMDIO_PCI->DEV_CTL & MG_BIT(15)) (void) 0;
|
||||||
|
NETC_EMDIO_PCI->CMD |= MG_BIT(2) | MG_BIT(1); // BUS_MASTER_EN, MEM_ACCESS
|
||||||
|
NETC_EMDIO->EMDIO_CFG =
|
||||||
|
((mdc_cr & 0x1ffU) << 7) | MG_BIT(2); // MDIO_CLK_DIV, MDIO_HOLD
|
||||||
|
mg_phy_init(&phy, d->phy_addr, 0);
|
||||||
|
|
||||||
|
// Initialize ENETC1 PSI0 and port
|
||||||
|
NETC_ENETC1_PCI->CMD |= MG_BIT(2) | MG_BIT(1); // BUS_MASTER_EN, MEM_ACCESS
|
||||||
|
NETC_ENETC1_PCI->MSIX_MSG_CTL = MG_BIT(14); // FUNC_MASK
|
||||||
|
NETC_ENETC1_PORT->POR |= MG_BIT(1) | MG_BIT(0); // RXDIS, TXDIS
|
||||||
|
NETC_ENETC1_PCI->DEV_CTL |= MG_BIT(15); // INIT_FLR
|
||||||
|
while (NETC_ENETC1_PCI->DEV_CTL & MG_BIT(15)) (void) 0;
|
||||||
|
NETC_ENETC1_PCI->CMD |= MG_BIT(2) | MG_BIT(1); // BUS_MASTER_EN, MEM_ACCESS
|
||||||
|
NETC_ENETC1_PCI->MSIX_MSG_CTL = MG_BIT(14); // FUNC_MASK
|
||||||
|
NETC_ENETC1_PORT->PCR = 99U << 16; // PSPEED: 1 Gbps
|
||||||
|
NETC_ENETC1_PORT->PMAR0 = mac0;
|
||||||
|
NETC_ENETC1_PORT->PMAR1 = mac1;
|
||||||
|
NETC_ENETC1_PSEUDO_LINK->PPMCR = MG_BIT(16); // TXPAD
|
||||||
|
NETC_ENETC1->PSIPMMR &= ~(MG_BIT(16) | MG_BIT(0));
|
||||||
|
NETC_ENETC1_SI0->SIMR &=
|
||||||
|
~(MG_BIT(31) | MG_BIT(3) | MG_BIT(2) | MG_BIT(1));
|
||||||
|
NETC_ENETC1_SI0->SIRBGCR = 0;
|
||||||
|
NETC_ENETC1->PSI[0].PSICFGR0 =
|
||||||
|
MG_BIT(16) | MG_BIT(0); // one Rx BDR, one Tx BDR
|
||||||
|
|
||||||
|
NETC_ENETC1->PSI[0].PSICFGR2 = 0; // NUM_MSIX: one entry
|
||||||
|
NETC_ENETC1_MSIX[NETC_RX_MSIX_ENTRY].VECTOR_CTRL = MG_BIT(0); // MASK
|
||||||
|
NETC_ENETC1_MSIX[NETC_RX_MSIX_ENTRY].MSG_ADDR =
|
||||||
|
(uint32_t) (uintptr_t) &NETC_MSGINTR1->MSI[0].MSIIR;
|
||||||
|
NETC_ENETC1_MSIX[NETC_RX_MSIX_ENTRY].MSG_UPPER_ADDR = 0;
|
||||||
|
NETC_ENETC1_MSIX[NETC_RX_MSIX_ENTRY].MSG_DATA = NETC_RX_MSG_INDEX;
|
||||||
|
NETC_ENETC1_SI0->SIMSIRRVR[0] = NETC_RX_MSIX_ENTRY; // Rx ring 0 -> entry 0
|
||||||
|
|
||||||
|
// Initialize switch function 2 to drive the selected physical port
|
||||||
|
NETC_SWITCH_PCI->CMD |= MG_BIT(2) | MG_BIT(1);
|
||||||
|
NETC_SWITCH_PCI->MSIX_MSG_CTL = MG_BIT(14); // FUNC_MASK
|
||||||
|
switch_port->POR |= MG_BIT(1) | MG_BIT(0);
|
||||||
|
eth_link->PM[0].COMMAND_CONFIG |= MG_BIT(26); // SWR
|
||||||
|
while (eth_link->PM[0].COMMAND_CONFIG & MG_BIT(26)) (void) 0;
|
||||||
|
eth_link->PM[1].COMMAND_CONFIG |= MG_BIT(26); // SWR
|
||||||
|
while (eth_link->PM[1].COMMAND_CONFIG & MG_BIT(26)) (void) 0;
|
||||||
|
NETC_SWITCH_PCI->DEV_CTL |= MG_BIT(15); // INIT_FLR
|
||||||
|
while (NETC_SWITCH_PCI->DEV_CTL & MG_BIT(15)) (void) 0;
|
||||||
|
NETC_SWITCH_PCI->CMD |= MG_BIT(2) | MG_BIT(1);
|
||||||
|
NETC_SWITCH_PCI->MSIX_MSG_CTL = MG_BIT(14); // FUNC_MASK
|
||||||
|
|
||||||
|
// Configure switch physical port and ethernet link (ETH_X)
|
||||||
|
switch_port->PCR = 99U << 16; // PSPEED: 1 Gbps
|
||||||
|
switch_port->PMAR0 = mac0;
|
||||||
|
switch_port->PMAR1 = mac1;
|
||||||
|
switch_port->TCT[0].PTCTMSDUR = MG_BIT(24) | MG_BIT(16) | ETH_PKT_SIZE;
|
||||||
|
eth_link->PM[0].MAXFRM = ETH_PKT_SIZE;
|
||||||
|
eth_link->PM[0].MINFRM = 64;
|
||||||
|
eth_link->PM[0].IF_MODE = MG_BIT(14) | 4U; // 1G RGMII
|
||||||
|
eth_link->PM[0].COMMAND_CONFIG =
|
||||||
|
MG_BIT(15) | MG_BIT(1) | MG_BIT(0); // TXP, RX_EN, TX_EN
|
||||||
|
eth_link->PM[1].MAXFRM = ETH_PKT_SIZE;
|
||||||
|
eth_link->PM[1].MINFRM = 64;
|
||||||
|
eth_link->PM[1].IF_MODE = MG_BIT(14) | 4U;
|
||||||
|
eth_link->PM[1].COMMAND_CONFIG =
|
||||||
|
MG_BIT(15) | MG_BIT(1) | MG_BIT(0);
|
||||||
|
|
||||||
|
// Bridge the selected physical port with management port 4 (54.4.1.1)
|
||||||
|
NETC_SWITCH->VFHTDECR0 = MG_BIT(4) | MG_BIT(d->link);
|
||||||
|
NETC_SWITCH->VFHTDECR2 = (1UL << 27) | (1UL << 24); // MFO=1, MLO=1
|
||||||
|
switch_port->BPSTGSR = 2U; // STG0: forwarding
|
||||||
|
NETC_SW0_PORT4->BPSTGSR = 2U; // STG0: forwarding
|
||||||
|
switch_port->POR &= ~(MG_BIT(1) | MG_BIT(0));
|
||||||
|
NETC_SW0_PORT4->POR &= ~(MG_BIT(1) | MG_BIT(0));
|
||||||
|
|
||||||
|
// Initialize Tx BD ring 0 (direct switch enqueue)
|
||||||
|
NETC_ENETC1_SI0->BDR[0].TBBAR0 = (uint32_t) tx_base;
|
||||||
|
NETC_ENETC1_SI0->BDR[0].TBBAR1 = (uint32_t) (tx_base >> 32);
|
||||||
|
NETC_ENETC1_SI0->BDR[0].TBLENR = ETH_DESC_CNT;
|
||||||
|
|
||||||
|
// Initialize Rx BD ring 0
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBICR0 =
|
||||||
|
MG_BIT(31) | MG_BIT(0); // ICEN, interrupt after one frame
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBBAR0 = (uint32_t) rx_base;
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBBAR1 = (uint32_t) (rx_base >> 32);
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBBSR = ETH_PKT_SIZE;
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBLENR = ETH_DESC_CNT;
|
||||||
|
|
||||||
|
// Clear stale device and MSGINTR state before the ring becomes active
|
||||||
|
NETC_ENETC1_SI0->SIRXIDR0 = MG_BIT(0); // RX0, W1C
|
||||||
|
(void) NETC_MSGINTR1->MSI[0].MSIR; // clear pending messages
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBIER = MG_BIT(0); // RXTIE
|
||||||
|
NETC_ENETC1_SI0->BDR[0].TBMR = MG_BIT(31);
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBMR = MG_BIT(31); // EN, CRC=0: strip FCS
|
||||||
|
NETC_ENETC1_PORT->POR &= ~(MG_BIT(1) | MG_BIT(0));
|
||||||
|
NETC_ENETC1->PMR |= MG_BIT(16); // SI0EN
|
||||||
|
NETC_ENETC1_SI0->SIMR |= MG_BIT(31); // EN
|
||||||
|
MG_DSB();
|
||||||
|
if ((NETC_ENETC1->SMCAPR & MG_BIT(0)) == 0U ||
|
||||||
|
(NETC_SWITCH->MPCR & 0x1fU) != 4U) {
|
||||||
|
MG_ERROR(("NETC fixed management path unavailable: SMCAPR=%08lx MPCR=%08lx",
|
||||||
|
(unsigned long) NETC_ENETC1->SMCAPR,
|
||||||
|
(unsigned long) NETC_SWITCH->MPCR));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NETC_ENETC1_PCI->MSIX_MSG_CTL =
|
||||||
|
MG_BIT(15); // Enable RX interrupt, MSIX_EN=1, FUNC_MASK=0
|
||||||
|
MG_DSB();
|
||||||
|
NETC_ENETC1_MSIX[NETC_RX_MSIX_ENTRY].VECTOR_CTRL = 0; // MASK=0, last
|
||||||
|
MG_DSB();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t mg_tcpip_driver_netc_tx(const void *buf, size_t len,
|
||||||
|
struct mg_tcpip_if *ifp) {
|
||||||
|
struct mg_tcpip_driver_netc_data *d =
|
||||||
|
(struct mg_tcpip_driver_netc_data *) ifp->driver_data;
|
||||||
|
uint16_t next;
|
||||||
|
s_tx_clean =
|
||||||
|
(uint16_t) (NETC_ENETC1_SI0->BDR[0].TBCIR & 0xffffU); // BDR_INDEX
|
||||||
|
next = (uint16_t) (s_tx_prod + 1U);
|
||||||
|
if (next >= ETH_DESC_CNT) next = 0;
|
||||||
|
if (len > sizeof(s_txbuf[0])) {
|
||||||
|
MG_ERROR(("Frame too big, %ld", (long) len));
|
||||||
|
len = (size_t) -1; // fail
|
||||||
|
} else if (next == s_tx_clean) {
|
||||||
|
ifp->nerr++;
|
||||||
|
MG_ERROR(("No descriptors available"));
|
||||||
|
len = 0; // retry later
|
||||||
|
} else {
|
||||||
|
memcpy(s_txbuf[s_tx_prod], buf, len); // Copy data
|
||||||
|
s_txdesc[s_tx_prod].buffer_address =
|
||||||
|
(uint64_t) (uintptr_t) s_txbuf[s_tx_prod];
|
||||||
|
s_txdesc[s_tx_prod].buffer_length = (uint16_t) len; // BUFF_LEN
|
||||||
|
s_txdesc[s_tx_prod].frame_length = (uint16_t) len; // FRAME_LEN
|
||||||
|
s_txdesc[s_tx_prod].control =
|
||||||
|
MG_BIT(31) | (2UL << 24) | MG_BIT(23) | ((uint32_t) d->link << 16);
|
||||||
|
// F=1, FLQ=2, SMSO=1, EGR_PORT=link, IPV=0, DR=0
|
||||||
|
MG_DSB();
|
||||||
|
if (++s_tx_prod >= ETH_DESC_CNT) s_tx_prod = 0;
|
||||||
|
NETC_ENETC1_SI0->BDR[0].TBPIR = s_tx_prod; // BDR_INDEX
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_tcpip_driver_netc_update_hash_table(struct mg_tcpip_if *ifp) {
|
||||||
|
// mcast address has NETC XOR hash index 56 (RM 53.4.2.3.1.3.8.4.1)
|
||||||
|
NETC_ENETC1->PSI[0].PSIMMHFR0 = 0;
|
||||||
|
NETC_ENETC1->PSI[0].PSIMMHFR1 = MG_BIT(24);
|
||||||
|
(void) ifp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mg_tcpip_driver_netc_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||||
|
if (ifp->update_mac_hash_table) {
|
||||||
|
mg_tcpip_driver_netc_update_hash_table(ifp);
|
||||||
|
ifp->update_mac_hash_table = false;
|
||||||
|
}
|
||||||
|
if (!s1) return false;
|
||||||
|
struct mg_tcpip_driver_netc_data *d =
|
||||||
|
(struct mg_tcpip_driver_netc_data *) ifp->driver_data;
|
||||||
|
struct netc_port *switch_port = NETC_SW0_PORT(d->link);
|
||||||
|
struct netc_eth_link *eth_link = NETC_SW0_ETH(d->link);
|
||||||
|
uint8_t speed = MG_PHY_SPEED_10M;
|
||||||
|
bool up, full_duplex = false;
|
||||||
|
struct mg_phy phy = {enet_read_phy, enet_write_phy};
|
||||||
|
|
||||||
|
up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
|
||||||
|
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) {
|
||||||
|
uint16_t port_speed = speed == MG_PHY_SPEED_1000M ? 99U
|
||||||
|
: speed == MG_PHY_SPEED_100M ? 9U
|
||||||
|
: 0U;
|
||||||
|
uint32_t config = switch_port->PCR;
|
||||||
|
uint32_t mode = eth_link->PM[0].IF_MODE;
|
||||||
|
config &= ~(0x3fffU << 16); // PCR[29:16] PSPEED
|
||||||
|
config |= (uint32_t) port_speed << 16;
|
||||||
|
mode &= ~(MG_BIT(14) | MG_BIT(13) | MG_BIT(6)); // SSP, HD
|
||||||
|
if (speed == MG_PHY_SPEED_1000M) {
|
||||||
|
mode |= MG_BIT(14); // SSP: 1 Gbps, full duplex
|
||||||
|
} else {
|
||||||
|
if (speed == MG_PHY_SPEED_10M) mode |= MG_BIT(13); // SSP
|
||||||
|
if (!full_duplex) mode |= MG_BIT(6); // HD
|
||||||
|
}
|
||||||
|
switch_port->PCR = config;
|
||||||
|
eth_link->PM[0].IF_MODE = mode;
|
||||||
|
eth_link->PM[1].IF_MODE = mode;
|
||||||
|
|
||||||
|
// TX_CSD, RX_CSD, and SPD_DUP are latched while the PHY is reset/down.
|
||||||
|
eth_link->PM[0].IEVENT = MG_BIT(23) | MG_BIT(22) | MG_BIT(21);
|
||||||
|
MG_DSB();
|
||||||
|
MG_DEBUG(("Link is %uM %s-duplex",
|
||||||
|
speed == MG_PHY_SPEED_1000M ? 1000U
|
||||||
|
: speed == MG_PHY_SPEED_100M ? 100U
|
||||||
|
: 10U,
|
||||||
|
full_duplex ? "full" : "half"));
|
||||||
|
}
|
||||||
|
return up;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSGINTR1_IRQHandler(void);
|
||||||
|
void MSGINTR1_IRQHandler(void) {
|
||||||
|
if (NETC_MSGINTR1->MSI[0].MSIR & MG_BIT(NETC_RX_MSG_INDEX)) {
|
||||||
|
NETC_ENETC1_SI0->SIRXIDR0 = MG_BIT(0); // RX0, W1C
|
||||||
|
MG_DSB();
|
||||||
|
for (int i = 0; i < 10; i++) { // Read as they arrive but not forever
|
||||||
|
uint32_t status;
|
||||||
|
size_t len;
|
||||||
|
if (s_rx_cons ==
|
||||||
|
(uint16_t) (NETC_ENETC1_SI0->BDR[0].RBPIR & 0xffffU))
|
||||||
|
break;
|
||||||
|
status = s_rxdesc[s_rx_cons].writeback.status;
|
||||||
|
if (!(status & MG_BIT(30))) break; // R: writeback is ready
|
||||||
|
len = s_rxdesc[s_rx_cons].writeback.buffer_length;
|
||||||
|
if ((status & (MG_BIT(31) | MG_BIT(30))) ==
|
||||||
|
(MG_BIT(31) | MG_BIT(30)) && // F, R
|
||||||
|
!(status & (0xffU << 16)) && // ERROR
|
||||||
|
!(status & (0xfU << 2)) && // HR=0: regular frame
|
||||||
|
len > 0 && len <= sizeof(s_rxbuf[0])) {
|
||||||
|
mg_tcpip_qwrite(s_rxbuf[s_rx_cons], len, s_ifp); // FCS stripped
|
||||||
|
}
|
||||||
|
s_rxdesc[s_rx_cons].descriptor.buffer_address =
|
||||||
|
(uint64_t) (uintptr_t) s_rxbuf[s_rx_cons];
|
||||||
|
s_rxdesc[s_rx_cons].descriptor.reserved = 0;
|
||||||
|
MG_DSB();
|
||||||
|
if (++s_rx_cons >= ETH_DESC_CNT) s_rx_cons = 0;
|
||||||
|
NETC_ENETC1_SI0->BDR[0].RBCIR = s_rx_cons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MG_DSB();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mg_tcpip_driver mg_tcpip_driver_netc = {mg_tcpip_driver_netc_init,
|
||||||
|
mg_tcpip_driver_netc_tx, NULL,
|
||||||
|
mg_tcpip_driver_netc_poll};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP && \
|
||||||
|
defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
|
||||||
|
|
||||||
|
struct mg_tcpip_driver_netc_data {
|
||||||
|
int mdc_cr;
|
||||||
|
uint8_t phy_addr;
|
||||||
|
|
||||||
|
// Selects the physical Ethernet link (ETH0 through ETH3)
|
||||||
|
// Its switch port is bridged to pseudo-port 4,
|
||||||
|
// which connects internally to ENETC1
|
||||||
|
// IMXRT1180 RM 53.4.1.1 NETC Block diagram
|
||||||
|
uint8_t link;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_PHY_ADDR
|
||||||
|
#define MG_TCPIP_PHY_ADDR 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_DRIVER_MDC_CR
|
||||||
|
#define MG_DRIVER_MDC_CR 48
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_NETC_LINK
|
||||||
|
#define MG_TCPIP_NETC_LINK 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct mg_tcpip_driver;
|
||||||
|
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
||||||
|
|
||||||
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
|
do { \
|
||||||
|
static struct mg_tcpip_driver_netc_data driver_data_; \
|
||||||
|
static struct mg_tcpip_if mif_; \
|
||||||
|
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
||||||
|
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
||||||
|
driver_data_.link = MG_TCPIP_NETC_LINK; \
|
||||||
|
mif_.ip = MG_TCPIP_IP; \
|
||||||
|
mif_.mask = MG_TCPIP_MASK; \
|
||||||
|
mif_.gw = MG_TCPIP_GW; \
|
||||||
|
mif_.driver = &mg_tcpip_driver_netc; \
|
||||||
|
mif_.driver_data = &driver_data_; \
|
||||||
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
|
MG_INFO(("Driver: netc, MAC: %M", mg_print_mac, mif_.mac)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
+30
-2
@@ -11,7 +11,9 @@ enum { // ID1 ID2
|
|||||||
MG_PHY_RTL8201 = 0xc816, // 001c c816 - RTL8201F
|
MG_PHY_RTL8201 = 0xc816, // 001c c816 - RTL8201F
|
||||||
MG_PHY_RTL8211 = 0xc916, // 001c c916 - RTL8211F
|
MG_PHY_RTL8211 = 0xc916, // 001c c916 - RTL8211F
|
||||||
MG_PHY_ICS1894x = 0x15,
|
MG_PHY_ICS1894x = 0x15,
|
||||||
MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894
|
MG_PHY_ICS189432 = 0xf450, // 0015 f450 - ICS1894
|
||||||
|
MG_PHY_YT85x = 0x4f51,
|
||||||
|
MG_PHY_YT8531 = 0xe91a // 4f51 e91a - Motorcomm YT8531
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -29,7 +31,10 @@ enum {
|
|||||||
MG_PHY_RTL82x_REG_PAGESEL = 31,
|
MG_PHY_RTL82x_REG_PAGESEL = 31,
|
||||||
MG_PHY_RTL8201_REG_RMSR = 16, // in page 7
|
MG_PHY_RTL8201_REG_RMSR = 16, // in page 7
|
||||||
MG_PHY_RTL8211_REG_PHYSR = 26, // in page a43
|
MG_PHY_RTL8211_REG_PHYSR = 26, // in page a43
|
||||||
MG_PHY_ICS189432_REG_POLL = 17
|
MG_PHY_ICS189432_REG_POLL = 17,
|
||||||
|
MG_PHY_YT8531_REG_PHYSTS = 17,
|
||||||
|
MG_PHY_YT8531_REG_EXT_ADDR = 30,
|
||||||
|
MG_PHY_YT8531_REG_EXT_DATA = 31
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
|
static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
|
||||||
@@ -60,6 +65,8 @@ static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
|
|||||||
}
|
}
|
||||||
case MG_PHY_ICS1894x:
|
case MG_PHY_ICS1894x:
|
||||||
return "ICS1894x";
|
return "ICS1894x";
|
||||||
|
case MG_PHY_YT85x:
|
||||||
|
return id2 == MG_PHY_YT8531 ? "YT8531" : "YT85x";
|
||||||
default:
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
@@ -75,6 +82,18 @@ void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) {
|
|||||||
id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
|
id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
|
||||||
MG_INFO(("PHY ID: %#04x %#04x (%s)", id1, id2, mg_phy_id_to_str(id1, id2)));
|
MG_INFO(("PHY ID: %#04x %#04x (%s)", id1, id2, mg_phy_id_to_str(id1, id2)));
|
||||||
|
|
||||||
|
if (id1 == MG_PHY_YT85x && id2 == MG_PHY_YT8531) {
|
||||||
|
uint16_t value;
|
||||||
|
// TODO (robertc2000): Autonegotiation remains disabled after PHY is reset
|
||||||
|
// We temporarily force enabling autoneg, until a workaround is found.
|
||||||
|
phy->write_reg(phy_addr, MG_PHY_REG_BCR, MG_BIT(12));
|
||||||
|
phy->write_reg(phy_addr, MG_PHY_YT8531_REG_EXT_ADDR, 0xa003);
|
||||||
|
value = phy->read_reg(phy_addr, MG_PHY_YT8531_REG_EXT_DATA);
|
||||||
|
value &= (uint16_t) ~(MG_BIT(3) | MG_BIT(2) | MG_BIT(1) | MG_BIT(0));
|
||||||
|
phy->write_reg(phy_addr, MG_PHY_YT8531_REG_EXT_DATA,
|
||||||
|
(uint16_t) (value | MG_BIT(1))); // TXDLY: 2 * 150 ps
|
||||||
|
}
|
||||||
|
|
||||||
if (id1 == MG_PHY_DP83x && id2 == MG_PHY_DP83867) {
|
if (id1 == MG_PHY_DP83x && id2 == MG_PHY_DP83867) {
|
||||||
phy->write_reg(phy_addr, 0x0d, 0x1f); // write 0x10d to IO_MUX_CFG (0x0170)
|
phy->write_reg(phy_addr, 0x0d, 0x1f); // write 0x10d to IO_MUX_CFG (0x0170)
|
||||||
phy->write_reg(phy_addr, 0x0e, 0x170);
|
phy->write_reg(phy_addr, 0x0e, 0x170);
|
||||||
@@ -168,6 +187,15 @@ bool mg_phy_up(struct mg_phy *phy, uint8_t phy_addr, bool *full_duplex,
|
|||||||
uint16_t poll_reg = phy->read_reg(phy_addr, MG_PHY_ICS189432_REG_POLL);
|
uint16_t poll_reg = phy->read_reg(phy_addr, MG_PHY_ICS189432_REG_POLL);
|
||||||
*full_duplex = poll_reg & MG_BIT(14);
|
*full_duplex = poll_reg & MG_BIT(14);
|
||||||
*speed = (poll_reg & MG_BIT(15)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
|
*speed = (poll_reg & MG_BIT(15)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
|
||||||
|
} else if (id1 == MG_PHY_YT85x) {
|
||||||
|
uint16_t id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
|
||||||
|
if (id2 == MG_PHY_YT8531) {
|
||||||
|
uint16_t physts = phy->read_reg(phy_addr, MG_PHY_YT8531_REG_PHYSTS);
|
||||||
|
*full_duplex = physts & MG_BIT(13);
|
||||||
|
*speed = (physts & MG_BIT(15)) ? MG_PHY_SPEED_1000M
|
||||||
|
: (physts & MG_BIT(14)) ? MG_PHY_SPEED_100M
|
||||||
|
: MG_PHY_SPEED_10M;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return up;
|
return up;
|
||||||
|
|||||||
@@ -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') {
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ typedef size_t (*mg_pm_t)(mg_pfn_t fn, void *arg, va_list *);
|
|||||||
// - mg_print_base64 - prints a buffer as a base64-encoded string
|
// - mg_print_base64 - prints a buffer as a base64-encoded string
|
||||||
// - mg_print_esc - prints a JSON-escaped string
|
// - mg_print_esc - prints a JSON-escaped string
|
||||||
// - mg_print_hex - prints a buffer as a hex string
|
// - mg_print_hex - prints a buffer as a hex string
|
||||||
// - mg_print_ip - prints an IP address in a struct mg_str
|
// - mg_print_ip - prints an IP address in a struct mg_addr
|
||||||
// - mg_print_ip_port - prints IP address and port in a struct mg_str
|
// - mg_print_ip_port - prints IP address and port in a struct mg_addr
|
||||||
// - mg_print_ip4 - prints an IPv4 address
|
// - mg_print_ip4 - prints an IPv4 address
|
||||||
// - mg_print_ip6 - prints an IPv6 address
|
// - mg_print_ip6 - prints an IPv6 address
|
||||||
// - mg_print_mac - prints a MAC address
|
// - mg_print_mac - prints a MAC address
|
||||||
|
|||||||
@@ -67,8 +67,10 @@ bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) {
|
|||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
data = mg_vmprintf(fmt, &ap);
|
data = mg_vmprintf(fmt, &ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
result = mg_file_write(fs, path, data, strlen(data));
|
if (data != NULL) {
|
||||||
mg_free(data);
|
result = mg_file_write(fs, path, data, strlen(data));
|
||||||
|
mg_free(data);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-35
@@ -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) {
|
||||||
@@ -75,9 +50,9 @@ static void *ff_open(const char *path, int flags) {
|
|||||||
if (flags & MG_FS_WRITE) {
|
if (flags & MG_FS_WRITE) {
|
||||||
mode |= FA_WRITE;
|
mode |= FA_WRITE;
|
||||||
if (flags & MG_FS_EXCL) {
|
if (flags & MG_FS_EXCL) {
|
||||||
mode |= FA_OPEN_ALWAYS | FA_OPEN_APPEND;
|
|
||||||
} else {
|
|
||||||
mode |= FA_CREATE_NEW;
|
mode |= FA_CREATE_NEW;
|
||||||
|
} else {
|
||||||
|
mode |= FA_OPEN_ALWAYS | FA_OPEN_APPEND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((fp = mg_calloc(1, sizeof(*fp))) != NULL &&
|
if ((fp = mg_calloc(1, sizeof(*fp))) != NULL &&
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "health.h"
|
||||||
|
#include "health_cortex.h"
|
||||||
|
|
||||||
|
struct mg_health mg_health_record;
|
||||||
|
|
||||||
|
void mg_health_init(void) {
|
||||||
|
static const char magic[] = MG_HEALTH_MAGIC;
|
||||||
|
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||||
|
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||||
|
#if MG_HEALTH == MG_HEALTH_CORTEX
|
||||||
|
mg_health_cortex_init(&mg_health_cortex);
|
||||||
|
mg_health_record.get_blob = mg_health_cortex_get_blob;
|
||||||
|
mg_health_record.fn_data = &mg_health_cortex;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mg_str mg_health_get_blob(void) {
|
||||||
|
return !mg_health_valid() || mg_health_record.get_blob == NULL
|
||||||
|
? mg_str("")
|
||||||
|
: mg_health_record.get_blob(mg_health_record.fn_data);
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// Device health monitoring
|
||||||
|
//
|
||||||
|
// A health provider owns the report format and returns it as an opaque blob.
|
||||||
|
// Applications and transports use mg_health_get_blob() without knowing how the
|
||||||
|
// report was collected or encoded.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
#define MG_HEALTH_NONE 0
|
||||||
|
#define MG_HEALTH_CORTEX 1
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH
|
||||||
|
#define MG_HEALTH MG_HEALTH_NONE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_MAGIC
|
||||||
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Why the device restarted. Hardware usually reports several flags at once,
|
||||||
|
// so these are ordered by how much they tell you: the most specific cause a
|
||||||
|
// device reports wins
|
||||||
|
enum mg_health_reason {
|
||||||
|
MG_HEALTH_RESET_UNKNOWN = 0, // Nothing reported, or no masks configured
|
||||||
|
MG_HEALTH_RESET_POWER, // Power-on reset, RAM contents are lost
|
||||||
|
MG_HEALTH_RESET_BROWNOUT, // Supply dipped below the brown-out level
|
||||||
|
MG_HEALTH_RESET_PIN, // External reset pin, e.g. the reset button
|
||||||
|
MG_HEALTH_RESET_SOFTWARE, // Firmware asked for it, e.g. NVIC_SystemReset
|
||||||
|
MG_HEALTH_RESET_LOWPOWER, // Illegal or failed low-power transition
|
||||||
|
MG_HEALTH_RESET_WATCHDOG, // Watchdog fired: the application stopped
|
||||||
|
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct mg_str mg_health_blob;
|
||||||
|
|
||||||
|
struct mg_health {
|
||||||
|
char magic[4];
|
||||||
|
mg_health_blob (*get_blob)(void *);
|
||||||
|
void *fn_data;
|
||||||
|
uint32_t reset_reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct mg_health mg_health_record;
|
||||||
|
|
||||||
|
// Return true if the record holds valid data, i.e. it survived the reset and
|
||||||
|
// was written by this firmware layout
|
||||||
|
static inline bool mg_health_valid(void) {
|
||||||
|
static const char magic[] = MG_HEALTH_MAGIC;
|
||||||
|
return memcmp(mg_health_record.magic, magic,
|
||||||
|
sizeof(mg_health_record.magic)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select and initialise the configured health provider. Call after
|
||||||
|
// initializing the board-specific persistent-storage
|
||||||
|
void mg_health_init(void);
|
||||||
|
|
||||||
|
// Return the provider's opaque report. The returned memory is provider-owned
|
||||||
|
// and remains valid until the next call to this function
|
||||||
|
struct mg_str mg_health_get_blob(void);
|
||||||
|
|
||||||
|
static inline int mg_health_reason(void) {
|
||||||
|
return mg_health_valid() ? (int) mg_health_record.reset_reason
|
||||||
|
: MG_HEALTH_RESET_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *mg_health_reason_str(int reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case MG_HEALTH_RESET_POWER:
|
||||||
|
return "power";
|
||||||
|
case MG_HEALTH_RESET_BROWNOUT:
|
||||||
|
return "brownout";
|
||||||
|
case MG_HEALTH_RESET_PIN:
|
||||||
|
return "pin";
|
||||||
|
case MG_HEALTH_RESET_SOFTWARE:
|
||||||
|
return "software";
|
||||||
|
case MG_HEALTH_RESET_LOWPOWER:
|
||||||
|
return "lowpower";
|
||||||
|
case MG_HEALTH_RESET_WATCHDOG:
|
||||||
|
return "watchdog";
|
||||||
|
case MG_HEALTH_RESET_FAULT:
|
||||||
|
return "fault";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
#include "health_cortex.h"
|
||||||
|
#include "printf.h"
|
||||||
|
|
||||||
|
#if MG_HEALTH == MG_HEALTH_CORTEX
|
||||||
|
|
||||||
|
#define MG_SCB_CPUID (*(volatile uint32_t *) 0xe000ed00)
|
||||||
|
#define MG_SCB_CFSR (*(volatile uint32_t *) 0xe000ed28)
|
||||||
|
#define MG_SCB_HFSR (*(volatile uint32_t *) 0xe000ed2c)
|
||||||
|
#define MG_SCB_DFSR (*(volatile uint32_t *) 0xe000ed30)
|
||||||
|
#define MG_SCB_MMFAR (*(volatile uint32_t *) 0xe000ed34)
|
||||||
|
#define MG_SCB_BFAR (*(volatile uint32_t *) 0xe000ed38)
|
||||||
|
#define MG_SCB_AFSR (*(volatile uint32_t *) 0xe000ed3c)
|
||||||
|
#define MG_SCB_ABFSR (*(volatile uint32_t *) 0xe000efa8)
|
||||||
|
|
||||||
|
#define MG_CPU_CORTEX_M0 0xc20
|
||||||
|
#define MG_CPU_CORTEX_M23 0xd20
|
||||||
|
#define MG_CPU_CORTEX_M3 0xc23
|
||||||
|
#define MG_CPU_CORTEX_M4 0xc24
|
||||||
|
#define MG_CPU_CORTEX_M7 0xc27
|
||||||
|
#define MG_CPU_CORTEX_M0P 0xc60
|
||||||
|
#define MG_CPU_CORTEX_M33 0xd21
|
||||||
|
#define MG_CPU_CORTEX_M35P 0xd31
|
||||||
|
#define MG_CPU_CORTEX_M52 0xd24
|
||||||
|
#define MG_CPU_CORTEX_M55 0xd22
|
||||||
|
#define MG_CPU_CORTEX_M85 0xd23
|
||||||
|
|
||||||
|
extern uint32_t __StackLimit __attribute__((weak));
|
||||||
|
extern uint32_t __StackTop __attribute__((weak));
|
||||||
|
extern uint32_t _sstack __attribute__((weak));
|
||||||
|
extern uint32_t _estack __attribute__((weak));
|
||||||
|
extern bool mg_health_cortex_stack_range(const void *, uintptr_t *, uintptr_t *)
|
||||||
|
__attribute__((weak));
|
||||||
|
|
||||||
|
static struct mg_health_cortex_record s_record;
|
||||||
|
static char s_report[MG_HEALTH_CORTEX_REPORT_SIZE];
|
||||||
|
|
||||||
|
static bool storage_init(void *storage, size_t size) {
|
||||||
|
// Note: actual init is handled in main()
|
||||||
|
(void) storage;
|
||||||
|
(void) size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool storage_read(const void *storage, size_t offset, void *buf,
|
||||||
|
size_t len) {
|
||||||
|
volatile const uint8_t *src = (volatile const uint8_t *) storage + offset;
|
||||||
|
uint8_t *dst = (uint8_t *) buf;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < len; i++) dst[i] = src[i];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool storage_write(void *storage, size_t offset, const void *buf,
|
||||||
|
size_t len) {
|
||||||
|
volatile uint8_t *dst = (volatile uint8_t *) storage + offset;
|
||||||
|
const uint8_t *src = (const uint8_t *) buf;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < len; i++) dst[i] = src[i];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mg_health_cortex mg_health_cortex = {
|
||||||
|
MG_HEALTH_CORTEX_STORAGE,
|
||||||
|
MG_HEALTH_CORTEX_STORAGE_SIZE,
|
||||||
|
storage_init,
|
||||||
|
storage_read,
|
||||||
|
storage_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool storage_bounds(const struct mg_health_cortex *ctx, size_t offset,
|
||||||
|
size_t len) {
|
||||||
|
return ctx != NULL && ctx->storage != NULL && offset <= ctx->storage_size &&
|
||||||
|
len <= ctx->storage_size - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool storage_configured(const struct mg_health_cortex *ctx) {
|
||||||
|
return ctx != NULL && ctx->storage != NULL && ctx->storage_size != 0 &&
|
||||||
|
ctx->storage_size >= sizeof(struct mg_health_cortex_record) &&
|
||||||
|
ctx->init != NULL && ctx->read != NULL && ctx->write != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool record_read(struct mg_health_cortex *ctx,
|
||||||
|
struct mg_health_cortex_record *record) {
|
||||||
|
static const char magic[] = MG_HEALTH_MAGIC;
|
||||||
|
if (ctx == NULL || ctx->read == NULL ||
|
||||||
|
!storage_bounds(ctx, 0, sizeof(*record)) ||
|
||||||
|
!ctx->read(ctx->storage, 0, record, sizeof(*record))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return memcmp(record->magic, magic, sizeof(record->magic)) == 0 &&
|
||||||
|
record->version == MG_HEALTH_CORTEX_VERSION &&
|
||||||
|
record->stack_word_count <= MG_HEALTH_CORTEX_STACK_WORDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool record_write(struct mg_health_cortex *ctx,
|
||||||
|
const struct mg_health_cortex_record *record) {
|
||||||
|
char invalid[sizeof(record->magic)] = {0};
|
||||||
|
size_t offset = sizeof(record->magic);
|
||||||
|
if (ctx == NULL || ctx->write == NULL ||
|
||||||
|
!storage_bounds(ctx, 0, sizeof(*record)) ||
|
||||||
|
!ctx->write(ctx->storage, 0, &invalid, sizeof(invalid))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ctx->write(ctx->storage, offset, (const uint8_t *) record + offset,
|
||||||
|
sizeof(*record) - offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ctx->write(ctx->storage, 0, record->magic, sizeof(record->magic)))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stack_range(const void *ptr, uintptr_t *lo, uintptr_t *hi) {
|
||||||
|
if (mg_health_cortex_stack_range != NULL &&
|
||||||
|
mg_health_cortex_stack_range(ptr, lo, hi)) {
|
||||||
|
return *lo < *hi;
|
||||||
|
} else if (&__StackLimit != NULL && &__StackTop != NULL) {
|
||||||
|
*lo = (uintptr_t) &__StackLimit;
|
||||||
|
*hi = (uintptr_t) &__StackTop;
|
||||||
|
} else if (&_sstack != NULL && &_estack != NULL) {
|
||||||
|
*lo = (uintptr_t) &_sstack;
|
||||||
|
*hi = (uintptr_t) &_estack;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return *lo < *hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stack_valid(const void *ptr, size_t words) {
|
||||||
|
uintptr_t lo, hi, p = (uintptr_t) ptr;
|
||||||
|
if ((p & 3U) != 0) return 0;
|
||||||
|
if (!stack_range(ptr, &lo, &hi)) return -1;
|
||||||
|
return p >= lo && p <= hi && words <= (hi - p) / sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool has_fault_status(uint32_t part) {
|
||||||
|
return part == MG_CPU_CORTEX_M3 || part == MG_CPU_CORTEX_M4 ||
|
||||||
|
part == MG_CPU_CORTEX_M7 || part == MG_CPU_CORTEX_M33 ||
|
||||||
|
part == MG_CPU_CORTEX_M35P || part == MG_CPU_CORTEX_M52 ||
|
||||||
|
part == MG_CPU_CORTEX_M55 || part == MG_CPU_CORTEX_M85;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *core_name(uint32_t part) {
|
||||||
|
switch (part) {
|
||||||
|
case MG_CPU_CORTEX_M0:
|
||||||
|
return "cortex-m0";
|
||||||
|
case MG_CPU_CORTEX_M0P:
|
||||||
|
return "cortex-m0+";
|
||||||
|
case MG_CPU_CORTEX_M23:
|
||||||
|
return "cortex-m23";
|
||||||
|
case MG_CPU_CORTEX_M3:
|
||||||
|
return "cortex-m3";
|
||||||
|
case MG_CPU_CORTEX_M4:
|
||||||
|
return "cortex-m4";
|
||||||
|
case MG_CPU_CORTEX_M7:
|
||||||
|
return "cortex-m7";
|
||||||
|
case MG_CPU_CORTEX_M33:
|
||||||
|
return "cortex-m33";
|
||||||
|
case MG_CPU_CORTEX_M35P:
|
||||||
|
return "cortex-m35p";
|
||||||
|
case MG_CPU_CORTEX_M52:
|
||||||
|
return "cortex-m52";
|
||||||
|
case MG_CPU_CORTEX_M55:
|
||||||
|
return "cortex-m55";
|
||||||
|
case MG_CPU_CORTEX_M85:
|
||||||
|
return "cortex-m85";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *arch_name(uint32_t part) {
|
||||||
|
if (part == MG_CPU_CORTEX_M0 || part == MG_CPU_CORTEX_M0P) return "armv6-m";
|
||||||
|
if (part == MG_CPU_CORTEX_M3) return "armv7-m";
|
||||||
|
if (part == MG_CPU_CORTEX_M4 || part == MG_CPU_CORTEX_M7) return "armv7e-m";
|
||||||
|
return "armv8-m";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool json_append(char *buf, size_t size, size_t *offset, const char *fmt,
|
||||||
|
...) {
|
||||||
|
va_list ap;
|
||||||
|
size_t available, n;
|
||||||
|
if (*offset >= size) return false;
|
||||||
|
available = size - *offset;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
n = mg_vsnprintf(buf + *offset, available, fmt, &ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (n >= available) {
|
||||||
|
*offset = size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*offset += n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_health_cortex_init(struct mg_health_cortex *ctx) {
|
||||||
|
if (storage_configured(ctx)) {
|
||||||
|
(void) ctx->init(ctx->storage, ctx->storage_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mg_str mg_health_cortex_get_blob(void *data) {
|
||||||
|
struct mg_health_cortex *ctx = (struct mg_health_cortex *) data;
|
||||||
|
struct mg_health_cortex_record *r = &s_record;
|
||||||
|
uint32_t part;
|
||||||
|
size_t n = 0, i;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
if (!storage_configured(ctx)) {
|
||||||
|
return mg_str(
|
||||||
|
"{\"valid\":false,\"error\":\"health storage is not configured\"}");
|
||||||
|
}
|
||||||
|
if (!record_read(ctx, r)) return mg_str("{\"valid\":false}");
|
||||||
|
part = (r->cpuid >> 4) & 0xfffU;
|
||||||
|
ok = json_append(
|
||||||
|
s_report, sizeof(s_report), &n,
|
||||||
|
"{\"valid\":true,\"version\":%lu,\"arch\":\"%s\","
|
||||||
|
"\"core\":\"%s\",\"image\":\"firmware.elf\","
|
||||||
|
"\"map\":\"firmware.elf.map\",\"binary\":\"firmware.bin\","
|
||||||
|
"\"cpuid\":\"0x%08lx\",\"exception_sp\":\"0x%08lx\","
|
||||||
|
"\"exc_return\":\"0x%08lx\",\"exception_valid\":%s,"
|
||||||
|
"\"stack_valid\":%s,\"regs\":{"
|
||||||
|
"\"r0\":\"0x%08lx\",\"r1\":\"0x%08lx\","
|
||||||
|
"\"r2\":\"0x%08lx\",\"r3\":\"0x%08lx\","
|
||||||
|
"\"r4\":\"0x%08lx\",\"r5\":\"0x%08lx\","
|
||||||
|
"\"r6\":\"0x%08lx\",\"r7\":\"0x%08lx\","
|
||||||
|
"\"r8\":\"0x%08lx\",\"r9\":\"0x%08lx\","
|
||||||
|
"\"r10\":\"0x%08lx\",\"r11\":\"0x%08lx\","
|
||||||
|
"\"r12\":\"0x%08lx\",\"sp\":\"0x%08lx\","
|
||||||
|
"\"lr\":\"0x%08lx\",\"pc\":\"0x%08lx\","
|
||||||
|
"\"xpsr\":\"0x%08lx\"},\"fault\":{"
|
||||||
|
"\"cfsr\":\"0x%08lx\",\"hfsr\":\"0x%08lx\","
|
||||||
|
"\"dfsr\":\"0x%08lx\",\"afsr\":\"0x%08lx\","
|
||||||
|
"\"mmfar\":\"0x%08lx\",\"bfar\":\"0x%08lx\","
|
||||||
|
"\"abfsr\":\"0x%08lx\"},\"stack\":{"
|
||||||
|
"\"addr\":\"0x%08lx\",\"word_size\":4,\"words\":[",
|
||||||
|
(unsigned long) r->version, arch_name(part), core_name(part),
|
||||||
|
(unsigned long) r->cpuid, (unsigned long) r->exception_sp,
|
||||||
|
(unsigned long) r->exc_return, r->exception_valid > 0 ? "true" : "false",
|
||||||
|
r->stack_valid > 0 ? "true" : "false", (unsigned long) r->r0,
|
||||||
|
(unsigned long) r->r1, (unsigned long) r->r2, (unsigned long) r->r3,
|
||||||
|
(unsigned long) r->r4, (unsigned long) r->r5, (unsigned long) r->r6,
|
||||||
|
(unsigned long) r->r7, (unsigned long) r->r8, (unsigned long) r->r9,
|
||||||
|
(unsigned long) r->r10, (unsigned long) r->r11, (unsigned long) r->r12,
|
||||||
|
(unsigned long) r->sp, (unsigned long) r->lr, (unsigned long) r->pc,
|
||||||
|
(unsigned long) r->psr, (unsigned long) r->cfsr, (unsigned long) r->hfsr,
|
||||||
|
(unsigned long) r->dfsr, (unsigned long) r->afsr,
|
||||||
|
(unsigned long) r->mmfar, (unsigned long) r->bfar,
|
||||||
|
(unsigned long) r->abfsr, (unsigned long) r->sp);
|
||||||
|
for (i = 0; ok && i < r->stack_word_count; i++) {
|
||||||
|
ok = json_append(s_report, sizeof(s_report), &n, "%s\"0x%08lx\"",
|
||||||
|
i == 0 ? "" : ",", (unsigned long) r->stack_words[i]);
|
||||||
|
}
|
||||||
|
if (ok) ok = json_append(s_report, sizeof(s_report), &n, "]}}");
|
||||||
|
return ok ? mg_str_n(s_report, n)
|
||||||
|
: mg_str("{\"valid\":false,\"error\":\"JSON overflow\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((used)) mg_health_cortex_handler(
|
||||||
|
uint32_t *exception_stack, uint32_t lr,
|
||||||
|
const uint32_t *callee_saved) {
|
||||||
|
static const char magic[] = MG_HEALTH_MAGIC;
|
||||||
|
uint32_t cpuid = MG_SCB_CPUID;
|
||||||
|
uint32_t part = (cpuid >> 4) & 0xfffU;
|
||||||
|
size_t fp_words = (lr & (1U << 4)) == 0 ? 18U : 0U;
|
||||||
|
int valid = stack_valid(exception_stack, fp_words + 8U);
|
||||||
|
struct mg_health_cortex_record *r = &s_record;
|
||||||
|
volatile uint32_t *words = (volatile uint32_t *) r;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(*r) / sizeof(*words); i++) words[i] = 0;
|
||||||
|
r->version = MG_HEALTH_CORTEX_VERSION;
|
||||||
|
r->cpuid = cpuid;
|
||||||
|
r->exception_sp = (uint32_t) (uintptr_t) exception_stack;
|
||||||
|
r->exc_return = lr;
|
||||||
|
r->exception_valid = valid;
|
||||||
|
r->r4 = callee_saved[0];
|
||||||
|
r->r5 = callee_saved[1];
|
||||||
|
r->r6 = callee_saved[2];
|
||||||
|
r->r7 = callee_saved[3];
|
||||||
|
r->r8 = callee_saved[4];
|
||||||
|
r->r9 = callee_saved[5];
|
||||||
|
r->r10 = callee_saved[6];
|
||||||
|
r->r11 = callee_saved[7];
|
||||||
|
if (valid > 0) {
|
||||||
|
uint32_t *core_frame = exception_stack + fp_words;
|
||||||
|
uint32_t *fault_sp;
|
||||||
|
uintptr_t lo, hi, p;
|
||||||
|
size_t count;
|
||||||
|
r->r0 = core_frame[0];
|
||||||
|
r->r1 = core_frame[1];
|
||||||
|
r->r2 = core_frame[2];
|
||||||
|
r->r3 = core_frame[3];
|
||||||
|
r->r12 = core_frame[4];
|
||||||
|
r->lr = core_frame[5];
|
||||||
|
r->pc = core_frame[6];
|
||||||
|
r->psr = core_frame[7];
|
||||||
|
fault_sp = core_frame + 8;
|
||||||
|
if (r->psr & (1U << 9)) fault_sp++;
|
||||||
|
r->sp = (uint32_t) (uintptr_t) fault_sp;
|
||||||
|
r->stack_valid = stack_valid(fault_sp, 0);
|
||||||
|
p = (uintptr_t) fault_sp;
|
||||||
|
if (r->stack_valid > 0 && stack_range(fault_sp, &lo, &hi)) {
|
||||||
|
(void) lo;
|
||||||
|
count = (hi - p) / sizeof(uint32_t);
|
||||||
|
if (count > MG_HEALTH_CORTEX_STACK_WORDS)
|
||||||
|
count = MG_HEALTH_CORTEX_STACK_WORDS;
|
||||||
|
r->stack_word_count = (uint32_t) count;
|
||||||
|
for (i = 0; i < count; i++) r->stack_words[i] = fault_sp[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_fault_status(part)) {
|
||||||
|
r->cfsr = MG_SCB_CFSR;
|
||||||
|
r->hfsr = MG_SCB_HFSR;
|
||||||
|
r->dfsr = MG_SCB_DFSR;
|
||||||
|
r->afsr = MG_SCB_AFSR;
|
||||||
|
r->bfar = MG_SCB_BFAR;
|
||||||
|
r->mmfar = MG_SCB_MMFAR;
|
||||||
|
if (part == MG_CPU_CORTEX_M7) r->abfsr = MG_SCB_ABFSR;
|
||||||
|
}
|
||||||
|
memcpy(r->magic, magic, sizeof(r->magic));
|
||||||
|
(void) record_write(&mg_health_cortex, r);
|
||||||
|
for (;;) (void) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardFault_Handler(void);
|
||||||
|
__attribute__((naked)) void HardFault_Handler(void) {
|
||||||
|
__asm volatile(
|
||||||
|
"mov r0, lr \n"
|
||||||
|
"movs r1, #4 \n"
|
||||||
|
"tst r0, r1 \n"
|
||||||
|
"beq 1f \n"
|
||||||
|
"mrs r0, psp \n"
|
||||||
|
"b 2f \n"
|
||||||
|
"1: \n"
|
||||||
|
"mrs r0, msp \n"
|
||||||
|
"2: \n"
|
||||||
|
"mov r1, lr \n"
|
||||||
|
"stmdb sp!, {r4-r11} \n"
|
||||||
|
"mov r2, sp \n"
|
||||||
|
"b mg_health_cortex_handler \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "health.h"
|
||||||
|
|
||||||
|
#define MG_HEALTH_CORTEX_VERSION 1U
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_STORAGE
|
||||||
|
#define MG_HEALTH_CORTEX_STORAGE NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_STORAGE_SIZE
|
||||||
|
#define MG_HEALTH_CORTEX_STORAGE_SIZE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_STACK_WORDS
|
||||||
|
#define MG_HEALTH_CORTEX_STACK_WORDS 980U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_HEALTH_CORTEX_REPORT_SIZE
|
||||||
|
#define MG_HEALTH_CORTEX_REPORT_SIZE 16384U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Storage operations are deliberately format-agnostic. H723 uses the default
|
||||||
|
// direct-memory implementation; boards with different retained storage can
|
||||||
|
// replace these callbacks before calling MG_HEALTH_INIT()
|
||||||
|
struct mg_health_cortex {
|
||||||
|
void *storage;
|
||||||
|
size_t storage_size;
|
||||||
|
bool (*init)(void *, size_t);
|
||||||
|
bool (*read)(const void *, size_t, void *, size_t);
|
||||||
|
bool (*write)(void *, size_t, const void *, size_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mg_health_cortex_record {
|
||||||
|
char magic[4];
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
uint32_t cpuid;
|
||||||
|
uint32_t exception_sp;
|
||||||
|
uint32_t sp;
|
||||||
|
uint32_t exc_return;
|
||||||
|
int32_t exception_valid;
|
||||||
|
int32_t stack_valid;
|
||||||
|
|
||||||
|
uint32_t r0;
|
||||||
|
uint32_t r1;
|
||||||
|
uint32_t r2;
|
||||||
|
uint32_t r3;
|
||||||
|
uint32_t r4;
|
||||||
|
uint32_t r5;
|
||||||
|
uint32_t r6;
|
||||||
|
uint32_t r7;
|
||||||
|
uint32_t r8;
|
||||||
|
uint32_t r9;
|
||||||
|
uint32_t r10;
|
||||||
|
uint32_t r11;
|
||||||
|
uint32_t r12;
|
||||||
|
uint32_t lr;
|
||||||
|
uint32_t pc;
|
||||||
|
uint32_t psr;
|
||||||
|
|
||||||
|
uint32_t cfsr;
|
||||||
|
uint32_t hfsr;
|
||||||
|
uint32_t dfsr;
|
||||||
|
uint32_t afsr;
|
||||||
|
uint32_t bfar;
|
||||||
|
uint32_t mmfar;
|
||||||
|
uint32_t abfsr;
|
||||||
|
|
||||||
|
uint32_t stack_word_count;
|
||||||
|
uint32_t stack_words[MG_HEALTH_CORTEX_STACK_WORDS];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct mg_health_cortex mg_health_cortex;
|
||||||
|
|
||||||
|
void mg_health_cortex_init(struct mg_health_cortex *);
|
||||||
|
struct mg_str mg_health_cortex_get_blob(void *);
|
||||||
+62
-12
@@ -170,6 +170,7 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
|
|||||||
mg_str_to_num(mg_str_n(src + i + 1, 2), 16, &dst[j], sizeof(uint8_t));
|
mg_str_to_num(mg_str_n(src + i + 1, 2), 16, &dst[j], sizeof(uint8_t));
|
||||||
i += 2;
|
i += 2;
|
||||||
} else {
|
} else {
|
||||||
|
dst[0] = '\0';
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (is_form_url_encoded && src[i] == '+') {
|
} else if (is_form_url_encoded && src[i] == '+') {
|
||||||
@@ -818,9 +819,11 @@ static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
path[path_size - 1] = '\0';
|
path[path_size - 1] = '\0';
|
||||||
// Terminate root dir with slash
|
// Terminate root dir with slash
|
||||||
if (n > 0 && path[n - 1] != '/') path[n++] = '/', path[n] = '\0';
|
if (n > 0 && path[n - 1] != '/') path[n++] = '/', path[n] = '\0';
|
||||||
if (url.len < hm->uri.len) {
|
if (url.len < hm->uri.len &&
|
||||||
mg_url_decode(hm->uri.buf + url.len, hm->uri.len - url.len, path + n,
|
mg_url_decode(hm->uri.buf + url.len, hm->uri.len - url.len, path + n,
|
||||||
path_size - n, 0);
|
path_size - n, 0) < 0) {
|
||||||
|
mg_http_reply(c, 400, "", "Invalid path");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
path[path_size - 1] = '\0'; // Double-check
|
path[path_size - 1] = '\0'; // Double-check
|
||||||
n = strlen(path);
|
n = strlen(path);
|
||||||
@@ -972,8 +975,14 @@ long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
struct mg_fs *fs, const char *dir, size_t max_size) {
|
struct mg_fs *fs, const char *dir, size_t max_size) {
|
||||||
char buf[20] = "0", file[MG_PATH_MAX], path[MG_PATH_MAX];
|
char buf[20] = "0", file[MG_PATH_MAX], path[MG_PATH_MAX];
|
||||||
long res = 0, offset;
|
long res = 0, offset;
|
||||||
mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
|
if (mg_http_get_var(&hm->query, "offset", buf, sizeof(buf)) == -3) {
|
||||||
mg_http_get_var(&hm->query, "file", file, sizeof(file));
|
mg_http_reply(c, 400, "", "invalid offset");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
if (mg_http_get_var(&hm->query, "file", file, sizeof(file)) == -3) {
|
||||||
|
mg_http_reply(c, 400, "", "invalid file");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
offset = strtol(buf, NULL, 0);
|
offset = strtol(buf, NULL, 0);
|
||||||
mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file);
|
mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file);
|
||||||
if (hm->body.len == 0) {
|
if (hm->body.len == 0) {
|
||||||
@@ -1030,7 +1039,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) {
|
||||||
@@ -1108,21 +1117,61 @@ void mg_http_start_ota(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
mg_call(c, MG_EV_READ, &c->recv.len);
|
mg_call(c, MG_EV_READ, &c->recv.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mg_http_stream_body(struct mg_connection *c, int ev, void *ev_data,
|
||||||
|
struct mg_str uri_pattern,
|
||||||
|
void (*cb)(struct mg_http_message *, struct mg_str *,
|
||||||
|
void **user_data)) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
|
||||||
|
// Catch upload requests early, without buffering whole body
|
||||||
|
// When we receive MG_EV_HTTP_HDRS event, that means we've received all
|
||||||
|
// HTTP headers but not necessarily full HTTP body
|
||||||
|
if (ev == MG_EV_HTTP_HDRS &&
|
||||||
|
(mg_strcmp(hm->method, mg_str("POST")) == 0 ||
|
||||||
|
mg_strcmp(hm->method, mg_str("PUT")) == 0) &&
|
||||||
|
hm->body.len != (size_t) ~0 && mg_match(hm->uri, uri_pattern, NULL)) {
|
||||||
|
c->pfn = NULL; // Silence HTTP protocol handler, we'll use MG_EV_READ
|
||||||
|
c->pfn_data = (void *) (ptrdiff_t) hm->body.len; // Record expected len
|
||||||
|
cb(hm, NULL, &c->fn_data); // Call handler before deleting headers
|
||||||
|
if (mg_http_get_header(hm, "Expect") != NULL) {
|
||||||
|
mg_http_reply(c, 100, NULL, ""); // If curl expects, we continue
|
||||||
|
}
|
||||||
|
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->pfn == NULL && ev != MG_EV_OPEN) {
|
||||||
|
if (c->pfn_data != NULL && c->recv.len > 0) {
|
||||||
|
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA
|
||||||
|
size_t left = (size_t) (ptrdiff_t) c->pfn_data, r = c->recv.len;
|
||||||
|
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left;
|
||||||
|
struct mg_str data = mg_str_n((char *) c->recv.buf, aligned);
|
||||||
|
if (data.len) cb(NULL, &data, &c->fn_data);
|
||||||
|
c->pfn_data = (void *) (ptrdiff_t) (left - aligned);
|
||||||
|
c->recv.len -= aligned;
|
||||||
|
}
|
||||||
|
if (c->pfn_data == NULL && c->is_draining == 0) {
|
||||||
|
// Finished upload
|
||||||
|
mg_http_reply(c, 200, NULL, "ok\n");
|
||||||
|
c->is_draining = 1;
|
||||||
|
cb(NULL, NULL, &c->fn_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_hex_digit(int c) {
|
static bool is_hex_digit(int c) {
|
||||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
|
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
|
||||||
(c >= 'A' && c <= 'F');
|
(c >= 'A' && c <= 'F');
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -1184,8 +1233,9 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
// contain a Content-length header (unless the body length is 0, in
|
// contain a Content-length header (unless the body length is 0, in
|
||||||
// which case it can be omitted). Other requests can also contain a
|
// which case it can be omitted). Other requests can also contain a
|
||||||
// body, but their content has no defined semantics (RFC 7231)
|
// body, but their content has no defined semantics (RFC 7231)
|
||||||
|
// It is a non-chunked request, so the offset (ofs) is updated after
|
||||||
|
// exiting this branch.
|
||||||
if (hm.body.len != 0) require_content_len = true;
|
if (hm.body.len != 0) require_content_len = true;
|
||||||
ofs += (size_t) n; // this request has been processed
|
|
||||||
} else if (is_response) {
|
} else if (is_response) {
|
||||||
// HTTP spec 7.2 Entity body: All other responses must include a body
|
// HTTP spec 7.2 Entity body: All other responses must include a body
|
||||||
// or Content-Length header field defined with a value of 0.
|
// or Content-Length header field defined with a value of 0.
|
||||||
|
|||||||
+23
@@ -201,3 +201,26 @@ size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
|
|||||||
|
|
||||||
// Returns the HTTP status code from a parsed response message (e.g. 200, 404).
|
// Returns the HTTP status code from a parsed response message (e.g. 200, 404).
|
||||||
int mg_http_status(const struct mg_http_message *hm);
|
int mg_http_status(const struct mg_http_message *hm);
|
||||||
|
|
||||||
|
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
||||||
|
// Call it from your connection handler on every event.
|
||||||
|
//
|
||||||
|
// Callback upload_fn(hm, data, user_data) is called:
|
||||||
|
// - at start: hm is set, data is NULL. Open the file, set user_data.
|
||||||
|
// - per chunk: hm is NULL, data is set. Chunks are 512 bytes aligned
|
||||||
|
// except the last one.
|
||||||
|
// - at end: both NULL. Close the file.
|
||||||
|
// After the last chunk, "200 ok" is sent and the connection is drained.
|
||||||
|
// Example:
|
||||||
|
// mg_http_stream_body(c, ev, ev_data, mg_str("/fs/*"), upload);
|
||||||
|
// Full example:
|
||||||
|
// tutorials/http/file-upload-single-post
|
||||||
|
// Related APIs:
|
||||||
|
// mg_http_start_upload(), mg_http_start_ota()
|
||||||
|
// Notes:
|
||||||
|
// Requires Content-Length, chunked bodies are not streamed. user_data is
|
||||||
|
// &c->fn_data. While streaming, c->pfn is NULL
|
||||||
|
void mg_http_stream_body(struct mg_connection *c, int ev, void *ev_data,
|
||||||
|
struct mg_str uri_pattern,
|
||||||
|
void (*upload_fn)(struct mg_http_message *,
|
||||||
|
struct mg_str *, void **user_data));
|
||||||
+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)
|
||||||
|
|||||||
+14
-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,18 +126,20 @@ 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;
|
||||||
pay->len = len - hdrlen;
|
pay->len = len - hdrlen;
|
||||||
|
if (len > ifp->framesize) return false; // Oversized
|
||||||
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);
|
||||||
|
|||||||
+23
-35
@@ -101,7 +101,7 @@ static uint16_t s_id;
|
|||||||
|
|
||||||
void mg_l2_ppp_init(struct mg_tcpip_if *ifp) {
|
void mg_l2_ppp_init(struct mg_tcpip_if *ifp) {
|
||||||
ifp->l2mtu = 1500;
|
ifp->l2mtu = 1500;
|
||||||
ifp->framesize = 1500 + sizeof(struct ppp) + sizeof(struct hdlc_);
|
ifp->framesize = ifp->l2mtu + sizeof(struct ppp) + sizeof(struct hdlc_) + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void mg_l2_eth_init(struct mg_tcpip_if *);
|
extern void mg_l2_eth_init(struct mg_tcpip_if *);
|
||||||
@@ -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;
|
||||||
@@ -481,6 +466,7 @@ bool mg_l2_ppp_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
|||||||
pay->buf = (char *) raw->buf;
|
pay->buf = (char *) raw->buf;
|
||||||
pay->len = raw->len - 2;
|
pay->len = raw->len - 2;
|
||||||
}
|
}
|
||||||
|
if (pay->len > ifp->l2mtu + sizeof(struct ppp)) return false; // Oversized
|
||||||
return ppp_rx(ifp, proto, pay, raw);
|
return ppp_rx(ifp, proto, pay, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,6 +549,8 @@ bool mg_l2_pppoe_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
|||||||
if (!mg_l2_eth_rx(ifp, ð_proto, pay, raw)) return false;
|
if (!mg_l2_eth_rx(ifp, ð_proto, pay, raw)) return false;
|
||||||
pppoe = (struct pppoe *) pay->buf; // here we handle pay, not raw
|
pppoe = (struct pppoe *) pay->buf; // here we handle pay, not raw
|
||||||
if (pay->len < sizeof(*pppoe)) return false; // Truncated
|
if (pay->len < sizeof(*pppoe)) return false; // Truncated
|
||||||
|
if (pay->len - sizeof(*pppoe) > ifp->l2mtu + sizeof(struct ppp))
|
||||||
|
return false; // Oversized
|
||||||
if (eth_proto == MG_TCPIP_L2PROTO_PPPoE_DISC) {
|
if (eth_proto == MG_TCPIP_L2PROTO_PPPoE_DISC) {
|
||||||
MG_VERBOSE(("PPPoE_DISC"));
|
MG_VERBOSE(("PPPoE_DISC"));
|
||||||
if (s_state == MG_PPPoE_ST_DISC && pppoe->code == MG_PPPoE_PADO &&
|
if (s_state == MG_PPPoE_ST_DISC && pppoe->code == MG_PPPoE_PADO &&
|
||||||
@@ -573,10 +561,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;
|
||||||
}
|
}
|
||||||
|
|||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
#include "mdash.h"
|
||||||
|
|
||||||
|
#if MG_ENABLE_MDASH
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
#include "health.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "ota.h"
|
||||||
|
#include "printf.h"
|
||||||
|
#include "rpc.h"
|
||||||
|
#include "sntp.h"
|
||||||
|
#include "tls.h"
|
||||||
|
#include "url.h"
|
||||||
|
#include "ws.h"
|
||||||
|
|
||||||
|
static const char *mg_mdash_ca_pem =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
|
||||||
|
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
|
||||||
|
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
|
||||||
|
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
|
||||||
|
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
|
||||||
|
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
|
||||||
|
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
|
||||||
|
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
|
||||||
|
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
|
||||||
|
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
|
||||||
|
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
|
||||||
|
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
|
||||||
|
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
|
||||||
|
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
|
||||||
|
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
|
||||||
|
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
|
||||||
|
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
|
||||||
|
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
|
||||||
|
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
|
||||||
|
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
|
||||||
|
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
|
||||||
|
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
|
||||||
|
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
|
||||||
|
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
|
||||||
|
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
|
||||||
|
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
|
||||||
|
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
|
||||||
|
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
|
||||||
|
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
|
||||||
|
"-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
static struct mg_connection *mg_mdash_conn = NULL;
|
||||||
|
// static struct mg_str mg_mdash_key;
|
||||||
|
// static void mg_connection *mg_mdash_conn = NULL;
|
||||||
|
|
||||||
|
static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
if (ev == MG_EV_CONNECT) {
|
||||||
|
// c->is_hexdumping = true;
|
||||||
|
if (mg_url_is_ssl(MG_MDASH_URL)) {
|
||||||
|
struct mg_str host = mg_url_host(MG_MDASH_URL);
|
||||||
|
struct mg_tls_opts opts = {.ca = mg_str(mg_mdash_ca_pem), .name = host};
|
||||||
|
mg_tls_init(c, &opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ev == MG_EV_WS_OPEN) {
|
||||||
|
MG_INFO(("Connected to %s", MG_MDASH_URL));
|
||||||
|
// s_mDashState = MDASH_STATE_CLOUD_CONNECTED;
|
||||||
|
// mDashTriggerEvent(s_mDashState, NULL);
|
||||||
|
} else if (ev == MG_EV_WS_MSG) {
|
||||||
|
struct mg_iobuf io = {0, 0, 0, 512};
|
||||||
|
struct mg_ws_message *wm = ev_data;
|
||||||
|
struct mg_str s = wm->data;
|
||||||
|
while (s.len > 0 && isspace((uint8_t) s.buf[s.len - 1])) s.len--;
|
||||||
|
MG_INFO(("RPC: %.*s", (int) s.len, s.buf));
|
||||||
|
struct mg_rpc_req r = {.head = &c->mgr->rpcs,
|
||||||
|
.rpc = NULL,
|
||||||
|
.pfn = mg_pfn_iobuf,
|
||||||
|
.pfn_data = &io,
|
||||||
|
.req_data = NULL,
|
||||||
|
.frame = s};
|
||||||
|
mg_rpc_process(&r);
|
||||||
|
if (io.buf != NULL) {
|
||||||
|
MG_INFO((" %s", io.buf));
|
||||||
|
mg_ws_send(c, (const char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
|
||||||
|
} else {
|
||||||
|
MG_DEBUG(("%s", "RPC unhandled"));
|
||||||
|
}
|
||||||
|
mg_iobuf_free(&io);
|
||||||
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
mg_mdash_conn = NULL;
|
||||||
|
MG_INFO(("disconnected, ram %lu", 0));
|
||||||
|
}
|
||||||
|
(void) c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
||||||
|
struct mg_str blob = mg_health_get_blob();
|
||||||
|
if (blob.buf == NULL) blob.len = 0;
|
||||||
|
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:{%m:%m,%m:%llu,%m:%m}}",
|
||||||
|
MG_ESC("fw_version"), MG_ESC(MG_MDASH_FIRMWARE_VERSION), MG_ESC("uptime"),
|
||||||
|
(uint64_t) (mg_millis() / 1000), MG_ESC("reboot_reason"),
|
||||||
|
MG_ESC(mg_health_reason_str(mg_health_reason())), MG_ESC("arch"), MG_ARCH,
|
||||||
|
MG_ESC("health"), MG_ESC("encoding"), MG_ESC("base64"), MG_ESC("size"),
|
||||||
|
(uint64_t) blob.len, MG_ESC("data"), mg_print_base64, (int) blob.len,
|
||||||
|
(uint8_t *) (blob.buf == NULL ? "" : blob.buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||||
|
size_t total = (size_t) mg_json_get_long(r->frame, "$.params.size", -1);
|
||||||
|
if (mg_ota_begin(total)) {
|
||||||
|
mg_rpc_ok(r, "true");
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_mdash_rpc_ota_write(struct mg_rpc_req *r) {
|
||||||
|
int len = 0, ofs = 0;
|
||||||
|
if ((ofs = mg_json_get(r->frame, "$.params.data", &len)) <= 0 ||
|
||||||
|
r->frame.buf[ofs] != '"') {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("data required"));
|
||||||
|
} else {
|
||||||
|
struct mg_str s = mg_str_n(&r->frame.buf[ofs] + 1, (size_t) (len - 2));
|
||||||
|
size_t n = mg_base64_decode(s.buf, s.len, s.buf, s.len); // Decode in-place
|
||||||
|
if (n == 0) {
|
||||||
|
mg_rpc_ok(r, "%m", MG_ESC("finished"));
|
||||||
|
mg_ota_end();
|
||||||
|
} else if (mg_ota_write(s.buf, n)) {
|
||||||
|
mg_rpc_ok(r, "{%m:%d}", MG_ESC("queued"), n);
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("write failed"));
|
||||||
|
mg_ota_end();
|
||||||
|
}
|
||||||
|
// mDashNotify("State.Set", "{%m:{%m:%d}}", MG_ESC("ota"),
|
||||||
|
// MG_ESC("percent"), (int) (s_ota_written * 100 / s_ota_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_mdash_rpc_ota_end(struct mg_rpc_req *r) {
|
||||||
|
if (mg_ota_end()) {
|
||||||
|
mg_rpc_ok(r, "true");
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
// mDashNotify("State.Set", "{%m:{%m:null}}", MG_ESC("ota"),
|
||||||
|
// MG_ESC("percent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_mdash_init(struct mg_mgr *mgr) {
|
||||||
|
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("Sys.GetInfo"), mg_mdash_rpc_get_info, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||||
|
static uint64_t timer = 0;
|
||||||
|
if (mg_mdash_conn == NULL && mg_timer_expired(&timer, 1000, mg_millis())) {
|
||||||
|
MG_INFO(("Connecting to %s", MG_MDASH_URL));
|
||||||
|
mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL,
|
||||||
|
"Origin: %s\r\n"
|
||||||
|
"Authorization: Bearer %s\r\n",
|
||||||
|
MG_MDASH_URL, MG_MDASH_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
void mg_mdash_init(struct mg_mgr *mgr) {
|
||||||
|
(void) mgr;
|
||||||
|
}
|
||||||
|
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||||
|
(void) mgr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// Cloud connector API to https://mdash.net - device management cloud
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "net.h"
|
||||||
|
|
||||||
|
#ifndef MG_MDASH_URL
|
||||||
|
#define MG_MDASH_URL "wss://mdash.net/api/v2/rpc"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_MDASH_KEY
|
||||||
|
#define MG_MDASH_KEY ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_MDASH_FIRMWARE_VERSION
|
||||||
|
#define MG_MDASH_FIRMWARE_VERSION "1.0.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_MDASH
|
||||||
|
#define MG_ENABLE_MDASH 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void mg_mdash_init(struct mg_mgr *);
|
||||||
|
void mg_mdash_poll(struct mg_mgr *);
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ota.h"
|
#include "mdash.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
@@ -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,20 +151,25 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mg_aton_(struct mg_str str, struct mg_addr *addr);
|
||||||
|
bool mg_aton_(struct mg_str str, struct mg_addr *addr) {
|
||||||
|
return mg_aton4(str, addr) || mg_aton6(str, addr);
|
||||||
|
}
|
||||||
|
|
||||||
bool mg_aton(struct mg_str str, struct mg_addr *addr) {
|
bool mg_aton(struct mg_str str, struct mg_addr *addr) {
|
||||||
// MG_INFO(("[%.*s]", (int) str.len, str.buf));
|
return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton_(str, addr);
|
||||||
return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) ||
|
|
||||||
mg_aton6(str, addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
|
struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
|
||||||
@@ -314,6 +350,9 @@ void mg_mgr_init(struct mg_mgr *mgr) {
|
|||||||
: MG_TLS == MG_TLS_BUILTIN ? "builtin"
|
: MG_TLS == MG_TLS_BUILTIN ? "builtin"
|
||||||
: MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL"
|
: MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL"
|
||||||
: "custom"));
|
: "custom"));
|
||||||
|
#if MG_ENABLE_MDASH
|
||||||
|
mg_mdash_init(mgr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ struct mg_mgr {
|
|||||||
struct mg_tcpip_if *ifp; // Builtin TCP/IP stack: network interface pointer
|
struct mg_tcpip_if *ifp; // Builtin TCP/IP stack: network interface pointer
|
||||||
size_t extraconnsize; // Builtin TCP/IP stack: extra bytes allocated per connection
|
size_t extraconnsize; // Builtin TCP/IP stack: extra bytes allocated per connection
|
||||||
union mg_pipe pipe; // Socketpair write-end / queue, used by mg_wakeup()
|
union mg_pipe pipe; // Socketpair write-end / queue, used by mg_wakeup()
|
||||||
|
struct mg_rpc *rpcs; // Linked list of registered RPC handlers
|
||||||
#if MG_ENABLE_FREERTOS_TCP
|
#if MG_ENABLE_FREERTOS_TCP
|
||||||
SocketSet_t ss; // FreeRTOS-TCP socket set
|
SocketSet_t ss; // FreeRTOS-TCP socket set
|
||||||
#endif
|
#endif
|
||||||
@@ -74,6 +75,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 +159,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 *);
|
||||||
|
|||||||
+323
-149
@@ -13,17 +13,32 @@
|
|||||||
#define MG_TCPIP_ARP_MS 100 // Timeout for ARP response
|
#define MG_TCPIP_ARP_MS 100 // Timeout for ARP response
|
||||||
#define MG_TCPIP_SYN_MS 15000 // Timeout for connection establishment
|
#define MG_TCPIP_SYN_MS 15000 // Timeout for connection establishment
|
||||||
#define MG_TCPIP_FIN_MS 1000 // Timeout for closing connection
|
#define MG_TCPIP_FIN_MS 1000 // Timeout for closing connection
|
||||||
|
#define MG_TCPIP_RTX_MS 1000 // Initial retransmission timeout (RFC-6298, 2.1)
|
||||||
|
#define MG_TCPIP_RTX_MAX_MS 60000 // Minimum permitted RTO cap (RFC-6298, 2.5)
|
||||||
|
#define MG_TCPIP_RTX_R1 3 // Delivery-problem threshold (RFC-9293, 3.8.3)
|
||||||
|
// No R2: MG_TCPIP_KEEPALIVE_MS eventually closes non-responsive peers
|
||||||
|
|
||||||
#ifndef MG_TCPIP_WIN
|
#ifndef MG_TCPIP_WIN
|
||||||
#define MG_TCPIP_WIN 6000 // TCP window size
|
#define MG_TCPIP_WIN 6000 // TCP window size
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_MIN_MSS
|
||||||
|
#define MG_TCPIP_MIN_MSS 64 // Minimum peer MSS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_TCPIP_TXQ_ALIGN
|
||||||
|
#define MG_TCPIP_TXQ_ALIGN 1024 // Retransmit queue allocation quantum
|
||||||
|
#endif
|
||||||
|
|
||||||
struct connstate {
|
struct connstate {
|
||||||
uint32_t seq, ack; // TCP seq/ack counters
|
uint32_t seq, ack; // TCP seq/ack counters
|
||||||
uint64_t timer; // TCP timer (see 'ttype' below)
|
uint64_t timer; // TCP timer (see 'ttype' below)
|
||||||
|
uint64_t txq_timer; // TCP retransmission timer (RFC-6298, 5)
|
||||||
uint32_t acked; // Last ACK-ed number
|
uint32_t acked; // Last ACK-ed number
|
||||||
size_t unacked; // Not acked bytes
|
size_t unacked; // Not acked bytes
|
||||||
uint32_t maxseq; // Max send seq (ack + window)
|
uint32_t maxseq; // Max send seq (ack + window)
|
||||||
|
uint32_t txq_seq; // Sequence of first txq record (RFC-9293, 3.8)
|
||||||
|
uint32_t txq_una; // Oldest unacknowledged sequence (RFC-9293, 3.4)
|
||||||
uint16_t win; // destination current window size
|
uint16_t win; // destination current window size
|
||||||
uint16_t dmss; // destination MSS (from TCP opts)
|
uint16_t dmss; // destination MSS (from TCP opts)
|
||||||
uint8_t mac[sizeof(struct mg_l2addr)]; // Peer hw address
|
uint8_t mac[sizeof(struct mg_l2addr)]; // Peer hw address
|
||||||
@@ -34,8 +49,12 @@ struct connstate {
|
|||||||
#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response
|
#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response
|
||||||
#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection
|
#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection
|
||||||
uint8_t tmiss; // Number of keep-alive misses
|
uint8_t tmiss; // Number of keep-alive misses
|
||||||
|
uint8_t txq_retries; // Retransmissions since ACK progress (RFC-9293, 3.8.3)
|
||||||
bool fin_rcvd; // We have received FIN from the peer
|
bool fin_rcvd; // We have received FIN from the peer
|
||||||
bool twclosure; // 3-way closure done
|
bool twclosure; // 3-way closure done
|
||||||
|
bool retransmit; // Retain sent data until acknowledged
|
||||||
|
bool is_full; // Advertise a zero receive window
|
||||||
|
struct mg_iobuf txq; // Length-prefixed sent TCP segments (RFC-9293, 3.8)
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
@@ -46,7 +65,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
|
||||||
@@ -292,7 +311,7 @@ static bool tcpcsum_ok(void *d, void *t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
static uint16_t p6csum(void *d, void *p, size_t plen) {
|
static uint16_t p6csum(void *d, void *p, uint8_t proto, size_t plen) {
|
||||||
uint32_t sum;
|
uint32_t sum;
|
||||||
struct ip6 *ip6 = (struct ip6 *) d;
|
struct ip6 *ip6 = (struct ip6 *) d;
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
@@ -303,7 +322,7 @@ static uint16_t p6csum(void *d, void *p, size_t plen) {
|
|||||||
pip6.dst[0] = ip6->dst[0], pip6.dst[1] = ip6->dst[1];
|
pip6.dst[0] = ip6->dst[0], pip6.dst[1] = ip6->dst[1];
|
||||||
pip6.zero[0] = 0, pip6.zero[1] = 0, pip6.zero[2] = 0;
|
pip6.zero[0] = 0, pip6.zero[1] = 0, pip6.zero[2] = 0;
|
||||||
pip6.plen = mg_htonl((uint32_t) plen);
|
pip6.plen = mg_htonl((uint32_t) plen);
|
||||||
pip6.next = ip6->next;
|
pip6.next = proto;
|
||||||
sum = csumup(0, &pip6, sizeof(pip6)); // even length
|
sum = csumup(0, &pip6, sizeof(pip6)); // even length
|
||||||
sum = csumup(sum, p, plen); // possibly odd length: last
|
sum = csumup(sum, p, plen); // possibly odd length: last
|
||||||
return csumfin(sum);
|
return csumfin(sum);
|
||||||
@@ -313,17 +332,15 @@ static bool udp6csum_ok(void *d, void *u) {
|
|||||||
struct udp *udp = (struct udp *) u;
|
struct udp *udp = (struct udp *) u;
|
||||||
if (udp->csum == 0) return false; // mandatory in IPv6
|
if (udp->csum == 0) return false; // mandatory in IPv6
|
||||||
if (udp->csum == 0xFFFF) udp->csum = 0;
|
if (udp->csum == 0xFFFF) udp->csum = 0;
|
||||||
return (p6csum(d, u, (size_t) mg_ntohs(udp->len)) == 0);
|
return (p6csum(d, u, 17, (size_t) mg_ntohs(udp->len)) == 0);
|
||||||
}
|
}
|
||||||
static bool tcp6csum_ok(void *d, void *t) {
|
static bool tcp6csum_ok(void *d, void *t, size_t len) {
|
||||||
struct ip6 *ip6 = (struct ip6 *) d;
|
return p6csum(d, t, 6, len) == 0;
|
||||||
return (p6csum(d, t, (size_t) mg_ntohs(ip6->plen)) == 0);
|
|
||||||
}
|
|
||||||
static bool icmp6csum_ok(void *d, void *i) {
|
|
||||||
struct ip6 *ip6 = (struct ip6 *) d;
|
|
||||||
return (p6csum(d, i, (size_t) mg_ntohs(ip6->plen)) == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool icmp6csum_ok(void *d, void *i, size_t len) {
|
||||||
|
return p6csum(d, i, 58, len) == 0;
|
||||||
|
}
|
||||||
static void ip6sn(uint64_t *addr, uint64_t *sn_addr) {
|
static void ip6sn(uint64_t *addr, uint64_t *sn_addr) {
|
||||||
// Build solicited-node multicast address from a given unicast IP
|
// Build solicited-node multicast address from a given unicast IP
|
||||||
// RFC-4291 2.7
|
// RFC-4291 2.7
|
||||||
@@ -412,13 +429,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 +450,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 +464,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 +472,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;
|
||||||
@@ -466,7 +485,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
|||||||
memmove(udp + 1, buf, len);
|
memmove(udp + 1, buf, len);
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (ip_dst->is_ip6) {
|
if (ip_dst->is_ip6) {
|
||||||
udp->csum = p6csum(ip6, udp, sizeof(*udp) + len);
|
udp->csum = p6csum(ip6, udp, 17, sizeof(*udp) + len);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
@@ -486,7 +505,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 +661,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 +685,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 +764,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 +802,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,14 +826,14 @@ 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;
|
||||||
icmp6->code = code;
|
icmp6->code = code;
|
||||||
memcpy(icmp6 + 1, buf, len); // Copy payload
|
memcpy(icmp6 + 1, buf, len); // Copy payload
|
||||||
icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1
|
icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1
|
||||||
icmp6->csum = p6csum(ip6, icmp6, sizeof(*icmp6) + len);
|
icmp6->csum = p6csum(ip6, icmp6, 58, sizeof(*icmp6) + len);
|
||||||
driver_output(ifp,
|
driver_output(ifp,
|
||||||
mg_l2_trailer(ifp, sizeof(*ip6) + sizeof(*icmp6) + len,
|
mg_l2_trailer(ifp, sizeof(*ip6) + sizeof(*icmp6) + len,
|
||||||
(uint8_t *) (ip6 + 1) + sizeof(*icmp6) + len));
|
(uint8_t *) (ip6 + 1) + sizeof(*icmp6) + len));
|
||||||
@@ -828,15 +858,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 +878,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 +891,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 +1004,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 +1021,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 +1037,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 +1050,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;
|
||||||
}
|
}
|
||||||
@@ -1037,7 +1072,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
if (!icmp6csum_ok(pkt->ip6, pkt->icmp6)) return;
|
if (!icmp6csum_ok(pkt->ip6, pkt->icmp6, sizeof(*pkt->icmp6) + pkt->pay.len))
|
||||||
|
return;
|
||||||
switch (pkt->icmp6->type) {
|
switch (pkt->icmp6->type) {
|
||||||
case 128: { // Echo Request, RFC-4443 4.1
|
case 128: { // Echo Request, RFC-4443 4.1
|
||||||
uint64_t target[2];
|
uint64_t target[2];
|
||||||
@@ -1101,7 +1137,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 +1228,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) {
|
uint16_t win, 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 +1254,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;
|
||||||
}
|
}
|
||||||
@@ -1236,11 +1274,11 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
|||||||
tcp->seq = seq;
|
tcp->seq = seq;
|
||||||
tcp->ack = ack;
|
tcp->ack = ack;
|
||||||
tcp->flags = flags;
|
tcp->flags = flags;
|
||||||
tcp->win = mg_htons(MG_TCPIP_WIN);
|
tcp->win = mg_htons(win);
|
||||||
tcp->off = (uint8_t) (hlen / 4 << 4);
|
tcp->off = (uint8_t) (hlen / 4 << 4);
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (ip_dst->is_ip6) {
|
if (ip_dst->is_ip6) {
|
||||||
tcp->csum = p6csum(ip6, tcp, hlen + len);
|
tcp->csum = p6csum(ip6, tcp, 6, hlen + len);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
@@ -1274,11 +1312,13 @@ 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, MG_TCPIP_WIN,
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1292,9 +1332,11 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
s = (struct connstate *) (c + 1);
|
s = (struct connstate *) (c + 1);
|
||||||
|
s->retransmit = lsn->mgr->ifp->enable_tcp_retransmit;
|
||||||
|
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
||||||
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 +1421,27 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t txq_next(struct connstate *s, uint8_t **buf) {
|
||||||
|
uint32_t len;
|
||||||
|
if (s->txq.len == 0) return 0;
|
||||||
|
assert(s->txq.len >= sizeof(len));
|
||||||
|
memcpy(&len, s->txq.buf, sizeof(len));
|
||||||
|
assert(len > 0 && len <= s->txq.len - sizeof(len));
|
||||||
|
if (buf != NULL) *buf = s->txq.buf + sizeof(len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool txq_add(struct connstate *s, const void *buf, size_t len) {
|
||||||
|
uint32_t n = (uint32_t) len;
|
||||||
|
size_t off = s->txq.len, total = sizeof(n) + len;
|
||||||
|
assert(len > 0);
|
||||||
|
if (mg_iobuf_add(&s->txq, off, NULL, total) != total) return false;
|
||||||
|
memcpy(s->txq.buf + off, &n, sizeof(n));
|
||||||
|
memcpy(s->txq.buf + off + sizeof(n), buf, len);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -1391,34 +1453,50 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||||
size_t sent;
|
size_t sent;
|
||||||
uint32_t room = s->maxseq - s->seq;
|
uint32_t room = s->maxseq - s->seq;
|
||||||
if (room == 0) return MG_IO_WAIT;
|
bool was_empty = s->txq.len == 0;
|
||||||
if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts
|
if (s->retransmit) {
|
||||||
if ((uint32_t) len > room) len = room;
|
uint32_t in_flight = s->txq.len > 0 ? s->seq - s->txq_una : 0;
|
||||||
sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_PUSH | TH_ACK,
|
uint32_t qroom = in_flight < MG_TCPIP_WIN ? MG_TCPIP_WIN - in_flight : 0;
|
||||||
mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
|
room = s->win > in_flight ? s->win - in_flight : 0;
|
||||||
if (sent == 0) {
|
if (room > qroom) room = qroom;
|
||||||
return MG_IO_WAIT;
|
|
||||||
} else if (sent == (size_t) -1) {
|
|
||||||
return MG_IO_ERR;
|
|
||||||
} else {
|
|
||||||
s->seq += (uint32_t) len;
|
|
||||||
if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
|
|
||||||
}
|
}
|
||||||
|
if (room == 0) return MG_IO_WAIT;
|
||||||
|
if (len > s->dmss) len = s->dmss; // RFC-6691, reduce if sending opts
|
||||||
|
if (len > room) len = room;
|
||||||
|
if (s->retransmit) {
|
||||||
|
if (was_empty) s->txq_seq = s->txq_una = s->seq;
|
||||||
|
if (!txq_add(s, buf, len)) return MG_IO_WAIT;
|
||||||
|
}
|
||||||
|
sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
|
||||||
|
mg_htonl(s->seq), mg_htonl(s->ack),
|
||||||
|
s->is_full ? 0 : MG_TCPIP_WIN, buf, len);
|
||||||
|
if (sent == 0 || sent == (size_t) -1) {
|
||||||
|
if (s->retransmit)
|
||||||
|
mg_iobuf_del(&s->txq, s->txq.len - len - sizeof(uint32_t),
|
||||||
|
len + sizeof(uint32_t));
|
||||||
|
return sent == 0 ? MG_IO_WAIT : MG_IO_ERR;
|
||||||
|
}
|
||||||
|
s->seq += (uint32_t) len;
|
||||||
|
if (s->retransmit && was_empty) {
|
||||||
|
s->txq_timer = ifp->now + MG_TCPIP_RTX_MS;
|
||||||
|
s->txq_retries = 0;
|
||||||
|
}
|
||||||
|
if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||||
}
|
}
|
||||||
return (long) len;
|
return (long) 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) {
|
||||||
@@ -1431,22 +1509,66 @@ 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;
|
uint64_t now) {
|
||||||
s->maxseq = (uint32_t)(ackno + win);
|
if (ackno > s->seq) return;
|
||||||
|
if (s->retransmit && s->txq.len > 0) {
|
||||||
|
if (ackno < s->txq_una) return;
|
||||||
|
if (ackno > s->txq_una) {
|
||||||
|
size_t len;
|
||||||
|
// Remove only entirely acknowledged segments (RFC-9293, 3.10.7.4)
|
||||||
|
while ((len = txq_next(s, NULL)) > 0 && ackno >= s->txq_seq + len) {
|
||||||
|
mg_iobuf_del(&s->txq, 0, len + sizeof(uint32_t));
|
||||||
|
s->txq_seq += (uint32_t) len;
|
||||||
|
}
|
||||||
|
s->txq_una = ackno;
|
||||||
|
s->txq_retries = 0;
|
||||||
|
// Restart RTO when an ACK acknowledges new data (RFC-6298, 5.3)
|
||||||
|
s->txq_timer = now + MG_TCPIP_RTX_MS;
|
||||||
|
} // else: dup ack, ignore it but handle its window upddate
|
||||||
|
} else if (ackno < (s->seq - s->win)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->maxseq = (uint32_t) (ackno + win);
|
||||||
s->win = win;
|
s->win = win;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void retransmit(struct mg_connection *c) {
|
||||||
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||||
|
uint8_t *buf, i;
|
||||||
|
uint64_t rto;
|
||||||
|
size_t len = txq_next(s, &buf);
|
||||||
|
// Send the front retransmission-queue segment only (RFC-9293, 3.10.8)
|
||||||
|
if (len == 0 ||
|
||||||
|
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
|
||||||
|
mg_htonl(s->txq_seq), mg_htonl(s->ack),
|
||||||
|
s->is_full ? 0 : MG_TCPIP_WIN, buf, len) == (size_t) -1) {
|
||||||
|
mg_error(c, "retransmit");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Back off RTO after every timeout (RFC-6298, 5.5)
|
||||||
|
for (i = 0, rto = MG_TCPIP_RTX_MS;
|
||||||
|
i < s->txq_retries && rto < MG_TCPIP_RTX_MAX_MS; i++) {
|
||||||
|
rto *= 2;
|
||||||
|
if (rto > MG_TCPIP_RTX_MAX_MS) rto = MG_TCPIP_RTX_MAX_MS;
|
||||||
|
}
|
||||||
|
s->txq_timer = ifp->now + rto;
|
||||||
|
}
|
||||||
|
|
||||||
static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
struct mg_iobuf *io = c->is_tls ? &c->rtls : &c->recv;
|
struct mg_iobuf *io = c->is_tls ? &c->rtls : &c->recv;
|
||||||
uint32_t seq = mg_ntohl(pkt->tcp->seq);
|
uint32_t seq = mg_ntohl(pkt->tcp->seq);
|
||||||
|
// Evaluation order is important: 1) process FIN, 2) process keep-alive,
|
||||||
|
// 3) discard invalid SEG.SEQ, 4) process ACK and payload.
|
||||||
if (pkt->tcp->flags & TH_FIN) {
|
if (pkt->tcp->flags & TH_FIN) {
|
||||||
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), s->is_full ? 0 : MG_TCPIP_WIN,
|
||||||
|
"", 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,37 +1589,41 @@ 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), s->is_full ? 0 : MG_TCPIP_WIN,
|
||||||
if (pkt->pay.len == 0) return; // if no data, we're done
|
"", 0);
|
||||||
} 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), s->is_full ? 0 : MG_TCPIP_WIN,
|
||||||
return; // no data to process
|
NULL, 0);
|
||||||
} else if (pkt->pay.len == 0) { // this is an ACK
|
return; // RFC-9293 3.10.7.4 discard (incorrect) payload, ACK, window
|
||||||
if (pkt->tcp->flags & TH_ACK)
|
|
||||||
handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win));
|
|
||||||
if (s->fin_rcvd && s->ttype == MIP_TTYPE_FIN) s->twclosure = true;
|
|
||||||
return; // no data to process
|
|
||||||
} else if (seq != s->ack) {
|
} else if (seq != s->ack) {
|
||||||
uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
|
uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
|
||||||
if (s->ack == ack) {
|
if (s->ack == ack) {
|
||||||
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),
|
|
||||||
mg_htonl(s->ack), "", 0);
|
|
||||||
}
|
}
|
||||||
return; // drop it
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
|
||||||
} else if (io->size - io->len < pkt->pay.len &&
|
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
|
||||||
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
|
"", 0);
|
||||||
|
return; // drop it, RFC-9293 3.10.7.4: ignore ACKno
|
||||||
|
}
|
||||||
|
// Now process the segment for ACK and payload
|
||||||
|
if (pkt->tcp->flags & TH_ACK) {
|
||||||
|
handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win),
|
||||||
|
c->mgr->ifp->now);
|
||||||
|
if (pkt->pay.len == 0 && s->fin_rcvd && s->ttype == MIP_TTYPE_FIN)
|
||||||
|
s->twclosure = true;
|
||||||
|
}
|
||||||
|
if (pkt->pay.len == 0) return;
|
||||||
|
if (io->size - io->len < pkt->pay.len &&
|
||||||
|
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
|
||||||
mg_error(c, "oom");
|
mg_error(c, "oom");
|
||||||
return; // drop it
|
return; // drop it
|
||||||
}
|
}
|
||||||
if (pkt->tcp->flags & TH_ACK)
|
|
||||||
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,
|
||||||
@@ -1506,15 +1632,18 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
|||||||
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
|
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
|
||||||
io->len += pkt->pay.len;
|
io->len += pkt->pay.len;
|
||||||
MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
|
MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
|
||||||
// Advance ACK counter
|
if (!(pkt->tcp->flags & TH_FIN)) { // FIN already advanced s->ack
|
||||||
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
|
// Advance ACK counter
|
||||||
s->unacked += pkt->pay.len;
|
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
|
||||||
|
s->unacked += pkt->pay.len;
|
||||||
|
}
|
||||||
// size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack;
|
// size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack;
|
||||||
if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) {
|
if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) {
|
||||||
// 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), s->is_full ? 0 : MG_TCPIP_WIN,
|
||||||
|
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,12 +1716,15 @@ 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]);
|
uint16_t mss = (uint16_t) (((uint16_t) opts[2] << 8) + opts[3]);
|
||||||
|
if (mss == 0 || mss < MG_TCPIP_MIN_MSS) return false;
|
||||||
|
s->dmss = mss;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MG_VERBOSE(("kind: %u, optlen: %u, len: %d\n", kind, optlen, len));
|
MG_VERBOSE(("kind: %u, optlen: %u, len: %d\n", kind, optlen, len));
|
||||||
opts += optlen;
|
opts += optlen;
|
||||||
@@ -1605,7 +1737,10 @@ static void rx_tcp(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);
|
||||||
struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
|
struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
|
||||||
#if MG_ENABLE_IPV6 // matching of v4/v6 to dest is done by getpeer()
|
#if MG_ENABLE_IPV6 // matching of v4/v6 to dest is done by getpeer()
|
||||||
if (pkt->ip6 != NULL && !tcp6csum_ok(pkt->ip6, pkt->tcp)) return;
|
if (pkt->ip6 != NULL &&
|
||||||
|
!tcp6csum_ok(pkt->ip6, pkt->tcp,
|
||||||
|
PDIFF(pkt->tcp, pkt->pay.buf) + pkt->pay.len))
|
||||||
|
return;
|
||||||
#endif
|
#endif
|
||||||
if (pkt->ip != NULL && !tcpcsum_ok(pkt->ip, pkt->tcp)) return;
|
if (pkt->ip != NULL && !tcpcsum_ok(pkt->ip, pkt->tcp)) return;
|
||||||
pkt->tcp->flags &= TH_STDFLAGS; // tolerate creative usage (ECN, ?)
|
pkt->tcp->flags &= TH_STDFLAGS; // tolerate creative usage (ECN, ?)
|
||||||
@@ -1616,7 +1751,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);
|
||||||
@@ -1636,6 +1771,12 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
settmout(c,
|
settmout(c,
|
||||||
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
|
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
|
||||||
read_conn(c, pkt); // Override timer with ACK timeout if needed
|
read_conn(c, pkt); // Override timer with ACK timeout if needed
|
||||||
|
if (c->is_full && !s->is_full) {
|
||||||
|
// The receive handler paused the connection: advertise zero window now
|
||||||
|
s->is_full = true;
|
||||||
|
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq),
|
||||||
|
mg_htonl(s->ack), 0, NULL, 0);
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
// - we don't listen on that port; RFC-9293 3.5.2 Group 1
|
// - we don't listen on that port; RFC-9293 3.5.2 Group 1
|
||||||
// - check listening connections; RFC-9293 3.5.2 Group 2
|
// - check listening connections; RFC-9293 3.5.2 Group 2
|
||||||
@@ -1740,7 +1881,8 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
||||||
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
|
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
|
||||||
off = pkt->tcp->off >> 4; // account for opts
|
off = pkt->tcp->off >> 4; // account for opts
|
||||||
if (pkt->pay.len < (uint16_t) (4 * off)) return;
|
if (off < sizeof(*pkt->tcp) / 4 || pkt->pay.len < (uint16_t) (4 * off))
|
||||||
|
return;
|
||||||
mkpay(pkt, (uint32_t *) pkt->tcp + off);
|
mkpay(pkt, (uint32_t *) pkt->tcp + off);
|
||||||
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
|
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
|
||||||
mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
|
mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
|
||||||
@@ -1836,7 +1978,8 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
pkt->tcp = (struct tcp *) (pkt->pay.buf);
|
||||||
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
|
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
|
||||||
off = pkt->tcp->off >> 4; // account for opts
|
off = pkt->tcp->off >> 4; // account for opts
|
||||||
if (pkt->pay.len < (uint16_t) (4 * off)) return;
|
if (off < sizeof(*pkt->tcp) / 4 || pkt->pay.len < (uint16_t) (4 * off))
|
||||||
|
return;
|
||||||
mkpay(pkt, (uint32_t *) pkt->tcp + off);
|
mkpay(pkt, (uint32_t *) pkt->tcp + off);
|
||||||
MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
||||||
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
|
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
|
||||||
@@ -1922,11 +2065,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 +2078,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 +2145,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);
|
||||||
}
|
}
|
||||||
@@ -2027,6 +2178,19 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
|||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving)
|
if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving)
|
||||||
continue;
|
continue;
|
||||||
|
if (!c->is_full && s->is_full) {
|
||||||
|
// Application resumed the connection: advertise the restored window
|
||||||
|
s->is_full = false;
|
||||||
|
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq),
|
||||||
|
mg_htonl(s->ack), MG_TCPIP_WIN, NULL, 0);
|
||||||
|
}
|
||||||
|
if (s->retransmit && s->txq.len > 0 && ifp->now > s->txq_timer) {
|
||||||
|
if (s->txq_retries < 255) s->txq_retries++;
|
||||||
|
// R1 reports a delivery problem; it does not close (RFC-9293, 3.8.3).
|
||||||
|
if (s->txq_retries == MG_TCPIP_RTX_R1)
|
||||||
|
MG_ERROR(("%lu retransmit", c->id));
|
||||||
|
retransmit(c);
|
||||||
|
}
|
||||||
if (ifp->now > s->timer) {
|
if (ifp->now > s->timer) {
|
||||||
if (s->ttype == MIP_TTYPE_ARP) {
|
if (s->ttype == MIP_TTYPE_ARP) {
|
||||||
mg_error(c, "ARP timeout");
|
mg_error(c, "ARP timeout");
|
||||||
@@ -2034,8 +2198,8 @@ 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), s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
|
||||||
s->acked = s->ack;
|
s->acked = s->ack;
|
||||||
} else if (s->ttype == MIP_TTYPE_SYN) {
|
} else if (s->ttype == MIP_TTYPE_SYN) {
|
||||||
mg_error(c, "Connection timeout");
|
mg_error(c, "Connection timeout");
|
||||||
@@ -2047,8 +2211,9 @@ 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),
|
||||||
|
s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2075,6 +2240,7 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
|||||||
// If L2 address is not set, make a random one; fill MTU
|
// If L2 address is not set, make a random one; fill MTU
|
||||||
mg_l2_init(ifp);
|
mg_l2_init(ifp);
|
||||||
ifp->mtu = ifp->l2mtu;
|
ifp->mtu = ifp->l2mtu;
|
||||||
|
if (MG_ENABLE_TCPIP_TCPRTX) ifp->enable_tcp_retransmit = true;
|
||||||
|
|
||||||
if (ifp->dhcp_name[0] == '\0') // If DHCP name is not set, use "mip"
|
if (ifp->dhcp_name[0] == '\0') // If DHCP name is not set, use "mip"
|
||||||
memcpy(ifp->dhcp_name, "mip", 4);
|
memcpy(ifp->dhcp_name, "mip", 4);
|
||||||
@@ -2111,7 +2277,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,
|
||||||
|
MG_TCPIP_WIN, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l2addr_resolved(struct mg_connection *c) {
|
static void l2addr_resolved(struct mg_connection *c) {
|
||||||
@@ -2127,14 +2294,17 @@ static void l2addr_resolved(struct mg_connection *c) {
|
|||||||
void mg_connect_resolved(struct mg_connection *c) {
|
void mg_connect_resolved(struct mg_connection *c) {
|
||||||
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||||
uint8_t *l2addr;
|
uint8_t *l2addr;
|
||||||
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
s->retransmit = ifp->enable_tcp_retransmit;
|
||||||
|
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
||||||
c->is_resolving = 0;
|
c->is_resolving = 0;
|
||||||
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
||||||
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;
|
||||||
@@ -2148,7 +2318,6 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
mg_call(c, MG_EV_RESOLVE, NULL);
|
mg_call(c, MG_EV_RESOLVE, NULL);
|
||||||
c->is_connecting = 1;
|
c->is_connecting = 1;
|
||||||
if (c->is_udp && (l2addr = tcpip_mapip(ifp, &c->rem)) != NULL) {
|
if (c->is_udp && (l2addr = tcpip_mapip(ifp, &c->rem)) != NULL) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
|
||||||
memcpy(s->mac, l2addr, sizeof(s->mac));
|
memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
l2addr_resolved(c); // broadcast or multicast
|
l2addr_resolved(c); // broadcast or multicast
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
@@ -2163,7 +2332,6 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
settmout(c, MIP_TTYPE_ARP);
|
settmout(c, MIP_TTYPE_ARP);
|
||||||
c->is_arplooking = 1;
|
c->is_arplooking = 1;
|
||||||
} else if (ifp->gw6_ready) {
|
} else if (ifp->gw6_ready) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
|
||||||
memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
} else {
|
} else {
|
||||||
@@ -2180,7 +2348,6 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
settmout(c, MIP_TTYPE_ARP);
|
settmout(c, MIP_TTYPE_ARP);
|
||||||
c->is_arplooking = 1;
|
c->is_arplooking = 1;
|
||||||
} else if (ifp->gw_ready) {
|
} else if (ifp->gw_ready) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
|
||||||
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
} else {
|
} else {
|
||||||
@@ -2212,18 +2379,18 @@ static void write_conn(struct mg_connection *c) {
|
|||||||
|
|
||||||
static void init_closure(struct mg_connection *c) {
|
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_listening == false && c->is_connecting == false) {
|
||||||
c->is_connecting == false) { // For TCP conns,
|
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_FIN | TH_ACK,
|
||||||
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_FIN | TH_ACK,
|
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
|
||||||
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
|
NULL, 0);
|
||||||
settmout(c, MIP_TTYPE_FIN);
|
settmout(c, MIP_TTYPE_FIN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_conn(struct mg_connection *c) {
|
static void close_conn(struct mg_connection *c) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
mg_iobuf_free(&s->txq);
|
||||||
mg_close_conn(c);
|
mg_close_conn(c);
|
||||||
(void) s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool can_write(struct mg_connection *c) {
|
static bool can_write(struct mg_connection *c) {
|
||||||
@@ -2236,10 +2403,12 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
uint64_t now = mg_millis();
|
uint64_t now = mg_millis();
|
||||||
mg_timer_poll(&mgr->timers, now);
|
mg_timer_poll(&mgr->timers, now);
|
||||||
mg_ota_poll(mgr);
|
mg_ota_poll(mgr);
|
||||||
|
mg_mdash_poll(mgr);
|
||||||
if (mgr->ifp == NULL || mgr->ifp->driver == NULL) return;
|
if (mgr->ifp == NULL || mgr->ifp->driver == NULL) return;
|
||||||
mg_tcpip_poll(mgr->ifp, now);
|
mg_tcpip_poll(mgr->ifp, now);
|
||||||
for (c = mgr->conns; c != NULL; c = tmp) {
|
for (c = mgr->conns; c != NULL; c = tmp) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
long flush = 0;
|
||||||
bool is_tls = c->is_tls && !c->is_resolving && !c->is_arplooking &&
|
bool is_tls = c->is_tls && !c->is_resolving && !c->is_arplooking &&
|
||||||
!c->is_listening && !c->is_connecting;
|
!c->is_listening && !c->is_connecting;
|
||||||
tmp = c->next;
|
tmp = c->next;
|
||||||
@@ -2252,9 +2421,14 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0))
|
if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0))
|
||||||
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
|
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
|
||||||
if (can_write(c)) write_conn(c);
|
if (can_write(c)) write_conn(c);
|
||||||
if (is_tls && c->send.len == 0) mg_tls_flush(c);
|
if (is_tls && c->send.len == 0) flush = mg_tls_flush(c);
|
||||||
if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN)
|
if (flush == MG_IO_ERR) mg_error(c, "tx err");
|
||||||
init_closure(c);
|
if (c->is_draining && c->send.len == 0) {
|
||||||
|
if (c->is_udp) c->is_closing = 1;
|
||||||
|
if (!c->is_udp && flush == 0 && s->txq.len == 0 &&
|
||||||
|
s->ttype != MIP_TTYPE_FIN)
|
||||||
|
init_closure(c);
|
||||||
|
}
|
||||||
// For non-TLS, close immediately upon completing the 3-way closure
|
// For non-TLS, close immediately upon completing the 3-way closure
|
||||||
// For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires
|
// For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires
|
||||||
if (s->twclosure &&
|
if (s->twclosure &&
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ struct mg_tcpip_if {
|
|||||||
bool enable_req_sntp; // DHCP client requests an SNTP server address
|
bool enable_req_sntp; // DHCP client requests an SNTP server address
|
||||||
bool enable_fcs_check; // Verify and strip FCS from received frames
|
bool enable_fcs_check; // Verify and strip FCS from received frames
|
||||||
bool enable_mac_check; // Drop frames not addressed to this MAC
|
bool enable_mac_check; // Drop frames not addressed to this MAC
|
||||||
|
bool enable_tcp_retransmit; // Enable optional TCP retransmission queue
|
||||||
bool update_mac_hash_table; // Signal driver to refresh MAC multicast hash table
|
bool update_mac_hash_table; // Signal driver to refresh MAC multicast hash table
|
||||||
bool is_ip_changed; // Set by stack when IP changes; triggers connection restart
|
bool is_ip_changed; // Set by stack when IP changes; triggers connection restart
|
||||||
struct mg_tcpip_driver *driver; // Hardware driver; must be set before mg_tcpip_init()
|
struct mg_tcpip_driver *driver; // Hardware driver; must be set before mg_tcpip_init()
|
||||||
@@ -152,6 +153,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
|
|||||||
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
||||||
|
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
||||||
|
|
||||||
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||||
// Populate and assign to the driver's driver_data field.
|
// Populate and assign to the driver's driver_data field.
|
||||||
|
|||||||
+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
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
#define MG_OTA_RT1060 302 // IMXRT1060
|
#define MG_OTA_RT1060 302 // IMXRT1060
|
||||||
#define MG_OTA_RT1064 303 // IMXRT1064
|
#define MG_OTA_RT1064 303 // IMXRT1064
|
||||||
#define MG_OTA_RT1170 304 // IMXRT1170
|
#define MG_OTA_RT1170 304 // IMXRT1170
|
||||||
|
#define MG_OTA_RT1180 305 // IMXRT1180
|
||||||
#define MG_OTA_MCXN 310 // MCXN947
|
#define MG_OTA_MCXN 310 // MCXN947
|
||||||
#define MG_OTA_RW612 320 // FRDM-RW612
|
#define MG_OTA_RW612 320 // FRDM-RW612
|
||||||
#define MG_OTA_FLASH 900 // OTA via internal flash
|
#define MG_OTA_FLASH 900 // OTA via internal flash
|
||||||
@@ -149,8 +150,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 +163,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 +204,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+68
-35
@@ -2,7 +2,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
|
|
||||||
#if MG_OTA >= MG_OTA_RT1020 && MG_OTA <= MG_OTA_RT1170
|
#if MG_OTA >= MG_OTA_RT1020 && MG_OTA <= MG_OTA_RT1180
|
||||||
|
|
||||||
static bool mg_imxrt_write(void *, const void *, size_t);
|
static bool mg_imxrt_write(void *, const void *, size_t);
|
||||||
static bool mg_imxrt_swap(void);
|
static bool mg_imxrt_swap(void);
|
||||||
@@ -13,9 +13,12 @@ static bool mg_imxrt_swap(void);
|
|||||||
#elif MG_OTA == MG_OTA_RT1064
|
#elif MG_OTA == MG_OTA_RT1064
|
||||||
#define MG_IMXRT_FLASH_START 0x70000000
|
#define MG_IMXRT_FLASH_START 0x70000000
|
||||||
#define FLEXSPI_NOR_INSTANCE 1
|
#define FLEXSPI_NOR_INSTANCE 1
|
||||||
#else // RT1170
|
#elif MG_OTA == MG_OTA_RT1170
|
||||||
#define MG_IMXRT_FLASH_START 0x30000000
|
#define MG_IMXRT_FLASH_START 0x30000000
|
||||||
#define FLEXSPI_NOR_INSTANCE 1
|
#define FLEXSPI_NOR_INSTANCE 1
|
||||||
|
#else // RT1180 FlexSPI2
|
||||||
|
#define MG_IMXRT_FLASH_START 0x04000000
|
||||||
|
#define FLEXSPI_NOR_INSTANCE 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if MG_OTA == MG_OTA_RT1050
|
#if MG_OTA == MG_OTA_RT1050
|
||||||
@@ -324,7 +327,7 @@ struct mg_flexspi_nor_driver_interface {
|
|||||||
uint32_t *option);
|
uint32_t *option);
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
// RT117x support ROM API version 1.7
|
// RT117x and RT118x
|
||||||
struct mg_flexspi_nor_driver_interface {
|
struct mg_flexspi_nor_driver_interface {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
|
int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
|
||||||
@@ -359,11 +362,16 @@ struct mg_flexspi_nor_driver_interface {
|
|||||||
#define flexspi_nor \
|
#define flexspi_nor \
|
||||||
(*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0020001c + \
|
(*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0020001c + \
|
||||||
16)))
|
16)))
|
||||||
#else
|
#elif MG_OTA == MG_OTA_RT1170
|
||||||
#define MG_FLEXSPI_BASE 0x400CC000
|
#define MG_FLEXSPI_BASE 0x400CC000
|
||||||
#define flexspi_nor \
|
#define flexspi_nor \
|
||||||
(*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0021001c + \
|
(*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0021001c + \
|
||||||
12)))
|
12)))
|
||||||
|
#else
|
||||||
|
#define MG_FLEXSPI_BASE 0x445E0000
|
||||||
|
#define flexspi_nor \
|
||||||
|
(*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x1000001c + \
|
||||||
|
12)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool s_flash_irq_disabled;
|
static bool s_flash_irq_disabled;
|
||||||
@@ -408,6 +416,13 @@ static struct mg_flexspi_nor_config default_config = {
|
|||||||
#else
|
#else
|
||||||
// Note: this QSPI configuration works for RTs supporting QSPI
|
// Note: this QSPI configuration works for RTs supporting QSPI
|
||||||
// Configuration for QSPI memory
|
// Configuration for QSPI memory
|
||||||
|
#if MG_OTA == MG_OTA_RT1180
|
||||||
|
#define MG_FLEXSPI_QSPI_SERIAL_CLK_FREQ 5 // 100MHz
|
||||||
|
#define MG_FLEXSPI_QSPI_FLASH_SIZE (16 * 1024 * 1024)
|
||||||
|
#else
|
||||||
|
#define MG_FLEXSPI_QSPI_SERIAL_CLK_FREQ 7 // 133MHz
|
||||||
|
#define MG_FLEXSPI_QSPI_FLASH_SIZE (8 * 1024 * 1024)
|
||||||
|
#endif
|
||||||
static struct mg_flexspi_nor_config default_config = {
|
static struct mg_flexspi_nor_config default_config = {
|
||||||
.memConfig = {.tag = MG_FLEXSPI_CFG_BLK_TAG,
|
.memConfig = {.tag = MG_FLEXSPI_CFG_BLK_TAG,
|
||||||
.version = MG_FLEXSPI_CFG_BLK_VERSION,
|
.version = MG_FLEXSPI_CFG_BLK_VERSION,
|
||||||
@@ -417,8 +432,8 @@ static struct mg_flexspi_nor_config default_config = {
|
|||||||
.controllerMiscOption = MG_BIT(4),
|
.controllerMiscOption = MG_BIT(4),
|
||||||
.deviceType = 1, // serial NOR
|
.deviceType = 1, // serial NOR
|
||||||
.sflashPadType = 4,
|
.sflashPadType = 4,
|
||||||
.serialClkFreq = 7, // 133MHz
|
.serialClkFreq = MG_FLEXSPI_QSPI_SERIAL_CLK_FREQ,
|
||||||
.sflashA1Size = 8 * 1024 * 1024,
|
.sflashA1Size = MG_FLEXSPI_QSPI_FLASH_SIZE,
|
||||||
.lookupTable = MG_FLEXSPI_QSPI_LUT},
|
.lookupTable = MG_FLEXSPI_QSPI_LUT},
|
||||||
.pageSize = 256,
|
.pageSize = 256,
|
||||||
.sectorSize = 4 * 1024,
|
.sectorSize = 4 * 1024,
|
||||||
@@ -453,6 +468,7 @@ MG_IRAM static int flexspi_nor_get_config(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MG_OTA == MG_OTA_RT1020 || MG_OTA == MG_OTA_RT1050
|
||||||
MG_IRAM static void mg_spin(volatile uint32_t count) {
|
MG_IRAM static void mg_spin(volatile uint32_t count) {
|
||||||
while (count--) (void) 0;
|
while (count--) (void) 0;
|
||||||
}
|
}
|
||||||
@@ -461,6 +477,7 @@ MG_IRAM static void flash_wait(void) {
|
|||||||
while ((*((volatile uint32_t *) (MG_FLEXSPI_BASE + 0xE0)) & MG_BIT(1)) == 0)
|
while ((*((volatile uint32_t *) (MG_FLEXSPI_BASE + 0xE0)) & MG_BIT(1)) == 0)
|
||||||
mg_spin(1);
|
mg_spin(1);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
MG_IRAM static bool flash_erase(struct mg_flexspi_nor_config *config,
|
MG_IRAM static bool flash_erase(struct mg_flexspi_nor_config *config,
|
||||||
void *addr) {
|
void *addr) {
|
||||||
@@ -491,10 +508,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;
|
||||||
@@ -513,6 +526,8 @@ MG_IRAM static bool mg_imxrt_write(void *addr, const void *buf, size_t len) {
|
|||||||
uint32_t *dst = (uint32_t *) addr;
|
uint32_t *dst = (uint32_t *) addr;
|
||||||
uint32_t *src = (uint32_t *) buf;
|
uint32_t *src = (uint32_t *) buf;
|
||||||
uint32_t *end = (uint32_t *) ((char *) buf + len);
|
uint32_t *end = (uint32_t *) ((char *) buf + len);
|
||||||
|
char *flash_start = (char *) s_mg_flash_imxrt.start;
|
||||||
|
char *flash_end = flash_start + s_mg_flash_imxrt.size;
|
||||||
ok = true;
|
ok = true;
|
||||||
|
|
||||||
while (ok && src < end) {
|
while (ok && src < end) {
|
||||||
@@ -520,20 +535,32 @@ MG_IRAM static bool mg_imxrt_write(void *addr, const void *buf, size_t len) {
|
|||||||
ok = false;
|
ok = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uint32_t status;
|
uint32_t status = 0;
|
||||||
uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_imxrt.start;
|
uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_imxrt.start;
|
||||||
if ((char *) buf >= (char *) s_mg_flash_imxrt.start) {
|
if ((char *) buf >= flash_start && (char *) buf < flash_end) {
|
||||||
// If we copy from FLASH to FLASH, then we first need to copy the source
|
// If we copy from FLASH to FLASH, then we first need to copy the source
|
||||||
// to RAM
|
// to RAM
|
||||||
size_t tmp_buf_size = s_mg_flash_imxrt.align / sizeof(uint32_t);
|
size_t tmp_buf_size = s_mg_flash_imxrt.align / sizeof(uint32_t);
|
||||||
uint32_t tmp[tmp_buf_size];
|
uint32_t tmp[tmp_buf_size];
|
||||||
|
|
||||||
|
#if MG_OTA == MG_OTA_RT1020 || MG_OTA == MG_OTA_RT1050
|
||||||
|
// ROM API 1.4 does not provide flexspi_nor_flash_read().
|
||||||
|
// Manually copy byte by byte into the RAM buffer.
|
||||||
for (size_t i = 0; i < tmp_buf_size; i++) {
|
for (size_t i = 0; i < tmp_buf_size; i++) {
|
||||||
flash_wait();
|
flash_wait();
|
||||||
tmp[i] = src[i];
|
tmp[i] = src[i];
|
||||||
}
|
}
|
||||||
status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr,
|
#else
|
||||||
(uint32_t) dst_ofs, tmp);
|
// When supported, prefer the read API call.
|
||||||
|
uint32_t src_ofs =
|
||||||
|
(uint32_t) src - (uint32_t) s_mg_flash_imxrt.start;
|
||||||
|
status = flexspi_nor->read(FLEXSPI_NOR_INSTANCE, config_ptr, tmp,
|
||||||
|
src_ofs, s_mg_flash_imxrt.align);
|
||||||
|
#endif
|
||||||
|
if (status == 0) {
|
||||||
|
status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr,
|
||||||
|
dst_ofs, tmp);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr,
|
status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr,
|
||||||
(uint32_t) dst_ofs, src);
|
(uint32_t) dst_ofs, src);
|
||||||
@@ -550,16 +577,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 +619,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -53,24 +53,24 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
|
MG_IRAM static bool flash_page_start(uint32_t *dst) {
|
||||||
char *base = (char *) s_mg_flash_stm32h7.start,
|
char *base = (char *) s_mg_flash_stm32h7.start,
|
||||||
*end = base + s_mg_flash_stm32h7.size;
|
*end = base + s_mg_flash_stm32h7.size;
|
||||||
volatile char *p = (char *) dst;
|
char *p = (char *) dst;
|
||||||
return p >= base && p < end && ((p - base) % s_mg_flash_stm32h7.secsz) == 0;
|
return p >= base && p < end && ((p - base) % s_mg_flash_stm32h7.secsz) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ MG_IRAM static uint32_t flash_bank(void *addr) {
|
|||||||
// read-while-write, no need to disable IRQs for standalone usage
|
// read-while-write, no need to disable IRQs for standalone usage
|
||||||
MG_IRAM static bool mg_stm32h7_erase(void *addr) {
|
MG_IRAM static bool mg_stm32h7_erase(void *addr) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
if (flash_page_start(addr) == false) {
|
if (flash_page_start((uint32_t *) addr) == false) {
|
||||||
MG_ERROR(("%p is not on a sector boundary", addr));
|
MG_ERROR(("%p is not on a sector boundary", addr));
|
||||||
} else {
|
} else {
|
||||||
uintptr_t diff = (char *) addr - (char *) s_mg_flash_stm32h7.start;
|
uintptr_t diff = (char *) addr - (char *) s_mg_flash_stm32h7.start;
|
||||||
|
|||||||
+3
-3
@@ -34,15 +34,15 @@ char *mg_mprintf(const char *fmt, ...);
|
|||||||
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
|
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
|
||||||
|
|
||||||
// Built-in %M/%m printer functions. Each reads its argument(s) from ap.
|
// Built-in %M/%m printer functions. Each reads its argument(s) from ap.
|
||||||
size_t mg_print_base64(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len
|
size_t mg_print_base64(mg_pfn_t, void *arg, va_list *ap); // expects: int len, uint8_t *buf
|
||||||
size_t mg_print_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
size_t mg_print_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
||||||
size_t mg_print_hex(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len
|
size_t mg_print_hex(mg_pfn_t, void *arg, va_list *ap); // expects: int len, uint8_t *buf
|
||||||
size_t mg_print_ip(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
size_t mg_print_ip(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
||||||
size_t mg_print_ip_port(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
size_t mg_print_ip_port(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
|
||||||
size_t mg_print_ip4(mg_pfn_t, void *arg, va_list *ap); // expects: uint32_t *ipv4
|
size_t mg_print_ip4(mg_pfn_t, void *arg, va_list *ap); // expects: uint32_t *ipv4
|
||||||
size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[16] ipv6
|
size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[16] ipv6
|
||||||
size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac
|
size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac
|
||||||
size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint64_t
|
size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[8] ieee64
|
||||||
size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address
|
size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address
|
||||||
size_t mg_print_html_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
size_t mg_print_html_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
|
||||||
|
|
||||||
|
|||||||
+16
-3
@@ -1,6 +1,6 @@
|
|||||||
#include "dns.h"
|
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "mdash.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
#include "str.h"
|
#include "str.h"
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -759,8 +768,10 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
now = mg_millis();
|
now = mg_millis();
|
||||||
mg_timer_poll(&mgr->timers, now);
|
mg_timer_poll(&mgr->timers, now);
|
||||||
mg_ota_poll(mgr);
|
mg_ota_poll(mgr);
|
||||||
|
mg_mdash_poll(mgr);
|
||||||
|
|
||||||
for (c = mgr->conns; c != NULL; c = tmp) {
|
for (c = mgr->conns; c != NULL; c = tmp) {
|
||||||
|
long flush = 0;
|
||||||
bool is_resp = c->is_resp;
|
bool is_resp = c->is_resp;
|
||||||
tmp = c->next;
|
tmp = c->next;
|
||||||
mg_call(c, MG_EV_POLL, &now);
|
mg_call(c, MG_EV_POLL, &now);
|
||||||
@@ -782,10 +793,12 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
} else {
|
} else {
|
||||||
if (c->is_readable) read_conn(c);
|
if (c->is_readable) read_conn(c);
|
||||||
if (c->is_writable) write_conn(c);
|
if (c->is_writable) write_conn(c);
|
||||||
if (c->is_tls && !c->is_tls_hs && c->send.len == 0) mg_tls_flush(c);
|
if (c->is_tls && !c->is_tls_hs && c->send.len == 0)
|
||||||
|
flush = mg_tls_flush(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->is_draining && c->send.len == 0) c->is_closing = 1;
|
if (flush == MG_IO_ERR) mg_error(c, "tx err");
|
||||||
|
if (c->is_draining && c->send.len == 0 && flush == 0) c->is_closing = 1;
|
||||||
if (c->is_closing) close_conn(c);
|
if (c->is_closing) close_conn(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,12 +81,18 @@ void mg_tls_free(struct mg_connection *);
|
|||||||
long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
|
long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
|
||||||
long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
|
long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
|
||||||
size_t mg_tls_pending(struct mg_connection *);
|
size_t mg_tls_pending(struct mg_connection *);
|
||||||
void mg_tls_flush(struct mg_connection *);
|
long mg_tls_flush(struct mg_connection *);
|
||||||
void mg_tls_handshake(struct mg_connection *);
|
void mg_tls_handshake(struct mg_connection *);
|
||||||
void mg_tls_ctx_init(struct mg_mgr *);
|
void mg_tls_ctx_init(struct mg_mgr *);
|
||||||
void mg_tls_ctx_free(struct mg_mgr *);
|
void mg_tls_ctx_free(struct mg_mgr *);
|
||||||
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
|
#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);
|
||||||
}
|
}
|
||||||
|
|||||||
+150
-74
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,9 +267,14 @@ static void mg_der_debug(struct mg_der_tlv *tlv, int depth) {
|
|||||||
|
|
||||||
// Did we receive a full TLS record in the c->rtls buffer?
|
// Did we receive a full TLS record in the c->rtls buffer?
|
||||||
static bool mg_tls_got_record(struct mg_connection *c) {
|
static bool mg_tls_got_record(struct mg_connection *c) {
|
||||||
return c->rtls.len >= (size_t) TLS_RECHDR_SIZE &&
|
uint16_t rl;
|
||||||
c->rtls.len >=
|
if (c->rtls.len < (size_t) TLS_RECHDR_SIZE) return false;
|
||||||
(size_t) (TLS_RECHDR_SIZE + MG_LOAD_BE16(c->rtls.buf + 3));
|
rl = MG_LOAD_BE16(c->rtls.buf + 3);
|
||||||
|
if (rl > 16384 + 256) { // TLS 1.3 record max length (RFC-9846 5.1)
|
||||||
|
mg_error(c, "TLS record too large");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return c->rtls.len >= (size_t) (TLS_RECHDR_SIZE + rl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a single TLS record from the recv buffer
|
// Remove a single TLS record from the recv buffer
|
||||||
@@ -557,7 +562,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 +1324,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 +1410,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 +1418,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,
|
||||||
@@ -1624,8 +1625,9 @@ static int countdots(struct mg_str s) {
|
|||||||
|
|
||||||
static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
|
static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
|
||||||
const char *server_name,
|
const char *server_name,
|
||||||
struct mg_addr *server_ip) {
|
struct mg_addr *ref_ip) {
|
||||||
struct mg_der_tlv root, field, name;
|
struct mg_der_tlv root, field, name;
|
||||||
|
bool is_addr = ref_ip != NULL;
|
||||||
if (mg_der_parse((uint8_t *) der, dersz, &root) < 0) {
|
if (mg_der_parse((uint8_t *) der, dersz, &root) < 0) {
|
||||||
MG_ERROR(("failed to parse certificate"));
|
MG_ERROR(("failed to parse certificate"));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1639,20 +1641,24 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (mg_der_next(&field, &name) > 0) {
|
while (mg_der_next(&field, &name) > 0) {
|
||||||
if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address
|
if (is_addr) { // match ref IP to IP and name to DNS name (RFC-9525 6.2)
|
||||||
MG_VERBOSE(("Found SAN, IP: %M", mg_print_ip4, name.value));
|
if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address
|
||||||
if (!server_ip->is_ip6 &&
|
uint32_t cert_ip;
|
||||||
*((uint32_t *) name.value) == server_ip->addr.ip4)
|
MG_VERBOSE(("Found SAN, IP: %M", mg_print_ip4, name.value));
|
||||||
return 1; // and matches the one we're connected to
|
memcpy(&cert_ip, name.value, 4);
|
||||||
|
if (!ref_ip->is_ip6 && cert_ip == ref_ip->addr.ip4) // RFC-9525 6.4
|
||||||
|
return 1; // and byte-matches the one we wanted to connected to
|
||||||
|
}
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
} else if (name.type == 0x87 && name.len == 16) { // is an IPv6 address
|
else if (name.type == 0x87 && name.len == 16) { // is an IPv6 address
|
||||||
MG_VERBOSE(("Found SAN, IPv6: %M", mg_print_ip6, name.value));
|
MG_VERBOSE(("Found SAN, IPv6: %M", mg_print_ip6, name.value));
|
||||||
if (server_ip->is_ip6 && memcmp(name.value, server_ip->addr.ip6, 16) == 0)
|
if (ref_ip->is_ip6 && memcmp(name.value, ref_ip->addr.ip6, 16) == 0)
|
||||||
return 1; // and matches the one we're connected to
|
return 1; // and byte-matches the one we wanted to connected to
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
} else { // this is a text SAN
|
} else if (name.type == 0x82) { // this is a DNS name
|
||||||
struct mg_str sn, tn;
|
struct mg_str sn, tn;
|
||||||
MG_VERBOSE(("Found SAN, (%u): %.*s", name.type, name.len, name.value));
|
MG_VERBOSE(("Found SAN, DNS: %.*s", name.len, name.value));
|
||||||
sn = mg_str(server_name), tn = mg_str_n((char *) name.value, name.len);
|
sn = mg_str(server_name), tn = mg_str_n((char *) name.value, name.len);
|
||||||
if (countdots(sn) == countdots(tn) && mg_match(sn, tn, NULL))
|
if (countdots(sn) == countdots(tn) && mg_match(sn, tn, NULL))
|
||||||
return 1; // and matches the host name
|
return 1; // and matches the host name
|
||||||
@@ -1704,7 +1710,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 +1729,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1748,17 +1745,18 @@ static int mg_tls_verify_cert_cn(struct mg_der_tlv *subj, const char *host) {
|
|||||||
return matched;
|
return matched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1: found, 0: not found, < 0: parse error: -1: bundle (unused), -2: name
|
||||||
static int tls_bundle_find(struct tls_data *tls, struct mg_der_tlv *name,
|
static int tls_bundle_find(struct tls_data *tls, struct mg_der_tlv *name,
|
||||||
struct mg_tls_cert *cert) {
|
struct mg_tls_cert *cert) {
|
||||||
size_t i;
|
size_t i;
|
||||||
struct mg_der_tlv v;
|
struct mg_der_tlv v;
|
||||||
struct mg_str *p = tls->ca_bundle_der, tgt;
|
struct mg_str *p = tls->ca_bundle_der, tgt;
|
||||||
if (!mg_der_find_oid(name, (uint8_t *) "\x55\x04\x03", 3, &v)) return false;
|
if (mg_der_find_oid(name, (uint8_t *) "\x55\x04\x03", 3, &v) <= 0) return -2;
|
||||||
tgt = mg_str_n((const char *) v.value, v.len);
|
tgt = mg_str_n((const char *) v.value, v.len);
|
||||||
for (i = 0; i < tls->ca_bundle_len; i++, p++) {
|
for (i = 0; i < tls->ca_bundle_len; i++, p++) {
|
||||||
struct mg_str subj;
|
struct mg_str subj;
|
||||||
if (mg_tls_parse_cert_der(p->buf, p->len, cert) < 0 ||
|
if (mg_tls_parse_cert_der(p->buf, p->len, cert) < 0 ||
|
||||||
!mg_der_find_oid(&cert->subj, (uint8_t *) "\x55\x04\x03", 3, &v)) {
|
mg_der_find_oid(&cert->subj, (uint8_t *) "\x55\x04\x03", 3, &v) <= 0) {
|
||||||
MG_ERROR(("failed to parse certificate #%u in bundle", i + 1));
|
MG_ERROR(("failed to parse certificate #%u in bundle", i + 1));
|
||||||
continue; // skip this certificate but don't halt the process
|
continue; // skip this certificate but don't halt the process
|
||||||
}
|
}
|
||||||
@@ -1769,6 +1767,8 @@ static int tls_bundle_find(struct tls_data *tls, struct mg_der_tlv *name,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mg_aton_(struct mg_str str, struct mg_addr *addr);
|
||||||
|
|
||||||
static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
unsigned char *recv_buf;
|
unsigned char *recv_buf;
|
||||||
@@ -1855,11 +1855,16 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
|||||||
if (ci == certs) {
|
if (ci == certs) {
|
||||||
// First certificate in the chain is peer cert, check SAN if requested,
|
// First certificate in the chain is peer cert, check SAN if requested,
|
||||||
// and store public key for further CertVerify step
|
// and store public key for further CertVerify step
|
||||||
if (tls->hostname[0] != '\0' &&
|
if (tls->hostname[0] != '\0') {
|
||||||
mg_tls_verify_cert_san(cert, certsz, tls->hostname, &c->rem) <= 0 &&
|
struct mg_addr addr; // use aton_ so localhost evals as a name
|
||||||
mg_tls_verify_cert_cn(&ci->subj, tls->hostname) <= 0) {
|
bool is_addr = mg_aton_(mg_str_s(tls->hostname), &addr);
|
||||||
mg_error(c, "failed to verify hostname");
|
if (mg_tls_verify_cert_san(cert, certsz, tls->hostname,
|
||||||
return -1;
|
is_addr ? &addr : NULL) <= 0 &&
|
||||||
|
(!is_addr || // DNS CN fallback
|
||||||
|
mg_tls_verify_cert_cn(&ci->subj, tls->hostname) <= 0)) {
|
||||||
|
mg_error(c, "failed to verify hostname");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ci->pubkey.len > sizeof(tls->pubkey)) {
|
if (ci->pubkey.len > sizeof(tls->pubkey)) {
|
||||||
mg_error(c, "peer public key too large");
|
mg_error(c, "peer public key too large");
|
||||||
@@ -1878,7 +1883,7 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
|||||||
int r;
|
int r;
|
||||||
MG_VERBOSE(("Search current cert in bundle"));
|
MG_VERBOSE(("Search current cert in bundle"));
|
||||||
r = tls_bundle_find(tls, &ci->subj, &ca); // find current cert ci
|
r = tls_bundle_find(tls, &ci->subj, &ca); // find current cert ci
|
||||||
if (r < 0) {
|
if (r == -1) {
|
||||||
mg_error(c, "failed to parse CA bundle");
|
mg_error(c, "failed to parse CA bundle");
|
||||||
return -1;
|
return -1;
|
||||||
} else if (r > 0 && ca.pubkey.len == ci->pubkey.len &&
|
} else if (r > 0 && ca.pubkey.len == ci->pubkey.len &&
|
||||||
@@ -1886,7 +1891,7 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
|||||||
found_ca = true;
|
found_ca = true;
|
||||||
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));
|
||||||
break;
|
break;
|
||||||
} // else either r = 0 => subj not found or pubkey not matching
|
} // else r = 0 or -2 => subj not found or pubkey not matching
|
||||||
}
|
}
|
||||||
|
|
||||||
if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) {
|
if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) {
|
||||||
@@ -1900,10 +1905,16 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
|
|||||||
MG_VERBOSE(("Search bundle for issuer of last cert in chain"));
|
MG_VERBOSE(("Search bundle for issuer of last cert in chain"));
|
||||||
r = tls_bundle_find(tls, &certs[certnum - 1].issuer, &ca);
|
r = tls_bundle_find(tls, &certs[certnum - 1].issuer, &ca);
|
||||||
if (r <= 0) {
|
if (r <= 0) {
|
||||||
mg_error(c, r < 0 ? "failed to parse CA bundle"
|
mg_error(c, "failed to %s",
|
||||||
: "failed to find issuing CA in bundle");
|
r == 0 ? "find issuing CA in bundle"
|
||||||
|
: r == -1 ? "parse CA bundle"
|
||||||
|
: "parse name OID");
|
||||||
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 +2170,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 +2210,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 +2223,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 +2265,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 +2324,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 +2338,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 +2346,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 +2359,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 +2370,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 +2431,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 +2440,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 +2464,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 +2490,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 +2595,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 +2672,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 +2680,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 +2751,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 +2768,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 +2788,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++) {
|
||||||
@@ -2793,13 +2866,16 @@ size_t mg_tls_pending(struct mg_connection *c) {
|
|||||||
return tls != NULL ? tls->recv_len : 0;
|
return tls != NULL ? tls->recv_len : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_tls_flush(struct mg_connection *c) {
|
long mg_tls_flush(struct mg_connection *c) {
|
||||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||||
long n;
|
long n = 0;
|
||||||
while (tls->send.len > 0 &&
|
while (tls->send.len > 0 &&
|
||||||
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
||||||
mg_iobuf_del(&tls->send, 0, (size_t) n);
|
mg_iobuf_del(&tls->send, 0, (size_t) n);
|
||||||
}
|
}
|
||||||
|
if (n == MG_IO_ERR) return n;
|
||||||
|
c->is_tls_throttled = tls->send.len > 0;
|
||||||
|
return c->is_tls_throttled ? MG_IO_WAIT : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
||||||
|
|||||||
+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)
|
||||||
|
|||||||
+2
-1
@@ -21,8 +21,9 @@ size_t mg_tls_pending(struct mg_connection *c) {
|
|||||||
(void) c;
|
(void) c;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void mg_tls_flush(struct mg_connection *c) {
|
long mg_tls_flush(struct mg_connection *c) {
|
||||||
(void) c;
|
(void) c;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
||||||
(void) mgr;
|
(void) mgr;
|
||||||
|
|||||||
+33
-18
@@ -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;
|
||||||
@@ -237,17 +249,20 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_tls_flush(struct mg_connection *c) {
|
long mg_tls_flush(struct mg_connection *c) {
|
||||||
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
||||||
|
long n = 0;
|
||||||
if (c->is_tls_throttled && c->is_draining) {
|
if (c->is_tls_throttled && c->is_draining) {
|
||||||
long n =
|
n = mbedtls_ssl_write(&tls->ssl, tls->throttled_buf, tls->throttled_len);
|
||||||
mbedtls_ssl_write(&tls->ssl, tls->throttled_buf, tls->throttled_len);
|
|
||||||
#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
|
#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
|
||||||
if (n == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) return;
|
if (n == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) return MG_IO_WAIT;
|
||||||
#endif
|
#endif
|
||||||
c->is_tls_throttled =
|
c->is_tls_throttled =
|
||||||
(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE);
|
(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE);
|
||||||
|
if (c->is_tls_throttled) return MG_IO_WAIT;
|
||||||
|
if (n <= 0) return MG_IO_ERR;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
||||||
|
|||||||
+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
|
||||||
+35
-9
@@ -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;
|
||||||
@@ -310,8 +335,9 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_tls_flush(struct mg_connection *c) {
|
long mg_tls_flush(struct mg_connection *c) {
|
||||||
(void) c;
|
(void) c;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -182,6 +182,12 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
op = msg.flags & 15;
|
op = msg.flags & 15;
|
||||||
// MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op,
|
// MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op,
|
||||||
// (int) m.data.len, (int) m.data.len, m.data.buf));
|
// (int) m.data.len, (int) m.data.len, m.data.buf));
|
||||||
|
if ((op == WEBSOCKET_OP_CONTINUE && ofs == 0) ||
|
||||||
|
((op == WEBSOCKET_OP_TEXT || op == WEBSOCKET_OP_BINARY) &&
|
||||||
|
ofs != 0)) {
|
||||||
|
mg_error(c, "invalid WS fragment sequence");
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case WEBSOCKET_OP_CONTINUE:
|
case WEBSOCKET_OP_CONTINUE:
|
||||||
mg_call(c, MG_EV_WS_CTL, &m);
|
mg_call(c, MG_EV_WS_CTL, &m);
|
||||||
|
|||||||
+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/health.h src/health_cortex.h src/mdash.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/bsd.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||||
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 * 256)
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Loaded 100 of 2206 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user