Files

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)
}