commit
a08c332184
20
irc.go
20
irc.go
@ -439,6 +439,25 @@ func (irc *Connection) Connect(server string) error {
|
|||||||
if len(irc.Password) > 0 {
|
if len(irc.Password) > 0 {
|
||||||
irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password)
|
irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resChan := make(chan *SASLResult)
|
||||||
|
if irc.UseSASL {
|
||||||
|
irc.setupSASLCallbacks(resChan)
|
||||||
|
irc.pwrite <- fmt.Sprintf("CAP LS\r\n")
|
||||||
|
// request SASL
|
||||||
|
irc.pwrite <- fmt.Sprintf("CAP REQ :sasl\r\n")
|
||||||
|
// if sasl request doesn't complete in 15 seconds, close chan and timeout
|
||||||
|
select {
|
||||||
|
case res := <-resChan:
|
||||||
|
if res.Failed {
|
||||||
|
close(resChan)
|
||||||
|
return res.Err
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second * 15):
|
||||||
|
close(resChan)
|
||||||
|
return errors.New("SASL setup timed out. This shouldn't happen.")
|
||||||
|
}
|
||||||
|
}
|
||||||
irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick)
|
irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick)
|
||||||
irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user)
|
irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user)
|
||||||
return nil
|
return nil
|
||||||
@ -466,6 +485,7 @@ func IRC(nick, user string) *Connection {
|
|||||||
KeepAlive: 4 * time.Minute,
|
KeepAlive: 4 * time.Minute,
|
||||||
Timeout: 1 * time.Minute,
|
Timeout: 1 * time.Minute,
|
||||||
PingFreq: 15 * time.Minute,
|
PingFreq: 15 * time.Minute,
|
||||||
|
SASLMech: "PLAIN",
|
||||||
QuitMessage: "",
|
QuitMessage: "",
|
||||||
}
|
}
|
||||||
irc.setupCallbacks()
|
irc.setupCallbacks()
|
||||||
|
@ -14,16 +14,20 @@ import (
|
|||||||
|
|
||||||
type Connection struct {
|
type Connection struct {
|
||||||
sync.WaitGroup
|
sync.WaitGroup
|
||||||
Debug bool
|
Debug bool
|
||||||
Error chan error
|
Error chan error
|
||||||
Password string
|
Password string
|
||||||
UseTLS bool
|
UseTLS bool
|
||||||
TLSConfig *tls.Config
|
UseSASL bool
|
||||||
Version string
|
SASLLogin string
|
||||||
Timeout time.Duration
|
SASLPassword string
|
||||||
PingFreq time.Duration
|
SASLMech string
|
||||||
KeepAlive time.Duration
|
TLSConfig *tls.Config
|
||||||
Server string
|
Version string
|
||||||
|
Timeout time.Duration
|
||||||
|
PingFreq time.Duration
|
||||||
|
KeepAlive time.Duration
|
||||||
|
Server string
|
||||||
|
|
||||||
socket net.Conn
|
socket net.Conn
|
||||||
pwrite chan string
|
pwrite chan string
|
||||||
|
54
sasl.go
Normal file
54
sasl.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SASLResult struct {
|
||||||
|
Failed bool
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (irc *Connection) setupSASLCallbacks(result chan<- *SASLResult) {
|
||||||
|
irc.AddCallback("CAP", func(e *Event) {
|
||||||
|
if len(e.Arguments) == 3 {
|
||||||
|
if e.Arguments[1] == "LS" {
|
||||||
|
if !strings.Contains(e.Arguments[2], "sasl") {
|
||||||
|
result <- &SASLResult{true, errors.New("no SASL capability " + e.Arguments[2])}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e.Arguments[1] == "ACK" {
|
||||||
|
if irc.SASLMech != "PLAIN" {
|
||||||
|
result <- &SASLResult{true, errors.New("only PLAIN is supported")}
|
||||||
|
}
|
||||||
|
irc.SendRaw("AUTHENTICATE " + irc.SASLMech)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
irc.AddCallback("AUTHENTICATE", func(e *Event) {
|
||||||
|
str := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s\x00%s\x00%s", irc.SASLLogin, irc.SASLLogin, irc.SASLPassword)))
|
||||||
|
irc.SendRaw("AUTHENTICATE " + str)
|
||||||
|
})
|
||||||
|
irc.AddCallback("901", func(e *Event) {
|
||||||
|
irc.SendRaw("CAP END")
|
||||||
|
irc.SendRaw("QUIT")
|
||||||
|
result <- &SASLResult{true, errors.New(e.Arguments[1])}
|
||||||
|
})
|
||||||
|
irc.AddCallback("902", func(e *Event) {
|
||||||
|
irc.SendRaw("CAP END")
|
||||||
|
irc.SendRaw("QUIT")
|
||||||
|
result <- &SASLResult{true, errors.New(e.Arguments[1])}
|
||||||
|
})
|
||||||
|
irc.AddCallback("903", func(e *Event) {
|
||||||
|
irc.SendRaw("CAP END")
|
||||||
|
result <- &SASLResult{false, nil}
|
||||||
|
})
|
||||||
|
irc.AddCallback("904", func(e *Event) {
|
||||||
|
irc.SendRaw("CAP END")
|
||||||
|
irc.SendRaw("QUIT")
|
||||||
|
result <- &SASLResult{true, errors.New(e.Arguments[1])}
|
||||||
|
})
|
||||||
|
}
|
40
sasl_test.go
Normal file
40
sasl_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// set SASLLogin and SASLPassword environment variables before testing
|
||||||
|
func TestConnectionSASL(t *testing.T) {
|
||||||
|
SASLServer := "irc.freenode.net:7000"
|
||||||
|
SASLLogin := os.Getenv("SASLLogin")
|
||||||
|
SASLPassword := os.Getenv("SASLPassword")
|
||||||
|
|
||||||
|
if SASLLogin == "" {
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
irccon := IRC("go-eventirc", "go-eventirc")
|
||||||
|
irccon.VerboseCallbackHandler = true
|
||||||
|
irccon.Debug = true
|
||||||
|
irccon.UseTLS = true
|
||||||
|
irccon.UseSASL = true
|
||||||
|
irccon.SASLLogin = SASLLogin
|
||||||
|
irccon.SASLPassword = SASLPassword
|
||||||
|
irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
irccon.AddCallback("001", func(e *Event) { irccon.Join("#go-eventirc") })
|
||||||
|
|
||||||
|
irccon.AddCallback("366", func(e *Event) {
|
||||||
|
irccon.Privmsg("#go-eventirc", "Test Message SASL\n")
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
irccon.Quit()
|
||||||
|
})
|
||||||
|
|
||||||
|
err := irccon.Connect(SASLServer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("SASL failed")
|
||||||
|
}
|
||||||
|
irccon.Loop()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user