seccat (to test sio)
Juan Batiz-Benet committed
Dec 10, 2014 at 23:49 UTC
5b142948ab413ad20921a9d7ffdd00ffcdb6d693
3 files changed
+292
cmd/seccat/.gitignore
new
+1
@@ -0,0 +1 @@
1
+seccat
cmd/seccat/seccat.go
new
+240
@@ -0,0 +1,240 @@
1
+// package main provides an implementation of netcat using the secio package.
2
+// This means the channel is encrypted (and MACed).
3
+// It is meant to exercise the spipe package.
4
+// Usage:
5
+// seccat [<local address>] <remote address>
6
+// seccat -l <local address>
7
+//
8
+// Address format is: [host]:port
9
+package main
10
+
11
+import (
12
+ "errors"
13
+ "flag"
14
+ "fmt"
15
+ "io"
16
+ "net"
17
+ "os"
18
+ "os/signal"
19
+ "syscall"
20
+
21
+ ci "github.com/jbenet/go-ipfs/crypto"
22
+ secio "github.com/jbenet/go-ipfs/crypto/secio"
23
+ peer "github.com/jbenet/go-ipfs/peer"
24
+ u "github.com/jbenet/go-ipfs/util"
25
+)
26
+
27
+var verbose = false
28
+
29
+// Usage prints out the usage of this module.
30
+// Assumes flags use go stdlib flag pacakage.
31
+var Usage = func() {
32
+ text := `seccat - secure netcat in Go
33
+
34
+Usage:
35
+
36
+ listen: %s [<local address>] <remote address>
37
+ dial: %s -l <local address>
38
+
39
+Address format is Go's: [host]:port
40
+`
41
+
42
+ fmt.Fprintf(os.Stderr, text, os.Args[0], os.Args[0])
43
+ flag.PrintDefaults()
44
+}
45
+
46
+type args struct {
47
+ listen bool
48
+ verbose bool
49
+ debug bool
50
+ localAddr string
51
+ remoteAddr string
52
+ // keyfile string
53
+ keybits int
54
+}
55
+
56
+func parseArgs() args {
57
+ var a args
58
+
59
+ // setup + parse flags
60
+ flag.BoolVar(&a.listen, "listen", false, "listen for connections")
61
+ flag.BoolVar(&a.listen, "l", false, "listen for connections (short)")
62
+ flag.BoolVar(&a.verbose, "v", true, "verbose")
63
+ flag.BoolVar(&a.debug, "debug", false, "debugging")
64
+ // flag.StringVar(&a.keyfile, "key", "", "private key file")
65
+ flag.IntVar(&a.keybits, "keybits", 2048, "num bits for generating private key")
66
+ flag.Usage = Usage
67
+ flag.Parse()
68
+ osArgs := flag.Args()
69
+
70
+ if len(osArgs) < 1 {
71
+ exit("")
72
+ }
73
+
74
+ if a.verbose {
75
+ out("verbose on")
76
+ }
77
+
78
+ if a.listen {
79
+ a.localAddr = osArgs[0]
80
+ } else {
81
+ if len(osArgs) > 1 {
82
+ a.localAddr = osArgs[0]
83
+ a.remoteAddr = osArgs[1]
84
+ } else {
85
+ a.remoteAddr = osArgs[0]
86
+ }
87
+ }
88
+
89
+ return a
90
+}
91
+
92
+func main() {
93
+ args := parseArgs()
94
+ verbose = args.verbose
95
+ if args.debug {
96
+ u.SetDebugLogging()
97
+ }
98
+
99
+ go func() {
100
+ // wait until we exit.
101
+ sigc := make(chan os.Signal, 1)
102
+ signal.Notify(sigc, syscall.SIGABRT)
103
+ <-sigc
104
+ panic("ABORT! ABORT! ABORT!")
105
+ }()
106
+
107
+ if err := connect(args); err != nil {
108
+ exit("%s", err)
109
+ }
110
+}
111
+
112
+func setupPeer(a args) (peer.Peer, peer.Peerstore, error) {
113
+ if a.keybits < 1024 {
114
+ return nil, nil, errors.New("Bitsize less than 1024 is considered unsafe.")
115
+ }
116
+
117
+ out("generating key pair...")
118
+ sk, pk, err := ci.GenerateKeyPair(ci.RSA, a.keybits)
119
+ if err != nil {
120
+ return nil, nil, err
121
+ }
122
+
123
+ ps := peer.NewPeerstore()
124
+ peer, err := ps.WithKeyPair(sk, pk)
125
+ if err != nil {
126
+ return nil, nil, err
127
+ }
128
+ out("local peer id: %s", peer.ID())
129
+ return peer, ps, nil
130
+}
131
+
132
+func connect(args args) error {
133
+ p, ps, err := setupPeer(args)
134
+ if err != nil {
135
+ return err
136
+ }
137
+
138
+ var conn net.Conn
139
+ if args.listen {
140
+ conn, err = Listen(args.localAddr)
141
+ } else {
142
+ conn, err = Dial(args.localAddr, args.remoteAddr)
143
+ }
144
+ if err != nil {
145
+ return err
146
+ }
147
+
148
+ // log everything that goes through conn
149
+ rwc := &logRW{n: "conn", rw: conn}
150
+
151
+ // OK, let's setup the channel.
152
+ sg := secio.SessionGenerator{Local: p, Peerstore: ps}
153
+ sess, err := sg.NewSession(nil, rwc)
154
+ if err != nil {
155
+ return err
156
+ }
157
+ out("remote peer id: %s", sess.RemotePeer().ID())
158
+ netcat(sess.ReadWriter().(io.ReadWriteCloser))
159
+ return nil
160
+}
161
+
162
+// Listen listens and accepts one incoming UDT connection on a given port,
163
+// and pipes all incoming data to os.Stdout.
164
+func Listen(localAddr string) (net.Conn, error) {
165
+ l, err := net.Listen("tcp", localAddr)
166
+ if err != nil {
167
+ return nil, err
168
+ }
169
+ out("listening at %s", l.Addr())
170
+
171
+ c, err := l.Accept()
172
+ if err != nil {
173
+ return nil, err
174
+ }
175
+ out("accepted connection from %s", c.RemoteAddr())
176
+
177
+ // done with listener
178
+ l.Close()
179
+
180
+ return c, nil
181
+}
182
+
183
+// Dial connects to a remote address and pipes all os.Stdin to the remote end.
184
+// If localAddr is set, uses it to Dial from.
185
+func Dial(localAddr, remoteAddr string) (net.Conn, error) {
186
+
187
+ var laddr net.Addr
188
+ var err error
189
+ if localAddr != "" {
190
+ laddr, err = net.ResolveTCPAddr("tcp", localAddr)
191
+ if err != nil {
192
+ return nil, fmt.Errorf("failed to resolve address %s", localAddr)
193
+ }
194
+ }
195
+
196
+ if laddr != nil {
197
+ out("dialing %s from %s", remoteAddr, laddr)
198
+ } else {
199
+ out("dialing %s", remoteAddr)
200
+ }
201
+
202
+ d := net.Dialer{LocalAddr: laddr}
203
+ c, err := d.Dial("tcp", remoteAddr)
204
+ if err != nil {
205
+ return nil, err
206
+ }
207
+ out("connected to %s", c.RemoteAddr())
208
+
209
+ return c, nil
210
+}
211
+
212
+func netcat(c io.ReadWriteCloser) {
213
+ out("piping stdio to connection")
214
+
215
+ done := make(chan struct{}, 2)
216
+
217
+ go func() {
218
+ n, _ := io.Copy(c, os.Stdin)
219
+ out("sent %d bytes", n)
220
+ done <- struct{}{}
221
+ }()
222
+ go func() {
223
+ n, _ := io.Copy(os.Stdout, c)
224
+ out("received %d bytes", n)
225
+ done <- struct{}{}
226
+ }()
227
+
228
+ // wait until we exit.
229
+ sigc := make(chan os.Signal, 1)
230
+ signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT,
231
+ syscall.SIGTERM, syscall.SIGQUIT)
232
+
233
+ select {
234
+ case <-done:
235
+ case <-sigc:
236
+ return
237
+ }
238
+
239
+ c.Close()
240
+}
cmd/seccat/util.go
new
+51
@@ -0,0 +1,51 @@
1
+package main
2
+
3
+import (
4
+ "fmt"
5
+ "io"
6
+ "os"
7
+
8
+ u "github.com/jbenet/go-ipfs/util"
9
+)
10
+
11
+var log = u.Logger("seccat")
12
+
13
+func exit(format string, vals ...interface{}) {
14
+ if format != "" {
15
+ fmt.Fprintf(os.Stderr, "seccat: error: "+format+"\n", vals...)
16
+ }
17
+ Usage()
18
+ os.Exit(1)
19
+}
20
+
21
+func out(format string, vals ...interface{}) {
22
+ if verbose {
23
+ fmt.Fprintf(os.Stderr, "seccat: "+format+"\n", vals...)
24
+ }
25
+}
26
+
27
+type logRW struct {
28
+ n string
29
+ rw io.ReadWriter
30
+}
31
+
32
+func (r *logRW) Read(buf []byte) (int, error) {
33
+ n, err := r.rw.Read(buf)
34
+ if err == nil {
35
+ log.Debugf("%s read: %v", r.n, buf)
36
+ }
37
+ return n, err
38
+}
39
+
40
+func (r *logRW) Write(buf []byte) (int, error) {
41
+ log.Debugf("%s write: %v", r.n, buf)
42
+ return r.rw.Write(buf)
43
+}
44
+
45
+func (r *logRW) Close() error {
46
+ c, ok := r.rw.(io.Closer)
47
+ if ok {
48
+ return c.Close()
49
+ }
50
+ return nil
51
+}