diff --git a/dialog_client.go b/dialog_client.go index 85dde7e..ab982c5 100644 --- a/dialog_client.go +++ b/dialog_client.go @@ -36,6 +36,19 @@ func (s *DialogClient) loadDialog(id string) *DialogClientSession { return t } +func (s *DialogClient) MatchDialogRequest(req *sip.Request) (*DialogClientSession, error) { + id, err := sip.UACReadRequestDialogID(req) + if err != nil { + return nil, errors.Join(err, ErrDialogOutsideDialog) + } + + dt := s.loadDialog(id) + if dt == nil { + return nil, ErrDialogDoesNotExists + } + return dt, nil +} + // NewDialogClient provides handle for managing UAC dialog // Contact hdr must be provided for correct invite // In case handling different transports you should have multiple instances per transport @@ -59,7 +72,7 @@ func (e ErrDialogResponse) Error() string { // Invite sends INVITE request and creates early dialog session. // You need to call WaitAnswer after for establishing dialog // For passing custom Invite request use WriteInvite -func (dc *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []byte, headers ...sip.Header) (*DialogClientSession, error) { +func (c *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []byte, headers ...sip.Header) (*DialogClientSession, error) { req := sip.NewRequest(sip.INVITE, recipient) if body != nil { req.SetBody(body) @@ -68,13 +81,13 @@ func (dc *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []by for _, h := range headers { req.AppendHeader(h) } - return dc.WriteInvite(ctx, req) + return c.WriteInvite(ctx, req) } -func (dc *DialogClient) WriteInvite(ctx context.Context, inviteRequest *sip.Request) (*DialogClientSession, error) { - cli := dc.c +func (c *DialogClient) WriteInvite(ctx context.Context, inviteRequest *sip.Request) (*DialogClientSession, error) { + cli := c.c - inviteRequest.AppendHeader(&dc.contactHDR) + inviteRequest.AppendHeader(&c.contactHDR) // TODO passing client transaction options is now hidden tx, err := cli.TransactionRequest(ctx, inviteRequest) @@ -92,23 +105,17 @@ func (dc *DialogClient) WriteInvite(ctx context.Context, inviteRequest *sip.Requ ctx: ctx, cancel: cancel, }, - dc: dc, + dc: c, inviteTx: tx, } return dtx, nil } -func (dc *DialogClient) ReadBye(req *sip.Request, tx sip.ServerTransaction) error { - callid := req.CallID() - from := req.From() - to := req.To() - - id := sip.MakeDialogID(callid.Value(), from.Params["tag"], to.Params["tag"]) - - dt := dc.loadDialog(id) - if dt == nil { - return fmt.Errorf("callid=%q: %w", callid.Value(), ErrDialogDoesNotExists) +func (c *DialogClient) ReadBye(req *sip.Request, tx sip.ServerTransaction) error { + dt, err := c.MatchDialogRequest(req) + if err != nil { + return err } dt.setState(sip.DialogStateEnded) diff --git a/dialog_server.go b/dialog_server.go index c4e9dbd..f8717a0 100644 --- a/dialog_server.go +++ b/dialog_server.go @@ -27,6 +27,19 @@ func (s *DialogServer) loadDialog(id string) *DialogServerSession { return t } +func (s *DialogServer) MatchDialogRequest(req *sip.Request) (*DialogServerSession, error) { + id, err := sip.UASReadRequestDialogID(req) + if err != nil { + return nil, errors.Join(ErrDialogOutsideDialog, err) + } + + dt := s.loadDialog(id) + if dt == nil { + return nil, ErrDialogDoesNotExists + } + return dt, nil +} + // NewDialogServer provides handle for managing UAS dialog // Contact hdr must be provided for responses // Client is needed for termination dialog session @@ -68,36 +81,21 @@ func (s *DialogServer) ReadInvite(req *sip.Request, tx sip.ServerTransaction) (* // ReadAck should read from your OnAck handler func (s *DialogServer) ReadAck(req *sip.Request, tx sip.ServerTransaction) error { - id, err := sip.MakeDialogIDFromRequest(req) + dt, err := s.MatchDialogRequest(req) if err != nil { - return errors.Join(ErrDialogOutsideDialog, err) - } - - dt := s.loadDialog(id) - if dt == nil { - // res := sip.NewResponseFromRequest(req, sip.StatusCallTransactionDoesNotExists, "Call/Transaction Does Not Exist", nil) - // if err := tx.Respond(res); err != nil { - // return err - // } - return ErrDialogDoesNotExists + return err } dt.setState(sip.DialogStateConfirmed) - // Acks are normally just absorbed, but in case of proxy // they still need to be passed return nil } -// ReadAck should read from your OnBye handler +// ReadBye should read from your OnBye handler func (s *DialogServer) ReadBye(req *sip.Request, tx sip.ServerTransaction) error { - id, err := sip.MakeDialogIDFromRequest(req) + dt, err := s.MatchDialogRequest(req) if err != nil { - return err - } - - dt := s.loadDialog(id) - if dt == nil { // https://datatracker.ietf.org/doc/html/rfc3261#section-15.1.2 // If the BYE does not // match an existing dialog, the UAS core SHOULD generate a 481 @@ -106,7 +104,7 @@ func (s *DialogServer) ReadBye(req *sip.Request, tx sip.ServerTransaction) error // if err := tx.Respond(res); err != nil { // return err // } - return ErrDialogDoesNotExists + return err } // Make sure this is bye for this dialog if req.CSeq().SeqNo != (dt.lastCSeqNo + 1) { diff --git a/sip/sip.go b/sip/sip.go index d38716a..cff1fbb 100644 --- a/sip/sip.go +++ b/sip/sip.go @@ -63,37 +63,44 @@ func DefaultPort(transport string) int { } } +// MakeDialogIDFromMessage creates dialog ID of message. +// Use UASReadRequestDialogID UACReadRequestDialogID for more specific +// returns error if callid or to tag or from tag does not exists func MakeDialogIDFromRequest(msg *Request) (string, error) { - var callID, innerID, externalID string = "", "", "" - if err := getDialogIDFromMessage(msg, &callID, &innerID, &externalID); err != nil { + return UASReadRequestDialogID(msg) +} + +// MakeDialogIDFromResponse creates dialog ID of message. +// returns error if callid or to tag or from tag does not exists +func MakeDialogIDFromResponse(msg *Response) (string, error) { + var callID, toTag, fromTag string = "", "", "" + if err := getDialogIDFromMessage(msg, &callID, &toTag, &fromTag); err != nil { return "", err } - return MakeDialogID(callID, innerID, externalID), nil + return MakeDialogID(callID, toTag, fromTag), nil } -func MakeDialogIDFromResponse(msg *Response) (string, error) { - var callID, innerID, externalID string = "", "", "" - // TODO switching this fields make no sense? - if err := getDialogIDFromMessage(msg, &callID, &innerID, &externalID); err != nil { +// UASReadRequestDialogID creates dialog ID of message if receiver has UAS role. +// returns error if callid or to tag or from tag does not exists +func UASReadRequestDialogID(msg *Request) (string, error) { + var callID, toTag, fromTag string = "", "", "" + if err := getDialogIDFromMessage(msg, &callID, &toTag, &fromTag); err != nil { return "", err } - return MakeDialogID(callID, innerID, externalID), nil + return MakeDialogID(callID, toTag, fromTag), nil } -// MakeDialogIDFromMessage creates dialog ID of message. +// UACReadRequestDialogID creates dialog ID of message if receiver has UAC role. // returns error if callid or to tag or from tag does not exists -// Deprecated! Will be removed -func MakeDialogIDFromMessage(msg Message) (string, error) { - switch m := msg.(type) { - case *Request: - return MakeDialogIDFromRequest(m) - case *Response: - return MakeDialogIDFromResponse(m) +func UACReadRequestDialogID(msg *Request) (string, error) { + var callID, toTag, fromTag string = "", "", "" + if err := getDialogIDFromMessage(msg, &callID, &toTag, &fromTag); err != nil { + return "", err } - return "", fmt.Errorf("unknown message format") + return MakeDialogID(callID, fromTag, toTag), nil } -func getDialogIDFromMessage(msg Message, callId, innerId, externalId *string) error { +func getDialogIDFromMessage(msg Message, callId, toHeaderTag, fromHeaderTag *string) error { callID := msg.CallID() if callID == nil { return fmt.Errorf("missing Call-ID header") @@ -119,11 +126,11 @@ func getDialogIDFromMessage(msg Message, callId, innerId, externalId *string) er return fmt.Errorf("missing tag param in From header") } *callId = string(*callID) - *innerId = toTag - *externalId = fromTag + *toHeaderTag = toTag + *fromHeaderTag = fromTag return nil } func MakeDialogID(callID, innerID, externalID string) string { - return strings.Join([]string{callID, innerID, externalID}, "__") + return strings.Join([]string{callID, innerID, externalID}, TxSeperator) } diff --git a/sip/transaction_server_tx.go b/sip/transaction_server_tx.go index ed094a8..07500e1 100644 --- a/sip/transaction_server_tx.go +++ b/sip/transaction_server_tx.go @@ -8,8 +8,6 @@ import ( "github.com/rs/zerolog" ) -type FSMfunc func() FSMfunc - type ServerTx struct { commonTx lastAck *Request @@ -245,6 +243,9 @@ func (tx *ServerTx) initFSM() { tx.fsmMu.Unlock() } +func (tx *ServerTx) State() { +} + func (tx *ServerTx) delete() { tx.closeOnce.Do(func() { tx.mu.Lock()