From f77bf79fe890198894dc75700fece10354eec8ae Mon Sep 17 00:00:00 2001 From: Yoshiyuki Kurauchi Date: Sat, 4 Jan 2025 16:26:33 +0100 Subject: [PATCH] Use generics for Cause --- examples/server/receiver.go | 26 ++- params/global-title.go | 1 + params/params.go | 370 +++++++++--------------------------- params/params_test.go | 12 +- 4 files changed, 118 insertions(+), 291 deletions(-) diff --git a/examples/server/receiver.go b/examples/server/receiver.go index 095ea44..e6a92b0 100644 --- a/examples/server/receiver.go +++ b/examples/server/receiver.go @@ -9,9 +9,11 @@ package main import ( "context" + "errors" "flag" "io" "log" + "net" "time" "github.com/wmnsk/go-m3ua/messages/params" @@ -28,21 +30,25 @@ func serve(conn *m3ua.Conn) { for { n, err := conn.Read(buf) if err != nil { - if err == io.EOF { - log.Printf("Closed M3UA conn with: %s, waiting to come back...", conn.RemoteAddr()) + if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) { + log.Printf("Closed M3UA conn with: %s, waiting to come back on", conn.RemoteAddr()) return } log.Printf("Error reading from M3UA conn: %s", err) return } - msg, err := sccp.ParseMessage(buf[:n]) - if err != nil { - log.Printf("Failed to parse SCCP message: %s, %x", err, buf[:n]) - continue - } + b := make([]byte, n) + copy(b, buf[:n]) + go func() { + msg, err := sccp.ParseMessage(b) + if err != nil { + log.Printf("Failed to parse SCCP message: %s, %x", err, b) + return + } - log.Printf("Received SCCP message: %s", msg) + log.Printf("Received SCCP message: %v", msg) + }() } } @@ -52,7 +58,8 @@ func main() { ) flag.Parse() - // create *Config to be used in M3UA connection + // see go-m3ua for the details of the configuration. + // https://github.com/wmnsk/go-m3ua config := m3ua.NewServerConfig( &m3ua.HeartbeatInfo{ Enabled: true, @@ -74,7 +81,6 @@ func main() { config.AspIdentifier = nil config.CorrelationID = nil - // setup SCTP listener on the specified IPs and Port. laddr, err := sctp.ResolveSCTPAddr("sctp", *addr) if err != nil { log.Fatalf("Failed to resolve SCTP address: %s", err) diff --git a/params/global-title.go b/params/global-title.go index b0bbff1..42a7a62 100644 --- a/params/global-title.go +++ b/params/global-title.go @@ -118,6 +118,7 @@ func NewGlobalTitle( return gt } +// Write serializes GlobalTitle to the given byte sequence. func (g *GlobalTitle) Write(b []byte) (int, error) { l := g.MarshalLen() if len(b) < l { diff --git a/params/params.go b/params/params.go index 1207262..04be011 100644 --- a/params/params.go +++ b/params/params.go @@ -30,6 +30,7 @@ type Parameter interface { // ParameterType is a type for Parameter described in the tables in section 4 of Q.713. type ParameterType uint8 +// ParameterType values. const ( // F: mandatory fixed length parameter PTypeF ParameterType = 0 // F @@ -1021,96 +1022,123 @@ func (c *Credit) String() string { return fmt.Sprintf("{%s (%s): %d}", c.code, c.paramType, c.value) } -// ReleaseCause represents the Release Cause. -type ReleaseCause struct { +// Cause represents a common structure for all Cause types. +type Cause[T ~uint8] struct { paramType ParameterType code ParameterNameCode length int - value ReleaseCauseValue + value T } -// ReleaseCauseValue is a type for ReleaseCause. -type ReleaseCauseValue uint8 - -// ReleaseCauseValue values. -const ( - ReleaseCauseEndUserOriginated ReleaseCauseValue = 0b00000000 // end user originated - ReleaseCauseEndUserCongestion ReleaseCauseValue = 0b00000001 // end user congestion - ReleaseCauseEndUserFailure ReleaseCauseValue = 0b00000010 // end user failure - ReleaseCauseSCCPUserOriginated ReleaseCauseValue = 0b00000011 // SCCP user originated - ReleaseCauseRemoteProcedureError ReleaseCauseValue = 0b00000100 // remote procedure error - ReleaseCauseInconsistentConnectionData ReleaseCauseValue = 0b00000101 // inconsistent connection data - ReleaseCauseAccessFailure ReleaseCauseValue = 0b00000110 // access failure - ReleaseCauseAccessCongestion ReleaseCauseValue = 0b00000111 // access congestion - ReleaseCauseSubsystemFailure ReleaseCauseValue = 0b00001000 // subsystem failure - ReleaseCauseSubsystemCongestion ReleaseCauseValue = 0b00001001 // subsystem congestion - ReleaseCauseMTPFailure ReleaseCauseValue = 0b00001010 // MTP failure - ReleaseCauseNetworkCongestion ReleaseCauseValue = 0b00001011 // network congestion - ReleaseCauseExpirationOfResetTimer ReleaseCauseValue = 0b00001100 // expiration of reset timer - ReleaseCauseExpirationOfReceiveInactivityTimer ReleaseCauseValue = 0b00001101 // expiration of receive inactivity timer - _ ReleaseCauseValue = 0b00001110 // reserved - ReleaseCauseUnqualified ReleaseCauseValue = 0b00001111 // unqualified - ReleaseCauseSCCPFailure ReleaseCauseValue = 0b00010000 // SCCP failure -) - -// NewReleaseCause creates a new ReleaseCause. -func NewReleaseCause(v ReleaseCauseValue) *ReleaseCause { - return &ReleaseCause{ +// NewCause creates a new Cause. +func NewCause[T ~uint8](value T) *Cause[T] { + c := &Cause[T]{ paramType: PTypeF, - code: PCodeReleaseCause, length: 1, - value: v, + value: value, + } + + switch any(c).(type) { + case *ReleaseCause: + c.code = PCodeReleaseCause + case *ReturnCause: + c.code = PCodeReturnCause + case *ResetCause: + c.code = PCodeResetCause + case *ErrorCause: + c.code = PCodeErrorCause + case *RefusalCause: + c.code = PCodeRefusalCause + default: + logf("invalid Cause type: %T", c) } + + return c } -// Read sets the values retrieved from byte sequence in a ReleaseCause. -func (r *ReleaseCause) Read(b []byte) (int, error) { +// Read sets the values retrieved from byte sequence in a Cause. +func (c *Cause[T]) Read(b []byte) (int, error) { n := 1 if len(b) < n { return 0, io.ErrUnexpectedEOF } - r.code = PCodeReleaseCause - r.length = n - r.value = ReleaseCauseValue(b[0]) + c.paramType = PTypeF + + switch any(c).(type) { + case *ReleaseCause: + c.code = PCodeReleaseCause + case *ReturnCause: + c.code = PCodeReturnCause + case *ResetCause: + c.code = PCodeResetCause + case *ErrorCause: + c.code = PCodeErrorCause + case *RefusalCause: + c.code = PCodeRefusalCause + default: + return 0, UnsupportedParameterError(b[0]) + } + + c.length = n + c.value = T(b[0]) return n, nil } -// Write serializes the ReleaseCause parameter and returns it as a byte slice. -func (r *ReleaseCause) Write(b []byte) (int, error) { - if len(b) < r.length { +// Write serializes the Cause parameter and returns it as a byte slice. +func (c *Cause[T]) Write(b []byte) (int, error) { + if len(b) < c.length { return 0, io.ErrUnexpectedEOF } - b[0] = uint8(r.value) - return r.length, nil + b[0] = uint8(c.value) + return c.length, nil } -// Value returns the ReleaseCause in ReleaseCauseValue. -func (r *ReleaseCause) Value() ReleaseCauseValue { - return r.value +// Value returns the value in the Cause. +func (c *Cause[T]) Value() T { + return c.value } -// Code returns the ReleaseCause in ParameterNameCode. -func (r *ReleaseCause) Code() ParameterNameCode { - return r.code +// Code returns the code in the Cause. +func (c *Cause[T]) Code() ParameterNameCode { + return c.code } -// String returns the ReleaseCause in string. -func (r *ReleaseCause) String() string { - return fmt.Sprintf("{%s (%s): %s}", r.code, r.paramType, r.value) +// String returns the Cause as a string. +func (c *Cause[T]) String() string { + return fmt.Sprintf("{%s (%s): %v}", c.code, c.paramType, c.value) } -// ReturnCause represents the Return Cause. -type ReturnCause struct { - paramType ParameterType - code ParameterNameCode - length int - value ReturnCauseValue -} +// ReleaseCauseValue is a type for ReleaseCause. +type ReleaseCauseValue uint8 -// ReturnCauseValue is a type for ReturnCause. +// ReleaseCauseValue values. +const ( + ReleaseCauseEndUserOriginated ReleaseCauseValue = 0b00000000 // end user originated + ReleaseCauseEndUserCongestion ReleaseCauseValue = 0b00000001 // end user congestion + ReleaseCauseEndUserFailure ReleaseCauseValue = 0b00000010 // end user failure + ReleaseCauseSCCPUserOriginated ReleaseCauseValue = 0b00000011 // SCCP user originated + ReleaseCauseRemoteProcedureError ReleaseCauseValue = 0b00000100 // remote procedure error + ReleaseCauseInconsistentConnectionData ReleaseCauseValue = 0b00000101 // inconsistent connection data + ReleaseCauseAccessFailure ReleaseCauseValue = 0b00000110 // access failure + ReleaseCauseAccessCongestion ReleaseCauseValue = 0b00000111 // access congestion + ReleaseCauseSubsystemFailure ReleaseCauseValue = 0b00001000 // subsystem failure + ReleaseCauseSubsystemCongestion ReleaseCauseValue = 0b00001001 // subsystem congestion + ReleaseCauseMTPFailure ReleaseCauseValue = 0b00001010 // MTP failure + ReleaseCauseNetworkCongestion ReleaseCauseValue = 0b00001011 // network congestion + ReleaseCauseExpirationOfResetTimer ReleaseCauseValue = 0b00001100 // expiration of reset timer + ReleaseCauseExpirationOfReceiveInactivityTimer ReleaseCauseValue = 0b00001101 // expiration of receive inactivity timer + _ ReleaseCauseValue = 0b00001110 // reserved + ReleaseCauseUnqualified ReleaseCauseValue = 0b00001111 // unqualified + ReleaseCauseSCCPFailure ReleaseCauseValue = 0b00010000 // SCCP failure +) + +// ReleaseCause is a specific Cause for ReleaseCause. +type ReleaseCause = Cause[ReleaseCauseValue] + +// ReturnCause is a specific Cause for ReturnCause. type ReturnCauseValue uint8 // ReturnCauseValue values. @@ -1132,62 +1160,8 @@ const ( ReturnCauseSegmentationFailure ReturnCauseValue = 0b00001110 // segmentation failure ) -// NewReturnCause creates a new ReturnCause. -func NewReturnCause(v ReturnCauseValue) *ReturnCause { - return &ReturnCause{ - paramType: PTypeF, - code: PCodeReturnCause, - length: 1, - value: v, - } -} - -// Read sets the values retrieved from byte sequence in a ReturnCause. -func (r *ReturnCause) Read(b []byte) (int, error) { - n := 1 - if len(b) < n { - return 0, io.ErrUnexpectedEOF - } - - r.code = PCodeReturnCause - r.length = n - r.value = ReturnCauseValue(b[0]) - - return n, nil -} - -// Write serializes the ReturnCause parameter and returns it as a byte slice. -func (r *ReturnCause) Write(b []byte) (int, error) { - if len(b) < r.length { - return 0, io.ErrUnexpectedEOF - } - - b[0] = uint8(r.value) - return r.length, nil -} - -// Value returns the ReturnCause in ReturnCauseValue. -func (r *ReturnCause) Value() ReturnCauseValue { - return r.value -} - -// Code returns the ReturnCause in ParameterNameCode. -func (r *ReturnCause) Code() ParameterNameCode { - return r.code -} - -// String returns the ReturnCause in string. -func (r *ReturnCause) String() string { - return fmt.Sprintf("{%s (%s): %s}", r.code, r.paramType, r.value) -} - -// ResetCause represents the Reset Cause. -type ResetCause struct { - paramType ParameterType - code ParameterNameCode - length int - value ResetCauseValue -} +// ReturnCause is a specific instance of Cause. +type ReturnCause = Cause[ReturnCauseValue] // ResetCauseValue is a type for ResetCause. type ResetCauseValue uint8 @@ -1209,62 +1183,8 @@ const ( ResetCauseUnqualified ResetCauseValue = 0b00001100 // unqualified ) -// NewResetCause creates a new ResetCause. -func NewResetCause(v ResetCauseValue) *ResetCause { - return &ResetCause{ - paramType: PTypeF, - code: PCodeResetCause, - length: 1, - value: v, - } -} - -// Read sets the values retrieved from byte sequence in a ResetCause. -func (r *ResetCause) Read(b []byte) (int, error) { - n := 1 - if len(b) < n { - return 0, io.ErrUnexpectedEOF - } - - r.code = PCodeResetCause - r.length = n - r.value = ResetCauseValue(b[0]) - - return n, nil -} - -// Write serializes the ResetCause parameter and returns it as a byte slice. -func (r *ResetCause) Write(b []byte) (int, error) { - if len(b) < r.length { - return 0, io.ErrUnexpectedEOF - } - - b[0] = uint8(r.value) - return r.length, nil -} - -// Value returns the ResetCause in ResetCauseValue. -func (r *ResetCause) Value() ResetCauseValue { - return r.value -} - -// Code returns the ResetCause in ParameterNameCode. -func (r *ResetCause) Code() ParameterNameCode { - return r.code -} - -// String returns the ResetCause in string. -func (r *ResetCause) String() string { - return fmt.Sprintf("{%s (%s): %s}", r.code, r.paramType, r.value) -} - -// ErrorCause represents the Error Cause. -type ErrorCause struct { - paramType ParameterType - code ParameterNameCode - length int - value ErrorCauseValue -} +// ResetCause is a specific Cause for ResetCause. +type ResetCause = Cause[ResetCauseValue] // ErrorCauseValue is a type for ErrorCause. type ErrorCauseValue uint8 @@ -1278,62 +1198,8 @@ const ( ErrorCauseUnqualified ErrorCauseValue = 0b00000100 // unqualified ) -// NewErrorCause creates a new ErrorCause. -func NewErrorCause(v ErrorCauseValue) *ErrorCause { - return &ErrorCause{ - paramType: PTypeF, - code: PCodeErrorCause, - length: 1, - value: v, - } -} - -// Read sets the values retrieved from byte sequence in a ErrorCause. -func (e *ErrorCause) Read(b []byte) (int, error) { - n := 1 - if len(b) < n { - return 0, io.ErrUnexpectedEOF - } - - e.code = PCodeErrorCause - e.length = n - e.value = ErrorCauseValue(b[0]) - - return n, nil -} - -// Write serializes the ErrorCause parameter and returns it as a byte slice. -func (e *ErrorCause) Write(b []byte) (int, error) { - if len(b) < e.length { - return 0, io.ErrUnexpectedEOF - } - - b[0] = uint8(e.value) - return e.length, nil -} - -// Value returns the ErrorCause in ErrorCauseValue. -func (e *ErrorCause) Value() ErrorCauseValue { - return e.value -} - -// Code returns the ErrorCause in ParameterNameCode. -func (e *ErrorCause) Code() ParameterNameCode { - return e.code -} - -// String returns the ErrorCause in string. -func (e *ErrorCause) String() string { - return fmt.Sprintf("{%s (%s): %s}", e.code, e.paramType, e.value) -} - -// RefusalCause represents the Refusal Cause. -type RefusalCause struct { - paramType ParameterType - code ParameterNameCode - length int - value RefusalCauseValue -} +// ErrorCause is a specific Cause for ErrorCause. +type ErrorCause = Cause[ErrorCauseValue] // RefusalCauseValue is a type for RefusalCause. type RefusalCauseValue uint8 @@ -1362,54 +1228,8 @@ const ( RefusalCauseUnequippedUser RefusalCauseValue = 0b00010011 // unequipped user ) -// NewRefusalCause creates a new RefusalCause. -func NewRefusalCause(v RefusalCauseValue) *RefusalCause { - return &RefusalCause{ - paramType: PTypeF, - code: PCodeRefusalCause, - length: 1, - value: v, - } -} - -// Read sets the values retrieved from byte sequence in a RefusalCause. -func (r *RefusalCause) Read(b []byte) (int, error) { - n := 1 - if len(b) < n { - return 0, io.ErrUnexpectedEOF - } - - r.code = PCodeRefusalCause - r.length = n - r.value = RefusalCauseValue(b[0]) - - return n, nil -} - -// Write serializes the RefusalCause parameter and returns it as a byte slice. -func (r *RefusalCause) Write(b []byte) (int, error) { - if len(b) < r.length { - return 0, io.ErrUnexpectedEOF - } - - b[0] = uint8(r.value) - return r.length, nil -} - -// Value returns the RefusalCause in RefusalCauseValue. -func (r *RefusalCause) Value() RefusalCauseValue { - return r.value -} - -// Code returns the RefusalCause in ParameterNameCode. -func (r *RefusalCause) Code() ParameterNameCode { - return r.code -} - -// String returns the RefusalCause in string. -func (r *RefusalCause) String() string { - return fmt.Sprintf("{%s (%s): %s}", r.code, r.paramType, r.value) -} +// RefusalCause is a specific Cause for RefusalCause. +type RefusalCause = Cause[RefusalCauseValue] // Data represents the Data. type Data struct { diff --git a/params/params_test.go b/params/params_test.go index 2410704..b120eac 100644 --- a/params/params_test.go +++ b/params/params_test.go @@ -222,8 +222,8 @@ var cases = []struct { return p, nil }, }, { - description: "ReleaseCause", - structured: params.NewReleaseCause(params.ReleaseCauseSCCPUserOriginated), + description: "ReleaseCause/Generics", + structured: params.NewCause(params.ReleaseCauseSCCPUserOriginated), serialized: []byte{0x03}, decodeFunc: func(b []byte) (serializable, error) { p := ¶ms.ReleaseCause{} @@ -234,7 +234,7 @@ var cases = []struct { }, }, { description: "ReturnCause", - structured: params.NewReturnCause(params.ReturnCauseSubsystemFailure), + structured: params.NewCause(params.ReturnCauseSubsystemFailure), serialized: []byte{0x03}, decodeFunc: func(b []byte) (serializable, error) { p := ¶ms.ReturnCause{} @@ -245,7 +245,7 @@ var cases = []struct { }, }, { description: "ResetCause", - structured: params.NewResetCause(params.ResetCauseMessageOutOfOrderIncorrectReceiveSequenceNumber), + structured: params.NewCause(params.ResetCauseMessageOutOfOrderIncorrectReceiveSequenceNumber), serialized: []byte{0x03}, decodeFunc: func(b []byte) (serializable, error) { p := ¶ms.ResetCause{} @@ -256,7 +256,7 @@ var cases = []struct { }, }, { description: "ErrorCause", - structured: params.NewErrorCause(params.ErrorCauseServiceClassMismatch), + structured: params.NewCause(params.ErrorCauseServiceClassMismatch), serialized: []byte{0x03}, decodeFunc: func(b []byte) (serializable, error) { p := ¶ms.ErrorCause{} @@ -267,7 +267,7 @@ var cases = []struct { }, }, { description: "RefusalCause", - structured: params.NewRefusalCause(params.RefusalCauseSCCPUserOriginated), + structured: params.NewCause(params.RefusalCauseSCCPUserOriginated), serialized: []byte{0x03}, decodeFunc: func(b []byte) (serializable, error) { p := ¶ms.RefusalCause{}