Skip to content

Commit 0c9bcd9

Browse files
authored
Merge branch 'master' into matter_events
2 parents 64cd198 + 23c6abc commit 0c9bcd9

File tree

7 files changed

+291
-62
lines changed

7 files changed

+291
-62
lines changed

.github/scripts/package_esptool.sh

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
# Check version argument
6+
if [[ $# -ne 3 ]]; then
7+
echo "Usage: $0 <version> <base_folder> <json_path>"
8+
echo "Example: $0 5.0.dev1 /tmp/esptool /tmp/esptool-5.0.dev1.json"
9+
exit 1
10+
fi
11+
12+
VERSION=$1
13+
BASE_FOLDER=$2
14+
JSON_PATH=$3
15+
16+
export COPYFILE_DISABLE=1
17+
18+
shopt -s nullglob # So for loop doesn't run if no matches
19+
20+
# Function to update JSON for a given host
21+
function update_json_for_host {
22+
local host=$1
23+
local archive=$2
24+
25+
# Extract the old url from the JSON for this host, then replace only the filename
26+
old_url=$(jq -r --arg host "$host" '
27+
.packages[].tools[] | select(.name == "esptool_py") | .systems[] | select(.host == $host) | .url // empty
28+
' "$tmp_json")
29+
if [[ -n "$old_url" ]]; then
30+
base_url="${old_url%/*}"
31+
url="$base_url/$archive"
32+
else
33+
echo "No old url found for $host"
34+
exit 1
35+
fi
36+
37+
archiveFileName="$archive"
38+
checksum="SHA-256:$(shasum -a 256 "$archive" | awk '{print $1}')"
39+
size=$(stat -f%z "$archive")
40+
41+
# Use jq to update the JSON
42+
jq --arg host "$host" \
43+
--arg url "$url" \
44+
--arg archiveFileName "$archiveFileName" \
45+
--arg checksum "$checksum" \
46+
--arg size "$size" \
47+
'
48+
.packages[].tools[]
49+
|= if .name == "esptool_py" then
50+
.systems = (
51+
((.systems // []) | map(select(.host != $host))) + [{
52+
host: $host,
53+
url: $url,
54+
archiveFileName: $archiveFileName,
55+
checksum: $checksum,
56+
size: $size
57+
}]
58+
)
59+
else
60+
.
61+
end
62+
' "$tmp_json" > "$tmp_json.new" && mv "$tmp_json.new" "$tmp_json"
63+
}
64+
65+
cd "$BASE_FOLDER"
66+
67+
# Delete all archives before starting
68+
rm -f esptool-*.tar.gz esptool-*.zip
69+
70+
for dir in esptool-*; do
71+
# Check if directory exists and is a directory
72+
if [[ ! -d "$dir" ]]; then
73+
continue
74+
fi
75+
76+
base="${dir#esptool-}"
77+
78+
# Add 'linux-' prefix if base doesn't contain linux/macos/win64
79+
if [[ "$base" != *linux* && "$base" != *macos* && "$base" != *win64* ]]; then
80+
base="linux-${base}"
81+
fi
82+
83+
if [[ "$dir" == esptool-win* ]]; then
84+
# Windows zip archive
85+
zipfile="esptool-v${VERSION}-${base}.zip"
86+
echo "Creating $zipfile from $dir ..."
87+
zip -r "$zipfile" "$dir"
88+
else
89+
# Non-Windows: set permissions and tar.gz archive
90+
tarfile="esptool-v${VERSION}-${base}.tar.gz"
91+
echo "Setting permissions and creating $tarfile from $dir ..."
92+
chmod -R u=rwx,g=rx,o=rx "$dir"
93+
tar -cvzf "$tarfile" "$dir"
94+
fi
95+
done
96+
97+
# After the for loop, update the JSON for each archive
98+
# Create a temporary JSON file to accumulate changes
99+
tmp_json="${JSON_PATH}.tmp"
100+
cp "$JSON_PATH" "$tmp_json"
101+
102+
for archive in esptool-v"${VERSION}"-*.tar.gz esptool-v"${VERSION}"-*.zip; do
103+
[ -f "$archive" ] || continue
104+
105+
echo "Updating JSON for $archive"
106+
107+
# Determine host from archive name
108+
case "$archive" in
109+
*linux-amd64*) host="x86_64-pc-linux-gnu" ;;
110+
*linux-armv7*) host="arm-linux-gnueabihf" ;;
111+
*linux-aarch64*) host="aarch64-linux-gnu" ;;
112+
*macos-amd64*) host="x86_64-apple-darwin" ;;
113+
*macos-arm64*) host="arm64-apple-darwin" ;;
114+
*win64*) hosts=("x86_64-mingw32" "i686-mingw32") ;;
115+
*) echo "Unknown host for $archive"; continue ;;
116+
esac
117+
118+
# For win64, loop over both hosts; otherwise, use a single host
119+
if [[ "$archive" == *win64* ]]; then
120+
for host in "${hosts[@]}"; do
121+
update_json_for_host "$host" "$archive"
122+
done
123+
else
124+
update_json_for_host "$host" "$archive"
125+
fi
126+
done
127+
128+
# After all archives are processed, move the temporary JSON to the final file
129+
mv "$tmp_json" "$JSON_PATH"

