-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.go
125 lines (102 loc) · 3.02 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package telnet
import (
"context"
"crypto/tls"
"fmt"
"io"
"log/slog"
"os"
"strings"
)
type (
// A Caller represents the client end of a TELNET (or TELNETS) connection.
//
// Writing data to the Writer passed as an argument to the CallTELNET method
// will send data to the TELNET (or TELNETS) server.
//
// Reading data from the Reader passed as an argument to the CallTELNET method
// will receive data from the TELNET server.
//
// The Writer's Write method sends "escaped" TELNET (and TELNETS) data.
//
// The Reader's Read method "un-escapes" TELNET (and TELNETS) data, and filters
// out TELNET (and TELNETS) command sequences.
Caller interface {
CallTELNET(context.Context, io.Writer, io.Reader)
}
Client struct {
Caller Caller
Logger *slog.Logger
}
)
func NewClient(caller Caller, logger *slog.Logger) *Client {
if logger == nil {
logger = slog.Default()
}
return &Client{Caller: caller, Logger: logger}
}
func (client *Client) Call(conn *Conn) error {
caller := client.Caller
if caller == nil {
client.Logger.Debug("defaulted caller to EchoCaller")
caller = EchoCaller
}
caller.CallTELNET(context.Background(), conn.writer, conn.reader)
// TODO: should this be closed here? Seems irresponsible to not leave it up to the caller
conn.Close()
return nil
}
func DialAndCall(srvAddr string, caller Caller) error {
conn, err := Dial("", srvAddr)
if err != nil {
return err
}
client := NewClient(caller, nil)
return client.Call(conn)
}
func DialAndCallTLS(srvAddr string, caller Caller, tlsConfig *tls.Config) error {
conn, err := DialTLS("", srvAddr, tlsConfig)
if err != nil {
return err
}
client := NewClient(caller, nil)
return client.Call(conn)
}
// The CallerFunc type is an adapter to allow the use of ordinary functions as TELNET callers.
type CallerFunc func(context.Context, io.Writer, io.Reader)
// CallTELNET calls f(ctx, w, r).
func (f CallerFunc) CallTELNET(ctx context.Context, w io.Writer, r io.Reader) {
f(ctx, w, r)
}
// EchoCaller is a simple TELNET client which sends to the server any data it gets from os.Stdin
// as TELNET data, and writes any TELNET data it receives from the server to os.Stdout.
var EchoCaller CallerFunc = func(ctx context.Context, w io.Writer, r io.Reader) {
for {
serverLine, err := ReadLine(r)
if err != nil {
if err == io.EOF {
fmt.Println("Connection closed by foreign host.")
return
}
fmt.Printf("Failed to read from the server: %v\n", err)
return
}
if _, err = os.Stdout.WriteString(serverLine); err != nil {
fmt.Printf("Failed to write server response to stdout: %v\n", err)
return
}
clientLine, err := ReadLine(os.Stdin)
if err != nil {
fmt.Printf("Failed to read client input: %v\n", err)
return
}
if !strings.HasSuffix(clientLine, "\r\n") {
// The client may have supplied a new line without a carriage return.
clientLine = strings.TrimSuffix(clientLine, "\n") + "\r\n"
}
if err = WriteLine(w, clientLine); err != nil {
fmt.Printf("Failed to write to server: %v\n", err)
return
}
}
}