277 lines
5.9 KiB
Go
277 lines
5.9 KiB
Go
// Sshwifty - A Web SSH client
|
|
//
|
|
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package commands
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"strconv"
|
|
|
|
"github.com/niruix/sshwifty/application/rw"
|
|
)
|
|
|
|
//Errors
|
|
var (
|
|
ErrAddressParseBufferTooSmallForHeader = errors.New(
|
|
"Buffer space was too small to parse the address header")
|
|
|
|
ErrAddressParseBufferTooSmallForIPv4 = errors.New(
|
|
"Buffer space was too small to parse the IPv4 address")
|
|
|
|
ErrAddressParseBufferTooSmallForIPv6 = errors.New(
|
|
"Buffer space was too small to parse the IPv6 address")
|
|
|
|
ErrAddressParseBufferTooSmallForHostName = errors.New(
|
|
"Buffer space was too small to parse the hostname address")
|
|
|
|
ErrAddressMarshalBufferTooSmall = errors.New(
|
|
"Buffer space was too small to marshal the address")
|
|
|
|
ErrAddressInvalidAddressType = errors.New(
|
|
"Invalid address type")
|
|
)
|
|
|
|
// AddressType Type of the address
|
|
type AddressType byte
|
|
|
|
// Address types
|
|
const (
|
|
LoopbackAddr AddressType = 0x00
|
|
IPv4Addr AddressType = 0x01
|
|
IPv6Addr AddressType = 0x02
|
|
HostNameAddr AddressType = 0x03
|
|
)
|
|
|
|
// Address data
|
|
type Address struct {
|
|
port uint16
|
|
kind AddressType
|
|
data []byte
|
|
}
|
|
|
|
// ParseAddress parses the reader and return an Address
|
|
//
|
|
// Address data format:
|
|
// +-------------+--------------+---------------+
|
|
// | 2 bytes | 1 byte | n bytes |
|
|
// +-------------+--------------+---------------+
|
|
// | Port number | Address type | Address data |
|
|
// +-------------+--------------+---------------+
|
|
//
|
|
// Address types:
|
|
// - LoopbackAddr: 00 Localhost, don't carry Address data
|
|
// - IPv4Addr: 01 IPv4 Address, carries 4 bytes of Address data
|
|
// - IPv6Addr: 10 IPv6 Address, carries 16 bytes Address data
|
|
// - HostnameAddr: 11 Host name string, length of Address data is indicated
|
|
// by the remainer of the byte (11-- ----). maxlen = 63
|
|
//
|
|
func ParseAddress(reader rw.ReaderFunc, buf []byte) (Address, error) {
|
|
if len(buf) < 3 {
|
|
return Address{}, ErrAddressParseBufferTooSmallForHeader
|
|
}
|
|
|
|
_, rErr := rw.ReadFull(reader, buf[:3])
|
|
|
|
if rErr != nil {
|
|
return Address{}, rErr
|
|
}
|
|
|
|
portNum := uint16(0)
|
|
portNum |= uint16(buf[0])
|
|
portNum <<= 8
|
|
portNum |= uint16(buf[1])
|
|
|
|
addrType := AddressType(buf[2] >> 6)
|
|
|
|
var addrData []byte
|
|
|
|
switch addrType {
|
|
case LoopbackAddr:
|
|
// Do nothing
|
|
|
|
case IPv4Addr:
|
|
if len(buf) < 4 {
|
|
return Address{}, ErrAddressParseBufferTooSmallForIPv4
|
|
}
|
|
|
|
_, rErr := rw.ReadFull(reader, buf[:4])
|
|
|
|
if rErr != nil {
|
|
return Address{}, rErr
|
|
}
|
|
|
|
addrData = buf[:4]
|
|
|
|
case IPv6Addr:
|
|
if len(buf) < 16 {
|
|
return Address{}, ErrAddressParseBufferTooSmallForIPv6
|
|
}
|
|
|
|
_, rErr := rw.ReadFull(reader, buf[:16])
|
|
|
|
if rErr != nil {
|
|
return Address{}, rErr
|
|
}
|
|
|
|
addrData = buf[:16]
|
|
|
|
case HostNameAddr:
|
|
addrDataLen := int(0x3f & buf[2])
|
|
|
|
if len(buf) < addrDataLen {
|
|
return Address{}, ErrAddressParseBufferTooSmallForHostName
|
|
}
|
|
|
|
_, rErr := rw.ReadFull(reader, buf[:addrDataLen])
|
|
|
|
if rErr != nil {
|
|
return Address{}, rErr
|
|
}
|
|
|
|
addrData = buf[:addrDataLen]
|
|
|
|
default:
|
|
return Address{}, ErrAddressInvalidAddressType
|
|
}
|
|
|
|
return Address{
|
|
port: portNum,
|
|
kind: addrType,
|
|
data: addrData,
|
|
}, nil
|
|
}
|
|
|
|
// NewAddress creates a new Address
|
|
func NewAddress(addrType AddressType, data []byte, port uint16) Address {
|
|
return Address{
|
|
port: port,
|
|
kind: addrType,
|
|
data: data,
|
|
}
|
|
}
|
|
|
|
// Type returns the type of the address
|
|
func (a Address) Type() AddressType {
|
|
return a.kind
|
|
}
|
|
|
|
// Data returns the address data
|
|
func (a Address) Data() []byte {
|
|
return a.data
|
|
}
|
|
|
|
// Port returns port number
|
|
func (a Address) Port() uint16 {
|
|
return a.port
|
|
}
|
|
|
|
// Marshal writes address data to the given b
|
|
func (a Address) Marshal(b []byte) (int, error) {
|
|
bLen := len(b)
|
|
|
|
switch a.Type() {
|
|
case LoopbackAddr:
|
|
if bLen < 3 {
|
|
return 0, ErrAddressMarshalBufferTooSmall
|
|
}
|
|
|
|
b[0] = byte(a.port >> 8)
|
|
b[1] = byte(a.port)
|
|
b[2] = byte(LoopbackAddr << 6)
|
|
|
|
return 3, nil
|
|
|
|
case IPv4Addr:
|
|
if bLen < 7 {
|
|
return 0, ErrAddressMarshalBufferTooSmall
|
|
}
|
|
|
|
b[0] = byte(a.port >> 8)
|
|
b[1] = byte(a.port)
|
|
b[2] = byte(IPv4Addr << 6)
|
|
|
|
copy(b[3:], a.data)
|
|
|
|
return 7, nil
|
|
|
|
case IPv6Addr:
|
|
if bLen < 19 {
|
|
return 0, ErrAddressMarshalBufferTooSmall
|
|
}
|
|
|
|
b[0] = byte(a.port >> 8)
|
|
b[1] = byte(a.port)
|
|
b[2] = byte(IPv6Addr << 6)
|
|
|
|
copy(b[3:], a.data)
|
|
|
|
return 19, nil
|
|
|
|
case HostNameAddr:
|
|
hLen := len(a.data)
|
|
|
|
if hLen > 0x3f {
|
|
panic("Host name cannot longer than 0x3f")
|
|
}
|
|
|
|
if bLen < hLen+3 {
|
|
return 0, ErrAddressMarshalBufferTooSmall
|
|
}
|
|
|
|
b[0] = byte(a.port >> 8)
|
|
b[1] = byte(a.port)
|
|
b[2] = byte(HostNameAddr << 6)
|
|
b[2] |= byte(hLen)
|
|
|
|
copy(b[3:], a.data)
|
|
|
|
return hLen + 3, nil
|
|
|
|
default:
|
|
return 0, ErrAddressInvalidAddressType
|
|
}
|
|
}
|
|
|
|
// String return the Address as string
|
|
func (a Address) String() string {
|
|
switch a.Type() {
|
|
case LoopbackAddr:
|
|
return net.JoinHostPort(
|
|
"localhost",
|
|
strconv.FormatUint(uint64(a.Port()), 10))
|
|
|
|
case IPv4Addr:
|
|
return net.JoinHostPort(
|
|
net.IPv4(a.data[0], a.data[1], a.data[2], a.data[3]).String(),
|
|
strconv.FormatUint(uint64(a.Port()), 10))
|
|
|
|
case IPv6Addr:
|
|
return net.JoinHostPort(
|
|
net.IP(a.data[:net.IPv6len]).String(),
|
|
strconv.FormatUint(uint64(a.Port()), 10))
|
|
|
|
case HostNameAddr:
|
|
return net.JoinHostPort(
|
|
string(a.data),
|
|
strconv.FormatUint(uint64(a.Port()), 10))
|
|
|
|
default:
|
|
panic("Unknown Address type")
|
|
}
|
|
}
|