media/gstreamer: stop including gstwebrtcbin.h as it appears the things we need are now in the public API headers
sip5060.net / en es
media/gstreamer: stop including gstwebrtcbin.h as it appears the things we need are now in the public API headers
media/gstreamer, resip/recon, apps: move flag GST_USE_UNSTABLE_API from code to CMakeLists.txt
Merge pull request #485 from Lastique/feature/known_headers_bitmask Add ordered view over `SipMessage::KnownHeaders`
Add ordered view over SipMessage::KnownHeaders. This commit implements an ordered view over known headers in a SipMessage. The view provides read-only access to headers in the order of Headers::Type enum values, which is useful for deterministic serialization of the message. The view relies on the bit mask of non-"unused" entries in KnownHeaders. The bit mask is transparently maintained when elements are inserted and removed from the container. On iteration, the bit mask is scanned for set bits indicating valid headers. This bit mask duplicates information that was already present in the HeaderInfo struct, as the special value of mType == Headers::UNKNOWN. The special value of mType and the associated logic is preserved to keep iteration over the unordered headers fast, as it keeps memory accesses localized in the mHeaders vector. Use this ordered view in SipMessage::encode and encodeEmbedded instead of looping over all known header types. This significantly optimizes serialization performance.
Don't expire an INVITE session while a session modification is in flight - fixes #483 - an UPDATE session refresh that the peer never answered kept being retransmitted by the stack after the session had expired and the application had destroyed it, eventually reaching an unrelated peer - regression from 3c70f0113, which started a Session-Expiration timer on the refresher side. That timer fires before the refresh transaction's TimerF, so the session and its dialog could be destroyed while the stack was still retransmitting the refresh - and there is currently no way for DUM to cancel a transaction that is in flight in the stack - when the Session-Expiration timer fires while a session modification is outstanding (SentUpdate, SentReinvite, SentReinviteNoOffer or SentOptions), defer the expiry instead of reporting it, and apply it once that transaction completes. This restores the property that a session outlives the transactions it started - a successful refresh restarts the session timers and discards the deferred expiry - a failed refresh reports onSessionExpired rather than the failure of the request itself, so the application still gets the callback it expects and ends the session the usual way. If it chooses to ignore the expiry after an outright failure (a 408 from TimerF, for instance) the session is still torn down as before - the Glare states are deliberately not treated as in flight - the transaction has already completed there, so nothing can be left retransmitting - also ignore a Session-Expiration timer that arrives once the session has terminated. The timer sequence numbers are only bumped when the timers are restarted, and DUM delivers the timeout as long as the usage hasn't been destroyed yet, so onSessionExpired could otherwise follow onTerminated
Merge pull request #482 from Nexory/smime-verify-received-bytes Verify multipart/signed against the received bytes (RFC 1847)
Correct the header encode ordering comments in SipMessage The comments added in 7a9b423 claimed that looping over Headers::Type values and calling find() on each was "no slower than iterating the container directly" because find() is O(1). That is wrong. It measures against the pre-df482da fixed array, which also looped over all MAX_HEADERS types -- but that is not what 7a9b423 replaced. It replaced the container iteration introduced in df482da, which visited only the headers actually present in the message. Probing all 97 header types to emit the ~20 a typical message carries is a real regression against that baseline, and the comment asserting otherwise was misleading. Reword both encode() and encodeEmbedded() to state the cost accurately: find() is O(1), but the loop is O(MAX_HEADERS) regardless of how few headers the message holds. Also record why the ordering itself is worth keeping, so it is not mistaken for arbitrary and undone later: HeaderTypes.hxx documents the enum as controlling encode order, and its leading entries follow the RFC 3261 section 7.3.1 recommendation that headers needed for proxy processing appear towards the top of the message. Thanks to Andrey Semashev for catching this: https://github.com/resiprocate/resiprocate/commit/7a9b42395df800b032904fbe2581ca91ed54ebb9#r197392200 Comments only -- no functional change. Recovering the enum order without paying the O(MAX_HEADERS) scan is left for a follow-up.
resip/stack: say the same thing in all three places about copies The comment on the copy constructor still claimed a copy is never parsed again and therefore never has a record. That is what the header used to say and no longer does, and operator= points a reader straight at this comment, so the two contradicted each other. It now describes both cases and refers to getRawFirstPart(). operator= carried the same claim, "It stays empty afterwards". It does not: assigning from a source nothing has read leaves this object unparsed with a buffer of its own, and its first access records a view into that buffer, exactly as for the copy constructor. Only assignment from an already parsed source leaves no record. Two cases pin both directions. Two wording fixes in the same area while here. The header paragraph opened with "A copy is not empty by construction", which reads as the opposite of what follows it. And the fallback log in checkSignature() named two cases where there are three: a copy of an already parsed body has no record and is perfectly ordinary, since SipMessage::init() clones mContents.
resip/stack: correct what getRawFirstPart says about copies The note claimed a copy never carries a record because it is not parsed again. That is only true for a copy of a body that was already parsed. LazyParser's copy constructor carries mState over and HeaderFieldValue's deep-copies the buffer, so copying a body that is still unparsed leaves a copy that is unparsed as well, with a buffer of its own; its first access parses that buffer and establishes a view into it. Describe both cases and cover the second one in the test, which so far only copied after the original had been read. The new case checks that the copy reports the same bytes and that the original still works afterwards. Also drop TestSupport.cxx from the test's sources and rutil/DataStream.hxx from its includes; neither is used.
resip/stack: say which path checkSignature took When no received bytes were recorded, checkSignature() silently verifies against a re-encoding. That is correct for a body built locally, and a regression for a body that was parsed and then reached through the mutable parts(). From inside the function the two are indistinguishable. Log it. parts() now has a security-relevant side effect that is not visible at the call site, so a future caller that drops the bytes should show up as a greppable line rather than as signatures that quietly stop verifying.
resip/stack: read a signed body's parts without dropping the received bytes Helper::getSdpRecurse() reached the parts through a non-const MultipartSignedContents, which resolves to the mutable parts(). That runs the non-const checkParsed(), marks the body DIRTY and drops the bytes that arrived, so BaseSecurity::checkSignature() falls back to a re-encoding. DUM callers do not see it, because EncryptionManager runs ahead of InviteSession in the feature chain, but anything calling Helper::getSdp() directly does. Bind a const pointer and read through that, the same treatment EncryptionManager::Decrypt::isEncryptedRecurse() already got. The first part was also dereferenced without checking that there is one. Guard it, as in the other place.
resip/stack: verify multipart/signed against the received bytes checkSignature() built the digest input with encodeHeaders() plus encode(), which writes a fixed set of headers in a fixed order with one space after the colon and rebuilds the Content-Type from the parsed Mime. A signature made by a peer whose header block differs in a single byte therefore failed to verify. Use the received bytes when they are available and fall back to the previous behaviour when they are not.
resip/stack/test: add testMultipartRawParts Pins the contract: the first part comes back byte for byte for header layouts a conforming peer sends, a differing part reads back differently so the check cannot pass on a stub, only the first part is kept and later parts do not change it, const access keeps the bytes while mutable access drops them, and a copy or a locally built body has none. Needs no certificates and no arguments.
resip/stack: record the first body part as it arrived RFC 1847 section 2.1 says the signature of a multipart/signed body covers the first body part as it was received. Keep a read-only view of those bytes while parsing, header block included and the CRLF that belongs to the following boundary excluded. Data::Share means the view points into the buffer being parsed rather than copying it, so parsing an ordinary multipart body costs nothing extra; the accessor hands back a self contained copy so the value survives a later parts() call. Only the first part is kept, since that is the part the signature covers. Mutable access through parts() drops the view, and so does a copy, which is never parsed again and would otherwise point at the original.
resip/dum: inspect a signed body without dropping the received bytes Decrypt::decrypt() calls isEncrypted() before getContents(), and isEncryptedRecurse() reached for the mutable parts() just to see what kind of body it had. The non-const checkParsed() marks the object DIRTY, so by the time checkSignature() ran, the bytes that arrived were gone and it fell back to a re-encoding. Inspect through the const overload, and write back into the parts only when the recursion really replaced one. Note that the body is no longer marked DIRTY by this inspection alone, which is the point: DIRTY should mean that something changed. A replacement below the first part still leaves the body unmarked, same as before this change.
Merge pull request #477 from jmariasc/feature/urn-scheme The urn: scheme (RFC 8141 ) is commonly used in IMS to request emergency services, where the UE requests for a specific service such as urn:service:sos (RFC 5031) and the network routes the call to the most appropriate PSAP. The UAC must place the URN in the Request-URI, and optionally in the To header, and all the B2BUAs and SIP proxies involved in the call path must be capable of handling it. Since resiprocate/resip could not previously process SIP messages containing a URN in the URI because of resip::Uri had no notion of the urn: scheme, and DUM rejected such request-URIs, I have added some basic support for this feature for your review and consideration.
Fixes to urn: parsing, Aor memory safety and, NID validation.
- Fix a bare (unbracketed) urn: swallowing a following NameAddr parameter
(e.g. "To: urn:service:sos;tag=abc123" lost the tag). Uri::parse() now
distinguishes a bare addr-spec inside a header value (new
Uri::setIsBareAddrSpec(), set only by NameAddr::parse()'s unbracketed
branch, where ';'/'?'/',' must terminate the NSS) from every other
context -- Request-Line, a standalone Uri built from a string, or
between angle brackets -- which keep the permissive whitespace-only
rule, since a Request-URI can never be bracketed and a truncation
there would corrupt the target instead of fixing an ambiguity.
- Fix Aor::mPort/mOldPort being left uninitialized on the tel:/urn:
early-return paths (in-class default initializers). While adding
test coverage for this, found and fixed a second, unrelated
pre-existing bug: Aor::Aor(const Data&) never copied its input --
mScheme/mUser/mHost were non-owning views into the caller's buffer,
so Aor aor("sip:...") with a literal left them dangling once the
temporary Data was destroyed. Added Aor::mOwnedBuffer to fix this.
- Normalize the urn: NID to lowercase at parse time (RFC 8141 sec 2.2,
same treatment schemeLowercase() already gives the scheme), and
remove the special-case urnUserEqual() comparator it replaces.
operator==, operator<, std::hash<Uri>, and getAorInternal() now all
agree on urn: equality instead of only operator== doing the
case-insensitive NID comparison.
- Revert addSupportedScheme(Symbols::Urn) from MasterProfile's default
constructor -- accepting urn: Request-URIs is deployment policy, not
something the library should impose on every DUM application.
Documented the opt-in with a code example on
MasterProfile::addSupportedScheme().
- Add NID syntax validation in the urn: parse branch, throwing
ParseException for a missing NID:NSS separator, a NID outside the
2-32 character range (RFC 8141 sec 2), invalid NID characters, a
leading/trailing '-', an empty NSS, or NID == "urn" (sec 5.1).
- Collapse encodeParsed()'s urn: branch into the generic one (single
encoding table selected by scheme); extract the '%'-scan/mUserRaw
logic in parse() into a shared decodeUserPreservingEscapes() helper
used by both the sip:/sips: and urn: branches; add Uri::isUserRelevant()
and use it in BaseCreator.cxx/Dialog.cxx instead of the
Sip||Sips||Tel triplet copied three times; use isEqualNoCase()
consistently for scheme comparisons in getAorInternal()/encodeParsed();
document the RFC 8141 sec 3.1 deviation (r-/q-/f-components folded
into user-part equality) as a deliberate comment instead of changing
behavior.
- Fix two issues found during self-review: Uri::isUserRelevant() didn't
call checkParsed() like every other public accessor (inert with the
current call sites, but a trap for future ones); NID character
validation used isalnum(), which is locale-dependent and can accept
non-ASCII bytes under a non-C locale -- replaced with an explicit
ASCII range check.
- Add regression tests: bare urn: with a following NameAddr parameter;
a urn: Request-URI with a literal ';' in the NSS (must not truncate);
malformed NID/NSS cases (empty, too short/long, invalid characters,
NID == "urn"); operator</hash/getAor() consistency for a
case-varying NID; Aor("urn:...") round-tripping. Wire testAor.cxx
into CMake -- it existed but was never registered, so it had never
actually been compiled or run. Add
resip/dum/test/testUrnContact.cxx, an end-to-end DUM test with a
real UAC/UAS pair asserting the Contact built for a urn: To/Request-URI
has no user part.
Known pre-existing issue, out of scope here: std::hash<Uri> can differ
between two Uris that compare equal when the user part contains a %XX
escape (hashes the encoded form via encodeParsed(), not the decoded
mUser); reproduced identically for sip:, so urn: only inherits it by
reusing the same mUserRaw mechanism.Restyle/improve repro web admin UI
Rework the repro web admin pages onto a dark theme. Presentation now lives
entirely in the stylesheet in webadmin/pageOutlinePre.html rather than in
per-element HTML attributes.
Page shell:
- New header, sidebar and footer; sidebar highlights the current page.
- Product mark (SVG, also used as the favicon), linking to the About Repro
wiki page. The footer link now points there too - the old
resiprocate.org/About_Repro URL was dead.
- Standalone login and HTTP 401/404/301 pages restyled to match.
- Menu: "Configure" -> "Base Configuration", "Statistics" -> "Live Info",
"Settings" -> "Stack Info & Settings" and moved to the end of its group.
Content and copy:
- Placeholder help text on every input across the add and edit forms.
- Explanatory copy moved above the form it describes on ACLs, Add/Edit User,
Add Filter and Add Route, and no longer boxed.
- Status messages ("Added", "Error", "WARNING") render as colour-coded
notices; thead/tbody added to the three listing tables that lacked them.
- Filter and route tests now report "No matches" instead of rendering
nothing, which was indistinguishable from not having run the test.
- Registrations: the add form is labelled as adding a static registration
and explains what one is; the listing below it is headed "Active
Registrations".
- Stack Info & Settings reordered: Logging and Admin, DNS Cache, Stack Info,
Congestion, Settings. Config dumps grow to fit instead of scrolling
inside a fixed-height box.
Behaviour changes:
- Fix the Restart Proxy button never appearing on a default install.
ReproRunner defaults CommandPort to 5081 when the setting is absent, but
WebAdmin defaulted it to 0 in both the button test and buildRestartSubPage.
Since the shipped repro.config leaves the line commented out, the
CommandServer was running while the web UI hid the button and would have
refused the restart. Both reads now use REPRO_DEFAULT_COMMAND_PORT (5081).
- The per-domain TLS port is removed from the GUI and every record is now
written with 0. Nothing reads the value back - ConfigStore::getTlsPort()
has no callers, and ReproRunner uses only mDomain from these records. The
field stays in ConfigRecord and the DB serialization for schema
compatibility, and the REST API is unchanged.
- Adding a static registration now submits action="Add Static Registration"
rather than action="Add". Anything driving that form directly must be
updated.
- webadmin/*.ixx regenerated from the .html sources with doublequoteme.
- bumped resip version for an upcoming 1.15 releaseMerge pull request #481 from Nexory/cert-serial-positive Keep certificate serial numbers positive (RFC 5280 4.1.2.2)
resip/stack/test: fix testCertSerial build without openssl/rsa.h The RSA_* symbols were only reaching this file transitively through openssl/x509.h. That does not hold everywhere: with the OpenSSL headers on the CI image (1.1.1 under OPENSSL_API_COMPAT=0x10101000L) x509.h leaves RSA_new, RSA_F4, RSA_generate_key_ex and RSA_free undeclared, so the build failed. Include openssl/rsa.h and openssl/evp.h explicitly, matching the include list ssl/Security.cxx already uses. Also guard the whole body with USE_SSL, as the other OpenSSL-using tests in the stack do, so the file compiles to a no-op main when built without SSL. Reproduced the failure and verified the fix on debian:bullseye-slim (OpenSSL 1.1.1w); the full stack suite stays at 69/69 on OpenSSL 3.5.
resip/stack: keep certificate serial numbers positive RFC 5280 section 4.1.2.2 requires the serial number to be a positive integer. Random::getCryptoRandom() fills a whole int from RAND_bytes, so it is negative about half the time, where the getRandom() it replaced in #433 never was: 49851 of 100000 draws came back negative here, and generating 40 certificates through generateUserCert gave 29 whose serial BN_is_negative reports as negative. openssl x509 prints them as such. Mask both sites down to a positive value. Security.cxx is the live one, reached from repro through CertSubscriptionHandler. makeCert.cxx carries the same line but is in no CMakeLists and has its own main(), so that one is for consistency. reflow/FlowManager.cxx and resip/stack/test/makeSelfCert.cxx also set a serial, but both still use getRandom() and are always positive, so they need nothing.
resip/stack/test: add testCertSerial Generates certificates through generateUserCert and checks that none of them carries a negative serial. Before the previous commit this reports 29 of 40 and exits non-zero. The control builds its certificate from scratch and signs it rather than parsing one and writing a serial afterwards: i2d_X509() re-emits the DER it cached while parsing, so a serial set after the fact never reaches the encoding and the control would quietly be looking at the original value instead of the one it meant to plant. That version passed for the wrong reason while the fix was absent and failed once it was there. Registered inside the existing if(OPENSSL_FOUND) block next to testSecurity, since it needs Security.hxx.
Fix integer overflow and resource exhaustion in WsFrameExtractor (#478) The WebSocket frame parser is reachable pre-authentication, before any SIP credentials are exchanged, so every one of these is remotely triggerable by an unauthenticated peer. Reported in https://github.com/resiprocate/resiprocate/issues/478 (thanks to @afldl): * The frame size check computed mMessageSize + mPayloadLength and compared it against mMaxMessage. mPayloadLength comes straight off the wire as a 64 bit field, so a fragment followed by a frame declaring a length near 2^64 wrapped the sum to a small value and slipped past the check, leading to an uncaught std::bad_alloc or a huge allocation per connection. Rewritten to test the two bounds separately without overflowing. mPayloadLength was also a Data::size_type, which silently truncated the wire value on 32 bit builds; it is now a uint64_t and is narrowed only once mMaxMessage has bounded it. * The 64 bit length was assembled with || instead of | on the last byte. Since || has the lowest precedence, the whole expression collapsed to 0 or 1 and lengths over 65535 never parsed at all. Found while writing the regression tests, in the same code path: * parseHeader() required only 8 header bytes before reading the 64 bit length, which actually occupies bytes 2 through 9. A masked frame split across reads could leave mHeaderLen at 8 or 9, and the length and the mask key were then built out of uninitialized bytes of the header buffer (mHeaderLen - hdrPos underflowed past the mask guard). Every branch now guarantees mHeaderLen >= hdrPos. * A complete header of 14 bytes, the maximum, was rejected as "header too long", so any masked frame using a 64 bit length - which is what a browser sends for a message over 64 KB - dropped the connection. Only an incomplete header that would exceed the buffer is rejected now, and the header is re-parsed as soon as its bytes arrive so an oversized frame is refused when its header is known rather than when its payload starts turning up. * joinFrames() deleted the Data of the first fragment without freeing the buffer it had borrowed, leaking it on every multi-frame message, and the destructor never freed a partially received mPayload. mPayload, mPayloadPos, mPayloadLength, mMasked and mFinalFrame were left uninitialized by the constructor. * Empty continuation frames never advance mMessageSize, so the size limit could not terminate a flood of them: two bytes on the wire bought unbounded heap. Bounded the number of fragments per message at 1024. Adds resip/stack/test/testWsFrameExtractor.cxx, which covers 7, 16 and 64 bit lengths masked and unmasked, fragmentation, the overflowing lengths from the report, the mMaxMessage boundaries, the fragment flood, destructor safety and malformed input, feeding the bytes at every chunk size from one byte up so each split point of the header is exercised. 214 of its checks fail against the unfixed parser.
Merge pull request #479 from jmariasc/fix/cmake-soci Fix to CMakeList.txt for soci inclusion when reConServer is built
CMake: Fix soci inclusion for reConServer. reConServer's B2BCallManager includes soci.h unconditionally, so soci_core is needed whenever BUILD_RECONSERVER is on, even without USE_SOCI_POSTGRESQL OR USE_SOCI_MYSQL are ON.
Sanity checks before copying the user part from a urn to a sip Uri. The ABNF is not the same for sip and urn schemes so some checks are neccesary in order to prevent the copy of the user part from urn to sip Uris.
Added the urn scheme to the MasterProfile in DUM. Added the urn scheme to the MasterProfile to avoid getting a 416 Unsupported URI Scheme in DialogUsageManager::validateRequestURI() wheh receiving an INVITE with urn.
Merge branch 'master' into feature/urn-scheme
Add urn: (RFC 8141) scheme support to resip::Uri resip previously could not send or receive SIP messages using a urn: URI (e.g. urn:service:sos for emergency calling). - Urn added to the Symbols table like Sip, Sips and Tel. - Uri parses and encodes urn: as an NID:NSS blob with its own ABNF distinct from sip:/sips: and tel:. - operator== compares the NID case-insensitively and the NSS case-sensitively, per RFC 8141, instead of the whole blob alike. - Reuse the recent mUserRaw mechanism to preserve as-received escaping, so a literal '#'/'?' and an escaped '%23'/'%3F' don't collapse and corrupt real rq-components or fragments on re-encode. - Unit tests added covering parsing, encoding and equality for urn:.
Merge branch 'master' of https://github.com/resiprocate/resiprocate
Fix digest auth failing when the user file has CRLF line endings
std::getline strips only the LF, so a CRLF file leaves a trailing CR in the
last field each of these parsers reads. Windows opens the file in text mode
and the CRT translates CRLF to LF, so the CR never arrives; on POSIX there is
no translation. .gitattributes marks both files text=auto, so a Windows
checkout has CRLF in the working tree - running a Linux build against that
tree, as WSL on /mnt/c does, is enough to hit this.
In WebAdmin::parseUserFile() the affected field is the HA1. dbA1 became 33
bytes against a 32 byte computed A1, Data::operator== rejected the pair on
its size check before memcmp, and every web admin login failed with
user admin failed to authenticate to web server
compA1=587c67fddee5b46eef47c36d93016965
dbA1=587c67fddee5b46eef47c36d93016965
which reads as though the two matched. The CR is the last byte of the
message, so it prints in the one position where nothing renders it.
ReTurnConfig::authParse() has the same defect in its state field, which
already stopped on \n but not \r. There the CR leaves state unequal to
"authorized" and the record is dropped with "Invalid state value at line N",
so the account goes missing rather than failing to authenticate.
Add the EOL characters to both skipToOneOf() sets, which is what
ConfigParse::parseConfigFile() and ReproAuthenticatorFactory::parseFile()
already do for their line-final fields. Tab joins the repro set as well - the
username and realm there reach skipWhitespace() after their colon, but the
HA1 had no equivalent.
Also replace a stray CP-1252 em dash (0x97) in .gitattributes with an ASCII
hyphen. It is not valid UTF-8 and rendered as a replacement character.Quiet the post-debify apt-get update; retry install on mirror failures
apt-get update -qq still prints errors, so un-quieting it only added the
Hit:/Get: chatter. The verify step's apt-cache policy libsipxtapi-dev
reports the same thing more directly - which version won, and from which
origin - so put the -qq back.
Separately, deb.debian.org reset the connection partway through two .deb
downloads, and a single failed archive aborts apt-get install and with it
the whole job:
E: Failed to fetch .../cmake_3.18.4-2+deb11u1_amd64.deb Error reading
from server - read (104: Connection reset by peer)
Nothing about that run was wrong - the debify repository was added and its
indexes fetched normally - so a retry is the whole fix. Move the package
list into $PACKAGES and wrap the install in an until loop that makes up to
three attempts, sleeping and refreshing the lists in between. $PACKAGES
is expanded unquoted so that it word splits into arguments. The loop
condition is exempt from set -e, so a failed attempt does not abort the
step before the retry runs.Fetch add-apt-debify to a file and run it rather than piping it into a shell, so a failed download fails the step: a pipeline reports only the shell's exit status, and an empty script exits 0.
Fail Linux CI at setup when libsipxtapi-dev is the wrong one
recon includes <CpTopologyGraphFactoryImpl.h>, a sipXtapi header that
upstream kept in noinst_HEADERS - deliberately not installed - until
commit 6efe991 (Nov 2025) moved it to nobase_include_HEADERS. The only
package that provides it is libsipxtapi-dev 3.3.0~test20-2 from
apt.debify.org; Debian bullseye's own 3.3.0~test18+dfsg.1-0.1 does not.
Adding the debify repository is therefore not optional, but every way it
can fail is silent:
- wget -O - http://apt.debify.org/add-apt-debify | bash reports only
bash's exit status, so a failed fetch pipes an empty script into a
shell that exits 0 and the repository is never added.
- apt-get update -qq suppresses the Err:/W: lines that would name a
broken source.
- FindsipXtapi.cmake probes for CpTopologyGraphInterface.h, which both
versions ship, so configure succeeds against either.
The build then dies ~86% in on a missing include, pointing at recon
rather than at the dependency that was never installed.
Set pipefail so the wget failure fails the step, drop -qq from the update
that follows the repository being added (the earlier one only covers
Debian's own sources), and add a verification step that records
apt-cache policy libsipxtapi-dev and hard-fails if the header is absent.
Assert on the header rather than on a version string: it is the actual
requirement, and it stays correct if debify ever ships a different
version that still works.
This does not fix the underlying exposure - a third-party repository
whose indexes have not moved since 2022, on a Debian release that is
nearly EOL - it only makes the failure legible and immediate.Warn instead of aborting for POSIX-only settings on Windows All four ServerProcess subclasses - repro, reConServer, registrationAgent and reTurnServer - called these unguarded, so all four were affected. Add ServerProcess::checkPosixProcessControl(dependentSettings), which reports whether this build has fork(), setuid/setgid and PID file handling, and on Windows logs a warning naming the settings before returning false.
Fix RPM packaging overrides broken by the repro.config cleanup Commit 68651a3 commented out every repro.config setting that was already at its default. The %install section applied its packaging values with 13 sed expressions anchored to exact uncommented lines, so 7 of them quietly became no-ops - sed says nothing when a pattern misses, and the package kept building. Rewriting lines is simply the wrong mechanism now - after the cleanup there are, by design, no uncommented lines to rewrite. Append the values instead, under a "Packaging overrides" heading: later settings win, and a future repro.config edit cannot silently disable an override again. All of them move into the block, including the few whose sed still matched, so there is one mechanism rather than two. TlsDHParamsFilename is the exception and stays a sed. It is an active line in repro.config, and ConfigParse::insertConfigValue() throws on a repeated key, so appending it would abort startup instead. The block warns about this, since uncommenting an example that also appears below fails the same way.
Add repro/dh2048.pem; group .pem with config files in IDE projects
repro.config ships TlsDHParamsFilename = dh2048.pem, but no such file
existed anywhere in the repro source tree. Running repro from a build tree
therefore logged two warnings for every SSL context created
Unable to load DH parameters (required for PFS): BIO_new_file failed to
open file dh2048.pem
Add it to CONFIG_FILES so the existing POST_BUILD step copies it next to the
binary, and to the Windows install(FILES) list, whose stated purpose is
letting repro be run in place. Both were already doing this for users.txt and
repro.config.
The Linux packages deliberately do not ship this file and are unchanged:
debian/repro.postinst and the %post scriptlet in resiprocate.spec.in each
generate a fresh /etc/repro/dh2048.pem at install time, so no two
installations share parameters. The spec already rewrites TlsDHParamsFilename
to that absolute path, and the RPM lists it %ghost. A comment in
repro/CMakeLists.txt records this so the in-tree copy is not mistaken for
something the packages install.
Extend the "Config and Text Files" source_group regex to match .pem as well as
.config and .txt. A .pem is configuration in the same sense those are, and
without this the new file would fall back to the generated project root
instead of grouping with users.txt and repro.config. CMake classifies the
unknown extension as a <None> item, so Visual Studio lists it without trying
to compile it.Clean up repro.config: document defaults, comment out default settings Every setting in the shipped repro.config now ends its documentation block with a "# Default:" line stating the value repro applies when the setting is absent. Settings whose value already matched the default are commented out, since a default-valued assignment carries no information, and their example values were changed to differ from the default so an uncommented line always means something. Fix documentation that was wrong: - OpenSSLCTXSetOptions claimed its flags "are added (logical OR) to any existing flags already set by default". They are not. ReproRunner::setOpenSSLCTXOptionsFromConfig assigns opts = 0 before OR'ing, so the setting replaces the defaults entirely and every required flag must be listed. The default set also includes SSL_OP_NO_COMPRESSION, which was undocumented. - MessageSiloExpirationTime said "Default (259200 seconds = 30 days)". The default is 2592000; 259200 seconds is 3 days. - LogFilename's default is argv[0] + ".log", not a fixed repro.log. - The LogLevel value list omitted EMERG, ALERT and NOTICE, and did not mention that an unrecognized level falls back to DEBUG. Pull the Database settings out of the Misc section into their own section above it, and split the Transport section, which had accumulated the HTTP admin interface, the command server and registration/publication replication under a heading none of them belong to. Add DatabasePath, which was undocumented despite being the fallback for Database<N>Path, the BerkeleyDB location when no indexed database is declared, and the location of the accounting event queues. Normalize formatting: uniform section bars, one blank line between settings, two before a section header, "#Name = value" for every commented example (previously a mix of "#Name" and "# Name"), comments wrapped near 79 columns, no tabs or trailing whitespace. Add a preamble documenting the file syntax, including that trailing '#' comments are not supported - ConfigParse only honours '#' as the first non-whitespace character on a line, so anything after '=' becomes part of the value. Two placeholder values that shipped uncommented are now commented out, the only behavioural change in this commit: - CommandEventTopic = localhost:5672/topic/sip.repro.event, which would open a broker connection on a Qpid Proton build, while every comparable AMQP setting (CommandQueue, RegSyncBrokerTopic) ships commented out. - GeoProximityRequestUriFilter = ^sip:mediaserver.*@mydomain.com$, an example regex naming mydomain.com, effective only if GeoProximityTargetSorting is enabled.
Merge pull request #476 from mykhailopopivniak/preserve-user-part-escaping Problem Uri decodes percent-escapes in the user part at parse time (dataUnescaped into mUser) and re-encodes them on serialization via the process-global user-encoding table. When that table is configured not to escape a character through setUriUserEncoding() — e.g. to let a literal # pass through unescaped — an inbound percent-escaped form of that character is silently unquoted on output: sip:1234%2300@h parses mUser to 1234#00 and then serializes as sip:1234#00@h. The user part no longer round-trips, and a strict RFC 3261 parser rejects the result because # is not allowed unescaped in the user part. Fix Keep the as-received user-part bytes (mUserRaw) whenever the user part contains a percent-escape, and emit them verbatim on encode as long as they still decode (via the same dataUnescaped) to the current mUser. A post-parse modification of the user part breaks that equality and falls back to the existing table-based escaping. The raw bytes are propagated across the copy constructor, operator=, and getAorAsUri, and cleared in setUserAsTelephoneSubscriber. An escaped character now round-trips unchanged regardless of the encoding-table configuration, while an unescaped character is still encoded per the table exactly as before. The common path is unaffected — mUserRaw is only populated when the user part contains a %. Compatibility This also changes serialization under the default encoding table: a user part containing any percent-escape is now emitted with its escaping preserved rather than canonicalized (e.g. sip:12%2a34@h stays 12%2a34 instead of becoming 12*34). This is RFC 3261-equivalent — user parts compare after unescaping and operator==/aorEqual compare the decoded value — but it is a visible on-wire change for every caller, not only those that customize the table via setUriUserEncoding(). Tests testUri extended: an escaped # survives a host retarget, a copy, an assignment, and getAorAsUri; a post-parse modification falls back to table-based escaping; a password-bearing user part and a lowercase escape are preserved byte-for-byte.
Preserve percent-escaping in the URI user part Uri decodes percent-escapes in the user part when it parses a message (dataUnescaped into mUser) and re-encodes them on serialization through the process-global user-encoding table. When that table is configured not to escape a character via setUriUserEncoding() (for example to let a literal '#' pass through), an inbound percent-escaped form of that character is silently unquoted on output: "%23" is decoded to '#' at parse and then written back as a bare '#'. The user part no longer round-trips, and a strict RFC 3261 parser rejects it because '#' is not allowed unescaped there. Keep the as-received user-part bytes (mUserRaw) whenever the user part contains a percent-escape, and emit them verbatim on encode as long as they still decode to the current user value; a later modification of the user part falls back to table-based escaping. The raw bytes are carried across copy, assignment, and getAorAsUri, and cleared when the user part is rewritten via setUserAsTelephoneSubscriber. An escaped character now round-trips unchanged regardless of the encoding-table configuration, while an unescaped character is still encoded per the table as before. Note that this also changes serialization under the default encoding table: a user part containing any percent-escape is now emitted with its escaping preserved rather than canonicalized through the table (e.g. "sip:12%2a34@h" stays "12%2a34" instead of becoming "12*34"). This is RFC 3261-equivalent -- user parts are compared after unescaping and operator==/aorEqual compare the decoded value -- but it is a visible on-wire change for every caller, not only those that customize the table via setUriUserEncoding(). Extend testUri: an escaped '#' survives a host retarget, a copy, an assignment, and getAorAsUri; a post-parse modification falls back to table escaping; a password-bearing user part and a lowercase escape are preserved byte-for-byte.
Fix session-level SDP direction attribute overriding media-level SdpHelper::parseMediaLine resolved a media line's direction with an if/else chain over resipMedia.exists(), testing "sendrecv" first. Medium::exists() falls back to the session level, so a session-level a=sendrecv short-circuited the chain before the medium's own a=sendonly was ever considered, and the media line came out SENDRECV. This inverts RFC 4566: a session-level attribute is only a default for media sections that do not state their own, so a media-level direction must win. Firefox emits a session-level a=sendrecv alongside media-level directions, so any offer from it was mis-parsed. Consult the medium's own attributes first, falling back to the session only when the medium states no direction of its own. Medium::exists() cannot distinguish an inherited attribute from a media-level one, but Session::exists() consults only session-level attributes, so media.exists(k) && !session.exists(k) isolates a media-level declaration. An attribute stated at both levels reports false and falls through to the session, which yields the same direction. Add tests to testSdpHelper covering the full matrix: media overriding a conflicting session, session applying when the medium is silent, media applying when the session is silent, both stating the same direction, neither stating one, and independent per-m-line resolution in a multi-line offer. Misc: CMake: for Visual Studio - Flatten header and source files into a single location - Add .config and .txt files to a "Config and Text Files" folder
Fix stunRand() seeding on unlisted platforms; delegate to Random (Issue #150) stunRand() in rutil/stun/Stun.cxx hand-rolled a platform-specific seed for the C library RNG (rdtsc on x86, gethrtime on Solaris, /dev/random or /dev/urandom on macOS/Linux) and #error'd on any platform not in that list. On OpenBSD/arm64 none of the branches match, so the build failed on the "Need some way to seed the random number generator" #error (resiprocate issue #150). Replace the whole seed-and-generate body with a call to the resip Random class, which is already included in this file (rutil/Random.hxx) and in the same namespace. Random::getRandom() self-initializes, seeds portably from /dev/urandom (present on OpenBSD), and has no platform #error, so this fixes OpenBSD/arm64 and any other unlisted target while removing the fragile per-platform code. Behavior is equivalent (a positive ~31-bit int consumed modulo a range by stunRandomPort), and it now uses the better-maintained generator instead of the self-described "weak" one. All rand()/random()/srand()/srandom() usage in the file was confined to stunRand(), so nothing else depended on its seeding side effect.
Enable Data move semantics; remove obsolete RESIP_HAS_RVALUE_REFS guard resip::Data's move constructor and move-assignment operator were both gated behind RESIP_HAS_RVALUE_REFS, a define the build never sets (it was only listed in build/BuildSystem.txt as a define "outside the build system"). As a result Data had only copy operations on every platform, and every std::move(Data) across the codebase silently copied. The guard was a C++03-era relic: rvalue references are C++11, which the rest of the codebase already requires everywhere (std::move, noexcept, unique_ptr, lambdas, override), so its disabled branch was unreachable on any compiler that can build this project. Remove the guards in Data.hxx and Data.cxx so the move constructor and move-assignment are always compiled, and drop the stale entry from build/BuildSystem.txt. Also fix the move-assignment before enabling it: the hand-rolled operator=(Data&&) overwrote mBuf without freeing the buffer *this already owned (a leak) and left the moved-from object aliasing the stolen buffer with stale size/capacity. It now delegates to takeBuf(), which releases the current buffer, steals the source's (or copies a local-buffer source), and resets the source to a valid empty state. Add move-semantics tests to testData.cxx covering buffer stealing, the move-assign-into-a-heap-owning-target case (the one the old code leaked), local-buffer moves, and a safe self-move. Effect: Data now genuinely moves throughout the codebase (return-by-value, std::move, container growth), and the clang-tidy performance-move-const-arg warnings in reTurn/client/TurnAsyncSocket.cxx are resolved at the source.
Speed up Windows CI: pin, cache, and Debug-only sipXtapi build The Windows build clones and msbuilds sipXtapi from GitHub via ExternalProject on every CI run, building both Debug and Release from an unpinned default-branch HEAD. That is the bulk of Windows CI time. - Pin sipXtapi to a specific upstream commit. The SHA lives in build/sipxtapi-commit.txt (single source of truth for the CMake pin and the CI cache key); CMake reads the first pure-hex line and passes it as ExternalProject GIT_TAG. If no SHA is present it falls back to the default branch HEAD and warns. -DSIPXTAPI_GIT_TAG=<ref> still overrides. This makes the sipXtapi version reproducible and gives CI a stable key. - Build only the configuration CI needs. New SIPXTAPI_CONFIGURATIONS cache variable (default "Debug;Release", unchanged for local/dev) drives the msbuild invocations; the Windows workflow passes -DSIPXTAPI_CONFIGURATIONS=Debug, roughly halving the sipXtapi build. - Cache the built sipXtapi tree. windows-ci.yml adds an actions/cache step for _build/sipXtapi-prefix keyed on the pinned commit + runner OS + toolchain salt. tar preserves the ExternalProject stamp mtimes, so on a hit the build step sees sipXtapi as up to date and skips the clone and msbuild; bumping the pin invalidates the key and rebuilds once. Only the Windows build is affected: the Linux build links against the system libsipxtapi-dev via find_package(sipXtapi) and does not use any of this (noted in build/sipxtapi-commit.txt).
Clean up clang and clang-tidy warnings across the tree Compiler (clang) warnings: - Add RESIP_LAMBDA_CAPTURE_ALL_AND_THIS to rutil/compat.hxx and use it in reTurn/client/TurnAsyncSocket.cxx. It expands to '=, this' on C++20+ and '=' otherwise, so the lambda captures are warning-free from C++11 through C++20 (fixes -Wc++20-extensions without regressing older standards). - reTurn TurnLoadGenClient: return 1 instead of 'false' from main (-Wmain; also corrects a success exit code on a config-parse failure). - Add missing 'override' specifiers in repro pyroute plugin (PyRoutePlugin, PyRouteWorker) and tfm DumUserAgent (-Winconsistent-missing-override). - Mark the sipXtapi include directory SYSTEM in resip/recon so third-party header pragmas (e.g. GCC's -Wclass-memaccess, unknown to clang) stop warning, and so it links into the shared recon library. clang-tidy findings: - rutil/stun Stun.hxx: replace the constructor's memset(this,0,sizeof(*this)) with default member initializers. The struct is not trivially copyable (user-declared destructor), so the memset was undefined behavior; value- initializing each member is equivalent but well defined (bugprone-undefined-memory-manipulation). - bugprone-exception-escape: guard destructors that do cleanup/logging (~TurnAllocation, ~TurnAsyncSocket, ~RequestEntry) with an inner try/catch so they cannot throw; wrap the real main() entry points (stunTestVectors, reConServer) in a function-try-block; suppress the name-heuristic match on the virtual method ReConServerProcess::main with NOLINT (its exceptions are handled by the real main()). clang-tidy third-party noise: - Mark the Python3/PyCXX include directories SYSTEM (rutil, repro pyroute, reConServer) so PyCXX header findings (self-assignment, exception-escape) are no longer reported.
Build Netxx from contrib on Linux; drop libnetxx-dev dependency libnetxx-dev is no longer available on modern distributions and the CI was fetching it from a third-party apt repo (apt.debify.org) that frequently fails. Build Netxx from the in-tree contrib copy instead, as we already do on Windows. - Add USE_CONTRIB_NETXX option (default ON on all platforms) that decouples building Netxx from contrib from the global USE_CONTRIB, so Linux gets contrib Netxx without pulling in every other contrib lib. - Mark the contrib Netxx target position-independent so it links into the shared tfm library on Linux, and relax warnings/-Werror and add -fpermissive for this old third-party code (no-op on MSVC). - Remove libnetxx-dev from the Linux CI apt list and the README package list, and document USE_CONTRIB_NETXX in the README. Also fix two latent build failures surfaced by building the full tree with -Werror on Linux: - TFM and REND use popt unconditionally, so force USE_POPT on when BUILD_TFM/BUILD_REND are enabled (mirrors how BUILD_TFM already forces USE_NETXX/USE_CPPUNIT). Fixes undefined popt symbols when linking sanityTests. - Replace deprecated std::auto_ptr with std::unique_ptr in testSdpHelper.cxx (clean drop-in; createSdpFromResipSdp returns a raw pointer).
.gitattrbutes change to exclude resip/stack/test/*.dat and testTFSM-* fixtures, where CRLF is protocol-significant for the SIP parser tests.
Normalize line endings to LF per .gitattributes. Excludes resip/stack/test/*.dat and testTFSM-* fixtures, where CRLF is protocol-significant for the SIP parser tests.
Merge branch 'master' of https://github.com/resiprocate/resiprocate
Add .gitattributes to normalize line endings across platforms
Merge pull request #475 from resiprocate/no-deprecations rutil: downgrade c-ares deprecation errors to warnings
rutil: downgrade c-ares deprecation errors to warnings
Merge pull request #474 from resiprocate/close-exec Properly set close on exec for BSDs
Fix non-deterministic SIP header encoding order
PR #438 (https://github.com/resiprocate/resiprocate/pull/438) replaced the
fixed-size, enum-indexed header array with a std::vector<HeaderInfo> in the
new KnownHeaders class. That vector stores headers in first-access/insertion
order, and encode()/encodeEmbedded() iterated it directly, so headers were
emitted on the wire in whatever order they happened to be touched first when
building the message -- varying from call to call.
Restore deterministic, enum-declared ordering by iterating both encode() and
encodeEmbedded() over the Headers::Type enum values (0 .. MAX_HEADERS-1) and
looking each type up via KnownHeaders::find(), which is O(1) via mHeaderIndices
and therefore no slower than the old array approach. This matches the behavior
documented in HeaderTypes.hxx ("The Type enum controls the order of output").Properly set close on exec for BSDs
Merge branch 'master' of https://github.com/resiprocate/resiprocate
Improve gain calculations in SipXBridgeMixer Updated `outputGain` and `inputGain` calculations in `SipXBridgeMixer::calculateMixWeightsForParticipant` to use a precise scaling factor based on the bridge's fixed-point unity (1024). This ensures accurate and consistent gain scaling. Added detailed comments to explain the new logic.
Update README with build configuration options Added information about disabling QPID Proton and TFM test framework in the build process.
Add ACL configuration logic to HttpProvider so that implementations can use it
Add new utility method isAddressAllowed to Tuple class - allows passing an ACL of allow/deny addresses/masks to check if tuple is allowed