pax_global_header00006660000000000000000000000064146566347660014541gustar00rootroot0000000000000052 comment=715c2a6d167ac6ba3300236f6cfa4eb2dda1184c telnet-0.1.0/000077500000000000000000000000001465663476600130325ustar00rootroot00000000000000telnet-0.1.0/LICENSE000066400000000000000000000025621465663476600140440ustar00rootroot00000000000000Copyright (c) 2013, Michal Derkacz All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. telnet-0.1.0/README.md000066400000000000000000000000731465663476600143110ustar00rootroot00000000000000[documentation](http://godoc.org/github.com/ziutek/telnet) telnet-0.1.0/conn.go000066400000000000000000000204141465663476600143170ustar00rootroot00000000000000// Package telnet provides simple interface for interacting with Telnet // connection. package telnet import ( "bufio" "bytes" "fmt" "net" "time" "unicode" ) const ( CR = byte('\r') LF = byte('\n') ) const ( cmdSE = 240 cmdNOP = 241 cmdData = 242 cmdBreak = 243 cmdGA = 249 cmdSB = 250 cmdWill = 251 cmdWont = 252 cmdDo = 253 cmdDont = 254 cmdIAC = 255 ) const ( optEcho = 1 optSuppressGoAhead = 3 // optTerminalType = 24 optNAWS = 31 ) // Conn implements net.Conn interface for Telnet protocol plus some set of // Telnet specific methods. type Conn struct { net.Conn r *bufio.Reader unixWriteMode bool cliSuppressGoAhead bool cliEcho bool } func NewConn(conn net.Conn) (*Conn, error) { c := Conn{ Conn: conn, r: bufio.NewReaderSize(conn, 256), } return &c, nil } func Dial(network, addr string) (*Conn, error) { conn, err := net.Dial(network, addr) if err != nil { return nil, err } return NewConn(conn) } func DialTimeout(network, addr string, timeout time.Duration) (*Conn, error) { conn, err := net.DialTimeout(network, addr, timeout) if err != nil { return nil, err } return NewConn(conn) } // SetUnixWriteMode sets flag that applies only to the Write method. // If set, Write converts any '\n' (LF) to '\r\n' (CR LF). func (c *Conn) SetUnixWriteMode(uwm bool) { c.unixWriteMode = uwm } func (c *Conn) do(option byte) error { //log.Println("do:", option) _, err := c.Conn.Write([]byte{cmdIAC, cmdDo, option}) return err } func (c *Conn) dont(option byte) error { //log.Println("dont:", option) _, err := c.Conn.Write([]byte{cmdIAC, cmdDont, option}) return err } func (c *Conn) will(option byte) error { //log.Println("will:", option) _, err := c.Conn.Write([]byte{cmdIAC, cmdWill, option}) return err } func (c *Conn) wont(option byte) error { //log.Println("wont:", option) _, err := c.Conn.Write([]byte{cmdIAC, cmdWont, option}) return err } func (c *Conn) sub(opt byte, data ...byte) error { if _, err := c.Conn.Write([]byte{cmdIAC, cmdSB, opt}); err != nil { return err } if _, err := c.Conn.Write(data); err != nil { return err } _, err := c.Conn.Write([]byte{cmdIAC, cmdSE}) return err } func (c *Conn) deny(cmd, opt byte) (err error) { switch cmd { case cmdDo: err = c.wont(opt) case cmdDont: // nop case cmdWill, cmdWont: err = c.dont(opt) } return } func (c *Conn) skipSubneg() error { for { if b, err := c.r.ReadByte(); err != nil { return err } else if b == cmdIAC { if b, err = c.r.ReadByte(); err != nil { return err } else if b == cmdSE { return nil } } } } func (c *Conn) cmd(cmd byte) error { switch cmd { case cmdGA: return nil case cmdDo, cmdDont, cmdWill, cmdWont: // Process cmd after this switch. case cmdSB: return c.skipSubneg() default: return fmt.Errorf("unknown command: %d", cmd) } // Read an option o, err := c.r.ReadByte() if err != nil { return err } //log.Println("received cmd:", cmd, o) switch o { case optEcho: // Accept any echo configuration. switch cmd { case cmdDo: if !c.cliEcho { c.cliEcho = true err = c.will(o) } case cmdDont: if c.cliEcho { c.cliEcho = false err = c.wont(o) } case cmdWill: if !c.cliEcho { c.cliEcho = true err = c.do(o) } case cmdWont: if c.cliEcho { c.cliEcho = false err = c.dont(o) } } case optSuppressGoAhead: // We don't use GA so can allways accept every configuration switch cmd { case cmdDo: if !c.cliSuppressGoAhead { c.cliSuppressGoAhead = true err = c.will(o) } case cmdDont: if c.cliSuppressGoAhead { c.cliSuppressGoAhead = false err = c.wont(o) } case cmdWill: if !c.cliSuppressGoAhead { c.cliSuppressGoAhead = true err = c.do(o) } case cmdWont: if c.cliSuppressGoAhead { c.cliSuppressGoAhead = false err = c.dont(o) } } case optNAWS: if cmd != cmdDo { err = c.deny(cmd, o) break } if err = c.will(o); err != nil { break } // Reply with max window size: 65535x65535 err = c.sub(o, 255, 255, 255, 255, 255, 255, 255, 255) default: // Deny any other option err = c.deny(cmd, o) } return err } func (c *Conn) tryReadByte() (b byte, retry bool, err error) { b, err = c.r.ReadByte() if err != nil || b != cmdIAC { return } b, err = c.r.ReadByte() if err != nil { return } if b != cmdIAC { err = c.cmd(b) if err != nil { return } retry = true } return } // SetEcho tries to enable/disable echo on server side. Typically telnet // servers doesn't support this. func (c *Conn) SetEcho(echo bool) error { if echo { return c.do(optEcho) } return c.dont(optEcho) } // ReadByte works like bufio.ReadByte func (c *Conn) ReadByte() (b byte, err error) { retry := true for retry && err == nil { b, retry, err = c.tryReadByte() } return } // ReadRune works like bufio.ReadRune func (c *Conn) ReadRune() (r rune, size int, err error) { loop: r, size, err = c.r.ReadRune() if err != nil { return } if r != unicode.ReplacementChar || size != 1 { // Properly readed rune return } // Bad rune err = c.r.UnreadRune() if err != nil { return } // Read telnet command or escaped IAC _, retry, err := c.tryReadByte() if err != nil { return } if retry { // This bad rune was a begining of telnet command. Try read next rune. goto loop } // Return escaped IAC as unicode.ReplacementChar return } // Read is for implement an io.Reader interface func (c *Conn) Read(buf []byte) (int, error) { var n int for n < len(buf) { b, retry, err := c.tryReadByte() if err != nil { return n, err } if !retry { buf[n] = b n++ } if n > 0 && c.r.Buffered() == 0 { // Don't block if can't return more data. return n, err } } return n, nil } // ReadBytes works like bufio.ReadBytes func (c *Conn) ReadBytes(delim byte) ([]byte, error) { var line []byte for { b, err := c.ReadByte() if err != nil { return nil, err } line = append(line, b) if b == delim { break } } return line, nil } // SkipBytes works like ReadBytes but skips all read data. func (c *Conn) SkipBytes(delim byte) error { for { b, err := c.ReadByte() if err != nil { return err } if b == delim { break } } return nil } // ReadString works like bufio.ReadString func (c *Conn) ReadString(delim byte) (string, error) { bytes, err := c.ReadBytes(delim) return string(bytes), err } func (c *Conn) readUntil(read bool, delims ...string) ([]byte, int, error) { if len(delims) == 0 { return nil, 0, nil } p := make([]string, len(delims)) for i, s := range delims { if len(s) == 0 { return nil, 0, nil } p[i] = s } var line []byte for { b, err := c.ReadByte() if err != nil { return nil, 0, err } if read { line = append(line, b) } for i, s := range p { if s[0] == b { if len(s) == 1 { return line, i, nil } p[i] = s[1:] } else { p[i] = delims[i] } } } panic(nil) } // ReadUntilIndex reads from connection until one of delimiters occurs. Returns // read data and an index of delimiter or error. func (c *Conn) ReadUntilIndex(delims ...string) ([]byte, int, error) { return c.readUntil(true, delims...) } // ReadUntil works like ReadUntilIndex but don't return a delimiter index. func (c *Conn) ReadUntil(delims ...string) ([]byte, error) { d, _, err := c.readUntil(true, delims...) return d, err } // SkipUntilIndex works like ReadUntilIndex but skips all read data. func (c *Conn) SkipUntilIndex(delims ...string) (int, error) { _, i, err := c.readUntil(false, delims...) return i, err } // SkipUntil works like ReadUntil but skips all read data. func (c *Conn) SkipUntil(delims ...string) error { _, _, err := c.readUntil(false, delims...) return err } // Write is for implement an io.Writer interface func (c *Conn) Write(buf []byte) (int, error) { search := "\xff" if c.unixWriteMode { search = "\xff\n" } var ( n int err error ) for len(buf) > 0 { var k int i := bytes.IndexAny(buf, search) if i == -1 { k, err = c.Conn.Write(buf) n += k break } k, err = c.Conn.Write(buf[:i]) n += k if err != nil { break } switch buf[i] { case LF: k, err = c.Conn.Write([]byte{CR, LF}) case cmdIAC: k, err = c.Conn.Write([]byte{cmdIAC, cmdIAC}) } n += k if err != nil { break } buf = buf[i+1:] } return n, err } telnet-0.1.0/examples/000077500000000000000000000000001465663476600146505ustar00rootroot00000000000000telnet-0.1.0/examples/bat.org/000077500000000000000000000000001465663476600162045ustar00rootroot00000000000000telnet-0.1.0/examples/bat.org/main.go000066400000000000000000000005511465663476600174600ustar00rootroot00000000000000package main import ( "log" "os" "github.com/ziutek/telnet" ) func checkErr(err error) { if err != nil { log.Fatalln("Error:", err) } } func main() { t, err := telnet.Dial("tcp", "bat.org:23") checkErr(err) buf := make([]byte, 512) for { n, err := t.Read(buf) // Use raw read to find issue #15. os.Stdout.Write(buf[:n]) checkErr(err) } } telnet-0.1.0/examples/unix-cisco/000077500000000000000000000000001465663476600167315ustar00rootroot00000000000000telnet-0.1.0/examples/unix-cisco/main.go000066400000000000000000000024041465663476600202040ustar00rootroot00000000000000package main import ( "log" "os" "time" "github.com/ziutek/telnet" ) const timeout = 10 * time.Second func checkErr(err error) { if err != nil { log.Fatalln("Error:", err) } } func expect(t *telnet.Conn, d ...string) { checkErr(t.SetReadDeadline(time.Now().Add(timeout))) checkErr(t.SkipUntil(d...)) } func sendln(t *telnet.Conn, s string) { checkErr(t.SetWriteDeadline(time.Now().Add(timeout))) buf := make([]byte, len(s)+1) copy(buf, s) buf[len(s)] = '\n' _, err := t.Write(buf) checkErr(err) } func main() { if len(os.Args) != 5 { log.Printf("Usage: %s {unix|cisco} HOST:PORT USER PASSWD", os.Args[0]) return } typ, dst, user, passwd := os.Args[1], os.Args[2], os.Args[3], os.Args[4] t, err := telnet.Dial("tcp", dst) checkErr(err) t.SetUnixWriteMode(true) var data []byte switch typ { case "unix": expect(t, "login: ") sendln(t, user) expect(t, "ssword: ") sendln(t, passwd) expect(t, "$") sendln(t, "ls -l") data, err = t.ReadBytes('$') case "cisco": expect(t, "name: ") sendln(t, user) expect(t, "ssword: ") sendln(t, passwd) expect(t, ">") sendln(t, "sh ver") data, err = t.ReadBytes('>') default: log.Fatalln("bad host type: " + typ) } checkErr(err) os.Stdout.Write(data) os.Stdout.WriteString("\n") } telnet-0.1.0/go.mod000066400000000000000000000000531465663476600141360ustar00rootroot00000000000000module github.com/ziutek/telnet go 1.22.5