Properly parses most message types

This commit is contained in:
Christopher Ramey 2019-10-01 07:57:15 -08:00 committed by Christopher Ramey
parent 4e785a0377
commit 9030148906

226
sdb.go
View File

@ -1,22 +1,22 @@
package main package main
import ( import (
"os"
"fmt"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt"
"io/ioutil" "io/ioutil"
"os"
) )
type IridiumSDB struct { type IridiumSDB struct {
ProtocolRevision byte ProtocolRevision byte
MessageLength uint16 Length uint16
Message MOMessage Elements []interface{}
} }
type MOMessage struct { type MOHeader struct {
HeaderIEI byte IEI byte
HeaderLength uint16 Length uint16
CDRReference uint32 CDRReference uint32
IMEI [15]byte IMEI [15]byte
Status byte Status byte
@ -25,52 +25,192 @@ type MOMessage struct {
SessionTime uint32 SessionTime uint32
} }
var SessionStatusString = []string{ func (h MOHeader) StatusString() string {
"The SBD session completed successfully.", switch h.Status {
"The MO message transfer, if any, was successful. The MT message queued at the Iridium Gateway is too large to be transferred within a single SBD session.", case 0:
"The MO message transfer, if any, was successful. The reported location was determined to be of unacceptable quality. This value is only applicable to IMEIs using SBD protocol revision 1.", return "The SBD session completed successfully."
"", // 3 case 1:
"", // 4 return "The MO message transfer, if any, was successful. The MT message queued at the Iridium Gateway is too large to be transferred within a single SBD session."
"", // 5 case 2:
"", // 6 return "The MO message transfer, if any, was successful. The reported location was determined to be of unacceptable quality. This value is only applicable to IMEIs using SBD protocol revision 1."
"", // 7 case 10:
"", // 8 return "The SBD session timed out before session completion."
"", // 9 case 12:
"The SBD session timed out before session completion.", return "The MO message being transferred by the IMEI is too large to be transferred within a single SBD session."
"", // 11 case 13:
"The MO message being transferred by the IMEI is too large to be transferred within a single SBD session.", return "An RF link loss occurred during the SBD session."
"An RF link loss occurred during the SBD session.", case 14:
"An IMEI protocol anomaly occurred during SBD session.", return "An IMEI protocol anomaly occurred during SBD session."
"The IMEI is prohibited from accessing the Iridium Gateway."} case 15:
return "The IMEI is prohibited from accessing the Iridium Gateway."
default:
return ""
}
}
type MOPayload struct {
IEI byte
Length uint16
Payload []byte
}
type MOLocation struct {
IEI byte
Length uint16
LocationFlags byte
LatitudeDegrees uint8
LatitudeTHMinute uint16
LongitudeDegrees uint8
LongitudeTHMinute uint16
CEPRadius uint32
}
type MOConfirmation struct {
IEI byte
Length uint16
Status byte
}
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "required argument missing\n") fmt.Fprintf(os.Stderr,
"required argument missing\n")
os.Exit(1) os.Exit(1)
} }
rdt, err := ioutil.ReadFile(os.Args[1]) raw, err := ioutil.ReadFile(os.Args[1])
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "error reading %s: %s\n", os.Args[1], err.Error()) fmt.Fprintf(os.Stderr,
os.Exit(1) "error reading \"%s\": %s\n",
os.Args[1], err.Error())
} }
header := IridiumSDB{} err = parseSDB(raw)
buf := bytes.NewBuffer(rdt)
err = binary.Read(buf, binary.BigEndian, &header)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "error parsing %s: %s\n", os.Args[1], err.Error()) fmt.Fprintf(os.Stderr,
"%s\n", err.Error())
os.Exit(1) os.Exit(1)
} }
}
fmt.Fprintf(os.Stdout, "Protocol Revision: %d\n", header.ProtocolRevision)
fmt.Fprintf(os.Stdout, "Message Length: %d\n", header.MessageLength) func parseSDB(raw []byte) error {
fmt.Fprintf(os.Stdout, "MO Header IEI: %x\n", header.Message.HeaderIEI) raw_len := len(raw)
fmt.Fprintf(os.Stdout, "MO Header Length: %d\n", header.Message.HeaderLength) if raw_len < 3 {
fmt.Fprintf(os.Stdout, "CDR Reference: %d\n", header.Message.CDRReference) return fmt.Errorf("message structure too short: %d bytes",
fmt.Fprintf(os.Stdout, "IMEI: %s\n", string(header.Message.IMEI[:])) len(raw))
fmt.Fprintf(os.Stdout, "Status: %s (%d)\n", SessionStatusString[header.Message.Status], header.Message.Status) }
fmt.Fprintf(os.Stdout, "MOMSN: %d\n", header.Message.MOMSN)
fmt.Fprintf(os.Stdout, "MTMSN: %d\n", header.Message.MTMSN) sdb := IridiumSDB{}
fmt.Fprintf(os.Stdout, "Session Time: %d\n", header.Message.SessionTime) sdb.ProtocolRevision = raw[0]
sdb.Length = binary.BigEndian.Uint16(raw[1:3])
if (int(sdb.Length) + 3) != raw_len {
return fmt.Errorf("invalid message length (expected %d, got %d)",
(sdb.Length + 3), raw_len)
}
for i := 3; i < raw_len; {
if (i + 3) > raw_len {
return fmt.Errorf("invalid element length (at %d)",
i)
}
el_iei := raw[i]
el_len := binary.BigEndian.Uint16(raw[i+1 : i+3])
if (i + 3 + int(el_len)) > raw_len {
return fmt.Errorf("invalid element length (at %d): %d",
i+1, el_len)
}
switch el_iei {
// MO Header IEI
case 1:
buf := bytes.NewReader(raw[i : i+3+int(el_len)])
moheader := MOHeader{}
err := binary.Read(buf, binary.BigEndian, &moheader)
if err != nil {
return fmt.Errorf("error reading MO header (at %d): %s",
i, err.Error())
}
sdb.Elements = append(sdb.Elements, moheader)
// MO Payload IEI
case 2:
mopayload := MOPayload{}
mopayload.IEI = el_iei
mopayload.Length = el_len
mopayload.Payload = raw[i+3 : i+3+int(el_len)]
sdb.Elements = append(sdb.Elements, mopayload)
// MO Location IEI
case 3:
buf := bytes.NewReader(raw[i : i+3+int(el_len)])
molocation := MOLocation{}
err := binary.Read(buf, binary.BigEndian, &molocation)
if err != nil {
return fmt.Errorf("error reading MO location (at %d): %s",
i, err.Error())
}
sdb.Elements = append(sdb.Elements, molocation)
// MO Confirmation IEI
case 5:
moconfirmation := MOConfirmation{}
moconfirmation.IEI = el_iei
moconfirmation.Length = el_len
moconfirmation.Status = raw[i+3]
sdb.Elements = append(sdb.Elements, moconfirmation)
default:
return fmt.Errorf("invalid element IEI (at %d): %d",
i, el_iei)
}
i += (3 + int(el_len))
}
fmt.Printf("Protocol Revision: %d\n",
sdb.ProtocolRevision)
fmt.Printf("Message Length: %d\n",
sdb.Length)
for c, i := range sdb.Elements {
if c > 0 {
fmt.Println()
}
switch v := i.(type) {
case MOHeader:
fmt.Printf("\tMO Header IEI: %d\n",
v.IEI)
fmt.Printf("\tMO Header Length: %d\n",
v.Length)
fmt.Printf("\tMO Header CDR Reference: %d\n",
v.CDRReference)
fmt.Printf("\tMO Header IMEI: %s\n",
v.IMEI)
fmt.Printf("\tMO Header Status: %s (%d)\n",
v.StatusString(), v.Status)
fmt.Printf("\tMO Header MOMSN: %d\n",
v.MOMSN)
fmt.Printf("\tMO Header MTMSN: %d\n",
v.MTMSN)
fmt.Printf("\tMO Header Session Time: %d\n",
v.SessionTime)
case MOPayload:
fmt.Printf("\tMO Payload IEI: %d\n",
v.IEI)
fmt.Printf("\tMO Payload Length: %d\n",
v.Length)
case MOConfirmation:
fmt.Printf("\tMO Confirmation IEI: %d\n",
v.IEI)
fmt.Printf("\tMO Confirmation Length: %d\n",
v.Length)
fmt.Printf("\tMO Confirmation Status: %d\n",
v.Status)
}
}
return nil
} }