Skip to main content

netget/server/m3ua/
cap.rs

1//! CAMEL Application Part (3GPP TS 29.078) circuit-switched call control: the gsmSSF reports a
2//! call with InitialDP, the gsmSCF (here, the model) instructs it with Connect, Continue,
3//! ReleaseCall and RequestReportBCSMEvent, and armed detection points come back as
4//! EventReportBCSM.
5//!
6//! Application contexts accepted: CAP v2 `{0 4 0 0 1 0 50 1}`, v3 `{0 4 0 0 1 21 3 4}` and v4
7//! `{0 4 0 0 1 23 3 4}` gsmSSF-to-gsmSCF. The operations below have the same tags in all three;
8//! fields that only exist in later phases are reported by tag. The CAP modules use IMPLICIT
9//! TAGS; CHOICEs (bearerCapability, legID) are explicitly tagged.
10
11use super::ber::{self, Tag, Tlv};
12use super::gsm::{self, Address};
13use serde_json::{json, Map, Value};
14
15pub type CapResult<T> = Result<T, String>;
16
17pub const OP_INITIAL_DP: i64 = 0;
18pub const OP_CONNECT: i64 = 20;
19pub const OP_RELEASE_CALL: i64 = 22;
20pub const OP_REQUEST_REPORT_BCSM_EVENT: i64 = 23;
21pub const OP_EVENT_REPORT_BCSM: i64 = 24;
22pub const OP_CONTINUE: i64 = 31;
23pub const OP_ACTIVITY_TEST: i64 = 55;
24
25/// gsmSSF-to-gsmSCF application contexts by phase.
26pub const CONTEXTS: &[(&str, u64, &[u64])] = &[
27    ("capV2-gsmSSF-to-gsmSCF", 2, &[0, 4, 0, 0, 1, 0, 50, 1]),
28    ("capV3-gsmSSF-scfGeneric", 3, &[0, 4, 0, 0, 1, 21, 3, 4]),
29    ("capV4-gsmSSF-scfGeneric", 4, &[0, 4, 0, 0, 1, 23, 3, 4]),
30];
31
32pub fn context_of(oid: &[u64]) -> Option<(&'static str, u64)> {
33    CONTEXTS
34        .iter()
35        .find(|(_, _, o)| *o == oid)
36        .map(|(n, v, _)| (*n, *v))
37}
38
39pub fn context_for_phase(phase: u64) -> Option<&'static [u64]> {
40    CONTEXTS
41        .iter()
42        .find(|(_, v, _)| *v == phase)
43        .map(|(_, _, o)| *o)
44}
45
46pub const OPERATIONS: &[(&str, i64)] = &[
47    ("initialDP", OP_INITIAL_DP),
48    ("connect", OP_CONNECT),
49    ("releaseCall", OP_RELEASE_CALL),
50    ("requestReportBCSMEvent", OP_REQUEST_REPORT_BCSM_EVENT),
51    ("eventReportBCSM", OP_EVENT_REPORT_BCSM),
52    ("continue", OP_CONTINUE),
53    ("activityTest", OP_ACTIVITY_TEST),
54];
55
56pub fn operation_name(code: i64) -> Option<&'static str> {
57    OPERATIONS.iter().find(|(_, c)| *c == code).map(|(n, _)| *n)
58}
59
60pub fn operation_code(name: &str) -> Option<i64> {
61    let norm = |s: &str| s.replace(['-', '_'], "").to_ascii_lowercase();
62    OPERATIONS
63        .iter()
64        .find(|(n, _)| norm(n) == norm(name))
65        .map(|(_, c)| *c)
66}
67
68/// EventTypeBCSM (TS 29.078 section 5.1).
69pub const EVENT_TYPES: &[(&str, i64)] = &[
70    ("collectedInfo", 2),
71    ("analyzedInformation", 3),
72    ("routeSelectFailure", 4),
73    ("oCalledPartyBusy", 5),
74    ("oNoAnswer", 6),
75    ("oAnswer", 7),
76    ("oMidCall", 8),
77    ("oDisconnect", 9),
78    ("oAbandon", 10),
79    ("termAttemptAuthorized", 12),
80    ("tBusy", 13),
81    ("tNoAnswer", 14),
82    ("tAnswer", 15),
83    ("tMidCall", 16),
84    ("tDisconnect", 17),
85    ("tAbandon", 18),
86    ("oTermSeized", 19),
87    ("callAccepted", 27),
88    ("oChangeOfPosition", 28),
89    ("tChangeOfPosition", 29),
90    ("oServiceChange", 30),
91    ("tServiceChange", 31),
92];
93
94pub const MONITOR_MODES: &[&str] = &["interrupted", "notifyAndContinue", "transparent"];
95
96pub const CAP_ERRORS: &[(&str, i64)] = &[
97    ("canceled", 0),
98    ("cancelFailed", 1),
99    ("eTCFailed", 3),
100    ("improperCallerResponse", 4),
101    ("missingCustomerRecord", 6),
102    ("missingParameter", 7),
103    ("parameterOutOfRange", 8),
104    ("requestedInfoError", 10),
105    ("systemFailure", 11),
106    ("taskRefused", 12),
107    ("unavailableResource", 13),
108    ("unexpectedComponentSequence", 14),
109    ("unexpectedDataValue", 15),
110    ("unexpectedParameter", 16),
111    ("unknownLegID", 17),
112];
113
114pub fn error_name(code: i64) -> String {
115    CAP_ERRORS
116        .iter()
117        .find(|(_, c)| *c == code)
118        .map(|(n, _)| n.to_string())
119        .unwrap_or_else(|| format!("error{code}"))
120}
121
122fn event_name(v: i64) -> Value {
123    EVENT_TYPES
124        .iter()
125        .find(|(_, c)| *c == v)
126        .map(|(n, _)| json!(n))
127        .unwrap_or_else(|| json!(v))
128}
129
130fn event_value(v: &Value, field: &str) -> CapResult<i64> {
131    if let Some(n) = v.as_i64() {
132        if EVENT_TYPES.iter().any(|(_, c)| *c == n) {
133            return Ok(n);
134        }
135    }
136    let s = v.as_str().unwrap_or_default();
137    EVENT_TYPES
138        .iter()
139        .find(|(n, _)| n.eq_ignore_ascii_case(s))
140        .map(|(_, c)| *c)
141        .ok_or_else(|| {
142            format!(
143                "{field} must be one of {}",
144                EVENT_TYPES
145                    .iter()
146                    .map(|(n, _)| *n)
147                    .collect::<Vec<_>>()
148                    .join(", ")
149            )
150        })
151}
152
153fn ctx(n: u32) -> Tag {
154    Tag::context(n, false)
155}
156
157fn ctxc(n: u32) -> Tag {
158    Tag::context(n, true)
159}
160
161fn find(kids: &[Tlv], tag: Tag) -> Option<&Tlv> {
162    kids.iter().find(|c| c.tag == tag)
163}
164
165/// Decode an Invoke's argument.
166pub fn decode_argument(code: i64, p: Option<&Tlv>) -> CapResult<Value> {
167    match code {
168        OP_CONTINUE | OP_ACTIVITY_TEST => return Ok(json!({})),
169        OP_RELEASE_CALL => {
170            let p = p.ok_or("releaseCall without a cause")?;
171            return Ok(json!({"cause": gsm::cause_decode(&p.value)}));
172        }
173        _ => {}
174    }
175    let p = p.ok_or("the operation's argument is missing")?;
176    let kids = &p.children;
177    let mut out = Map::new();
178    let used: Vec<Tag> = match code {
179        OP_INITIAL_DP => {
180            out.insert(
181                "service_key".into(),
182                json!(find(kids, ctx(0))
183                    .ok_or("serviceKey [0] missing")?
184                    .as_i64()
185                    .map_err(|e| e.0)?),
186            );
187            let num = |n: u32| find(kids, ctx(n)).map(|t| gsm::isup_number_decode(&t.value));
188            out.insert("called_party_number".into(), json!(num(2)));
189            out.insert("calling_party_number".into(), json!(num(3)));
190            out.insert(
191                "calling_partys_category".into(),
192                json!(find(kids, ctx(5)).and_then(|t| t.value.first().copied())),
193            );
194            out.insert("location_number".into(), json!(num(10)));
195            out.insert(
196                "event_type_bcsm".into(),
197                find(kids, ctx(28))
198                    .and_then(|t| t.as_i64().ok())
199                    .map(event_name)
200                    .unwrap_or(Value::Null),
201            );
202            out.insert(
203                "imsi".into(),
204                json!(find(kids, ctx(50)).map(|t| gsm::tbcd_decode(&t.value))),
205            );
206            out.insert(
207                "call_reference_number".into(),
208                json!(find(kids, ctx(54)).map(|t| ber::hex(&t.value))),
209            );
210            let addr = |n: u32| {
211                find(kids, ctx(n))
212                    .and_then(|t| Address::decode(&t.value).ok())
213                    .map(|a| a.to_json())
214            };
215            out.insert("msc_address".into(), json!(addr(55)));
216            out.insert("called_party_bcd_number".into(), json!(addr(56)));
217            out.insert(
218                "location_information_present".into(),
219                json!(find(kids, ctxc(52)).is_some()),
220            );
221            vec![
222                ctx(0),
223                ctx(2),
224                ctx(3),
225                ctx(5),
226                ctx(10),
227                ctx(28),
228                ctx(50),
229                ctxc(52),
230                ctx(54),
231                ctx(55),
232                ctx(56),
233            ]
234        }
235        OP_CONNECT => {
236            let dra = find(kids, ctxc(0)).ok_or("destinationRoutingAddress [0] missing")?;
237            out.insert(
238                "destination_routing_address".into(),
239                json!(dra
240                    .children
241                    .iter()
242                    .map(|n| gsm::isup_number_decode(&n.value))
243                    .collect::<Vec<_>>()),
244            );
245            out.insert(
246                "suppression_of_announcement".into(),
247                json!(find(kids, ctx(55)).is_some()),
248            );
249            vec![ctxc(0), ctx(55)]
250        }
251        OP_REQUEST_REPORT_BCSM_EVENT => {
252            let events = find(kids, ctxc(0)).ok_or("bcsmEvents [0] missing")?;
253            if events.children.len() > 30 {
254                return Err("more than 30 BCSM events".into());
255            }
256            let mut list = Vec::new();
257            for e in &events.children {
258                let ev = find(&e.children, ctx(0)).and_then(|t| t.as_i64().ok());
259                let mode = find(&e.children, ctx(1)).and_then(|t| t.as_i64().ok());
260                let leg = find(&e.children, ctxc(2))
261                    .and_then(|l| l.children.first())
262                    .map(|c| {
263                        json!({
264                            "side": if c.tag.number == 0 { "sending" } else { "receiving" },
265                            "leg_type": c.value.first().copied(),
266                        })
267                    });
268                list.push(json!({
269                    "event_type_bcsm": ev.map(event_name),
270                    "monitor_mode": mode.and_then(|m| MONITOR_MODES.get(m as usize).copied()),
271                    "leg_id": leg,
272                }));
273            }
274            out.insert("bcsm_events".into(), json!(list));
275            vec![ctxc(0)]
276        }
277        OP_EVENT_REPORT_BCSM => {
278            out.insert(
279                "event_type_bcsm".into(),
280                find(kids, ctx(0))
281                    .and_then(|t| t.as_i64().ok())
282                    .map(event_name)
283                    .ok_or("eventTypeBCSM [0] missing")?,
284            );
285            out.insert(
286                "leg_id".into(),
287                json!(find(kids, ctxc(3))
288                    .and_then(|l| l.children.first())
289                    .and_then(|c| c.value.first().copied())),
290            );
291            out.insert(
292                "message_type".into(),
293                json!(match find(kids, ctxc(4))
294                    .and_then(|m| m.ctx(0))
295                    .and_then(|t| t.as_i64().ok())
296                {
297                    Some(1) => "notification",
298                    _ => "request",
299                }),
300            );
301            out.insert(
302                "event_specific_information_present".into(),
303                json!(find(kids, ctxc(2)).is_some()),
304            );
305            vec![ctx(0), ctxc(2), ctxc(3), ctxc(4)]
306        }
307        other => return Err(format!("CAP operation {other} is not implemented")),
308    };
309    let rest: Vec<String> = kids
310        .iter()
311        .filter(|c| !used.contains(&c.tag))
312        .map(|c| c.tag.label())
313        .collect();
314    out.insert("uninterpreted".into(), json!(rest));
315    Ok(Value::Object(out))
316}
317
318/// Encode an Invoke's argument. `None` for the operations that take none.
319pub fn encode_argument(code: i64, v: &Value) -> CapResult<Option<Vec<u8>>> {
320    Ok(Some(match code {
321        OP_CONTINUE | OP_ACTIVITY_TEST => return Ok(None),
322        OP_RELEASE_CALL => {
323            let cause = v
324                .get("cause")
325                .cloned()
326                .unwrap_or(json!("normal_unspecified"));
327            ber::tlv(Tag::OCTET_STRING, &gsm::cause_encode(&cause)?)
328        }
329        OP_CONNECT => {
330            let dra = v
331                .get("destination_routing_address")
332                .ok_or("destination_routing_address is required")?;
333            let first = match dra {
334                Value::Array(a) => {
335                    if a.len() != 1 {
336                        return Err(
337                            "destination_routing_address holds exactly one number in CAP".into(),
338                        );
339                    }
340                    a[0].clone()
341                }
342                other => other.clone(),
343            };
344            let mut parts = vec![ber::seq(
345                ctxc(0),
346                &[ber::tlv(
347                    Tag::OCTET_STRING,
348                    &gsm::isup_number_encode(&first, "destination_routing_address", false)?,
349                )],
350            )];
351            if v.get("suppression_of_announcement")
352                .and_then(Value::as_bool)
353                == Some(true)
354            {
355                parts.push(ber::tlv(ctx(55), &[]));
356            }
357            ber::seq(Tag::SEQUENCE, &parts)
358        }
359        OP_REQUEST_REPORT_BCSM_EVENT => {
360            let events = v
361                .get("bcsm_events")
362                .and_then(Value::as_array)
363                .ok_or("bcsm_events is required (a list)")?;
364            if events.is_empty() || events.len() > 30 {
365                return Err("bcsm_events must hold 1..30 events".into());
366            }
367            let mut list = Vec::new();
368            for (i, e) in events.iter().enumerate() {
369                let field = format!("bcsm_events[{i}]");
370                let ev = event_value(
371                    e.get("event_type_bcsm").unwrap_or(&Value::Null),
372                    &format!("{field}.event_type_bcsm"),
373                )?;
374                let mode = e
375                    .get("monitor_mode")
376                    .and_then(Value::as_str)
377                    .unwrap_or("notifyAndContinue");
378                let m = MONITOR_MODES
379                    .iter()
380                    .position(|x| *x == mode)
381                    .ok_or_else(|| {
382                        format!(
383                            "{field}.monitor_mode must be one of {}",
384                            MONITOR_MODES.join(", ")
385                        )
386                    })?;
387                let mut parts = vec![ber::integer(ctx(0), ev), ber::integer(ctx(1), m as i64)];
388                // Leg 2 (the called party) for the answer/busy/no-answer family; leg 1 otherwise,
389                // unless the model names it.
390                let leg = e.get("leg").and_then(Value::as_u64).unwrap_or(match ev {
391                    5..=7 | 9 => 2,
392                    _ => 1,
393                });
394                if !(1..=2).contains(&leg) {
395                    return Err(format!("{field}.leg must be 1 or 2"));
396                }
397                // In a request the SCF names the leg as sendingSideID [0]; the gsmSSF reports
398                // back with receivingSideID.
399                if ev != 2 && ev != 3 {
400                    parts.push(ber::seq(ctxc(2), &[ber::tlv(ctx(0), &[leg as u8])]));
401                }
402                list.push(ber::seq(Tag::SEQUENCE, &parts));
403            }
404            ber::seq(Tag::SEQUENCE, &[ber::seq(ctxc(0), &list)])
405        }
406        OP_EVENT_REPORT_BCSM => {
407            let ev = event_value(
408                v.get("event_type_bcsm").unwrap_or(&Value::Null),
409                "event_type_bcsm",
410            )?;
411            let leg = v
412                .get("leg")
413                .and_then(Value::as_u64)
414                .unwrap_or(2)
415                .clamp(1, 2) as u8;
416            let notification =
417                v.get("message_type").and_then(Value::as_str) == Some("notification");
418            ber::seq(
419                Tag::SEQUENCE,
420                &[
421                    ber::integer(ctx(0), ev),
422                    ber::seq(ctxc(3), &[ber::tlv(ctx(1), &[leg])]),
423                    ber::seq(ctxc(4), &[ber::integer(ctx(0), notification as i64)]),
424                ],
425            )
426        }
427        OP_INITIAL_DP => {
428            let sk = v
429                .get("service_key")
430                .and_then(Value::as_i64)
431                .filter(|k| (0..=2147483647).contains(k))
432                .ok_or("service_key is required (0..2147483647)")?;
433            let mut parts = vec![ber::integer(ctx(0), sk)];
434            if let Some(n) = v.get("called_party_number").filter(|x| !x.is_null()) {
435                parts.push(ber::tlv(
436                    ctx(2),
437                    &gsm::isup_number_encode(n, "called_party_number", false)?,
438                ));
439            }
440            if let Some(n) = v.get("calling_party_number").filter(|x| !x.is_null()) {
441                parts.push(ber::tlv(
442                    ctx(3),
443                    &gsm::isup_number_encode(n, "calling_party_number", true)?,
444                ));
445            }
446            parts.push(ber::tlv(
447                ctx(5),
448                &[v.get("calling_partys_category")
449                    .and_then(Value::as_u64)
450                    .unwrap_or(10)
451                    .min(255) as u8],
452            ));
453            let ev = match v.get("event_type_bcsm") {
454                Some(e) => event_value(e, "event_type_bcsm")?,
455                None => 2,
456            };
457            parts.push(ber::integer(ctx(28), ev));
458            if let Some(i) = v.get("imsi").and_then(Value::as_str) {
459                parts.push(ber::tlv(ctx(50), &gsm::imsi_encode(i)?));
460            }
461            if let Some(a) = v.get("msc_address").filter(|x| !x.is_null()) {
462                let a = Address::from_json(a, "msc_address")?;
463                parts.push(ber::tlv(ctx(55), &a.encode(gsm::MAX_ISDN_ADDRESS_DIGITS)?));
464            }
465            if let Some(a) = v.get("called_party_bcd_number").filter(|x| !x.is_null()) {
466                let a = Address::from_json(a, "called_party_bcd_number")?;
467                parts.push(ber::tlv(ctx(56), &a.encode(40)?));
468            }
469            ber::seq(Tag::SEQUENCE, &parts)
470        }
471        other => return Err(format!("CAP operation {other} is not implemented")),
472    }))
473}