# sipgo **Repository Path**: jf_linux/sipgo ## Basic Information - **Project Name**: sipgo - **Description**: No description available - **Primary Language**: Unknown - **License**: BSD-2-Clause - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-11-30 - **Last Updated**: 2024-11-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README SIPGO [![Go Report Card](https://goreportcard.com/badge/github.com/emiago/sipgo)](https://goreportcard.com/report/github.com/emiago/sipgo) ![Used By](https://sourcegraph.com/github.com/emiago/sipgo/-/badge.svg) ![Coverage](https://img.shields.io/badge/coverage-47.0%25-blue) [![License](https://img.shields.io/badge/License-BSD_2--Clause-orange.svg)](https://github.com/emiago/sipgo/LICENCE) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/emiago/sipgo) **SIPGO** is library for writing fast SIP services in GO language. It comes with [SIP stack](/sip/README.md) ([RFC 3261](https://datatracker.ietf.org/doc/html/rfc3261)|[RFC3581](https://datatracker.ietf.org/doc/html/rfc3581)|[RFC6026](https://datatracker.ietf.org/doc/html/rfc6026) optimized for fast parsing. --- **NOTE**: LIBRARY IS IN PROCESS GETTING TO 1.0. THIS MAY TAKE TIME UNTIL WE CLOSE ALL ISSUES. PLEASE OPEN ISSUES FOR DISCUSSION FIRST INSTEAD PULL REQUESTS. OTHER NOTES: - dialog managment may be refactored or reduced only to keep some basic functionality handling dialogs per RFC. Rest is moved to diago project - only small optimizations/refactoring is considered to happen. - if something is missing before 1.0 and it is good to have, it will be moved to sipgox package. --- **Libs on top of sipgo:** - ***diago*** [github.com/emiago/diago](https://github.com/emiago/diago): Full VOIP library/framework with media stack - ***sipgox*** [github.com/emiago/sipgox](https://github.com/emiago/sipgox): Fast building SIP phone or SIP helpers (It is recomended to switch to **Diago**) Fetch lib with: `go get github.com/emiago/sipgo` If you like/use project currently and looking for support/sponsoring checkout [Support section](#support) You can follow on [X/Twitter](https://twitter.com/emiago123) for more updates. More on documentation you can find on [Go doc](https://pkg.go.dev/github.com/emiago/sipgo) ## Supported protocols - [x] UDP - [x] TCP - [x] TLS - [x] WS - [x] WSS ## Examples - Stateful proxy [example/proxysip](example/proxysip) - Register with authentication [example/register](example/register) - RTP echo with sipgox [example/dialog](https://github.com/emiago/sipgox/tree/main/echome) Also thanks to [pion](https://github.com/pion/webrtc) project sharing this example of using SIPgo with webrtc: - https://github.com/pion/example-webrtc-applications/tree/master/sip-to-webrtc original post [on X](https://twitter.com/_pion/status/1742955942314913958) ## Tools developed: - CLI softphone for easy testing [gophone](https://github.com/emiago/gophone) - Simple proxy where NAT is problem [psip](https://github.com/emiago/psip) - ... *your tool can be here* ## Performance As example you can find `example/proxysip` as simple version of statefull proxy. It is used for stress testing with `sipp`. To find out more about performance check the latest results: [example/proxysip](example/proxysip) # Usage Lib allows you to write easily sip servers, clients, stateful proxies, registrars or any sip routing. Writing in GO we are not limited to handle SIP requests/responses in many ways, or to integrate and scale with any external services (databases, caches...). ## UAS/UAC build Using server or client handle for UA you can build incoming or outgoing requests. ```go ua, _ := sipgo.NewUA() // Build user agent srv, _ := sipgo.NewServer(ua) // Creating server handle for ua client, _ := sipgo.NewClient(ua) // Creating client handle for ua srv.OnInvite(inviteHandler) srv.OnAck(ackHandler) srv.OnBye(byeHandler) // For registrars // srv.OnRegister(registerHandler) ctx, _ := signal.NotifyContext(ctx, os.Interrupt) go srv.ListenAndServe(ctx, "udp", "127.0.0.1:5060") go srv.ListenAndServe(ctx, "tcp", "127.0.0.1:5061") go srv.ListenAndServe(ctx, "ws", "127.0.0.1:5080") <-ctx.Done() ``` - Server handle creates listeners and reacts on incoming requests. [More on server transactions](#server-transaction) - Client handle allows creating transaction requests [More on client transactions](#client-transaction) ### TLS transports ```go // TLS conf := sipgo.GenerateTLSConfig(certFile, keyFile, rootPems) srv.ListenAndServeTLS(ctx, "tcp", "127.0.0.1:5061", conf) srv.ListenAndServeTLS(ctx, "ws", "127.0.0.1:5081", conf) ``` ### UAC first If you are acting as client first, you can say to client which host:port to use, and this connection will be reused until closing UA. Any request received can be still processed with server handle. ```go ua, _ := sipgo.NewUA() // Build user agent defer ua.Close() client, _ := sipgo.NewClient(ua, sipgo.WithClientHostname("127.0.0.1"), sipgo.WithClientPort(5060)) server, _ := sipgo.NewServer(ua) srv.OnBye(func(req *sip.Request, tx sip.ServerTransaction)) { // This will be received on 127.0.0.1:5060 } tx, err := client.TransactionRequest(ctx, sip.NewRequest(sip.INVITE, recipient)) ``` ## Server Transaction Server transaction is passed on handler ```go // Incoming request srv.OnInvite(func(req *sip.Request, tx sip.ServerTransaction) { res := sip.NewResponseFromRequest(req, code, reason, body) // Send response tx.Respond(res) select { case m := <-tx.Acks(): // Handle ACK for response . ACKs on 2xx are send as different request case <-tx.Done(): // Signal transaction is done. // Check any errors with tx.Err() to have more info why terminated return } // terminating handler terminates Server transaction automaticaly }) ``` ## Server stateless response ```go srv := sipgo.NewServer() ... func ackHandler(req *sip.Request, tx sip.ServerTransaction) { res := sip.NewResponseFromRequest(req, code, reason, body) srv.WriteResponse(res) } srv.OnACK(ackHandler) ``` ## Client Transaction Using client handle allows easy creating and sending request. Unless you customize transaction request with opts by default `client.TransactionRequest` will build all other headers needed to pass correct sip request. Here is full example: ```go ctx := context.Background() client, _ := sipgo.NewClient(ua) // Creating client handle // Request is either from server request handler or created req.SetDestination("10.1.2.3") // Change sip.Request destination tx, err := client.TransactionRequest(ctx, req) // Send request and get client transaction handle defer tx.Terminate() // Client Transaction must be terminated for cleanup ... select { case res := <-tx.Responses(): // Handle responses case <-tx.Done(): // Wait for termination return } ``` ## Client Do request Unless you need more control over [Client Transaction](#client-transaction) you can simply go with client `Do` request and wait final response. ```go req := sip.NewRequest(sip.INVITE, sip.Uri{User:"bob", Host: "example.com"}) res, err := client.Do(req) ``` ## Client stateless request ```go client, _ := sipgo.NewClient(ua) // Creating client handle req := sip.NewRequest(method, recipient) // Send request and forget client.WriteRequest(req) ``` ## Dialog handling `DialogUA` is helper struct to create `Dialog`. **Dialog** can be **as server** or **as client** created. Later on this provides you RFC way of sending request within dialog `Do` or `TransactionRequest` functions. For basic usage `DialogClientCache` and `DialogServerCache` are created to be part of library to manage and cache dialog accross multiple request. They are seperated based on your **request context**, but they act more like `peer`. --- **NOTE**: **It is recomended that you build your OWN Dialog Server/Client Cache mechanism for dialogs.** --- For basic control some handling request wrappers like `Ack`, `Bye`, `ReadAck`, `ReadBye` is provided. Sending any other request should be done with `Do` or receiving can be validated with `ReadRequest` **UAC**: ```go ua, _ := sipgo.NewUA() // Build user agent srv, _ := sipgo.NewServer(ua) // Creating server handle client, _ := sipgo.NewClient(ua) // Creating client handle contactHDR := sip.ContactHeader{ Address: sip.Uri{User: "test", Host: "127.0.0.200", Port: 5088}, } dialogCli := sipgo.NewDialogClientCache(client, contactHDR) // Attach Bye handling for dialog srv.OnBye(func(req *sip.Request, tx sip.ServerTransaction) { err := dialogCli.ReadBye(req, tx) //handle error }) // Create dialog session dialog, err := dialogCli.Invite(ctx, recipientURI, nil) defer dialog.Close() // Cleans up from dialog pool // Wait for answer err = dialog.WaitAnswer(ctx, AnswerOptions{}) // Check dialog response dialog.InviteResponse (SDP) and return ACK err = dialog.Ack(ctx) // Send BYE to terminate call err = dialog.Bye(ctx) ``` **UAS**: ```go ua, _ := sipgo.NewUA() // Build user agent srv, _ := sipgo.NewServer(ua) // Creating server handle client, _ := sipgo.NewClient(ua) // Creating client handle uasContact := sip.ContactHeader{ Address: sip.Uri{User: "test", Host: "127.0.0.200", Port: 5099}, } dialogSrv := sipgo.NewDialogServerCache(client, uasContact) srv.OnInvite(func(req *sip.Request, tx sip.ServerTransaction) { dlg, err := dialogSrv.ReadInvite(req, tx) if err != nil { return err } defer dlg.Close() // Close for cleanup from cache // handle error dlg.Respond(sip.StatusTrying, "Trying", nil) dlg.Respond(sip.StatusOK, "OK", nil) // Instead Done also dlg.State() can be used for granular state checking <-dlg.Context().Done() }) srv.OnAck(func(req *sip.Request, tx sip.ServerTransaction) { dialogSrv.ReadAck(req, tx) }) srv.OnBye(func(req *sip.Request, tx sip.ServerTransaction) { dialogSrv.ReadBye(req, tx) }) ``` ### Dialog Do/Transaction request For any other request within dialog you should use `dialog.Do` to send request. Requests withing dialog need more checks to pass and therefore this API helps to keep requests within dialog session. ```go req := sip.NewRequest(sip.INFO, recipient) res, err := dialog.Do(ctx, req) ``` ## Stateful Proxy build Proxy is combination client and server handle that creates server/client transaction. They need to share same **ua** same like uac/uas build. Forwarding request is done via client handle: ```go ua, _ := sipgo.NewUA() // Build user agent srv, _ := sipgo.NewServer(ua) // Creating server handle client, _ := sipgo.NewClient(ua) // Creating client handle srv.OnInvite(func(req *sip.Request, tx sip.ServerTransaction) { ctx := context.Background() req.SetDestination("10.1.2.3") // Change sip.Request destination // Start client transaction and relay our request. Add Via and Record-Route header clTx, err := client.TransactionRequest(ctx, req, sipgo.ClientRequestAddVia, sipgo.ClientRequestAddRecordRoute) // Send back response res := <-cltx.Responses() tx.Respond(res) }) ``` ## SIP Debug You can have full SIP messages dumped from transport into Debug level message. Example: ```go sip.SIPDebug = true ``` ``` Feb 24 23:32:26.493191 DBG UDP read 10.10.0.10:5060 <- 10.10.0.100:5060: SIP/2.0 100 Trying Via: SIP/2.0/UDP 10.10.0.10:5060;rport=5060;received=10.10.0.10;branch=z9hG4bK.G3nCwpXAKJQ0T2oZUII70wuQx9NeXc61;alias Via: SIP/2.0/UDP 10.10.1.1:5060;branch=z9hG4bK-1-1-0 Record-Route: Call-ID: 1-1@10.10.1.1 From: "sipp" ;tag=1SIPpTag001 To: "uac" CSeq: 1 INVITE Server: Asterisk PBX 18.16.0 Content-Length: 0 ``` ## Support If you find this project interesting for bigger support or consulting, you can contact me on [mail](mailto:emirfreelance91@gmail.com) For bugs features pls create [issue](https://github.com/emiago/sipgo/issues). ## Trusted By babelforce      avero --- *If you are using in company, your logo can be here.* ## Extra ### E2E/integration testing If you are interested using lib for your testing services then checkout [article on how easy you can make calls and other](https://github.com/emiago/sipgo/wiki/E2E-testing) ### Tests Library will be covered with more tests. Focus is more on benchmarking currently. ``` go test ./... ``` ## Credits This project was influenced by [gosip](https://github.com/ghettovoice/gosip), project by @ghetovoice, but started as new project to achieve best/better performance and to improve API. This unfortunately required many design changes, therefore this libraries are not compatible.