Add channel modes +i, +m and +r, MOTD config option and banned message when connecting

main
Trevor Slocum 2017-12-20 00:48:32 -08:00
parent 7db983d592
commit e91d8a4dae
21 changed files with 1409 additions and 202 deletions

6
Gopkg.lock generated
View File

@ -23,7 +23,7 @@
branch = "master"
name = "github.com/jmoiron/sqlx"
packages = [".","reflectx"]
revision = "99f3ad6d85ae53d0fecf788ab62d0e9734b3c117"
revision = "de8647470aafe4854c976707c431dbe1eb2822c6"
[[projects]]
name = "github.com/mattn/go-sqlite3"
@ -41,13 +41,13 @@
branch = "master"
name = "golang.org/x/crypto"
packages = ["sha3"]
revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122"
revision = "d585fd2cc9195196078f516b69daff6744ef5e84"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context"]
revision = "dc871a5d77e227f5bbf6545176ef3eeebf87e76e"
revision = "d866cfc389cec985d6fda2859936a575a55a3ab6"
[[projects]]
branch = "v2"

View File

@ -13,7 +13,7 @@ const ENTITY_STATE_TERMINATING = 0
const ENTITY_STATE_NORMAL = 1
const CLIENT_MODES = "cD"
const CHANNEL_MODES = "cDipstz"
const CHANNEL_MODES = "cDimprstz"
const CHANNEL_MODES_ARG = "kl"
type Entity struct {

View File

@ -35,7 +35,7 @@ import (
var prefixAnonIRC = irc.Prefix{Name: "AnonIRC"}
var prefixAnonymous = irc.Prefix{Name: "Anonymous", User: "Anon", Host: "IRC"}
const motd = `
const DEFAULT_MOTD = `
_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_|_| _|_| _|_|_| _| _| _| _|
_|_|_|_| _| _| _| _| _| _| _| _|_|_| _|

428
server.go
View File

@ -33,6 +33,9 @@ const (
COMMAND_USERNAME = "USERNAME"
COMMAND_PASSWORD = "PASSWORD"
// User/channel commands
COMMAND_MODE = "MODE"
// Channel/server commands
COMMAND_FOUND = "FOUND"
COMMAND_DROP = "DROP"
@ -42,7 +45,7 @@ const (
COMMAND_BAN = "BAN"
COMMAND_AUDIT = "AUDIT"
// Server admins only
// Server admin commands
COMMAND_KILL = "KILL"
COMMAND_STATS = "STATS"
COMMAND_REHASH = "REHASH"
@ -51,6 +54,7 @@ const (
var serverCommands = []string{COMMAND_KILL, COMMAND_STATS, COMMAND_REHASH, COMMAND_UPGRADE}
// TODO: Reorder
const (
PERMISSION_CLIENT = 0
PERMISSION_REGISTERED = 1
@ -69,11 +73,11 @@ var permissionLabels = map[int]string{
PERMISSION_SUPERADMIN: "Super Administrator",
}
var ALL_PERMISSIONS = "Client, Registered Client, VIP, Moderator, Adminsitrator and Super Administrator"
var ALL_PERMISSIONS = "Client, Registered Client, VIP, Moderator, Administrator and Super Administrator"
var commandRestrictions = map[int][]string{
PERMISSION_REGISTERED: {COMMAND_TOKEN, COMMAND_USERNAME, COMMAND_PASSWORD, COMMAND_FOUND},
PERMISSION_MODERATOR: {COMMAND_REVEAL, COMMAND_KICK, COMMAND_BAN},
PERMISSION_MODERATOR: {COMMAND_MODE, COMMAND_REVEAL, COMMAND_KICK, COMMAND_BAN},
PERMISSION_ADMIN: {COMMAND_GRANT, COMMAND_AUDIT},
PERMISSION_SUPERADMIN: {COMMAND_DROP, COMMAND_KILL, COMMAND_STATS, COMMAND_REHASH, COMMAND_UPGRADE}}
@ -81,7 +85,7 @@ var helpDuration = "Duration can be 0 to never expire, or e.g. 30m, 1h, 2d, 3w"
var commandUsage = map[string][]string{
COMMAND_HELP: {"[command|all]",
"Print usage information regarding a specific command or 'all'",
"Without a command or 'all', only commands currently available are printed"},
"Without a command or 'all', only commands you have permission to use are printed"},
COMMAND_INFO: {"[channel]",
"When a channel is specified, prints info including whether it is registered",
"Without a channel, server info is printed"},
@ -135,6 +139,7 @@ var commandUsage = map[string][]string{
}
type Config struct {
MOTD string
Salt string
DBDriver string
DBSource string
@ -146,6 +151,7 @@ type Server struct {
config *Config
configfile string
created int64
motd []string
clients *sync.Map
channels *sync.Map
odyssey *os.File
@ -314,6 +320,54 @@ func (s *Server) inChannel(channel string, client string) bool {
return false
}
func (s *Server) canAccessChannel(c *Client, channel string) (bool, string) {
dbch, err := db.Channel(channel)
if err != nil || dbch.Channel == "" {
return false, "invalid channel"
}
ch := s.getChannel(channel)
if ch == nil {
return false, "invalid channel"
} else if banned, reason := c.isBanned(channel); banned {
if reason != "" {
reason = fmt.Sprintf(" (%s)", reason)
}
return false, "you are banned" + reason
} else if ch.hasMode("z") && !c.ssl {
return false, "only clients connected via SSL are allowed"
}
permission := c.globalPermission()
requiredPermission := PERMISSION_CLIENT
reason := ""
if channel[0] == '&' {
if permission < PERMISSION_VIP {
return false, "restricted channel"
}
} else if channel[0] != '#' {
return false, "invalid channel"
}
if permission < requiredPermission && c.account > 0 {
chp, err := db.GetPermission(c.account, channel)
if err != nil && chp.Permission > permission {
permission = chp.Permission
}
}
if ch.hasMode("r") {
requiredPermission = PERMISSION_REGISTERED
reason = "only registered clients are allowed"
}
if ch.hasMode("i") {
requiredPermission = PERMISSION_VIP
reason = "only VIP are allowed"
}
return permission >= requiredPermission, reason
}
func (s *Server) joinChannel(channel string, client string) {
if s.inChannel(channel, client) {
return // Already in channel
@ -324,33 +378,14 @@ func (s *Server) joinChannel(channel string, client string) {
return
}
if len(channel) == 0 {
return
} else if channel[0] == '&' {
if cl.globalPermission() < PERMISSION_VIP {
cl.accessDenied(0)
return
}
} else if channel[0] != '#' {
return
}
ch := s.getChannel(channel)
if ch == nil {
ch = NewChannel(channel)
s.channels.Store(channel, ch)
} else if ch.hasMode("z") && !cl.ssl {
cl.sendNotice("Unable to join " + channel + ": SSL connections only (channel mode +z)")
return
}
banned, reason := cl.isBanned(channel)
if banned {
ex := ""
if reason != "" {
ex = ". Reason: " + reason
}
cl.sendNotice("Unable to join " + channel + ": You are banned" + ex)
} else if canaccess, reason := s.canAccessChannel(cl, channel); !canaccess {
errmsg := fmt.Sprintf("Cannot join %s: %s", channel, reason)
cl.writeMessage(irc.ERR_INVITEONLYCHAN, []string{channel, errmsg})
cl.sendNotice(errmsg)
return
}
@ -391,7 +426,6 @@ func (s *Server) revealChannelLog(channel string, client string, page int, showA
return
}
// TODO: Check channel permission
ch := s.getChannel(channel)
if ch == nil {
cl.sendError("Unable to reveal, invalid channel specified")
@ -574,7 +608,123 @@ func (s *Server) handleTopic(channel string, client string, topic string) {
ch.Log(cl, irc.TOPIC, ch.topic)
}
func (s *Server) handleChannelMode(c *Client, params []string) {
ch := s.getChannel(params[0])
if ch == nil || !s.inChannel(params[0], c.identifier) {
return
}
if len(params) == 1 || params[1] == "" {
c.writeMessage(strings.Join([]string{irc.RPL_CHANNELMODEIS, c.nick, params[0], ch.printModes(ch.getModes(), nil)}, " "), []string{})
// Send channel creation time
c.writeMessage(strings.Join([]string{"329", c.nick, params[0], fmt.Sprintf("%d", int32(ch.created))}, " "), []string{})
} else if len(params) > 1 && (params[1] == "" || params[1][0] == '+' || params[1][0] == '-') {
if !c.canUse(COMMAND_MODE, params[0]) {
c.accessDenied(c.permissionRequired(COMMAND_MODE))
return
}
lastmodes := make(map[string]string)
for m, mv := range ch.getModes() {
lastmodes[m] = mv
}
if params[1][0] == '+' {
ch.addModes(params[1][1:])
} else {
ch.removeModes(params[1][1:])
}
s.enforceModes(params[0])
if reflect.DeepEqual(ch.getModes(), lastmodes) {
return
}
// TODO: Check if local modes were set/unset, only send changes to local client
addedmodes, removedmodes := ch.diffModes(lastmodes)
resendusercount := false
if _, ok := addedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["D"]; ok {
resendusercount = true
}
if len(addedmodes) == 0 && len(removedmodes) == 0 {
addedmodes = c.getModes()
}
ch.clients.Range(func(k, v interface{}) bool {
cl := s.getClient(k.(string))
if cl != nil {
cl.write(&prefixAnonymous, irc.MODE, []string{params[0], ch.printModes(addedmodes, removedmodes)})
}
return true
})
if resendusercount {
s.updateClientCount(params[0], c.identifier, "Enforcing MODEs")
}
}
}
func (s *Server) handleUserMode(c *Client, params []string) {
if len(params) == 1 || params[1] == "" {
c.writeMessage(strings.Join([]string{irc.RPL_UMODEIS, c.nick, c.printModes(c.getModes(), nil)}, " "), []string{})
return
}
lastmodes := c.getModes()
if len(params) > 1 && len(params[1]) > 0 && (params[1][0] == '+' || params[1][0] == '-') {
if params[1][0] == '+' {
c.addModes(params[1][1:])
} else {
c.removeModes(params[1][1:])
}
}
if reflect.DeepEqual(c.modes, lastmodes) {
return
}
addedmodes, removedmodes := c.diffModes(lastmodes)
resendusercount := false
if _, ok := addedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["D"]; ok {
resendusercount = true
}
if len(addedmodes) == 0 && len(removedmodes) == 0 {
addedmodes = c.getModes()
}
c.writeMessage(strings.Join([]string{irc.MODE, c.nick}, " "), []string{c.printModes(addedmodes, removedmodes)})
if resendusercount {
for ch := range s.getChannels(c.identifier) {
s.updateClientCount(ch, c.identifier, "Enforcing MODEs")
}
}
}
func (s *Server) handleMode(c *Client, params []string) {
if c == nil {
return
}
if len(params) == 0 || len(params[0]) == 0 {
c.sendNotice("Invalid use of MODE")
return
@ -585,111 +735,10 @@ func (s *Server) handleMode(c *Client, params []string) {
return
}
if validChannelPrefix(params[0]) {
ch := s.getChannel(params[0])
if ch == nil {
return
}
if len(params) == 1 || params[1] == "" {
c.writeMessage(strings.Join([]string{irc.RPL_CHANNELMODEIS, c.nick, params[0], ch.printModes(ch.getModes(), nil)}, " "), []string{})
// Send channel creation time
c.writeMessage(strings.Join([]string{"329", c.nick, params[0], fmt.Sprintf("%d", int32(ch.created))}, " "), []string{})
} else if len(params) > 1 && len(params[1]) > 0 && params[1][0] == '+' || params[1][0] == '-' {
if !c.canUse(irc.MODE, params[0]) {
c.accessDenied(c.permissionRequired(irc.MODE))
return
}
lastmodes := make(map[string]string)
for m, mv := range ch.getModes() {
lastmodes[m] = mv
}
if params[1][0] == '+' {
ch.addModes(params[1][1:])
} else {
ch.removeModes(params[1][1:])
}
s.enforceModes(params[0])
if !reflect.DeepEqual(ch.getModes(), lastmodes) {
// TODO: Check if local modes were set/unset, only send changes to local client
addedmodes, removedmodes := ch.diffModes(lastmodes)
resendusercount := false
if _, ok := addedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["D"]; ok {
resendusercount = true
}
if len(addedmodes) == 0 && len(removedmodes) == 0 {
addedmodes = c.getModes()
}
ch.clients.Range(func(k, v interface{}) bool {
cl := s.getClient(k.(string))
if cl != nil {
cl.write(&prefixAnonymous, irc.MODE, []string{params[0], ch.printModes(addedmodes, removedmodes)})
}
return true
})
if resendusercount {
s.updateClientCount(params[0], c.identifier, "Enforcing MODEs")
}
}
}
if params[0][0] == '#' || params[0][0] == '&' {
s.handleChannelMode(c, params)
} else {
if len(params) == 1 || params[1] == "" {
c.writeMessage(strings.Join([]string{irc.RPL_UMODEIS, c.nick, c.printModes(c.getModes(), nil)}, " "), []string{})
return
}
lastmodes := c.getModes()
if len(params) > 1 && len(params[1]) > 0 && (params[1][0] == '+' || params[1][0] == '-') {
if params[1][0] == '+' {
c.addModes(params[1][1:])
} else {
c.removeModes(params[1][1:])
}
}
if !reflect.DeepEqual(c.modes, lastmodes) {
addedmodes, removedmodes := c.diffModes(lastmodes)
resendusercount := false
if _, ok := addedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["c"]; ok {
resendusercount = true
}
if _, ok := removedmodes["D"]; ok {
resendusercount = true
}
if len(addedmodes) == 0 && len(removedmodes) == 0 {
addedmodes = c.getModes()
}
c.writeMessage(strings.Join([]string{irc.MODE, c.nick}, " "), []string{c.printModes(addedmodes, removedmodes)})
if resendusercount {
for ch := range s.getChannels(c.identifier) {
s.updateClientCount(ch, c.identifier, "Enforcing MODEs")
}
}
}
s.handleUserMode(c, params)
}
}
@ -776,7 +825,7 @@ func (s *Server) ban(channel string, iphash string, accountid int64, expires int
return err
}
}
log.Println("B")
if accountid > 0 {
b = DBBan{Channel: generateHash(channel), Type: BAN_TYPE_ACCOUNT, Target: fmt.Sprintf("%d", accountid), Expires: expires}
err := db.AddBan(b)
@ -784,9 +833,8 @@ func (s *Server) ban(channel string, iphash string, accountid int64, expires int
return err
}
}
log.Println("C")
if b.Channel == "" {
log.Println("blank chan")
return nil
}
@ -796,7 +844,7 @@ func (s *Server) ban(channel string, iphash string, accountid int64, expires int
ch = ""
rs = formatAction("Killed", reason)
}
log.Println("D")
cls := s.getClients(ch)
for _, cl := range cls {
if cl == nil {
@ -841,8 +889,27 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
s.sendUsage(cl, cmd)
return
case COMMAND_INFO:
// TODO: when channel is supplied, send whether it is registered and show a notice that it is dropping soon if no super admins have logged in in X days
cl.sendMessage("Server info: AnonIRCd https://github.com/sageru-6ch/anonircd")
if len(params) > 0 && len(params[0]) > 0 {
if canaccess, reason := s.canAccessChannel(cl, params[0]); !canaccess {
cl.sendError("Failed to fetch channel INFO, " + reason)
return
}
dbch, err := db.Channel(params[0])
if err != nil {
cl.sendError("Failed to fetch channel INFO, " + err.Error())
return
}
chst := "Unfounded"
if dbch.Channel != "" {
chst = "Founded"
}
cl.sendMessage(fmt.Sprintf("%s: %s", params[0], chst))
} else {
cl.sendMessage("AnonIRCd https://github.com/sageru-6ch/anonircd")
}
return
case COMMAND_REGISTER:
if len(params) == 0 {
@ -945,7 +1012,6 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
}
cl.sendMessage("Password changed successfully")
case COMMAND_REVEAL, COMMAND_AUDIT:
// TODO: &#chan shows moderator audit log, & alone shows server admin audit log
if len(params) == 0 {
s.sendUsage(cl, command)
return
@ -1044,6 +1110,7 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
bch = CHANNEL_SERVER
}
err := s.ban(bch, rcl.iphash, rcl.account, expires, reason)
log.Println("A")
if err != nil {
cl.sendError(fmt.Sprintf("Unable to %s, %v", strings.ToLower(command), err))
return
@ -1051,7 +1118,6 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
cl.sendMessage(fmt.Sprintf("%sed %s %s", strings.ToLower(command), params[0], params[1]))
case COMMAND_STATS:
cl.sendMessage(fmt.Sprintf("%d clients in %d channels", s.clientCount(), s.channelCount()))
case COMMAND_REHASH:
@ -1066,41 +1132,46 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
}
}
func (s *Server) handlePrivmsg(channel string, client string, message string) {
func (s *Server) handlePrivmsg(target string, client string, message string) {
cl := s.getClient(client)
if cl == nil {
if cl == nil || len(target) == 0 {
return
}
if strings.ToLower(channel) == "anonirc" {
if strings.ToLower(target) == "anonirc" {
params := strings.Split(message, " ")
if len(params) > 0 && len(params[0]) > 0 {
var otherparams []string
if len(params) > 1 {
otherparams = params[1:]
}
s.handleUserCommand(client, params[0], otherparams)
if len(params) == 0 || len(params[0]) == 0 {
return
}
var otherparams []string
if len(params) > 1 {
otherparams = params[1:]
}
s.handleUserCommand(client, params[0], otherparams)
return
} else if channel == "" || !validChannelPrefix(channel) {
} else if target[0] != '#' && target[0] != '&' {
cl.writeMessage(irc.ERR_NOSUCHNICK, []string{target, "No such nick/channel"})
return
} else if !s.inChannel(target, client) {
cl.writeMessage(irc.ERR_CANNOTSENDTOCHAN, []string{target, fmt.Sprintf("No external channel messages (%s)", target)})
return
} else if !s.inChannel(channel, client) {
return // Not in channel
}
ch := s.getChannel(channel)
ch := s.getChannel(target)
if ch == nil {
return
} else if ch.hasMode("m") && cl.getPermission(target) < PERMISSION_VIP {
cl.writeMessage(irc.ERR_CANNOTSENDTOCHAN, []string{target, fmt.Sprintf("Channel is moderated, only VIP may speak (%s)", target)})
return
}
s.updateClientCount(channel, "", "")
s.updateClientCount(target, "", "")
ch.clients.Range(func(k, v interface{}) bool {
chcl := s.getClient(k.(string))
if chcl != nil && chcl.identifier != client {
chcl.write(&prefixAnonymous, irc.PRIVMSG, []string{channel, message})
chcl.write(&prefixAnonymous, irc.PRIVMSG, []string{target, message})
}
return true
@ -1114,19 +1185,19 @@ func (s *Server) handleRead(c *Client) {
return
}
c.conn.SetReadDeadline(time.Now().Add(300 * time.Second))
if _, ok := s.clients.Load(c.identifier); !ok {
s.killClient(c, "")
return
}
c.conn.SetReadDeadline(time.Now().Add(300 * time.Second))
msg, err := c.reader.Decode()
if msg == nil || err != nil {
// Error decoding message, client probably disconnected
s.killClient(c, "")
return
}
if debugMode && (verbose || len(msg.Command) < 4 || (msg.Command[0:4] != irc.PING && msg.Command[0:4] != irc.PONG)) {
log.Printf("%s -> %s", c.identifier, msg)
}
@ -1142,12 +1213,11 @@ func (s *Server) handleRead(c *Client) {
c.writeMessage(irc.RPL_CREATED, []string{fmt.Sprintf("This server was created %s", time.Unix(s.created, 0).UTC())})
c.writeMessage(strings.Join([]string{irc.RPL_MYINFO, c.nick, "AnonIRC", "AnonIRCd", CLIENT_MODES, CHANNEL_MODES, CHANNEL_MODES_ARG}, " "), []string{})
motdsplit := strings.Split(motd, "\n")
for i, motdmsg := range motdsplit {
for i, motdmsg := range s.motd {
var motdcode string
if i == 0 {
motdcode = irc.RPL_MOTDSTART
} else if i < len(motdsplit)-1 {
} else if i < len(s.motd)-1 {
motdcode = irc.RPL_MOTD
} else {
motdcode = irc.RPL_ENDOFMOTD
@ -1335,23 +1405,27 @@ func (s *Server) handleConnection(conn net.Conn, ssl bool) {
}
}
client := NewClient(identifier, conn, ssl)
c := NewClient(identifier, conn, ssl)
banned := true
reason := ""
if client != nil {
banned, reason = client.isBanned("")
if c != nil {
banned, reason = c.isBanned(CHANNEL_SERVER)
}
if banned {
// TODO: Send banned message
_ = reason
return // Banned
go s.handleWrite(c)
if !banned {
s.clients.Store(c.identifier, c)
s.handleRead(c) // Block until the connection is closed
} else {
if reason != "" {
reason = fmt.Sprintf(" (%s)", reason)
}
c.writeMessage(irc.ERR_YOUREBANNEDCREEP, []string{"You are banned from this server" + reason})
time.Sleep(1 * time.Second)
}
s.clients.Store(client.identifier, client)
go s.handleWrite(client)
s.handleRead(client) // Block until the connection is closed
s.killClient(client, "")
s.killClient(c, "")
s.clients.Delete(identifier)
}
func (s *Server) killClient(c *Client, reason string) {
@ -1493,6 +1567,12 @@ func (s *Server) loadConfig() error {
return errors.New(fmt.Sprintf("DBDriver and DBSource must be configured in %s\nExample:\n\nDBDriver=\"sqlite3\"\nDBSource=\"/home/user/anonircd/anonircd.db\"", s.configfile))
}
motd := DEFAULT_MOTD
if s.config.MOTD != "" {
motd = s.config.MOTD
}
s.motd = strings.Split(strings.TrimRight(motd, " \t\r\n"), "\n")
return nil
}

View File

@ -55,10 +55,6 @@ func randomIdentifier() string {
return string(b)
}
func validChannelPrefix(channel string) bool {
return channel[0] == '&' || channel[0] == '#'
}
func generateHash(s string) string {
sha512 := sha3.New512()
_, err := sha512.Write([]byte(strings.Join([]string{s, fmt.Sprintf("%x", md5.Sum([]byte(s))), s}, "-")))

View File

@ -163,16 +163,18 @@ func bindArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{
v = v.Elem()
}
fields := m.TraversalsByName(v.Type(), names)
for i, t := range fields {
err := m.TraversalsByNameFunc(v.Type(), names, func(i int, t []int) error {
if len(t) == 0 {
return arglist, fmt.Errorf("could not find name %s in %#v", names[i], arg)
return fmt.Errorf("could not find name %s in %#v", names[i], arg)
}
val := reflectx.FieldByIndexesReadOnly(v, t)
arglist = append(arglist, val.Interface())
}
return arglist, nil
return nil
})
return arglist, err
}
// like bindArgs, but for maps.

View File

@ -166,20 +166,39 @@ func (m *Mapper) FieldsByName(v reflect.Value, names []string) []reflect.Value {
// traversals for each mapped name. Panics if t is not a struct or Indirectable
// to a struct. Returns empty int slice for each name not found.
func (m *Mapper) TraversalsByName(t reflect.Type, names []string) [][]int {
r := make([][]int, 0, len(names))
m.TraversalsByNameFunc(t, names, func(_ int, i []int) error {
if i == nil {
r = append(r, []int{})
} else {
r = append(r, i)
}
return nil
})
return r
}
// TraversalsByNameFunc traverses the mapped names and calls fn with the index of
// each name and the struct traversal represented by that name. Panics if t is not
// a struct or Indirectable to a struct. Returns the first error returned by fn or nil.
func (m *Mapper) TraversalsByNameFunc(t reflect.Type, names []string, fn func(int, []int) error) error {
t = Deref(t)
mustBe(t, reflect.Struct)
tm := m.TypeMap(t)
r := make([][]int, 0, len(names))
for _, name := range names {
for i, name := range names {
fi, ok := tm.Names[name]
if !ok {
r = append(r, []int{})
if err := fn(i, nil); err != nil {
return err
}
} else {
r = append(r, fi.Index)
if err := fn(i, fi.Index); err != nil {
return err
}
}
}
return r
return nil
}
// FieldByIndexes returns a value for the field given by the struct traversal

View File

@ -903,3 +903,72 @@ func BenchmarkFieldByIndexL4(b *testing.B) {
}
}
}
func BenchmarkTraversalsByName(b *testing.B) {
type A struct {
Value int
}
type B struct {
A A
}
type C struct {
B B
}
type D struct {
C C
}
m := NewMapper("")
t := reflect.TypeOf(D{})
names := []string{"C", "B", "A", "Value"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if l := len(m.TraversalsByName(t, names)); l != len(names) {
b.Errorf("expected %d values, got %d", len(names), l)
}
}
}
func BenchmarkTraversalsByNameFunc(b *testing.B) {
type A struct {
Z int
}
type B struct {
A A
}
type C struct {
B B
}
type D struct {
C C
}
m := NewMapper("")
t := reflect.TypeOf(D{})
names := []string{"C", "B", "A", "Z", "Y"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var l int
if err := m.TraversalsByNameFunc(t, names, func(_ int, _ []int) error {
l++
return nil
}); err != nil {
b.Errorf("unexpected error %s", err)
}
if l != len(names) {
b.Errorf("expected %d values, got %d", len(names), l)
}
}
}

219
vendor/golang.org/x/crypto/argon2/argon2.go generated vendored Normal file
View File

@ -0,0 +1,219 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package argon2 implements the key derivation function Argon2.
// Argon2 was selected as the winner of the Password Hashing Competition and can
// be used to derive cryptographic keys from passwords.
// Argon2 is specfifed at https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
package argon2
import (
"encoding/binary"
"sync"
"golang.org/x/crypto/blake2b"
)
// The Argon2 version implemented by this package.
const Version = 0x13
const (
argon2d = iota
argon2i
argon2id
)
// Key derives a key from the password, salt, and cost parameters using Argon2i
// returning a byte slice of length keyLen that can be used as cryptographic key.
// The CPU cost and parallism degree must be greater than zero.
//
// For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:
// `key := argon2.Key([]byte("some password"), salt, 4, 32*1024, 4, 32)`
//
// The recommended parameters for interactive logins as of 2017 are time=4, memory=32*1024.
// The number of threads can be adjusted to the numbers of available CPUs.
// The time parameter specifies the number of passes over the memory and the memory
// parameter specifies the size of the memory in KiB. For example memory=32*1024 sets the
// memory cost to ~32 MB.
// The cost parameters should be increased as memory latency and CPU parallelism increases.
// Remember to get a good random salt.
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
}
func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
if time < 1 {
panic("argon2: number of rounds too small")
}
if threads < 1 {
panic("argon2: paralisim degree too low")
}
mem := memory / (4 * uint32(threads)) * (4 * uint32(threads))
if mem < 8*uint32(threads) {
mem = 8 * uint32(threads)
}
B := initBlocks(password, salt, secret, data, time, mem, uint32(threads), keyLen, mode)
processBlocks(B, time, mem, uint32(threads), mode)
return extractKey(B, mem, uint32(threads), keyLen)
}
const blockLength = 128
type block [blockLength]uint64
func initBlocks(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) []block {
var (
block0 [1024]byte
h0 [blake2b.Size + 8]byte
params [24]byte
tmp [4]byte
)
b2, _ := blake2b.New512(nil)
binary.LittleEndian.PutUint32(params[0:4], threads)
binary.LittleEndian.PutUint32(params[4:8], keyLen)
binary.LittleEndian.PutUint32(params[8:12], memory)
binary.LittleEndian.PutUint32(params[12:16], time)
binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
b2.Write(params[:])
binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
b2.Write(tmp[:])
b2.Write(password)
binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
b2.Write(tmp[:])
b2.Write(salt)
binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
b2.Write(tmp[:])
b2.Write(key)
binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
b2.Write(tmp[:])
b2.Write(data)
b2.Sum(h0[:0])
B := make([]block, memory)
for lane := uint32(0); lane < threads; lane++ {
j := lane * (memory / threads)
binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
blake2bHash(block0[:], h0[:])
for i := range B[0] {
B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
}
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
blake2bHash(block0[:], h0[:])
for i := range B[0] {
B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
}
}
return B
}
func processBlocks(B []block, time, memory, threads uint32, mode int) {
const syncPoints = 4
lanes := memory / threads
segments := lanes / syncPoints
processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
var addresses, in, zero block
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
in[0] = uint64(n)
in[1] = uint64(lane)
in[2] = uint64(slice)
in[3] = uint64(memory)
in[4] = uint64(time)
in[5] = uint64(mode)
}
index := uint32(0)
if n == 0 && slice == 0 {
index = 2 // we have already generated the first two blocks
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
in[6]++
processBlock(&addresses, &in, &zero)
processBlock(&addresses, &addresses, &zero)
}
}
offset := lane*lanes + slice*segments + index
var random uint64
for index < segments {
prev := offset - 1
if index == 0 && slice == 0 {
prev = lane*lanes + lanes - 1 // last block in lane
}
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
if index%blockLength == 0 {
in[6]++
processBlock(&addresses, &in, &zero)
processBlock(&addresses, &addresses, &zero)
}
random = addresses[index%blockLength]
} else {
random = B[prev][0]
}
newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
processBlockXOR(&B[offset], &B[prev], &B[newOffset])
index, offset = index+1, offset+1
}
wg.Done()
}
for n := uint32(0); n < time; n++ {
for slice := uint32(0); slice < syncPoints; slice++ {
var wg sync.WaitGroup
for lane := uint32(0); lane < threads; lane++ {
wg.Add(1)
go processSegment(n, slice, lane, &wg)
}
wg.Wait()
}
}
}
func extractKey(B []block, memory, threads, keyLen uint32) []byte {
lanes := memory / threads
for lane := uint32(0); lane < threads-1; lane++ {
for i, v := range B[(lane*lanes)+lanes-1] {
B[memory-1][i] ^= v
}
}
var block [1024]byte
for i, v := range B[memory-1] {
binary.LittleEndian.PutUint64(block[i*8:], v)
}
key := make([]byte, keyLen)
blake2bHash(key, block[:])
return key
}
func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
refLane := uint32(rand>>32) % threads
m, s := 3*segments, (slice+1)%4*segments
if lane == refLane {
m += index
}
if n == 0 {
m, s = slice*segments, 0
if slice == 0 || lane == refLane {
m += index
}
}
if index == 0 || lane == refLane {
m--
}
return phi(rand, uint64(m), uint64(s), refLane, lanes)
}
func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
p := rand & 0xFFFFFFFF
p = (p * p) >> 32
p = (p * m) >> 32
return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
}

113
vendor/golang.org/x/crypto/argon2/argon2_test.go generated vendored Normal file
View File

@ -0,0 +1,113 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package argon2
import (
"bytes"
"encoding/hex"
"testing"
)
var (
genKatPassword = []byte{
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
}
genKatSalt = []byte{0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}
genKatSecret = []byte{0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}
genKatAAD = []byte{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}
)
func TestArgon2(t *testing.T) {
defer func(sse4 bool) { useSSE4 = sse4 }(useSSE4)
if useSSE4 {
t.Log("SSE4.1 version")
testArgon2i(t)
testArgon2d(t)
testArgon2id(t)
useSSE4 = false
}
t.Log("generic version")
testArgon2i(t)
testArgon2d(t)
testArgon2id(t)
}
func testArgon2d(t *testing.T) {
want := []byte{
0x51, 0x2b, 0x39, 0x1b, 0x6f, 0x11, 0x62, 0x97,
0x53, 0x71, 0xd3, 0x09, 0x19, 0x73, 0x42, 0x94,
0xf8, 0x68, 0xe3, 0xbe, 0x39, 0x84, 0xf3, 0xc1,
0xa1, 0x3a, 0x4d, 0xb9, 0xfa, 0xbe, 0x4a, 0xcb,
}
hash := deriveKey(argon2d, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
if !bytes.Equal(hash, want) {
t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
}
}
func testArgon2i(t *testing.T) {
want := []byte{
0xc8, 0x14, 0xd9, 0xd1, 0xdc, 0x7f, 0x37, 0xaa,
0x13, 0xf0, 0xd7, 0x7f, 0x24, 0x94, 0xbd, 0xa1,
0xc8, 0xde, 0x6b, 0x01, 0x6d, 0xd3, 0x88, 0xd2,
0x99, 0x52, 0xa4, 0xc4, 0x67, 0x2b, 0x6c, 0xe8,
}
hash := deriveKey(argon2i, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
if !bytes.Equal(hash, want) {
t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
}
}
func testArgon2id(t *testing.T) {
want := []byte{
0x0d, 0x64, 0x0d, 0xf5, 0x8d, 0x78, 0x76, 0x6c,
0x08, 0xc0, 0x37, 0xa3, 0x4a, 0x8b, 0x53, 0xc9,
0xd0, 0x1e, 0xf0, 0x45, 0x2d, 0x75, 0xb6, 0x5e,
0xb5, 0x25, 0x20, 0xe9, 0x6b, 0x01, 0xe6, 0x59,
}
hash := deriveKey(argon2id, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
if !bytes.Equal(hash, want) {
t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
}
}
func benchmarkArgon2(mode int, time, memory uint32, threads uint8, keyLen uint32, b *testing.B) {
password := []byte("password")
salt := []byte("choosing random salts is hard")
b.ReportAllocs()
for i := 0; i < b.N; i++ {
deriveKey(mode, password, salt, nil, nil, time, memory, threads, keyLen)
}
}
func BenchmarkArgon2i(b *testing.B) {
b.Run(" Time: 3 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 3, 32*1024, 1, 32, b) })
b.Run(" Time: 4 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 4, 32*1024, 1, 32, b) })
b.Run(" Time: 5 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 5, 32*1024, 1, 32, b) })
b.Run(" Time: 3 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 3, 64*1024, 4, 32, b) })
b.Run(" Time: 4 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 4, 64*1024, 4, 32, b) })
b.Run(" Time: 5 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 5, 64*1024, 4, 32, b) })
}
func BenchmarkArgon2d(b *testing.B) {
b.Run(" Time: 3, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 3, 32*1024, 1, 32, b) })
b.Run(" Time: 4, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 4, 32*1024, 1, 32, b) })
b.Run(" Time: 5, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 5, 32*1024, 1, 32, b) })
b.Run(" Time: 3, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 3, 64*1024, 4, 32, b) })
b.Run(" Time: 4, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 4, 64*1024, 4, 32, b) })
b.Run(" Time: 5, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 5, 64*1024, 4, 32, b) })
}
func BenchmarkArgon2id(b *testing.B) {
b.Run(" Time: 3, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 3, 32*1024, 1, 32, b) })
b.Run(" Time: 4, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 4, 32*1024, 1, 32, b) })
b.Run(" Time: 5, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 5, 32*1024, 1, 32, b) })
b.Run(" Time: 3, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 3, 64*1024, 4, 32, b) })
b.Run(" Time: 4, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 4, 64*1024, 4, 32, b) })
b.Run(" Time: 5, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 5, 64*1024, 4, 32, b) })
}

53
vendor/golang.org/x/crypto/argon2/blake2b.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package argon2
import (
"encoding/binary"
"hash"
"golang.org/x/crypto/blake2b"
)
// blake2bHash computes an arbitrary long hash value of in
// and writes the hash to out.
func blake2bHash(out []byte, in []byte) {
var b2 hash.Hash
if n := len(out); n < blake2b.Size {
b2, _ = blake2b.New(n, nil)
} else {
b2, _ = blake2b.New512(nil)
}
var buffer [blake2b.Size]byte
binary.LittleEndian.PutUint32(buffer[:4], uint32(len(out)))
b2.Write(buffer[:4])
b2.Write(in)
if len(out) <= blake2b.Size {
b2.Sum(out[:0])
return
}
outLen := len(out)
b2.Sum(buffer[:0])
b2.Reset()
copy(out, buffer[:32])
out = out[32:]
for len(out) > blake2b.Size {
b2.Write(buffer[:])
b2.Sum(buffer[:0])
copy(out, buffer[:32])
out = out[32:]
b2.Reset()
}
if outLen%blake2b.Size > 0 { // outLen > 64
r := ((outLen + 31) / 32) - 2 // ⌈τ /32⌉-2
b2, _ = blake2b.New(outLen-32*r, nil)
}
b2.Write(buffer[:])
b2.Sum(out[:0])
}

59
vendor/golang.org/x/crypto/argon2/blamka_amd64.go generated vendored Normal file
View File

@ -0,0 +1,59 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package argon2
func init() {
useSSE4 = supportsSSE4()
}
//go:noescape
func supportsSSE4() bool
//go:noescape
func mixBlocksSSE2(out, a, b, c *block)
//go:noescape
func xorBlocksSSE2(out, a, b, c *block)
//go:noescape
func blamkaSSE4(b *block)
func processBlockSSE(out, in1, in2 *block, xor bool) {
var t block
mixBlocksSSE2(&t, in1, in2, &t)
if useSSE4 {
blamkaSSE4(&t)
} else {
for i := 0; i < blockLength; i += 16 {
blamkaGeneric(
&t[i+0], &t[i+1], &t[i+2], &t[i+3],
&t[i+4], &t[i+5], &t[i+6], &t[i+7],
&t[i+8], &t[i+9], &t[i+10], &t[i+11],
&t[i+12], &t[i+13], &t[i+14], &t[i+15],
)
}
for i := 0; i < blockLength/8; i += 2 {
blamkaGeneric(
&t[i], &t[i+1], &t[16+i], &t[16+i+1],
&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
)
}
}
if xor {
xorBlocksSSE2(out, in1, in2, &t)
} else {
mixBlocksSSE2(out, in1, in2, &t)
}
}
func processBlock(out, in1, in2 *block) {
processBlockSSE(out, in1, in2, false)
}
func processBlockXOR(out, in1, in2 *block) {
processBlockSSE(out, in1, in2, true)
}

252
vendor/golang.org/x/crypto/argon2/blamka_amd64.s generated vendored Normal file
View File

@ -0,0 +1,252 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!gccgo,!appengine
#include "textflag.h"
DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
MOVO v4, t1; \
MOVO v5, v4; \
MOVO t1, v5; \
MOVO v6, t1; \
PUNPCKLQDQ v6, t2; \
PUNPCKHQDQ v7, v6; \
PUNPCKHQDQ t2, v6; \
PUNPCKLQDQ v7, t2; \
MOVO t1, v7; \
MOVO v2, t1; \
PUNPCKHQDQ t2, v7; \
PUNPCKLQDQ v3, t2; \
PUNPCKHQDQ t2, v2; \
PUNPCKLQDQ t1, t2; \
PUNPCKHQDQ t2, v3
#define SHUFFLE_INV(v2, v3, v4, v5,