wsip

package module
v0.0.0-...-d4b6160 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 16, 2024 License: BSD-2-Clause Imports: 11 Imported by: 0

README

Wiresip (WIP)

Wiresip is GO SIP library that hides some complexity of building SIP stateful proxies.

Inspiration for this is that current some solutions are hard to customize but also hard to test changes.

Main goal is that you are able to relaying request responses but under control and all that with simple API.

Here are some features path so that you know where this project is heading:

  • Defining (inbound) targets and relaying request
  • Dialog and cached DNS destinations
  • Built in memory register handling
  • Proxy dialog (call) routing works
  • Create cli single binary based proxy for easy running as POC
  • Unit testing handler
  • Matching Inbound or Outbound targets based on ip ranges, prefix numbers etc..
  • Extend dialog with current sipgo.Dialog
  • Dialog external caching interface for HA and scaling
  • IP auth module based on request Source
  • HA solutions with external dialog/transaction storage (sipgo patching)
  • Digest auth out of box
  • Access dialogs and monitoring
  • RTP Proxy and media gateway
  • Topos module like kamailio for full topology hidding

Controling request and relay

Here is example of building proxy with several lines of code

ua, _ := sipgo.NewUA()
srv, _ := sipgo.NewServer(ua)
p := NewProxy(
    srv,
    "", // hostname
    WithProxyInboundTarget(sip.Uri{Host: "127.0.0.200", Port: 5060}),
)
go p.s.ListenAndServe(ctx, "udp", "127.0.0.1:5060")
go p.s.ListenAndServe(ctx, "tcp", "127.0.0.1:5060")

proxy.OnRequest(func(rc RequestContext) {
    // RelayRequest will:
    // - Check registrar 
    // - Detect call direction. Src IP is compared to Inbound(internal) IP Targets ranges
    // - Relay reqeust
    // req.SetDestination() will skip above checking
    respCh, err := rc.RelayRequest(req)
    if err != nil { /*log error*/return}

    // Keep relaying responses until it gets closed
    for res := range respCH {
        // Handle failure
        switch res.StatusCode {
            case 401, 403, 505, 600:
                // Handle bad codes
                req.SetDestination("failcarrier.com")
                respCh, err := rc.RelayRequest(req)
                // Check again ...
            default:
                // relay all

        }
        rc.RelayResponse(res)
    }
})

Inbound Outbound targets (TODO)

More granual control of defining IP.

WithProxyInboundTarget(
    sip.Uri{Host: "my.asterisk.xy", Port: 5060},
    // MatchIP should be defined in case URI is host name, otherwise it will be DNS resolved each time
    wsip.MatchIP( 
        "10.1.1.0/24", // With range ips to match in case this is dynamic
        "10.2.2.1", // With static ips
    ),
)
WithProxyOutboundTarget(
    sip.Uri{Host: "sip.carrier.com", Port: 5060},
    wsip.ToPrefix("49"), // or gsip.ToRegex("^49(1-2)"),
    0.5 // In case multiple matching carrier  < 1.0 request is loadbalanced
)

RelayRequest (WIP)

RelayRequest flow:

  • If no destination is set with req.SetDestination then
  • Check registrar if used as registrar
  • Detect call direction Src IP is compared to Inbound MatchIP
  • Filter all with matching Destination via Prefix, Regex
  • Load balance unless target has weight 1.0
  • Relay reqeust

RelayRequest internally:

  • creates client transaction
  • sends request and returns all responses
  • responses can be manipulated but mostly they should be relayed to originator
  • IN case 200 response for INVITE it creates dialog which can be accessed via rc.Dialog()
RTP proxy (IDEA)

RTP Proxy signaling:

  • Reads INVITE SDP
  • Setups RTP/RTCP ports for proxy external and internal interface (interface call as it can be seperate Service)
  • Changes to local interface address where our proxy 172.17.0.0/12
  • Applies new SDP with internal RTP/RTCP ports
  • Relays request
  • Reads Response (only 200)
  • Changes SDP to external interface
  • Changes SDP port to external PORTs
  • Returns response
  • When RequestContext dies RTP/RTCP will be closed

How to unit test request/response

This requires simulating UAC and UAS and testing our OnRequest handler

TODO

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrRegistryDoesNotExist = errors.New("registry does not exist")
)
View Source
var (
	ErrRelayNotFound = errors.New("Destination not found")
)

Functions

func ReadHeaderByType

func ReadHeaderByType[T sip.Header](m sip.Message, name string) ([]T, error)

Types

type Dialog

type Dialog struct {
	// contains filtered or unexported fields
}

type DialogMutex

type DialogMutex struct {
	// contains filtered or unexported fields
}

type Proxy

type Proxy struct {
	InboundTargets []sip.Uri
	OutboundTarget sip.Uri

	Authentication string

	OnRequest func(rc *RequestContext)
	// contains filtered or unexported fields
}

proxy aka core

func NewProxy

func NewProxy(srv *sipgo.Server, hostname string, options ...ProxyOption) *Proxy

type ProxyOption

type ProxyOption func(p *Proxy)

func WithProxyInboundTarget

func WithProxyInboundTarget(uri sip.Uri) ProxyOption

WithProxyInboundTarget adds inbound target can be called multiple

func WithProxyLogger

func WithProxyLogger(logger zerolog.Logger) ProxyOption

func WithProxyOutboundTarget

func WithProxyOutboundTarget(uri sip.Uri) ProxyOption

func WithProxyRegistry

func WithProxyRegistry(registry RegistryStore) ProxyOption

type RegisterBinding

type RegisterBinding struct {
	Aor      sip.Uri
	CallID   sip.CallIDHeader
	Contacts []sip.Uri
	Expiry   time.Duration
}

type RegistryMemory

type RegistryMemory struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

func NewRegistryMemory

func NewRegistryMemory() *RegistryMemory

func (*RegistryMemory) CreateRegisterBinding

func (r *RegistryMemory) CreateRegisterBinding(rec RegisterBinding) error

func (*RegistryMemory) DeleteRegisterBinding

func (r *RegistryMemory) DeleteRegisterBinding(user string) error

func (*RegistryMemory) DeleteRegisterBindingContact

func (r *RegistryMemory) DeleteRegisterBindingContact(user string, contact sip.Uri) error

func (*RegistryMemory) GetRegisterBinding

func (r *RegistryMemory) GetRegisterBinding(user string) (RegisterBinding, error)

type RegistryStore

type RegistryStore interface {
	// TODO add context
	CreateRegisterBinding(r RegisterBinding) error
	DeleteRegisterBinding(user string) error
	DeleteRegisterBindingContact(user string, contactAddress sip.Uri) error
	GetRegisterBinding(user string) (RegisterBinding, error)
}

type RequestContext

type RequestContext struct {
	Request *sip.Request
	// contains filtered or unexported fields
}

func (*RequestContext) Close

func (rc *RequestContext) Close()

func (*RequestContext) Dialog

func (rc *RequestContext) Dialog() *DialogMutex

func (*RequestContext) RelayRequest

func (rc *RequestContext) RelayRequest(req *sip.Request) (<-chan *sip.Response, error)

func (*RequestContext) RelayResponse

func (rc *RequestContext) RelayResponse(res *sip.Response) error

func (*RequestContext) SetLogger

func (rc *RequestContext) SetLogger(log zerolog.Logger)

SetLogger can be used to change default logger before any relaying or other action on request

func (*RequestContext) Topos

func (rc *RequestContext) Topos(req *sip.Request) *sip.Request

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL