Giter Site home page Giter Site logo

gossip's Introduction

gossip

SIP stack in Golang, for use by stateful SIP UAs, whether client, server, or proxy.

Project status

Version: V0.2

Gossip is now capable of basic SIP 2.0 transactions over UDP and TCP and has been live tested with real softphones. It is still missing some compatibility features, isn't well tested and has a few known bugs.

APIs will change without warning until V1.0.

This readme will be updated as work progresses.

gossip's People

Contributors

kuun avatar rynorris avatar stefankopieczek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gossip's Issues

Gossip re-orders header and URI params

  • If we are acting as a transparent proxy, we may reorder header params and URI params when we forward messages.
  • Gossip processes header and URI params in an undefined order.
  • String representations of SIP messages received by gossip may show params in a diferent order to that received off the wire.

This is because the Params object that stores them holds them in a map, which doesn't have a defined iteration order.

Fixer should uncomment the broken stringify tests.

Unable to capture responses corresponding to a request

Here's a small test I wrote to check register messages. It is almost similar to existent test for INVITE message with some modifications

var userid string = "user1"
func TestSendRegisterUDP(t *testing.T) {
    register, err := request([]string{
        "REGISTER sip:ims.hom SIP/2.0",
        "Via: SIP/2.0/UDP " + c_CLIENT + ";rport;branch=z9hG4bK996329494",
        "Route: <sip:pcscf.ims.hom;lr>",
        "From: <sip:"+userid+"@ims.hom>;tag=1224392795",
        "To: <sip:"+userid+"@ims.hom>",
        "Call-ID: 1379275388",
        "CSeq: 1 REGISTER",
        "Contact: <sip:"+userid+"@192.168.1.39:5060>",
        "Authorization: Digest username=\""+userid+"@ims.hom\", realm=\"ims.hom\", nonce=\" \", uri=\"sip:ims.hom\", response=\" \"",
        "Max-Forwards: 70",
        "User-Agent: eXosip/3.1.0",
        "Expires: 600000",
        "Supported: path",
        "Supported: gruu",
        "Content-Length: 0",
        "",
        "",
    })
    assertNoError(t, err)
    ok, err := response([]string{
        "SIP/2.0 401 Unauthorized",
        "CSeq: 1 REGISTER",
        "Via: SIP/2.0/UDP " + c_SERVER + ";branch=z9hG4bK996329494",
        "",
        "",
    })

    test := transactionTest{actions: []action{
        &clientSend{register},
        &clientRecv{ok},
    }}
    test.Execute(t)
}

func (test *transactionTest) Execute(t *testing.T) {
    var err error
    test.client, err = NewManager("udp", c_CLIENT)
    assertNoError(t, err)
    defer test.client.Stop()

    for _, actn := range test.actions {
        testLog.Debug("Performing action %v", actn)
        actn.Act(test)
    }
}

type clientSend struct {
    msg *base.Request
}

func (actn *clientSend) Act(test *transactionTest) error {
    test.lastTx = test.client.Send(actn.msg, c_SERVER)
    return nil
}

type clientRecv struct {
    expected *base.Response
}

func (actn *clientRecv) Act(test *transactionTest) error {
    go func() error {
        for {
            fmt.Println("Checking Responses")
            responses := test.lastTx.Responses()
            select {
            case response, ok := <-responses:
                if !ok {
                    return fmt.Errorf("Response channel prematurely closed")
                } else if response.String() != actn.expected.String() {
                    return fmt.Errorf("Unexpected response:\n%s", response.String())
                } else {
                    return nil
                }
            case <-time.After(time.Duration(1)*time.Second):
                return fmt.Errorf("Timed out waiting for response")
            }
        }
    }()
    return nil
}

However, the issue here is, although I can see a response from the server via tcpdump, I'm not able to capture it via the response channel.
I've couple of questions:

  • Is this the correct way to listen for response? If not, can you please guide me on the same.
  • If this is the correct way, is there any way I could help fix the bug?

Note: We've a working version of Project Clearwater which acts like a server.

Examples

Hello,

Any chance of you including some example apps? Maybe something akin to a "hello-world.go" ?

John

Replies are being sent back from a random source port

Hello,
First of all, thanks for the excellent work..
Seems like when running a UDP 'server' all replies are being sent from a random source port.
This will mostly work but in many cases (especially NATed clinets) it might cause some problems. Firewalls will usually drop such message if the source/dest port/ip cannot be matched.
Ideally, the server should send responses to the same ip:port that the request was received from.

Panic in connWatcher when connTable is stopped

If connTable.Stop() is called when at least one connection in the table has expired, the connWatcher panics when it receives the stop message from the connTable.

Cause is that when the connWatcher nil-s out its connection both on expiry and when it is stopped; the second nil causes a nil dereference.

Could just do a nil check here, but there's a wider issue that the connWatcher should be GC'd on socket expiry, and it currently isn't. We can't just remove it from the table inline, because we're running on a separate goroutine so this could cause races. So we'll need a dedicated goroutine for the connTable which serializes conn updates and expiries.

