mirror of https://gogs.blitter.com/RLabs/xs
				
				
				
			Completed net.Conn interface implementation for hkex.Conn; some tests of Op protocol in server
This commit is contained in:
		
							parent
							
								
									e09f052f45
								
							
						
					
					
						commit
						9fb9d073ab
					
				| 
						 | 
					@ -4,11 +4,82 @@ import (
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"os/user"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hkex "blitter.com/herradurakex"
 | 
						hkex "blitter.com/herradurakex"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						OpR   = 'r' // read(file) (binary mode)
 | 
				
			||||||
 | 
						OpW   = 'w' // (over)write
 | 
				
			||||||
 | 
						OpA   = 'a' // append
 | 
				
			||||||
 | 
						OpRm  = 'd' // rm
 | 
				
			||||||
 | 
						OpRmD = 'D' // rmdir (rm -rf)
 | 
				
			||||||
 | 
						OpM   = 'm' // mkdir (-p)
 | 
				
			||||||
 | 
						OpN   = 'n' // re(n)ame (mv)
 | 
				
			||||||
 | 
						OpCm  = 'c' // chmod
 | 
				
			||||||
 | 
						OpCo  = 'C' // chown
 | 
				
			||||||
 | 
						OpX   = 'x' // exec
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Op uint8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cmdRunner struct {
 | 
				
			||||||
 | 
						op           Op
 | 
				
			||||||
 | 
						who          string
 | 
				
			||||||
 | 
						arg          string
 | 
				
			||||||
 | 
						authCookie   string
 | 
				
			||||||
 | 
						CloseHandler func(*cmdRunner)
 | 
				
			||||||
 | 
						status       int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testCloseHandler(r *cmdRunner) {
 | 
				
			||||||
 | 
						fmt.Println("[testCloseHandler()]")
 | 
				
			||||||
 | 
						r.arg = "/usr/bin/touch " + r.arg
 | 
				
			||||||
 | 
						cmd(r)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func cmd(r *cmdRunner) {
 | 
				
			||||||
 | 
						switch r.op {
 | 
				
			||||||
 | 
						case OpR:
 | 
				
			||||||
 | 
							//Clean up r.cmd beforehand
 | 
				
			||||||
 | 
							r.arg = strings.TrimSpace(r.arg)
 | 
				
			||||||
 | 
							fmt.Printf("[cmd was:'%s']\n", r.arg)
 | 
				
			||||||
 | 
							runCmdAs(r.who, r.arg)
 | 
				
			||||||
 | 
							fmt.Println(r.arg)
 | 
				
			||||||
 | 
							break
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							fmt.Printf("[cmd %d ignored:%d]\n", int(r.op))
 | 
				
			||||||
 | 
							break
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Run a command (via os.exec) as a specific user
 | 
				
			||||||
 | 
					func runCmdAs(who string, cmd string) (err error) {
 | 
				
			||||||
 | 
						u, _ := user.Lookup(who)
 | 
				
			||||||
 | 
						var uid, gid uint32
 | 
				
			||||||
 | 
						fmt.Sscanf(u.Uid, "%d", &uid)
 | 
				
			||||||
 | 
						fmt.Sscanf(u.Gid, "%d", &gid)
 | 
				
			||||||
 | 
						//fmt.Println("uid:", uid, "gid:", gid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						args := strings.Split(cmd, " ")
 | 
				
			||||||
 | 
						arg0 := args[0]
 | 
				
			||||||
 | 
						args = args[1:]
 | 
				
			||||||
 | 
						c := exec.Command(arg0, args...)
 | 
				
			||||||
 | 
						c.SysProcAttr = &syscall.SysProcAttr{}
 | 
				
			||||||
 | 
						c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
 | 
				
			||||||
 | 
						err = c.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf("Command finished with error: %v", err)
 | 
				
			||||||
 | 
							log.Printf("[%s]\n", cmd)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Demo of a simple server that listens and spawns goroutines for each
 | 
					// Demo of a simple server that listens and spawns goroutines for each
 | 
				
			||||||
// connecting client. Note this code is identical to standard tcp
 | 
					// connecting client. Note this code is identical to standard tcp
 | 
				
			||||||
// server code, save for declaring 'hkex' rather than 'net'
 | 
					// server code, save for declaring 'hkex' rather than 'net'
 | 
				
			||||||
| 
						 | 
					@ -41,10 +112,10 @@ func main() {
 | 
				
			||||||
		// The loop then returns to accepting, so that
 | 
							// The loop then returns to accepting, so that
 | 
				
			||||||
		// 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()
 | 
				
			||||||
			ch := make(chan []byte)
 | 
								ch := make(chan []byte)
 | 
				
			||||||
			chN := 0
 | 
								chN := 0
 | 
				
			||||||
			eCh := make(chan error)
 | 
								eCh := make(chan error)
 | 
				
			||||||
			var connOp *byte = nil
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Start a goroutine to read from our net connection
 | 
								// Start a goroutine to read from our net connection
 | 
				
			||||||
			go func(ch chan []byte, eCh chan error) {
 | 
								go func(ch chan []byte, eCh chan error) {
 | 
				
			||||||
| 
						 | 
					@ -57,23 +128,14 @@ func main() {
 | 
				
			||||||
						eCh <- err
 | 
											eCh <- err
 | 
				
			||||||
						return
 | 
											return
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					if connOp == nil {
 | 
					 | 
				
			||||||
						// Initial xmit - get op byte
 | 
					 | 
				
			||||||
						// (TODO: determine valid ops
 | 
					 | 
				
			||||||
						//  for now 'e' (echo), 'i' (interactive), 'x' (exec), ... ?)
 | 
					 | 
				
			||||||
						connOp = new(byte)
 | 
					 | 
				
			||||||
						*connOp = data[0]
 | 
					 | 
				
			||||||
						data = data[1:]
 | 
					 | 
				
			||||||
						chN -= 1
 | 
					 | 
				
			||||||
						fmt.Printf("[* connOp '%c']\n", *connOp)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// send data if we read some.
 | 
										// send data if we read some.
 | 
				
			||||||
					ch <- data[0:chN]
 | 
										ch <- data[0:chN]
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}(ch, eCh)
 | 
								}(ch, eCh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ticker := time.Tick(time.Second / 100)
 | 
								ticker := time.Tick(time.Second / 100)
 | 
				
			||||||
 | 
								var r cmdRunner
 | 
				
			||||||
 | 
								var connOp *byte = nil
 | 
				
			||||||
		Term:
 | 
							Term:
 | 
				
			||||||
			// continuously read from the connection
 | 
								// continuously read from the connection
 | 
				
			||||||
			for {
 | 
								for {
 | 
				
			||||||
| 
						 | 
					@ -82,12 +144,32 @@ func main() {
 | 
				
			||||||
				case data := <-ch:
 | 
									case data := <-ch:
 | 
				
			||||||
					// Do something with the data
 | 
										// Do something with the data
 | 
				
			||||||
					fmt.Printf("Client sent %+v\n", data[0:chN])
 | 
										fmt.Printf("Client sent %+v\n", data[0:chN])
 | 
				
			||||||
 | 
										if connOp == nil {
 | 
				
			||||||
 | 
											// Initial xmit - get op byte
 | 
				
			||||||
 | 
											// (TODO: determine valid ops
 | 
				
			||||||
 | 
											//  for now 'e' (echo), 'i' (interactive), 'x' (exec), ... ?)
 | 
				
			||||||
 | 
											connOp = new(byte)
 | 
				
			||||||
 | 
											*connOp = data[0]
 | 
				
			||||||
 | 
											data = data[1:chN]
 | 
				
			||||||
 | 
											chN -= 1
 | 
				
			||||||
 | 
											
 | 
				
			||||||
 | 
											fmt.Printf("[* connOp '%c']\n", *connOp)
 | 
				
			||||||
 | 
											// The CloseHandler typically handles the
 | 
				
			||||||
 | 
											// accumulated command data
 | 
				
			||||||
 | 
											r = cmdRunner{op: Op(*connOp),
 | 
				
			||||||
 | 
												who: "larissa", arg: string(data),
 | 
				
			||||||
 | 
												authCookie:   "c00ki3",
 | 
				
			||||||
 | 
												CloseHandler: testCloseHandler,
 | 
				
			||||||
 | 
												status:       0}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					//fmt.Printf("Client sent %s\n", string(data))
 | 
										//fmt.Printf("Client sent %s\n", string(data))
 | 
				
			||||||
				// This case means we got an error and the goroutine has finished
 | 
									// This case means we got an error and the goroutine has finished
 | 
				
			||||||
				case err := <-eCh:
 | 
									case err := <-eCh:
 | 
				
			||||||
					// handle our error then exit for loop
 | 
										// handle our error then exit for loop
 | 
				
			||||||
					if err.Error() == "EOF" {
 | 
										if err.Error() == "EOF" {
 | 
				
			||||||
						fmt.Printf("[Client disconnected]\n")
 | 
											fmt.Printf("[Client disconnected]\n")
 | 
				
			||||||
 | 
											r.CloseHandler(&r)
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
						fmt.Printf("Error reading client data! (%+v)\n", err)
 | 
											fmt.Printf("Error reading client data! (%+v)\n", err)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
| 
						 | 
					@ -100,7 +182,7 @@ func main() {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// Shut down the connection.
 | 
								// Shut down the connection.
 | 
				
			||||||
			c.Close()
 | 
								//c.Close()
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}(conn)
 | 
							}(conn)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										70
									
								
								hkexnet.go
								
								
								
								
							
							
						
						
									
										70
									
								
								hkexnet.go
								
								
								
								
							| 
						 | 
					@ -34,7 +34,6 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Conn is a HKex connection - a drop-in replacement for net.Conn
 | 
					// Conn is a HKex connection - a drop-in replacement for net.Conn
 | 
				
			||||||
type Conn struct {
 | 
					type Conn struct {
 | 
				
			||||||
	//net.Conn
 | 
					 | 
				
			||||||
	c          net.Conn // which also implements io.Reader, io.Writer, ...
 | 
						c          net.Conn // which also implements io.Reader, io.Writer, ...
 | 
				
			||||||
	h          *HerraduraKEx
 | 
						h          *HerraduraKEx
 | 
				
			||||||
	cipheropts uint32 // post-KEx cipher/hmac options
 | 
						cipheropts uint32 // post-KEx cipher/hmac options
 | 
				
			||||||
| 
						 | 
					@ -44,16 +43,12 @@ type Conn struct {
 | 
				
			||||||
	w          cipher.Stream
 | 
						w          cipher.Stream
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Conn) SetReadDeadline(t time.Time) error {
 | 
					 | 
				
			||||||
	return c.SetReadDeadline(t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ConnOpts returns the cipher/hmac options value, which is sent to the
 | 
					// ConnOpts returns the cipher/hmac options value, which is sent to the
 | 
				
			||||||
// peer but is not itself part of the KEx.
 | 
					// peer but is not itself part of the KEx.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// (Used for protocol-level negotiations after KEx such as
 | 
					// (Used for protocol-level negotiations after KEx such as
 | 
				
			||||||
// cipher/HMAC algorithm options etc.)
 | 
					// cipher/HMAC algorithm options etc.)
 | 
				
			||||||
func (c *Conn) ConnOpts() uint32 {
 | 
					func (c Conn) ConnOpts() uint32 {
 | 
				
			||||||
	return c.cipheropts
 | 
						return c.cipheropts
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,7 +65,7 @@ func (c *Conn) SetConnOpts(copts uint32) {
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Consumers of this lib may use this for protocol-level options not part
 | 
					// Consumers of this lib may use this for protocol-level options not part
 | 
				
			||||||
// of the KEx or encryption info used by the connection.
 | 
					// of the KEx or encryption info used by the connection.
 | 
				
			||||||
func (c *Conn) Opts() uint32 {
 | 
					func (c Conn) Opts() uint32 {
 | 
				
			||||||
	return c.opts
 | 
						return c.opts
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,7 +76,7 @@ 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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,7 +85,7 @@ func (c *Conn) SetOpts(opts uint32) {
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Consumers of this lib may use this to indicate connection-specific
 | 
					// Consumers of this lib may use this to indicate connection-specific
 | 
				
			||||||
// operations not part of the KEx or encryption info used by the connection.
 | 
					// operations not part of the KEx or encryption info used by the connection.
 | 
				
			||||||
func (c *Conn) Op() uint8 {
 | 
					func (c Conn) Op() uint8 {
 | 
				
			||||||
	return c.op
 | 
						return c.op
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,11 +96,11 @@ func (c *Conn) Op() uint8 {
 | 
				
			||||||
// operations not part of the KEx or encryption info used by the connection.
 | 
					// operations not part of the KEx or encryption info used by the connection.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// op - a uint8, caller-defined
 | 
					// op - a uint8, caller-defined
 | 
				
			||||||
func (c *Conn) SetOp(op uint8) {
 | 
					func (c Conn) SetOp(op uint8) {
 | 
				
			||||||
	c.op = op
 | 
						c.op = op
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Conn) applyConnExtensions(extensions ...string) {
 | 
					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":
 | 
				
			||||||
| 
						 | 
					@ -177,12 +172,57 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Close a hkex.Conn
 | 
					// Close a hkex.Conn
 | 
				
			||||||
func (c *Conn) Close() (err error) {
 | 
					func (c Conn) Close() (err error) {
 | 
				
			||||||
	err = c.c.Close()
 | 
						err = c.c.Close()
 | 
				
			||||||
	fmt.Println("[Conn Closing]")
 | 
						fmt.Println("[Conn Closing]")
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LocalAddr returns the local network address.
 | 
				
			||||||
 | 
					func (c Conn) LocalAddr() net.Addr {
 | 
				
			||||||
 | 
						return c.c.LocalAddr()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoteAddr returns the remote network address.
 | 
				
			||||||
 | 
					func (c Conn) RemoteAddr() net.Addr {
 | 
				
			||||||
 | 
						return c.c.RemoteAddr()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetDeadline sets the read and write deadlines associated
 | 
				
			||||||
 | 
					// with the connection. It is equivalent to calling both
 | 
				
			||||||
 | 
					// SetReadDeadline and SetWriteDeadline.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// A deadline is an absolute time after which I/O operations
 | 
				
			||||||
 | 
					// fail with a timeout (see type Error) instead of
 | 
				
			||||||
 | 
					// blocking. The deadline applies to all future and pending
 | 
				
			||||||
 | 
					// I/O, not just the immediately following call to Read or
 | 
				
			||||||
 | 
					// Write. After a deadline has been exceeded, the connection
 | 
				
			||||||
 | 
					// can be refreshed by setting a deadline in the future.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// An idle timeout can be implemented by repeatedly extending
 | 
				
			||||||
 | 
					// the deadline after successful Read or Write calls.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// A zero value for t means I/O operations will not time out.
 | 
				
			||||||
 | 
					func (c Conn) SetDeadline(t time.Time) error {
 | 
				
			||||||
 | 
						return c.SetDeadline(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetWriteDeadline sets the deadline for future Write calls
 | 
				
			||||||
 | 
					// and any currently-blocked Write call.
 | 
				
			||||||
 | 
					// Even if write times out, it may return n > 0, indicating that
 | 
				
			||||||
 | 
					// some of the data was successfully written.
 | 
				
			||||||
 | 
					// A zero value for t means Write will not time out.
 | 
				
			||||||
 | 
					func (c Conn) SetWriteDeadline(t time.Time) error {
 | 
				
			||||||
 | 
						return c.SetWriteDeadline(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetReadDeadline sets the deadline for future Read calls
 | 
				
			||||||
 | 
					// and any currently-blocked Read call.
 | 
				
			||||||
 | 
					// A zero value for t means Read will not time out.
 | 
				
			||||||
 | 
					func (c Conn) SetReadDeadline(t time.Time) error {
 | 
				
			||||||
 | 
						return c.SetReadDeadline(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*---------------------------------------------------------------------*/
 | 
					/*---------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HKExListener is a Listener conforming to net.Listener
 | 
					// HKExListener is a Listener conforming to net.Listener
 | 
				
			||||||
| 
						 | 
					@ -208,7 +248,7 @@ func Listen(protocol string, ipport string) (hl HKExListener, e error) {
 | 
				
			||||||
// Close a hkex Listener
 | 
					// Close a hkex Listener
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// See go doc io.Close
 | 
					// See go doc io.Close
 | 
				
			||||||
func (hl *HKExListener) Close() error {
 | 
					func (hl HKExListener) Close() error {
 | 
				
			||||||
	fmt.Println("[Listener Closed]")
 | 
						fmt.Println("[Listener Closed]")
 | 
				
			||||||
	return hl.l.Close()
 | 
						return hl.l.Close()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -216,7 +256,7 @@ func (hl *HKExListener) Close() error {
 | 
				
			||||||
// Accept a client connection, conforming to net.Listener.Accept()
 | 
					// Accept a client connection, conforming to net.Listener.Accept()
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// See go doc net.Listener.Accept
 | 
					// See go doc net.Listener.Accept
 | 
				
			||||||
func (hl *HKExListener) Accept() (hc Conn, err error) {
 | 
					func (hl HKExListener) Accept() (hc Conn, err error) {
 | 
				
			||||||
	c, err := hl.l.Accept()
 | 
						c, err := hl.l.Accept()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,
 | 
							return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue