libnmdc: fix panic() on connection failure, owing to storing nil in an interface

This commit is contained in:
mappu 2016-04-03 13:15:57 +12:00
parent 052c3e2cba
commit ec0a781538
2 changed files with 29 additions and 7 deletions

View File

@ -3,9 +3,11 @@ package libnmdc
import ( import (
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
"reflect"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@ -65,6 +67,7 @@ const (
var rx_protocolMessage *regexp.Regexp var rx_protocolMessage *regexp.Regexp
var rx_publicChat *regexp.Regexp var rx_publicChat *regexp.Regexp
var rx_incomingTo *regexp.Regexp var rx_incomingTo *regexp.Regexp
var ErrNotConnected error = errors.New("Not connected")
func init() { func init() {
rx_protocolMessage = regexp.MustCompile("(?ms)^[^|]*|") rx_protocolMessage = regexp.MustCompile("(?ms)^[^|]*|")
@ -92,10 +95,21 @@ type HubConnection struct {
OnEvent chan HubEvent OnEvent chan HubEvent
// Private state // Private state
conn net.Conn conn net.Conn // this is an interface
sentOurHello bool sentOurHello bool
} }
func isNil(a interface{}) bool {
// Workaround to catch the potential for storing nil in an interface.
// @ref http://stackoverflow.com/q/13476349
defer func() { recover() }()
return a == nil || reflect.ValueOf(a).IsNil()
}
func (this *HubConnection) isConnValid() bool {
return isNil(this.conn)
}
type HubEvent struct { type HubEvent struct {
EventType HubEventType EventType HubEventType
Nick string Nick string
@ -159,8 +173,12 @@ func (this *HubConnection) userJoined_Full(uinf *UserInfo) {
// Note that protocol messages are transmitted on the caller thread, not from // Note that protocol messages are transmitted on the caller thread, not from
// any internal libnmdc thread. // any internal libnmdc thread.
func (this *HubConnection) SayRaw(protocolCommand string) error { func (this *HubConnection) SayRaw(protocolCommand string) error {
_, err := this.conn.Write([]byte(protocolCommand)) if this.isConnValid() {
return err _, err := this.conn.Write([]byte(protocolCommand))
return err
} else {
return ErrNotConnected
}
} }
func parseLock(lock []byte) string { func parseLock(lock []byte) string {
@ -333,11 +351,12 @@ func (this *HubConnection) worker() {
if err == nil { if err == nil {
this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING} this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING}
this.conn = nil // WARNING: storing nil in an interface(!)
} }
} }
// Read from socket into our local buffer (blocking) // Read from socket into our local buffer (blocking)
if this.conn != nil { if this.isConnValid() {
readBuff := make([]byte, 4096) readBuff := make([]byte, 4096)
nbytes, err = this.conn.Read(readBuff) nbytes, err = this.conn.Read(readBuff)
if nbytes > 0 { if nbytes > 0 {

View File

@ -15,6 +15,7 @@ var BaseDir string = "."
var PMResponse string = "" var PMResponse string = ""
var LogConnectionState bool var LogConnectionState bool
var DebugMode bool var DebugMode bool
var VerifyTLS bool
var CharacterMatcher *regexp.Regexp var CharacterMatcher *regexp.Regexp
func init() { func init() {
@ -53,9 +54,10 @@ func LogMessage(hub, message string) {
func HubWorker(addr, nick, password string) { func HubWorker(addr, nick, password string) {
opts := libnmdc.HubConnectionOptions{ opts := libnmdc.HubConnectionOptions{
Address: libnmdc.HubAddress(addr), Address: libnmdc.HubAddress(addr),
Self: libnmdc.UserInfo{Nick: nick}, SkipVerifyTLS: !VerifyTLS,
NickPassword: password, Self: libnmdc.UserInfo{Nick: nick},
NickPassword: password,
} }
hub := opts.Connect() hub := opts.Connect()
@ -104,6 +106,7 @@ func main() {
flag.BoolVar(&LogConnectionState, "LogConnectionState", true, "Include connection state changes in log") flag.BoolVar(&LogConnectionState, "LogConnectionState", true, "Include connection state changes in log")
flag.StringVar(&PMResponse, "PMResponse", "This is an automated service. For enquiries, please contact an administrator.", "Message to respond with on PM") flag.StringVar(&PMResponse, "PMResponse", "This is an automated service. For enquiries, please contact an administrator.", "Message to respond with on PM")
flag.BoolVar(&DebugMode, "Debug", false, "Print additional information on stdout") flag.BoolVar(&DebugMode, "Debug", false, "Print additional information on stdout")
flag.BoolVar(&VerifyTLS, "VerifyTLS", true, "Verify TLS certificates")
flag.Parse() flag.Parse()
// Assert dir exists // Assert dir exists