Skip to content

Commit 650c122

Browse files
authored
Merge pull request #42 from yeoleobun/patch1
fix: handle_client_authenticate not update via branch
2 parents be87dcc + e21419d commit 650c122

File tree

3 files changed

+162
-4
lines changed

3 files changed

+162
-4
lines changed

src/dialog/authenticate.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,9 @@ pub async fn handle_client_authenticate(
249249
qop: auth_qop,
250250
};
251251

252-
let via_header = tx.original.via_header()?.clone();
253-
254-
// update new branch
255-
let mut params = via_header.params().clone()?;
252+
let mut via_header = tx.original.via_header()?.clone().typed()?;
253+
let params = &mut via_header.params;
254+
params.retain(|p| !matches!(p, rsip::Param::Branch(_)));
256255
params.push(make_via_branch());
257256
params.push(Param::Other("rport".into(), None));
258257
new_req.headers_mut().unique_push(via_header.into());

src/dialog/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod test_authenticate;
12
mod test_client_dialog;
23
mod test_dialog_layer;
34
mod test_dialog_states;
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//! Authentication tests
2+
//!
3+
//! Tests for SIP authentication handling, including Via header parameter updates
4+
5+
use crate::dialog::authenticate::{handle_client_authenticate, Credential};
6+
use crate::transaction::{
7+
endpoint::EndpointBuilder,
8+
key::{TransactionKey, TransactionRole},
9+
transaction::Transaction,
10+
};
11+
use crate::transport::TransportLayer;
12+
use rsip::headers::*;
13+
use rsip::prelude::{HeadersExt, ToTypedHeader};
14+
use rsip::{Request, Response, StatusCode};
15+
use tokio_util::sync::CancellationToken;
16+
17+
async fn create_test_endpoint() -> crate::Result<crate::transaction::endpoint::Endpoint> {
18+
let token = CancellationToken::new();
19+
let tl = TransportLayer::new(token.child_token());
20+
let endpoint = EndpointBuilder::new()
21+
.with_user_agent("rsipstack-test")
22+
.with_transport_layer(tl)
23+
.build();
24+
Ok(endpoint)
25+
}
26+
27+
fn create_request_with_branch(branch: &str) -> Request {
28+
Request {
29+
method: rsip::Method::Register,
30+
uri: rsip::Uri::try_from("sip:example.com:5060").unwrap(),
31+
headers: vec![
32+
Via::new(&format!(
33+
"SIP/2.0/UDP alice.example.com:5060;branch={}",
34+
branch
35+
))
36+
.into(),
37+
CSeq::new("1 REGISTER").into(),
38+
From::new("Alice <sip:alice@example.com>;tag=1928301774").into(),
39+
To::new("Bob <sip:bob@example.com>").into(),
40+
CallId::new("a84b4c76e66710@pc33.atlanta.com").into(),
41+
MaxForwards::new("70").into(),
42+
]
43+
.into(),
44+
version: rsip::Version::V2,
45+
body: vec![],
46+
}
47+
}
48+
49+
fn create_401_response() -> Response {
50+
Response {
51+
status_code: StatusCode::Unauthorized,
52+
version: rsip::Version::V2,
53+
headers: vec![
54+
Via::new("SIP/2.0/UDP alice.example.com:5060;branch=z9hG4bKnashds").into(),
55+
CSeq::new("1 REGISTER").into(),
56+
From::new("Alice <sip:alice@example.com>;tag=1928301774").into(),
57+
To::new("Bob <sip:bob@example.com>").into(),
58+
CallId::new("a84b4c76e66710@pc33.atlanta.com").into(),
59+
WwwAuthenticate::new(
60+
r#"Digest realm="example.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", algorithm=MD5, qop="auth""#,
61+
)
62+
.into(),
63+
]
64+
.into(),
65+
body: vec![],
66+
}
67+
}
68+
69+
#[tokio::test]
70+
async fn test_authenticate_via_header_branch_update() -> crate::Result<()> {
71+
let endpoint = create_test_endpoint().await?;
72+
73+
// Create a request with a specific branch parameter
74+
let original_branch = "z9hG4bKoriginal123";
75+
let original_req = create_request_with_branch(original_branch);
76+
77+
// Verify the original request has the branch
78+
let original_via = original_req
79+
.via_header()
80+
.expect("Request should have Via header")
81+
.typed()
82+
.expect("Via header should be parseable");
83+
let original_branch_param = original_via
84+
.params
85+
.iter()
86+
.find(|p| matches!(p, rsip::Param::Branch(_)))
87+
.expect("Original request should have branch parameter");
88+
let original_branch_value = match original_branch_param {
89+
rsip::Param::Branch(b) => b.to_string(),
90+
_ => unreachable!(),
91+
};
92+
assert_eq!(original_branch_value, original_branch);
93+
94+
// Create transaction
95+
let key = TransactionKey::from_request(&original_req, TransactionRole::Client)?;
96+
let tx = Transaction::new_client(key, original_req, endpoint.inner.clone(), None);
97+
98+
// Create 401 response
99+
let resp = create_401_response();
100+
101+
// Create credential
102+
let cred = Credential {
103+
username: "alice".to_string(),
104+
password: "secret123".to_string(),
105+
realm: None,
106+
};
107+
108+
// Call handle_client_authenticate
109+
let new_tx = handle_client_authenticate(2, tx, resp, &cred).await?;
110+
111+
// Verify the new request has updated Via header
112+
let new_via = new_tx
113+
.original
114+
.via_header()
115+
.expect("New request should have Via header")
116+
.typed()
117+
.expect("Via header should be parseable");
118+
119+
// Verify old branch is removed
120+
let old_branch_exists = new_via
121+
.params
122+
.iter()
123+
.any(|p| matches!(p, rsip::Param::Branch(b) if b.to_string() == original_branch_value));
124+
assert!(
125+
!old_branch_exists,
126+
"Old branch parameter should be removed from Via header"
127+
);
128+
129+
// Verify new branch is added (and different from old one)
130+
let new_branch_param = new_via
131+
.params
132+
.iter()
133+
.find(|p| matches!(p, rsip::Param::Branch(_)))
134+
.expect("New request should have a new branch parameter");
135+
let new_branch_value = match new_branch_param {
136+
rsip::Param::Branch(b) => b.to_string(),
137+
_ => unreachable!(),
138+
};
139+
assert_ne!(
140+
new_branch_value, original_branch_value,
141+
"New branch should be different from old branch"
142+
);
143+
assert!(
144+
new_branch_value.starts_with("z9hG4bK"),
145+
"New branch should start with z9hG4bK"
146+
);
147+
148+
// Verify rport parameter is added
149+
let has_rport = new_via.params.iter().any(
150+
|p| matches!(p, rsip::Param::Other(key, _) if key.value().eq_ignore_ascii_case("rport")),
151+
);
152+
assert!(
153+
has_rport,
154+
"Via header should have rport parameter after authentication"
155+
);
156+
157+
Ok(())
158+
}

0 commit comments

Comments
 (0)