forked from lug/matterbridge
		
	Compare commits
	
		
			39 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 612acfddff | ||
|   | 932b80d4f7 | ||
|   | fac5f69ad2 | ||
|   | 97c944bb63 | ||
|   | d0c4fe78ee | ||
|   | 265457b451 | ||
|   | 4a4a29c9f6 | ||
|   | 0a91b9e1c9 | ||
|   | f56163295c | ||
|   | d1c87c068b | ||
|   | fa20761110 | ||
|   | e4a0e0a0e9 | ||
|   | d30ae19e2a | ||
|   | 5c919e6bff | ||
|   | 434393d1c3 | ||
|   | af9aa5d7cb | ||
|   | 05eb75442a | ||
|   | 3496ed0c7e | ||
|   | 1b89604c7a | ||
|   | 67a9d133e9 | ||
|   | ed9118b346 | ||
|   | 59e55cfbd5 | ||
|   | 788d3b32ac | ||
|   | 1d414cf2fd | ||
|   | cc3c168162 | ||
|   | 1ee6837f0e | ||
|   | 27dcea7c5b | ||
|   | dcda7f7b8c | ||
|   | e0cbb69a4f | ||
|   | 7ec95f786d | ||
|   | 1efe40add5 | ||
|   | cbd73ee313 | ||
|   | 34227a7a39 | ||
|   | 71cb9b2d1d | ||
|   | cd4c9b194f | ||
|   | 98762a0235 | ||
|   | 2fd1fd9573 | ||
|   | aff3964078 | ||
|   | 2778580397 | 
| @@ -54,7 +54,7 @@ See https://github.com/42wim/matterbridge/wiki | ||||
|  | ||||
| # Installing | ||||
| ## Binaries | ||||
| * Latest stable release [v1.4.1](https://github.com/42wim/matterbridge/releases/latest) | ||||
| * Latest stable release [v1.6.1](https://github.com/42wim/matterbridge/releases/latest) | ||||
| * Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)   | ||||
|  | ||||
| ## Building | ||||
| @@ -175,7 +175,7 @@ Matterbridge wouldn't exist without these libraries: | ||||
| * echo - https://github.com/labstack/echo | ||||
| * gitter - https://github.com/sromku/go-gitter | ||||
| * gops - https://github.com/google/gops | ||||
| * irc - https://github.com/thoj/go-ircevent | ||||
| * irc - https://github.com/lrstanley/girc | ||||
| * mattermost - https://github.com/mattermost/platform | ||||
| * matrix - https://github.com/matrix-org/gomatrix | ||||
| * slack - https://github.com/nlopes/slack | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/labstack/echo" | ||||
| @@ -8,14 +9,13 @@ import ( | ||||
| 	"github.com/zfjagann/golang-ring" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type Api struct { | ||||
| 	Config   *config.Protocol | ||||
| 	Remote   chan config.Message | ||||
| 	Account  string | ||||
| 	Messages ring.Ring | ||||
| 	sync.RWMutex | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| type ApiMessage struct { | ||||
| @@ -33,23 +33,21 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Api { | ||||
| 	b := &Api{} | ||||
| func New(cfg *config.BridgeConfig) *Api { | ||||
| 	b := &Api{BridgeConfig: cfg} | ||||
| 	e := echo.New() | ||||
| 	b.Messages = ring.Ring{} | ||||
| 	b.Messages.SetCapacity(cfg.Buffer) | ||||
| 	b.Config = &cfg | ||||
| 	b.Account = account | ||||
| 	b.Remote = c | ||||
| 	b.Messages.SetCapacity(b.Config.Buffer) | ||||
| 	if b.Config.Token != "" { | ||||
| 		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { | ||||
| 			return key == b.Config.Token, nil | ||||
| 		})) | ||||
| 	} | ||||
| 	e.GET("/api/messages", b.handleMessages) | ||||
| 	e.GET("/api/stream", b.handleStream) | ||||
| 	e.POST("/api/message", b.handlePostMessage) | ||||
| 	go func() { | ||||
| 		flog.Fatal(e.Start(cfg.BindAddress)) | ||||
| 		flog.Fatal(e.Start(b.Config.BindAddress)) | ||||
| 	}() | ||||
| 	return b | ||||
| } | ||||
| @@ -103,3 +101,24 @@ func (b *Api) handleMessages(c echo.Context) error { | ||||
| 	b.Messages = ring.Ring{} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Api) handleStream(c echo.Context) error { | ||||
| 	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON) | ||||
| 	c.Response().WriteHeader(http.StatusOK) | ||||
| 	closeNotifier := c.Response().CloseNotify() | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-closeNotifier: | ||||
| 			return nil | ||||
| 		default: | ||||
| 			msg := b.Messages.Dequeue() | ||||
| 			if msg != nil { | ||||
| 				if err := json.NewEncoder(c.Response()).Encode(msg); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				c.Response().Flush() | ||||
| 			} | ||||
| 			time.Sleep(200 * time.Millisecond) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/mattermost" | ||||
| 	"github.com/42wim/matterbridge/bridge/rocketchat" | ||||
| 	"github.com/42wim/matterbridge/bridge/slack" | ||||
| 	"github.com/42wim/matterbridge/bridge/sshchat" | ||||
| 	"github.com/42wim/matterbridge/bridge/steam" | ||||
| 	"github.com/42wim/matterbridge/bridge/telegram" | ||||
| 	"github.com/42wim/matterbridge/bridge/xmpp" | ||||
| @@ -45,44 +46,49 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid | ||||
| 	b.Protocol = protocol | ||||
| 	b.Account = bridge.Account | ||||
| 	b.Joined = make(map[string]bool) | ||||
| 	bridgeConfig := &config.BridgeConfig{General: &cfg.General, Account: bridge.Account, Remote: c} | ||||
|  | ||||
| 	// override config from environment | ||||
| 	config.OverrideCfgFromEnv(cfg, protocol, name) | ||||
| 	switch protocol { | ||||
| 	case "mattermost": | ||||
| 		b.Config = cfg.Mattermost[name] | ||||
| 		b.Bridger = bmattermost.New(cfg.Mattermost[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Mattermost[name] | ||||
| 		b.Bridger = bmattermost.New(bridgeConfig) | ||||
| 	case "irc": | ||||
| 		b.Config = cfg.IRC[name] | ||||
| 		b.Bridger = birc.New(cfg.IRC[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.IRC[name] | ||||
| 		b.Bridger = birc.New(bridgeConfig) | ||||
| 	case "gitter": | ||||
| 		b.Config = cfg.Gitter[name] | ||||
| 		b.Bridger = bgitter.New(cfg.Gitter[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Gitter[name] | ||||
| 		b.Bridger = bgitter.New(bridgeConfig) | ||||
| 	case "slack": | ||||
| 		b.Config = cfg.Slack[name] | ||||
| 		b.Bridger = bslack.New(cfg.Slack[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Slack[name] | ||||
| 		b.Bridger = bslack.New(bridgeConfig) | ||||
| 	case "xmpp": | ||||
| 		b.Config = cfg.Xmpp[name] | ||||
| 		b.Bridger = bxmpp.New(cfg.Xmpp[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Xmpp[name] | ||||
| 		b.Bridger = bxmpp.New(bridgeConfig) | ||||
| 	case "discord": | ||||
| 		b.Config = cfg.Discord[name] | ||||
| 		b.Bridger = bdiscord.New(cfg.Discord[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Discord[name] | ||||
| 		b.Bridger = bdiscord.New(bridgeConfig) | ||||
| 	case "telegram": | ||||
| 		b.Config = cfg.Telegram[name] | ||||
| 		b.Bridger = btelegram.New(cfg.Telegram[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Telegram[name] | ||||
| 		b.Bridger = btelegram.New(bridgeConfig) | ||||
| 	case "rocketchat": | ||||
| 		b.Config = cfg.Rocketchat[name] | ||||
| 		b.Bridger = brocketchat.New(cfg.Rocketchat[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Rocketchat[name] | ||||
| 		b.Bridger = brocketchat.New(bridgeConfig) | ||||
| 	case "matrix": | ||||
| 		b.Config = cfg.Matrix[name] | ||||
| 		b.Bridger = bmatrix.New(cfg.Matrix[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Matrix[name] | ||||
| 		b.Bridger = bmatrix.New(bridgeConfig) | ||||
| 	case "steam": | ||||
| 		b.Config = cfg.Steam[name] | ||||
| 		b.Bridger = bsteam.New(cfg.Steam[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Steam[name] | ||||
| 		b.Bridger = bsteam.New(bridgeConfig) | ||||
| 	case "sshchat": | ||||
| 		bridgeConfig.Config = cfg.Sshchat[name] | ||||
| 		b.Bridger = bsshchat.New(bridgeConfig) | ||||
| 	case "api": | ||||
| 		b.Config = cfg.Api[name] | ||||
| 		b.Bridger = api.New(cfg.Api[name], bridge.Account, c) | ||||
| 		bridgeConfig.Config = cfg.Api[name] | ||||
| 		b.Bridger = api.New(bridgeConfig) | ||||
| 	} | ||||
| 	b.Config = bridgeConfig.Config | ||||
| 	return b | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -36,6 +36,7 @@ type FileInfo struct { | ||||
| 	Name    string | ||||
| 	Data    *[]byte | ||||
| 	Comment string | ||||
| 	URL     string | ||||
| } | ||||
|  | ||||
| type ChannelInfo struct { | ||||
| @@ -59,41 +60,48 @@ type Protocol struct { | ||||
| 	IgnoreMessages         string // all protocols | ||||
| 	Jid                    string // xmpp | ||||
| 	Login                  string // mattermost, matrix | ||||
| 	Muc                    string // xmpp | ||||
| 	Name                   string // all protocols | ||||
| 	Nick                   string // all protocols | ||||
| 	NickFormatter          string // mattermost, slack | ||||
| 	NickServNick           string // IRC | ||||
| 	NickServUsername       string // IRC | ||||
| 	NickServPassword       string // IRC | ||||
| 	NicksPerRow            int    // mattermost, slack | ||||
| 	NoHomeServerSuffix     bool   // matrix | ||||
| 	NoTLS                  bool   // mattermost | ||||
| 	Password               string // IRC,mattermost,XMPP,matrix | ||||
| 	PrefixMessagesWithNick bool   // mattemost, slack | ||||
| 	Protocol               string //all protocols | ||||
| 	MessageQueue           int    // IRC, size of message queue for flood control | ||||
| 	MessageDelay           int    // IRC, time in millisecond to wait between messages | ||||
| 	MessageLength          int    // IRC, max length of a message allowed | ||||
| 	MessageFormat          string // telegram | ||||
| 	RemoteNickFormat       string // all protocols | ||||
| 	Server                 string // IRC,mattermost,XMPP,discord | ||||
| 	ShowJoinPart           bool   // all protocols | ||||
| 	ShowEmbeds             bool   // discord | ||||
| 	SkipTLSVerify          bool   // IRC, mattermost | ||||
| 	StripNick              bool   // all protocols | ||||
| 	Team                   string // mattermost | ||||
| 	Token                  string // gitter, slack, discord, api | ||||
| 	URL                    string // mattermost, slack // DEPRECATED | ||||
| 	UseAPI                 bool   // mattermost, slack | ||||
| 	UseSASL                bool   // IRC | ||||
| 	UseTLS                 bool   // IRC | ||||
| 	UseFirstName           bool   // telegram | ||||
| 	UseUserName            bool   // discord | ||||
| 	UseInsecureURL         bool   // telegram | ||||
| 	WebhookBindAddress     string // mattermost, slack | ||||
| 	WebhookURL             string // mattermost, slack | ||||
| 	WebhookUse             string // mattermost, slack, discord | ||||
| 	MediaDownloadSize      int    // all protocols | ||||
| 	MediaServerDownload    string | ||||
| 	MediaServerUpload      string | ||||
| 	MessageDelay           int        // IRC, time in millisecond to wait between messages | ||||
| 	MessageFormat          string     // telegram | ||||
| 	MessageLength          int        // IRC, max length of a message allowed | ||||
| 	MessageQueue           int        // IRC, size of message queue for flood control | ||||
| 	MessageSplit           bool       // IRC, split long messages with newlines on MessageLength instead of clipping | ||||
| 	Muc                    string     // xmpp | ||||
| 	Name                   string     // all protocols | ||||
| 	Nick                   string     // all protocols | ||||
| 	NickFormatter          string     // mattermost, slack | ||||
| 	NickServNick           string     // IRC | ||||
| 	NickServUsername       string     // IRC | ||||
| 	NickServPassword       string     // IRC | ||||
| 	NicksPerRow            int        // mattermost, slack | ||||
| 	NoHomeServerSuffix     bool       // matrix | ||||
| 	NoTLS                  bool       // mattermost | ||||
| 	Password               string     // IRC,mattermost,XMPP,matrix | ||||
| 	PrefixMessagesWithNick bool       // mattemost, slack | ||||
| 	Protocol               string     // all protocols | ||||
| 	RejoinDelay            int        // IRC | ||||
| 	ReplaceMessages        [][]string // all protocols | ||||
| 	ReplaceNicks           [][]string // all protocols | ||||
| 	RemoteNickFormat       string     // all protocols | ||||
| 	Server                 string     // IRC,mattermost,XMPP,discord | ||||
| 	ShowJoinPart           bool       // all protocols | ||||
| 	ShowEmbeds             bool       // discord | ||||
| 	SkipTLSVerify          bool       // IRC, mattermost | ||||
| 	StripNick              bool       // all protocols | ||||
| 	Team                   string     // mattermost | ||||
| 	Token                  string     // gitter, slack, discord, api | ||||
| 	URL                    string     // mattermost, slack // DEPRECATED | ||||
| 	UseAPI                 bool       // mattermost, slack | ||||
| 	UseSASL                bool       // IRC | ||||
| 	UseTLS                 bool       // IRC | ||||
| 	UseFirstName           bool       // telegram | ||||
| 	UseUserName            bool       // discord | ||||
| 	UseInsecureURL         bool       // telegram | ||||
| 	WebhookBindAddress     string     // mattermost, slack | ||||
| 	WebhookURL             string     // mattermost, slack | ||||
| 	WebhookUse             string     // mattermost, slack, discord | ||||
| } | ||||
|  | ||||
| type ChannelOptions struct { | ||||
| @@ -135,11 +143,19 @@ type Config struct { | ||||
| 	Discord            map[string]Protocol | ||||
| 	Telegram           map[string]Protocol | ||||
| 	Rocketchat         map[string]Protocol | ||||
| 	Sshchat            map[string]Protocol | ||||
| 	General            Protocol | ||||
| 	Gateway            []Gateway | ||||
| 	SameChannelGateway []SameChannelGateway | ||||
| } | ||||
|  | ||||
| type BridgeConfig struct { | ||||
| 	Config  Protocol | ||||
| 	General *Protocol | ||||
| 	Account string | ||||
| 	Remote  chan Message | ||||
| } | ||||
|  | ||||
| func NewConfig(cfgfile string) *Config { | ||||
| 	var cfg Config | ||||
| 	if _, err := toml.DecodeFile(cfgfile, &cfg); err != nil { | ||||
| @@ -167,6 +183,9 @@ func NewConfig(cfgfile string) *Config { | ||||
| 	if fail { | ||||
| 		log.Fatalf("Fix your config. Please see changelog for more information") | ||||
| 	} | ||||
| 	if cfg.General.MediaDownloadSize == 0 { | ||||
| 		cfg.General.MediaDownloadSize = 1000000 | ||||
| 	} | ||||
| 	return &cfg | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,9 +12,6 @@ import ( | ||||
|  | ||||
| type bdiscord struct { | ||||
| 	c              *discordgo.Session | ||||
| 	Config         *config.Protocol | ||||
| 	Remote         chan config.Message | ||||
| 	Account        string | ||||
| 	Channels       []*discordgo.Channel | ||||
| 	Nick           string | ||||
| 	UseChannelID   bool | ||||
| @@ -24,6 +21,7 @@ type bdiscord struct { | ||||
| 	webhookToken   string | ||||
| 	channelInfoMap map[string]*config.ChannelInfo | ||||
| 	sync.RWMutex | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -33,11 +31,8 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *bdiscord { | ||||
| 	b := &bdiscord{} | ||||
| 	b.Config = &cfg | ||||
| 	b.Remote = c | ||||
| 	b.Account = account | ||||
| func New(cfg *config.BridgeConfig) *bdiscord { | ||||
| 	b := &bdiscord{BridgeConfig: cfg} | ||||
| 	b.userMemberMap = make(map[string]*discordgo.Member) | ||||
| 	b.channelInfoMap = make(map[string]*config.ChannelInfo) | ||||
| 	if b.Config.WebhookURL != "" { | ||||
|   | ||||
| @@ -9,13 +9,11 @@ import ( | ||||
| ) | ||||
|  | ||||
| type Bgitter struct { | ||||
| 	c       *gitter.Gitter | ||||
| 	Config  *config.Protocol | ||||
| 	Remote  chan config.Message | ||||
| 	Account string | ||||
| 	User    *gitter.User | ||||
| 	Users   []gitter.User | ||||
| 	Rooms   []gitter.Room | ||||
| 	c     *gitter.Gitter | ||||
| 	User  *gitter.User | ||||
| 	Users []gitter.User | ||||
| 	Rooms []gitter.Room | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -25,12 +23,8 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Bgitter { | ||||
| 	b := &Bgitter{} | ||||
| 	b.Config = &cfg | ||||
| 	b.Remote = c | ||||
| 	b.Account = account | ||||
| 	return b | ||||
| func New(cfg *config.BridgeConfig) *Bgitter { | ||||
| 	return &Bgitter{BridgeConfig: cfg} | ||||
| } | ||||
|  | ||||
| func (b *Bgitter) Connect() error { | ||||
| @@ -125,6 +119,23 @@ func (b *Bgitter) Send(msg config.Message) (string, error) { | ||||
| 		} | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	if msg.Extra != nil { | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			for _, f := range msg.Extra["file"] { | ||||
| 				fi := f.(config.FileInfo) | ||||
| 				if fi.URL != "" { | ||||
| 					msg.Text = fi.URL | ||||
| 				} | ||||
| 				_, err := b.c.SendMessage(roomID, msg.Username+msg.Text) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
| 			} | ||||
| 			return "", nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
|   | ||||
| @@ -26,3 +26,15 @@ func DownloadFile(url string) (*[]byte, error) { | ||||
| 	resp.Body.Close() | ||||
| 	return &data, nil | ||||
| } | ||||
|  | ||||
| func SplitStringLength(input string, length int) string { | ||||
| 	a := []rune(input) | ||||
| 	str := "" | ||||
| 	for i, r := range a { | ||||
| 		str = str + string(r) | ||||
| 		if i > 0 && (i+1)%length == 0 { | ||||
| 			str += "\n" | ||||
| 		} | ||||
| 	} | ||||
| 	return str | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/lrstanley/girc" | ||||
| 	"github.com/paulrosania/go-charset/charset" | ||||
| @@ -24,12 +25,11 @@ type Birc struct { | ||||
| 	i               *girc.Client | ||||
| 	Nick            string | ||||
| 	names           map[string][]string | ||||
| 	Config          *config.Protocol | ||||
| 	Remote          chan config.Message | ||||
| 	connected       chan struct{} | ||||
| 	Local           chan config.Message // local queue for flood control | ||||
| 	Account         string | ||||
| 	FirstConnection bool | ||||
|  | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -39,13 +39,11 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Birc { | ||||
| func New(cfg *config.BridgeConfig) *Birc { | ||||
| 	b := &Birc{} | ||||
| 	b.Config = &cfg | ||||
| 	b.BridgeConfig = cfg | ||||
| 	b.Nick = b.Config.Nick | ||||
| 	b.Remote = c | ||||
| 	b.names = make(map[string][]string) | ||||
| 	b.Account = account | ||||
| 	b.connected = make(chan struct{}) | ||||
| 	if b.Config.MessageDelay == 0 { | ||||
| 		b.Config.MessageDelay = 1300 | ||||
| @@ -178,9 +176,27 @@ func (b *Birc) Send(msg config.Message) (string, error) { | ||||
| 		msg.Text = buf.String() | ||||
| 	} | ||||
|  | ||||
| 	if msg.Extra != nil { | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			for _, f := range msg.Extra["file"] { | ||||
| 				fi := f.(config.FileInfo) | ||||
| 				if fi.URL != "" { | ||||
| 					msg.Text = fi.URL | ||||
| 				} | ||||
| 				b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event} | ||||
| 			} | ||||
| 			return "", nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// split long messages on messageLength, to avoid clipped messages #281 | ||||
| 	if b.Config.MessageSplit { | ||||
| 		msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength) | ||||
| 	} | ||||
| 	for _, text := range strings.Split(msg.Text, "\n") { | ||||
| 		input := []rune(text) | ||||
| 		if len(text) > b.Config.MessageLength { | ||||
| 			text = text[:b.Config.MessageLength] + " <message clipped>" | ||||
| 			text = string(input[:b.Config.MessageLength]) + " <message clipped>" | ||||
| 		} | ||||
| 		if len(b.Local) < b.Config.MessageQueue { | ||||
| 			if len(b.Local) == b.Config.MessageQueue-1 { | ||||
| @@ -251,6 +267,7 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) { | ||||
| 	channel := event.Params[0] | ||||
| 	if event.Command == "KICK" { | ||||
| 		flog.Infof("Got kicked from %s by %s", channel, event.Source.Name) | ||||
| 		time.Sleep(time.Duration(b.Config.RejoinDelay) * time.Second) | ||||
| 		b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS} | ||||
| 		return | ||||
| 	} | ||||
| @@ -309,11 +326,10 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { | ||||
| 	rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host} | ||||
| 	flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event) | ||||
| 	msg := "" | ||||
| 	if event.Command == "CTCP_ACTION" { | ||||
| 		//	msg = event.Source.Name + " " | ||||
| 	if event.IsAction() { | ||||
| 		rmsg.Event = config.EVENT_USER_ACTION | ||||
| 	} | ||||
| 	msg += event.Trailing | ||||
| 	msg += event.StripAction() | ||||
| 	// strip IRC colors | ||||
| 	re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`) | ||||
| 	msg = re.ReplaceAllString(msg, "") | ||||
|   | ||||
| @@ -1,22 +1,24 @@ | ||||
| package bmatrix | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"mime" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	matrix "github.com/matrix-org/gomatrix" | ||||
| ) | ||||
|  | ||||
| type Bmatrix struct { | ||||
| 	mc      *matrix.Client | ||||
| 	Config  *config.Protocol | ||||
| 	Remote  chan config.Message | ||||
| 	Account string | ||||
| 	UserID  string | ||||
| 	RoomMap map[string]string | ||||
| 	sync.RWMutex | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -26,12 +28,9 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Bmatrix { | ||||
| 	b := &Bmatrix{} | ||||
| func New(cfg *config.BridgeConfig) *Bmatrix { | ||||
| 	b := &Bmatrix{BridgeConfig: cfg} | ||||
| 	b.RoomMap = make(map[string]string) | ||||
| 	b.Config = &cfg | ||||
| 	b.Account = account | ||||
| 	b.Remote = c | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| @@ -87,6 +86,44 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) { | ||||
| 			matrix.TextMessage{"m.emote", msg.Username + msg.Text}) | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	if msg.Extra != nil { | ||||
| 		// check if we have files to upload (from slack, telegram or mattermost) | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			for _, f := range msg.Extra["file"] { | ||||
| 				fi := f.(config.FileInfo) | ||||
| 				content := bytes.NewReader(*fi.Data) | ||||
| 				sp := strings.Split(fi.Name, ".") | ||||
| 				mtype := mime.TypeByExtension("." + sp[len(sp)-1]) | ||||
| 				if strings.Contains(mtype, "image") || | ||||
| 					strings.Contains(mtype, "video") { | ||||
| 					flog.Debugf("uploading file: %s %s", fi.Name, mtype) | ||||
| 					res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data))) | ||||
| 					if err != nil { | ||||
| 						flog.Errorf("file upload failed: %#v", err) | ||||
| 						continue | ||||
| 					} | ||||
| 					if strings.Contains(mtype, "video") { | ||||
| 						flog.Debugf("sendVideo %s", res.ContentURI) | ||||
| 						_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI) | ||||
| 						if err != nil { | ||||
| 							flog.Errorf("sendVideo failed: %#v", err) | ||||
| 						} | ||||
| 					} | ||||
| 					if strings.Contains(mtype, "image") { | ||||
| 						flog.Debugf("sendImage %s", res.ContentURI) | ||||
| 						_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI) | ||||
| 						if err != nil { | ||||
| 							flog.Errorf("sendImage failed: %#v", err) | ||||
| 						} | ||||
| 					} | ||||
| 					flog.Debugf("result: %#v", res) | ||||
| 				} | ||||
| 			} | ||||
| 			return "", nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b.mc.SendText(channel, msg.Username+msg.Text) | ||||
| 	return "", nil | ||||
| } | ||||
| @@ -104,7 +141,13 @@ func (b *Bmatrix) getRoomID(channel string) string { | ||||
| func (b *Bmatrix) handlematrix() error { | ||||
| 	syncer := b.mc.Syncer.(*matrix.DefaultSyncer) | ||||
| 	syncer.OnEventType("m.room.message", func(ev *matrix.Event) { | ||||
| 		if (ev.Content["msgtype"].(string) == "m.text" || ev.Content["msgtype"].(string) == "m.notice" || ev.Content["msgtype"].(string) == "m.emote") && ev.Sender != b.UserID { | ||||
| 		flog.Debugf("Received: %#v", ev) | ||||
| 		if (ev.Content["msgtype"].(string) == "m.text" || | ||||
| 			ev.Content["msgtype"].(string) == "m.notice" || | ||||
| 			ev.Content["msgtype"].(string) == "m.emote" || | ||||
| 			ev.Content["msgtype"].(string) == "m.file" || | ||||
| 			ev.Content["msgtype"].(string) == "m.image" || | ||||
| 			ev.Content["msgtype"].(string) == "m.video") && ev.Sender != b.UserID { | ||||
| 			b.RLock() | ||||
| 			channel, ok := b.RoomMap[ev.RoomID] | ||||
| 			b.RUnlock() | ||||
| @@ -121,10 +164,31 @@ func (b *Bmatrix) handlematrix() error { | ||||
| 			if ev.Content["msgtype"].(string) == "m.emote" { | ||||
| 				rmsg.Event = config.EVENT_USER_ACTION | ||||
| 			} | ||||
| 			if ev.Content["msgtype"].(string) == "m.image" || | ||||
| 				ev.Content["msgtype"].(string) == "m.video" || | ||||
| 				ev.Content["msgtype"].(string) == "m.file" { | ||||
| 				flog.Debugf("ev: %#v", ev) | ||||
| 				rmsg.Extra = make(map[string][]interface{}) | ||||
| 				url := ev.Content["url"].(string) | ||||
| 				url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1) | ||||
| 				info := ev.Content["info"].(map[string]interface{}) | ||||
| 				size := info["size"].(float64) | ||||
| 				name := ev.Content["body"].(string) | ||||
| 				flog.Debugf("trying to download %#v with size %#v", name, size) | ||||
| 				if size <= float64(b.General.MediaDownloadSize) { | ||||
| 					data, err := helper.DownloadFile(url) | ||||
| 					if err != nil { | ||||
| 						flog.Errorf("download %s failed %#v", url, err) | ||||
| 					} else { | ||||
| 						flog.Debugf("download OK %#v %#v %#v", name, len(*data), len(url)) | ||||
| 						rmsg.Extra["file"] = append(rmsg.Extra["file"], config.FileInfo{Name: name, Data: data}) | ||||
| 					} | ||||
| 				} | ||||
| 				rmsg.Text = "" | ||||
| 			} | ||||
| 			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account) | ||||
| 			b.Remote <- rmsg | ||||
| 		} | ||||
| 		flog.Debugf("Received: %#v", ev) | ||||
| 	}) | ||||
| 	go func() { | ||||
| 		for { | ||||
|   | ||||
| @@ -36,6 +36,7 @@ type Bmattermost struct { | ||||
| 	Remote  chan config.Message | ||||
| 	TeamId  string | ||||
| 	Account string | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -45,11 +46,8 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Bmattermost { | ||||
| 	b := &Bmattermost{} | ||||
| 	b.Config = &cfg | ||||
| 	b.Remote = c | ||||
| 	b.Account = account | ||||
| func New(cfg *config.BridgeConfig) *Bmattermost { | ||||
| 	b := &Bmattermost{BridgeConfig: cfg} | ||||
| 	b.mmMap = make(map[string]string) | ||||
| 	return b | ||||
| } | ||||
|   | ||||
| @@ -14,9 +14,7 @@ type MMhook struct { | ||||
|  | ||||
| type Brocketchat struct { | ||||
| 	MMhook | ||||
| 	Config  *config.Protocol | ||||
| 	Remote  chan config.Message | ||||
| 	Account string | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -26,12 +24,8 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Brocketchat { | ||||
| 	b := &Brocketchat{} | ||||
| 	b.Config = &cfg | ||||
| 	b.Remote = c | ||||
| 	b.Account = account | ||||
| 	return b | ||||
| func New(cfg *config.BridgeConfig) *Brocketchat { | ||||
| 	return &Brocketchat{BridgeConfig: cfg} | ||||
| } | ||||
|  | ||||
| func (b *Brocketchat) Command(cmd string) string { | ||||
|   | ||||
| @@ -27,14 +27,12 @@ type MMMessage struct { | ||||
| type Bslack struct { | ||||
| 	mh       *matterhook.Client | ||||
| 	sc       *slack.Client | ||||
| 	Config   *config.Protocol | ||||
| 	rtm      *slack.RTM | ||||
| 	Plus     bool | ||||
| 	Remote   chan config.Message | ||||
| 	Users    []slack.User | ||||
| 	Account  string | ||||
| 	si       *slack.Info | ||||
| 	channels []slack.Channel | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -44,12 +42,8 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Bslack { | ||||
| 	b := &Bslack{} | ||||
| 	b.Config = &cfg | ||||
| 	b.Remote = c | ||||
| 	b.Account = account | ||||
| 	return b | ||||
| func New(cfg *config.BridgeConfig) *Bslack { | ||||
| 	return &Bslack{BridgeConfig: cfg} | ||||
| } | ||||
|  | ||||
| func (b *Bslack) Command(cmd string) string { | ||||
| @@ -161,7 +155,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) { | ||||
| 		np.AsUser = true | ||||
| 	} | ||||
| 	np.Username = nick | ||||
| 	np.IconURL = config.GetIconURL(&msg, b.Config) | ||||
| 	np.IconURL = config.GetIconURL(&msg, &b.Config) | ||||
| 	if msg.Avatar != "" { | ||||
| 		np.IconURL = msg.Avatar | ||||
| 	} | ||||
| @@ -294,7 +288,7 @@ func (b *Bslack) handleSlack() { | ||||
| 		// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra | ||||
| 		if message.Raw.File != nil { | ||||
| 			// limit to 1MB for now | ||||
| 			if message.Raw.File.Size <= 1000000 { | ||||
| 			if message.Raw.File.Size <= b.General.MediaDownloadSize { | ||||
| 				comment := "" | ||||
| 				data, err := b.downloadFile(message.Raw.File.URLPrivateDownload) | ||||
| 				if err != nil { | ||||
|   | ||||
							
								
								
									
										132
									
								
								bridge/sshchat/sshchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								bridge/sshchat/sshchat.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| package bsshchat | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/shazow/ssh-chat/sshd" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| type Bsshchat struct { | ||||
| 	r *bufio.Scanner | ||||
| 	w io.WriteCloser | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| var protocol = "sshchat" | ||||
|  | ||||
| func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg *config.BridgeConfig) *Bsshchat { | ||||
| 	return &Bsshchat{BridgeConfig: cfg} | ||||
| } | ||||
|  | ||||
| func (b *Bsshchat) Connect() error { | ||||
| 	var err error | ||||
| 	flog.Infof("Connecting %s", b.Config.Server) | ||||
| 	go func() { | ||||
| 		err = sshd.ConnectShell(b.Config.Server, b.Config.Nick, func(r io.Reader, w io.WriteCloser) error { | ||||
| 			b.r = bufio.NewScanner(r) | ||||
| 			b.w = w | ||||
| 			b.r.Scan() | ||||
| 			w.Write([]byte("/theme mono\r\n")) | ||||
| 			b.handleSshChat() | ||||
| 			return nil | ||||
| 		}) | ||||
| 	}() | ||||
| 	if err != nil { | ||||
| 		flog.Debugf("%#v", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	flog.Info("Connection succeeded") | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bsshchat) Disconnect() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bsshchat) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bsshchat) Send(msg config.Message) (string, error) { | ||||
| 	// ignore delete messages | ||||
| 	if msg.Event == config.EVENT_MSG_DELETE { | ||||
| 		return "", nil | ||||
| 	} | ||||
| 	flog.Debugf("Receiving %#v", msg) | ||||
| 	if msg.Extra != nil { | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			for _, f := range msg.Extra["file"] { | ||||
| 				fi := f.(config.FileInfo) | ||||
| 				if fi.URL != "" { | ||||
| 					msg.Text = fi.URL | ||||
| 				} | ||||
| 				b.w.Write([]byte(msg.Username + msg.Text)) | ||||
| 			} | ||||
| 			return "", nil | ||||
| 		} | ||||
| 	} | ||||
| 	b.w.Write([]byte(msg.Username + msg.Text + "\r\n")) | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| /* | ||||
| func (b *Bsshchat) sshchatKeepAlive() chan bool { | ||||
| 	done := make(chan bool) | ||||
| 	go func() { | ||||
| 		ticker := time.NewTicker(90 * time.Second) | ||||
| 		defer ticker.Stop() | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-ticker.C: | ||||
| 				flog.Debugf("PING") | ||||
| 				err := b.xc.PingC2S("", "") | ||||
| 				if err != nil { | ||||
| 					flog.Debugf("PING failed %#v", err) | ||||
| 				} | ||||
| 			case <-done: | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 	return done | ||||
| } | ||||
| */ | ||||
|  | ||||
| func stripPrompt(s string) string { | ||||
| 	pos := strings.LastIndex(s, "\033[K") | ||||
| 	if pos < 0 { | ||||
| 		return s | ||||
| 	} | ||||
| 	return s[pos+3:] | ||||
| } | ||||
|  | ||||
| func (b *Bsshchat) handleSshChat() error { | ||||
| 	/* | ||||
| 		done := b.sshchatKeepAlive() | ||||
| 		defer close(done) | ||||
| 	*/ | ||||
| 	wait := true | ||||
| 	for { | ||||
| 		if b.r.Scan() { | ||||
| 			res := strings.Split(stripPrompt(b.r.Text()), ":") | ||||
| 			if res[0] == "-> Set theme" { | ||||
| 				wait = false | ||||
| 				log.Debugf("mono found, allowing") | ||||
| 				continue | ||||
| 			} | ||||
| 			if !wait { | ||||
| 				flog.Debugf("message %#v", res) | ||||
| 				rmsg := config.Message{Username: res[0], Text: strings.Join(res[1:], ":"), Channel: "sshchat", Account: b.Account, UserID: "nick"} | ||||
| 				b.Remote <- rmsg | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -16,11 +16,9 @@ import ( | ||||
| type Bsteam struct { | ||||
| 	c         *steam.Client | ||||
| 	connected chan struct{} | ||||
| 	Config    *config.Protocol | ||||
| 	Remote    chan config.Message | ||||
| 	Account   string | ||||
| 	userMap   map[steamid.SteamId]string | ||||
| 	sync.RWMutex | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -30,11 +28,8 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Bsteam { | ||||
| 	b := &Bsteam{} | ||||
| 	b.Config = &cfg | ||||
| 	b.Remote = c | ||||
| 	b.Account = account | ||||
| func New(cfg *config.BridgeConfig) *Bsteam { | ||||
| 	b := &Bsteam{BridgeConfig: cfg} | ||||
| 	b.userMap = make(map[steamid.SteamId]string) | ||||
| 	b.connected = make(chan struct{}) | ||||
| 	return b | ||||
|   | ||||
| @@ -12,10 +12,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| type Btelegram struct { | ||||
| 	c       *tgbotapi.BotAPI | ||||
| 	Config  *config.Protocol | ||||
| 	Remote  chan config.Message | ||||
| 	Account string | ||||
| 	c *tgbotapi.BotAPI | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -25,12 +23,8 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Btelegram { | ||||
| 	b := &Btelegram{} | ||||
| 	b.Config = &cfg | ||||
| 	b.Remote = c | ||||
| 	b.Account = account | ||||
| 	return b | ||||
| func New(cfg *config.BridgeConfig) *Btelegram { | ||||
| 	return &Btelegram{BridgeConfig: cfg} | ||||
| } | ||||
|  | ||||
| func (b *Btelegram) Connect() error { | ||||
| @@ -90,6 +84,9 @@ func (b *Btelegram) Send(msg config.Message) (string, error) { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		m := tgbotapi.NewEditMessageText(chatid, msgid, msg.Username+msg.Text) | ||||
| 		if b.Config.MessageFormat == "HTML" { | ||||
| 			m.ParseMode = tgbotapi.ModeHTML | ||||
| 		} | ||||
| 		_, err = b.c.Send(m) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| @@ -179,6 +176,29 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | ||||
| 		if message.Document != nil { | ||||
| 			b.handleDownload(message.Document, &fmsg) | ||||
| 		} | ||||
| 		if message.Voice != nil { | ||||
| 			b.handleDownload(message.Voice, &fmsg) | ||||
| 		} | ||||
| 		if message.Audio != nil { | ||||
| 			b.handleDownload(message.Audio, &fmsg) | ||||
| 		} | ||||
|  | ||||
| 		if message.ForwardFrom != nil { | ||||
| 			usernameForward := "" | ||||
| 			if b.Config.UseFirstName { | ||||
| 				usernameForward = message.ForwardFrom.FirstName | ||||
| 			} | ||||
| 			if usernameForward == "" { | ||||
| 				usernameForward = message.ForwardFrom.UserName | ||||
| 				if usernameForward == "" { | ||||
| 					usernameForward = message.ForwardFrom.FirstName | ||||
| 				} | ||||
| 			} | ||||
| 			if usernameForward == "" { | ||||
| 				usernameForward = "unknown" | ||||
| 			} | ||||
| 			text = "Forwarded from " + usernameForward + ": " + text | ||||
| 		} | ||||
|  | ||||
| 		// quote the previous message | ||||
| 		if message.ReplyToMessage != nil { | ||||
| @@ -224,11 +244,31 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) { | ||||
| 	text := "" | ||||
| 	fileid := "" | ||||
| 	switch v := file.(type) { | ||||
| 	case *tgbotapi.Audio: | ||||
| 		size = v.FileSize | ||||
| 		url = b.getFileDirectURL(v.FileID) | ||||
| 		urlPart := strings.Split(url, "/") | ||||
| 		name = urlPart[len(urlPart)-1] | ||||
| 		text = " " + url | ||||
| 		fileid = v.FileID | ||||
| 	case *tgbotapi.Voice: | ||||
| 		size = v.FileSize | ||||
| 		url = b.getFileDirectURL(v.FileID) | ||||
| 		urlPart := strings.Split(url, "/") | ||||
| 		name = urlPart[len(urlPart)-1] | ||||
| 		text = " " + url | ||||
| 		if !strings.HasSuffix(name, ".ogg") { | ||||
| 			name = name + ".ogg" | ||||
| 		} | ||||
| 		fileid = v.FileID | ||||
| 	case *tgbotapi.Sticker: | ||||
| 		size = v.FileSize | ||||
| 		url = b.getFileDirectURL(v.FileID) | ||||
| 		urlPart := strings.Split(url, "/") | ||||
| 		name = urlPart[len(urlPart)-1] | ||||
| 		if !strings.HasSuffix(name, ".webp") { | ||||
| 			name = name + ".webp" | ||||
| 		} | ||||
| 		text = " " + url | ||||
| 		fileid = v.FileID | ||||
| 	case *tgbotapi.Video: | ||||
| @@ -259,7 +299,7 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) { | ||||
| 	// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra | ||||
| 	// limit to 1MB for now | ||||
| 	flog.Debugf("trying to download %#v fileid %#v with size %#v", name, fileid, size) | ||||
| 	if size <= 1000000 { | ||||
| 	if size <= b.General.MediaDownloadSize { | ||||
| 		data, err := helper.DownloadFile(url) | ||||
| 		if err != nil { | ||||
| 			flog.Errorf("download %s failed %#v", url, err) | ||||
|   | ||||
| @@ -14,9 +14,7 @@ import ( | ||||
| type Bxmpp struct { | ||||
| 	xc      *xmpp.Client | ||||
| 	xmppMap map[string]string | ||||
| 	Config  *config.Protocol | ||||
| 	Remote  chan config.Message | ||||
| 	Account string | ||||
| 	*config.BridgeConfig | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -26,12 +24,9 @@ func init() { | ||||
| 	flog = log.WithFields(log.Fields{"module": protocol}) | ||||
| } | ||||
|  | ||||
| func New(cfg config.Protocol, account string, c chan config.Message) *Bxmpp { | ||||
| 	b := &Bxmpp{} | ||||
| func New(cfg *config.BridgeConfig) *Bxmpp { | ||||
| 	b := &Bxmpp{BridgeConfig: cfg} | ||||
| 	b.xmppMap = make(map[string]string) | ||||
| 	b.Config = &cfg | ||||
| 	b.Account = account | ||||
| 	b.Remote = c | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| @@ -85,6 +80,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) { | ||||
| 		return "", nil | ||||
| 	} | ||||
| 	flog.Debugf("Receiving %#v", msg) | ||||
| 	if msg.Extra != nil { | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			for _, f := range msg.Extra["file"] { | ||||
| 				fi := f.(config.FileInfo) | ||||
| 				if fi.URL != "" { | ||||
| 					msg.Text = fi.URL | ||||
| 				} | ||||
| 				b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text}) | ||||
| 			} | ||||
| 			return "", nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text}) | ||||
| 	return "", nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										37
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,40 @@ | ||||
| # v1.6.1 | ||||
| ## Bugfix | ||||
| * general: Display of nicks not longer working (regression). Closes #323 | ||||
|  | ||||
| # v1.6.0 | ||||
| ## New features | ||||
| * sshchat: New protocol support added (https://github.com/shazow/ssh-chat) | ||||
| * general: Allow specifying maximum download size of media using MediaDownloadSize (slack,telegram,matrix) | ||||
| * api: Add (simple, one listener) long-polling support (api). Closes #307 | ||||
| * telegram: Add support for forwarded messages. Closes #313 | ||||
| * telegram: Add support for Audio/Voice files (telegram). Closes #314 | ||||
| * irc: Add RejoinDelay option. Delay to rejoin after channel kick (irc). Closes #322 | ||||
|  | ||||
| ## Bugfix | ||||
| * telegram: Also use HTML in edited messages (telegram). Closes #315 | ||||
| * matrix: Fix panic (matrix). Closes #316 | ||||
|  | ||||
| # v1.5.1 | ||||
|  | ||||
| ## Bugfix | ||||
| * irc: Fix irc ACTION regression (irc). Closes #306 | ||||
| * irc: Split on UTF-8 for MessageSplit (irc). Closes #308 | ||||
|  | ||||
| # v1.5.0 | ||||
| ## New features | ||||
| * general: remote mediaserver support. See MediaServerDownload and MediaServerUpload in matterbridge.toml.sample | ||||
|   more information on https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D | ||||
| * general: Add support for ReplaceNicks using regexp to replace nicks. Closes #269 (see matterbridge.toml.sample) | ||||
| * general: Add support for ReplaceMessages using regexp to replace messages. #269 (see matterbridge.toml.sample) | ||||
| * irc: Add MessageSplit option to split messages on MessageLength (irc). Closes #281 | ||||
| * matrix: Add support for uploading images/video (matrix). Closes #302 | ||||
| * matrix: Add support for uploaded images/video (matrix)  | ||||
|  | ||||
| ## Bugfix | ||||
| * telegram: Add webp extension to stickers if necessary (telegram) | ||||
| * mattermost: Break when re-login fails (mattermost) | ||||
|  | ||||
| # v1.4.1 | ||||
| ## Bugfix | ||||
| * telegram: fix issue with uploading for images/documents/stickers | ||||
|   | ||||
| @@ -1,13 +1,16 @@ | ||||
| package gateway | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	//	"github.com/davecgh/go-spew/spew" | ||||
| 	"crypto/sha1" | ||||
| 	"github.com/hashicorp/golang-lru" | ||||
| 	"github.com/peterhellberg/emojilib" | ||||
| 	"net/http" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| @@ -155,7 +158,8 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM | ||||
| 		if dest.Protocol != "discord" && | ||||
| 			dest.Protocol != "slack" && | ||||
| 			dest.Protocol != "mattermost" && | ||||
| 			dest.Protocol != "telegram" { | ||||
| 			dest.Protocol != "telegram" && | ||||
| 			dest.Protocol != "matrix" { | ||||
| 			if msg.Text == "" { | ||||
| 				return brMsgIDs | ||||
| 			} | ||||
| @@ -254,6 +258,20 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin | ||||
| 	if nick == "" { | ||||
| 		nick = gw.Config.General.RemoteNickFormat | ||||
| 	} | ||||
|  | ||||
| 	// loop to replace nicks | ||||
| 	for _, outer := range br.Config.ReplaceNicks { | ||||
| 		search := outer[0] | ||||
| 		replace := outer[1] | ||||
| 		// TODO move compile to bridge init somewhere | ||||
| 		re, err := regexp.Compile(search) | ||||
| 		if err != nil { | ||||
| 			log.Errorf("regexp in %s failed: %s", msg.Account, err) | ||||
| 			break | ||||
| 		} | ||||
| 		msg.Username = re.ReplaceAllString(msg.Username, replace) | ||||
| 	} | ||||
|  | ||||
| 	if len(msg.Username) > 0 { | ||||
| 		// fix utf-8 issue #193 | ||||
| 		i := 0 | ||||
| @@ -287,9 +305,50 @@ func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string | ||||
| func (gw *Gateway) modifyMessage(msg *config.Message) { | ||||
| 	// replace :emoji: to unicode | ||||
| 	msg.Text = emojilib.Replace(msg.Text) | ||||
| 	br := gw.Bridges[msg.Account] | ||||
| 	// loop to replace messages | ||||
| 	for _, outer := range br.Config.ReplaceMessages { | ||||
| 		search := outer[0] | ||||
| 		replace := outer[1] | ||||
| 		// TODO move compile to bridge init somewhere | ||||
| 		re, err := regexp.Compile(search) | ||||
| 		if err != nil { | ||||
| 			log.Errorf("regexp in %s failed: %s", msg.Account, err) | ||||
| 			break | ||||
| 		} | ||||
| 		msg.Text = re.ReplaceAllString(msg.Text, replace) | ||||
| 	} | ||||
| 	msg.Gateway = gw.Name | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) handleFiles(msg *config.Message) { | ||||
| 	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	if len(msg.Extra["file"]) > 0 { | ||||
| 		client := &http.Client{ | ||||
| 			Timeout: time.Second * 5, | ||||
| 		} | ||||
| 		for i, f := range msg.Extra["file"] { | ||||
| 			fi := f.(config.FileInfo) | ||||
| 			sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data)) | ||||
| 			reader := bytes.NewReader(*fi.Data) | ||||
| 			url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name | ||||
| 			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name | ||||
| 			extra := msg.Extra["file"][i].(config.FileInfo) | ||||
| 			extra.URL = durl | ||||
| 			msg.Extra["file"][i] = extra | ||||
| 			req, _ := http.NewRequest("PUT", url, reader) | ||||
| 			req.Header.Set("Content-Type", "binary/octet-stream") | ||||
| 			_, err := client.Do(req) | ||||
| 			if err != nil { | ||||
| 				log.Errorf("mediaserver upload failed: %#v", err) | ||||
| 			} | ||||
| 			log.Debugf("mediaserver download URL = %s", durl) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func getChannelID(msg config.Message) string { | ||||
| 	return msg.Channel + msg.Account | ||||
| } | ||||
|   | ||||
| @@ -99,6 +99,7 @@ func (r *Router) handleReceive() { | ||||
| 			if !gw.ignoreMessage(&msg) { | ||||
| 				msg.Timestamp = time.Now() | ||||
| 				gw.modifyMessage(&msg) | ||||
| 				gw.handleFiles(&msg) | ||||
| 				for _, br := range gw.Bridges { | ||||
| 					msgIDs = append(msgIDs, gw.handleMessage(msg, br)...) | ||||
| 				} | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	version = "1.4.1" | ||||
| 	version = "1.6.1" | ||||
| 	githash string | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -80,6 +80,15 @@ MessageQueue=30 | ||||
| #OPTIONAL (default 400) | ||||
| MessageLength=400 | ||||
|  | ||||
| #Split messages on MessageLength instead of showing the <message clipped> | ||||
| #WARNING: this could lead to flooding | ||||
| #OPTIONAL (default false) | ||||
| MessageSplit=false | ||||
|  | ||||
| #Delay in seconds to rejoin a channel when kicked | ||||
| #OPTIONAL (default 0) | ||||
| RejoinDelay=0 | ||||
|  | ||||
| #Nicks you want to ignore.  | ||||
| #Messages from those users will not be sent to other bridges. | ||||
| #OPTIONAL | ||||
| @@ -91,6 +100,23 @@ IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -154,6 +180,23 @@ IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #Messages you want to replace. | ||||
| #It replaces outgoing messages from the bridge. | ||||
| #So you need to place it by the sending bridge definition. | ||||
| #Regular expressions supported | ||||
| #Some examples: | ||||
| #This replaces cat => dog and sleep => awake | ||||
| #ReplaceMessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #This Replaces every number with number.  123 => numbernumbernumber | ||||
| #ReplaceMessages=[ ["[0-9]","number"] ] | ||||
| #OPTIONAL (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #Nicks you want to replace. | ||||
| #See ReplaceMessages for syntaxA | ||||
| #OPTIONAL (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -208,6 +251,23 @@ IgnoreNicks="spammer1 spammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -322,6 +382,23 @@ IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -366,6 +443,23 @@ IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -457,6 +551,23 @@ IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -525,6 +636,23 @@ IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -592,6 +720,23 @@ IgnoreNicks="spammer1 spammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -660,6 +805,23 @@ IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -720,6 +882,23 @@ IgnoreNicks="spammer1 spammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -774,6 +953,23 @@ IgnoreNicks="spammer1 spammer2" | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #messages you want to replace. | ||||
| #it replaces outgoing messages from the bridge. | ||||
| #so you need to place it by the sending bridge definition. | ||||
| #regular expressions supported | ||||
| #some examples: | ||||
| #this replaces cat => dog and sleep => awake | ||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] | ||||
| #this replaces every number with number.  123 => numbernumbernumber | ||||
| #replacemessages=[ ["[0-9]","number"] ] | ||||
| #optional (default empty) | ||||
| ReplaceMessages=[ ["cat","dog"] ] | ||||
|  | ||||
| #nicks you want to replace. | ||||
| #see replacemessages for syntaxa | ||||
| #optional (default empty) | ||||
| ReplaceNicks=[ ["user--","user"] ] | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -838,6 +1034,30 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> " | ||||
| #OPTIONAL (default false) | ||||
| StripNick=false | ||||
|  | ||||
|  | ||||
| #MediaServerUpload and MediaServerDownload are used for uploading images/files/video to | ||||
| #a remote "mediaserver" (a webserver like caddy for example). | ||||
| #When configured images/files uploaded on bridges like mattermost,slack, telegram will be downloaded | ||||
| #and uploaded again to MediaServerUpload URL | ||||
| #The MediaServerDownload will be used so that bridges without native uploading support: | ||||
| #gitter, irc and xmpp will be shown links to the files on MediaServerDownload | ||||
| # | ||||
| #More information https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D | ||||
| #OPTIONAL (default empty) | ||||
| MediaServerUpload="https://user:pass@yourserver.com/upload" | ||||
| #OPTIONAL (default empty) | ||||
| MediaServerDownload="https://youserver.com/download" | ||||
|  | ||||
| #MediaDownloadSize is the maximum size of attachments, videos, images | ||||
| #matterbridge will download and upload this file to bridges that also support uploading files. | ||||
| #eg downloading from slack to upload it to mattermost | ||||
| # | ||||
| #It will only download from bridges that don't have public links available, which are for the moment | ||||
| #slack, telegram and matrix | ||||
| # | ||||
| #Optional (default 1000000 (1 megabyte)) | ||||
| MediaDownloadSize=1000000 | ||||
|  | ||||
| ################################################################### | ||||
| #Gateway configuration | ||||
| ################################################################### | ||||
|   | ||||
| @@ -817,9 +817,14 @@ func (m *MMClient) StatusLoop() { | ||||
| 				backoff = time.Second * 60 | ||||
| 			case <-time.After(time.Second * 5): | ||||
| 				if retries > 3 { | ||||
| 					m.log.Debug("StatusLoop() timeout") | ||||
| 					m.Logout() | ||||
| 					m.WsQuit = false | ||||
| 					m.Login() | ||||
| 					err := m.Login() | ||||
| 					if err != nil { | ||||
| 						log.Errorf("Login failed: %#v", err) | ||||
| 						break | ||||
| 					} | ||||
| 					if m.OnWsConnect != nil { | ||||
| 						m.OnWsConnect() | ||||
| 					} | ||||
|   | ||||
							
								
								
									
										11
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -494,14 +494,9 @@ func (c *context) Stream(code int, contentType string, r io.Reader) (err error) | ||||
| } | ||||
|  | ||||
| func (c *context) File(file string) (err error) { | ||||
| 	file, err = url.QueryUnescape(file) // Issue #839 | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	f, err := os.Open(file) | ||||
| 	if err != nil { | ||||
| 		return ErrNotFound | ||||
| 		return NotFoundHandler(c) | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| @@ -510,7 +505,7 @@ func (c *context) File(file string) (err error) { | ||||
| 		file = filepath.Join(file, indexPage) | ||||
| 		f, err = os.Open(file) | ||||
| 		if err != nil { | ||||
| 			return ErrNotFound | ||||
| 			return NotFoundHandler(c) | ||||
| 		} | ||||
| 		defer f.Close() | ||||
| 		if fi, err = f.Stat(); err != nil { | ||||
| @@ -530,7 +525,7 @@ func (c *context) Inline(file, name string) (err error) { | ||||
| } | ||||
|  | ||||
| func (c *context) contentDisposition(file, name, dispositionType string) (err error) { | ||||
| 	c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%s", dispositionType, name)) | ||||
| 	c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name)) | ||||
| 	c.File(file) | ||||
| 	return | ||||
| } | ||||
|   | ||||
							
								
								
									
										136
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -72,6 +72,7 @@ type ( | ||||
| 		TLSServer        *http.Server | ||||
| 		Listener         net.Listener | ||||
| 		TLSListener      net.Listener | ||||
| 		AutoTLSManager   autocert.Manager | ||||
| 		DisableHTTP2     bool | ||||
| 		Debug            bool | ||||
| 		HideBanner       bool | ||||
| @@ -79,22 +80,22 @@ type ( | ||||
| 		Binder           Binder | ||||
| 		Validator        Validator | ||||
| 		Renderer         Renderer | ||||
| 		AutoTLSManager   autocert.Manager | ||||
| 		// Mutex            sync.RWMutex | ||||
| 		Logger Logger | ||||
| 	} | ||||
|  | ||||
| 	// Route contains a handler and information for matching against requests. | ||||
| 	Route struct { | ||||
| 		Method  string `json:"method"` | ||||
| 		Path    string `json:"path"` | ||||
| 		Handler string `json:"handler"` | ||||
| 		Method string `json:"method"` | ||||
| 		Path   string `json:"path"` | ||||
| 		Name   string `json:"name"` | ||||
| 	} | ||||
|  | ||||
| 	// HTTPError represents an error that occurred while handling a request. | ||||
| 	HTTPError struct { | ||||
| 		Code    int | ||||
| 		Message interface{} | ||||
| 		Inner   error // Stores the error returned by an external dependency | ||||
| 	} | ||||
|  | ||||
| 	// MiddlewareFunc defines a function to process middleware. | ||||
| @@ -121,7 +122,7 @@ type ( | ||||
|  | ||||
| 	// i is the interface for Echo and Group. | ||||
| 	i interface { | ||||
| 		GET(string, HandlerFunc, ...MiddlewareFunc) | ||||
| 		GET(string, HandlerFunc, ...MiddlewareFunc) *Route | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| @@ -212,7 +213,7 @@ const ( | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	version = "3.1.0" | ||||
| 	version = "3.2.5" | ||||
| 	website = "https://echo.labstack.com" | ||||
| 	// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo | ||||
| 	banner = ` | ||||
| @@ -282,7 +283,7 @@ func New() (e *Echo) { | ||||
| 	e.TLSServer.Handler = e | ||||
| 	e.HTTPErrorHandler = e.DefaultHTTPErrorHandler | ||||
| 	e.Binder = &DefaultBinder{} | ||||
| 	e.Logger.SetLevel(log.OFF) | ||||
| 	e.Logger.SetLevel(log.ERROR) | ||||
| 	e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0) | ||||
| 	e.pool.New = func() interface{} { | ||||
| 		return e.NewContext(nil, nil) | ||||
| @@ -319,6 +320,9 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) { | ||||
| 	if he, ok := err.(*HTTPError); ok { | ||||
| 		code = he.Code | ||||
| 		msg = he.Message | ||||
| 		if he.Inner != nil { | ||||
| 			msg = fmt.Sprintf("%v, %v", err, he.Inner) | ||||
| 		} | ||||
| 	} else if e.Debug { | ||||
| 		msg = err.Error() | ||||
| 	} else { | ||||
| @@ -328,19 +332,19 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) { | ||||
| 		msg = Map{"message": msg} | ||||
| 	} | ||||
|  | ||||
| 	e.Logger.Error(err) | ||||
|  | ||||
| 	// Send response | ||||
| 	if !c.Response().Committed { | ||||
| 		if c.Request().Method == HEAD { // Issue #608 | ||||
| 			if err := c.NoContent(code); err != nil { | ||||
| 				goto ERROR | ||||
| 			} | ||||
| 			err = c.NoContent(code) | ||||
| 		} else { | ||||
| 			if err := c.JSON(code, msg); err != nil { | ||||
| 				goto ERROR | ||||
| 			} | ||||
| 			err = c.JSON(code, msg) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			e.Logger.Error(err) | ||||
| 		} | ||||
| 	} | ||||
| ERROR: | ||||
| 	e.Logger.Error(err) | ||||
| } | ||||
|  | ||||
| // Pre adds middleware to the chain which is run before router. | ||||
| @@ -355,104 +359,114 @@ func (e *Echo) Use(middleware ...MiddlewareFunc) { | ||||
|  | ||||
| // CONNECT registers a new CONNECT route for a path with matching handler in the | ||||
| // router with optional route-level middleware. | ||||
| func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(CONNECT, path, h, m...) | ||||
| func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(CONNECT, path, h, m...) | ||||
| } | ||||
|  | ||||
| // DELETE registers a new DELETE route for a path with matching handler in the router | ||||
| // with optional route-level middleware. | ||||
| func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(DELETE, path, h, m...) | ||||
| func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(DELETE, path, h, m...) | ||||
| } | ||||
|  | ||||
| // GET registers a new GET route for a path with matching handler in the router | ||||
| // with optional route-level middleware. | ||||
| func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(GET, path, h, m...) | ||||
| func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(GET, path, h, m...) | ||||
| } | ||||
|  | ||||
| // HEAD registers a new HEAD route for a path with matching handler in the | ||||
| // router with optional route-level middleware. | ||||
| func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(HEAD, path, h, m...) | ||||
| func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(HEAD, path, h, m...) | ||||
| } | ||||
|  | ||||
| // OPTIONS registers a new OPTIONS route for a path with matching handler in the | ||||
| // router with optional route-level middleware. | ||||
| func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(OPTIONS, path, h, m...) | ||||
| func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(OPTIONS, path, h, m...) | ||||
| } | ||||
|  | ||||
| // PATCH registers a new PATCH route for a path with matching handler in the | ||||
| // router with optional route-level middleware. | ||||
| func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(PATCH, path, h, m...) | ||||
| func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(PATCH, path, h, m...) | ||||
| } | ||||
|  | ||||
| // POST registers a new POST route for a path with matching handler in the | ||||
| // router with optional route-level middleware. | ||||
| func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(POST, path, h, m...) | ||||
| func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(POST, path, h, m...) | ||||
| } | ||||
|  | ||||
| // PUT registers a new PUT route for a path with matching handler in the | ||||
| // router with optional route-level middleware. | ||||
| func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(PUT, path, h, m...) | ||||
| func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(PUT, path, h, m...) | ||||
| } | ||||
|  | ||||
| // TRACE registers a new TRACE route for a path with matching handler in the | ||||
| // router with optional route-level middleware. | ||||
| func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	e.add(TRACE, path, h, m...) | ||||
| func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return e.Add(TRACE, path, h, m...) | ||||
| } | ||||
|  | ||||
| // Any registers a new route for all HTTP methods and path with matching handler | ||||
| // in the router with optional route-level middleware. | ||||
| func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) { | ||||
| func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route { | ||||
| 	routes := make([]*Route, 0) | ||||
| 	for _, m := range methods { | ||||
| 		e.add(m, path, handler, middleware...) | ||||
| 		routes = append(routes, e.Add(m, path, handler, middleware...)) | ||||
| 	} | ||||
| 	return routes | ||||
| } | ||||
|  | ||||
| // Match registers a new route for multiple HTTP methods and path with matching | ||||
| // handler in the router with optional route-level middleware. | ||||
| func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { | ||||
| func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route { | ||||
| 	routes := make([]*Route, 0) | ||||
| 	for _, m := range methods { | ||||
| 		e.add(m, path, handler, middleware...) | ||||
| 		routes = append(routes, e.Add(m, path, handler, middleware...)) | ||||
| 	} | ||||
| 	return routes | ||||
| } | ||||
|  | ||||
| // Static registers a new route with path prefix to serve static files from the | ||||
| // provided root directory. | ||||
| func (e *Echo) Static(prefix, root string) { | ||||
| func (e *Echo) Static(prefix, root string) *Route { | ||||
| 	if root == "" { | ||||
| 		root = "." // For security we want to restrict to CWD. | ||||
| 	} | ||||
| 	static(e, prefix, root) | ||||
| 	return static(e, prefix, root) | ||||
| } | ||||
|  | ||||
| func static(i i, prefix, root string) { | ||||
| func static(i i, prefix, root string) *Route { | ||||
| 	h := func(c Context) error { | ||||
| 		name := filepath.Join(root, path.Clean("/"+c.Param("*"))) // "/"+ for security | ||||
| 		p, err := PathUnescape(c.Param("*")) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security | ||||
| 		return c.File(name) | ||||
| 	} | ||||
| 	i.GET(prefix, h) | ||||
| 	if prefix == "/" { | ||||
| 		i.GET(prefix+"*", h) | ||||
| 	} else { | ||||
| 		i.GET(prefix+"/*", h) | ||||
| 		return i.GET(prefix+"*", h) | ||||
| 	} | ||||
|  | ||||
| 	return i.GET(prefix+"/*", h) | ||||
| } | ||||
|  | ||||
| // File registers a new route with path to serve a static file. | ||||
| func (e *Echo) File(path, file string) { | ||||
| 	e.GET(path, func(c Context) error { | ||||
| func (e *Echo) File(path, file string) *Route { | ||||
| 	return e.GET(path, func(c Context) error { | ||||
| 		return c.File(file) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { | ||||
| // Add registers a new route for an HTTP method and path with matching handler | ||||
| // in the router with optional route-level middleware. | ||||
| func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route { | ||||
| 	name := handlerName(handler) | ||||
| 	e.router.Add(method, path, func(c Context) error { | ||||
| 		h := handler | ||||
| @@ -463,11 +477,12 @@ func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...Middl | ||||
| 		return h(c) | ||||
| 	}) | ||||
| 	r := &Route{ | ||||
| 		Method:  method, | ||||
| 		Path:    path, | ||||
| 		Handler: name, | ||||
| 		Method: method, | ||||
| 		Path:   path, | ||||
| 		Name:   name, | ||||
| 	} | ||||
| 	e.router.routes[method+path] = r | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| // Group creates a new router group with prefix and optional group-level middleware. | ||||
| @@ -479,12 +494,22 @@ func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) { | ||||
|  | ||||
| // URI generates a URI from handler. | ||||
| func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string { | ||||
| 	name := handlerName(handler) | ||||
| 	return e.Reverse(name, params...) | ||||
| } | ||||
|  | ||||
| // URL is an alias for `URI` function. | ||||
| func (e *Echo) URL(h HandlerFunc, params ...interface{}) string { | ||||
| 	return e.URI(h, params...) | ||||
| } | ||||
|  | ||||
| // Reverse generates an URL from route name and provided parameters. | ||||
| func (e *Echo) Reverse(name string, params ...interface{}) string { | ||||
| 	uri := new(bytes.Buffer) | ||||
| 	ln := len(params) | ||||
| 	n := 0 | ||||
| 	name := handlerName(handler) | ||||
| 	for _, r := range e.router.routes { | ||||
| 		if r.Handler == name { | ||||
| 		if r.Name == name { | ||||
| 			for i, l := 0, len(r.Path); i < l; i++ { | ||||
| 				if r.Path[i] == ':' && n < ln { | ||||
| 					for ; i < l && r.Path[i] != '/'; i++ { | ||||
| @@ -502,11 +527,6 @@ func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string { | ||||
| 	return uri.String() | ||||
| } | ||||
|  | ||||
| // URL is an alias for `URI` function. | ||||
| func (e *Echo) URL(h HandlerFunc, params ...interface{}) string { | ||||
| 	return e.URI(h, params...) | ||||
| } | ||||
|  | ||||
| // Routes returns the registered routes. | ||||
| func (e *Echo) Routes() []*Route { | ||||
| 	routes := []*Route{} | ||||
| @@ -653,7 +673,7 @@ func NewHTTPError(code int, message ...interface{}) *HTTPError { | ||||
|  | ||||
| // Error makes it compatible with `error` interface. | ||||
| func (he *HTTPError) Error() string { | ||||
| 	return fmt.Sprintf("code=%d, message=%s", he.Code, he.Message) | ||||
| 	return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message) | ||||
| } | ||||
|  | ||||
| // WrapHandler wraps `http.Handler` into `echo.HandlerFunc`. | ||||
|   | ||||
							
								
								
									
										47
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -21,66 +21,66 @@ func (g *Group) Use(middleware ...MiddlewareFunc) { | ||||
| 	// Allow all requests to reach the group as they might get dropped if router | ||||
| 	// doesn't find a match, making none of the group middleware process. | ||||
| 	g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error { | ||||
| 		return ErrNotFound | ||||
| 		return NotFoundHandler(c) | ||||
| 	}, g.middleware...) | ||||
| } | ||||
|  | ||||
| // CONNECT implements `Echo#CONNECT()` for sub-routes within the Group. | ||||
| func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(CONNECT, path, h, m...) | ||||
| func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(CONNECT, path, h, m...) | ||||
| } | ||||
|  | ||||
| // DELETE implements `Echo#DELETE()` for sub-routes within the Group. | ||||
| func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(DELETE, path, h, m...) | ||||
| func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(DELETE, path, h, m...) | ||||
| } | ||||
|  | ||||
| // GET implements `Echo#GET()` for sub-routes within the Group. | ||||
| func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(GET, path, h, m...) | ||||
| func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(GET, path, h, m...) | ||||
| } | ||||
|  | ||||
| // HEAD implements `Echo#HEAD()` for sub-routes within the Group. | ||||
| func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(HEAD, path, h, m...) | ||||
| func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(HEAD, path, h, m...) | ||||
| } | ||||
|  | ||||
| // OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group. | ||||
| func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(OPTIONS, path, h, m...) | ||||
| func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(OPTIONS, path, h, m...) | ||||
| } | ||||
|  | ||||
| // PATCH implements `Echo#PATCH()` for sub-routes within the Group. | ||||
| func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(PATCH, path, h, m...) | ||||
| func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(PATCH, path, h, m...) | ||||
| } | ||||
|  | ||||
| // POST implements `Echo#POST()` for sub-routes within the Group. | ||||
| func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(POST, path, h, m...) | ||||
| func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(POST, path, h, m...) | ||||
| } | ||||
|  | ||||
| // PUT implements `Echo#PUT()` for sub-routes within the Group. | ||||
| func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(PUT, path, h, m...) | ||||
| func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(PUT, path, h, m...) | ||||
| } | ||||
|  | ||||
| // TRACE implements `Echo#TRACE()` for sub-routes within the Group. | ||||
| func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) { | ||||
| 	g.add(TRACE, path, h, m...) | ||||
| func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { | ||||
| 	return g.Add(TRACE, path, h, m...) | ||||
| } | ||||
|  | ||||
| // Any implements `Echo#Any()` for sub-routes within the Group. | ||||
| func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) { | ||||
| 	for _, m := range methods { | ||||
| 		g.add(m, path, handler, middleware...) | ||||
| 		g.Add(m, path, handler, middleware...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Match implements `Echo#Match()` for sub-routes within the Group. | ||||
| func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { | ||||
| 	for _, m := range methods { | ||||
| 		g.add(m, path, handler, middleware...) | ||||
| 		g.Add(m, path, handler, middleware...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -102,12 +102,13 @@ func (g *Group) File(path, file string) { | ||||
| 	g.echo.File(g.prefix+path, file) | ||||
| } | ||||
|  | ||||
| func (g *Group) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { | ||||
| // Add implements `Echo#Add()` for sub-routes within the Group. | ||||
| func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route { | ||||
| 	// Combine into a new slice to avoid accidentally passing the same slice for | ||||
| 	// multiple routes, which would lead to later add() calls overwriting the | ||||
| 	// middleware from earlier calls. | ||||
| 	m := []MiddlewareFunc{} | ||||
| 	m = append(m, g.middleware...) | ||||
| 	m = append(m, middleware...) | ||||
| 	g.echo.add(method, g.prefix+path, handler, m...) | ||||
| 	return g.echo.Add(method, g.prefix+path, handler, m...) | ||||
| } | ||||
|   | ||||
							
								
								
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ package middleware | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
| @@ -27,7 +28,7 @@ type ( | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	basic        = "Basic" | ||||
| 	basic        = "basic" | ||||
| 	defaultRealm = "Restricted" | ||||
| ) | ||||
|  | ||||
| @@ -54,7 +55,7 @@ func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc { | ||||
| func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { | ||||
| 	// Defaults | ||||
| 	if config.Validator == nil { | ||||
| 		panic("basic-auth middleware requires a validator function") | ||||
| 		panic("echo: basic-auth middleware requires a validator function") | ||||
| 	} | ||||
| 	if config.Skipper == nil { | ||||
| 		config.Skipper = DefaultBasicAuthConfig.Skipper | ||||
| @@ -72,7 +73,7 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { | ||||
| 			auth := c.Request().Header.Get(echo.HeaderAuthorization) | ||||
| 			l := len(basic) | ||||
|  | ||||
| 			if len(auth) > l+1 && auth[:l] == basic { | ||||
| 			if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic { | ||||
| 				b, err := base64.StdEncoding.DecodeString(auth[l+1:]) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
|   | ||||
							
								
								
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| package middleware | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	// BodyDumpConfig defines the config for BodyDump middleware. | ||||
| 	BodyDumpConfig struct { | ||||
| 		// Skipper defines a function to skip middleware. | ||||
| 		Skipper Skipper | ||||
|  | ||||
| 		// Handler receives request and response payload. | ||||
| 		// Required. | ||||
| 		Handler BodyDumpHandler | ||||
| 	} | ||||
|  | ||||
| 	// BodyDumpHandler receives the request and response payload. | ||||
| 	BodyDumpHandler func(echo.Context, []byte, []byte) | ||||
|  | ||||
| 	bodyDumpResponseWriter struct { | ||||
| 		io.Writer | ||||
| 		http.ResponseWriter | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// DefaultBodyDumpConfig is the default Gzip middleware config. | ||||
| 	DefaultBodyDumpConfig = BodyDumpConfig{ | ||||
| 		Skipper: DefaultSkipper, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| // BodyDump returns a BodyDump middleware. | ||||
| // | ||||
| // BodyLimit middleware captures the request and response payload and calls the | ||||
| // registered handler. | ||||
| func BodyDump(handler BodyDumpHandler) echo.MiddlewareFunc { | ||||
| 	c := DefaultBodyDumpConfig | ||||
| 	c.Handler = handler | ||||
| 	return BodyDumpWithConfig(c) | ||||
| } | ||||
|  | ||||
| // BodyDumpWithConfig returns a BodyDump middleware with config. | ||||
| // See: `BodyDump()`. | ||||
| func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc { | ||||
| 	// Defaults | ||||
| 	if config.Handler == nil { | ||||
| 		panic("echo: body-dump middleware requires a handler function") | ||||
| 	} | ||||
| 	if config.Skipper == nil { | ||||
| 		config.Skipper = DefaultBodyDumpConfig.Skipper | ||||
| 	} | ||||
|  | ||||
| 	return func(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 		return func(c echo.Context) (err error) { | ||||
| 			if config.Skipper(c) { | ||||
| 				return next(c) | ||||
| 			} | ||||
|  | ||||
| 			// Request | ||||
| 			reqBody := []byte{} | ||||
| 			if c.Request().Body != nil { // Read | ||||
| 				reqBody, _ = ioutil.ReadAll(c.Request().Body) | ||||
| 			} | ||||
| 			c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // Reset | ||||
|  | ||||
| 			// Response | ||||
| 			resBody := new(bytes.Buffer) | ||||
| 			mw := io.MultiWriter(c.Response().Writer, resBody) | ||||
| 			writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer} | ||||
| 			c.Response().Writer = writer | ||||
|  | ||||
| 			if err = next(c); err != nil { | ||||
| 				c.Error(err) | ||||
| 			} | ||||
|  | ||||
| 			// Callback | ||||
| 			config.Handler(c, reqBody, resBody.Bytes()) | ||||
|  | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *bodyDumpResponseWriter) WriteHeader(code int) { | ||||
| 	w.ResponseWriter.WriteHeader(code) | ||||
| } | ||||
|  | ||||
| func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) { | ||||
| 	return w.Writer.Write(b) | ||||
| } | ||||
|  | ||||
| func (w *bodyDumpResponseWriter) Flush() { | ||||
| 	w.ResponseWriter.(http.Flusher).Flush() | ||||
| } | ||||
|  | ||||
| func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { | ||||
| 	return w.ResponseWriter.(http.Hijacker).Hijack() | ||||
| } | ||||
|  | ||||
| func (w *bodyDumpResponseWriter) CloseNotify() <-chan bool { | ||||
| 	return w.ResponseWriter.(http.CloseNotifier).CloseNotify() | ||||
| } | ||||
							
								
								
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -30,7 +30,7 @@ type ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// DefaultBodyLimitConfig is the default Gzip middleware config. | ||||
| 	// DefaultBodyLimitConfig is the default BodyLimit middleware config. | ||||
| 	DefaultBodyLimitConfig = BodyLimitConfig{ | ||||
| 		Skipper: DefaultSkipper, | ||||
| 	} | ||||
| @@ -60,7 +60,7 @@ func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc { | ||||
|  | ||||
| 	limit, err := bytes.Parse(config.Limit) | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Errorf("invalid body-limit=%s", config.Limit)) | ||||
| 		panic(fmt.Errorf("echo: invalid body-limit=%s", config.Limit)) | ||||
| 	} | ||||
| 	config.limit = limit | ||||
| 	pool := limitedReaderPool(config) | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -67,7 +67,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { | ||||
| 			res := c.Response() | ||||
| 			res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding) | ||||
| 			if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) { | ||||
| 				res.Header().Add(echo.HeaderContentEncoding, gzipScheme) // Issue #806 | ||||
| 				res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806 | ||||
| 				rw := res.Writer | ||||
| 				w, err := gzip.NewWriterLevel(rw, config.Level) | ||||
| 				if err != nil { | ||||
| @@ -98,6 +98,7 @@ func (w *gzipResponseWriter) WriteHeader(code int) { | ||||
| 	if code == http.StatusNoContent { // Issue #489 | ||||
| 		w.ResponseWriter.Header().Del(echo.HeaderContentEncoding) | ||||
| 	} | ||||
| 	w.Header().Del(echo.HeaderContentLength) // Issue #444 | ||||
| 	w.ResponseWriter.WriteHeader(code) | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| package middleware | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"reflect" | ||||
| @@ -57,6 +56,12 @@ const ( | ||||
| 	AlgorithmHS256 = "HS256" | ||||
| ) | ||||
|  | ||||
| // Errors | ||||
| var ( | ||||
| 	ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "Missing or malformed jwt") | ||||
| 	ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "Invalid or expired jwt") | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// DefaultJWTConfig is the default JWT auth middleware config. | ||||
| 	DefaultJWTConfig = JWTConfig{ | ||||
| @@ -77,7 +82,7 @@ var ( | ||||
| // | ||||
| // See: https://jwt.io/introduction | ||||
| // See `JWTConfig.TokenLookup` | ||||
| func JWT(key []byte) echo.MiddlewareFunc { | ||||
| func JWT(key interface{}) echo.MiddlewareFunc { | ||||
| 	c := DefaultJWTConfig | ||||
| 	c.SigningKey = key | ||||
| 	return JWTWithConfig(c) | ||||
| @@ -134,14 +139,15 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { | ||||
|  | ||||
| 			auth, err := extractor(c) | ||||
| 			if err != nil { | ||||
| 				return echo.NewHTTPError(http.StatusBadRequest, err.Error()) | ||||
| 				return err | ||||
| 			} | ||||
| 			token := new(jwt.Token) | ||||
| 			// Issue #647, #656 | ||||
| 			if _, ok := config.Claims.(jwt.MapClaims); ok { | ||||
| 				token, err = jwt.Parse(auth, config.keyFunc) | ||||
| 			} else { | ||||
| 				claims := reflect.ValueOf(config.Claims).Interface().(jwt.Claims) | ||||
| 				t := reflect.ValueOf(config.Claims).Type().Elem() | ||||
| 				claims := reflect.New(t).Interface().(jwt.Claims) | ||||
| 				token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc) | ||||
| 			} | ||||
| 			if err == nil && token.Valid { | ||||
| @@ -149,7 +155,11 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { | ||||
| 				c.Set(config.ContextKey, token) | ||||
| 				return next(c) | ||||
| 			} | ||||
| 			return echo.ErrUnauthorized | ||||
| 			return &echo.HTTPError{ | ||||
| 				Code:    ErrJWTInvalid.Code, | ||||
| 				Message: ErrJWTInvalid.Message, | ||||
| 				Inner:   err, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -162,7 +172,7 @@ func jwtFromHeader(header string, authScheme string) jwtExtractor { | ||||
| 		if len(auth) > l+1 && auth[:l] == authScheme { | ||||
| 			return auth[l+1:], nil | ||||
| 		} | ||||
| 		return "", errors.New("Missing or invalid jwt in the request header") | ||||
| 		return "", ErrJWTMissing | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -171,7 +181,7 @@ func jwtFromQuery(param string) jwtExtractor { | ||||
| 	return func(c echo.Context) (string, error) { | ||||
| 		token := c.QueryParam(param) | ||||
| 		if token == "" { | ||||
| 			return "", errors.New("Missing jwt in the query string") | ||||
| 			return "", ErrJWTMissing | ||||
| 		} | ||||
| 		return token, nil | ||||
| 	} | ||||
| @@ -182,7 +192,7 @@ func jwtFromCookie(name string) jwtExtractor { | ||||
| 	return func(c echo.Context) (string, error) { | ||||
| 		cookie, err := c.Cookie(name) | ||||
| 		if err != nil { | ||||
| 			return "", errors.New("Missing jwt in the cookie") | ||||
| 			return "", ErrJWTMissing | ||||
| 		} | ||||
| 		return cookie.Value, nil | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -72,7 +72,7 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc { | ||||
| 		config.KeyLookup = DefaultKeyAuthConfig.KeyLookup | ||||
| 	} | ||||
| 	if config.Validator == nil { | ||||
| 		panic("key-auth middleware requires a validator function") | ||||
| 		panic("echo: key-auth middleware requires a validator function") | ||||
| 	} | ||||
|  | ||||
| 	// Initialize | ||||
|   | ||||
							
								
								
									
										42
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| package middleware | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/rand" | ||||
| @@ -54,35 +53,38 @@ type ( | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// DefaultProxyConfig is the default Proxy middleware config. | ||||
| 	DefaultProxyConfig = ProxyConfig{ | ||||
| 		Skipper: DefaultSkipper, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func proxyHTTP(t *ProxyTarget) http.Handler { | ||||
| 	return httputil.NewSingleHostReverseProxy(t.URL) | ||||
| } | ||||
|  | ||||
| func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		h, ok := w.(http.Hijacker) | ||||
| 		if !ok { | ||||
| 			c.Error(errors.New("proxy raw, not a hijacker")) | ||||
| 			return | ||||
| 		} | ||||
| 		in, _, err := h.Hijack() | ||||
| 		in, _, err := c.Response().Hijack() | ||||
| 		if err != nil { | ||||
| 			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", r.URL, err)) | ||||
| 			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err)) | ||||
| 			return | ||||
| 		} | ||||
| 		defer in.Close() | ||||
|  | ||||
| 		out, err := net.Dial("tcp", t.URL.Host) | ||||
| 		if err != nil { | ||||
| 			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", r.URL, err)) | ||||
| 			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err)) | ||||
| 			c.Error(he) | ||||
| 			return | ||||
| 		} | ||||
| 		defer out.Close() | ||||
|  | ||||
| 		// Write header | ||||
| 		err = r.Write(out) | ||||
| 		if err != nil { | ||||
| 			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request copy error=%v, url=%s", r.URL, err)) | ||||
| 			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err)) | ||||
| 			c.Error(he) | ||||
| 			return | ||||
| 		} | ||||
| @@ -97,7 +99,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { | ||||
| 		go cp(in, out) | ||||
| 		err = <-errc | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			c.Logger().Errorf("proxy raw, error=%v, url=%s", r.URL, err) | ||||
| 			c.Logger().Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| @@ -118,8 +120,18 @@ func (r *RoundRobinBalancer) Next() *ProxyTarget { | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| // Proxy returns an HTTP/WebSocket reverse proxy middleware. | ||||
| func Proxy(config ProxyConfig) echo.MiddlewareFunc { | ||||
| // Proxy returns a Proxy middleware. | ||||
| // | ||||
| // Proxy middleware forwards the request to upstream server using a configured load balancing technique. | ||||
| func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc { | ||||
| 	c := DefaultProxyConfig | ||||
| 	c.Balancer = balancer | ||||
| 	return ProxyWithConfig(c) | ||||
| } | ||||
|  | ||||
| // ProxyWithConfig returns a Proxy middleware with config. | ||||
| // See: `Proxy()` | ||||
| func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { | ||||
| 	// Defaults | ||||
| 	if config.Skipper == nil { | ||||
| 		config.Skipper = DefaultLoggerConfig.Skipper | ||||
| @@ -130,6 +142,10 @@ func Proxy(config ProxyConfig) echo.MiddlewareFunc { | ||||
|  | ||||
| 	return func(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 		return func(c echo.Context) (err error) { | ||||
| 			if config.Skipper(c) { | ||||
| 				return next(c) | ||||
| 			} | ||||
|  | ||||
| 			req := c.Request() | ||||
| 			res := c.Response() | ||||
| 			tgt := config.Balancer.Next() | ||||
|   | ||||
							
								
								
									
										10
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -5,7 +5,6 @@ import ( | ||||
| 	"runtime" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/gommon/color" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| @@ -64,17 +63,14 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc { | ||||
|  | ||||
| 			defer func() { | ||||
| 				if r := recover(); r != nil { | ||||
| 					var err error | ||||
| 					switch r := r.(type) { | ||||
| 					case error: | ||||
| 						err = r | ||||
| 					default: | ||||
| 					err, ok := r.(error) | ||||
| 					if !ok { | ||||
| 						err = fmt.Errorf("%v", r) | ||||
| 					} | ||||
| 					stack := make([]byte, config.StackSize) | ||||
| 					length := runtime.Stack(stack, !config.DisableStackAll) | ||||
| 					if !config.DisablePrintStack { | ||||
| 						c.Logger().Printf("[%s] %s %s\n", color.Red("PANIC RECOVER"), err, stack[:length]) | ||||
| 						c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length]) | ||||
| 					} | ||||
| 					c.Error(err) | ||||
| 				} | ||||
|   | ||||
							
								
								
									
										33
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,6 +2,7 @@ package middleware | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| @@ -66,7 +67,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { | ||||
| 	} | ||||
|  | ||||
| 	return func(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 		return func(c echo.Context) error { | ||||
| 		return func(c echo.Context) (err error) { | ||||
| 			if config.Skipper(c) { | ||||
| 				return next(c) | ||||
| 			} | ||||
| @@ -75,17 +76,25 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { | ||||
| 			if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`. | ||||
| 				p = c.Param("*") | ||||
| 			} | ||||
| 			p, err = echo.PathUnescape(p) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 			name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security | ||||
|  | ||||
| 			fi, err := os.Stat(name) | ||||
| 			if err != nil { | ||||
| 				if os.IsNotExist(err) { | ||||
| 					if config.HTML5 && path.Ext(p) == "" { | ||||
| 						return c.File(filepath.Join(config.Root, config.Index)) | ||||
| 					if err = next(c); err != nil { | ||||
| 						if he, ok := err.(*echo.HTTPError); ok { | ||||
| 							if config.HTML5 && he.Code == http.StatusNotFound { | ||||
| 								return c.File(filepath.Join(config.Root, config.Index)) | ||||
| 							} | ||||
| 						} | ||||
| 						return | ||||
| 					} | ||||
| 					return next(c) | ||||
| 				} | ||||
| 				return err | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			if fi.IsDir() { | ||||
| @@ -99,7 +108,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { | ||||
| 					if os.IsNotExist(err) { | ||||
| 						return next(c) | ||||
| 					} | ||||
| 					return err | ||||
| 					return | ||||
| 				} | ||||
|  | ||||
| 				return c.File(index) | ||||
| @@ -110,20 +119,20 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func listDir(name string, res *echo.Response) error { | ||||
| func listDir(name string, res *echo.Response) (err error) { | ||||
| 	dir, err := os.Open(name) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return | ||||
| 	} | ||||
| 	dirs, err := dir.Readdir(-1) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Create a directory index | ||||
| 	res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8) | ||||
| 	if _, err = fmt.Fprintf(res, "<pre>\n"); err != nil { | ||||
| 		return err | ||||
| 		return | ||||
| 	} | ||||
| 	for _, d := range dirs { | ||||
| 		name := d.Name() | ||||
| @@ -133,9 +142,9 @@ func listDir(name string, res *echo.Response) error { | ||||
| 			name += "/" | ||||
| 		} | ||||
| 		if _, err = fmt.Fprintf(res, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name); err != nil { | ||||
| 			return err | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	_, err = fmt.Fprintf(res, "</pre>\n") | ||||
| 	return err | ||||
| 	return | ||||
| } | ||||
|   | ||||
							
								
								
									
										19
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -11,11 +11,12 @@ type ( | ||||
| 	// by an HTTP handler to construct an HTTP response. | ||||
| 	// See: https://golang.org/pkg/net/http/#ResponseWriter | ||||
| 	Response struct { | ||||
| 		Writer    http.ResponseWriter | ||||
| 		Status    int | ||||
| 		Size      int64 | ||||
| 		Committed bool | ||||
| 		echo      *Echo | ||||
| 		echo        *Echo | ||||
| 		beforeFuncs []func() | ||||
| 		Writer      http.ResponseWriter | ||||
| 		Status      int | ||||
| 		Size        int64 | ||||
| 		Committed   bool | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| @@ -34,6 +35,11 @@ func (r *Response) Header() http.Header { | ||||
| 	return r.Writer.Header() | ||||
| } | ||||
|  | ||||
| // Before registers a function which is called just before the response is written. | ||||
| func (r *Response) Before(fn func()) { | ||||
| 	r.beforeFuncs = append(r.beforeFuncs, fn) | ||||
| } | ||||
|  | ||||
| // WriteHeader sends an HTTP response header with status code. If WriteHeader is | ||||
| // not called explicitly, the first call to Write will trigger an implicit | ||||
| // WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly | ||||
| @@ -43,6 +49,9 @@ func (r *Response) WriteHeader(code int) { | ||||
| 		r.echo.Logger.Warn("response already committed") | ||||
| 		return | ||||
| 	} | ||||
| 	for _, fn := range r.beforeFuncs { | ||||
| 		fn() | ||||
| 	} | ||||
| 	r.Status = code | ||||
| 	r.Writer.WriteHeader(code) | ||||
| 	r.Committed = true | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -394,7 +394,7 @@ func (r *Router) Find(method, path string, c Context) { | ||||
| 		if cn = cn.findChildByKind(akind); cn == nil { | ||||
| 			if nn != nil { | ||||
| 				cn = nn | ||||
| 				nn = nil // Next | ||||
| 				nn = cn.parent // Next (Issue #954) | ||||
| 				search = ns | ||||
| 				if nk == pkind { | ||||
| 					goto Param | ||||
|   | ||||
							
								
								
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| // +build go1.7, !go1.8 | ||||
|  | ||||
| package echo | ||||
|  | ||||
| import ( | ||||
| 	"net/url" | ||||
| ) | ||||
|  | ||||
| // PathUnescape is wraps `url.QueryUnescape` | ||||
| func PathUnescape(s string) (string, error) { | ||||
| 	return url.QueryUnescape(s) | ||||
| } | ||||
							
								
								
									
										10
									
								
								vendor/github.com/labstack/echo/util_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/util_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| // +build go1.8 | ||||
|  | ||||
| package echo | ||||
|  | ||||
| import "net/url" | ||||
|  | ||||
| // PathUnescape is wraps `url.PathUnescape` | ||||
| func PathUnescape(s string) (string, error) { | ||||
| 	return url.PathUnescape(s) | ||||
| } | ||||
							
								
								
									
										80
									
								
								vendor/github.com/lrstanley/girc/builtin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/lrstanley/girc/builtin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -16,64 +16,62 @@ func (c *Client) registerBuiltins() { | ||||
| 	c.Handlers.mu.Lock() | ||||
|  | ||||
| 	// Built-in things that should always be supported. | ||||
| 	c.Handlers.register(true, RPL_WELCOME, HandlerFunc(func(c *Client, e Event) { | ||||
| 		go handleConnect(c, e) | ||||
| 	})) | ||||
| 	c.Handlers.register(true, PING, HandlerFunc(handlePING)) | ||||
| 	c.Handlers.register(true, PONG, HandlerFunc(handlePONG)) | ||||
| 	c.Handlers.register(true, true, RPL_WELCOME, HandlerFunc(handleConnect)) | ||||
| 	c.Handlers.register(true, false, PING, HandlerFunc(handlePING)) | ||||
| 	c.Handlers.register(true, false, PONG, HandlerFunc(handlePONG)) | ||||
|  | ||||
| 	if !c.Config.disableTracking { | ||||
| 		// Joins/parts/anything that may add/remove/rename users. | ||||
| 		c.Handlers.register(true, JOIN, HandlerFunc(handleJOIN)) | ||||
| 		c.Handlers.register(true, PART, HandlerFunc(handlePART)) | ||||
| 		c.Handlers.register(true, KICK, HandlerFunc(handleKICK)) | ||||
| 		c.Handlers.register(true, QUIT, HandlerFunc(handleQUIT)) | ||||
| 		c.Handlers.register(true, NICK, HandlerFunc(handleNICK)) | ||||
| 		c.Handlers.register(true, RPL_NAMREPLY, HandlerFunc(handleNAMES)) | ||||
| 		c.Handlers.register(true, false, JOIN, HandlerFunc(handleJOIN)) | ||||
| 		c.Handlers.register(true, false, PART, HandlerFunc(handlePART)) | ||||
| 		c.Handlers.register(true, false, KICK, HandlerFunc(handleKICK)) | ||||
| 		c.Handlers.register(true, false, QUIT, HandlerFunc(handleQUIT)) | ||||
| 		c.Handlers.register(true, false, NICK, HandlerFunc(handleNICK)) | ||||
| 		c.Handlers.register(true, false, RPL_NAMREPLY, HandlerFunc(handleNAMES)) | ||||
|  | ||||
| 		// Modes. | ||||
| 		c.Handlers.register(true, MODE, HandlerFunc(handleMODE)) | ||||
| 		c.Handlers.register(true, RPL_CHANNELMODEIS, HandlerFunc(handleMODE)) | ||||
| 		c.Handlers.register(true, false, MODE, HandlerFunc(handleMODE)) | ||||
| 		c.Handlers.register(true, false, RPL_CHANNELMODEIS, HandlerFunc(handleMODE)) | ||||
|  | ||||
| 		// WHO/WHOX responses. | ||||
| 		c.Handlers.register(true, RPL_WHOREPLY, HandlerFunc(handleWHO)) | ||||
| 		c.Handlers.register(true, RPL_WHOSPCRPL, HandlerFunc(handleWHO)) | ||||
| 		c.Handlers.register(true, false, RPL_WHOREPLY, HandlerFunc(handleWHO)) | ||||
| 		c.Handlers.register(true, false, RPL_WHOSPCRPL, HandlerFunc(handleWHO)) | ||||
|  | ||||
| 		// Other misc. useful stuff. | ||||
| 		c.Handlers.register(true, TOPIC, HandlerFunc(handleTOPIC)) | ||||
| 		c.Handlers.register(true, RPL_TOPIC, HandlerFunc(handleTOPIC)) | ||||
| 		c.Handlers.register(true, RPL_MYINFO, HandlerFunc(handleMYINFO)) | ||||
| 		c.Handlers.register(true, RPL_ISUPPORT, HandlerFunc(handleISUPPORT)) | ||||
| 		c.Handlers.register(true, RPL_MOTDSTART, HandlerFunc(handleMOTD)) | ||||
| 		c.Handlers.register(true, RPL_MOTD, HandlerFunc(handleMOTD)) | ||||
| 		c.Handlers.register(true, false, TOPIC, HandlerFunc(handleTOPIC)) | ||||
| 		c.Handlers.register(true, false, RPL_TOPIC, HandlerFunc(handleTOPIC)) | ||||
| 		c.Handlers.register(true, false, RPL_MYINFO, HandlerFunc(handleMYINFO)) | ||||
| 		c.Handlers.register(true, false, RPL_ISUPPORT, HandlerFunc(handleISUPPORT)) | ||||
| 		c.Handlers.register(true, false, RPL_MOTDSTART, HandlerFunc(handleMOTD)) | ||||
| 		c.Handlers.register(true, false, RPL_MOTD, HandlerFunc(handleMOTD)) | ||||
|  | ||||
| 		// Keep users lastactive times up to date. | ||||
| 		c.Handlers.register(true, PRIVMSG, HandlerFunc(updateLastActive)) | ||||
| 		c.Handlers.register(true, NOTICE, HandlerFunc(updateLastActive)) | ||||
| 		c.Handlers.register(true, TOPIC, HandlerFunc(updateLastActive)) | ||||
| 		c.Handlers.register(true, KICK, HandlerFunc(updateLastActive)) | ||||
| 		c.Handlers.register(true, false, PRIVMSG, HandlerFunc(updateLastActive)) | ||||
| 		c.Handlers.register(true, false, NOTICE, HandlerFunc(updateLastActive)) | ||||
| 		c.Handlers.register(true, false, TOPIC, HandlerFunc(updateLastActive)) | ||||
| 		c.Handlers.register(true, false, KICK, HandlerFunc(updateLastActive)) | ||||
|  | ||||
| 		// CAP IRCv3-specific tracking and functionality. | ||||
| 		c.Handlers.register(true, CAP, HandlerFunc(handleCAP)) | ||||
| 		c.Handlers.register(true, CAP_CHGHOST, HandlerFunc(handleCHGHOST)) | ||||
| 		c.Handlers.register(true, CAP_AWAY, HandlerFunc(handleAWAY)) | ||||
| 		c.Handlers.register(true, CAP_ACCOUNT, HandlerFunc(handleACCOUNT)) | ||||
| 		c.Handlers.register(true, ALL_EVENTS, HandlerFunc(handleTags)) | ||||
| 		c.Handlers.register(true, false, CAP, HandlerFunc(handleCAP)) | ||||
| 		c.Handlers.register(true, false, CAP_CHGHOST, HandlerFunc(handleCHGHOST)) | ||||
| 		c.Handlers.register(true, false, CAP_AWAY, HandlerFunc(handleAWAY)) | ||||
| 		c.Handlers.register(true, false, CAP_ACCOUNT, HandlerFunc(handleACCOUNT)) | ||||
| 		c.Handlers.register(true, false, ALL_EVENTS, HandlerFunc(handleTags)) | ||||
|  | ||||
| 		// SASL IRCv3 support. | ||||
| 		c.Handlers.register(true, AUTHENTICATE, HandlerFunc(handleSASL)) | ||||
| 		c.Handlers.register(true, RPL_SASLSUCCESS, HandlerFunc(handleSASL)) | ||||
| 		c.Handlers.register(true, RPL_NICKLOCKED, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, ERR_SASLFAIL, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, ERR_SASLTOOLONG, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, ERR_SASLABORTED, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, RPL_SASLMECHS, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, false, AUTHENTICATE, HandlerFunc(handleSASL)) | ||||
| 		c.Handlers.register(true, false, RPL_SASLSUCCESS, HandlerFunc(handleSASL)) | ||||
| 		c.Handlers.register(true, false, RPL_NICKLOCKED, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, false, ERR_SASLFAIL, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, false, ERR_SASLTOOLONG, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, false, ERR_SASLABORTED, HandlerFunc(handleSASLError)) | ||||
| 		c.Handlers.register(true, false, RPL_SASLMECHS, HandlerFunc(handleSASLError)) | ||||
| 	} | ||||
|  | ||||
| 	// Nickname collisions. | ||||
| 	c.Handlers.register(true, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler)) | ||||
| 	c.Handlers.register(true, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler)) | ||||
| 	c.Handlers.register(true, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler)) | ||||
| 	c.Handlers.register(true, false, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler)) | ||||
| 	c.Handlers.register(true, false, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler)) | ||||
| 	c.Handlers.register(true, false, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler)) | ||||
|  | ||||
| 	c.Handlers.mu.Unlock() | ||||
| } | ||||
| @@ -389,7 +387,7 @@ func handleISUPPORT(c *Client, e Event) { | ||||
| 	c.state.Lock() | ||||
| 	// Skip the first parameter, as it's our nickname. | ||||
| 	for i := 1; i < len(e.Params); i++ { | ||||
| 		j := strings.IndexByte(e.Params[i], 0x3D) // = | ||||
| 		j := strings.IndexByte(e.Params[i], '=') | ||||
|  | ||||
| 		if j < 1 || (j+1) == len(e.Params[i]) { | ||||
| 			c.state.serverOptions[e.Params[i]] = "" | ||||
|   | ||||
							
								
								
									
										16
									
								
								vendor/github.com/lrstanley/girc/cap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/lrstanley/girc/cap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -136,7 +136,7 @@ func handleCAP(c *Client, e Event) { | ||||
| 			} | ||||
|  | ||||
| 			// Let them know which ones we'd like to enable. | ||||
| 			c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " ")}) | ||||
| 			c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " "), EmptyTrailing: true}) | ||||
|  | ||||
| 			// Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests | ||||
| 			// due to cap-notify, we can re-evaluate what we can support. | ||||
| @@ -375,11 +375,11 @@ func handleTags(c *Client, e Event) { | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	prefixTag      byte = 0x40 // @ | ||||
| 	prefixTagValue byte = 0x3D // = | ||||
| 	prefixUserTag  byte = 0x2B // + | ||||
| 	tagSeparator   byte = 0x3B // ; | ||||
| 	maxTagLength   int  = 511  // 510 + @ and " " (space), though space usually not included. | ||||
| 	prefixTag      byte = '@' | ||||
| 	prefixTagValue byte = '=' | ||||
| 	prefixUserTag  byte = '+' | ||||
| 	tagSeparator   byte = ';' | ||||
| 	maxTagLength   int  = 511 // 510 + @ and " " (space), though space usually not included. | ||||
| ) | ||||
|  | ||||
| // Tags represents the key-value pairs in IRCv3 message tags. The map contains | ||||
| @@ -618,7 +618,7 @@ func validTag(name string) bool { | ||||
|  | ||||
| 	for i := 0; i < len(name); i++ { | ||||
| 		// A-Z, a-z, 0-9, -/._ | ||||
| 		if (name[i] < 0x41 || name[i] > 0x5A) && (name[i] < 0x61 || name[i] > 0x7A) && (name[i] < 0x2D || name[i] > 0x39) && name[i] != 0x5F { | ||||
| 		if (name[i] < 'A' || name[i] > 'Z') && (name[i] < 'a' || name[i] > 'z') && (name[i] < '-' || name[i] > '9') && name[i] != '_' { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| @@ -631,7 +631,7 @@ func validTag(name string) bool { | ||||
| func validTagValue(value string) bool { | ||||
| 	for i := 0; i < len(value); i++ { | ||||
| 		// Don't allow any invisible chars within the tag, or semicolons. | ||||
| 		if value[i] < 0x21 || value[i] > 0x7E || value[i] == 0x3B { | ||||
| 		if value[i] < '!' || value[i] > '~' || value[i] == ';' { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										105
									
								
								vendor/github.com/lrstanley/girc/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										105
									
								
								vendor/github.com/lrstanley/girc/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -191,18 +191,6 @@ func (conf *Config) isValid() error { | ||||
| // connected. | ||||
| var ErrNotConnected = errors.New("client is not connected to server") | ||||
|  | ||||
| // ErrDisconnected is called when Config.Retries is less than 1, and we | ||||
| // non-intentionally disconnected from the server. | ||||
| var ErrDisconnected = errors.New("unexpectedly disconnected") | ||||
|  | ||||
| // ErrInvalidTarget should be returned if the target which you are | ||||
| // attempting to send an event to is invalid or doesn't match RFC spec. | ||||
| type ErrInvalidTarget struct { | ||||
| 	Target string | ||||
| } | ||||
|  | ||||
| func (e *ErrInvalidTarget) Error() string { return "invalid target: " + e.Target } | ||||
|  | ||||
| // New creates a new IRC client with the specified server, name and config. | ||||
| func New(config Config) *Client { | ||||
| 	c := &Client{ | ||||
| @@ -253,6 +241,37 @@ func (c *Client) String() string { | ||||
| 	) | ||||
| } | ||||
|  | ||||
| // TLSConnectionState returns the TLS connection state from tls.Conn{}, which | ||||
| // is useful to return needed TLS fingerprint info, certificates, verify cert | ||||
| // expiration dates, etc. Will only return an error if the underlying | ||||
| // connection wasn't established using TLS (see ErrConnNotTLS), or if the | ||||
| // client isn't connected. | ||||
| func (c *Client) TLSConnectionState() (*tls.ConnectionState, error) { | ||||
| 	c.mu.RLock() | ||||
| 	defer c.mu.RUnlock() | ||||
| 	if c.conn == nil { | ||||
| 		return nil, ErrNotConnected | ||||
| 	} | ||||
|  | ||||
| 	c.conn.mu.RLock() | ||||
| 	defer c.conn.mu.RUnlock() | ||||
|  | ||||
| 	if !c.conn.connected { | ||||
| 		return nil, ErrNotConnected | ||||
| 	} | ||||
|  | ||||
| 	if tlsConn, ok := c.conn.sock.(*tls.Conn); ok { | ||||
| 		cs := tlsConn.ConnectionState() | ||||
| 		return &cs, nil | ||||
| 	} | ||||
|  | ||||
| 	return nil, ErrConnNotTLS | ||||
| } | ||||
|  | ||||
| // ErrConnNotTLS is returned when Client.TLSConnectionState() is called, and | ||||
| // the connection to the server wasn't made with TLS. | ||||
| var ErrConnNotTLS = errors.New("underlying connection is not tls") | ||||
|  | ||||
| // Close closes the network connection to the server, and sends a STOPPED | ||||
| // event. This should cause Connect() to return with nil. This should be | ||||
| // safe to call multiple times. See Connect()'s documentation on how | ||||
| @@ -387,7 +406,7 @@ func (c *Client) ConnSince() (since *time.Duration, err error) { | ||||
| } | ||||
|  | ||||
| // IsConnected returns true if the client is connected to the server. | ||||
| func (c *Client) IsConnected() (connected bool) { | ||||
| func (c *Client) IsConnected() bool { | ||||
| 	c.mu.RLock() | ||||
| 	if c.conn == nil { | ||||
| 		c.mu.RUnlock() | ||||
| @@ -395,7 +414,7 @@ func (c *Client) IsConnected() (connected bool) { | ||||
| 	} | ||||
|  | ||||
| 	c.conn.mu.RLock() | ||||
| 	connected = c.conn.connected | ||||
| 	connected := c.conn.connected | ||||
| 	c.conn.mu.RUnlock() | ||||
| 	c.mu.RUnlock() | ||||
|  | ||||
| @@ -445,9 +464,9 @@ func (c *Client) GetHost() string { | ||||
| 	return c.state.host | ||||
| } | ||||
|  | ||||
| // Channels returns the active list of channels that the client is in. | ||||
| // ChannelList returns the active list of channel names that the client is in. | ||||
| // Panics if tracking is disabled. | ||||
| func (c *Client) Channels() []string { | ||||
| func (c *Client) ChannelList() []string { | ||||
| 	c.panicIfNotTracking() | ||||
|  | ||||
| 	c.state.RLock() | ||||
| @@ -463,9 +482,26 @@ func (c *Client) Channels() []string { | ||||
| 	return channels | ||||
| } | ||||
|  | ||||
| // Users returns the active list of users that the client is tracking across | ||||
| // all files. Panics if tracking is disabled. | ||||
| func (c *Client) Users() []string { | ||||
| // Channels returns the active channels that the client is in. Panics if | ||||
| // tracking is disabled. | ||||
| func (c *Client) Channels() []*Channel { | ||||
| 	c.panicIfNotTracking() | ||||
|  | ||||
| 	c.state.RLock() | ||||
| 	channels := make([]*Channel, len(c.state.channels)) | ||||
| 	var i int | ||||
| 	for channel := range c.state.channels { | ||||
| 		channels[i] = c.state.channels[channel].Copy() | ||||
| 		i++ | ||||
| 	} | ||||
| 	c.state.RUnlock() | ||||
|  | ||||
| 	return channels | ||||
| } | ||||
|  | ||||
| // UserList returns the active list of nicknames that the client is tracking | ||||
| // across all networks. Panics if tracking is disabled. | ||||
| func (c *Client) UserList() []string { | ||||
| 	c.panicIfNotTracking() | ||||
|  | ||||
| 	c.state.RLock() | ||||
| @@ -481,6 +517,23 @@ func (c *Client) Users() []string { | ||||
| 	return users | ||||
| } | ||||
|  | ||||
| // Users returns the active users that the client is tracking across all | ||||
| // networks. Panics if tracking is disabled. | ||||
| func (c *Client) Users() []*User { | ||||
| 	c.panicIfNotTracking() | ||||
|  | ||||
| 	c.state.RLock() | ||||
| 	users := make([]*User, len(c.state.users)) | ||||
| 	var i int | ||||
| 	for user := range c.state.users { | ||||
| 		users[i] = c.state.users[user].Copy() | ||||
| 		i++ | ||||
| 	} | ||||
| 	c.state.RUnlock() | ||||
|  | ||||
| 	return users | ||||
| } | ||||
|  | ||||
| // LookupChannel looks up a given channel in state. If the channel doesn't | ||||
| // exist, nil is returned. Panics if tracking is disabled. | ||||
| func (c *Client) LookupChannel(name string) *Channel { | ||||
| @@ -562,30 +615,30 @@ func (c *Client) NetworkName() (name string) { | ||||
| // supplied this information during connection. May be empty if the server | ||||
| // does not support RPL_MYINFO. Will panic if used when tracking has been | ||||
| // disabled. | ||||
| func (c *Client) ServerVersion() (version string) { | ||||
| func (c *Client) ServerVersion() string { | ||||
| 	c.panicIfNotTracking() | ||||
|  | ||||
| 	version, _ = c.GetServerOption("VERSION") | ||||
| 	version, _ := c.GetServerOption("VERSION") | ||||
|  | ||||
| 	return version | ||||
| } | ||||
|  | ||||
| // ServerMOTD returns the servers message of the day, if the server has sent | ||||
| // it upon connect. Will panic if used when tracking has been disabled. | ||||
| func (c *Client) ServerMOTD() (motd string) { | ||||
| func (c *Client) ServerMOTD() string { | ||||
| 	c.panicIfNotTracking() | ||||
|  | ||||
| 	c.state.RLock() | ||||
| 	motd = c.state.motd | ||||
| 	motd := c.state.motd | ||||
| 	c.state.RUnlock() | ||||
|  | ||||
| 	return motd | ||||
| } | ||||
|  | ||||
| // Lag is the latency between the server and the client. This is measured by | ||||
| // determining the difference in time between when we ping the server, and | ||||
| // Latency is the latency between the server and the client. This is measured | ||||
| // by determining the difference in time between when we ping the server, and | ||||
| // when we receive a pong. | ||||
| func (c *Client) Lag() time.Duration { | ||||
| func (c *Client) Latency() time.Duration { | ||||
| 	c.mu.RLock() | ||||
| 	c.conn.mu.RLock() | ||||
| 	delta := c.conn.lastPong.Sub(c.conn.lastPing) | ||||
|   | ||||
							
								
								
									
										10
									
								
								vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,8 +12,9 @@ import ( | ||||
|  | ||||
| // Input is a wrapper for events, based around private messages. | ||||
| type Input struct { | ||||
| 	Origin *girc.Event | ||||
| 	Args   []string | ||||
| 	Origin  *girc.Event | ||||
| 	Args    []string | ||||
| 	RawArgs string | ||||
| } | ||||
|  | ||||
| // Command is an IRC command, supporting aliases, help documentation and easy | ||||
| @@ -189,8 +190,9 @@ func (ch *CmdHandler) Execute(client *girc.Client, event girc.Event) { | ||||
| 	} | ||||
|  | ||||
| 	in := &Input{ | ||||
| 		Origin: &event, | ||||
| 		Args:   args, | ||||
| 		Origin:  &event, | ||||
| 		Args:    args, | ||||
| 		RawArgs: parsed[2], | ||||
| 	} | ||||
|  | ||||
| 	go cmd.Fn(client, in) | ||||
|   | ||||
							
								
								
									
										261
									
								
								vendor/github.com/lrstanley/girc/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										261
									
								
								vendor/github.com/lrstanley/girc/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -16,18 +16,13 @@ type Commands struct { | ||||
| } | ||||
|  | ||||
| // Nick changes the client nickname. | ||||
| func (cmd *Commands) Nick(name string) error { | ||||
| 	if !IsValidNick(name) { | ||||
| 		return &ErrInvalidTarget{Target: name} | ||||
| 	} | ||||
|  | ||||
| func (cmd *Commands) Nick(name string) { | ||||
| 	cmd.c.Send(&Event{Command: NICK, Params: []string{name}}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Join attempts to enter a list of IRC channels, at bulk if possible to | ||||
| // prevent sending extensive JOIN commands. | ||||
| func (cmd *Commands) Join(channels ...string) error { | ||||
| func (cmd *Commands) Join(channels ...string) { | ||||
| 	// We can join multiple channels at once, however we need to ensure that | ||||
| 	// we are not exceeding the line length. (see maxLength) | ||||
| 	max := maxLength - len(JOIN) - 1 | ||||
| @@ -35,10 +30,6 @@ func (cmd *Commands) Join(channels ...string) error { | ||||
| 	var buffer string | ||||
|  | ||||
| 	for i := 0; i < len(channels); i++ { | ||||
| 		if !IsValidChannel(channels[i]) { | ||||
| 			return &ErrInvalidTarget{Target: channels[i]} | ||||
| 		} | ||||
|  | ||||
| 		if len(buffer+","+channels[i]) > max { | ||||
| 			cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}}) | ||||
| 			buffer = "" | ||||
| @@ -53,91 +44,74 @@ func (cmd *Commands) Join(channels ...string) error { | ||||
|  | ||||
| 		if i == len(channels)-1 { | ||||
| 			cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}}) | ||||
| 			return nil | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // JoinKey attempts to enter an IRC channel with a password. | ||||
| func (cmd *Commands) JoinKey(channel, password string) error { | ||||
| 	if !IsValidChannel(channel) { | ||||
| 		return &ErrInvalidTarget{Target: channel} | ||||
| 	} | ||||
|  | ||||
| func (cmd *Commands) JoinKey(channel, password string) { | ||||
| 	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel, password}}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Part leaves an IRC channel. | ||||
| func (cmd *Commands) Part(channel, message string) error { | ||||
| 	if !IsValidChannel(channel) { | ||||
| 		return &ErrInvalidTarget{Target: channel} | ||||
| func (cmd *Commands) Part(channels ...string) { | ||||
| 	for i := 0; i < len(channels); i++ { | ||||
| 		cmd.c.Send(&Event{Command: PART, Params: []string{channels[i]}}) | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // PartMessage leaves an IRC channel with a specified leave message. | ||||
| func (cmd *Commands) PartMessage(channel, message string) error { | ||||
| 	if !IsValidChannel(channel) { | ||||
| 		return &ErrInvalidTarget{Target: channel} | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message}) | ||||
| 	return nil | ||||
| func (cmd *Commands) PartMessage(channel, message string) { | ||||
| 	cmd.c.Send(&Event{Command: PART, Params: []string{channel}, Trailing: message, EmptyTrailing: true}) | ||||
| } | ||||
|  | ||||
| // SendCTCP sends a CTCP request to target. Note that this method uses | ||||
| // PRIVMSG specifically. | ||||
| func (cmd *Commands) SendCTCP(target, ctcpType, message string) error { | ||||
| // PRIVMSG specifically. ctcpType is the CTCP command, e.g. "FINGER", "TIME", | ||||
| // "VERSION", etc. | ||||
| func (cmd *Commands) SendCTCP(target, ctcpType, message string) { | ||||
| 	out := encodeCTCPRaw(ctcpType, message) | ||||
| 	if out == "" { | ||||
| 		return errors.New("invalid CTCP") | ||||
| 		panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message)) | ||||
| 	} | ||||
|  | ||||
| 	return cmd.Message(target, out) | ||||
| 	cmd.Message(target, out) | ||||
| } | ||||
|  | ||||
| // SendCTCPf sends a CTCP request to target using a specific format. Note that | ||||
| // this method uses PRIVMSG specifically. | ||||
| func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) error { | ||||
| 	return cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...)) | ||||
| // this method uses PRIVMSG specifically. ctcpType is the CTCP command, e.g. | ||||
| // "FINGER", "TIME", "VERSION", etc. | ||||
| func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) { | ||||
| 	cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // SendCTCPReplyf sends a CTCP response to target using a specific format. | ||||
| // Note that this method uses NOTICE specifically. | ||||
| func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) error { | ||||
| 	return cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...)) | ||||
| // Note that this method uses NOTICE specifically. ctcpType is the CTCP | ||||
| // command, e.g. "FINGER", "TIME", "VERSION", etc. | ||||
| func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) { | ||||
| 	cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // SendCTCPReply sends a CTCP response to target. Note that this method uses | ||||
| // NOTICE specifically. | ||||
| func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) error { | ||||
| func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) { | ||||
| 	out := encodeCTCPRaw(ctcpType, message) | ||||
| 	if out == "" { | ||||
| 		return errors.New("invalid CTCP") | ||||
| 		panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message)) | ||||
| 	} | ||||
|  | ||||
| 	return cmd.Notice(target, out) | ||||
| 	cmd.Notice(target, out) | ||||
| } | ||||
|  | ||||
| // Message sends a PRIVMSG to target (either channel, service, or user). | ||||
| func (cmd *Commands) Message(target, message string) error { | ||||
| 	if !IsValidNick(target) && !IsValidChannel(target) { | ||||
| 		return &ErrInvalidTarget{Target: target} | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message}) | ||||
| 	return nil | ||||
| func (cmd *Commands) Message(target, message string) { | ||||
| 	cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message, EmptyTrailing: true}) | ||||
| } | ||||
|  | ||||
| // Messagef sends a formated PRIVMSG to target (either channel, service, or | ||||
| // user). | ||||
| func (cmd *Commands) Messagef(target, format string, a ...interface{}) error { | ||||
| 	return cmd.Message(target, fmt.Sprintf(format, a...)) | ||||
| func (cmd *Commands) Messagef(target, format string, a ...interface{}) { | ||||
| 	cmd.Message(target, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // ErrInvalidSource is returned when a method needs to know the origin of an | ||||
| @@ -146,94 +120,95 @@ func (cmd *Commands) Messagef(target, format string, a ...interface{}) error { | ||||
| var ErrInvalidSource = errors.New("event has nil or invalid source address") | ||||
|  | ||||
| // Reply sends a reply to channel or user, based on where the supplied event | ||||
| // originated from. See also ReplyTo(). | ||||
| func (cmd *Commands) Reply(event Event, message string) error { | ||||
| // originated from. See also ReplyTo(). Panics if the incoming event has no | ||||
| // source. | ||||
| func (cmd *Commands) Reply(event Event, message string) { | ||||
| 	if event.Source == nil { | ||||
| 		return ErrInvalidSource | ||||
| 		panic(ErrInvalidSource) | ||||
| 	} | ||||
|  | ||||
| 	if len(event.Params) > 0 && IsValidChannel(event.Params[0]) { | ||||
| 		return cmd.Message(event.Params[0], message) | ||||
| 		cmd.Message(event.Params[0], message) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	return cmd.Message(event.Source.Name, message) | ||||
| 	cmd.Message(event.Source.Name, message) | ||||
| } | ||||
|  | ||||
| // Replyf sends a reply to channel or user with a format string, based on | ||||
| // where the supplied event originated from. See also ReplyTof(). | ||||
| func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) error { | ||||
| 	return cmd.Reply(event, fmt.Sprintf(format, a...)) | ||||
| // where the supplied event originated from. See also ReplyTof(). Panics if | ||||
| // the incoming event has no source. | ||||
| func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) { | ||||
| 	cmd.Reply(event, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // ReplyTo sends a reply to a channel or user, based on where the supplied | ||||
| // event originated from. ReplyTo(), when originating from a channel will | ||||
| // default to replying with "<user>, <message>". See also Reply(). | ||||
| func (cmd *Commands) ReplyTo(event Event, message string) error { | ||||
| // default to replying with "<user>, <message>". See also Reply(). Panics if | ||||
| // the incoming event has no source. | ||||
| func (cmd *Commands) ReplyTo(event Event, message string) { | ||||
| 	if event.Source == nil { | ||||
| 		return ErrInvalidSource | ||||
| 		panic(ErrInvalidSource) | ||||
| 	} | ||||
|  | ||||
| 	if len(event.Params) > 0 && IsValidChannel(event.Params[0]) { | ||||
| 		return cmd.Message(event.Params[0], event.Source.Name+", "+message) | ||||
| 		cmd.Message(event.Params[0], event.Source.Name+", "+message) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	return cmd.Message(event.Source.Name, message) | ||||
| 	cmd.Message(event.Source.Name, message) | ||||
| } | ||||
|  | ||||
| // ReplyTof sends a reply to a channel or user with a format string, based | ||||
| // on where the supplied event originated from. ReplyTo(), when originating | ||||
| // from a channel will default to replying with "<user>, <message>". See | ||||
| // also Replyf(). | ||||
| func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) error { | ||||
| 	return cmd.ReplyTo(event, fmt.Sprintf(format, a...)) | ||||
| // also Replyf(). Panics if the incoming event has no source. | ||||
| func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) { | ||||
| 	cmd.ReplyTo(event, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // Action sends a PRIVMSG ACTION (/me) to target (either channel, service, | ||||
| // or user). | ||||
| func (cmd *Commands) Action(target, message string) error { | ||||
| 	if !IsValidNick(target) && !IsValidChannel(target) { | ||||
| 		return &ErrInvalidTarget{Target: target} | ||||
| 	} | ||||
|  | ||||
| func (cmd *Commands) Action(target, message string) { | ||||
| 	cmd.c.Send(&Event{ | ||||
| 		Command:  PRIVMSG, | ||||
| 		Params:   []string{target}, | ||||
| 		Trailing: fmt.Sprintf("\001ACTION %s\001", message), | ||||
| 	}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Actionf sends a formated PRIVMSG ACTION (/me) to target (either channel, | ||||
| // service, or user). | ||||
| func (cmd *Commands) Actionf(target, format string, a ...interface{}) error { | ||||
| 	return cmd.Action(target, fmt.Sprintf(format, a...)) | ||||
| func (cmd *Commands) Actionf(target, format string, a ...interface{}) { | ||||
| 	cmd.Action(target, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // Notice sends a NOTICE to target (either channel, service, or user). | ||||
| func (cmd *Commands) Notice(target, message string) error { | ||||
| 	if !IsValidNick(target) && !IsValidChannel(target) { | ||||
| 		return &ErrInvalidTarget{Target: target} | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message}) | ||||
| 	return nil | ||||
| func (cmd *Commands) Notice(target, message string) { | ||||
| 	cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message, EmptyTrailing: true}) | ||||
| } | ||||
|  | ||||
| // Noticef sends a formated NOTICE to target (either channel, service, or | ||||
| // user). | ||||
| func (cmd *Commands) Noticef(target, format string, a ...interface{}) error { | ||||
| 	return cmd.Notice(target, fmt.Sprintf(format, a...)) | ||||
| func (cmd *Commands) Noticef(target, format string, a ...interface{}) { | ||||
| 	cmd.Notice(target, fmt.Sprintf(format, a...)) | ||||
| } | ||||
|  | ||||
| // SendRaw sends a raw string back to the server, without carriage returns | ||||
| // or newlines. | ||||
| func (cmd *Commands) SendRaw(raw string) error { | ||||
| 	e := ParseEvent(raw) | ||||
| 	if e == nil { | ||||
| 		return errors.New("invalid event: " + raw) | ||||
| // SendRaw sends a raw string (or multiple) to the server, without carriage | ||||
| // returns or newlines. Returns an error if one of the raw strings cannot be | ||||
| // properly parsed. | ||||
| func (cmd *Commands) SendRaw(raw ...string) error { | ||||
| 	var event *Event | ||||
|  | ||||
| 	for i := 0; i < len(raw); i++ { | ||||
| 		event = ParseEvent(raw[i]) | ||||
| 		if event == nil { | ||||
| 			return errors.New("invalid event: " + raw[i]) | ||||
| 		} | ||||
|  | ||||
| 		cmd.c.Send(event) | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(e) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -246,31 +221,26 @@ func (cmd *Commands) SendRawf(format string, a ...interface{}) error { | ||||
| // Topic sets the topic of channel to message. Does not verify the length | ||||
| // of the topic. | ||||
| func (cmd *Commands) Topic(channel, message string) { | ||||
| 	cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message}) | ||||
| 	cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message, EmptyTrailing: true}) | ||||
| } | ||||
|  | ||||
| // Who sends a WHO query to the server, which will attempt WHOX by default. | ||||
| // See http://faerion.sourceforge.net/doc/irc/whox.var for more details. This | ||||
| // sends "%tcuhnr,2" per default. Do not use "1" as this will conflict with | ||||
| // girc's builtin tracking functionality. | ||||
| func (cmd *Commands) Who(target string) error { | ||||
| 	if !IsValidNick(target) && !IsValidChannel(target) && !IsValidUser(target) { | ||||
| 		return &ErrInvalidTarget{Target: target} | ||||
| func (cmd *Commands) Who(users ...string) { | ||||
| 	for i := 0; i < len(users); i++ { | ||||
| 		cmd.c.Send(&Event{Command: WHO, Params: []string{users[i], "%tcuhnr,2"}}) | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhnr,2"}}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Whois sends a WHOIS query to the server, targeted at a specific user. | ||||
| // as WHOIS is a bit slower, you may want to use WHO for brief user info. | ||||
| func (cmd *Commands) Whois(nick string) error { | ||||
| 	if !IsValidNick(nick) { | ||||
| 		return &ErrInvalidTarget{Target: nick} | ||||
| // Whois sends a WHOIS query to the server, targeted at a specific user (or | ||||
| // set of users). As WHOIS is a bit slower, you may want to use WHO for brief | ||||
| // user info. | ||||
| func (cmd *Commands) Whois(users ...string) { | ||||
| 	for i := 0; i < len(users); i++ { | ||||
| 		cmd.c.Send(&Event{Command: WHOIS, Params: []string{users[i]}}) | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: WHOIS, Params: []string{nick}}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Ping sends a PING query to the server, with a specific identifier that | ||||
| @@ -294,36 +264,40 @@ func (cmd *Commands) Oper(user, pass string) { | ||||
| // Kick sends a KICK query to the server, attempting to kick nick from | ||||
| // channel, with reason. If reason is blank, one will not be sent to the | ||||
| // server. | ||||
| func (cmd *Commands) Kick(channel, nick, reason string) error { | ||||
| 	if !IsValidChannel(channel) { | ||||
| 		return &ErrInvalidTarget{Target: channel} | ||||
| 	} | ||||
|  | ||||
| 	if !IsValidNick(nick) { | ||||
| 		return &ErrInvalidTarget{Target: nick} | ||||
| 	} | ||||
|  | ||||
| func (cmd *Commands) Kick(channel, user, reason string) { | ||||
| 	if reason != "" { | ||||
| 		cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}, Trailing: reason}) | ||||
| 		return nil | ||||
| 		cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}, Trailing: reason, EmptyTrailing: true}) | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}}) | ||||
| 	return nil | ||||
| 	cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}}) | ||||
| } | ||||
|  | ||||
| // Ban adds the +b mode on the given mask on a channel. | ||||
| func (cmd *Commands) Ban(channel, mask string) { | ||||
| 	cmd.Mode(channel, "+b", mask) | ||||
| } | ||||
|  | ||||
| // Unban removes the +b mode on the given mask on a channel. | ||||
| func (cmd *Commands) Unban(channel, mask string) { | ||||
| 	cmd.Mode(channel, "-b", mask) | ||||
| } | ||||
|  | ||||
| // Mode sends a mode change to the server which should be applied to target | ||||
| // (usually a channel or user), along with a set of modes (generally "+m", | ||||
| // "+mmmm", or "-m", where "m" is the mode you want to change). Params is only | ||||
| // needed if the mode change requires a parameter (ban or invite-only exclude.) | ||||
| func (cmd *Commands) Mode(target, modes string, params ...string) { | ||||
| 	out := []string{target, modes} | ||||
| 	out = append(out, params...) | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: MODE, Params: out}) | ||||
| } | ||||
|  | ||||
| // Invite sends a INVITE query to the server, to invite nick to channel. | ||||
| func (cmd *Commands) Invite(channel, nick string) error { | ||||
| 	if !IsValidChannel(channel) { | ||||
| 		return &ErrInvalidTarget{Target: channel} | ||||
| func (cmd *Commands) Invite(channel string, users ...string) { | ||||
| 	for i := 0; i < len(users); i++ { | ||||
| 		cmd.c.Send(&Event{Command: INVITE, Params: []string{users[i], channel}}) | ||||
| 	} | ||||
|  | ||||
| 	if !IsValidNick(nick) { | ||||
| 		return &ErrInvalidTarget{Target: nick} | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: INVITE, Params: []string{nick, channel}}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Away sends a AWAY query to the server, suggesting that the client is no | ||||
| @@ -348,10 +322,10 @@ func (cmd *Commands) Back() { | ||||
| // Supports multiple channels at once, in hopes it will reduce extensive | ||||
| // LIST queries to the server. Supply no channels to run a list against the | ||||
| // entire server (warning, that may mean LOTS of channels!) | ||||
| func (cmd *Commands) List(channels ...string) error { | ||||
| func (cmd *Commands) List(channels ...string) { | ||||
| 	if len(channels) == 0 { | ||||
| 		cmd.c.Send(&Event{Command: LIST}) | ||||
| 		return nil | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// We can LIST multiple channels at once, however we need to ensure that | ||||
| @@ -361,10 +335,6 @@ func (cmd *Commands) List(channels ...string) error { | ||||
| 	var buffer string | ||||
|  | ||||
| 	for i := 0; i < len(channels); i++ { | ||||
| 		if !IsValidChannel(channels[i]) { | ||||
| 			return &ErrInvalidTarget{Target: channels[i]} | ||||
| 		} | ||||
|  | ||||
| 		if len(buffer+","+channels[i]) > max { | ||||
| 			cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}}) | ||||
| 			buffer = "" | ||||
| @@ -379,20 +349,13 @@ func (cmd *Commands) List(channels ...string) error { | ||||
|  | ||||
| 		if i == len(channels)-1 { | ||||
| 			cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}}) | ||||
| 			return nil | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Whowas sends a WHOWAS query to the server. amount is the amount of results | ||||
| // you want back. | ||||
| func (cmd *Commands) Whowas(nick string, amount int) error { | ||||
| 	if !IsValidNick(nick) { | ||||
| 		return &ErrInvalidTarget{Target: nick} | ||||
| 	} | ||||
|  | ||||
| 	cmd.c.Send(&Event{Command: WHOWAS, Params: []string{nick, string(amount)}}) | ||||
| 	return nil | ||||
| func (cmd *Commands) Whowas(user string, amount int) { | ||||
| 	cmd.c.Send(&Event{Command: WHOWAS, Params: []string{user, string(amount)}}) | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								vendor/github.com/lrstanley/girc/ctcp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/lrstanley/girc/ctcp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -58,7 +58,7 @@ func decodeCTCP(e *Event) *CTCPEvent { | ||||
| 	if s < 0 { | ||||
| 		for i := 0; i < len(text); i++ { | ||||
| 			// Check for A-Z, 0-9. | ||||
| 			if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) { | ||||
| 			if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') { | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| @@ -74,7 +74,7 @@ func decodeCTCP(e *Event) *CTCPEvent { | ||||
| 	// Loop through checking the tag first. | ||||
| 	for i := 0; i < s; i++ { | ||||
| 		// Check for A-Z, 0-9. | ||||
| 		if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) { | ||||
| 		if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| @@ -168,7 +168,7 @@ func (c *CTCP) parseCMD(cmd string) string { | ||||
|  | ||||
| 	for i := 0; i < len(cmd); i++ { | ||||
| 		// Check for A-Z, 0-9. | ||||
| 		if (cmd[i] < 0x41 || cmd[i] > 0x5A) && (cmd[i] < 0x30 || cmd[i] > 0x39) { | ||||
| 		if (cmd[i] < 'A' || cmd[i] > 'Z') && (cmd[i] < '0' || cmd[i] > '9') { | ||||
| 			return "" | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										12
									
								
								vendor/github.com/lrstanley/girc/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/lrstanley/girc/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -11,8 +11,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	eventSpace byte = 0x20 // Separator. | ||||
| 	maxLength       = 510  // Maximum length is 510 (2 for line endings). | ||||
| 	eventSpace byte = ' ' // Separator. | ||||
| 	maxLength       = 510 // Maximum length is 510 (2 for line endings). | ||||
| ) | ||||
|  | ||||
| // cutCRFunc is used to trim CR characters from prefixes/messages. | ||||
| @@ -256,7 +256,7 @@ func (e *Event) Bytes() []byte { | ||||
|  | ||||
| 	// Strip newlines and carriage returns. | ||||
| 	for i := 0; i < len(out); i++ { | ||||
| 		if out[i] == 0x0A || out[i] == 0x0D { | ||||
| 		if out[i] == '\n' || out[i] == '\r' { | ||||
| 			out = append(out[:i], out[i+1:]...) | ||||
| 			i-- // Decrease the index so we can pick up where we left off. | ||||
| 		} | ||||
| @@ -432,9 +432,9 @@ func (e *Event) StripAction() string { | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	messagePrefix byte = 0x3A // ":" -- prefix or last argument | ||||
| 	prefixIdent   byte = 0x21 // "!" -- username | ||||
| 	prefixHost    byte = 0x40 // "@" -- hostname | ||||
| 	messagePrefix byte = ':' // Prefix or last argument. | ||||
| 	prefixIdent   byte = '!' // Username. | ||||
| 	prefixHost    byte = '@' // Hostname. | ||||
| ) | ||||
|  | ||||
| // Source represents the sender of an IRC event, see RFC1459 section 2.3.1. | ||||
|   | ||||
							
								
								
									
										46
									
								
								vendor/github.com/lrstanley/girc/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/lrstanley/girc/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,8 +12,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	fmtOpenChar  = 0x7B // { | ||||
| 	fmtCloseChar = 0x7D // } | ||||
| 	fmtOpenChar  = '{' | ||||
| 	fmtCloseChar = '}' | ||||
| ) | ||||
|  | ||||
| var fmtColors = map[string]int{ | ||||
| @@ -113,7 +113,7 @@ func Fmt(text string) string { | ||||
|  | ||||
| 		if last > -1 { | ||||
| 			// A-Z, a-z, and "," | ||||
| 			if text[i] != 0x2c && (text[i] <= 0x41 || text[i] >= 0x5a) && (text[i] <= 0x61 || text[i] >= 0x7a) { | ||||
| 			if text[i] != ',' && (text[i] <= 'A' || text[i] >= 'Z') && (text[i] <= 'a' || text[i] >= 'z') { | ||||
| 				last = -1 | ||||
| 				continue | ||||
| 			} | ||||
| @@ -127,10 +127,10 @@ func Fmt(text string) string { | ||||
| // See Fmt() for more information. | ||||
| func TrimFmt(text string) string { | ||||
| 	for color := range fmtColors { | ||||
| 		text = strings.Replace(text, "{"+color+"}", "", -1) | ||||
| 		text = strings.Replace(text, string(fmtOpenChar)+color+string(fmtCloseChar), "", -1) | ||||
| 	} | ||||
| 	for code := range fmtCodes { | ||||
| 		text = strings.Replace(text, "{"+code+"}", "", -1) | ||||
| 		text = strings.Replace(text, string(fmtOpenChar)+code+string(fmtCloseChar), "", -1) | ||||
| 	} | ||||
|  | ||||
| 	return text | ||||
| @@ -175,9 +175,10 @@ func IsValidChannel(channel string) bool { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	// #, +, !<channelid>, or & | ||||
| 	// Including "*" in the prefix list, as this is commonly used (e.g. ZNC) | ||||
| 	if bytes.IndexByte([]byte{0x21, 0x23, 0x26, 0x2A, 0x2B}, channel[0]) == -1 { | ||||
| 	// #, +, !<channelid>, ~, or & | ||||
| 	// Including "*" and "~" in the prefix list, as these are commonly used | ||||
| 	// (e.g. ZNC.) | ||||
| 	if bytes.IndexByte([]byte{'!', '#', '&', '*', '~', '+'}, channel[0]) == -1 { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| @@ -186,14 +187,14 @@ func IsValidChannel(channel string) bool { | ||||
| 	//   1 (prefix) + 5 (id) + 1 (+, channel name) | ||||
| 	// On some networks, this may be extended with ISUPPORT capabilities, | ||||
| 	// however this is extremely uncommon. | ||||
| 	if channel[0] == 0x21 { | ||||
| 	if channel[0] == '!' { | ||||
| 		if len(channel) < 7 { | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		// check for valid ID | ||||
| 		for i := 1; i < 6; i++ { | ||||
| 			if (channel[i] < 0x30 || channel[i] > 0x39) && (channel[i] < 0x41 || channel[i] > 0x5A) { | ||||
| 			if (channel[i] < '0' || channel[i] > '9') && (channel[i] < 'A' || channel[i] > 'Z') { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| @@ -222,17 +223,15 @@ func IsValidNick(nick string) bool { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	nick = ToRFC1459(nick) | ||||
|  | ||||
| 	// Check the first index. Some characters aren't allowed for the first | ||||
| 	// index of an IRC nickname. | ||||
| 	if nick[0] < 0x41 || nick[0] > 0x7D { | ||||
| 		// a-z, A-Z, and _\[]{}^| | ||||
| 	if (nick[0] < 'A' || nick[0] > '}') && nick[0] != '?' { | ||||
| 		// a-z, A-Z, '_\[]{}^|', and '?' in the case of znc. | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	for i := 1; i < len(nick); i++ { | ||||
| 		if (nick[i] < 0x41 || nick[i] > 0x7D) && (nick[i] < 0x30 || nick[i] > 0x39) && nick[i] != 0x2D { | ||||
| 		if (nick[i] < 'A' || nick[i] > '}') && (nick[i] < '0' || nick[i] > '9') && nick[i] != '-' { | ||||
| 			// a-z, A-Z, 0-9, -, and _\[]{}^| | ||||
| 			return false | ||||
| 		} | ||||
| @@ -261,10 +260,8 @@ func IsValidUser(name string) bool { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	name = ToRFC1459(name) | ||||
|  | ||||
| 	// "~" is prepended (commonly) if there was no ident server response. | ||||
| 	if name[0] == 0x7E { | ||||
| 	if name[0] == '~' { | ||||
| 		// Means name only contained "~". | ||||
| 		if len(name) < 2 { | ||||
| 			return false | ||||
| @@ -274,12 +271,12 @@ func IsValidUser(name string) bool { | ||||
| 	} | ||||
|  | ||||
| 	// Check to see if the first index is alphanumeric. | ||||
| 	if (name[0] < 0x41 || name[0] > 0x4A) && (name[0] < 0x61 || name[0] > 0x7A) && (name[0] < 0x30 || name[0] > 0x39) { | ||||
| 	if (name[0] < 'A' || name[0] > 'J') && (name[0] < 'a' || name[0] > 'z') && (name[0] < '0' || name[0] > '9') { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	for i := 1; i < len(name); i++ { | ||||
| 		if (name[i] < 0x41 || name[i] > 0x7D) && (name[i] < 0x30 || name[i] > 0x39) && name[i] != 0x2D && name[i] != 0x2E { | ||||
| 		if (name[i] < 'A' || name[i] > '}') && (name[i] < '0' || name[i] > '9') && name[i] != '-' && name[i] != '.' { | ||||
| 			// a-z, A-Z, 0-9, -, and _\[]{}^| | ||||
| 			return false | ||||
| 		} | ||||
| @@ -290,8 +287,13 @@ func IsValidUser(name string) bool { | ||||
|  | ||||
| // ToRFC1459 converts a string to the stripped down conversion within RFC | ||||
| // 1459. This will do things like replace an "A" with an "a", "[]" with "{}", | ||||
| // and so forth. Useful to compare two nicknames or channels. | ||||
| func ToRFC1459(input string) (out string) { | ||||
| // and so forth. Useful to compare two nicknames or channels. Note that this | ||||
| // should not be used to normalize nicknames or similar, as this may convert | ||||
| // valid input characters to non-rfc-valid characters. As such, it's main use | ||||
| // is for comparing two nicks. | ||||
| func ToRFC1459(input string) string { | ||||
| 	var out string | ||||
|  | ||||
| 	for i := 0; i < len(input); i++ { | ||||
| 		if input[i] >= 65 && input[i] <= 94 { | ||||
| 			out += string(rune(input[i]) + 32) | ||||
|   | ||||
							
								
								
									
										131
									
								
								vendor/github.com/lrstanley/girc/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										131
									
								
								vendor/github.com/lrstanley/girc/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -29,11 +29,12 @@ func (c *Client) RunHandlers(event *Event) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Regular wildcard handlers. | ||||
| 	c.Handlers.exec(ALL_EVENTS, c, event.Copy()) | ||||
| 	// Background handlers first. | ||||
| 	c.Handlers.exec(ALL_EVENTS, true, c, event.Copy()) | ||||
| 	c.Handlers.exec(event.Command, true, c, event.Copy()) | ||||
|  | ||||
| 	// Then regular handlers. | ||||
| 	c.Handlers.exec(event.Command, c, event.Copy()) | ||||
| 	c.Handlers.exec(ALL_EVENTS, false, c, event.Copy()) | ||||
| 	c.Handlers.exec(event.Command, false, c, event.Copy()) | ||||
|  | ||||
| 	// Check if it's a CTCP. | ||||
| 	if ctcp := decodeCTCP(event.Copy()); ctcp != nil { | ||||
| @@ -144,7 +145,7 @@ func (c *Caller) cuid(cmd string, n int) (cuid, uid string) { | ||||
| // cuidToID allows easy mapping between a generated cuid and the caller | ||||
| // external/internal handler maps. | ||||
| func (c *Caller) cuidToID(input string) (cmd, uid string) { | ||||
| 	i := strings.IndexByte(input, 0x3A) | ||||
| 	i := strings.IndexByte(input, ':') | ||||
| 	if i < 0 { | ||||
| 		return "", "" | ||||
| 	} | ||||
| @@ -160,9 +161,9 @@ type execStack struct { | ||||
| // exec executes all handlers pertaining to specified event. Internal first, | ||||
| // then external. | ||||
| // | ||||
| // Please note that there is no specific order/priority for which the | ||||
| // handler types themselves or the handlers are executed. | ||||
| func (c *Caller) exec(command string, client *Client, event *Event) { | ||||
| // Please note that there is no specific order/priority for which the handlers | ||||
| // are executed. | ||||
| func (c *Caller) exec(command string, bg bool, client *Client, event *Event) { | ||||
| 	// Build a stack of handlers which can be executed concurrently. | ||||
| 	var stack []execStack | ||||
|  | ||||
| @@ -170,13 +171,21 @@ func (c *Caller) exec(command string, client *Client, event *Event) { | ||||
| 	// Get internal handlers first. | ||||
| 	if _, ok := c.internal[command]; ok { | ||||
| 		for cuid := range c.internal[command] { | ||||
| 			if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			stack = append(stack, execStack{c.internal[command][cuid], cuid}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Aaand then external handlers. | ||||
| 	// Then external handlers. | ||||
| 	if _, ok := c.external[command]; ok { | ||||
| 		for cuid := range c.external[command] { | ||||
| 			if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			stack = append(stack, execStack{c.external[command][cuid], cuid}) | ||||
| 		} | ||||
| 	} | ||||
| @@ -189,18 +198,29 @@ func (c *Caller) exec(command string, client *Client, event *Event) { | ||||
| 	wg.Add(len(stack)) | ||||
| 	for i := 0; i < len(stack); i++ { | ||||
| 		go func(index int) { | ||||
| 			c.debug.Printf("executing handler %s for event %s (%d of %d)", stack[index].cuid, command, index+1, len(stack)) | ||||
| 			defer wg.Done() | ||||
| 			c.debug.Printf("[%d/%d] exec %s => %s", index+1, len(stack), stack[index].cuid, command) | ||||
| 			start := time.Now() | ||||
|  | ||||
| 			// If they want to catch any panics, add to defer stack. | ||||
| 			if bg { | ||||
| 				go func() { | ||||
| 					if client.Config.RecoverFunc != nil { | ||||
| 						defer recoverHandlerPanic(client, event, stack[index].cuid, 3) | ||||
| 					} | ||||
|  | ||||
| 					stack[index].Execute(client, *event) | ||||
| 					c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start)) | ||||
| 				}() | ||||
|  | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			if client.Config.RecoverFunc != nil { | ||||
| 				defer recoverHandlerPanic(client, event, stack[index].cuid, 3) | ||||
| 			} | ||||
|  | ||||
| 			stack[index].Execute(client, *event) | ||||
|  | ||||
| 			c.debug.Printf("execution of %s took %s (%d of %d)", stack[index].cuid, time.Since(start), index+1, len(stack)) | ||||
| 			wg.Done() | ||||
| 			c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start)) | ||||
| 		}(i) | ||||
| 	} | ||||
|  | ||||
| @@ -281,9 +301,9 @@ func (c *Caller) remove(cuid string) (success bool) { | ||||
|  | ||||
| // sregister is much like Caller.register(), except that it safely locks | ||||
| // the Caller mutex. | ||||
| func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid string) { | ||||
| func (c *Caller) sregister(internal, bg bool, cmd string, handler Handler) (cuid string) { | ||||
| 	c.mu.Lock() | ||||
| 	cuid = c.register(internal, cmd, handler) | ||||
| 	cuid = c.register(internal, bg, cmd, handler) | ||||
| 	c.mu.Unlock() | ||||
|  | ||||
| 	return cuid | ||||
| @@ -291,30 +311,34 @@ func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid str | ||||
|  | ||||
| // register will register a handler in the internal tracker. Unsafe (you | ||||
| // must lock c.mu yourself!) | ||||
| func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid string) { | ||||
| func (c *Caller) register(internal, bg bool, cmd string, handler Handler) (cuid string) { | ||||
| 	var uid string | ||||
|  | ||||
| 	cmd = strings.ToUpper(cmd) | ||||
|  | ||||
| 	cuid, uid = c.cuid(cmd, 20) | ||||
| 	if bg { | ||||
| 		uid += ":bg" | ||||
| 		cuid += ":bg" | ||||
| 	} | ||||
|  | ||||
| 	if internal { | ||||
| 		if _, ok := c.internal[cmd]; !ok { | ||||
| 			c.internal[cmd] = map[string]Handler{} | ||||
| 		} | ||||
|  | ||||
| 		cuid, uid = c.cuid(cmd, 20) | ||||
| 		c.internal[cmd][uid] = handler | ||||
| 	} else { | ||||
| 		if _, ok := c.external[cmd]; !ok { | ||||
| 			c.external[cmd] = map[string]Handler{} | ||||
| 		} | ||||
|  | ||||
| 		cuid, uid = c.cuid(cmd, 20) | ||||
| 		c.external[cmd][uid] = handler | ||||
| 	} | ||||
|  | ||||
| 	_, file, line, _ := runtime.Caller(3) | ||||
|  | ||||
| 	c.debug.Printf("registering handler for %q with cuid %q (internal: %t) from: %s:%d", cmd, cuid, internal, file, line) | ||||
| 	c.debug.Printf("reg %q => %s [int:%t bg:%t] %s:%d", uid, cmd, internal, bg, file, line) | ||||
|  | ||||
| 	return cuid | ||||
| } | ||||
| @@ -323,31 +347,20 @@ func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid stri | ||||
| // given event. cuid is the handler uid which can be used to remove the | ||||
| // handler with Caller.Remove(). | ||||
| func (c *Caller) AddHandler(cmd string, handler Handler) (cuid string) { | ||||
| 	return c.sregister(false, cmd, handler) | ||||
| 	return c.sregister(false, false, cmd, handler) | ||||
| } | ||||
|  | ||||
| // Add registers the handler function for the given event. cuid is the | ||||
| // handler uid which can be used to remove the handler with Caller.Remove(). | ||||
| func (c *Caller) Add(cmd string, handler func(client *Client, event Event)) (cuid string) { | ||||
| 	return c.sregister(false, cmd, HandlerFunc(handler)) | ||||
| 	return c.sregister(false, false, cmd, HandlerFunc(handler)) | ||||
| } | ||||
|  | ||||
| // AddBg registers the handler function for the given event and executes it | ||||
| // in a go-routine. cuid is the handler uid which can be used to remove the | ||||
| // handler with Caller.Remove(). | ||||
| func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (cuid string) { | ||||
| 	return c.sregister(false, cmd, HandlerFunc(func(client *Client, event Event) { | ||||
| 		// Setting up background-based handlers this way allows us to get | ||||
| 		// clean call stacks for use with panic recovery. | ||||
| 		go func() { | ||||
| 			// If they want to catch any panics, add to defer stack. | ||||
| 			if client.Config.RecoverFunc != nil { | ||||
| 				defer recoverHandlerPanic(client, &event, "goroutine", 3) | ||||
| 			} | ||||
|  | ||||
| 			handler(client, event) | ||||
| 		}() | ||||
| 	})) | ||||
| 	return c.sregister(false, true, cmd, HandlerFunc(handler)) | ||||
| } | ||||
|  | ||||
| // AddTmp adds a "temporary" handler, which is good for one-time or few-time | ||||
| @@ -361,47 +374,37 @@ func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (c | ||||
| // | ||||
| // Additionally, AddTmp has a useful option, deadline. When set to greater | ||||
| // than 0, deadline will be the amount of time that passes before the handler | ||||
| // is removed from the stack, regardless if the handler returns true or not. | ||||
| // is removed from the stack, regardless of if the handler returns true or not. | ||||
| // This is useful in that it ensures that the handler is cleaned up if the | ||||
| // server does not respond appropriately, or takes too long to respond. | ||||
| // | ||||
| // Note that handlers supplied with AddTmp are executed in a goroutine to | ||||
| // ensure that they are not blocking other handlers. Additionally, use cuid | ||||
| // with Caller.Remove() to prematurely remove the handler from the stack, | ||||
| // bypassing the timeout or waiting for the handler to return that it wants | ||||
| // to be removed from the stack. | ||||
| // ensure that they are not blocking other handlers. However, if you are | ||||
| // creating a temporary handler from another handler, it should be a | ||||
| // background handler. | ||||
| // | ||||
| // Use cuid with Caller.Remove() to prematurely remove the handler from the | ||||
| // stack, bypassing the timeout or waiting for the handler to return that it | ||||
| // wants to be removed from the stack. | ||||
| func (c *Caller) AddTmp(cmd string, deadline time.Duration, handler func(client *Client, event Event) bool) (cuid string, done chan struct{}) { | ||||
| 	var uid string | ||||
| 	cuid, uid = c.cuid(cmd, 20) | ||||
|  | ||||
| 	done = make(chan struct{}) | ||||
|  | ||||
| 	c.mu.Lock() | ||||
| 	if _, ok := c.external[cmd]; !ok { | ||||
| 		c.external[cmd] = map[string]Handler{} | ||||
| 	} | ||||
| 	c.external[cmd][uid] = HandlerFunc(func(client *Client, event Event) { | ||||
| 		// Setting up background-based handlers this way allows us to get | ||||
| 		// clean call stacks for use with panic recovery. | ||||
| 		go func() { | ||||
| 			// If they want to catch any panics, add to defer stack. | ||||
| 			if client.Config.RecoverFunc != nil { | ||||
| 				defer recoverHandlerPanic(client, &event, "tmp-goroutine", 3) | ||||
| 	cuid = c.sregister(false, true, cmd, HandlerFunc(func(client *Client, event Event) { | ||||
| 		remove := handler(client, event) | ||||
| 		if remove { | ||||
| 			if ok := c.Remove(cuid); ok { | ||||
| 				close(done) | ||||
| 			} | ||||
|  | ||||
| 			remove := handler(client, event) | ||||
| 			if remove { | ||||
| 				if ok := c.Remove(cuid); ok { | ||||
| 					close(done) | ||||
| 				} | ||||
| 			} | ||||
| 		}() | ||||
| 	}) | ||||
| 	c.mu.Unlock() | ||||
| 		} | ||||
| 	})) | ||||
|  | ||||
| 	if deadline > 0 { | ||||
| 		go func() { | ||||
| 			<-time.After(deadline) | ||||
| 			select { | ||||
| 			case <-time.After(deadline): | ||||
| 			case <-done: | ||||
| 			} | ||||
|  | ||||
| 			if ok := c.Remove(cuid); ok { | ||||
| 				close(done) | ||||
| 			} | ||||
|   | ||||
							
								
								
									
										10
									
								
								vendor/github.com/lrstanley/girc/modes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/lrstanley/girc/modes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -206,11 +206,11 @@ func (c *CModes) Parse(flags string, args []string) (out []CMode) { | ||||
| 	var argCount int | ||||
|  | ||||
| 	for i := 0; i < len(flags); i++ { | ||||
| 		if flags[i] == 0x2B { | ||||
| 		if flags[i] == '+' { | ||||
| 			add = true | ||||
| 			continue | ||||
| 		} | ||||
| 		if flags[i] == 0x2D { | ||||
| 		if flags[i] == '-' { | ||||
| 			add = false | ||||
| 			continue | ||||
| 		} | ||||
| @@ -265,7 +265,7 @@ func IsValidChannelMode(raw string) bool { | ||||
|  | ||||
| 	for i := 0; i < len(raw); i++ { | ||||
| 		// Allowed are: ",", A-Z and a-z. | ||||
| 		if raw[i] != 0x2C && (raw[i] < 0x41 || raw[i] > 0x5A) && (raw[i] < 0x61 || raw[i] > 0x7A) { | ||||
| 		if raw[i] != ',' && (raw[i] < 'A' || raw[i] > 'Z') && (raw[i] < 'a' || raw[i] > 'z') { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| @@ -279,7 +279,7 @@ func isValidUserPrefix(raw string) bool { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if raw[0] != 0x28 { // (. | ||||
| 	if raw[0] != '(' { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| @@ -288,7 +288,7 @@ func isValidUserPrefix(raw string) bool { | ||||
|  | ||||
| 	// Skip the first one as we know it's (. | ||||
| 	for i := 1; i < len(raw); i++ { | ||||
| 		if raw[i] == 0x29 { // ). | ||||
| 		if raw[i] == ')' { | ||||
| 			passedKeys = true | ||||
| 			continue | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										90
									
								
								vendor/github.com/matrix-org/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										90
									
								
								vendor/github.com/matrix-org/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -79,7 +79,7 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string { | ||||
| 	return hsURL.String() | ||||
| } | ||||
|  | ||||
| // BuildURLWithQuery builds a URL with query paramters in addition to the Client's homeserver/prefix/access_token set already. | ||||
| // BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already. | ||||
| func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string { | ||||
| 	u, _ := url.Parse(cli.BuildURL(urlPath...)) | ||||
| 	q := u.Query() | ||||
| @@ -387,6 +387,20 @@ func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{ | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname | ||||
| func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) { | ||||
| 	urlPath := cli.BuildURL("profile", mxid, "displayname") | ||||
| 	_, err = cli.MakeRequest("GET", urlPath, nil, &resp) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname | ||||
| func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) { | ||||
| 	urlPath := cli.BuildURL("profile", cli.UserID, "displayname") | ||||
| 	_, err = cli.MakeRequest("GET", urlPath, nil, &resp) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname | ||||
| func (cli *Client) SetDisplayName(displayName string) (err error) { | ||||
| 	urlPath := cli.BuildURL("profile", cli.UserID, "displayname") | ||||
| @@ -450,6 +464,35 @@ func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) { | ||||
| 		TextMessage{"m.text", text}) | ||||
| } | ||||
|  | ||||
| // SendImage sends an m.room.message event into the given room with a msgtype of m.image | ||||
| // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image | ||||
| func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) { | ||||
| 	return cli.SendMessageEvent(roomID, "m.room.message", | ||||
| 		ImageMessage{ | ||||
| 			MsgType: "m.image", | ||||
| 			Body:    body, | ||||
| 			URL:     url, | ||||
| 		}) | ||||
| } | ||||
|  | ||||
| // SendVideo sends an m.room.message event into the given room with a msgtype of m.video | ||||
| // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video | ||||
| func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) { | ||||
| 	return cli.SendMessageEvent(roomID, "m.room.message", | ||||
| 		VideoMessage{ | ||||
| 			MsgType: "m.video", | ||||
| 			Body:    body, | ||||
| 			URL:     url, | ||||
| 		}) | ||||
| } | ||||
|  | ||||
| // SendNotice sends an m.room.message event into the given room with a msgtype of m.notice | ||||
| // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice | ||||
| func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { | ||||
| 	return cli.SendMessageEvent(roomID, "m.room.message", | ||||
| 		TextMessage{"m.notice", text}) | ||||
| } | ||||
|  | ||||
| // RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid | ||||
| func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) { | ||||
| 	txnID := txnID() | ||||
| @@ -518,6 +561,14 @@ func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanU | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid | ||||
| func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) { | ||||
| 	req := ReqTyping{Typing: typing, Timeout: timeout} | ||||
| 	u := cli.BuildURL("rooms", roomID, "typing", cli.UserID) | ||||
| 	_, err = cli.MakeRequest("PUT", u, req, &resp) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with | ||||
| // the HTTP response body, or return an error. | ||||
| // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey | ||||
| @@ -556,8 +607,15 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if res.StatusCode != 200 { | ||||
| 		contents, err := ioutil.ReadAll(res.Body) | ||||
| 		if err != nil { | ||||
| 			return nil, HTTPError{ | ||||
| 				Message: "Upload request failed - Failed to read response body: " + err.Error(), | ||||
| 				Code:    res.StatusCode, | ||||
| 			} | ||||
| 		} | ||||
| 		return nil, HTTPError{ | ||||
| 			Message: "Upload request failed", | ||||
| 			Message: "Upload request failed: " + string(contents), | ||||
| 			Code:    res.StatusCode, | ||||
| 		} | ||||
| 	} | ||||
| @@ -588,6 +646,34 @@ func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Messages returns a list of message and state events for a room. It uses | ||||
| // pagination query parameters to paginate history in the room. | ||||
| // See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages | ||||
| func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) { | ||||
| 	query := map[string]string{ | ||||
| 		"from": from, | ||||
| 		"dir":  string(dir), | ||||
| 	} | ||||
| 	if to != "" { | ||||
| 		query["to"] = to | ||||
| 	} | ||||
| 	if limit != 0 { | ||||
| 		query["limit"] = strconv.Itoa(limit) | ||||
| 	} | ||||
|  | ||||
| 	urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query) | ||||
| 	_, err = cli.MakeRequest("GET", urlPath, nil, &resp) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // TurnServer returns turn server details and credentials for the client to use when initiating calls. | ||||
| // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver | ||||
| func (cli *Client) TurnServer() (resp *RespTurnServer, err error) { | ||||
| 	urlPath := cli.BuildURL("voip", "turnServer") | ||||
| 	_, err = cli.MakeRequest("GET", urlPath, nil, &resp) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func txnID() string { | ||||
| 	return "go" + strconv.FormatInt(time.Now().UnixNano(), 10) | ||||
| } | ||||
|   | ||||
							
								
								
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,13 +7,13 @@ import ( | ||||
|  | ||||
| // Event represents a single Matrix event. | ||||
| type Event struct { | ||||
| 	StateKey  string                 `json:"state_key"`        // The state key for the event. Only present on State Events. | ||||
| 	Sender    string                 `json:"sender"`           // The user ID of the sender of the event | ||||
| 	Type      string                 `json:"type"`             // The event type | ||||
| 	Timestamp int                    `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server | ||||
| 	ID        string                 `json:"event_id"`         // The unique ID of this event | ||||
| 	RoomID    string                 `json:"room_id"`          // The room the event was sent to. May be nil (e.g. for presence) | ||||
| 	Content   map[string]interface{} `json:"content"`          // The JSON content of the event. | ||||
| 	StateKey  *string                `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. | ||||
| 	Sender    string                 `json:"sender"`              // The user ID of the sender of the event | ||||
| 	Type      string                 `json:"type"`                // The event type | ||||
| 	Timestamp int64                  `json:"origin_server_ts"`    // The unix timestamp when this message was sent by the origin server | ||||
| 	ID        string                 `json:"event_id"`            // The unique ID of this event | ||||
| 	RoomID    string                 `json:"room_id"`             // The room the event was sent to. May be nil (e.g. for presence) | ||||
| 	Content   map[string]interface{} `json:"content"`             // The JSON content of the event. | ||||
| } | ||||
|  | ||||
| // Body returns the value of the "body" key in the event content if it is | ||||
| @@ -44,12 +44,31 @@ type TextMessage struct { | ||||
| 	Body    string `json:"body"` | ||||
| } | ||||
|  | ||||
| // ImageInfo contains info about an image | ||||
| // ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image | ||||
| type ImageInfo struct { | ||||
| 	Height   uint   `json:"h"` | ||||
| 	Width    uint   `json:"w"` | ||||
| 	Mimetype string `json:"mimetype"` | ||||
| 	Size     uint   `json:"size"` | ||||
| 	Height   uint   `json:"h,omitempty"` | ||||
| 	Width    uint   `json:"w,omitempty"` | ||||
| 	Mimetype string `json:"mimetype,omitempty"` | ||||
| 	Size     uint   `json:"size,omitempty"` | ||||
| } | ||||
|  | ||||
| // VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video | ||||
| type VideoInfo struct { | ||||
| 	Mimetype      string    `json:"mimetype,omitempty"` | ||||
| 	ThumbnailInfo ImageInfo `json:"thumbnail_info"` | ||||
| 	ThumbnailURL  string    `json:"thumbnail_url,omitempty"` | ||||
| 	Height        uint      `json:"h,omitempty"` | ||||
| 	Width         uint      `json:"w,omitempty"` | ||||
| 	Duration      uint      `json:"duration,omitempty"` | ||||
| 	Size          uint      `json:"size,omitempty"` | ||||
| } | ||||
|  | ||||
| // VideoMessage is an m.video  - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video | ||||
| type VideoMessage struct { | ||||
| 	MsgType string    `json:"msgtype"` | ||||
| 	Body    string    `json:"body"` | ||||
| 	URL     string    `json:"url"` | ||||
| 	Info    VideoInfo `json:"info"` | ||||
| } | ||||
|  | ||||
| // ImageMessage is an m.image event | ||||
|   | ||||
							
								
								
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| // Copyright 2017 Jan Christian Grünhage | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package gomatrix | ||||
|  | ||||
| //Filter is used by clients to specify how the server should filter responses to e.g. sync requests | ||||
| //Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering | ||||
| type Filter struct { | ||||
| 	AccountData FilterPart `json:"account_data,omitempty"` | ||||
| 	EventFields []string   `json:"event_fields,omitempty"` | ||||
| 	EventFormat string     `json:"event_format,omitempty"` | ||||
| 	Presence    FilterPart `json:"presence,omitempty"` | ||||
| 	Room        struct { | ||||
| 		AccountData  FilterPart `json:"account_data,omitempty"` | ||||
| 		Ephemeral    FilterPart `json:"ephemeral,omitempty"` | ||||
| 		IncludeLeave bool       `json:"include_leave,omitempty"` | ||||
| 		NotRooms     []string   `json:"not_rooms,omitempty"` | ||||
| 		Rooms        []string   `json:"rooms,omitempty"` | ||||
| 		State        FilterPart `json:"state,omitempty"` | ||||
| 		Timeline     FilterPart `json:"timeline,omitempty"` | ||||
| 	} `json:"room,omitempty"` | ||||
| } | ||||
|  | ||||
| type FilterPart struct { | ||||
| 	NotRooms   []string `json:"not_rooms,omitempty"` | ||||
| 	Rooms      []string `json:"rooms,omitempty"` | ||||
| 	Limit      *int     `json:"limit,omitempty"` | ||||
| 	NotSenders []string `json:"not_senders,omitempty"` | ||||
| 	NotTypes   []string `json:"not_types,omitempty"` | ||||
| 	Senders    []string `json:"senders,omitempty"` | ||||
| 	Types      []string `json:"types,omitempty"` | ||||
| } | ||||
							
								
								
									
										6
									
								
								vendor/github.com/matrix-org/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/matrix-org/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -70,3 +70,9 @@ type ReqBanUser struct { | ||||
| type ReqUnbanUser struct { | ||||
| 	UserID string `json:"user_id"` | ||||
| } | ||||
|  | ||||
| // ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid | ||||
| type ReqTyping struct { | ||||
| 	Typing  bool  `json:"typing"` | ||||
| 	Timeout int64 `json:"timeout"` | ||||
| } | ||||
|   | ||||
							
								
								
									
										32
									
								
								vendor/github.com/matrix-org/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/matrix-org/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -45,6 +45,9 @@ type RespBanUser struct{} | ||||
| // RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban | ||||
| type RespUnbanUser struct{} | ||||
|  | ||||
| // RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid | ||||
| type RespTyping struct{} | ||||
|  | ||||
| // RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680 | ||||
| type RespJoinedRooms struct { | ||||
| 	JoinedRooms []string `json:"joined_rooms"` | ||||
| @@ -58,6 +61,13 @@ type RespJoinedMembers struct { | ||||
| 	} `json:"joined"` | ||||
| } | ||||
|  | ||||
| // RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages | ||||
| type RespMessages struct { | ||||
| 	Start string  `json:"start"` | ||||
| 	Chunk []Event `json:"chunk"` | ||||
| 	End   string  `json:"end"` | ||||
| } | ||||
|  | ||||
| // RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid | ||||
| type RespSendEvent struct { | ||||
| 	EventID string `json:"event_id"` | ||||
| @@ -90,6 +100,11 @@ func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname | ||||
| type RespUserDisplayName struct { | ||||
| 	DisplayName string `json:"displayname"` | ||||
| } | ||||
|  | ||||
| // RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register | ||||
| type RespRegister struct { | ||||
| 	AccessToken  string `json:"access_token"` | ||||
| @@ -125,6 +140,16 @@ type RespSync struct { | ||||
| 		Events []Event `json:"events"` | ||||
| 	} `json:"presence"` | ||||
| 	Rooms struct { | ||||
| 		Leave map[string]struct { | ||||
| 			State struct { | ||||
| 				Events []Event `json:"events"` | ||||
| 			} `json:"state"` | ||||
| 			Timeline struct { | ||||
| 				Events    []Event `json:"events"` | ||||
| 				Limited   bool    `json:"limited"` | ||||
| 				PrevBatch string  `json:"prev_batch"` | ||||
| 			} `json:"timeline"` | ||||
| 		} `json:"leave"` | ||||
| 		Join map[string]struct { | ||||
| 			State struct { | ||||
| 				Events []Event `json:"events"` | ||||
| @@ -142,3 +167,10 @@ type RespSync struct { | ||||
| 		} `json:"invite"` | ||||
| 	} `json:"rooms"` | ||||
| } | ||||
|  | ||||
| type RespTurnServer struct { | ||||
| 	Username string   `json:"username"` | ||||
| 	Password string   `json:"password"` | ||||
| 	TTL      int      `json:"ttl"` | ||||
| 	URIs     []string `json:"uris"` | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/matrix-org/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/matrix-org/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,7 +13,7 @@ func (room Room) UpdateState(event *Event) { | ||||
| 	if !exists { | ||||
| 		room.State[event.Type] = make(map[string]*Event) | ||||
| 	} | ||||
| 	room.State[event.Type][event.StateKey] = event | ||||
| 	room.State[event.Type][*event.StateKey] = event | ||||
| } | ||||
|  | ||||
| // GetStateEvent returns the state event for the given type/state_key combo, or nil. | ||||
|   | ||||
							
								
								
									
										12
									
								
								vendor/github.com/matrix-org/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/matrix-org/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -73,6 +73,16 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) | ||||
| 			s.notifyListeners(&event) | ||||
| 		} | ||||
| 	} | ||||
| 	for roomID, roomData := range res.Rooms.Leave { | ||||
| 		room := s.getOrCreateRoom(roomID) | ||||
| 		for _, event := range roomData.Timeline.Events { | ||||
| 			if event.StateKey != nil { | ||||
| 				event.RoomID = roomID | ||||
| 				room.UpdateState(&event) | ||||
| 				s.notifyListeners(&event) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @@ -102,7 +112,7 @@ func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool | ||||
| 	for roomID, roomData := range resp.Rooms.Join { | ||||
| 		for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- { | ||||
| 			e := roomData.Timeline.Events[i] | ||||
| 			if e.Type == "m.room.member" && e.StateKey == s.UserID { | ||||
| 			if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID { | ||||
| 				m := e.Content["membership"] | ||||
| 				mship, ok := m.(string) | ||||
| 				if !ok { | ||||
|   | ||||
							
								
								
									
										21
									
								
								vendor/github.com/shazow/rateio/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/shazow/rateio/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2015 Andrey Petrov | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
							
								
								
									
										29
									
								
								vendor/github.com/shazow/rateio/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/shazow/rateio/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
| Package rateio provides an io interfaces for rate-limiting. | ||||
|  | ||||
| This can be used to apply rate limiting to any type that implements an io-style interface. | ||||
|  | ||||
| For example, we can use it to restrict the reading rate of a net.Conn: | ||||
|  | ||||
| 	type limitedConn struct { | ||||
| 		net.Conn | ||||
| 		io.Reader // Our rate-limited io.Reader for net.Conn | ||||
| 	} | ||||
|  | ||||
| 	func (r *limitedConn) Read(p []byte) (n int, err error) { | ||||
| 		return r.Reader.Read(p) | ||||
| 	} | ||||
|  | ||||
| 	// ReadLimitConn returns a net.Conn whose io.Reader interface is rate-limited by limiter. | ||||
| 	func ReadLimitConn(conn net.Conn, limiter rateio.Limiter) net.Conn { | ||||
| 		return &limitedConn{ | ||||
| 			Conn:   conn, | ||||
| 			Reader: rateio.NewReader(conn, limiter), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| Then we can use ReadLimitConn to wrap our existing net.Conn and continue using | ||||
| the wrapped version in its place. | ||||
|  | ||||
| */ | ||||
| package rateio | ||||
							
								
								
									
										62
									
								
								vendor/github.com/shazow/rateio/limiter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/shazow/rateio/limiter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| package rateio | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| const minInt = -int(^uint(0)>>1) - 1 | ||||
|  | ||||
| // The error returned when the read rate exceeds our specification. | ||||
| var ErrRateExceeded = errors.New("Read rate exceeded.") | ||||
|  | ||||
| // Limiter is an interface for a rate limiter. | ||||
| // There are a few example limiters included in the package, but feel free to go wild with your own. | ||||
| type Limiter interface { | ||||
| 	// Apply this many bytes to the limiter, return ErrRateExceeded if the defined rate is exceeded. | ||||
| 	Count(int) error | ||||
| } | ||||
|  | ||||
| // simpleLimiter is a rate limiter that restricts Amount bytes in Frequency duration. | ||||
| type simpleLimiter struct { | ||||
| 	Amount    int | ||||
| 	Frequency time.Duration | ||||
|  | ||||
| 	numRead  int | ||||
| 	timeRead time.Time | ||||
| } | ||||
|  | ||||
| // NewSimpleLimiter creates a Limiter that restricts a given number of bytes per frequency. | ||||
| func NewSimpleLimiter(amount int, frequency time.Duration) Limiter { | ||||
| 	return &simpleLimiter{ | ||||
| 		Amount:    amount, | ||||
| 		Frequency: frequency, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewGracefulLimiter returns a Limiter that is the same as a | ||||
| // SimpleLimiter but adds a grace period at the start of the rate | ||||
| // limiting where it allows unlimited bytes to be read during that | ||||
| // period. | ||||
| func NewGracefulLimiter(amount int, frequency time.Duration, grace time.Duration) Limiter { | ||||
| 	return &simpleLimiter{ | ||||
| 		Amount:    amount, | ||||
| 		Frequency: frequency, | ||||
| 		numRead:   minInt, | ||||
| 		timeRead:  time.Now().Add(grace), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Count applies n bytes to the limiter. | ||||
| func (limit *simpleLimiter) Count(n int) error { | ||||
| 	now := time.Now() | ||||
| 	if now.After(limit.timeRead) { | ||||
| 		limit.numRead = 0 | ||||
| 		limit.timeRead = now.Add(limit.Frequency) | ||||
| 	} | ||||
| 	limit.numRead += n | ||||
| 	if limit.numRead > limit.Amount { | ||||
| 		return ErrRateExceeded | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/shazow/rateio/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/shazow/rateio/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| package rateio | ||||
|  | ||||
| import "io" | ||||
|  | ||||
| type reader struct { | ||||
| 	io.Reader | ||||
| 	Limiter | ||||
| } | ||||
|  | ||||
| // Read reads data into p. | ||||
| // Returns ErrRateExceeded error if our specified read is exceeded. | ||||
| func (r *reader) Read(p []byte) (n int, err error) { | ||||
| 	n, err = r.Reader.Read(p) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	err = r.Limiter.Count(n) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // NewReader proxies an io.Reader but keeps track of bytes read based on our Limiter. | ||||
| func NewReader(r io.Reader, limiter Limiter) io.Reader { | ||||
| 	return &reader{r, limiter} | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/shazow/rateio/writer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/shazow/rateio/writer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| package rateio | ||||
|  | ||||
| import "io" | ||||
|  | ||||
| type writer struct { | ||||
| 	io.Writer | ||||
| 	Limiter | ||||
| } | ||||
|  | ||||
| // Write writes the contents of p into the buffer. | ||||
| // Returns ErrRateExceeded error if our specified read is exceeded. | ||||
| func (w *writer) Write(p []byte) (n int, err error) { | ||||
| 	n, err = w.Writer.Write(p) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	err = w.Limiter.Count(n) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // NewWriter proxies an io.Writer but keeps track of bytes read based on our Limiter. | ||||
| func NewWriter(w io.Writer, limiter Limiter) io.Writer { | ||||
| 	return &writer{w, limiter} | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/shazow/ssh-chat/sshd/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/shazow/ssh-chat/sshd/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2014 Andrey Petrov | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
							
								
								
									
										72
									
								
								vendor/github.com/shazow/ssh-chat/sshd/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								vendor/github.com/shazow/ssh-chat/sshd/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| package sshd | ||||
|  | ||||
| import ( | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"net" | ||||
|  | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // Auth is used to authenticate connections based on public keys. | ||||
| type Auth interface { | ||||
| 	// Whether to allow connections without a public key. | ||||
| 	AllowAnonymous() bool | ||||
| 	// Given address and public key, return if the connection should be permitted. | ||||
| 	Check(net.Addr, ssh.PublicKey) (bool, error) | ||||
| } | ||||
|  | ||||
| // MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation. | ||||
| func MakeAuth(auth Auth) *ssh.ServerConfig { | ||||
| 	config := ssh.ServerConfig{ | ||||
| 		NoClientAuth: false, | ||||
| 		// Auth-related things should be constant-time to avoid timing attacks. | ||||
| 		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { | ||||
| 			ok, err := auth.Check(conn.RemoteAddr(), key) | ||||
| 			if !ok { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			perm := &ssh.Permissions{Extensions: map[string]string{ | ||||
| 				"pubkey": string(key.Marshal()), | ||||
| 			}} | ||||
| 			return perm, nil | ||||
| 		}, | ||||
| 		KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) { | ||||
| 			if !auth.AllowAnonymous() { | ||||
| 				return nil, errors.New("public key authentication required") | ||||
| 			} | ||||
| 			_, err := auth.Check(conn.RemoteAddr(), nil) | ||||
| 			return nil, err | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return &config | ||||
| } | ||||
|  | ||||
| // MakeNoAuth makes a simple ssh.ServerConfig which allows all connections. | ||||
| // Primarily used for testing. | ||||
| func MakeNoAuth() *ssh.ServerConfig { | ||||
| 	config := ssh.ServerConfig{ | ||||
| 		NoClientAuth: false, | ||||
| 		// Auth-related things should be constant-time to avoid timing attacks. | ||||
| 		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { | ||||
| 			perm := &ssh.Permissions{Extensions: map[string]string{ | ||||
| 				"pubkey": string(key.Marshal()), | ||||
| 			}} | ||||
| 			return perm, nil | ||||
| 		}, | ||||
| 		KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) { | ||||
| 			return nil, nil | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return &config | ||||
| } | ||||
|  | ||||
| // Fingerprint performs a SHA256 BASE64 fingerprint of the PublicKey, similar to OpenSSH. | ||||
| // See: https://anongit.mindrot.org/openssh.git/commit/?id=56d1c83cdd1ac | ||||
| func Fingerprint(k ssh.PublicKey) string { | ||||
| 	hash := sha256.Sum256(k.Marshal()) | ||||
| 	return "SHA256:" + base64.StdEncoding.EncodeToString(hash[:]) | ||||
| } | ||||
							
								
								
									
										76
									
								
								vendor/github.com/shazow/ssh-chat/sshd/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/shazow/ssh-chat/sshd/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| package sshd | ||||
|  | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| 	"io" | ||||
|  | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // NewRandomSigner generates a random key of a desired bit length. | ||||
| func NewRandomSigner(bits int) (ssh.Signer, error) { | ||||
| 	key, err := rsa.GenerateKey(rand.Reader, bits) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return ssh.NewSignerFromKey(key) | ||||
| } | ||||
|  | ||||
| // NewClientConfig creates a barebones ssh.ClientConfig to be used with ssh.Dial. | ||||
| func NewClientConfig(name string) *ssh.ClientConfig { | ||||
| 	return &ssh.ClientConfig{ | ||||
| 		User: name, | ||||
| 		Auth: []ssh.AuthMethod{ | ||||
| 			ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) (answers []string, err error) { | ||||
| 				return | ||||
| 			}), | ||||
| 		}, | ||||
| 		HostKeyCallback: ssh.InsecureIgnoreHostKey(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ConnectShell makes a barebones SSH client session, used for testing. | ||||
| func ConnectShell(host string, name string, handler func(r io.Reader, w io.WriteCloser) error) error { | ||||
| 	config := NewClientConfig(name) | ||||
| 	conn, err := ssh.Dial("tcp", host, config) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
|  | ||||
| 	session, err := conn.NewSession() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer session.Close() | ||||
|  | ||||
| 	in, err := session.StdinPipe() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	out, err := session.StdoutPipe() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 		err = session.RequestPty("xterm", 80, 40, ssh.TerminalModes{}) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	*/ | ||||
|  | ||||
| 	err = session.Shell() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	_, err = session.SendRequest("ping", true, nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return handler(out, in) | ||||
| } | ||||
							
								
								
									
										34
									
								
								vendor/github.com/shazow/ssh-chat/sshd/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/shazow/ssh-chat/sshd/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| package sshd | ||||
|  | ||||
| /* | ||||
|  | ||||
| 	signer, err := ssh.ParsePrivateKey(privateKey) | ||||
|  | ||||
| 	config := MakeNoAuth() | ||||
| 	config.AddHostKey(signer) | ||||
|  | ||||
| 	s, err := ListenSSH("0.0.0.0:2022", config) | ||||
| 	if err != nil { | ||||
| 		// Handle opening socket error | ||||
| 	} | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	terminals := s.ServeTerminal() | ||||
|  | ||||
| 	for term := range terminals { | ||||
| 		go func() { | ||||
| 			defer term.Close() | ||||
| 			term.SetPrompt("...") | ||||
| 			term.AutoCompleteCallback = nil // ... | ||||
|  | ||||
| 			for { | ||||
| 				line, err := term.ReadLine() | ||||
| 				if err != nil { | ||||
| 					break | ||||
| 				} | ||||
| 				term.Write(...) | ||||
| 			} | ||||
|  | ||||
| 		}() | ||||
| 	} | ||||
| */ | ||||
							
								
								
									
										22
									
								
								vendor/github.com/shazow/ssh-chat/sshd/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/shazow/ssh-chat/sshd/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| package sshd | ||||
|  | ||||
| import "io" | ||||
| import stdlog "log" | ||||
|  | ||||
| var logger *stdlog.Logger | ||||
|  | ||||
| func SetLogger(w io.Writer) { | ||||
| 	flags := stdlog.Flags() | ||||
| 	prefix := "[sshd] " | ||||
| 	logger = stdlog.New(w, prefix, flags) | ||||
| } | ||||
|  | ||||
| type nullWriter struct{} | ||||
|  | ||||
| func (nullWriter) Write(data []byte) (int, error) { | ||||
| 	return len(data), nil | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	SetLogger(nullWriter{}) | ||||
| } | ||||
							
								
								
									
										67
									
								
								vendor/github.com/shazow/ssh-chat/sshd/net.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/shazow/ssh-chat/sshd/net.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| package sshd | ||||
|  | ||||
| import ( | ||||
| 	"net" | ||||
|  | ||||
| 	"github.com/shazow/rateio" | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // Container for the connection and ssh-related configuration | ||||
| type SSHListener struct { | ||||
| 	net.Listener | ||||
| 	config *ssh.ServerConfig | ||||
|  | ||||
| 	RateLimit   func() rateio.Limiter | ||||
| 	HandlerFunc func(term *Terminal) | ||||
| } | ||||
|  | ||||
| // Make an SSH listener socket | ||||
| func ListenSSH(laddr string, config *ssh.ServerConfig) (*SSHListener, error) { | ||||
| 	socket, err := net.Listen("tcp", laddr) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	l := SSHListener{Listener: socket, config: config} | ||||
| 	return &l, nil | ||||
| } | ||||
|  | ||||
| func (l *SSHListener) handleConn(conn net.Conn) (*Terminal, error) { | ||||
| 	if l.RateLimit != nil { | ||||
| 		// TODO: Configurable Limiter? | ||||
| 		conn = ReadLimitConn(conn, l.RateLimit()) | ||||
| 	} | ||||
|  | ||||
| 	// Upgrade TCP connection to SSH connection | ||||
| 	sshConn, channels, requests, err := ssh.NewServerConn(conn, l.config) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: Disconnect if too many faulty requests? (Avoid DoS.) | ||||
| 	go ssh.DiscardRequests(requests) | ||||
| 	return NewSession(sshConn, channels) | ||||
| } | ||||
|  | ||||
| // Accept incoming connections as terminal requests and yield them | ||||
| func (l *SSHListener) Serve() { | ||||
| 	defer l.Close() | ||||
| 	for { | ||||
| 		conn, err := l.Accept() | ||||
|  | ||||
| 		if err != nil { | ||||
| 			logger.Printf("Failed to accept connection: %s", err) | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		// Goroutineify to resume accepting sockets early | ||||
| 		go func() { | ||||
| 			term, err := l.handleConn(conn) | ||||
| 			if err != nil { | ||||
| 				logger.Printf("[%s] Failed to handshake: %s", conn.RemoteAddr(), err) | ||||
| 				return | ||||
| 			} | ||||
| 			l.HandlerFunc(term) | ||||
| 		}() | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										70
									
								
								vendor/github.com/shazow/ssh-chat/sshd/pty.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								vendor/github.com/shazow/ssh-chat/sshd/pty.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| package sshd | ||||
|  | ||||
| import "encoding/binary" | ||||
|  | ||||
| // Helpers below are borrowed from go.crypto circa 2011: | ||||
|  | ||||
| // parsePtyRequest parses the payload of the pty-req message and extracts the | ||||
| // dimensions of the terminal. See RFC 4254, section 6.2. | ||||
| func parsePtyRequest(s []byte) (width, height int, ok bool) { | ||||
| 	_, s, ok = parseString(s) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	width32, s, ok := parseUint32(s) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	height32, _, ok := parseUint32(s) | ||||
| 	width = int(width32) | ||||
| 	height = int(height32) | ||||
| 	if width < 1 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 	if height < 1 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func parseWinchRequest(s []byte) (width, height int, ok bool) { | ||||
| 	width32, _, ok := parseUint32(s) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	height32, _, ok := parseUint32(s) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	width = int(width32) | ||||
| 	height = int(height32) | ||||
| 	if width < 1 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 	if height < 1 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func parseString(in []byte) (out string, rest []byte, ok bool) { | ||||
| 	if len(in) < 4 { | ||||
| 		return | ||||
| 	} | ||||
| 	length := binary.BigEndian.Uint32(in) | ||||
| 	if uint32(len(in)) < 4+length { | ||||
| 		return | ||||
| 	} | ||||
| 	out = string(in[4 : 4+length]) | ||||
| 	rest = in[4+length:] | ||||
| 	ok = true | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func parseUint32(in []byte) (uint32, []byte, bool) { | ||||
| 	if len(in) < 4 { | ||||
| 		return 0, nil, false | ||||
| 	} | ||||
| 	return binary.BigEndian.Uint32(in), in[4:], true | ||||
| } | ||||
							
								
								
									
										71
									
								
								vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| package sshd | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/shazow/rateio" | ||||
| ) | ||||
|  | ||||
| type limitedConn struct { | ||||
| 	net.Conn | ||||
| 	io.Reader // Our rate-limited io.Reader for net.Conn | ||||
| } | ||||
|  | ||||
| func (r *limitedConn) Read(p []byte) (n int, err error) { | ||||
| 	return r.Reader.Read(p) | ||||
| } | ||||
|  | ||||
| // ReadLimitConn returns a net.Conn whose io.Reader interface is rate-limited by limiter. | ||||
| func ReadLimitConn(conn net.Conn, limiter rateio.Limiter) net.Conn { | ||||
| 	return &limitedConn{ | ||||
| 		Conn:   conn, | ||||
| 		Reader: rateio.NewReader(conn, limiter), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Count each read as 1 unless it exceeds some number of bytes. | ||||
| type inputLimiter struct { | ||||
| 	// TODO: Could do all kinds of fancy things here, like be more forgiving of | ||||
| 	// connections that have been around for a while. | ||||
|  | ||||
| 	Amount    int | ||||
| 	Frequency time.Duration | ||||
|  | ||||
| 	remaining int | ||||
| 	readCap   int | ||||
| 	numRead   int | ||||
| 	timeRead  time.Time | ||||
| } | ||||
|  | ||||
| // NewInputLimiter returns a rateio.Limiter with sensible defaults for | ||||
| // differentiating between humans typing and bots spamming. | ||||
| func NewInputLimiter() rateio.Limiter { | ||||
| 	grace := time.Second * 3 | ||||
| 	return &inputLimiter{ | ||||
| 		Amount:    2 << 14, // ~16kb, should be plenty for a high typing rate/copypasta/large key handshakes. | ||||
| 		Frequency: time.Minute * 1, | ||||
| 		readCap:   128,          // Allow up to 128 bytes per read (anecdotally, 1 character = 52 bytes over ssh) | ||||
| 		numRead:   -1024 * 1024, // Start with a 1mb grace | ||||
| 		timeRead:  time.Now().Add(grace), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Count applies 1 if n<readCap, else n | ||||
| func (limit *inputLimiter) Count(n int) error { | ||||
| 	now := time.Now() | ||||
| 	if now.After(limit.timeRead) { | ||||
| 		limit.numRead = 0 | ||||
| 		limit.timeRead = now.Add(limit.Frequency) | ||||
| 	} | ||||
| 	if n <= limit.readCap { | ||||
| 		limit.numRead += 1 | ||||
| 	} else { | ||||
| 		limit.numRead += n | ||||
| 	} | ||||
| 	if limit.numRead > limit.Amount { | ||||
| 		return rateio.ErrRateExceeded | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										167
									
								
								vendor/github.com/shazow/ssh-chat/sshd/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								vendor/github.com/shazow/ssh-chat/sshd/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| package sshd | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| 	"golang.org/x/crypto/ssh/terminal" | ||||
| ) | ||||
|  | ||||
| var keepaliveInterval = time.Second * 30 | ||||
| var keepaliveRequest = "keepalive@ssh-chat" | ||||
|  | ||||
| var ErrNoSessionChannel = errors.New("no session channel") | ||||
| var ErrNotSessionChannel = errors.New("terminal requires session channel") | ||||
|  | ||||
| // Connection is an interface with fields necessary to operate an sshd host. | ||||
| type Connection interface { | ||||
| 	PublicKey() ssh.PublicKey | ||||
| 	RemoteAddr() net.Addr | ||||
| 	Name() string | ||||
| 	ClientVersion() []byte | ||||
| 	Close() error | ||||
| } | ||||
|  | ||||
| type sshConn struct { | ||||
| 	*ssh.ServerConn | ||||
| } | ||||
|  | ||||
| func (c sshConn) PublicKey() ssh.PublicKey { | ||||
| 	if c.Permissions == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	s, ok := c.Permissions.Extensions["pubkey"] | ||||
| 	if !ok { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	key, err := ssh.ParsePublicKey([]byte(s)) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return key | ||||
| } | ||||
|  | ||||
| func (c sshConn) Name() string { | ||||
| 	return c.User() | ||||
| } | ||||
|  | ||||
| // Extending ssh/terminal to include a closer interface | ||||
| type Terminal struct { | ||||
| 	terminal.Terminal | ||||
| 	Conn    Connection | ||||
| 	Channel ssh.Channel | ||||
|  | ||||
| 	done      chan struct{} | ||||
| 	closeOnce sync.Once | ||||
| } | ||||
|  | ||||
| // Make new terminal from a session channel | ||||
| func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) { | ||||
| 	if ch.ChannelType() != "session" { | ||||
| 		return nil, ErrNotSessionChannel | ||||
| 	} | ||||
| 	channel, requests, err := ch.Accept() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	term := Terminal{ | ||||
| 		Terminal: *terminal.NewTerminal(channel, "Connecting..."), | ||||
| 		Conn:     sshConn{conn}, | ||||
| 		Channel:  channel, | ||||
|  | ||||
| 		done: make(chan struct{}), | ||||
| 	} | ||||
|  | ||||
| 	go term.listen(requests) | ||||
|  | ||||
| 	go func() { | ||||
| 		// Keep-Alive Ticker | ||||
| 		ticker := time.Tick(keepaliveInterval) | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-ticker: | ||||
| 				_, err := channel.SendRequest(keepaliveRequest, true, nil) | ||||
| 				if err != nil { | ||||
| 					// Connection is gone | ||||
| 					logger.Printf("[%s] Keepalive failed, closing terminal: %s", term.Conn.RemoteAddr(), err) | ||||
| 					term.Close() | ||||
| 					return | ||||
| 				} | ||||
| 			case <-term.done: | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return &term, nil | ||||
| } | ||||
|  | ||||
| // Find session channel and make a Terminal from it | ||||
| func NewSession(conn *ssh.ServerConn, channels <-chan ssh.NewChannel) (*Terminal, error) { | ||||
| 	// Make a terminal from the first session found | ||||
| 	for ch := range channels { | ||||
| 		if t := ch.ChannelType(); t != "session" { | ||||
| 			logger.Printf("[%s] Ignored channel type: %s", conn.RemoteAddr(), t) | ||||
| 			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t)) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		return NewTerminal(conn, ch) | ||||
| 	} | ||||
|  | ||||
| 	return nil, ErrNoSessionChannel | ||||
| } | ||||
|  | ||||
| // Close terminal and ssh connection | ||||
| func (t *Terminal) Close() error { | ||||
| 	var err error | ||||
| 	t.closeOnce.Do(func() { | ||||
| 		close(t.done) | ||||
| 		t.Channel.Close() | ||||
| 		err = t.Conn.Close() | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Negotiate terminal type and settings | ||||
| func (t *Terminal) listen(requests <-chan *ssh.Request) { | ||||
| 	hasShell := false | ||||
|  | ||||
| 	for req := range requests { | ||||
| 		var width, height int | ||||
| 		var ok bool | ||||
|  | ||||
| 		switch req.Type { | ||||
| 		case "shell": | ||||
| 			if !hasShell { | ||||
| 				ok = true | ||||
| 				hasShell = true | ||||
| 			} | ||||
| 		case "pty-req": | ||||
| 			width, height, ok = parsePtyRequest(req.Payload) | ||||
| 			if ok { | ||||
| 				// TODO: Hardcode width to 100000? | ||||
| 				err := t.SetSize(width, height) | ||||
| 				ok = err == nil | ||||
| 			} | ||||
| 		case "window-change": | ||||
| 			width, height, ok = parseWinchRequest(req.Payload) | ||||
| 			if ok { | ||||
| 				// TODO: Hardcode width to 100000? | ||||
| 				err := t.SetSize(width, height) | ||||
| 				ok = err == nil | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if req.WantReply { | ||||
| 			req.Reply(ok, nil) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/crypto/curve25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/crypto/curve25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| Copyright (c) 2009 The Go Authors. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
|  | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										8
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.h
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.h
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| // Copyright 2012 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. | ||||
|  | ||||
| // This code was translated into a form compatible with 6a from the public | ||||
| // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html | ||||
|  | ||||
| #define REDMASK51     0x0007FFFFFFFFFFFF | ||||
							
								
								
									
										20
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| // Copyright 2012 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. | ||||
|  | ||||
| // This code was translated into a form compatible with 6a from the public | ||||
| // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html | ||||
|  | ||||
| // +build amd64,!gccgo,!appengine | ||||
|  | ||||
| // These constants cannot be encoded in non-MOVQ immediates. | ||||
| // We access them directly from memory instead. | ||||
|  | ||||
| DATA ·_121666_213(SB)/8, $996687872 | ||||
| GLOBL ·_121666_213(SB), 8, $8 | ||||
|  | ||||
| DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA | ||||
| GLOBL ·_2P0(SB), 8, $8 | ||||
|  | ||||
| DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE | ||||
| GLOBL ·_2P1234(SB), 8, $8 | ||||
							
								
								
									
										65
									
								
								vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| // Copyright 2012 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 | ||||
|  | ||||
| // func cswap(inout *[4][5]uint64, v uint64) | ||||
| TEXT ·cswap(SB),7,$0 | ||||
| 	MOVQ inout+0(FP),DI | ||||
| 	MOVQ v+8(FP),SI | ||||
|  | ||||
| 	SUBQ $1, SI | ||||
| 	NOTQ SI | ||||
| 	MOVQ SI, X15 | ||||
| 	PSHUFD $0x44, X15, X15 | ||||
|  | ||||
| 	MOVOU 0(DI), X0 | ||||
| 	MOVOU 16(DI), X2 | ||||
| 	MOVOU 32(DI), X4 | ||||
| 	MOVOU 48(DI), X6 | ||||
| 	MOVOU 64(DI), X8 | ||||
| 	MOVOU 80(DI), X1 | ||||
| 	MOVOU 96(DI), X3 | ||||
| 	MOVOU 112(DI), X5 | ||||
| 	MOVOU 128(DI), X7 | ||||
| 	MOVOU 144(DI), X9 | ||||
|  | ||||
| 	MOVO X1, X10 | ||||
| 	MOVO X3, X11 | ||||
| 	MOVO X5, X12 | ||||
| 	MOVO X7, X13 | ||||
| 	MOVO X9, X14 | ||||
|  | ||||
| 	PXOR X0, X10 | ||||
| 	PXOR X2, X11 | ||||
| 	PXOR X4, X12 | ||||
| 	PXOR X6, X13 | ||||
| 	PXOR X8, X14 | ||||
| 	PAND X15, X10 | ||||
| 	PAND X15, X11 | ||||
| 	PAND X15, X12 | ||||
| 	PAND X15, X13 | ||||
| 	PAND X15, X14 | ||||
| 	PXOR X10, X0 | ||||
| 	PXOR X10, X1 | ||||
| 	PXOR X11, X2 | ||||
| 	PXOR X11, X3 | ||||
| 	PXOR X12, X4 | ||||
| 	PXOR X12, X5 | ||||
| 	PXOR X13, X6 | ||||
| 	PXOR X13, X7 | ||||
| 	PXOR X14, X8 | ||||
| 	PXOR X14, X9 | ||||
|  | ||||
| 	MOVOU X0, 0(DI) | ||||
| 	MOVOU X2, 16(DI) | ||||
| 	MOVOU X4, 32(DI) | ||||
| 	MOVOU X6, 48(DI) | ||||
| 	MOVOU X8, 64(DI) | ||||
| 	MOVOU X1, 80(DI) | ||||
| 	MOVOU X3, 96(DI) | ||||
| 	MOVOU X5, 112(DI) | ||||
| 	MOVOU X7, 128(DI) | ||||
| 	MOVOU X9, 144(DI) | ||||
| 	RET | ||||
							
								
								
									
										834
									
								
								vendor/golang.org/x/crypto/curve25519/curve25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										834
									
								
								vendor/golang.org/x/crypto/curve25519/curve25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,834 @@ | ||||
| // Copyright 2013 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. | ||||
|  | ||||
| // We have an implementation in amd64 assembly so this code is only run on | ||||
| // non-amd64 platforms. The amd64 assembly does not support gccgo. | ||||
| // +build !amd64 gccgo appengine | ||||
|  | ||||
| package curve25519 | ||||
|  | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| ) | ||||
|  | ||||
| // This code is a port of the public domain, "ref10" implementation of | ||||
| // curve25519 from SUPERCOP 20130419 by D. J. Bernstein. | ||||
|  | ||||
| // fieldElement represents an element of the field GF(2^255 - 19). An element | ||||
| // t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 | ||||
| // t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on | ||||
| // context. | ||||
| type fieldElement [10]int32 | ||||
|  | ||||
| func feZero(fe *fieldElement) { | ||||
| 	for i := range fe { | ||||
| 		fe[i] = 0 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func feOne(fe *fieldElement) { | ||||
| 	feZero(fe) | ||||
| 	fe[0] = 1 | ||||
| } | ||||
|  | ||||
| func feAdd(dst, a, b *fieldElement) { | ||||
| 	for i := range dst { | ||||
| 		dst[i] = a[i] + b[i] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func feSub(dst, a, b *fieldElement) { | ||||
| 	for i := range dst { | ||||
| 		dst[i] = a[i] - b[i] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func feCopy(dst, src *fieldElement) { | ||||
| 	for i := range dst { | ||||
| 		dst[i] = src[i] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0. | ||||
| // | ||||
| // Preconditions: b in {0,1}. | ||||
| func feCSwap(f, g *fieldElement, b int32) { | ||||
| 	b = -b | ||||
| 	for i := range f { | ||||
| 		t := b & (f[i] ^ g[i]) | ||||
| 		f[i] ^= t | ||||
| 		g[i] ^= t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // load3 reads a 24-bit, little-endian value from in. | ||||
| func load3(in []byte) int64 { | ||||
| 	var r int64 | ||||
| 	r = int64(in[0]) | ||||
| 	r |= int64(in[1]) << 8 | ||||
| 	r |= int64(in[2]) << 16 | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| // load4 reads a 32-bit, little-endian value from in. | ||||
| func load4(in []byte) int64 { | ||||
| 	return int64(binary.LittleEndian.Uint32(in)) | ||||
| } | ||||
|  | ||||
| func feFromBytes(dst *fieldElement, src *[32]byte) { | ||||
| 	h0 := load4(src[:]) | ||||
| 	h1 := load3(src[4:]) << 6 | ||||
| 	h2 := load3(src[7:]) << 5 | ||||
| 	h3 := load3(src[10:]) << 3 | ||||
| 	h4 := load3(src[13:]) << 2 | ||||
| 	h5 := load4(src[16:]) | ||||
| 	h6 := load3(src[20:]) << 7 | ||||
| 	h7 := load3(src[23:]) << 5 | ||||
| 	h8 := load3(src[26:]) << 4 | ||||
| 	h9 := load3(src[29:]) << 2 | ||||
|  | ||||
| 	var carry [10]int64 | ||||
| 	carry[9] = (h9 + 1<<24) >> 25 | ||||
| 	h0 += carry[9] * 19 | ||||
| 	h9 -= carry[9] << 25 | ||||
| 	carry[1] = (h1 + 1<<24) >> 25 | ||||
| 	h2 += carry[1] | ||||
| 	h1 -= carry[1] << 25 | ||||
| 	carry[3] = (h3 + 1<<24) >> 25 | ||||
| 	h4 += carry[3] | ||||
| 	h3 -= carry[3] << 25 | ||||
| 	carry[5] = (h5 + 1<<24) >> 25 | ||||
| 	h6 += carry[5] | ||||
| 	h5 -= carry[5] << 25 | ||||
| 	carry[7] = (h7 + 1<<24) >> 25 | ||||
| 	h8 += carry[7] | ||||
| 	h7 -= carry[7] << 25 | ||||
|  | ||||
| 	carry[0] = (h0 + 1<<25) >> 26 | ||||
| 	h1 += carry[0] | ||||
| 	h0 -= carry[0] << 26 | ||||
| 	carry[2] = (h2 + 1<<25) >> 26 | ||||
| 	h3 += carry[2] | ||||
| 	h2 -= carry[2] << 26 | ||||
| 	carry[4] = (h4 + 1<<25) >> 26 | ||||
| 	h5 += carry[4] | ||||
| 	h4 -= carry[4] << 26 | ||||
| 	carry[6] = (h6 + 1<<25) >> 26 | ||||
| 	h7 += carry[6] | ||||
| 	h6 -= carry[6] << 26 | ||||
| 	carry[8] = (h8 + 1<<25) >> 26 | ||||
| 	h9 += carry[8] | ||||
| 	h8 -= carry[8] << 26 | ||||
|  | ||||
| 	dst[0] = int32(h0) | ||||
| 	dst[1] = int32(h1) | ||||
| 	dst[2] = int32(h2) | ||||
| 	dst[3] = int32(h3) | ||||
| 	dst[4] = int32(h4) | ||||
| 	dst[5] = int32(h5) | ||||
| 	dst[6] = int32(h6) | ||||
| 	dst[7] = int32(h7) | ||||
| 	dst[8] = int32(h8) | ||||
| 	dst[9] = int32(h9) | ||||
| } | ||||
|  | ||||
| // feToBytes marshals h to s. | ||||
| // Preconditions: | ||||
| //   |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | ||||
| // | ||||
| // Write p=2^255-19; q=floor(h/p). | ||||
| // Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). | ||||
| // | ||||
| // Proof: | ||||
| //   Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. | ||||
| //   Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. | ||||
| // | ||||
| //   Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). | ||||
| //   Then 0<y<1. | ||||
| // | ||||
| //   Write r=h-pq. | ||||
| //   Have 0<=r<=p-1=2^255-20. | ||||
| //   Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1. | ||||
| // | ||||
| //   Write x=r+19(2^-255)r+y. | ||||
| //   Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q. | ||||
| // | ||||
| //   Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1)) | ||||
| //   so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. | ||||
| func feToBytes(s *[32]byte, h *fieldElement) { | ||||
| 	var carry [10]int32 | ||||
|  | ||||
| 	q := (19*h[9] + (1 << 24)) >> 25 | ||||
| 	q = (h[0] + q) >> 26 | ||||
| 	q = (h[1] + q) >> 25 | ||||
| 	q = (h[2] + q) >> 26 | ||||
| 	q = (h[3] + q) >> 25 | ||||
| 	q = (h[4] + q) >> 26 | ||||
| 	q = (h[5] + q) >> 25 | ||||
| 	q = (h[6] + q) >> 26 | ||||
| 	q = (h[7] + q) >> 25 | ||||
| 	q = (h[8] + q) >> 26 | ||||
| 	q = (h[9] + q) >> 25 | ||||
|  | ||||
| 	// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. | ||||
| 	h[0] += 19 * q | ||||
| 	// Goal: Output h-2^255 q, which is between 0 and 2^255-20. | ||||
|  | ||||
| 	carry[0] = h[0] >> 26 | ||||
| 	h[1] += carry[0] | ||||
| 	h[0] -= carry[0] << 26 | ||||
| 	carry[1] = h[1] >> 25 | ||||
| 	h[2] += carry[1] | ||||
| 	h[1] -= carry[1] << 25 | ||||
| 	carry[2] = h[2] >> 26 | ||||
| 	h[3] += carry[2] | ||||
| 	h[2] -= carry[2] << 26 | ||||
| 	carry[3] = h[3] >> 25 | ||||
| 	h[4] += carry[3] | ||||
| 	h[3] -= carry[3] << 25 | ||||
| 	carry[4] = h[4] >> 26 | ||||
| 	h[5] += carry[4] | ||||
| 	h[4] -= carry[4] << 26 | ||||
| 	carry[5] = h[5] >> 25 | ||||
| 	h[6] += carry[5] | ||||
| 	h[5] -= carry[5] << 25 | ||||
| 	carry[6] = h[6] >> 26 | ||||
| 	h[7] += carry[6] | ||||
| 	h[6] -= carry[6] << 26 | ||||
| 	carry[7] = h[7] >> 25 | ||||
| 	h[8] += carry[7] | ||||
| 	h[7] -= carry[7] << 25 | ||||
| 	carry[8] = h[8] >> 26 | ||||
| 	h[9] += carry[8] | ||||
| 	h[8] -= carry[8] << 26 | ||||
| 	carry[9] = h[9] >> 25 | ||||
| 	h[9] -= carry[9] << 25 | ||||
| 	// h10 = carry9 | ||||
|  | ||||
| 	// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. | ||||
| 	// Have h[0]+...+2^230 h[9] between 0 and 2^255-1; | ||||
| 	// evidently 2^255 h10-2^255 q = 0. | ||||
| 	// Goal: Output h[0]+...+2^230 h[9]. | ||||
|  | ||||
| 	s[0] = byte(h[0] >> 0) | ||||
| 	s[1] = byte(h[0] >> 8) | ||||
| 	s[2] = byte(h[0] >> 16) | ||||
| 	s[3] = byte((h[0] >> 24) | (h[1] << 2)) | ||||
| 	s[4] = byte(h[1] >> 6) | ||||
| 	s[5] = byte(h[1] >> 14) | ||||
| 	s[6] = byte((h[1] >> 22) | (h[2] << 3)) | ||||
| 	s[7] = byte(h[2] >> 5) | ||||
| 	s[8] = byte(h[2] >> 13) | ||||
| 	s[9] = byte((h[2] >> 21) | (h[3] << 5)) | ||||
| 	s[10] = byte(h[3] >> 3) | ||||
| 	s[11] = byte(h[3] >> 11) | ||||
| 	s[12] = byte((h[3] >> 19) | (h[4] << 6)) | ||||
| 	s[13] = byte(h[4] >> 2) | ||||
| 	s[14] = byte(h[4] >> 10) | ||||
| 	s[15] = byte(h[4] >> 18) | ||||
| 	s[16] = byte(h[5] >> 0) | ||||
| 	s[17] = byte(h[5] >> 8) | ||||
| 	s[18] = byte(h[5] >> 16) | ||||
| 	s[19] = byte((h[5] >> 24) | (h[6] << 1)) | ||||
| 	s[20] = byte(h[6] >> 7) | ||||
| 	s[21] = byte(h[6] >> 15) | ||||
| 	s[22] = byte((h[6] >> 23) | (h[7] << 3)) | ||||
| 	s[23] = byte(h[7] >> 5) | ||||
| 	s[24] = byte(h[7] >> 13) | ||||
| 	s[25] = byte((h[7] >> 21) | (h[8] << 4)) | ||||
| 	s[26] = byte(h[8] >> 4) | ||||
| 	s[27] = byte(h[8] >> 12) | ||||
| 	s[28] = byte((h[8] >> 20) | (h[9] << 6)) | ||||
| 	s[29] = byte(h[9] >> 2) | ||||
| 	s[30] = byte(h[9] >> 10) | ||||
| 	s[31] = byte(h[9] >> 18) | ||||
| } | ||||
|  | ||||
| // feMul calculates h = f * g | ||||
| // Can overlap h with f or g. | ||||
| // | ||||
| // Preconditions: | ||||
| //    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | ||||
| //    |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | ||||
| // | ||||
| // Postconditions: | ||||
| //    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | ||||
| // | ||||
| // Notes on implementation strategy: | ||||
| // | ||||
| // Using schoolbook multiplication. | ||||
| // Karatsuba would save a little in some cost models. | ||||
| // | ||||
| // Most multiplications by 2 and 19 are 32-bit precomputations; | ||||
| // cheaper than 64-bit postcomputations. | ||||
| // | ||||
| // There is one remaining multiplication by 19 in the carry chain; | ||||
| // one *19 precomputation can be merged into this, | ||||
| // but the resulting data flow is considerably less clean. | ||||
| // | ||||
| // There are 12 carries below. | ||||
| // 10 of them are 2-way parallelizable and vectorizable. | ||||
| // Can get away with 11 carries, but then data flow is much deeper. | ||||
| // | ||||
| // With tighter constraints on inputs can squeeze carries into int32. | ||||
| func feMul(h, f, g *fieldElement) { | ||||
| 	f0 := f[0] | ||||
| 	f1 := f[1] | ||||
| 	f2 := f[2] | ||||
| 	f3 := f[3] | ||||
| 	f4 := f[4] | ||||
| 	f5 := f[5] | ||||
| 	f6 := f[6] | ||||
| 	f7 := f[7] | ||||
| 	f8 := f[8] | ||||
| 	f9 := f[9] | ||||
| 	g0 := g[0] | ||||
| 	g1 := g[1] | ||||
| 	g2 := g[2] | ||||
| 	g3 := g[3] | ||||
| 	g4 := g[4] | ||||
| 	g5 := g[5] | ||||
| 	g6 := g[6] | ||||
| 	g7 := g[7] | ||||
| 	g8 := g[8] | ||||
| 	g9 := g[9] | ||||
| 	g1_19 := 19 * g1 // 1.4*2^29 | ||||
| 	g2_19 := 19 * g2 // 1.4*2^30; still ok | ||||
| 	g3_19 := 19 * g3 | ||||
| 	g4_19 := 19 * g4 | ||||
| 	g5_19 := 19 * g5 | ||||
| 	g6_19 := 19 * g6 | ||||
| 	g7_19 := 19 * g7 | ||||
| 	g8_19 := 19 * g8 | ||||
| 	g9_19 := 19 * g9 | ||||
| 	f1_2 := 2 * f1 | ||||
| 	f3_2 := 2 * f3 | ||||
| 	f5_2 := 2 * f5 | ||||
| 	f7_2 := 2 * f7 | ||||
| 	f9_2 := 2 * f9 | ||||
| 	f0g0 := int64(f0) * int64(g0) | ||||
| 	f0g1 := int64(f0) * int64(g1) | ||||
| 	f0g2 := int64(f0) * int64(g2) | ||||
| 	f0g3 := int64(f0) * int64(g3) | ||||
| 	f0g4 := int64(f0) * int64(g4) | ||||
| 	f0g5 := int64(f0) * int64(g5) | ||||
| 	f0g6 := int64(f0) * int64(g6) | ||||
| 	f0g7 := int64(f0) * int64(g7) | ||||
| 	f0g8 := int64(f0) * int64(g8) | ||||
| 	f0g9 := int64(f0) * int64(g9) | ||||
| 	f1g0 := int64(f1) * int64(g0) | ||||
| 	f1g1_2 := int64(f1_2) * int64(g1) | ||||
| 	f1g2 := int64(f1) * int64(g2) | ||||
| 	f1g3_2 := int64(f1_2) * int64(g3) | ||||
| 	f1g4 := int64(f1) * int64(g4) | ||||
| 	f1g5_2 := int64(f1_2) * int64(g5) | ||||
| 	f1g6 := int64(f1) * int64(g6) | ||||
| 	f1g7_2 := int64(f1_2) * int64(g7) | ||||
| 	f1g8 := int64(f1) * int64(g8) | ||||
| 	f1g9_38 := int64(f1_2) * int64(g9_19) | ||||
| 	f2g0 := int64(f2) * int64(g0) | ||||
| 	f2g1 := int64(f2) * int64(g1) | ||||
| 	f2g2 := int64(f2) * int64(g2) | ||||
| 	f2g3 := int64(f2) * int64(g3) | ||||
| 	f2g4 := int64(f2) * int64(g4) | ||||
| 	f2g5 := int64(f2) * int64(g5) | ||||
| 	f2g6 := int64(f2) * int64(g6) | ||||
| 	f2g7 := int64(f2) * int64(g7) | ||||
| 	f2g8_19 := int64(f2) * int64(g8_19) | ||||
| 	f2g9_19 := int64(f2) * int64(g9_19) | ||||
| 	f3g0 := int64(f3) * int64(g0) | ||||
| 	f3g1_2 := int64(f3_2) * int64(g1) | ||||
| 	f3g2 := int64(f3) * int64(g2) | ||||
| 	f3g3_2 := int64(f3_2) * int64(g3) | ||||
| 	f3g4 := int64(f3) * int64(g4) | ||||
| 	f3g5_2 := int64(f3_2) * int64(g5) | ||||
| 	f3g6 := int64(f3) * int64(g6) | ||||
| 	f3g7_38 := int64(f3_2) * int64(g7_19) | ||||
| 	f3g8_19 := int64(f3) * int64(g8_19) | ||||
| 	f3g9_38 := int64(f3_2) * int64(g9_19) | ||||
| 	f4g0 := int64(f4) * int64(g0) | ||||
| 	f4g1 := int64(f4) * int64(g1) | ||||
| 	f4g2 := int64(f4) * int64(g2) | ||||
| 	f4g3 := int64(f4) * int64(g3) | ||||
| 	f4g4 := int64(f4) * int64(g4) | ||||
| 	f4g5 := int64(f4) * int64(g5) | ||||
| 	f4g6_19 := int64(f4) * int64(g6_19) | ||||
| 	f4g7_19 := int64(f4) * int64(g7_19) | ||||
| 	f4g8_19 := int64(f4) * int64(g8_19) | ||||
| 	f4g9_19 := int64(f4) * int64(g9_19) | ||||
| 	f5g0 := int64(f5) * int64(g0) | ||||
| 	f5g1_2 := int64(f5_2) * int64(g1) | ||||
| 	f5g2 := int64(f5) * int64(g2) | ||||
| 	f5g3_2 := int64(f5_2) * int64(g3) | ||||
| 	f5g4 := int64(f5) * int64(g4) | ||||
| 	f5g5_38 := int64(f5_2) * int64(g5_19) | ||||
| 	f5g6_19 := int64(f5) * int64(g6_19) | ||||
| 	f5g7_38 := int64(f5_2) * int64(g7_19) | ||||
| 	f5g8_19 := int64(f5) * int64(g8_19) | ||||
| 	f5g9_38 := int64(f5_2) * int64(g9_19) | ||||
| 	f6g0 := int64(f6) * int64(g0) | ||||
| 	f6g1 := int64(f6) * int64(g1) | ||||
| 	f6g2 := int64(f6) * int64(g2) | ||||
| 	f6g3 := int64(f6) * int64(g3) | ||||
| 	f6g4_19 := int64(f6) * int64(g4_19) | ||||
| 	f6g5_19 := int64(f6) * int64(g5_19) | ||||
| 	f6g6_19 := int64(f6) * int64(g6_19) | ||||
| 	f6g7_19 := int64(f6) * int64(g7_19) | ||||
| 	f6g8_19 := int64(f6) * int64(g8_19) | ||||
| 	f6g9_19 := int64(f6) * int64(g9_19) | ||||
| 	f7g0 := int64(f7) * int64(g0) | ||||
| 	f7g1_2 := int64(f7_2) * int64(g1) | ||||
| 	f7g2 := int64(f7) * int64(g2) | ||||
| 	f7g3_38 := int64(f7_2) * int64(g3_19) | ||||
| 	f7g4_19 := int64(f7) * int64(g4_19) | ||||
| 	f7g5_38 := int64(f7_2) * int64(g5_19) | ||||
| 	f7g6_19 := int64(f7) * int64(g6_19) | ||||
| 	f7g7_38 := int64(f7_2) * int64(g7_19) | ||||
| 	f7g8_19 := int64(f7) * int64(g8_19) | ||||
| 	f7g9_38 := int64(f7_2) * int64(g9_19) | ||||
| 	f8g0 := int64(f8) * int64(g0) | ||||
| 	f8g1 := int64(f8) * int64(g1) | ||||
| 	f8g2_19 := int64(f8) * int64(g2_19) | ||||
| 	f8g3_19 := int64(f8) * int64(g3_19) | ||||
| 	f8g4_19 := int64(f8) * int64(g4_19) | ||||
| 	f8g5_19 := int64(f8) * int64(g5_19) | ||||
| 	f8g6_19 := int64(f8) * int64(g6_19) | ||||
| 	f8g7_19 := int64(f8) * int64(g7_19) | ||||
| 	f8g8_19 := int64(f8) * int64(g8_19) | ||||
| 	f8g9_19 := int64(f8) * int64(g9_19) | ||||
| 	f9g0 := int64(f9) * int64(g0) | ||||
| 	f9g1_38 := int64(f9_2) * int64(g1_19) | ||||
| 	f9g2_19 := int64(f9) * int64(g2_19) | ||||
| 	f9g3_38 := int64(f9_2) * int64(g3_19) | ||||
| 	f9g4_19 := int64(f9) * int64(g4_19) | ||||
| 	f9g5_38 := int64(f9_2) * int64(g5_19) | ||||
| 	f9g6_19 := int64(f9) * int64(g6_19) | ||||
| 	f9g7_38 := int64(f9_2) * int64(g7_19) | ||||
| 	f9g8_19 := int64(f9) * int64(g8_19) | ||||
| 	f9g9_38 := int64(f9_2) * int64(g9_19) | ||||
| 	h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38 | ||||
| 	h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19 | ||||
| 	h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38 | ||||
| 	h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19 | ||||
| 	h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38 | ||||
| 	h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19 | ||||
| 	h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38 | ||||
| 	h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19 | ||||
| 	h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38 | ||||
| 	h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 | ||||
| 	var carry [10]int64 | ||||
|  | ||||
| 	// |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) | ||||
| 	//   i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 | ||||
| 	// |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) | ||||
| 	//   i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 | ||||
|  | ||||
| 	carry[0] = (h0 + (1 << 25)) >> 26 | ||||
| 	h1 += carry[0] | ||||
| 	h0 -= carry[0] << 26 | ||||
| 	carry[4] = (h4 + (1 << 25)) >> 26 | ||||
| 	h5 += carry[4] | ||||
| 	h4 -= carry[4] << 26 | ||||
| 	// |h0| <= 2^25 | ||||
| 	// |h4| <= 2^25 | ||||
| 	// |h1| <= 1.51*2^58 | ||||
| 	// |h5| <= 1.51*2^58 | ||||
|  | ||||
| 	carry[1] = (h1 + (1 << 24)) >> 25 | ||||
| 	h2 += carry[1] | ||||
| 	h1 -= carry[1] << 25 | ||||
| 	carry[5] = (h5 + (1 << 24)) >> 25 | ||||
| 	h6 += carry[5] | ||||
| 	h5 -= carry[5] << 25 | ||||
| 	// |h1| <= 2^24; from now on fits into int32 | ||||
| 	// |h5| <= 2^24; from now on fits into int32 | ||||
| 	// |h2| <= 1.21*2^59 | ||||
| 	// |h6| <= 1.21*2^59 | ||||
|  | ||||
| 	carry[2] = (h2 + (1 << 25)) >> 26 | ||||
| 	h3 += carry[2] | ||||
| 	h2 -= carry[2] << 26 | ||||
| 	carry[6] = (h6 + (1 << 25)) >> 26 | ||||
| 	h7 += carry[6] | ||||
| 	h6 -= carry[6] << 26 | ||||
| 	// |h2| <= 2^25; from now on fits into int32 unchanged | ||||
| 	// |h6| <= 2^25; from now on fits into int32 unchanged | ||||
| 	// |h3| <= 1.51*2^58 | ||||
| 	// |h7| <= 1.51*2^58 | ||||
|  | ||||
| 	carry[3] = (h3 + (1 << 24)) >> 25 | ||||
| 	h4 += carry[3] | ||||
| 	h3 -= carry[3] << 25 | ||||
| 	carry[7] = (h7 + (1 << 24)) >> 25 | ||||
| 	h8 += carry[7] | ||||
| 	h7 -= carry[7] << 25 | ||||
| 	// |h3| <= 2^24; from now on fits into int32 unchanged | ||||
| 	// |h7| <= 2^24; from now on fits into int32 unchanged | ||||
| 	// |h4| <= 1.52*2^33 | ||||
| 	// |h8| <= 1.52*2^33 | ||||
|  | ||||
| 	carry[4] = (h4 + (1 << 25)) >> 26 | ||||
| 	h5 += carry[4] | ||||
| 	h4 -= carry[4] << 26 | ||||
| 	carry[8] = (h8 + (1 << 25)) >> 26 | ||||
| 	h9 += carry[8] | ||||
| 	h8 -= carry[8] << 26 | ||||
| 	// |h4| <= 2^25; from now on fits into int32 unchanged | ||||
| 	// |h8| <= 2^25; from now on fits into int32 unchanged | ||||
| 	// |h5| <= 1.01*2^24 | ||||
| 	// |h9| <= 1.51*2^58 | ||||
|  | ||||
| 	carry[9] = (h9 + (1 << 24)) >> 25 | ||||
| 	h0 += carry[9] * 19 | ||||
| 	h9 -= carry[9] << 25 | ||||
| 	// |h9| <= 2^24; from now on fits into int32 unchanged | ||||
| 	// |h0| <= 1.8*2^37 | ||||
|  | ||||
| 	carry[0] = (h0 + (1 << 25)) >> 26 | ||||
| 	h1 += carry[0] | ||||
| 	h0 -= carry[0] << 26 | ||||
| 	// |h0| <= 2^25; from now on fits into int32 unchanged | ||||
| 	// |h1| <= 1.01*2^24 | ||||
|  | ||||
| 	h[0] = int32(h0) | ||||
| 	h[1] = int32(h1) | ||||
| 	h[2] = int32(h2) | ||||
| 	h[3] = int32(h3) | ||||
| 	h[4] = int32(h4) | ||||
| 	h[5] = int32(h5) | ||||
| 	h[6] = int32(h6) | ||||
| 	h[7] = int32(h7) | ||||
| 	h[8] = int32(h8) | ||||
| 	h[9] = int32(h9) | ||||
| } | ||||
|  | ||||
| // feSquare calculates h = f*f. Can overlap h with f. | ||||
| // | ||||
| // Preconditions: | ||||
| //    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | ||||
| // | ||||
| // Postconditions: | ||||
| //    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | ||||
| func feSquare(h, f *fieldElement) { | ||||
| 	f0 := f[0] | ||||
| 	f1 := f[1] | ||||
| 	f2 := f[2] | ||||
| 	f3 := f[3] | ||||
| 	f4 := f[4] | ||||
| 	f5 := f[5] | ||||
| 	f6 := f[6] | ||||
| 	f7 := f[7] | ||||
| 	f8 := f[8] | ||||
| 	f9 := f[9] | ||||
| 	f0_2 := 2 * f0 | ||||
| 	f1_2 := 2 * f1 | ||||
| 	f2_2 := 2 * f2 | ||||
| 	f3_2 := 2 * f3 | ||||
| 	f4_2 := 2 * f4 | ||||
| 	f5_2 := 2 * f5 | ||||
| 	f6_2 := 2 * f6 | ||||
| 	f7_2 := 2 * f7 | ||||
| 	f5_38 := 38 * f5 // 1.31*2^30 | ||||
| 	f6_19 := 19 * f6 // 1.31*2^30 | ||||
| 	f7_38 := 38 * f7 // 1.31*2^30 | ||||
| 	f8_19 := 19 * f8 // 1.31*2^30 | ||||
| 	f9_38 := 38 * f9 // 1.31*2^30 | ||||
| 	f0f0 := int64(f0) * int64(f0) | ||||
| 	f0f1_2 := int64(f0_2) * int64(f1) | ||||
| 	f0f2_2 := int64(f0_2) * int64(f2) | ||||
| 	f0f3_2 := int64(f0_2) * int64(f3) | ||||
| 	f0f4_2 := int64(f0_2) * int64(f4) | ||||
| 	f0f5_2 := int64(f0_2) * int64(f5) | ||||
| 	f0f6_2 := int64(f0_2) * int64(f6) | ||||
| 	f0f7_2 := int64(f0_2) * int64(f7) | ||||
| 	f0f8_2 := int64(f0_2) * int64(f8) | ||||
| 	f0f9_2 := int64(f0_2) * int64(f9) | ||||
| 	f1f1_2 := int64(f1_2) * int64(f1) | ||||
| 	f1f2_2 := int64(f1_2) * int64(f2) | ||||
| 	f1f3_4 := int64(f1_2) * int64(f3_2) | ||||
| 	f1f4_2 := int64(f1_2) * int64(f4) | ||||
| 	f1f5_4 := int64(f1_2) * int64(f5_2) | ||||
| 	f1f6_2 := int64(f1_2) * int64(f6) | ||||
| 	f1f7_4 := int64(f1_2) * int64(f7_2) | ||||
| 	f1f8_2 := int64(f1_2) * int64(f8) | ||||
| 	f1f9_76 := int64(f1_2) * int64(f9_38) | ||||
| 	f2f2 := int64(f2) * int64(f2) | ||||
| 	f2f3_2 := int64(f2_2) * int64(f3) | ||||
| 	f2f4_2 := int64(f2_2) * int64(f4) | ||||
| 	f2f5_2 := int64(f2_2) * int64(f5) | ||||
| 	f2f6_2 := int64(f2_2) * int64(f6) | ||||
| 	f2f7_2 := int64(f2_2) * int64(f7) | ||||
| 	f2f8_38 := int64(f2_2) * int64(f8_19) | ||||
| 	f2f9_38 := int64(f2) * int64(f9_38) | ||||
| 	f3f3_2 := int64(f3_2) * int64(f3) | ||||
| 	f3f4_2 := int64(f3_2) * int64(f4) | ||||
| 	f3f5_4 := int64(f3_2) * int64(f5_2) | ||||
| 	f3f6_2 := int64(f3_2) * int64(f6) | ||||
| 	f3f7_76 := int64(f3_2) * int64(f7_38) | ||||
| 	f3f8_38 := int64(f3_2) * int64(f8_19) | ||||
| 	f3f9_76 := int64(f3_2) * int64(f9_38) | ||||
| 	f4f4 := int64(f4) * int64(f4) | ||||
| 	f4f5_2 := int64(f4_2) * int64(f5) | ||||
| 	f4f6_38 := int64(f4_2) * int64(f6_19) | ||||
| 	f4f7_38 := int64(f4) * int64(f7_38) | ||||
| 	f4f8_38 := int64(f4_2) * int64(f8_19) | ||||
| 	f4f9_38 := int64(f4) * int64(f9_38) | ||||
| 	f5f5_38 := int64(f5) * int64(f5_38) | ||||
| 	f5f6_38 := int64(f5_2) * int64(f6_19) | ||||
| 	f5f7_76 := int64(f5_2) * int64(f7_38) | ||||
| 	f5f8_38 := int64(f5_2) * int64(f8_19) | ||||
| 	f5f9_76 := int64(f5_2) * int64(f9_38) | ||||
| 	f6f6_19 := int64(f6) * int64(f6_19) | ||||
| 	f6f7_38 := int64(f6) * int64(f7_38) | ||||
| 	f6f8_38 := int64(f6_2) * int64(f8_19) | ||||
| 	f6f9_38 := int64(f6) * int64(f9_38) | ||||
| 	f7f7_38 := int64(f7) * int64(f7_38) | ||||
| 	f7f8_38 := int64(f7_2) * int64(f8_19) | ||||
| 	f7f9_76 := int64(f7_2) * int64(f9_38) | ||||
| 	f8f8_19 := int64(f8) * int64(f8_19) | ||||
| 	f8f9_38 := int64(f8) * int64(f9_38) | ||||
| 	f9f9_38 := int64(f9) * int64(f9_38) | ||||
| 	h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38 | ||||
| 	h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38 | ||||
| 	h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19 | ||||
| 	h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38 | ||||
| 	h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38 | ||||
| 	h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38 | ||||
| 	h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19 | ||||
| 	h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38 | ||||
| 	h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38 | ||||
| 	h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2 | ||||
| 	var carry [10]int64 | ||||
|  | ||||
| 	carry[0] = (h0 + (1 << 25)) >> 26 | ||||
| 	h1 += carry[0] | ||||
| 	h0 -= carry[0] << 26 | ||||
| 	carry[4] = (h4 + (1 << 25)) >> 26 | ||||
| 	h5 += carry[4] | ||||
| 	h4 -= carry[4] << 26 | ||||
|  | ||||
| 	carry[1] = (h1 + (1 << 24)) >> 25 | ||||
| 	h2 += carry[1] | ||||
| 	h1 -= carry[1] << 25 | ||||
| 	carry[5] = (h5 + (1 << 24)) >> 25 | ||||
| 	h6 += carry[5] | ||||
| 	h5 -= carry[5] << 25 | ||||
|  | ||||
| 	carry[2] = (h2 + (1 << 25)) >> 26 | ||||
| 	h3 += carry[2] | ||||
| 	h2 -= carry[2] << 26 | ||||
| 	carry[6] = (h6 + (1 << 25)) >> 26 | ||||
| 	h7 += carry[6] | ||||
| 	h6 -= carry[6] << 26 | ||||
|  | ||||
| 	carry[3] = (h3 + (1 << 24)) >> 25 | ||||
| 	h4 += carry[3] | ||||
| 	h3 -= carry[3] << 25 | ||||
| 	carry[7] = (h7 + (1 << 24)) >> 25 | ||||
| 	h8 += carry[7] | ||||
| 	h7 -= carry[7] << 25 | ||||
|  | ||||
| 	carry[4] = (h4 + (1 << 25)) >> 26 | ||||
| 	h5 += carry[4] | ||||
| 	h4 -= carry[4] << 26 | ||||
| 	carry[8] = (h8 + (1 << 25)) >> 26 | ||||
| 	h9 += carry[8] | ||||
| 	h8 -= carry[8] << 26 | ||||
|  | ||||
| 	carry[9] = (h9 + (1 << 24)) >> 25 | ||||
| 	h0 += carry[9] * 19 | ||||
| 	h9 -= carry[9] << 25 | ||||
|  | ||||
| 	carry[0] = (h0 + (1 << 25)) >> 26 | ||||
| 	h1 += carry[0] | ||||
| 	h0 -= carry[0] << 26 | ||||
|  | ||||
| 	h[0] = int32(h0) | ||||
| 	h[1] = int32(h1) | ||||
| 	h[2] = int32(h2) | ||||
| 	h[3] = int32(h3) | ||||
| 	h[4] = int32(h4) | ||||
| 	h[5] = int32(h5) | ||||
| 	h[6] = int32(h6) | ||||
| 	h[7] = int32(h7) | ||||
| 	h[8] = int32(h8) | ||||
| 	h[9] = int32(h9) | ||||
| } | ||||
|  | ||||
| // feMul121666 calculates h = f * 121666. Can overlap h with f. | ||||
| // | ||||
| // Preconditions: | ||||
| //    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | ||||
| // | ||||
| // Postconditions: | ||||
| //    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | ||||
| func feMul121666(h, f *fieldElement) { | ||||
| 	h0 := int64(f[0]) * 121666 | ||||
| 	h1 := int64(f[1]) * 121666 | ||||
| 	h2 := int64(f[2]) * 121666 | ||||
| 	h3 := int64(f[3]) * 121666 | ||||
| 	h4 := int64(f[4]) * 121666 | ||||
| 	h5 := int64(f[5]) * 121666 | ||||
| 	h6 := int64(f[6]) * 121666 | ||||
| 	h7 := int64(f[7]) * 121666 | ||||
| 	h8 := int64(f[8]) * 121666 | ||||
| 	h9 := int64(f[9]) * 121666 | ||||
| 	var carry [10]int64 | ||||
|  | ||||
| 	carry[9] = (h9 + (1 << 24)) >> 25 | ||||
| 	h0 += carry[9] * 19 | ||||
| 	h9 -= carry[9] << 25 | ||||
| 	carry[1] = (h1 + (1 << 24)) >> 25 | ||||
| 	h2 += carry[1] | ||||
| 	h1 -= carry[1] << 25 | ||||
| 	carry[3] = (h3 + (1 << 24)) >> 25 | ||||
| 	h4 += carry[3] | ||||
| 	h3 -= carry[3] << 25 | ||||
| 	carry[5] = (h5 + (1 << 24)) >> 25 | ||||
| 	h6 += carry[5] | ||||
| 	h5 -= carry[5] << 25 | ||||
| 	carry[7] = (h7 + (1 << 24)) >> 25 | ||||
| 	h8 += carry[7] | ||||
| 	h7 -= carry[7] << 25 | ||||
|  | ||||
| 	carry[0] = (h0 + (1 << 25)) >> 26 | ||||
| 	h1 += carry[0] | ||||
| 	h0 -= carry[0] << 26 | ||||
| 	carry[2] = (h2 + (1 << 25)) >> 26 | ||||
| 	h3 += carry[2] | ||||
| 	h2 -= carry[2] << 26 | ||||
| 	carry[4] = (h4 + (1 << 25)) >> 26 | ||||
| 	h5 += carry[4] | ||||
| 	h4 -= carry[4] << 26 | ||||
| 	carry[6] = (h6 + (1 << 25)) >> 26 | ||||
| 	h7 += carry[6] | ||||
| 	h6 -= carry[6] << 26 | ||||
| 	carry[8] = (h8 + (1 << 25)) >> 26 | ||||
| 	h9 += carry[8] | ||||
| 	h8 -= carry[8] << 26 | ||||
|  | ||||
| 	h[0] = int32(h0) | ||||
| 	h[1] = int32(h1) | ||||
| 	h[2] = int32(h2) | ||||
| 	h[3] = int32(h3) | ||||
| 	h[4] = int32(h4) | ||||
| 	h[5] = int32(h5) | ||||
| 	h[6] = int32(h6) | ||||
| 	h[7] = int32(h7) | ||||
| 	h[8] = int32(h8) | ||||
| 	h[9] = int32(h9) | ||||
| } | ||||
|  | ||||
| // feInvert sets out = z^-1. | ||||
| func feInvert(out, z *fieldElement) { | ||||
| 	var t0, t1, t2, t3 fieldElement | ||||
| 	var i int | ||||
|  | ||||
| 	feSquare(&t0, z) | ||||
| 	for i = 1; i < 1; i++ { | ||||
| 		feSquare(&t0, &t0) | ||||
| 	} | ||||
| 	feSquare(&t1, &t0) | ||||
| 	for i = 1; i < 2; i++ { | ||||
| 		feSquare(&t1, &t1) | ||||
| 	} | ||||
| 	feMul(&t1, z, &t1) | ||||
| 	feMul(&t0, &t0, &t1) | ||||
| 	feSquare(&t2, &t0) | ||||
| 	for i = 1; i < 1; i++ { | ||||
| 		feSquare(&t2, &t2) | ||||
| 	} | ||||
| 	feMul(&t1, &t1, &t2) | ||||
| 	feSquare(&t2, &t1) | ||||
| 	for i = 1; i < 5; i++ { | ||||
| 		feSquare(&t2, &t2) | ||||
| 	} | ||||
| 	feMul(&t1, &t2, &t1) | ||||
| 	feSquare(&t2, &t1) | ||||
| 	for i = 1; i < 10; i++ { | ||||
| 		feSquare(&t2, &t2) | ||||
| 	} | ||||
| 	feMul(&t2, &t2, &t1) | ||||
| 	feSquare(&t3, &t2) | ||||
| 	for i = 1; i < 20; i++ { | ||||
| 		feSquare(&t3, &t3) | ||||
| 	} | ||||
| 	feMul(&t2, &t3, &t2) | ||||
| 	feSquare(&t2, &t2) | ||||
| 	for i = 1; i < 10; i++ { | ||||
| 		feSquare(&t2, &t2) | ||||
| 	} | ||||
| 	feMul(&t1, &t2, &t1) | ||||
| 	feSquare(&t2, &t1) | ||||
| 	for i = 1; i < 50; i++ { | ||||
| 		feSquare(&t2, &t2) | ||||
| 	} | ||||
| 	feMul(&t2, &t2, &t1) | ||||
| 	feSquare(&t3, &t2) | ||||
| 	for i = 1; i < 100; i++ { | ||||
| 		feSquare(&t3, &t3) | ||||
| 	} | ||||
| 	feMul(&t2, &t3, &t2) | ||||
| 	feSquare(&t2, &t2) | ||||
| 	for i = 1; i < 50; i++ { | ||||
| 		feSquare(&t2, &t2) | ||||
| 	} | ||||
| 	feMul(&t1, &t2, &t1) | ||||
| 	feSquare(&t1, &t1) | ||||
| 	for i = 1; i < 5; i++ { | ||||
| 		feSquare(&t1, &t1) | ||||
| 	} | ||||
| 	feMul(out, &t1, &t0) | ||||
| } | ||||
|  | ||||
| func scalarMult(out, in, base *[32]byte) { | ||||
| 	var e [32]byte | ||||
|  | ||||
| 	copy(e[:], in[:]) | ||||
| 	e[0] &= 248 | ||||
| 	e[31] &= 127 | ||||
| 	e[31] |= 64 | ||||
|  | ||||
| 	var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement | ||||
| 	feFromBytes(&x1, base) | ||||
| 	feOne(&x2) | ||||
| 	feCopy(&x3, &x1) | ||||
| 	feOne(&z3) | ||||
|  | ||||
| 	swap := int32(0) | ||||
| 	for pos := 254; pos >= 0; pos-- { | ||||
| 		b := e[pos/8] >> uint(pos&7) | ||||
| 		b &= 1 | ||||
| 		swap ^= int32(b) | ||||
| 		feCSwap(&x2, &x3, swap) | ||||
| 		feCSwap(&z2, &z3, swap) | ||||
| 		swap = int32(b) | ||||
|  | ||||
| 		feSub(&tmp0, &x3, &z3) | ||||
| 		feSub(&tmp1, &x2, &z2) | ||||
| 		feAdd(&x2, &x2, &z2) | ||||
| 		feAdd(&z2, &x3, &z3) | ||||
| 		feMul(&z3, &tmp0, &x2) | ||||
| 		feMul(&z2, &z2, &tmp1) | ||||
| 		feSquare(&tmp0, &tmp1) | ||||
| 		feSquare(&tmp1, &x2) | ||||
| 		feAdd(&x3, &z3, &z2) | ||||
| 		feSub(&z2, &z3, &z2) | ||||
| 		feMul(&x2, &tmp1, &tmp0) | ||||
| 		feSub(&tmp1, &tmp1, &tmp0) | ||||
| 		feSquare(&z2, &z2) | ||||
| 		feMul121666(&z3, &tmp1) | ||||
| 		feSquare(&x3, &x3) | ||||
| 		feAdd(&tmp0, &tmp0, &z3) | ||||
| 		feMul(&z3, &x1, &z2) | ||||
| 		feMul(&z2, &tmp1, &tmp0) | ||||
| 	} | ||||
|  | ||||
| 	feCSwap(&x2, &x3, swap) | ||||
| 	feCSwap(&z2, &z3, swap) | ||||
|  | ||||
| 	feInvert(&z2, &z2) | ||||
| 	feMul(&x2, &x2, &z2) | ||||
| 	feToBytes(out, &x2) | ||||
| } | ||||
							
								
								
									
										23
									
								
								vendor/golang.org/x/crypto/curve25519/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/golang.org/x/crypto/curve25519/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // Copyright 2012 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 curve25519 provides an implementation of scalar multiplication on | ||||
| // the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html | ||||
| package curve25519 // import "golang.org/x/crypto/curve25519" | ||||
|  | ||||
| // basePoint is the x coordinate of the generator of the curve. | ||||
| var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} | ||||
|  | ||||
| // ScalarMult sets dst to the product in*base where dst and base are the x | ||||
| // coordinates of group points and all values are in little-endian form. | ||||
| func ScalarMult(dst, in, base *[32]byte) { | ||||
| 	scalarMult(dst, in, base) | ||||
| } | ||||
|  | ||||
| // ScalarBaseMult sets dst to the product in*base where dst and base are the x | ||||
| // coordinates of group points, base is the standard generator and all values | ||||
| // are in little-endian form. | ||||
| func ScalarBaseMult(dst, in *[32]byte) { | ||||
| 	ScalarMult(dst, in, &basePoint) | ||||
| } | ||||
							
								
								
									
										73
									
								
								vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| // Copyright 2012 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. | ||||
|  | ||||
| // This code was translated into a form compatible with 6a from the public | ||||
| // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html | ||||
|  | ||||
| // +build amd64,!gccgo,!appengine | ||||
|  | ||||
| #include "const_amd64.h" | ||||
|  | ||||
| // func freeze(inout *[5]uint64) | ||||
| TEXT ·freeze(SB),7,$0-8 | ||||
| 	MOVQ inout+0(FP), DI | ||||
|  | ||||
| 	MOVQ 0(DI),SI | ||||
| 	MOVQ 8(DI),DX | ||||
| 	MOVQ 16(DI),CX | ||||
| 	MOVQ 24(DI),R8 | ||||
| 	MOVQ 32(DI),R9 | ||||
| 	MOVQ $REDMASK51,AX | ||||
| 	MOVQ AX,R10 | ||||
| 	SUBQ $18,R10 | ||||
| 	MOVQ $3,R11 | ||||
| REDUCELOOP: | ||||
| 	MOVQ SI,R12 | ||||
| 	SHRQ $51,R12 | ||||
| 	ANDQ AX,SI | ||||
| 	ADDQ R12,DX | ||||
| 	MOVQ DX,R12 | ||||
| 	SHRQ $51,R12 | ||||
| 	ANDQ AX,DX | ||||
| 	ADDQ R12,CX | ||||
| 	MOVQ CX,R12 | ||||
| 	SHRQ $51,R12 | ||||
| 	ANDQ AX,CX | ||||
| 	ADDQ R12,R8 | ||||
| 	MOVQ R8,R12 | ||||
| 	SHRQ $51,R12 | ||||
| 	ANDQ AX,R8 | ||||
| 	ADDQ R12,R9 | ||||
| 	MOVQ R9,R12 | ||||
| 	SHRQ $51,R12 | ||||
| 	ANDQ AX,R9 | ||||
| 	IMUL3Q $19,R12,R12 | ||||
| 	ADDQ R12,SI | ||||
| 	SUBQ $1,R11 | ||||
| 	JA REDUCELOOP | ||||
| 	MOVQ $1,R12 | ||||
| 	CMPQ R10,SI | ||||
| 	CMOVQLT R11,R12 | ||||
| 	CMPQ AX,DX | ||||
| 	CMOVQNE R11,R12 | ||||
| 	CMPQ AX,CX | ||||
| 	CMOVQNE R11,R12 | ||||
| 	CMPQ AX,R8 | ||||
| 	CMOVQNE R11,R12 | ||||
| 	CMPQ AX,R9 | ||||
| 	CMOVQNE R11,R12 | ||||
| 	NEGQ R12 | ||||
| 	ANDQ R12,AX | ||||
| 	ANDQ R12,R10 | ||||
| 	SUBQ R10,SI | ||||
| 	SUBQ AX,DX | ||||
| 	SUBQ AX,CX | ||||
| 	SUBQ AX,R8 | ||||
| 	SUBQ AX,R9 | ||||
| 	MOVQ SI,0(DI) | ||||
| 	MOVQ DX,8(DI) | ||||
| 	MOVQ CX,16(DI) | ||||
| 	MOVQ R8,24(DI) | ||||
| 	MOVQ R9,32(DI) | ||||
| 	RET | ||||
							
								
								
									
										1377
									
								
								vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1377
									
								
								vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										240
									
								
								vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,240 @@ | ||||
| // Copyright 2012 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 | ||||
|  | ||||
| package curve25519 | ||||
|  | ||||
| // These functions are implemented in the .s files. The names of the functions | ||||
| // in the rest of the file are also taken from the SUPERCOP sources to help | ||||
| // people following along. | ||||
|  | ||||
| //go:noescape | ||||
|  | ||||
| func cswap(inout *[5]uint64, v uint64) | ||||
|  | ||||
| //go:noescape | ||||
|  | ||||
| func ladderstep(inout *[5][5]uint64) | ||||
|  | ||||
| //go:noescape | ||||
|  | ||||
| func freeze(inout *[5]uint64) | ||||
|  | ||||
| //go:noescape | ||||
|  | ||||
| func mul(dest, a, b *[5]uint64) | ||||
|  | ||||
| //go:noescape | ||||
|  | ||||
| func square(out, in *[5]uint64) | ||||
|  | ||||
| // mladder uses a Montgomery ladder to calculate (xr/zr) *= s. | ||||
| func mladder(xr, zr *[5]uint64, s *[32]byte) { | ||||
| 	var work [5][5]uint64 | ||||
|  | ||||
| 	work[0] = *xr | ||||
| 	setint(&work[1], 1) | ||||
| 	setint(&work[2], 0) | ||||
| 	work[3] = *xr | ||||
| 	setint(&work[4], 1) | ||||
|  | ||||
| 	j := uint(6) | ||||
| 	var prevbit byte | ||||
|  | ||||
| 	for i := 31; i >= 0; i-- { | ||||
| 		for j < 8 { | ||||
| 			bit := ((*s)[i] >> j) & 1 | ||||
| 			swap := bit ^ prevbit | ||||
| 			prevbit = bit | ||||
| 			cswap(&work[1], uint64(swap)) | ||||
| 			ladderstep(&work) | ||||
| 			j-- | ||||
| 		} | ||||
| 		j = 7 | ||||
| 	} | ||||
|  | ||||
| 	*xr = work[1] | ||||
| 	*zr = work[2] | ||||
| } | ||||
|  | ||||
| func scalarMult(out, in, base *[32]byte) { | ||||
| 	var e [32]byte | ||||
| 	copy(e[:], (*in)[:]) | ||||
| 	e[0] &= 248 | ||||
| 	e[31] &= 127 | ||||
| 	e[31] |= 64 | ||||
|  | ||||
| 	var t, z [5]uint64 | ||||
| 	unpack(&t, base) | ||||
| 	mladder(&t, &z, &e) | ||||
| 	invert(&z, &z) | ||||
| 	mul(&t, &t, &z) | ||||
| 	pack(out, &t) | ||||
| } | ||||
|  | ||||
| func setint(r *[5]uint64, v uint64) { | ||||
| 	r[0] = v | ||||
| 	r[1] = 0 | ||||
| 	r[2] = 0 | ||||
| 	r[3] = 0 | ||||
| 	r[4] = 0 | ||||
| } | ||||
|  | ||||
| // unpack sets r = x where r consists of 5, 51-bit limbs in little-endian | ||||
| // order. | ||||
| func unpack(r *[5]uint64, x *[32]byte) { | ||||
| 	r[0] = uint64(x[0]) | | ||||
| 		uint64(x[1])<<8 | | ||||
| 		uint64(x[2])<<16 | | ||||
| 		uint64(x[3])<<24 | | ||||
| 		uint64(x[4])<<32 | | ||||
| 		uint64(x[5])<<40 | | ||||
| 		uint64(x[6]&7)<<48 | ||||
|  | ||||
| 	r[1] = uint64(x[6])>>3 | | ||||
| 		uint64(x[7])<<5 | | ||||
| 		uint64(x[8])<<13 | | ||||
| 		uint64(x[9])<<21 | | ||||
| 		uint64(x[10])<<29 | | ||||
| 		uint64(x[11])<<37 | | ||||
| 		uint64(x[12]&63)<<45 | ||||
|  | ||||
| 	r[2] = uint64(x[12])>>6 | | ||||
| 		uint64(x[13])<<2 | | ||||
| 		uint64(x[14])<<10 | | ||||
| 		uint64(x[15])<<18 | | ||||
| 		uint64(x[16])<<26 | | ||||
| 		uint64(x[17])<<34 | | ||||
| 		uint64(x[18])<<42 | | ||||
| 		uint64(x[19]&1)<<50 | ||||
|  | ||||
| 	r[3] = uint64(x[19])>>1 | | ||||
| 		uint64(x[20])<<7 | | ||||
| 		uint64(x[21])<<15 | | ||||
| 		uint64(x[22])<<23 | | ||||
| 		uint64(x[23])<<31 | | ||||
| 		uint64(x[24])<<39 | | ||||
| 		uint64(x[25]&15)<<47 | ||||
|  | ||||
| 	r[4] = uint64(x[25])>>4 | | ||||
| 		uint64(x[26])<<4 | | ||||
| 		uint64(x[27])<<12 | | ||||
| 		uint64(x[28])<<20 | | ||||
| 		uint64(x[29])<<28 | | ||||
| 		uint64(x[30])<<36 | | ||||
| 		uint64(x[31]&127)<<44 | ||||
| } | ||||
|  | ||||
| // pack sets out = x where out is the usual, little-endian form of the 5, | ||||
| // 51-bit limbs in x. | ||||
| func pack(out *[32]byte, x *[5]uint64) { | ||||
| 	t := *x | ||||
| 	freeze(&t) | ||||
|  | ||||
| 	out[0] = byte(t[0]) | ||||
| 	out[1] = byte(t[0] >> 8) | ||||
| 	out[2] = byte(t[0] >> 16) | ||||
| 	out[3] = byte(t[0] >> 24) | ||||
| 	out[4] = byte(t[0] >> 32) | ||||
| 	out[5] = byte(t[0] >> 40) | ||||
| 	out[6] = byte(t[0] >> 48) | ||||
|  | ||||
| 	out[6] ^= byte(t[1]<<3) & 0xf8 | ||||
| 	out[7] = byte(t[1] >> 5) | ||||
| 	out[8] = byte(t[1] >> 13) | ||||
| 	out[9] = byte(t[1] >> 21) | ||||
| 	out[10] = byte(t[1] >> 29) | ||||
| 	out[11] = byte(t[1] >> 37) | ||||
| 	out[12] = byte(t[1] >> 45) | ||||
|  | ||||
| 	out[12] ^= byte(t[2]<<6) & 0xc0 | ||||
| 	out[13] = byte(t[2] >> 2) | ||||
| 	out[14] = byte(t[2] >> 10) | ||||
| 	out[15] = byte(t[2] >> 18) | ||||
| 	out[16] = byte(t[2] >> 26) | ||||
| 	out[17] = byte(t[2] >> 34) | ||||
| 	out[18] = byte(t[2] >> 42) | ||||
| 	out[19] = byte(t[2] >> 50) | ||||
|  | ||||
| 	out[19] ^= byte(t[3]<<1) & 0xfe | ||||
| 	out[20] = byte(t[3] >> 7) | ||||
| 	out[21] = byte(t[3] >> 15) | ||||
| 	out[22] = byte(t[3] >> 23) | ||||
| 	out[23] = byte(t[3] >> 31) | ||||
| 	out[24] = byte(t[3] >> 39) | ||||
| 	out[25] = byte(t[3] >> 47) | ||||
|  | ||||
| 	out[25] ^= byte(t[4]<<4) & 0xf0 | ||||
| 	out[26] = byte(t[4] >> 4) | ||||
| 	out[27] = byte(t[4] >> 12) | ||||
| 	out[28] = byte(t[4] >> 20) | ||||
| 	out[29] = byte(t[4] >> 28) | ||||
| 	out[30] = byte(t[4] >> 36) | ||||
| 	out[31] = byte(t[4] >> 44) | ||||
| } | ||||
|  | ||||
| // invert calculates r = x^-1 mod p using Fermat's little theorem. | ||||
| func invert(r *[5]uint64, x *[5]uint64) { | ||||
| 	var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 | ||||
|  | ||||
| 	square(&z2, x)        /* 2 */ | ||||
| 	square(&t, &z2)       /* 4 */ | ||||
| 	square(&t, &t)        /* 8 */ | ||||
| 	mul(&z9, &t, x)       /* 9 */ | ||||
| 	mul(&z11, &z9, &z2)   /* 11 */ | ||||
| 	square(&t, &z11)      /* 22 */ | ||||
| 	mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ | ||||
|  | ||||
| 	square(&t, &z2_5_0)      /* 2^6 - 2^1 */ | ||||
| 	for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ | ||||
| 		square(&t, &t) | ||||
| 	} | ||||
| 	mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ | ||||
|  | ||||
| 	square(&t, &z2_10_0)      /* 2^11 - 2^1 */ | ||||
| 	for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ | ||||
| 		square(&t, &t) | ||||
| 	} | ||||
| 	mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ | ||||
|  | ||||
| 	square(&t, &z2_20_0)      /* 2^21 - 2^1 */ | ||||
| 	for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ | ||||
| 		square(&t, &t) | ||||
| 	} | ||||
| 	mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ | ||||
|  | ||||
| 	square(&t, &t)            /* 2^41 - 2^1 */ | ||||
| 	for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ | ||||
| 		square(&t, &t) | ||||
| 	} | ||||
| 	mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ | ||||
|  | ||||
| 	square(&t, &z2_50_0)      /* 2^51 - 2^1 */ | ||||
| 	for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ | ||||
| 		square(&t, &t) | ||||
| 	} | ||||
| 	mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ | ||||
|  | ||||
| 	square(&t, &z2_100_0)      /* 2^101 - 2^1 */ | ||||
| 	for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ | ||||
| 		square(&t, &t) | ||||
| 	} | ||||
| 	mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ | ||||
|  | ||||
| 	square(&t, &t)            /* 2^201 - 2^1 */ | ||||
| 	for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ | ||||
| 		square(&t, &t) | ||||
| 	} | ||||
| 	mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ | ||||
|  | ||||
| 	square(&t, &t) /* 2^251 - 2^1 */ | ||||
| 	square(&t, &t) /* 2^252 - 2^2 */ | ||||
| 	square(&t, &t) /* 2^253 - 2^3 */ | ||||
|  | ||||
| 	square(&t, &t) /* 2^254 - 2^4 */ | ||||
|  | ||||
| 	square(&t, &t)   /* 2^255 - 2^5 */ | ||||
| 	mul(r, &t, &z11) /* 2^255 - 21 */ | ||||
| } | ||||
							
								
								
									
										169
									
								
								vendor/golang.org/x/crypto/curve25519/mul_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								vendor/golang.org/x/crypto/curve25519/mul_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| // Copyright 2012 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. | ||||
|  | ||||
| // This code was translated into a form compatible with 6a from the public | ||||
| // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html | ||||
|  | ||||
| // +build amd64,!gccgo,!appengine | ||||
|  | ||||
| #include "const_amd64.h" | ||||
|  | ||||
| // func mul(dest, a, b *[5]uint64) | ||||
| TEXT ·mul(SB),0,$16-24 | ||||
| 	MOVQ dest+0(FP), DI | ||||
| 	MOVQ a+8(FP), SI | ||||
| 	MOVQ b+16(FP), DX | ||||
|  | ||||
| 	MOVQ DX,CX | ||||
| 	MOVQ 24(SI),DX | ||||
| 	IMUL3Q $19,DX,AX | ||||
| 	MOVQ AX,0(SP) | ||||
| 	MULQ 16(CX) | ||||
| 	MOVQ AX,R8 | ||||
| 	MOVQ DX,R9 | ||||
| 	MOVQ 32(SI),DX | ||||
| 	IMUL3Q $19,DX,AX | ||||
| 	MOVQ AX,8(SP) | ||||
| 	MULQ 8(CX) | ||||
| 	ADDQ AX,R8 | ||||
| 	ADCQ DX,R9 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	MULQ 0(CX) | ||||
| 	ADDQ AX,R8 | ||||
| 	ADCQ DX,R9 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	MULQ 8(CX) | ||||
| 	MOVQ AX,R10 | ||||
| 	MOVQ DX,R11 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	MULQ 16(CX) | ||||
| 	MOVQ AX,R12 | ||||
| 	MOVQ DX,R13 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	MULQ 24(CX) | ||||
| 	MOVQ AX,R14 | ||||
| 	MOVQ DX,R15 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	MULQ 32(CX) | ||||
| 	MOVQ AX,BX | ||||
| 	MOVQ DX,BP | ||||
| 	MOVQ 8(SI),AX | ||||
| 	MULQ 0(CX) | ||||
| 	ADDQ AX,R10 | ||||
| 	ADCQ DX,R11 | ||||
| 	MOVQ 8(SI),AX | ||||
| 	MULQ 8(CX) | ||||
| 	ADDQ AX,R12 | ||||
| 	ADCQ DX,R13 | ||||
| 	MOVQ 8(SI),AX | ||||
| 	MULQ 16(CX) | ||||
| 	ADDQ AX,R14 | ||||
| 	ADCQ DX,R15 | ||||
| 	MOVQ 8(SI),AX | ||||
| 	MULQ 24(CX) | ||||
| 	ADDQ AX,BX | ||||
| 	ADCQ DX,BP | ||||
| 	MOVQ 8(SI),DX | ||||
| 	IMUL3Q $19,DX,AX | ||||
| 	MULQ 32(CX) | ||||
| 	ADDQ AX,R8 | ||||
| 	ADCQ DX,R9 | ||||
| 	MOVQ 16(SI),AX | ||||
| 	MULQ 0(CX) | ||||
| 	ADDQ AX,R12 | ||||
| 	ADCQ DX,R13 | ||||
| 	MOVQ 16(SI),AX | ||||
| 	MULQ 8(CX) | ||||
| 	ADDQ AX,R14 | ||||
| 	ADCQ DX,R15 | ||||
| 	MOVQ 16(SI),AX | ||||
| 	MULQ 16(CX) | ||||
| 	ADDQ AX,BX | ||||
| 	ADCQ DX,BP | ||||
| 	MOVQ 16(SI),DX | ||||
| 	IMUL3Q $19,DX,AX | ||||
| 	MULQ 24(CX) | ||||
| 	ADDQ AX,R8 | ||||
| 	ADCQ DX,R9 | ||||
| 	MOVQ 16(SI),DX | ||||
| 	IMUL3Q $19,DX,AX | ||||
| 	MULQ 32(CX) | ||||
| 	ADDQ AX,R10 | ||||
| 	ADCQ DX,R11 | ||||
| 	MOVQ 24(SI),AX | ||||
| 	MULQ 0(CX) | ||||
| 	ADDQ AX,R14 | ||||
| 	ADCQ DX,R15 | ||||
| 	MOVQ 24(SI),AX | ||||
| 	MULQ 8(CX) | ||||
| 	ADDQ AX,BX | ||||
| 	ADCQ DX,BP | ||||
| 	MOVQ 0(SP),AX | ||||
| 	MULQ 24(CX) | ||||
| 	ADDQ AX,R10 | ||||
| 	ADCQ DX,R11 | ||||
| 	MOVQ 0(SP),AX | ||||
| 	MULQ 32(CX) | ||||
| 	ADDQ AX,R12 | ||||
| 	ADCQ DX,R13 | ||||
| 	MOVQ 32(SI),AX | ||||
| 	MULQ 0(CX) | ||||
| 	ADDQ AX,BX | ||||
| 	ADCQ DX,BP | ||||
| 	MOVQ 8(SP),AX | ||||
| 	MULQ 16(CX) | ||||
| 	ADDQ AX,R10 | ||||
| 	ADCQ DX,R11 | ||||
| 	MOVQ 8(SP),AX | ||||
| 	MULQ 24(CX) | ||||
| 	ADDQ AX,R12 | ||||
| 	ADCQ DX,R13 | ||||
| 	MOVQ 8(SP),AX | ||||
| 	MULQ 32(CX) | ||||
| 	ADDQ AX,R14 | ||||
| 	ADCQ DX,R15 | ||||
| 	MOVQ $REDMASK51,SI | ||||
| 	SHLQ $13,R9:R8 | ||||
| 	ANDQ SI,R8 | ||||
| 	SHLQ $13,R11:R10 | ||||
| 	ANDQ SI,R10 | ||||
| 	ADDQ R9,R10 | ||||
| 	SHLQ $13,R13:R12 | ||||
| 	ANDQ SI,R12 | ||||
| 	ADDQ R11,R12 | ||||
| 	SHLQ $13,R15:R14 | ||||
| 	ANDQ SI,R14 | ||||
| 	ADDQ R13,R14 | ||||
| 	SHLQ $13,BP:BX | ||||
| 	ANDQ SI,BX | ||||
| 	ADDQ R15,BX | ||||
| 	IMUL3Q $19,BP,DX | ||||
| 	ADDQ DX,R8 | ||||
| 	MOVQ R8,DX | ||||
| 	SHRQ $51,DX | ||||
| 	ADDQ R10,DX | ||||
| 	MOVQ DX,CX | ||||
| 	SHRQ $51,DX | ||||
| 	ANDQ SI,R8 | ||||
| 	ADDQ R12,DX | ||||
| 	MOVQ DX,R9 | ||||
| 	SHRQ $51,DX | ||||
| 	ANDQ SI,CX | ||||
| 	ADDQ R14,DX | ||||
| 	MOVQ DX,AX | ||||
| 	SHRQ $51,DX | ||||
| 	ANDQ SI,R9 | ||||
| 	ADDQ BX,DX | ||||
| 	MOVQ DX,R10 | ||||
| 	SHRQ $51,DX | ||||
| 	ANDQ SI,AX | ||||
| 	IMUL3Q $19,DX,DX | ||||
| 	ADDQ DX,R8 | ||||
| 	ANDQ SI,R10 | ||||
| 	MOVQ R8,0(DI) | ||||
| 	MOVQ CX,8(DI) | ||||
| 	MOVQ R9,16(DI) | ||||
| 	MOVQ AX,24(DI) | ||||
| 	MOVQ R10,32(DI) | ||||
| 	RET | ||||
							
								
								
									
										132
									
								
								vendor/golang.org/x/crypto/curve25519/square_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								vendor/golang.org/x/crypto/curve25519/square_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| // Copyright 2012 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. | ||||
|  | ||||
| // This code was translated into a form compatible with 6a from the public | ||||
| // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html | ||||
|  | ||||
| // +build amd64,!gccgo,!appengine | ||||
|  | ||||
| #include "const_amd64.h" | ||||
|  | ||||
| // func square(out, in *[5]uint64) | ||||
| TEXT ·square(SB),7,$0-16 | ||||
| 	MOVQ out+0(FP), DI | ||||
| 	MOVQ in+8(FP), SI | ||||
|  | ||||
| 	MOVQ 0(SI),AX | ||||
| 	MULQ 0(SI) | ||||
| 	MOVQ AX,CX | ||||
| 	MOVQ DX,R8 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	SHLQ $1,AX | ||||
| 	MULQ 8(SI) | ||||
| 	MOVQ AX,R9 | ||||
| 	MOVQ DX,R10 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	SHLQ $1,AX | ||||
| 	MULQ 16(SI) | ||||
| 	MOVQ AX,R11 | ||||
| 	MOVQ DX,R12 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	SHLQ $1,AX | ||||
| 	MULQ 24(SI) | ||||
| 	MOVQ AX,R13 | ||||
| 	MOVQ DX,R14 | ||||
| 	MOVQ 0(SI),AX | ||||
| 	SHLQ $1,AX | ||||
| 	MULQ 32(SI) | ||||
| 	MOVQ AX,R15 | ||||
| 	MOVQ DX,BX | ||||
| 	MOVQ 8(SI),AX | ||||
| 	MULQ 8(SI) | ||||
| 	ADDQ AX,R11 | ||||
| 	ADCQ DX,R12 | ||||
| 	MOVQ 8(SI),AX | ||||
| 	SHLQ $1,AX | ||||
| 	MULQ 16(SI) | ||||
| 	ADDQ AX,R13 | ||||
| 	ADCQ DX,R14 | ||||
| 	MOVQ 8(SI),AX | ||||
| 	SHLQ $1,AX | ||||
| 	MULQ 24(SI) | ||||
| 	ADDQ AX,R15 | ||||
| 	ADCQ DX,BX | ||||
| 	MOVQ 8(SI),DX | ||||
| 	IMUL3Q $38,DX,AX | ||||
| 	MULQ 32(SI) | ||||
| 	ADDQ AX,CX | ||||
| 	ADCQ DX,R8 | ||||
| 	MOVQ 16(SI),AX | ||||
| 	MULQ 16(SI) | ||||
| 	ADDQ AX,R15 | ||||
| 	ADCQ DX,BX | ||||
| 	MOVQ 16(SI),DX | ||||
| 	IMUL3Q $38,DX,AX | ||||
| 	MULQ 24(SI) | ||||
| 	ADDQ AX,CX | ||||
| 	ADCQ DX,R8 | ||||
| 	MOVQ 16(SI),DX | ||||
| 	IMUL3Q $38,DX,AX | ||||
| 	MULQ 32(SI) | ||||
| 	ADDQ AX,R9 | ||||
| 	ADCQ DX,R10 | ||||
| 	MOVQ 24(SI),DX | ||||
| 	IMUL3Q $19,DX,AX | ||||
| 	MULQ 24(SI) | ||||
| 	ADDQ AX,R9 | ||||
| 	ADCQ DX,R10 | ||||
| 	MOVQ 24(SI),DX | ||||
| 	IMUL3Q $38,DX,AX | ||||
| 	MULQ 32(SI) | ||||
| 	ADDQ AX,R11 | ||||
| 	ADCQ DX,R12 | ||||
| 	MOVQ 32(SI),DX | ||||
| 	IMUL3Q $19,DX,AX | ||||
| 	MULQ 32(SI) | ||||
| 	ADDQ AX,R13 | ||||
| 	ADCQ DX,R14 | ||||
| 	MOVQ $REDMASK51,SI | ||||
| 	SHLQ $13,R8:CX | ||||
| 	ANDQ SI,CX | ||||
| 	SHLQ $13,R10:R9 | ||||
| 	ANDQ SI,R9 | ||||
| 	ADDQ R8,R9 | ||||
| 	SHLQ $13,R12:R11 | ||||
| 	ANDQ SI,R11 | ||||
| 	ADDQ R10,R11 | ||||
| 	SHLQ $13,R14:R13 | ||||
| 	ANDQ SI,R13 | ||||
| 	ADDQ R12,R13 | ||||
| 	SHLQ $13,BX:R15 | ||||
| 	ANDQ SI,R15 | ||||
| 	ADDQ R14,R15 | ||||
| 	IMUL3Q $19,BX,DX | ||||
| 	ADDQ DX,CX | ||||
| 	MOVQ CX,DX | ||||
| 	SHRQ $51,DX | ||||
| 	ADDQ R9,DX | ||||
| 	ANDQ SI,CX | ||||
| 	MOVQ DX,R8 | ||||
| 	SHRQ $51,DX | ||||
| 	ADDQ R11,DX | ||||
| 	ANDQ SI,R8 | ||||
| 	MOVQ DX,R9 | ||||
| 	SHRQ $51,DX | ||||
| 	ADDQ R13,DX | ||||
| 	ANDQ SI,R9 | ||||
| 	MOVQ DX,AX | ||||
| 	SHRQ $51,DX | ||||
| 	ADDQ R15,DX | ||||
| 	ANDQ SI,AX | ||||
| 	MOVQ DX,R10 | ||||
| 	SHRQ $51,DX | ||||
| 	IMUL3Q $19,DX,DX | ||||
| 	ADDQ DX,CX | ||||
| 	ANDQ SI,R10 | ||||
| 	MOVQ CX,0(DI) | ||||
| 	MOVQ R8,8(DI) | ||||
| 	MOVQ R9,16(DI) | ||||
| 	MOVQ AX,24(DI) | ||||
| 	MOVQ R10,32(DI) | ||||
| 	RET | ||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/crypto/ed25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/crypto/ed25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| Copyright (c) 2009 The Go Authors. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
|  | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										181
									
								
								vendor/golang.org/x/crypto/ed25519/ed25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								vendor/golang.org/x/crypto/ed25519/ed25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| // Copyright 2016 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 ed25519 implements the Ed25519 signature algorithm. See | ||||
| // https://ed25519.cr.yp.to/. | ||||
| // | ||||
| // These functions are also compatible with the “Ed25519” function defined in | ||||
| // RFC 8032. | ||||
| package ed25519 | ||||
|  | ||||
| // This code is a port of the public domain, “ref10” implementation of ed25519 | ||||
| // from SUPERCOP. | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto" | ||||
| 	cryptorand "crypto/rand" | ||||
| 	"crypto/sha512" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"golang.org/x/crypto/ed25519/internal/edwards25519" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// PublicKeySize is the size, in bytes, of public keys as used in this package. | ||||
| 	PublicKeySize = 32 | ||||
| 	// PrivateKeySize is the size, in bytes, of private keys as used in this package. | ||||
| 	PrivateKeySize = 64 | ||||
| 	// SignatureSize is the size, in bytes, of signatures generated and verified by this package. | ||||
| 	SignatureSize = 64 | ||||
| ) | ||||
|  | ||||
| // PublicKey is the type of Ed25519 public keys. | ||||
| type PublicKey []byte | ||||
|  | ||||
| // PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. | ||||
| type PrivateKey []byte | ||||
|  | ||||
| // Public returns the PublicKey corresponding to priv. | ||||
| func (priv PrivateKey) Public() crypto.PublicKey { | ||||
| 	publicKey := make([]byte, PublicKeySize) | ||||
| 	copy(publicKey, priv[32:]) | ||||
| 	return PublicKey(publicKey) | ||||
| } | ||||
|  | ||||
| // Sign signs the given message with priv. | ||||
| // Ed25519 performs two passes over messages to be signed and therefore cannot | ||||
| // handle pre-hashed messages. Thus opts.HashFunc() must return zero to | ||||
| // indicate the message hasn't been hashed. This can be achieved by passing | ||||
| // crypto.Hash(0) as the value for opts. | ||||
| func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { | ||||
| 	if opts.HashFunc() != crypto.Hash(0) { | ||||
| 		return nil, errors.New("ed25519: cannot sign hashed message") | ||||
| 	} | ||||
|  | ||||
| 	return Sign(priv, message), nil | ||||
| } | ||||
|  | ||||
| // GenerateKey generates a public/private key pair using entropy from rand. | ||||
| // If rand is nil, crypto/rand.Reader will be used. | ||||
| func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) { | ||||
| 	if rand == nil { | ||||
| 		rand = cryptorand.Reader | ||||
| 	} | ||||
|  | ||||
| 	privateKey = make([]byte, PrivateKeySize) | ||||
| 	publicKey = make([]byte, PublicKeySize) | ||||
| 	_, err = io.ReadFull(rand, privateKey[:32]) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	digest := sha512.Sum512(privateKey[:32]) | ||||
| 	digest[0] &= 248 | ||||
| 	digest[31] &= 127 | ||||
| 	digest[31] |= 64 | ||||
|  | ||||
| 	var A edwards25519.ExtendedGroupElement | ||||
| 	var hBytes [32]byte | ||||
| 	copy(hBytes[:], digest[:]) | ||||
| 	edwards25519.GeScalarMultBase(&A, &hBytes) | ||||
| 	var publicKeyBytes [32]byte | ||||
| 	A.ToBytes(&publicKeyBytes) | ||||
|  | ||||
| 	copy(privateKey[32:], publicKeyBytes[:]) | ||||
| 	copy(publicKey, publicKeyBytes[:]) | ||||
|  | ||||
| 	return publicKey, privateKey, nil | ||||
| } | ||||
|  | ||||
| // Sign signs the message with privateKey and returns a signature. It will | ||||
| // panic if len(privateKey) is not PrivateKeySize. | ||||
| func Sign(privateKey PrivateKey, message []byte) []byte { | ||||
| 	if l := len(privateKey); l != PrivateKeySize { | ||||
| 		panic("ed25519: bad private key length: " + strconv.Itoa(l)) | ||||
| 	} | ||||
|  | ||||
| 	h := sha512.New() | ||||
| 	h.Write(privateKey[:32]) | ||||
|  | ||||
| 	var digest1, messageDigest, hramDigest [64]byte | ||||
| 	var expandedSecretKey [32]byte | ||||
| 	h.Sum(digest1[:0]) | ||||
| 	copy(expandedSecretKey[:], digest1[:]) | ||||
| 	expandedSecretKey[0] &= 248 | ||||
| 	expandedSecretKey[31] &= 63 | ||||
| 	expandedSecretKey[31] |= 64 | ||||
|  | ||||
| 	h.Reset() | ||||
| 	h.Write(digest1[32:]) | ||||
| 	h.Write(message) | ||||
| 	h.Sum(messageDigest[:0]) | ||||
|  | ||||
| 	var messageDigestReduced [32]byte | ||||
| 	edwards25519.ScReduce(&messageDigestReduced, &messageDigest) | ||||
| 	var R edwards25519.ExtendedGroupElement | ||||
| 	edwards25519.GeScalarMultBase(&R, &messageDigestReduced) | ||||
|  | ||||
| 	var encodedR [32]byte | ||||
| 	R.ToBytes(&encodedR) | ||||
|  | ||||
| 	h.Reset() | ||||
| 	h.Write(encodedR[:]) | ||||
| 	h.Write(privateKey[32:]) | ||||
| 	h.Write(message) | ||||
| 	h.Sum(hramDigest[:0]) | ||||
| 	var hramDigestReduced [32]byte | ||||
| 	edwards25519.ScReduce(&hramDigestReduced, &hramDigest) | ||||
|  | ||||
| 	var s [32]byte | ||||
| 	edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced) | ||||
|  | ||||
| 	signature := make([]byte, SignatureSize) | ||||
| 	copy(signature[:], encodedR[:]) | ||||
| 	copy(signature[32:], s[:]) | ||||
|  | ||||
| 	return signature | ||||
| } | ||||
|  | ||||
| // Verify reports whether sig is a valid signature of message by publicKey. It | ||||
| // will panic if len(publicKey) is not PublicKeySize. | ||||
| func Verify(publicKey PublicKey, message, sig []byte) bool { | ||||
| 	if l := len(publicKey); l != PublicKeySize { | ||||
| 		panic("ed25519: bad public key length: " + strconv.Itoa(l)) | ||||
| 	} | ||||
|  | ||||
| 	if len(sig) != SignatureSize || sig[63]&224 != 0 { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	var A edwards25519.ExtendedGroupElement | ||||
| 	var publicKeyBytes [32]byte | ||||
| 	copy(publicKeyBytes[:], publicKey) | ||||
| 	if !A.FromBytes(&publicKeyBytes) { | ||||
| 		return false | ||||
| 	} | ||||
| 	edwards25519.FeNeg(&A.X, &A.X) | ||||
| 	edwards25519.FeNeg(&A.T, &A.T) | ||||
|  | ||||
| 	h := sha512.New() | ||||
| 	h.Write(sig[:32]) | ||||
| 	h.Write(publicKey[:]) | ||||
| 	h.Write(message) | ||||
| 	var digest [64]byte | ||||
| 	h.Sum(digest[:0]) | ||||
|  | ||||
| 	var hReduced [32]byte | ||||
| 	edwards25519.ScReduce(&hReduced, &digest) | ||||
|  | ||||
| 	var R edwards25519.ProjectiveGroupElement | ||||
| 	var b [32]byte | ||||
| 	copy(b[:], sig[32:]) | ||||
| 	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b) | ||||
|  | ||||
| 	var checkR [32]byte | ||||
| 	R.ToBytes(&checkR) | ||||
| 	return bytes.Equal(sig[:32], checkR[:]) | ||||
| } | ||||
							
								
								
									
										1422
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1422
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1771
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1771
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										27
									
								
								vendor/golang.org/x/crypto/ssh/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/crypto/ssh/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| Copyright (c) 2009 The Go Authors. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
|  | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										683
									
								
								vendor/golang.org/x/crypto/ssh/agent/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										683
									
								
								vendor/golang.org/x/crypto/ssh/agent/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,683 @@ | ||||
| // Copyright 2012 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 agent implements the ssh-agent protocol, and provides both | ||||
| // a client and a server. The client can talk to a standard ssh-agent | ||||
| // that uses UNIX sockets, and one could implement an alternative | ||||
| // ssh-agent process using the sample server. | ||||
| // | ||||
| // References: | ||||
| //  [PROTOCOL.agent]:    http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD | ||||
| package agent // import "golang.org/x/crypto/ssh/agent" | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/dsa" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rsa" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| 	"sync" | ||||
|  | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // Agent represents the capabilities of an ssh-agent. | ||||
| type Agent interface { | ||||
| 	// List returns the identities known to the agent. | ||||
| 	List() ([]*Key, error) | ||||
|  | ||||
| 	// Sign has the agent sign the data using a protocol 2 key as defined | ||||
| 	// in [PROTOCOL.agent] section 2.6.2. | ||||
| 	Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) | ||||
|  | ||||
| 	// Add adds a private key to the agent. | ||||
| 	Add(key AddedKey) error | ||||
|  | ||||
| 	// Remove removes all identities with the given public key. | ||||
| 	Remove(key ssh.PublicKey) error | ||||
|  | ||||
| 	// RemoveAll removes all identities. | ||||
| 	RemoveAll() error | ||||
|  | ||||
| 	// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. | ||||
| 	Lock(passphrase []byte) error | ||||
|  | ||||
| 	// Unlock undoes the effect of Lock | ||||
| 	Unlock(passphrase []byte) error | ||||
|  | ||||
| 	// Signers returns signers for all the known keys. | ||||
| 	Signers() ([]ssh.Signer, error) | ||||
| } | ||||
|  | ||||
| // ConstraintExtension describes an optional constraint defined by users. | ||||
| type ConstraintExtension struct { | ||||
| 	// ExtensionName consist of a UTF-8 string suffixed by the | ||||
| 	// implementation domain following the naming scheme defined | ||||
| 	// in Section 4.2 of [RFC4251], e.g.  "foo@example.com". | ||||
| 	ExtensionName string | ||||
| 	// ExtensionDetails contains the actual content of the extended | ||||
| 	// constraint. | ||||
| 	ExtensionDetails []byte | ||||
| } | ||||
|  | ||||
| // AddedKey describes an SSH key to be added to an Agent. | ||||
| type AddedKey struct { | ||||
| 	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or | ||||
| 	// *ecdsa.PrivateKey, which will be inserted into the agent. | ||||
| 	PrivateKey interface{} | ||||
| 	// Certificate, if not nil, is communicated to the agent and will be | ||||
| 	// stored with the key. | ||||
| 	Certificate *ssh.Certificate | ||||
| 	// Comment is an optional, free-form string. | ||||
| 	Comment string | ||||
| 	// LifetimeSecs, if not zero, is the number of seconds that the | ||||
| 	// agent will store the key for. | ||||
| 	LifetimeSecs uint32 | ||||
| 	// ConfirmBeforeUse, if true, requests that the agent confirm with the | ||||
| 	// user before each use of this key. | ||||
| 	ConfirmBeforeUse bool | ||||
| 	// ConstraintExtensions are the experimental or private-use constraints | ||||
| 	// defined by users. | ||||
| 	ConstraintExtensions []ConstraintExtension | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 3. | ||||
| const ( | ||||
| 	agentRequestV1Identities   = 1 | ||||
| 	agentRemoveAllV1Identities = 9 | ||||
|  | ||||
| 	// 3.2 Requests from client to agent for protocol 2 key operations | ||||
| 	agentAddIdentity         = 17 | ||||
| 	agentRemoveIdentity      = 18 | ||||
| 	agentRemoveAllIdentities = 19 | ||||
| 	agentAddIDConstrained    = 25 | ||||
|  | ||||
| 	// 3.3 Key-type independent requests from client to agent | ||||
| 	agentAddSmartcardKey            = 20 | ||||
| 	agentRemoveSmartcardKey         = 21 | ||||
| 	agentLock                       = 22 | ||||
| 	agentUnlock                     = 23 | ||||
| 	agentAddSmartcardKeyConstrained = 26 | ||||
|  | ||||
| 	// 3.7 Key constraint identifiers | ||||
| 	agentConstrainLifetime  = 1 | ||||
| 	agentConstrainConfirm   = 2 | ||||
| 	agentConstrainExtension = 3 | ||||
| ) | ||||
|  | ||||
| // maxAgentResponseBytes is the maximum agent reply size that is accepted. This | ||||
| // is a sanity check, not a limit in the spec. | ||||
| const maxAgentResponseBytes = 16 << 20 | ||||
|  | ||||
| // Agent messages: | ||||
| // These structures mirror the wire format of the corresponding ssh agent | ||||
| // messages found in [PROTOCOL.agent]. | ||||
|  | ||||
| // 3.4 Generic replies from agent to client | ||||
| const agentFailure = 5 | ||||
|  | ||||
| type failureAgentMsg struct{} | ||||
|  | ||||
| const agentSuccess = 6 | ||||
|  | ||||
| type successAgentMsg struct{} | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.5.2. | ||||
| const agentRequestIdentities = 11 | ||||
|  | ||||
| type requestIdentitiesAgentMsg struct{} | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.5.2. | ||||
| const agentIdentitiesAnswer = 12 | ||||
|  | ||||
| type identitiesAnswerAgentMsg struct { | ||||
| 	NumKeys uint32 `sshtype:"12"` | ||||
| 	Keys    []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.6.2. | ||||
| const agentSignRequest = 13 | ||||
|  | ||||
| type signRequestAgentMsg struct { | ||||
| 	KeyBlob []byte `sshtype:"13"` | ||||
| 	Data    []byte | ||||
| 	Flags   uint32 | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.6.2. | ||||
|  | ||||
| // 3.6 Replies from agent to client for protocol 2 key operations | ||||
| const agentSignResponse = 14 | ||||
|  | ||||
| type signResponseAgentMsg struct { | ||||
| 	SigBlob []byte `sshtype:"14"` | ||||
| } | ||||
|  | ||||
| type publicKey struct { | ||||
| 	Format string | ||||
| 	Rest   []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| // 3.7 Key constraint identifiers | ||||
| type constrainLifetimeAgentMsg struct { | ||||
| 	LifetimeSecs uint32 `sshtype:"1"` | ||||
| } | ||||
|  | ||||
| type constrainExtensionAgentMsg struct { | ||||
| 	ExtensionName    string `sshtype:"3"` | ||||
| 	ExtensionDetails []byte | ||||
|  | ||||
| 	// Rest is a field used for parsing, not part of message | ||||
| 	Rest []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| // Key represents a protocol 2 public key as defined in | ||||
| // [PROTOCOL.agent], section 2.5.2. | ||||
| type Key struct { | ||||
| 	Format  string | ||||
| 	Blob    []byte | ||||
| 	Comment string | ||||
| } | ||||
|  | ||||
| func clientErr(err error) error { | ||||
| 	return fmt.Errorf("agent: client error: %v", err) | ||||
| } | ||||
|  | ||||
| // String returns the storage form of an agent key with the format, base64 | ||||
| // encoded serialized key, and the comment if it is not empty. | ||||
| func (k *Key) String() string { | ||||
| 	s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob) | ||||
|  | ||||
| 	if k.Comment != "" { | ||||
| 		s += " " + k.Comment | ||||
| 	} | ||||
|  | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // Type returns the public key type. | ||||
| func (k *Key) Type() string { | ||||
| 	return k.Format | ||||
| } | ||||
|  | ||||
| // Marshal returns key blob to satisfy the ssh.PublicKey interface. | ||||
| func (k *Key) Marshal() []byte { | ||||
| 	return k.Blob | ||||
| } | ||||
|  | ||||
| // Verify satisfies the ssh.PublicKey interface. | ||||
| func (k *Key) Verify(data []byte, sig *ssh.Signature) error { | ||||
| 	pubKey, err := ssh.ParsePublicKey(k.Blob) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("agent: bad public key: %v", err) | ||||
| 	} | ||||
| 	return pubKey.Verify(data, sig) | ||||
| } | ||||
|  | ||||
| type wireKey struct { | ||||
| 	Format string | ||||
| 	Rest   []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| func parseKey(in []byte) (out *Key, rest []byte, err error) { | ||||
| 	var record struct { | ||||
| 		Blob    []byte | ||||
| 		Comment string | ||||
| 		Rest    []byte `ssh:"rest"` | ||||
| 	} | ||||
|  | ||||
| 	if err := ssh.Unmarshal(in, &record); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	var wk wireKey | ||||
| 	if err := ssh.Unmarshal(record.Blob, &wk); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &Key{ | ||||
| 		Format:  wk.Format, | ||||
| 		Blob:    record.Blob, | ||||
| 		Comment: record.Comment, | ||||
| 	}, record.Rest, nil | ||||
| } | ||||
|  | ||||
| // client is a client for an ssh-agent process. | ||||
| type client struct { | ||||
| 	// conn is typically a *net.UnixConn | ||||
| 	conn io.ReadWriter | ||||
| 	// mu is used to prevent concurrent access to the agent | ||||
| 	mu sync.Mutex | ||||
| } | ||||
|  | ||||
| // NewClient returns an Agent that talks to an ssh-agent process over | ||||
| // the given connection. | ||||
| func NewClient(rw io.ReadWriter) Agent { | ||||
| 	return &client{conn: rw} | ||||
| } | ||||
|  | ||||
| // call sends an RPC to the agent. On success, the reply is | ||||
| // unmarshaled into reply and replyType is set to the first byte of | ||||
| // the reply, which contains the type of the message. | ||||
| func (c *client) call(req []byte) (reply interface{}, err error) { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
|  | ||||
| 	msg := make([]byte, 4+len(req)) | ||||
| 	binary.BigEndian.PutUint32(msg, uint32(len(req))) | ||||
| 	copy(msg[4:], req) | ||||
| 	if _, err = c.conn.Write(msg); err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
|  | ||||
| 	var respSizeBuf [4]byte | ||||
| 	if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
| 	respSize := binary.BigEndian.Uint32(respSizeBuf[:]) | ||||
| 	if respSize > maxAgentResponseBytes { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
|  | ||||
| 	buf := make([]byte, respSize) | ||||
| 	if _, err = io.ReadFull(c.conn, buf); err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
| 	reply, err = unmarshal(buf) | ||||
| 	if err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
| 	return reply, err | ||||
| } | ||||
|  | ||||
| func (c *client) simpleCall(req []byte) error { | ||||
| 	resp, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, ok := resp.(*successAgentMsg); ok { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("agent: failure") | ||||
| } | ||||
|  | ||||
| func (c *client) RemoveAll() error { | ||||
| 	return c.simpleCall([]byte{agentRemoveAllIdentities}) | ||||
| } | ||||
|  | ||||
| func (c *client) Remove(key ssh.PublicKey) error { | ||||
| 	req := ssh.Marshal(&agentRemoveIdentityMsg{ | ||||
| 		KeyBlob: key.Marshal(), | ||||
| 	}) | ||||
| 	return c.simpleCall(req) | ||||
| } | ||||
|  | ||||
| func (c *client) Lock(passphrase []byte) error { | ||||
| 	req := ssh.Marshal(&agentLockMsg{ | ||||
| 		Passphrase: passphrase, | ||||
| 	}) | ||||
| 	return c.simpleCall(req) | ||||
| } | ||||
|  | ||||
| func (c *client) Unlock(passphrase []byte) error { | ||||
| 	req := ssh.Marshal(&agentUnlockMsg{ | ||||
| 		Passphrase: passphrase, | ||||
| 	}) | ||||
| 	return c.simpleCall(req) | ||||
| } | ||||
|  | ||||
| // List returns the identities known to the agent. | ||||
| func (c *client) List() ([]*Key, error) { | ||||
| 	// see [PROTOCOL.agent] section 2.5.2. | ||||
| 	req := []byte{agentRequestIdentities} | ||||
|  | ||||
| 	msg, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	switch msg := msg.(type) { | ||||
| 	case *identitiesAnswerAgentMsg: | ||||
| 		if msg.NumKeys > maxAgentResponseBytes/8 { | ||||
| 			return nil, errors.New("agent: too many keys in agent reply") | ||||
| 		} | ||||
| 		keys := make([]*Key, msg.NumKeys) | ||||
| 		data := msg.Keys | ||||
| 		for i := uint32(0); i < msg.NumKeys; i++ { | ||||
| 			var key *Key | ||||
| 			var err error | ||||
| 			if key, data, err = parseKey(data); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			keys[i] = key | ||||
| 		} | ||||
| 		return keys, nil | ||||
| 	case *failureAgentMsg: | ||||
| 		return nil, errors.New("agent: failed to list keys") | ||||
| 	} | ||||
| 	panic("unreachable") | ||||
| } | ||||
|  | ||||
| // Sign has the agent sign the data using a protocol 2 key as defined | ||||
| // in [PROTOCOL.agent] section 2.6.2. | ||||
| func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { | ||||
| 	req := ssh.Marshal(signRequestAgentMsg{ | ||||
| 		KeyBlob: key.Marshal(), | ||||
| 		Data:    data, | ||||
| 	}) | ||||
|  | ||||
| 	msg, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	switch msg := msg.(type) { | ||||
| 	case *signResponseAgentMsg: | ||||
| 		var sig ssh.Signature | ||||
| 		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		return &sig, nil | ||||
| 	case *failureAgentMsg: | ||||
| 		return nil, errors.New("agent: failed to sign challenge") | ||||
| 	} | ||||
| 	panic("unreachable") | ||||
| } | ||||
|  | ||||
| // unmarshal parses an agent message in packet, returning the parsed | ||||
| // form and the message type of packet. | ||||
| func unmarshal(packet []byte) (interface{}, error) { | ||||
| 	if len(packet) < 1 { | ||||
| 		return nil, errors.New("agent: empty packet") | ||||
| 	} | ||||
| 	var msg interface{} | ||||
| 	switch packet[0] { | ||||
| 	case agentFailure: | ||||
| 		return new(failureAgentMsg), nil | ||||
| 	case agentSuccess: | ||||
| 		return new(successAgentMsg), nil | ||||
| 	case agentIdentitiesAnswer: | ||||
| 		msg = new(identitiesAnswerAgentMsg) | ||||
| 	case agentSignResponse: | ||||
| 		msg = new(signResponseAgentMsg) | ||||
| 	case agentV1IdentitiesAnswer: | ||||
| 		msg = new(agentV1IdentityMsg) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("agent: unknown type tag %d", packet[0]) | ||||
| 	} | ||||
| 	if err := ssh.Unmarshal(packet, msg); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return msg, nil | ||||
| } | ||||
|  | ||||
| type rsaKeyMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	N           *big.Int | ||||
| 	E           *big.Int | ||||
| 	D           *big.Int | ||||
| 	Iqmp        *big.Int // IQMP = Inverse Q Mod P | ||||
| 	P           *big.Int | ||||
| 	Q           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| type dsaKeyMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	P           *big.Int | ||||
| 	Q           *big.Int | ||||
| 	G           *big.Int | ||||
| 	Y           *big.Int | ||||
| 	X           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| type ecdsaKeyMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	Curve       string | ||||
| 	KeyBytes    []byte | ||||
| 	D           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| type ed25519KeyMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	Pub         []byte | ||||
| 	Priv        []byte | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| // Insert adds a private key to the agent. | ||||
| func (c *client) insertKey(s interface{}, comment string, constraints []byte) error { | ||||
| 	var req []byte | ||||
| 	switch k := s.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		if len(k.Primes) != 2 { | ||||
| 			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) | ||||
| 		} | ||||
| 		k.Precompute() | ||||
| 		req = ssh.Marshal(rsaKeyMsg{ | ||||
| 			Type:        ssh.KeyAlgoRSA, | ||||
| 			N:           k.N, | ||||
| 			E:           big.NewInt(int64(k.E)), | ||||
| 			D:           k.D, | ||||
| 			Iqmp:        k.Precomputed.Qinv, | ||||
| 			P:           k.Primes[0], | ||||
| 			Q:           k.Primes[1], | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	case *dsa.PrivateKey: | ||||
| 		req = ssh.Marshal(dsaKeyMsg{ | ||||
| 			Type:        ssh.KeyAlgoDSA, | ||||
| 			P:           k.P, | ||||
| 			Q:           k.Q, | ||||
| 			G:           k.G, | ||||
| 			Y:           k.Y, | ||||
| 			X:           k.X, | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) | ||||
| 		req = ssh.Marshal(ecdsaKeyMsg{ | ||||
| 			Type:        "ecdsa-sha2-" + nistID, | ||||
| 			Curve:       nistID, | ||||
| 			KeyBytes:    elliptic.Marshal(k.Curve, k.X, k.Y), | ||||
| 			D:           k.D, | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	case *ed25519.PrivateKey: | ||||
| 		req = ssh.Marshal(ed25519KeyMsg{ | ||||
| 			Type:        ssh.KeyAlgoED25519, | ||||
| 			Pub:         []byte(*k)[32:], | ||||
| 			Priv:        []byte(*k), | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	default: | ||||
| 		return fmt.Errorf("agent: unsupported key type %T", s) | ||||
| 	} | ||||
|  | ||||
| 	// if constraints are present then the message type needs to be changed. | ||||
| 	if len(constraints) != 0 { | ||||
| 		req[0] = agentAddIDConstrained | ||||
| 	} | ||||
|  | ||||
| 	resp, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, ok := resp.(*successAgentMsg); ok { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("agent: failure") | ||||
| } | ||||
|  | ||||
| type rsaCertMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	CertBytes   []byte | ||||
| 	D           *big.Int | ||||
| 	Iqmp        *big.Int // IQMP = Inverse Q Mod P | ||||
| 	P           *big.Int | ||||
| 	Q           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| type dsaCertMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	CertBytes   []byte | ||||
| 	X           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| type ecdsaCertMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	CertBytes   []byte | ||||
| 	D           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| type ed25519CertMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	CertBytes   []byte | ||||
| 	Pub         []byte | ||||
| 	Priv        []byte | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| // Add adds a private key to the agent. If a certificate is given, | ||||
| // that certificate is added instead as public key. | ||||
| func (c *client) Add(key AddedKey) error { | ||||
| 	var constraints []byte | ||||
|  | ||||
| 	if secs := key.LifetimeSecs; secs != 0 { | ||||
| 		constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...) | ||||
| 	} | ||||
|  | ||||
| 	if key.ConfirmBeforeUse { | ||||
| 		constraints = append(constraints, agentConstrainConfirm) | ||||
| 	} | ||||
|  | ||||
| 	cert := key.Certificate | ||||
| 	if cert == nil { | ||||
| 		return c.insertKey(key.PrivateKey, key.Comment, constraints) | ||||
| 	} | ||||
| 	return c.insertCert(key.PrivateKey, cert, key.Comment, constraints) | ||||
| } | ||||
|  | ||||
| func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error { | ||||
| 	var req []byte | ||||
| 	switch k := s.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		if len(k.Primes) != 2 { | ||||
| 			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) | ||||
| 		} | ||||
| 		k.Precompute() | ||||
| 		req = ssh.Marshal(rsaCertMsg{ | ||||
| 			Type:        cert.Type(), | ||||
| 			CertBytes:   cert.Marshal(), | ||||
| 			D:           k.D, | ||||
| 			Iqmp:        k.Precomputed.Qinv, | ||||
| 			P:           k.Primes[0], | ||||
| 			Q:           k.Primes[1], | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	case *dsa.PrivateKey: | ||||
| 		req = ssh.Marshal(dsaCertMsg{ | ||||
| 			Type:        cert.Type(), | ||||
| 			CertBytes:   cert.Marshal(), | ||||
| 			X:           k.X, | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		req = ssh.Marshal(ecdsaCertMsg{ | ||||
| 			Type:        cert.Type(), | ||||
| 			CertBytes:   cert.Marshal(), | ||||
| 			D:           k.D, | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	case *ed25519.PrivateKey: | ||||
| 		req = ssh.Marshal(ed25519CertMsg{ | ||||
| 			Type:        cert.Type(), | ||||
| 			CertBytes:   cert.Marshal(), | ||||
| 			Pub:         []byte(*k)[32:], | ||||
| 			Priv:        []byte(*k), | ||||
| 			Comments:    comment, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	default: | ||||
| 		return fmt.Errorf("agent: unsupported key type %T", s) | ||||
| 	} | ||||
|  | ||||
| 	// if constraints are present then the message type needs to be changed. | ||||
| 	if len(constraints) != 0 { | ||||
| 		req[0] = agentAddIDConstrained | ||||
| 	} | ||||
|  | ||||
| 	signer, err := ssh.NewSignerFromKey(s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { | ||||
| 		return errors.New("agent: signer and cert have different public key") | ||||
| 	} | ||||
|  | ||||
| 	resp, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, ok := resp.(*successAgentMsg); ok { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("agent: failure") | ||||
| } | ||||
|  | ||||
| // Signers provides a callback for client authentication. | ||||
| func (c *client) Signers() ([]ssh.Signer, error) { | ||||
| 	keys, err := c.List() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var result []ssh.Signer | ||||
| 	for _, k := range keys { | ||||
| 		result = append(result, &agentKeyringSigner{c, k}) | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| type agentKeyringSigner struct { | ||||
| 	agent *client | ||||
| 	pub   ssh.PublicKey | ||||
| } | ||||
|  | ||||
| func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { | ||||
| 	return s.pub | ||||
| } | ||||
|  | ||||
| func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) { | ||||
| 	// The agent has its own entropy source, so the rand argument is ignored. | ||||
| 	return s.agent.Sign(s.pub, data) | ||||
| } | ||||
							
								
								
									
										103
									
								
								vendor/golang.org/x/crypto/ssh/agent/forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								vendor/golang.org/x/crypto/ssh/agent/forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| // Copyright 2014 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 agent | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"sync" | ||||
|  | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // RequestAgentForwarding sets up agent forwarding for the session. | ||||
| // ForwardToAgent or ForwardToRemote should be called to route | ||||
| // the authentication requests. | ||||
| func RequestAgentForwarding(session *ssh.Session) error { | ||||
| 	ok, err := session.SendRequest("auth-agent-req@openssh.com", true, nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if !ok { | ||||
| 		return errors.New("forwarding request denied") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ForwardToAgent routes authentication requests to the given keyring. | ||||
| func ForwardToAgent(client *ssh.Client, keyring Agent) error { | ||||
| 	channels := client.HandleChannelOpen(channelType) | ||||
| 	if channels == nil { | ||||
| 		return errors.New("agent: already have handler for " + channelType) | ||||
| 	} | ||||
|  | ||||
| 	go func() { | ||||
| 		for ch := range channels { | ||||
| 			channel, reqs, err := ch.Accept() | ||||
| 			if err != nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			go ssh.DiscardRequests(reqs) | ||||
| 			go func() { | ||||
| 				ServeAgent(keyring, channel) | ||||
| 				channel.Close() | ||||
| 			}() | ||||
| 		} | ||||
| 	}() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| const channelType = "auth-agent@openssh.com" | ||||
|  | ||||
| // ForwardToRemote routes authentication requests to the ssh-agent | ||||
| // process serving on the given unix socket. | ||||
| func ForwardToRemote(client *ssh.Client, addr string) error { | ||||
| 	channels := client.HandleChannelOpen(channelType) | ||||
| 	if channels == nil { | ||||
| 		return errors.New("agent: already have handler for " + channelType) | ||||
| 	} | ||||
| 	conn, err := net.Dial("unix", addr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	conn.Close() | ||||
|  | ||||
| 	go func() { | ||||
| 		for ch := range channels { | ||||
| 			channel, reqs, err := ch.Accept() | ||||
| 			if err != nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			go ssh.DiscardRequests(reqs) | ||||
| 			go forwardUnixSocket(channel, addr) | ||||
| 		} | ||||
| 	}() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func forwardUnixSocket(channel ssh.Channel, addr string) { | ||||
| 	conn, err := net.Dial("unix", addr) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(2) | ||||
| 	go func() { | ||||
| 		io.Copy(conn, channel) | ||||
| 		conn.(*net.UnixConn).CloseWrite() | ||||
| 		wg.Done() | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		io.Copy(channel, conn) | ||||
| 		channel.CloseWrite() | ||||
| 		wg.Done() | ||||
| 	}() | ||||
|  | ||||
| 	wg.Wait() | ||||
| 	conn.Close() | ||||
| 	channel.Close() | ||||
| } | ||||
							
								
								
									
										215
									
								
								vendor/golang.org/x/crypto/ssh/agent/keyring.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								vendor/golang.org/x/crypto/ssh/agent/keyring.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| // Copyright 2014 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 agent | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/subtle" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| type privKey struct { | ||||
| 	signer  ssh.Signer | ||||
| 	comment string | ||||
| 	expire  *time.Time | ||||
| } | ||||
|  | ||||
| type keyring struct { | ||||
| 	mu   sync.Mutex | ||||
| 	keys []privKey | ||||
|  | ||||
| 	locked     bool | ||||
| 	passphrase []byte | ||||
| } | ||||
|  | ||||
| var errLocked = errors.New("agent: locked") | ||||
|  | ||||
| // NewKeyring returns an Agent that holds keys in memory.  It is safe | ||||
| // for concurrent use by multiple goroutines. | ||||
| func NewKeyring() Agent { | ||||
| 	return &keyring{} | ||||
| } | ||||
|  | ||||
| // RemoveAll removes all identities. | ||||
| func (r *keyring) RemoveAll() error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.keys = nil | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // removeLocked does the actual key removal. The caller must already be holding the | ||||
| // keyring mutex. | ||||
| func (r *keyring) removeLocked(want []byte) error { | ||||
| 	found := false | ||||
| 	for i := 0; i < len(r.keys); { | ||||
| 		if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) { | ||||
| 			found = true | ||||
| 			r.keys[i] = r.keys[len(r.keys)-1] | ||||
| 			r.keys = r.keys[:len(r.keys)-1] | ||||
| 			continue | ||||
| 		} else { | ||||
| 			i++ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if !found { | ||||
| 		return errors.New("agent: key not found") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove removes all identities with the given public key. | ||||
| func (r *keyring) Remove(key ssh.PublicKey) error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return errLocked | ||||
| 	} | ||||
|  | ||||
| 	return r.removeLocked(key.Marshal()) | ||||
| } | ||||
|  | ||||
| // Lock locks the agent. Sign and Remove will fail, and List will return an empty list. | ||||
| func (r *keyring) Lock(passphrase []byte) error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.locked = true | ||||
| 	r.passphrase = passphrase | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Unlock undoes the effect of Lock | ||||
| func (r *keyring) Unlock(passphrase []byte) error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if !r.locked { | ||||
| 		return errors.New("agent: not locked") | ||||
| 	} | ||||
| 	if len(passphrase) != len(r.passphrase) || 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) { | ||||
| 		return fmt.Errorf("agent: incorrect passphrase") | ||||
| 	} | ||||
|  | ||||
| 	r.locked = false | ||||
| 	r.passphrase = nil | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // expireKeysLocked removes expired keys from the keyring. If a key was added | ||||
| // with a lifetimesecs contraint and seconds >= lifetimesecs seconds have | ||||
| // ellapsed, it is removed. The caller *must* be holding the keyring mutex. | ||||
| func (r *keyring) expireKeysLocked() { | ||||
| 	for _, k := range r.keys { | ||||
| 		if k.expire != nil && time.Now().After(*k.expire) { | ||||
| 			r.removeLocked(k.signer.PublicKey().Marshal()) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // List returns the identities known to the agent. | ||||
| func (r *keyring) List() ([]*Key, error) { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		// section 2.7: locked agents return empty. | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	r.expireKeysLocked() | ||||
| 	var ids []*Key | ||||
| 	for _, k := range r.keys { | ||||
| 		pub := k.signer.PublicKey() | ||||
| 		ids = append(ids, &Key{ | ||||
| 			Format:  pub.Type(), | ||||
| 			Blob:    pub.Marshal(), | ||||
| 			Comment: k.comment}) | ||||
| 	} | ||||
| 	return ids, nil | ||||
| } | ||||
|  | ||||
| // Insert adds a private key to the keyring. If a certificate | ||||
| // is given, that certificate is added as public key. Note that | ||||
| // any constraints given are ignored. | ||||
| func (r *keyring) Add(key AddedKey) error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return errLocked | ||||
| 	} | ||||
| 	signer, err := ssh.NewSignerFromKey(key.PrivateKey) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if cert := key.Certificate; cert != nil { | ||||
| 		signer, err = ssh.NewCertSigner(cert, signer) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	p := privKey{ | ||||
| 		signer:  signer, | ||||
| 		comment: key.Comment, | ||||
| 	} | ||||
|  | ||||
| 	if key.LifetimeSecs > 0 { | ||||
| 		t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second) | ||||
| 		p.expire = &t | ||||
| 	} | ||||
|  | ||||
| 	r.keys = append(r.keys, p) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Sign returns a signature for the data. | ||||
| func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return nil, errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.expireKeysLocked() | ||||
| 	wanted := key.Marshal() | ||||
| 	for _, k := range r.keys { | ||||
| 		if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) { | ||||
| 			return k.signer.Sign(rand.Reader, data) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, errors.New("not found") | ||||
| } | ||||
|  | ||||
| // Signers returns signers for all the known keys. | ||||
| func (r *keyring) Signers() ([]ssh.Signer, error) { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return nil, errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.expireKeysLocked() | ||||
| 	s := make([]ssh.Signer, 0, len(r.keys)) | ||||
| 	for _, k := range r.keys { | ||||
| 		s = append(s, k.signer) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
							
								
								
									
										523
									
								
								vendor/golang.org/x/crypto/ssh/agent/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								vendor/golang.org/x/crypto/ssh/agent/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,523 @@ | ||||
| // Copyright 2012 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 agent | ||||
|  | ||||
| import ( | ||||
| 	"crypto/dsa" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rsa" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"math/big" | ||||
|  | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // Server wraps an Agent and uses it to implement the agent side of | ||||
| // the SSH-agent, wire protocol. | ||||
| type server struct { | ||||
| 	agent Agent | ||||
| } | ||||
|  | ||||
| func (s *server) processRequestBytes(reqData []byte) []byte { | ||||
| 	rep, err := s.processRequest(reqData) | ||||
| 	if err != nil { | ||||
| 		if err != errLocked { | ||||
| 			// TODO(hanwen): provide better logging interface? | ||||
| 			log.Printf("agent %d: %v", reqData[0], err) | ||||
| 		} | ||||
| 		return []byte{agentFailure} | ||||
| 	} | ||||
|  | ||||
| 	if err == nil && rep == nil { | ||||
| 		return []byte{agentSuccess} | ||||
| 	} | ||||
|  | ||||
| 	return ssh.Marshal(rep) | ||||
| } | ||||
|  | ||||
| func marshalKey(k *Key) []byte { | ||||
| 	var record struct { | ||||
| 		Blob    []byte | ||||
| 		Comment string | ||||
| 	} | ||||
| 	record.Blob = k.Marshal() | ||||
| 	record.Comment = k.Comment | ||||
|  | ||||
| 	return ssh.Marshal(&record) | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.5.1. | ||||
| const agentV1IdentitiesAnswer = 2 | ||||
|  | ||||
| type agentV1IdentityMsg struct { | ||||
| 	Numkeys uint32 `sshtype:"2"` | ||||
| } | ||||
|  | ||||
| type agentRemoveIdentityMsg struct { | ||||
| 	KeyBlob []byte `sshtype:"18"` | ||||
| } | ||||
|  | ||||
| type agentLockMsg struct { | ||||
| 	Passphrase []byte `sshtype:"22"` | ||||
| } | ||||
|  | ||||
| type agentUnlockMsg struct { | ||||
| 	Passphrase []byte `sshtype:"23"` | ||||
| } | ||||
|  | ||||
| func (s *server) processRequest(data []byte) (interface{}, error) { | ||||
| 	switch data[0] { | ||||
| 	case agentRequestV1Identities: | ||||
| 		return &agentV1IdentityMsg{0}, nil | ||||
|  | ||||
| 	case agentRemoveAllV1Identities: | ||||
| 		return nil, nil | ||||
|  | ||||
| 	case agentRemoveIdentity: | ||||
| 		var req agentRemoveIdentityMsg | ||||
| 		if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		var wk wireKey | ||||
| 		if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob}) | ||||
|  | ||||
| 	case agentRemoveAllIdentities: | ||||
| 		return nil, s.agent.RemoveAll() | ||||
|  | ||||
| 	case agentLock: | ||||
| 		var req agentLockMsg | ||||
| 		if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		return nil, s.agent.Lock(req.Passphrase) | ||||
|  | ||||
| 	case agentUnlock: | ||||
| 		var req agentUnlockMsg | ||||
| 		if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, s.agent.Unlock(req.Passphrase) | ||||
|  | ||||
| 	case agentSignRequest: | ||||
| 		var req signRequestAgentMsg | ||||
| 		if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		var wk wireKey | ||||
| 		if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		k := &Key{ | ||||
| 			Format: wk.Format, | ||||
| 			Blob:   req.KeyBlob, | ||||
| 		} | ||||
|  | ||||
| 		sig, err := s.agent.Sign(k, req.Data) //  TODO(hanwen): flags. | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil | ||||
|  | ||||
| 	case agentRequestIdentities: | ||||
| 		keys, err := s.agent.List() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		rep := identitiesAnswerAgentMsg{ | ||||
| 			NumKeys: uint32(len(keys)), | ||||
| 		} | ||||
| 		for _, k := range keys { | ||||
| 			rep.Keys = append(rep.Keys, marshalKey(k)...) | ||||
| 		} | ||||
| 		return rep, nil | ||||
|  | ||||
| 	case agentAddIDConstrained, agentAddIdentity: | ||||
| 		return nil, s.insertIdentity(data) | ||||
| 	} | ||||
|  | ||||
| 	return nil, fmt.Errorf("unknown opcode %d", data[0]) | ||||
| } | ||||
|  | ||||
| func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse bool, extensions []ConstraintExtension, err error) { | ||||
| 	for len(constraints) != 0 { | ||||
| 		switch constraints[0] { | ||||
| 		case agentConstrainLifetime: | ||||
| 			lifetimeSecs = binary.BigEndian.Uint32(constraints[1:5]) | ||||
| 			constraints = constraints[5:] | ||||
| 		case agentConstrainConfirm: | ||||
| 			confirmBeforeUse = true | ||||
| 			constraints = constraints[1:] | ||||
| 		case agentConstrainExtension: | ||||
| 			var msg constrainExtensionAgentMsg | ||||
| 			if err = ssh.Unmarshal(constraints, &msg); err != nil { | ||||
| 				return 0, false, nil, err | ||||
| 			} | ||||
| 			extensions = append(extensions, ConstraintExtension{ | ||||
| 				ExtensionName:    msg.ExtensionName, | ||||
| 				ExtensionDetails: msg.ExtensionDetails, | ||||
| 			}) | ||||
| 			constraints = msg.Rest | ||||
| 		default: | ||||
| 			return 0, false, nil, fmt.Errorf("unknown constraint type: %d", constraints[0]) | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func setConstraints(key *AddedKey, constraintBytes []byte) error { | ||||
| 	lifetimeSecs, confirmBeforeUse, constraintExtensions, err := parseConstraints(constraintBytes) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	key.LifetimeSecs = lifetimeSecs | ||||
| 	key.ConfirmBeforeUse = confirmBeforeUse | ||||
| 	key.ConstraintExtensions = constraintExtensions | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func parseRSAKey(req []byte) (*AddedKey, error) { | ||||
| 	var k rsaKeyMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if k.E.BitLen() > 30 { | ||||
| 		return nil, errors.New("agent: RSA public exponent too large") | ||||
| 	} | ||||
| 	priv := &rsa.PrivateKey{ | ||||
| 		PublicKey: rsa.PublicKey{ | ||||
| 			E: int(k.E.Int64()), | ||||
| 			N: k.N, | ||||
| 		}, | ||||
| 		D:      k.D, | ||||
| 		Primes: []*big.Int{k.P, k.Q}, | ||||
| 	} | ||||
| 	priv.Precompute() | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func parseEd25519Key(req []byte) (*AddedKey, error) { | ||||
| 	var k ed25519KeyMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	priv := ed25519.PrivateKey(k.Priv) | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: &priv, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func parseDSAKey(req []byte) (*AddedKey, error) { | ||||
| 	var k dsaKeyMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	priv := &dsa.PrivateKey{ | ||||
| 		PublicKey: dsa.PublicKey{ | ||||
| 			Parameters: dsa.Parameters{ | ||||
| 				P: k.P, | ||||
| 				Q: k.Q, | ||||
| 				G: k.G, | ||||
| 			}, | ||||
| 			Y: k.Y, | ||||
| 		}, | ||||
| 		X: k.X, | ||||
| 	} | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) { | ||||
| 	priv = &ecdsa.PrivateKey{ | ||||
| 		D: privScalar, | ||||
| 	} | ||||
|  | ||||
| 	switch curveName { | ||||
| 	case "nistp256": | ||||
| 		priv.Curve = elliptic.P256() | ||||
| 	case "nistp384": | ||||
| 		priv.Curve = elliptic.P384() | ||||
| 	case "nistp521": | ||||
| 		priv.Curve = elliptic.P521() | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("agent: unknown curve %q", curveName) | ||||
| 	} | ||||
|  | ||||
| 	priv.X, priv.Y = elliptic.Unmarshal(priv.Curve, keyBytes) | ||||
| 	if priv.X == nil || priv.Y == nil { | ||||
| 		return nil, errors.New("agent: point not on curve") | ||||
| 	} | ||||
|  | ||||
| 	return priv, nil | ||||
| } | ||||
|  | ||||
| func parseEd25519Cert(req []byte) (*AddedKey, error) { | ||||
| 	var k ed25519CertMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	pubKey, err := ssh.ParsePublicKey(k.CertBytes) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	priv := ed25519.PrivateKey(k.Priv) | ||||
| 	cert, ok := pubKey.(*ssh.Certificate) | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("agent: bad ED25519 certificate") | ||||
| 	} | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func parseECDSAKey(req []byte) (*AddedKey, error) { | ||||
| 	var k ecdsaKeyMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	priv, err := unmarshalECDSA(k.Curve, k.KeyBytes, k.D) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func parseRSACert(req []byte) (*AddedKey, error) { | ||||
| 	var k rsaCertMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	pubKey, err := ssh.ParsePublicKey(k.CertBytes) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	cert, ok := pubKey.(*ssh.Certificate) | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("agent: bad RSA certificate") | ||||
| 	} | ||||
|  | ||||
| 	// An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go | ||||
| 	var rsaPub struct { | ||||
| 		Name string | ||||
| 		E    *big.Int | ||||
| 		N    *big.Int | ||||
| 	} | ||||
| 	if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil { | ||||
| 		return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if rsaPub.E.BitLen() > 30 { | ||||
| 		return nil, errors.New("agent: RSA public exponent too large") | ||||
| 	} | ||||
|  | ||||
| 	priv := rsa.PrivateKey{ | ||||
| 		PublicKey: rsa.PublicKey{ | ||||
| 			E: int(rsaPub.E.Int64()), | ||||
| 			N: rsaPub.N, | ||||
| 		}, | ||||
| 		D:      k.D, | ||||
| 		Primes: []*big.Int{k.Q, k.P}, | ||||
| 	} | ||||
| 	priv.Precompute() | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func parseDSACert(req []byte) (*AddedKey, error) { | ||||
| 	var k dsaCertMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	pubKey, err := ssh.ParsePublicKey(k.CertBytes) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	cert, ok := pubKey.(*ssh.Certificate) | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("agent: bad DSA certificate") | ||||
| 	} | ||||
|  | ||||
| 	// A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go | ||||
| 	var w struct { | ||||
| 		Name       string | ||||
| 		P, Q, G, Y *big.Int | ||||
| 	} | ||||
| 	if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil { | ||||
| 		return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	priv := &dsa.PrivateKey{ | ||||
| 		PublicKey: dsa.PublicKey{ | ||||
| 			Parameters: dsa.Parameters{ | ||||
| 				P: w.P, | ||||
| 				Q: w.Q, | ||||
| 				G: w.G, | ||||
| 			}, | ||||
| 			Y: w.Y, | ||||
| 		}, | ||||
| 		X: k.X, | ||||
| 	} | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func parseECDSACert(req []byte) (*AddedKey, error) { | ||||
| 	var k ecdsaCertMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	pubKey, err := ssh.ParsePublicKey(k.CertBytes) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	cert, ok := pubKey.(*ssh.Certificate) | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("agent: bad ECDSA certificate") | ||||
| 	} | ||||
|  | ||||
| 	// An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go | ||||
| 	var ecdsaPub struct { | ||||
| 		Name string | ||||
| 		ID   string | ||||
| 		Key  []byte | ||||
| 	} | ||||
| 	if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments} | ||||
| 	if err := setConstraints(addedKey, k.Constraints); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return addedKey, nil | ||||
| } | ||||
|  | ||||
| func (s *server) insertIdentity(req []byte) error { | ||||
| 	var record struct { | ||||
| 		Type string `sshtype:"17|25"` | ||||
| 		Rest []byte `ssh:"rest"` | ||||
| 	} | ||||
|  | ||||
| 	if err := ssh.Unmarshal(req, &record); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	var addedKey *AddedKey | ||||
| 	var err error | ||||
|  | ||||
| 	switch record.Type { | ||||
| 	case ssh.KeyAlgoRSA: | ||||
| 		addedKey, err = parseRSAKey(req) | ||||
| 	case ssh.KeyAlgoDSA: | ||||
| 		addedKey, err = parseDSAKey(req) | ||||
| 	case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521: | ||||
| 		addedKey, err = parseECDSAKey(req) | ||||
| 	case ssh.KeyAlgoED25519: | ||||
| 		addedKey, err = parseEd25519Key(req) | ||||
| 	case ssh.CertAlgoRSAv01: | ||||
| 		addedKey, err = parseRSACert(req) | ||||
| 	case ssh.CertAlgoDSAv01: | ||||
| 		addedKey, err = parseDSACert(req) | ||||
| 	case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01: | ||||
| 		addedKey, err = parseECDSACert(req) | ||||
| 	case ssh.CertAlgoED25519v01: | ||||
| 		addedKey, err = parseEd25519Cert(req) | ||||
| 	default: | ||||
| 		return fmt.Errorf("agent: not implemented: %q", record.Type) | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return s.agent.Add(*addedKey) | ||||
| } | ||||
|  | ||||
| // ServeAgent serves the agent protocol on the given connection. It | ||||
| // returns when an I/O error occurs. | ||||
| func ServeAgent(agent Agent, c io.ReadWriter) error { | ||||
| 	s := &server{agent} | ||||
|  | ||||
| 	var length [4]byte | ||||
| 	for { | ||||
| 		if _, err := io.ReadFull(c, length[:]); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		l := binary.BigEndian.Uint32(length[:]) | ||||
| 		if l > maxAgentResponseBytes { | ||||
| 			// We also cap requests. | ||||
| 			return fmt.Errorf("agent: request too large: %d", l) | ||||
| 		} | ||||
|  | ||||
| 		req := make([]byte, l) | ||||
| 		if _, err := io.ReadFull(c, req); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		repData := s.processRequestBytes(req) | ||||
| 		if len(repData) > maxAgentResponseBytes { | ||||
| 			return fmt.Errorf("agent: reply too large: %d bytes", len(repData)) | ||||
| 		} | ||||
|  | ||||
| 		binary.BigEndian.PutUint32(length[:], uint32(len(repData))) | ||||
| 		if _, err := c.Write(length[:]); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if _, err := c.Write(repData); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| // Copyright 2012 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| // buffer provides a linked list buffer for data exchange | ||||
| // between producer and consumer. Theoretically the buffer is | ||||
| // of unlimited capacity as it does no allocation of its own. | ||||
| type buffer struct { | ||||
| 	// protects concurrent access to head, tail and closed | ||||
| 	*sync.Cond | ||||
|  | ||||
| 	head *element // the buffer that will be read first | ||||
| 	tail *element // the buffer that will be read last | ||||
|  | ||||
| 	closed bool | ||||
| } | ||||
|  | ||||
| // An element represents a single link in a linked list. | ||||
| type element struct { | ||||
| 	buf  []byte | ||||
| 	next *element | ||||
| } | ||||
|  | ||||
| // newBuffer returns an empty buffer that is not closed. | ||||
| func newBuffer() *buffer { | ||||
| 	e := new(element) | ||||
| 	b := &buffer{ | ||||
| 		Cond: newCond(), | ||||
| 		head: e, | ||||
| 		tail: e, | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| // write makes buf available for Read to receive. | ||||
| // buf must not be modified after the call to write. | ||||
| func (b *buffer) write(buf []byte) { | ||||
| 	b.Cond.L.Lock() | ||||
| 	e := &element{buf: buf} | ||||
| 	b.tail.next = e | ||||
| 	b.tail = e | ||||
| 	b.Cond.Signal() | ||||
| 	b.Cond.L.Unlock() | ||||
| } | ||||
|  | ||||
| // eof closes the buffer. Reads from the buffer once all | ||||
| // the data has been consumed will receive io.EOF. | ||||
| func (b *buffer) eof() { | ||||
| 	b.Cond.L.Lock() | ||||
| 	b.closed = true | ||||
| 	b.Cond.Signal() | ||||
| 	b.Cond.L.Unlock() | ||||
| } | ||||
|  | ||||
| // Read reads data from the internal buffer in buf.  Reads will block | ||||
| // if no data is available, or until the buffer is closed. | ||||
| func (b *buffer) Read(buf []byte) (n int, err error) { | ||||
| 	b.Cond.L.Lock() | ||||
| 	defer b.Cond.L.Unlock() | ||||
|  | ||||
| 	for len(buf) > 0 { | ||||
| 		// if there is data in b.head, copy it | ||||
| 		if len(b.head.buf) > 0 { | ||||
| 			r := copy(buf, b.head.buf) | ||||
| 			buf, b.head.buf = buf[r:], b.head.buf[r:] | ||||
| 			n += r | ||||
| 			continue | ||||
| 		} | ||||
| 		// if there is a next buffer, make it the head | ||||
| 		if len(b.head.buf) == 0 && b.head != b.tail { | ||||
| 			b.head = b.head.next | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// if at least one byte has been copied, return | ||||
| 		if n > 0 { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		// if nothing was read, and there is nothing outstanding | ||||
| 		// check to see if the buffer is closed. | ||||
| 		if b.closed { | ||||
| 			err = io.EOF | ||||
| 			break | ||||
| 		} | ||||
| 		// out of buffers, wait for producer | ||||
| 		b.Cond.Wait() | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										519
									
								
								vendor/golang.org/x/crypto/ssh/certs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								vendor/golang.org/x/crypto/ssh/certs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,519 @@ | ||||
| // Copyright 2012 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"sort" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // These constants from [PROTOCOL.certkeys] represent the algorithm names | ||||
| // for certificate types supported by this package. | ||||
| const ( | ||||
| 	CertAlgoRSAv01      = "ssh-rsa-cert-v01@openssh.com" | ||||
| 	CertAlgoDSAv01      = "ssh-dss-cert-v01@openssh.com" | ||||
| 	CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" | ||||
| 	CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" | ||||
| 	CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" | ||||
| 	CertAlgoED25519v01  = "ssh-ed25519-cert-v01@openssh.com" | ||||
| ) | ||||
|  | ||||
| // Certificate types distinguish between host and user | ||||
| // certificates. The values can be set in the CertType field of | ||||
| // Certificate. | ||||
| const ( | ||||
| 	UserCert = 1 | ||||
| 	HostCert = 2 | ||||
| ) | ||||
|  | ||||
| // Signature represents a cryptographic signature. | ||||
| type Signature struct { | ||||
| 	Format string | ||||
| 	Blob   []byte | ||||
| } | ||||
|  | ||||
| // CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that | ||||
| // a certificate does not expire. | ||||
| const CertTimeInfinity = 1<<64 - 1 | ||||
|  | ||||
| // An Certificate represents an OpenSSH certificate as defined in | ||||
| // [PROTOCOL.certkeys]?rev=1.8. | ||||
| type Certificate struct { | ||||
| 	Nonce           []byte | ||||
| 	Key             PublicKey | ||||
| 	Serial          uint64 | ||||
| 	CertType        uint32 | ||||
| 	KeyId           string | ||||
| 	ValidPrincipals []string | ||||
| 	ValidAfter      uint64 | ||||
| 	ValidBefore     uint64 | ||||
| 	Permissions | ||||
| 	Reserved     []byte | ||||
| 	SignatureKey PublicKey | ||||
| 	Signature    *Signature | ||||
| } | ||||
|  | ||||
| // genericCertData holds the key-independent part of the certificate data. | ||||
| // Overall, certificates contain an nonce, public key fields and | ||||
| // key-independent fields. | ||||
| type genericCertData struct { | ||||
| 	Serial          uint64 | ||||
| 	CertType        uint32 | ||||
| 	KeyId           string | ||||
| 	ValidPrincipals []byte | ||||
| 	ValidAfter      uint64 | ||||
| 	ValidBefore     uint64 | ||||
| 	CriticalOptions []byte | ||||
| 	Extensions      []byte | ||||
| 	Reserved        []byte | ||||
| 	SignatureKey    []byte | ||||
| 	Signature       []byte | ||||
| } | ||||
|  | ||||
| func marshalStringList(namelist []string) []byte { | ||||
| 	var to []byte | ||||
| 	for _, name := range namelist { | ||||
| 		s := struct{ N string }{name} | ||||
| 		to = append(to, Marshal(&s)...) | ||||
| 	} | ||||
| 	return to | ||||
| } | ||||
|  | ||||
| type optionsTuple struct { | ||||
| 	Key   string | ||||
| 	Value []byte | ||||
| } | ||||
|  | ||||
| type optionsTupleValue struct { | ||||
| 	Value string | ||||
| } | ||||
|  | ||||
| // serialize a map of critical options or extensions | ||||
| // issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, | ||||
| // we need two length prefixes for a non-empty string value | ||||
| func marshalTuples(tups map[string]string) []byte { | ||||
| 	keys := make([]string, 0, len(tups)) | ||||
| 	for key := range tups { | ||||
| 		keys = append(keys, key) | ||||
| 	} | ||||
| 	sort.Strings(keys) | ||||
|  | ||||
| 	var ret []byte | ||||
| 	for _, key := range keys { | ||||
| 		s := optionsTuple{Key: key} | ||||
| 		if value := tups[key]; len(value) > 0 { | ||||
| 			s.Value = Marshal(&optionsTupleValue{value}) | ||||
| 		} | ||||
| 		ret = append(ret, Marshal(&s)...) | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
| // issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, | ||||
| // we need two length prefixes for a non-empty option value | ||||
| func parseTuples(in []byte) (map[string]string, error) { | ||||
| 	tups := map[string]string{} | ||||
| 	var lastKey string | ||||
| 	var haveLastKey bool | ||||
|  | ||||
| 	for len(in) > 0 { | ||||
| 		var key, val, extra []byte | ||||
| 		var ok bool | ||||
|  | ||||
| 		if key, in, ok = parseString(in); !ok { | ||||
| 			return nil, errShortRead | ||||
| 		} | ||||
| 		keyStr := string(key) | ||||
| 		// according to [PROTOCOL.certkeys], the names must be in | ||||
| 		// lexical order. | ||||
| 		if haveLastKey && keyStr <= lastKey { | ||||
| 			return nil, fmt.Errorf("ssh: certificate options are not in lexical order") | ||||
| 		} | ||||
| 		lastKey, haveLastKey = keyStr, true | ||||
| 		// the next field is a data field, which if non-empty has a string embedded | ||||
| 		if val, in, ok = parseString(in); !ok { | ||||
| 			return nil, errShortRead | ||||
| 		} | ||||
| 		if len(val) > 0 { | ||||
| 			val, extra, ok = parseString(val) | ||||
| 			if !ok { | ||||
| 				return nil, errShortRead | ||||
| 			} | ||||
| 			if len(extra) > 0 { | ||||
| 				return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value") | ||||
| 			} | ||||
| 			tups[keyStr] = string(val) | ||||
| 		} else { | ||||
| 			tups[keyStr] = "" | ||||
| 		} | ||||
| 	} | ||||
| 	return tups, nil | ||||
| } | ||||
|  | ||||
| func parseCert(in []byte, privAlgo string) (*Certificate, error) { | ||||
| 	nonce, rest, ok := parseString(in) | ||||
| 	if !ok { | ||||
| 		return nil, errShortRead | ||||
| 	} | ||||
|  | ||||
| 	key, rest, err := parsePubKey(rest, privAlgo) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var g genericCertData | ||||
| 	if err := Unmarshal(rest, &g); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	c := &Certificate{ | ||||
| 		Nonce:       nonce, | ||||
| 		Key:         key, | ||||
| 		Serial:      g.Serial, | ||||
| 		CertType:    g.CertType, | ||||
| 		KeyId:       g.KeyId, | ||||
| 		ValidAfter:  g.ValidAfter, | ||||
| 		ValidBefore: g.ValidBefore, | ||||
| 	} | ||||
|  | ||||
| 	for principals := g.ValidPrincipals; len(principals) > 0; { | ||||
| 		principal, rest, ok := parseString(principals) | ||||
| 		if !ok { | ||||
| 			return nil, errShortRead | ||||
| 		} | ||||
| 		c.ValidPrincipals = append(c.ValidPrincipals, string(principal)) | ||||
| 		principals = rest | ||||
| 	} | ||||
|  | ||||
| 	c.CriticalOptions, err = parseTuples(g.CriticalOptions) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c.Extensions, err = parseTuples(g.Extensions) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c.Reserved = g.Reserved | ||||
| 	k, err := ParsePublicKey(g.SignatureKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	c.SignatureKey = k | ||||
| 	c.Signature, rest, ok = parseSignatureBody(g.Signature) | ||||
| 	if !ok || len(rest) > 0 { | ||||
| 		return nil, errors.New("ssh: signature parse error") | ||||
| 	} | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| type openSSHCertSigner struct { | ||||
| 	pub    *Certificate | ||||
| 	signer Signer | ||||
| } | ||||
|  | ||||
| // NewCertSigner returns a Signer that signs with the given Certificate, whose | ||||
| // private key is held by signer. It returns an error if the public key in cert | ||||
| // doesn't match the key used by signer. | ||||
| func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { | ||||
| 	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { | ||||
| 		return nil, errors.New("ssh: signer and cert have different public key") | ||||
| 	} | ||||
|  | ||||
| 	return &openSSHCertSigner{cert, signer}, nil | ||||
| } | ||||
|  | ||||
| func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { | ||||
| 	return s.signer.Sign(rand, data) | ||||
| } | ||||
|  | ||||
| func (s *openSSHCertSigner) PublicKey() PublicKey { | ||||
| 	return s.pub | ||||
| } | ||||
|  | ||||
| const sourceAddressCriticalOption = "source-address" | ||||
|  | ||||
| // CertChecker does the work of verifying a certificate. Its methods | ||||
| // can be plugged into ClientConfig.HostKeyCallback and | ||||
| // ServerConfig.PublicKeyCallback. For the CertChecker to work, | ||||
| // minimally, the IsAuthority callback should be set. | ||||
| type CertChecker struct { | ||||
| 	// SupportedCriticalOptions lists the CriticalOptions that the | ||||
| 	// server application layer understands. These are only used | ||||
| 	// for user certificates. | ||||
| 	SupportedCriticalOptions []string | ||||
|  | ||||
| 	// IsUserAuthority should return true if the key is recognized as an | ||||
| 	// authority for the given user certificate. This allows for | ||||
| 	// certificates to be signed by other certificates. This must be set | ||||
| 	// if this CertChecker will be checking user certificates. | ||||
| 	IsUserAuthority func(auth PublicKey) bool | ||||
|  | ||||
| 	// IsHostAuthority should report whether the key is recognized as | ||||
| 	// an authority for this host. This allows for certificates to be | ||||
| 	// signed by other keys, and for those other keys to only be valid | ||||
| 	// signers for particular hostnames. This must be set if this | ||||
| 	// CertChecker will be checking host certificates. | ||||
| 	IsHostAuthority func(auth PublicKey, address string) bool | ||||
|  | ||||
| 	// Clock is used for verifying time stamps. If nil, time.Now | ||||
| 	// is used. | ||||
| 	Clock func() time.Time | ||||
|  | ||||
| 	// UserKeyFallback is called when CertChecker.Authenticate encounters a | ||||
| 	// public key that is not a certificate. It must implement validation | ||||
| 	// of user keys or else, if nil, all such keys are rejected. | ||||
| 	UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) | ||||
|  | ||||
| 	// HostKeyFallback is called when CertChecker.CheckHostKey encounters a | ||||
| 	// public key that is not a certificate. It must implement host key | ||||
| 	// validation or else, if nil, all such keys are rejected. | ||||
| 	HostKeyFallback HostKeyCallback | ||||
|  | ||||
| 	// IsRevoked is called for each certificate so that revocation checking | ||||
| 	// can be implemented. It should return true if the given certificate | ||||
| 	// is revoked and false otherwise. If nil, no certificates are | ||||
| 	// considered to have been revoked. | ||||
| 	IsRevoked func(cert *Certificate) bool | ||||
| } | ||||
|  | ||||
| // CheckHostKey checks a host key certificate. This method can be | ||||
| // plugged into ClientConfig.HostKeyCallback. | ||||
| func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error { | ||||
| 	cert, ok := key.(*Certificate) | ||||
| 	if !ok { | ||||
| 		if c.HostKeyFallback != nil { | ||||
| 			return c.HostKeyFallback(addr, remote, key) | ||||
| 		} | ||||
| 		return errors.New("ssh: non-certificate host key") | ||||
| 	} | ||||
| 	if cert.CertType != HostCert { | ||||
| 		return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) | ||||
| 	} | ||||
| 	if !c.IsHostAuthority(cert.SignatureKey, addr) { | ||||
| 		return fmt.Errorf("ssh: no authorities for hostname: %v", addr) | ||||
| 	} | ||||
|  | ||||
| 	hostname, _, err := net.SplitHostPort(addr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Pass hostname only as principal for host certificates (consistent with OpenSSH) | ||||
| 	return c.CheckCert(hostname, cert) | ||||
| } | ||||
|  | ||||
| // Authenticate checks a user certificate. Authenticate can be used as | ||||
| // a value for ServerConfig.PublicKeyCallback. | ||||
| func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) { | ||||
| 	cert, ok := pubKey.(*Certificate) | ||||
| 	if !ok { | ||||
| 		if c.UserKeyFallback != nil { | ||||
| 			return c.UserKeyFallback(conn, pubKey) | ||||
| 		} | ||||
| 		return nil, errors.New("ssh: normal key pairs not accepted") | ||||
| 	} | ||||
|  | ||||
| 	if cert.CertType != UserCert { | ||||
| 		return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) | ||||
| 	} | ||||
| 	if !c.IsUserAuthority(cert.SignatureKey) { | ||||
| 		return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority") | ||||
| 	} | ||||
|  | ||||
| 	if err := c.CheckCert(conn.User(), cert); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &cert.Permissions, nil | ||||
| } | ||||
|  | ||||
| // CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and | ||||
| // the signature of the certificate. | ||||
| func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { | ||||
| 	if c.IsRevoked != nil && c.IsRevoked(cert) { | ||||
| 		return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial) | ||||
| 	} | ||||
|  | ||||
| 	for opt := range cert.CriticalOptions { | ||||
| 		// sourceAddressCriticalOption will be enforced by | ||||
| 		// serverAuthenticate | ||||
| 		if opt == sourceAddressCriticalOption { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		found := false | ||||
| 		for _, supp := range c.SupportedCriticalOptions { | ||||
| 			if supp == opt { | ||||
| 				found = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if !found { | ||||
| 			return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(cert.ValidPrincipals) > 0 { | ||||
| 		// By default, certs are valid for all users/hosts. | ||||
| 		found := false | ||||
| 		for _, p := range cert.ValidPrincipals { | ||||
| 			if p == principal { | ||||
| 				found = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if !found { | ||||
| 			return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	clock := c.Clock | ||||
| 	if clock == nil { | ||||
| 		clock = time.Now | ||||
| 	} | ||||
|  | ||||
| 	unixNow := clock().Unix() | ||||
| 	if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { | ||||
| 		return fmt.Errorf("ssh: cert is not yet valid") | ||||
| 	} | ||||
| 	if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { | ||||
| 		return fmt.Errorf("ssh: cert has expired") | ||||
| 	} | ||||
| 	if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { | ||||
| 		return fmt.Errorf("ssh: certificate signature does not verify") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SignCert sets c.SignatureKey to the authority's public key and stores a | ||||
| // Signature, by authority, in the certificate. | ||||
| func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { | ||||
| 	c.Nonce = make([]byte, 32) | ||||
| 	if _, err := io.ReadFull(rand, c.Nonce); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.SignatureKey = authority.PublicKey() | ||||
|  | ||||
| 	sig, err := authority.Sign(rand, c.bytesForSigning()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.Signature = sig | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| var certAlgoNames = map[string]string{ | ||||
| 	KeyAlgoRSA:      CertAlgoRSAv01, | ||||
| 	KeyAlgoDSA:      CertAlgoDSAv01, | ||||
| 	KeyAlgoECDSA256: CertAlgoECDSA256v01, | ||||
| 	KeyAlgoECDSA384: CertAlgoECDSA384v01, | ||||
| 	KeyAlgoECDSA521: CertAlgoECDSA521v01, | ||||
| 	KeyAlgoED25519:  CertAlgoED25519v01, | ||||
| } | ||||
|  | ||||
| // certToPrivAlgo returns the underlying algorithm for a certificate algorithm. | ||||
| // Panics if a non-certificate algorithm is passed. | ||||
| func certToPrivAlgo(algo string) string { | ||||
| 	for privAlgo, pubAlgo := range certAlgoNames { | ||||
| 		if pubAlgo == algo { | ||||
| 			return privAlgo | ||||
| 		} | ||||
| 	} | ||||
| 	panic("unknown cert algorithm") | ||||
| } | ||||
|  | ||||
| func (cert *Certificate) bytesForSigning() []byte { | ||||
| 	c2 := *cert | ||||
| 	c2.Signature = nil | ||||
| 	out := c2.Marshal() | ||||
| 	// Drop trailing signature length. | ||||
| 	return out[:len(out)-4] | ||||
| } | ||||
|  | ||||
| // Marshal serializes c into OpenSSH's wire format. It is part of the | ||||
| // PublicKey interface. | ||||
| func (c *Certificate) Marshal() []byte { | ||||
| 	generic := genericCertData{ | ||||
| 		Serial:          c.Serial, | ||||
| 		CertType:        c.CertType, | ||||
| 		KeyId:           c.KeyId, | ||||
| 		ValidPrincipals: marshalStringList(c.ValidPrincipals), | ||||
| 		ValidAfter:      uint64(c.ValidAfter), | ||||
| 		ValidBefore:     uint64(c.ValidBefore), | ||||
| 		CriticalOptions: marshalTuples(c.CriticalOptions), | ||||
| 		Extensions:      marshalTuples(c.Extensions), | ||||
| 		Reserved:        c.Reserved, | ||||
| 		SignatureKey:    c.SignatureKey.Marshal(), | ||||
| 	} | ||||
| 	if c.Signature != nil { | ||||
| 		generic.Signature = Marshal(c.Signature) | ||||
| 	} | ||||
| 	genericBytes := Marshal(&generic) | ||||
| 	keyBytes := c.Key.Marshal() | ||||
| 	_, keyBytes, _ = parseString(keyBytes) | ||||
| 	prefix := Marshal(&struct { | ||||
| 		Name  string | ||||
| 		Nonce []byte | ||||
| 		Key   []byte `ssh:"rest"` | ||||
| 	}{c.Type(), c.Nonce, keyBytes}) | ||||
|  | ||||
| 	result := make([]byte, 0, len(prefix)+len(genericBytes)) | ||||
| 	result = append(result, prefix...) | ||||
| 	result = append(result, genericBytes...) | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| // Type returns the key name. It is part of the PublicKey interface. | ||||
| func (c *Certificate) Type() string { | ||||
| 	algo, ok := certAlgoNames[c.Key.Type()] | ||||
| 	if !ok { | ||||
| 		panic("unknown cert key type " + c.Key.Type()) | ||||
| 	} | ||||
| 	return algo | ||||
| } | ||||
|  | ||||
| // Verify verifies a signature against the certificate's public | ||||
| // key. It is part of the PublicKey interface. | ||||
| func (c *Certificate) Verify(data []byte, sig *Signature) error { | ||||
| 	return c.Key.Verify(data, sig) | ||||
| } | ||||
|  | ||||
| func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) { | ||||
| 	format, in, ok := parseString(in) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	out = &Signature{ | ||||
| 		Format: string(format), | ||||
| 	} | ||||
|  | ||||
| 	if out.Blob, in, ok = parseString(in); !ok { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	return out, in, ok | ||||
| } | ||||
|  | ||||
| func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) { | ||||
| 	sigBytes, rest, ok := parseString(in) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	out, trailing, ok := parseSignatureBody(sigBytes) | ||||
| 	if !ok || len(trailing) > 0 { | ||||
| 		return nil, nil, false | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										633
									
								
								vendor/golang.org/x/crypto/ssh/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										633
									
								
								vendor/golang.org/x/crypto/ssh/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,633 @@ | ||||
| // Copyright 2011 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	minPacketLength = 9 | ||||
| 	// channelMaxPacket contains the maximum number of bytes that will be | ||||
| 	// sent in a single packet. As per RFC 4253, section 6.1, 32k is also | ||||
| 	// the minimum. | ||||
| 	channelMaxPacket = 1 << 15 | ||||
| 	// We follow OpenSSH here. | ||||
| 	channelWindowSize = 64 * channelMaxPacket | ||||
| ) | ||||
|  | ||||
| // NewChannel represents an incoming request to a channel. It must either be | ||||
| // accepted for use by calling Accept, or rejected by calling Reject. | ||||
| type NewChannel interface { | ||||
| 	// Accept accepts the channel creation request. It returns the Channel | ||||
| 	// and a Go channel containing SSH requests. The Go channel must be | ||||
| 	// serviced otherwise the Channel will hang. | ||||
| 	Accept() (Channel, <-chan *Request, error) | ||||
|  | ||||
| 	// Reject rejects the channel creation request. After calling | ||||
| 	// this, no other methods on the Channel may be called. | ||||
| 	Reject(reason RejectionReason, message string) error | ||||
|  | ||||
| 	// ChannelType returns the type of the channel, as supplied by the | ||||
| 	// client. | ||||
| 	ChannelType() string | ||||
|  | ||||
| 	// ExtraData returns the arbitrary payload for this channel, as supplied | ||||
| 	// by the client. This data is specific to the channel type. | ||||
| 	ExtraData() []byte | ||||
| } | ||||
|  | ||||
| // A Channel is an ordered, reliable, flow-controlled, duplex stream | ||||
| // that is multiplexed over an SSH connection. | ||||
| type Channel interface { | ||||
| 	// Read reads up to len(data) bytes from the channel. | ||||
| 	Read(data []byte) (int, error) | ||||
|  | ||||
| 	// Write writes len(data) bytes to the channel. | ||||
| 	Write(data []byte) (int, error) | ||||
|  | ||||
| 	// Close signals end of channel use. No data may be sent after this | ||||
| 	// call. | ||||
| 	Close() error | ||||
|  | ||||
| 	// CloseWrite signals the end of sending in-band | ||||
| 	// data. Requests may still be sent, and the other side may | ||||
| 	// still send data | ||||
| 	CloseWrite() error | ||||
|  | ||||
| 	// SendRequest sends a channel request.  If wantReply is true, | ||||
| 	// it will wait for a reply and return the result as a | ||||
| 	// boolean, otherwise the return value will be false. Channel | ||||
| 	// requests are out-of-band messages so they may be sent even | ||||
| 	// if the data stream is closed or blocked by flow control. | ||||
| 	// If the channel is closed before a reply is returned, io.EOF | ||||
| 	// is returned. | ||||
| 	SendRequest(name string, wantReply bool, payload []byte) (bool, error) | ||||
|  | ||||
| 	// Stderr returns an io.ReadWriter that writes to this channel | ||||
| 	// with the extended data type set to stderr. Stderr may | ||||
| 	// safely be read and written from a different goroutine than | ||||
| 	// Read and Write respectively. | ||||
| 	Stderr() io.ReadWriter | ||||
| } | ||||
|  | ||||
| // Request is a request sent outside of the normal stream of | ||||
| // data. Requests can either be specific to an SSH channel, or they | ||||
| // can be global. | ||||
| type Request struct { | ||||
| 	Type      string | ||||
| 	WantReply bool | ||||
| 	Payload   []byte | ||||
|  | ||||
| 	ch  *channel | ||||
| 	mux *mux | ||||
| } | ||||
|  | ||||
| // Reply sends a response to a request. It must be called for all requests | ||||
| // where WantReply is true and is a no-op otherwise. The payload argument is | ||||
| // ignored for replies to channel-specific requests. | ||||
| func (r *Request) Reply(ok bool, payload []byte) error { | ||||
| 	if !r.WantReply { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if r.ch == nil { | ||||
| 		return r.mux.ackRequest(ok, payload) | ||||
| 	} | ||||
|  | ||||
| 	return r.ch.ackRequest(ok) | ||||
| } | ||||
|  | ||||
| // RejectionReason is an enumeration used when rejecting channel creation | ||||
| // requests. See RFC 4254, section 5.1. | ||||
| type RejectionReason uint32 | ||||
|  | ||||
| const ( | ||||
| 	Prohibited RejectionReason = iota + 1 | ||||
| 	ConnectionFailed | ||||
| 	UnknownChannelType | ||||
| 	ResourceShortage | ||||
| ) | ||||
|  | ||||
| // String converts the rejection reason to human readable form. | ||||
| func (r RejectionReason) String() string { | ||||
| 	switch r { | ||||
| 	case Prohibited: | ||||
| 		return "administratively prohibited" | ||||
| 	case ConnectionFailed: | ||||
| 		return "connect failed" | ||||
| 	case UnknownChannelType: | ||||
| 		return "unknown channel type" | ||||
| 	case ResourceShortage: | ||||
| 		return "resource shortage" | ||||
| 	} | ||||
| 	return fmt.Sprintf("unknown reason %d", int(r)) | ||||
| } | ||||
|  | ||||
| func min(a uint32, b int) uint32 { | ||||
| 	if a < uint32(b) { | ||||
| 		return a | ||||
| 	} | ||||
| 	return uint32(b) | ||||
| } | ||||
|  | ||||
| type channelDirection uint8 | ||||
|  | ||||
| const ( | ||||
| 	channelInbound channelDirection = iota | ||||
| 	channelOutbound | ||||
| ) | ||||
|  | ||||
| // channel is an implementation of the Channel interface that works | ||||
| // with the mux class. | ||||
| type channel struct { | ||||
| 	// R/O after creation | ||||
| 	chanType          string | ||||
| 	extraData         []byte | ||||
| 	localId, remoteId uint32 | ||||
|  | ||||
| 	// maxIncomingPayload and maxRemotePayload are the maximum | ||||
| 	// payload sizes of normal and extended data packets for | ||||
| 	// receiving and sending, respectively. The wire packet will | ||||
| 	// be 9 or 13 bytes larger (excluding encryption overhead). | ||||
| 	maxIncomingPayload uint32 | ||||
| 	maxRemotePayload   uint32 | ||||
|  | ||||
| 	mux *mux | ||||
|  | ||||
| 	// decided is set to true if an accept or reject message has been sent | ||||
| 	// (for outbound channels) or received (for inbound channels). | ||||
| 	decided bool | ||||
|  | ||||
| 	// direction contains either channelOutbound, for channels created | ||||
| 	// locally, or channelInbound, for channels created by the peer. | ||||
| 	direction channelDirection | ||||
|  | ||||
| 	// Pending internal channel messages. | ||||
| 	msg chan interface{} | ||||
|  | ||||
| 	// Since requests have no ID, there can be only one request | ||||
| 	// with WantReply=true outstanding.  This lock is held by a | ||||
| 	// goroutine that has such an outgoing request pending. | ||||
| 	sentRequestMu sync.Mutex | ||||
|  | ||||
| 	incomingRequests chan *Request | ||||
|  | ||||
| 	sentEOF bool | ||||
|  | ||||
| 	// thread-safe data | ||||
| 	remoteWin  window | ||||
| 	pending    *buffer | ||||
| 	extPending *buffer | ||||
|  | ||||
| 	// windowMu protects myWindow, the flow-control window. | ||||
| 	windowMu sync.Mutex | ||||
| 	myWindow uint32 | ||||
|  | ||||
| 	// writeMu serializes calls to mux.conn.writePacket() and | ||||
| 	// protects sentClose and packetPool. This mutex must be | ||||
| 	// different from windowMu, as writePacket can block if there | ||||
| 	// is a key exchange pending. | ||||
| 	writeMu   sync.Mutex | ||||
| 	sentClose bool | ||||
|  | ||||
| 	// packetPool has a buffer for each extended channel ID to | ||||
| 	// save allocations during writes. | ||||
| 	packetPool map[uint32][]byte | ||||
| } | ||||
|  | ||||
| // writePacket sends a packet. If the packet is a channel close, it updates | ||||
| // sentClose. This method takes the lock c.writeMu. | ||||
| func (ch *channel) writePacket(packet []byte) error { | ||||
| 	ch.writeMu.Lock() | ||||
| 	if ch.sentClose { | ||||
| 		ch.writeMu.Unlock() | ||||
| 		return io.EOF | ||||
| 	} | ||||
| 	ch.sentClose = (packet[0] == msgChannelClose) | ||||
| 	err := ch.mux.conn.writePacket(packet) | ||||
| 	ch.writeMu.Unlock() | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (ch *channel) sendMessage(msg interface{}) error { | ||||
| 	if debugMux { | ||||
| 		log.Printf("send(%d): %#v", ch.mux.chanList.offset, msg) | ||||
| 	} | ||||
|  | ||||
| 	p := Marshal(msg) | ||||
| 	binary.BigEndian.PutUint32(p[1:], ch.remoteId) | ||||
| 	return ch.writePacket(p) | ||||
| } | ||||
|  | ||||
| // WriteExtended writes data to a specific extended stream. These streams are | ||||
| // used, for example, for stderr. | ||||
| func (ch *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) { | ||||
| 	if ch.sentEOF { | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
| 	// 1 byte message type, 4 bytes remoteId, 4 bytes data length | ||||
| 	opCode := byte(msgChannelData) | ||||
| 	headerLength := uint32(9) | ||||
| 	if extendedCode > 0 { | ||||
| 		headerLength += 4 | ||||
| 		opCode = msgChannelExtendedData | ||||
| 	} | ||||
|  | ||||
| 	ch.writeMu.Lock() | ||||
| 	packet := ch.packetPool[extendedCode] | ||||
| 	// We don't remove the buffer from packetPool, so | ||||
| 	// WriteExtended calls from different goroutines will be | ||||
| 	// flagged as errors by the race detector. | ||||
| 	ch.writeMu.Unlock() | ||||
|  | ||||
| 	for len(data) > 0 { | ||||
| 		space := min(ch.maxRemotePayload, len(data)) | ||||
| 		if space, err = ch.remoteWin.reserve(space); err != nil { | ||||
| 			return n, err | ||||
| 		} | ||||
| 		if want := headerLength + space; uint32(cap(packet)) < want { | ||||
| 			packet = make([]byte, want) | ||||
| 		} else { | ||||
| 			packet = packet[:want] | ||||
| 		} | ||||
|  | ||||
| 		todo := data[:space] | ||||
|  | ||||
| 		packet[0] = opCode | ||||
| 		binary.BigEndian.PutUint32(packet[1:], ch.remoteId) | ||||
| 		if extendedCode > 0 { | ||||
| 			binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode)) | ||||
| 		} | ||||
| 		binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo))) | ||||
| 		copy(packet[headerLength:], todo) | ||||
| 		if err = ch.writePacket(packet); err != nil { | ||||
| 			return n, err | ||||
| 		} | ||||
|  | ||||
| 		n += len(todo) | ||||
| 		data = data[len(todo):] | ||||
| 	} | ||||
|  | ||||
| 	ch.writeMu.Lock() | ||||
| 	ch.packetPool[extendedCode] = packet | ||||
| 	ch.writeMu.Unlock() | ||||
|  | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| func (ch *channel) handleData(packet []byte) error { | ||||
| 	headerLen := 9 | ||||
| 	isExtendedData := packet[0] == msgChannelExtendedData | ||||
| 	if isExtendedData { | ||||
| 		headerLen = 13 | ||||
| 	} | ||||
| 	if len(packet) < headerLen { | ||||
| 		// malformed data packet | ||||
| 		return parseError(packet[0]) | ||||
| 	} | ||||
|  | ||||
| 	var extended uint32 | ||||
| 	if isExtendedData { | ||||
| 		extended = binary.BigEndian.Uint32(packet[5:]) | ||||
| 	} | ||||
|  | ||||
| 	length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen]) | ||||
| 	if length == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if length > ch.maxIncomingPayload { | ||||
| 		// TODO(hanwen): should send Disconnect? | ||||
| 		return errors.New("ssh: incoming packet exceeds maximum payload size") | ||||
| 	} | ||||
|  | ||||
| 	data := packet[headerLen:] | ||||
| 	if length != uint32(len(data)) { | ||||
| 		return errors.New("ssh: wrong packet length") | ||||
| 	} | ||||
|  | ||||
| 	ch.windowMu.Lock() | ||||
| 	if ch.myWindow < length { | ||||
| 		ch.windowMu.Unlock() | ||||
| 		// TODO(hanwen): should send Disconnect with reason? | ||||
| 		return errors.New("ssh: remote side wrote too much") | ||||
| 	} | ||||
| 	ch.myWindow -= length | ||||
| 	ch.windowMu.Unlock() | ||||
|  | ||||
| 	if extended == 1 { | ||||
| 		ch.extPending.write(data) | ||||
| 	} else if extended > 0 { | ||||
| 		// discard other extended data. | ||||
| 	} else { | ||||
| 		ch.pending.write(data) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (c *channel) adjustWindow(n uint32) error { | ||||
| 	c.windowMu.Lock() | ||||
| 	// Since myWindow is managed on our side, and can never exceed | ||||
| 	// the initial window setting, we don't worry about overflow. | ||||
| 	c.myWindow += uint32(n) | ||||
| 	c.windowMu.Unlock() | ||||
| 	return c.sendMessage(windowAdjustMsg{ | ||||
| 		AdditionalBytes: uint32(n), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) { | ||||
| 	switch extended { | ||||
| 	case 1: | ||||
| 		n, err = c.extPending.Read(data) | ||||
| 	case 0: | ||||
| 		n, err = c.pending.Read(data) | ||||
| 	default: | ||||
| 		return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended) | ||||
| 	} | ||||
|  | ||||
| 	if n > 0 { | ||||
| 		err = c.adjustWindow(uint32(n)) | ||||
| 		// sendWindowAdjust can return io.EOF if the remote | ||||
| 		// peer has closed the connection, however we want to | ||||
| 		// defer forwarding io.EOF to the caller of Read until | ||||
| 		// the buffer has been drained. | ||||
| 		if n > 0 && err == io.EOF { | ||||
| 			err = nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| func (c *channel) close() { | ||||
| 	c.pending.eof() | ||||
| 	c.extPending.eof() | ||||
| 	close(c.msg) | ||||
| 	close(c.incomingRequests) | ||||
| 	c.writeMu.Lock() | ||||
| 	// This is not necessary for a normal channel teardown, but if | ||||
| 	// there was another error, it is. | ||||
| 	c.sentClose = true | ||||
| 	c.writeMu.Unlock() | ||||
| 	// Unblock writers. | ||||
| 	c.remoteWin.close() | ||||
| } | ||||
|  | ||||
| // responseMessageReceived is called when a success or failure message is | ||||
| // received on a channel to check that such a message is reasonable for the | ||||
| // given channel. | ||||
| func (ch *channel) responseMessageReceived() error { | ||||
| 	if ch.direction == channelInbound { | ||||
| 		return errors.New("ssh: channel response message received on inbound channel") | ||||
| 	} | ||||
| 	if ch.decided { | ||||
| 		return errors.New("ssh: duplicate response received for channel") | ||||
| 	} | ||||
| 	ch.decided = true | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (ch *channel) handlePacket(packet []byte) error { | ||||
| 	switch packet[0] { | ||||
| 	case msgChannelData, msgChannelExtendedData: | ||||
| 		return ch.handleData(packet) | ||||
| 	case msgChannelClose: | ||||
| 		ch.sendMessage(channelCloseMsg{PeersID: ch.remoteId}) | ||||
| 		ch.mux.chanList.remove(ch.localId) | ||||
| 		ch.close() | ||||
| 		return nil | ||||
| 	case msgChannelEOF: | ||||
| 		// RFC 4254 is mute on how EOF affects dataExt messages but | ||||
| 		// it is logical to signal EOF at the same time. | ||||
| 		ch.extPending.eof() | ||||
| 		ch.pending.eof() | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	decoded, err := decode(packet) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	switch msg := decoded.(type) { | ||||
| 	case *channelOpenFailureMsg: | ||||
| 		if err := ch.responseMessageReceived(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		ch.mux.chanList.remove(msg.PeersID) | ||||
| 		ch.msg <- msg | ||||
| 	case *channelOpenConfirmMsg: | ||||
| 		if err := ch.responseMessageReceived(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { | ||||
| 			return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize) | ||||
| 		} | ||||
| 		ch.remoteId = msg.MyID | ||||
| 		ch.maxRemotePayload = msg.MaxPacketSize | ||||
| 		ch.remoteWin.add(msg.MyWindow) | ||||
| 		ch.msg <- msg | ||||
| 	case *windowAdjustMsg: | ||||
| 		if !ch.remoteWin.add(msg.AdditionalBytes) { | ||||
| 			return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes) | ||||
| 		} | ||||
| 	case *channelRequestMsg: | ||||
| 		req := Request{ | ||||
| 			Type:      msg.Request, | ||||
| 			WantReply: msg.WantReply, | ||||
| 			Payload:   msg.RequestSpecificData, | ||||
| 			ch:        ch, | ||||
| 		} | ||||
|  | ||||
| 		ch.incomingRequests <- &req | ||||
| 	default: | ||||
| 		ch.msg <- msg | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel { | ||||
| 	ch := &channel{ | ||||
| 		remoteWin:        window{Cond: newCond()}, | ||||
| 		myWindow:         channelWindowSize, | ||||
| 		pending:          newBuffer(), | ||||
| 		extPending:       newBuffer(), | ||||
| 		direction:        direction, | ||||
| 		incomingRequests: make(chan *Request, chanSize), | ||||
| 		msg:              make(chan interface{}, chanSize), | ||||
| 		chanType:         chanType, | ||||
| 		extraData:        extraData, | ||||
| 		mux:              m, | ||||
| 		packetPool:       make(map[uint32][]byte), | ||||
| 	} | ||||
| 	ch.localId = m.chanList.add(ch) | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| var errUndecided = errors.New("ssh: must Accept or Reject channel") | ||||
| var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once") | ||||
|  | ||||
| type extChannel struct { | ||||
| 	code uint32 | ||||
| 	ch   *channel | ||||
| } | ||||
|  | ||||
| func (e *extChannel) Write(data []byte) (n int, err error) { | ||||
| 	return e.ch.WriteExtended(data, e.code) | ||||
| } | ||||
|  | ||||
| func (e *extChannel) Read(data []byte) (n int, err error) { | ||||
| 	return e.ch.ReadExtended(data, e.code) | ||||
| } | ||||
|  | ||||
| func (ch *channel) Accept() (Channel, <-chan *Request, error) { | ||||
| 	if ch.decided { | ||||
| 		return nil, nil, errDecidedAlready | ||||
| 	} | ||||
| 	ch.maxIncomingPayload = channelMaxPacket | ||||
| 	confirm := channelOpenConfirmMsg{ | ||||
| 		PeersID:       ch.remoteId, | ||||
| 		MyID:          ch.localId, | ||||
| 		MyWindow:      ch.myWindow, | ||||
| 		MaxPacketSize: ch.maxIncomingPayload, | ||||
| 	} | ||||
| 	ch.decided = true | ||||
| 	if err := ch.sendMessage(confirm); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	return ch, ch.incomingRequests, nil | ||||
| } | ||||
|  | ||||
| func (ch *channel) Reject(reason RejectionReason, message string) error { | ||||
| 	if ch.decided { | ||||
| 		return errDecidedAlready | ||||
| 	} | ||||
| 	reject := channelOpenFailureMsg{ | ||||
| 		PeersID:  ch.remoteId, | ||||
| 		Reason:   reason, | ||||
| 		Message:  message, | ||||
| 		Language: "en", | ||||
| 	} | ||||
| 	ch.decided = true | ||||
| 	return ch.sendMessage(reject) | ||||
| } | ||||
|  | ||||
| func (ch *channel) Read(data []byte) (int, error) { | ||||
| 	if !ch.decided { | ||||
| 		return 0, errUndecided | ||||
| 	} | ||||
| 	return ch.ReadExtended(data, 0) | ||||
| } | ||||
|  | ||||
| func (ch *channel) Write(data []byte) (int, error) { | ||||
| 	if !ch.decided { | ||||
| 		return 0, errUndecided | ||||
| 	} | ||||
| 	return ch.WriteExtended(data, 0) | ||||
| } | ||||
|  | ||||
| func (ch *channel) CloseWrite() error { | ||||
| 	if !ch.decided { | ||||
| 		return errUndecided | ||||
| 	} | ||||
| 	ch.sentEOF = true | ||||
| 	return ch.sendMessage(channelEOFMsg{ | ||||
| 		PeersID: ch.remoteId}) | ||||
| } | ||||
|  | ||||
| func (ch *channel) Close() error { | ||||
| 	if !ch.decided { | ||||
| 		return errUndecided | ||||
| 	} | ||||
|  | ||||
| 	return ch.sendMessage(channelCloseMsg{ | ||||
| 		PeersID: ch.remoteId}) | ||||
| } | ||||
|  | ||||
| // Extended returns an io.ReadWriter that sends and receives data on the given, | ||||
| // SSH extended stream. Such streams are used, for example, for stderr. | ||||
| func (ch *channel) Extended(code uint32) io.ReadWriter { | ||||
| 	if !ch.decided { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &extChannel{code, ch} | ||||
| } | ||||
|  | ||||
| func (ch *channel) Stderr() io.ReadWriter { | ||||
| 	return ch.Extended(1) | ||||
| } | ||||
|  | ||||
| func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { | ||||
| 	if !ch.decided { | ||||
| 		return false, errUndecided | ||||
| 	} | ||||
|  | ||||
| 	if wantReply { | ||||
| 		ch.sentRequestMu.Lock() | ||||
| 		defer ch.sentRequestMu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	msg := channelRequestMsg{ | ||||
| 		PeersID:             ch.remoteId, | ||||
| 		Request:             name, | ||||
| 		WantReply:           wantReply, | ||||
| 		RequestSpecificData: payload, | ||||
| 	} | ||||
|  | ||||
| 	if err := ch.sendMessage(msg); err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	if wantReply { | ||||
| 		m, ok := (<-ch.msg) | ||||
| 		if !ok { | ||||
| 			return false, io.EOF | ||||
| 		} | ||||
| 		switch m.(type) { | ||||
| 		case *channelRequestFailureMsg: | ||||
| 			return false, nil | ||||
| 		case *channelRequestSuccessMsg: | ||||
| 			return true, nil | ||||
| 		default: | ||||
| 			return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false, nil | ||||
| } | ||||
|  | ||||
| // ackRequest either sends an ack or nack to the channel request. | ||||
| func (ch *channel) ackRequest(ok bool) error { | ||||
| 	if !ch.decided { | ||||
| 		return errUndecided | ||||
| 	} | ||||
|  | ||||
| 	var msg interface{} | ||||
| 	if !ok { | ||||
| 		msg = channelRequestFailureMsg{ | ||||
| 			PeersID: ch.remoteId, | ||||
| 		} | ||||
| 	} else { | ||||
| 		msg = channelRequestSuccessMsg{ | ||||
| 			PeersID: ch.remoteId, | ||||
| 		} | ||||
| 	} | ||||
| 	return ch.sendMessage(msg) | ||||
| } | ||||
|  | ||||
| func (ch *channel) ChannelType() string { | ||||
| 	return ch.chanType | ||||
| } | ||||
|  | ||||
| func (ch *channel) ExtraData() []byte { | ||||
| 	return ch.extraData | ||||
| } | ||||
							
								
								
									
										629
									
								
								vendor/golang.org/x/crypto/ssh/cipher.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										629
									
								
								vendor/golang.org/x/crypto/ssh/cipher.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,629 @@ | ||||
| // Copyright 2011 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"crypto/aes" | ||||
| 	"crypto/cipher" | ||||
| 	"crypto/des" | ||||
| 	"crypto/rc4" | ||||
| 	"crypto/subtle" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"hash" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. | ||||
|  | ||||
| 	// RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations | ||||
| 	// MUST be able to process (plus a few more kilobytes for padding and mac). The RFC | ||||
| 	// indicates implementations SHOULD be able to handle larger packet sizes, but then | ||||
| 	// waffles on about reasonable limits. | ||||
| 	// | ||||
| 	// OpenSSH caps their maxPacket at 256kB so we choose to do | ||||
| 	// the same. maxPacket is also used to ensure that uint32 | ||||
| 	// length fields do not overflow, so it should remain well | ||||
| 	// below 4G. | ||||
| 	maxPacket = 256 * 1024 | ||||
| ) | ||||
|  | ||||
| // noneCipher implements cipher.Stream and provides no encryption. It is used | ||||
| // by the transport before the first key-exchange. | ||||
| type noneCipher struct{} | ||||
|  | ||||
| func (c noneCipher) XORKeyStream(dst, src []byte) { | ||||
| 	copy(dst, src) | ||||
| } | ||||
|  | ||||
| func newAESCTR(key, iv []byte) (cipher.Stream, error) { | ||||
| 	c, err := aes.NewCipher(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return cipher.NewCTR(c, iv), nil | ||||
| } | ||||
|  | ||||
| func newRC4(key, iv []byte) (cipher.Stream, error) { | ||||
| 	return rc4.NewCipher(key) | ||||
| } | ||||
|  | ||||
| type streamCipherMode struct { | ||||
| 	keySize    int | ||||
| 	ivSize     int | ||||
| 	skip       int | ||||
| 	createFunc func(key, iv []byte) (cipher.Stream, error) | ||||
| } | ||||
|  | ||||
| func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) { | ||||
| 	if len(key) < c.keySize { | ||||
| 		panic("ssh: key length too small for cipher") | ||||
| 	} | ||||
| 	if len(iv) < c.ivSize { | ||||
| 		panic("ssh: iv too small for cipher") | ||||
| 	} | ||||
|  | ||||
| 	stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var streamDump []byte | ||||
| 	if c.skip > 0 { | ||||
| 		streamDump = make([]byte, 512) | ||||
| 	} | ||||
|  | ||||
| 	for remainingToDump := c.skip; remainingToDump > 0; { | ||||
| 		dumpThisTime := remainingToDump | ||||
| 		if dumpThisTime > len(streamDump) { | ||||
| 			dumpThisTime = len(streamDump) | ||||
| 		} | ||||
| 		stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) | ||||
| 		remainingToDump -= dumpThisTime | ||||
| 	} | ||||
|  | ||||
| 	return stream, nil | ||||
| } | ||||
|  | ||||
| // cipherModes documents properties of supported ciphers. Ciphers not included | ||||
| // are not supported and will not be negotiated, even if explicitly requested in | ||||
| // ClientConfig.Crypto.Ciphers. | ||||
| var cipherModes = map[string]*streamCipherMode{ | ||||
| 	// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms | ||||
| 	// are defined in the order specified in the RFC. | ||||
| 	"aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, | ||||
| 	"aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, | ||||
| 	"aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, | ||||
|  | ||||
| 	// Ciphers from RFC4345, which introduces security-improved arcfour ciphers. | ||||
| 	// They are defined in the order specified in the RFC. | ||||
| 	"arcfour128": {16, 0, 1536, newRC4}, | ||||
| 	"arcfour256": {32, 0, 1536, newRC4}, | ||||
|  | ||||
| 	// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. | ||||
| 	// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and | ||||
| 	// RC4) has problems with weak keys, and should be used with caution." | ||||
| 	// RFC4345 introduces improved versions of Arcfour. | ||||
| 	"arcfour": {16, 0, 0, newRC4}, | ||||
|  | ||||
| 	// AES-GCM is not a stream cipher, so it is constructed with a | ||||
| 	// special case. If we add any more non-stream ciphers, we | ||||
| 	// should invest a cleaner way to do this. | ||||
| 	gcmCipherID: {16, 12, 0, nil}, | ||||
|  | ||||
| 	// CBC mode is insecure and so is not included in the default config. | ||||
| 	// (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely | ||||
| 	// needed, it's possible to specify a custom Config to enable it. | ||||
| 	// You should expect that an active attacker can recover plaintext if | ||||
| 	// you do. | ||||
| 	aes128cbcID: {16, aes.BlockSize, 0, nil}, | ||||
|  | ||||
| 	// 3des-cbc is insecure and is disabled by default. | ||||
| 	tripledescbcID: {24, des.BlockSize, 0, nil}, | ||||
| } | ||||
|  | ||||
| // prefixLen is the length of the packet prefix that contains the packet length | ||||
| // and number of padding bytes. | ||||
| const prefixLen = 5 | ||||
|  | ||||
| // streamPacketCipher is a packetCipher using a stream cipher. | ||||
| type streamPacketCipher struct { | ||||
| 	mac    hash.Hash | ||||
| 	cipher cipher.Stream | ||||
| 	etm    bool | ||||
|  | ||||
| 	// The following members are to avoid per-packet allocations. | ||||
| 	prefix      [prefixLen]byte | ||||
| 	seqNumBytes [4]byte | ||||
| 	padding     [2 * packetSizeMultiple]byte | ||||
| 	packetData  []byte | ||||
| 	macResult   []byte | ||||
| } | ||||
|  | ||||
| // readPacket reads and decrypt a single packet from the reader argument. | ||||
| func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | ||||
| 	if _, err := io.ReadFull(r, s.prefix[:]); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var encryptedPaddingLength [1]byte | ||||
| 	if s.mac != nil && s.etm { | ||||
| 		copy(encryptedPaddingLength[:], s.prefix[4:5]) | ||||
| 		s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) | ||||
| 	} else { | ||||
| 		s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) | ||||
| 	} | ||||
|  | ||||
| 	length := binary.BigEndian.Uint32(s.prefix[0:4]) | ||||
| 	paddingLength := uint32(s.prefix[4]) | ||||
|  | ||||
| 	var macSize uint32 | ||||
| 	if s.mac != nil { | ||||
| 		s.mac.Reset() | ||||
| 		binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) | ||||
| 		s.mac.Write(s.seqNumBytes[:]) | ||||
| 		if s.etm { | ||||
| 			s.mac.Write(s.prefix[:4]) | ||||
| 			s.mac.Write(encryptedPaddingLength[:]) | ||||
| 		} else { | ||||
| 			s.mac.Write(s.prefix[:]) | ||||
| 		} | ||||
| 		macSize = uint32(s.mac.Size()) | ||||
| 	} | ||||
|  | ||||
| 	if length <= paddingLength+1 { | ||||
| 		return nil, errors.New("ssh: invalid packet length, packet too small") | ||||
| 	} | ||||
|  | ||||
| 	if length > maxPacket { | ||||
| 		return nil, errors.New("ssh: invalid packet length, packet too large") | ||||
| 	} | ||||
|  | ||||
| 	// the maxPacket check above ensures that length-1+macSize | ||||
| 	// does not overflow. | ||||
| 	if uint32(cap(s.packetData)) < length-1+macSize { | ||||
| 		s.packetData = make([]byte, length-1+macSize) | ||||
| 	} else { | ||||
| 		s.packetData = s.packetData[:length-1+macSize] | ||||
| 	} | ||||
|  | ||||
| 	if _, err := io.ReadFull(r, s.packetData); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	mac := s.packetData[length-1:] | ||||
| 	data := s.packetData[:length-1] | ||||
|  | ||||
| 	if s.mac != nil && s.etm { | ||||
| 		s.mac.Write(data) | ||||
| 	} | ||||
|  | ||||
| 	s.cipher.XORKeyStream(data, data) | ||||
|  | ||||
| 	if s.mac != nil { | ||||
| 		if !s.etm { | ||||
| 			s.mac.Write(data) | ||||
| 		} | ||||
| 		s.macResult = s.mac.Sum(s.macResult[:0]) | ||||
| 		if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { | ||||
| 			return nil, errors.New("ssh: MAC failure") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return s.packetData[:length-paddingLength-1], nil | ||||
| } | ||||
|  | ||||
| // writePacket encrypts and sends a packet of data to the writer argument | ||||
| func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | ||||
| 	if len(packet) > maxPacket { | ||||
| 		return errors.New("ssh: packet too large") | ||||
| 	} | ||||
|  | ||||
| 	aadlen := 0 | ||||
| 	if s.mac != nil && s.etm { | ||||
| 		// packet length is not encrypted for EtM modes | ||||
| 		aadlen = 4 | ||||
| 	} | ||||
|  | ||||
| 	paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple | ||||
| 	if paddingLength < 4 { | ||||
| 		paddingLength += packetSizeMultiple | ||||
| 	} | ||||
|  | ||||
| 	length := len(packet) + 1 + paddingLength | ||||
| 	binary.BigEndian.PutUint32(s.prefix[:], uint32(length)) | ||||
| 	s.prefix[4] = byte(paddingLength) | ||||
| 	padding := s.padding[:paddingLength] | ||||
| 	if _, err := io.ReadFull(rand, padding); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if s.mac != nil { | ||||
| 		s.mac.Reset() | ||||
| 		binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) | ||||
| 		s.mac.Write(s.seqNumBytes[:]) | ||||
|  | ||||
| 		if s.etm { | ||||
| 			// For EtM algorithms, the packet length must stay unencrypted, | ||||
| 			// but the following data (padding length) must be encrypted | ||||
| 			s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) | ||||
| 		} | ||||
|  | ||||
| 		s.mac.Write(s.prefix[:]) | ||||
|  | ||||
| 		if !s.etm { | ||||
| 			// For non-EtM algorithms, the algorithm is applied on unencrypted data | ||||
| 			s.mac.Write(packet) | ||||
| 			s.mac.Write(padding) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if !(s.mac != nil && s.etm) { | ||||
| 		// For EtM algorithms, the padding length has already been encrypted | ||||
| 		// and the packet length must remain unencrypted | ||||
| 		s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) | ||||
| 	} | ||||
|  | ||||
| 	s.cipher.XORKeyStream(packet, packet) | ||||
| 	s.cipher.XORKeyStream(padding, padding) | ||||
|  | ||||
| 	if s.mac != nil && s.etm { | ||||
| 		// For EtM algorithms, packet and padding must be encrypted | ||||
| 		s.mac.Write(packet) | ||||
| 		s.mac.Write(padding) | ||||
| 	} | ||||
|  | ||||
| 	if _, err := w.Write(s.prefix[:]); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := w.Write(packet); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := w.Write(padding); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if s.mac != nil { | ||||
| 		s.macResult = s.mac.Sum(s.macResult[:0]) | ||||
| 		if _, err := w.Write(s.macResult); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type gcmCipher struct { | ||||
| 	aead   cipher.AEAD | ||||
| 	prefix [4]byte | ||||
| 	iv     []byte | ||||
| 	buf    []byte | ||||
| } | ||||
|  | ||||
| func newGCMCipher(iv, key []byte) (packetCipher, error) { | ||||
| 	c, err := aes.NewCipher(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	aead, err := cipher.NewGCM(c) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &gcmCipher{ | ||||
| 		aead: aead, | ||||
| 		iv:   iv, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| const gcmTagSize = 16 | ||||
|  | ||||
| func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | ||||
| 	// Pad out to multiple of 16 bytes. This is different from the | ||||
| 	// stream cipher because that encrypts the length too. | ||||
| 	padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) | ||||
| 	if padding < 4 { | ||||
| 		padding += packetSizeMultiple | ||||
| 	} | ||||
|  | ||||
| 	length := uint32(len(packet) + int(padding) + 1) | ||||
| 	binary.BigEndian.PutUint32(c.prefix[:], length) | ||||
| 	if _, err := w.Write(c.prefix[:]); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if cap(c.buf) < int(length) { | ||||
| 		c.buf = make([]byte, length) | ||||
| 	} else { | ||||
| 		c.buf = c.buf[:length] | ||||
| 	} | ||||
|  | ||||
| 	c.buf[0] = padding | ||||
| 	copy(c.buf[1:], packet) | ||||
| 	if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:]) | ||||
| 	if _, err := w.Write(c.buf); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.incIV() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (c *gcmCipher) incIV() { | ||||
| 	for i := 4 + 7; i >= 4; i-- { | ||||
| 		c.iv[i]++ | ||||
| 		if c.iv[i] != 0 { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | ||||
| 	if _, err := io.ReadFull(r, c.prefix[:]); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	length := binary.BigEndian.Uint32(c.prefix[:]) | ||||
| 	if length > maxPacket { | ||||
| 		return nil, errors.New("ssh: max packet length exceeded") | ||||
| 	} | ||||
|  | ||||
| 	if cap(c.buf) < int(length+gcmTagSize) { | ||||
| 		c.buf = make([]byte, length+gcmTagSize) | ||||
| 	} else { | ||||
| 		c.buf = c.buf[:length+gcmTagSize] | ||||
| 	} | ||||
|  | ||||
| 	if _, err := io.ReadFull(r, c.buf); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c.incIV() | ||||
|  | ||||
| 	padding := plain[0] | ||||
| 	if padding < 4 { | ||||
| 		// padding is a byte, so it automatically satisfies | ||||
| 		// the maximum size, which is 255. | ||||
| 		return nil, fmt.Errorf("ssh: illegal padding %d", padding) | ||||
| 	} | ||||
|  | ||||
| 	if int(padding+1) >= len(plain) { | ||||
| 		return nil, fmt.Errorf("ssh: padding %d too large", padding) | ||||
| 	} | ||||
| 	plain = plain[1 : length-uint32(padding)] | ||||
| 	return plain, nil | ||||
| } | ||||
|  | ||||
| // cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1 | ||||
| type cbcCipher struct { | ||||
| 	mac       hash.Hash | ||||
| 	macSize   uint32 | ||||
| 	decrypter cipher.BlockMode | ||||
| 	encrypter cipher.BlockMode | ||||
|  | ||||
| 	// The following members are to avoid per-packet allocations. | ||||
| 	seqNumBytes [4]byte | ||||
| 	packetData  []byte | ||||
| 	macResult   []byte | ||||
|  | ||||
| 	// Amount of data we should still read to hide which | ||||
| 	// verification error triggered. | ||||
| 	oracleCamouflage uint32 | ||||
| } | ||||
|  | ||||
| func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { | ||||
| 	cbc := &cbcCipher{ | ||||
| 		mac:        macModes[algs.MAC].new(macKey), | ||||
| 		decrypter:  cipher.NewCBCDecrypter(c, iv), | ||||
| 		encrypter:  cipher.NewCBCEncrypter(c, iv), | ||||
| 		packetData: make([]byte, 1024), | ||||
| 	} | ||||
| 	if cbc.mac != nil { | ||||
| 		cbc.macSize = uint32(cbc.mac.Size()) | ||||
| 	} | ||||
|  | ||||
| 	return cbc, nil | ||||
| } | ||||
|  | ||||
| func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { | ||||
| 	c, err := aes.NewCipher(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	cbc, err := newCBCCipher(c, iv, key, macKey, algs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return cbc, nil | ||||
| } | ||||
|  | ||||
| func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { | ||||
| 	c, err := des.NewTripleDESCipher(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	cbc, err := newCBCCipher(c, iv, key, macKey, algs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return cbc, nil | ||||
| } | ||||
|  | ||||
| func maxUInt32(a, b int) uint32 { | ||||
| 	if a > b { | ||||
| 		return uint32(a) | ||||
| 	} | ||||
| 	return uint32(b) | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	cbcMinPacketSizeMultiple = 8 | ||||
| 	cbcMinPacketSize         = 16 | ||||
| 	cbcMinPaddingSize        = 4 | ||||
| ) | ||||
|  | ||||
| // cbcError represents a verification error that may leak information. | ||||
| type cbcError string | ||||
|  | ||||
| func (e cbcError) Error() string { return string(e) } | ||||
|  | ||||
| func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | ||||
| 	p, err := c.readPacketLeaky(seqNum, r) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(cbcError); ok { | ||||
| 			// Verification error: read a fixed amount of | ||||
| 			// data, to make distinguishing between | ||||
| 			// failing MAC and failing length check more | ||||
| 			// difficult. | ||||
| 			io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage)) | ||||
| 		} | ||||
| 	} | ||||
| 	return p, err | ||||
| } | ||||
|  | ||||
| func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { | ||||
| 	blockSize := c.decrypter.BlockSize() | ||||
|  | ||||
| 	// Read the header, which will include some of the subsequent data in the | ||||
| 	// case of block ciphers - this is copied back to the payload later. | ||||
| 	// How many bytes of payload/padding will be read with this first read. | ||||
| 	firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize) | ||||
| 	firstBlock := c.packetData[:firstBlockLength] | ||||
| 	if _, err := io.ReadFull(r, firstBlock); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength | ||||
|  | ||||
| 	c.decrypter.CryptBlocks(firstBlock, firstBlock) | ||||
| 	length := binary.BigEndian.Uint32(firstBlock[:4]) | ||||
| 	if length > maxPacket { | ||||
| 		return nil, cbcError("ssh: packet too large") | ||||
| 	} | ||||
| 	if length+4 < maxUInt32(cbcMinPacketSize, blockSize) { | ||||
| 		// The minimum size of a packet is 16 (or the cipher block size, whichever | ||||
| 		// is larger) bytes. | ||||
| 		return nil, cbcError("ssh: packet too small") | ||||
| 	} | ||||
| 	// The length of the packet (including the length field but not the MAC) must | ||||
| 	// be a multiple of the block size or 8, whichever is larger. | ||||
| 	if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 { | ||||
| 		return nil, cbcError("ssh: invalid packet length multiple") | ||||
| 	} | ||||
|  | ||||
| 	paddingLength := uint32(firstBlock[4]) | ||||
| 	if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 { | ||||
| 		return nil, cbcError("ssh: invalid packet length") | ||||
| 	} | ||||
|  | ||||
| 	// Positions within the c.packetData buffer: | ||||
| 	macStart := 4 + length | ||||
| 	paddingStart := macStart - paddingLength | ||||
|  | ||||
| 	// Entire packet size, starting before length, ending at end of mac. | ||||
| 	entirePacketSize := macStart + c.macSize | ||||
|  | ||||
| 	// Ensure c.packetData is large enough for the entire packet data. | ||||
| 	if uint32(cap(c.packetData)) < entirePacketSize { | ||||
| 		// Still need to upsize and copy, but this should be rare at runtime, only | ||||
| 		// on upsizing the packetData buffer. | ||||
| 		c.packetData = make([]byte, entirePacketSize) | ||||
| 		copy(c.packetData, firstBlock) | ||||
| 	} else { | ||||
| 		c.packetData = c.packetData[:entirePacketSize] | ||||
| 	} | ||||
|  | ||||
| 	n, err := io.ReadFull(r, c.packetData[firstBlockLength:]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c.oracleCamouflage -= uint32(n) | ||||
|  | ||||
| 	remainingCrypted := c.packetData[firstBlockLength:macStart] | ||||
| 	c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted) | ||||
|  | ||||
| 	mac := c.packetData[macStart:] | ||||
| 	if c.mac != nil { | ||||
| 		c.mac.Reset() | ||||
| 		binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) | ||||
| 		c.mac.Write(c.seqNumBytes[:]) | ||||
| 		c.mac.Write(c.packetData[:macStart]) | ||||
| 		c.macResult = c.mac.Sum(c.macResult[:0]) | ||||
| 		if subtle.ConstantTimeCompare(c.macResult, mac) != 1 { | ||||
| 			return nil, cbcError("ssh: MAC failure") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return c.packetData[prefixLen:paddingStart], nil | ||||
| } | ||||
|  | ||||
| func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | ||||
| 	effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize()) | ||||
|  | ||||
| 	// Length of encrypted portion of the packet (header, payload, padding). | ||||
| 	// Enforce minimum padding and packet size. | ||||
| 	encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize) | ||||
| 	// Enforce block size. | ||||
| 	encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize | ||||
|  | ||||
| 	length := encLength - 4 | ||||
| 	paddingLength := int(length) - (1 + len(packet)) | ||||
|  | ||||
| 	// Overall buffer contains: header, payload, padding, mac. | ||||
| 	// Space for the MAC is reserved in the capacity but not the slice length. | ||||
| 	bufferSize := encLength + c.macSize | ||||
| 	if uint32(cap(c.packetData)) < bufferSize { | ||||
| 		c.packetData = make([]byte, encLength, bufferSize) | ||||
| 	} else { | ||||
| 		c.packetData = c.packetData[:encLength] | ||||
| 	} | ||||
|  | ||||
| 	p := c.packetData | ||||
|  | ||||
| 	// Packet header. | ||||
| 	binary.BigEndian.PutUint32(p, length) | ||||
| 	p = p[4:] | ||||
| 	p[0] = byte(paddingLength) | ||||
|  | ||||
| 	// Payload. | ||||
| 	p = p[1:] | ||||
| 	copy(p, packet) | ||||
|  | ||||
| 	// Padding. | ||||
| 	p = p[len(packet):] | ||||
| 	if _, err := io.ReadFull(rand, p); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if c.mac != nil { | ||||
| 		c.mac.Reset() | ||||
| 		binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) | ||||
| 		c.mac.Write(c.seqNumBytes[:]) | ||||
| 		c.mac.Write(c.packetData) | ||||
| 		// The MAC is now appended into the capacity reserved for it earlier. | ||||
| 		c.packetData = c.mac.Sum(c.packetData) | ||||
| 	} | ||||
|  | ||||
| 	c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength]) | ||||
|  | ||||
| 	if _, err := w.Write(c.packetData); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										278
									
								
								vendor/golang.org/x/crypto/ssh/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								vendor/golang.org/x/crypto/ssh/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| // Copyright 2011 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Client implements a traditional SSH client that supports shells, | ||||
| // subprocesses, TCP port/streamlocal forwarding and tunneled dialing. | ||||
| type Client struct { | ||||
| 	Conn | ||||
|  | ||||
| 	forwards        forwardList // forwarded tcpip connections from the remote side | ||||
| 	mu              sync.Mutex | ||||
| 	channelHandlers map[string]chan NewChannel | ||||
| } | ||||
|  | ||||
| // HandleChannelOpen returns a channel on which NewChannel requests | ||||
| // for the given type are sent. If the type already is being handled, | ||||
| // nil is returned. The channel is closed when the connection is closed. | ||||
| func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
| 	if c.channelHandlers == nil { | ||||
| 		// The SSH channel has been closed. | ||||
| 		c := make(chan NewChannel) | ||||
| 		close(c) | ||||
| 		return c | ||||
| 	} | ||||
|  | ||||
| 	ch := c.channelHandlers[channelType] | ||||
| 	if ch != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	ch = make(chan NewChannel, chanSize) | ||||
| 	c.channelHandlers[channelType] = ch | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // NewClient creates a Client on top of the given connection. | ||||
| func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { | ||||
| 	conn := &Client{ | ||||
| 		Conn:            c, | ||||
| 		channelHandlers: make(map[string]chan NewChannel, 1), | ||||
| 	} | ||||
|  | ||||
| 	go conn.handleGlobalRequests(reqs) | ||||
| 	go conn.handleChannelOpens(chans) | ||||
| 	go func() { | ||||
| 		conn.Wait() | ||||
| 		conn.forwards.closeAll() | ||||
| 	}() | ||||
| 	go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) | ||||
| 	go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com")) | ||||
| 	return conn | ||||
| } | ||||
|  | ||||
| // NewClientConn establishes an authenticated SSH connection using c | ||||
| // as the underlying transport.  The Request and NewChannel channels | ||||
| // must be serviced or the connection will hang. | ||||
| func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { | ||||
| 	fullConf := *config | ||||
| 	fullConf.SetDefaults() | ||||
| 	if fullConf.HostKeyCallback == nil { | ||||
| 		c.Close() | ||||
| 		return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback") | ||||
| 	} | ||||
|  | ||||
| 	conn := &connection{ | ||||
| 		sshConn: sshConn{conn: c}, | ||||
| 	} | ||||
|  | ||||
| 	if err := conn.clientHandshake(addr, &fullConf); err != nil { | ||||
| 		c.Close() | ||||
| 		return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) | ||||
| 	} | ||||
| 	conn.mux = newMux(conn.transport) | ||||
| 	return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil | ||||
| } | ||||
|  | ||||
| // clientHandshake performs the client side key exchange. See RFC 4253 Section | ||||
| // 7. | ||||
| func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error { | ||||
| 	if config.ClientVersion != "" { | ||||
| 		c.clientVersion = []byte(config.ClientVersion) | ||||
| 	} else { | ||||
| 		c.clientVersion = []byte(packageVersion) | ||||
| 	} | ||||
| 	var err error | ||||
| 	c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	c.transport = newClientTransport( | ||||
| 		newTransport(c.sshConn.conn, config.Rand, true /* is client */), | ||||
| 		c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) | ||||
| 	if err := c.transport.waitSession(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	c.sessionID = c.transport.getSessionID() | ||||
| 	return c.clientAuthenticate(config) | ||||
| } | ||||
|  | ||||
| // verifyHostKeySignature verifies the host key obtained in the key | ||||
| // exchange. | ||||
| func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error { | ||||
| 	sig, rest, ok := parseSignatureBody(result.Signature) | ||||
| 	if len(rest) > 0 || !ok { | ||||
| 		return errors.New("ssh: signature parse error") | ||||
| 	} | ||||
|  | ||||
| 	return hostKey.Verify(result.H, sig) | ||||
| } | ||||
|  | ||||
| // NewSession opens a new Session for this client. (A session is a remote | ||||
| // execution of a program.) | ||||
| func (c *Client) NewSession() (*Session, error) { | ||||
| 	ch, in, err := c.OpenChannel("session", nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return newSession(ch, in) | ||||
| } | ||||
|  | ||||
| func (c *Client) handleGlobalRequests(incoming <-chan *Request) { | ||||
| 	for r := range incoming { | ||||
| 		// This handles keepalive messages and matches | ||||
| 		// the behaviour of OpenSSH. | ||||
| 		r.Reply(false, nil) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // handleChannelOpens channel open messages from the remote side. | ||||
| func (c *Client) handleChannelOpens(in <-chan NewChannel) { | ||||
| 	for ch := range in { | ||||
| 		c.mu.Lock() | ||||
| 		handler := c.channelHandlers[ch.ChannelType()] | ||||
| 		c.mu.Unlock() | ||||
|  | ||||
| 		if handler != nil { | ||||
| 			handler <- ch | ||||
| 		} else { | ||||
| 			ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType())) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	c.mu.Lock() | ||||
| 	for _, ch := range c.channelHandlers { | ||||
| 		close(ch) | ||||
| 	} | ||||
| 	c.channelHandlers = nil | ||||
| 	c.mu.Unlock() | ||||
| } | ||||
|  | ||||
| // Dial starts a client connection to the given SSH server. It is a | ||||
| // convenience function that connects to the given network address, | ||||
| // initiates the SSH handshake, and then sets up a Client.  For access | ||||
| // to incoming channels and requests, use net.Dial with NewClientConn | ||||
| // instead. | ||||
| func Dial(network, addr string, config *ClientConfig) (*Client, error) { | ||||
| 	conn, err := net.DialTimeout(network, addr, config.Timeout) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c, chans, reqs, err := NewClientConn(conn, addr, config) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return NewClient(c, chans, reqs), nil | ||||
| } | ||||
|  | ||||
| // HostKeyCallback is the function type used for verifying server | ||||
| // keys.  A HostKeyCallback must return nil if the host key is OK, or | ||||
| // an error to reject it. It receives the hostname as passed to Dial | ||||
| // or NewClientConn. The remote address is the RemoteAddr of the | ||||
| // net.Conn underlying the the SSH connection. | ||||
| type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error | ||||
|  | ||||
| // BannerCallback is the function type used for treat the banner sent by | ||||
| // the server. A BannerCallback receives the message sent by the remote server. | ||||
| type BannerCallback func(message string) error | ||||
|  | ||||
| // A ClientConfig structure is used to configure a Client. It must not be | ||||
| // modified after having been passed to an SSH function. | ||||
| type ClientConfig struct { | ||||
| 	// Config contains configuration that is shared between clients and | ||||
| 	// servers. | ||||
| 	Config | ||||
|  | ||||
| 	// User contains the username to authenticate as. | ||||
| 	User string | ||||
|  | ||||
| 	// Auth contains possible authentication methods to use with the | ||||
| 	// server. Only the first instance of a particular RFC 4252 method will | ||||
| 	// be used during authentication. | ||||
| 	Auth []AuthMethod | ||||
|  | ||||
| 	// HostKeyCallback is called during the cryptographic | ||||
| 	// handshake to validate the server's host key. The client | ||||
| 	// configuration must supply this callback for the connection | ||||
| 	// to succeed. The functions InsecureIgnoreHostKey or | ||||
| 	// FixedHostKey can be used for simplistic host key checks. | ||||
| 	HostKeyCallback HostKeyCallback | ||||
|  | ||||
| 	// BannerCallback is called during the SSH dance to display a custom | ||||
| 	// server's message. The client configuration can supply this callback to | ||||
| 	// handle it as wished. The function BannerDisplayStderr can be used for | ||||
| 	// simplistic display on Stderr. | ||||
| 	BannerCallback BannerCallback | ||||
|  | ||||
| 	// ClientVersion contains the version identification string that will | ||||
| 	// be used for the connection. If empty, a reasonable default is used. | ||||
| 	ClientVersion string | ||||
|  | ||||
| 	// HostKeyAlgorithms lists the key types that the client will | ||||
| 	// accept from the server as host key, in order of | ||||
| 	// preference. If empty, a reasonable default is used. Any | ||||
| 	// string returned from PublicKey.Type method may be used, or | ||||
| 	// any of the CertAlgoXxxx and KeyAlgoXxxx constants. | ||||
| 	HostKeyAlgorithms []string | ||||
|  | ||||
| 	// Timeout is the maximum amount of time for the TCP connection to establish. | ||||
| 	// | ||||
| 	// A Timeout of zero means no timeout. | ||||
| 	Timeout time.Duration | ||||
| } | ||||
|  | ||||
| // InsecureIgnoreHostKey returns a function that can be used for | ||||
| // ClientConfig.HostKeyCallback to accept any host key. It should | ||||
| // not be used for production code. | ||||
| func InsecureIgnoreHostKey() HostKeyCallback { | ||||
| 	return func(hostname string, remote net.Addr, key PublicKey) error { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type fixedHostKey struct { | ||||
| 	key PublicKey | ||||
| } | ||||
|  | ||||
| func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error { | ||||
| 	if f.key == nil { | ||||
| 		return fmt.Errorf("ssh: required host key was nil") | ||||
| 	} | ||||
| 	if !bytes.Equal(key.Marshal(), f.key.Marshal()) { | ||||
| 		return fmt.Errorf("ssh: host key mismatch") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // FixedHostKey returns a function for use in | ||||
| // ClientConfig.HostKeyCallback to accept only a specific host key. | ||||
| func FixedHostKey(key PublicKey) HostKeyCallback { | ||||
| 	hk := &fixedHostKey{key} | ||||
| 	return hk.check | ||||
| } | ||||
|  | ||||
| // BannerDisplayStderr returns a function that can be used for | ||||
| // ClientConfig.BannerCallback to display banners on os.Stderr. | ||||
| func BannerDisplayStderr() BannerCallback { | ||||
| 	return func(banner string) error { | ||||
| 		_, err := os.Stderr.WriteString(banner) | ||||
|  | ||||
| 		return err | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										510
									
								
								vendor/golang.org/x/crypto/ssh/client_auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										510
									
								
								vendor/golang.org/x/crypto/ssh/client_auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,510 @@ | ||||
| // Copyright 2011 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| // clientAuthenticate authenticates with the remote server. See RFC 4252. | ||||
| func (c *connection) clientAuthenticate(config *ClientConfig) error { | ||||
| 	// initiate user auth session | ||||
| 	if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	packet, err := c.transport.readPacket() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	var serviceAccept serviceAcceptMsg | ||||
| 	if err := Unmarshal(packet, &serviceAccept); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// during the authentication phase the client first attempts the "none" method | ||||
| 	// then any untried methods suggested by the server. | ||||
| 	tried := make(map[string]bool) | ||||
| 	var lastMethods []string | ||||
|  | ||||
| 	sessionID := c.transport.getSessionID() | ||||
| 	for auth := AuthMethod(new(noneAuth)); auth != nil; { | ||||
| 		ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if ok { | ||||
| 			// success | ||||
| 			return nil | ||||
| 		} | ||||
| 		tried[auth.method()] = true | ||||
| 		if methods == nil { | ||||
| 			methods = lastMethods | ||||
| 		} | ||||
| 		lastMethods = methods | ||||
|  | ||||
| 		auth = nil | ||||
|  | ||||
| 	findNext: | ||||
| 		for _, a := range config.Auth { | ||||
| 			candidateMethod := a.method() | ||||
| 			if tried[candidateMethod] { | ||||
| 				continue | ||||
| 			} | ||||
| 			for _, meth := range methods { | ||||
| 				if meth == candidateMethod { | ||||
| 					auth = a | ||||
| 					break findNext | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried)) | ||||
| } | ||||
|  | ||||
| func keys(m map[string]bool) []string { | ||||
| 	s := make([]string, 0, len(m)) | ||||
|  | ||||
| 	for key := range m { | ||||
| 		s = append(s, key) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // An AuthMethod represents an instance of an RFC 4252 authentication method. | ||||
| type AuthMethod interface { | ||||
| 	// auth authenticates user over transport t. | ||||
| 	// Returns true if authentication is successful. | ||||
| 	// If authentication is not successful, a []string of alternative | ||||
| 	// method names is returned. If the slice is nil, it will be ignored | ||||
| 	// and the previous set of possible methods will be reused. | ||||
| 	auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error) | ||||
|  | ||||
| 	// method returns the RFC 4252 method name. | ||||
| 	method() string | ||||
| } | ||||
|  | ||||
| // "none" authentication, RFC 4252 section 5.2. | ||||
| type noneAuth int | ||||
|  | ||||
| func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { | ||||
| 	if err := c.writePacket(Marshal(&userAuthRequestMsg{ | ||||
| 		User:    user, | ||||
| 		Service: serviceSSH, | ||||
| 		Method:  "none", | ||||
| 	})); err != nil { | ||||
| 		return false, nil, err | ||||
| 	} | ||||
|  | ||||
| 	return handleAuthResponse(c) | ||||
| } | ||||
|  | ||||
| func (n *noneAuth) method() string { | ||||
| 	return "none" | ||||
| } | ||||
|  | ||||
| // passwordCallback is an AuthMethod that fetches the password through | ||||
| // a function call, e.g. by prompting the user. | ||||
| type passwordCallback func() (password string, err error) | ||||
|  | ||||
| func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { | ||||
| 	type passwordAuthMsg struct { | ||||
| 		User     string `sshtype:"50"` | ||||
| 		Service  string | ||||
| 		Method   string | ||||
| 		Reply    bool | ||||
| 		Password string | ||||
| 	} | ||||
|  | ||||
| 	pw, err := cb() | ||||
| 	// REVIEW NOTE: is there a need to support skipping a password attempt? | ||||
| 	// The program may only find out that the user doesn't have a password | ||||
| 	// when prompting. | ||||
| 	if err != nil { | ||||
| 		return false, nil, err | ||||
| 	} | ||||
|  | ||||
| 	if err := c.writePacket(Marshal(&passwordAuthMsg{ | ||||
| 		User:     user, | ||||
| 		Service:  serviceSSH, | ||||
| 		Method:   cb.method(), | ||||
| 		Reply:    false, | ||||
| 		Password: pw, | ||||
| 	})); err != nil { | ||||
| 		return false, nil, err | ||||
| 	} | ||||
|  | ||||
| 	return handleAuthResponse(c) | ||||
| } | ||||
|  | ||||
| func (cb passwordCallback) method() string { | ||||
| 	return "password" | ||||
| } | ||||
|  | ||||
| // Password returns an AuthMethod using the given password. | ||||
| func Password(secret string) AuthMethod { | ||||
| 	return passwordCallback(func() (string, error) { return secret, nil }) | ||||
| } | ||||
|  | ||||
| // PasswordCallback returns an AuthMethod that uses a callback for | ||||
| // fetching a password. | ||||
| func PasswordCallback(prompt func() (secret string, err error)) AuthMethod { | ||||
| 	return passwordCallback(prompt) | ||||
| } | ||||
|  | ||||
| type publickeyAuthMsg struct { | ||||
| 	User    string `sshtype:"50"` | ||||
| 	Service string | ||||
| 	Method  string | ||||
| 	// HasSig indicates to the receiver packet that the auth request is signed and | ||||
| 	// should be used for authentication of the request. | ||||
| 	HasSig   bool | ||||
| 	Algoname string | ||||
| 	PubKey   []byte | ||||
| 	// Sig is tagged with "rest" so Marshal will exclude it during | ||||
| 	// validateKey | ||||
| 	Sig []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| // publicKeyCallback is an AuthMethod that uses a set of key | ||||
| // pairs for authentication. | ||||
| type publicKeyCallback func() ([]Signer, error) | ||||
|  | ||||
| func (cb publicKeyCallback) method() string { | ||||
| 	return "publickey" | ||||
| } | ||||
|  | ||||
| func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { | ||||
| 	// Authentication is performed by sending an enquiry to test if a key is | ||||
| 	// acceptable to the remote. If the key is acceptable, the client will | ||||
| 	// attempt to authenticate with the valid key.  If not the client will repeat | ||||
| 	// the process with the remaining keys. | ||||
|  | ||||
| 	signers, err := cb() | ||||
| 	if err != nil { | ||||
| 		return false, nil, err | ||||
| 	} | ||||
| 	var methods []string | ||||
| 	for _, signer := range signers { | ||||
| 		ok, err := validateKey(signer.PublicKey(), user, c) | ||||
| 		if err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		pub := signer.PublicKey() | ||||
| 		pubKey := pub.Marshal() | ||||
| 		sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ | ||||
| 			User:    user, | ||||
| 			Service: serviceSSH, | ||||
| 			Method:  cb.method(), | ||||
| 		}, []byte(pub.Type()), pubKey)) | ||||
| 		if err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
|  | ||||
| 		// manually wrap the serialized signature in a string | ||||
| 		s := Marshal(sign) | ||||
| 		sig := make([]byte, stringLength(len(s))) | ||||
| 		marshalString(sig, s) | ||||
| 		msg := publickeyAuthMsg{ | ||||
| 			User:     user, | ||||
| 			Service:  serviceSSH, | ||||
| 			Method:   cb.method(), | ||||
| 			HasSig:   true, | ||||
| 			Algoname: pub.Type(), | ||||
| 			PubKey:   pubKey, | ||||
| 			Sig:      sig, | ||||
| 		} | ||||
| 		p := Marshal(&msg) | ||||
| 		if err := c.writePacket(p); err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
| 		var success bool | ||||
| 		success, methods, err = handleAuthResponse(c) | ||||
| 		if err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
|  | ||||
| 		// If authentication succeeds or the list of available methods does not | ||||
| 		// contain the "publickey" method, do not attempt to authenticate with any | ||||
| 		// other keys.  According to RFC 4252 Section 7, the latter can occur when | ||||
| 		// additional authentication methods are required. | ||||
| 		if success || !containsMethod(methods, cb.method()) { | ||||
| 			return success, methods, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false, methods, nil | ||||
| } | ||||
|  | ||||
| func containsMethod(methods []string, method string) bool { | ||||
| 	for _, m := range methods { | ||||
| 		if m == method { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // validateKey validates the key provided is acceptable to the server. | ||||
| func validateKey(key PublicKey, user string, c packetConn) (bool, error) { | ||||
| 	pubKey := key.Marshal() | ||||
| 	msg := publickeyAuthMsg{ | ||||
| 		User:     user, | ||||
| 		Service:  serviceSSH, | ||||
| 		Method:   "publickey", | ||||
| 		HasSig:   false, | ||||
| 		Algoname: key.Type(), | ||||
| 		PubKey:   pubKey, | ||||
| 	} | ||||
| 	if err := c.writePacket(Marshal(&msg)); err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	return confirmKeyAck(key, c) | ||||
| } | ||||
|  | ||||
| func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { | ||||
| 	pubKey := key.Marshal() | ||||
| 	algoname := key.Type() | ||||
|  | ||||
| 	for { | ||||
| 		packet, err := c.readPacket() | ||||
| 		if err != nil { | ||||
| 			return false, err | ||||
| 		} | ||||
| 		switch packet[0] { | ||||
| 		case msgUserAuthBanner: | ||||
| 			if err := handleBannerResponse(c, packet); err != nil { | ||||
| 				return false, err | ||||
| 			} | ||||
| 		case msgUserAuthPubKeyOk: | ||||
| 			var msg userAuthPubKeyOkMsg | ||||
| 			if err := Unmarshal(packet, &msg); err != nil { | ||||
| 				return false, err | ||||
| 			} | ||||
| 			if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) { | ||||
| 				return false, nil | ||||
| 			} | ||||
| 			return true, nil | ||||
| 		case msgUserAuthFailure: | ||||
| 			return false, nil | ||||
| 		default: | ||||
| 			return false, unexpectedMessageError(msgUserAuthSuccess, packet[0]) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // PublicKeys returns an AuthMethod that uses the given key | ||||
| // pairs. | ||||
| func PublicKeys(signers ...Signer) AuthMethod { | ||||
| 	return publicKeyCallback(func() ([]Signer, error) { return signers, nil }) | ||||
| } | ||||
|  | ||||
| // PublicKeysCallback returns an AuthMethod that runs the given | ||||
| // function to obtain a list of key pairs. | ||||
| func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod { | ||||
| 	return publicKeyCallback(getSigners) | ||||
| } | ||||
|  | ||||
| // handleAuthResponse returns whether the preceding authentication request succeeded | ||||
| // along with a list of remaining authentication methods to try next and | ||||
| // an error if an unexpected response was received. | ||||
| func handleAuthResponse(c packetConn) (bool, []string, error) { | ||||
| 	for { | ||||
| 		packet, err := c.readPacket() | ||||
| 		if err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
|  | ||||
| 		switch packet[0] { | ||||
| 		case msgUserAuthBanner: | ||||
| 			if err := handleBannerResponse(c, packet); err != nil { | ||||
| 				return false, nil, err | ||||
| 			} | ||||
| 		case msgUserAuthFailure: | ||||
| 			var msg userAuthFailureMsg | ||||
| 			if err := Unmarshal(packet, &msg); err != nil { | ||||
| 				return false, nil, err | ||||
| 			} | ||||
| 			return false, msg.Methods, nil | ||||
| 		case msgUserAuthSuccess: | ||||
| 			return true, nil, nil | ||||
| 		default: | ||||
| 			return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func handleBannerResponse(c packetConn, packet []byte) error { | ||||
| 	var msg userAuthBannerMsg | ||||
| 	if err := Unmarshal(packet, &msg); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	transport, ok := c.(*handshakeTransport) | ||||
| 	if !ok { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if transport.bannerCallback != nil { | ||||
| 		return transport.bannerCallback(msg.Message) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // KeyboardInteractiveChallenge should print questions, optionally | ||||
| // disabling echoing (e.g. for passwords), and return all the answers. | ||||
| // Challenge may be called multiple times in a single session. After | ||||
| // successful authentication, the server may send a challenge with no | ||||
| // questions, for which the user and instruction messages should be | ||||
| // printed.  RFC 4256 section 3.3 details how the UI should behave for | ||||
| // both CLI and GUI environments. | ||||
| type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) | ||||
|  | ||||
| // KeyboardInteractive returns an AuthMethod using a prompt/response | ||||
| // sequence controlled by the server. | ||||
| func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod { | ||||
| 	return challenge | ||||
| } | ||||
|  | ||||
| func (cb KeyboardInteractiveChallenge) method() string { | ||||
| 	return "keyboard-interactive" | ||||
| } | ||||
|  | ||||
| func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { | ||||
| 	type initiateMsg struct { | ||||
| 		User       string `sshtype:"50"` | ||||
| 		Service    string | ||||
| 		Method     string | ||||
| 		Language   string | ||||
| 		Submethods string | ||||
| 	} | ||||
|  | ||||
| 	if err := c.writePacket(Marshal(&initiateMsg{ | ||||
| 		User:    user, | ||||
| 		Service: serviceSSH, | ||||
| 		Method:  "keyboard-interactive", | ||||
| 	})); err != nil { | ||||
| 		return false, nil, err | ||||
| 	} | ||||
|  | ||||
| 	for { | ||||
| 		packet, err := c.readPacket() | ||||
| 		if err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
|  | ||||
| 		// like handleAuthResponse, but with less options. | ||||
| 		switch packet[0] { | ||||
| 		case msgUserAuthBanner: | ||||
| 			if err := handleBannerResponse(c, packet); err != nil { | ||||
| 				return false, nil, err | ||||
| 			} | ||||
| 			continue | ||||
| 		case msgUserAuthInfoRequest: | ||||
| 			// OK | ||||
| 		case msgUserAuthFailure: | ||||
| 			var msg userAuthFailureMsg | ||||
| 			if err := Unmarshal(packet, &msg); err != nil { | ||||
| 				return false, nil, err | ||||
| 			} | ||||
| 			return false, msg.Methods, nil | ||||
| 		case msgUserAuthSuccess: | ||||
| 			return true, nil, nil | ||||
| 		default: | ||||
| 			return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) | ||||
| 		} | ||||
|  | ||||
| 		var msg userAuthInfoRequestMsg | ||||
| 		if err := Unmarshal(packet, &msg); err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
|  | ||||
| 		// Manually unpack the prompt/echo pairs. | ||||
| 		rest := msg.Prompts | ||||
| 		var prompts []string | ||||
| 		var echos []bool | ||||
| 		for i := 0; i < int(msg.NumPrompts); i++ { | ||||
| 			prompt, r, ok := parseString(rest) | ||||
| 			if !ok || len(r) == 0 { | ||||
| 				return false, nil, errors.New("ssh: prompt format error") | ||||
| 			} | ||||
| 			prompts = append(prompts, string(prompt)) | ||||
| 			echos = append(echos, r[0] != 0) | ||||
| 			rest = r[1:] | ||||
| 		} | ||||
|  | ||||
| 		if len(rest) != 0 { | ||||
| 			return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs") | ||||
| 		} | ||||
|  | ||||
| 		answers, err := cb(msg.User, msg.Instruction, prompts, echos) | ||||
| 		if err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
|  | ||||
| 		if len(answers) != len(prompts) { | ||||
| 			return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback") | ||||
| 		} | ||||
| 		responseLength := 1 + 4 | ||||
| 		for _, a := range answers { | ||||
| 			responseLength += stringLength(len(a)) | ||||
| 		} | ||||
| 		serialized := make([]byte, responseLength) | ||||
| 		p := serialized | ||||
| 		p[0] = msgUserAuthInfoResponse | ||||
| 		p = p[1:] | ||||
| 		p = marshalUint32(p, uint32(len(answers))) | ||||
| 		for _, a := range answers { | ||||
| 			p = marshalString(p, []byte(a)) | ||||
| 		} | ||||
|  | ||||
| 		if err := c.writePacket(serialized); err != nil { | ||||
| 			return false, nil, err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type retryableAuthMethod struct { | ||||
| 	authMethod AuthMethod | ||||
| 	maxTries   int | ||||
| } | ||||
|  | ||||
| func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) { | ||||
| 	for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ { | ||||
| 		ok, methods, err = r.authMethod.auth(session, user, c, rand) | ||||
| 		if ok || err != nil { // either success or error terminate | ||||
| 			return ok, methods, err | ||||
| 		} | ||||
| 	} | ||||
| 	return ok, methods, err | ||||
| } | ||||
|  | ||||
| func (r *retryableAuthMethod) method() string { | ||||
| 	return r.authMethod.method() | ||||
| } | ||||
|  | ||||
| // RetryableAuthMethod is a decorator for other auth methods enabling them to | ||||
| // be retried up to maxTries before considering that AuthMethod itself failed. | ||||
| // If maxTries is <= 0, will retry indefinitely | ||||
| // | ||||
| // This is useful for interactive clients using challenge/response type | ||||
| // authentication (e.g. Keyboard-Interactive, Password, etc) where the user | ||||
| // could mistype their response resulting in the server issuing a | ||||
| // SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4 | ||||
| // [keyboard-interactive]); Without this decorator, the non-retryable | ||||
| // AuthMethod would be removed from future consideration, and never tried again | ||||
| // (and so the user would never be able to retry their entry). | ||||
| func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod { | ||||
| 	return &retryableAuthMethod{authMethod: auth, maxTries: maxTries} | ||||
| } | ||||
							
								
								
									
										373
									
								
								vendor/golang.org/x/crypto/ssh/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								vendor/golang.org/x/crypto/ssh/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,373 @@ | ||||
| // Copyright 2011 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/rand" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"sync" | ||||
|  | ||||
| 	_ "crypto/sha1" | ||||
| 	_ "crypto/sha256" | ||||
| 	_ "crypto/sha512" | ||||
| ) | ||||
|  | ||||
| // These are string constants in the SSH protocol. | ||||
| const ( | ||||
| 	compressionNone = "none" | ||||
| 	serviceUserAuth = "ssh-userauth" | ||||
| 	serviceSSH      = "ssh-connection" | ||||
| ) | ||||
|  | ||||
| // supportedCiphers specifies the supported ciphers in preference order. | ||||
| var supportedCiphers = []string{ | ||||
| 	"aes128-ctr", "aes192-ctr", "aes256-ctr", | ||||
| 	"aes128-gcm@openssh.com", | ||||
| 	"arcfour256", "arcfour128", | ||||
| } | ||||
|  | ||||
| // supportedKexAlgos specifies the supported key-exchange algorithms in | ||||
| // preference order. | ||||
| var supportedKexAlgos = []string{ | ||||
| 	kexAlgoCurve25519SHA256, | ||||
| 	// P384 and P521 are not constant-time yet, but since we don't | ||||
| 	// reuse ephemeral keys, using them for ECDH should be OK. | ||||
| 	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, | ||||
| 	kexAlgoDH14SHA1, kexAlgoDH1SHA1, | ||||
| } | ||||
|  | ||||
| // supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods | ||||
| // of authenticating servers) in preference order. | ||||
| var supportedHostKeyAlgos = []string{ | ||||
| 	CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, | ||||
| 	CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01, | ||||
|  | ||||
| 	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, | ||||
| 	KeyAlgoRSA, KeyAlgoDSA, | ||||
|  | ||||
| 	KeyAlgoED25519, | ||||
| } | ||||
|  | ||||
| // supportedMACs specifies a default set of MAC algorithms in preference order. | ||||
| // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed | ||||
| // because they have reached the end of their useful life. | ||||
| var supportedMACs = []string{ | ||||
| 	"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", | ||||
| } | ||||
|  | ||||
| var supportedCompressions = []string{compressionNone} | ||||
|  | ||||
| // hashFuncs keeps the mapping of supported algorithms to their respective | ||||
| // hashes needed for signature verification. | ||||
| var hashFuncs = map[string]crypto.Hash{ | ||||
| 	KeyAlgoRSA:          crypto.SHA1, | ||||
| 	KeyAlgoDSA:          crypto.SHA1, | ||||
| 	KeyAlgoECDSA256:     crypto.SHA256, | ||||
| 	KeyAlgoECDSA384:     crypto.SHA384, | ||||
| 	KeyAlgoECDSA521:     crypto.SHA512, | ||||
| 	CertAlgoRSAv01:      crypto.SHA1, | ||||
| 	CertAlgoDSAv01:      crypto.SHA1, | ||||
| 	CertAlgoECDSA256v01: crypto.SHA256, | ||||
| 	CertAlgoECDSA384v01: crypto.SHA384, | ||||
| 	CertAlgoECDSA521v01: crypto.SHA512, | ||||
| } | ||||
|  | ||||
| // unexpectedMessageError results when the SSH message that we received didn't | ||||
| // match what we wanted. | ||||
| func unexpectedMessageError(expected, got uint8) error { | ||||
| 	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected) | ||||
| } | ||||
|  | ||||
| // parseError results from a malformed SSH message. | ||||
| func parseError(tag uint8) error { | ||||
| 	return fmt.Errorf("ssh: parse error in message type %d", tag) | ||||
| } | ||||
|  | ||||
| func findCommon(what string, client []string, server []string) (common string, err error) { | ||||
| 	for _, c := range client { | ||||
| 		for _, s := range server { | ||||
| 			if c == s { | ||||
| 				return c, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server) | ||||
| } | ||||
|  | ||||
| type directionAlgorithms struct { | ||||
| 	Cipher      string | ||||
| 	MAC         string | ||||
| 	Compression string | ||||
| } | ||||
|  | ||||
| // rekeyBytes returns a rekeying intervals in bytes. | ||||
| func (a *directionAlgorithms) rekeyBytes() int64 { | ||||
| 	// According to RFC4344 block ciphers should rekey after | ||||
| 	// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is | ||||
| 	// 128. | ||||
| 	switch a.Cipher { | ||||
| 	case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID: | ||||
| 		return 16 * (1 << 32) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// For others, stick with RFC4253 recommendation to rekey after 1 Gb of data. | ||||
| 	return 1 << 30 | ||||
| } | ||||
|  | ||||
| type algorithms struct { | ||||
| 	kex     string | ||||
| 	hostKey string | ||||
| 	w       directionAlgorithms | ||||
| 	r       directionAlgorithms | ||||
| } | ||||
|  | ||||
| func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { | ||||
| 	result := &algorithms{} | ||||
|  | ||||
| 	result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| // If rekeythreshold is too small, we can't make any progress sending | ||||
| // stuff. | ||||
| const minRekeyThreshold uint64 = 256 | ||||
|  | ||||
| // Config contains configuration data common to both ServerConfig and | ||||
| // ClientConfig. | ||||
| type Config struct { | ||||
| 	// Rand provides the source of entropy for cryptographic | ||||
| 	// primitives. If Rand is nil, the cryptographic random reader | ||||
| 	// in package crypto/rand will be used. | ||||
| 	Rand io.Reader | ||||
|  | ||||
| 	// The maximum number of bytes sent or received after which a | ||||
| 	// new key is negotiated. It must be at least 256. If | ||||
| 	// unspecified, a size suitable for the chosen cipher is used. | ||||
| 	RekeyThreshold uint64 | ||||
|  | ||||
| 	// The allowed key exchanges algorithms. If unspecified then a | ||||
| 	// default set of algorithms is used. | ||||
| 	KeyExchanges []string | ||||
|  | ||||
| 	// The allowed cipher algorithms. If unspecified then a sensible | ||||
| 	// default is used. | ||||
| 	Ciphers []string | ||||
|  | ||||
| 	// The allowed MAC algorithms. If unspecified then a sensible default | ||||
| 	// is used. | ||||
| 	MACs []string | ||||
| } | ||||
|  | ||||
| // SetDefaults sets sensible values for unset fields in config. This is | ||||
| // exported for testing: Configs passed to SSH functions are copied and have | ||||
| // default values set automatically. | ||||
| func (c *Config) SetDefaults() { | ||||
| 	if c.Rand == nil { | ||||
| 		c.Rand = rand.Reader | ||||
| 	} | ||||
| 	if c.Ciphers == nil { | ||||
| 		c.Ciphers = supportedCiphers | ||||
| 	} | ||||
| 	var ciphers []string | ||||
| 	for _, c := range c.Ciphers { | ||||
| 		if cipherModes[c] != nil { | ||||
| 			// reject the cipher if we have no cipherModes definition | ||||
| 			ciphers = append(ciphers, c) | ||||
| 		} | ||||
| 	} | ||||
| 	c.Ciphers = ciphers | ||||
|  | ||||
| 	if c.KeyExchanges == nil { | ||||
| 		c.KeyExchanges = supportedKexAlgos | ||||
| 	} | ||||
|  | ||||
| 	if c.MACs == nil { | ||||
| 		c.MACs = supportedMACs | ||||
| 	} | ||||
|  | ||||
| 	if c.RekeyThreshold == 0 { | ||||
| 		// cipher specific default | ||||
| 	} else if c.RekeyThreshold < minRekeyThreshold { | ||||
| 		c.RekeyThreshold = minRekeyThreshold | ||||
| 	} else if c.RekeyThreshold >= math.MaxInt64 { | ||||
| 		// Avoid weirdness if somebody uses -1 as a threshold. | ||||
| 		c.RekeyThreshold = math.MaxInt64 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // buildDataSignedForAuth returns the data that is signed in order to prove | ||||
| // possession of a private key. See RFC 4252, section 7. | ||||
| func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { | ||||
| 	data := struct { | ||||
| 		Session []byte | ||||
| 		Type    byte | ||||
| 		User    string | ||||
| 		Service string | ||||
| 		Method  string | ||||
| 		Sign    bool | ||||
| 		Algo    []byte | ||||
| 		PubKey  []byte | ||||
| 	}{ | ||||
| 		sessionID, | ||||
| 		msgUserAuthRequest, | ||||
| 		req.User, | ||||
| 		req.Service, | ||||
| 		req.Method, | ||||
| 		true, | ||||
| 		algo, | ||||
| 		pubKey, | ||||
| 	} | ||||
| 	return Marshal(data) | ||||
| } | ||||
|  | ||||
| func appendU16(buf []byte, n uint16) []byte { | ||||
| 	return append(buf, byte(n>>8), byte(n)) | ||||
| } | ||||
|  | ||||
| func appendU32(buf []byte, n uint32) []byte { | ||||
| 	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) | ||||
| } | ||||
|  | ||||
| func appendU64(buf []byte, n uint64) []byte { | ||||
| 	return append(buf, | ||||
| 		byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), | ||||
| 		byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) | ||||
| } | ||||
|  | ||||
| func appendInt(buf []byte, n int) []byte { | ||||
| 	return appendU32(buf, uint32(n)) | ||||
| } | ||||
|  | ||||
| func appendString(buf []byte, s string) []byte { | ||||
| 	buf = appendU32(buf, uint32(len(s))) | ||||
| 	buf = append(buf, s...) | ||||
| 	return buf | ||||
| } | ||||
|  | ||||
| func appendBool(buf []byte, b bool) []byte { | ||||
| 	if b { | ||||
| 		return append(buf, 1) | ||||
| 	} | ||||
| 	return append(buf, 0) | ||||
| } | ||||
|  | ||||
| // newCond is a helper to hide the fact that there is no usable zero | ||||
| // value for sync.Cond. | ||||
| func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } | ||||
|  | ||||
| // window represents the buffer available to clients | ||||
| // wishing to write to a channel. | ||||
| type window struct { | ||||
| 	*sync.Cond | ||||
| 	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 | ||||
| 	writeWaiters int | ||||
| 	closed       bool | ||||
| } | ||||
|  | ||||
| // add adds win to the amount of window available | ||||
| // for consumers. | ||||
| func (w *window) add(win uint32) bool { | ||||
| 	// a zero sized window adjust is a noop. | ||||
| 	if win == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	w.L.Lock() | ||||
| 	if w.win+win < win { | ||||
| 		w.L.Unlock() | ||||
| 		return false | ||||
| 	} | ||||
| 	w.win += win | ||||
| 	// It is unusual that multiple goroutines would be attempting to reserve | ||||
| 	// window space, but not guaranteed. Use broadcast to notify all waiters | ||||
| 	// that additional window is available. | ||||
| 	w.Broadcast() | ||||
| 	w.L.Unlock() | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // close sets the window to closed, so all reservations fail | ||||
| // immediately. | ||||
| func (w *window) close() { | ||||
| 	w.L.Lock() | ||||
| 	w.closed = true | ||||
| 	w.Broadcast() | ||||
| 	w.L.Unlock() | ||||
| } | ||||
|  | ||||
| // reserve reserves win from the available window capacity. | ||||
| // If no capacity remains, reserve will block. reserve may | ||||
| // return less than requested. | ||||
| func (w *window) reserve(win uint32) (uint32, error) { | ||||
| 	var err error | ||||
| 	w.L.Lock() | ||||
| 	w.writeWaiters++ | ||||
| 	w.Broadcast() | ||||
| 	for w.win == 0 && !w.closed { | ||||
| 		w.Wait() | ||||
| 	} | ||||
| 	w.writeWaiters-- | ||||
| 	if w.win < win { | ||||
| 		win = w.win | ||||
| 	} | ||||
| 	w.win -= win | ||||
| 	if w.closed { | ||||
| 		err = io.EOF | ||||
| 	} | ||||
| 	w.L.Unlock() | ||||
| 	return win, err | ||||
| } | ||||
|  | ||||
| // waitWriterBlocked waits until some goroutine is blocked for further | ||||
| // writes. It is used in tests only. | ||||
| func (w *window) waitWriterBlocked() { | ||||
| 	w.Cond.L.Lock() | ||||
| 	for w.writeWaiters == 0 { | ||||
| 		w.Cond.Wait() | ||||
| 	} | ||||
| 	w.Cond.L.Unlock() | ||||
| } | ||||
							
								
								
									
										143
									
								
								vendor/golang.org/x/crypto/ssh/connection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/golang.org/x/crypto/ssh/connection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| // Copyright 2013 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| ) | ||||
|  | ||||
| // OpenChannelError is returned if the other side rejects an | ||||
| // OpenChannel request. | ||||
| type OpenChannelError struct { | ||||
| 	Reason  RejectionReason | ||||
| 	Message string | ||||
| } | ||||
|  | ||||
| func (e *OpenChannelError) Error() string { | ||||
| 	return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) | ||||
| } | ||||
|  | ||||
| // ConnMetadata holds metadata for the connection. | ||||
| type ConnMetadata interface { | ||||
| 	// User returns the user ID for this connection. | ||||
| 	User() string | ||||
|  | ||||
| 	// SessionID returns the session hash, also denoted by H. | ||||
| 	SessionID() []byte | ||||
|  | ||||
| 	// ClientVersion returns the client's version string as hashed | ||||
| 	// into the session ID. | ||||
| 	ClientVersion() []byte | ||||
|  | ||||
| 	// ServerVersion returns the server's version string as hashed | ||||
| 	// into the session ID. | ||||
| 	ServerVersion() []byte | ||||
|  | ||||
| 	// RemoteAddr returns the remote address for this connection. | ||||
| 	RemoteAddr() net.Addr | ||||
|  | ||||
| 	// LocalAddr returns the local address for this connection. | ||||
| 	LocalAddr() net.Addr | ||||
| } | ||||
|  | ||||
| // Conn represents an SSH connection for both server and client roles. | ||||
| // Conn is the basis for implementing an application layer, such | ||||
| // as ClientConn, which implements the traditional shell access for | ||||
| // clients. | ||||
| type Conn interface { | ||||
| 	ConnMetadata | ||||
|  | ||||
| 	// SendRequest sends a global request, and returns the | ||||
| 	// reply. If wantReply is true, it returns the response status | ||||
| 	// and payload. See also RFC4254, section 4. | ||||
| 	SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) | ||||
|  | ||||
| 	// OpenChannel tries to open an channel. If the request is | ||||
| 	// rejected, it returns *OpenChannelError. On success it returns | ||||
| 	// the SSH Channel and a Go channel for incoming, out-of-band | ||||
| 	// requests. The Go channel must be serviced, or the | ||||
| 	// connection will hang. | ||||
| 	OpenChannel(name string, data []byte) (Channel, <-chan *Request, error) | ||||
|  | ||||
| 	// Close closes the underlying network connection | ||||
| 	Close() error | ||||
|  | ||||
| 	// Wait blocks until the connection has shut down, and returns the | ||||
| 	// error causing the shutdown. | ||||
| 	Wait() error | ||||
|  | ||||
| 	// TODO(hanwen): consider exposing: | ||||
| 	//   RequestKeyChange | ||||
| 	//   Disconnect | ||||
| } | ||||
|  | ||||
| // DiscardRequests consumes and rejects all requests from the | ||||
| // passed-in channel. | ||||
| func DiscardRequests(in <-chan *Request) { | ||||
| 	for req := range in { | ||||
| 		if req.WantReply { | ||||
| 			req.Reply(false, nil) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // A connection represents an incoming connection. | ||||
| type connection struct { | ||||
| 	transport *handshakeTransport | ||||
| 	sshConn | ||||
|  | ||||
| 	// The connection protocol. | ||||
| 	*mux | ||||
| } | ||||
|  | ||||
| func (c *connection) Close() error { | ||||
| 	return c.sshConn.conn.Close() | ||||
| } | ||||
|  | ||||
| // sshconn provides net.Conn metadata, but disallows direct reads and | ||||
| // writes. | ||||
| type sshConn struct { | ||||
| 	conn net.Conn | ||||
|  | ||||
| 	user          string | ||||
| 	sessionID     []byte | ||||
| 	clientVersion []byte | ||||
| 	serverVersion []byte | ||||
| } | ||||
|  | ||||
| func dup(src []byte) []byte { | ||||
| 	dst := make([]byte, len(src)) | ||||
| 	copy(dst, src) | ||||
| 	return dst | ||||
| } | ||||
|  | ||||
| func (c *sshConn) User() string { | ||||
| 	return c.user | ||||
| } | ||||
|  | ||||
| func (c *sshConn) RemoteAddr() net.Addr { | ||||
| 	return c.conn.RemoteAddr() | ||||
| } | ||||
|  | ||||
| func (c *sshConn) Close() error { | ||||
| 	return c.conn.Close() | ||||
| } | ||||
|  | ||||
| func (c *sshConn) LocalAddr() net.Addr { | ||||
| 	return c.conn.LocalAddr() | ||||
| } | ||||
|  | ||||
| func (c *sshConn) SessionID() []byte { | ||||
| 	return dup(c.sessionID) | ||||
| } | ||||
|  | ||||
| func (c *sshConn) ClientVersion() []byte { | ||||
| 	return dup(c.clientVersion) | ||||
| } | ||||
|  | ||||
| func (c *sshConn) ServerVersion() []byte { | ||||
| 	return dup(c.serverVersion) | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/golang.org/x/crypto/ssh/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/golang.org/x/crypto/ssh/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| // Copyright 2011 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 ssh implements an SSH client and server. | ||||
|  | ||||
| SSH is a transport security protocol, an authentication protocol and a | ||||
| family of application protocols. The most typical application level | ||||
| protocol is a remote shell and this is specifically implemented.  However, | ||||
| the multiplexed nature of SSH is exposed to users that wish to support | ||||
| others. | ||||
|  | ||||
| References: | ||||
|   [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD | ||||
|   [SSH-PARAMETERS]:    http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 | ||||
|  | ||||
| This package does not fall under the stability promise of the Go language itself, | ||||
| so its API may be changed when pressing needs arise. | ||||
| */ | ||||
| package ssh // import "golang.org/x/crypto/ssh" | ||||
							
								
								
									
										646
									
								
								vendor/golang.org/x/crypto/ssh/handshake.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										646
									
								
								vendor/golang.org/x/crypto/ssh/handshake.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,646 @@ | ||||
| // Copyright 2013 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| // debugHandshake, if set, prints messages sent and received.  Key | ||||
| // exchange messages are printed as if DH were used, so the debug | ||||
| // messages are wrong when using ECDH. | ||||
| const debugHandshake = false | ||||
|  | ||||
| // chanSize sets the amount of buffering SSH connections. This is | ||||
| // primarily for testing: setting chanSize=0 uncovers deadlocks more | ||||
| // quickly. | ||||
| const chanSize = 16 | ||||
|  | ||||
| // keyingTransport is a packet based transport that supports key | ||||
| // changes. It need not be thread-safe. It should pass through | ||||
| // msgNewKeys in both directions. | ||||
| type keyingTransport interface { | ||||
| 	packetConn | ||||
|  | ||||
| 	// prepareKeyChange sets up a key change. The key change for a | ||||
| 	// direction will be effected if a msgNewKeys message is sent | ||||
| 	// or received. | ||||
| 	prepareKeyChange(*algorithms, *kexResult) error | ||||
| } | ||||
|  | ||||
| // handshakeTransport implements rekeying on top of a keyingTransport | ||||
| // and offers a thread-safe writePacket() interface. | ||||
| type handshakeTransport struct { | ||||
| 	conn   keyingTransport | ||||
| 	config *Config | ||||
|  | ||||
| 	serverVersion []byte | ||||
| 	clientVersion []byte | ||||
|  | ||||
| 	// hostKeys is non-empty if we are the server. In that case, | ||||
| 	// it contains all host keys that can be used to sign the | ||||
| 	// connection. | ||||
| 	hostKeys []Signer | ||||
|  | ||||
| 	// hostKeyAlgorithms is non-empty if we are the client. In that case, | ||||
| 	// we accept these key types from the server as host key. | ||||
| 	hostKeyAlgorithms []string | ||||
|  | ||||
| 	// On read error, incoming is closed, and readError is set. | ||||
| 	incoming  chan []byte | ||||
| 	readError error | ||||
|  | ||||
| 	mu             sync.Mutex | ||||
| 	writeError     error | ||||
| 	sentInitPacket []byte | ||||
| 	sentInitMsg    *kexInitMsg | ||||
| 	pendingPackets [][]byte // Used when a key exchange is in progress. | ||||
|  | ||||
| 	// If the read loop wants to schedule a kex, it pings this | ||||
| 	// channel, and the write loop will send out a kex | ||||
| 	// message. | ||||
| 	requestKex chan struct{} | ||||
|  | ||||
| 	// If the other side requests or confirms a kex, its kexInit | ||||
| 	// packet is sent here for the write loop to find it. | ||||
| 	startKex chan *pendingKex | ||||
|  | ||||
| 	// data for host key checking | ||||
| 	hostKeyCallback HostKeyCallback | ||||
| 	dialAddress     string | ||||
| 	remoteAddr      net.Addr | ||||
|  | ||||
| 	// bannerCallback is non-empty if we are the client and it has been set in | ||||
| 	// ClientConfig. In that case it is called during the user authentication | ||||
| 	// dance to handle a custom server's message. | ||||
| 	bannerCallback BannerCallback | ||||
|  | ||||
| 	// Algorithms agreed in the last key exchange. | ||||
| 	algorithms *algorithms | ||||
|  | ||||
| 	readPacketsLeft uint32 | ||||
| 	readBytesLeft   int64 | ||||
|  | ||||
| 	writePacketsLeft uint32 | ||||
| 	writeBytesLeft   int64 | ||||
|  | ||||
| 	// The session ID or nil if first kex did not complete yet. | ||||
| 	sessionID []byte | ||||
| } | ||||
|  | ||||
| type pendingKex struct { | ||||
| 	otherInit []byte | ||||
| 	done      chan error | ||||
| } | ||||
|  | ||||
| func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { | ||||
| 	t := &handshakeTransport{ | ||||
| 		conn:          conn, | ||||
| 		serverVersion: serverVersion, | ||||
| 		clientVersion: clientVersion, | ||||
| 		incoming:      make(chan []byte, chanSize), | ||||
| 		requestKex:    make(chan struct{}, 1), | ||||
| 		startKex:      make(chan *pendingKex, 1), | ||||
|  | ||||
| 		config: config, | ||||
| 	} | ||||
| 	t.resetReadThresholds() | ||||
| 	t.resetWriteThresholds() | ||||
|  | ||||
| 	// We always start with a mandatory key exchange. | ||||
| 	t.requestKex <- struct{}{} | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ClientConfig, dialAddr string, addr net.Addr) *handshakeTransport { | ||||
| 	t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) | ||||
| 	t.dialAddress = dialAddr | ||||
| 	t.remoteAddr = addr | ||||
| 	t.hostKeyCallback = config.HostKeyCallback | ||||
| 	t.bannerCallback = config.BannerCallback | ||||
| 	if config.HostKeyAlgorithms != nil { | ||||
| 		t.hostKeyAlgorithms = config.HostKeyAlgorithms | ||||
| 	} else { | ||||
| 		t.hostKeyAlgorithms = supportedHostKeyAlgos | ||||
| 	} | ||||
| 	go t.readLoop() | ||||
| 	go t.kexLoop() | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport { | ||||
| 	t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) | ||||
| 	t.hostKeys = config.hostKeys | ||||
| 	go t.readLoop() | ||||
| 	go t.kexLoop() | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) getSessionID() []byte { | ||||
| 	return t.sessionID | ||||
| } | ||||
|  | ||||
| // waitSession waits for the session to be established. This should be | ||||
| // the first thing to call after instantiating handshakeTransport. | ||||
| func (t *handshakeTransport) waitSession() error { | ||||
| 	p, err := t.readPacket() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if p[0] != msgNewKeys { | ||||
| 		return fmt.Errorf("ssh: first packet should be msgNewKeys") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) id() string { | ||||
| 	if len(t.hostKeys) > 0 { | ||||
| 		return "server" | ||||
| 	} | ||||
| 	return "client" | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) printPacket(p []byte, write bool) { | ||||
| 	action := "got" | ||||
| 	if write { | ||||
| 		action = "sent" | ||||
| 	} | ||||
|  | ||||
| 	if p[0] == msgChannelData || p[0] == msgChannelExtendedData { | ||||
| 		log.Printf("%s %s data (packet %d bytes)", t.id(), action, len(p)) | ||||
| 	} else { | ||||
| 		msg, err := decode(p) | ||||
| 		log.Printf("%s %s %T %v (%v)", t.id(), action, msg, msg, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) readPacket() ([]byte, error) { | ||||
| 	p, ok := <-t.incoming | ||||
| 	if !ok { | ||||
| 		return nil, t.readError | ||||
| 	} | ||||
| 	return p, nil | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) readLoop() { | ||||
| 	first := true | ||||
| 	for { | ||||
| 		p, err := t.readOnePacket(first) | ||||
| 		first = false | ||||
| 		if err != nil { | ||||
| 			t.readError = err | ||||
| 			close(t.incoming) | ||||
| 			break | ||||
| 		} | ||||
| 		if p[0] == msgIgnore || p[0] == msgDebug { | ||||
| 			continue | ||||
| 		} | ||||
| 		t.incoming <- p | ||||
| 	} | ||||
|  | ||||
| 	// Stop writers too. | ||||
| 	t.recordWriteError(t.readError) | ||||
|  | ||||
| 	// Unblock the writer should it wait for this. | ||||
| 	close(t.startKex) | ||||
|  | ||||
| 	// Don't close t.requestKex; it's also written to from writePacket. | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) pushPacket(p []byte) error { | ||||
| 	if debugHandshake { | ||||
| 		t.printPacket(p, true) | ||||
| 	} | ||||
| 	return t.conn.writePacket(p) | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) getWriteError() error { | ||||
| 	t.mu.Lock() | ||||
| 	defer t.mu.Unlock() | ||||
| 	return t.writeError | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) recordWriteError(err error) { | ||||
| 	t.mu.Lock() | ||||
| 	defer t.mu.Unlock() | ||||
| 	if t.writeError == nil && err != nil { | ||||
| 		t.writeError = err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) requestKeyExchange() { | ||||
| 	select { | ||||
| 	case t.requestKex <- struct{}{}: | ||||
| 	default: | ||||
| 		// something already requested a kex, so do nothing. | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) resetWriteThresholds() { | ||||
| 	t.writePacketsLeft = packetRekeyThreshold | ||||
| 	if t.config.RekeyThreshold > 0 { | ||||
| 		t.writeBytesLeft = int64(t.config.RekeyThreshold) | ||||
| 	} else if t.algorithms != nil { | ||||
| 		t.writeBytesLeft = t.algorithms.w.rekeyBytes() | ||||
| 	} else { | ||||
| 		t.writeBytesLeft = 1 << 30 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) kexLoop() { | ||||
|  | ||||
| write: | ||||
| 	for t.getWriteError() == nil { | ||||
| 		var request *pendingKex | ||||
| 		var sent bool | ||||
|  | ||||
| 		for request == nil || !sent { | ||||
| 			var ok bool | ||||
| 			select { | ||||
| 			case request, ok = <-t.startKex: | ||||
| 				if !ok { | ||||
| 					break write | ||||
| 				} | ||||
| 			case <-t.requestKex: | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			if !sent { | ||||
| 				if err := t.sendKexInit(); err != nil { | ||||
| 					t.recordWriteError(err) | ||||
| 					break | ||||
| 				} | ||||
| 				sent = true | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err := t.getWriteError(); err != nil { | ||||
| 			if request != nil { | ||||
| 				request.done <- err | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		// We're not servicing t.requestKex, but that is OK: | ||||
| 		// we never block on sending to t.requestKex. | ||||
|  | ||||
| 		// We're not servicing t.startKex, but the remote end | ||||
| 		// has just sent us a kexInitMsg, so it can't send | ||||
| 		// another key change request, until we close the done | ||||
| 		// channel on the pendingKex request. | ||||
|  | ||||
| 		err := t.enterKeyExchange(request.otherInit) | ||||
|  | ||||
| 		t.mu.Lock() | ||||
| 		t.writeError = err | ||||
| 		t.sentInitPacket = nil | ||||
| 		t.sentInitMsg = nil | ||||
|  | ||||
| 		t.resetWriteThresholds() | ||||
|  | ||||
| 		// we have completed the key exchange. Since the | ||||
| 		// reader is still blocked, it is safe to clear out | ||||
| 		// the requestKex channel. This avoids the situation | ||||
| 		// where: 1) we consumed our own request for the | ||||
| 		// initial kex, and 2) the kex from the remote side | ||||
| 		// caused another send on the requestKex channel, | ||||
| 	clear: | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-t.requestKex: | ||||
| 				// | ||||
| 			default: | ||||
| 				break clear | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		request.done <- t.writeError | ||||
|  | ||||
| 		// kex finished. Push packets that we received while | ||||
| 		// the kex was in progress. Don't look at t.startKex | ||||
| 		// and don't increment writtenSinceKex: if we trigger | ||||
| 		// another kex while we are still busy with the last | ||||
| 		// one, things will become very confusing. | ||||
| 		for _, p := range t.pendingPackets { | ||||
| 			t.writeError = t.pushPacket(p) | ||||
| 			if t.writeError != nil { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		t.pendingPackets = t.pendingPackets[:0] | ||||
| 		t.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	// drain startKex channel. We don't service t.requestKex | ||||
| 	// because nobody does blocking sends there. | ||||
| 	go func() { | ||||
| 		for init := range t.startKex { | ||||
| 			init.done <- t.writeError | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Unblock reader. | ||||
| 	t.conn.Close() | ||||
| } | ||||
|  | ||||
| // The protocol uses uint32 for packet counters, so we can't let them | ||||
| // reach 1<<32.  We will actually read and write more packets than | ||||
| // this, though: the other side may send more packets, and after we | ||||
| // hit this limit on writing we will send a few more packets for the | ||||
| // key exchange itself. | ||||
| const packetRekeyThreshold = (1 << 31) | ||||
|  | ||||
| func (t *handshakeTransport) resetReadThresholds() { | ||||
| 	t.readPacketsLeft = packetRekeyThreshold | ||||
| 	if t.config.RekeyThreshold > 0 { | ||||
| 		t.readBytesLeft = int64(t.config.RekeyThreshold) | ||||
| 	} else if t.algorithms != nil { | ||||
| 		t.readBytesLeft = t.algorithms.r.rekeyBytes() | ||||
| 	} else { | ||||
| 		t.readBytesLeft = 1 << 30 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { | ||||
| 	p, err := t.conn.readPacket() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if t.readPacketsLeft > 0 { | ||||
| 		t.readPacketsLeft-- | ||||
| 	} else { | ||||
| 		t.requestKeyExchange() | ||||
| 	} | ||||
|  | ||||
| 	if t.readBytesLeft > 0 { | ||||
| 		t.readBytesLeft -= int64(len(p)) | ||||
| 	} else { | ||||
| 		t.requestKeyExchange() | ||||
| 	} | ||||
|  | ||||
| 	if debugHandshake { | ||||
| 		t.printPacket(p, false) | ||||
| 	} | ||||
|  | ||||
| 	if first && p[0] != msgKexInit { | ||||
| 		return nil, fmt.Errorf("ssh: first packet should be msgKexInit") | ||||
| 	} | ||||
|  | ||||
| 	if p[0] != msgKexInit { | ||||
| 		return p, nil | ||||
| 	} | ||||
|  | ||||
| 	firstKex := t.sessionID == nil | ||||
|  | ||||
| 	kex := pendingKex{ | ||||
| 		done:      make(chan error, 1), | ||||
| 		otherInit: p, | ||||
| 	} | ||||
| 	t.startKex <- &kex | ||||
| 	err = <-kex.done | ||||
|  | ||||
| 	if debugHandshake { | ||||
| 		log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err) | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	t.resetReadThresholds() | ||||
|  | ||||
| 	// By default, a key exchange is hidden from higher layers by | ||||
| 	// translating it into msgIgnore. | ||||
| 	successPacket := []byte{msgIgnore} | ||||
| 	if firstKex { | ||||
| 		// sendKexInit() for the first kex waits for | ||||
| 		// msgNewKeys so the authentication process is | ||||
| 		// guaranteed to happen over an encrypted transport. | ||||
| 		successPacket = []byte{msgNewKeys} | ||||
| 	} | ||||
|  | ||||
| 	return successPacket, nil | ||||
| } | ||||
|  | ||||
| // sendKexInit sends a key change message. | ||||
| func (t *handshakeTransport) sendKexInit() error { | ||||
| 	t.mu.Lock() | ||||
| 	defer t.mu.Unlock() | ||||
| 	if t.sentInitMsg != nil { | ||||
| 		// kexInits may be sent either in response to the other side, | ||||
| 		// or because our side wants to initiate a key change, so we | ||||
| 		// may have already sent a kexInit. In that case, don't send a | ||||
| 		// second kexInit. | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	msg := &kexInitMsg{ | ||||
| 		KexAlgos:                t.config.KeyExchanges, | ||||
| 		CiphersClientServer:     t.config.Ciphers, | ||||
| 		CiphersServerClient:     t.config.Ciphers, | ||||
| 		MACsClientServer:        t.config.MACs, | ||||
| 		MACsServerClient:        t.config.MACs, | ||||
| 		CompressionClientServer: supportedCompressions, | ||||
| 		CompressionServerClient: supportedCompressions, | ||||
| 	} | ||||
| 	io.ReadFull(rand.Reader, msg.Cookie[:]) | ||||
|  | ||||
| 	if len(t.hostKeys) > 0 { | ||||
| 		for _, k := range t.hostKeys { | ||||
| 			msg.ServerHostKeyAlgos = append( | ||||
| 				msg.ServerHostKeyAlgos, k.PublicKey().Type()) | ||||
| 		} | ||||
| 	} else { | ||||
| 		msg.ServerHostKeyAlgos = t.hostKeyAlgorithms | ||||
| 	} | ||||
| 	packet := Marshal(msg) | ||||
|  | ||||
| 	// writePacket destroys the contents, so save a copy. | ||||
| 	packetCopy := make([]byte, len(packet)) | ||||
| 	copy(packetCopy, packet) | ||||
|  | ||||
| 	if err := t.pushPacket(packetCopy); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	t.sentInitMsg = msg | ||||
| 	t.sentInitPacket = packet | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) writePacket(p []byte) error { | ||||
| 	switch p[0] { | ||||
| 	case msgKexInit: | ||||
| 		return errors.New("ssh: only handshakeTransport can send kexInit") | ||||
| 	case msgNewKeys: | ||||
| 		return errors.New("ssh: only handshakeTransport can send newKeys") | ||||
| 	} | ||||
|  | ||||
| 	t.mu.Lock() | ||||
| 	defer t.mu.Unlock() | ||||
| 	if t.writeError != nil { | ||||
| 		return t.writeError | ||||
| 	} | ||||
|  | ||||
| 	if t.sentInitMsg != nil { | ||||
| 		// Copy the packet so the writer can reuse the buffer. | ||||
| 		cp := make([]byte, len(p)) | ||||
| 		copy(cp, p) | ||||
| 		t.pendingPackets = append(t.pendingPackets, cp) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if t.writeBytesLeft > 0 { | ||||
| 		t.writeBytesLeft -= int64(len(p)) | ||||
| 	} else { | ||||
| 		t.requestKeyExchange() | ||||
| 	} | ||||
|  | ||||
| 	if t.writePacketsLeft > 0 { | ||||
| 		t.writePacketsLeft-- | ||||
| 	} else { | ||||
| 		t.requestKeyExchange() | ||||
| 	} | ||||
|  | ||||
| 	if err := t.pushPacket(p); err != nil { | ||||
| 		t.writeError = err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) Close() error { | ||||
| 	return t.conn.Close() | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { | ||||
| 	if debugHandshake { | ||||
| 		log.Printf("%s entered key exchange", t.id()) | ||||
| 	} | ||||
|  | ||||
| 	otherInit := &kexInitMsg{} | ||||
| 	if err := Unmarshal(otherInitPacket, otherInit); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	magics := handshakeMagics{ | ||||
| 		clientVersion: t.clientVersion, | ||||
| 		serverVersion: t.serverVersion, | ||||
| 		clientKexInit: otherInitPacket, | ||||
| 		serverKexInit: t.sentInitPacket, | ||||
| 	} | ||||
|  | ||||
| 	clientInit := otherInit | ||||
| 	serverInit := t.sentInitMsg | ||||
| 	if len(t.hostKeys) == 0 { | ||||
| 		clientInit, serverInit = serverInit, clientInit | ||||
|  | ||||
| 		magics.clientKexInit = t.sentInitPacket | ||||
| 		magics.serverKexInit = otherInitPacket | ||||
| 	} | ||||
|  | ||||
| 	var err error | ||||
| 	t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// We don't send FirstKexFollows, but we handle receiving it. | ||||
| 	// | ||||
| 	// RFC 4253 section 7 defines the kex and the agreement method for | ||||
| 	// first_kex_packet_follows. It states that the guessed packet | ||||
| 	// should be ignored if the "kex algorithm and/or the host | ||||
| 	// key algorithm is guessed wrong (server and client have | ||||
| 	// different preferred algorithm), or if any of the other | ||||
| 	// algorithms cannot be agreed upon". The other algorithms have | ||||
| 	// already been checked above so the kex algorithm and host key | ||||
| 	// algorithm are checked here. | ||||
| 	if otherInit.FirstKexFollows && (clientInit.KexAlgos[0] != serverInit.KexAlgos[0] || clientInit.ServerHostKeyAlgos[0] != serverInit.ServerHostKeyAlgos[0]) { | ||||
| 		// other side sent a kex message for the wrong algorithm, | ||||
| 		// which we have to ignore. | ||||
| 		if _, err := t.conn.readPacket(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	kex, ok := kexAlgoMap[t.algorithms.kex] | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex) | ||||
| 	} | ||||
|  | ||||
| 	var result *kexResult | ||||
| 	if len(t.hostKeys) > 0 { | ||||
| 		result, err = t.server(kex, t.algorithms, &magics) | ||||
| 	} else { | ||||
| 		result, err = t.client(kex, t.algorithms, &magics) | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if t.sessionID == nil { | ||||
| 		t.sessionID = result.H | ||||
| 	} | ||||
| 	result.SessionID = t.sessionID | ||||
|  | ||||
| 	if err := t.conn.prepareKeyChange(t.algorithms, result); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if packet, err := t.conn.readPacket(); err != nil { | ||||
| 		return err | ||||
| 	} else if packet[0] != msgNewKeys { | ||||
| 		return unexpectedMessageError(msgNewKeys, packet[0]) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { | ||||
| 	var hostKey Signer | ||||
| 	for _, k := range t.hostKeys { | ||||
| 		if algs.hostKey == k.PublicKey().Type() { | ||||
| 			hostKey = k | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey) | ||||
| 	return r, err | ||||
| } | ||||
|  | ||||
| func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { | ||||
| 	result, err := kex.Client(t.conn, t.config.Rand, magics) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	hostKey, err := ParsePublicKey(result.HostKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if err := verifyHostKeySignature(hostKey, result); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return result, nil | ||||
| } | ||||
							
								
								
									
										540
									
								
								vendor/golang.org/x/crypto/ssh/kex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										540
									
								
								vendor/golang.org/x/crypto/ssh/kex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,540 @@ | ||||
| // Copyright 2013 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 ssh | ||||
|  | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/subtle" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
|  | ||||
| 	"golang.org/x/crypto/curve25519" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	kexAlgoDH1SHA1          = "diffie-hellman-group1-sha1" | ||||
| 	kexAlgoDH14SHA1         = "diffie-hellman-group14-sha1" | ||||
| 	kexAlgoECDH256          = "ecdh-sha2-nistp256" | ||||
| 	kexAlgoECDH384          = "ecdh-sha2-nistp384" | ||||
| 	kexAlgoECDH521          = "ecdh-sha2-nistp521" | ||||
| 	kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org" | ||||
| ) | ||||
|  | ||||
| // kexResult captures the outcome of a key exchange. | ||||
| type kexResult struct { | ||||
| 	// Session hash. See also RFC 4253, section 8. | ||||
| 	H []byte | ||||
|  | ||||
| 	// Shared secret. See also RFC 4253, section 8. | ||||
| 	K []byte | ||||
|  | ||||
| 	// Host key as hashed into H. | ||||
| 	HostKey []byte | ||||
|  | ||||
| 	// Signature of H. | ||||
| 	Signature []byte | ||||
|  | ||||
| 	// A cryptographic hash function that matches the security | ||||
| 	// level of the key exchange algorithm. It is used for | ||||
| 	// calculating H, and for deriving keys from H and K. | ||||
| 	Hash crypto.Hash | ||||
|  | ||||
| 	// The session ID, which is the first H computed. This is used | ||||
| 	// to derive key material inside the transport. | ||||
| 	SessionID []byte | ||||
| } | ||||
|  | ||||
| // handshakeMagics contains data that is always included in the | ||||
| // session hash. | ||||
| type handshakeMagics struct { | ||||
| 	clientVersion, serverVersion []byte | ||||
| 	clientKexInit, serverKexInit []byte | ||||
| } | ||||
|  | ||||
| func (m *handshakeMagics) write(w io.Writer) { | ||||
| 	writeString(w, m.clientVersion) | ||||
| 	writeString(w, m.serverVersion) | ||||
| 	writeString(w, m.clientKexInit) | ||||
| 	writeString(w, m.serverKexInit) | ||||
| } | ||||
|  | ||||
| // kexAlgorithm abstracts different key exchange algorithms. | ||||
| type kexAlgorithm interface { | ||||
| 	// Server runs server-side key agreement, signing the result | ||||
| 	// with a hostkey. | ||||
| 	Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) | ||||
|  | ||||
| 	// Client runs the client-side key agreement. Caller is | ||||
| 	// responsible for verifying the host key signature. | ||||
| 	Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) | ||||
| } | ||||
|  | ||||
| // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. | ||||
| type dhGroup struct { | ||||
| 	g, p, pMinus1 *big.Int | ||||
| } | ||||
|  | ||||
| func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { | ||||
| 	if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 { | ||||
| 		return nil, errors.New("ssh: DH parameter out of bounds") | ||||
| 	} | ||||
| 	return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil | ||||
| } | ||||
|  | ||||
| func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { | ||||
| 	hashFunc := crypto.SHA1 | ||||
|  | ||||
| 	var x *big.Int | ||||
| 	for { | ||||
| 		var err error | ||||
| 		if x, err = rand.Int(randSource, group.pMinus1); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if x.Sign() > 0 { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	X := new(big.Int).Exp(group.g, x, group.p) | ||||
| 	kexDHInit := kexDHInitMsg{ | ||||
| 		X: X, | ||||
| 	} | ||||
| 	if err := c.writePacket(Marshal(&kexDHInit)); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	packet, err := c.readPacket() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var kexDHReply kexDHReplyMsg | ||||
| 	if err = Unmarshal(packet, &kexDHReply); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	ki, err := group.diffieHellman(kexDHReply.Y, x) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	h := hashFunc.New() | ||||
| 	magics.write(h) | ||||
| 	writeString(h, kexDHReply.HostKey) | ||||
| 	writeInt(h, X) | ||||
| 	writeInt(h, kexDHReply.Y) | ||||
| 	K := make([]byte, intLength(ki)) | ||||
| 	marshalInt(K, ki) | ||||
| 	h.Write(K) | ||||
|  | ||||
| 	return &kexResult{ | ||||
| 		H:         h.Sum(nil), | ||||
| 		K:         K, | ||||
| 		HostKey:   kexDHReply.HostKey, | ||||
| 		Signature: kexDHReply.Signature, | ||||
| 		Hash:      crypto.SHA1, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { | ||||
| 	hashFunc := crypto.SHA1 | ||||
| 	packet, err := c.readPacket() | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	var kexDHInit kexDHInitMsg | ||||
| 	if err = Unmarshal(packet, &kexDHInit); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var y *big.Int | ||||
| 	for { | ||||
| 		if y, err = rand.Int(randSource, group.pMinus1); err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		if y.Sign() > 0 { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	Y := new(big.Int).Exp(group.g, y, group.p) | ||||
| 	ki, err := group.diffieHellman(kexDHInit.X, y) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	hostKeyBytes := priv.PublicKey().Marshal() | ||||
|  | ||||
| 	h := hashFunc.New() | ||||
| 	magics.write(h) | ||||
| 	writeString(h, hostKeyBytes) | ||||
| 	writeInt(h, kexDHInit.X) | ||||
| 	writeInt(h, Y) | ||||
|  | ||||
| 	K := make([]byte, intLength(ki)) | ||||
| 	marshalInt(K, ki) | ||||
| 	h.Write(K) | ||||
|  | ||||
| 	H := h.Sum(nil) | ||||
|  | ||||
| 	// H is already a hash, but the hostkey signing will apply its | ||||
| 	// own key-specific hash algorithm. | ||||
| 	sig, err := signAndMarshal(priv, randSource, H) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	kexDHReply := kexDHReplyMsg{ | ||||
| 		HostKey:   hostKeyBytes, | ||||
| 		Y:         Y, | ||||
| 		Signature: sig, | ||||
| 	} | ||||
| 	packet = Marshal(&kexDHReply) | ||||
|  | ||||
| 	err = c.writePacket(packet) | ||||
| 	return &kexResult{ | ||||
| 		H:         H, | ||||
| 		K:         K, | ||||
| 		HostKey:   hostKeyBytes, | ||||
| 		Signature: sig, | ||||
| 		Hash:      crypto.SHA1, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // ecdh performs Elliptic Curve Diffie-Hellman key exchange as | ||||
| // described in RFC 5656, section 4. | ||||
| type ecdh struct { | ||||
| 	curve elliptic.Curve | ||||
| } | ||||
|  | ||||
| func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { | ||||
| 	ephKey, err := ecdsa.GenerateKey(kex.curve, rand) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	kexInit := kexECDHInitMsg{ | ||||
| 		ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), | ||||
| 	} | ||||
|  | ||||
| 	serialized := Marshal(&kexInit) | ||||
| 	if err := c.writePacket(serialized); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	packet, err := c.readPacket() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var reply kexECDHReplyMsg | ||||
| 	if err = Unmarshal(packet, &reply); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// generate shared secret | ||||
| 	secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) | ||||
|  | ||||
| 	h := ecHash(kex.curve).New() | ||||
| 	magics.write(h) | ||||
| 	writeString(h, reply.HostKey) | ||||
| 	writeString(h, kexInit.ClientPubKey) | ||||
| 	writeString(h, reply.EphemeralPubKey) | ||||
| 	K := make([]byte, intLength(secret)) | ||||
| 	marshalInt(K, secret) | ||||
| 	h.Write(K) | ||||
|  | ||||
| 	return &kexResult{ | ||||
| 		H:         h.Sum(nil), | ||||
| 		K:         K, | ||||
| 		HostKey:   reply.HostKey, | ||||
| 		Signature: reply.Signature, | ||||
| 		Hash:      ecHash(kex.curve), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // unmarshalECKey parses and checks an EC key. | ||||
| func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { | ||||
| 	x, y = elliptic.Unmarshal(curve, pubkey) | ||||
| 	if x == nil { | ||||
| 		return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") | ||||
| 	} | ||||
| 	if !validateECPublicKey(curve, x, y) { | ||||
| 		return nil, nil, errors.New("ssh: public key not on curve") | ||||
| 	} | ||||
| 	return x, y, nil | ||||
| } | ||||
|  | ||||
| // validateECPublicKey checks that the point is a valid public key for | ||||
| // the given curve. See [SEC1], 3.2.2 | ||||
| func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { | ||||
| 	if x.Sign() == 0 && y.Sign() == 0 { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if x.Cmp(curve.Params().P) >= 0 { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if y.Cmp(curve.Params().P) >= 0 { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if !curve.IsOnCurve(x, y) { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	// We don't check if N * PubKey == 0, since | ||||
| 	// | ||||
| 	// - the NIST curves have cofactor = 1, so this is implicit. | ||||
| 	// (We don't foresee an implementation that supports non NIST | ||||
| 	// curves) | ||||
| 	// | ||||
| 	// - for ephemeral keys, we don't need to worry about small | ||||
| 	// subgroup attacks. | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { | ||||
| 	packet, err := c.readPacket() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var kexECDHInit kexECDHInitMsg | ||||
| 	if err = Unmarshal(packet, &kexECDHInit); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// We could cache this key across multiple users/multiple | ||||
| 	// connection attempts, but the benefit is small. OpenSSH | ||||
| 	// generates a new key for each incoming connection. | ||||
| 	ephKey, err := ecdsa.GenerateKey(kex.curve, rand) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	hostKeyBytes := priv.PublicKey().Marshal() | ||||
|  | ||||
| 	serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) | ||||
|  | ||||
| 	// generate shared secret | ||||
| 	secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) | ||||
|  | ||||
| 	h := ecHash(kex.curve).New() | ||||
| 	magics.write(h) | ||||
| 	writeString(h, hostKeyBytes) | ||||
| 	writeString(h, kexECDHInit.ClientPubKey) | ||||
| 	writeString(h, serializedEphKey) | ||||
|  | ||||
| 	K := make([]byte, intLength(secret)) | ||||
| 	marshalInt(K, secret) | ||||
| 	h.Write(K) | ||||
|  | ||||
| 	H := h.Sum(nil) | ||||
|  | ||||
| 	// H is already a hash, but the hostkey signing will apply its | ||||
| 	// own key-specific hash algorithm. | ||||
| 	sig, err := signAndMarshal(priv, rand, H) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	reply := kexECDHReplyMsg{ | ||||
| 		EphemeralPubKey: serializedEphKey, | ||||
| 		HostKey:         hostKeyBytes, | ||||
| 		Signature:       sig, | ||||
| 	} | ||||
|  | ||||
| 	serialized := Marshal(&reply) | ||||
| 	if err := c.writePacket(serialized); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &kexResult{ | ||||
| 		H:         H, | ||||
| 		K:         K, | ||||
| 		HostKey:   reply.HostKey, | ||||
| 		Signature: sig, | ||||
| 		Hash:      ecHash(kex.curve), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| var kexAlgoMap = map[string]kexAlgorithm{} | ||||
|  | ||||
| func init() { | ||||
| 	// This is the group called diffie-hellman-group1-sha1 in RFC | ||||
| 	// 4253 and Oakley Group 2 in RFC 2409. | ||||
| 	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) | ||||
| 	kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ | ||||
| 		g:       new(big.Int).SetInt64(2), | ||||
| 		p:       p, | ||||
| 		pMinus1: new(big.Int).Sub(p, bigOne), | ||||
| 	} | ||||
|  | ||||
| 	// This is the group called diffie-hellman-group14-sha1 in RFC | ||||
| 	// 4253 and Oakley Group 14 in RFC 3526. | ||||
| 	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) | ||||
|  | ||||
| 	kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ | ||||
| 		g:       new(big.Int).SetInt64(2), | ||||
| 		p:       p, | ||||
| 		pMinus1: new(big.Int).Sub(p, bigOne), | ||||
| 	} | ||||
|  | ||||
| 	kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} | ||||
| 	kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} | ||||
| 	kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} | ||||
| 	kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{} | ||||
| } | ||||
|  | ||||
| // curve25519sha256 implements the curve25519-sha256@libssh.org key | ||||
| // agreement protocol, as described in | ||||
| // https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt | ||||
| type curve25519sha256 struct{} | ||||
|  | ||||
| type curve25519KeyPair struct { | ||||
| 	priv [32]byte | ||||
| 	pub  [32]byte | ||||
| } | ||||
|  | ||||
| func (kp *curve25519KeyPair) generate(rand io.Reader) error { | ||||
| 	if _, err := io.ReadFull(rand, kp.priv[:]); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	curve25519.ScalarBaseMult(&kp.pub, &kp.priv) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // curve25519Zeros is just an array of 32 zero bytes so that we have something | ||||
| // convenient to compare against in order to reject curve25519 points with the | ||||
| // wrong order. | ||||
| var curve25519Zeros [32]byte | ||||
|  | ||||
| func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { | ||||
| 	var kp curve25519KeyPair | ||||
| 	if err := kp.generate(rand); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	packet, err := c.readPacket() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var reply kexECDHReplyMsg | ||||
| 	if err = Unmarshal(packet, &reply); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(reply.EphemeralPubKey) != 32 { | ||||
| 		return nil, errors.New("ssh: peer's curve25519 public value has wrong length") | ||||
| 	} | ||||
|  | ||||
| 	var servPub, secret [32]byte | ||||
| 	copy(servPub[:], reply.EphemeralPubKey) | ||||
| 	curve25519.ScalarMult(&secret, &kp.priv, &servPub) | ||||
| 	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { | ||||
| 		return nil, errors.New("ssh: peer's curve25519 public value has wrong order") | ||||
| 	} | ||||
|  | ||||
| 	h := crypto.SHA256.New() | ||||
| 	magics.write(h) | ||||
| 	writeString(h, reply.HostKey) | ||||
| 	writeString(h, kp.pub[:]) | ||||
| 	writeString(h, reply.EphemeralPubKey) | ||||
|  | ||||
| 	ki := new(big.Int).SetBytes(secret[:]) | ||||
| 	K := make([]byte, intLength(ki)) | ||||
| 	marshalInt(K, ki) | ||||
| 	h.Write(K) | ||||
|  | ||||
| 	return &kexResult{ | ||||
| 		H:         h.Sum(nil), | ||||
| 		K:         K, | ||||
| 		HostKey:   reply.HostKey, | ||||
| 		Signature: reply.Signature, | ||||
| 		Hash:      crypto.SHA256, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { | ||||
| 	packet, err := c.readPacket() | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	var kexInit kexECDHInitMsg | ||||
| 	if err = Unmarshal(packet, &kexInit); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if len(kexInit.ClientPubKey) != 32 { | ||||
| 		return nil, errors.New("ssh: peer's curve25519 public value has wrong length") | ||||
| 	} | ||||
|  | ||||
| 	var kp curve25519KeyPair | ||||
| 	if err := kp.generate(rand); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var clientPub, secret [32]byte | ||||
| 	copy(clientPub[:], kexInit.ClientPubKey) | ||||
| 	curve25519.ScalarMult(&secret, &kp.priv, &clientPub) | ||||
| 	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { | ||||
| 		return nil, errors.New("ssh: peer's curve25519 public value has wrong order") | ||||
| 	} | ||||
|  | ||||
| 	hostKeyBytes := priv.PublicKey().Marshal() | ||||
|  | ||||
| 	h := crypto.SHA256.New() | ||||
| 	magics.write(h) | ||||
| 	writeString(h, hostKeyBytes) | ||||
| 	writeString(h, kexInit.ClientPubKey) | ||||
| 	writeString(h, kp.pub[:]) | ||||
|  | ||||
| 	ki := new(big.Int).SetBytes(secret[:]) | ||||
| 	K := make([]byte, intLength(ki)) | ||||
| 	marshalInt(K, ki) | ||||
| 	h.Write(K) | ||||
|  | ||||
| 	H := h.Sum(nil) | ||||
|  | ||||
| 	sig, err := signAndMarshal(priv, rand, H) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	reply := kexECDHReplyMsg{ | ||||
| 		EphemeralPubKey: kp.pub[:], | ||||
| 		HostKey:         hostKeyBytes, | ||||
| 		Signature:       sig, | ||||
| 	} | ||||
| 	if err := c.writePacket(Marshal(&reply)); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &kexResult{ | ||||
| 		H:         H, | ||||
| 		K:         K, | ||||
| 		HostKey:   hostKeyBytes, | ||||
| 		Signature: sig, | ||||
| 		Hash:      crypto.SHA256, | ||||
| 	}, nil | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user