func CertAuth(c net.Conn, user, _, cred []byte) bool { tc, ok := c.(*tls.Conn) if !ok { return false } // discard path suffix i := bytes.IndexByte(user, '/') if i > 1 { user = user[0:i] } s := tc.ConnectionState() for _, chain := range s.VerifiedChains { cert := chain[0] if ssmp.Equal(user, cert.Subject.CommonName) { return true } for _, altName := range cert.DNSNames { if ssmp.Equal(user, altName) { return true } } for _, altName := range cert.EmailAddresses { if ssmp.Equal(user, altName) { return true } } } return false }
// NewConnection creates a SSMP connection out of a streaming netwrok connection. // // This method blocks until either a first message is received or a 10s timeout // elapses. // // Each accepted connection spawns a goroutine continuously reading from the // underlying network connection and triggering the Dispatcher. The caller must // keep track of the returned Connection and call the Close method to stop the // read goroutine and close the udnerlying netwrok connection. // // errInvalidLogin is returned if the first message is not a well-formed LOGIN // request. // errUnauthorized is returned if the authenticator doesn't accept the provided // credentials. func NewConnection(c net.Conn, a Authenticator, d *Dispatcher) (*Connection, error) { r := ssmp.NewDecoder(c) c.SetReadDeadline(time.Now().Add(10 * time.Second)) verb, err := r.DecodeVerb() if err != nil || !ssmp.Equal(verb, ssmp.LOGIN) { return nil, ErrInvalidLogin } user, err := r.DecodeId() if err != nil { return nil, ErrInvalidLogin } scheme, err := r.DecodeId() if err != nil { return nil, ErrInvalidLogin } var cred []byte if r.AtEnd() { cred = []byte{} } else if cred, err = r.DecodePayload(); err != nil { return nil, ErrInvalidLogin } if !a.Auth(c, user, scheme, cred) { return nil, ErrUnauthorized } r.Reset() cc := &Connection{ c: c, r: r, User: string(user), } go cc.readLoop(d) cc.Write(respOk) return cc, nil }
// NewConnection creates a SSMP connection out of a streaming netwrok connection. // // This method blocks until either a first message is received or a 10s timeout // elapses. // // Each accepted connection spawns a goroutine continuously reading from the // underlying network connection and triggering the Dispatcher. The caller must // keep track of the returned Connection and call the Close method to stop the // read goroutine and close the udnerlying netwrok connection. // // errInvalidLogin is returned if the first message is not a well-formed LOGIN // request. // errUnauthorized is returned if the authenticator doesn't accept the provided // credentials. func NewConnection(c net.Conn, a Authenticator, d *Dispatcher) (*Connection, error) { r := bufio.NewReaderSize(c, 1024) c.SetReadDeadline(time.Now().Add(10 * time.Second)) l, err := r.ReadSlice('\n') if err != nil { return nil, err } // strip LF delimiter l = l[0 : len(l)-1] cmd := ssmp.NewCommand(l) verb, err := ssmp.VerbField(cmd) if err != nil || !ssmp.Equal(verb, ssmp.LOGIN) { return nil, ErrInvalidLogin } user, err := ssmp.IdField(cmd) if err != nil { return nil, ErrInvalidLogin } scheme, err := ssmp.IdField(cmd) if err != nil { return nil, ErrInvalidLogin } cred := cmd.Trailing() if !a.Auth(c, user, scheme, cred) { return nil, ErrUnauthorized } cc := &Connection{ c: c, r: r, User: string(user), } go cc.readLoop(d) cc.Write(respOk) return cc, nil }
func (a *test_auth) Auth(c net.Conn, user, scheme, cred []byte) bool { return !ssmp.Equal(user, "reject") }
func (c *client) readLoop() { defer c.wg.Done() defer close(c.responses) idle := false r := ssmp.NewDecoder(c.c) for { c.c.SetReadDeadline(time.Now().Add(30 * time.Second)) code, err := r.DecodeCode() if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() && !idle { idle = true c.c.Write(ping) continue } // unwrap network error if oerr, ok := err.(*net.OpError); ok { err = oerr.Err } if err != io.EOF && err.Error() != "use of closed network connection" { fmt.Printf("Client[%p] Failed to read: %v\n", c, err) } break } idle = false if code == ssmp.CodeEvent { ev, err := parseEvent(r) if err != nil { fmt.Printf("Client[%p] Invalid event: %v\n", c, err) break } r.Reset() if ssmp.Equal(ev.Name, ssmp.PING) { c.c.Write(pong) continue } if ssmp.Equal(ev.Name, ssmp.PONG) { continue } h := c.EventHandler() if h == nil { continue } h.HandleEvent(ev) continue } var payload string if !r.AtEnd() { d, err := r.DecodePayload() if err != nil { fmt.Printf("Client[%p] Invalid response: %v\n", c, err) break } payload = string(d) } r.Reset() c.responses <- Response{ Code: code, Message: payload, } } c.c.Close() }
func (c *client) readLoop() { defer c.wg.Done() defer close(c.responses) idle := false r := bufio.NewReaderSize(c.c, 1024) for { c.c.SetReadDeadline(time.Now().Add(30 * time.Second)) l, err := r.ReadSlice('\n') if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() && !idle { idle = true c.c.Write(ping) continue } // unwrap network error if oerr, ok := err.(*net.OpError); ok { err = oerr.Err } if err != io.EOF && err.Error() != "use of closed network connection" { fmt.Printf("Client[%p] Failed to read: %v\n", c, err) } break } idle = false // strip LF delimiter l = l[0 : len(l)-1] if len(l) < 3 || (len(l) > 3 && l[3] != ' ') { if err != io.EOF { fmt.Printf("Client[%p] Invalid server message: %v\n", c, err) } break } code := responseCode(l) if code < 0 { fmt.Printf("Client[%p] Invalid server message: %v\n", c, l) break } var payload []byte if len(l) >= 4 { payload = l[4:] } if code == ssmp.CodeEvent { ev, err := ParseEvent(payload) if err != nil { fmt.Printf("Client[%p] Invalid event: %v\n", c, err) break } if ssmp.Equal(ev.Name, ssmp.PING) { c.c.Write(pong) continue } if ssmp.Equal(ev.Name, ssmp.PONG) { continue } h := c.EventHandler() if h == nil { continue } h.HandleEvent(ev) continue } c.responses <- Response{ Code: code, Message: string(payload), } } c.c.Close() }