commit 14b55747e53b717d8a3aee6124fb4908d57cbb05 Author: Hugo Thunnissen Date: Thu May 5 12:19:47 2022 +0200 Initial Commit: a dirty mess, just the way I like it diff --git a/filestore.go b/filestore.go new file mode 100644 index 0000000..9ac1c82 --- /dev/null +++ b/filestore.go @@ -0,0 +1,145 @@ +package main + +import ( + "errors" + "io" + "time" + + ipfsapi "github.com/ipfs/go-ipfs-api" + "golang.org/x/net/context" +) + +type File struct { + Name string `json:"name"` + AbsolutePath string `json:"absolute_path"` + Contents io.Reader `json:"-"` +} + +type Filestore interface { + Get(name string) (*File, error) + Set(file *File) error + Delete(file *File) error + Exists(name string) (bool, error) +} + +func NewIPFSFilestore(ipfs *ipfsapi.Shell, rootDir string) (*IPFSFilestore, error) { + store := &IPFSFilestore{ + ipfs: ipfs, + rootDir: rootDir, + } + + exists, err := store.Exists("") + if err != nil { + return nil, err + } + + if !exists { + err := ipfs.FilesMkdir(newContext(), rootDir) + if err != nil { + return nil, err + } + } + + return store, nil +} + +type IPFSFilestore struct { + ipfs *ipfsapi.Shell + rootDir string +} + +var ErrFileNotFound = errors.New("File not found") + +type ErrFailedFileExistsCheck struct { + parent error +} + +func (e *ErrFailedFileExistsCheck) Unwrap() error { + return e.parent +} + +func (e *ErrFailedFileExistsCheck) Error() string { + msg := "Failed to determine whether file exists" + + if e.parent != nil { + msg += ": " + e.parent.Error() + } + + return msg +} + +func newContext() context.Context { + ctx, _ := context.WithTimeout(context.Background(), 10*time.Minute) + return ctx +} + +func (s *IPFSFilestore) Exists(name string) (bool, error) { + ctx := newContext() + _, err := s.ipfs.FilesStat(ctx, s.rootDir+"/"+name) + if err != nil { + if err.Error() == "files/stat: file does not exist" { + return false, nil + } + + return false, &ErrFailedFileExistsCheck{parent: err} + } + + return true, nil +} + +type FileReader struct { + ipfs *ipfsapi.Shell + file *File + reader io.ReadCloser +} + +func (r *FileReader) Read(p []byte) (int, error) { + if r.reader == nil { + var err error + r.reader, err = r.ipfs.FilesRead(newContext(), r.file.AbsolutePath) + if err != nil { + return 0, err + } + } + + return r.reader.Read(p) +} + +func (r *FileReader) Close() error { + if r.reader == nil { + return nil + } + + return r.reader.Close() +} + +func (s *IPFSFilestore) Get(name string) (*File, error) { + exists, err := s.Exists(name) + if err != nil { + return nil, err + } + + if !exists { + return nil, ErrFileNotFound + } + + file := &File{ + Name: name, + AbsolutePath: s.rootDir + "/" + name, + } + + file.Contents = &FileReader{ + ipfs: s.ipfs, + file: file, + } + + return file, nil +} + +func (s *IPFSFilestore) Set(file *File) error { + return s.ipfs.FilesWrite(newContext(), s.rootDir+"/"+file.Name, file.Contents) +} + +func (s *IPFSFilestore) Delete(file *File) error { + return s.ipfs.FilesRm(newContext(), file.AbsolutePath, true) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..828ff05 --- /dev/null +++ b/go.mod @@ -0,0 +1,42 @@ +module git.snorba.art/hugo/nssh + +go 1.18 + +require ( + github.com/SkynetLabs/go-skynet/v2 v2.1.0 // indirect + github.com/btcsuite/btcd v0.22.1 // indirect + github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect + github.com/creack/pty v1.1.18 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/ipfs/go-cid v0.2.0 // indirect + github.com/ipfs/go-ipfs-api v0.3.0 // indirect + github.com/ipfs/go-ipfs-files v0.1.1 // indirect + github.com/klauspost/cpuid/v2 v2.0.12 // indirect + github.com/libp2p/go-buffer-pool v0.0.2 // indirect + github.com/libp2p/go-flow-metrics v0.0.3 // indirect + github.com/libp2p/go-libp2p-core v0.15.1 // indirect + github.com/libp2p/go-openssl v0.0.7 // indirect + github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.0.4 // indirect + github.com/multiformats/go-base36 v0.1.0 // indirect + github.com/multiformats/go-multiaddr v0.5.0 // indirect + github.com/multiformats/go-multibase v0.0.3 // indirect + github.com/multiformats/go-multicodec v0.4.1 // indirect + github.com/multiformats/go-multihash v0.1.0 // indirect + github.com/multiformats/go-varint v0.0.6 // indirect + github.com/pkg/term v1.1.0 // indirect + github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b // indirect + gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975 // indirect + go.opencensus.io v0.23.0 // indirect + golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect + golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + lukechampine.com/blake3 v1.1.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1114029 --- /dev/null +++ b/go.sum @@ -0,0 +1,252 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/SkynetLabs/go-skynet/v2 v2.1.0 h1:fYUoe2Lu8epLBkd+B2jaKbS82KQ2Vv+eqLG0R8zMwZM= +github.com/SkynetLabs/go-skynet/v2 v2.1.0/go.mod h1:XOk0zwGlXeGjHQgmhXTEk7qTD6FVv3dXPW38Wh3XsIc= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= +github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= +github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-cid v0.2.0 h1:01JTiihFq9en9Vz0lc0VDWvZe/uBonGpzo4THP0vcQ0= +github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= +github.com/ipfs/go-ipfs-api v0.3.0 h1:ZzVrsTV31Z53ZlUare6a5UJ46lC7lW93q/s1/fXyATk= +github.com/ipfs/go-ipfs-api v0.3.0/go.mod h1:A1naQGm0Jg01GxDq7oDyVSZxt20SuRTNIBFNZJgPDmg= +github.com/ipfs/go-ipfs-files v0.0.9/go.mod h1:aFv2uQ/qxWpL/6lidWvnSQmaVqCrf0TBGoUr+C1Fo84= +github.com/ipfs/go-ipfs-files v0.1.1 h1:/MbEowmpLo9PJTEQk16m9rKzUHjeP4KRU9nWJyJO324= +github.com/ipfs/go-ipfs-files v0.1.1/go.mod h1:8xkIrMWH+Y5P7HvJ4Yc5XWwIW2e52dyXUiC0tZyjDbM= +github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= +github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= +github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.15.1 h1:0RY+Mi/ARK9DgG1g9xVQLb8dDaaU8tCePMtGALEfBnM= +github.com/libp2p/go-libp2p-core v0.15.1/go.mod h1:agSaboYM4hzB1cWekgVReqV5M4g5M+2eNNejV+1EEhs= +github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= +github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= +github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base32 v0.0.4 h1:+qMh4a2f37b4xTNs6mqitDinryCI+tfO2dRVMN9mjSE= +github.com/multiformats/go-base32 v0.0.4/go.mod h1:jNLFzjPZtp3aIARHbJRZIaPuspdH0J6q39uUM5pnABM= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= +github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= +github.com/multiformats/go-multiaddr v0.5.0 h1:i/JuOoVg4szYQ4YEzDGtb2h0o8M7CG/Yq6cGlcjWZpM= +github.com/multiformats/go-multiaddr v0.5.0/go.mod h1:3KAxNkUqLTJ20AAwN4XVX4kZar+bR+gh4zgbfr3SNug= +github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-multicodec v0.4.1 h1:BSJbf+zpghcZMZrwTYBGwy0CPcVZGWiC72Cp8bBd4R4= +github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.1.0 h1:CgAgwqk3//SVEw3T+6DqI4mWMyRuDwZtOWcJT0q9+EA= +github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= +github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= +github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs= +github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b h1:wA3QeTsaAXybLL2kb2cKhCAQTHgYTMwuI8lBlJSv5V8= +github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b/go.mod h1:xT1Y5p2JR2PfSZihE0s4mjdJaRGp1waCTf5JzhQLBck= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= +gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975 h1:L/ENs/Ar1bFzUeKx6m3XjlmBgIUlykX9dzvp5k9NGxc= +gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= +lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..e921e15 --- /dev/null +++ b/handler.go @@ -0,0 +1,324 @@ +package main + +import ( + "errors" + "fmt" + "log" + "strconv" + "strings" + + "golang.org/x/sys/unix" +) + +type CommandHandler struct { + shell *Shell + filestore Filestore +} + +func NewCommandHandler(shell *Shell, filestore Filestore) *CommandHandler { + return &CommandHandler{ + shell: shell, + filestore: filestore, + } +} + +func Control(c rune) rune { + return c & 0x1f +} + +func sttyCharFromDesc(c string) (rune, error) { + if len(c) > 2 || len(c) < 1 { + return 'i', errors.New( + "stty char description has unsupported length: " + + strconv.Itoa(len(c)), + ) + } + + if rune(c[0]) == '^' { + return Control(rune(c[1])), nil + } + + return rune(c[0]), nil +} + +const fakeUname = "Linux 5.16.19-76051619-generic" + +type CommandExecutor interface { + Execute(shell *Shell, args []string) error +} + +type SttyCommand struct{} + +func (c *SttyCommand) Execute(shell *Shell, args []string) error { + // Tramp uses: + // -inlcr: don't translate newline to carriage return + // -onlcr: don't translate newline to carriage return-newline + // -echo: echo input characters + // kill CHAR: CHAR will erase the current line + // erase CHAR: CHAR will erase the last character typed + // icanon: enable special charactersq + + fd := int(shell.GetPty().Fd()) + const ioctlReadTermios = unix.TCGETS // Linux + const ioctlWriteTermios = unix.TCSETS // Linux + + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + if err != nil { + panic(err) + } + + newState := *termios + + var skipNext bool + for i, arg := range args { + if skipNext { + skipNext = false + continue + } + + var disable bool + if arg[0] == '-' { + disable = true + arg = arg[1:] + } + + switch arg { + case "onlcr": + if disable { + newState.Iflag &^= unix.ONLCR + continue + } + + newState.Iflag |= unix.ONLCR + case "inlcr": + if disable { + newState.Iflag &^= unix.INLCR + continue + } + + newState.Iflag |= unix.INLCR + case "echo": + if disable { + newState.Lflag &^= unix.ECHO + continue + } + + newState.Lflag |= unix.ECHO + case "kill": + skipNext = true + + char, err := sttyCharFromDesc(args[i+1]) + if err != nil { + log.Printf( + "Warning: Not applying unsupported character description for stty kill: %s", + args[i+1], + ) + continue + } + + newState.Cc[unix.VKILL] = uint8(char) + case "erase": + skipNext = true + + char, err := sttyCharFromDesc(args[i+1]) + if err != nil { + log.Printf( + "Warning: Not applying unsupported character description for stty erase: %s", + args[i+1], + ) + continue + } + + newState.Cc[unix.VERASE] = uint8(char) + case "icanon": + if disable { + newState.Lflag &^= unix.ICANON + continue + } + + newState.Lflag |= unix.ICANON + + case "tab0": + newState.Oflag = (newState.Oflag & ^uint32(unix.TABDLY)) | unix.TAB0 + case "iutf8": + if disable { + newState.Iflag &^= unix.IUTF8 + continue + } + + newState.Iflag |= unix.IUTF8 + + default: + log.Printf( + "Warning, tramp requested unexpected stty option %s", + arg, + ) + } + } + + if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil { + return err + } + + return nil +} + +type EchoCommand struct{} + +func (c *EchoCommand) Execute(shell *Shell, args []string) error { + if len(args) > 1 { + if "\"`uname -sr`\"" == args[0]+" "+args[1] { + log.Println("Tramp requested uname, returning a fake one: " + fakeUname) + _, err := shell.WriteOutput( + []byte( + fmt.Sprintf( + "\"%s\"\n%s\n", + fakeUname, + "tramp_exit_status 0", + ), + ), + ) + return err + } + + if "\"`tty`\"" == args[0] { + log.Println("Telling tramp that it's dealing with a tty") + _, err := shell.WriteOutputString("\"/dev/pts/0\"\n" + trampSuccess) + + return err + } + } + + if len(args) > 0 { + if args[0] == "~root" { + log.Println("Telling tramp root's home directory") + _, err := shell.WriteOutputString("/root") + return err + } + } + + _, err := shell.WriteOutput([]byte(strings.Join(args, " ") + "\n")) + return err +} + +func AssignmentsToMap(args []string) map[string]string { + var assignments = make(map[string]string) + for _, arg := range args { + split := strings.Split(arg, "=") + if len(split) > 1 { + assignments[split[0]] = split[1] + } + } + + return assignments +} + +func ApplyEnvIfPresent(shell *Shell, comm *Command) { + var args []string + args = append(args, comm.Name) + args = append(args, comm.Arguments...) + + assignments := AssignmentsToMap(args) + if ps1, ok := assignments["PS1"]; ok { + shell.SetPrompt(ps1) + } +} + +type TestCommand struct { + filestore Filestore +} + +func (c *TestCommand) Execute(shell *Shell, args []string) error { + if len(args) > 0 && args[0] == "0" { + _, err := shell.WriteOutputString(trampSuccess) + return err + } + + if len(args) > 1 && args[0] == "-d" { + log.Printf("Checking for existance of directory %s", args[1]) + exists, err := c.filestore.Exists(args[1]) + if err != nil { + return err + } + + if exists { + _, err := shell.WriteOutputString(trampSuccess) + return err + } + + _, err = shell.WriteOutputString(trampFailure) + return err + } + + if len(args) > 1 && args[0] == "-r" { + _, err := shell.WriteOutputString(trampSuccess) + return err + } + + return nil +} + +const fakePath = "/bin:/usr/bin" +const fakePipeBuf = "4096" +const trampSuccess = "tramp_exit_status 0\n" +const trampFailure = "tramp_exit_status 1\n" + +type GetConfCommand struct{} + +func (c *GetConfCommand) Execute(shell *Shell, args []string) error { + if len(args) > 0 { + switch args[0] { + case "PATH": + shell.WriteOutputString(fakePath + "\n" + trampSuccess) + case "PIPE_BUF": + shell.WriteOutputString(fakePipeBuf + "\n" + trampSuccess) + } + } + return nil +} + +func (h *CommandHandler) Handle(comm *Command) error { + if strings.Contains(comm.Name, "=") { + ApplyEnvIfPresent(h.shell, comm) + return nil + } + + switch comm.Name { + case "exec": + ApplyEnvIfPresent(h.shell, comm) + case "(cd": + h.shell.WriteOutput([]byte("tramp_exit_status 0\n")) + case "echo": + (&EchoCommand{}).Execute(h.shell, comm.Arguments) + case "(echo": + if strings.Join(comm.Arguments, " ") == "foo ; echo bar)" { + log.Println("Handling tramp's foobar test") + h.shell.WriteOutputString("foo\nbar\n") + } + case "stty": + return (&SttyCommand{}).Execute(h.shell, comm.Arguments) + case "set": + log.Println("Ignoring \"set\" command") + case "locale": + if len(comm.Arguments) > 0 { + if comm.Arguments[0] == "-a" { + locales := []string{"C", "C.UTF-8", "POSIX", "en_US.utf8"} + log.Println("Tramp requested locale, returning fake values: ", locales) + h.shell.WriteOutputString( + strings.Join(locales, "\n") + "\n", + ) + return nil + } + } + + log.Println("Ignoring \"locale\" command with unsupported parameters: ", comm.Arguments) + case "getconf": + return (&GetConfCommand{}).Execute(h.shell, comm.Arguments) + case "test": + return (&TestCommand{filestore: h.filestore}).Execute(h.shell, comm.Arguments) + default: + log.Printf("Error: Received unexpected command %s", comm.Name) + } + + return nil +} diff --git a/local.log b/local.log new file mode 100644 index 0000000..d1cc44c --- /dev/null +++ b/local.log @@ -0,0 +1,289 @@ +2022/05/05 11:04:59 Private Key generated +2022/05/05 11:05:06 logged in with key SHA256:e+GGc8xMdIe6nXdohM3LR8s2H4moA8zffv8CP5WjB8M +2022/05/05 11:05:06 received request of type "pty-req" +2022/05/05 11:05:06 window resize 106x42 +2022/05/05 11:05:06 pty-req 'xterm-256color' +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "env" +2022/05/05 11:05:06 received request of type "shell" +2022/05/05 11:05:09 command: "ls" [] +2022/05/05 11:05:09 Error: Received unexpected command ls +2022/05/05 11:05:26 command: "test" [-d /var/www] +2022/05/05 11:05:26 Checking for existance of directory /var/www +2022/05/05 11:06:36 command: "locale" [] +2022/05/05 11:06:36 Ignoring "locale" command with unsupported parameters: [] +2022/05/05 11:06:39 command: "locale" [-a] +2022/05/05 11:06:39 Tramp requested locale, returning fake values: [C C.UTF-8 POSIX en_US.utf8] +2022/05/05 11:07:01 command: "exec" [] +2022/05/05 11:07:08 command: "PS1=hallo" [] +2022/05/05 11:07:08 Changing prompt to "hallo" +2022/05/05 11:07:21 command: "exec" [PS1=heey] +2022/05/05 11:07:21 Changing prompt to "heey" +2022/05/05 11:07:35 command: "exec" [HAAi=DOEI PS1=heeaaaa] +2022/05/05 11:07:35 Changing prompt to "heeaaaa" +2022/05/05 11:08:57 command: "echo" [heeeeey] +2022/05/05 11:09:06 Received EOF, closing TTY +2022/05/05 11:09:06 logged in with key SHA256:e+GGc8xMdIe6nXdohM3LR8s2H4moA8zffv8CP5WjB8M +2022/05/05 11:09:06 received request of type "pty-req" +2022/05/05 11:09:06 window resize 106x42 +2022/05/05 11:09:06 pty-req 'xterm-256color' +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "env" +2022/05/05 11:09:06 received request of type "shell" +2022/05/05 11:09:11 command: "echo" [heey] +2022/05/05 11:09:16 command: "echo" [ + + +faiwiefjawiejfwae +ajeifwajef + + +] +2022/05/05 11:09:38 Heredoc current word: "HAAAAAA" +2022/05/05 11:09:38 Encountered heredoc marker: HAAAAAA +2022/05/05 11:09:40 Heredoc current word: "fijwoiajfawoie" +2022/05/05 11:09:40 Heredoc current word: "afjwaoeifj" +2022/05/05 11:09:41 Heredoc current word: "awfjoiwjfe" +2022/05/05 11:09:41 Heredoc current word: "" +2022/05/05 11:09:42 Heredoc current word: "jawif;eijaw" +2022/05/05 11:09:42 Heredoc current word: "" +2022/05/05 11:09:49 Heredoc current word: "HAAAAAAA" +2022/05/05 11:09:59 Heredoc current word: "HAAAAAA" +2022/05/05 11:09:59 exiting heredoc +2022/05/05 11:09:59 command: "while" [read -r heey; do hahahaha; done </dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 command: "set" [+o vi +o emacs] +2022/05/05 12:18:04 Ignoring "set" command +2022/05/05 12:18:04 command: "stty" [-inlcr -onlcr -echo kill ^U erase ^H] +2022/05/05 12:18:04 command: "echo" [foo] +2022/05/05 12:18:04 command: "PS1=///eac57010bddba5dae79f08f3cd43067c#$" [PS2= PS3= PROMPT_COMMAND=] +2022/05/05 12:18:04 Changing prompt to "///eac57010bddba5dae79f08f3cd43067c#$" +2022/05/05 12:18:04 command: "echo" ["`uname -sr`" 2>/dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 Tramp requested uname, returning a fake one: Linux 5.16.19-76051619-generic +2022/05/05 12:18:04 command: "(echo" [foo ; echo bar)] +2022/05/05 12:18:04 Handling tramp's foobar test +2022/05/05 12:18:04 command: "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/local/bin:/local/freeware/bin:/local/gnu/bin:/usr/freeware/bin:/usr/pkg/bin:/usr/contrib/bin:/opt/bin:/opt/sbin:/opt/local/bin" [&& export PATH] +2022/05/05 12:18:04 command: "mesg" [n 2>/dev/null; biff n 2>/dev/null] +2022/05/05 12:18:04 Error: Received unexpected command mesg +2022/05/05 12:18:04 command: "stty" [tab0] +2022/05/05 12:18:04 command: "stty" [iutf8 2>/dev/null] +2022/05/05 12:18:04 Warning, tramp requested unexpected stty option 2>/dev/null +2022/05/05 12:18:04 command: "echo" ["`tty`" 2>/dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 Telling tramp that it's dealing with a tty +2022/05/05 12:18:04 Heredoc current word: "'cf8110e8e028188d140944ad469ceb8f'" +2022/05/05 12:18:04 Encountered heredoc marker: 'cf8110e8e028188d140944ad469ceb8f' +2022/05/05 12:18:04 Heredoc current word: "LC_ALL en_US.utf8" +2022/05/05 12:18:04 Heredoc current word: "ENV ''" +2022/05/05 12:18:04 Heredoc current word: "TMOUT 0" +2022/05/05 12:18:04 Heredoc current word: "LC_CTYPE ''" +2022/05/05 12:18:04 Heredoc current word: "PAGER cat" +2022/05/05 12:18:04 Heredoc current word: "cf8110e8e028188d140944ad469ceb8f" +2022/05/05 12:18:04 exiting heredoc +2022/05/05 12:18:04 command: "while" [read var val; do export $var=$val; done </dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 command: "/bin/test" [-e / 2>/dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 Error: Received unexpected command /bin/test +2022/05/05 12:18:04 command: "/usr/bin/test" [-e / 2>/dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 Error: Received unexpected command /usr/bin/test +2022/05/05 12:18:04 Heredoc current word: "'cf8110e8e028188d140944ad469ceb8f'" +2022/05/05 12:18:04 Encountered heredoc marker: 'cf8110e8e028188d140944ad469ceb8f' +2022/05/05 12:18:04 Heredoc current word: "/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/bin" +2022/05/05 12:18:04 Heredoc current word: "/sbin" +2022/05/05 12:18:04 Heredoc current word: "/usr/sbin" +2022/05/05 12:18:04 Heredoc current word: "/usr/local/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/local/sbin" +2022/05/05 12:18:04 Heredoc current word: "/local/bin" +2022/05/05 12:18:04 Heredoc current word: "/local/freeware/bin" +2022/05/05 12:18:04 Heredoc current word: "/local/gnu/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/freeware/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/pkg/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/contrib/bin" +2022/05/05 12:18:04 Heredoc current word: "/opt/bin" +2022/05/05 12:18:04 Heredoc current word: "/opt/sbin" +2022/05/05 12:18:04 Heredoc current word: "/opt/local/bin" +2022/05/05 12:18:04 Heredoc current word: "cf8110e8e028188d140944ad469ceb8f" +2022/05/05 12:18:04 exiting heredoc +2022/05/05 12:18:04 command: "while" [read d; do if test -x $d/ls && test -f $d/ls; then echo tramp_executable $d/ls; break; fi; done </dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 command: "/bin/test" [-e / 2>/dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 Error: Received unexpected command /bin/test +2022/05/05 12:18:04 command: "/usr/bin/test" [-e / 2>/dev/null; echo tramp_exit_status $?] +2022/05/05 12:18:04 Error: Received unexpected command /usr/bin/test +2022/05/05 12:18:04 Heredoc current word: "'cf8110e8e028188d140944ad469ceb8f'" +2022/05/05 12:18:04 Encountered heredoc marker: 'cf8110e8e028188d140944ad469ceb8f' +2022/05/05 12:18:04 Heredoc current word: "/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/bin" +2022/05/05 12:18:04 Heredoc current word: "/sbin" +2022/05/05 12:18:04 Heredoc current word: "/usr/sbin" +2022/05/05 12:18:04 Heredoc current word: "/usr/local/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/local/sbin" +2022/05/05 12:18:04 Heredoc current word: "/local/bin" +2022/05/05 12:18:04 Heredoc current word: "/local/freeware/bin" +2022/05/05 12:18:04 Heredoc current word: "/local/gnu/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/freeware/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/pkg/bin" +2022/05/05 12:18:04 Heredoc current word: "/usr/contrib/bin" +2022/05/05 12:18:04 Heredoc current word: "/opt/bin" +2022/05/05 12:18:04 Heredoc current word: "/opt/sbin" +2022/05/05 12:18:04 Heredoc current word: "/opt/local/bin" +2022/05/05 12:18:04 Heredoc current word: "cf8110e8e028188d140944ad469ceb8f" +2022/05/05 12:18:04 exiting heredoc +2022/05/05 12:18:04 command: "while" [read d; do if test -x $d/ls && test -f $d/ls; then echo tramp_executable $d/ls; break; fi; done </dev/null; echo tramp_exit_status $?] diff --git a/main.go b/main.go new file mode 100644 index 0000000..de92528 --- /dev/null +++ b/main.go @@ -0,0 +1,144 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/binary" + "encoding/pem" + "io/ioutil" + "log" + + ipfsapi "github.com/ipfs/go-ipfs-api" + "golang.org/x/crypto/ssh" +) + +func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) { + // Private Key generation + privateKey, err := rsa.GenerateKey(rand.Reader, bitSize) + if err != nil { + return nil, err + } + + // Validate Private Key + err = privateKey.Validate() + if err != nil { + return nil, err + } + + log.Println("Private Key generated") + return privateKey, nil +} + +func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte { + // Get ASN.1 DER format + privDER := x509.MarshalPKCS1PrivateKey(privateKey) + + // pem.Block + privBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Headers: nil, + Bytes: privDER, + } + + // Private key in PEM format + privatePEM := pem.EncodeToMemory(&privBlock) + + return privatePEM +} + +// parseDims extracts two uint32s from the provided buffer. +func parseDims(b []byte) (uint32, uint32) { + w := binary.BigEndian.Uint32(b) + h := binary.BigEndian.Uint32(b[4:]) + return w, h +} + +const WebDir = "/hugo-website" + +func main() { + // shell := ipfsapi.NewShell("127.0.0.1:5001") + + // ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Hour)) + // _, err := shell.KeyGen(ctx, "website-index") + // if err != nil { + // log.Fatal(err) + // } + + // id, err := shell.Add( + // strings.NewReader( + // "" + + // "

heeeeeeeyaa

", + // ), + // ) + // if err != nil { + // log.Fatal(err) + // } + + // log.Println("added file by name " + id) + + // resp, err := shell.PublishWithDetails(id, "website/index.html", 24*365*10*time.Hour, time.Hour, true) + // if err != nil { + // log.Fatal(err) + // } + + // log.Println(resp.Name) + + // obj, err := shell.ObjectGet("QmbJSh4EQvxz6cD6NyZ92smxBYfwTU6zr6No5APjANZ92D") + + // if err != nil { + // log.Fatal(err) + // } + + // log.Println(obj.Links) + + // err = shell.FilesMkdir(ctx, WebDir) + // if err!= nil { + // log.Fatal(err) + // } + + // shell.FilesLs(ctx context.Context, path string, options ...ipfsapi.FilesOpt) + // stats, err := shell.FilesStat(ctx, WebDir) + // if err != nil { + // if ipfsErr, ok := err.(*ipfsapi.Error); ok { + // log.Println("IPFS error code: ", ipfsErr.Code, ipfsErr.Command) + + // } + + // log.Fatal(err) + // } + + // log.Println(stats) + + // Public key authentication is done by comparing + // the public key of a received connection + // with the entries in the authorized_keys file. + authorizedKeysBytes, err := ioutil.ReadFile("/home/hugo/.ssh/authorized_keys") + if err != nil { + log.Fatalf("Failed to load authorized_keys, err: %v", err) + } + + authorizedKeysMap := map[string]bool{} + for len(authorizedKeysBytes) > 0 { + pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes) + if err != nil { + log.Fatal(err) + } + + authorizedKeysMap[string(pubKey.Marshal())] = true + authorizedKeysBytes = rest + } + + // shell := + filestore, err := NewIPFSFilestore(ipfsapi.NewShell("127.0.0.1:5001"), WebDir) + if err != nil { + log.Fatal(err) + } + + server := &Sshd{ + AuthorizedKeysMap: authorizedKeysMap, + Filestore: filestore, + } + + log.Fatal(server.Listen("0.0.0.0:2022")) +} diff --git a/nssh b/nssh new file mode 100755 index 0000000..70df362 Binary files /dev/null and b/nssh differ diff --git a/shell.go b/shell.go new file mode 100644 index 0000000..3dda0aa --- /dev/null +++ b/shell.go @@ -0,0 +1,228 @@ +package main + +import ( + "bufio" + "errors" + "io" + "log" + "os" + "strings" + "unsafe" + + "github.com/google/shlex" + "github.com/pkg/term/termios" + "golang.org/x/sys/unix" +) + +type Shell struct { + pty *os.File + tty *os.File + prompt []byte + + readData []byte + readIndex int + winsize *Winsize +} + +type Command struct { + Name string + Arguments []string +} + +func (s *Shell) GetPty() *os.File { + return s.pty +} + +func (s *Shell) Attach(conn io.ReadWriteCloser) error { + pty, tty, err := termios.Pty() + if err != nil { + return err + } + + s.pty = pty + s.tty = tty + + if s.winsize != nil { + SetWinsize(s.pty.Fd(), s.winsize) + } + + go func() { + defer s.pty.Close() + io.Copy(s.pty, conn) + }() + + go func() { + defer conn.Close() + io.Copy(conn, s.pty) + }() + + _, err = s.tty.Write(s.prompt) + return err +} + +func NewShell(prompt string) *Shell { + return &Shell{ + prompt: []byte(prompt), + } +} + +func (s *Shell) SetWinsize(w, h uint32) { + s.winsize = &Winsize{Width: uint16(w), Height: uint16(h)} + + if s.pty != nil { + SetWinsize(s.pty.Fd(), s.winsize) + } +} + +func (s *Shell) Close() (error, error) { + return s.pty.Close(), s.tty.Close() +} + +var ErrEmptyCommand error = errors.New("Empty command line") + +func (s *Shell) ReadCommand() (*Command, error) { + var currentLine string + var backslash bool + var insideDoubleQuote bool + var insideSingleQuote bool + var maybeHeredoc bool + var insideHeredoc bool + var heredocMarker string + var heredocMarkerComplete bool + var heredocCurrentWord string + + reader := bufio.NewReader(s.tty) + +GatherCommand: + for { + char, _, err := reader.ReadRune() + if err != nil { + return nil, err + } + + if insideHeredoc { + currentLine += string(char) + + if !heredocMarkerComplete && char == '<' { + insideHeredoc = false + continue + } + + if char == '\n' { + log.Printf("Heredoc current word: \"%s\"", heredocCurrentWord) + if heredocCurrentWord == heredocMarker { + log.Println("exiting heredoc") + insideHeredoc = false + heredocMarkerComplete = false + break GatherCommand + } + + if !heredocMarkerComplete { + log.Println("Encountered heredoc marker: " + heredocCurrentWord) + heredocMarker = strings.Trim(heredocCurrentWord, "'") + heredocMarkerComplete = true + } + + heredocCurrentWord = "" + continue + } + + heredocCurrentWord += string(char) + continue + } + + if backslash { + if !insideDoubleQuote && char == '\n' { + currentLine += string(" ") + continue + } + + currentLine += "\\" + string(char) + backslash = false + continue + } + + switch rune(char) { + case '<': + currentLine += string(char) + if maybeHeredoc { + maybeHeredoc = false + insideHeredoc = true + continue + } + + maybeHeredoc = true + continue + case '\\': + backslash = true + continue + case '"': + insideDoubleQuote = !insideDoubleQuote + currentLine += string(char) + case '\'': + insideSingleQuote = !insideSingleQuote + currentLine += string(char) + case '\n': + if insideSingleQuote || insideDoubleQuote { + currentLine += string(char) + continue + } + + break GatherCommand + default: + currentLine += string(char) + } + } + + commandLine, err := shlex.Split(currentLine) + if err != nil { + return nil, err + } + + if len(commandLine) == 0 { + return nil, ErrEmptyCommand + } + + comm := &Command{Name: commandLine[0]} + if len(commandLine) > 1 { + comm.Arguments = commandLine[1:] + } + + return comm, nil +} + +func (s *Shell) SetPrompt(prompt string) { + log.Printf("Changing prompt to \"%s\"", prompt) + s.prompt = []byte(prompt) +} + +func (s *Shell) WritePrompt() error { + _, err := s.tty.Write(s.prompt) + return err +} + +func (s *Shell) WriteOutput(output []byte) (int, error) { + return s.tty.Write(output) +} + +func (s *Shell) WriteOutputString(output string) (int, error) { + return s.WriteOutput([]byte(output)) +} + +// Winsize stores the Height and Width of a terminal. +type Winsize struct { + Height uint16 + Width uint16 + x uint16 // unused + y uint16 // unused +} + +// SetWinsize sets the size of the given pty. +func SetWinsize(fd uintptr, winsize *Winsize) { + log.Printf("window resize %dx%d", winsize.Width, winsize.Height) + unix.Syscall( + unix.SYS_IOCTL, + fd, + uintptr(unix.TIOCSWINSZ), uintptr(unsafe.Pointer(winsize)), + ) +} diff --git a/skynet.go b/skynet.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/skynet.go @@ -0,0 +1 @@ +package main diff --git a/sshd.go b/sshd.go new file mode 100644 index 0000000..b7337d5 --- /dev/null +++ b/sshd.go @@ -0,0 +1,170 @@ +package main + +import ( + "errors" + "fmt" + "io" + "log" + "net" + + "golang.org/x/crypto/ssh" +) + +type Sshd struct { + AuthorizedKeysMap map[string]bool + Filestore Filestore +} + +func (s *Sshd) Listen(iface string) error { + // An SSH server is represented by a ServerConfig, which holds + // certificate details and handles authentication of ServerConns. + config := &ssh.ServerConfig{ + // Remove to disable public key auth. + PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) { + if s.AuthorizedKeysMap[string(pubKey.Marshal())] { + return &ssh.Permissions{ + // Record the public key used for authentication. + Extensions: map[string]string{ + "pubkey-fp": ssh.FingerprintSHA256(pubKey), + }, + }, nil + } + return nil, fmt.Errorf("unknown public key for %q", c.User()) + }, + } + + bitSize := 4096 + + privateKey, err := generatePrivateKey(bitSize) + if err != nil { + return err + } + + privateBytes := encodePrivateKeyToPEM(privateKey) + + private, err := ssh.ParsePrivateKey(privateBytes) + if err != nil { + return fmt.Errorf("Failed to parse private key: %w", err) + } + + config.AddHostKey(private) + + // Once a ServerConfig has been configured, connections can be + // accepted. + listener, err := net.Listen("tcp", iface) + if err != nil { + return err + } + + return s.HandleRequests(listener, config) +} + +func (s *Sshd) HandleRequests(listener net.Listener, config *ssh.ServerConfig) error { + for { + nConn, err := listener.Accept() + if err != nil { + return fmt.Errorf("failed to accept incoming connection: %w", err) + } + + // Before use, a handshake must be performed on the incoming + // net.Conn. + conn, chans, reqs, err := ssh.NewServerConn(nConn, config) + if err != nil { + log.Printf("failed to handshake: %s", err) + continue + } + log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"]) + + // The incoming Request channel must be serviced. + go ssh.DiscardRequests(reqs) + + // Service the incoming Channel channel. + for newChannel := range chans { + // Channels have a type, depending on the application level + // protocol intended. In the case of a shell, the type is + // "session" and ServerShell may be used to present a simple + // terminal interface. + if newChannel.ChannelType() != "session" { + newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") + continue + } + channel, requests, err := newChannel.Accept() + if err != nil { + return fmt.Errorf("Could not accept channel: %w", err) + } + + session := &SshSession{ + shell: NewShell("user@host:/# "), + filestore: s.Filestore, + } + + // Sessions have out-of-band requests such as "shell", + // "pty-req" and "env". Here we handle only the + // "shell" request. + go session.HandleRequests(requests) + + go session.RunShell(channel) + } + } +} + +type SshSession struct { + shell *Shell + filestore Filestore +} + +func (s *SshSession) HandleRequests(in <-chan *ssh.Request) { + for req := range in { + log.Printf("received request of type \"%s\"", req.Type) + + var ok bool + switch req.Type { + case "shell": + ok = true + case "pty-req": + // Responding 'ok' here will let the client + // know we have a pty ready for input + ok = true + // Parse body... + termLen := req.Payload[3] + termEnv := string(req.Payload[4 : termLen+4]) + w, h := parseDims(req.Payload[termLen+4:]) + s.shell.SetWinsize(w, h) + log.Printf("pty-req '%s'", termEnv) + } + + req.Reply(ok, nil) + } +} + +func (s *SshSession) RunShell(channel io.ReadWriteCloser) { + s.shell.Attach(channel) + handler := NewCommandHandler(s.shell, s.filestore) + + go func() { + for { + comm, err := s.shell.ReadCommand() + if err != nil { + if errors.Is(err, io.EOF) { + log.Println("Received EOF, closing TTY") + } else { + log.Println("Error reading command: ", err) + } + + if !errors.Is(err, ErrEmptyCommand) { + s.shell.Close() + return + } + } else { + log.Printf("command: \"%s\" %s", comm.Name, comm.Arguments) + + handler.Handle(comm) + } + + err = s.shell.WritePrompt() + if err != nil { + log.Println(err) + } + } + }() +}