docs/en/guides/core_compatibility.rst

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@ Welcome to the compatibility guide for library developers aiming to support mult
99
Code Adaptations
1010
----------------
1111

12-
To ensure compatibility with both versions of the ESP32 Arduino core, developers should utilize conditional compilation directives in their code. Below is an example of how to conditionally include code based on the ESP32 Arduino core version::
12+
To ensure compatibility with both versions of the ESP32 Arduino core, developers should utilize conditional compilation directives in their code. Below is an example of how to conditionally include code based on the ESP32 Arduino core version:
1313

14-
.. code-block:: cpp
14+
.. code-block:: cpp
1515
16-
#ifdef ESP_ARDUINO_VERSION_MAJOR
17-
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
18-
// Code for version 3.x
19-
#else
20-
// Code for version 2.x
21-
#endif
22-
#else
23-
// Code for version 1.x
24-
#endif
16+
#ifdef ESP_ARDUINO_VERSION_MAJOR
17+
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
18+
// Code for version 3.x
19+
#else
20+
// Code for version 2.x
21+
#endif
22+
#else
23+
// Code for version 1.x
24+
#endif
2525
2626
Version Print
2727
-------------
2828

29-
To easily print the ESP32 Arduino core version at runtime, developers can use the `ESP_ARDUINO_VERSION_STR` macro. Below is an example of how to print the ESP32 Arduino core version::
29+
To easily print the ESP32 Arduino core version at runtime, developers can use the `ESP_ARDUINO_VERSION_STR` macro. Below is an example of how to print the ESP32 Arduino core version:
3030

31-
.. code-block:: cpp
31+
.. code-block:: cpp
3232
33-
Serial.printf(" ESP32 Arduino core version: %s\n", ESP_ARDUINO_VERSION_STR);
33+
Serial.printf(" ESP32 Arduino core version: %s\n", ESP_ARDUINO_VERSION_STR);
3434
3535
API Differences
3636
---------------

libraries/DNSServer/src/DNSServer.cpp

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,22 @@ void DNSServer::_handleUDP(AsyncUDPPacket &pkt) {
111111
// will reply with IP only to "*" or if domain matches without www. subdomain
112112
if (dnsHeader.OPCode == DNS_OPCODE_QUERY && requestIncludesOnlyOneQuestion(dnsHeader)
113113
&& (_domainName.isEmpty() || getDomainNameWithoutWwwPrefix(static_cast<const unsigned char *>(dnsQuestion.QName), dnsQuestion.QNameLength) == _domainName)) {
114-
replyWithIP(pkt, dnsHeader, dnsQuestion);
114+
115+
// Qtype = A (1) or ANY (255): send an A record otherwise an empty response
116+
if (ntohs(dnsQuestion.QType) == 1 || ntohs(dnsQuestion.QType) == 255) {
117+
replyWithIP(pkt, dnsHeader, dnsQuestion);
118+
} else {
119+
replyWithNoAnsw(pkt, dnsHeader, dnsQuestion);
120+
}
115121
return;
116122
}
117-
118123
// otherwise reply with custom code
119124
replyWithCustomCode(pkt, dnsHeader);
120125
}
121126

