-
Notifications
You must be signed in to change notification settings - Fork 43
/
inbound.go
120 lines (109 loc) · 3.58 KB
/
inbound.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
* Copyright (c) 2020 Percipia
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* Contributor(s):
* Andrew Querol <aquerol@percipia.com>
*/
package eslgo
import (
"context"
"fmt"
"github.com/percipia/eslgo/command"
"net"
"time"
)
// InboundOptions - Used to dial a new inbound ESL connection to FreeSWITCH
type InboundOptions struct {
Options // Generic common options to both Inbound and Outbound Conn
Network string // The network type to use, should always be tcp, tcp4, tcp6.
Password string // The password used to authenticate with FreeSWITCH. Usually ClueCon
OnDisconnect func() // An optional function to be called with the inbound connection gets disconnected
AuthTimeout time.Duration // How long to wait for authentication to complete
}
// DefaultOutboundOptions - The default options used for creating the inbound connection
var DefaultInboundOptions = InboundOptions{
Options: DefaultOptions,
Network: "tcp",
Password: "ClueCon",
AuthTimeout: 5 * time.Second,
}
// Dial - Connects to FreeSWITCH ESL at the provided address and authenticates with the provided password. onDisconnect is called when the connection is closed either by us, FreeSWITCH, or network error
func Dial(address, password string, onDisconnect func()) (*Conn, error) {
opts := DefaultInboundOptions
opts.Password = password
opts.OnDisconnect = onDisconnect
return opts.Dial(address)
}
// Dial - Connects to FreeSWITCH ESL on the address with the provided options. Returns the connection and any errors encountered
func (opts InboundOptions) Dial(address string) (*Conn, error) {
c, err := net.Dial(opts.Network, address)
if err != nil {
return nil, err
}
connection := newConnection(c, false, opts.Options)
// First auth
<-connection.responseChannels[TypeAuthRequest]
authCtx, cancel := context.WithTimeout(connection.runningContext, opts.AuthTimeout)
err = connection.doAuth(authCtx, command.Auth{Password: opts.Password})
cancel()
if err != nil {
// Try to gracefully disconnect, we have the wrong password.
connection.ExitAndClose()
if opts.OnDisconnect != nil {
go opts.OnDisconnect()
}
return nil, err
} else {
connection.logger.Info("Successfully authenticated %s\n", connection.conn.RemoteAddr())
}
// Inbound only handlers
go connection.authLoop(command.Auth{Password: opts.Password}, opts.AuthTimeout)
go connection.disconnectLoop(opts.OnDisconnect)
return connection, nil
}
func (c *Conn) disconnectLoop(onDisconnect func()) {
select {
case <-c.responseChannels[TypeDisconnect]:
c.Close()
if onDisconnect != nil {
onDisconnect()
}
return
case <-c.runningContext.Done():
return
}
}
func (c *Conn) authLoop(auth command.Auth, authTimeout time.Duration) {
for {
select {
case <-c.responseChannels[TypeAuthRequest]:
authCtx, cancel := context.WithTimeout(c.runningContext, authTimeout)
err := c.doAuth(authCtx, auth)
cancel()
if err != nil {
c.logger.Warn("Failed to auth %e\n", err)
// Close the connection, we have the wrong password
c.ExitAndClose()
return
} else {
c.logger.Info("Successfully authenticated %s\n", c.conn.RemoteAddr())
}
case <-c.runningContext.Done():
return
}
}
}
func (c *Conn) doAuth(ctx context.Context, auth command.Auth) error {
response, err := c.SendCommand(ctx, auth)
if err != nil {
return err
}
if !response.IsOk() {
return fmt.Errorf("failed to auth %#v", response)
}
return nil
}