hnc: add a custom netcat-alike
This commit is contained in:
parent
9b6c4e7028
commit
4fc4426587
150
hnc/main.go
Normal file
150
hnc/main.go
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
|
||||
// hnc is a netcat-alike that shuts down properly.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
// #include <unistd.h>
|
||||
import "C"
|
||||
|
||||
func isatty(fd uintptr) bool { return C.isatty(C.int(fd)) != 0 }
|
||||
|
||||
func log(format string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(format+"\n", args...)
|
||||
if isatty(os.Stderr.Fd()) {
|
||||
msg = "\x1b[0;1;31m" + msg + "\x1b[m"
|
||||
}
|
||||
os.Stderr.WriteString(msg)
|
||||
}
|
||||
|
||||
var (
|
||||
flagTLS = flag.Bool("tls", false, "connect using TLS")
|
||||
flagCRLF = flag.Bool("crlf", false, "translate LF into CRLF")
|
||||
)
|
||||
|
||||
// Network connection that can shut down the write end.
|
||||
type connCloseWriter interface {
|
||||
net.Conn
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
func dial(address string) (connCloseWriter, error) {
|
||||
if *flagTLS {
|
||||
return tls.Dial("tcp", address, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
}
|
||||
transport, err := net.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transport.(connCloseWriter), nil
|
||||
}
|
||||
|
||||
func expand(raw []byte) []byte {
|
||||
if !*flagCRLF {
|
||||
return raw
|
||||
}
|
||||
var res []byte
|
||||
for _, b := range raw {
|
||||
if b == '\n' {
|
||||
res = append(res, '\r')
|
||||
}
|
||||
res = append(res, b)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Asynchronously delivered result of io.Reader.
|
||||
type readResult struct {
|
||||
b []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func read(r io.Reader, ch chan<- readResult) {
|
||||
defer close(ch)
|
||||
for {
|
||||
var buf [8192]byte
|
||||
n, err := r.Read(buf[:])
|
||||
ch <- readResult{buf[:n], err}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(flag.CommandLine.Output(),
|
||||
"Usage: %s [OPTION]... HOST PORT\n"+
|
||||
"Connect to a remote host over TCP/IP.\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
if flag.NArg() != 2 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
conn, err := dial(net.JoinHostPort(flag.Arg(0), flag.Arg(1)))
|
||||
if err != nil {
|
||||
log("dial: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fromUser := make(chan readResult)
|
||||
go read(os.Stdin, fromUser)
|
||||
|
||||
fromConn := make(chan readResult)
|
||||
go read(conn, fromConn)
|
||||
|
||||
for fromUser != nil || fromConn != nil {
|
||||
select {
|
||||
case result := <-fromUser:
|
||||
if len(result.b) > 0 {
|
||||
if _, err := conn.Write(expand(result.b)); err != nil {
|
||||
log("remote: %s", err)
|
||||
}
|
||||
}
|
||||
if result.err != nil {
|
||||
log("%s: %s", "stdin", result.err)
|
||||
fromUser = nil
|
||||
if err := conn.CloseWrite(); err != nil {
|
||||
log("remote: %s", err)
|
||||
}
|
||||
}
|
||||
case result := <-fromConn:
|
||||
if len(result.b) > 0 {
|
||||
if _, err := os.Stdout.Write(result.b); err != nil {
|
||||
log("stdout: %s", err)
|
||||
}
|
||||
}
|
||||
if result.err != nil {
|
||||
log("remote: %s", result.err)
|
||||
fromConn = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user