func (c *FrontendConnection) sendNotification(n *pq.Notification) error { var message fbcore.Message buf := &bytes.Buffer{} fbbuf.WriteInt32(buf, int32(n.BePid)) fbbuf.WriteCString(buf, n.Channel) fbbuf.WriteCString(buf, n.Extra) message.InitFromBytes(fbproto.MsgNotificationResponseA, buf.Bytes()) return c.WriteAndFlush(&message) }
func initFatalMessage(message *fbcore.Message, sqlstate, errorMessage string) { buf := &bytes.Buffer{} buf.WriteByte('S') fbbuf.WriteCString(buf, "FATAL") buf.WriteByte('C') fbbuf.WriteCString(buf, sqlstate) buf.WriteByte('M') fbbuf.WriteCString(buf, errorMessage) buf.WriteByte('\x00') message.InitFromBytes(fbproto.MsgErrorResponseE, buf.Bytes()) }
func (r trivialSelectResult) Respond(f Frontend) error { var msg fbcore.Message buf := &bytes.Buffer{} fbbuf.WriteInt16(buf, 1) fbbuf.WriteInt32(buf, 1) buf.WriteByte('1') msg.InitFromBytes(fbproto.MsgDataRowD, buf.Bytes()) err := f.WriteMessage(&msg) if err != nil { return err } return commandComplete("SELECT").Respond(f) }
func (qr errorResponse) Respond(f Frontend) error { var message fbcore.Message buf := &bytes.Buffer{} buf.WriteByte('S') fbbuf.WriteCString(buf, "ERROR") buf.WriteByte('C') fbbuf.WriteCString(buf, qr.sqlstate) buf.WriteByte('M') fbbuf.WriteCString(buf, qr.errorMessage) buf.WriteByte('\x00') message.InitFromBytes(fbproto.MsgErrorResponseE, buf.Bytes()) return f.WriteMessage(&message) }
func (c *FrontendConnection) startup(startupParameters map[string]string, dbcfg VirtualDatabaseConfiguration) bool { var message fbcore.Message var err error for { err = c.stream.Next(&message) if err != nil { elog.Logf("error while reading startup packet: %s", err) return false } if fbproto.IsStartupMessage(&message) { break } else if fbproto.IsSSLRequest(&message) { _, err = message.Force() if err != nil { elog.Logf("error while reading SSLRequest: %s", err) return false } err = c.stream.SendSSLRequestResponse(fbcore.RejectSSLRequest) if err != nil { elog.Logf("error during startup sequence: %s", err) return false } err = c.FlushStream() if err != nil { elog.Logf("error during startup sequence: %s", err) } } else if fbproto.IsCancelRequest(&message) { _ = c.stream.Close() return false } else { elog.Warningf("unrecognized frontend message type 0x%x during startup", message.MsgType()) return false } } sm, err := fbproto.ReadStartupMessage(&message) if err != nil { elog.Logf("error while reading startup packet: %s", err) return false } if !c.auth(dbcfg, sm) { // error already logged _ = c.stream.Close() return false } fbproto.InitAuthenticationOk(&message) err = c.WriteMessage(&message) if err != nil { elog.Logf("error during startup sequence: %s", err) return false } for k, v := range startupParameters { buf := &bytes.Buffer{} fbbuf.WriteCString(buf, k) fbbuf.WriteCString(buf, v) message.InitFromBytes(fbproto.MsgParameterStatusS, buf.Bytes()) err = c.WriteMessage(&message) if err != nil { elog.Logf("error during startup sequence: %s", err) return false } } fbproto.InitReadyForQuery(&message, fbproto.RfqIdle) err = c.WriteMessage(&message) if err != nil { elog.Logf("error during startup sequence: %s", err) return false } err = c.FlushStream() if err != nil { elog.Logf("error during startup sequence: %s", err) return false } return true }
func (c *FrontendConnection) auth(dbcfg VirtualDatabaseConfiguration, sm *fbproto.StartupMessage) bool { authFailed := func(sqlstate, format string, v ...interface{}) bool { var msg fbcore.Message message := fmt.Sprintf(format, v...) initFatalMessage(&msg, sqlstate, message) _ = c.WriteMessage(&msg) _ = c.FlushStream() return false } username, ok := sm.Params["user"] if !ok { return authFailed("08P01", `required startup parameter "user" nor present in startup packet`) } dbname, ok := sm.Params["database"] if !ok { dbname = username } authMethod, ok := dbcfg.FindDatabase(dbname) if !ok { return authFailed("3D000", "database %q does not exist", dbname) } switch authMethod { case "trust": return true case "md5": // handled below default: elog.Errorf("unrecognized authentication method %q", authMethod) return authFailed("XX000", "internal error") } salt := make([]byte, 4) _, err := rand.Read(salt) if err != nil { elog.Errorf("could not generate random salt: %s", err) return authFailed("XX000", "internal error") } var msg fbcore.Message buf := &bytes.Buffer{} fbbuf.WriteInt32(buf, 5) buf.Write(salt) msg.InitFromBytes(fbproto.MsgAuthenticationMD5PasswordR, buf.Bytes()) err = c.WriteAndFlush(&msg) if err != nil { elog.Logf("error during startup sequence: %s", err) return false } err = c.stream.Next(&msg) if err == io.EOF { elog.Debugf("EOF during startup sequence") return false } else if err != nil { elog.Logf("error during startup sequence: %s", err) return false } if msg.MsgType() != fbproto.MsgPasswordMessageP { return authFailed("08P01", "unexpected response %x", msg.MsgType()) } // don't bother with messages which are clearly too big if msg.Size() > 100 { return authFailed("28001", "password authentication failed for user %q", username) } password, err := msg.Force() if err != nil { elog.Logf("error during startup sequence: %s", err) return false } success, err := dbcfg.MD5Auth(dbname, username, salt, password) if err != nil { elog.Logf("error during startup sequence: %s", err) return false } if !success { return authFailed("28001", "password authentication failed for user %q", username) } return true }
func (qr noData) Respond(f Frontend) error { var message fbcore.Message message.InitFromBytes(fbproto.MsgNoDataN, []byte{}) return f.WriteMessage(&message) }
func (qr bindComplete) Respond(f Frontend) error { var message fbcore.Message message.InitFromBytes(fbproto.MsgBindComplete2, []byte{}) return f.WriteMessage(&message) }
func (eqr emptyQueryResponse) Respond(f Frontend) error { var message fbcore.Message message.InitFromBytes(fbproto.MsgEmptyQueryResponseI, nil) return f.WriteMessage(&message) }