diff --git a/irc/chanserv.go b/irc/chanserv.go
index 6fb748e5..9817ccc2 100644
--- a/irc/chanserv.go
+++ b/irc/chanserv.go
@@ -4,17 +4,15 @@
package irc
import (
- "bytes"
"fmt"
- "hash/crc32"
"sort"
- "strconv"
"strings"
"time"
"github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/sno"
+ "github.com/oragono/oragono/irc/utils"
)
const chanservHelp = `ChanServ lets you register and manage channels.`
@@ -352,7 +350,7 @@ func csUnregisterHandler(server *Server, client *Client, command string, params
}
info := channel.ExportRegistration(0)
- expectedCode := unregisterConfirmationCode(info.Name, info.RegisteredAt)
+ expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
if expectedCode != verificationCode {
csNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
csNotice(rb, fmt.Sprintf(client.t("To confirm channel unregistration, type: /CS UNREGISTER %[1]s %[2]s"), channelKey, expectedCode))
@@ -363,14 +361,6 @@ func csUnregisterHandler(server *Server, client *Client, command string, params
csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
}
-// deterministically generates a confirmation code for unregistering a channel / account
-func unregisterConfirmationCode(name string, registeredAt time.Time) (code string) {
- var codeInput bytes.Buffer
- codeInput.WriteString(name)
- codeInput.WriteString(strconv.FormatInt(registeredAt.Unix(), 16))
- return strconv.Itoa(int(crc32.ChecksumIEEE(codeInput.Bytes())))
-}
-
func csClearHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channel := server.channels.Get(params[0])
if channel == nil {
@@ -426,7 +416,7 @@ func csTransferHandler(server *Server, client *Client, command string, params []
return
}
if targetAccount.NameCasefolded != account {
- expectedCode := unregisterConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
+ expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
codeValidated := 2 < len(params) && params[2] == expectedCode
if !codeValidated {
csNotice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b")))
diff --git a/irc/handlers.go b/irc/handlers.go
index f5999daa..b3799a23 100644
--- a/irc/handlers.go
+++ b/irc/handlers.go
@@ -804,6 +804,26 @@ func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
case "STOPCPUPROFILE":
pprof.StopCPUProfile()
rb.Notice(fmt.Sprintf("CPU profiling stopped"))
+
+ case "CRASHSERVER":
+ if !client.HasRoleCapabs("oper:rehash") {
+ rb.Notice(client.t("You must have rehash permissions in order to execute DEBUG CRASHSERVER"))
+ return false
+ }
+ code := utils.ConfirmationCode(server.name, server.ctime)
+ if len(msg.Params) == 1 || msg.Params[1] != code {
+ rb.Notice(fmt.Sprintf(client.t("To crash the server, issue the following command: /DEBUG CRASHSERVER %s"), code))
+ return false
+ }
+ server.logger.Error("server", fmt.Sprintf("DEBUG CRASHSERVER executed by operator %s", client.Oper().Name))
+ go func() {
+ // intentional nil dereference on a new goroutine, bypassing recover-from-errors
+ var i, j *int
+ *i = *j
+ }()
+
+ default:
+ rb.Notice(client.t("Unrecognized DEBUG subcommand"))
}
return false
}
diff --git a/irc/help.go b/irc/help.go
index abcce6de..e3742f11 100644
--- a/irc/help.go
+++ b/irc/help.go
@@ -153,13 +153,14 @@ https://gist.github.com/DanielOaks/c104ad6e8759c01eb5c826d627caf80d`,
oper: true,
text: `DEBUG