Interface rewrite

Our interface is a bit weird at the moment, and uses channels and stuff to communicate with the user. I think exposing channels like this is a bad idea. If we expose a simpler interface, the user can decide to send the messages down channels themselves if they like.

I propose a handler based interface similar to net/http. Something like:

// Create transport.
transport := transport.NewUDP(...)

// Create TM.
tm := &transaction.Manager{
    Transport: transport,
    Handler: myHandler,
}

// Blocks forever.
tm.ListenAndServeSIP()

Client API becomes blocking (greatly simplifying testing)

// Non-invite flow.
req := &base.Request{...}
resp, err := tm.Send(req)
// ... do stuff
// Invite flow.
req := &base.Request{...}
resp, err := tm.Send(req)
tm.Ack(resp)

Additionally propose renaming "base" package to "sip" so the main objects are called helpful things like sip.Request, sip.Response etc etc.

[Bug] Uninitialized timer, Sending Requests via TCP

I was trying to run the INVITE tests on top of TCP as opposed to that of UDP.
Here is a minor relevant log:

=== RUN TestAAAASetup
--- PASS: TestAAAASetup (0.00s)
=== RUN TestSendInviteTCP
DEBUG: Chan 0x1854430c gets '0'
DEBUG: Chan 0x1854430c sends '0'
DEBUG: Parser buffer returns line 'INVITE sip:[email protected] SIP/2.0'
DEBUG: Parser buffer returns line 'Via: SIP/2.0/TCP 192.168.1.39:5060;branch=z9hG4bK776asdhds'
DEBUG: Parser buffer returns line ''
DEBUG: Parser 0x18544300 parsing header "Via: SIP/2.0/TCP 192.168.1.39:5060;branch=z9hG4bK776asdhds"
DEBUG: Parser buffer returns chunk ''
DEBUG: Stopping parser 0x18544300
DEBUG: Parser 0x18544300 stopped
DEBUG: Notifier 0x18536b20 has new listener 0x18544640
>>> DEBUG: Performing action &{0x18544480}
DEBUG: [manager::Send]Sending to 192.168.1.41:5060: INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/TCP 192.168.1.39:5060;branch=z9hG4bK776asdhds


DEBUG: Query connection for address 192.168.1.41:5060 returns nil (no registered watcher)
DEBUG: No stored connection for address 192.168.1.41:5060; generate a new one
DEBUG: Parser 0x18544300 stopped
INFO: Begin serving TCP on address 192.168.1.39:5060
DEBUG: Chan 0x1854430c will dispose
DEBUG: Chan 0x1854430c disposing...
DEBUG: Chan 0x1854430c disposed
DEBUG: No connection watcher registered for 192.168.1.41:5060; spawn one
DEBUG: Connection 0x18536cc0 waiting for new data on sock
DEBUG: Stopping parser 0x18544880
DEBUG: Parser 0x18544880 stopped
panic: time: Stop called on uninitialized Timer

goroutine 16 [running]:
time.(*Timer).Stop(0x18538660, 0x18544900)
    /usr/local/go/src/time/sleep.go:59 +0x61
github.com/stefankopieczek/gossip/transport.func·002(0x18536d20)
    /home/ubuntu32/gossip/src/github.com/stefankopieczek/gossip/transport/conntable.go:66 +0x2fb
created by github.com/stefankopieczek/gossip/transport.(*connTable).Notify
    /home/ubuntu32/gossip/src/github.com/stefankopieczek/gossip/transport/conntable.go:79 +0x32a

Is the following function correct place to initialize the timer and fix the bug?

// Create a new connection table.
func (t *connTable) Init() {
    t.conns = make(map[string]*connWatcher)
}

Why Send via UDP should open and close connection?

Hi,
I would like to ask what is the reason behind opening a new connection and closing it immediately when sending the data using Send() function on UDP transport?

Why don't the function use the one from listeningPoints as there should be a minimum of one connection in here due to the call of Listen() in transport.manager.NewManager() ?

And shouldn't we also need to wait for response of the Send() function (e.g. in ClientTransaction case) in the same source port? Closing the connection will close the source port and the response will not be received.

Improve logging, bonus edition

Logs should include the last line of the stack trace before the log call, and a line reference if possible, e.g.

2015-07-17 08:40 [18425] foo.go (fooDoer.doBar:83): The fooDoer did a bar.

Where is the main function

Hi,
I am trying to lean the sip protocol in Go. I am new to network programming. I wanted to follow the code from main section until I understand how its implemnted. I cant find the main function. How is this code/server started.
Regards
Bheki

Abandoned?

I'd like to use this library in one of my projects, but it looks quite abandoned with no licensing.

Is there a plan to do any more work on this?

transport.Manager.Stop() returns before closing all sockets

... they close shortly afterwards, but there's a race window where Stop() has returned but the sockets are still bound.

This means that the sockets can't be reused immediately – subsequent binds might fail with a "socket in use" if they happen too soon after the previous manager is stopped.

In particular this causes occasional failures in the transport tests.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.