From 03ba861433b511a8b7f77f340e06b1c20d94d33e Mon Sep 17 00:00:00 2001
From: Simon Morlat <simon.morlat@linphone.org>
Date: Thu, 2 Jan 2025 05:46:05 +0100
Subject: [PATCH] Fix parsing issue when reading a hostname with a trailling
 dot (which is meaningfull and accepted). Remove "fast-*" variants of SIP URI,
 which were usefull at the time of antlr parser, but no longer now.

---
 src/belle_sip_headers_impl.cc      |  15 ++++++++---
 src/belle_sip_uri_impl.cc          |   7 ++---
 src/sip/sip.txt                    |  41 +++++++++++++++--------------
 src/sip/sip_grammar.belr           | Bin 20550 -> 19797 bytes
 src/sip/sip_parser.cc              |  16 -----------
 tester/belle_sip_base_uri_tester.c |  20 +++++++++++---
 tester/belle_sip_tester_runtime.c  |   2 +-
 7 files changed, 54 insertions(+), 47 deletions(-)

diff --git a/src/belle_sip_headers_impl.cc b/src/belle_sip_headers_impl.cc
index 7c7da15e..5d8f6aa4 100644
--- a/src/belle_sip_headers_impl.cc
+++ b/src/belle_sip_headers_impl.cc
@@ -393,10 +393,17 @@ int belle_sip_header_address_equals(const belle_sip_header_address_t *addr_a,
 }
 
 /*fast header address implementation*/
-typedef belle_sip_header_address_t belle_sip_fast_header_address_t;
-#define belle_sip_fast_header_address_parse belle_sip_header_address_fast_parse
-#define belle_sip_try_fast_header_address_parse belle_sip_header_address_try_fast_parse
-BELLE_SIP_ADDRESS_PARSE(fast_header_address);
+
+belle_sip_header_address_t *belle_sip_header_address_fast_parse(const char *address) {
+	return belle_sip_header_address_parse(address);
+}
+
+/*
+ same as belle_sip_header_address_fast_parse but with no error messagging
+ */
+belle_sip_header_address_t *belle_sip_header_address_try_fast_parse(const char *address) {
+	return belle_sip_header_address_parse(address);
+}
 
 /******************************
  * Allow header inherits from header
diff --git a/src/belle_sip_uri_impl.cc b/src/belle_sip_uri_impl.cc
index 80652a14..39213db4 100644
--- a/src/belle_sip_uri_impl.cc
+++ b/src/belle_sip_uri_impl.cc
@@ -573,6 +573,7 @@ int belle_sip_uri_check_components_from_context(const belle_sip_uri_t *uri,
 		return check_uri_components(uri, &uri_component_use_for_header_refer_to);
 	else return check_uri_components(uri, &uri_component_use_for_external);
 }
-/*fast uri implemenation*/
-typedef belle_sip_uri_t belle_sip_fast_uri_t;
-BELLE_SIP_PARSE(fast_uri);
+
+BELLESIP_EXPORT belle_sip_uri_t *belle_sip_fast_uri_parse(const char *uri) {
+	return belle_sip_uri_parse(uri);
+}
diff --git a/src/sip/sip.txt b/src/sip/sip.txt
index dfc56908..8d09a490 100644
--- a/src/sip/sip.txt
+++ b/src/sip/sip.txt
@@ -1,3 +1,16 @@
+;
+; Modified rules for 'hostname'
+; Indeed, the official definitions is not straightforward and are not compatible with
+; the consume as much as possible belr's Loop behavior, leading to failed recognition of syntaxically correct inputs.
+;
+
+domainlabel    = alphanum / (alphanum *( alphanum / ( *("-") alphanum) ) ) ; modified to avoid an ambiguity
+toplabel       = alphanum / (alphanum *( alphanum / ( *("-") alphanum) ) ) ; modified to avoid an ambiguity & to relax the restriction on the 1st character as stated in RFC 1123 2.1
+fqdn = *( domainlabel "." ) toplabel  ; classic fqdn, for example 'sip.example.org'
+afqdn = *( domainlabel "." )          ; absolute fully qualified domain name, for example 'sip.example.org.' . This exists.
+simplified-hostname = fqdn / afqdn
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Rules from RFC 3261 with IPv6Address fix from RFC 5954, changes from RFC 7235 and changes from RFC 8760 ;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -58,7 +71,7 @@ user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
 password        = *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
 hostport        = host [ ":" port ]
 host            = hostname / IPv4address / IPv6reference
-hostname        = *( domainlabel "." ) toplabel [ "." ]
+hostname        = simplified-hostname ; was initially *( domainlabel "." ) toplabel [ "." ]
 IPv6reference   = "[" IPv6address "]"
 h16             = 1*4HEXDIG
 ls32            = ( h16 ":" h16 ) / IPv4address
@@ -392,16 +405,10 @@ sips-scheme    = "sips:"
 userinfo       = user [ ":" password ] "@"
 uri-parameters = *( (SEMI uri-parameter) / SEMI ) ; allow semi only even if not in the RFC
 uri-parameter  = other-param ; all parameters are considered as other
-domainlabel    = alphanum / (alphanum *( alphanum / ( *("-") alphanum) ) ) ; modified to avoid an ambiguity
-toplabel       = alphanum / (alphanum *( alphanum / (*("-") alphanum ) ) ) ; modified to avoid an ambiguity & to relax the restriction on the 1st character as stated in RFC 1123 2.1
-
-fast-uri      = sip-uri-scheme [ userinfo ] fast-hostport uri-parameters [ headers ]
-fast-hostport = fast-host [ ":" port ]
-fast-host     = fast-hostname / IPv6reference ; IPv4address not included because cannot discriminate quickly between 192.168.0.1 and 192.168.0.com
-fast-hostname = alphanum *( alphanum / "-" / "." )
 
 paramless-uri = sip-uri-scheme [ userinfo ] hostport [ headers ]
 
+
 generic-uri = hier-part / absoluteURI
 
 generic-uri-for-from-to-contact-addr-spec   = scheme ":" opaque-part-for-from-to-contact-addr-spec
@@ -410,24 +417,18 @@ uric-no-slash-for-from-to-contact-addr-spec = unreserved / escaped / ":" / "@" /
 uric-for-from-to-contact-addr-spec          = reserved-for-from-to-contact-addr-spec / unreserved / escaped
 reserved-for-from-to-contact-addr-spec      = ":" / "@" / "&" / "=" / "+" / "$" / "/"
 
+name-addr-with-generic-uri           = [ display-name ] LAQUOT addr-spec-with-generic-uri RAQUOT
+paramless-name-addr-with-generic-uri = [ quoted-display-name ] addr-spec-with-generic-uri
+addr-spec-with-generic-uri           = [ LWS ] ( uri / generic-uri ) [ LWS ]
+paramless-addr-spec-with-generic-uri = paramless-uri / generic-uri-for-from-to-contact-addr-spec
+
 header-address-base = name-addr-with-generic-uri / addr-spec-with-generic-uri
 
 header-address                       = [ LWS ] ( ( name-addr-with-generic-uri *( SEMI generic-param ) )
                                                  / paramless-name-addr-with-generic-uri
                                                  / addr-spec-with-generic-uri )
                                        [ LWS ]
-name-addr-with-generic-uri           = [ display-name ] LAQUOT addr-spec-with-generic-uri RAQUOT
-paramless-name-addr-with-generic-uri = [ quoted-display-name ] addr-spec-with-generic-uri
-addr-spec-with-generic-uri           = [ LWS ] ( uri / generic-uri ) [ LWS ]
-paramless-addr-spec-with-generic-uri = paramless-uri / generic-uri-for-from-to-contact-addr-spec
 
-fast-header-address                       = [ LWS ] ( ( fast-name-addr-with-generic-uri *( SEMI generic-param ) )
-                                                    / fast-paramless-name-addr-with-generic-uri
-                                                    / fast-addr-spec-with-generic-uri )
-                                            [ LWS ]
-fast-name-addr-with-generic-uri           = [ display-name ] LAQUOT fast-addr-spec-with-generic-uri RAQUOT
-fast-paramless-name-addr-with-generic-uri = [ quoted-display-name ] fast-addr-spec-with-generic-uri
-fast-addr-spec-with-generic-uri           = [ LWS ] ( fast-uri / generic-uri ) [ LWS ]
 
 
 ;;;;;;;;;;;;;;;;;;;;;;;
