255 lines
6.1 KiB
Go
255 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/racingmars/go3270"
|
|
)
|
|
|
|
// Telnet Command Constants (RFC 854 / RFC 885)
|
|
const (
|
|
IAC byte = 0xFF
|
|
DONT byte = 0xFE
|
|
DO byte = 0xFD
|
|
WONT byte = 0xFC
|
|
WILL byte = 0xFB
|
|
SB byte = 0xFA
|
|
GA byte = 0xF9
|
|
NOP byte = 0xF1
|
|
SE byte = 0xF0
|
|
EOR byte = 0xEF
|
|
|
|
OPT_BINARY byte = 0x00
|
|
OPT_SUPPRESS_GA byte = 0x03
|
|
OPT_TIME_MARK byte = 0x06
|
|
OPT_TERMINAL_TYPE byte = 0x18
|
|
OPT_EOR byte = 0x19
|
|
OPT_TN3270E byte = 0x28
|
|
|
|
// Terminal-Type Subnegotiation Codes (RFC 1091)
|
|
TTYPE_IS byte = 0x00
|
|
TTYPE_SEND byte = 0x01
|
|
|
|
// TN3270E Subnegotiation Command Codes (RFC 2355)
|
|
TN3270E_DEVICE_TYPE byte = 0x02
|
|
TN3270E_FUNCTIONS byte = 0x03
|
|
TN3270E_IS byte = 0x04
|
|
TN3270E_REJECT byte = 0x06
|
|
TN3270E_REQUEST byte = 0x07
|
|
TN3270E_SEND byte = 0x08
|
|
|
|
// Data Types in TN3270E Header
|
|
TN3270E_DATA_3270 byte = 0x00
|
|
TN3270E_DATA_RESP byte = 0x02
|
|
TN3270E_DATA_BIND byte = 0x03
|
|
TN3270E_DATA_UNBIND byte = 0x04
|
|
TN3270E_DATA_SSCP_LU byte = 0x07
|
|
|
|
// Functions & Header Flags in RFC 2355
|
|
TN3270E_FUNC_BIND_IMAGE byte = 0x00
|
|
TN3270E_FUNC_RESPONSES byte = 0x02
|
|
TN3270E_RESP_ALWAYS byte = 0x02
|
|
TN3270E_UNBIND_NORMAL byte = 0x01
|
|
TN3270E_MAX_SEQ uint16 = 0x7FFF
|
|
|
|
// 3270 Command Codes
|
|
CMD_WRITE byte = 0xF1
|
|
CMD_WSF byte = 0xF3
|
|
CMD_ERASE_WRITE byte = 0xF5
|
|
CMD_ERASE_WRITE_SNA byte = 0x05
|
|
CMD_ERASE_WRITE_ALT byte = 0x7E
|
|
CMD_ERASE_WRITE_ALT_SNA byte = 0x0D
|
|
CMD_ERASE_ALL_UNPROTECTED byte = 0x6F
|
|
|
|
// 3270 Structured Field & Query Reply Constants
|
|
AID_QUERY_REPLY byte = 0x88
|
|
SF_READ_PARTITION byte = 0x01
|
|
SF_RP_QUERY byte = 0xFF
|
|
SF_QUERY_REPLY byte = 0x81
|
|
QR_USABLE_AREA byte = 0x81
|
|
QR_COLOR byte = 0x86
|
|
QR_HIGHLIGHTING byte = 0x87
|
|
QR_IMPLICIT_PARTITION byte = 0xA6
|
|
QR_NULL byte = 0xFF
|
|
|
|
// 3270 Extended Color & Highlighting Attribute Constants
|
|
ATTR_COLOR_BLUE byte = 0xF1
|
|
ATTR_COLOR_RED byte = 0xF2
|
|
ATTR_COLOR_PINK byte = 0xF3
|
|
ATTR_COLOR_GREEN byte = 0xF4
|
|
ATTR_COLOR_TURQUOISE byte = 0xF5
|
|
ATTR_COLOR_YELLOW byte = 0xF6
|
|
ATTR_COLOR_WHITE byte = 0xF7
|
|
|
|
ATTR_HL_BLINK byte = 0xF1
|
|
ATTR_HL_REVERSE byte = 0xF2
|
|
ATTR_HL_UNDERSCORE byte = 0xF4
|
|
)
|
|
|
|
type LoginCredentials struct {
|
|
Username string
|
|
Password string
|
|
SessionKey string
|
|
}
|
|
|
|
type AuthResult struct {
|
|
OK bool
|
|
Reason string
|
|
IsAdmin bool
|
|
}
|
|
|
|
type TargetSystem struct {
|
|
Name string `toml:"name"`
|
|
Description string `toml:"description"`
|
|
HostPort string `toml:"host_port"`
|
|
TLS bool `toml:"tls"`
|
|
InsecureSkipVerify bool `toml:"insecure_skip_verify"`
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
BindAddress string `toml:"bind_address"`
|
|
Codepage string `toml:"codepage"`
|
|
TLS bool `toml:"tls"`
|
|
CertFile string `toml:"cert_file"`
|
|
KeyFile string `toml:"key_file"`
|
|
Public bool `toml:"public"`
|
|
AutoHoldDiscardLimit int `toml:"auto_hold_discard_limit"`
|
|
AutoHoldBurstLimit int `toml:"auto_hold_burst_limit"`
|
|
}
|
|
|
|
type LoginConfig struct {
|
|
Title string `toml:"title"`
|
|
Subtitle string `toml:"subtitle"`
|
|
PromptUsername string `toml:"prompt_username"`
|
|
PromptPassword string `toml:"prompt_password"`
|
|
Instructions string `toml:"instructions"`
|
|
}
|
|
|
|
type MenuConfig struct {
|
|
Title string `toml:"title"`
|
|
PromptSelect string `toml:"prompt_select"`
|
|
Footer string `toml:"footer"`
|
|
}
|
|
|
|
type UserConfig struct {
|
|
Username string `toml:"username"`
|
|
Hash string `toml:"hash"`
|
|
Password string `toml:"password"`
|
|
Admin bool `toml:"admin"`
|
|
}
|
|
|
|
type Config struct {
|
|
Public bool `toml:"public"`
|
|
Server ServerConfig `toml:"server"`
|
|
Login LoginConfig `toml:"login"`
|
|
Menu MenuConfig `toml:"menu"`
|
|
Systems []TargetSystem `toml:"systems"`
|
|
Users []UserConfig `toml:"users"`
|
|
}
|
|
|
|
const (
|
|
defaultAutoHoldDiscardLimit = 5
|
|
autoHoldDiscardWindow = 5 * time.Second
|
|
defaultAutoHoldBurstLimit = 20
|
|
autoHoldBurstWindow = 1 * time.Second
|
|
)
|
|
|
|
type SessionSlot struct {
|
|
System TargetSystem
|
|
Upstream net.Conn
|
|
LastScreen []byte
|
|
ScreenChanged bool
|
|
IsTN3270E bool
|
|
IsBound bool
|
|
SeqNumber uint16
|
|
IsHeld bool
|
|
MaxBufferSize int
|
|
DiscardCount int
|
|
DiscardWindow time.Time
|
|
WriteBurstCount int
|
|
WriteBurstWindow time.Time
|
|
AutoHoldDiscardLimit int
|
|
AutoHoldBurstLimit int
|
|
Mu sync.Mutex
|
|
}
|
|
|
|
func (s *SessionSlot) resetHoldAndLoopCountersLocked() {
|
|
s.IsHeld = false
|
|
s.ScreenChanged = false
|
|
s.DiscardCount = 0
|
|
s.DiscardWindow = time.Time{}
|
|
s.WriteBurstCount = 0
|
|
s.WriteBurstWindow = time.Time{}
|
|
}
|
|
|
|
type slotRenderInfo struct {
|
|
status string
|
|
color go3270.Color
|
|
hasNotice bool
|
|
}
|
|
|
|
type UserMasterSession struct {
|
|
Username string
|
|
SessionKey string
|
|
DisplayTag string
|
|
TerminalType string
|
|
DevInfo go3270.DevInfo
|
|
ClientConn net.Conn
|
|
ActiveSlotIdx int
|
|
Slots []*SessionSlot
|
|
IsDisconnected bool
|
|
MenuPage int
|
|
IsAdmin bool
|
|
Mu sync.Mutex
|
|
}
|
|
|
|
func (u *UserConfig) GetHash() string {
|
|
if u.Hash != "" {
|
|
return u.Hash
|
|
}
|
|
return u.Password
|
|
}
|
|
|
|
func (s *UserMasterSession) Identity() (username, displayTag string) {
|
|
s.Mu.Lock()
|
|
defer s.Mu.Unlock()
|
|
return s.Username, s.DisplayTag
|
|
}
|
|
|
|
func (s *UserMasterSession) Codepage() go3270.Codepage {
|
|
s.Mu.Lock()
|
|
defer s.Mu.Unlock()
|
|
return s.codepageLocked()
|
|
}
|
|
|
|
func (s *UserMasterSession) codepageLocked() go3270.Codepage {
|
|
if s.DevInfo != nil {
|
|
return s.DevInfo.Codepage()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *UserMasterSession) CountActiveSlots() int {
|
|
s.Mu.Lock()
|
|
defer s.Mu.Unlock()
|
|
return s.countActiveSlotsLocked()
|
|
}
|
|
|
|
func (s *UserMasterSession) countActiveSlotsLocked() int {
|
|
count := 0
|
|
for _, slot := range s.Slots {
|
|
if slot != nil {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (s *UserMasterSession) GetMatrix() (rows, cols, maxBuf int) {
|
|
s.Mu.Lock()
|
|
defer s.Mu.Unlock()
|
|
return GetTerminalMatrix(s.TerminalType, s.DevInfo)
|
|
}
|