122127
bool DNSServer::requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader) {
123-
return ntohs(dnsHeader.QDCount) == 1 && dnsHeader.ANCount == 0 && dnsHeader.NSCount == 0 && dnsHeader.ARCount == 0;
128+
dnsHeader.ARCount = 0; // We assume that if ARCount !=0 there is a EDNS OPT packet, just ignore
129+
return ntohs(dnsHeader.QDCount) == 1 && dnsHeader.ANCount == 0 && dnsHeader.NSCount == 0;
124130
}
125131

126132
String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size_t len) {
@@ -139,7 +145,6 @@ String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size
139145

140146
void DNSServer::replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) {
141147
AsyncUDPMessage rpl;
142-
143148
// Change the type of message to a response and set the number of answers equal to
144149
// the number of questions in the header
145150
dnsHeader.QR = DNS_QR_RESPONSE;
@@ -187,3 +192,76 @@ void DNSServer::replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader) {
187192
rpl.write(reinterpret_cast<const uint8_t *>(&dnsHeader), sizeof(DNSHeader));
188193
_udp.sendTo(rpl, req.remoteIP(), req.remotePort());
189194
}
195+
196+
void DNSServer::replyWithNoAnsw(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) {
197+
198+
dnsHeader.QR = DNS_QR_RESPONSE;
199+
dnsHeader.ANCount = 0;
200+
dnsHeader.NSCount = htons(1);
201+
202+
AsyncUDPMessage rpl;
203+
rpl.write(reinterpret_cast<const uint8_t *>(&dnsHeader), sizeof(DNSHeader));
204+
205+
// Write the question
206+
rpl.write(dnsQuestion.QName, dnsQuestion.QNameLength);
207+
rpl.write((uint8_t *)&dnsQuestion.QType, 2);
208+
rpl.write((uint8_t *)&dnsQuestion.QClass, 2);
209+
210+
// An empty answer contains an authority section with a SOA,
211+
// We take the name of the query as the root of the zone for which the SOA is generated
212+
// and use a value of DNS_MINIMAL_TTL seconds in order to minimize negative caching
213+
// Write the authority section:
214+
// The SOA RR's ownername is set equal to the query name, and we use made up names for
215+
// the MNAME and RNAME - it doesn't really matter from a protocol perspective - as for
216+
// a no such QTYPE answer only the timing fields are used.
217+
// a protocol perspective - it
218+
// Use DNS name compression : instead of repeating the name in this RNAME occurrence,
219+
// set the two MSB of the byte corresponding normally to the length to 1. The following
220+
// 14 bits must be used to specify the offset of the domain name in the message
221+
// (<255 here so the first byte has the 6 LSB at 0)
222+
rpl.write((uint8_t)0xC0);
223+
rpl.write((uint8_t)DNS_OFFSET_DOMAIN_NAME);
224+
225+
// DNS type A : host address, DNS class IN for INternet, returning an IPv4 address
226+
uint16_t answerType = htons(DNS_TYPE_SOA), answerClass = htons(DNS_CLASS_IN);
227+
uint32_t Serial = htonl(DNS_SOA_SERIAL); // Date type serial based on the date this piece of code was written
228+
uint32_t Refresh = htonl(DNS_SOA_REFRESH); // These timers don't matter, we don't serve zone transfers
229+
uint32_t Retry = htonl(DNS_SOA_RETRY);
230+
uint32_t Expire = htonl(DNS_SOA_EXPIRE);
231+
uint32_t MinTTL = htonl(DNS_MINIMAL_TTL); // See RFC2308 section 5
232+
char MLabel[] = DNS_SOA_MNAME_LABEL;
233+
char RLabel[] = DNS_SOA_RNAME_LABEL;
234+
char PostFixLabel[] = DNS_SOA_POSTFIX_LABEL;
235+
236+
// 4 accounts for len fields and for both rname
237+
// and lname and their postfix labels and there are 5 32 bit fields
238+
239+
uint16_t RdataLength = htons((uint16_t)(strlen(MLabel) + strlen(RLabel) + 2 * strlen(PostFixLabel) + 4 + 5 * sizeof(Serial)));
240+
241+
rpl.write((unsigned char *)&answerType, 2);
242+
rpl.write((unsigned char *)&answerClass, 2);
243+
rpl.write((unsigned char *)&MinTTL, 4); // DNS Time To Live
244+
245+
rpl.write((unsigned char *)&RdataLength, 2);
246+
247+
rpl.write((uint8_t)strlen(MLabel));
248+
rpl.write((unsigned char *)&MLabel, strlen(MLabel));
249+
250+
rpl.write((unsigned char *)&PostFixLabel, strlen(PostFixLabel));
251+
rpl.write((uint8_t)0);
252+
// rpl.write((uint8_t)0xC0);
253+
// rpl.write((uint8_t)DNS_OFFSET_DOMAIN_NAME);
254+
255+
rpl.write((uint8_t)strlen(RLabel));
256+
rpl.write((unsigned char *)&RLabel, strlen(RLabel));
257+
rpl.write((unsigned char *)&PostFixLabel, strlen(PostFixLabel));
258+
rpl.write((uint8_t)0);
259+
260+
rpl.write((unsigned char *)&Serial, 4);
261+
rpl.write((unsigned char *)&Refresh, 4);
262+
rpl.write((unsigned char *)&Retry, 4);
263+
rpl.write((unsigned char *)&Expire, 4);
264+
rpl.write((unsigned char *)&MinTTL, 4);
265+
266+
_udp.sendTo(rpl, req.remoteIP(), req.remotePort());
267+
}

