1use super::ber::{self, Tag, Tlv};
26use super::gsm::{self, Address};
27use serde_json::{json, Map, Value};
28
29pub type MapResult<T> = Result<T, String>;
30
31pub fn acn(ac: u64, version: u64) -> Vec<u64> {
33 vec![0, 4, 0, 0, 1, 0, ac, version]
34}
35
36pub const CONTEXTS: &[(&str, u64)] = &[
38 ("networkLocUpContext", 1),
39 ("locationInfoRetrievalContext", 5),
40 ("infoRetrievalContext", 14),
41 ("subscriberDataMngtContext", 16),
42 ("networkUnstructuredSsContext", 19),
43 ("shortMsgGatewayContext", 20),
44 ("shortMsgMORelayContext", 21),
45 ("shortMsgMTRelayContext", 25),
46 ("anyTimeEnquiryContext", 29),
47];
48
49pub fn context_of(oid: &[u64]) -> Option<(&'static str, u64)> {
51 if oid.len() != 8 || oid[..6] != [0, 4, 0, 0, 1, 0] {
52 return None;
53 }
54 CONTEXTS
57 .iter()
58 .find(|(_, c)| *c == oid[6])
59 .map(|(n, _)| (*n, oid[7]))
60}
61
62pub struct Operation {
64 pub name: &'static str,
65 pub code: i64,
66 pub context: u64,
67 pub version: u64,
68 pub has_result: bool,
70}
71
72pub const OPERATIONS: &[Operation] = &[
73 Operation {
74 name: "updateLocation",
75 code: 2,
76 context: 1,
77 version: 3,
78 has_result: true,
79 },
80 Operation {
81 name: "sendRoutingInfo",
82 code: 22,
83 context: 5,
84 version: 3,
85 has_result: true,
86 },
87 Operation {
88 name: "mt-forwardSM",
89 code: 44,
90 context: 25,
91 version: 3,
92 has_result: true,
93 },
94 Operation {
95 name: "sendRoutingInfoForSM",
96 code: 45,
97 context: 20,
98 version: 3,
99 has_result: true,
100 },
101 Operation {
102 name: "mo-forwardSM",
103 code: 46,
104 context: 21,
105 version: 3,
106 has_result: true,
107 },
108 Operation {
109 name: "processUnstructuredSS-Request",
110 code: 59,
111 context: 19,
112 version: 2,
113 has_result: true,
114 },
115 Operation {
116 name: "unstructuredSS-Request",
117 code: 60,
118 context: 19,
119 version: 2,
120 has_result: true,
121 },
122 Operation {
123 name: "unstructuredSS-Notify",
124 code: 61,
125 context: 19,
126 version: 2,
127 has_result: true,
128 },
129 Operation {
130 name: "anyTimeInterrogation",
131 code: 71,
132 context: 29,
133 version: 3,
134 has_result: true,
135 },
136];
137
138pub fn operation_by_code(code: i64) -> Option<&'static Operation> {
139 OPERATIONS.iter().find(|o| o.code == code)
140}
141
142pub fn operation_by_name(name: &str) -> Option<&'static Operation> {
143 let norm = |s: &str| s.replace(['-', '_'], "").to_ascii_lowercase();
144 OPERATIONS.iter().find(|o| norm(o.name) == norm(name))
145}
146
147pub fn operation_names() -> String {
148 OPERATIONS
149 .iter()
150 .map(|o| o.name)
151 .collect::<Vec<_>>()
152 .join(", ")
153}
154
155pub const ERRORS: &[(&str, i64)] = &[
157 ("unknownSubscriber", 1),
158 ("unknownMSC", 3),
159 ("unidentifiedSubscriber", 5),
160 ("absentSubscriberSM", 6),
161 ("unknownEquipment", 7),
162 ("roamingNotAllowed", 8),
163 ("illegalSubscriber", 9),
164 ("bearerServiceNotProvisioned", 10),
165 ("teleserviceNotProvisioned", 11),
166 ("illegalEquipment", 12),
167 ("callBarred", 13),
168 ("forwardingViolation", 14),
169 ("cug-Reject", 15),
170 ("illegalSS-Operation", 16),
171 ("ss-ErrorStatus", 17),
172 ("ss-NotAvailable", 18),
173 ("ss-SubscriptionViolation", 19),
174 ("ss-Incompatibility", 20),
175 ("facilityNotSupported", 21),
176 ("absentSubscriber", 27),
177 ("subscriberBusyForMT-SMS", 31),
178 ("sm-DeliveryFailure", 32),
179 ("messageWaitingListFull", 33),
180 ("systemFailure", 34),
181 ("dataMissing", 35),
182 ("unexpectedDataValue", 36),
183 ("noRoamingNumberAvailable", 39),
184 ("busySubscriber", 45),
185 ("noSubscriberReply", 46),
186 ("ati-NotAllowed", 49),
187 ("resourceLimitation", 51),
188 ("unknownAlphabet", 71),
189 ("ussd-Busy", 72),
190];
191
192pub const ERROR_SYSTEM_FAILURE: i64 = 34;
193
194pub fn error_name(code: i64) -> String {
195 ERRORS
196 .iter()
197 .find(|(_, c)| *c == code)
198 .map(|(n, _)| n.to_string())
199 .unwrap_or_else(|| format!("error{code}"))
200}
201
202pub fn error_code(v: &Value) -> MapResult<i64> {
204 if let Some(n) = v.as_i64() {
205 if ERRORS.iter().any(|(_, c)| *c == n) {
206 return Ok(n);
207 }
208 return Err(format!("MAP error code {n} is not one NetGet names"));
209 }
210 let s = v.as_str().ok_or("error must be a MAP error name or code")?;
211 let norm = |x: &str| x.replace(['-', '_'], "").to_ascii_lowercase();
212 ERRORS
213 .iter()
214 .find(|(n, _)| norm(n) == norm(s))
215 .map(|(_, c)| *c)
216 .ok_or_else(|| {
217 format!(
218 "{s:?} is not a MAP error; use one of {}",
219 ERRORS
220 .iter()
221 .map(|(n, _)| *n)
222 .collect::<Vec<_>>()
223 .join(", ")
224 )
225 })
226}
227
228fn addr(t: &Tlv) -> Value {
233 Address::decode(&t.value)
234 .map(|a| a.to_json())
235 .unwrap_or_else(|e| json!({ "error": e }))
236}
237
238fn imsi(t: &Tlv) -> Value {
239 json!(gsm::tbcd_decode(&t.value))
240}
241
242fn address_tlv(tag: Tag, v: &Value, field: &str, max_digits: usize) -> MapResult<Vec<u8>> {
243 let a = Address::from_json(v, field)?;
244 Ok(ber::tlv(tag, &a.encode(max_digits)?))
245}
246
247fn required<'a>(v: &'a Value, field: &str) -> MapResult<&'a Value> {
248 v.get(field)
249 .filter(|x| !x.is_null())
250 .ok_or_else(|| format!("{field} is required"))
251}
252
253fn imsi_tlv(tag: Tag, v: &Value, field: &str) -> MapResult<Vec<u8>> {
254 let s = v
255 .as_str()
256 .ok_or_else(|| format!("{field} must be a digit string"))?;
257 Ok(ber::tlv(tag, &gsm::imsi_encode(s)?))
258}
259
260fn seq_children(t: &Tlv, what: &str) -> MapResult<Vec<Tlv>> {
261 if t.tag != Tag::SEQUENCE && !(t.tag.constructed) {
262 return Err(format!("{what} is {}, expected a SEQUENCE", t.tag.label()));
263 }
264 Ok(t.children.clone())
265}
266
267fn leftovers(children: &[Tlv], used: &[Tag]) -> Vec<String> {
269 children
270 .iter()
271 .filter(|c| !used.contains(&c.tag))
272 .map(|c| c.tag.label())
273 .collect()
274}
275
276fn ctx(n: u32) -> Tag {
277 Tag::context(n, false)
278}
279
280fn ctxc(n: u32) -> Tag {
281 Tag::context(n, true)
282}
283
284pub fn decode_argument(code: i64, p: Option<&Tlv>) -> MapResult<Value> {
291 let Some(p) = p else {
292 return Err("the operation's argument is missing".into());
293 };
294 let kids = seq_children(p, "argument")?;
295 let find = |tag: Tag| kids.iter().find(|c| c.tag == tag);
296 let mut out = Map::new();
297 let used: Vec<Tag> = match code {
298 45 => {
299 out.insert(
300 "msisdn".into(),
301 find(ctx(0)).map(addr).ok_or("msisdn [0] missing")?,
302 );
303 out.insert(
304 "sm_rp_pri".into(),
305 json!(find(ctx(1))
306 .map(|b| b.as_bool())
307 .transpose()
308 .map_err(|e| e.0)?),
309 );
310 out.insert(
311 "service_centre_address".into(),
312 find(ctx(2))
313 .map(addr)
314 .ok_or("serviceCentreAddress [2] missing")?,
315 );
316 out.insert(
317 "gprs_support_indicator".into(),
318 json!(find(ctx(7)).is_some()),
319 );
320 vec![ctx(0), ctx(1), ctx(2), ctx(7)]
321 }
322 71 => {
323 let ident = find(ctxc(0)).ok_or("subscriberIdentity [0] missing")?;
324 let sub = match ident.children.first() {
325 Some(c) if c.tag == ctx(0) => json!({"imsi": imsi(c)}),
326 Some(c) if c.tag == ctx(1) => json!({"msisdn": addr(c)}),
327 _ => return Err("subscriberIdentity is neither imsi [0] nor msisdn [1]".into()),
328 };
329 out.insert("subscriber".into(), sub);
330 let mut requested = Vec::new();
331 if let Some(ri) = find(ctxc(1)) {
332 for (n, name) in [
333 (0, "location_information"),
334 (1, "subscriber_state"),
335 (3, "current_location"),
336 (5, "ms_classmark"),
337 (6, "imei"),
338 (7, "mnp_requested_info"),
339 ] {
340 if ri.children.iter().any(|c| c.tag.is_context(n)) {
341 requested.push(json!(name));
342 }
343 }
344 }
345 out.insert("requested_info".into(), json!(requested));
346 out.insert(
347 "gsm_scf_address".into(),
348 find(ctx(3)).map(addr).unwrap_or(Value::Null),
349 );
350 vec![ctxc(0), ctxc(1), ctx(3)]
351 }
352 59..=61 => {
353 let (dcs, text) = ussd_fields(&kids)?;
354 out.insert("ussd_data_coding_scheme".into(), json!(dcs));
355 out.insert("ussd_text".into(), text);
356 out.insert(
357 "msisdn".into(),
358 find(ctx(0)).map(addr).unwrap_or(Value::Null),
359 );
360 vec![Tag::OCTET_STRING, ctx(0)]
361 }
362 44 | 46 => {
363 let da = kids.first().ok_or("sm-RP-DA missing")?;
364 let oa = kids.get(1).ok_or("sm-RP-OA missing")?;
365 let ui = kids
366 .get(2)
367 .filter(|u| u.tag == Tag::OCTET_STRING)
368 .ok_or("sm-RP-UI missing")?;
369 out.insert("sm_rp_da".into(), sm_rp_da(da));
370 out.insert("sm_rp_oa".into(), sm_rp_oa(oa));
371 out.insert(
372 "sms".into(),
373 gsm::sms_tpdu_decode(&ui.value, code == 46)
374 .unwrap_or_else(|e| json!({ "error": e })),
375 );
376 if code == 44 {
377 out.insert(
378 "more_messages_to_send".into(),
379 json!(kids.iter().skip(3).any(|c| c.tag == Tag::NULL)),
380 );
381 } else if let Some(i) = kids.iter().skip(3).find(|c| c.tag == Tag::OCTET_STRING) {
382 out.insert("imsi".into(), imsi(i));
383 }
384 let mut u = vec![da.tag, oa.tag, Tag::OCTET_STRING, Tag::NULL];
385 u.dedup();
386 u
387 }
388 22 => {
389 out.insert(
390 "msisdn".into(),
391 find(ctx(0)).map(addr).ok_or("msisdn [0] missing")?,
392 );
393 out.insert(
394 "interrogation_type".into(),
395 json!(match find(ctx(3))
396 .map(|t| t.as_i64())
397 .transpose()
398 .map_err(|e| e.0)?
399 {
400 Some(0) => "basicCall",
401 Some(1) => "forwarding",
402 _ => "unknown",
403 }),
404 );
405 out.insert(
406 "gmsc_or_gsm_scf_address".into(),
407 find(ctx(6)).map(addr).unwrap_or(Value::Null),
408 );
409 out.insert("or_interrogation".into(), json!(find(ctx(4)).is_some()));
410 vec![ctx(0), ctx(3), ctx(6), ctx(4)]
411 }
412 2 => {
413 out.insert(
414 "imsi".into(),
415 find(Tag::OCTET_STRING).map(imsi).ok_or("imsi missing")?,
416 );
417 out.insert(
418 "msc_number".into(),
419 find(ctx(1)).map(addr).ok_or("msc-Number [1] missing")?,
420 );
421 out.insert(
422 "vlr_number".into(),
423 kids.iter()
424 .filter(|c| c.tag == Tag::OCTET_STRING)
425 .nth(1)
426 .map(addr)
427 .ok_or("vlr-Number missing")?,
428 );
429 out.insert(
430 "lmsi".into(),
431 find(ctx(10))
432 .map(|l| json!(ber::hex(&l.value)))
433 .unwrap_or(Value::Null),
434 );
435 vec![Tag::OCTET_STRING, ctx(1), ctx(10)]
436 }
437 other => return Err(format!("operation {other} is not implemented")),
438 };
439 out.insert("uninterpreted".into(), json!(leftovers(&kids, &used)));
440 Ok(Value::Object(out))
441}
442
443fn ussd_fields(kids: &[Tlv]) -> MapResult<(u8, Value)> {
444 let mut octets = kids.iter().filter(|c| c.tag == Tag::OCTET_STRING);
445 let dcs = octets
446 .next()
447 .and_then(|d| d.value.first().copied())
448 .ok_or("ussd-DataCodingScheme missing")?;
449 let s = octets.next().ok_or("ussd-String missing")?;
450 if s.value.len() > gsm::MAX_USSD_OCTETS {
451 return Err(format!(
452 "ussd-String of {} octets (at most {})",
453 s.value.len(),
454 gsm::MAX_USSD_OCTETS
455 ));
456 }
457 Ok((dcs, json!(gsm::ussd_decode(dcs, &s.value))))
458}
459
460fn sm_rp_da(t: &Tlv) -> Value {
461 match t.tag.number {
462 0 => json!({"imsi": imsi(t)}),
463 1 => json!({"lmsi": ber::hex(&t.value)}),
464 4 => json!({"service_centre_address": addr(t)}),
465 5 => json!({"none": true}),
466 n => json!({"unknown_choice": n}),
467 }
468}
469
470fn sm_rp_oa(t: &Tlv) -> Value {
471 match t.tag.number {
472 2 => json!({"msisdn": addr(t)}),
473 4 => json!({"service_centre_address": addr(t)}),
474 5 => json!({"none": true}),
475 n => json!({"unknown_choice": n}),
476 }
477}
478
479pub fn encode_argument(code: i64, v: &Value) -> MapResult<Vec<u8>> {
481 let parts: Vec<Vec<u8>> = match code {
482 45 => vec![
483 address_tlv(
484 ctx(0),
485 required(v, "msisdn")?,
486 "msisdn",
487 gsm::MAX_ISDN_ADDRESS_DIGITS,
488 )?,
489 ber::tlv(
490 ctx(1),
491 &[
492 if v.get("sm_rp_pri").and_then(Value::as_bool).unwrap_or(true) {
493 0xff
494 } else {
495 0
496 },
497 ],
498 ),
499 address_tlv(
500 ctx(2),
501 required(v, "service_centre_address")?,
502 "service_centre_address",
503 gsm::MAX_ADDRESS_DIGITS,
504 )?,
505 ],
506 71 => {
507 let sub = required(v, "subscriber")?;
508 let ident = if let Some(i) = sub.get("imsi") {
509 imsi_tlv(ctx(0), i, "subscriber.imsi")?
510 } else if let Some(m) = sub.get("msisdn") {
511 address_tlv(ctx(1), m, "subscriber.msisdn", gsm::MAX_ISDN_ADDRESS_DIGITS)?
512 } else {
513 return Err("subscriber must be {\"msisdn\": ...} or {\"imsi\": ...}".into());
514 };
515 let wanted: Vec<String> = match v.get("requested_info") {
516 Some(Value::Array(a)) => a
517 .iter()
518 .filter_map(|x| x.as_str().map(str::to_string))
519 .collect(),
520 _ => vec!["location_information".into(), "subscriber_state".into()],
521 };
522 let mut ri = Vec::new();
523 for (n, name) in [
524 (0u32, "location_information"),
525 (1, "subscriber_state"),
526 (3, "current_location"),
527 (5, "ms_classmark"),
528 (6, "imei"),
529 ] {
530 if wanted.iter().any(|w| w == name) {
531 ri.push(ber::tlv(ctx(n), &[]));
532 }
533 }
534 if ri.is_empty() {
535 return Err("requested_info names nothing NetGet can request".into());
536 }
537 vec![
538 ber::seq(ctxc(0), &[ident]),
539 ber::seq(ctxc(1), &ri),
540 address_tlv(
541 ctx(3),
542 required(v, "gsm_scf_address")?,
543 "gsm_scf_address",
544 gsm::MAX_ISDN_ADDRESS_DIGITS,
545 )?,
546 ]
547 }
548 59..=61 => {
549 let text = v
550 .get("ussd_text")
551 .or_else(|| v.get("text"))
552 .and_then(Value::as_str)
553 .ok_or("ussd_text is required")?;
554 let (dcs, octets) = gsm::ussd_encode(text)?;
555 let mut p = vec![
556 ber::tlv(Tag::OCTET_STRING, &[dcs]),
557 ber::tlv(Tag::OCTET_STRING, &octets),
558 ];
559 if let Some(m) = v.get("msisdn").filter(|m| !m.is_null()) {
560 p.push(address_tlv(
561 ctx(0),
562 m,
563 "msisdn",
564 gsm::MAX_ISDN_ADDRESS_DIGITS,
565 )?);
566 }
567 p
568 }
569 46 => {
570 let da = required(v, "sm_rp_da")?;
571 let oa = required(v, "sm_rp_oa")?;
572 vec![
573 encode_sm_rp_da(da)?,
574 encode_sm_rp_oa(oa)?,
575 ber::tlv(
576 Tag::OCTET_STRING,
577 &gsm::sms_submit_encode(required(v, "sms")?)?,
578 ),
579 ]
580 }
581 44 => {
582 let mut p = vec![
583 encode_sm_rp_da(required(v, "sm_rp_da")?)?,
584 encode_sm_rp_oa(required(v, "sm_rp_oa")?)?,
585 ber::tlv(
586 Tag::OCTET_STRING,
587 &gsm::sms_deliver_encode(required(v, "sms")?)?,
588 ),
589 ];
590 if v.get("more_messages_to_send").and_then(Value::as_bool) == Some(true) {
591 p.push(ber::tlv(Tag::NULL, &[]));
592 }
593 p
594 }
595 22 => {
596 let it = match v.get("interrogation_type").and_then(Value::as_str) {
597 None | Some("basicCall") => 0,
598 Some("forwarding") => 1,
599 Some(other) => {
600 return Err(format!(
601 "interrogation_type {other:?} is not basicCall or forwarding"
602 ))
603 }
604 };
605 vec![
606 address_tlv(
607 ctx(0),
608 required(v, "msisdn")?,
609 "msisdn",
610 gsm::MAX_ISDN_ADDRESS_DIGITS,
611 )?,
612 ber::integer(ctx(3), it),
613 address_tlv(
614 ctx(6),
615 required(v, "gmsc_or_gsm_scf_address")?,
616 "gmsc_or_gsm_scf_address",
617 gsm::MAX_ISDN_ADDRESS_DIGITS,
618 )?,
619 ]
620 }
621 2 => vec![
622 imsi_tlv(Tag::OCTET_STRING, required(v, "imsi")?, "imsi")?,
623 address_tlv(
624 ctx(1),
625 required(v, "msc_number")?,
626 "msc_number",
627 gsm::MAX_ISDN_ADDRESS_DIGITS,
628 )?,
629 address_tlv(
630 Tag::OCTET_STRING,
631 required(v, "vlr_number")?,
632 "vlr_number",
633 gsm::MAX_ISDN_ADDRESS_DIGITS,
634 )?,
635 ],
636 other => return Err(format!("operation {other} is not implemented")),
637 };
638 Ok(ber::seq(Tag::SEQUENCE, &parts))
639}
640
641fn encode_sm_rp_da(v: &Value) -> MapResult<Vec<u8>> {
642 if let Some(i) = v.get("imsi") {
643 imsi_tlv(ctx(0), i, "sm_rp_da.imsi")
644 } else if let Some(l) = v.get("lmsi").and_then(Value::as_str) {
645 let b = ber::unhex(l).map_err(|e| e.0)?;
646 if b.len() != 4 {
647 return Err("sm_rp_da.lmsi must be 4 octets (8 hex digits)".into());
648 }
649 Ok(ber::tlv(ctx(1), &b))
650 } else if let Some(s) = v.get("service_centre_address") {
651 address_tlv(
652 ctx(4),
653 s,
654 "sm_rp_da.service_centre_address",
655 gsm::MAX_ADDRESS_DIGITS,
656 )
657 } else {
658 Err("sm_rp_da must be {\"imsi\"}, {\"lmsi\"} or {\"service_centre_address\"}".into())
659 }
660}
661
662fn encode_sm_rp_oa(v: &Value) -> MapResult<Vec<u8>> {
663 if let Some(m) = v.get("msisdn") {
664 address_tlv(ctx(2), m, "sm_rp_oa.msisdn", gsm::MAX_ISDN_ADDRESS_DIGITS)
665 } else if let Some(s) = v.get("service_centre_address") {
666 address_tlv(
667 ctx(4),
668 s,
669 "sm_rp_oa.service_centre_address",
670 gsm::MAX_ADDRESS_DIGITS,
671 )
672 } else {
673 Err("sm_rp_oa must be {\"msisdn\"} or {\"service_centre_address\"}".into())
674 }
675}
676
677pub const SUBSCRIBER_STATES: &[&str] = &[
682 "assumedIdle",
683 "camelBusy",
684 "notProvidedFromVLR",
685 "netDetNotReachable",
686];
687
688pub const NOT_REACHABLE_REASONS: &[&str] = &[
689 "msPurged",
690 "imsiDetached",
691 "restrictedArea",
692 "notRegistered",
693];
694
695pub fn encode_result(code: i64, v: &Value) -> MapResult<Option<Vec<u8>>> {
698 Ok(Some(match code {
699 45 => {
700 let mut loc = vec![address_tlv(
701 ctx(1),
702 required(v, "network_node_number")?,
703 "network_node_number",
704 gsm::MAX_ISDN_ADDRESS_DIGITS,
705 )?];
706 if let Some(l) = v.get("lmsi").and_then(Value::as_str) {
707 let b = ber::unhex(l).map_err(|e| e.0)?;
708 if b.len() != 4 {
709 return Err("lmsi must be 4 octets (8 hex digits)".into());
710 }
711 loc.push(ber::tlv(Tag::OCTET_STRING, &b));
712 }
713 if v.get("gprs_node_indicator").and_then(Value::as_bool) == Some(true) {
714 loc.push(ber::tlv(ctx(5), &[]));
715 }
716 ber::seq(
717 Tag::SEQUENCE,
718 &[
719 imsi_tlv(Tag::OCTET_STRING, required(v, "imsi")?, "imsi")?,
720 ber::seq(ctxc(0), &loc),
721 ],
722 )
723 }
724 71 => {
725 let mut info = Vec::new();
726 if let Some(li) = v.get("location_information").filter(|x| !x.is_null()) {
727 let mut l = Vec::new();
728 if let Some(age) = li
729 .get("age_of_location_information")
730 .and_then(Value::as_i64)
731 {
732 if !(0..=32767).contains(&age) {
733 return Err("age_of_location_information must be 0..32767".into());
734 }
735 l.push(ber::integer(Tag::INTEGER, age));
736 }
737 if let Some(vlr) = li.get("vlr_number").filter(|x| !x.is_null()) {
738 l.push(address_tlv(
739 ctx(1),
740 vlr,
741 "vlr_number",
742 gsm::MAX_ISDN_ADDRESS_DIGITS,
743 )?);
744 }
745 if let Some(cgi) = li.get("cell_global_id").filter(|x| !x.is_null()) {
746 l.push(ber::seq(
747 ctxc(3),
748 &[ber::tlv(ctx(0), &gsm::cgi_encode(cgi)?)],
749 ));
750 }
751 info.push(ber::seq(ctxc(0), &l));
752 }
753 if let Some(state) = v.get("subscriber_state").and_then(Value::as_str) {
754 let inner = match state {
755 "assumedIdle" => ber::tlv(ctx(0), &[]),
756 "camelBusy" => ber::tlv(ctx(1), &[]),
757 "notProvidedFromVLR" => ber::tlv(ctx(2), &[]),
758 "netDetNotReachable" => {
759 let reason = v
760 .get("not_reachable_reason")
761 .and_then(Value::as_str)
762 .unwrap_or("imsiDetached");
763 let r = NOT_REACHABLE_REASONS
764 .iter()
765 .position(|n| *n == reason)
766 .ok_or_else(|| {
767 format!(
768 "not_reachable_reason must be one of {}",
769 NOT_REACHABLE_REASONS.join(", ")
770 )
771 })?;
772 ber::integer(Tag::ENUMERATED, r as i64)
773 }
774 other => {
775 return Err(format!(
776 "subscriber_state {other:?} must be one of {}",
777 SUBSCRIBER_STATES.join(", ")
778 ))
779 }
780 };
781 info.push(ber::seq(ctxc(1), &[inner]));
782 }
783 if let Some(imei) = v.get("imei").and_then(Value::as_str) {
784 if imei.len() != 15 && imei.len() != 16 {
785 return Err("imei must be 15 or 16 digits".into());
786 }
787 info.push(ber::tlv(ctx(5), &gsm::tbcd_encode(imei)?));
788 }
789 ber::seq(Tag::SEQUENCE, &[ber::seq(Tag::SEQUENCE, &info)])
790 }
791 59 | 60 => {
792 let text = v
793 .get("ussd_text")
794 .or_else(|| v.get("text"))
795 .and_then(Value::as_str)
796 .ok_or("ussd_text is required")?;
797 let (dcs, octets) = gsm::ussd_encode(text)?;
798 ber::seq(
799 Tag::SEQUENCE,
800 &[
801 ber::tlv(Tag::OCTET_STRING, &[dcs]),
802 ber::tlv(Tag::OCTET_STRING, &octets),
803 ],
804 )
805 }
806 61 | 44 | 46 => return Ok(None),
807 22 => {
808 let mut p = Vec::new();
809 if let Some(i) = v.get("imsi").filter(|x| !x.is_null()) {
810 p.push(imsi_tlv(ctx(9), i, "imsi")?);
811 }
812 p.push(address_tlv(
813 Tag::OCTET_STRING,
814 required(v, "roaming_number")?,
815 "roaming_number",
816 gsm::MAX_ISDN_ADDRESS_DIGITS,
817 )?);
818 ber::seq(ctxc(3), &p)
819 }
820 2 => ber::seq(
821 Tag::SEQUENCE,
822 &[address_tlv(
823 Tag::OCTET_STRING,
824 required(v, "hlr_number")?,
825 "hlr_number",
826 gsm::MAX_ISDN_ADDRESS_DIGITS,
827 )?],
828 ),
829 other => return Err(format!("operation {other} is not implemented")),
830 }))
831}
832
833pub fn decode_result(code: i64, p: Option<&Tlv>) -> MapResult<Value> {
835 let Some(p) = p else {
836 return Ok(json!({}));
837 };
838 let kids = p.children.clone();
839 let find = |tag: Tag| kids.iter().find(|c| c.tag == tag);
840 Ok(match code {
841 45 => {
842 let loc = find(ctxc(0));
843 json!({
844 "imsi": find(Tag::OCTET_STRING).map(imsi),
845 "network_node_number": loc.and_then(|l| l.ctx(1)).map(addr),
846 "lmsi": loc.and_then(|l| l.univ(ber::UNIV_OCTET_STRING)).map(|x| ber::hex(&x.value)),
847 "gprs_node_indicator": loc.map(|l| l.ctx(5).is_some()).unwrap_or(false),
848 })
849 }
850 71 => {
851 let info = kids.first();
852 let li = info.and_then(|i| i.ctx(0));
853 let state = info.and_then(|i| i.ctx(1)).and_then(|s| s.children.first());
854 json!({
855 "location_information": li.map(|l| json!({
856 "age_of_location_information": l.univ(ber::UNIV_INTEGER).and_then(|a| a.as_i64().ok()),
857 "vlr_number": l.ctx(1).map(addr),
858 "cell_global_id": l.ctx(3).and_then(|c| c.children.first()).map(|c| gsm::cgi_decode(&c.value)),
859 })),
860 "subscriber_state": state.map(|s| match (s.tag.class, s.tag.number) {
861 (ber::CLASS_CONTEXT, 0) => json!("assumedIdle"),
862 (ber::CLASS_CONTEXT, 1) => json!("camelBusy"),
863 (ber::CLASS_CONTEXT, 2) => json!("notProvidedFromVLR"),
864 _ => json!(format!("netDetNotReachable:{}", s.as_i64().ok()
865 .and_then(|r| NOT_REACHABLE_REASONS.get(r as usize).copied())
866 .unwrap_or("unknown"))),
867 }),
868 "imei": info.and_then(|i| i.ctx(5)).map(|x| gsm::tbcd_decode(&x.value)),
869 })
870 }
871 59 | 60 => {
872 let (dcs, text) = ussd_fields(&kids)?;
873 json!({"ussd_data_coding_scheme": dcs, "ussd_text": text})
874 }
875 44 | 46 => json!({
876 "sm_rp_ui_present": find(Tag::OCTET_STRING).is_some(),
877 }),
878 22 => json!({
879 "imsi": find(ctx(9)).map(imsi),
880 "roaming_number": find(Tag::OCTET_STRING).map(addr),
881 "uninterpreted": leftovers(&kids, &[ctx(9), Tag::OCTET_STRING]),
882 }),
883 2 => json!({
884 "hlr_number": find(Tag::OCTET_STRING).map(addr),
885 }),
886 _ => json!({"uninterpreted": p.child_labels()}),
887 })
888}
889
890pub const SM_DELIVERY_FAILURE_CAUSES: &[&str] = &[
892 "memoryCapacityExceeded",
893 "equipmentProtocolError",
894 "equipmentNotSM-Equipped",
895 "unknownServiceCentre",
896 "sc-Congestion",
897 "invalidSME-Address",
898 "subscriberNotSC-Subscriber",
899];
900
901pub fn encode_error_parameter(code: i64, v: &Value) -> MapResult<Option<Vec<u8>>> {
904 match code {
905 32 => {
906 let cause = v
907 .get("delivery_failure_cause")
908 .and_then(Value::as_str)
909 .unwrap_or("memoryCapacityExceeded");
910 let i = SM_DELIVERY_FAILURE_CAUSES
911 .iter()
912 .position(|c| *c == cause)
913 .ok_or_else(|| {
914 format!(
915 "delivery_failure_cause must be one of {}",
916 SM_DELIVERY_FAILURE_CAUSES.join(", ")
917 )
918 })?;
919 Ok(Some(ber::seq(
920 Tag::SEQUENCE,
921 &[ber::integer(Tag::ENUMERATED, i as i64)],
922 )))
923 }
924 6 => match v
925 .get("absent_subscriber_diagnostic")
926 .and_then(Value::as_i64)
927 {
928 Some(d) if (0..=255).contains(&d) => Ok(Some(ber::seq(
929 Tag::SEQUENCE,
930 &[ber::integer(Tag::INTEGER, d)],
931 ))),
932 Some(_) => Err("absent_subscriber_diagnostic must be 0..255".into()),
933 None => Ok(None),
934 },
935 _ => Ok(None),
936 }
937}
938
939pub fn decode_error_parameter(code: i64, p: Option<&Tlv>) -> Value {
940 let Some(p) = p else {
941 return Value::Null;
942 };
943 match code {
944 32 => json!({
945 "delivery_failure_cause": p.univ(ber::UNIV_ENUMERATED)
946 .and_then(|e| e.as_i64().ok())
947 .and_then(|i| SM_DELIVERY_FAILURE_CAUSES.get(i as usize).copied()),
948 }),
949 6 => json!({
950 "absent_subscriber_diagnostic": p.univ(ber::UNIV_INTEGER).and_then(|e| e.as_i64().ok()),
951 }),
952 _ => json!({"present": p.tag.label()}),
953 }
954}