-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Connection-Oriented SCCP: CR & CC, to start with #29
base: master
Are you sure you want to change the base?
Changes from 7 commits
d905ee5
1641461
275a534
050edaf
c80fd9b
da155b4
32482e4
ac14d55
3d9bcab
036dec9
8de405b
87cd35d
dc81aa6
c3ea28c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,175 @@ | ||||||
package sccp | ||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
"io" | ||||||
|
||||||
"github.com/wmnsk/go-sccp/params" | ||||||
) | ||||||
|
||||||
/* | ||||||
Message type 2.1 F 1 | ||||||
Destination local reference 3.2 F 3 | ||||||
Source local reference 3.3 F 3 | ||||||
Protocol class 3.6 F 1 | ||||||
Credit 3.10 O 3 | ||||||
Called party address 3.4 O 4 minimum | ||||||
Data 3.16 O 3-130 | ||||||
Importance 3.19 O 3 | ||||||
End of optional parameter 3.1 O 1 | ||||||
*/ | ||||||
type CC struct { | ||||||
Type MsgType | ||||||
DestinationLocalReference params.LocalReference | ||||||
SourceLocalReference params.LocalReference | ||||||
params.ProtocolClass | ||||||
|
||||||
Opts []*params.Optional | ||||||
|
||||||
Data *params.Optional | ||||||
CalledPartyAddress *params.PartyAddress | ||||||
Comment on lines
+17
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handling of the optional parameters could be nicer, but it's due to the bad-designed parameter handling in the current code base. I will work on refactoring parameters. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, let me comment on this In fact, for the practical case, I just need convenient access to Data and Called/Calling pty-s only. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q.713 is such a small spec and I'm more comfortable to just have everything implemented rather than to ignore some of them. I think I can quickly make it done soon-ish. |
||||||
} | ||||||
|
||||||
func ParseCC(b []byte) (*CC, error) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please follow the function order (see udt.go and scmg.go). Also, constructor ( |
||||||
msg := &CC{} | ||||||
if err := msg.UnmarshalBinary(b); err != nil { | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
return msg, nil | ||||||
} | ||||||
|
||||||
func (msg *CC) UnmarshalBinary(b []byte) error { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Please use a single character There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||||||
l := uint8(len(b)) | ||||||
|
||||||
if l < (1 + 3 + 3 + 1 + 1) { | ||||||
return io.ErrUnexpectedEOF | ||||||
} | ||||||
|
||||||
msg.Type = MsgType(b[0]) | ||||||
if err := msg.DestinationLocalReference.Read(b[1:4]); err != nil { | ||||||
return err | ||||||
} | ||||||
if err := msg.SourceLocalReference.Read(b[4:7]); err != nil { | ||||||
return err | ||||||
} | ||||||
|
||||||
msg.ProtocolClass = params.ProtocolClass(b[7]) | ||||||
|
||||||
optr := b[8] | ||||||
|
||||||
if optr == 0 { | ||||||
return nil | ||||||
} | ||||||
if optr != 1 { | ||||||
return io.ErrUnexpectedEOF | ||||||
} | ||||||
|
||||||
if err := msg.parseOptional(b[9:]); err != nil { | ||||||
return io.ErrUnexpectedEOF | ||||||
} | ||||||
return nil | ||||||
} | ||||||
|
||||||
func (msg *CC) parseOptional(b []byte) error { | ||||||
p := uint8(0) | ||||||
for p < uint8(len(b)) { | ||||||
t := b[p] | ||||||
|
||||||
if t == 0 { | ||||||
return nil | ||||||
} | ||||||
if (p + 1) >= uint8(len(b)) { | ||||||
return io.ErrUnexpectedEOF | ||||||
} | ||||||
|
||||||
l := b[p+1] | ||||||
if (p + 1 + l) >= uint8(len(b)) { | ||||||
return io.ErrUnexpectedEOF | ||||||
} | ||||||
|
||||||
o := ¶ms.Optional{ | ||||||
Tag: t, | ||||||
Len: l, | ||||||
Value: b[p+2 : p+2+l], | ||||||
} | ||||||
|
||||||
switch t { | ||||||
case params.DataTag: | ||||||
msg.Data = o | ||||||
case params.CdPtyAddrTag: | ||||||
var err error | ||||||
msg.CalledPartyAddress, err = params.ParsePartyAddress(b[p : p+2+l]) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
} | ||||||
|
||||||
msg.Opts = append(msg.Opts, o) | ||||||
p += 2 + l | ||||||
|
||||||
} | ||||||
|
||||||
return nil | ||||||
} | ||||||
|
||||||
// MarshalBinary returns the byte sequence generated from a UDT instance. | ||||||
func (msg *CC) MarshalBinary() ([]byte, error) { | ||||||
b := make([]byte, msg.MarshalLen()) | ||||||
if err := msg.MarshalTo(b); err != nil { | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
return b, nil | ||||||
} | ||||||
|
||||||
func (msg *CC) MarshalLen() int { | ||||||
if len(msg.Opts) == 0 { | ||||||
return 9 // 8 fixed + 0 ptr | ||||||
} | ||||||
l := 10 // 8 fixed + 1 ptr + last optional | ||||||
for _, v := range msg.Opts { | ||||||
l += int(v.Len) + 2 | ||||||
} | ||||||
|
||||||
return l | ||||||
} | ||||||
|
||||||
func (msg *CC) MarshalTo(b []byte) error { | ||||||
b[0] = uint8(msg.Type) | ||||||
msg.DestinationLocalReference.Read(b[1:4]) | ||||||
msg.SourceLocalReference.Read(b[4:7]) | ||||||
b[7] = byte(msg.ProtocolClass) | ||||||
|
||||||
if len(msg.Opts) == 0 { | ||||||
return nil | ||||||
} | ||||||
|
||||||
b[8] = 1 | ||||||
p := uint8(9) | ||||||
|
||||||
for i := 0; i < len(msg.Opts); i++ { | ||||||
b[p] = msg.Opts[i].Tag | ||||||
b[p+1] = msg.Opts[i].Len | ||||||
copy(b[p+2:], msg.Opts[i].Value) | ||||||
|
||||||
p += msg.Opts[i].Len + 2 | ||||||
} | ||||||
return nil | ||||||
} | ||||||
|
||||||
func (msg *CC) String() string { | ||||||
if msg.CalledPartyAddress != nil { | ||||||
return fmt.Sprintf("{Type: CC, CalledPartyAddress: %v}", msg.CalledPartyAddress) | ||||||
} | ||||||
return "{Type: CC}" | ||||||
} | ||||||
Comment on lines
+149
to
+154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do similar to what UDT and SCMG do. |
||||||
|
||||||
// MessageType returns the Message Type in int. | ||||||
func (msg *CC) MessageType() MsgType { | ||||||
return msg.Type | ||||||
} | ||||||
|
||||||
func (msg *CC) MessageTypeName() string { | ||||||
return "CR" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package sccp | ||
|
||
import ( | ||
"bytes" | ||
"encoding/hex" | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
var mockCCs = [][]byte{ | ||
{0x2, 0x0, 0x3, 0x75, 0x3, 0x20, 0x48, 0x2, 0x0}, | ||
{0x2, 0x0, 0x70, 0x3e, 0x0, 0x0, 0x5, 0x2, 0x1, 0x3, 0x4, 0x43, 0x1c, 0x2d, 0xfe, 0x0}, | ||
} | ||
|
||
func TestCC(t *testing.T) { | ||
for i, v := range mockCCs { | ||
cc, err := ParseCC(v) | ||
if err != nil { | ||
t.Fatal(i, err) | ||
} | ||
b, err := cc.MarshalBinary() | ||
if err != nil { | ||
t.Fatal(i, err) | ||
} | ||
if !bytes.Equal(v, b) { | ||
fmt.Println(hex.EncodeToString(v)) | ||
fmt.Println(hex.EncodeToString(b)) | ||
|
||
t.Fatal(i, err) | ||
} | ||
fmt.Println(cc) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
package sccp | ||
|
||
import ( | ||
"encoding/hex" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/wmnsk/go-sccp/params" | ||
) | ||
|
||
/* | ||
Message type code 2.1 F 1 | ||
Source local reference 3.3 F 3 | ||
Protocol class 3.6 F 1 | ||
Called party address 3.4 V 3 minimum | ||
Credit 3.10 O 3 | ||
Calling party address 3.5 O 4 minimum | ||
Data 3.16 O 3-130 | ||
Hop counter 3.18 O 3 | ||
Importance 3.19 O 3 | ||
End of optional parameters 3.1 O 1 | ||
*/ | ||
|
||
type CR struct { | ||
Type MsgType | ||
SourceLocalReference params.LocalReference | ||
params.ProtocolClass | ||
CalledPartyAddress *params.PartyAddress | ||
|
||
Opts []*params.Optional // all others | ||
|
||
// just pointers, not used for Marshal-ing, I kust really need these two | ||
// similar objects are expected to be found in Opts | ||
Data *params.Optional | ||
CallingPartyAddress *params.PartyAddress | ||
|
||
mptr uint8 | ||
optr uint8 | ||
} | ||
|
||
func ParseCR(b []byte) (*CR, error) { | ||
msg := &CR{} | ||
if err := msg.UnmarshalBinary(b); err != nil { | ||
return nil, err | ||
} | ||
|
||
return msg, nil | ||
} | ||
|
||
func (msg *CR) UnmarshalBinary(b []byte) error { | ||
l := uint8(len(b)) | ||
if l <= (1 + 3 + 1 + 2 /*ptrs*/ + 3) { // where CdPA starts | ||
return io.ErrUnexpectedEOF | ||
} | ||
|
||
msg.Type = MsgType(b[0]) | ||
if err := msg.SourceLocalReference.Read(b[1:4]); err != nil { | ||
return err | ||
} | ||
msg.ProtocolClass = params.ProtocolClass(b[4]) | ||
|
||
msg.mptr = b[5] | ||
if l < (5 + msg.mptr + 2) { | ||
return io.ErrUnexpectedEOF | ||
} | ||
msg.optr = b[6] | ||
if l < (6 + msg.optr + 1) { | ||
return io.ErrUnexpectedEOF | ||
} | ||
|
||
var err error | ||
if msg.CalledPartyAddress, err = params.ParsePartyAddress(b[5+msg.mptr : 6+msg.optr]); err != nil { | ||
return err | ||
} | ||
if msg.optr == 0 { | ||
return nil | ||
} | ||
return msg.parseOptional(b[6+msg.optr:]) | ||
} | ||
|
||
func (msg *CR) parseOptional(b []byte) error { | ||
p := uint8(0) | ||
for p < uint8(len(b)) { | ||
t := b[p] | ||
|
||
if t == 0 { | ||
return nil | ||
} | ||
if (p + 1) >= uint8(len(b)) { | ||
return io.ErrUnexpectedEOF | ||
} | ||
|
||
l := b[p+1] | ||
if (p + 1 + l) >= uint8(len(b)) { | ||
return io.ErrUnexpectedEOF | ||
} | ||
|
||
o := ¶ms.Optional{ | ||
Tag: t, | ||
Len: l, | ||
Value: b[p+2 : p+2+l], | ||
} | ||
|
||
switch t { | ||
case params.DataTag: | ||
msg.Data = o | ||
case params.CgPtyAddrTag: | ||
var err error | ||
msg.CallingPartyAddress, err = params.ParsePartyAddress(b[p : p+2+l]) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
msg.Opts = append(msg.Opts, o) | ||
p += 2 + l | ||
|
||
} | ||
|
||
return nil | ||
} | ||
|
||
// MarshalBinary returns the byte sequence generated from a UDT instance. | ||
func (msg *CR) MarshalBinary() ([]byte, error) { | ||
b := make([]byte, msg.MarshalLen()) | ||
if err := msg.MarshalTo(b); err != nil { | ||
return nil, err | ||
} | ||
|
||
return b, nil | ||
} | ||
|
||
func (msg *CR) MarshalLen() int { | ||
l := 5 + 2 + 1 // fixed + ptrs + last optional | ||
for _, v := range msg.Opts { | ||
l += int(v.Len) + 2 | ||
} | ||
l += int(msg.CalledPartyAddress.Length) + 1 | ||
|
||
return l | ||
} | ||
|
||
func (msg *CR) MarshalTo(b []byte) error { | ||
b[0] = uint8(msg.Type) | ||
msg.SourceLocalReference.Read(b[1:4]) | ||
b[4] = byte(msg.ProtocolClass) | ||
b[5] = 2 | ||
b[6] = msg.CalledPartyAddress.Length + 2 | ||
if err := msg.CalledPartyAddress.MarshalTo(b[7 : 7+int(msg.CalledPartyAddress.Length)+1]); err != nil { | ||
return err | ||
} | ||
p := 6 + msg.CalledPartyAddress.Length + 1 + 1 | ||
for i := 0; i < len(msg.Opts); i++ { | ||
b[p] = msg.Opts[i].Tag | ||
b[p+1] = msg.Opts[i].Len | ||
copy(b[p+2:], msg.Opts[i].Value) | ||
|
||
p += msg.Opts[i].Len + 2 | ||
} | ||
return nil | ||
} | ||
|
||
func (msg *CR) String() string { | ||
s := fmt.Sprintf("{Type: CR, CalledPartyAddress: %v", msg.CalledPartyAddress) | ||
if msg.CallingPartyAddress != nil { | ||
s += fmt.Sprintf(", CallingPartyAddress: %v", msg.CallingPartyAddress) | ||
} | ||
if msg.Data != nil { | ||
s += fmt.Sprintf(", DataLength: %d, Data: %s", msg.Data.Len, hex.EncodeToString(msg.Data.Value)) | ||
} | ||
|
||
return s + "}" | ||
} | ||
|
||
// MessageType returns the Message Type in int. | ||
func (msg *CR) MessageType() MsgType { | ||
return msg.Type | ||
} | ||
|
||
func (msg *CR) MessageTypeName() string { | ||
return "CR" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need such a detailed reference to the spec, and please follow the Go's convention of writing a comment as a doc (godoc). You can see
udt.go
for reference.