Skip to content

Commit

Permalink
Adding TLS support
Browse files Browse the repository at this point in the history
  • Loading branch information
emiago committed Jan 1, 2023
1 parent 17acb4b commit 9ff3cc0
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 92 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/TODO.md
/proxysip
/.vscode
certs
*.pcap
/self-signed-root-ca-and-certificates.sh
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,25 @@ srv.OnBye(byeHandler)

// For registrars
// srv.OnRegister(registerHandler)


// Add listeners
srv.Listen("udp", "127.0.0.1:5060")
srv.Listen("tcp", "127.0.0.1:5061")
srv.Listen("ws", "127.0.0.1:5080")
...
// fire server
srv.Serve()
go srv.ListenAndServe(ctx, "tcp", "127.0.0.1:5061")
go srv.ListenAndServe(ctx, "ws", "127.0.0.1:5080")
go srv.ListenAndServe(ctx, "udp", "127.0.0.1:5060")
<-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 []byte)
srv.ListenAndServeTLS(ctx, "tcp", "127.0.0.1:5061", conf)

```


## Stateful Proxy build

Proxy is combination client and server handle that creates server/client transaction.
Expand Down Expand Up @@ -176,7 +180,7 @@ More on documentation you can find on [Go doc](https://pkg.go.dev/github.com/emi

- [x] UDP
- [x] TCP
- [ ] TLS
- [x] TLS
- [x] WS
- [ ] WSS

Expand Down
4 changes: 2 additions & 2 deletions example/dialog/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"flag"
"os"

Expand Down Expand Up @@ -40,8 +41,7 @@ func main() {
setupRoutes(srv, h)

log.Info().Str("ip", *extIP).Str("dst", *dst).Msg("Starting server")
srv.Listen("udp", *extIP)
if err := srv.Serve(); err != nil {
if err := srv.ListenAndServe(context.TODO(), "udp", *extIP); err != nil {
log.Error().Err(err).Msg("Fail to serve")
}
}
Expand Down
44 changes: 44 additions & 0 deletions example/proxysip/docker-compose-tls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
version: "2.4"

services:
proxy:
build:
context: ../../

# image: golang:latest
# command: go run ./example/proxysip -t tcp -ip=10.5.0.10:5060 -dst 10.5.0.20:5060 -debug
command: go run ./example/proxysip -t tcp -ip=10.5.0.10:5060 -dst 10.5.0.20:5060 -cert=certs/localhost.dev.cert.pem -key=certs/localhost.dev.key.pem -rootpems=certs/ca/localtesting_ca.crt.pem -debug
# stdin_open: true
# command: bash
# working_dir: /usr/src/sipgo
volumes:
- ../../../sipgo:/go/sipgo

cpus: 4.0
cpuset: "0,1,2,3"

networks:
mynet:
ipv4_address: 10.5.0.10

# mem_limit: 4G
# network_mode: "host"
environment:
- GOMAXPROCS=4 # Go does not match this by default
# - GODEBUG=madvdontneed=0
# - GODEBUG=madvdontneed=1,gctrace=1
# - GOGC=70

uas: # TODO

uac:
# TODO


networks:
mynet:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/16
gateway: 10.5.0.1
31 changes: 29 additions & 2 deletions example/proxysip/main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package main

import (
"context"
"encoding/json"
"flag"
"io/ioutil"
"net/http"
"os"
"runtime"
Expand Down Expand Up @@ -30,6 +32,9 @@ func main() {
extIP := flag.String("ip", "127.0.0.1:5060", "My exernal ip")
dst := flag.String("dst", "", "Destination pbx, sip server")
transportType := flag.String("t", "udp", "Transport, default will be determined by request")
certFile := flag.String("cert", "", "")
keyFile := flag.String("key", "", "")
rootPems := flag.String("rootpems", "", "")
flag.Parse()

if *pprof {
Expand Down Expand Up @@ -61,8 +66,30 @@ func main() {
// srv.TransportLayer().ConnectionReuse = false
}

srv.Listen(*transportType, *extIP)
if err := srv.Serve(); err != nil {
if *certFile != "" {
rootPems, err := ioutil.ReadFile(*rootPems)
if err != nil {
log.Fatal().Err(err).Msg("Fail to read rootpems")
}
conf, err := sipgo.GenerateTLSConfig(*certFile, *keyFile, rootPems)
if err != nil {
log.Fatal().Err(err).Msg("Fail to generate conf")
}

// keylog, err := os.OpenFile("key.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
// if err != nil {
// log.Fatal().Err(err).Msg("Fail to open keylogd")
// }

// conf.KeyLogWriter =

if err := srv.ListenAndServeTLS(context.TODO(), *transportType, *extIP, conf); err != nil {
log.Error().Err(err).Msg("Fail to start sip server")
return
}
}

if err := srv.ListenAndServe(context.TODO(), *transportType, *extIP); err != nil {
log.Error().Err(err).Msg("Fail to start sip server")
return
}
Expand Down
56 changes: 33 additions & 23 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package sipgo

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"

"github.com/emiago/sipgo/sip"
"github.com/emiago/sipgo/transport"
Expand All @@ -19,15 +22,11 @@ type Server struct {

// requestHandlers map of all registered request handlers
requestHandlers map[sip.RequestMethod]RequestHandler
listeners map[string]string //addr:network

log zerolog.Logger

requestMiddlewares []func(r *sip.Request)
responseMiddlewares []func(r *sip.Response)

// Default server behavior for sending request in preflight
RemoveViaHeader bool
}

type ServerOption func(s *Server) error
Expand Down Expand Up @@ -59,7 +58,6 @@ func newBaseServer(ua *UserAgent, options ...ServerOption) (*Server, error) {
requestMiddlewares: make([]func(r *sip.Request), 0),
responseMiddlewares: make([]func(r *sip.Response), 0),
requestHandlers: make(map[sip.RequestMethod]RequestHandler),
listeners: make(map[string]string),
log: log.Logger.With().Str("caller", "Server").Logger(),
}
for _, o := range options {
Expand All @@ -71,26 +69,14 @@ func newBaseServer(ua *UserAgent, options ...ServerOption) (*Server, error) {
return s, nil
}

// Listen adds listener for serve
func (srv *Server) Listen(network string, addr string) {
srv.listeners[addr] = network
}

// Serve will fire all listeners
func (srv *Server) Serve() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
return srv.ServeWithContext(ctx)
// Serve will fire all listeners. Ctx allows canceling
func (srv *Server) ListenAndServe(ctx context.Context, network string, addr string) error {
return srv.tp.ListenAndServe(ctx, network, addr)
}

// Serve will fire all listeners. Ctx allows canceling
func (srv *Server) ServeWithContext(ctx context.Context) error {
defer srv.shutdown()
for addr, network := range srv.listeners {
go srv.tp.ListenAndServe(ctx, network, addr)
}
<-ctx.Done()
return ctx.Err()
// Serve will fire all listeners that are secured. Ctx allows canceling
func (srv *Server) ListenAndServeTLS(ctx context.Context, network string, addr string, conf *tls.Config) error {
return srv.tp.ListenAndServeTLS(ctx, network, addr, conf)
}

// onRequest gets request from Transaction layer
Expand Down Expand Up @@ -259,3 +245,27 @@ func (srv *Server) onTransportMessage(m sip.Message) {
func (srv *Server) TransportLayer() *transport.Layer {
return srv.tp
}

// GenerateTLSConfig creates basic tls.Config that you can pass for ServerTLS
// It needs rootPems for client side
func GenerateTLSConfig(certFile string, keyFile string, rootPems []byte) (*tls.Config, error) {
roots := x509.NewCertPool()
if rootPems != nil {
ok := roots.AppendCertsFromPEM(rootPems)
if !ok {
return nil, fmt.Errorf("failed to parse root certificate")
}
}

cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, fmt.Errorf("fail to load cert. err=%w", err)
}

conf := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: roots,
}

return conf, nil
}
8 changes: 3 additions & 5 deletions server_integration_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sipgo

import (
"context"
"testing"
"time"

Expand Down Expand Up @@ -30,10 +31,8 @@ func TestWebsocket(t *testing.T) {
<-tx.Done()
})

srv.Listen("ws", "127.0.0.1:5060")

go func() {
if err := srv.Serve(); err != nil {
if err := srv.ListenAndServe(context.TODO(), "ws", "127.0.0.1:5060"); err != nil {
log.Error().Err(err).Msg("Fail to serve")
}
}()
Expand All @@ -45,9 +44,8 @@ func TestWebsocket(t *testing.T) {

csrv, err := NewServer(ua) // Create server handle
require.Nil(t, err)
csrv.Listen("ws", "127.0.0.2:5060")
go func() {
if err := csrv.Serve(); err != nil {
if err := csrv.ListenAndServe(context.TODO(), "ws", "127.0.0.2:5060"); err != nil {
log.Error().Err(err).Msg("Fail to serve")
}
}()
Expand Down
46 changes: 5 additions & 41 deletions transport/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package transport
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"math/rand"
Expand Down Expand Up @@ -131,30 +130,12 @@ func (l *Layer) ServeWS(c net.Listener) error {
}

// ServeTLS will listen on tcp connection. rootPems can be nil if there is no need for client use
func (l *Layer) ServeTLS(c net.Listener, certFile string, keyFile string, rootPems []byte) error {
func (l *Layer) ServeTLS(c net.Listener, conf *tls.Config) error {
_, port, err := sip.ParseAddr(c.Addr().String())
if err != nil {
return err
}

roots := x509.NewCertPool()
if rootPems != nil {
ok := roots.AppendCertsFromPEM(rootPems)
if !ok {
return fmt.Errorf("failed to parse root certificate")
}
}

cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return fmt.Errorf("fail to load cert. err=%w", err)
}

conf := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: roots,
}

transport := NewTLSTransport(c.Addr().String(), parser.NewParser(), conf)
l.addTransport(transport, "tls", port)

Expand Down Expand Up @@ -200,7 +181,7 @@ func (l *Layer) ListenAndServe(ctx context.Context, network string, addr string)

// Serve on any tls network. This function will block
// Network supported: tcp
func (l *Layer) ListenAndServeTLS(ctx context.Context, network string, addr string, certFile string, keyFile string, rootPems []byte) error {
func (l *Layer) ListenAndServeTLS(ctx context.Context, network string, addr string, conf *tls.Config) error {
network = strings.ToLower(network)
_, port, err := sip.ParseAddr(addr)
if err != nil {
Expand All @@ -214,27 +195,9 @@ func (l *Layer) ListenAndServeTLS(ctx context.Context, network string, addr stri

p := parser.NewParser()

roots := x509.NewCertPool()
if rootPems != nil {
ok := roots.AppendCertsFromPEM(rootPems)
if !ok {
return fmt.Errorf("failed to parse root certificate")
}
}

cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return fmt.Errorf("fail to load cert. err=%w", err)
}

conf := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: roots,
}

var t Transport
switch network {
case "tcp":
case "tcp", "tls":
t = NewTLSTransport(addr, p, conf)
// case "ws":
// t = NewWSTransport(addr, p)
Expand All @@ -243,13 +206,14 @@ func (l *Layer) ListenAndServeTLS(ctx context.Context, network string, addr stri
}

// Add transport to list
l.addTransport(t, "tls", port)
l.addTransport(t, t.Network(), port)

err = t.ListenAndServe(l.handleMessage)
return err
}

func (l *Layer) addTransport(t Transport, network string, port int) {
network = NetworkToLower(network)
if _, ok := l.listenPorts[network]; !ok {
if l.listenPorts[network] == nil {
l.listenPorts[network] = make([]int, 0)
Expand Down
Loading

0 comments on commit 9ff3cc0

Please sign in to comment.