@@ -540,5 +541,5 @@ refresher-param        = "refresher" EQUAL ("uas" / "uac")
 ;;;;;;;;;;;;;;;;;;;;;;;
 
 header-diversion = "Diversion" ":" diversion-spec
-diversion-spec   = ( name-addr-with-generic-uri / paramless-addr-spec-with-generic-uri) *( SEMI diversion-params) ; Initially 1# (name-addr *( ";" diversion_params ))
+diversion-spec   = name-addr-with-generic-uri *( SEMI diversion-params) ; Initially 1# (name-addr *( ";" diversion_params ))
 diversion-params = generic-param
diff --git a/src/sip/sip_grammar.belr b/src/sip/sip_grammar.belr
index feda31855290004522e56bd81779a7cd3c4c520a..532f835dbf11d381237e2365a4a0113092daf51d 100644
GIT binary patch
delta 161
zcmX@Mfbr@q#tm7FY^;fCg(-QHXR9b|&SLE1nq1^AGuc2`d~*noFEa;Fh5-neCeKpQ
z-n>_6H!~x{<UDu9&9lTl@oI2p<QJFZCFZ6wa203f7UX26Wu~U+!Ub7Ctb+WalF1XC
eq&I(8v6oY0!ln*v0T;*-3|wHhY&LZk<OKjC1vMi8

delta 389
zcmcaQi}Bb3#tm7Fn<p@KaWOM7Fifs>iQVkW<IBvPSX?rBouSNReMV{4G$2QJvKONQ
z8w-TFNyq>y_@7^TvV(y5<O!C1Y+MjYE+bV|FcYlbL4a*?mM~X6SS};KxP*a`fq{Vu
zq87x@OUzAW;L0p0Gb>6>OD#&xOHO5gSOb>@Ye>u~$Vkj9&1GN(av2!@0|67*EM_ja
z6eEK!BO`+zBSL>ceo+ZXIXl$G$)^<MVZH#lT})RMtQh20KCYs~!qWT_sP&V%j6C6T
zFg@qobfG@~?WVi=9RDun$u%xYn^y^c;^k*Sb}%#R<N`(o39gd-f}F&p)Eua@fldLt
PaPw}(WVy|*4g$OY6)kf+

diff --git a/src/sip/sip_parser.cc b/src/sip/sip_parser.cc
index d1395163..42bf3f76 100644
--- a/src/sip/sip_parser.cc
+++ b/src/sip/sip_parser.cc
@@ -150,15 +150,6 @@ bellesip::SIP::Parser::Parser() {
 	    ->setCollector("uri-parameter", make_fn(&belle_sip_parameters_add_escaped))
 	    ->setCollector("header", make_fn(&belle_sip_uri_add_escaped_header));
 
-	_parser->setHandler("fast-uri", make_fn(&belle_sip_uri_new))
-	    ->setCollector("sip-uri-scheme", make_fn(&belle_sip_uri_set_scheme))
-	    ->setCollector("user", make_fn(&belle_sip_uri_set_escaped_user))
-	    ->setCollector("password", make_fn(&belle_sip_uri_set_escaped_user_password))
-	    ->setCollector("fast-host", make_fn(&belle_sip_uri_set_host))
-	    ->setCollector("port", make_fn(&belle_sip_uri_set_port))
-	    ->setCollector("uri-parameter", make_fn(&belle_sip_parameters_add_escaped))
-	    ->setCollector("header", make_fn(&belle_sip_uri_add_escaped_header));
-
 	_parser->setHandler("paramless-uri", make_fn(&belle_sip_uri_new))
 	    ->setCollector("sip-uri-scheme", make_fn(&belle_sip_uri_set_scheme))
 	    ->setCollector("user", make_fn(&belle_sip_uri_set_escaped_user))
@@ -174,13 +165,6 @@ bellesip::SIP::Parser::Parser() {
 	    ->setCollector("generic-uri", make_fn(&belle_sip_header_address_set_generic_uri))
 	    ->setCollector("generic-param", make_fn(&belle_sip_parameters_add));
 
-	_parser->setHandler("fast-header-address", make_fn(&belle_sip_header_address_new))
-	    ->setCollector("display-name", make_fn(&belle_sip_header_address_set_quoted_displayname_with_slashes))
-	    ->setCollector("quoted-display-name", make_fn(&belle_sip_header_address_set_quoted_displayname_with_slashes))
-	    ->setCollector("fast-uri", make_fn(&belle_sip_header_address_set_uri))
-	    ->setCollector("generic-uri", make_fn(&belle_sip_header_address_set_generic_uri))
-	    ->setCollector("generic-param", make_fn(&belle_sip_parameters_add));
-
 	_parser->setHandler("generic-uri", make_fn(&belle_generic_uri_new))
 	    ->setCollector("scheme", make_fn(&belle_generic_uri_set_scheme))
 	    ->setCollector("user", make_fn(&belle_generic_uri_set_escaped_user))
diff --git a/tester/belle_sip_base_uri_tester.c b/tester/belle_sip_base_uri_tester.c
index 60cfb6f0..3e3f3c2f 100644
--- a/tester/belle_sip_base_uri_tester.c
+++ b/tester/belle_sip_base_uri_tester.c
@@ -120,6 +120,19 @@ static void test_ip_host(void) {
 	belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri));
 }
 
