├── LICENSE ├── README.md └── main.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Vadym Fedorov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## go-netcat 2 | The Go-netcat is simple implementation of the netcat utility in go that allows to listen and send data over TCP and UDP protocols. 3 | 4 | This utility was created for Golang learning purposes and was inspired by https://github.com/dddpaul/go-netcat 5 | 6 | ### Usage: 7 | 8 | ``` 9 | go-nc [-lu] [-p source port ] [-s source ip address ] [hostname ] [port[s]] 10 | ``` 11 | 12 | ### Description: 13 | 14 | The utility allows to listen UDP\TCP ports and send data to remote ports over TCP\UDP. Main usage scenario is testing network protocols and accessibility of the open ports. 15 | 16 | The options are as follows: 17 | 18 | **-l** 19 | Used to specify that go-nc should listen for an incoming connection rather than initiate a connection to a remote host. 20 | 21 | **-p** __port__ 22 | Specifies the source port netcat should use, subject to privilege restrictions and availability. 23 | 24 | **-u** 25 | Use UDP instead of the default option of TCP. 26 | 27 | ### Examples: 28 | 29 | **$ go-netcat hostname 42** 30 | 31 | Open a TCP connection to port 42 of hostname. 32 | 33 | **$ go-netcat -u hostname 53** 34 | 35 | Open a UDP connection to port 53 of hostname. 36 | 37 | **$ go-netcat -l 3000** 38 | 39 | Listen on TCP port 3000, and once there is a connection, send stdin to the remote host, and send data from the remote host to stdout. 40 | 41 | **$ go-netcat -u -l 3000** 42 | 43 | Listen on UDP port 3000, and once there is a connection, send stdin to the remote host, and send data from the remote host to stdout. 44 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // go-nc project main.go 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "os" 11 | "strconv" 12 | ) 13 | 14 | // Handles TC connection and perform synchorinization: 15 | // TCP -> Stdout and Stdin -> TCP 16 | func tcp_con_handle(con net.Conn) { 17 | chan_to_stdout := stream_copy(con, os.Stdout) 18 | chan_to_remote := stream_copy(os.Stdin, con) 19 | select { 20 | case <-chan_to_stdout: 21 | log.Println("Remote connection is closed") 22 | case <-chan_to_remote: 23 | log.Println("Local program is terminated") 24 | } 25 | } 26 | 27 | // Performs copy operation between streams: os and tcp streams 28 | func stream_copy(src io.Reader, dst io.Writer) <-chan int { 29 | buf := make([]byte, 1024) 30 | sync_channel := make(chan int) 31 | go func() { 32 | defer func() { 33 | if con, ok := dst.(net.Conn); ok { 34 | con.Close() 35 | log.Printf("Connection from %v is closed\n", con.RemoteAddr()) 36 | } 37 | sync_channel <- 0 // Notify that processing is finished 38 | }() 39 | for { 40 | var nBytes int 41 | var err error 42 | nBytes, err = src.Read(buf) 43 | if err != nil { 44 | if err != io.EOF { 45 | log.Printf("Read error: %s\n", err) 46 | } 47 | break 48 | } 49 | _, err = dst.Write(buf[0:nBytes]) 50 | if err != nil { 51 | log.Fatalf("Write error: %s\n", err) 52 | } 53 | } 54 | }() 55 | return sync_channel 56 | } 57 | 58 | //Accept data from UPD connection and copy it to the stream 59 | func accept_from_udp_to_stream(src net.Conn, dst io.Writer) <-chan net.Addr { 60 | buf := make([]byte, 1024) 61 | sync_channel := make(chan net.Addr) 62 | con, ok := src.(*net.UDPConn) 63 | if !ok { 64 | log.Printf("Input must be UDP connection") 65 | return sync_channel 66 | } 67 | go func() { 68 | var remoteAddr net.Addr 69 | for { 70 | var nBytes int 71 | var err error 72 | var addr net.Addr 73 | nBytes, addr, err = con.ReadFromUDP(buf) 74 | if err != nil { 75 | if err != io.EOF { 76 | log.Printf("Read error: %s\n", err) 77 | } 78 | break 79 | } 80 | if remoteAddr == nil && remoteAddr != addr { 81 | remoteAddr = addr 82 | sync_channel <- remoteAddr 83 | } 84 | _, err = dst.Write(buf[0:nBytes]) 85 | if err != nil { 86 | log.Fatalf("Write error: %s\n", err) 87 | } 88 | } 89 | }() 90 | log.Println("Exit write_from_udp_to_stream") 91 | return sync_channel 92 | } 93 | 94 | // Put input date from the stream to UDP connection 95 | func put_from_stream_to_udp(src io.Reader, dst net.Conn, remoteAddr net.Addr) <-chan net.Addr { 96 | buf := make([]byte, 1024) 97 | sync_channel := make(chan net.Addr) 98 | go func() { 99 | for { 100 | var nBytes int 101 | var err error 102 | nBytes, err = src.Read(buf) 103 | if err != nil { 104 | if err != io.EOF { 105 | log.Printf("Read error: %s\n", err) 106 | } 107 | break 108 | } 109 | log.Println("Write to the remote address:", remoteAddr) 110 | if con, ok := dst.(*net.UDPConn); ok && remoteAddr != nil { 111 | _, err = con.WriteTo(buf[0:nBytes], remoteAddr) 112 | } 113 | if err != nil { 114 | log.Fatalf("Write error: %s\n", err) 115 | } 116 | } 117 | }() 118 | return sync_channel 119 | } 120 | 121 | // Handle UDP connection 122 | func udp_con_handle(con net.Conn) { 123 | in_channel := accept_from_udp_to_stream(con, os.Stdout) 124 | log.Println("Waiting for remote connection") 125 | remoteAddr := <-in_channel 126 | log.Println("Connected from", remoteAddr) 127 | out_channel := put_from_stream_to_udp(os.Stdin, con, remoteAddr) 128 | select { 129 | case <-in_channel: 130 | log.Println("Remote connection is closed") 131 | case <-out_channel: 132 | log.Println("Local program is terminated") 133 | } 134 | } 135 | 136 | func main() { 137 | var sourcePort string 138 | var destinationPort string 139 | var isUdp bool 140 | var isListen bool 141 | var host string 142 | flag.StringVar(&sourcePort, "p", "", "Specifies the source port netcat should use, subject to privilege restrictions and availability.") 143 | flag.BoolVar(&isUdp, "u", false, "Use UDP instead of the default option of TCP.") 144 | flag.BoolVar(&isListen, "l", false, "Used to specify that netcat should listen for an incoming connection rather than initiate a connection to a remote host.") 145 | flag.Parse() 146 | if flag.NFlag() == 0 && flag.NArg() == 0 { 147 | fmt.Println("go-nc [-lu] [-p source port ] [-s source ip address ] [hostname ] [port[s]]") 148 | flag.Usage() 149 | os.Exit(1) 150 | } 151 | log.Println("Source port:", sourcePort) 152 | if flag.Lookup("u") != nil { 153 | log.Println("Protocol:", "udp") 154 | isUdp = true 155 | } else { 156 | log.Println("Protocol:", "tcp") 157 | } 158 | if sourcePort != "" { 159 | if _, err := strconv.Atoi(sourcePort); err != nil { 160 | log.Println("Source port shall be not empty and have integer value") 161 | os.Exit(1) 162 | } 163 | } 164 | 165 | if !isListen { 166 | if flag.NArg() < 2 { 167 | log.Println("[hostname ] [port] are mandatory arguments") 168 | os.Exit(1) 169 | } 170 | 171 | if _, err := strconv.Atoi(flag.Arg(1)); err != nil { 172 | log.Println("Destination port shall be not empty and have integer value") 173 | os.Exit(1) 174 | } 175 | host = flag.Arg(0) 176 | destinationPort = fmt.Sprintf(":%v", flag.Arg(1)) 177 | 178 | } else { 179 | if flag.NArg() < 1 { 180 | fmt.Println("when you use -l option [port] is mandatory argument") 181 | os.Exit(1) 182 | } 183 | if _, err := strconv.Atoi(flag.Arg(0)); err != nil { 184 | log.Println("Destination port shall be not empty and have integer value") 185 | os.Exit(1) 186 | } 187 | destinationPort = fmt.Sprintf(":%v", flag.Arg(0)) 188 | } 189 | 190 | log.Println("Hostname:", host) 191 | log.Println("Port:", destinationPort) 192 | if !isUdp { 193 | log.Println("Work with TCP protocol") 194 | if isListen { 195 | listener, err := net.Listen("tcp", destinationPort) 196 | if err != nil { 197 | log.Fatalln(err) 198 | } 199 | log.Println("Listening on", destinationPort) 200 | con, err := listener.Accept() 201 | if err != nil { 202 | log.Fatalln(err) 203 | } 204 | log.Println("Connect from", con.RemoteAddr()) 205 | tcp_con_handle(con) 206 | 207 | } else if host != "" { 208 | con, err := net.Dial("tcp", host+destinationPort) 209 | if err != nil { 210 | log.Fatalln(err) 211 | } 212 | log.Println("Connected to", host+destinationPort) 213 | tcp_con_handle(con) 214 | } else { 215 | flag.Usage() 216 | } 217 | } else { 218 | log.Println("Work with UDP protocol") 219 | if isListen { 220 | addr, err := net.ResolveUDPAddr("udp", destinationPort) 221 | if err != nil { 222 | log.Fatalln(err) 223 | } 224 | con, err := net.ListenUDP("udp", addr) 225 | if err != nil { 226 | log.Fatalln(err) 227 | } 228 | log.Println("Has been resolved UDP address:", addr) 229 | log.Println("Listening on", destinationPort) 230 | udp_con_handle(con) 231 | } else if host != "" { 232 | addr, err := net.ResolveUDPAddr("udp", host+destinationPort) 233 | if err != nil { 234 | log.Fatalln(err) 235 | } 236 | log.Println("Has been resolved UDP address:", addr) 237 | con, err := net.DialUDP("udp", nil, addr) 238 | if err != nil { 239 | log.Fatalln(err) 240 | } 241 | udp_con_handle(con) 242 | } 243 | } 244 | } 245 | --------------------------------------------------------------------------------