mirror of
https://gogs.blitter.com/RLabs/xs
synced 2024-08-14 10:26:42 +00:00
Removed channel-based server loop goroutine, solving eaten initial byte issue.
Made receivers on hkex.Conn mutators *Conn again (whoops) TODO: Padding in ciphertext data!
This commit is contained in:
parent
732005d9bf
commit
3efdd5cfbd
5 changed files with 110 additions and 138 deletions
|
@ -14,6 +14,14 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type cmdSpec struct {
|
||||||
|
op []byte
|
||||||
|
who []byte
|
||||||
|
cmd []byte
|
||||||
|
authCookie []byte
|
||||||
|
status int
|
||||||
|
}
|
||||||
|
|
||||||
// Demo of a simple client that dials up to a simple test server to
|
// Demo of a simple client that dials up to a simple test server to
|
||||||
// send data.
|
// send data.
|
||||||
//
|
//
|
||||||
|
@ -39,6 +47,7 @@ func main() {
|
||||||
flag.StringVar(&server, "s", "localhost:2000", "server hostname/address[:port]")
|
flag.StringVar(&server, "s", "localhost:2000", "server hostname/address[:port]")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
//log.SetOutput(os.Stdout)
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
conn, err := hkex.Dial("tcp", server, cAlg, hAlg)
|
conn, err := hkex.Dial("tcp", server, cAlg, hAlg)
|
||||||
|
@ -60,6 +69,19 @@ func main() {
|
||||||
fmt.Println("NOT A TTY")
|
fmt.Println("NOT A TTY")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rec := &cmdSpec{op: []byte{'s'},
|
||||||
|
who: []byte("ABCD"),
|
||||||
|
cmd: []byte("EFGH"),
|
||||||
|
authCookie: []byte("99"),
|
||||||
|
status: 0}
|
||||||
|
|
||||||
|
_, err = fmt.Fprintf(conn, "%d %d %d %d\n", len(rec.op), len(rec.who), len(rec.cmd), len(rec.authCookie))
|
||||||
|
_, err = conn.Write(rec.op)
|
||||||
|
_, err = conn.Write(rec.who)
|
||||||
|
_, err = conn.Write(rec.cmd)
|
||||||
|
_, err = conn.Write(rec.authCookie)
|
||||||
|
|
||||||
|
//client reader (from server) goroutine
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
// By deferring a call to wg.Done(),
|
// By deferring a call to wg.Done(),
|
||||||
|
@ -82,11 +104,12 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isInteractive {
|
if isInteractive {
|
||||||
log.Println("[Got Write EOF]")
|
log.Println("[Got EOF]")
|
||||||
wg.Done() // client hanging up, close WaitGroup to exit client
|
wg.Done() // server hung up, close WaitGroup to exit client
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// client writer (to server) goroutine
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
@ -100,8 +123,8 @@ func main() {
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Println("[Got Read EOF]")
|
log.Println("[Sent EOF]")
|
||||||
wg.Done() // server hung up, close WaitGroup to exit client
|
wg.Done() // client hung up, close WaitGroup to exit client
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Wait until both stdin and stdout goroutines finish
|
// Wait until both stdin and stdout goroutines finish
|
||||||
|
|
|
@ -4,13 +4,12 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
hkex "blitter.com/herradurakex"
|
hkex "blitter.com/herradurakex"
|
||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
|
@ -32,13 +31,13 @@ const (
|
||||||
OpX = 'x' // exec
|
OpX = 'x' // exec
|
||||||
)
|
)
|
||||||
|
|
||||||
type Op uint8
|
//type Op uint8
|
||||||
|
|
||||||
type cmdRunner struct {
|
type cmdSpec struct {
|
||||||
op Op
|
op []byte
|
||||||
who string
|
who []byte
|
||||||
arg string
|
cmd []byte
|
||||||
authCookie string
|
authCookie []byte
|
||||||
status int
|
status int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +94,7 @@ func main() {
|
||||||
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
|
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(os.Stdout /*ioutil.Discard*/)
|
||||||
|
|
||||||
// Listen on TCP port 2000 on all available unicast and
|
// Listen on TCP port 2000 on all available unicast and
|
||||||
// anycast IP addresses of the local system.
|
// anycast IP addresses of the local system.
|
||||||
|
@ -119,85 +118,63 @@ func main() {
|
||||||
// multiple connections may be served concurrently.
|
// multiple connections may be served concurrently.
|
||||||
go func(c hkex.Conn) (e error) {
|
go func(c hkex.Conn) (e error) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
var connOp *byte = nil
|
|
||||||
ch := make(chan []byte)
|
|
||||||
chN := 0
|
|
||||||
eCh := make(chan error)
|
|
||||||
|
|
||||||
// Start a goroutine to read from our net connection
|
//We use io.ReadFull() here to guarantee we consume
|
||||||
go func(ch chan []byte, eCh chan error) {
|
//just the data we want for the cmdSpec, and no more.
|
||||||
for {
|
//Otherwise data will be sitting in the channel that isn't
|
||||||
// try to read the data
|
//passed down to the command handlers.
|
||||||
data := make([]byte, 512)
|
var rec cmdSpec
|
||||||
chN, err = c.Read(data)
|
var len1, len2, len3, len4 uint32
|
||||||
|
|
||||||
|
n, err := fmt.Fscanf(c, "%d %d %d %d\n", &len1, &len2, &len3, &len4)
|
||||||
|
if err != nil || n < 4 {
|
||||||
|
fmt.Println("[Bad cmdSpec fmt]")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf(" lens:%d %d %d %d\n", len1, len2, len3, len4)
|
||||||
|
|
||||||
|
rec.op = make([]byte, len1, len1)
|
||||||
|
_, err = io.ReadFull(c, rec.op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// send an error if it's encountered
|
fmt.Println("[Bad cmdSpec.op]")
|
||||||
eCh <- err
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// send data if we read some.
|
rec.who = make([]byte, len2, len2)
|
||||||
ch <- data[0:chN]
|
_, err = io.ReadFull(c, rec.who)
|
||||||
}
|
if err != nil {
|
||||||
}(ch, eCh)
|
fmt.Println("[Bad cmdSpec.who]")
|
||||||
|
return err
|
||||||
ticker := time.Tick(time.Second / 100)
|
|
||||||
Term:
|
|
||||||
// continuously read from the connection
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
// This case means we recieved data on the connection
|
|
||||||
case data := <-ch:
|
|
||||||
// Do something with the data
|
|
||||||
fmt.Printf("Client sent %+v\n", data[0:chN])
|
|
||||||
if connOp == nil {
|
|
||||||
// Initial xmit - get op byte
|
|
||||||
// Have op here and first block of data[]
|
|
||||||
connOp = new(byte)
|
|
||||||
*connOp = data[0]
|
|
||||||
fmt.Printf("[* connOp '%c']\n", *connOp)
|
|
||||||
}
|
|
||||||
if len(data) > 1 {
|
|
||||||
data = data[1:chN]
|
|
||||||
chN -= 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) > 0 {
|
rec.cmd = make([]byte, len3, len3)
|
||||||
// From here, one could pass all subsequent data
|
_, err = io.ReadFull(c, rec.cmd)
|
||||||
// between client/server attached to an exec.Cmd,
|
if err != nil {
|
||||||
// as data to/from a file, etc.
|
fmt.Println("[Bad cmdSpec.cmd]")
|
||||||
if connOp != nil && *connOp == 's' {
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rec.authCookie = make([]byte, len4, len4)
|
||||||
|
_, err = io.ReadFull(c, rec.authCookie)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[Bad cmdSpec.authCookie]")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[cmdSpec: op:%c who:%s cmd:%s auth:%s]\n",
|
||||||
|
rec.op[0], string(rec.who), string(rec.cmd), string(rec.authCookie))
|
||||||
|
|
||||||
|
if rec.op[0] == 's' {
|
||||||
fmt.Println("[Running shell]")
|
fmt.Println("[Running shell]")
|
||||||
runCmdAs("larissa", "bash -l -i", conn)
|
runCmdAs("larissa", "bash -l -i", conn)
|
||||||
// Returned hopefully via an EOF or exit/logout;
|
// Returned hopefully via an EOF or exit/logout;
|
||||||
// Clear current op so user can enter next, or EOF
|
// Clear current op so user can enter next, or EOF
|
||||||
connOp = nil
|
rec.op[0] = 0
|
||||||
fmt.Println("[Exiting shell]")
|
fmt.Println("[Exiting shell]")
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
if strings.Trim(string(data), "\r\n") == "exit" {
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//fmt.Printf("Client sent %s\n", string(data))
|
|
||||||
// This case means we got an error and the goroutine has finished
|
|
||||||
case err := <-eCh:
|
|
||||||
// handle our error then exit for loop
|
|
||||||
if err.Error() == "EOF" {
|
|
||||||
fmt.Printf("[Client disconnected]\n")
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Error reading client data! (%+v)\n", err)
|
fmt.Println("[Bad cmdSpec]")
|
||||||
}
|
}
|
||||||
break Term
|
|
||||||
// This will timeout on the read.
|
|
||||||
case <-ticker:
|
|
||||||
// do nothing? this is just so we can time out if we need to.
|
|
||||||
// you probably don't even need to have this here unless you want
|
|
||||||
// do something specifically on the timeout.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Shut down the connection.
|
|
||||||
//c.Close()
|
|
||||||
return
|
return
|
||||||
}(conn)
|
}(conn)
|
||||||
}
|
} //endfor
|
||||||
|
fmt.Println("[Exiting]")
|
||||||
}
|
}
|
||||||
|
|
21
hkexchan.go
21
hkexchan.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -29,16 +30,6 @@ const (
|
||||||
HmacNoneDisallowed
|
HmacNoneDisallowed
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChanOp uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChanOpNop = '.'
|
|
||||||
ChanOpEcho = 'e' // For testing - echo client data to stderr
|
|
||||||
//ChanOpFileWrite = "w"
|
|
||||||
//ChanOpFileRead = "r"
|
|
||||||
//ChanOpRemoteCmd = "x"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*TODO: HMAC derived from HKEx FA.*/
|
/*TODO: HMAC derived from HKEx FA.*/
|
||||||
/* Support functionality to set up encryption after a channel has
|
/* Support functionality to set up encryption after a channel has
|
||||||
been negotiated via hkexnet.go
|
been negotiated via hkexnet.go
|
||||||
|
@ -59,7 +50,7 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
||||||
ivlen = aes.BlockSize
|
ivlen = aes.BlockSize
|
||||||
iv := keymat.Bytes()[aes.BlockSize : aes.BlockSize+ivlen]
|
iv := keymat.Bytes()[aes.BlockSize : aes.BlockSize+ivlen]
|
||||||
ret = cipher.NewOFB(block, iv)
|
ret = cipher.NewOFB(block, iv)
|
||||||
fmt.Printf("[cipher AES_256 (%d)]\n", copts)
|
log.Printf("[cipher AES_256 (%d)]\n", copts)
|
||||||
break
|
break
|
||||||
case CAlgTwofish128:
|
case CAlgTwofish128:
|
||||||
key = keymat.Bytes()[0:twofish.BlockSize]
|
key = keymat.Bytes()[0:twofish.BlockSize]
|
||||||
|
@ -67,7 +58,7 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
||||||
ivlen = twofish.BlockSize
|
ivlen = twofish.BlockSize
|
||||||
iv := keymat.Bytes()[twofish.BlockSize : twofish.BlockSize+ivlen]
|
iv := keymat.Bytes()[twofish.BlockSize : twofish.BlockSize+ivlen]
|
||||||
ret = cipher.NewOFB(block, iv)
|
ret = cipher.NewOFB(block, iv)
|
||||||
fmt.Printf("[cipher TWOFISH_128 (%d)]\n", copts)
|
log.Printf("[cipher TWOFISH_128 (%d)]\n", copts)
|
||||||
break
|
break
|
||||||
case CAlgBlowfish64:
|
case CAlgBlowfish64:
|
||||||
key = keymat.Bytes()[0:blowfish.BlockSize]
|
key = keymat.Bytes()[0:blowfish.BlockSize]
|
||||||
|
@ -84,9 +75,10 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
||||||
// copy what's needed whereas blowfish does no such check.
|
// copy what's needed whereas blowfish does no such check.
|
||||||
iv := keymat.Bytes()[blowfish.BlockSize : blowfish.BlockSize+ivlen]
|
iv := keymat.Bytes()[blowfish.BlockSize : blowfish.BlockSize+ivlen]
|
||||||
ret = cipher.NewOFB(block, iv)
|
ret = cipher.NewOFB(block, iv)
|
||||||
fmt.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
|
log.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
log.Printf("[invalid cipher (%d)]\n", copts)
|
||||||
fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
|
fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -94,9 +86,10 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
||||||
hopts := (hc.cipheropts >> 8) & 0xFF
|
hopts := (hc.cipheropts >> 8) & 0xFF
|
||||||
switch hopts {
|
switch hopts {
|
||||||
case HmacSHA256:
|
case HmacSHA256:
|
||||||
fmt.Printf("[nop HmacSHA256 (%d)]\n", hopts)
|
log.Printf("[nop HmacSHA256 (%d)]\n", hopts)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
log.Printf("[invalid hmac (%d)]\n", hopts)
|
||||||
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)
|
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
47
hkexnet.go
47
hkexnet.go
|
@ -39,7 +39,6 @@ type Conn struct {
|
||||||
h *HerraduraKEx
|
h *HerraduraKEx
|
||||||
cipheropts uint32 // post-KEx cipher/hmac options
|
cipheropts uint32 // post-KEx cipher/hmac options
|
||||||
opts uint32 // post-KEx protocol options (caller-defined)
|
opts uint32 // post-KEx protocol options (caller-defined)
|
||||||
op uint8 // post-KEx 'op' (caller-defined)
|
|
||||||
r cipher.Stream
|
r cipher.Stream
|
||||||
w cipher.Stream
|
w cipher.Stream
|
||||||
}
|
}
|
||||||
|
@ -57,7 +56,7 @@ func (c Conn) ConnOpts() uint32 {
|
||||||
// peer as part of KEx but not part of the KEx itself.
|
// peer as part of KEx but not part of the KEx itself.
|
||||||
//
|
//
|
||||||
// opts - bitfields for cipher and hmac alg. to use after KEx
|
// opts - bitfields for cipher and hmac alg. to use after KEx
|
||||||
func (c Conn) SetConnOpts(copts uint32) {
|
func (c *Conn) SetConnOpts(copts uint32) {
|
||||||
c.cipheropts = copts
|
c.cipheropts = copts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,31 +76,11 @@ func (c Conn) Opts() uint32 {
|
||||||
// of the KEx of encryption info used by the connection.
|
// of the KEx of encryption info used by the connection.
|
||||||
//
|
//
|
||||||
// opts - a uint32, caller-defined
|
// opts - a uint32, caller-defined
|
||||||
func (c Conn) SetOpts(opts uint32) {
|
func (c *Conn) SetOpts(opts uint32) {
|
||||||
c.opts = opts
|
c.opts = opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Op returns the 'op' value, which is sent to the peer
|
func (c *Conn) applyConnExtensions(extensions ...string) {
|
||||||
// but is not itself part of the KEx or connection (cipher/hmac) setup.
|
|
||||||
//
|
|
||||||
// Consumers of this lib may use this to indicate connection-specific
|
|
||||||
// operations not part of the KEx or encryption info used by the connection.
|
|
||||||
func (c Conn) Op() uint8 {
|
|
||||||
return c.op
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOp sets the 'op' value, which is sent to the peer
|
|
||||||
// but is not itself part of the KEx or connection (cipher/hmac) setup.
|
|
||||||
//
|
|
||||||
// Consumers of this lib may use this to indicate connection-specific
|
|
||||||
// operations not part of the KEx or encryption info used by the connection.
|
|
||||||
//
|
|
||||||
// op - a uint8, caller-defined
|
|
||||||
func (c Conn) SetOp(op uint8) {
|
|
||||||
c.op = op
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Conn) applyConnExtensions(extensions ...string) {
|
|
||||||
for _, s := range extensions {
|
for _, s := range extensions {
|
||||||
switch s {
|
switch s {
|
||||||
case "C_AES_256":
|
case "C_AES_256":
|
||||||
|
@ -143,20 +122,20 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hc = &Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, op: 0, r: nil, w: nil}
|
hc = &Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, r: nil, w: nil}
|
||||||
|
|
||||||
hc.applyConnExtensions(extensions...)
|
hc.applyConnExtensions(extensions...)
|
||||||
|
|
||||||
fmt.Fprintf(c, "0x%s\n%08x:%08x:%02x\n", hc.h.d.Text(16),
|
fmt.Fprintf(c, "0x%s\n%08x:%08x\n", hc.h.d.Text(16),
|
||||||
hc.cipheropts, hc.opts, hc.op)
|
hc.cipheropts, hc.opts)
|
||||||
|
|
||||||
d := big.NewInt(0)
|
d := big.NewInt(0)
|
||||||
_, err = fmt.Fscanln(c, d)
|
_, err = fmt.Fscanln(c, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = fmt.Fscanf(c, "%08x:%08x:%02x\n",
|
_, err = fmt.Fscanf(c, "%08x:%08x\n",
|
||||||
&hc.cipheropts, &hc.opts, &hc.op)
|
&hc.cipheropts, &hc.opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -265,15 +244,15 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
||||||
}
|
}
|
||||||
log.Println("[Accepted]")
|
log.Println("[Accepted]")
|
||||||
|
|
||||||
hc = Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, op: 0, r: nil, w: nil}
|
hc = Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, r: nil, w: nil}
|
||||||
|
|
||||||
d := big.NewInt(0)
|
d := big.NewInt(0)
|
||||||
_, err = fmt.Fscanln(c, d)
|
_, err = fmt.Fscanln(c, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hc, err
|
return hc, err
|
||||||
}
|
}
|
||||||
_, err = fmt.Fscanf(c, "%08x:%08x:%02x\n",
|
_, err = fmt.Fscanf(c, "%08x:%08x\n",
|
||||||
&hc.cipheropts, &hc.opts, &hc.op)
|
&hc.cipheropts, &hc.opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hc, err
|
return hc, err
|
||||||
}
|
}
|
||||||
|
@ -283,8 +262,8 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
||||||
hc.h.FA()
|
hc.h.FA()
|
||||||
log.Printf("**(s)** FA:%s\n", hc.h.fa)
|
log.Printf("**(s)** FA:%s\n", hc.h.fa)
|
||||||
|
|
||||||
fmt.Fprintf(c, "0x%s\n%08x:%08x:%02x\n", hc.h.d.Text(16),
|
fmt.Fprintf(c, "0x%s\n%08x:%08x\n", hc.h.d.Text(16),
|
||||||
hc.cipheropts, hc.opts, hc.op)
|
hc.cipheropts, hc.opts)
|
||||||
|
|
||||||
hc.r = hc.getStream(hc.h.fa)
|
hc.r = hc.getStream(hc.h.fa)
|
||||||
hc.w = hc.getStream(hc.h.fa)
|
hc.w = hc.getStream(hc.h.fa)
|
||||||
|
|
Loading…
Reference in a new issue