libraries/DNSServer/src/DNSServer.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@
99
#define DNS_OFFSET_DOMAIN_NAME DNS_HEADER_SIZE // Offset in bytes to reach the domain name labels in the DNS message
1010
#define DNS_DEFAULT_PORT 53
1111

12+
#define DNS_SOA_MNAME_LABEL "ns"
13+
#define DNS_SOA_RNAME_LABEL "esp32"
14+
// The POSTFIX_LABEL will be concatenated to the RName and MName Label label
15+
// do not use a multilabel name here. "local" is a good choice as it is reserved for
16+
// local use by IANA
17+
// The postfix label is defined as an array of characters that follows the
18+
// definition of RFC1035 3.1
19+
// for instance, a postfix of example.com would be defined as:
20+
// #define DNS_SOA_POSTFIX_LABEL {'\7', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '\3', 'c', 'o', 'm', '\0'}
21+
#define DNS_SOA_POSTFIX_LABEL \
22+
{ '\5', 'l', 'o', 'c', 'a', 'l', '\0' }
23+
// From the following values only the MINIMAL_TTL has relevance
24+
// in the context of client-server protocol interactions.
25+
// The other values are arbitrary chosen as they are only relevant for
26+
// in a zone-transfer scenario.
27+
#define DNS_SOA_SERIAL 2025052900 // Arbitrary serial (format: YYYYMMDDnn)
28+
#define DNS_SOA_REFRESH 100000 // Arbitrary (seconds)
29+
#define DNS_SOA_RETRY 10000 // Arbitrary (seconds)
30+
#define DNS_SOA_EXPIRE 1000000 // Arbitrary (seconds)
31+
#define DNS_MINIMAL_TTL 5 // Time to live for negative answers RFC2308
1232
enum class DNSReplyCode : uint16_t {
1333
NoError = 0,
1434
FormError = 1,
@@ -179,5 +199,7 @@ class DNSServer {
179199
inline bool requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader);
180200
void replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion);
181201
inline void replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader);
202+
inline void replyWithNoAnsw(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion);
203+
182204
void _handleUDP(AsyncUDPPacket &pkt);
183205
};

0 commit comments

Comments
 (0)