github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/cmd/seccat/seccat.go (about)

     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/ipfs/go-ipfs/p2p/crypto"
    22  	secio "github.com/ipfs/go-ipfs/p2p/crypto/secio"
    23  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    24  	u "github.com/ipfs/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.ID, peer.Peerstore, error) {
   113  	if a.keybits < 1024 {
   114  		return "", 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, err
   121  	}
   122  
   123  	p, err := peer.IDFromPublicKey(pk)
   124  	if err != nil {
   125  		return "", nil, err
   126  	}
   127  
   128  	ps := peer.NewPeerstore()
   129  	ps.AddPrivKey(p, sk)
   130  	ps.AddPubKey(p, pk)
   131  
   132  	out("local peer id: %s", p)
   133  	return p, ps, nil
   134  }
   135  
   136  func connect(args args) error {
   137  	p, ps, err := setupPeer(args)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	var conn net.Conn
   143  	if args.listen {
   144  		conn, err = Listen(args.localAddr)
   145  	} else {
   146  		conn, err = Dial(args.localAddr, args.remoteAddr)
   147  	}
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	// log everything that goes through conn
   153  	rwc := &logRW{n: "conn", rw: conn}
   154  
   155  	// OK, let's setup the channel.
   156  	sk := ps.PrivKey(p)
   157  	sg := secio.SessionGenerator{LocalID: p, PrivateKey: sk}
   158  	sess, err := sg.NewSession(nil, rwc)
   159  	if err != nil {
   160  		return err
   161  	}
   162  	out("remote peer id: %s", sess.RemotePeer())
   163  	netcat(sess.ReadWriter().(io.ReadWriteCloser))
   164  	return nil
   165  }
   166  
   167  // Listen listens and accepts one incoming UDT connection on a given port,
   168  // and pipes all incoming data to os.Stdout.
   169  func Listen(localAddr string) (net.Conn, error) {
   170  	l, err := net.Listen("tcp", localAddr)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	out("listening at %s", l.Addr())
   175  
   176  	c, err := l.Accept()
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	out("accepted connection from %s", c.RemoteAddr())
   181  
   182  	// done with listener
   183  	l.Close()
   184  
   185  	return c, nil
   186  }
   187  
   188  // Dial connects to a remote address and pipes all os.Stdin to the remote end.
   189  // If localAddr is set, uses it to Dial from.
   190  func Dial(localAddr, remoteAddr string) (net.Conn, error) {
   191  
   192  	var laddr net.Addr
   193  	var err error
   194  	if localAddr != "" {
   195  		laddr, err = net.ResolveTCPAddr("tcp", localAddr)
   196  		if err != nil {
   197  			return nil, fmt.Errorf("failed to resolve address %s", localAddr)
   198  		}
   199  	}
   200  
   201  	if laddr != nil {
   202  		out("dialing %s from %s", remoteAddr, laddr)
   203  	} else {
   204  		out("dialing %s", remoteAddr)
   205  	}
   206  
   207  	d := net.Dialer{LocalAddr: laddr}
   208  	c, err := d.Dial("tcp", remoteAddr)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	out("connected to %s", c.RemoteAddr())
   213  
   214  	return c, nil
   215  }
   216  
   217  func netcat(c io.ReadWriteCloser) {
   218  	out("piping stdio to connection")
   219  
   220  	done := make(chan struct{}, 2)
   221  
   222  	go func() {
   223  		n, _ := io.Copy(c, os.Stdin)
   224  		out("sent %d bytes", n)
   225  		done <- struct{}{}
   226  	}()
   227  	go func() {
   228  		n, _ := io.Copy(os.Stdout, c)
   229  		out("received %d bytes", n)
   230  		done <- struct{}{}
   231  	}()
   232  
   233  	// wait until we exit.
   234  	sigc := make(chan os.Signal, 1)
   235  	signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT,
   236  		syscall.SIGTERM, syscall.SIGQUIT)
   237  
   238  	select {
   239  	case <-done:
   240  	case <-sigc:
   241  		return
   242  	}
   243  
   244  	c.Close()
   245  }