+static void testAFQDN(void) {
+	/* Notice the trailing dot at the end of the domain name. This is legal. */
+	belle_sip_uri_t *L_uri = belle_sip_uri_parse("sip:bob@example.net.");
+	if (BC_ASSERT_PTR_NOT_NULL(L_uri)) {
+		char *l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri));
+		belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri));
+		L_uri = belle_sip_uri_parse(l_raw_uri);
+		belle_sip_free(l_raw_uri);
+		BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "example.net.");
+		belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri));
+	}
+}
+
 static void test_lr(void) {
 	belle_sip_uri_t *L_uri = belle_sip_uri_parse("sip:192.168.0.1;lr");
 	char *l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri));
@@ -476,10 +489,10 @@ static void test_unescaping_good_chars(void) {
 	belle_sip_free(unescaped);
 
 	char username[10] = {'F', 'r', 'a', 'n', (char)0xc3, (char)0xa7, 'o', 'i', 's', 0x0};
-	unescaped = belle_sip_username_unescape_unnecessary_characters(username);
-	// 'ç' needs to be kept escaped
+	char *escaped = belle_sip_uri_to_escaped_username(username);
+	// 'ç' needs to be escaped
 	const char *expected2 = "Fran%c3%a7ois";
-	BC_ASSERT_STRING_EQUAL(unescaped, expected2);
+	BC_ASSERT_STRING_EQUAL(escaped, expected2);
 	belle_sip_free(unescaped);
 }
 
@@ -517,6 +530,7 @@ static void test_empty_password(void) {
 
 static test_t uri_tests[] = {
     TEST_NO_TAG("Simple URI", testSIMPLEURI),
+    TEST_NO_TAG("Uri with absolute fully qualified domain name", testAFQDN),
     TEST_NO_TAG("Complex URI", testCOMPLEXURI),
     TEST_NO_TAG("Escaped username", test_escaped_username),
     TEST_NO_TAG("Escaped username with bad chars", test_escaping_bad_chars),
diff --git a/tester/belle_sip_tester_runtime.c b/tester/belle_sip_tester_runtime.c
index 03450264..2680aa10 100644
--- a/tester/belle_sip_tester_runtime.c
+++ b/tester/belle_sip_tester_runtime.c
@@ -217,7 +217,7 @@ void belle_sip_tester_after_each(void) {
 		                                       bc_tester_current_suite_name(), bc_tester_current_test_name());
 		belle_sip_object_dump_active_objects();
 		belle_sip_object_flush_active_objects();
-		bc_tester_printf(BELLE_SIP_LOG_MESSAGE, format);
+		bc_tester_printf(BELLE_SIP_LOG_MESSAGE, "%s", format);
 		belle_sip_error("%s", format);
 		belle_sip_free(format);
 
-- 
GitLab